- UX-14: remove startNewFolder/cancelNewFolder/submitNewFolder + all related state
(showNewFolderInput, newFolderName, newFolderError) from script and template
- UX-14: remove <button @click="startNewFolder">New</button> from folder section header
- UX-14: remove inline new-folder <div v-if="showNewFolderInput"> input block
- UX-03: replace Loading… divs with animate-pulse skeleton rows (3-row pattern) in
all three sections (folders, cloud, topics)
- UX-01 sidebar micro: wire EmptyState size=sm for each empty section
- folders: icon=folder headline="Create a folder in the file manager"
- cloud: icon=cloud headline="Connect in Settings" with #cta Settings link
- topics: icon=tag headline="No topics yet"
- Add EmptyState import; StorageBrowser.startNewFolder untouched (invariant)
- Update tests to work with script setup (Composition API) reality; all 9 GREEN
- Replace 10 it.todo stubs with 9 real tests across 3 describe blocks
- UX-03: 3 tests verify skeleton rows during loading (folders/topics/cloud)
- UX-01 sidebar micro: 3 tests verify EmptyState size=sm for each empty section
- UX-14: 3 tests verify "New" button gone, methods removed, no inline folder input
- 8/9 tests RED (1 passes trivially: input hidden while showNewFolderInput=false)
- Adapted for script setup (Composition API) rather than Options API as described in plan
- Options API component with segments/{id?,label}, rootLabel, showRoot props
- Emits navigate(segment.id) for intermediate clicks, navigate(null) for root
- Computed visibleSegments collapses >4 segments to first+ellipsis+last two
- Segments without id render as plain non-clickable spans (admin static segments)
- Uses AppIcon for chevronRight separator (no inline SVG)
- All 10 Vitest tests pass
- Also creates AppIcon.vue (Rule 3: BreadcrumbBar imports it)
- Options API component with props: icon, headline, subtext, size
- Computed containerClass/iconClass/headlineClass/subtextClass for md and sm variants
- Named #cta slot with no fallback content
- Imports AppIcon from ./AppIcon.vue (child component)
- Also creates AppIcon.vue (deviation Rule 3: required to resolve EmptyState import)
- All 7 EmptyState tests pass; all 144 tests pass
- toast.js: reactive toasts array, show/dismiss with auto-dismiss via setTimeout
- ToastContainer.vue: Options API, Teleport to body, TransitionGroup, data-test attr
- type-to-class maps: accentClass/iconName/iconColorClass for success/error/warning/info
- App.vue: import and mount <ToastContainer /> after layout div
- all 10 tests pass; SettingsAccountTab and TotpEnrollment regressions green
- Options API component with inheritAttrs: false for class forwarding
- 32 named icons: 31 single-path + 1 dual-path (cog as Array)
- resolvedPaths computed warns in DEV for unknown icon names
- v-if guard renders nothing for unknown names
- Array.isArray check renders v-for paths for dual-path icons
- All 6 AppIcon tests pass (GREEN)
- 6 tests covering name→path rendering, class forwarding, dual-path (cog), svg attrs, unknown-name warn, and path stroke attrs
- RED phase: AppIcon.vue does not exist yet; tests fail on import
All 9 UAT items verified via live API + source inspection:
- GET /api/admin/overview live data (78 users, 8MB storage, 10 audit rows)
- Auth guard: 401 unauthenticated, 403 non-admin on all protected endpoints
- Admin login redirect (D-08) confirmed in LoginView.vue line 212
- Sidebar: 5 nav links, indigo Admin badge, no back-to-app link
- 4-stat-card overview + When/Event/Actor/Target/IP audit table
- 5 standalone admin views (Users, Quotas, AI, Audit) with no Pinia stores
- API key write-only invariant confirmed in AdminAiView.vue
- Color-coded audit badges (auth=blue, admin=amber, folder/share=purple)
- to.matched.some() guard covers all /admin/* child routes
- No sensitive fields (password_hash, credentials_enc) in any response
Phase 9 marked complete. STATE.md and PROJECT.md updated for Phase 10.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- After successful login, admin users redirect to /admin; regular users to /
- Honors ?redirect= query param for non-admin users as before
- authStore.user.role is synchronously populated before handleLoginResult fires
- Created frontend/src/views/admin/AdminAiView.vue (extracted from AdminAiConfigTab.vue)
- Import path for SearchableModelSelect rewritten to ../../components/ui/SearchableModelSelect.vue
- System AI Providers (Global) section preserved above per-user table (Phase 7 constraint)
- Created frontend/src/views/admin/AdminAuditView.vue (verbatim from AuditLogTab.vue per D-13)
- actionTypeClass() helper preserved verbatim with all bg-amber-50, bg-purple-50 dynamic classes
- Filter bar, paginated table, CSV download, daily-export list all preserved as-is
- Both files have no defineProps, no top-level padding, identical line counts to sources
- Build succeeds: 143 modules transformed, no errors
- Created frontend/src/views/admin/AdminUsersView.vue (verbatim extraction of AdminUsersTab.vue)
- Created frontend/src/views/admin/AdminQuotasView.vue (verbatim extraction of AdminQuotasTab.vue)
- Both files have no defineProps, no top-level padding, identical import paths (../../api/client.js, ../../utils/formatters.js)
- Line counts match source exactly (480 and 182 lines respectively)
- Build succeeds with no Vite errors referencing new view files
- New backend/api/admin/overview.py: aggregate stats (user_count,
total_storage_bytes, doc_status) + last 10 audit entries in one payload
- Sub-router carries NO prefix (parent __init__.py carries /api/admin)
- Reuses _build_filtered_query_with_handles + _audit_to_dict_with_handles
from api.audit — no new serializer or query logic
- Registered overview_router on admin parent router in __init__.py
- All 8 ADMIN-11 tests pass; existing admin and audit suites stay green