feat: pagination
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
+57
-78
@@ -1,9 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch, nextTick, defineAsyncComponent } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted, watch, nextTick, defineAsyncComponent } from 'vue'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { useModuleStore, useIntegrationStore, usePreviewStore } from '@KTXC'
|
||||
import { useDocumentManager, useDocumentSelection, useFileUpload, useFileEditor } from '@/composables'
|
||||
import type { ViewMode, SortField, SortOrder, BreadcrumbItem } from '@/types'
|
||||
import type { ListFilter, ListSort } from '@DocumentsManager/types'
|
||||
import { CollectionObject } from '@DocumentsManager/models/collection'
|
||||
import { EntityObject } from '@DocumentsManager/models/entity'
|
||||
import { ServiceObject } from '@DocumentsManager/models/service'
|
||||
@@ -118,7 +119,7 @@ const mediaViewerDialog = computed(() => {
|
||||
})
|
||||
|
||||
const viewerItems = computed<ViewerItem[]>(() =>
|
||||
sortedItems.value.entities.map(entityToViewerItem),
|
||||
documentManager.currentEntities.value.map(entityToViewerItem),
|
||||
)
|
||||
|
||||
function entityToViewerItem(entity: EntityObject): ViewerItem {
|
||||
@@ -294,76 +295,45 @@ const breadcrumbs = computed<BreadcrumbItem[]>(() => {
|
||||
return items
|
||||
})
|
||||
|
||||
const sortedItems = computed(() => {
|
||||
const collections = [...documentManager.currentCollections.value]
|
||||
const entities = [...documentManager.currentEntities.value]
|
||||
|
||||
// Sort collections
|
||||
collections.sort((a, b) => {
|
||||
const aVal = sortField.value === 'label'
|
||||
? (a.properties.label || nodeId(a))
|
||||
: sortField.value === 'modifiedOn'
|
||||
? (a.modified?.getTime() ?? 0)
|
||||
: sortField.value === 'createdOn'
|
||||
? (a.created?.getTime() ?? 0)
|
||||
: ''
|
||||
const bVal = sortField.value === 'label'
|
||||
? (b.properties.label || nodeId(b))
|
||||
: sortField.value === 'modifiedOn'
|
||||
? (b.modified?.getTime() ?? 0)
|
||||
: sortField.value === 'createdOn'
|
||||
? (b.created?.getTime() ?? 0)
|
||||
: ''
|
||||
const cmp = String(aVal).localeCompare(String(bVal))
|
||||
return sortOrder.value === 'asc' ? cmp : -cmp
|
||||
})
|
||||
|
||||
// Sort entities
|
||||
entities.sort((a, b) => {
|
||||
const aVal = sortField.value === 'label'
|
||||
? (a.properties.label || nodeId(a))
|
||||
: sortField.value === 'mime'
|
||||
? (a.properties.mime || '')
|
||||
: sortField.value === 'size'
|
||||
? a.properties.size
|
||||
: sortField.value === 'modifiedOn'
|
||||
? (a.modified?.getTime() ?? 0)
|
||||
: sortField.value === 'createdOn'
|
||||
? (a.created?.getTime() ?? 0)
|
||||
: ''
|
||||
const bVal = sortField.value === 'label'
|
||||
? (b.properties.label || nodeId(b))
|
||||
: sortField.value === 'mime'
|
||||
? (b.properties.mime || '')
|
||||
: sortField.value === 'size'
|
||||
? b.properties.size
|
||||
: sortField.value === 'modifiedOn'
|
||||
? (b.modified?.getTime() ?? 0)
|
||||
: sortField.value === 'createdOn'
|
||||
? (b.created?.getTime() ?? 0)
|
||||
: ''
|
||||
const cmp = String(aVal).localeCompare(String(bVal))
|
||||
return sortOrder.value === 'asc' ? cmp : -cmp
|
||||
})
|
||||
|
||||
// Filter by search
|
||||
const filterFn = (item: CollectionObject | EntityObject) => {
|
||||
if (!searchQuery.value) return true
|
||||
const label = item instanceof EntityObject
|
||||
? (item.properties.label || nodeId(item))
|
||||
: (item.properties.label || nodeId(item))
|
||||
return label.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||
}
|
||||
|
||||
return {
|
||||
collections: collections.filter(filterFn),
|
||||
entities: entities.filter(filterFn),
|
||||
const hasItems = computed(() =>
|
||||
documentManager.currentCollections.value.length > 0 || documentManager.currentEntities.value.length > 0
|
||||
)
|
||||
|
||||
// Search/sort are server-driven: build the current filter/sort and reset-load
|
||||
// the current location whenever either changes.
|
||||
function buildFilter(): ListFilter | undefined {
|
||||
return searchQuery.value ? { label: searchQuery.value } : undefined
|
||||
}
|
||||
|
||||
function buildSort(): ListSort {
|
||||
return { [sortField.value]: sortOrder.value === 'asc' }
|
||||
}
|
||||
|
||||
async function reload() {
|
||||
await documentManager.resetAndLoad(buildFilter(), buildSort())
|
||||
}
|
||||
|
||||
let searchDebounceTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
watch(searchQuery, () => {
|
||||
if (searchDebounceTimer !== null) {
|
||||
clearTimeout(searchDebounceTimer)
|
||||
}
|
||||
searchDebounceTimer = setTimeout(() => {
|
||||
searchDebounceTimer = null
|
||||
reload()
|
||||
}, 300)
|
||||
})
|
||||
|
||||
const hasItems = computed(() =>
|
||||
sortedItems.value.collections.length > 0 || sortedItems.value.entities.length > 0
|
||||
)
|
||||
watch([sortField, sortOrder], () => {
|
||||
reload()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (searchDebounceTimer !== null) {
|
||||
clearTimeout(searchDebounceTimer)
|
||||
}
|
||||
})
|
||||
|
||||
const hasSelection = computed(() => selection.hasSelection.value)
|
||||
|
||||
@@ -695,7 +665,7 @@ async function readEntriesRecursively(
|
||||
async function handleUploadAll() {
|
||||
try {
|
||||
await upload.uploadAll()
|
||||
await documentManager.refresh()
|
||||
await reload()
|
||||
notify('Uploads completed', 'success')
|
||||
} catch (error) {
|
||||
console.error('Failed to upload files:', error)
|
||||
@@ -712,7 +682,7 @@ function handleUploadDialogClose() {
|
||||
// Refresh
|
||||
async function handleRefresh() {
|
||||
try {
|
||||
await documentManager.refresh()
|
||||
await reload()
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh files:', error)
|
||||
notify(getErrorMessage(error, 'Failed to refresh files'), 'error')
|
||||
@@ -725,7 +695,7 @@ onMounted(async () => {
|
||||
await documentManager.initialize()
|
||||
await servicesStore.list()
|
||||
syncActiveService(availableServices.value)
|
||||
await documentManager.refresh()
|
||||
await reload()
|
||||
console.log('[Files] - Initialized with:', {
|
||||
provider: activeProviderId.value,
|
||||
service: activeServiceId.value,
|
||||
@@ -818,9 +788,11 @@ onMounted(async () => {
|
||||
<!-- Grid View -->
|
||||
<FilesGridView
|
||||
v-else-if="viewMode === 'grid'"
|
||||
:collections="sortedItems.collections"
|
||||
:entities="sortedItems.entities"
|
||||
:collections="documentManager.currentCollections.value"
|
||||
:entities="documentManager.currentEntities.value"
|
||||
:selected-ids="selectedIds"
|
||||
:has-more="documentManager.hasMore.value"
|
||||
:is-loading-more="documentManager.isLoadingMore.value"
|
||||
:preview-source="previewsAvailable ? resolvePreviewSource : undefined"
|
||||
:thumbnail-source="previewsAvailable ? resolveThumbnailSource : undefined"
|
||||
@item-click="handleItemClick"
|
||||
@@ -830,14 +802,17 @@ onMounted(async () => {
|
||||
@delete="handleDeleteItem"
|
||||
@download="handleDownloadItem"
|
||||
@show-details="handleShowDetails"
|
||||
@load-more="documentManager.loadMore"
|
||||
/>
|
||||
|
||||
<!-- List View -->
|
||||
<FilesListView
|
||||
v-else-if="viewMode === 'list'"
|
||||
:collections="sortedItems.collections"
|
||||
:entities="sortedItems.entities"
|
||||
:collections="documentManager.currentCollections.value"
|
||||
:entities="documentManager.currentEntities.value"
|
||||
:selected-ids="selectedIds"
|
||||
:has-more="documentManager.hasMore.value"
|
||||
:is-loading-more="documentManager.isLoadingMore.value"
|
||||
:preview-source="previewsAvailable ? resolvePreviewSource : undefined"
|
||||
:thumbnail-source="previewsAvailable ? resolveThumbnailSource : undefined"
|
||||
@item-click="handleItemClick"
|
||||
@@ -847,14 +822,17 @@ onMounted(async () => {
|
||||
@delete="handleDeleteItem"
|
||||
@download="handleDownloadItem"
|
||||
@show-details="handleShowDetails"
|
||||
@load-more="documentManager.loadMore"
|
||||
/>
|
||||
|
||||
<!-- Details View -->
|
||||
<FilesDetailsView
|
||||
v-else-if="viewMode === 'details'"
|
||||
:collections="sortedItems.collections"
|
||||
:entities="sortedItems.entities"
|
||||
:collections="documentManager.currentCollections.value"
|
||||
:entities="documentManager.currentEntities.value"
|
||||
:selected-ids="selectedIds"
|
||||
:has-more="documentManager.hasMore.value"
|
||||
:is-loading-more="documentManager.isLoadingMore.value"
|
||||
:preview-source="previewsAvailable ? resolvePreviewSource : undefined"
|
||||
:thumbnail-source="previewsAvailable ? resolveThumbnailSource : undefined"
|
||||
@item-click="handleItemClick"
|
||||
@@ -864,6 +842,7 @@ onMounted(async () => {
|
||||
@delete="handleDeleteItem"
|
||||
@download="handleDownloadItem"
|
||||
@show-details="handleShowDetails"
|
||||
@load-more="documentManager.loadMore"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user