import { useCallback, useMemo, useState } from 'react' import { Link, useParams } from 'react-router-dom' import { de } from '../strings/de' import { getLitter } from '../api/litters' import { getGerbil, listGerbils } from '../api/gerbils' import { listColorVarieties, listContacts } from '../api/lookups' import { API_BASE_URL } from '../api/client' import { condition } from '../api/gridify' import { type Gerbil } from '../api/types' import { useApi } from '../hooks/useApi' import { formatDate, genderLabel } from '../format/labels' import { isValidGenotype } from '../format/genotypeText' import { breed, fromDisplayString, genotypeToFarbschlag, UNKNOWN_FARBSCHLAG, type BreedingResult } from '../genetics' import BreedingResultView from '../components/BreedingResultView' import GerbilIcon from '../components/GerbilIcon' import './wuerfe.css' type JuvenileFields = (typeof de.pages.litters.detail.juvenileFields) function JuvenileCard({ g, farbe, receiver, tJf }: { g: Gerbil farbe: string | null receiver: string | null tJf: JuvenileFields }) { const [failedUrl, setFailedUrl] = useState(null) const rawUrl = g.profilePhotoUrl ? /^(https?:|data:|\/\/)/.test(g.profilePhotoUrl) ? g.profilePhotoUrl : `${API_BASE_URL}${g.profilePhotoUrl}` : null const photoUrl = rawUrl !== failedUrl ? rawUrl : null return (
{photoUrl ? ( {g.name setFailedUrl(rawUrl)} /> ) : (
{g.name} {genderLabel(g.gender)} {farbe && {tJf.colorVariety}: {farbe}} {g.goHomeDate && {tJf.goHomeDate}: {formatDate(g.goHomeDate)}} {receiver && {tJf.receiver}: {receiver}} {g.dateOfDeath && {tJf.dateOfDeath}: {formatDate(g.dateOfDeath)}} {g.causeOfDeath && {tJf.causeOfDeath}: {g.causeOfDeath}}
) } export default function WurfDetailPage() { const t = de.pages.litters const { id = '' } = useParams() const litter = useApi(() => getLitter(id), [id]) const fatherId = litter.data?.fatherId ?? null const motherId = litter.data?.motherId ?? null const father = useApi(() => (fatherId ? getGerbil(fatherId) : Promise.resolve(null)), [fatherId]) const mother = useApi(() => (motherId ? getGerbil(motherId) : Promise.resolve(null)), [motherId]) // Juveniles = gerbils whose litterId is this litter. const juveniles = useApi( () => id ? listGerbils({ filter: condition({ field: 'litterId', op: '==', value: id }), orderBy: 'name', page: 1, pageSize: 1000, }) : Promise.resolve(null), [id], ) // Expected Farbschlag distribution for this pairing (client-side GEN-1). const expected: BreedingResult | null = useMemo(() => { const fg = father.data?.genotype const mg = mother.data?.genotype if (!fg || !mg || !isValidGenotype(fg) || !isValidGenotype(mg)) return null return breed(fromDisplayString(fg), fromDisplayString(mg)) }, [father.data, mother.data]) // Farbschlag + Abnehmer lookups for the juvenile list. const colorVarieties = useApi(() => listColorVarieties(), []) const contacts = useApi(() => listContacts(), []) const colorNameById = useMemo( () => new Map((colorVarieties.data ?? []).map((c) => [c.id, c.name])), [colorVarieties.data], ) const contactNameById = useMemo( () => new Map((contacts.data ?? []).map((c) => [c.id, c.name])), [contacts.data], ) const farbschlagOf = useCallback( (g: { colorVarietyId: string | null; genotype: string | null }): string | null => { if (g.colorVarietyId) { const name = colorNameById.get(g.colorVarietyId) if (name) return name } if (g.genotype?.trim()) { try { const name = genotypeToFarbschlag(fromDisplayString(g.genotype)) return name === UNKNOWN_FARBSCHLAG ? null : name } catch { return null } } return null }, [colorNameById], ) if (litter.loading) return

{de.common.loading}

if (litter.error || !litter.data) { return (

{litter.error ?? t.detail.notFound}

{t.detail.back}
) } const l = litter.data const registered = juveniles.data?.totalCount ?? 0 const parentLink = (id: string | null, g: { name: string } | null) => id && g ? {g.name} : t.detail.unknownParent return (

{l.name}

{t.detail.edit} {t.detail.back}
{t.fields.date}
{formatDate(l.date)}
{t.detail.parents}
{parentLink(fatherId, father.data)} × {parentLink(motherId, mother.data)}
{t.detail.registered}
{registered} {l.totalBorn != null ? ` / ${l.totalBorn}` : ''}
{l.deathsWithin8Weeks != null && (
{t.fields.deathsWithin8Weeks}
{l.deathsWithin8Weeks}
)}
{t.fields.expectedGoHomeDate}
{formatDate(l.expectedGoHomeDate)}
{l.notes && (
{t.fields.notes}
{l.notes}
)}

{t.detail.juveniles}

+ {t.detail.registerJuvenile}
{juveniles.loading &&

{de.common.loading}

} {!juveniles.loading && registered === 0 &&

{t.detail.noJuveniles}

} {registered > 0 && ( )} {expected ? ( ) : (

{t.detail.needParentsGenotype}

)}
) }