fix(timeline): preserve date/precision/end across a fail(400)
preservedFormFields echoed back only title/type/description/pickers, and EventForm seeded dateIso/precision/endDateIso solely from `event` (undefined on /new). So a no-JS validation-error reload silently dropped the entire When-section the curator had entered, while every other field survived. Echo eventDate/precision/eventDateEnd in the fail payload and seed the date controls from `form` ahead of `event`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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<string>(form?.type ?? event?.type ?? 'PERSONAL');
|
||||
let dateIso = $state(event?.eventDate ?? '');
|
||||
let precision = $state<DatePrecision>((event?.precision as DatePrecision) ?? 'DAY');
|
||||
let endDateIso = $state(event?.eventDateEnd ?? '');
|
||||
let dateIso = $state(form?.eventDate ?? event?.eventDate ?? '');
|
||||
let precision = $state<DatePrecision>(
|
||||
(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
|
||||
|
||||
@@ -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.' } });
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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: '' }));
|
||||
|
||||
Reference in New Issue
Block a user