// packages/web/src/components/scan/ScanControls.tsx // // THIS IS THE KEY COMPONENT that makes "Scanner maintenant" actually work. // It connects the date range selection + scan button to the backend API. // // Usage in your DashboardPage or ScanPage: // import ScanControls from '@/components/scan/ScanControls'; // refetchTransactions()} /> import { useState, useCallback } from 'react'; import { useEmailScan, type ScanPreset } from '../../hooks/useEmailScan'; // Import your UI components (adjust paths to your project) // import { Button } from '@/components/ui/button'; // import { ScanLine, Loader2 } from 'lucide-react'; interface ScanControlsProps { onScanComplete?: (result: { found: number; parsed: number; skipped: number; errors: number }) => void; onNewTransaction?: (transaction: any) => void; } export default function ScanControls({ onScanComplete, onNewTransaction }: ScanControlsProps) { // ═══ STATE ═══ const [selectedPreset, setSelectedPreset] = useState('last7days'); const [customStartDate, setCustomStartDate] = useState(''); const [customEndDate, setCustomEndDate] = useState(''); const [forceRescan, setForceRescan] = useState(false); // ═══ SCAN HOOK — connects to API + WebSocket ═══ const { isScanning, progress, result, error, startScan, progressPercent, } = useEmailScan(); // Notify parent when scan completes if (result && onScanComplete) { onScanComplete(result); } // ═══ HANDLERS ═══ const handleScanClick = useCallback(() => { startScan({ preset: selectedPreset, startDate: selectedPreset === 'custom' ? customStartDate : undefined, endDate: selectedPreset === 'custom' ? customEndDate : undefined, forceRescan, }); }, [selectedPreset, customStartDate, customEndDate, forceRescan, startScan]); const presetButtons: { value: ScanPreset; label: string }[] = [ { value: 'today', label: "Aujourd'hui" }, { value: 'last7days', label: '7 derniers jours' }, { value: 'custom', label: 'Période personnalisée' }, ]; // ═══ DATE DISPLAY ═══ const getDateRangeDisplay = () => { const now = new Date(); const fmt = (d: Date) => d.toLocaleDateString('fr-CA', { day: 'numeric', month: 'short' }); switch (selectedPreset) { case 'today': return fmt(now); case 'last7days': { const start = new Date(now); start.setDate(start.getDate() - 7); return `${fmt(start)} → ${fmt(now)}`; } case 'custom': if (customStartDate && customEndDate) { return `${fmt(new Date(customStartDate))} → ${fmt(new Date(customEndDate))}`; } return 'Sélectionnez les dates'; } }; // ═══ RENDER ═══ return (
{/* PRESET BUTTONS + SCAN BUTTON ROW */}
{/* Date preset toggle group */}
{presetButtons.map((btn) => ( ))}
{/* Date range display */}
{getDateRangeDisplay()}
{/* Separator */}
{/* ════════════════════════════════════════════════ */} {/* 🔥 THE SCAN BUTTON — this is what triggers it! */} {/* ════════════════════════════════════════════════ */}
{/* CUSTOM DATE PICKERS (only when custom is selected) */} {selectedPreset === 'custom' && (
setCustomStartDate(e.target.value)} className="px-3 py-1.5 text-sm border border-border rounded-md bg-card shadow-sm" />
setCustomEndDate(e.target.value)} max={new Date().toISOString().split('T')[0]} className="px-3 py-1.5 text-sm border border-border rounded-md bg-card shadow-sm" />
)} {/* FORCE RESCAN CHECKBOX */} {/* ═══ PROGRESS BAR (during scan) ═══ */} {isScanning && progress && (
{/* Progress bar */}
{progress.processed}/{progress.total} courriels traités {progressPercent}%
{progress.latest && (
Dernier traité: {progress.latest.sender} — {progress.latest.amount?.toFixed(2)} $ → {progress.latest.branch}
)} {progress.currentProvider && (
Fournisseur IA: {progress.currentProvider}
)} {progress.errored > 0 && (
{progress.errored} erreur(s) de traitement
)}
)} {/* ═══ SCAN RESULT (after completion) ═══ */} {result && !isScanning && (
Scan terminé ✓
{result.parsed} nouveaux virements importés {result.skipped > 0 && ( <> · {result.skipped} déjà en base )} {result.errors > 0 && ( <> · {result.errors} erreurs )}
)} {/* ═══ ERROR MESSAGE ═══ */} {error && (
Erreur de scan
{error}
)}
); }