--- phase: 10-ux-interaction plan: "08" subsystem: frontend/views tags: [skeleton, breadcrumb, empty-state, tdd, admin, ux] dependency_graph: requires: [10-02, 10-03, 10-05] provides: [AdminAuditView-skeleton, AdminUsersView-skeleton, AdminAuditView-EmptyState, SharedView-EmptyState, CloudStorageView-EmptyState, BreadcrumbBar-in-all-admin-views] affects: [AdminAuditView.vue, AdminUsersView.vue, AdminQuotasView.vue, AdminAiView.vue, AdminOverviewView.vue, SettingsView.vue, SharedView.vue, CloudStorageView.vue] tech_stack: added: [] patterns: [skeleton-tbody-v-for, EmptyState-named-cta-slot, BreadcrumbBar-showRoot-false, computed-breadcrumbSegments] key_files: created: [] modified: - frontend/src/views/admin/AdminAuditView.vue - frontend/src/views/admin/AdminUsersView.vue - frontend/src/views/admin/AdminQuotasView.vue - frontend/src/views/admin/AdminAiView.vue - frontend/src/views/admin/AdminOverviewView.vue - frontend/src/views/SettingsView.vue - frontend/src/views/SharedView.vue - frontend/src/views/CloudStorageView.vue - frontend/src/views/admin/__tests__/AdminAuditView.skeleton.test.js - frontend/src/views/admin/__tests__/AdminUsersView.skeleton.test.js decisions: - "CloudStorageView BreadcrumbBar placed inside the toolbar div (replaces static span text) to preserve existing layout structure" - "AdminOverviewView keeps existing h2 heading alongside the empty-segments BreadcrumbBar (plan permits this when heading exists)" - "CTA slot test for AdminAuditView mounts without stubbing EmptyState so #cta slot content renders" - "Worktree symlinks node_modules to main repo for test execution" metrics: duration: "394s (~6.5 minutes)" completed: "2026-06-15T18:27:30Z" tasks_completed: 3 tasks_total: 3 files_created: 0 files_modified: 10 requirements: [UX-04, UX-01, UX-12] --- # Phase 10 Plan 08: Admin Skeletons, EmptyStates, and BreadcrumbBar Wiring Summary **One-liner:** Skeleton tbody rows (8 for audit / 5 for users), BreadcrumbBar added to all 5 admin views + SettingsView + SharedView + CloudStorageView, and EmptyState components replacing inline empty divs in AdminAuditView / SharedView / CloudStorageView. ## Tasks Completed | Task | Name | Commit | Files | |------|------|--------|-------| | 1 | Promote AdminAuditView + AdminUsersView skeleton test stubs | ec5fd23 | AdminAuditView.skeleton.test.js, AdminUsersView.skeleton.test.js | | 2 | Update AdminAuditView.vue and AdminUsersView.vue | 8e360f4 | AdminAuditView.vue, AdminUsersView.vue, AdminAuditView.skeleton.test.js (CTA fix) | | 3 | BreadcrumbBar in remaining admin views + SettingsView; EmptyState in SharedView + CloudStorageView | 3e79423 | AdminQuotasView.vue, AdminAiView.vue, AdminOverviewView.vue, SettingsView.vue, SharedView.vue, CloudStorageView.vue | ## What Was Built ### UX-04: Skeleton Table Rows **AdminAuditView.vue** — The top-level loading spinner (`Loading audit log…` text + animate-spin div) has been replaced. The `` now renders 8 skeleton rows (5 `` cells each, all `animate-pulse`) when `loading=true`. When loading is false and entries is empty, an `EmptyState` renders in a colspan=5 cell. When entries exist, the real rows render. **AdminUsersView.vue** — Same pattern: 5 skeleton rows x 6 `` cells each. The `v-if="loading"` spinner div is gone. The existing empty/real rendering is now inside a single always-visible table. ### UX-01: EmptyState Components | View | icon | headline | CTA | |------|------|----------|-----| | AdminAuditView | clipboardList | No entries found | Clear filters button | | SharedView | inbox | Nothing shared with you yet | (none) | | CloudStorageView | cloud | No cloud storage connected | Go to Settings router-link | ### UX-12: BreadcrumbBar Static Segments | View | segments | showRoot | |------|----------|----------| | AdminOverviewView | `[]` | false | | AdminUsersView | `[{ label: 'Users' }]` | false | | AdminQuotasView | `[{ label: 'Quotas' }]` | false | | AdminAiView | `[{ label: 'AI Config' }]` | false | | AdminAuditView | `[{ label: 'Audit Log' }]` | false | | SettingsView | `[{ label: 'Settings' }, { label: activeTabLabel }]` | false | | SharedView | `[{ label: 'Shared with me' }]` | false | | CloudStorageView | `[{ label: 'Cloud Storage' }]` | false | SettingsView uses a `breadcrumbSegments` computed that maps `activeTab.value` → `tabs.find(t => t.id === activeTab.value).label`. ## TDD Compliance | Gate | Commit | Status | |------|--------|--------| | RED — 8 failing tests | ec5fd23 | PASS | | GREEN — all 8 tests pass | 8e360f4 | PASS | ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 1 - Bug] CTA slot test required EmptyState not to be stubbed** - **Found during:** Task 2 (GREEN verification) - **Issue:** The test `EmptyState includes a Clear filters CTA button` used `EmptyState: true` (full stub). When an EmptyState is stubbed as `true`, Vue Test Utils renders the component as `` — named slots are discarded. The `#cta` slot content (the Clear filters button) never rendered, so `wrapper.text()` did not contain 'Clear filters'. - **Fix:** The CTA test mounts AdminAuditView with `EmptyState` NOT stubbed (only `BreadcrumbBar` and `AppIcon` are stubbed). The real EmptyState renders, which renders the slot content. - **Files modified:** `AdminAuditView.skeleton.test.js` - **Commit:** 8e360f4 **2. [Rule 3 - Layout preservation] CloudStorageView BreadcrumbBar placed inside toolbar** - **Found during:** Task 3 - **Issue:** CloudStorageView has a sticky toolbar div with a static `Cloud Storage` label. Adding BreadcrumbBar as a separate element would create visual duplication with the existing toolbar. - **Fix:** Replaced the `Cloud Storage` with `` directly in the toolbar, preserving the sticky header layout. - **Files modified:** `CloudStorageView.vue` - **Commit:** 3e79423 ## Known Stubs None. All EmptyState usages are fully wired with real data conditions. The BreadcrumbBar segments are static strings derived from the view's own label — no async data needed. ## Threat Flags None. All changes are presentational — no new network endpoints, no auth paths, no file access. The EmptyState components display static strings. ## Self-Check: PASSED - [x] `frontend/src/views/admin/AdminAuditView.vue` — FOUND, contains BreadcrumbBar + EmptyState (clipboardList) - [x] `frontend/src/views/admin/AdminUsersView.vue` — FOUND, contains BreadcrumbBar + 5-row skeleton tbody - [x] `frontend/src/views/admin/AdminQuotasView.vue` — FOUND, contains BreadcrumbBar - [x] `frontend/src/views/admin/AdminAiView.vue` — FOUND, contains BreadcrumbBar - [x] `frontend/src/views/admin/AdminOverviewView.vue` — FOUND, contains BreadcrumbBar - [x] `frontend/src/views/SettingsView.vue` — FOUND, contains BreadcrumbBar + breadcrumbSegments computed - [x] `frontend/src/views/SharedView.vue` — FOUND, contains BreadcrumbBar + EmptyState (inbox) - [x] `frontend/src/views/CloudStorageView.vue` — FOUND, contains BreadcrumbBar + EmptyState (cloud) - [x] Commit ec5fd23 (RED tests) — FOUND - [x] Commit 8e360f4 (GREEN implementation) — FOUND - [x] Commit 3e79423 (Task 3 remaining views) — FOUND - [x] All 8 skeleton tests pass — 8/8 - [x] Full suite (worktree): 178/178 pass, 0 failures