import { useMemo, useState, type ReactNode } from 'react'
import { Link, useParams } from 'react-router-dom'
import { de } from '../strings/de'
import { getGerbil, updateGerbil } from '../api/gerbils'
import { listColorVarieties, listContacts, listEnclosures, listLitters } from '../api/lookups'
import { useApi, useMutation } from '../hooks/useApi'
import { formatDate, genderLabel, statusLabel } from '../format/labels'
import { fromDisplayString, genotypeToFarbschlag, toDisplayString } from '../genetics'
import FarbschlagImage from '../components/FarbschlagImage'
import Charakterbogen from '../components/Charakterbogen'
// FEAT-6 (Oscar): Tab-Inhalte + Profilfoto
import GerbilHealthTab from '../components/GerbilHealthTab'
import GerbilPhotosTab from '../components/GerbilPhotosTab'
import GerbilProfilePhoto from '../components/GerbilProfilePhoto'
import GerbilWeightTab from '../components/GerbilWeightTab'
type DetailTab = 'photos' | 'health' | 'weight'
/** Parse a stored genotype string and derive its normalized form + Farbschlag. */
function describeGenotype(genotype: string | null): { display: string; farbschlag: string } | null {
if (!genotype || !genotype.trim()) return null
try {
const g = fromDisplayString(genotype)
return { display: toDisplayString(g), farbschlag: genotypeToFarbschlag(g) }
} catch {
// Unparseable: show the raw value, no derived Farbschlag.
return { display: genotype, farbschlag: de.genetics.unknownFarbschlag }
}
}
function Row({ label, value }: { label: string; value: ReactNode }) {
return (
{label}
{value ?? '—'}
)
}
export default function GerbilDetailPage() {
const t = de.pages.gerbils
const { id = '' } = useParams()
const [tab, setTab] = useState('photos')
const gerbil = useApi(() => getGerbil(id), [id])
const forSale = useMutation(() => updateGerbil(id, { status: 'ForSale' }))
// FEAT-14: Charakterbogen (persisted on the Gerbil).
const [charTraits, setCharTraits] = useState([])
const [charNote, setCharNote] = useState('')
const [charInit, setCharInit] = useState(null)
const saveCharacter = useMutation(() =>
updateGerbil(id, { characterTraits: charTraits, characterNote: charNote.trim() || null }),
)
const colorVarieties = useApi(() => listColorVarieties(), [])
const enclosures = useApi(() => listEnclosures(), [])
const contacts = useApi(() => listContacts(), [])
const litters = useApi(() => listLitters(), [])
const colorName = useMemo(
() => new Map((colorVarieties.data ?? []).map((c) => [c.id, c.name])),
[colorVarieties.data],
)
const enclosureName = useMemo(
() => new Map((enclosures.data ?? []).map((e) => [e.id, e.name])),
[enclosures.data],
)
const contactName = useMemo(
() => new Map((contacts.data ?? []).map((c) => [c.id, c.name])),
[contacts.data],
)
const litterName = useMemo(
() => new Map((litters.data ?? []).map((l) => [l.id, l.name])),
[litters.data],
)
if (gerbil.loading) return {de.common.loading}
if (gerbil.error || !gerbil.data) {
return (
{gerbil.error ?? t.detail.notFound}
{t.detail.back}
)
}
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
// Seed the Charakterbogen once from the loaded gerbil (adjust-state-during-render).
if (charInit !== g.id) {
setCharInit(g.id)
setCharTraits(g.characterTraits ?? [])
setCharNote(g.characterNote ?? '')
}
return (
{g.name}
{statusLabel(g.status)}
{t.detail.edit}
{t.detail.testMating}
{g.status !== 'ForSale' && (
)}
{de.pages.stammbaum.openButton}
{/* FEAT-13: Abgabe abschließen — Vertrag-Assistent mit diesem Tier vorausgewählt. */}
{g.status !== 'Deceased' && g.status !== 'GivenAway' && (
{de.pages.vertraege.wizard.title}
)}
{t.detail.back}
{t.detail.masterData}
{g.status === 'Deceased' && (
<>
>
)}
{g.status === 'GivenAway' && (
)}
{storedColorName}
) : (
'—'
)
}
/>
{t.detail.genetics}
{geno ? (
{geno.display}} />
{geno.farbschlag}
{storedColorName &&
geno.farbschlag !== de.genetics.unknownFarbschlag &&
storedColorName !== geno.farbschlag && (
⚠ {t.detail.farbschlagMismatch}
)}
>
}
/>
) : (
{t.detail.genotypeNotSet}
)}
{de.character.sectionTitle}
{saveCharacter.error && {saveCharacter.error}}
{t.detail.moreData}
{(['photos', 'health', 'weight'] as const).map((key) => (
))}
{tab === 'photos' && }
{tab === 'health' && }
{tab === 'weight' && }
)
}