'use client' import { useTranslations } from 'next-intl' import { motion } from 'framer-motion' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Checkbox } from '@/components/ui/checkbox' import { DataListRow, DataListPrimary, DataListMeta, DataListMetaSeparator, } from '@/components/ui/data-list' import { cn, formatCurrency, formatDate } from '@/lib/utils' import { AlertCircle, ArrowUpRight, ArrowDownRight, FileText, Link2, Loader2, Trash2, } from 'lucide-react' import { TransactionAttachmentIndicator } from './TransactionAttachmentIndicator' import type { TransactionWithInvoice, CategorizeHandler } from './transaction-types' interface TransactionInboxCardProps { transaction: TransactionWithInvoice /** When set, this bank tx looks like the bank side of a 1930↔1630 * transfer that the user will later see on /skattekonto. */ skvCounterpartDate?: string processingId: string | null isBatchMode: boolean isSelected: boolean entityType?: string onCategorize: CategorizeHandler /** Confirm an auto-detected invoice match (1-click shortcut). */ onOpenMatchDialog: (transaction: TransactionWithInvoice) => void /** Open the manual picker — routes to customer or supplier picker by amount sign. */ onOpenMatchInvoicePicker: (transaction: TransactionWithInvoice) => void onOpenCategoryDialog: (transaction: TransactionWithInvoice) => void onDelete?: (id: string) => void onToggleSelect: (id: string) => void onAnimationComplete?: (id: string) => void } export default function TransactionInboxCard({ transaction, skvCounterpartDate, processingId, isBatchMode, isSelected, onOpenMatchDialog, onOpenMatchInvoicePicker, onOpenCategoryDialog, onDelete, onToggleSelect, onAnimationComplete, }: TransactionInboxCardProps) { const t = useTranslations('tx_inbox_card') const isProcessing = processingId === transaction.id const isDisabled = processingId !== null && processingId !== transaction.id const isIncome = transaction.amount > 0 const hasInvoiceMatch = !!transaction.potential_invoice && !transaction.invoice_id const hasSupplierInvoiceMatch = !!transaction.potential_supplier_invoice && !transaction.supplier_invoice_id const isUncategorized = transaction.is_business === null && !transaction.journal_entry_id const showCheckbox = isBatchMode && isUncategorized const isDeletable = !transaction.journal_entry_id // Primary action: invoice/supplier-invoice match keeps the 1-click shortcut; // otherwise the user opens the template picker. const primaryAction = (() => { if (hasInvoiceMatch) { return ( { e.stopPropagation() onOpenMatchDialog(transaction) }} disabled={isProcessing || isDisabled} > {isProcessing ? ( ) : ( )} {t('match_invoice_btn', { number: transaction.potential_invoice!.invoice_number ?? '', })} ) } if (hasSupplierInvoiceMatch) { return ( { e.stopPropagation() onOpenMatchDialog(transaction) }} disabled={isProcessing || isDisabled} > {isProcessing ? ( ) : ( )} {t('match_supplier_invoice_btn', { number: transaction.potential_supplier_invoice!.supplier_invoice_number ?? '', })} ) } return ( { e.stopPropagation() onOpenCategoryDialog(transaction) }} disabled={isProcessing || isDisabled} > Bokför ) })() // Manual invoice-match affordance. Hidden once an auto-detected match is // already shown as the primary button — having both makes the row noisy. const showInvoiceMatchButton = isDeletable && !hasInvoiceMatch && !hasSupplierInvoiceMatch const invoiceMatchLabel = isIncome ? 'Matcha mot kundfaktura' : 'Matcha mot leverantörsfaktura' return ( { if (typeof definition === 'object' && 'opacity' in definition && definition.opacity === 0) { onAnimationComplete?.(transaction.id) } }} > onToggleSelect(transaction.id) : undefined} leading={ showCheckbox ? ( onToggleSelect(transaction.id)} onClick={(e) => e.stopPropagation()} aria-label="Välj transaktion" /> ) : ( {isIncome ? ( ) : ( )} ) } trailing={ <> {isIncome ? '+' : ''} {formatCurrency(transaction.amount, transaction.currency)} {transaction.currency !== 'SEK' && transaction.amount_sek != null && ( {formatCurrency(transaction.amount_sek)} )} {!isBatchMode && ( <> {primaryAction} {showInvoiceMatchButton && ( { e.stopPropagation() onOpenMatchInvoicePicker(transaction) }} aria-label={invoiceMatchLabel} title={invoiceMatchLabel} disabled={isProcessing || isDisabled} > )} {isDeletable && onDelete && ( { e.stopPropagation() onDelete(transaction.id) }} aria-label={t('delete_aria')} disabled={isProcessing || isDisabled} > )} > )} > } > {transaction.description} {formatDate(transaction.date)} {skvCounterpartDate && ( <> Möjlig 1930↔1630 > )} ) }
{isIncome ? '+' : ''} {formatCurrency(transaction.amount, transaction.currency)}
{formatCurrency(transaction.amount_sek)}