Katalog (catalog.ts): - 8 'X dd'/'dd X' Einträge → 'Dilute X' (Agouti/Silberagouti/Kohlfuchs/Anthrazit/ Topas/Blaufuchs dd + dd Gold/Platin → Dilute Gold/Platin etc.) - 4 neue Dilute-Fuchs-Basiseinträge (Dilute Algierfuchs/Goldfuchs/Rotfuchs/Polarfuchs) → verhindert naked 'Fuchs' Fallback für agouti+dilute+fox Genotypen. 66→70 Einträge. - colourpointName: 'Dilute X' Base → 'Dilute CP-X' statt 'CP-Dilute X' (Präfix-Reihenfolge) → AA cchmcchm dd ee GG PP = 'Dilute CP-Algierfuchs' statt 'CP-Fuchs'. Engine (farbschlagFor): - REW-Check: c0==cchm && c1==cchm && p0==p && p1==p → 'REW' (Rotaugenweiß). Unabhängig von A/D/E/G. Flagged to god zur Bestätigung. BreedingResultView: - Gencode (bracket notation) pro Farbschlag-Karte (erster/wahrscheinlichster Genotyp). - Layout-Fix: flexbox body (name+gencode gestapelt), min-width:0 gegen Overflow, word-break, grid min 15rem, prob flex-shrink:0 rechtsbündig. Tests: GEN-4 describe (Dilute/REW/no-bare-Fuchs Fixtures); CATALOG_SIZE 66→70; CP-Fuchs/CP-Fuchs-Hell → Dilute CP-Algierfuchs(-Hell) in bestehenden Tests. Gate: build ✓ eslint ✓ vitest 95/95 ✓ e2e 148/148 ✓
111 lines
3.7 KiB
TypeScript
111 lines
3.7 KiB
TypeScript
import { useState } from 'react'
|
|
import { de } from '../strings/de'
|
|
import type { BreedingResult } from '../genetics'
|
|
import FarbschlagImage from './FarbschlagImage'
|
|
|
|
function warningText(code: string): string {
|
|
const map = de.genetics.warnings as Record<string, string>
|
|
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 (
|
|
<div className="results">
|
|
{result.warnings.length > 0 && (
|
|
<div className="warnings">
|
|
<h4>{t.warningsTitle}</h4>
|
|
{result.warnings.map((w) => (
|
|
<div key={w.code} className="alert alert--warning">
|
|
<span>{warningText(w.code)}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
<h4>{title ?? t.resultsTitle}</h4>
|
|
{result.byFarbschlag.length === 0 ? (
|
|
<p className="muted">{t.noOffspring}</p>
|
|
) : (
|
|
<>
|
|
<ul className="farbschlag-cards">
|
|
{result.byFarbschlag.map((f) => {
|
|
// Representative genotype: highest-prob offspring for this farbschlag.
|
|
const repGenotype = result.offspring.find(
|
|
(o) => o.farbschlag === f.farbschlag,
|
|
)?.genotype
|
|
return (
|
|
<li key={f.farbschlag} className="farbschlag-card">
|
|
<div className="farbschlag-card__img">
|
|
<FarbschlagImage name={f.farbschlag} />
|
|
</div>
|
|
<div className="farbschlag-card__body">
|
|
<span className="farbschlag-card__name">{f.farbschlag}</span>
|
|
{repGenotype && (
|
|
<code className="farbschlag-card__geno">{repGenotype}</code>
|
|
)}
|
|
</div>
|
|
<span className="farbschlag-card__prob">
|
|
{f.probability.percent}
|
|
<small> ({f.probability.text})</small>
|
|
</span>
|
|
</li>
|
|
)
|
|
})}
|
|
</ul>
|
|
|
|
<button
|
|
type="button"
|
|
className="link-btn"
|
|
onClick={() => setShowGenotypes((s) => !s)}
|
|
aria-expanded={showGenotypes}
|
|
>
|
|
{showGenotypes ? t.hideGenotypes : t.showGenotypes}
|
|
</button>
|
|
|
|
{showGenotypes && (
|
|
<div className="genotype-table-scroll">
|
|
<table className="genotype-table">
|
|
<thead>
|
|
<tr>
|
|
<th>{t.genotypeLabel}</th>
|
|
<th>{t.genotypePreview}</th>
|
|
<th>{t.probability}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{result.offspring.map((o) => (
|
|
<tr key={o.genotype}>
|
|
<td>
|
|
<code>{o.genotype}</code>
|
|
</td>
|
|
<td>{o.farbschlag}</td>
|
|
<td>
|
|
{o.probability.percent} <small>({o.probability.text})</small>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|