fix(timeline): validate RANGE end-date client-side with a field-level error
A RANGE event with a blank end date passed validateEventForm and reached the backend, which 400s with a generic INVALID_DATE_RANGE mapped to "end must not be before start" — wrong for a missing end date, and shown only as a top-of-form alert. Validate it before the API call and surface a dedicated event_editor_end_date_required message on the end-date field via a new DatePrecisionField endDateError prop (defaults '', so the document form is unchanged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,7 @@ let {
|
||||
dateLabel = m.form_label_date(),
|
||||
dateRequired = true,
|
||||
dateError = '',
|
||||
endDateError = '',
|
||||
onchange = undefined,
|
||||
dateTestId = undefined,
|
||||
precisionTestId = undefined,
|
||||
@@ -53,6 +54,8 @@ let {
|
||||
dateRequired?: boolean;
|
||||
/** Server-side date error (e.g. blank required field) wired to the field's aria-invalid. */
|
||||
dateError?: string;
|
||||
/** Server-side end-date error (e.g. RANGE without an end date) wired to the end field. */
|
||||
endDateError?: string;
|
||||
/** Called on any user edit (date, precision, end-date) — lets a parent track dirtiness. */
|
||||
onchange?: () => void;
|
||||
dateTestId?: string;
|
||||
@@ -99,6 +102,9 @@ const dateFieldInvalid = $derived(dateInvalid || dateError.length > 0);
|
||||
const endBeforeStart = $derived(
|
||||
showEndDate && endDateIso !== '' && dateIso !== '' && endDateIso < dateIso
|
||||
);
|
||||
// Either the inline end-before-start cue or a server-provided end-date error
|
||||
// (e.g. a RANGE event missing its end date) marks the end field invalid.
|
||||
const endDateFieldInvalid = $derived(endBeforeStart || endDateError.length > 0);
|
||||
|
||||
function handleDateInput(e: Event) {
|
||||
const result = handleGermanDateInput(e);
|
||||
@@ -190,10 +196,10 @@ $effect(() => {
|
||||
oninput={handleEndDateInput}
|
||||
placeholder={m.form_placeholder_date()}
|
||||
maxlength="10"
|
||||
aria-invalid={endBeforeStart ? 'true' : undefined}
|
||||
aria-describedby={endBeforeStart ? `${dateInputName}-end-error` : undefined}
|
||||
aria-invalid={endDateFieldInvalid ? 'true' : undefined}
|
||||
aria-describedby={endDateFieldInvalid ? `${dateInputName}-end-error` : undefined}
|
||||
class="block min-h-[48px] w-full rounded border border-line px-2 py-3 text-sm shadow-sm
|
||||
{endBeforeStart
|
||||
{endDateFieldInvalid
|
||||
? 'border-red-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-500'
|
||||
: 'focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring'}"
|
||||
/>
|
||||
@@ -202,6 +208,10 @@ $effect(() => {
|
||||
<p id="{dateInputName}-end-error" class="mt-1 text-xs text-danger">
|
||||
<span aria-hidden="true">⚠ </span>{m.error_invalid_date_range()}
|
||||
</p>
|
||||
{:else if endDateError}
|
||||
<p id="{dateInputName}-end-error" class="mt-1 text-xs text-danger">
|
||||
<span aria-hidden="true">⚠ </span>{endDateError}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user