- 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>
78 lines
2.4 KiB
TypeScript
78 lines
2.4 KiB
TypeScript
/**
|
||
* Document display & preview helpers for edit pages.
|
||
*
|
||
* Extracted from pages/component/[id]/edit.vue, pieces/[id]/edit.vue,
|
||
* product/[id]/edit.vue – each had an identical copy of these utilities.
|
||
*/
|
||
|
||
import { getFileIcon } from '~/utils/fileIcons'
|
||
import { isImageDocument, isPdfDocument } from '~/utils/documentPreview'
|
||
|
||
export const PDF_PREVIEW_MAX_BYTES = 5 * 1024 * 1024
|
||
|
||
export const formatSize = (size: number | null | undefined): string => {
|
||
if (size === null || size === undefined) return '—'
|
||
if (size === 0) return '0 B'
|
||
const units = ['B', 'KB', 'MB', 'GB']
|
||
const index = Math.min(units.length - 1, Math.floor(Math.log(size) / Math.log(1024)))
|
||
const formatted = size / Math.pow(1024, index)
|
||
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)) 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) 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 => {
|
||
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) && resolveUrl(doc))) return 'h-24 w-20'
|
||
return 'h-16 w-16'
|
||
}
|
||
|
||
export interface FileIconResult {
|
||
component: unknown
|
||
colorClass: string
|
||
label: string
|
||
}
|
||
|
||
export const documentIcon = (doc: any): FileIconResult =>
|
||
getFileIcon({ name: doc?.filename || doc?.name, mime: doc?.mimeType })
|
||
|
||
export const downloadDocument = (doc: any): void => {
|
||
// 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
|
||
link.download = doc.filename || doc.name || 'document'
|
||
link.click()
|
||
return
|
||
}
|
||
window.open(target, '_blank')
|
||
}
|