chore(phase-4): UAT complete — Phase 4 marked done, sidebar collapse, duplicate-folder fix

UAT: 14/15 passed. Bug fixed: folders/rootFolders array alias in fetchFolders caused
duplicate folder row on creation (rootFolders = [...list] breaks the shared reference).
Sidebar: Folders section now has a collapse/expand chevron, collapsed by default.
State: Phase 4 complete, Phase 5 (Cloud Storage Backends) is next.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-05-28 17:34:07 +02:00
parent 87a32b7ee8
commit d6f742a3c1
5 changed files with 158 additions and 42 deletions
+46 -26
View File
@@ -43,11 +43,26 @@
<!-- Folders root + collapsible tree -->
<div class="mt-3">
<!-- "Folders" is the root entry clicking navigates to the root folder view -->
<div class="flex items-center justify-between">
<div class="flex items-center gap-0.5">
<!-- Expand/collapse chevron -->
<button
@click="foldersExpanded = !foldersExpanded"
class="p-1 rounded hover:bg-gray-100 text-gray-400 hover:text-gray-600 transition-colors shrink-0"
:title="foldersExpanded ? 'Collapse folders' : 'Expand folders'"
>
<svg
class="w-3 h-3 transition-transform duration-150"
:class="foldersExpanded ? 'rotate-90' : ''"
fill="none" stroke="currentColor" viewBox="0 0 24 24"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</button>
<!-- "Folders" navigates to root file manager -->
<router-link
to="/"
class="nav-link flex-1"
class="nav-link flex-1 min-w-0"
:class="{ 'nav-link-active': $route.path === '/' || $route.path.startsWith('/folders/') }"
>
<svg class="w-4 h-4 mr-2 shrink-0 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -56,6 +71,7 @@
</svg>
Folders
</router-link>
<button
@click="startNewFolder"
class="text-xs text-indigo-600 hover:underline shrink-0 mr-1"
@@ -65,30 +81,33 @@
</button>
</div>
<!-- Inline new root folder input -->
<div v-if="showNewFolderInput" class="px-3 mb-2 mt-1">
<input
v-model="newFolderName"
type="text"
placeholder="Folder name"
class="block w-full border border-gray-300 rounded-lg px-2 py-1 text-xs focus:outline-none focus:ring-2 focus:ring-indigo-500"
@keydown.enter="submitNewFolder"
@keydown.escape="cancelNewFolder"
autofocus
/>
<p v-if="newFolderError" class="text-red-500 text-xs mt-1">{{ newFolderError }}</p>
</div>
<!-- Collapsible content -->
<template v-if="foldersExpanded">
<!-- Inline new root folder input -->
<div v-if="showNewFolderInput" class="px-3 mb-2 mt-1">
<input
v-model="newFolderName"
type="text"
placeholder="Folder name"
class="block w-full border border-gray-300 rounded-lg px-2 py-1 text-xs focus:outline-none focus:ring-2 focus:ring-indigo-500"
@keydown.enter="submitNewFolder"
@keydown.escape="cancelNewFolder"
autofocus
/>
<p v-if="newFolderError" class="text-red-500 text-xs mt-1">{{ newFolderError }}</p>
</div>
<!-- Sub-folders tree (indented under Folders) -->
<div v-if="loadingRoots" class="pl-7 py-1 text-xs text-gray-400">Loading</div>
<div v-else-if="foldersStore.rootFolders.length === 0 && !showNewFolderInput"
class="pl-7 py-1 text-xs text-gray-400">No folders yet</div>
<FolderTreeItem
v-for="folder in foldersStore.rootFolders"
:key="folder.id"
:folder="folder"
:depth="1"
/>
<!-- Sub-folders tree -->
<div v-if="loadingRoots" class="pl-7 py-1 text-xs text-gray-400">Loading</div>
<div v-else-if="foldersStore.rootFolders.length === 0 && !showNewFolderInput"
class="pl-7 py-1 text-xs text-gray-400">No folders yet</div>
<FolderTreeItem
v-for="folder in foldersStore.rootFolders"
:key="folder.id"
:folder="folder"
:depth="1"
/>
</template>
</div>
<!-- Topics list -->
@@ -186,6 +205,7 @@ const showNewFolderInput = ref(false)
const newFolderName = ref('')
const newFolderError = ref('')
const loadingRoots = ref(true)
const foldersExpanded = ref(false)
watch(() => foldersStore.treeVersion, () => foldersStore.fetchRootFolders())
+1 -1
View File
@@ -18,7 +18,7 @@ export const useFoldersStore = defineStore('folders', () => {
const data = await api.listFolders(parentId)
const list = data.items ?? data
folders.value = list
if (parentId === null) rootFolders.value = list
if (parentId === null) rootFolders.value = [...list]
} catch (e) {
error.value = e.message || 'Failed to load folders'
} finally {