/** * FEAT-2: Becken-Detailseite — Stammdaten, BELEGUNG (welche Tiere wohnen * aktuell hier, mit Farbschlag + Link zur Rennmaus), Löschen mit * Bestätigung; Konflikt (Becken nicht leer) wird deutsch gemeldet. */ import { useMemo, useState } from 'react' import { Link, useNavigate, useParams } from 'react-router-dom' import { de } from '../strings/de' import { ApiError } from '../api/client' import { deleteEnclosure, getEnclosure, markEnclosureCleaned } from '../api/enclosures' import { listGerbils } from '../api/gerbils' import { listColorVarieties } from '../api/lookups' import { condition } from '../api/gridify' import { useApi, useMutation } from '../hooks/useApi' import { formatDate } from '../format/labels' import { useToast } from '../components/toast' import EnclosurePhotosSection from '../components/EnclosurePhotosSection' /** true, wenn die nächste fällige Reinigung am/vor heute liegt. */ function isCleaningDue(nextCleaningDate: string | null): boolean { if (!nextCleaningDate) return false const today = new Date().toISOString().slice(0, 10) return nextCleaningDate <= today } export default function BeckenDetailPage() { const t = de.pages.becken const { id = '' } = useParams() const navigate = useNavigate() const toast = useToast() const [deleteError, setDeleteError] = useState(null) const enclosure = useApi(() => getEnclosure(id), [id]) // Belegung: alle Tiere, deren aktuelles Becken dieses ist. const occupants = useApi( () => listGerbils({ filter: condition({ field: 'enclosureId', op: '==', value: id }), orderBy: 'name', page: 1, pageSize: 500, }), [id], ) const colorVarieties = useApi(() => listColorVarieties(), []) const colorNameById = useMemo( () => new Map((colorVarieties.data ?? []).map((c) => [c.id, c.name])), [colorVarieties.data], ) const removal = useMutation(() => deleteEnclosure(id)) const cleaning = useMutation(() => markEnclosureCleaned(id)) async function onMarkCleaned() { const result = await cleaning.run() if (result.ok) { toast.success(t.cleaning.marked) enclosure.reload() } else { toast.error(result.error) } } async function onDelete() { if (!window.confirm(t.delete.confirmMessage)) return setDeleteError(null) const result = await removal.run() if (result.ok) { navigate('/gehege') } else if (result.cause instanceof ApiError && result.cause.status === 409) { // Backend meldet Konflikt, wenn noch Tiere im Becken wohnen. setDeleteError(t.delete.conflict) } else { setDeleteError(result.error) } } if (enclosure.loading) return

{de.common.loading}

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

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

{t.detail.back}
) } const e = enclosure.data const animals = occupants.data?.items ?? [] const count = occupants.data?.totalCount ?? 0 return (

{e.name}

{occupants.data && (

{count} {count === 1 ? t.occupancy.countOne : t.occupancy.countMany}

)}
{t.detail.edit} {t.detail.back}
{deleteError &&
{deleteError}
} {(e.notes || e.size || e.capacity != null) && (
{e.notes && (
{t.fields.notes}
{e.notes}
)} {e.size && (
{t.fields.size}
{e.size}
)} {e.capacity != null && (
{t.fields.capacity}
{t.cleaning.capacityUnit(e.capacity)}
)}
)}

{t.cleaning.title}

{isCleaningDue(e.nextCleaningDate) && (
{e.nextCleaningDate ? t.cleaning.dueSince(formatDate(e.nextCleaningDate)) : t.cleaning.due}
)}
{t.fields.lastCleanedDate}
{e.lastCleanedDate ? formatDate(e.lastCleanedDate) : t.cleaning.neverCleaned}
{t.fields.cleaningCycleDays}
{e.cleaningCycleDays != null ? t.cleaning.cycleUnit(e.cleaningCycleDays) : t.cleaning.noCycle}
{e.nextCleaningDate && (
{t.fields.nextCleaningDate}
{formatDate(e.nextCleaningDate)}
)}

{t.photosTitle}

{t.occupancy.title}

{occupants.loading &&

{de.common.loading}

} {occupants.error && (
{occupants.error}
)} {!occupants.loading && !occupants.error && animals.length === 0 && (

{t.occupancy.empty}

)} {animals.length > 0 && (
    {animals.map((g) => (
  • {g.name} {g.colorVarietyId ? (colorNameById.get(g.colorVarietyId) ?? '—') : '—'}
  • ))}
)}
) }