- FileManagerView: add mappedBreadcrumb computed ({id,label} from foldersStore.breadcrumb)
- FileManagerView: import useToastStore; add toast.show() to doMove, doDeleteDoc, onFilesSelected
- FileManagerView: remove console.error from doMove/doDeleteDoc (toast communicates errors)
- CloudFolderView: add mappedBreadcrumb computed; pass to StorageBrowser :breadcrumb
- Delete FolderBreadcrumb.vue + FolderBreadcrumb.test.js (no dead code per CLAUDE.md)
- FileManagerView.test.js: replace dead FolderBreadcrumb mock with BreadcrumbBar mock
- StorageBrowser.skeleton.test.js: remove unused FolderBreadcrumb stub
- Full suite: 164/164 pass, 0 failures
98 lines
3.2 KiB
Vue
98 lines
3.2 KiB
Vue
<template>
|
|
<StorageBrowser
|
|
mode="cloud"
|
|
:folders="folders"
|
|
:files="files"
|
|
:breadcrumb="mappedBreadcrumb"
|
|
:upload-queue="uploadQueue"
|
|
:loading="loading"
|
|
:empty-message="error || 'This folder is empty'"
|
|
:empty-hint="error ? '' : 'Files stored here are managed by ' + providerLabel(provider)"
|
|
@breadcrumb-navigate="handleBreadcrumbNavigate"
|
|
@upload="onFilesSelected"
|
|
@folder-navigate="item => navigateTo(item)"
|
|
@file-open="file => {}"
|
|
/>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, watch, onMounted, reactive } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import * as api from '../api/client.js'
|
|
import StorageBrowser from '../components/storage/StorageBrowser.vue'
|
|
import { providerLabel } from '../utils/formatters.js'
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
|
|
const items = ref([])
|
|
const loading = ref(false)
|
|
const error = ref('')
|
|
|
|
const provider = computed(() => route.params.provider)
|
|
const folderId = computed(() => route.params.folderId)
|
|
|
|
const folders = computed(() => items.value.filter(i => i.is_dir))
|
|
const files = computed(() => items.value.filter(i => !i.is_dir))
|
|
|
|
/** Breadcrumb built from the folder path segments. */
|
|
const breadcrumb = computed(() => {
|
|
if (!folderId.value || folderId.value === 'root') return []
|
|
const parts = folderId.value.replace(/\/$/, '').split('/')
|
|
return parts.map((seg, idx) => ({
|
|
id: parts.slice(0, idx + 1).join('/'),
|
|
name: seg,
|
|
}))
|
|
})
|
|
|
|
const mappedBreadcrumb = computed(() =>
|
|
breadcrumb.value.map(f => ({ id: f.id, label: f.name }))
|
|
)
|
|
|
|
async function load() {
|
|
loading.value = true
|
|
error.value = ''
|
|
try {
|
|
const data = await api.getCloudFolders(provider.value, folderId.value ?? 'root')
|
|
items.value = data.items ?? []
|
|
} catch (e) {
|
|
const msg = e.message || ''
|
|
if (msg.toLowerCase().includes('no active connection') || msg.includes('404') || msg.toLowerCase().includes('not found')) {
|
|
error.value = 'No cloud provider connected. Go to Settings to connect a cloud storage account.'
|
|
} else {
|
|
error.value = msg || 'Failed to load folder contents.'
|
|
}
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
function navigateTo(item) {
|
|
router.push(`/cloud/${provider.value}/${item.id}`)
|
|
}
|
|
|
|
function handleBreadcrumbNavigate(id) {
|
|
if (id == null) router.push(`/cloud/${provider.value}/root`)
|
|
else router.push(`/cloud/${provider.value}/${id}`)
|
|
}
|
|
|
|
// ── Upload ────────────────────────────────────────────────────────────────────
|
|
|
|
const uploadQueue = ref([])
|
|
|
|
async function onFilesSelected({ files: selectedFiles }) {
|
|
const promises = selectedFiles.map(file => {
|
|
const item = reactive({ name: file.name, done: false, error: null, status: 'Uploading…' })
|
|
uploadQueue.value.unshift(item)
|
|
return api.uploadToCloud(file, provider.value, folderId.value || null)
|
|
.then(() => { item.done = true; item.status = null })
|
|
.catch(e => { item.error = e.message || 'Upload failed' })
|
|
})
|
|
await Promise.allSettled(promises)
|
|
await load()
|
|
}
|
|
|
|
onMounted(load)
|
|
watch([provider, folderId], load)
|
|
</script>
|