import { Download, Loader2, Upload } from "lucide-react"; import { useId, useRef, useState } from "react"; import { getDefaultFetchUrl } from "../adapters/bestiary-index-adapter.js"; import { Button } from "./ui/button.js"; import { Input } from "./ui/input.js"; interface SourceFetchPromptProps { sourceCode: string; sourceDisplayName: string; fetchAndCacheSource: (sourceCode: string, url: string) => Promise; onSourceLoaded: () => void; onUploadSource: (sourceCode: string, jsonData: unknown) => Promise; } export function SourceFetchPrompt({ sourceCode, sourceDisplayName, fetchAndCacheSource, onSourceLoaded, onUploadSource, }: Readonly) { const [url, setUrl] = useState(() => getDefaultFetchUrl(sourceCode)); const [status, setStatus] = useState<"idle" | "fetching" | "error">("idle"); const [error, setError] = useState(""); const fileInputRef = useRef(null); const sourceUrlId = useId(); const handleFetch = async () => { setStatus("fetching"); setError(""); try { await fetchAndCacheSource(sourceCode, url); onSourceLoaded(); } catch (e) { setStatus("error"); setError(e instanceof Error ? e.message : "Failed to fetch source data"); } }; const handleFileUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; setStatus("fetching"); setError(""); try { const text = await file.text(); const json = JSON.parse(text); await onUploadSource(sourceCode, json); onSourceLoaded(); } catch (err) { setStatus("error"); setError( err instanceof Error ? err.message : "Failed to process uploaded file", ); } // Reset file input if (fileInputRef.current) { fileInputRef.current.value = ""; } }; return (

Load {sourceDisplayName}

Stat block data for this source needs to be loaded. Enter a URL or upload a JSON file.

setUrl(e.target.value)} disabled={status === "fetching"} className="text-xs" />
or
{status === "error" && (
{error}
)}
); }