diff --git a/gerbil-manager-web/src/index.css b/gerbil-manager-web/src/index.css index 2ad00a7..2e244f8 100644 --- a/gerbil-manager-web/src/index.css +++ b/gerbil-manager-web/src/index.css @@ -597,3 +597,8 @@ textarea { .animal-picker__hint { padding: 0.5rem 0.75rem; } + +.mismatch-hint { + color: #7a5a14; + margin-left: 0.4rem; +} diff --git a/gerbil-manager-web/src/pages/GenetikPage.tsx b/gerbil-manager-web/src/pages/GenetikPage.tsx index 2111dc5..d1f3440 100644 --- a/gerbil-manager-web/src/pages/GenetikPage.tsx +++ b/gerbil-manager-web/src/pages/GenetikPage.tsx @@ -1,6 +1,9 @@ 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 { isValidGenotype } from '../format/genotypeText' @@ -18,6 +21,22 @@ export default function GenetikPage() { 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. + const [searchParams] = useSearchParams() + const parentId = searchParams.get('parent') + const prefill = useApi( + () => (parentId ? getGerbil(parentId) : Promise.resolve(null)), + [parentId], + ) + const [prefilledFor, setPrefilledFor] = useState(null) + if (prefill.data && prefilledFor !== prefill.data.id) { + setPrefilledFor(prefill.data.id) + const value: ParentValue = { genotype: prefill.data.genotype ?? '', animalName: prefill.data.name } + if (prefill.data.gender === 'female') setMother(value) + else setFather(value) + } + const bothValid = isValidGenotype(father.genotype) && isValidGenotype(mother.genotype) const result: BreedingResult | null = useMemo(() => { diff --git a/gerbil-manager-web/src/pages/GerbilDetailPage.tsx b/gerbil-manager-web/src/pages/GerbilDetailPage.tsx index 262449f..8c76632 100644 --- a/gerbil-manager-web/src/pages/GerbilDetailPage.tsx +++ b/gerbil-manager-web/src/pages/GerbilDetailPage.tsx @@ -73,6 +73,7 @@ export default function GerbilDetailPage() { const g = gerbil.data const geno = describeGenotype(g.genotype) const lookup = (map: Map, key: string | null) => (key ? (map.get(key) ?? '—') : '—') + const storedColorName = g.colorVarietyId ? (colorName.get(g.colorVarietyId) ?? null) : null return (
@@ -85,6 +86,9 @@ export default function GerbilDetailPage() { {t.detail.edit} + + {t.detail.testMating} + {t.detail.back} @@ -117,7 +121,19 @@ export default function GerbilDetailPage() { {geno ? (
{geno.display}} /> - + + {geno.farbschlag} + {storedColorName && + geno.farbschlag !== de.genetics.unknownFarbschlag && + storedColorName !== geno.farbschlag && ( + ⚠ {t.detail.farbschlagMismatch} + )} + + } + />
) : (

{t.detail.genotypeNotSet}

diff --git a/gerbil-manager-web/src/strings/de.ts b/gerbil-manager-web/src/strings/de.ts index f109e2f..82457ae 100644 --- a/gerbil-manager-web/src/strings/de.ts +++ b/gerbil-manager-web/src/strings/de.ts @@ -73,7 +73,10 @@ export const de = { masterData: 'Stammdaten', genetics: 'Genetik', resolvedFarbschlag: 'Errechneter Farbschlag', + resolvedPrefix: 'Errechnet', + farbschlagMismatch: 'Weicht vom eingetragenen Farbschlag ab.', genotypeNotSet: 'Kein Genotyp hinterlegt.', + testMating: 'Probeverpaarung', edit: 'Bearbeiten', back: 'Zurück zur Liste', tabs: { @@ -192,6 +195,34 @@ export const de = { 'Becken ist nicht leer — bitte zuerst alle Tiere in ein anderes Becken umsetzen.', }, }, + // ── FEAT-4 (Kelly): Stammbaum (Ahnentafel) ── + stammbaum: { + title: 'Stammbaum', + /** Überschrift mit Tiername. */ + titleFor: (name: string) => `Stammbaum von ${name}`, + /** Button auf der Tier-Detailseite (Integration durch FEAT-1). */ + openButton: 'Stammbaum', + backToAnimal: 'Zur Tierakte', + notFound: 'Diese Rennmaus wurde nicht gefunden.', + unknown: 'unbekannt', + /** Auf Karten am Rand: weitere Vorfahren nachladen. */ + expand: 'Vorfahren laden', + tapHint: 'Tippe auf ein Tier, um dessen Stammbaum anzuzeigen.', + zoomIn: 'Vergrößern', + zoomOut: 'Verkleinern', + zoomFit: 'Ansicht einpassen', + print: 'Drucken / PDF', + inbreeding: { + label: 'Inzuchtkoeffizient', + unavailable: 'wird berechnet …', + }, + printView: { + /** Spaltenüberschriften der Ahnentafel, Index = Generation. */ + generations: ['Tier', 'Eltern', 'Großeltern', 'Urgroßeltern', 'Ur-Urgroßeltern'], + createdOn: 'Erstellt am', + born: 'geb.', + }, + }, // ── FEAT-2 (Oscar): Kontakte (Contacts — Herkunft/Abnehmer) ── kontakte: { title: 'Kontakte',