import { useRef, useState } from "react"; import { ImageIcon, Trash2, Upload } from "lucide-react"; import { Button } from "~/components/ui/button"; import { SportIcon } from "~/components/SportIcon"; import { cn } from "~/lib/utils"; interface SportIconUploaderProps { name?: string; uploadUrl: string; initialIconUrl?: string | null; sportName?: string; } const MAX_UPLOAD_BYTES = 1024 * 1024; const SVG_TYPE = "image/svg+xml"; export function SportIconUploader({ name = "iconUrl", uploadUrl, initialIconUrl = null, sportName = "Sport icon", }: SportIconUploaderProps) { const fileInputRef = useRef(null); const [iconUrl, setIconUrl] = useState(initialIconUrl ?? ""); const [selectedFile, setSelectedFile] = useState(null); const [previewUrl, setPreviewUrl] = useState(null); const [status, setStatus] = useState<"idle" | "uploading" | "uploaded" | "error">("idle"); const [error, setError] = useState(null); const [isDragging, setIsDragging] = useState(false); function prepareFile(file: File | undefined) { setError(null); setStatus("idle"); if (!file) return; if (file.type !== SVG_TYPE && !file.name.toLowerCase().endsWith(".svg")) { setError("Choose an SVG file."); return; } if (file.size > MAX_UPLOAD_BYTES) { setError("Choose an SVG under 1 MB."); return; } setSelectedFile(file); const reader = new FileReader(); reader.addEventListener("load", () => setPreviewUrl(String(reader.result))); reader.readAsDataURL(file); } function clearSelectedFile() { setSelectedFile(null); setPreviewUrl(null); setError(null); setStatus("idle"); } function removeIcon() { setIconUrl(""); clearSelectedFile(); } async function uploadSelectedFile() { if (!selectedFile) { setError("Choose an SVG first."); return; } setStatus("uploading"); setError(null); try { const formData = new FormData(); formData.append("file", selectedFile); const response = await fetch(uploadUrl, { method: "POST", body: formData }); const result = await response.json().catch(() => ({})); if (!response.ok) { throw new Error(typeof result.error === "string" ? result.error : "Upload failed."); } if (typeof result.iconUrl !== "string") { throw new Error("Upload response did not include an icon URL."); } setIconUrl(result.iconUrl); setSelectedFile(null); setPreviewUrl(null); setStatus("uploaded"); } catch (err) { setStatus("error"); setError(err instanceof Error ? err.message : "Upload failed."); } } return (
{ prepareFile(event.target.files?.[0]); event.target.value = ""; }} className="sr-only" /> {iconUrl && !selectedFile && (

Current icon

{iconUrl}

)} {!iconUrl && !selectedFile && ( )} {selectedFile && previewUrl && (

Ready to upload

{selectedFile.name}

)} {status === "uploaded" &&

Icon uploaded. Save the sport to apply it.

} {error &&

{error}

}

SVG only, up to 1 MB. Existing local icons keep working until replaced.

); }