98 lines
3.9 KiB
TypeScript
98 lines
3.9 KiB
TypeScript
import { useMemo } from 'react';
|
|
import { DateRangeInput, type DateRange as AstryxDateRange, type ISODateString } from '@astryxdesign/core';
|
|
import type { DateRange } from '@/api/insights';
|
|
|
|
export type RangePreset = 'today' | 'yesterday' | 'week' | 'month' | 'custom';
|
|
|
|
/** `YYYY-MM-DD` in LOCAL time — `toISOString()` would shift a shop in IST back a day. */
|
|
export function isoDay(date: Date): string {
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
return `${year}-${month}-${day}`;
|
|
}
|
|
|
|
export function presetRange(preset: RangePreset, now = new Date()): DateRange {
|
|
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
|
|
switch (preset) {
|
|
case 'today':
|
|
return { fromdate: isoDay(today), todate: isoDay(today) };
|
|
case 'yesterday': {
|
|
const yesterday = new Date(today);
|
|
yesterday.setDate(today.getDate() - 1);
|
|
return { fromdate: isoDay(yesterday), todate: isoDay(yesterday) };
|
|
}
|
|
case 'week': {
|
|
// Monday-first. A retail week that starts on Sunday makes Monday's
|
|
// takings the previous week's, which is not how anyone here counts.
|
|
const weekday = (today.getDay() + 6) % 7;
|
|
const monday = new Date(today);
|
|
monday.setDate(today.getDate() - weekday);
|
|
return { fromdate: isoDay(monday), todate: isoDay(today) };
|
|
}
|
|
case 'month': {
|
|
const first = new Date(today.getFullYear(), today.getMonth(), 1);
|
|
return { fromdate: isoDay(first), todate: isoDay(today) };
|
|
}
|
|
case 'custom':
|
|
return {};
|
|
}
|
|
}
|
|
|
|
export interface DateRangePickerProps {
|
|
preset: RangePreset;
|
|
range: DateRange;
|
|
onChange: (preset: RangePreset, range: DateRange) => void;
|
|
}
|
|
|
|
export function DateRangePicker({ range, onChange }: DateRangePickerProps) {
|
|
const astryxRange = useMemo<AstryxDateRange | null>(() => {
|
|
if (range.fromdate && range.todate) {
|
|
return { start: range.fromdate as ISODateString, end: range.todate as ISODateString };
|
|
}
|
|
return null;
|
|
}, [range]);
|
|
|
|
const presets = useMemo(() => {
|
|
return [
|
|
{ label: 'Today', getRange: () => { const r = presetRange('today'); return { start: r.fromdate as ISODateString, end: r.todate as ISODateString }; } },
|
|
{ label: 'Yesterday', getRange: () => { const r = presetRange('yesterday'); return { start: r.fromdate as ISODateString, end: r.todate as ISODateString }; } },
|
|
{ label: 'This week', getRange: () => { const r = presetRange('week'); return { start: r.fromdate as ISODateString, end: r.todate as ISODateString }; } },
|
|
{ label: 'This month', getRange: () => { const r = presetRange('month'); return { start: r.fromdate as ISODateString, end: r.todate as ISODateString }; } },
|
|
];
|
|
}, []);
|
|
|
|
return (
|
|
<div style={{ width: 180 }}>
|
|
<DateRangeInput
|
|
width={180}
|
|
size="sm"
|
|
label="Date range"
|
|
isLabelHidden
|
|
value={astryxRange}
|
|
presets={presets}
|
|
onChange={(newRange) => {
|
|
if (!newRange) {
|
|
onChange('custom', {});
|
|
return;
|
|
}
|
|
// Determine if the new range matches a preset
|
|
const today = presetRange('today');
|
|
const yesterday = presetRange('yesterday');
|
|
const week = presetRange('week');
|
|
const month = presetRange('month');
|
|
|
|
let matchedPreset: RangePreset = 'custom';
|
|
if (newRange.start === today.fromdate && newRange.end === today.todate) matchedPreset = 'today';
|
|
else if (newRange.start === yesterday.fromdate && newRange.end === yesterday.todate) matchedPreset = 'yesterday';
|
|
else if (newRange.start === week.fromdate && newRange.end === week.todate) matchedPreset = 'week';
|
|
else if (newRange.start === month.fromdate && newRange.end === month.todate) matchedPreset = 'month';
|
|
|
|
onChange(matchedPreset, { fromdate: newRange.start, todate: newRange.end });
|
|
}}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|