# Phase 9: Admin Panel Rearchitecture - Research **Researched:** 2026-06-12 **Domain:** Vue Router 4 nested routes, layout components, FastAPI sub-module pattern, Tailwind safelist **Confidence:** HIGH (all findings sourced directly from the live codebase) --- ## User Constraints (from CONTEXT.md) ### Locked Decisions - **D-01:** Overview layout: 3-4 stat cards top row (total users, total storage, document status: processing/ready/failed), then a table of the last 10 audit entries. - **D-02:** Backend: new `overview.py` sub-module in `backend/api/admin/`. Single `GET /api/admin/overview` endpoint, one payload with all stats. - **D-03:** Audit entries inline in the overview response — no separate `/api/audit` call from the overview component. - **D-04:** SVG icons for each sidebar nav item, consistent with `AppSidebar.vue`. - **D-05:** "Admin" label/badge below "DocuVault" logo in sidebar header. Same indigo brand color. - **D-06:** No "Back to app" link. Admin accounts are operators only. Overrides ADMIN-09 requirement text. - **D-07:** Nav items (in order): Overview, Users, Quotas, AI Config, Audit Log. - **D-08:** After login, `user.role === 'admin'` → router redirects to `/admin` instead of `/`. - **D-09:** `beforeEach` guard: admin on non-admin route → redirect to `/admin`. Admin cannot navigate to user routes. - **D-10:** Two guard branches in `beforeEach`: (a) non-admin → `/admin/*` → redirect `/`; (b) admin → non-admin route → redirect `/admin`. Both use `to.matched.some(r => r.meta.requiresAdmin)`. - **D-11:** Four existing tab components become standalone view files in `frontend/src/views/admin/`: `AdminUsersView.vue`, `AdminQuotasView.vue`, `AdminAiView.vue`, `AdminAuditView.vue`. Structural rename only — strip top-level padding. - **D-12:** Original `components/admin/AdminXxxTab.vue` files and `AdminView.vue` deleted after extraction. - **D-13:** `AuditLogTab.vue` promoted as-is. No new features for the audit view in Phase 9. - **D-14:** Tailwind safelist pattern: ```js safelist: [ { pattern: /bg-(blue|green|purple|orange|gray|indigo|red)-(50|100|500|600)/ }, { pattern: /text-(blue|green|purple|orange|gray|indigo|red)-(500|600|700)/ }, ] ``` Researcher verifies which color families `formatters.js` actually uses. - **D-15:** CODE-09 comment purge covers all Phase 9 files + retroactive purge of Phase 8 backend sub-packages. - **D-16:** Purge criterion: remove "what" comments (generic docstrings, "this does X"). Keep "why" comments (constraints, pitfall notes, non-obvious invariants). ### Claude's Discretion - Exact SVG icon choices for each admin sidebar nav item (researcher picks from the same icon family used in `AppSidebar.vue`). - Exact Python queries for the overview aggregate endpoint. - Naming of the new view files if `views/admin/` directory structure is adopted. ### Deferred Ideas (OUT OF SCOPE) None — discussion stayed within phase scope. ## Phase Requirements | ID | Description | Research Support | |----|-------------|------------------| | ADMIN-08 | Admin panel moved to `/admin/*` route subtree with `AdminLayout.vue` as route component. `AdminView.vue` deleted. | Router restructure in §Architecture Patterns; AuthLayout.vue pattern; App.vue analysis | | ADMIN-09 | Admin sidebar nav links (D-06 overrides "Back to app" item). | AppSidebar.vue deep-read; nav-link CSS classes documented | | ADMIN-10 | Each admin section is its own deep-linkable URL. Browser back button works. | Vue Router 4 nested routes pattern; child route structure | | ADMIN-11 | Admin overview page at `/admin` showing platform stats + last 10 audit entries. New backend endpoint. | ORM models for aggregate query; audit.py query pattern reuse; `overview.py` design | | ADMIN-12 | `requiresAdmin` guard uses `to.matched.some(r => r.meta.requiresAdmin)`. | PITFALLS.md §Pitfall 3 — exact guard fix documented; current guard code shown | | CODE-06 | Tailwind safelist for dynamic `providerColor`/`providerBg`/`providerLabel` patterns. | formatters.js fully read — exact color families documented | | CODE-09 | Comment purge over Phase 9 files + retroactive purge of Phase 8 backend sub-packages. | Phase 8 backend sub-packages inspected; purge criterion from D-16 | --- ## Summary Phase 9 is a structural rearchitecture of the admin interface. The existing `AdminView.vue` (tabs-on-user-layout pattern) is replaced with `AdminLayout.vue` (its own Vue Router parent route whose `` renders child views). Four existing tab components are promoted to standalone views under `frontend/src/views/admin/`. A new admin overview route at `/admin` needs a new backend aggregate endpoint (`GET /api/admin/overview`). The `requiresAdmin` guard must switch from `to.meta.requiresAdmin` to `to.matched.some(r => r.meta.requiresAdmin)` — the current flat check is a security regression waiting to happen once children are added. Strict admin role separation (admins redirected away from user routes) is a new guard enhancement. The Tailwind safelist is a one-file configuration change. The comment purge is the final cleanup pass. All changes are structural — no behavior changes to existing admin functionality. The planner should organize work as: (1) backend `overview.py` new endpoint, (2) router restructure + guard updates, (3) AdminLayout + admin views extraction, (4) Tailwind safelist, (5) comment purge. **Primary recommendation:** Follow the `AuthLayout.vue` pattern exactly for `AdminLayout.vue` — a minimal wrapper with ``. Use `to.matched.some()` as the single source of truth for admin route detection everywhere in the guard. --- ## Architectural Responsibility Map | Capability | Primary Tier | Secondary Tier | Rationale | |------------|-------------|----------------|-----------| | Admin layout shell (sidebar + content area) | Frontend (new `AdminLayout.vue`) | — | Route component that owns the admin chrome | | Admin route protection | Frontend (router `beforeEach`) | — | Guard runs before any component mounts | | Admin role redirect on login | Frontend (`LoginView.vue` / router guard) | — | Role from `authStore.user.role` after token refresh | | Admin overview stats | API / Backend (`overview.py`) | — | Aggregate query across users, quotas, documents | | Admin overview data fetch | Frontend (`AdminOverviewView.vue`) | — | Single-fetch view, no store needed | | Existing admin CRUD | API / Backend (unchanged) | — | users.py, quotas.py, ai.py, audit.py unchanged | | Dynamic color class rendering | Frontend (Tailwind safelist in `tailwind.config.js`) | — | Build-time config, no runtime code | --- ## Standard Stack No new packages are installed in Phase 9. All work uses the existing stack. ### Existing Stack Used | Component | File | How Used in Phase 9 | |-----------|------|---------------------| | Vue Router 4 | Already installed | Nested route with `AdminLayout` as parent component | | FastAPI / SQLAlchemy 2.0 | Already installed | New `overview.py` endpoint with async aggregate queries | | Tailwind CSS + Vite | Already configured | Add `safelist` array to `tailwind.config.js` | | `@tailwindcss/forms` | Already installed (Phase 8) | No change needed | **No package installs required for Phase 9.** --- ## Package Legitimacy Audit Not applicable — Phase 9 installs no new packages. --- ## Architecture Patterns ### System Architecture Diagram ``` Browser (Vue 3 SPA) │ ├── /login → AuthLayout → LoginView │ └── on success: user.role === 'admin' → push('/admin') │ user.role === 'user' → push(redirect || '/') │ ├── / (user routes) │ ├── App.vue renders: flex h-screen + AppSidebar + │ └── beforeEach guard: admin role on non-admin route → redirect /admin │ └── /admin (admin routes — NEW) ├── App.vue renders: AdminLayout (resolves as /admin route's component) │ AdminLayout: w-64 AdminSidebar + for child content │ ├── /admin → AdminOverviewView (new, single fetch from /api/admin/overview) ├── /admin/users → AdminUsersView (extracted from AdminUsersTab.vue) ├── /admin/quotas → AdminQuotasView (extracted from AdminQuotasTab.vue) ├── /admin/ai → AdminAiView (extracted from AdminAiConfigTab.vue) └── /admin/audit → AdminAuditView (extracted from AuditLogTab.vue) Backend API (unchanged URL surface) │ └── GET /api/admin/overview (NEW) ├── SELECT COUNT(*) FROM users WHERE role = 'user' ├── SELECT SUM(used_bytes) FROM quotas ├── SELECT status, COUNT(*) FROM documents GROUP BY status └── Last 10 audit entries (reuse _build_filtered_query_with_handles, limit=10) ``` ### Recommended Project Structure ``` frontend/src/ ├── layouts/ │ ├── AuthLayout.vue # existing — reference pattern │ └── AdminLayout.vue # NEW — mirrors AuthLayout structure ├── views/ │ ├── AdminView.vue # DELETED after extraction │ └── admin/ # NEW directory │ ├── AdminOverviewView.vue # NEW — stats cards + audit table │ ├── AdminUsersView.vue # extracted from AdminUsersTab.vue │ ├── AdminQuotasView.vue # extracted from AdminQuotasTab.vue │ ├── AdminAiView.vue # extracted from AdminAiConfigTab.vue │ └── AdminAuditView.vue # extracted from AuditLogTab.vue ├── components/admin/ │ ├── AdminUsersTab.vue # DELETED (dead code after extraction) │ ├── AdminQuotasTab.vue # DELETED │ ├── AdminAiConfigTab.vue # DELETED │ └── AuditLogTab.vue # DELETED backend/api/admin/ ├── __init__.py # add overview_router import + include_router call ├── shared.py # unchanged ├── users.py # unchanged ├── quotas.py # unchanged ├── ai.py # unchanged └── overview.py # NEW — GET /api/admin/overview ``` ### Pattern 1: AdminLayout follows AuthLayout pattern exactly `AuthLayout.vue` is 12 lines. It is a plain `