(null)
const enclosure = useApi(() => getEnclosure(id), [id])
@@ -39,6 +49,17 @@ export default function BeckenDetailPage() {
)
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
@@ -101,15 +122,62 @@ export default function BeckenDetailPage() {
{deleteError && {deleteError}
}
- {e.notes && (
+ {(e.notes || e.size || e.capacity != null) && (
-
-
- {t.fields.notes}
- - {e.notes}
-
+ {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}
diff --git a/gerbil-manager-web/src/pages/BeckenFormPage.tsx b/gerbil-manager-web/src/pages/BeckenFormPage.tsx
index ea5e0a2..81b4ccf 100644
--- a/gerbil-manager-web/src/pages/BeckenFormPage.tsx
+++ b/gerbil-manager-web/src/pages/BeckenFormPage.tsx
@@ -9,13 +9,32 @@ import { useToast } from '../components/toast'
interface FormState {
name: string
notes: string
+ size: string
+ capacity: string
+ lastCleanedDate: string
+ cleaningCycleDays: string
}
-const EMPTY: FormState = { name: '', notes: '' }
+const EMPTY: FormState = {
+ name: '',
+ notes: '',
+ size: '',
+ capacity: '',
+ lastCleanedDate: '',
+ cleaningCycleDays: '',
+}
/** "" -> null, sonst der Wert. */
const nn = (s: string): string | null => (s.trim() === '' ? null : s)
+/** "" -> null, sonst die geparste Ganzzahl (NaN -> null). */
+const ni = (s: string): number | null => {
+ const t = s.trim()
+ if (t === '') return null
+ const n = Number.parseInt(t, 10)
+ return Number.isNaN(n) ? null : n
+}
+
export default function BeckenFormPage() {
const t = de.pages.becken
const navigate = useNavigate()
@@ -32,7 +51,14 @@ export default function BeckenFormPage() {
// Vorbefüllen im Bearbeiten-Modus (adjust-state-during-render, wie FEAT-1).
if (existing.data && initializedFor !== existing.data.id) {
setInitializedFor(existing.data.id)
- setForm({ name: existing.data.name, notes: existing.data.notes ?? '' })
+ setForm({
+ name: existing.data.name,
+ notes: existing.data.notes ?? '',
+ size: existing.data.size ?? '',
+ capacity: existing.data.capacity?.toString() ?? '',
+ lastCleanedDate: existing.data.lastCleanedDate ?? '',
+ cleaningCycleDays: existing.data.cleaningCycleDays?.toString() ?? '',
+ })
}
const mutation = useMutation((body: CreateEnclosure) =>
@@ -46,7 +72,14 @@ export default function BeckenFormPage() {
return
}
setErrors({})
- const result = await mutation.run({ name: form.name.trim(), notes: nn(form.notes) })
+ const result = await mutation.run({
+ name: form.name.trim(),
+ notes: nn(form.notes),
+ size: nn(form.size),
+ capacity: ni(form.capacity),
+ lastCleanedDate: nn(form.lastCleanedDate),
+ cleaningCycleDays: ni(form.cleaningCycleDays),
+ })
if (result.ok) {
toast.success(de.common.saved)
navigate(`/gehege/${result.value.id}`)
@@ -82,6 +115,48 @@ export default function BeckenFormPage() {
/>
+
+
+
+
+
+
+
+
{mutation.error && {mutation.error}
}
diff --git a/gerbil-manager-web/src/strings/de.ts b/gerbil-manager-web/src/strings/de.ts
index 361b691..e75ee4e 100644
--- a/gerbil-manager-web/src/strings/de.ts
+++ b/gerbil-manager-web/src/strings/de.ts
@@ -372,6 +372,26 @@ export const de = {
fields: {
name: 'Name',
notes: 'Notizen',
+ // Reinigungszyklus (RennmausPro becken_tb).
+ size: 'Maße',
+ capacity: 'Empf. Tieranzahl',
+ lastCleanedDate: 'Zuletzt gereinigt',
+ cleaningCycleDays: 'Reinigungszyklus (Tage)',
+ nextCleaningDate: 'Nächste Reinigung',
+ },
+ // Reinigungszyklus: Hinweise + Aktion auf der Detailseite.
+ cleaning: {
+ title: 'Reinigung',
+ due: 'Reinigung fällig',
+ dueSince: (date: string) => `Reinigung fällig (seit ${date})`,
+ nextOn: (date: string) => `Nächste Reinigung am ${date}`,
+ noCycle: 'Kein Reinigungszyklus hinterlegt.',
+ neverCleaned: 'Noch nie als gereinigt vermerkt.',
+ markCleaned: 'Als gereinigt markieren',
+ marking: 'Wird gespeichert …',
+ marked: 'Reinigung vermerkt.',
+ capacityUnit: (n: number) => `${n} ${n === 1 ? 'Tier' : 'Tiere'}`,
+ cycleUnit: (n: number) => `alle ${n} Tage`,
},
// Bilder-Sektion auf der Gehege-Detailseite.
photosTitle: 'Bilder',