import classNames from "classnames"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { Controller, useFieldArray, useFormContext } from "react-hook-form"; import { GroupBase, Props } from "react-select"; import dayjs, { Dayjs, ConfigType } from "@calcom/dayjs"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import Button from "@calcom/ui/Button"; import Dropdown, { DropdownMenuContent, DropdownMenuTrigger } from "@calcom/ui/Dropdown"; import { Icon } from "@calcom/ui/Icon"; import { Tooltip } from "@calcom/ui/Tooltip"; import { defaultDayRange } from "@lib/availability"; import { weekdayNames } from "@lib/core/i18n/weekday"; import useMeQuery from "@lib/hooks/useMeQuery"; import { TimeRange } from "@lib/types/schedule"; import Select from "@components/ui/form/Select"; /** Begin Time Increments For Select */ const increment = 15; type Option = { readonly label: string; readonly value: number; }; /** * 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 useOptions = () => { // Get user so we can determine 12/24 hour format preferences const query = useMeQuery(); const { timeFormat } = query.data || { timeFormat: null }; const [filteredOptions, setFilteredOptions] = useState([]); const options = useMemo(() => { const end = dayjs().utc().endOf("day"); let t: Dayjs = dayjs().utc().startOf("day"); const options: Option[] = []; while (t.isBefore(end)) { options.push({ value: t.toDate().valueOf(), label: dayjs(t) .utc() .format(timeFormat === 12 ? "h:mma" : "HH:mm"), }); t = t.add(increment, "minutes"); } return options; }, [timeFormat]); const filter = useCallback( ({ offset, limit, current }: { offset?: ConfigType; limit?: ConfigType; current?: ConfigType }) => { if (current) { const currentOption = options.find((option) => option.value === dayjs(current).toDate().valueOf()); if (currentOption) setFilteredOptions([currentOption]); } else setFilteredOptions( options.filter((option) => { const time = dayjs(option.value); return (!limit || time.isBefore(limit)) && (!offset || time.isAfter(offset)); }) ); }, [options] ); return { options: filteredOptions, filter }; }; type TimeRangeFieldProps = { name: string; className?: string; }; const LazySelect = ({ value, min, max, ...props }: Omit>, "value"> & { value: ConfigType; min?: ConfigType; max?: ConfigType; }) => { // Lazy-loaded options, otherwise adding a field has a noticable redraw delay. const { options, filter } = useOptions(); useEffect(() => { filter({ current: value }); }, [filter, value]); return ( { if (e.target.checked && !selected.includes(num)) { setSelected(selected.concat([num])); } else if (!e.target.checked && selected.includes(num)) { setSelected(selected.slice(selected.indexOf(num), 1)); } }} type="checkbox" className="inline-block rounded-sm border-gray-300 text-neutral-900 focus:ring-neutral-500 disabled:text-neutral-400" /> ))}
); }; export const DayRanges = ({ name, defaultValue = [defaultDayRange], }: { name: string; defaultValue?: TimeRange[]; }) => { const { setValue, watch } = useFormContext(); // XXX: Hack to make copying times work; `fields` is out of date until save. const watcher = watch(name); const { t } = useLocale(); const { fields, replace, append, remove } = useFieldArray({ name, }); useEffect(() => { if (defaultValue.length && !fields.length) { replace(defaultValue); } }, [replace, defaultValue, fields.length]); 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) => (
{index === 0 && (
)}
))}
); }; const ScheduleBlock = ({ name, day, weekday }: ScheduleBlockProps) => { const { t } = useLocale(); const form = useFormContext(); const watchAvailable = form.watch(`${name}.${day}`, []); return (
{!!watchAvailable.length && (
)}
); }; const Schedule = ({ name }: { name: string }) => { const { i18n } = useLocale(); return (
{weekdayNames(i18n.language).map((weekday, num) => ( ))}
); }; export default Schedule;