'use client' import { motion } from 'framer-motion' import { Card, CardContent } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Checkbox } from '@/components/ui/checkbox' import { cn, formatCurrency, formatDate } from '@/lib/utils' import { AlertCircle, ArrowUpRight, ArrowDownRight, FileText, Loader2, Trash2 } from 'lucide-react' import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@/components/ui/info-tooltip' import { getAccountName, formatAccountWithName } from '@/lib/bookkeeping/client-account-names' import { getTemplateById } from '@/lib/bookkeeping/booking-templates' import { isCounterpartyTemplateId } from '@/lib/bookkeeping/counterparty-templates' import { TransactionAttachmentIndicator } from './TransactionAttachmentIndicator' import type { TransactionWithInvoice, CategorizeHandler } from './transaction-types' import type { SuggestedCategory, SuggestedTemplate } from '@/lib/transactions/category-suggestions' interface TransactionInboxCardProps { transaction: TransactionWithInvoice suggestions?: SuggestedCategory[] templateSuggestions?: SuggestedTemplate[] /** When set, this bank tx looks like the bank side of a 1930↔1630 * transfer that the user will later see on /skattekonto. Renders a * hint warning so the user doesn't book both sides separately. */ skvCounterpartDate?: string processingId: string | null isBatchMode: boolean isSelected: boolean entityType?: string onCategorize: CategorizeHandler onMarkPrivate: (id: string) => void onOpenMatchDialog: (transaction: TransactionWithInvoice) => void onOpenCategoryDialog: (transaction: TransactionWithInvoice) => void onDelete?: (id: string) => void onOpenQuickReview?: (transaction: TransactionWithInvoice, suggestion: SuggestedCategory) => void onOpenTemplateReview?: (transaction: TransactionWithInvoice, templateId: string) => void onToggleSelect: (id: string) => void onAnimationComplete?: (id: string) => void } export default function TransactionInboxCard({ transaction, suggestions, templateSuggestions, skvCounterpartDate, processingId, isBatchMode, isSelected, entityType = 'enskild_firma', onCategorize, onMarkPrivate, onOpenMatchDialog, onOpenCategoryDialog, onDelete, onOpenQuickReview, onOpenTemplateReview, onToggleSelect, onAnimationComplete, }: TransactionInboxCardProps) { 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 topSuggestion = suggestions?.[0] const isUncategorized = transaction.is_business === null && !transaction.journal_entry_id const showCheckbox = isBatchMode && isUncategorized const isDeletable = !transaction.journal_entry_id function handleSuggestionClick(suggestion: SuggestedCategory) { if (onOpenQuickReview) { onOpenQuickReview(transaction, suggestion) } else { onCategorize(transaction.id, true, suggestion.category) } } return ( { // Only call on exit animation if (typeof definition === 'object' && 'opacity' in definition && definition.opacity === 0) { onAnimationComplete?.(transaction.id) } }} > onToggleSelect(transaction.id) : undefined} > {skvCounterpartDate && (

Möjlig 1930↔1630-överföring.{' '} Det finns en skattekonto-händelse den{' '} {skvCounterpartDate} som matchar — bokför detta verifikat först, koppla sedan skattekonto-raden mot samma verifikat istället för att bokföra två gånger.

)}
{/* Left: checkbox + icon + info */}
{showCheckbox && ( onToggleSelect(transaction.id)} onClick={(e) => e.stopPropagation()} className="mt-1" /> )}

{transaction.description}

{formatDate(transaction.date)}

{/* Right: amount */}

{isIncome ? '+' : ''} {formatCurrency(transaction.amount, transaction.currency)}

{transaction.currency !== 'SEK' && transaction.amount_sek && (

{formatCurrency(transaction.amount_sek)}

)}
{/* Inline action buttons - only shown when not in batch mode */} {!isBatchMode && (
{/* Primary action: invoice match or top suggestion */} {hasInvoiceMatch ? ( ) : hasSupplierInvoiceMatch ? ( ) : templateSuggestions && templateSuggestions.length > 0 ? ( <> {templateSuggestions.slice(0, 2).map((ts, idx) => { const isCounterparty = isCounterpartyTemplateId(ts.template_id) const tmpl = isCounterparty ? null : getTemplateById(ts.template_id) return ( ) })} ) : topSuggestion ? ( ) : null} {/* Open category dialog / template picker */} {/* Delete button — available for all unbooked transactions */} {isDeletable && onDelete && ( )}
)}
) }