diff --git a/app/components/DateInput.tsx b/app/components/DateInput.tsx index 3caf43d4f..a34c407c9 100644 --- a/app/components/DateInput.tsx +++ b/app/components/DateInput.tsx @@ -1,5 +1,5 @@ import { useIsMounted } from "~/hooks/useIsMounted"; -import { dateToYearMonthDayHourMinuteString } from "~/utils/dates"; +import { dateToYearMonthDayHourMinuteString, isValidDate } from "~/utils/dates"; import * as React from "react"; export function DateInput({ @@ -9,6 +9,7 @@ export function DateInput({ min, max, required, + onChange, }: { id?: string; name?: string; @@ -16,6 +17,7 @@ export function DateInput({ min?: Date; max?: Date; required?: boolean; + onChange?: (newDate: Date) => void; }) { const [date, setDate] = React.useState(defaultValue ?? new Date()); const isMounted = useIsMounted(); @@ -41,7 +43,20 @@ export function DateInput({ value={dateToYearMonthDayHourMinuteString(date)} min={min ? dateToYearMonthDayHourMinuteString(min) : undefined} max={max ? dateToYearMonthDayHourMinuteString(max) : undefined} - onChange={(e) => setDate(new Date(e.target.value))} + onChange={(e) => { + //TODO: fix invalid Date Input handling: https://github.com/Sendouc/sendou.ink/issues/1082 + const updatedDate = new Date(e.target.value); + if (!isValidDate(updatedDate)) { + console.warn("Invalid date"); + // throw new RangeError("Invalid Date"); + } + setDate(updatedDate); + + // Update the correct entry in the React hook from the parent via the passed on callback function + if (typeof onChange !== "undefined") { + onChange(updatedDate); + } + }} required={required} /> diff --git a/app/routes/calendar/new.tsx b/app/routes/calendar/new.tsx index c28563c90..c7505d4af 100644 --- a/app/routes/calendar/new.tsx +++ b/app/routes/calendar/new.tsx @@ -31,8 +31,9 @@ import { canEditCalendarEvent } from "~/permissions"; import calendarNewStyles from "~/styles/calendar-new.css"; import mapsStyles from "~/styles/maps.css"; import { - databaseTimestampToDate, dateToDatabaseTimestamp, + databaseTimestampToDate, + getDateWithHoursOffset, } from "~/utils/dates"; import { badRequestIfFalsy, @@ -288,14 +289,31 @@ function DescriptionTextarea() { function DatesInput() { const { t } = useTranslation(["common", "calendar"]); const { eventToEdit } = useLoaderData(); - const [datesCount, setDatesCount] = React.useState( - eventToEdit?.startTimes.length ?? 1 - ); - const isMounted = useIsMounted(); + // Initialize datesInputState by retrieving pre-existing events if they exist + let eventDatesInputState = null; + if (typeof eventToEdit?.startTimes !== "undefined") { + eventDatesInputState = eventToEdit.startTimes.map((t) => { + return { finalDateInputDate: databaseTimestampToDate(t) }; + }); + } + + // React hook that keeps contains an array of parameters that corresponds to each DateInput child object generated + const [datesInputState, setDatesInputState] = React.useState( + eventDatesInputState ?? [ + { + finalDateInputDate: new Date(), + }, + ] + ); + + const datesCount = datesInputState.length; + + const isMounted = useIsMounted(); const usersTimeZone = isMounted ? Intl.DateTimeFormat().resolvedOptions().timeZone : ""; + const NEW_CALENDAR_EVENT_HOURS_OFFSET = 24; return (
@@ -304,38 +322,58 @@ function DatesInput() { {t("calendar:forms.dates")}
- {new Array(datesCount).fill(null).map((_, i) => { - const defaultStartTime = eventToEdit?.startTimes[i]; - + {datesInputState.map((inputState, i) => { return (
{ + setDatesInputState((current) => + current.map((obj, objIndex) => { + if (objIndex === i) { + return { ...obj, finalDateInputDate: newDate }; + } + + return obj; + }) + ); + }} /> {i === datesCount - 1 && ( <> + {/* "Add" button */} + + {/* "Remove" button */} {datesCount > 1 && (