import { Download, Loader2, Upload } from "lucide-react"; import { useId, useRef, useState } from "react"; import { useAdapters } from "../contexts/adapter-context.js"; import { useBestiaryContext } from "../contexts/bestiary-context.js"; import { useRulesEditionContext } from "../contexts/rules-edition-context.js"; import { Button } from "./ui/button.js"; import { Input } from "./ui/input.js"; interface SourceFetchPromptProps { sourceCode: string; onSourceLoaded: (skippedNames: string[]) => void; } export function SourceFetchPrompt({ sourceCode, onSourceLoaded, }: Readonly) { const { bestiaryIndex, pf2eBestiaryIndex } = useAdapters(); const { fetchAndCacheSource, uploadAndCacheSource } = useBestiaryContext(); const { edition } = useRulesEditionContext(); const indexPort = edition === "pf2e" ? pf2eBestiaryIndex : bestiaryIndex; const sourceDisplayName = indexPort.getSourceDisplayName(sourceCode); const [url, setUrl] = useState(() => indexPort.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 { const { skippedNames } = await fetchAndCacheSource(sourceCode, url); setStatus("idle"); onSourceLoaded(skippedNames); } 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 uploadAndCacheSource(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}
)}
); }