UNPKG

@pagamio/frontend-commons-lib

Version:

Pagamio library for Frontend reusable components like the form engine and table container

90 lines (89 loc) 6.21 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { endOfDay, format, isBefore, isEqual, isValid, parse, parseISO, startOfDay } from 'date-fns'; import { Modal } from 'flowbite-react'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import { useEffect, useState } from 'react'; import { Button } from '../ui'; const CustomDateRangeModal = ({ isOpen, showTimeSelect, onClose, onApply, initialStartDate, initialEndDate, minDate, maxDate, dateFormat = 'dd/MM/yyyy', timeFormat = 'h:mm aa', timeIntervals = 15, }) => { // Parse date string to Date object using the specified format const parseInitialDate = (dateString, isEndDate = false) => { if (!dateString) { const now = new Date(); return isEndDate ? endOfDay(now) : startOfDay(now); } try { // Try to parse using the provided format const parsedDate = parse(dateString, dateFormat, new Date()); // Check if the date is valid if (!isValid(parsedDate)) { throw new Error('Invalid date'); } return isEndDate ? endOfDay(parsedDate) : startOfDay(parsedDate); } catch (error) { // Fallback to ISO format if the format parsing fails try { const isoDate = parseISO(dateString); if (isValid(isoDate)) { return isEndDate ? endOfDay(isoDate) : startOfDay(isoDate); } } catch { // Ignore ISO parsing error console.error(error); } // Return current date if all parsing attempts fail const now = new Date(); return isEndDate ? endOfDay(now) : startOfDay(now); } }; const [startDate, setStartDate] = useState(parseInitialDate(initialStartDate)); const [endDate, setEndDate] = useState(parseInitialDate(initialEndDate, true)); const [isValidRange, setIsValidRange] = useState(true); // Define the complete format for DatePicker const datePickerFormat = showTimeSelect ? `${dateFormat} ${timeFormat}` : dateFormat; // Update internal state when initial values change useEffect(() => { if (initialStartDate) setStartDate(parseInitialDate(initialStartDate)); if (initialEndDate) setEndDate(parseInitialDate(initialEndDate, true)); }, [initialStartDate, initialEndDate, dateFormat]); // Validate date range whenever dates change useEffect(() => { setIsValidRange(isBefore(startDate, endDate) ?? isEqual(startDate, endDate)); }, [startDate, endDate]); const handleApply = () => { if (!isValidRange) return; // Format dates appropriately before applying const formattedStartDate = showTimeSelect ? format(startDate, `${dateFormat} ${timeFormat}`) : format(startOfDay(startDate), dateFormat); const formattedEndDate = showTimeSelect ? format(endDate, `${dateFormat} ${timeFormat}`) : format(endOfDay(endDate), dateFormat); onApply(formattedStartDate, formattedEndDate); onClose(); }; const handleStartDateChange = (date) => { if (date) { const newDate = showTimeSelect ? date : startOfDay(date); setStartDate(newDate); // Auto-adjust end date if it's before start date if (isBefore(endDate, newDate)) { const newEndDate = showTimeSelect ? newDate : endOfDay(newDate); setEndDate(newEndDate); } } }; const handleEndDateChange = (date) => { if (date) { const newDate = showTimeSelect ? date : endOfDay(date); setEndDate(newDate); } }; return (_jsxs(Modal, { show: isOpen, size: "xl", popup: true, onClose: onClose, dismissible: true, children: [_jsx(Modal.Header, { className: "border-b border-gray-200", children: _jsx("h3", { className: "px-4 text-lg font-bold text-gray-900", children: "Select Custom Date Range" }) }), _jsx(Modal.Body, { className: "p-6", children: _jsxs("div", { className: "flex flex-col space-y-4", children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col", children: [_jsx("label", { htmlFor: "startDate", className: "mb-2 text-sm font-medium text-gray-700", children: "Start Date" }), _jsx(DatePicker, { selected: startDate, onChange: handleStartDateChange, selectsStart: true, startDate: startDate, endDate: endDate, showTimeSelect: showTimeSelect, timeIntervals: timeIntervals, dateFormat: datePickerFormat, placeholderText: "Select start date", className: `w-full rounded-md border ${!isValidRange ? 'border-red-500' : 'border-gray-300'} px-3 py-2 shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500`, minDate: minDate, maxDate: maxDate })] }), _jsxs("div", { className: "flex flex-col", children: [_jsx("label", { htmlFor: "endDate", className: "mb-2 text-sm font-medium text-gray-700", children: "End Date" }), _jsx(DatePicker, { selected: endDate, onChange: handleEndDateChange, selectsEnd: true, startDate: startDate, endDate: endDate, minDate: startDate, maxDate: maxDate, showTimeSelect: showTimeSelect, timeIntervals: timeIntervals, dateFormat: datePickerFormat, placeholderText: "Select end date", className: `w-full rounded-md border ${!isValidRange ? 'border-red-500' : 'border-gray-300'} px-3 py-2 shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500` })] })] }), !isValidRange && _jsx("p", { className: "text-sm text-red-600 mt-2", children: "End date must be on or after start date." })] }) }), _jsxs(Modal.Footer, { className: "flex justify-end space-x-3 border-t border-gray-200 px-6 py-4", children: [_jsx(Button, { variant: "outline", onClick: onClose, className: "px-4 py-2", children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: handleApply, disabled: !isValidRange, className: "px-4 py-2 disabled:opacity-50 disabled:cursor-not-allowed", children: "Apply" })] })] })); }; export default CustomDateRangeModal;