---
phase: 10-ux-interaction
plan: 08
type: execute
wave: 1
depends_on: [10-02, 10-03, 10-05]
files_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
autonomous: true
requirements: [UX-04, UX-01, UX-12]
must_haves:
  truths:
    - "AdminAuditView shows >=5 skeleton <tr> rows during loading"
    - "AdminUsersView shows >=5 skeleton <tr> rows during loading"
    - "AdminAuditView empty state uses EmptyState icon=clipboardList with a Clear filters CTA"
    - "SharedView empty state uses EmptyState icon=inbox"
    - "CloudStorageView empty state uses EmptyState icon=cloud with a Settings router-link CTA"
    - "Each admin view + SettingsView renders a BreadcrumbBar with the static segments per the wiring table (D-13)"
  artifacts:
    - path: "frontend/src/views/admin/AdminAuditView.vue"
      provides: "Audit view with skeleton + EmptyState + BreadcrumbBar"
    - path: "frontend/src/views/admin/AdminUsersView.vue"
      provides: "Users view with skeleton + BreadcrumbBar"
    - path: "frontend/src/views/SettingsView.vue"
      provides: "Settings view with BreadcrumbBar reflecting active tab"
  key_links:
    - from: "Each admin view"
      to: "BreadcrumbBar.vue"
      via: "static segments computed"
      pattern: "<BreadcrumbBar"
---

<objective>
Wire UX-04 (skeleton table rows in admin tables), the remaining UX-01 (EmptyState in SharedView, CloudStorageView, AdminAuditView), and the admin/settings parts of UX-12 (BreadcrumbBar static segments) across the admin views, SettingsView, SharedView, and CloudStorageView.

Per D-13: each view computes its own segments array. Admin/settings/topics views use `showRoot=false` so they do NOT show a "Home" button.

Output:
- AdminUsersView, AdminAuditView, AdminQuotasView, AdminAiView, AdminOverviewView: each gains a `<BreadcrumbBar>` block at the top
- AdminUsersView + AdminAuditView: skeleton table rows replace loading spinner
- AdminAuditView empty state replaced with EmptyState
- SettingsView: BreadcrumbBar with `Settings > {activeTab}` static segments
- SharedView: EmptyState replaces inline empty div + BreadcrumbBar
- CloudStorageView: EmptyState replaces inline empty div with Settings #cta + BreadcrumbBar
- Two admin skeleton stub files promoted to real tests
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@CLAUDE.md
@.planning/phases/10-ux-interaction/10-CONTEXT.md
@.planning/phases/10-ux-interaction/10-RESEARCH.md
@.planning/phases/10-ux-interaction/10-PATTERNS.md
@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/components/ui/BreadcrumbBar.vue
@frontend/src/components/ui/EmptyState.vue

<interfaces>
BreadcrumbBar per-view wiring table (from RESEARCH.md):

| 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 active tab labels: map tab id to display label (existing tabs in SettingsView.vue - read the file to find the mapping; typical tabs: 'Account', 'Cloud Storage', 'Preferences').

Skeleton table row pattern (RESEARCH.md Pitfall 7 admin variant):

AdminAuditView (5 cols: Timestamp | User | Email | Action | IP) - 8 rows.
AdminUsersView (6 cols: Email | Handle | Role | Status | Created | Actions) - 5 rows.

Skeleton rows render inside the existing `<tbody>` when loading. The existing loading spinner block is replaced.

EmptyState wiring (RESEARCH.md Component Inventory #3):

- AdminAuditView empty entries: `<EmptyState icon="clipboardList" headline="No entries found" subtext="Try adjusting your filters or date range.">` with #cta button `Clear filters`
- SharedView empty shared docs: `<EmptyState icon="inbox" headline="Nothing shared with you yet" subtext="When someone shares a document with you, it will appear here." />`
- CloudStorageView empty connections: `<EmptyState icon="cloud" headline="No cloud storage connected" subtext="Connect Google Drive, OneDrive, Nextcloud, or a WebDAV server in Settings.">` with #cta router-link to /settings
</interfaces>
</context>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: Promote AdminAuditView + AdminUsersView skeleton test stubs</name>
  <files>frontend/src/views/admin/__tests__/AdminAuditView.skeleton.test.js, frontend/src/views/admin/__tests__/AdminUsersView.skeleton.test.js</files>
  <read_first>
    - The two stub files (Wave 0 outputs)
    - frontend/src/views/admin/AdminAuditView.vue + AdminUsersView.vue (current state)
  </read_first>
  <behavior>
    Replace the `.todo` entries with real assertions:

    AdminAuditView.skeleton.test.js (5 tests):
    1. `renders 8 skeleton <tr> rows when loading=true` - mount AdminAuditView with loading mocked to true, find `<tbody>` and assert findAll('tr').length >= 8
    2. `skeleton rows have exactly 5 <td> cells matching column count` - assert each skeleton row has 5 `<td>` children
    3. `Loading audit log text is absent when loading=true` - assert wrapper.text() does NOT include 'Loading audit log'
    4. `renders <EmptyState icon="clipboardList" headline="No entries found"> when entries empty and not loading` - assert EmptyState stub is found with those props
    5. `EmptyState includes a Clear filters CTA button` - assert the rendered EmptyState slot contains 'Clear filters'

    AdminUsersView.skeleton.test.js (3 tests):
    1. `renders 5 skeleton <tr> rows when loading=true` - assert >= 5
    2. `skeleton rows have exactly 6 <td> cells` - assert each row has 6 cells
    3. `Loading users text is absent when loading=true` - assert wrapper.text() excludes 'Loading users'

    Use setActivePinia(createPinia()) and override store state. Stub child components via global.stubs: { BreadcrumbBar: true, EmptyState: true, AppIcon: true }.
  </behavior>
  <action>
    Replace `it.todo(...)` entries in both files with the real tests defined above. Tests fail until Task 2-3 update the views.
  </action>
  <verify>
    <automated>cd frontend && npm run test -- --run AdminAuditView.skeleton; cd frontend && npm run test -- --run AdminUsersView.skeleton</automated>
    Expected: 5 + 3 = 8 tests RED.
  </verify>
  <acceptance_criteria>
    - AdminAuditView.skeleton.test.js contains 5 real `it(...)` blocks (no `.todo`)
    - AdminUsersView.skeleton.test.js contains 3 real `it(...)` blocks
    - All 8 tests are RED before Task 2
  </acceptance_criteria>
  <done>8 RED skeleton tests in place.</done>
</task>

<task type="auto">
  <name>Task 2: Update AdminAuditView.vue and AdminUsersView.vue</name>
  <files>frontend/src/views/admin/AdminAuditView.vue, frontend/src/views/admin/AdminUsersView.vue</files>
  <read_first>
    - Both view files (current state - read in full to learn the loading block + table structure + filter state)
    - The two failing test files from Task 1
    - frontend/src/components/ui/BreadcrumbBar.vue
    - frontend/src/components/ui/EmptyState.vue
    - .planning/phases/10-ux-interaction/10-PATTERNS.md sections for AdminAuditView.vue and AdminUsersView.vue
  </read_first>
  <action>
    AdminAuditView.vue changes:
    1. Add imports: `import BreadcrumbBar from '../../components/ui/BreadcrumbBar.vue'` and `import EmptyState from '../../components/ui/EmptyState.vue'`. Register both in `components: { ... }` (Options API) or rely on import resolution (script setup).
    2. At the very top of the `<template>` root (before any existing content), add:
       `<BreadcrumbBar :segments="[{ label: 'Audit Log' }]" :show-root="false" class="mb-4" />`
    3. Replace the existing loading block (the `<div v-if="loading">...<span class="animate-spin">...Loading audit log...</div>`) with skeleton table rows. The new structure: when `loading` is true, the `<tbody>` renders 8 skeleton rows. When `entries.length === 0` and not loading, render the `<EmptyState>` block. When entries exist, render the existing real rows. Concretely:
       ```
       <tbody>
         <template v-if="loading">
           <tr v-for="n in 8" :key="`sk-${n}`" class="border-b border-gray-100">
             <td class="px-4 py-3"><div class="h-3 bg-gray-100 rounded animate-pulse w-32"></div></td>
             <td class="px-4 py-3"><div class="h-3 bg-gray-100 rounded animate-pulse w-20"></div></td>
             <td class="px-4 py-3"><div class="h-3 bg-gray-100 rounded animate-pulse w-36"></div></td>
             <td class="px-4 py-3"><div class="h-4 bg-gray-100 rounded-full animate-pulse w-16"></div></td>
             <td class="px-4 py-3"><div class="h-3 bg-gray-100 rounded animate-pulse w-24"></div></td>
           </tr>
         </template>
         <tr v-else-if="entries.length === 0">
           <td colspan="5" class="px-0 py-0">
             <EmptyState icon="clipboardList" headline="No entries found" subtext="Try adjusting your filters or date range.">
               <template #cta>
                 <button @click="clearFilters" class="mt-3 text-sm text-indigo-600 hover:underline">Clear filters</button>
               </template>
             </EmptyState>
           </td>
         </tr>
         <template v-else>
           <!-- existing real rows preserved unchanged -->
         </template>
       </tbody>
       ```
       Remove the outer loading `<div>` panel that contained Loading audit log spinner.
    4. If `clearFilters` does not already exist as a function/method, add one that resets the filter state to defaults (read the filter refs/data and set them back to their initial values).

    AdminUsersView.vue changes:
    1. Add `import BreadcrumbBar from '../../components/ui/BreadcrumbBar.vue'`. Register if Options API.
    2. Add `<BreadcrumbBar :segments="[{ label: 'Users' }]" :show-root="false" class="mb-4" />` at the top of the template root.
    3. Replace the existing loading spinner block with skeleton table rows: 5 rows x 6 `<td>` cells per row, classes per <interfaces> section.

    Preserve all other functionality (filters, action buttons, sort, pagination, modals).
  </action>
  <verify>
    <automated>cd frontend && npm run test -- --run AdminAuditView.skeleton; cd frontend && npm run test -- --run AdminUsersView.skeleton</automated>
    Expected: all 8 tests GREEN.
  </verify>
  <acceptance_criteria>
    - `grep -E "BreadcrumbBar" frontend/src/views/admin/AdminAuditView.vue` returns >= 2 (import + usage)
    - `grep -E "BreadcrumbBar" frontend/src/views/admin/AdminUsersView.vue` returns >= 2
    - `grep -E "icon=\"clipboardList\"" frontend/src/views/admin/AdminAuditView.vue` returns 1
    - `grep -E "animate-pulse" frontend/src/views/admin/AdminAuditView.vue` returns >= 5
    - `grep -E "animate-pulse" frontend/src/views/admin/AdminUsersView.vue` returns >= 5
    - `grep -v '^#' frontend/src/views/admin/AdminAuditView.vue | grep -c "Loading audit log"` returns 0
    - `grep -v '^#' frontend/src/views/admin/AdminUsersView.vue | grep -c "Loading users"` returns 0
    - Both skeleton tests pass
  </acceptance_criteria>
  <done>AuditView + UsersView updated; skeletons green; BreadcrumbBar in place.</done>
</task>

<task type="auto">
  <name>Task 3: BreadcrumbBar in remaining admin views + SettingsView; EmptyState in SharedView + CloudStorageView</name>
  <files>
    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
  </files>
  <read_first>
    - All six view files (current state)
    - frontend/src/components/ui/BreadcrumbBar.vue + EmptyState.vue
    - .planning/phases/10-ux-interaction/10-PATTERNS.md sections for SharedView and CloudStorageView (target EmptyState blocks)
  </read_first>
  <action>
    AdminQuotasView.vue: Add `import BreadcrumbBar` (register if Options API). Add `<BreadcrumbBar :segments="[{ label: 'Quotas' }]" :show-root="false" class="mb-4" />` at top of template. No other changes.

    AdminAiView.vue: Same pattern with segments `[{ label: 'AI Config' }]`.

    AdminOverviewView.vue: Same pattern with `:segments="[]" :show-root="false"`. The empty segments block renders a `<nav>` with empty `<ol>` - acceptable. Alternatively keep the existing page heading and skip BreadcrumbBar if it would visually duplicate. Read the file: if there is already a clear `<h1>Admin Overview</h1>` heading, skip; otherwise add the BreadcrumbBar.

    SettingsView.vue:
    Read the file to find the `activeTab` state and its display labels. Add a computed `breadcrumbSegments` that returns `[{ label: 'Settings' }, { label: activeTabLabel }]` where `activeTabLabel` maps the current tab id to its user-facing label. Add `<BreadcrumbBar :segments="breadcrumbSegments" :show-root="false" class="mb-4" />` at the top of the template root.

    SharedView.vue:
    Add `import EmptyState from '../components/ui/EmptyState.vue'` and `import BreadcrumbBar from '../components/ui/BreadcrumbBar.vue'`. Register if Options API. At top of template add `<BreadcrumbBar :segments="[{ label: 'Shared with me' }]" :show-root="false" class="mb-4" />`. Replace the inline empty `<div v-else-if="sharedDocs.length === 0" class="text-center py-12 text-gray-400">...</div>` with:
    ```
    <EmptyState
      v-else-if="sharedDocs.length === 0"
      icon="inbox"
      headline="Nothing shared with you yet"
      subtext="When someone shares a document with you, it will appear here."
    />
    ```

    CloudStorageView.vue:
    Add `import EmptyState` + `BreadcrumbBar`. Register if Options API. At top of template add `<BreadcrumbBar :segments="[{ label: 'Cloud Storage' }]" :show-root="false" class="mb-4" />`. Replace the inline empty div with:
    ```
    <EmptyState
      v-else-if="connections.length === 0"
      icon="cloud"
      headline="No cloud storage connected"
      subtext="Connect Google Drive, OneDrive, Nextcloud, or a WebDAV server in Settings."
    >
      <template #cta>
        <router-link to="/settings" class="mt-3 inline-block text-sm text-indigo-600 hover:underline">
          Go to Settings
        </router-link>
      </template>
    </EmptyState>
    ```

    All six files: preserve existing functionality untouched aside from the additions above.
  </action>
  <verify>
    <automated>cd frontend && npm run test -- --run</automated>
    Expected: full suite passes; no regression in any existing test.
  </verify>
  <acceptance_criteria>
    - `grep -E "<BreadcrumbBar" frontend/src/views/admin/AdminQuotasView.vue` returns 1
    - `grep -E "<BreadcrumbBar" frontend/src/views/admin/AdminAiView.vue` returns 1
    - `grep -E "<BreadcrumbBar" frontend/src/views/SettingsView.vue` returns 1
    - `grep -E "breadcrumbSegments" frontend/src/views/SettingsView.vue` returns >= 2 (computed + binding)
    - `grep -E "<BreadcrumbBar" frontend/src/views/SharedView.vue` returns 1
    - `grep -E "<EmptyState\\s+v-else-if=\"sharedDocs" frontend/src/views/SharedView.vue` returns 1
    - `grep -E "icon=\"inbox\"" frontend/src/views/SharedView.vue` returns 1
    - `grep -E "<BreadcrumbBar" frontend/src/views/CloudStorageView.vue` returns 1
    - `grep -E "<EmptyState\\s+v-else-if=\"connections" frontend/src/views/CloudStorageView.vue` returns 1
    - `grep -E "icon=\"cloud\"" frontend/src/views/CloudStorageView.vue` returns 1
    - `cd frontend && npm run test -- --run` exits 0 (no regression)
  </acceptance_criteria>
  <done>All 6 views wired with BreadcrumbBar + EmptyState; full test suite green.</done>
</task>

</tasks>

<verification>
- `cd frontend && npm run test -- --run AdminAuditView.skeleton` exits 0
- `cd frontend && npm run test -- --run AdminUsersView.skeleton` exits 0
- `cd frontend && npm run test -- --run` (full suite) exits 0
- Every admin view + SettingsView + SharedView + CloudStorageView contains exactly one `<BreadcrumbBar` invocation
- AdminAuditView contains both `<BreadcrumbBar` and `<EmptyState icon="clipboardList"`
- SharedView and CloudStorageView contain `<EmptyState`
- No "Loading audit log" / "Loading users" text remains in those views
</verification>

<success_criteria>
Every admin section + SettingsView shows a `Section name` breadcrumb at the top with no "Home" prefix. AdminAuditView + AdminUsersView display animated skeleton tables during load. AdminAuditView's "no entries" state renders the EmptyState with a Clear filters button. SharedView and CloudStorageView render proper EmptyState components with appropriate icons (inbox / cloud) and Settings link where applicable.
</success_criteria>

<output>
Create `.planning/phases/10-ux-interaction/10-08-SUMMARY.md` when done.
</output>
