import { attempt } from "~~/types/result"; import * as schema from '~~/drizzle/schema'; export type ResourceFile = typeof schema.files.$inferSelect; export type ResourceCategory = 'all' | 'documents' | 'images' | 'audio' | 'videos'; const categoryMimeMap: Record boolean> = { all: () => true, documents: (mime) => mime === 'application/pdf' || mime.startsWith('text/') || mime.includes('document') || mime.includes('spreadsheet') || mime.includes('presentation'), images: (mime) => mime.startsWith('image/'), audio: (mime) => mime.startsWith('audio/'), videos: (mime) => mime.startsWith('video/'), }; export const useResources = async () => { const files = useState('resources:files', () => []); const loaded = useState('resources:loaded', () => false); const selectedIds = useState>('resources:selected', () => new Set()); const category = useState('resources:category', () => 'all'); const { refresh } = await useFetch('/api/files', { key: 'resources_files_request', immediate: !loaded.value, onRequest() { loaded.value = true; }, onResponse({ response }) { if (response.ok) { files.value = response._data ?? []; } }, }); const filteredFiles = computed(() => { const filter = categoryMimeMap[category.value]; return files.value.filter(f => filter(f.mimeType)); }); const setCategory = (cat: ResourceCategory) => { category.value = cat; clearSelection(); }; const categoryCount = (cat: ResourceCategory): number => { const filter = categoryMimeMap[cat]; return files.value.filter(f => filter(f.mimeType)).length; }; const toggleSelect = (id: string) => { const next = new Set(selectedIds.value); if (next.has(id)) { next.delete(id); } else { next.add(id); } selectedIds.value = next; }; const selectAll = () => { selectedIds.value = new Set(filteredFiles.value.map(f => f.id)); }; const clearSelection = () => { selectedIds.value = new Set(); }; const isAllSelected = computed(() => filteredFiles.value.length > 0 && selectedIds.value.size === filteredFiles.value.length ); const hasSelection = computed(() => selectedIds.value.size > 0); const selectedCount = computed(() => selectedIds.value.size); const deleteFile = async (id: string) => { const file = files.value.find(f => f.id === id); if (!file) return; const res = await attempt($fetch(`/api/file/${id}`, { method: 'DELETE', onRequest() { files.value = files.value.filter(f => f.id !== id); const next = new Set(selectedIds.value); next.delete(id); selectedIds.value = next; }, onRequestError() { files.value = [...files.value, file].sort( (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() ); }, onResponseError() { files.value = [...files.value, file].sort( (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() ); }, })); return res; }; const deleteSelected = async () => { const ids = Array.from(selectedIds.value); clearSelection(); const results = await Promise.allSettled( ids.map(id => deleteFile(id)) ); await refresh(); return results; }; const copyLink = async (file: ResourceFile) => { const url = file.url; await navigator.clipboard.writeText(url); }; const downloadFile = (file: ResourceFile) => { const url = file.url; const a = document.createElement('a'); a.href = url; a.download = file.name; document.body.appendChild(a); a.click(); document.body.removeChild(a); }; const getFileIcon = (mimeType: string): string => { if (mimeType.startsWith('image/')) return 'i-mynaui-image'; if (mimeType === 'application/pdf') return 'i-tabler-file-type-pdf'; if (mimeType.startsWith('text/')) return 'i-mynaui-file-text'; if (mimeType.startsWith('video/')) return 'i-mynaui-video'; if (mimeType.startsWith('audio/')) return 'i-mynaui-music'; return 'i-mynaui-file-text'; }; const isImage = (mimeType: string): boolean => mimeType.startsWith('image/'); const formatSize = (bytes: number): string => { if (bytes === 0) return '-'; const units = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(1024)); const size = bytes / Math.pow(1024, i); return `${size.toFixed(i === 0 ? 0 : 1)} ${units[i]}`; }; const formatDate = (date: Date | string): string => { const d = new Date(date); return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', }); }; return { files, filteredFiles, selectedIds, selectedCount, hasSelection, isAllSelected, category, setCategory, categoryCount, toggleSelect, selectAll, clearSelection, deleteFile, deleteSelected, copyLink, downloadFile, getFileIcon, isImage, formatSize, formatDate, refresh, }; };