diff --git a/apps/web/src/app/[locale]/poll/[urlId]/edit-options/page.tsx b/apps/web/src/app/[locale]/poll/[urlId]/edit-options/page.tsx index b449af2b5..e4ba24e2c 100644 --- a/apps/web/src/app/[locale]/poll/[urlId]/edit-options/page.tsx +++ b/apps/web/src/app/[locale]/poll/[urlId]/edit-options/page.tsx @@ -16,8 +16,16 @@ import { usePoll } from "@/components/poll-context"; import { Trans } from "@/components/trans"; import { encodeDateOption } from "@/utils/date-time-utils"; -const convertOptionToString = (option: { start: Date; duration: number }) => { - const start = dayjs(option.start).utc(); +const convertOptionToString = ( + option: { startTime: Date; duration: number }, + timeZone: string | null, +) => { + let start = dayjs(option.startTime); + if (timeZone) { + start = start.tz(timeZone); + } else { + start = start.utc(); + } return option.duration === 0 ? start.format("YYYY-MM-DD") : `${start.format("YYYY-MM-DDTHH:mm:ss")}/${start @@ -37,12 +45,26 @@ const Page = () => { const redirectBackToPoll = () => { router.push(pollLink); }; + + let firstDate = dayjs(poll.options[0]?.startTime); + + if (poll.timeZone) { + firstDate = firstDate.tz(poll.timeZone); + } else { + firstDate = firstDate.utc(); + } + const form = useForm({ defaultValues: { - navigationDate: dayjs(poll.options[0]?.start).utc().format("YYYY-MM-DD"), + navigationDate: firstDate.format("YYYY-MM-DD"), view: "month", options: poll.options.map((option) => { - const start = dayjs(option.start).utc(); + let start = dayjs(option.startTime); + if (poll.timeZone) { + start = start.tz(poll.timeZone); + } else { + start = start.utc(); + } return option.duration > 0 ? { type: "timeSlot", @@ -68,13 +90,16 @@ const Page = () => { onSubmit={form.handleSubmit((data) => { const encodedOptions = data.options.map(encodeDateOption); const optionsToDelete = poll.options.filter((option) => { - return !encodedOptions.includes(convertOptionToString(option)); + return !encodedOptions.includes( + convertOptionToString(option, data.timeZone), + ); }); const optionsToAdd = encodedOptions.filter( (encodedOption) => !poll.options.find( - (o) => convertOptionToString(o) === encodedOption, + (o) => + convertOptionToString(o, data.timeZone) === encodedOption, ), ); diff --git a/apps/web/src/components/poll-context.tsx b/apps/web/src/components/poll-context.tsx index 3b8fc784f..95e185ee8 100644 --- a/apps/web/src/components/poll-context.tsx +++ b/apps/web/src/components/poll-context.tsx @@ -5,9 +5,7 @@ import { TrashIcon } from "lucide-react"; import { useTranslation } from "next-i18next"; import React from "react"; -import { useUser } from "@/components/user-provider"; import { - decodeOptions, getDuration, ParsedDateOption, ParsedTimeSlotOption, @@ -223,27 +221,15 @@ function createOptionsContextValue( export const OptionsProvider = (props: React.PropsWithChildren) => { const { poll } = usePoll(); const { timeZone: targetTimeZone, timeFormat } = useDayjs(); - const { isInternalUser } = useUser(); const options = React.useMemo(() => { - let res: OptionsContextValue; - if (isInternalUser) { - res = createOptionsContextValue( - poll.options, - targetTimeZone, - poll.timeZone, - ); - } else { - // @deprecated - Stop using this method and drop the start column from the database in favor of startTime - res = decodeOptions( - poll.options, - poll.timeZone, - targetTimeZone, - timeFormat, - ); - } - return res; - }, [isInternalUser, poll.options, poll.timeZone, targetTimeZone, timeFormat]); + return createOptionsContextValue( + poll.options, + targetTimeZone, + poll.timeZone, + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [poll.options, poll.timeZone, targetTimeZone, timeFormat]); return ( diff --git a/apps/web/src/components/poll/manage-poll/finalize-poll-dialog.tsx b/apps/web/src/components/poll/manage-poll/finalize-poll-dialog.tsx index 971ed63b3..7f787c440 100644 --- a/apps/web/src/components/poll/manage-poll/finalize-poll-dialog.tsx +++ b/apps/web/src/components/poll/manage-poll/finalize-poll-dialog.tsx @@ -18,7 +18,8 @@ import { DateIcon } from "@/components/date-icon"; import { useParticipants } from "@/components/participants-provider"; import { ConnectedScoreSummary } from "@/components/poll/score-summary"; import { VoteSummaryProgressBar } from "@/components/vote-summary-progress-bar"; -import { useDateFormatter, usePoll } from "@/contexts/poll"; +import { usePoll } from "@/contexts/poll"; +import { useDayjs } from "@/utils/dayjs"; const formSchema = z.object({ selectedOptionId: z.string(), @@ -69,6 +70,7 @@ export const FinalizePollForm = ({ const poll = usePoll(); const [max, setMax] = React.useState(pageSize); + const { adjustTimeZone } = useDayjs(); const scoreByOptionId = useScoreByOptionId(); const { participants } = useParticipants(); @@ -96,7 +98,6 @@ export const FinalizePollForm = ({ return { ...option, votes: scoreByOptionId[option.id] }; }); - const dateFormatter = useDateFormatter(); const form = useForm({ defaultValues: { selectedOptionId: options[0].id, @@ -127,10 +128,16 @@ export const FinalizePollForm = ({ className="grid gap-2" > {options.slice(0, max).map((option) => { - const start = dateFormatter(option.start); - const end = dateFormatter( - dayjs(option.start).add(option.duration, "minute"), + const start = adjustTimeZone( + option.startTime, + !poll.timeZone, ); + + const end = adjustTimeZone( + dayjs(option.startTime).add(option.duration, "minute"), + !poll.timeZone, + ); + return (