Files
GerbilManager/gerbil-manager-web/src/pages/GenetikPage.tsx

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>
)
}