import {
ActionIcon,
Anchor,
Badge,
Box,
Button,
FileButton,
Group,
Image,
Loader,
Paper,
Stack,
Text,
Tooltip,
} from "@mantine/core";
import { notifications } from "@mantine/notifications";
import {
IconDownload,
IconFile,
IconFileSpreadsheet,
IconFileText,
IconFileTypePdf,
IconPhoto,
IconTrash,
IconUpload,
} from "@tabler/icons-react";
import { useCallback, useEffect, useState } from "react";
import * as api from "../api";
import type { CardFile } from "../types";
interface Props {
cardId: string;
refreshKey?: number;
}
function formatSize(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
}
function isImage(mime: string): boolean {
return mime.startsWith("image/");
}
function fileIcon(mime: string, size = 18) {
const m = mime.toLowerCase();
if (m.startsWith("image/")) return ;
if (m === "application/pdf") return ;
if (
m.includes("spreadsheet") ||
m.includes("excel") ||
m === "text/csv" ||
m === "application/vnd.ms-excel"
) {
return ;
}
if (m.startsWith("text/")) return ;
return ;
}
function sourceBadge(s: CardFile["source"]) {
if (s === "description") return { color: "blue", label: "descripcion" };
if (s === "chat") return { color: "teal", label: "chat" };
return { color: "gray", label: "subido" };
}
export function CardFilesPanel({ cardId, refreshKey }: Props) {
const [files, setFiles] = useState([]);
const [loading, setLoading] = useState(true);
const [uploading, setUploading] = useState(false);
const reload = useCallback(async () => {
try {
const list = await api.listCardFiles(cardId);
setFiles(list);
} catch (e) {
notifications.show({ color: "red", message: (e as Error).message });
} finally {
setLoading(false);
}
}, [cardId]);
useEffect(() => {
reload();
}, [reload, refreshKey]);
const onUpload = async (file: File | null) => {
if (!file) return;
setUploading(true);
try {
const cf = await api.uploadCardFile(cardId, file, "upload");
setFiles((prev) => [...prev, cf]);
} catch (e) {
notifications.show({ color: "red", message: (e as Error).message });
} finally {
setUploading(false);
}
};
const onDelete = async (id: string) => {
if (!window.confirm("¿Borrar este archivo?")) return;
try {
await api.deleteCardFile(id);
setFiles((prev) => prev.filter((f) => f.id !== id));
} catch (e) {
notifications.show({ color: "red", message: (e as Error).message });
}
};
return (
{files.length} archivo{files.length === 1 ? "" : "s"}
{(props) => (
}
loading={uploading}
{...props}
>
Subir
)}
{loading ? (
) : files.length === 0 ? (
Sin archivos
Sube archivos con el boton, arrastra al chat o a la descripcion.
) : (
{files.map((f) => {
const badge = sourceBadge(f.source);
return (
{isImage(f.mime) ? (
) : (
{fileIcon(f.mime, 28)}
)}
{f.filename}
{badge.label}
{formatSize(f.size)}
{f.mime || "?"}
onDelete(f.id)}
aria-label="Borrar"
>
);
})}
)}
);
}