diff --git a/gerbil-manager-web/src/components/BreedingResultView.tsx b/gerbil-manager-web/src/components/BreedingResultView.tsx new file mode 100644 index 0000000..d054751 --- /dev/null +++ b/gerbil-manager-web/src/components/BreedingResultView.tsx @@ -0,0 +1,93 @@ +import { useState } from 'react' +import { de } from '../strings/de' +import type { BreedingResult } from '../genetics' + +function warningText(code: string): string { + const map = de.genetics.warnings as Record + return map[code] ?? code +} + +export interface BreedingResultViewProps { + result: BreedingResult + /** Heading shown above the Farbschlag cards. Defaults to "Mögliche Nachkommen". */ + title?: string +} + +/** + * Renders a GEN-1 breeding result: warnings (SCHECKE_LETHAL prominent), + * offspring grouped byFarbschlag (percent + exact fraction), and a collapsible + * per-genotype table. Shared by the Genetik page (FEAT-5) and the Wurf + * workspace (FEAT-3) where it shows the expected distribution for a pairing. + */ +export default function BreedingResultView({ result, title }: BreedingResultViewProps) { + const t = de.pages.genetik + const [showGenotypes, setShowGenotypes] = useState(false) + + return ( +
+ {result.warnings.length > 0 && ( +
+

{t.warningsTitle}

+ {result.warnings.map((w) => ( +
+ {warningText(w.code)} +
+ ))} +
+ )} + +

{title ?? t.resultsTitle}

+ {result.byFarbschlag.length === 0 ? ( +

{t.noOffspring}

+ ) : ( + <> + + + + + {showGenotypes && ( + + + + + + + + + + {result.offspring.map((o) => ( + + + + + + ))} + +
{t.genotypeLabel}{t.genotypePreview}{t.probability}
+ {o.genotype} + {o.farbschlag} + {o.probability.percent} ({o.probability.text}) +
+ )} + + )} +
+ ) +} diff --git a/gerbil-manager-web/src/pages/GenetikPage.tsx b/gerbil-manager-web/src/pages/GenetikPage.tsx index d1f3440..a23d7bb 100644 --- a/gerbil-manager-web/src/pages/GenetikPage.tsx +++ b/gerbil-manager-web/src/pages/GenetikPage.tsx @@ -6,20 +6,15 @@ 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 } -function warningText(code: string): string { - const map = de.genetics.warnings as Record - return map[code] ?? code -} - export default function GenetikPage() { const t = de.pages.genetik const [father, setFather] = useState(EMPTY_PARENT) const [mother, setMother] = useState(EMPTY_PARENT) - const [showGenotypes, setShowGenotypes] = useState(false) // Pre-fill a parent when arriving from an animal's detail page (?parent=). // Gender decides the side: female -> Mutter, otherwise Vater. @@ -78,74 +73,7 @@ export default function GenetikPage() { {!bothValid &&

{t.needBoth}

} - {result && ( -
- {result.warnings.length > 0 && ( -
-

{t.warningsTitle}

- {result.warnings.map((w) => ( -
- {warningText(w.code)} -
- ))} -
- )} - -

{t.resultsTitle}

- {result.byFarbschlag.length === 0 ? ( -

{t.noOffspring}

- ) : ( - <> -

{t.byFarbschlag}

-
    - {result.byFarbschlag.map((f) => ( -
  • - {f.farbschlag} - - {f.probability.percent} - ({f.probability.text}) - -
  • - ))} -
- - - - {showGenotypes && ( - - - - - - - - - - {result.offspring.map((o) => ( - - - - - - ))} - -
{t.genotypeLabel}{t.genotypePreview}{t.probability}
- {o.genotype} - {o.farbschlag} - {o.probability.percent} ({o.probability.text}) -
- )} - - )} -
- )} + {result && } ) }