import { useState, type FormEvent } from 'react' import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom' import { de } from '../strings/de' import { createGerbil, getGerbil, updateGerbil } from '../api/gerbils' import { listColorVarieties, listContacts, listEnclosures, listLitters } from '../api/lookups' import { GENDERS, GERBIL_STATUSES, type CreateGerbil, type Gender, type GerbilStatus } from '../api/types' import { useApi, useMutation } from '../hooks/useApi' import { genderLabel, statusLabel } from '../format/labels' import { fromDisplayString } from '../genetics' import FarbschlagImage from '../components/FarbschlagImage' interface FormState { name: string gender: Gender | '' status: GerbilStatus dateOfBirth: string dateOfDeath: string causeOfDeath: string goHomeDate: string colorVarietyId: string enclosureId: string litterId: string originContactId: string receiverContactId: string genotype: string notes: string isResident: boolean } const EMPTY: FormState = { name: '', gender: '', status: 'Active', dateOfBirth: '', dateOfDeath: '', causeOfDeath: '', goHomeDate: '', colorVarietyId: '', enclosureId: '', litterId: '', originContactId: '', receiverContactId: '', genotype: '', notes: '', isResident: true, } function formFromGerbil(g: { name: string gender: Gender status: GerbilStatus dateOfBirth: string | null dateOfDeath: string | null causeOfDeath: string | null goHomeDate: string | null colorVarietyId: string | null enclosureId: string | null litterId: string | null originContactId: string | null receiverContactId: string | null genotype: string | null notes: string | null isResident?: boolean | null }): FormState { return { name: g.name, gender: g.gender, status: g.status, dateOfBirth: g.dateOfBirth ?? '', dateOfDeath: g.dateOfDeath ?? '', causeOfDeath: g.causeOfDeath ?? '', goHomeDate: g.goHomeDate ?? '', colorVarietyId: g.colorVarietyId ?? '', enclosureId: g.enclosureId ?? '', litterId: g.litterId ?? '', originContactId: g.originContactId ?? '', receiverContactId: g.receiverContactId ?? '', genotype: g.genotype ?? '', notes: g.notes ?? '', isResident: g.isResident ?? true, } } /** "" -> null, else the value. */ const nn = (s: string): string | null => (s.trim() === '' ? null : s) function isGenotypeValid(genotype: string): boolean { if (genotype.trim() === '') return true try { fromDisplayString(genotype) return true } catch { return false } } export default function GerbilFormPage() { const t = de.pages.gerbils const navigate = useNavigate() const { id } = useParams() const isEdit = Boolean(id) const [form, setForm] = useState(EMPTY) const [errors, setErrors] = useState>>({}) const [initializedFor, setInitializedFor] = useState(null) const existing = useApi(() => (id ? getGerbil(id) : Promise.resolve(null)), [id]) const colorVarieties = useApi(() => listColorVarieties(), []) const enclosures = useApi(() => listEnclosures(), []) const contacts = useApi(() => listContacts(), []) const litters = useApi(() => listLitters(), []) // Prefill from the loaded gerbil once (edit mode). React's "adjust state // during render" pattern — runs without an effect and re-renders immediately. if (existing.data && initializedFor !== existing.data.id) { setInitializedFor(existing.data.id) setForm(formFromGerbil(existing.data)) } // Create-mode prefill from query params (?litterId, ?dob) — used by the Wurf // workspace's "Jungtier erfassen" action to register a pup in one tap. const [searchParams] = useSearchParams() if (!isEdit && initializedFor === null) { const litterId = searchParams.get('litterId') const dob = searchParams.get('dob') if (litterId || dob) { setInitializedFor('new') setForm((f) => ({ ...f, litterId: litterId ?? f.litterId, dateOfBirth: dob ?? f.dateOfBirth, })) } } const set = (key: K, value: FormState[K]) => setForm((f) => ({ ...f, [key]: value })) const mutation = useMutation((body: CreateGerbil) => isEdit && id ? updateGerbil(id, body) : createGerbil(body), ) function validate(): boolean { const next: Partial> = {} if (form.name.trim() === '') next.name = t.validation.nameRequired if (form.gender === '') next.gender = t.validation.genderRequired if (!isGenotypeValid(form.genotype)) next.genotype = t.validation.genotypeInvalid if (form.dateOfBirth && form.dateOfDeath && form.dateOfDeath < form.dateOfBirth) { next.dateOfDeath = t.validation.dateOfDeathBeforeBirth } setErrors(next) return Object.keys(next).length === 0 } async function onSubmit(e: FormEvent) { e.preventDefault() if (!validate()) return const body: CreateGerbil = { name: form.name.trim(), gender: form.gender as Gender, status: form.status, dateOfBirth: nn(form.dateOfBirth), dateOfDeath: nn(form.dateOfDeath), causeOfDeath: nn(form.causeOfDeath), goHomeDate: nn(form.goHomeDate), colorVarietyId: nn(form.colorVarietyId), enclosureId: nn(form.enclosureId), litterId: nn(form.litterId), originContactId: nn(form.originContactId), receiverContactId: nn(form.receiverContactId), genotype: nn(form.genotype), notes: nn(form.notes), isResident: form.isResident, } const result = await mutation.run(body) if (result.ok) navigate(`/rennmaeuse/${result.value.id}`) // On failure mutation.error drives the inline alert; run() never throws. } if (isEdit && existing.loading) return

{de.common.loading}

const selectedColorName = (colorVarieties.data ?? []).find((cv) => cv.id === form.colorVarietyId)?.name ?? null return (

{isEdit ? t.form.editTitle : t.form.createTitle}

{form.status === 'Deceased' && ( <> )} {form.status === 'GivenAway' && ( )}