Moves phases 08–11 execution artifacts from .planning/phases/ to .planning/milestones/v0.2-phases/ to keep .planning/phases/ clean for the next milestone. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
24 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10-ux-interaction | 06 | execute | 1 |
|
|
true |
|
|
Per CLAUDE.md "no dead code", FolderBreadcrumb.vue and its test file are deleted in the SAME commit as the BreadcrumbBar swap.
Output:
- StorageBrowser: 5-row skeleton (UX-02), three EmptyState variants (root/folder/search), import swap FolderBreadcrumb → BreadcrumbBar
- FileManagerView: breadcrumb
{id, name}→{id, label}mapping, toast.show() on doMove + doDeleteDoc success/error - CloudFolderView: breadcrumb mapping + BreadcrumbBar with rootLabel='Cloud'
- FolderBreadcrumb.vue + FolderBreadcrumb.test.js DELETED
- StorageBrowser.skeleton.test.js stub promoted to real tests
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_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/components/storage/StorageBrowser.vue @frontend/src/views/FileManagerView.vue @frontend/src/views/CloudFolderView.vue @frontend/src/components/folders/FolderBreadcrumb.vue @frontend/src/components/ui/EmptyState.vue @frontend/src/components/ui/BreadcrumbBar.vue @frontend/src/stores/toast.js **StorageBrowser already uses `<script setup>`** — modifications stay in that style. Skeleton classes from RESEARCH.md §Pitfall 7: - Container: `grid grid-cols-[2rem_1fr_6rem_8rem_6rem] gap-3 px-4 py-2.5 items-center border-b border-gray-100` - Col 1: `` - Col 2: `` - Col 3: `` - Col 4: `` - Col 5: ``EmptyState contexts in StorageBrowser (from RESEARCH.md §Component Inventory §3):
| Condition | icon | headline | subtext |
|---|---|---|---|
| Root: !currentFolderId && lists empty && !searchQuery | folder | "Nothing here yet" | "Create a folder or upload your first file to get started." |
| In folder: currentFolderId && lists empty && !searchQuery | document | "This folder is empty" | "Upload files above or create a sub-folder." |
| Search: searchQuery && lists empty | search | No results for "{{ searchQuery }}" |
"Try a different search term or clear the filter." |
StorageBrowser does not know currentFolderId directly — keep using the existing emptyMessage and emptyHint props, but change FileManagerView to pass per-context strings, OR replace the inline empty divs with three explicit conditionals inside StorageBrowser using breadcrumb.length > 0 as a proxy for "in folder".
Decision: keep the props (emptyMessage, emptyHint) but render via <EmptyState> block. Use breadcrumb.length === 0 (root) vs > 0 (in folder) as the discriminator inside StorageBrowser for the icon name (folder vs document). For search no-results, the searchQuery check overrides.
BreadcrumbBar wiring:
- StorageBrowser passes through
:segments="breadcrumb"— parents must already map name→label - FileManagerView template:
:breadcrumb="mappedBreadcrumb"wheremappedBreadcrumb= computed mappingfoldersStore.breadcrumbto[{id: f.id, label: f.name}] - CloudFolderView: existing
breadcrumbcomputed already returns{id, name}— add amappedBreadcrumbcomputed - Pass
rootLabel="Home"for local mode androotLabel="Cloud"for cloud mode (handled by passing through a prop or hardcoding in StorageBrowser based onmode)
Toast call sites in FileManagerView (per RESEARCH.md §Toast System):
doMove(docId, folderId): on success →useToastStore().show('Document moved', 'success'); on catch →useToastStore().show('Move failed: ' + (e.message || 'unknown error'), 'error')doDeleteDoc(docId): on success →useToastStore().show('Document deleted', 'success'); on catch →useToastStore().show('Delete failed: ' + (e.message || 'unknown error'), 'error')- (Upload toasts are handled in plan 10-07 to keep file ownership clean? — NO, FileManagerView owns upload too. Defer upload toast wiring to this plan as well.)
onFilesSelected({files, autoClassify}): afterPromise.allSettled, count items withdone=trueand items with errors, and show one toast:show(${successCount} of ${files.length} file(s) uploaded, successCount === files.length ? 'success' : 'warning')and for each item withitem.errorset, showshow('Upload failed: ' + item.error, 'error').
FolderBreadcrumb.vue deletion:
- Delete
frontend/src/components/folders/FolderBreadcrumb.vue - Delete
frontend/src/components/folders/__tests__/FolderBreadcrumb.test.js - Grep verify no remaining import:
grep -r "FolderBreadcrumb" frontend/src/returns 0 matches after edits
Keep the UX-13 `it.todo` stubs as-is (deferred to plan 10-12).
**Step 1 — Imports (in `<script setup>` block):**
Replace `import FolderBreadcrumb from '../folders/FolderBreadcrumb.vue'` with `import BreadcrumbBar from '../ui/BreadcrumbBar.vue'` and add `import EmptyState from '../ui/EmptyState.vue'`.
**Step 2 — Template breadcrumb usage:**
Replace lines 7-10 (the `<FolderBreadcrumb>` block) with:
```
<BreadcrumbBar
:segments="breadcrumb"
:root-label="mode === 'cloud' ? 'Cloud' : 'Home'"
@navigate="$emit('breadcrumb-navigate', $event)"
/>
```
**Step 3 — Replace empty state and loading blocks (current lines 226-244):**
Replace the existing three blocks (lines 226-244) with:
```
<template v-if="loading">
<div
v-for="n in 5"
:key="`sk-${n}`"
class="px-4 py-2.5 grid grid-cols-[2rem_1fr_6rem_8rem_6rem] gap-3 items-center border-b border-gray-100"
>
<div class="w-7 h-7 bg-gray-100 rounded-lg animate-pulse"></div>
<div class="h-4 bg-gray-100 rounded animate-pulse w-2/3"></div>
<div class="h-3 bg-gray-100 rounded animate-pulse hidden md:block"></div>
<div class="h-3 bg-gray-100 rounded animate-pulse hidden sm:block"></div>
<div class="w-14 h-3 bg-gray-100 rounded animate-pulse"></div>
</div>
</template>
<EmptyState
v-else-if="searchQuery && folders.length === 0 && files.length === 0"
icon="search"
:headline="`No results for "${searchQuery}"`"
subtext="Try a different search term or clear the filter."
>
<template #cta>
<button @click="$emit('search-change', '')" class="mt-3 text-sm text-indigo-600 hover:underline">
Clear search
</button>
</template>
</EmptyState>
<EmptyState
v-else-if="breadcrumb.length > 0 && folders.length === 0 && files.length === 0 && !showNewFolderInput"
icon="document"
:headline="emptyMessage"
:subtext="emptyHint"
/>
<EmptyState
v-else-if="folders.length === 0 && files.length === 0 && !showNewFolderInput"
icon="folder"
:headline="emptyMessage"
:subtext="emptyHint"
/>
```
No comments. Keep the rest of StorageBrowser.vue unchanged.
Add `import { useToastStore } from '../stores/toast.js'` near the other imports.
Add a `computed` for `mappedBreadcrumb`:
```js
const mappedBreadcrumb = computed(() =>
(foldersStore.breadcrumb || []).map(f => ({ id: f.id, label: f.name }))
)
```
Change the template `:breadcrumb="foldersStore.breadcrumb"` to `:breadcrumb="mappedBreadcrumb"` on the `<StorageBrowser>` element.
Update `doMove`:
```js
async function doMove(docId, folderId) {
const toast = useToastStore()
try {
await docsStore.moveToFolder(docId, folderId)
toast.show('Document moved', 'success')
} catch (e) {
toast.show('Move failed: ' + (e.message || 'unknown error'), 'error')
}
}
```
Update `doDeleteDoc`:
```js
async function doDeleteDoc(docId) {
const toast = useToastStore()
try {
await docsStore.remove(docId)
toast.show('Document deleted', 'success')
} catch (e) {
toast.show('Delete failed: ' + (e.message || 'unknown error'), 'error')
}
}
```
Update `onFilesSelected` to fire a summary toast at the end:
```js
async function onFilesSelected({ files, autoClassify }) {
const folderId = currentFolderId.value
const toast = useToastStore()
const promises = files.map(file => {
const item = reactive({ name: file.name, done: false, error: null, quotaError: null, topics: null })
uploadQueue.value.unshift(item)
return docsStore.upload(file, autoClassify, folderId)
.then(({ doc }) => { item.done = true; item.topics = doc.topics ?? [] })
.catch(e => {
if (e.status === 413 && e.payload) item.quotaError = e.payload
else item.error = e.message
})
})
await Promise.allSettled(promises)
await topicsStore.fetchTopics()
const succeeded = uploadQueue.value.slice(0, files.length).filter(i => i.done).length
if (succeeded === files.length) {
toast.show(`${succeeded} file(s) uploaded`, 'success')
} else if (succeeded > 0) {
toast.show(`${succeeded} of ${files.length} file(s) uploaded`, 'warning')
} else {
toast.show('Upload failed', 'error')
}
}
```
(Important: the upload error per-item already populates `item.error`; UploadProgress already renders these. The summary toast is in addition.)
**Step B — Modify `frontend/src/views/CloudFolderView.vue`:**
Add a `mappedBreadcrumb` computed mapping `breadcrumb.value` (or the existing computed) to `[{id, label: name}]`. Pass `:breadcrumb="mappedBreadcrumb"` instead of `:breadcrumb="breadcrumb"`. Leave everything else unchanged.
**Step C — Delete FolderBreadcrumb files:**
Delete both files:
- `frontend/src/components/folders/FolderBreadcrumb.vue`
- `frontend/src/components/folders/__tests__/FolderBreadcrumb.test.js`
Use the file deletion tool or `rm` via Bash. Confirm with `ls frontend/src/components/folders/`.
No comments added. No `console.error` removed from doMove/doDeleteDoc — leave existing behavior intact aside from the toast addition. Actually: REMOVE the `console.error(e.message)` lines because the toast now communicates the error to the user.
Per D-11/D-13: the `mappedBreadcrumb` computed is also used in any test for FileManagerView — update those tests in the next step.
Final grep verification: `grep -r "FolderBreadcrumb" frontend/src/` returns 0 matches.
<success_criteria>
Loading the file manager with loading=true shows 5 animated skeleton rows. After load completes:
- Empty root folder → "Nothing here yet" with folder icon
- Empty sub-folder → "This folder is empty" with document icon
- Search with no matches → "No results for {query}" with search icon and Clear button Breadcrumbs use the shared BreadcrumbBar with rootLabel="Home"/"Cloud" per mode. Document move/delete/upload actions fire toasts. </success_criteria>