@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
JavaScript
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;