diff --git a/.phase4-state.json b/.phase4-state.json index e926df6..ec24560 100644 --- a/.phase4-state.json +++ b/.phase4-state.json @@ -3,8 +3,9 @@ "admin-user-management", "csv-export-time-entries", "error-boundary", - "dashboard-charts" + "dashboard-charts", + "api-client-phase4" ], - "current_feature": "api-client-phase4", + "current_feature": "router-with-admin", "started_at": "2026-05-23T05:10:51.482879" } \ No newline at end of file diff --git a/GENERATION_LOG.md b/GENERATION_LOG.md index a7f7317..727249d 100644 --- a/GENERATION_LOG.md +++ b/GENERATION_LOG.md @@ -515,3 +515,15 @@ undefined - `05:17:24` **INFO** wrote 3845 chars in 34.1s (attempt 1) - `05:17:24` **INFO** Running tsc --noEmit on api… - `05:17:26` **INFO** tsc clean ✓ +- `05:17:26` **INFO** Committed feature api-client-phase4 +- `05:17:26` **INFO** Pushed: rc=0 + +## Phase-3 Feature: router-with-admin (2026-05-23 05:17:26) + +- `05:17:26` **INFO** Description: App.tsx +/admin route + Nav admin-link bei admin-role +- `05:17:26` **INFO** Generating apps/web/src/App.tsx (ERWEITERT — füge Route /admin (AdminUsers component) hinzu. Auth-Check…) +- `05:17:50` **INFO** wrote 2745 chars in 24.1s (attempt 1) +- `05:17:50` **INFO** Generating apps/web/src/components/Nav.tsx (ERWEITERT — Nav zeigt Admin-Link nur wenn current user role='admin'. u…) +- `05:18:20` **INFO** wrote 3580 chars in 29.7s (attempt 1) +- `05:18:20` **INFO** Running tsc --noEmit on api… +- `05:18:21` **INFO** tsc clean ✓ diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 28066e6..47b0b51 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -5,6 +5,7 @@ import TimeEntries from "./pages/TimeEntries" import Customers from "./pages/Customers" import Projects from "./pages/Projects" import Profile from "./pages/Profile" +import AdminUsers from "./pages/AdminUsers" import Nav from "./components/Nav" import { ToastProvider } from "./components/Toast" import ErrorBoundary from "./components/ErrorBoundary" @@ -35,6 +36,22 @@ const authCheck = async () => { } } +const adminCheck = async () => { + try { + const user = await api.getMe() + if (user.role !== "admin") { + throw redirect({ to: "/" }) + } + } catch (error: any) { + if (error.status === 401) { + throw redirect({ to: "/login" }) + } else if (error.status === 302 || error.status === 403) { + throw error + } + throw redirect({ to: "/" }) + } +} + const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: "/", @@ -70,13 +87,21 @@ const profileRoute = createRoute({ component: Profile }) +const adminRoute = createRoute({ + getParentRoute: () => rootRoute, + path: "/admin", + beforeLoad: adminCheck, + component: AdminUsers +}) + const routeTree = rootRoute.addChildren([ indexRoute, loginRoute, timeEntriesRoute, customersRoute, projectsRoute, - profileRoute + profileRoute, + adminRoute ]) const router = createRouter({ diff --git a/apps/web/src/components/Nav.tsx b/apps/web/src/components/Nav.tsx index 9d4de00..d75fe67 100644 --- a/apps/web/src/components/Nav.tsx +++ b/apps/web/src/components/Nav.tsx @@ -6,12 +6,19 @@ import { Users, FolderKanban, User, - LogOut + LogOut, + ShieldCheck } from "lucide-react" +import { useQuery } from "@tanstack/react-query" import { api } from "../lib/api" export default function Nav() { const location = useLocation() + + const { data: user } = useQuery({ + queryKey: ['me'], + queryFn: api.getMe + }) const navItems = [ { label: "Dashboard", to: "/", icon: Home }, @@ -60,6 +67,20 @@ export default function Nav() { ) })} + + {user?.role === 'admin' && ( + + + Admin + + )}