194 lines
6.0 KiB
JavaScript

import React, { useState, useRef, useEffect } from "react";
import {THRESH_HOLD} from "../../utils/constants"
const TimePicker = ({ label, onChange, interval = 10, value,checkInTime,checkOutTime }) => {
const [time, setTime] = useState(value || "");
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(null);
const slotRefs = useRef({});
const getCurrentTime = () => {
const now = new Date();
const minutes = now.getMinutes();
const roundedMinutes = Math.round(minutes / interval) * interval;
now.setMinutes(roundedMinutes);
now.setSeconds(0);
now.setMilliseconds(0);
return now;
};
const formatTime = (date) => {
let hours = date.getHours();
const minutes = date.getMinutes();
const period = hours >= 12 ? "PM" : "AM";
hours = hours % 12; // Convert 24-hour format to 12-hour format
hours = hours ? hours : 12; // The hour '0' should be '12'
const hoursFormatted = hours < 10 ? `0${hours}` : hours; // Ensure two-digit hour
const minutesFormatted = minutes < 10 ? `0${minutes}` : minutes; // Ensure two-digit minutes
return `${hoursFormatted}:${minutesFormatted} ${period}`;
};
const generateTimeSlots = () => {
const slots = [];
const now = new Date();
const currentSlot = new Date();
currentSlot.setMinutes(
Math.floor(currentSlot.getMinutes() / interval) * interval
);
currentSlot.setSeconds(0);
currentSlot.setMilliseconds(0);
const checkInDate = checkInTime ? new Date(checkInTime) : null;
const checkOutDate = checkOutTime ? new Date(checkOutTime) : null;
const dayStart = new Date();
const dayEnd = new Date();
if (checkInDate) {
dayStart.setTime(checkInDate.getTime());
dayEnd.setTime(checkInDate.getTime());
}
dayStart.setHours(0, 0, 0, 0);
dayEnd.setHours(23, 59, 59, 999);
let minSelectable = null;
let maxSelectable = null;
// Activity 0: Time slots based on current time
if (!checkInDate) {
// Case 1: No check-in time (new check-in)
minSelectable = new Date(dayStart);
maxSelectable = new Date(currentSlot);
}
// Activity 1: Time slots based on check-in time today
else if (checkInDate && !checkOutDate) {
const hoursDiff = (now - checkInDate) / (1000 * 60 * 60);
if (hoursDiff < THRESH_HOLD) {
// Case 2: Check-in present, no checkout, within THRESH_HOLD hours
minSelectable = new Date(checkInDate);
maxSelectable = new Date(currentSlot);
} else {
// Case 4: Check-in present, no checkout, more than THRESH_HOLD hours
minSelectable = new Date(checkInDate);
maxSelectable = new Date(dayEnd);
}
} else if (checkInDate && checkOutDate) {
// Case 3: Both check-in and checkout present
const isSameDay = new Date(checkOutDate).toDateString() === new Date().toDateString();
minSelectable = new Date(checkOutDate);
maxSelectable = isSameDay ? new Date(dayEnd) : new Date(currentSlot);
}
const slot = new Date(dayStart.getTime());
while (slot <= dayEnd) {
const formatted = formatTime(slot);
const isSelectable = slot >= minSelectable && slot <= maxSelectable;
slots.push({ time: formatted, isSelectable });
slot.setMinutes(slot.getMinutes() + interval);
}
return slots;
};
const handleSelect = (selectedTime) => {
setTime(selectedTime);
if (onChange) onChange(selectedTime);
setIsOpen(false);
};
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
useEffect(() => {
if (!value) {
const defaultTime = formatTime(getCurrentTime());
setTime(defaultTime);
if (onChange) onChange(defaultTime);
}
}, [value, interval, onChange]);
useEffect(() => {
if (isOpen && time && slotRefs.current[time]) {
const selectedSlot = slotRefs.current[time];
selectedSlot.scrollIntoView({
behavior: "smooth",
block: "center",
});
}
}, [isOpen, time]);
return (
<div className="position-relative w-100" ref={dropdownRef}>
{label && <label className="form-label">{label}</label>}
<div className="position-relative">
<input
type="text"
className="form-control pe-5"
value={time}
readOnly
onClick={() => setIsOpen(!isOpen)}
style={{ cursor: "pointer" }}
/>
<span
className="position-absolute end-0 top-50 translate-middle-y pe-3 d-flex align-items-center"
style={{ cursor: "pointer" }}
onClick={() => setIsOpen(!isOpen)}
>
<i className="bx bx-time fs-4"></i>
</span>
</div>
{isOpen && (
<div
className="dropdown-menu show shadow-lg border rounded p-2"
style={{
position: "absolute",
width: "auto",
background: "#fff",
display: "flex",
flexDirection: "column",
gap: "5px",
maxHeight: "200px",
overflowY: "auto",
}}
>
{generateTimeSlots().map(({ time: slotTime, isSelectable }, index) => (
<button
key={index}
className={`dropdown-item px-3 py-1 text-center rounded-1 ${
time === slotTime ? "btn-primary" : ""
}`}
ref={(el) => (slotRefs.current[slotTime] = el)}
onClick={(event) => {
event.preventDefault();
if (isSelectable) handleSelect(slotTime);
}}
disabled={!isSelectable}
style={
!isSelectable ? { opacity: 0.5, cursor: "not-allowed" } : {}
}
>
{slotTime}
</button>
))}
</div>
)}
</div>
);
};
export default TimePicker;