import { PlusIcon, TrashIcon } from "@heroicons/react/outline"; import dayjs, { Dayjs, ConfigType } from "dayjs"; import timezone from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; import React, { useCallback, useState } from "react"; import { Controller, useFieldArray } from "react-hook-form"; import { defaultDayRange } from "@lib/availability"; 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"; dayjs.extend(utc); dayjs.extend(timezone); /** Begin Time Increments For Select */ const increment = 15; /** * Creates an array of times on a 15 minute interval from * 00:00:00 (Start of day) to * 23:45:00 (End of day with enough time for 15 min booking) */ const TIMES = (() => { const end = dayjs().utc().endOf("day"); let t: Dayjs = dayjs().utc().startOf("day"); const times: Dayjs[] = []; while (t.isBefore(end)) { times.push(t); t = t.add(increment, "minutes"); } return times; })(); /** End Time Increments For Select */ type Option = { readonly label: string; readonly value: number; }; type TimeRangeFieldProps = { name: string; }; 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(); // const { i18n } = useLocale(); const handleSelected = (value: number | undefined) => { setSelected(value); }; const getOption = (time: ConfigType) => ({ value: dayjs(time).toDate().valueOf(), label: dayjs(time) .utc() .format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm"), // .toLocaleTimeString(i18n.language, { minute: "numeric", hour: "numeric" }), }); const timeOptions = useCallback( (offsetOrLimitorSelected: { offset?: number; limit?: number; selected?: number } = {}) => { const { limit, offset, selected } = offsetOrLimitorSelected; return TIMES.filter( (time) => (!limit || time.isBefore(limit)) && (!offset || time.isAfter(offset)) && (!selected || time.isAfter(selected)) ).map((t) => getOption(t)); }, [] ); return ( <> { handleSelected(value); return ( setOptions(timeOptions({ selected }))} onBlur={() => setOptions([])} defaultValue={getOption(value)} onChange={(option) => onChange(new Date(option?.value as number))} /> )} /> ); }; type ScheduleBlockProps = { day: number; weekday: string; name: string; }; const ScheduleBlock = ({ name, day, weekday }: ScheduleBlockProps) => { const { t } = useLocale(); const { fields, append, remove, replace } = useFieldArray({ name: `${name}.${day}`, }); const handleAppend = () => { // FIXME: Fix type-inference, can't get this to work. @see https://github.com/react-hook-form/react-hook-form/issues/4499 const nextRangeStart = dayjs((fields[fields.length - 1] as unknown as TimeRange).end); const nextRangeEnd = dayjs(nextRangeStart).add(1, "hour"); if (nextRangeEnd.isBefore(nextRangeStart.endOf("day"))) { return append({ start: nextRangeStart.toDate(), end: nextRangeEnd.toDate(), }); } }; return (
{fields.map((field, index) => (
))} {!fields.length && t("no_availability")}
); }; const Schedule = ({ name }: { name: string }) => { const { i18n } = useLocale(); return (
{weekdayNames(i18n.language).map((weekday, num) => ( ))}
); }; export default Schedule;