Fix dynamic min/max values for schedule form (#1940)
Co-authored-by: Alex van Andel <me@alexvanandel.com>pull/1950/head^2
parent
652c2e342f
commit
ac0c3bdfb9
|
@ -1,11 +1,15 @@
|
||||||
import { ClockIcon } from "@heroicons/react/outline";
|
import { ClockIcon } from "@heroicons/react/outline";
|
||||||
import { useRef } from "react";
|
import dayjs from "dayjs";
|
||||||
|
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
import { useLocale } from "@lib/hooks/useLocale";
|
||||||
import showToast from "@lib/notification";
|
import showToast from "@lib/notification";
|
||||||
|
|
||||||
import Button from "@components/ui/Button";
|
import Button from "@components/ui/Button";
|
||||||
|
|
||||||
|
dayjs.extend(customParseFormat);
|
||||||
|
|
||||||
interface SetTimesModalProps {
|
interface SetTimesModalProps {
|
||||||
startTime: number;
|
startTime: number;
|
||||||
endTime: number;
|
endTime: number;
|
||||||
|
@ -21,6 +25,11 @@ export default function SetTimesModal(props: SetTimesModalProps) {
|
||||||
const startMinsRef = useRef<HTMLInputElement>(null!);
|
const startMinsRef = useRef<HTMLInputElement>(null!);
|
||||||
const endHoursRef = useRef<HTMLInputElement>(null!);
|
const endHoursRef = useRef<HTMLInputElement>(null!);
|
||||||
const endMinsRef = useRef<HTMLInputElement>(null!);
|
const endMinsRef = useRef<HTMLInputElement>(null!);
|
||||||
|
const [endMinuteDisable, setEndMinuteDisable] = useState(false);
|
||||||
|
const [maximumStartTime, setMaximumStartTime] = useState({ hour: endHours, minute: 59 });
|
||||||
|
const [minimumEndTime, setMinimumEndTime] = useState({ hour: startHours, minute: 59 });
|
||||||
|
|
||||||
|
const STEP = 15;
|
||||||
|
|
||||||
const isValidTime = (startTime: number, endTime: number) => {
|
const isValidTime = (startTime: number, endTime: number) => {
|
||||||
if (new Date(startTime) > new Date(endTime)) {
|
if (new Date(startTime) > new Date(endTime)) {
|
||||||
|
@ -34,6 +43,48 @@ export default function SetTimesModal(props: SetTimesModalProps) {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// compute dynamic range for minimum and maximum allowed hours/minutes.
|
||||||
|
const setEdgeTimes = (
|
||||||
|
(step) =>
|
||||||
|
(
|
||||||
|
startHoursRef: React.MutableRefObject<HTMLInputElement>,
|
||||||
|
startMinsRef: React.MutableRefObject<HTMLInputElement>,
|
||||||
|
endHoursRef: React.MutableRefObject<HTMLInputElement>,
|
||||||
|
endMinsRef: React.MutableRefObject<HTMLInputElement>
|
||||||
|
) => {
|
||||||
|
//parse all the refs
|
||||||
|
const startHour = parseInt(startHoursRef.current.value);
|
||||||
|
let startMinute = parseInt(startMinsRef.current.value);
|
||||||
|
const endHour = parseInt(endHoursRef.current.value);
|
||||||
|
let endMinute = parseInt(endMinsRef.current.value);
|
||||||
|
|
||||||
|
//convert to dayjs object
|
||||||
|
const startTime = dayjs(`${startHour}-${startMinute}`, "hh:mm");
|
||||||
|
const endTime = dayjs(`${endHour}-${endMinute}`, "hh:mm");
|
||||||
|
|
||||||
|
//compute minimin and maximum allowed
|
||||||
|
const maximumStartTime = endTime.subtract(step, "minute");
|
||||||
|
const maximumStartHour = maximumStartTime.hour();
|
||||||
|
const maximumStartMinute = startHour === endHour ? maximumStartTime.minute() : 59;
|
||||||
|
|
||||||
|
const minimumEndTime = startTime.add(step, "minute");
|
||||||
|
const minimumEndHour = minimumEndTime.hour();
|
||||||
|
const minimumEndMinute = startHour === endHour ? minimumEndTime.minute() : 0;
|
||||||
|
|
||||||
|
//check allow min/max minutes when the end/start hour matches
|
||||||
|
if (startHoursRef.current.value === endHoursRef.current.value) {
|
||||||
|
if (parseInt(startMinsRef.current.value) >= maximumStartMinute)
|
||||||
|
startMinsRef.current.value = maximumStartMinute.toString();
|
||||||
|
if (parseInt(endMinsRef.current.value) <= minimumEndMinute)
|
||||||
|
endMinsRef.current.value = minimumEndMinute.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
//save into state
|
||||||
|
setMaximumStartTime({ hour: maximumStartHour, minute: maximumStartMinute });
|
||||||
|
setMinimumEndTime({ hour: minimumEndHour, minute: minimumEndMinute });
|
||||||
|
}
|
||||||
|
)(STEP);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-50 overflow-y-auto"
|
className="fixed inset-0 z-50 overflow-y-auto"
|
||||||
|
@ -65,7 +116,7 @@ export default function SetTimesModal(props: SetTimesModalProps) {
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-4 flex">
|
<div className="mb-4 flex">
|
||||||
<label className="block w-1/4 pt-2 text-sm font-medium text-gray-700">{t("start_time")}</label>
|
<label className="block w-1/4 pt-2 text-sm font-medium text-gray-700">{t("start_time")}</label>
|
||||||
<div>
|
<div className="w-1/6">
|
||||||
<label htmlFor="startHours" className="sr-only">
|
<label htmlFor="startHours" className="sr-only">
|
||||||
{t("hours")}
|
{t("hours")}
|
||||||
</label>
|
</label>
|
||||||
|
@ -73,17 +124,18 @@ export default function SetTimesModal(props: SetTimesModalProps) {
|
||||||
ref={startHoursRef}
|
ref={startHoursRef}
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="23"
|
max={maximumStartTime.hour}
|
||||||
maxLength={2}
|
minLength={2}
|
||||||
name="hours"
|
name="hours"
|
||||||
id="startHours"
|
id="startHours"
|
||||||
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
||||||
placeholder="9"
|
placeholder="9"
|
||||||
defaultValue={startHours}
|
defaultValue={startHours}
|
||||||
|
onChange={() => setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="mx-2 pt-1">:</span>
|
<span className="mx-2 pt-1">:</span>
|
||||||
<div>
|
<div className="w-1/6">
|
||||||
<label htmlFor="startMinutes" className="sr-only">
|
<label htmlFor="startMinutes" className="sr-only">
|
||||||
{t("minutes")}
|
{t("minutes")}
|
||||||
</label>
|
</label>
|
||||||
|
@ -91,27 +143,28 @@ export default function SetTimesModal(props: SetTimesModalProps) {
|
||||||
ref={startMinsRef}
|
ref={startMinsRef}
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="59"
|
max={maximumStartTime.minute}
|
||||||
step="15"
|
step={STEP}
|
||||||
maxLength={2}
|
maxLength={2}
|
||||||
name="minutes"
|
name="minutes"
|
||||||
id="startMinutes"
|
id="startMinutes"
|
||||||
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
||||||
placeholder="30"
|
placeholder="30"
|
||||||
defaultValue={startMinutes}
|
defaultValue={startMinutes}
|
||||||
|
onChange={() => setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<label className="block w-1/4 pt-2 text-sm font-medium text-gray-700">{t("end_time")}</label>
|
<label className="block w-1/4 pt-2 text-sm font-medium text-gray-700">{t("end_time")}</label>
|
||||||
<div>
|
<div className="w-1/6">
|
||||||
<label htmlFor="endHours" className="sr-only">
|
<label htmlFor="endHours" className="sr-only">
|
||||||
{t("hours")}
|
{t("hours")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
ref={endHoursRef}
|
ref={endHoursRef}
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min={minimumEndTime.hour}
|
||||||
max="24"
|
max="24"
|
||||||
maxLength={2}
|
maxLength={2}
|
||||||
name="hours"
|
name="hours"
|
||||||
|
@ -119,25 +172,32 @@ export default function SetTimesModal(props: SetTimesModalProps) {
|
||||||
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
||||||
placeholder="17"
|
placeholder="17"
|
||||||
defaultValue={endHours}
|
defaultValue={endHours}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (endHoursRef.current.value === "24") endMinsRef.current.value = "0";
|
||||||
|
setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef);
|
||||||
|
setEndMinuteDisable(endHoursRef.current.value === "24");
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="mx-2 pt-1">:</span>
|
<span className="mx-2 pt-1">:</span>
|
||||||
<div>
|
<div className="w-1/6">
|
||||||
<label htmlFor="endMinutes" className="sr-only">
|
<label htmlFor="endMinutes" className="sr-only">
|
||||||
{t("minutes")}
|
{t("minutes")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
ref={endMinsRef}
|
ref={endMinsRef}
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min={minimumEndTime.minute}
|
||||||
max="59"
|
max="59"
|
||||||
maxLength={2}
|
maxLength={2}
|
||||||
step="15"
|
step={STEP}
|
||||||
name="minutes"
|
name="minutes"
|
||||||
id="endMinutes"
|
id="endMinutes"
|
||||||
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
||||||
placeholder="30"
|
placeholder="30"
|
||||||
defaultValue={endMinutes}
|
defaultValue={endMinutes}
|
||||||
|
disabled={endMinuteDisable}
|
||||||
|
onChange={() => setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue