MichaelEdou
Initial commit — ICC Interac Manager full-stack app
149698e
import { useState, useRef, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { ScanLine, Calendar, ArrowRight } from 'lucide-react';
import { format } from 'date-fns';
import { fr } from 'date-fns/locale/fr';
import { enUS } from 'date-fns/locale/en-US';
import { Button } from '@/components/ui/button';
type Period = 'today' | '7days' | 'custom';
interface HeaderProps {
title: string;
onScanClick?: () => void;
activePeriod?: Period;
onPeriodChange?: (period: Period) => void;
customStartDate?: string;
customEndDate?: string;
onCustomDateChange?: (startDate: string, endDate: string) => void;
}
function computeDateRange(period: Period, customStart?: string, customEnd?: string): { start: Date; end: Date } {
const now = new Date();
switch (period) {
case 'today': {
const midnight = new Date(now);
midnight.setHours(0, 0, 0, 0);
return { start: midnight, end: now };
}
case '7days': {
const sevenDaysAgo = new Date(now);
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
sevenDaysAgo.setHours(0, 0, 0, 0);
return { start: sevenDaysAgo, end: now };
}
case 'custom': {
if (customStart && customEnd) {
// Parse as local date (YYYY-MM-DD) to avoid UTC timezone shift
const [sy, sm, sd] = customStart.split('-').map(Number);
const [ey, em, ed] = customEnd.split('-').map(Number);
return { start: new Date(sy, sm - 1, sd), end: new Date(ey, em - 1, ed) };
}
return { start: now, end: now };
}
}
}
function formatDateLabel(date: Date, locale: string): string {
const loc = locale === 'fr' ? fr : enUS;
return format(date, 'd MMM', { locale: loc });
}
export default function Header({
title,
onScanClick,
activePeriod: controlledPeriod,
onPeriodChange,
customStartDate,
customEndDate,
onCustomDateChange,
}: HeaderProps) {
const { t, i18n } = useTranslation();
const [showDatePicker, setShowDatePicker] = useState(false);
const [internalPeriod, setInternalPeriod] = useState<Period>('today');
const [internalStart, setInternalStart] = useState('');
const [internalEnd, setInternalEnd] = useState('');
const activePeriod = controlledPeriod ?? internalPeriod;
const startDate = customStartDate ?? internalStart;
const endDate = customEndDate ?? internalEnd;
const handlePeriodChange = (p: Period) => {
setInternalPeriod(p);
onPeriodChange?.(p);
if (p === 'custom') {
setShowDatePicker(true);
} else {
setShowDatePicker(false);
}
};
const handleCustomDateChange = (start: string, end: string) => {
setInternalStart(start);
setInternalEnd(end);
onCustomDateChange?.(start, end);
};
const datePickerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!showDatePicker) return;
function handleClick(e: MouseEvent) {
if (datePickerRef.current && !datePickerRef.current.contains(e.target as Node)) {
setShowDatePicker(false);
}
}
document.addEventListener('mousedown', handleClick);
return () => document.removeEventListener('mousedown', handleClick);
}, [showDatePicker]);
const dateRange = useMemo(
() => computeDateRange(activePeriod, startDate, endDate),
[activePeriod, startDate, endDate]
);
const today = format(new Date(), 'yyyy-MM-dd');
return (
<header className="flex h-20 items-center justify-between bg-background px-8 z-10 sticky top-0 border-b border-border/50">
<h2 className="text-2xl font-bold tracking-tight text-slate-900">{title}</h2>
<div className="flex items-center gap-4">
{/* Period filter */}
<div className="flex items-center gap-2 bg-card p-1 rounded-lg border border-border shadow-sm">
<button
onClick={() => handlePeriodChange('today')}
className={`px-3 py-1.5 text-xs font-medium rounded-md transition-colors ${
activePeriod === 'today'
? 'bg-slate-100 text-slate-900 hover:bg-slate-200'
: 'text-slate-500 hover:bg-slate-50 hover:text-slate-900'
}`}
>
{t('scan.today')}
</button>
<button
onClick={() => handlePeriodChange('7days')}
className={`px-3 py-1.5 text-xs font-medium rounded-md transition-colors ${
activePeriod === '7days'
? 'bg-slate-100 text-slate-900 hover:bg-slate-200'
: 'text-slate-500 hover:bg-slate-50 hover:text-slate-900'
}`}
>
{t('scan.last7days')}
</button>
<div className="relative" ref={datePickerRef}>
<button
onClick={() => {
if (activePeriod === 'custom') {
setShowDatePicker(!showDatePicker);
} else {
handlePeriodChange('custom');
}
}}
className={`px-3 py-1.5 text-xs font-medium rounded-md flex items-center gap-1.5 transition-colors ${
activePeriod === 'custom'
? 'bg-primary/10 text-primary border border-primary/20 hover:bg-primary/20'
: 'text-slate-500 hover:bg-slate-50 hover:text-slate-900'
}`}
>
<span>{t('scan.custom')}</span>
<Calendar className="h-3.5 w-3.5" />
</button>
{/* Custom date picker dropdown */}
{showDatePicker && (
<div className="absolute right-0 top-full mt-2 w-72 rounded-xl bg-white p-4 shadow-lg border border-slate-200 z-50">
<div className="space-y-3">
<div>
<label className="block text-xs font-medium text-slate-500 uppercase tracking-wide mb-1.5">
{t('scan.startDate')}
</label>
<input
type="date"
max={endDate || today}
value={startDate}
onChange={(e) => handleCustomDateChange(e.target.value, endDate)}
className="w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-900 focus:border-primary focus:ring-1 focus:ring-primary/30 outline-none transition-colors"
/>
</div>
<div>
<label className="block text-xs font-medium text-slate-500 uppercase tracking-wide mb-1.5">
{t('scan.endDate')}
</label>
<input
type="date"
min={startDate}
max={today}
value={endDate}
onChange={(e) => handleCustomDateChange(startDate, e.target.value)}
className="w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-900 focus:border-primary focus:ring-1 focus:ring-primary/30 outline-none transition-colors"
/>
</div>
{startDate && endDate && (
<button
onClick={() => setShowDatePicker(false)}
className="w-full rounded-lg bg-primary px-3 py-2 text-sm font-medium text-white hover:bg-primary/90 transition-colors"
>
{t('common.save')}
</button>
)}
</div>
</div>
)}
</div>
</div>
{/* Date range display */}
<div className="hidden sm:flex items-center gap-2 bg-card px-3 py-1.5 rounded-lg border border-border shadow-sm text-sm text-slate-600">
<span className="font-medium text-slate-900">
{formatDateLabel(dateRange.start, i18n.language)}
</span>
<ArrowRight className="h-3.5 w-3.5 text-slate-400" />
<span className="font-medium text-slate-900">
{formatDateLabel(dateRange.end, i18n.language)}
</span>
</div>
<div className="h-8 w-px bg-slate-200 mx-2" />
{/* Scan Button */}
<Button
onClick={onScanClick}
className="gap-2 shadow-md shadow-blue-500/20 hover:shadow-blue-500/30 active:scale-95 transition-all"
>
<ScanLine className="h-[18px] w-[18px]" />
{t('header.scanNow')}
</Button>
</div>
</header>
);
}