From 242f20aeb19912e1b904aa9af4ebabaa9cc966ea Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Tue, 3 Jun 2025 17:33:57 +0300 Subject: [PATCH] Refactor DateTimeFormField to use RAC based input Closes #2359 --- app/components/form/DateFormField.tsx | 37 +++++++++++++++++------ app/components/form/DateTimeFormField.tsx | 33 -------------------- app/features/scrims/routes/scrims.new.tsx | 8 +++-- app/utils/dates.ts | 16 +++++++++- locales/de/scrims.json | 2 +- locales/en/scrims.json | 2 +- locales/fr-EU/scrims.json | 2 +- 7 files changed, 51 insertions(+), 49 deletions(-) delete mode 100644 app/components/form/DateTimeFormField.tsx diff --git a/app/components/form/DateFormField.tsx b/app/components/form/DateFormField.tsx index bc32963a1..01e5158b8 100644 --- a/app/components/form/DateFormField.tsx +++ b/app/components/form/DateFormField.tsx @@ -1,10 +1,11 @@ +import type { CalendarDateTime } from "@internationalized/date"; import { Controller, type FieldPath, type FieldValues, useFormContext, } from "react-hook-form"; -import { dayMonthYearToDateValue } from "../../utils/dates"; +import { dateToDateValue, dayMonthYearToDateValue } from "../../utils/dates"; import type { DayMonthYear } from "../../utils/zod"; import { SendouDatePicker } from "../elements/DatePicker"; import type { FormFieldSize } from "./form-utils"; @@ -15,12 +16,14 @@ export function DateFormField({ bottomText, required, size, + granularity = "day", }: { label: string; name: FieldPath; bottomText?: string; required?: boolean; size?: FormFieldSize; + granularity?: "day" | "minute"; }) { const methods = useFormContext(); @@ -33,17 +36,21 @@ export function DateFormField({ fieldState: { invalid, error }, }) => { const getValue = () => { - const originalValue = value as DayMonthYear | null; + const originalValue = value as DayMonthYear | Date | null; if (!originalValue) return null; - return dayMonthYearToDateValue(originalValue); + if (originalValue instanceof Date) { + return dateToDateValue(originalValue); + } + + return dayMonthYearToDateValue(originalValue as DayMonthYear); }; return ( ({ onBlur={onBlur} onChange={(value) => { if (value) { - onChange({ - day: value.day, - month: value.month - 1, - year: value.year, - }); + if (granularity === "minute") { + onChange( + new Date( + value.year, + value.month - 1, + value.day, + (value as CalendarDateTime).hour, + (value as CalendarDateTime).minute, + ), + ); + } else { + onChange({ + day: value.day, + month: value.month - 1, + year: value.year, + }); + } } if (!value) { diff --git a/app/components/form/DateTimeFormField.tsx b/app/components/form/DateTimeFormField.tsx deleted file mode 100644 index cac1c6ff6..000000000 --- a/app/components/form/DateTimeFormField.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import * as React from "react"; -import { - type FieldPath, - type FieldValues, - get, - useFormContext, -} from "react-hook-form"; -import { FormMessage } from "~/components/FormMessage"; -import { Label } from "~/components/Label"; - -export function DateTimeFormField({ - label, - name, - bottomText, -}: { label: string; name: FieldPath; bottomText?: string }) { - const methods = useFormContext(); - const id = React.useId(); - - const error = get(methods.formState.errors, name); - - return ( -
- - - {error && ( - {error.message as string} - )} - {bottomText && !error ? ( - {bottomText} - ) : null} -
- ); -} diff --git a/app/features/scrims/routes/scrims.new.tsx b/app/features/scrims/routes/scrims.new.tsx index 91d7e99db..13347f3d8 100644 --- a/app/features/scrims/routes/scrims.new.tsx +++ b/app/features/scrims/routes/scrims.new.tsx @@ -4,7 +4,7 @@ import { Controller, useFormContext, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import type { z } from "zod"; import { Label } from "~/components/Label"; -import { DateTimeFormField } from "~/components/form/DateTimeFormField"; +import { DateFormField } from "~/components/form/DateFormField"; import { SendouForm } from "~/components/form/SendouForm"; import { TextAreaFormField } from "~/components/form/TextAreaFormField"; import { nullFilledArray } from "~/utils/arrays"; @@ -62,10 +62,11 @@ export default function NewScrimPage() { > - + label={t("scrims:forms.when.title")} name="at" bottomText={t("scrims:forms.when.explanation")} + granularity="minute" /> @@ -143,9 +144,10 @@ function NotFoundVisibilityFormField({ return (
- + label={t("scrims:forms.notFoundVisibility.title")} name="notFoundVisibility.at" + granularity="minute" /> {date ? (
diff --git a/app/utils/dates.ts b/app/utils/dates.ts index 94fdab6a7..b7e7d6d3f 100644 --- a/app/utils/dates.ts +++ b/app/utils/dates.ts @@ -1,4 +1,4 @@ -import { parseDate } from "@internationalized/date"; +import { CalendarDateTime, parseDate } from "@internationalized/date"; import { getWeek } from "date-fns"; import type { MonthYear } from "~/features/plus-voting/core"; import type { DayMonthYear } from "./zod"; @@ -26,6 +26,20 @@ export function dayMonthYearToDate({ day, month, year }: DayMonthYear) { return new Date(Date.UTC(year, month, day, 12)); } +/** + * Converts a JavaScript Date object into a CalendarDateTime object (used by react-aria-components). + */ +export function dateToDateValue(date: Date) { + return new CalendarDateTime( + date.getFullYear(), + date.getMonth() + 1, + date.getDate(), + date.getHours(), + date.getMinutes(), + date.getSeconds(), + ); +} + /** * Converts a date represented by day, month, and year into a DateValue object (used by react-aria-components), noon UTC. */ diff --git a/locales/de/scrims.json b/locales/de/scrims.json index fe4727393..f23319ea6 100644 --- a/locales/de/scrims.json +++ b/locales/de/scrims.json @@ -26,7 +26,7 @@ "forms.with.user": "Nutzer {{nth}}", "forms.with.pick-up": "Pick-up", "forms.when.title": "Wann", - "forms.when.explanation": "Leer lassen, wenn du jetzt nach einem Scrim suchen willst.", + "forms.when.explanation": "", "forms.text.title": "Text", "forms.visibility.title": "Sichtbarkeit", "forms.visibility.public": "Öffentlich", diff --git a/locales/en/scrims.json b/locales/en/scrims.json index 1ccd33cb1..be8396512 100644 --- a/locales/en/scrims.json +++ b/locales/en/scrims.json @@ -26,7 +26,7 @@ "forms.with.user": "User {{nth}}", "forms.with.pick-up": "Pick-up", "forms.when.title": "When", - "forms.when.explanation": "Leave blank if you want to look for a scrim now", + "forms.when.explanation": "Leave to default if you want to look for a scrim now", "forms.text.title": "Text", "forms.visibility.title": "Visibility", "forms.visibility.public": "Public", diff --git a/locales/fr-EU/scrims.json b/locales/fr-EU/scrims.json index 3b0477bf6..d2b3adc42 100644 --- a/locales/fr-EU/scrims.json +++ b/locales/fr-EU/scrims.json @@ -26,7 +26,7 @@ "forms.with.user": "Utilisateur {{nth}}", "forms.with.pick-up": "Pick-up", "forms.when.title": "Quand", - "forms.when.explanation": "Laissez vide si vous voulez chercher un scrim dès maintenant", + "forms.when.explanation": "", "forms.text.title": "Texte", "forms.visibility.title": "Visibilité", "forms.visibility.public": "Public",