import React, { useState, useRef } from 'react';
import { useDrop } from 'react-dnd';
import { useReservations } from './ReservationsProvider';
import { Reservation } from '@/types/types';
const HOUR_HEIGHT = 50; // pixels per hour
export const roundToNearest15Min = (date: Date) => {
const minutes = date.getMinutes();
const remainder = minutes % 15;
const roundedMinutes =
remainder < 8 ? minutes - remainder : minutes + (15 - remainder);
const rounded = new Date(date);
rounded.setMinutes(roundedMinutes);
rounded.setSeconds(0);
rounded.setMilliseconds(0);
return rounded;
};
export type TimeSlotProps = {
hour: number;
date: Date;
onDrop: (reservation: Reservation, startTime: Date, endTime: Date) => void;
showHour?: 'left' | 'right';
};
export default function TimeSlot({ hour, date, onDrop, showHour }: TimeSlotProps) {
const { createReservation } = useReservations();
const [isDrawing, setIsDrawing] = useState(false);
const [startY, setStartY] = useState<number | null>(null);
const timeSlotRef = useRef<HTMLDivElement>(null);
const handleMouseDown = (e: React.MouseEvent) => {
setIsDrawing(true);
setStartY(e.clientY);
};
const handleMouseUp = (e: React.MouseEvent) => {
if (isDrawing && startY !== null) {
const rect = e.currentTarget.getBoundingClientRect();
const startTime = new Date(date);
const endTime = new Date(date);
const startMinute = Math.floor(((startY - rect.top) / HOUR_HEIGHT) * 60);
const endMinute = Math.floor(((e.clientY - rect.top) / HOUR_HEIGHT) * 60);
startTime.setHours(hour, Math.min(startMinute, endMinute));
endTime.setHours(hour + 2, Math.max(startMinute, endMinute));
const roundedStart = roundToNearest15Min(startTime);
const roundedEnd = roundToNearest15Min(endTime);
createReservation({
from_date: roundedStart.toISOString(),
to_date: roundedEnd.toISOString()
});
}
setIsDrawing(false);
setStartY(null);
};
const [{ isOver }, drop] = useDrop(() => ({
accept: 'RESERVATION',
drop: (item: any, monitor) => {
const dropOffset = monitor.getClientOffset();
if (!dropOffset) return;
const rect = timeSlotRef.current?.getBoundingClientRect();
if (!rect) return;
const offsetY = dropOffset.y - rect.top - item.offsetY;
const minute = Math.floor((offsetY / HOUR_HEIGHT) * 60);
const startTimeUnrounded = new Date(date);
startTimeUnrounded.setHours(hour, minute);
const startTime = roundToNearest15Min(startTimeUnrounded);
const duration =
new Date(item.to_date).getTime() - new Date(item.from_date).getTime();
const endTimeUnrounded = new Date(startTime.getTime() + duration);
const endTime = roundToNearest15Min(endTimeUnrounded);
onDrop(item, startTime, endTime);
},
collect: (monitor) => ({
isOver: monitor.isOver()
})
}));
drop(timeSlotRef);
return (
<div
ref={timeSlotRef}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
className={`border-b border-gray-200 relative ${
isOver ? 'bg-orange-100' : ''
}`}
style={{ height: HOUR_HEIGHT }}
>
{showHour && (
<span
className={`absolute ${showHour === 'left' ? '-left-0' : '-right-0'} top-0 text-xs text-gray-500`}
>
{hour.toString().padStart(2, '0')}:00
</span>
)}
</div>
);
}