import type { Dispatch, SetStateAction } from 'react' import { Wallet, Info, ChevronDown, ChevronRight, TrendingUp, TrendingDown, PieChart as PieChartIcon } from 'lucide-react' import type { BudgetItem } from '../../types' import { currencyDecimals } from '../../utils/formatters' import { SYMBOLS } from './BudgetPanel.constants' import { hexLighten, widgetTheme } from './BudgetPanel.helpers' import RingAvatar from './BudgetPanelRingAvatar' import PerPersonInline from './BudgetPanelPerPersonInline' import type { SettlementData, PieSegment } from './useBudgetPanel' interface BudgetSummaryProps { theme: ReturnType currency: string locale: string grandTotal: number hasMultipleMembers: boolean budgetItems: BudgetItem[] settlement: SettlementData | null settlementOpen: boolean setSettlementOpen: Dispatch> pieSegments: PieSegment[] isDark: boolean tripId: number t: (key: string) => string fmt: (v: number | null | undefined, cur: string) => string } export default function BudgetSummary({ theme, currency, locale, grandTotal, hasMultipleMembers, budgetItems, settlement, settlementOpen, setSettlementOpen, pieSegments, isDark, tripId, t, fmt }: BudgetSummaryProps) { return (
{t('budget.totalBudget')}
{(() => { const decimals = currencyDecimals(currency) const full = Number(grandTotal).toLocaleString(locale, { minimumFractionDigits: decimals, maximumFractionDigits: decimals }) const sep = (0.1).toLocaleString(locale).replace(/\d/g, '') const [integerPart, decimalPart] = decimals > 0 ? full.split(sep) : [full, ''] return (
{integerPart} {decimalPart && {sep}{decimalPart}} {SYMBOLS[currency] || currency}
) })()}
{currency}
{hasMultipleMembers && (budgetItems || []).some(i => (i.members?.length ?? 0) > 0) && ( )} {/* Settlement dropdown inside the total card */} {hasMultipleMembers && settlement && settlement.flows.length > 0 && (
{settlementOpen && (
{settlement.flows.map((flow, i) => (
{ e.currentTarget.style.background = theme.flowHoverBg; e.currentTarget.style.borderColor = theme.flowHoverBorder }} onMouseLeave={e => { e.currentTarget.style.background = theme.flowBg; e.currentTarget.style.borderColor = theme.flowBorder }} >
{fmt(flow.amount, currency)}
))} {settlement.balances.filter(b => Math.abs(b.balance) > 0.01).length > 0 && (
{t('budget.netBalances')}
{settlement.balances.filter(b => Math.abs(b.balance) > 0.01).map(b => { const positive = b.balance > 0 const Trend = positive ? TrendingUp : TrendingDown return (
{b.username} {positive ? '+' : ''}{fmt(b.balance, currency)}
) })}
)}
)}
)}
{pieSegments.length > 0 && (() => { const decimals = currencyDecimals(currency) const total = pieSegments.reduce((s, x) => s + x.value, 0) const totalFmt = Number(total).toLocaleString(locale, { minimumFractionDigits: decimals, maximumFractionDigits: decimals }) const decimalSep = (0.1).toLocaleString(locale).replace(/\d/g, '') const [totalInt, totalDec] = decimals > 0 ? totalFmt.split(decimalSep) : [totalFmt, ''] const R = 80 const CIRC = 2 * Math.PI * R let dashOffset = 0 return (
{t('budget.byCategory')}
{pieSegments.map((seg, i) => { const c2 = hexLighten(seg.color, 0.2) return ( ) })} {pieSegments.map((seg, i) => { const segLen = total > 0 ? (seg.value / total) * CIRC : 0 const circle = ( ) dashOffset += segLen return circle })}
{t('budget.total')}
{totalInt} {totalDec && {decimalSep}{totalDec}}
{currency}
{pieSegments.map((seg, i) => { const pct = total > 0 ? (seg.value / total) * 100 : 0 const pctLabel = pct.toFixed(1).replace('.', decimalSep) + '%' const c2 = hexLighten(seg.color, 0.2) const chipColor = isDark ? hexLighten(seg.color, 0.35) : seg.color return (
e.currentTarget.style.background = theme.rowHover} onMouseLeave={e => e.currentTarget.style.background = 'transparent'} >
{seg.name}
{fmt(seg.value, currency)}
{pctLabel}
) })}
) })()}
) }