'use client'; import { useMemo, useState } from 'react'; import { Upload } from 'lucide-react'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Label } from '@/components/ui/label'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { toast } from '@/components/ui/toast'; import { plannedTabsApi } from '@/lib/api/client'; import { useLang } from '@/lib/i18n'; import { buildPreview, columnLabel, detectMapping, readSheet, type ColumnMapping, type SheetData } from '@/lib/customer-import'; const PREVIEW_ROWS = 8; const selectClassName = 'flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring'; interface Props { venueId: string; open: boolean; onOpenChange: (open: boolean) => void; onImported: () => void; } export function ImportCustomersDialog({ venueId, open, onOpenChange, onImported }: Props) { const { t } = useLang(); const [fileName, setFileName] = useState(null); const [sheet, setSheet] = useState(null); const [mapping, setMapping] = useState(null); const [mode, setMode] = useState<'merge' | 'replace'>('merge'); const [submitting, setSubmitting] = useState(false); const preview = useMemo(() => (sheet && mapping ? buildPreview(sheet, mapping) : null), [sheet, mapping]); function reset() { setFileName(null); setSheet(null); setMapping(null); setMode('merge'); } function handleOpenChange(next: boolean) { if (!next) reset(); onOpenChange(next); } async function handleFile(file: File | undefined) { if (!file) return; try { const data = await readSheet(file); if (data.rows.length === 0) { toast({ title: t('import.failed'), description: t('import.empty_file'), variant: 'destructive' }); return; } setFileName(file.name); setSheet(data); setMapping(detectMapping(data)); } catch { toast({ title: t('import.failed'), description: t('import.unreadable'), variant: 'destructive' }); } } async function submit() { if (!preview || preview.entries.length === 0) return; setSubmitting(true); try { const result = await plannedTabsApi.import(venueId, preview.entries, mode); toast({ title: t('import.done'), description: t('import.result', result), variant: 'success' }); onImported(); handleOpenChange(false); } catch (err: any) { toast({ title: t('import.failed'), description: err.message, variant: 'destructive' }); } finally { setSubmitting(false); } } const header = sheet && mapping?.hasHeader ? sheet.rows[0] : null; const columnOptions = sheet ? Array.from({ length: sheet.columnCount }, (_, i) => ({ value: i, label: header?.[i] ? `${columnLabel(i)} – ${header[i]}` : columnLabel(i), })) : []; function columnSelect(value: number | null, onChange: (value: number | null) => void, optional: boolean) { return ( ); } return ( {t('import.title')} {t('import.description')}
{sheet && mapping && preview && ( <>
{columnSelect(mapping.nameColumn, (v) => setMapping({ ...mapping, nameColumn: v ?? 0 }), false)}
{columnSelect(mapping.lastNameColumn, (v) => setMapping({ ...mapping, lastNameColumn: v }), true)}
{columnSelect(mapping.emailColumn, (v) => setMapping({ ...mapping, emailColumn: v }), true)}

{t('import.summary', { count: preview.entries.length, emails: preview.entries.filter((e) => e.email).length, })}

{(preview.skippedRows > 0 || preview.invalidEmails > 0) && (

{t('import.warnings', { skipped: preview.skippedRows, invalid: preview.invalidEmails })}

)}
{t('tabs.customer')} {t('import.email')} {preview.entries.slice(0, PREVIEW_ROWS).map((entry, i) => ( {entry.customerName} {entry.email ?? '—'} ))}
{preview.entries.length > PREVIEW_ROWS && (

{t('import.more', { count: preview.entries.length - PREVIEW_ROWS })}

)}
setMode(v as 'merge' | 'replace')}>
)}
); }