import { useState } from 'react'; import { Box, Stack, Typography, IconButton, Button, Popover, Divider, Chip, useMediaQuery } from '@mui/material'; import { alpha } from '@mui/material/styles'; import dayjs from 'dayjs'; import CalendarTodayOutlinedIcon from '@mui/icons-material/CalendarTodayOutlined'; import ChevronLeftRoundedIcon from '@mui/icons-material/ChevronLeftRounded'; import ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded'; import ArrowRightAltRoundedIcon from '@mui/icons-material/ArrowRightAltRounded'; // ───────────────────────────────────────────────────────────────────────────── // Shared date-range picker — the same two-month brand calendar used on the // Dashboard, packaged for reuse. Controlled: pass `value={{ from, to }}` (both // 'YYYY-MM-DD') and an `onChange({ from, to })` handler. Optionally cap the // selectable range with `maxDate` (defaults to today, so no future dates). // ───────────────────────────────────────────────────────────────────────────── export const DATE_FMT = 'YYYY-MM-DD'; const BRAND = '#C01227'; const DAY_LABELS = ['S', 'M', 'T', 'W', 'T', 'F', 'S']; // A single compact month grid. function MonthGrid({ view, start, end, max, onDay }) { const gridStart = view.startOf('month').subtract(view.startOf('month').day(), 'day'); const cells = Array.from({ length: 42 }, (_, i) => gridStart.add(i, 'day')); return ( {view.format('MMMM YYYY')} {DAY_LABELS.map((d, i) => ( {d} ))} {cells.map((d) => { const inMonth = d.month() === view.month(); const isStart = start && d.isSame(start, 'day'); const isEnd = end && d.isSame(end, 'day'); const isEndpoint = isStart || isEnd; const inRange = start && end && d.isAfter(start, 'day') && d.isBefore(end, 'day'); const isToday = d.isSame(dayjs(), 'day'); const disabled = max && d.isAfter(max, 'day'); return ( onDay(d)} sx={{ width: 28, height: 28, m: '1px', border: 'none', cursor: disabled ? 'default' : 'pointer', borderRadius: 1.5, fontSize: '0.72rem', fontFamily: 'inherit', fontWeight: isEndpoint ? 700 : 500, color: disabled ? '#D5D9DD' : isEndpoint ? '#fff' : inMonth ? '#3C4043' : '#C4C9CE', bgcolor: isEndpoint ? BRAND : 'transparent', boxShadow: isToday && !isEndpoint ? `inset 0 0 0 1.5px ${BRAND}` : 'none', transition: 'background-color .12s', '&:hover': { bgcolor: disabled ? 'transparent' : isEndpoint ? '#9E0E20' : alpha(BRAND, 0.1) } }} > {d.date()} ); })} ); } // Click a day to set the start, click again to set the end (auto-swaps if reversed). function RangeCalendar({ from, to, maxDate, onSelect }) { const [view, setView] = useState(dayjs(to || from || undefined).startOf('month')); const [anchorDate, setAnchorDate] = useState(null); const start = from ? dayjs(from) : null; const end = to ? dayjs(to) : null; const max = maxDate ? dayjs(maxDate) : null; const handleDay = (d) => { if (!anchorDate) { setAnchorDate(d); onSelect(d.format(DATE_FMT), d.format(DATE_FMT)); } else { const a = anchorDate; const lo = d.isBefore(a) ? d : a; const hi = d.isBefore(a) ? a : d; onSelect(lo.format(DATE_FMT), hi.format(DATE_FMT)); setAnchorDate(null); } }; return ( setView((v) => v.subtract(1, 'month'))} sx={{ color: '#9AA0A6', p: 0.5 }}> setView((v) => v.add(1, 'month'))} sx={{ color: '#9AA0A6', p: 0.5 }}> ); } const PRESETS = [ { label: 'Today', days: 1 }, { label: 'Last 7 days', days: 7 }, { label: 'Last 30 days', days: 30 } ]; export default function DateRangePicker({ value, onChange, maxDate }) { const today = dayjs().format(DATE_FMT); const max = maxDate ?? today; const [anchor, setAnchor] = useState(null); const isMobile = useMediaQuery('(max-width:600px)'); const from = value?.from || today; const to = value?.to || today; const invalid = dayjs(to).isBefore(dayjs(from)); const dayCount = (() => { const f = dayjs(from); const t = dayjs(to); if (!f.isValid() || !t.isValid() || t.isBefore(f)) return 1; return t.diff(f, 'day') + 1; })(); const applyPreset = (days) => onChange({ from: dayjs().subtract(days - 1, 'day').format(DATE_FMT), to: today }); const isPreset = (days) => to === today && from === dayjs().subtract(days - 1, 'day').format(DATE_FMT); return ( <> {PRESETS.map((p) => { const active = isPreset(p.days); return ( applyPreset(p.days)} sx={{ height: 36, fontSize: '0.825rem', fontWeight: 600, borderRadius: 2, bgcolor: active ? BRAND : '#F1F5F9', color: active ? '#fff' : '#475569', boxShadow: active ? '0 4px 10px rgba(192,18,39,0.15)' : 'none', '& .MuiChip-label': { px: 1.5 }, '&:hover': { bgcolor: active ? '#9E0E20' : '#E2E8F0' } }} /> ); })} setAnchor(null)} anchorOrigin={{ vertical: 'bottom', horizontal: isMobile ? 'left' : 'right' }} transformOrigin={{ vertical: 'top', horizontal: isMobile ? 'left' : 'right' }} marginThreshold={12} PaperProps={{ sx: { mt: 1, borderRadius: 2.5, border: '1px solid #EEF0F2', boxShadow: '0 8px 28px rgba(0,0,0,0.10)', overflow: 'hidden', maxWidth: 'calc(100vw - 24px)' } }} > Select date range {dayjs(from).format('DD MMM')} – {dayjs(to).format('DD MMM YYYY')} · {dayCount} {dayCount === 1 ? 'day' : 'days'} onChange({ from: f, to: t })} /> ); }