import React from 'react'; import { ArrowDown, ArrowUp, Copy, GripVertical, PenLine, EyeOff, Eye, Clock, Trash2 } from 'lucide-react'; import { cn } from '@/lib/utils'; import { scheduleLabel } from '@/lib/billingSchedule'; import { formatUSDWhole } from '@/lib/money'; import { MobileBillRow } from '@/components/MobileBillRow'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import type { Bill } from '@/types'; import type { DragProps, MoveControls } from '@/components/tracker/MobileTrackerRow'; interface BillPrefs { showCategory?: boolean; showDueDay?: boolean; showAmount?: boolean; showCycle?: boolean; showApr?: boolean; showBalance?: boolean; showMinPayment?: boolean; showAutopay?: boolean; show2fa?: boolean; showSubscription?: boolean; } function ordinal(n: number | null | undefined): string { const d = Number(n); if (!d) return '—'; if (d > 3 && d < 21) return `${d}th`; switch (d % 10) { case 1: return `${d}st`; case 2: return `${d}nd`; case 3: return `${d}rd`; default: return `${d}th`; } } function hasHistoricalVisibility(bill: Bill): boolean { return !!bill.has_history_ranges || (!!bill.history_visibility && bill.history_visibility !== 'default'); } function AprColor({ rate }: { rate: number }) { const cls = rate >= 25 ? 'text-rose-400' : rate >= 15 ? 'text-amber-400' : 'text-muted-foreground'; return {rate}% APR; } const ALL_ON: BillPrefs = { showCategory: true, showDueDay: true, showAmount: true, showCycle: true, showApr: true, showBalance: true, showMinPayment: true, showAutopay: true, show2fa: true, }; interface BillCardProps { bill: Bill; prefs?: BillPrefs; onEdit?: (id: number) => void; onToggle?: (bill: Bill) => void; onDelete?: (bill: Bill) => void; onHistory?: (bill: Bill) => void; onDuplicate?: (bill: Bill) => void; moveControls?: MoveControls; dragProps?: DragProps; } function BillCard({ bill, prefs = ALL_ON, onEdit, onToggle, onDelete, onHistory, onDuplicate, moveControls, dragProps }: BillCardProps) { const isDebt = bill.current_balance != null || bill.minimum_payment != null; const hasHistory = hasHistoricalVisibility(bill); return (
['onDragStart']} onDragEnter={dragProps?.onDragEnter as React.ComponentProps<'div'>['onDragEnter']} onDragOver={dragProps?.onDragOver as React.ComponentProps<'div'>['onDragOver']} onDragEnd={dragProps?.onDragEnd as React.ComponentProps<'div'>['onDragEnd']} onDrop={dragProps?.onDrop as React.ComponentProps<'div'>['onDrop']} className={cn( 'group flex items-center gap-3 px-5 py-3.5 transition-colors', 'hover:bg-accent/20', !bill.active && 'opacity-60', dragProps?.isDragging && 'opacity-45', dragProps?.isDropTarget && 'ring-2 ring-inset ring-primary/35', )}>
{/* Main info */}
{/* Name + badges */}
{prefs.showCategory && bill.category_name && ( {bill.category_name} )} {prefs.showAutopay && !!bill.autopay_enabled && ( AP Autopay enabled )} {prefs.show2fa && !!bill.has_2fa && ( 2FA Two-factor authentication configured )} {prefs.showSubscription && !!bill.is_subscription && ( S Subscription )} {(!!bill.has_merchant_rule || !!bill.has_linked_transactions) && ( L Linked to bank transactions )} {hasHistory && ( Historical visibility configured )}
{/* Meta row */}
{prefs.showCycle && {scheduleLabel(bill)}} {prefs.showCycle && prefs.showDueDay && ·} {prefs.showDueDay && Due {ordinal(bill.due_day)}} {prefs.showApr && isDebt && bill.interest_rate != null && ( <> {(prefs.showCycle || prefs.showDueDay) && ·} )} {prefs.showBalance && isDebt && bill.current_balance != null && ( <> {(prefs.showCycle || prefs.showDueDay || (prefs.showApr && bill.interest_rate != null)) && ·} {formatUSDWhole(bill.current_balance)} balance )}
{/* Amount */} {prefs.showAmount && (

${Number(bill.expected_amount).toFixed(2)}

{prefs.showMinPayment && bill.minimum_payment != null && (

${Number(bill.minimum_payment).toFixed(0)} min

)}
)} {/* Action icons */}
{!bill.active && onHistory && ( )}
); } interface BillsTableInnerProps { bills: Bill[]; prefs?: BillPrefs; onEdit?: (id: number) => void; onToggle?: (bill: Bill) => void; onDelete?: (bill: Bill) => void; onHistory?: (bill: Bill) => void; onDuplicate?: (bill: Bill) => void; moveControlsFor?: (bill: Bill, index: number) => MoveControls | undefined; dragPropsFor?: (bill: Bill, index: number) => DragProps | undefined; } export default function BillsTableInner({ bills, prefs = ALL_ON, onEdit, onToggle, onDelete, onHistory, onDuplicate, moveControlsFor, dragPropsFor }: BillsTableInnerProps) { return ( <>
{bills.map((bill, index) => ( ))}
{bills.map((bill, index) => ( ))}
); }