From 7826a34b007f5c59fc736ae8cffb6066500c64b2 Mon Sep 17 00:00:00 2001 From: Bailey Pumfleet Date: Mon, 28 Feb 2022 16:24:47 +0000 Subject: [PATCH] Let users set 12/24 hour time format (#2002) --- .../components/booking/BookingListItem.tsx | 7 ++++- apps/web/components/ui/form/Schedule.tsx | 13 ++++++--- apps/web/pages/settings/profile.tsx | 27 +++++++++++++++++++ apps/web/public/static/locales/en/common.json | 5 +++- apps/web/server/createContext.ts | 1 + apps/web/server/routers/viewer.tsx | 2 ++ .../migration.sql | 2 ++ packages/prisma/schema.prisma | 1 + 8 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 packages/prisma/migrations/20220228122419_add_time_format/migration.sql diff --git a/apps/web/components/booking/BookingListItem.tsx b/apps/web/components/booking/BookingListItem.tsx index 3ffb359d..6281768f 100644 --- a/apps/web/components/booking/BookingListItem.tsx +++ b/apps/web/components/booking/BookingListItem.tsx @@ -9,6 +9,7 @@ import { useLocale } from "@lib/hooks/useLocale"; import { inferQueryOutput, trpc } from "@lib/trpc"; import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@components/Dialog"; +import { useMeQuery } from "@components/Shell"; import { TextArea } from "@components/form/fields"; import Button from "@components/ui/Button"; import TableActions, { ActionType } from "@components/ui/TableActions"; @@ -16,6 +17,9 @@ import TableActions, { ActionType } from "@components/ui/TableActions"; type BookingItem = inferQueryOutput<"viewer.bookings">["bookings"][number]; function BookingListItem(booking: BookingItem) { + // Get user so we can determine 12/24 hour format preferences + const query = useMeQuery(); + const user = query.data; const { t, i18n } = useLocale(); const utils = trpc.useContext(); const [rejectionReason, setRejectionReason] = useState(""); @@ -120,7 +124,8 @@ function BookingListItem(booking: BookingItem) {
{startTime}
- {dayjs(booking.startTime).format("HH:mm")} - {dayjs(booking.endTime).format("HH:mm")} + {dayjs(booking.startTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")} -{" "} + {dayjs(booking.endTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")}
diff --git a/apps/web/components/ui/form/Schedule.tsx b/apps/web/components/ui/form/Schedule.tsx index 033704d4..69628d66 100644 --- a/apps/web/components/ui/form/Schedule.tsx +++ b/apps/web/components/ui/form/Schedule.tsx @@ -10,6 +10,7 @@ import { weekdayNames } from "@lib/core/i18n/weekday"; import { useLocale } from "@lib/hooks/useLocale"; import { TimeRange } from "@lib/types/schedule"; +import { useMeQuery } from "@components/Shell"; import Button from "@components/ui/Button"; import Select from "@components/ui/form/Select"; @@ -46,6 +47,10 @@ type TimeRangeFieldProps = { }; const TimeRangeField = ({ name }: TimeRangeFieldProps) => { + // Get user so we can determine 12/24 hour format preferences + const query = useMeQuery(); + const user = query.data; + // Lazy-loaded options, otherwise adding a field has a noticable redraw delay. const [options, setOptions] = useState([]); const [selected, setSelected] = useState(); @@ -57,7 +62,9 @@ const TimeRangeField = ({ name }: TimeRangeFieldProps) => { const getOption = (time: ConfigType) => ({ value: dayjs(time).toDate().valueOf(), - label: dayjs(time).utc().format("HH:mm"), + label: dayjs(time) + .utc() + .format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm"), // .toLocaleTimeString(i18n.language, { minute: "numeric", hour: "numeric" }), }); @@ -82,7 +89,7 @@ const TimeRangeField = ({ name }: TimeRangeFieldProps) => { handleSelected(value); return ( setOptions(timeOptions({ selected }))} onBlur={() => setOptions([])} diff --git a/apps/web/pages/settings/profile.tsx b/apps/web/pages/settings/profile.tsx index daebfb1c..1a9f4b1b 100644 --- a/apps/web/pages/settings/profile.tsx +++ b/apps/web/pages/settings/profile.tsx @@ -146,6 +146,11 @@ function SettingsView(props: ComponentProps & { localeProp: str { value: "light", label: t("light") }, { value: "dark", label: t("dark") }, ]; + + const timeFormatOptions = [ + { value: 12, label: t("12_hour") }, + { value: 24, label: t("24_hour") }, + ]; const usernameRef = useRef(null!); const nameRef = useRef(null!); const emailRef = useRef(null!); @@ -153,6 +158,10 @@ function SettingsView(props: ComponentProps & { localeProp: str const avatarRef = useRef(null!); const hideBrandingRef = useRef(null!); const [selectedTheme, setSelectedTheme] = useState(); + const [selectedTimeFormat, setSelectedTimeFormat] = useState({ + value: props.user.timeFormat || 12, + label: timeFormatOptions.find((option) => option.value === props.user.timeFormat)?.label || 12, + }); const [selectedTimeZone, setSelectedTimeZone] = useState(props.user.timeZone); const [selectedWeekStartDay, setSelectedWeekStartDay] = useState({ value: props.user.weekStart, @@ -189,6 +198,7 @@ function SettingsView(props: ComponentProps & { localeProp: str const enteredWeekStartDay = selectedWeekStartDay.value; const enteredHideBranding = hideBrandingRef.current.checked; const enteredLanguage = selectedLanguage.value; + const enteredTimeFormat = selectedTimeFormat.value; // TODO: Add validation @@ -204,6 +214,7 @@ function SettingsView(props: ComponentProps & { localeProp: str theme: asStringOrNull(selectedTheme?.value), brandColor: enteredBrandColor, locale: enteredLanguage, + timeFormat: enteredTimeFormat, }); } @@ -347,6 +358,21 @@ function SettingsView(props: ComponentProps & { localeProp: str /> +
+ +
+