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
+
+ )}