/** * FEAT-2: Kontakt-Detailseite — Stammdaten + verknüpfte Tiere * (Herkunft von / Abnehmer von, mit Farbschlag + Link zur Rennmaus), * Löschen mit Bestätigung; Referenz-Konflikt 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 { deleteContact, getContact } from '../api/contacts' import { listGerbils } from '../api/gerbils' import { listColorVarieties } from '../api/lookups' import { condition } from '../api/gridify' import { useApi, useMutation } from '../hooks/useApi' export default function KontaktDetailPage() { const t = de.pages.kontakte const { id = '' } = useParams() const navigate = useNavigate() const [deleteError, setDeleteError] = useState(null) const contact = useApi(() => getContact(id), [id]) // Verknüpfte Tiere: Kontakt ist Herkunft ODER Abnehmer (Gridify-OR via |). const linked = useApi( () => listGerbils({ filter: [ condition({ field: 'originContactId', op: '==', value: id }), condition({ field: 'receiverContactId', op: '==', value: id }), ].join('|'), 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(() => deleteContact(id)) async function onDelete() { if (!window.confirm(t.delete.confirmMessage)) return setDeleteError(null) const result = await removal.run() if (result.ok) { navigate('/kontakte') } else if (result.cause instanceof ApiError && result.cause.status === 409) { // Backend meldet Konflikt, wenn der Kontakt noch referenziert wird. setDeleteError(t.delete.conflict) } else { setDeleteError(result.error) } } if (contact.loading) return

{de.common.loading}

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

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

{t.detail.back}
) } const c = contact.data const animals = linked.data?.items ?? [] return (

{c.name}

{t.detail.edit} {t.detail.back}
{deleteError &&
{deleteError}
}
Rolle
{[ c.isBreeder ? 'Züchter' : null, c.isReceiver ? 'Abnehmer' : null, ] .filter(Boolean) .join(' · ') || '—'}
{t.fields.email}
{c.email ?? '—'}
{t.fields.phone}
{c.phone ?? '—'}
{t.fields.address}
{c.address ?? '—'}
{t.fields.notes}
{c.notes ?? '—'}

{t.linked.title}

{linked.loading &&

{de.common.loading}

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

{t.linked.empty}

)} {animals.length > 0 && ( )}
) }