'use client'; import { ChangeEventHandler, FocusEventHandler, useEffect, useState, } from 'react'; import { cn, formatDate } from '@/lib/helper'; import Modal, { useModal } from '@/components/Modal'; import { DateRange, DayPicker, Matcher } from 'react-day-picker'; import 'react-day-picker/dist/style.css'; import Button from '@/components/Button'; import { Icon } from '@iconify/react'; export interface DateInputProps { label?: string; bottomLabel?: string; name: string; value?: string | { from?: string; to?: string }; placeholder?: string; min?: string; max?: string; className?: { wrapper?: string; label?: string; inputWrapper?: string; input?: string; }; isError?: boolean; isValid?: boolean; disabled?: boolean; readOnly?: boolean; required?: boolean; isLoading?: boolean; isRange?: boolean; errorMessage?: string; onChange?: ChangeEventHandler; onBlur?: FocusEventHandler; } const DateInput = ({ label, bottomLabel, name, value, placeholder = 'dd/mm/yyyy', min, max, className, isError: externalError, isValid: externalValid, errorMessage: externalErrorMessage, disabled = false, required = false, onChange, onBlur, readOnly = false, isLoading = false, isRange = false, }: DateInputProps) => { const [internalError, setInternalError] = useState(null); const [selected, setSelected] = useState(); const [selectedRange, setSelectedRange] = useState<{ from?: Date; to?: Date; }>({}); const [displayValue, setDisplayValue] = useState(''); const minDate = min ? new Date(min.split('/').reverse().join('-')) : undefined; const maxDate = max ? new Date(max.split('/').reverse().join('-')) : undefined; const calendarModal = useModal(); // --- Sync value props --- useEffect(() => { if (!value) return; if (isRange && typeof value === 'object') { const from = value.from ? new Date(value.from) : undefined; const to = value.to ? new Date(value.to) : undefined; setSelectedRange({ from, to }); setDisplayValue( `${from ? formatDate(from, 'DD/MM/YYYY') : ''} ${ to ? '- ' + formatDate(to, 'DD/MM/YYYY') : '' }` ); } else if (typeof value === 'string') { const iso = value.includes('/') ? value.split('/').reverse().join('-') : value; const date = new Date(iso); setSelected(date); setDisplayValue(formatDate(iso, 'DD/MM/YYYY')); } }, [value, isRange]); const handleClick = (e: React.MouseEvent) => { e.preventDefault(); if (!disabled && !readOnly) calendarModal.openModal(); }; const handleBlur: FocusEventHandler = (e) => { onBlur?.(e); }; const handleSelectSingle = (selectedDate?: Date) => { if (!selectedDate) return; if (minDate && selectedDate < minDate) { setInternalError(`Tanggal tidak boleh sebelum ${min}`); return; } if (maxDate && selectedDate > maxDate) { setInternalError(`Tanggal tidak boleh setelah ${max}`); return; } setInternalError(null); setSelected(selectedDate); const formattedDisplay = formatDate(selectedDate, 'DD/MM/YYYY'); const formattedISO = formatDate(selectedDate, 'YYYY-MM-DD'); setDisplayValue(formattedDisplay); const syntheticEvent = { target: { name, value: formattedISO }, } as unknown as React.ChangeEvent; onChange?.(syntheticEvent); calendarModal.closeModal(); }; const handleSelectRange = (range?: { from?: Date; to?: Date }) => { if (!range) return; setSelectedRange(range); const fromStr = range.from ? formatDate(range.from, 'DD/MM/YYYY') : ''; const toStr = range.to ? formatDate(range.to, 'DD/MM/YYYY') : ''; setDisplayValue(`${fromStr}${toStr ? ' - ' + toStr : ''}`); // Jika kedua tanggal sudah terpilih if (range.from && range.to) { if (minDate && range.from < minDate) { setInternalError(`Tanggal mulai tidak boleh sebelum ${min}`); return; } if (maxDate && range.to > maxDate) { setInternalError(`Tanggal akhir tidak boleh setelah ${max}`); return; } setInternalError(null); const syntheticEvent = { target: { name, value: { from: formatDate(range.from, 'YYYY-MM-DD'), to: formatDate(range.to, 'YYYY-MM-DD'), }, }, } as unknown as React.ChangeEvent; onChange?.(syntheticEvent); } }; const handleResetDate = () => { setSelected(undefined); setSelectedRange({}); setDisplayValue(''); const syntheticEvent = { target: { name, value: isRange ? { from: '', to: '' } : '' }, } as unknown as React.ChangeEvent; onChange?.(syntheticEvent); calendarModal.closeModal(); }; const handleSaveDate = () => { if (internalError) return; calendarModal.closeModal(); }; const finalIsError = externalError || !!internalError; const finalErrorMessage = internalError || externalErrorMessage; return (
{label && ( )}
{isLoading && (
)} handleClick(e as unknown as React.MouseEvent) } />
{!finalIsError && bottomLabel && (

{bottomLabel}

)} {finalIsError && finalErrorMessage && (

{finalErrorMessage}

)} {isRange ? ( {displayValue}
} disabled={ [ minDate ? { before: minDate } : undefined, maxDate ? { after: maxDate } : undefined, ].filter(Boolean) as Matcher[] } /> ) : ( )}
{isRange && ( Tekan dua kali untuk memilih tanggal awal )}
{isRange && ( )}
); }; export default DateInput;