Moves phases 08–11 execution artifacts from .planning/phases/ to .planning/milestones/v0.2-phases/ to keep .planning/phases/ clean for the next milestone. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
109 lines
5.5 KiB
Markdown
109 lines
5.5 KiB
Markdown
---
|
|
phase: "09"
|
|
plan: "02"
|
|
subsystem: frontend-admin-shell
|
|
tags: [admin, layout, vue, routing]
|
|
dependency_graph:
|
|
requires: [09-01]
|
|
provides: [AdminLayout.vue, AdminSidebar.vue, AdminOverviewView.vue, getAdminOverview]
|
|
affects: [frontend/src/api/admin.js, frontend/src/layouts/, frontend/src/components/admin/, frontend/src/views/admin/]
|
|
tech_stack:
|
|
added: []
|
|
patterns: [layout-as-route-component, single-fetch-view, scoped-css-apply]
|
|
key_files:
|
|
created:
|
|
- frontend/src/layouts/AdminLayout.vue
|
|
- frontend/src/components/admin/AdminSidebar.vue
|
|
- frontend/src/views/admin/AdminOverviewView.vue
|
|
modified:
|
|
- frontend/src/api/admin.js
|
|
decisions:
|
|
- "Four stat cards chosen (Users, Storage, Processing, Ready) — ADMIN-11 lists 4 stats explicitly; D-01 allows 3-4 cards so 4 was chosen as the natural number matching the backend response fields"
|
|
- "No Pinia store for AdminOverviewView — single-fetch component consistent with all existing admin tab patterns (confirmed by RESEARCH.md open question 2)"
|
|
- "D-06 applied: no Back-to-app link anywhere in AdminSidebar"
|
|
- "AdminOverviewView has no top-level padding — AdminLayout owns p-8 max-w-5xl mx-auto (Pitfall 9 avoided)"
|
|
metrics:
|
|
duration: "18 minutes"
|
|
completed: "2026-06-12"
|
|
tasks_completed: 3
|
|
tasks_total: 3
|
|
files_created: 3
|
|
files_modified: 1
|
|
---
|
|
|
|
# Phase 09 Plan 02: Admin Route Shell — Layout, Sidebar, Overview View Summary
|
|
|
|
**One-liner:** AdminLayout (flex sidebar shell), AdminSidebar (5 nav links, indigo Admin badge, no Back-to-app), AdminOverviewView (4-card stats + 10-row audit table fetching GET /api/admin/overview).
|
|
|
|
## Tasks Completed
|
|
|
|
| Task | Name | Commit | Files |
|
|
|------|------|--------|-------|
|
|
| 1 | Add getAdminOverview() to api/admin.js | fd12ee2 | frontend/src/api/admin.js |
|
|
| 2 | Create AdminLayout.vue + AdminSidebar.vue | 4eb489f | frontend/src/layouts/AdminLayout.vue, frontend/src/components/admin/AdminSidebar.vue |
|
|
| 3 | Create AdminOverviewView.vue | 1e14e15 | frontend/src/views/admin/AdminOverviewView.vue |
|
|
|
|
## What Was Built
|
|
|
|
### Task 1 — getAdminOverview()
|
|
|
|
Added `export async function getAdminOverview()` to `frontend/src/api/admin.js`. The function calls `request('/api/admin/overview')` using the pre-existing `request` import from `utils.js`. No new import added. The function is automatically re-exported by `client.js` via the barrel export pattern established in Phase 8 CODE-04.
|
|
|
|
### Task 2 — AdminLayout.vue + AdminSidebar.vue
|
|
|
|
`AdminLayout.vue` is a minimal flex-row shell: `<div class="flex h-screen overflow-hidden">` wrapping `<AdminSidebar />` and a `<main>` with the content padding wrapper (`p-8 max-w-5xl mx-auto`) that contains `<router-view />`. Uses `<script setup>` importing `AdminSidebar` only.
|
|
|
|
`AdminSidebar.vue` mirrors `AppSidebar.vue`'s chrome exactly:
|
|
- `<aside class="w-64 bg-white border-r border-gray-200 flex flex-col h-full shrink-0">`
|
|
- Logo header with `DocuVault` h1 and `Admin` subtitle in `text-xs text-indigo-500 font-semibold` (D-05)
|
|
- 5 `<router-link>` elements in D-07 order with SVG icons (Overview, Users, Quotas, AI Config, Audit Log)
|
|
- Overview uses `$route.path === '/admin'` exact match; the four children use `startsWith('/admin/<segment>')`
|
|
- User identity footer + sign-out (copied verbatim from AppSidebar.vue)
|
|
- Scoped CSS: `.nav-link` and `.nav-link-active` with identical `@apply` rules to AppSidebar.vue
|
|
- No Back-to-app link (D-06 applied)
|
|
|
|
### Task 3 — AdminOverviewView.vue
|
|
|
|
Single-fetch view with local `ref` state (no Pinia store). `onMounted` calls `getAdminOverview()` with loading/error handling. Template structure:
|
|
- `<h2>Overview</h2>` heading
|
|
- Loading and error states
|
|
- `<template v-else-if="overview">` containing:
|
|
- 4-column stat cards grid (`grid grid-cols-1 md:grid-cols-4 gap-4`) with Users, Storage, Processing, Ready cards
|
|
- Recent audit table (`<table>`) with columns: When / Event / Actor / Target / IP
|
|
- "No recent activity" placeholder when `recent_audit` is empty
|
|
|
|
## Key Decisions
|
|
|
|
### 4-Card Choice Rationale
|
|
|
|
ADMIN-11 lists exactly 4 stats: total users, total storage, and document status breakdown (processing/ready). D-01 says "3-4 stat cards". 4 was chosen because:
|
|
1. The backend `doc_status` field naturally provides two metrics (processing + ready) — splitting them into two cards makes the breakdown visible without extra clicks
|
|
2. A 4-column grid fills the `max-w-5xl` content area proportionally
|
|
|
|
### No Top-Level Padding in AdminOverviewView
|
|
|
|
`AdminLayout.vue` owns `p-8 max-w-5xl mx-auto`. Per Pitfall 9 (RESEARCH.md), adding padding inside the view would double the padding. The view's root `<div>` has no padding classes.
|
|
|
|
### No Back-to-App Link
|
|
|
|
D-06 overrides ADMIN-09: admin accounts are operators only. The sidebar has exactly 5 destination links. No `/` router-link exists anywhere in `AdminSidebar.vue`.
|
|
|
|
## Deviations from Plan
|
|
|
|
None — plan executed exactly as written.
|
|
|
|
## Threat Flags
|
|
|
|
None — no new network endpoints, auth paths, or trust boundaries beyond the plan's threat model.
|
|
|
|
## Self-Check: PASSED
|
|
|
|
- `frontend/src/layouts/AdminLayout.vue` exists: FOUND
|
|
- `frontend/src/components/admin/AdminSidebar.vue` exists: FOUND
|
|
- `frontend/src/views/admin/AdminOverviewView.vue` exists: FOUND
|
|
- `frontend/src/api/admin.js` has `getAdminOverview`: FOUND
|
|
- All 3 commits exist in git log: fd12ee2, 4eb489f, 1e14e15 confirmed
|
|
- `npm run build` succeeds: PASSED (143 modules transformed, no errors)
|
|
- No Back-to-app link: `grep -ci "back to app" AdminSidebar.vue` returns 0
|
|
- No double-padding: `grep -cE "p-8|max-w-5xl" AdminOverviewView.vue` returns 0
|