From 4288f14a79370bb4575009bd694eddd72a8e0abb Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 11 Mar 2026 23:19:25 +0000 Subject: [PATCH] refactor: use localDateTimeToUtcIso in component and clean up tests - Import and call localDateTimeToUtcIso in the add-game onSubmit handler instead of duplicating the conversion inline; also applies the null/ invalid-date safety from the utility to the component - Remove the redundant "incompatible with datetime-local" test case (its intent is better expressed as a code comment than a test assertion) - Move the datetime-local incompatibility explanation into a NOTE comment on the utility function's JSDoc https://claude.ai/code/session_0148fgZiXpyvGX8ZX3BFRGCs --- app/lib/__tests__/date-utils.test.ts | 11 ----------- app/lib/date-utils.ts | 6 ++++++ ...min.sports-seasons.$id.events.$eventId.bracket.tsx | 7 +++---- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/app/lib/__tests__/date-utils.test.ts b/app/lib/__tests__/date-utils.test.ts index 8d5f6ff..8b3133d 100644 --- a/app/lib/__tests__/date-utils.test.ts +++ b/app/lib/__tests__/date-utils.test.ts @@ -41,15 +41,4 @@ describe("localDateTimeToUtcIso", () => { expect(result).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/); }); - it("the returned ISO string is incompatible with datetime-local input format", () => { - // This documents WHY we need a hidden input instead of setting the - // datetime-local input's value directly. datetime-local only accepts - // "YYYY-MM-DDTHH:MM" — a 'Z' suffix makes the value invalid and the - // browser silently clears the field. - const result = localDateTimeToUtcIso("2026-03-11T10:00"); - expect(result).not.toBeNull(); - expect(result).toContain("Z"); - // The value does NOT match the datetime-local format (no Z allowed) - expect(result).not.toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/); - }); }); diff --git a/app/lib/date-utils.ts b/app/lib/date-utils.ts index 70702b7..5455009 100644 --- a/app/lib/date-utils.ts +++ b/app/lib/date-utils.ts @@ -9,6 +9,12 @@ * to hold the converted UTC value before form submission. * * Returns null when value is empty, nullish, or not a valid date. + * + * NOTE: The returned ISO string (e.g. "2026-03-11T17:00:00.000Z") is intentionally + * incompatible with datetime-local inputs, which only accept "YYYY-MM-DDTHH:MM". + * Setting a Z-suffixed string on a datetime-local input causes browsers to silently + * clear the field. Always write the result to a separate hidden input, not back to + * the datetime-local input itself. */ export function localDateTimeToUtcIso(value: string | null | undefined): string | null { if (!value) return null; diff --git a/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx b/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx index 3cb06cc..52472fb 100644 --- a/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx +++ b/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx @@ -1,5 +1,6 @@ import { Form, Link } from "react-router"; import { Fragment, useState, useEffect, useMemo } from "react"; +import { localDateTimeToUtcIso } from "~/lib/date-utils"; import type { Route } from "./+types/admin.sports-seasons.$id.events.$eventId.bracket"; import { loader, action } from "./admin.sports-seasons.$id.events.$eventId.bracket.server"; @@ -732,10 +733,8 @@ export default function EventBracket({ const scheduledAtLocal = form.elements.namedItem("scheduledAtLocal") as HTMLInputElement; // Write the UTC ISO string into the hidden input that IS submitted const scheduledAtHidden = form.elements.namedItem("scheduledAt") as HTMLInputElement; - if (scheduledAtLocal?.value && scheduledAtHidden) { - // datetime-local inputs reject ISO strings with a 'Z' suffix and silently - // clear their value, so we use a separate hidden input for the UTC value. - scheduledAtHidden.value = new Date(scheduledAtLocal.value).toISOString(); + if (scheduledAtHidden) { + scheduledAtHidden.value = localDateTimeToUtcIso(scheduledAtLocal?.value) ?? ""; } }}>