diff --git a/web/src/components/TimePicker.jsx b/web/src/components/TimePicker.jsx index edb39bd52..4a70e9c29 100644 --- a/web/src/components/TimePicker.jsx +++ b/web/src/components/TimePicker.jsx @@ -1,182 +1,18 @@ import { h } from 'preact'; -import { useCallback, useEffect, useMemo, useState } from 'preact/hooks'; +import { useState } from 'preact/hooks'; import { ArrowDropdown } from '../icons/ArrowDropdown'; import { ArrowDropup } from '../icons/ArrowDropup'; +import Heading from './Heading'; -const TimePicker = ({ dateRange, onChange }) => { - const [error, setError] = useState(null); - const [timeRange, setTimeRange] = useState(new Set()); - const [hoverIdx, setHoverIdx] = useState(null); - const [reset, setReset] = useState(false); +const TimePicker = ({ timeRange, onChange }) => { + const times = timeRange.split(','); + const [after, setAfter] = useState(times[0]); + const [before, setBefore] = useState(times[1]); - /** - * Initializes two variables before and after with date objects, - * If they are not null, it creates a new Date object with the value of the property and if not, - * it creates a new Date object with the current hours to 0 and 24 respectively. - */ - const before = useMemo(() => { - return dateRange.before ? new Date(dateRange.before) : new Date(new Date().setHours(24, 0, 0, 0)); - }, [dateRange]); - - const after = useMemo(() => { - return dateRange.after ? new Date(dateRange.after) : new Date(new Date().setHours(0, 0, 0, 0)); - }, [dateRange]); - - useEffect(() => { - /** - * This will reset hours when user selects another date in the calendar. - */ - if (before.getHours() === 0 && after.getHours() === 0 && timeRange.size > 1) return setTimeRange(new Set()); - }, [after, before, timeRange]); - - useEffect(() => { - if (reset || !after) return; - /** - * calculates the number of hours between two dates, by finding the difference in days, - * converting it to hours and adding the hours from the before date. - */ - const days = Math.max(before.getDate() - after.getDate()); - const hourOffset = days * 24; - const beforeOffset = before.getHours() ? hourOffset + before.getHours() : 0; - - /** - * Fills the timeRange by iterating over the hours between 'after' and 'before' during component mount, to keep the selected hours persistent. - */ - for (let hour = after.getHours(); hour < beforeOffset; hour++) { - setTimeRange((timeRange) => timeRange.add(hour)); - } - - /** - * find an element by the id timeIndex- concatenated with the minimum value from timeRange array, - * and if that element is present, it will scroll into view if needed - */ - if (timeRange.size > 1) { - const element = document.getElementById(`timeIndex-${Math.max(...timeRange)}`); - if (element) { - element.scrollIntoViewIfNeeded(true); - } - } - }, [after, before, timeRange, reset]); - - /** - * numberOfDaysSelected is a set that holds the number of days selected in the dateRange. - * The loop iterates through the days starting from the after date's day to the before date's day. - * If the before date's hour is 0, it skips it. - */ - const numberOfDaysSelected = useMemo(() => { - return new Set([...Array(Math.max(1, before.getDate() - after.getDate() + 1))].map((_, i) => after.getDate() + i)); - }, [before, after]); - - if (before.getHours() === 0) numberOfDaysSelected.delete(before.getDate()); - - // Create repeating array with the number of hours for each day selected ...23,24,0,1,2... - const hoursInDays = useMemo(() => { - return Array.from({ length: numberOfDaysSelected.size * 24 }, (_, i) => i % 24); - }, [numberOfDaysSelected]); - - // function for handling the selected time from the provided list - const handleTime = useCallback( - (hour) => { - if (isNaN(hour)) return; - - const _timeRange = new Set([...timeRange]); - _timeRange.add(hour); - - // reset error messages - setError(null); - - /** - * Check if the variable "hour" exists in the "timeRange" set. - * If it does, reset the timepicker - */ - if (timeRange.has(hour)) { - setTimeRange(new Set()); - setReset(true); - const resetBefore = before.setDate(after.getDate() + numberOfDaysSelected.size - 1); - return onChange({ - after: after.setHours(0, 0, 0, 0) / 1000, - before: new Date(resetBefore).setHours(24, 0, 0, 0) / 1000, - }); - } - - //update after - if (_timeRange.size === 1) { - // check if the first selected value is within first day - const firstSelectedHour = Math.ceil(Math.max(..._timeRange)); - if (firstSelectedHour > 23) { - return setError('Select a time on the initial day!'); - } - - // calculate days offset - const dayOffsetAfter = new Date(after).setHours(Math.min(..._timeRange)); - - let dayOffsetBefore = before; - if (numberOfDaysSelected.size === 1) { - dayOffsetBefore = new Date(after).setHours(Math.min(..._timeRange) + 1); - } - - onChange({ - after: dayOffsetAfter / 1000, - before: dayOffsetBefore / 1000, - }); - } - - //update before - if (_timeRange.size > 1) { - let selectedDay = Math.ceil(Math.max(..._timeRange) / 24); - - // if user selects time 00:00 for the next day, add one day - if (hour === 24 && selectedDay === numberOfDaysSelected.size - 1) { - selectedDay += 1; - } - - // Check if end time is on the last day - if (selectedDay !== numberOfDaysSelected.size) { - return setError('Ending must occur on final day!'); - } - - // Check if end time is later than start time - const startHour = Math.min(..._timeRange); - if (hour <= startHour) { - return setError('Ending hour must be greater than start time!'); - } - - // Add all hours between start and end times to the set - for (let x = startHour; x <= hour; x++) { - _timeRange.add(x); - } - - // calculate days offset - const dayOffsetBefore = new Date(dateRange.after); - onChange({ - after: dateRange.after / 1000, - // we add one hour to get full 60min of last selected hour - before: dayOffsetBefore.setHours(Math.max(..._timeRange) + 1) / 1000, - }); - } - - for (let i = 0; i < _timeRange.size; i++) { - setTimeRange((timeRange) => timeRange.add(Array.from(_timeRange)[i])); - } - }, - [after, before, timeRange, dateRange.after, numberOfDaysSelected.size, onChange] - ); - const isSelected = useCallback( - (idx) => { - return !!timeRange.has(idx); - }, - [timeRange] - ); - - const isHovered = useCallback( - (idx) => { - return timeRange.size === 1 && idx > Math.max(...timeRange) && idx <= hoverIdx; - }, - [timeRange, hoverIdx] - ); + // Create repeating array with the number of hours for 1 day ...23,24,0,1,2... + const hoursInDays = Array.from({ length: 24 }, (_, i) => String(i % 24).padStart(2, '0')); // background colors for each day - const isSelectedCss = 'bg-blue-600 transition duration-300 ease-in-out hover:rounded-none'; function randomGrayTone(shade) { const grayTones = [ 'bg-[#212529]/50', @@ -193,44 +29,72 @@ const TimePicker = ({ dateRange, onChange }) => { return grayTones[shade % grayTones.length]; } + const isSelected = (idx, current) => { + return current == `${idx}:00`; + }; + + const isSelectedCss = 'bg-blue-600 transition duration-300 ease-in-out hover:rounded-none'; + const handleTime = (after, before) => { + setAfter(after); + setBefore(before); + onChange(`${after},${before}`); + }; + return ( <> - {error ? {error} : null}