94 lines
3.6 KiB
TypeScript
94 lines
3.6 KiB
TypeScript
import { useMemo, useState } from 'react'
|
|
import { useSearchParams } from 'react-router-dom'
|
|
import { de } from '../strings/de'
|
|
import { breed, fromDisplayString, type BreedingResult } from '../genetics'
|
|
import { getGerbil } from '../api/gerbils'
|
|
import { useApi } from '../hooks/useApi'
|
|
import ParentSelector, { type ParentValue } from '../components/ParentSelector'
|
|
import AnimalPicker from '../components/AnimalPicker'
|
|
import BreedingResultView from '../components/BreedingResultView'
|
|
import { isValidGenotype } from '../format/genotypeText'
|
|
|
|
const EMPTY_PARENT: ParentValue = { genotype: '', animalName: null }
|
|
|
|
export default function GenetikPage() {
|
|
const t = de.pages.genetik
|
|
const [father, setFather] = useState<ParentValue>(EMPTY_PARENT)
|
|
const [mother, setMother] = useState<ParentValue>(EMPTY_PARENT)
|
|
|
|
// Pre-fill parents from query params:
|
|
// ?parent=<id> single animal from its detail page (gender picks side)
|
|
// ?vater=<id>&mutter=<id> a whole pair, e.g. from the Zuchtpaar overview
|
|
const [searchParams] = useSearchParams()
|
|
const parentId = searchParams.get('parent')
|
|
const vaterId = searchParams.get('vater')
|
|
const mutterId = searchParams.get('mutter')
|
|
|
|
const prefill = useApi(() => (parentId ? getGerbil(parentId) : Promise.resolve(null)), [parentId])
|
|
const prefillVater = useApi(() => (vaterId ? getGerbil(vaterId) : Promise.resolve(null)), [vaterId])
|
|
const prefillMutter = useApi(() => (mutterId ? getGerbil(mutterId) : Promise.resolve(null)), [mutterId])
|
|
|
|
const [prefilledKey, setPrefilledKey] = useState<string | null>(null)
|
|
const prefillKey = `${parentId ?? ''}|${vaterId ?? ''}|${mutterId ?? ''}`
|
|
const allReady =
|
|
(!parentId || prefill.data) && (!vaterId || prefillVater.data) && (!mutterId || prefillMutter.data)
|
|
if (prefillKey !== '||' && allReady && prefilledKey !== prefillKey) {
|
|
setPrefilledKey(prefillKey)
|
|
const toValue = (g: { genotype: string | null; name: string }): ParentValue => ({
|
|
genotype: g.genotype ?? '',
|
|
animalName: g.name,
|
|
})
|
|
if (prefillVater.data) setFather(toValue(prefillVater.data))
|
|
if (prefillMutter.data) setMother(toValue(prefillMutter.data))
|
|
if (prefill.data) {
|
|
if (prefill.data.gender === 'female') setMother(toValue(prefill.data))
|
|
else setFather(toValue(prefill.data))
|
|
}
|
|
}
|
|
|
|
const bothValid = isValidGenotype(father.genotype) && isValidGenotype(mother.genotype)
|
|
|
|
const result: BreedingResult | null = useMemo(() => {
|
|
if (!bothValid) return null
|
|
// breed() is pure & client-side; ParentSelector already validated both, so
|
|
// fromDisplayString won't throw.
|
|
return breed(fromDisplayString(father.genotype), fromDisplayString(mother.genotype))
|
|
}, [bothValid, father.genotype, mother.genotype])
|
|
|
|
return (
|
|
<section className="page">
|
|
<h2>{t.title}</h2>
|
|
<p className="muted">{t.subtitle}</p>
|
|
|
|
<div className="parents-grid">
|
|
<ParentSelector
|
|
label={t.father}
|
|
value={father}
|
|
onChange={setFather}
|
|
picker={
|
|
<AnimalPicker
|
|
gender="male"
|
|
onPick={(g) => setFather({ genotype: g.genotype ?? '', animalName: g.name })}
|
|
/>
|
|
}
|
|
/>
|
|
<ParentSelector
|
|
label={t.mother}
|
|
value={mother}
|
|
onChange={setMother}
|
|
picker={
|
|
<AnimalPicker
|
|
gender="female"
|
|
onPick={(g) => setMother({ genotype: g.genotype ?? '', animalName: g.name })}
|
|
/>
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
{!bothValid && <p className="muted">{t.needBoth}</p>}
|
|
|
|
{result && <BreedingResultView result={result} />}
|
|
</section>
|
|
)
|
|
}
|