diff --git a/gerbil-manager-web/e2e/litter-mortality.spec.ts b/gerbil-manager-web/e2e/litter-mortality.spec.ts new file mode 100644 index 0000000..bc9d211 --- /dev/null +++ b/gerbil-manager-web/e2e/litter-mortality.spec.ts @@ -0,0 +1,46 @@ +/** LITTER-MORTALITY: deathsWithin8Weeks im Wurf-Formular und -Detail. */ +import { de, expect, skipUnlessMock, test, uniqueName } from './fixtures' + +const t = de.pages.litters + +test('Wurf-Detail zeigt Frühverluste wenn gesetzt', async ({ page }) => { + skipUnlessMock() + await page.goto('/wuerfe/w-kruemel') + await expect(page.getByRole('heading', { name: 'Wurf K' })).toBeVisible() + // Frühverluste-Zeile im def-list: Label + Wert '1' + const mortalityRow = page.locator('.def-row', { + has: page.locator('dt', { hasText: t.fields.deathsWithin8Weeks }), + }) + await expect(mortalityRow).toBeVisible() + await expect(mortalityRow.locator('dd')).toHaveText('1') +}) + +test('Wurf-Formular speichert Frühverluste und zeigt sie im Detail', async ({ page }) => { + skipUnlessMock() + const name = uniqueName('WurfM') + await page.goto('/wuerfe/neu') + await page.getByLabel(`${t.fields.name} *`).fill(name) + await page.getByLabel(`${t.fields.date} *`).fill('2026-01-10') + + // Frühverluste eingeben + await page.getByLabel(t.fields.deathsWithin8Weeks).fill('2') + await expect(page.getByText(t.validation.deathsExceedTotalBorn)).not.toBeVisible() + + await page.getByRole('button', { name: t.form.save, exact: true }).click() + await expect(page.getByRole('heading', { name })).toBeVisible() + // Frühverluste-Zeile im Detail + const mortalityRow = page.locator('.def-row', { + has: page.locator('dt', { hasText: t.fields.deathsWithin8Weeks }), + }) + await expect(mortalityRow).toBeVisible() + await expect(mortalityRow.locator('dd')).toHaveText('2') +}) + +test('Frühverluste > Wurfstärke zeigt Warnmeldung', async ({ page }) => { + skipUnlessMock() + await page.goto('/wuerfe/neu') + + await page.getByLabel(t.fields.totalBorn).fill('3') + await page.getByLabel(t.fields.deathsWithin8Weeks).fill('5') + await expect(page.getByText(t.validation.deathsExceedTotalBorn)).toBeVisible() +}) diff --git a/gerbil-manager-web/e2e/mock-data.ts b/gerbil-manager-web/e2e/mock-data.ts index d80e457..29580a0 100644 --- a/gerbil-manager-web/e2e/mock-data.ts +++ b/gerbil-manager-web/e2e/mock-data.ts @@ -170,7 +170,7 @@ export function seedDb(): MockDb { ] const litters: Litter[] = [ - { id: 'w-kruemel', name: 'Wurf K', date: '2025-03-12', totalBorn: 5, expectedGoHomeDate: '2025-04-16', notes: null, fatherId: 'fridolin', motherId: 'luna' }, + { id: 'w-kruemel', name: 'Wurf K', date: '2025-03-12', totalBorn: 5, expectedGoHomeDate: '2025-04-16', notes: null, fatherId: 'fridolin', motherId: 'luna', deathsWithin8Weeks: 1 }, { id: 'w-fridolin', name: 'Wurf F', date: '2023-05-01', totalBorn: 4, expectedGoHomeDate: null, notes: null, fatherId: 'balu', motherId: 'maja' }, { id: 'w-luna', name: 'Wurf L', date: '2023-08-15', totalBorn: 6, expectedGoHomeDate: null, notes: null, fatherId: 'karlsson', motherId: 'smilla' }, { id: 'w-balu', name: 'Wurf B', date: '2021-04-20', totalBorn: 3, expectedGoHomeDate: null, notes: null, fatherId: 'anton', motherId: 'greta' }, diff --git a/gerbil-manager-web/src/api/types.ts b/gerbil-manager-web/src/api/types.ts index 6e6b976..6091f95 100644 --- a/gerbil-manager-web/src/api/types.ts +++ b/gerbil-manager-web/src/api/types.ts @@ -124,6 +124,8 @@ export interface Litter { notes?: string | null fatherId: string | null motherId: string | null + /** LITTER-MORTALITY: pups that died within the first 8 weeks. */ + deathsWithin8Weeks?: number | null } /** Payload for POST /litters. */ @@ -135,6 +137,7 @@ export interface CreateLitter { notes?: string | null fatherId?: string | null motherId?: string | null + deathsWithin8Weeks?: number | null } export type UpdateLitter = Partial diff --git a/gerbil-manager-web/src/pages/WurfDetailPage.tsx b/gerbil-manager-web/src/pages/WurfDetailPage.tsx index 4596727..477a6bb 100644 --- a/gerbil-manager-web/src/pages/WurfDetailPage.tsx +++ b/gerbil-manager-web/src/pages/WurfDetailPage.tsx @@ -125,6 +125,12 @@ export default function WurfDetailPage() { {l.totalBorn != null ? ` / ${l.totalBorn}` : ''} + {l.deathsWithin8Weeks != null && ( +
+
{t.fields.deathsWithin8Weeks}
+
{l.deathsWithin8Weeks}
+
+ )}
{t.fields.expectedGoHomeDate}
{formatDate(l.expectedGoHomeDate)}
diff --git a/gerbil-manager-web/src/pages/WurfFormPage.tsx b/gerbil-manager-web/src/pages/WurfFormPage.tsx index 91afde9..2e1c325 100644 --- a/gerbil-manager-web/src/pages/WurfFormPage.tsx +++ b/gerbil-manager-web/src/pages/WurfFormPage.tsx @@ -16,6 +16,7 @@ interface FormState { motherId: string motherName: string totalBorn: string + deathsWithin8Weeks: string expectedGoHomeDate: string notes: string } @@ -28,6 +29,7 @@ const EMPTY: FormState = { motherId: '', motherName: '', totalBorn: '', + deathsWithin8Weeks: '', expectedGoHomeDate: '', notes: '', } @@ -75,6 +77,7 @@ export default function WurfFormPage() { motherId: l.motherId ?? '', motherName: l.motherId ? (nameById.get(l.motherId) || de.pages.gerbils.nameless) : '', totalBorn: l.totalBorn != null ? String(l.totalBorn) : '', + deathsWithin8Weeks: l.deathsWithin8Weeks != null ? String(l.deathsWithin8Weeks) : '', expectedGoHomeDate: l.expectedGoHomeDate ?? '', notes: l.notes ?? '', }) @@ -109,12 +112,14 @@ export default function WurfFormPage() { e.preventDefault() if (!validate()) return const totalBornNum = form.totalBorn.trim() === '' ? null : Number(form.totalBorn) + const deathsNum = form.deathsWithin8Weeks.trim() === '' ? null : Number(form.deathsWithin8Weeks) const body: CreateLitter = { name: form.name.trim(), date: form.date, fatherId: nn(form.fatherId), motherId: nn(form.motherId), totalBorn: totalBornNum != null && Number.isFinite(totalBornNum) ? totalBornNum : null, + deathsWithin8Weeks: deathsNum != null && Number.isFinite(deathsNum) ? deathsNum : null, expectedGoHomeDate: nn(form.expectedGoHomeDate), notes: nn(form.notes), } @@ -215,6 +220,26 @@ export default function WurfFormPage() { /> + +