'use client' import { useState, useEffect } from 'react' import type { InvoiceInboxItem, Supplier, SupplierType } from '@/types' import type { InvoiceExtractionResult } from '@/extensions/general/invoice-inbox/types' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Separator } from '@/components/ui/separator' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { Progress } from '@/components/ui/progress' import { getStatusLabel, getStatusVariant, getConfidenceLabel, } from '@/lib/extensions/invoice-inbox-utils' import { Loader2, RefreshCw, Check, X, ChevronDown } from 'lucide-react' export interface NewSupplierData { name: string supplier_type: SupplierType org_number: string vat_number: string bankgiro: string plusgiro: string default_expense_account: string default_currency: string } interface InboxDetailDialogProps { item: InvoiceInboxItem | null open: boolean onOpenChange: (open: boolean) => void onConfirm: (itemId: string, supplierId?: string, newSupplierData?: NewSupplierData) => Promise onReject: (itemId: string) => Promise onReprocess: (itemId: string) => Promise suppliers: Supplier[] } function formatAmount(amount: number | null, currency: string = 'SEK'): string { if (amount == null) return '-' return new Intl.NumberFormat('sv-SE', { style: 'currency', currency, minimumFractionDigits: 2, maximumFractionDigits: 2, }).format(amount) } export default function InboxDetailDialog({ item, open, onOpenChange, onConfirm, onReject, onReprocess, suppliers, }: InboxDetailDialogProps) { const [loading, setLoading] = useState<'confirm' | 'reject' | 'reprocess' | null>(null) const [selectedSupplierId, setSelectedSupplierId] = useState(undefined) const [supplierFormOpen, setSupplierFormOpen] = useState(false) const [newSupplier, setNewSupplier] = useState({ name: '', supplier_type: 'swedish_business', org_number: '', vat_number: '', bankgiro: '', plusgiro: '', default_expense_account: '6200', default_currency: 'SEK', }) // Pre-populate supplier form when item changes const extractionForEffect = item?.extracted_data as unknown as InvoiceExtractionResult | null useEffect(() => { if (!extractionForEffect?.supplier) return const s = extractionForEffect.supplier setNewSupplier({ name: s.name ?? '', supplier_type: 'swedish_business', org_number: s.orgNumber ?? '', vat_number: s.vatNumber ?? '', bankgiro: s.bankgiro ?? '', plusgiro: s.plusgiro ?? '', default_expense_account: '6200', default_currency: extractionForEffect.invoice?.currency || 'SEK', }) }, [extractionForEffect]) if (!item) return null const extraction = item.extracted_data as unknown as InvoiceExtractionResult | null const currency = extraction?.invoice.currency || 'SEK' const confidence = getConfidenceLabel(item.confidence) const confidenceVariant = confidence.variant as 'default' | 'secondary' | 'destructive' | 'outline' | 'success' | 'warning' const statusVariant = getStatusVariant(item.status) as 'default' | 'secondary' | 'destructive' | 'outline' | 'success' | 'warning' const matchedSupplierName = (item.supplier as { name?: string } | undefined)?.name const supplierId = selectedSupplierId ?? item.matched_supplier_id ?? undefined const isCreatingNewSupplier = !supplierId const canConfirm = item.status === 'ready' && extraction != null const canReprocess = item.status !== 'confirmed' const canReject = item.status !== 'confirmed' && item.status !== 'rejected' function updateSupplierField(field: K, value: NewSupplierData[K]) { setNewSupplier((prev) => ({ ...prev, [field]: value })) } async function handleAction(action: 'confirm' | 'reject' | 'reprocess') { setLoading(action) try { if (action === 'confirm') { await onConfirm( item!.id, supplierId, isCreatingNewSupplier ? newSupplier : undefined ) } else if (action === 'reject') { await onReject(item!.id) } else { await onReprocess(item!.id) } } finally { setLoading(null) } } return (
Granska faktura {getStatusLabel(item.status)}
{/* Confidence */} {item.confidence != null && (
AI-konfidens {confidence.label} ({Math.round(item.confidence * 100)}%)
)} {item.error_message && (
{item.error_message}
)} {extraction && ( <> {/* Supplier info */}

Leverantör

Namn

{extraction.supplier.name ?? '-'}

Org.nr

{extraction.supplier.orgNumber ?? '-'}

{extraction.supplier.bankgiro && (
Bankgiro

{extraction.supplier.bankgiro}

)} {extraction.supplier.plusgiro && (
Plusgiro

{extraction.supplier.plusgiro}

)}
{/* Supplier match override */}
{/* Inline new supplier form */} {isCreatingNewSupplier && (
{supplierFormOpen && (
updateSupplierField('name', e.target.value)} />
updateSupplierField('org_number', e.target.value)} />
updateSupplierField('bankgiro', e.target.value)} />
updateSupplierField('plusgiro', e.target.value)} />
updateSupplierField('default_expense_account', e.target.value)} />
updateSupplierField('default_currency', e.target.value)} />
)}
)}
{/* Invoice details */}

Fakturadetaljer

Fakturanummer

{extraction.invoice.invoiceNumber ?? '-'}

Valuta

{extraction.invoice.currency}

Fakturadatum

{extraction.invoice.invoiceDate ?? '-'}

Förfallodatum

{extraction.invoice.dueDate ?? '-'}

{extraction.invoice.paymentReference && (
Betalningsreferens

{extraction.invoice.paymentReference}

)}
{/* Line items */} {extraction.lineItems.length > 0 && ( <>

Rader

Beskrivning Antal À-pris Belopp Moms {extraction.lineItems.map((line, i) => ( {line.description} {line.quantity} {line.unitPrice != null ? formatAmount(line.unitPrice, currency) : '-'} {formatAmount(line.lineTotal, currency)} {line.vatRate != null ? `${line.vatRate}%` : '-'} ))}
)} {/* Totals */}
Netto {formatAmount(extraction.totals.subtotal, currency)}
Moms {formatAmount(extraction.totals.vatAmount, currency)}
Totalt {formatAmount(extraction.totals.total, currency)}
)} {canReject && ( )} {canReprocess && ( )} {canConfirm && ( )}
) }