feat(documents): migrate storage to filesystem, add server-side pagination

- Replace Base64 data URIs with file-based storage served via dedicated endpoints
- Add DocumentPreviewModal navigation, DocumentThumbnail fileUrl support
- Refactor documents page with server-side pagination, search, sort and filters
- Update all components to use fileUrl/downloadUrl instead of raw path
- Add pagination composable support (total, page, itemsPerPage, attachmentFilter)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-03-03 15:17:59 +01:00
parent 546cc37a09
commit e88ed5b8f2
22 changed files with 560 additions and 281 deletions

View File

@@ -19,26 +19,32 @@ export const formatSize = (size: number | null | undefined): string => {
return `${formatted.toFixed(1)} ${units[index]}`
}
const resolveUrl = (doc: any): string => doc?.fileUrl || doc?.path || ''
export const shouldInlinePdf = (doc: any): boolean => {
if (!doc || !isPdfDocument(doc) || !doc.path) return false
if (!doc || !isPdfDocument(doc)) return false
const url = resolveUrl(doc)
if (!url) return false
if (typeof doc.size === 'number' && doc.size > PDF_PREVIEW_MAX_BYTES) return false
return true
}
export const appendPdfViewerParams = (src: string): string => {
if (!src || src.startsWith('data:')) return src || ''
if (!src) return ''
if (src.startsWith('data:')) return src
if (src.includes('#')) return `${src}&toolbar=0&navpanes=0`
return `${src}#toolbar=0&navpanes=0`
}
export const documentPreviewSrc = (doc: any): string => {
if (!doc?.path) return ''
if (isPdfDocument(doc)) return appendPdfViewerParams(doc.path)
return doc.path
const url = resolveUrl(doc)
if (!url) return ''
if (isPdfDocument(doc)) return appendPdfViewerParams(url)
return url
}
export const documentThumbnailClass = (doc: any): string => {
if (shouldInlinePdf(doc) || (isImageDocument(doc) && doc?.path)) return 'h-24 w-20'
if (shouldInlinePdf(doc) || (isImageDocument(doc) && resolveUrl(doc))) return 'h-24 w-20'
return 'h-16 w-16'
}
@@ -52,8 +58,14 @@ export const documentIcon = (doc: any): FileIconResult =>
getFileIcon({ name: doc?.filename || doc?.name, mime: doc?.mimeType })
export const downloadDocument = (doc: any): void => {
if (!doc?.path) return
const target = String(doc.path)
// Prefer dedicated download endpoint
if (doc?.downloadUrl) {
window.open(doc.downloadUrl, '_blank')
return
}
// Fallback for legacy data: URIs during migration
const target = resolveUrl(doc)
if (!target) return
if (target.startsWith('data:')) {
const link = document.createElement('a')
link.href = target