import { useState, useEffect, useCallback } from 'react' import { FlaskConical, AlertTriangle, CheckCircle, XCircle, GitMerge, ShieldAlert } from 'lucide-react' export default function PairingSimulator() { const [dogs, setDogs] = useState([]) const [sireId, setSireId] = useState('') const [damId, setDamId] = useState('') const [result, setResult] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [dogsLoading, setDogsLoading] = useState(true) const [relationWarning, setRelationWarning] = useState(null) const [relationChecking, setRelationChecking] = useState(false) useEffect(() => { fetch('/api/dogs') .then(r => r.json()) .then(data => { setDogs(Array.isArray(data) ? data : (data.dogs || [])) setDogsLoading(false) }) .catch(() => setDogsLoading(false)) }, []) // Check for direct relation whenever both sire and dam are selected const checkRelation = useCallback(async (sid, did) => { if (!sid || !did) { setRelationWarning(null) return } setRelationChecking(true) try { const res = await fetch(`/api/pedigree/relations/${sid}/${did}`) const data = await res.json() setRelationWarning(data.related ? data.relationship : null) } catch { setRelationWarning(null) } finally { setRelationChecking(false) } }, []) function handleSireChange(e) { const val = e.target.value setSireId(val) setResult(null) checkRelation(val, damId) } function handleDamChange(e) { const val = e.target.value setDamId(val) setResult(null) checkRelation(sireId, val) } const males = dogs.filter(d => d.sex === 'male') const females = dogs.filter(d => d.sex === 'female') async function handleSimulate(e) { e.preventDefault() if (!sireId || !damId) return setLoading(true) setError(null) setResult(null) try { const res = await fetch('/api/pedigree/trial-pairing', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sire_id: parseInt(sireId), dam_id: parseInt(damId) }) }) if (!res.ok) { const err = await res.json() throw new Error(err.error || 'Failed to calculate') } setResult(await res.json()) } catch (err) { setError(err.message) } finally { setLoading(false) } } function RiskBadge({ coi, recommendation }) { const isLow = coi < 5 const isMed = coi >= 5 && coi < 10 const isHigh = coi >= 10 return (
{isLow && } {isMed && } {isHigh && } {recommendation}
) } return (
{/* Header */}

Trial Pairing Simulator

Select a sire and dam to calculate the estimated inbreeding coefficient (COI) and view common ancestors.

{/* Selector Card */}
{!dogsLoading && males.length === 0 && (

No male dogs registered.

)}
{!dogsLoading && females.length === 0 && (

No female dogs registered.

)}
{/* Direct-relation warning banner */} {relationChecking && (

Checking relationship…

)} {!relationChecking && relationWarning && (

Direct Relation Detected

{relationWarning}. COI will reflect the high inbreeding coefficient for this pairing.

)}
{/* Error */} {error &&
{error}
} {/* Results */} {result && (
{/* Direct-relation alert in results */} {result.directRelation && (

Direct Relation — High Inbreeding Risk

{result.directRelation}

)} {/* COI Summary */}

Pairing

{result.sire.name} × {result.dam.name}

COI

{result.coi.toFixed(2)}%

COI Guide: <5% Low risk · 5–10% Moderate risk · >10% High risk
{/* Common Ancestors */}

Common Ancestors

{result.commonAncestors.length} found
{result.commonAncestors.length === 0 ? (

No common ancestors found within 6 generations. This pairing has excellent genetic diversity.

) : (
{result.commonAncestors.map((anc, i) => ( ))}
Ancestor Sire Gen Dam Gen
{anc.name} Gen {anc.sireGen} Gen {anc.damGen}
)}
)}
) }