import { useState, useRef, useCallback } from "react"; import { Upload, X, File, AlertCircle, Link as LinkIcon } from "lucide-react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { FileConstraints } from "@/server/types/plugin"; interface FileUploadProps { name: string; description: string; fileConstraints?: FileConstraints; acceptUrl?: boolean; onFileChange: (file: File | null) => void; onUrlChange?: (url: string) => void; disabled?: boolean; } export function FileUpload({ name, description, fileConstraints, acceptUrl = false, onFileChange, onUrlChange, disabled = false, }: FileUploadProps) { const [selectedFile, setSelectedFile] = useState(null); const [fileUrl, setFileUrl] = useState(""); const [error, setError] = useState(null); const [dragActive, setDragActive] = useState(false); const [mode, setMode] = useState<"upload" | "url">("upload"); const fileInputRef = useRef(null); const formatFileSize = (bytes: number): string => { if (bytes === 0) return "0 Bytes"; const k = 1024; const sizes = ["Bytes", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i]; }; const validateFile = (file: File): string | null => { if (!fileConstraints) return null; if (fileConstraints.maxSize && file.size > fileConstraints.maxSize) { return `File size (${formatFileSize(file.size)}) exceeds maximum allowed size (${formatFileSize(fileConstraints.maxSize)})`; } if (fileConstraints.acceptedTypes && fileConstraints.acceptedTypes.length > 0) { if (!fileConstraints.acceptedTypes.includes(file.type)) { return `File type ${file.type} is not accepted. Allowed types: ${fileConstraints.acceptedTypes.join(", ")}`; } } if (fileConstraints.acceptedExtensions && fileConstraints.acceptedExtensions.length > 0) { const extension = "." + file.name.split(".").pop()?.toLowerCase(); if (!fileConstraints.acceptedExtensions.includes(extension)) { return `File extension ${extension} is not accepted. Allowed extensions: ${fileConstraints.acceptedExtensions.join(", ")}`; } } return null; }; const handleFileSelect = useCallback((file: File) => { const validationError = validateFile(file); if (validationError) { setError(validationError); setSelectedFile(null); onFileChange(null); return; } setError(null); setSelectedFile(file); onFileChange(file); }, [fileConstraints, onFileChange]); const handleFileChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { handleFileSelect(file); } }; const handleDrag = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); if (e.type === "dragenter" || e.type === "dragover") { setDragActive(true); } else if (e.type === "dragleave") { setDragActive(false); } }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); if (e.dataTransfer.files && e.dataTransfer.files[0]) { handleFileSelect(e.dataTransfer.files[0]); } }; const handleRemoveFile = () => { setSelectedFile(null); setError(null); onFileChange(null); if (fileInputRef.current) { fileInputRef.current.value = ""; } }; const handleUrlChange = (url: string) => { setFileUrl(url); if (onUrlChange) { onUrlChange(url); } }; const getAcceptAttribute = (): string | undefined => { if (!fileConstraints) return undefined; const types = []; if (fileConstraints.acceptedTypes) { types.push(...fileConstraints.acceptedTypes); } if (fileConstraints.acceptedExtensions) { types.push(...fileConstraints.acceptedExtensions); } return types.length > 0 ? types.join(",") : undefined; }; const renderContent = () => { if (!acceptUrl) { return (
{renderUploadArea()} {renderConstraints()}
); } return ( setMode(v as "upload" | "url")} className="w-full"> Upload File Use URL {renderUploadArea()} {renderConstraints()} handleUrlChange(e.target.value)} disabled={disabled} className="bg-black/50 border-white/10 text-white focus:border-purple-500" />

Enter the URL of the file you want to process

{renderConstraints()}
); }; const renderUploadArea = () => ( <>
!disabled && fileInputRef.current?.click()} > {selectedFile ? (

{selectedFile.name}

{formatFileSize(selectedFile.size)}

) : (

{dragActive ? "Drop file here" : "Click to upload or drag and drop"}

{description}

)}
{error && (

{error}

)} ); const renderConstraints = () => { if (!fileConstraints) return null; return (
{fileConstraints.maxSize && (

• Max file size: {formatFileSize(fileConstraints.maxSize)}

)} {fileConstraints.acceptedTypes && fileConstraints.acceptedTypes.length > 0 && (

• Accepted types: {fileConstraints.acceptedTypes.join(", ")}

)} {fileConstraints.acceptedExtensions && fileConstraints.acceptedExtensions.length > 0 && (

• Accepted extensions: {fileConstraints.acceptedExtensions.join(", ")}

)}
); }; return (
{renderContent()}
); }