'use client' import { useState } from 'react' import { useTranslations } from 'next-intl' import { Card, CardContent, 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, FileText, Scale } from 'lucide-react' import { formatCurrency, cn } from '@/lib/utils' import { roundOre } from '@/lib/money' import type { SkattekontoFileParseResult } from '@/lib/import/skattekonto-file/types' interface SkattekontoFilePreviewStepProps { parseResult: SkattekontoFileParseResult duplicateIndexes: number[] promotionIndexes: number[] orgNumberMismatch: boolean isLoading: boolean onExecute: () => void onBack: () => void } const PREVIEW_ROW_LIMIT = 100 export default function SkattekontoFilePreviewStep({ parseResult, duplicateIndexes, promotionIndexes, orgNumberMismatch, isLoading, onExecute, onBack, }: SkattekontoFilePreviewStepProps) { const t = useTranslations('import') const [mismatchConfirmed, setMismatchConfirmed] = useState(false) const [sumGapConfirmed, setSumGapConfirmed] = useState(false) const { rows, stats, issues, date_from, date_to, opening_saldo, closing_saldo, events_sum, sum_difference, sum_valid, } = parseResult const duplicateSet = new Set(duplicateIndexes) const promotionSet = new Set(promotionIndexes) const newCount = rows.length - duplicateIndexes.length const warnings = issues.filter((i) => i.severity !== 'error') // A statement that does not sum (truncated, filtered, unreadable rows) is // a confirm gate, not a block: nothing is booked at import and every event // is reviewed on the skattekonto page, so importing what IS in the file is // safe. The gate exists so the user knows the picture is incomplete. const sumGap = sum_valid === false const sumGapHasFigures = opening_saldo !== null && closing_saldo !== null && events_sum !== null && sum_difference !== null const importBlocked = (orgNumberMismatch && !mismatchConfirmed) || (sumGap && !sumGapConfirmed) return (
{t('skattekonto_preview_rows')}

{stats.parsed_rows}

{stats.skipped_rows > 0 && (

{t('skattekonto_preview_skipped', { count: stats.skipped_rows })}

)}
{t('skattekonto_preview_period')}

{date_from || '-'} – {date_to || '-'}

{t('skattekonto_preview_closing_saldo')}

{closing_saldo !== null ? formatCurrency(closing_saldo) : '-'}

{orgNumberMismatch && ( {t('skattekonto_org_mismatch_title')}

{t('skattekonto_org_mismatch_body', { orgNumber: parseResult.org_number ?? '?', companyName: parseResult.company_name ?? '?', })}

)} {sumGap && ( {t('skattekonto_sum_gap_title')} {sumGapHasFigures ? (
{t('skattekonto_sum_gap_opening')}
{formatCurrency(opening_saldo)}
{t('skattekonto_sum_gap_events')}
{formatCurrency(roundOre(events_sum - opening_saldo))}
{t('skattekonto_sum_gap_expected')}
{formatCurrency(events_sum)}
{t('skattekonto_sum_gap_closing')}
{formatCurrency(closing_saldo)}
{t('skattekonto_sum_gap_difference')}
{formatCurrency(sum_difference)}
) : (

{t('skattekonto_sum_gap_no_saldo')}

)}

{stats.unreadable_amount_rows > 0 ? t('skattekonto_sum_gap_body_unreadable', { count: stats.unreadable_amount_rows }) : t('skattekonto_sum_gap_body')}

)} {duplicateIndexes.length > 0 && (

{t('skattekonto_duplicates_note', { count: duplicateIndexes.length })}

)} {/* What happens after import: nothing is booked automatically, and an event that already has a 1630 verifikat (a deposit booked from the bank side) is offered as a link, not a second booking. Answers the "some of these are already booked" question before the click. */}

{t('skattekonto_after_import_note')}

{warnings.length > 0 && ( {t('skattekonto_issues_title', { count: warnings.length })} {warnings.slice(0, 5).map((issue, i) => (

{issue.row > 0 ? `${t('skattekonto_issue_row', { row: issue.row })}: ` : ''} {issue.message}

))}
)} {t('skattekonto_col_date')} {t('skattekonto_col_text')} {t('skattekonto_col_amount')} {rows.slice(0, PREVIEW_ROW_LIMIT).map((row, index) => { const isDuplicate = duplicateSet.has(index) return ( {row.transaktionsdatum} {row.transaktionstext} {formatCurrency(row.belopp)} {isDuplicate ? ( {t('skattekonto_chip_duplicate')} ) : promotionSet.has(index) ? ( {t('skattekonto_chip_promotion')} ) : null} ) })}
{rows.length > PREVIEW_ROW_LIMIT && (

{t('skattekonto_preview_truncated', { shown: PREVIEW_ROW_LIMIT, total: rows.length, })}

)}
) }