I think finished?

This commit is contained in:
Leons Aleksandrovs
2025-07-02 22:25:03 +03:00
parent acb81d671d
commit 46dd731eab
9 changed files with 129 additions and 27 deletions

View File

@@ -1,11 +1,12 @@
import { API_BASE_URL } from "@/consts";
import type { User } from "@/types/api";
import { useQuery, type UseQueryResult } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router";
import { useEffect, useState } from "react";
function RenderList({ list }: { list: UseQueryResult<User[]> }) {
// Base classes for each row
const baseRowClass = "text-xl p-2 px-4 rounded capitalize bg-panel/75";
const baseRowClass = "flex text-xl p-2 px-4 rounded capitalize bg-panel/75";
// Display loading and error states
if (list.isLoading) return <div className={`${baseRowClass} text-gray-400`}>Loading...</div>;
@@ -14,7 +15,13 @@ function RenderList({ list }: { list: UseQueryResult<User[]> }) {
// Render all users to the list
return list.data?.map((u) => {
return (
<p className={`${baseRowClass} transition cursor-pointer hover:bg-panel hover:text-accent`}>{u.username}</p>
<Link
to="/$userId"
params={{ userId: u.id.toString() }}
className={`${baseRowClass} transition cursor-pointer hover:bg-panel hover:text-accent`}
>
{u.username}
</Link>
);
});
}
@@ -45,9 +52,9 @@ export default function UsersTable() {
className="bg-panel/75 transition focus:bg-panel border border-transparent outline-none focus:border-accent rounded px-4 py-2 mt-8 w-full "
onChange={(e) => setSearch(e.target.value)}
/>
<ul className="space-y-2 mt-2">
<div className="flex flex-col gap-2 mt-2">
<RenderList list={users} />
</ul>
</div>
</div>
);
}

View File

@@ -9,8 +9,14 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
import { Route as UserIdRouteImport } from './routes/$userId'
import { Route as IndexRouteImport } from './routes/index'
const UserIdRoute = UserIdRouteImport.update({
id: '/$userId',
path: '/$userId',
getParentRoute: () => rootRouteImport,
} as any)
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
@@ -19,28 +25,39 @@ const IndexRoute = IndexRouteImport.update({
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/$userId': typeof UserIdRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/$userId': typeof UserIdRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/$userId': typeof UserIdRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/'
fullPaths: '/' | '/$userId'
fileRoutesByTo: FileRoutesByTo
to: '/'
id: '__root__' | '/'
to: '/' | '/$userId'
id: '__root__' | '/' | '/$userId'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
UserIdRoute: typeof UserIdRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/$userId': {
id: '/$userId'
path: '/$userId'
fullPath: '/$userId'
preLoaderRoute: typeof UserIdRouteImport
parentRoute: typeof rootRouteImport
}
'/': {
id: '/'
path: '/'
@@ -53,6 +70,7 @@ declare module '@tanstack/react-router' {
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
UserIdRoute: UserIdRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)

View File

@@ -0,0 +1,67 @@
import MainContainer from "@/components/MainContainer";
import { API_BASE_URL } from "@/consts";
import type { UserInfo } from "@/types/api";
import { capitalizeFirstLetter } from "@/utils/capitalizeFirstLetter";
import { useQuery, type UseQueryResult } from "@tanstack/react-query";
import { createFileRoute, useParams } from "@tanstack/react-router";
export const Route = createFileRoute("/$userId")({
component: RouteComponent,
});
function DisplayUserName({ info }: { info: UseQueryResult<UserInfo> }) {
// Base classes for headers
const baseClasses = "text-4xl font-bold text-center";
// Display loading and error states
if (info.isLoading) return <h1 className={`${baseClasses} text-gray-400`}>Loading user info ...</h1>;
if (info.isError) return <h1 className={`${baseClasses} text-red-400`}>Error: {info.error.message}</h1>;
// Check if user is null
if (!info.data?.username) return <h1 className={`${baseClasses} text-red-400`}>User not found</h1>;
return <h1 className={`${baseClasses} text-accent`}>{capitalizeFirstLetter(info.data.username)}</h1>;
}
function DisplayUserTable({ info }: { info: UseQueryResult<UserInfo> }) {
// Base classes for headers
const baseClasses = "p-2 text-center";
if (!info.data) return; // return nothing when no data
if (!info.data.username) return; // Return nothing when user is null
if (info.data.attendance.length === 0) return <p className={`text-gray-400 text-center`}>No attendance</p>; // Empty attendance, return msg
// Display attendance in a table
return (
<table className="w-full bg-panel rounded">
<thead>
<th className={`${baseClasses}`}>Date</th>
<th className={`${baseClasses}`}>Hours worked</th>
</thead>
<tbody>
{info.data.attendance.map((a) => (
<tr key={a.date} className="odd:bg-body/50">
<td className={`${baseClasses}`}>{a.date}</td>
<td className={`${baseClasses}`}>{a.hours_worked}</td>
</tr>
))}
</tbody>
</table>
);
}
function RouteComponent() {
const { userId } = Route.useParams();
// Define url for user info fetch
const userInfoURL = new URL(`${API_BASE_URL}/${userId}`);
const fetchUserInfo = () => fetch(userInfoURL).then((res) => res.json());
const userInfo = useQuery<UserInfo>({ queryKey: ["user", userId], queryFn: fetchUserInfo });
return (
<MainContainer className="pt-8 space-y-4">
<DisplayUserName info={userInfo} />
<DisplayUserTable info={userInfo} />
</MainContainer>
);
}

View File

@@ -9,7 +9,7 @@ export const Route = createFileRoute("/")({
function App() {
return (
<MainContainer className="pt-8">
<h1 className="text-4xl font-bold text-center text-accent">Darbinieki</h1>
<h1 className="text-4xl font-bold text-center text-accent">Employees</h1>
<UsersTable />
</MainContainer>
);

View File

@@ -1,3 +1,14 @@
export interface User {
id: number;
username: string;
}
export interface Attendance {
date: string;
hours_worked: number;
}
export interface UserInfo {
username: string | null;
attendance: Attendance[];
}

View File

@@ -0,0 +1,3 @@
export function capitalizeFirstLetter(val: string) {
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
}