'use client'; import { useState, useEffect } from 'react'; import api, { assetsApi } from '@/lib/api'; import { clsx } from 'clsx'; import { toast } from 'react-hot-toast'; import { X, Search, Image as ImageIcon, Video, Mic, FileText, Check, Loader2, FolderOpen, Upload, } from 'lucide-react'; import FileUpload from './FileUpload'; interface Asset { id: string; filename: string; file_type: string; mime_type: string; width?: number; height?: number; thumbnail_url: string | null; file_url: string; created_at: string; source_module?: string; } interface AssetLibraryProps { isOpen: boolean; onClose: () => void; onSelect: (asset: Asset) => void; fileTypes?: string[]; // ['image', 'video', 'audio'] title?: string; multiple?: boolean; } const FILE_TYPE_ICONS = { image: ImageIcon, video: Video, audio: Mic, document: FileText, }; export default function AssetLibrary({ isOpen, onClose, onSelect, fileTypes = ['image', 'video', 'audio'], title = 'Select from My Files', multiple = false, }: AssetLibraryProps) { const [assets, setAssets] = useState([]); const [loading, setLoading] = useState(false); const [search, setSearch] = useState(''); const [selectedType, setSelectedType] = useState(null); const [page, setPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [selectedAssets, setSelectedAssets] = useState>(new Set()); const [showUpload, setShowUpload] = useState(false); const [uploading, setUploading] = useState(false); useEffect(() => { if (isOpen) { loadAssets(); } }, [isOpen, search, selectedType, page]); const loadAssets = async () => { setLoading(true); try { const types = selectedType ? selectedType : fileTypes.join(','); const response = await api.get('/assets/library', { params: { file_types: types, search: search || undefined, page, limit: 20, }, }); setAssets(response.data.items); setTotalPages(response.data.pages); } catch (error) { console.error('Failed to load assets:', error); } finally { setLoading(false); } }; const handleSelect = (asset: Asset) => { if (multiple) { const newSelected = new Set(selectedAssets); if (newSelected.has(asset.id)) { newSelected.delete(asset.id); } else { newSelected.add(asset.id); } setSelectedAssets(newSelected); } else { onSelect(asset); onClose(); } }; const handleConfirmMultiple = () => { const selected = assets.filter(a => selectedAssets.has(a.id)); selected.forEach(asset => onSelect(asset)); onClose(); }; const handleUpload = async (file: File) => { setUploading(true); try { const response = await assetsApi.upload(file); toast.success('File uploaded!'); loadAssets(); // Reload to show new file // Auto-select the new file if (!multiple) { onSelect(response.data); setShowUpload(false); onClose(); } } catch (error) { toast.error('Failed to upload file'); } finally { setUploading(false); } }; if (!isOpen) return null; return (
{/* Header */}

{title}

{/* Filters */}
{/* Search */}
{ setSearch(e.target.value); setPage(1); }} className="w-full pl-10 pr-4 py-2 bg-forge-gray border border-gray-700 rounded-lg text-white placeholder-gray-500 focus:border-forge-yellow focus:outline-none" />
{/* Type filters */}
{fileTypes.map((type) => { const Icon = FILE_TYPE_ICONS[type as keyof typeof FILE_TYPE_ICONS] || FileText; return ( ); })}
{/* Upload Section */} {showUpload && (
{uploading && (
Uploading...
)}
)} {/* Asset Grid */}
{loading ? (
) : assets.length === 0 ? (

No files found

Upload some files or generate content to see them here

) : (
{assets.map((asset) => { const isSelected = selectedAssets.has(asset.id); const Icon = FILE_TYPE_ICONS[asset.file_type as keyof typeof FILE_TYPE_ICONS] || FileText; // Determine preview URL let previewUrl = null; if (asset.thumbnail_url) { previewUrl = `${process.env.NEXT_PUBLIC_API_URL}${asset.thumbnail_url}`; } else if (asset.mime_type?.startsWith('image/')) { // Fallback to direct download for images without thumbnails previewUrl = `/api/v1/assets/${asset.id}/download`; } return ( ); })}
)}
{/* Pagination */} {totalPages > 1 && (
Page {page} of {totalPages}
)} {/* Footer for multiple selection */} {multiple && selectedAssets.size > 0 && (
{selectedAssets.size} file(s) selected
)}
); }