import { useCallback, useEffect, useState } from "react"; import { api } from "../api"; import type { AdminCaseDetail } from "../types"; import MatchCard from "./MatchCard"; const TYPE_BADGE: Record = { lost: "bg-red-100 text-red-700", found: "bg-accent-100 text-accent-700", }; // Admin modal: a case with its subject dog (all photos) + persisted matches, and a run/re-run // matching action. Shared by the All Cases table and the owner-detail view. export default function CaseDetailModal({ caseId, onClose, onChanged, }: { caseId: number; onClose: () => void; onChanged?: () => void; }) { const [data, setData] = useState(null); const [error, setError] = useState(null); const [running, setRunning] = useState(false); const load = useCallback(() => { return api .getCaseDetail(caseId) .then(setData) .catch((e) => setError(e instanceof Error ? e.message : "Failed to load")); }, [caseId]); useEffect(() => { setData(null); setError(null); load(); }, [load]); async function runMatch() { setRunning(true); setError(null); try { await api.runCaseMatch(caseId); await load(); onChanged?.(); } catch (e) { setError(e instanceof Error ? e.message : "Matching failed"); } finally { setRunning(false); } } const c = data?.case; const dog = data?.dog; return (
e.stopPropagation()}> {error &&

{error}

} {!data ? (

Loading…

) : ( <>

{c!.type} {dog?.profile.name ?? "—"}

{c!.type === "lost" ? "Reported by " : "Found by "} {c!.person?.name || c!.finder_name || c!.finder_email || "—"} {" · "}ZIP {c!.event_zip} · {c!.event_date} · {c!.status}

{dog && (

{c!.type === "lost" ? "Lost dog" : "Found dog"} · {dog.photos.length} photo(s)

{dog.photos.map((p) => ( {dog.profile.name} ))} {dog.photos.length === 0 && (

No photos.

)}
)}

Matches{" "} ({c!.type === "lost" ? "candidate found dogs" : "candidate lost dogs"})

{data.matches.length === 0 ? (

No matches yet. Run matching to find candidates; if none appear, widen the search or the dog may not be in the system.

) : (
{data.matches.map((m) => ( ))}
)} )}
); }