docs(36-02): create SUMMARY.md for plan 36-02 completion
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
plan: 36-02
|
||||||
|
phase: 36
|
||||||
|
title: "Client /admin route, admin shell with sidebar, UserMenu admin link"
|
||||||
|
status: complete
|
||||||
|
completed: 2026-04-19
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Was Built
|
||||||
|
|
||||||
|
Client-side admin foundation for Phase 36:
|
||||||
|
|
||||||
|
1. **AuthState.isAdmin** — `src/client/hooks/useAuth.ts` `AuthState` interface updated with `isAdmin?: boolean` in the user object type. The hook fetches from `/api/auth/me` which now returns this field after plan 36-01.
|
||||||
|
|
||||||
|
2. **Admin layout route** — `src/client/routes/admin.tsx` with `createFileRoute("/admin")`:
|
||||||
|
- `useEffect` guard redirects non-admin users to `/` (chosen over `beforeLoad` because router context is `{}` — no queryClient available at route load time).
|
||||||
|
- Returns `null` while loading or if not admin — no flash of admin shell.
|
||||||
|
- Sidebar with "Admin" heading and two disabled nav items: "Items" (package icon, "Soon" badge) and "Tags" (tag icon, "Soon" badge).
|
||||||
|
- `<Outlet />` renders child routes in the main content area.
|
||||||
|
|
||||||
|
3. **Admin index placeholder** — `src/client/routes/admin/index.tsx` with `createFileRoute("/admin/")`:
|
||||||
|
- Centered placeholder with shield icon, "Admin Panel" text, and "Select a section from the sidebar" subtext.
|
||||||
|
|
||||||
|
4. **UserMenu Admin link** — `src/client/components/UserMenu.tsx`:
|
||||||
|
- Conditional `{auth?.user?.isAdmin && (...)}` block renders an Admin link (shield icon, `to="/admin"`) at the top of the dropdown menu.
|
||||||
|
- Followed by a `border-t border-gray-100 my-1` divider before the existing Profile link.
|
||||||
|
- Non-admin users see no Admin link or divider.
|
||||||
|
|
||||||
|
5. **Route tree regenerated** — `src/client/routeTree.gen.ts` updated with `/admin` and `/admin/` routes.
|
||||||
|
|
||||||
|
6. **__root.tsx unchanged** — `/admin` is correctly absent from `isPublicRoute`, so unauthenticated users hitting `/admin` are redirected to `/login` by the root guard. The admin route's own guard handles non-admin authenticated users.
|
||||||
|
|
||||||
|
## Key Files
|
||||||
|
|
||||||
|
- `src/client/hooks/useAuth.ts` — isAdmin? in AuthState interface
|
||||||
|
- `src/client/routes/admin.tsx` — admin layout with sidebar shell and guard
|
||||||
|
- `src/client/routes/admin/index.tsx` — admin index placeholder
|
||||||
|
- `src/client/components/UserMenu.tsx` — conditional Admin link
|
||||||
|
- `src/client/routeTree.gen.ts` — regenerated with /admin routes
|
||||||
|
|
||||||
|
## Deviations
|
||||||
|
|
||||||
|
- Used `useEffect + navigate` guard instead of `beforeLoad` — the plan's primary recommendation. `beforeLoad` was documented as an alternative but requires queryClient in router context which is not configured (`context: {}`). The `useEffect` approach is functionally equivalent and renders `null` during the auth check so no flash occurs.
|
||||||
|
|
||||||
|
## Self-Check: PASSED
|
||||||
|
|
||||||
|
- [x] src/client/routes/admin.tsx exists with createFileRoute("/admin") and guard logic
|
||||||
|
- [x] src/client/routes/admin/index.tsx exists with placeholder UI
|
||||||
|
- [x] Admin sidebar renders "Items" (package icon) and "Tags" (tag icon) both disabled with "Soon" badge
|
||||||
|
- [x] Non-admin redirect implemented via useEffect
|
||||||
|
- [x] UserMenu shows Admin link when auth.user.isAdmin is true
|
||||||
|
- [x] bun run build exits 0
|
||||||
|
- [x] routeTree.gen.ts includes /admin and /admin/ routes
|
||||||
Reference in New Issue
Block a user