docs(10): create phase plan — 12 plans, 6 waves, UX-01..14 + CODE-05

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-06-15 06:55:11 +02:00
co-authored by Claude Sonnet 4.6
parent c84dcb77c1
commit d3d3f711eb
16 changed files with 4611 additions and 2 deletions
@@ -0,0 +1,311 @@
---
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>