FEAT-4: sync inbreeding DTO to FEAT-1b InbreedingResult + anchor root card left so parents are visible on phone at load
Verified end-to-end against a DATA-2-contract mock (5-gen family, shared ancestor): phone/desktop, re-root, lazy expand, zoom/fit, print Ahnentafel, 404 state. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,20 +3,25 @@
|
|||||||
*
|
*
|
||||||
* Der Inzuchtkoeffizient wird serverseitig über die VOLLE Ahnentiefe berechnet
|
* Der Inzuchtkoeffizient wird serverseitig über die VOLLE Ahnentiefe berechnet
|
||||||
* (Board-Entscheidung; UI zeigt 4 Generationen, Koeffizient zählt alle).
|
* (Board-Entscheidung; UI zeigt 4 Generationen, Koeffizient zählt alle).
|
||||||
* Endpunkt entsteht in DATA-2-Folgearbeit — bis dahin antwortet er mit 404 und
|
* Endpunkt: FEAT-1b (InbreedingController, Wright-Pfadmethode).
|
||||||
* die UI zeigt einen „wird berechnet …“-Zustand.
|
|
||||||
*/
|
*/
|
||||||
import { api, resources } from './client'
|
import { api, resources } from './client'
|
||||||
|
|
||||||
export interface InbreedingCoefficient {
|
/** Spiegelt GerbilManagerWebAPI.Genetics.InbreedingResult. */
|
||||||
/** Koeffizient nach Wright, 0..1. */
|
export interface InbreedingResult {
|
||||||
|
/** Koeffizient F nach Wright, 0..1. */
|
||||||
coefficient: number
|
coefficient: number
|
||||||
|
/** F in Prozent (Server-Komfortfeld). */
|
||||||
|
percent: number
|
||||||
|
/** Tiefste bekannte Ahnen-Generation (Eltern = 1; 0 = keine erfasst). */
|
||||||
|
generationsAvailable: number
|
||||||
|
commonAncestors: { id: string; name: string; contribution: number }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getInbreedingCoefficient(gerbilId: string): Promise<number> {
|
export async function getInbreedingCoefficient(gerbilId: string): Promise<number> {
|
||||||
const raw = await api.get<InbreedingCoefficient | number>(
|
const raw = await api.get<InbreedingResult | number>(
|
||||||
`${resources.gerbils}/${gerbilId}/inbreeding-coefficient`,
|
`${resources.gerbils}/${gerbilId}/inbreeding-coefficient`,
|
||||||
)
|
)
|
||||||
// Defensiv: Endpunkt-Form ist noch nicht final (nacktes number vs. Objekt).
|
// Defensiv gegen Vertragsdrift (nacktes number vs. Objekt).
|
||||||
return typeof raw === 'number' ? raw : raw.coefficient
|
return typeof raw === 'number' ? raw : raw.coefficient
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,13 +174,14 @@ export default function StammbaumPage() {
|
|||||||
return () => observer.disconnect()
|
return () => observer.disconnect()
|
||||||
}, [hasRoot])
|
}, [hasRoot])
|
||||||
|
|
||||||
/* ── Ansicht (Zoom/Position): Wurzelkarte beim Laden zentriert ── */
|
/* ── Ansicht (Zoom/Position): Wurzelkarte links, vertikal zentriert —
|
||||||
|
so sind die Eltern auch auf dem Smartphone sofort sichtbar. ── */
|
||||||
const defaultView = useMemo<View | null>(() => {
|
const defaultView = useMemo<View | null>(() => {
|
||||||
if (!size) return null
|
if (!size) return null
|
||||||
const zoom = size.w < 520 ? 0.8 : 1
|
const zoom = size.w < 520 ? 0.8 : 1
|
||||||
return {
|
return {
|
||||||
zoom,
|
zoom,
|
||||||
translate: { x: Math.min(size.w / 2, CARD_W * zoom + 48), y: size.h / 2 },
|
translate: { x: (CARD_W / 2) * zoom + 24, y: size.h / 2 },
|
||||||
}
|
}
|
||||||
}, [size])
|
}, [size])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user