import { PlusIcon, TrashIcon } from "@heroicons/react/outline"; import classnames from "classnames"; import dayjs, { Dayjs } from "dayjs"; import React from "react"; import Text from "@components/ui/Text"; export const SCHEDULE_FORM_ID = "SCHEDULE_FORM_ID"; export const toCalendsoAvailabilityFormat = (schedule: Schedule) => { return schedule; }; export const _24_HOUR_TIME_FORMAT = `HH:mm:ss`; const DEFAULT_START_TIME = "09:00:00"; const DEFAULT_END_TIME = "17:00:00"; /** 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 starting_time = dayjs().startOf("day"); const ending_time = dayjs().endOf("day"); const times = []; let t: Dayjs = starting_time; while (t.isBefore(ending_time)) { times.push(t); t = t.add(increment, "minutes"); } return times; })(); /** End Time Increments For Select */ const DEFAULT_SCHEDULE: Schedule = { monday: [{ start: "09:00:00", end: "17:00:00" }], tuesday: [{ start: "09:00:00", end: "17:00:00" }], wednesday: [{ start: "09:00:00", end: "17:00:00" }], thursday: [{ start: "09:00:00", end: "17:00:00" }], friday: [{ start: "09:00:00", end: "17:00:00" }], saturday: null, sunday: null, }; type DayOfWeek = "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"; export type TimeRange = { start: string; end: string; }; export type FreeBusyTime = TimeRange[]; export type Schedule = { monday?: FreeBusyTime | null; tuesday?: FreeBusyTime | null; wednesday?: FreeBusyTime | null; thursday?: FreeBusyTime | null; friday?: FreeBusyTime | null; saturday?: FreeBusyTime | null; sunday?: FreeBusyTime | null; }; type ScheduleBlockProps = { day: DayOfWeek; ranges?: FreeBusyTime | null; selected?: boolean; }; type Props = { schedule?: Schedule; onChange?: (data: Schedule) => void; onSubmit: (data: Schedule) => void; }; const SchedulerForm = ({ schedule = DEFAULT_SCHEDULE, onSubmit }: Props) => { const ref = React.useRef(null); const transformElementsToSchedule = (elements: HTMLFormControlsCollection): Schedule => { const schedule: Schedule = {}; const formElements = Array.from(elements) .map((element) => { return element.id; }) .filter((value) => value); /** * elementId either {day} or {day.N.start} or {day.N.end} * If elementId in DAYS_ARRAY add elementId to scheduleObj * then element is the checkbox and can be ignored * * If elementId starts with a day in DAYS_ARRAY * the elementId should be split by "." resulting in array length 3 * [day, rangeIndex, "start" | "end"] */ formElements.forEach((elementId) => { const [day, rangeIndex, rangeId] = elementId.split("."); if (rangeIndex && rangeId) { if (!schedule[day]) { schedule[day] = []; } if (!schedule[day][parseInt(rangeIndex)]) { schedule[day][parseInt(rangeIndex)] = {}; } schedule[day][parseInt(rangeIndex)][rangeId] = elements[elementId].value; } }); return schedule; }; const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); const elements = ref.current?.elements; if (elements) { const schedule = transformElementsToSchedule(elements); onSubmit && typeof onSubmit === "function" && onSubmit(schedule); } }; const ScheduleBlock = ({ day, ranges: defaultRanges, selected: defaultSelected }: ScheduleBlockProps) => { const [ranges, setRanges] = React.useState(defaultRanges); const [selected, setSelected] = React.useState(defaultSelected); React.useEffect(() => { if (!ranges || ranges.length === 0) { setSelected(false); } else { setSelected(true); } }, [ranges]); const handleSelectedChange = () => { if (!selected && (!ranges || ranges.length === 0)) { setRanges([ { start: "09:00:00", end: "17:00:00", }, ]); } setSelected(!selected); }; const handleAddRange = () => { let rangeToAdd; if (!ranges || ranges?.length === 0) { rangeToAdd = { start: DEFAULT_START_TIME, end: DEFAULT_END_TIME, }; setRanges([rangeToAdd]); } else { const lastRange = ranges[ranges.length - 1]; const [hour, minute, second] = lastRange.end.split(":"); const date = dayjs() .set("hour", parseInt(hour)) .set("minute", parseInt(minute)) .set("second", parseInt(second)); const nextStartTime = date.add(1, "hour"); const nextEndTime = date.add(2, "hour"); /** * If next range goes over into "tomorrow" * i.e. time greater that last value in Times * return */ if (nextStartTime.isAfter(date.endOf("day"))) { return; } rangeToAdd = { start: nextStartTime.format(_24_HOUR_TIME_FORMAT), end: nextEndTime.format(_24_HOUR_TIME_FORMAT), }; setRanges([...ranges, rangeToAdd]); } }; const handleDeleteRange = (range: TimeRange) => { if (ranges && ranges.length > 0) { setRanges( ranges.filter((r: TimeRange) => { return r.start != range.start; }) ); } }; /** * Should update ranges values */ const handleSelectRangeChange = (event: React.ChangeEvent) => { const [day, rangeIndex, rangeId] = event.currentTarget.name.split("."); if (day && ranges) { const newRanges = ranges.map((range, index) => { const newRange = { ...range, [rangeId]: event.currentTarget.value, }; return index === parseInt(rangeIndex) ? newRange : range; }); setRanges(newRanges); } }; const TimeRangeField = ({ range, day, index }: { range: TimeRange; day: DayOfWeek; index: number }) => { const timeOptions = (type: "start" | "end") => TIMES.map((time) => ( )); return (
-
); }; const Actions = () => { return (
); }; const DeleteAction = ({ range }: { range: TimeRange }) => { return ( ); }; return (
1 ? "sm:items-start" : "sm:items-center" )}>
{day}
{selected && ranges && ranges.length != 0 ? ( ranges.map((range, index) => ( )) ) : ( Unavailable )}
); }; return ( <>
{Object.keys(schedule).map((day) => { const selected = schedule[day as DayOfWeek] != null; return ( ); })} ); }; export default SchedulerForm;