feat(05-08): add Cloud Storage collapsible section to AppSidebar
- Import CloudProviderTreeItem and useCloudConnectionsStore - Add cloudExpanded ref (default true) and activeCloudConnections/loadingCloudConnections computed - Insert Cloud Storage section between Folders and Topics sections - Fetch connections on mount; render one CloudProviderTreeItem per ACTIVE connection - Empty state: 'No cloud storage connected'; loading state: 'Loading...'
This commit is contained in:
@@ -110,6 +110,52 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Cloud Storage section -->
|
||||||
|
<div class="mt-3">
|
||||||
|
<div class="flex items-center gap-0.5">
|
||||||
|
<!-- Expand/collapse chevron -->
|
||||||
|
<button
|
||||||
|
@click="cloudExpanded = !cloudExpanded"
|
||||||
|
class="p-1 rounded hover:bg-gray-100 text-gray-400 hover:text-gray-600 transition-colors shrink-0"
|
||||||
|
:title="cloudExpanded ? 'Collapse cloud storage' : 'Expand cloud storage'"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="w-3 h-3 transition-transform duration-150"
|
||||||
|
:class="cloudExpanded ? '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>
|
||||||
|
|
||||||
|
<!-- "Cloud Storage" navigates to /settings -->
|
||||||
|
<a
|
||||||
|
href="/settings"
|
||||||
|
class="nav-link flex-1 min-w-0"
|
||||||
|
>
|
||||||
|
<svg class="w-4 h-4 mr-2 shrink-0 text-sky-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z" />
|
||||||
|
</svg>
|
||||||
|
Cloud Storage
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Collapsible content -->
|
||||||
|
<template v-if="cloudExpanded">
|
||||||
|
<div v-if="loadingCloudConnections" class="pl-7 py-1 text-xs text-gray-400">Loading…</div>
|
||||||
|
<div v-else-if="activeCloudConnections.length === 0" class="pl-7 py-1 text-xs text-gray-400">
|
||||||
|
No cloud storage connected
|
||||||
|
</div>
|
||||||
|
<CloudProviderTreeItem
|
||||||
|
v-for="connection in activeCloudConnections"
|
||||||
|
:key="connection.id"
|
||||||
|
:connection="connection"
|
||||||
|
:depth="1"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Topics list -->
|
<!-- Topics list -->
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<p class="px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider mb-1">Topics</p>
|
<p class="px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider mb-1">Topics</p>
|
||||||
@@ -186,18 +232,21 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, watch } from 'vue'
|
import { ref, computed, onMounted, watch } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useTopicsStore } from '../../stores/topics.js'
|
import { useTopicsStore } from '../../stores/topics.js'
|
||||||
import { useAuthStore } from '../../stores/auth.js'
|
import { useAuthStore } from '../../stores/auth.js'
|
||||||
import { useFoldersStore } from '../../stores/folders.js'
|
import { useFoldersStore } from '../../stores/folders.js'
|
||||||
|
import { useCloudConnectionsStore } from '../../stores/cloudConnections.js'
|
||||||
import QuotaBar from './QuotaBar.vue'
|
import QuotaBar from './QuotaBar.vue'
|
||||||
import FolderTreeItem from '../folders/FolderTreeItem.vue'
|
import FolderTreeItem from '../folders/FolderTreeItem.vue'
|
||||||
|
import CloudProviderTreeItem from '../cloud/CloudProviderTreeItem.vue'
|
||||||
import * as api from '../../api/client.js'
|
import * as api from '../../api/client.js'
|
||||||
|
|
||||||
const topicsStore = useTopicsStore()
|
const topicsStore = useTopicsStore()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const foldersStore = useFoldersStore()
|
const foldersStore = useFoldersStore()
|
||||||
|
const cloudConnectionsStore = useCloudConnectionsStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const sharedCount = ref(0)
|
const sharedCount = ref(0)
|
||||||
@@ -206,6 +255,12 @@ const newFolderName = ref('')
|
|||||||
const newFolderError = ref('')
|
const newFolderError = ref('')
|
||||||
const loadingRoots = ref(true)
|
const loadingRoots = ref(true)
|
||||||
const foldersExpanded = ref(false)
|
const foldersExpanded = ref(false)
|
||||||
|
const cloudExpanded = ref(true)
|
||||||
|
|
||||||
|
const activeCloudConnections = computed(() =>
|
||||||
|
cloudConnectionsStore.connections.filter(c => c.status === 'ACTIVE')
|
||||||
|
)
|
||||||
|
const loadingCloudConnections = computed(() => cloudConnectionsStore.loading)
|
||||||
|
|
||||||
watch(() => foldersStore.treeVersion, () => foldersStore.fetchRootFolders())
|
watch(() => foldersStore.treeVersion, () => foldersStore.fetchRootFolders())
|
||||||
|
|
||||||
@@ -222,6 +277,7 @@ onMounted(async () => {
|
|||||||
} catch {
|
} catch {
|
||||||
sharedCount.value = 0
|
sharedCount.value = 0
|
||||||
}
|
}
|
||||||
|
cloudConnectionsStore.fetchConnections()
|
||||||
})
|
})
|
||||||
|
|
||||||
async function signOut() {
|
async function signOut() {
|
||||||
|
|||||||
Reference in New Issue
Block a user