---
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
