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(() => { // include_external=1 ensures external sires/dams appear for pairing fetch('/api/dogs?include_external=1') .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) } async function handleSimulate(e) { e.preventDefault() if (!sireId || !damId) return setLoading(true) setError(null) setResult(null) try { const res = await fetch('/api/pedigree/coi', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sire_id: parseInt(sireId), dam_id: parseInt(damId) }), }) const data = await res.json() if (!res.ok) throw new Error(data.error || 'Simulation failed') setResult(data) } catch (err) { setError(err.message) } finally { setLoading(false) } } const males = dogs.filter(d => d.sex === 'male') const females = dogs.filter(d => d.sex === 'female') const coiColor = (coi) => { if (coi < 0.0625) return 'var(--success)' if (coi < 0.125) return 'var(--warning)' return 'var(--danger)' } const coiLabel = (coi) => { if (coi < 0.0625) return 'Low' if (coi < 0.125) return 'Moderate' if (coi < 0.25) return 'High' return 'Very High' } return (

Pairing Simulator

Estimate the Coefficient of Inbreeding (COI) for a hypothetical pairing before breeding. Includes both kennel and external dogs.

{dogsLoading ? (
Loading dogs...
) : ( )}
{dogsLoading ? (
Loading dogs...
) : ( )}
{relationChecking && (
Checking relationship...
)} {relationWarning && !relationChecking && (
Related: {relationWarning}
)}
{error && (
Error: {error}
)} {result && (

Simulation Result

{result.coi < 0.0625 ? : }
{(result.coi * 100).toFixed(2)}%
COI — {coiLabel(result.coi)}
{result.common_ancestors && result.common_ancestors.length > 0 && (

Common Ancestors ({result.common_ancestors.length})

{result.common_ancestors.map((a, i) => ( {a} ))}
)} {result.recommendation && (
{result.recommendation}
)}
)}
) }