import { useEffect, useRef, useState } from 'react' import DatePicker from 'react-datepicker' import { format } from 'date-fns' import { __ } from '@wordpress/i18n' import { TimeSlot } from '../BusinessHoursField/TimeSlot' import { DateTimeRangeItem, DateTimeRangeSlot } from './types' import { createDefaultTimeSlot, hasOverlappingTimeSlots, } from './utils' import calendarIcon from '../../../../../../public/images/icon-calendar.svg' import trashIcon from '../../../../../../public/images/icon-trash.svg' import plusIcon from '../../../../../../public/images/icon-plus-green.svg' import 'react-datepicker/dist/react-datepicker.css' type DateTimeRangeRowProps = { item: DateTimeRangeItem displayFormat: string onChange: (next: DateTimeRangeItem) => void onRemove: () => void } export const DateTimeRangeRow = ({ item, displayFormat, onChange, onRemove, }: DateTimeRangeRowProps) => { const [calendarOpen, setCalendarOpen] = useState(false) const [calendarRange, setCalendarRange] = useState<[Date | null, Date | null]>([ item.startDate, item.endDate, ]) const wrapperRef = useRef(null) const inputRef = useRef(null) useEffect(() => { setCalendarRange([item.startDate, item.endDate]) }, [item.startDate, item.endDate]) useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( wrapperRef.current && !wrapperRef.current.contains(event.target as Node) ) { setCalendarOpen(false) } } if (calendarOpen) { document.addEventListener('mousedown', handleClickOutside) } return () => { document.removeEventListener('mousedown', handleClickOutside) } }, [calendarOpen]) const formatRangeForDisplay = () => { const start = item.startDate ? format(item.startDate, displayFormat) : '' const end = item.endDate ? format(item.endDate, displayFormat) : '' if (start && end) { return `${start} - ${end}` } return start || end || '' } const handleRangeChange = (newRange: [Date | null, Date | null]) => { setCalendarRange(newRange) onChange({ ...item, startDate: newRange[0], endDate: newRange[1], }) if (newRange[0] && newRange[1]) { setCalendarOpen(false) } } const updateTimeSlot = ( slotIndex: number, updates: Partial> ) => { const slots = [...item.timeSlots] slots[slotIndex] = { ...slots[slotIndex], ...updates } onChange({ ...item, timeSlots: slots }) } const addTimeSlot = () => { const slots = item.timeSlots.filter( (slot) => !(slot.start === 0 && slot.end === 0) ) const lastSlot = slots[slots.length - 1] const newSlot: DateTimeRangeSlot = lastSlot ? { start: lastSlot.end, end: lastSlot.end + 10800, status: 'active', } : createDefaultTimeSlot() onChange({ ...item, timeSlots: [...slots, newSlot], }) } const removeTimeSlot = (slotIndex: number) => { onChange({ ...item, timeSlots: item.timeSlots.filter((_, index) => index !== slotIndex), }) } const visibleSlots = item.timeSlots.filter( (slot) => !(slot.start === 0 && slot.end === 0) ) return (
{ setCalendarOpen(true) setCalendarRange([item.startDate, item.endDate]) }} /> {calendarOpen && (
handleRangeChange(range) } startDate={calendarRange[0]} endDate={calendarRange[1]} selectsRange inline calendarClassName="wbk_dateTimeRangesField__calendar" dayClassName={() => 'wbk_dateTimeRangesField__day wbk_dateTimeRangesField__day--dayNotSelected' } />
)}
{__('Time slots', 'webba-booking-lite')}
{visibleSlots.length === 0 ? (
{__('No time slots set', 'webba-booking-lite')}
) : ( visibleSlots.map((slot, slotIndex) => ( updateTimeSlot(slotIndex, { start }) } onEndChange={(end) => updateTimeSlot(slotIndex, { end }) } onStatusChange={(status) => updateTimeSlot(slotIndex, { status }) } onRemove={() => removeTimeSlot(slotIndex)} /> )) )}
{hasOverlappingTimeSlots(item.timeSlots) && (
{__( 'Time slots cannot overlap within this date range.', 'webba-booking-lite' )}
)}
) }