Files
kite/.planning/milestones/v0.2-phases/10-ux-interaction/10-07-PLAN.md
T
curo1305andClaude Sonnet 4.6 123ae5b29b chore: archive v0.2 phase directories to milestones/v0.2-phases/
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>
2026-06-17 14:34:52 +02:00

15 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 07 execute 1
10-02
10-03
10-05
frontend/src/components/layout/AppSidebar.vue
frontend/src/components/layout/__tests__/AppSidebar.empty.test.js
true
UX-03
UX-01
UX-14
truths artifacts key_links
AppSidebar renders sidebar-indent skeleton placeholders for folders, topics, and cloud sections while loading
AppSidebar renders EmptyState size='sm' for each empty section (folders, topics, cloud)
AppSidebar no longer contains a New button for creating root folders (UX-14)
AppSidebar no longer contains startNewFolder/cancelNewFolder/submitNewFolder methods or the related state (showNewFolderInput, newFolderName, newFolderError)
StorageBrowser's own startNewFolder (file manager) is UNTOUCHED
path provides
frontend/src/components/layout/AppSidebar.vue Updated sidebar with skeleton, EmptyState, no inline New button
from to via pattern
AppSidebar.vue EmptyState.vue <EmptyState size="sm" :icon="..." /> <EmptyState size="sm"
Wire UX-03 (sidebar skeletons), UX-01 (sidebar EmptyState micro states), and UX-14 (remove sidebar "New" folder button + related state/methods) in `frontend/src/components/layout/AppSidebar.vue`.

Per the planning_guidance, UX-14 removal targets ONLY AppSidebar's own folder-creation flow. The file manager's own inline new-folder UI (StorageBrowser.startNewFolder) is UNTOUCHED — it remains the canonical way to create folders.

Output: AppSidebar.vue with skeleton placeholders matching TreeItem indent (pl-7), three EmptyState size='sm' micro states (folders/topics/cloud), and the "New" button + helper methods + state removed.

<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/layout/AppSidebar.vue @frontend/src/components/ui/EmptyState.vue @frontend/src/components/ui/TreeItem.vue @frontend/src/components/storage/StorageBrowser.vue **AppSidebar.vue uses Options API** — current file, see PATTERNS.md §"AppSidebar.vue".

Sidebar skeleton pattern (PATTERNS.md §3 Sidebar skeleton):

<div class="pl-7 py-1 space-y-1">
  <div v-for="n in 3" :key="n" class="flex items-center gap-2 py-1">
    <div class="w-4 h-4 bg-gray-100 rounded animate-pulse shrink-0"></div>
    <div class="h-3 bg-gray-100 rounded animate-pulse" :style="{ width: (50 + n * 15) + 'px' }"></div>
  </div>
</div>

Three sections requiring skeleton + EmptyState (RESEARCH.md §Component Inventory §3):

Section Loading condition Empty condition EmptyState icon EmptyState headline EmptyState CTA
Folders loadingRoots foldersStore.rootFolders.length === 0 folder "Create a folder in the file manager" (none)
Topics topicsStore.loading topicsStore.topics.length === 0 tag "No topics yet" (none)
Cloud loadingCloudConnections activeCloudConnections.length === 0 cloud "Connect in Settings" router-link to /settings

All EmptyStates use size="sm".

UX-14 removal targets in AppSidebar.vue:

  • Template: the <button @click="startNewFolder">New</button> element near the folder section header (current lines 75-82)
  • Template: the inline new-folder <div v-if="showNewFolderInput"> block (current lines 87-98)
  • Script: methods startNewFolder(), cancelNewFolder(), submitNewFolder() (current lines 289-312)
  • Script: state showNewFolderInput, newFolderName, newFolderError
  • Update the empty-folder text block (current lines 102-103) to remove the && !showNewFolderInput condition since that variable no longer exists.

StorageBrowser invariant: frontend/src/components/storage/StorageBrowser.vue still has its own startNewFolder function, showNewFolderInput state, and the inline new-folder input row — DO NOT touch any of these.

Task 1: Promote AppSidebar.empty.test.js stubs to real tests frontend/src/components/layout/__tests__/AppSidebar.empty.test.js - frontend/src/components/layout/__tests__/AppSidebar.empty.test.js (Wave 0 stub) - frontend/src/components/layout/AppSidebar.vue (current state) - frontend/src/stores/folders.js, frontend/src/stores/topics.js, frontend/src/stores/cloudConnections.js (store shapes for mocking) Replace `.todo` entries with real assertions (skeleton, EmptyState micro, UX-14 absence): - UX-03 group (3 tests): 1. `renders folder skeleton rows when loadingRoots is true` — mount AppSidebar with foldersStore.loadingRoots=true (use a setup or component data override), assert `wrapper.findAll('.animate-pulse').length >= 3` within the folders section 2. `renders topics skeleton rows when topicsStore.loading is true` — similar assertion for topics 3. `renders cloud skeleton rows when loadingCloudConnections is true` — similar assertion for cloud - UX-01 sidebar micro (3 tests): 4. `renders in folders section when empty` — mount with all loading=false and empty store arrays, stub EmptyState, assert presence in DOM via component lookup 5. `renders in topics section when empty` — similar 6. `renders in cloud section when empty with #cta router-link` — similar - UX-14 group (3 tests): 7. `template does NOT include a "New" button in the folder section header` — assert no `` element in the rendered AppSidebar has text content equal to 'New' 8. `component does NOT expose startNewFolder method` — Vue Options API: mount component, assert `wrapper.vm.startNewFolder` is undefined 9. `component data does NOT include showNewFolderInput` — assert `wrapper.vm.showNewFolderInput` is undefined
For mocking stores, use a `createMockPinia` helper or use `setActivePinia(createPinia())` and override store state after creation (e.g., `useFoldersStore().rootFolders = []`).
Modify `frontend/src/components/layout/__tests__/AppSidebar.empty.test.js`. Replace each `it.todo(...)` from Task 1's stub set with the real tests above. Use `import { mount } from '@vue/test-utils'`, `import { setActivePinia, createPinia } from 'pinia'`, and stub `router-link` + `EmptyState` + `AppIcon` + `TreeItem` + `FolderTreeItem` + `CloudProviderTreeItem` via `global.stubs`.
Tests fail initially because AppSidebar still has the old structure.
cd frontend && npm run test -- --run AppSidebar.empty Expected: 9 tests fail (RED). - File contains exactly 9 `it(...)` tests across 3 describe blocks (UX-03, UX-01, UX-14) - No `.todo` entries remain - Tests stub child components via `global.stubs` - All 9 tests are RED 9 RED tests describe the AppSidebar contract. Task 2: Edit AppSidebar.vue — remove UX-14 elements, add skeleton + EmptyState wiring frontend/src/components/layout/AppSidebar.vue - frontend/src/components/layout/AppSidebar.vue (current state) - frontend/src/components/layout/__tests__/AppSidebar.empty.test.js (failing tests) - frontend/src/components/ui/EmptyState.vue (interface) - .planning/phases/10-ux-interaction/10-PATTERNS.md §"AppSidebar.vue" - .planning/phases/10-ux-interaction/10-RESEARCH.md §"Component Inventory §3" (sidebar micro EmptyState specs) 1. UX-14 removals (script and template): - Remove `New` near folder section header - Remove the inline new-folder `
` block - Remove the data properties `showNewFolderInput`, `newFolderName`, `newFolderError` - Remove the methods `startNewFolder`, `cancelNewFolder`, `submitNewFolder` - Remove any `nextTick(() => this.$refs.newFolderInputRef?.focus())` or related ref code in mounted/methods 2. Skeleton wiring (UX-03): - Replace each `
Loading…
` placeholder with a `
` skeleton block containing 3 `
` rows (icon + text block, animate-pulse), per the PATTERNS template. - Three locations: folders section, cloud section, topics section. 3. EmptyState wiring (UX-01 sidebar micro): - Folders empty: replace `
No folders yet
` with `` - Topics empty: replace existing text with `` - Cloud empty: replace with: ``` Settings ``` 4. Register EmptyState as a component import: `import EmptyState from '../ui/EmptyState.vue'` + add to `components: { ... }` in the Options API export. Edit `frontend/src/components/layout/AppSidebar.vue`:
**Step 1 — Imports:**
Add `import EmptyState from '../ui/EmptyState.vue'` near the existing imports. Add `EmptyState` to the `components: { ... }` registration object in the Options API `export default`.

**Step 2 — Remove UX-14 elements:**
- Delete the entire `<button @click="startNewFolder">...New...</button>` element (current ~lines 75-82)
- Delete the entire `<div v-if="showNewFolderInput">...</div>` inline new-folder block (current ~lines 87-98)
- In the `data()` return, remove the three properties: `showNewFolderInput: false`, `newFolderName: ''`, `newFolderError: ''`
- In the `methods: { ... }` object, remove `startNewFolder`, `cancelNewFolder`, `submitNewFolder`
- Remove the corresponding template `ref="newFolderInputRef"` if present

**Step 3 — Folder section update:**
- Replace the `<div v-if="loadingRoots" class="pl-7 py-1 text-xs text-gray-400">Loading…</div>` element with:
  ```
  <div v-if="loadingRoots" class="pl-7 py-1 space-y-1">
    <div v-for="n in 3" :key="`sk-f-${n}`" class="flex items-center gap-2 py-1">
      <div class="w-4 h-4 bg-gray-100 rounded animate-pulse shrink-0"></div>
      <div class="h-3 bg-gray-100 rounded animate-pulse" :style="{ width: (50 + n * 15) + 'px' }"></div>
    </div>
  </div>
  ```
- Replace the `<div v-else-if="foldersStore.rootFolders.length === 0 && !showNewFolderInput" class="pl-7 py-1 text-xs text-gray-400">No folders yet</div>` with:
  ```
  <EmptyState
    v-else-if="foldersStore.rootFolders.length === 0"
    size="sm"
    icon="folder"
    headline="Create a folder in the file manager"
    class="pl-7"
  />
  ```
  (Drop the `&& !showNewFolderInput` since the variable no longer exists.)

**Step 4 — Cloud section update:**
- Replace `<div v-if="loadingCloudConnections" class="pl-7 py-1 text-xs text-gray-400">Loading…</div>` with the same 3-row skeleton (with `key="sk-c-${n}"`).
- Replace `<div v-else-if="activeCloudConnections.length === 0" class="pl-7 py-1 text-xs text-gray-400">No cloud storage connected</div>` with:
  ```
  <EmptyState
    v-else-if="activeCloudConnections.length === 0"
    size="sm"
    icon="cloud"
    headline="Connect in Settings"
    class="pl-7"
  >
    <template #cta>
      <router-link to="/settings" class="ml-1 text-indigo-600 hover:underline">Settings</router-link>
    </template>
  </EmptyState>
  ```

**Step 5 — Topics section update:**
- Replace `<div v-if="topicsStore.loading" class="px-3 py-1 text-xs text-gray-400">Loading…</div>` with a 3-row skeleton (with `key="sk-t-${n}"`), using `class="px-3 py-1 space-y-1"` for the outer wrapper to match the existing topics indent.
- Replace `<div v-else-if="topicsStore.topics.length === 0" class="px-3 py-1 text-xs text-gray-400">No topics yet</div>` with:
  ```
  <EmptyState
    v-else-if="topicsStore.topics.length === 0"
    size="sm"
    icon="tag"
    headline="No topics yet"
    class="px-3"
  />
  ```

No comments. Preserve all other functionality (Topics, Shared, Admin, Settings, sign-out, etc.) untouched.
cd frontend && npm run test -- --run AppSidebar.empty Expected: all 9 tests PASS. - `grep -E "import EmptyState" frontend/src/components/layout/AppSidebar.vue` returns 1 - `grep -E "\\s*New\\s*'` returns 0 (no "New" button) - StorageBrowser.vue invariant: `grep -E "function startNewFolder" frontend/src/components/storage/StorageBrowser.vue` still returns 1 (untouched) - `cd frontend && npm run test -- --run AppSidebar.empty` exits 0 - Full sidebar tests pass: `cd frontend && npm run test -- --run AppSidebar` exits 0 AppSidebar updated; UX-03 + UX-01 (micro) + UX-14 all GREEN; StorageBrowser's own startNewFolder preserved. - `cd frontend && npm run test -- --run AppSidebar.empty` exits 0 - StorageBrowser invariant intact: `grep -E "function startNewFolder" frontend/src/components/storage/StorageBrowser.vue` returns 1 - `grep -E "Loading…" frontend/src/components/layout/AppSidebar.vue` returns 0 (loading text replaced by skeletons) - AppSidebar EmptyState count: `grep -c "

<success_criteria> The sidebar shows shimmering skeleton rows while loading folders/topics/cloud connections. When loading completes and any section has no items, a compact icon + label appears (with a Settings link for cloud). The inline "New folder" button is gone — folder creation is accessible only from the file manager toolbar, as required by UX-14. </success_criteria>

Create `.planning/phases/10-ux-interaction/10-07-SUMMARY.md` when done.