tahamajs/IE / Projects /front /src /components /multimodal /FilePreviewCard.tsx
tahamajs's picture
download
raw
3 kB
// src/components/multimodal/FilePreviewCard.tsx
import type { ProcessedFile } from "@/types/multimodal";
import {
FileImage, FileSpreadsheet, FileText, FileCode, File,
FlaskConical, X, Loader2,
} from "lucide-react";
interface FilePreviewCardProps {
file: ProcessedFile;
onRemove: (id: string) => void;
onAnalyze?: (file: ProcessedFile) => void;
}
const CATEGORY_ICONS: Record<string, typeof File> = {
image: FileImage,
csv: FileSpreadsheet,
pdf: FileText,
code: FileCode,
latex: FileCode,
chemical: FlaskConical,
document: FileText,
audio: File,
video: FileImage,
unknown: File,
};
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(1)} MB`;
}
export function FilePreviewCard({ file, onRemove, onAnalyze }: FilePreviewCardProps) {
const Icon = CATEGORY_ICONS[file.category] ?? File;
return (
<div className="relative group bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden shadow-sm hover:shadow-md transition">
{/* Thumbnail area */}
{file.category === "image" && file.thumbnailUrl ? (
<div className="w-full h-24 bg-gray-100 dark:bg-gray-700">
<img
src={file.thumbnailUrl}
alt={file.name}
className="w-full h-full object-cover"
/>
</div>
) : (
<div className="w-full h-24 bg-linear-to-br from-gray-50 to-gray-100 dark:from-gray-700 dark:to-gray-800 flex items-center justify-center">
<Icon className="w-10 h-10 text-gray-400" />
</div>
)}
{/* Info bar */}
<div className="p-2">
<p className="text-xs font-medium text-gray-700 dark:text-gray-300 truncate">
{file.name}
</p>
<p className="text-[10px] text-gray-400">{formatSize(file.size)}</p>
</div>
{/* Progress bar */}
{file.uploadStatus === "uploading" && (
<div className="absolute inset-x-0 bottom-0 h-1 bg-gray-200 dark:bg-gray-700">
<div
className="h-full bg-primary transition-all"
style={{ width: `${file.uploadProgress ?? 0}%` }}
/>
</div>
)}
{/* Remove button */}
<button
onClick={() => onRemove(file.id)}
className="absolute top-1 right-1 w-5 h-5 bg-white dark:bg-gray-900 rounded-full flex items-center justify-center shadow opacity-0 group-hover:opacity-100 transition"
>
<X className="w-3 h-3 text-gray-500" />
</button>
{/* AI analyze button */}
{onAnalyze && file.uploadStatus === "done" && (
<button
onClick={() => onAnalyze(file)}
className="absolute bottom-8 right-1 text-[10px] bg-primary text-white px-2 py-0.5 rounded-full opacity-0 group-hover:opacity-100 transition"
>
Ask AI
</button>
)}
</div>
);
}

Xet Storage Details

Size:
3 kB
·
Xet hash:
233e96ffb51a715abc5a1a29098ebb19ac85251f94e933fcebd01db4d74aab1f

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.