diff --git a/frontend/src/lib/timeline/EventForm.svelte b/frontend/src/lib/timeline/EventForm.svelte index ee627cf2..e329e231 100644 --- a/frontend/src/lib/timeline/EventForm.svelte +++ b/frontend/src/lib/timeline/EventForm.svelte @@ -28,6 +28,10 @@ interface FormResult { title?: string; description?: string; type?: string; + // When-section values preserved across a fail(400) so a no-JS reload re-seeds them. + eventDate?: string; + precision?: string; + eventDateEnd?: string | null; personIds?: string[]; documentIds?: string[]; // Rehydrated chip data (id + label) so the pickers re-render after a fail(400) @@ -57,9 +61,11 @@ let { let title = $state(form?.title ?? event?.title ?? ''); let description = $state(form?.description ?? event?.description ?? ''); let type = $state(form?.type ?? event?.type ?? 'PERSONAL'); -let dateIso = $state(event?.eventDate ?? ''); -let precision = $state((event?.precision as DatePrecision) ?? 'DAY'); -let endDateIso = $state(event?.eventDateEnd ?? ''); +let dateIso = $state(form?.eventDate ?? event?.eventDate ?? ''); +let precision = $state( + (form?.precision as DatePrecision) ?? (event?.precision as DatePrecision) ?? 'DAY' +); +let endDateIso = $state(form?.eventDateEnd ?? event?.eventDateEnd ?? ''); // On a fail(400) the server returns rehydrated chip data (form.persons/documents) // so the pickers survive the round-trip — even without JS — ahead of the seeded diff --git a/frontend/src/lib/timeline/EventForm.svelte.spec.ts b/frontend/src/lib/timeline/EventForm.svelte.spec.ts index 8e7f5f89..99180218 100644 --- a/frontend/src/lib/timeline/EventForm.svelte.spec.ts +++ b/frontend/src/lib/timeline/EventForm.svelte.spec.ts @@ -132,6 +132,22 @@ describe('EventForm — delete is gated behind confirmation (REQ-006)', () => { }); }); +describe('EventForm — seeds the When-section from the fail payload (review #2 — no-JS)', () => { + it('re-seeds date, precision and end-date from the form payload on /new', async () => { + renderForm({ + form: { eventDate: '1944-03-12', precision: 'RANGE', eventDateEnd: '1944-03-14' } + }); + + // precision=RANGE seeded → end-date field revealed (proves precision survived). + await expect.element(page.getByLabelText('Enddatum')).toBeVisible(); + + const dateInput = document.querySelector('#eventDate') as HTMLInputElement; + expect(dateInput.value).toBe('12.03.1944'); + const endInput = document.querySelector('#eventDateEnd') as HTMLInputElement; + expect(endInput.value).toBe('14.03.1944'); + }); +}); + describe('EventForm — server date error wired per-field (REQ-011)', () => { it('marks the date field aria-invalid and shows the message on a server date error', async () => { renderForm({ form: { dateError: 'Bitte ein Datum eingeben.' } }); diff --git a/frontend/src/lib/timeline/eventFormServer.ts b/frontend/src/lib/timeline/eventFormServer.ts index aa7334a5..b633835e 100644 --- a/frontend/src/lib/timeline/eventFormServer.ts +++ b/frontend/src/lib/timeline/eventFormServer.ts @@ -90,6 +90,11 @@ export function preservedFormFields(parsed: ParsedEventForm) { title: parsed.title, description: parsed.description, type: parsed.type, + // The When-section too, so a no-JS full reload re-seeds the date controls + // instead of dropping the curator's date/precision/end-date. + eventDate: parsed.eventDate, + precision: parsed.precision, + eventDateEnd: parsed.eventDateEnd, personIds: parsed.personIds, documentIds: parsed.documentIds }; diff --git a/frontend/src/routes/zeitstrahl/events/new/page.server.spec.ts b/frontend/src/routes/zeitstrahl/events/new/page.server.spec.ts index 208dabdf..a6bbb02b 100644 --- a/frontend/src/routes/zeitstrahl/events/new/page.server.spec.ts +++ b/frontend/src/routes/zeitstrahl/events/new/page.server.spec.ts @@ -187,6 +187,27 @@ describe('zeitstrahl/events/new save action (REQ-004/009/010/015)', () => { expect((failData(result).documents as { id: string }[])[0].id).toBe('d1'); }); + it('preserves date, precision and end-date in the fail payload (review #2 — no-JS reload)', async () => { + // A blank-title fail must echo back the entered When-section so a no-JS full + // reload re-seeds it; otherwise the curator loses the date/precision/end-date. + vi.mocked(createApiClient).mockReturnValue({ POST: vi.fn(), GET: vi.fn() } as never); + + const result = await actions.save( + saveEvent({ + title: '', + type: 'PERSONAL', + eventDate: '1944-03-12', + precision: 'RANGE', + eventDateEnd: '1944-03-14' + }) + ); + + expect(result).toMatchObject({ status: 400 }); + expect(failData(result).eventDate).toBe('1944-03-12'); + expect(failData(result).precision).toBe('RANGE'); + expect(failData(result).eventDateEnd).toBe('1944-03-14'); + }); + it('surfaces both title and date errors when both blank (REQ-011)', async () => { vi.mocked(createApiClient).mockReturnValue({ POST: vi.fn() } as never); const result = await actions.save(saveEvent({ title: '', type: 'PERSONAL', eventDate: '' }));