Files
kite/frontend/src/views/FileManagerView.vue
T
curo1305andClaude Sonnet 4.6 cce70b2ef6 refactor(frontend): extract shared modules, thin views, delete dead code
Shared utilities:
- Add src/utils/formatters.js — formatDate, formatSize, providerColor,
  providerBg, providerLabel; all components import from here, no inline duplicates
- Add src/components/ui/TreeItem.vue — generic expand/collapse tree node;
  FolderTreeItem, CloudFolderTreeItem, CloudProviderTreeItem now wrap it
- Add src/components/storage/StorageBrowser.vue — unified file browser grid
  used by both FileManagerView and CloudFolderView

View refactor (thin data-providers):
- FileManagerView.vue: stripped to props + event wiring; all layout moved to StorageBrowser
- CloudFolderView.vue: same treatment — feeds props into StorageBrowser
- All tree sidebar components delegate expand/collapse to TreeItem.vue

Dead code removed:
- Delete HomeView.vue — no active route, replaced by FileManagerView
- Delete FolderView.vue — no active route, logic merged into FileManagerView

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 16:10:47 +02:00

159 lines
5.9 KiB
Vue

<template>
<StorageBrowser
mode="local"
:folders="foldersStore.folders"
:files="docsStore.documents"
:breadcrumb="foldersStore.breadcrumb"
:upload-queue="uploadQueue"
:loading="docsStore.loading || foldersStore.loading"
:search-query="docsStore.searchQuery"
:sort-field="docsStore.sortField"
:sort-order="docsStore.sortOrder"
:root-folders="foldersStore.rootFolders"
:topic-color-fn="topicColor"
:empty-message="currentFolderId ? 'This folder is empty' : 'No folders yet'"
:empty-hint="currentFolderId ? 'Upload files above or create a sub-folder' : 'Create a folder to get started'"
ref="browserRef"
@breadcrumb-navigate="handleBreadcrumbNavigate"
@new-folder="browserRef?.startNewFolder()"
@search-change="val => (docsStore.searchQuery = val)"
@sort-change="handleSortChange"
@upload="onFilesSelected"
@folder-navigate="folder => navigateToFolder(folder.id)"
@folder-create="handleFolderCreate"
@folder-rename="handleFolderRename"
@folder-delete="folder => (folderToDelete = folder)"
@file-open="file => $router.push(`/document/${file.id}`)"
@file-share="file => (shareDoc = file)"
@file-move="({ fileId, folderId }) => doMove(fileId, folderId)"
@file-delete="doDeleteDoc"
/>
<FolderDeleteModal
v-if="folderToDelete"
:folder="folderToDelete"
@confirm="confirmDeleteFolder"
@cancel="folderToDelete = null"
/>
<ShareModal
v-if="shareDoc"
:doc="shareDoc"
@close="shareDoc = null"
@unshared="(id) => { const d = docsStore.documents.find(x => x.id === id); if (d) d.is_shared = false }"
/>
</template>
<script setup>
import { ref, reactive, computed, watch, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useFoldersStore } from '../stores/folders.js'
import { useDocumentsStore } from '../stores/documents.js'
import { useTopicsStore } from '../stores/topics.js'
import StorageBrowser from '../components/storage/StorageBrowser.vue'
import FolderDeleteModal from '../components/folders/FolderDeleteModal.vue'
import ShareModal from '../components/sharing/ShareModal.vue'
const route = useRoute()
const router = useRouter()
const foldersStore = useFoldersStore()
const docsStore = useDocumentsStore()
const topicsStore = useTopicsStore()
const browserRef = ref(null)
// ── Navigation ────────────────────────────────────────────────────────────────
const currentFolderId = computed(() => route.params.folderId ?? null)
async function loadFolder(folderId) {
if (folderId === null) {
foldersStore.navigateTo(null)
await foldersStore.fetchFolders(null)
return
}
await foldersStore.navigateTo(folderId)
await Promise.all([
foldersStore.fetchFolders(folderId),
docsStore.fetchDocuments({ folderId, sort: docsStore.sortField, order: docsStore.sortOrder }),
])
docsStore.currentFolderId = folderId
}
onMounted(() => loadFolder(currentFolderId.value))
watch(currentFolderId, (id) => loadFolder(id))
function navigateToFolder(id) { router.push(`/folders/${id}`) }
function handleBreadcrumbNavigate(id) {
if (id == null) router.push('/')
else router.push(`/folders/${id}`)
}
function handleSortChange({ sort, order }) {
docsStore.sortField = sort
docsStore.sortOrder = order
docsStore.fetchDocuments({ folderId: currentFolderId.value, sort, order })
}
// ── Upload ────────────────────────────────────────────────────────────────────
const uploadQueue = ref([])
async function onFilesSelected({ files, autoClassify }) {
const folderId = currentFolderId.value
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()
}
// ── Folder CRUD ───────────────────────────────────────────────────────────────
async function handleFolderCreate({ name, onError, onSuccess }) {
try {
await foldersStore.createFolder(name, currentFolderId.value)
onSuccess()
} catch (e) {
onError(e.message || 'Failed to create folder.')
}
}
async function handleFolderRename({ id, name }) {
if (!name) return
try { await foldersStore.renameFolder(id, name) } catch {}
}
const folderToDelete = ref(null)
async function confirmDeleteFolder() {
if (!folderToDelete.value) return
try { await foldersStore.deleteFolder(folderToDelete.value.id) }
finally { folderToDelete.value = null }
}
// ── Document actions ──────────────────────────────────────────────────────────
const shareDoc = ref(null)
async function doMove(docId, folderId) {
try { await docsStore.moveToFolder(docId, folderId) } catch (e) { console.error(e.message) }
}
async function doDeleteDoc(docId) {
try { await docsStore.remove(docId) } catch (e) { console.error(e.message) }
}
// ── Topic color lookup ────────────────────────────────────────────────────────
function topicColor(name) {
return topicsStore.topics.find(t => t.name === name)?.color ?? '#6366f1'
}
</script>