'use client' import { useTranslations } from 'next-intl' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { ArrowLeft, ArrowRight, AlertTriangle, Calendar, TrendingUp, TrendingDown, FileText, } from 'lucide-react' import { formatCurrency } from '@/lib/utils' import { summarizeByCurrency } from '@/lib/import/bank-file/currency-summary' import type { BankFileParseResult, BankFileDuplicateInfo } from '@/lib/import/bank-file/types' interface BankFilePreviewStepProps { parseResult: BankFileParseResult duplicateInfo?: BankFileDuplicateInfo | null onContinue: () => void onBack: () => void } export default function BankFilePreviewStep({ parseResult, duplicateInfo, onContinue, onBack, }: BankFilePreviewStepProps) { const t = useTranslations('transactions') const { transactions, stats, issues, date_from, date_to } = parseResult const errors = issues.filter((i) => i.severity === 'error') const hasIssues = errors.length > 0 const warnings = issues.filter((i) => i.severity === 'warning') // Wise/camt.053 files can mix currencies per row: the parser-level totals // sum across currencies, so income/expenses are grouped per currency here. const currencyTotals = summarizeByCurrency(transactions) const duplicateCount = duplicateInfo?.duplicate_count ?? 0 // Table rows render transactions.slice(0, 50), so the slice index IS the // original array index the server flagged. const duplicateRowSet = new Set(duplicateInfo?.duplicate_row_indexes ?? []) return (
{/* Summary cards */}
Transaktioner

{stats.parsed_rows}

{stats.skipped_rows > 0 && (

{stats.skipped_rows} rader hoppades över

)}
Period

{date_from || '-'} till {date_to || '-'}

Inkomster
{(currencyTotals.length ? currencyTotals : [{ currency: 'SEK', total_income: 0, total_expenses: 0 }]).map((row) => (

{formatCurrency(row.total_income, row.currency)}

))}
Utgifter
{(currencyTotals.length ? currencyTotals : [{ currency: 'SEK', total_income: 0, total_expenses: 0 }]).map((row) => (

{formatCurrency(row.total_expenses, row.currency)}

))}
{/* Duplicate rows already in bookkeeping: advisory, ingest skips them */} {duplicateCount > 0 && ( {t('import_duplicate_rows_title', { count: duplicateCount })}

{t('import_duplicate_rows_body')}

)} {/* Warnings */} {warnings.length > 0 && ( {warnings.length} varning{warnings.length !== 1 ? 'ar' : ''}
{warnings.slice(0, 10).map((issue, i) => (

Rad {issue.row}: {issue.message}

))} {warnings.length > 10 && (

...och {warnings.length - 10} till

)}
)} {/* Transaction preview table */} Transaktioner Förhandsgranskning av de {Math.min(transactions.length, 50)} första transaktionerna
Datum Beskrivning Belopp {transactions.some((t) => t.balance != null) && ( Saldo )} {transactions.some((t) => t.reference) && ( Referens )} {transactions.slice(0, 50).map((tx, i) => ( {tx.date} {tx.description} {duplicateRowSet.has(i) && ( {t('import_duplicate_row_badge')} )} {formatCurrency(tx.amount, tx.currency || 'SEK')} {transactions.some((t) => t.balance != null) && ( {tx.balance != null ? formatCurrency(tx.balance, tx.currency || 'SEK') : '-'} )} {transactions.some((t) => t.reference) && ( {tx.reference ? tx.reference : '-'} )} ))}
{transactions.length > 50 && (

Visar 50 av {transactions.length} transaktioner

)}
{/* Error blocking continuation */} {hasIssues && (

Filen innehåller fel som förhindrar import

{errors.map((issue, i) => (

Rad {issue.row}: {issue.message}

))}
)} {/* Navigation */}
) }