import { Button } from "@/src/components/ui/button";
import { formatBytes, type UploadedFileInfo } from "@/src/lib/file-utils";
import { cn } from "@/src/lib/general-utils";
import {
File,
FileArchive,
FileCode,
FileJson,
FileSpreadsheet,
FileText,
X,
FileDigit,
FileImage,
FileType,
FileMusic,
Loader2,
} from "lucide-react";
interface FileChipProps {
file: UploadedFileInfo;
onRemove?: (key: string) => void;
size?: "compact" | "default" | "large";
rounded?: "none" | "sm" | "md" | "full";
showOriginalName?: boolean;
showSize?: boolean;
showKey?: boolean;
isLoading?: boolean;
maxWidth?: string;
className?: string;
}
const getFileIcon = (filename: string) => {
const ext = filename.toLowerCase().split(".").pop() || "";
switch (ext) {
// JSON
case "json":
return FileJson;
// Spreadsheets
case "csv":
return FileSpreadsheet;
case "xlsx":
return FileSpreadsheet;
case "xls":
return FileSpreadsheet;
// Programming languages
case "py":
return FileCode;
case "js":
return FileCode;
case "ts":
return FileCode;
case "tsx":
return FileCode;
case "jsx":
return FileCode;
case "java":
return FileCode;
case "cpp":
return FileCode;
case "c":
return FileCode;
case "go":
return FileCode;
case "rs":
return FileCode;
case "rb":
return FileCode;
case "php":
return FileCode;
case "swift":
return FileCode;
case "kt":
return FileCode;
case "xml":
return FileCode;
case "html":
return FileCode;
case "css":
return FileCode;
// Text files
case "txt":
return FileType;
case "md":
return FileType;
// PDF
case "pdf":
return File;
// Images
case "png":
return FileImage;
case "jpg":
return FileImage;
case "jpeg":
return FileImage;
case "gif":
return FileImage;
case "svg":
return FileImage;
case "webp":
return FileImage;
// Audio
case "mp3":
return FileMusic;
case "wav":
return FileMusic;
case "ogg":
return FileMusic;
case "m4a":
return FileMusic;
// Binary
case "bin":
return FileDigit;
case "exe":
return FileDigit;
case "dll":
return FileDigit;
// Archives
case "zip":
return FileArchive;
case "tar":
return FileArchive;
case "gz":
return FileArchive;
case "rar":
return FileArchive;
case "7z":
return FileArchive;
default:
return File;
}
};
export function FileChip({
file,
onRemove,
size = "default",
rounded = "md",
showOriginalName = false,
showSize = true,
showKey = false,
isLoading = false,
maxWidth,
className,
}: FileChipProps) {
const FileIcon = getFileIcon(file.name);
const displayName = showOriginalName ? file.name : file.key;
// Handle cases where size or status might not be available
const fileSize = file.size || 0;
const fileStatus = file.status || "ready";
const sizeText = showSize && fileSize > 0 ? formatBytes(fileSize) : "";
const keyText = showKey && showOriginalName ? ` • JSON Input Key: ${file.key}` : "";
const subtitleText =
fileStatus === "processing"
? "Parsing..."
: fileStatus === "error"
? file.error || "Failed to parse"
: `${sizeText}${keyText}`;
const roundedClass = {
none: "rounded-none",
sm: "rounded-sm",
md: "rounded-md",
full: "rounded-full",
}[rounded];
const sizeStyles =
size === "compact" ? "px-2 py-1.5" : size === "large" ? "px-4 py-3" : "px-3 py-2";
return (
<div
className={cn(
"flex items-center justify-between transition-all border",
roundedClass,
sizeStyles,
fileStatus === "error"
? "bg-destructive/10 border-destructive/20"
: fileStatus === "processing"
? "bg-amber-50 dark:bg-amber-950/30 border-amber-200 dark:border-amber-800"
: "bg-muted/30 border-border",
className,
)}
style={maxWidth ? { maxWidth } : undefined}
>
<div className="flex items-center gap-2 min-w-0 flex-1">
<FileIcon className="h-4 w-4 text-gray-700 dark:text-gray-400 flex-shrink-0" />
<div className="flex flex-col min-w-0 flex-1">
<span className="text-xs font-medium truncate" title={file.name}>
{displayName}
</span>
{subtitleText && (
<span className="text-[10px] text-muted-foreground">{subtitleText}</span>
)}
</div>
</div>
<div className="flex items-center gap-1 ml-2 relative group/actions">
{(fileStatus === "processing" || isLoading) && (
<Loader2 className="h-3.5 w-3.5 animate-spin text-gray-400 dark:text-gray-500 group-hover/actions:opacity-0 transition-opacity" />
)}
{onRemove && (
<Button
variant="ghost"
size="icon"
className={cn(
"h-6 w-6 hover:bg-background/80",
fileStatus === "processing" || isLoading
? "absolute opacity-0 group-hover/actions:opacity-100 transition-opacity"
: "",
)}
onClick={() => onRemove(file.key)}
>
<X className="h-3 w-3" />
</Button>
)}
</div>
</div>
);
}