plan(36): admin role & panel foundation — 2 plans ready
- 36-RESEARCH.md: schema migration, requireAdmin middleware, /api/auth/me surface, client routing patterns, grant script, wave breakdown - 36-UI-SPEC.md: admin shell layout, sidebar disabled nav items, UserMenu admin link, palette and responsive notes - 36-01-PLAN.md (wave 1): isAdmin schema column + Drizzle migration, requireAdmin middleware, /api/auth/me isAdmin field, /api/admin placeholder route, scripts/grant-admin.ts - 36-02-PLAN.md (wave 2): AuthState isAdmin type, /admin client route with sidebar shell, admin/index.tsx placeholder, UserMenu admin link - STATE.md: updated to Phase 36, ready to execute, 2 plans Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
126
.planning/phases/36-admin-role-panel-foundation/36-UI-SPEC.md
Normal file
126
.planning/phases/36-admin-role-panel-foundation/36-UI-SPEC.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Phase 36: Admin Role & Panel Foundation — UI Design Contract
|
||||
|
||||
**Phase:** 36 — Admin Role & Panel Foundation
|
||||
**Created:** 2026-04-19
|
||||
**Status:** Ready for planning
|
||||
|
||||
---
|
||||
|
||||
## Design Intent
|
||||
|
||||
The admin panel is a protected, minimal shell consistent with the app's existing light/airy aesthetic. It is not a distinct visual world — it reuses the same white background, gray borders, and sans-serif type as the rest of GearBox. The only indicator of admin context is the sidebar and a subtle "Admin" badge or heading.
|
||||
|
||||
---
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ TopNav (existing — unchanged) │
|
||||
├──────────────┬──────────────────────────────────────────┤
|
||||
│ Sidebar │ Main content area │
|
||||
│ w-56 │ flex-1, min-h │
|
||||
│ border-r │ │
|
||||
│ │ <Outlet /> (placeholder for now) │
|
||||
│ Admin │ │
|
||||
│ ────────── │ │
|
||||
│ □ Items │ │
|
||||
│ □ Tags │ │
|
||||
│ │ │
|
||||
└──────────────┴──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Component Specs
|
||||
|
||||
### Admin Shell (`src/client/routes/admin.tsx`)
|
||||
|
||||
**Outer wrapper:** `flex min-h-[calc(100vh-3.5rem)]` (full height minus TopNav 3.5rem/14)
|
||||
|
||||
**Sidebar:**
|
||||
- `w-56 border-r border-gray-100 bg-white p-4 flex flex-col gap-1`
|
||||
- Header: `text-xs font-semibold text-gray-400 uppercase tracking-wide mb-3` — "Admin"
|
||||
- Nav items: `flex items-center gap-2 px-3 py-2 rounded-lg text-sm` (disabled state below)
|
||||
|
||||
**Main content:**
|
||||
- `flex-1 p-6 bg-gray-50`
|
||||
- Contains `<Outlet />`
|
||||
|
||||
### Sidebar Nav Items (Disabled / Coming Soon)
|
||||
|
||||
Both "Items" and "Tags" are disabled in this phase.
|
||||
|
||||
**Disabled item style:**
|
||||
```
|
||||
flex items-center gap-2 px-3 py-2 rounded-lg text-sm
|
||||
text-gray-300 cursor-not-allowed
|
||||
```
|
||||
|
||||
**Icon + label + badge:**
|
||||
```tsx
|
||||
<div className="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-300 cursor-not-allowed">
|
||||
<LucideIcon name="package" size={16} />
|
||||
<span>Items</span>
|
||||
<span className="ml-auto text-xs bg-gray-100 text-gray-400 px-1.5 py-0.5 rounded">Soon</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
Icons to use:
|
||||
- Items → `"package"` (matches existing collection icon)
|
||||
- Tags → `"tag"`
|
||||
|
||||
### Admin Index Placeholder (`src/client/routes/admin/index.tsx`)
|
||||
|
||||
Simple centered placeholder:
|
||||
```tsx
|
||||
<div className="flex flex-col items-center justify-center h-64 text-center">
|
||||
<LucideIcon name="shield" size={32} className="text-gray-300 mb-3" />
|
||||
<p className="text-sm text-gray-500">Admin Panel</p>
|
||||
<p className="text-xs text-gray-400 mt-1">Select a section from the sidebar</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Admin Link in UserMenu
|
||||
|
||||
Position: before Profile link (top of menu).
|
||||
Only rendered when `auth?.user?.isAdmin === true`.
|
||||
|
||||
```tsx
|
||||
{auth?.user?.isAdmin && (
|
||||
<>
|
||||
<Link
|
||||
to="/admin"
|
||||
onClick={() => setOpen(false)}
|
||||
className="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<LucideIcon name="shield" size={16} className="text-gray-400" />
|
||||
Admin
|
||||
</Link>
|
||||
<div className="border-t border-gray-100 my-1" />
|
||||
</>
|
||||
)}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Palette (existing conventions)
|
||||
|
||||
| Token | Value | Usage |
|
||||
|-------|-------|-------|
|
||||
| bg-white | #ffffff | Sidebar, TopNav |
|
||||
| bg-gray-50 | #f9fafb | Page background, main content |
|
||||
| border-gray-100 | #f3f4f6 | Sidebar border, dividers |
|
||||
| text-gray-900 | #111827 | Active/primary text |
|
||||
| text-gray-500 | #6b7280 | Secondary text |
|
||||
| text-gray-300 | #d1d5db | Disabled items |
|
||||
| text-gray-400 | #9ca3af | Icons, muted labels |
|
||||
|
||||
---
|
||||
|
||||
## Responsive
|
||||
|
||||
- Sidebar is always visible (no mobile collapse in this phase — admin is desktop-only usage)
|
||||
- `hidden md:flex` wrapper if needed to keep mobile layout clean, but admin route is inherently desktop
|
||||
|
||||
## UI-SPEC COMPLETE
|
||||
Reference in New Issue
Block a user