'use client'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { CheckCircle2, Download, FileText, FolderOpen, Loader2, Trash2, Upload, } from 'lucide-react'; type Asset = { id: string; title: string; folder?: string; file_name: string; mime_type?: string | null; bytes?: number | null; created_at?: string; }; function formatBytes(n?: number | null) { if (!n) return '—'; if (n < 1024) return `${n} B`; if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`; return `${(n / (1024 * 1024)).toFixed(1)} MB`; } function iconHint(mime?: string | null) { if (!mime) return 'FILE'; if (mime.startsWith('image/')) return 'IMG'; if (mime.includes('pdf')) return 'PDF'; if (mime.includes('sheet') || mime.includes('csv') || mime.includes('excel')) return 'XLS'; if (mime.includes('zip') || mime.includes('rar')) return 'ZIP'; return 'FILE'; } export default function AssetsLibrary({ tenantLabel }: { tenantLabel: string }) { const [assets, setAssets] = useState([]); const [warning, setWarning] = useState(null); const [folder, setFolder] = useState(''); const [loading, setLoading] = useState(true); const [uploading, setUploading] = useState(false); const [err, setErr] = useState(null); const [drag, setDrag] = useState(false); const load = useCallback(async () => { setLoading(true); const res = await fetch('/api/assets'); const data = await res.json(); setAssets(data.assets || []); setWarning(data.warning || null); setLoading(false); }, []); useEffect(() => { void load(); }, [load]); const folders = useMemo(() => { const s = new Set(); for (const a of assets) { if (a.folder) s.add(a.folder); } return [...s].sort(); }, [assets]); const visible = useMemo(() => { if (!folder) return assets; return assets.filter((a) => (a.folder || '') === folder); }, [assets, folder]); async function uploadFiles(fileList: FileList | File[]) { const files = [...fileList]; if (!files.length) return; setUploading(true); setErr(null); const fd = new FormData(); fd.set('folder', folder); for (const f of files) fd.append('files', f); const res = await fetch('/api/assets', { method: 'POST', body: fd }); const data = await res.json(); if (!res.ok) { setErr( data.error || (data.errors || []).join(' · ') || 'Error al subir' ); } else if (data.errors?.length) { setErr(data.errors.join(' · ')); } await load(); setUploading(false); } async function download(id: string) { const res = await fetch(`/api/assets/download?id=${encodeURIComponent(id)}`); const data = await res.json(); if (!res.ok || !data.url) { setErr(data.error || 'No se pudo descargar'); return; } window.open(data.url, '_blank', 'noopener,noreferrer'); } async function remove(id: string) { if (!confirm('¿Eliminar este archivo de la biblioteca?')) return; const res = await fetch(`/api/assets?id=${encodeURIComponent(id)}`, { method: 'DELETE', }); if (!res.ok) { const data = await res.json().catch(() => ({})); setErr(data.error || 'No se pudo borrar'); } await load(); } return (

Biblioteca de activos

Archivo oficial de {tenantLabel}: sube PDF, Excel, CSV, imágenes, ZIP u otros archivos ya validados. Opcional: carpeta lógica (p. ej. briefs/2026).

{warning && (

{warning}

)} {err &&

{err}

}
setFolder(e.target.value)} list="asset-folders" /> {folders.map((f) => (
{ e.preventDefault(); setDrag(true); }} onDragOver={(e) => e.preventDefault()} onDragLeave={() => setDrag(false)} onDrop={(e) => { e.preventDefault(); setDrag(false); if (e.dataTransfer.files?.length) { void uploadFiles(e.dataTransfer.files); } }} className={`rounded-xl border border-dashed px-6 py-10 text-center transition-colors ${ drag ? 'border-brand-500 bg-brand-500/10' : 'border-gray-700 bg-[#111827]' }`} >

Arrastra aquí cualquier archivo (máx. 80 MB c/u)

PDF · Excel · CSV · imágenes · ZIP · vídeo · etc.

{folders.length > 0 && (
{folders.map((f) => ( ))}
)} {loading ? (

Cargando…

) : visible.length === 0 ? (

Sin archivos en esta vista

) : (
    {visible.map((a) => (
  • {iconHint(a.mime_type)}

    {a.file_name}

    {a.folder ? `${a.folder}/` : ''} {formatBytes(a.bytes)} {a.created_at ? ` · ${new Date(a.created_at).toLocaleString('es-ES')}` : ''}

    Validado
  • ))}
)}
); }