// BalanceAccountsTable — one-row-per-active-account table on /balance. // // Issue #141 (Bilan #3). Columns: // - Account name + category label // - Latest snapshot value (or "—" when no snapshot exists yet) // - Δ% over the active period (latest value vs the period-anchor value; // null when no anchor exists, rendered as "—"). // - Actions menu (Detail no-op for now, Archive via service). // // Future return-metric columns (3M / 1A / since-creation / unadjusted) // land in Issue #142. They have a TODO marker below. import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Archive, MoreVertical } from "lucide-react"; import type { AccountLatestSnapshot, AccountPeriodAnchor, } from "../../services/balance.service"; const cadFormatter = (locale: string) => new Intl.NumberFormat(locale, { style: "currency", currency: "CAD", maximumFractionDigits: 2, }); interface BalanceAccountsTableProps { accounts: AccountLatestSnapshot[]; periodAnchor: AccountPeriodAnchor[]; onArchiveAccount?: (account: AccountLatestSnapshot) => void; } export default function BalanceAccountsTable({ accounts, periodAnchor, onArchiveAccount, }: BalanceAccountsTableProps) { const { t, i18n } = useTranslation(); const fmt = cadFormatter(i18n.language === "fr" ? "fr-CA" : "en-CA"); /** account_id → period anchor (start-of-period value). */ const anchorMap = useMemo(() => { const m = new Map(); for (const a of periodAnchor) m.set(a.account_id, a); return m; }, [periodAnchor]); const [openMenuFor, setOpenMenuFor] = useState(null); if (accounts.length === 0) { return (
{t("balance.overview.noAccounts")}
); } return (
{/* TODO Issue #142: 3M / 1A / depuis-création / non-ajusté columns */} {accounts.map((acc) => { const anchor = anchorMap.get(acc.account_id); const deltaPct = acc.latest_value !== null && anchor && anchor.anchor_value !== 0 ? ((acc.latest_value - anchor.anchor_value) / Math.abs(anchor.anchor_value)) * 100 : null; return ( ); })}
{t("balance.account.fields.name")} {t("balance.account.fields.category")} {t("balance.overview.latestValue")} {t("balance.overview.periodDelta")} {t("balance.account.fields.actions")}
{acc.account_name} {acc.symbol ? ( ({acc.symbol}) ) : null} {t(acc.category_i18n_key, { defaultValue: acc.category_key })} {acc.latest_value !== null ? fmt.format(acc.latest_value) : "—"} {deltaPct !== null ? ( = 0 ? "text-[var(--positive)]" : "text-[var(--negative)]" } > {deltaPct >= 0 ? "+" : ""} {deltaPct.toFixed(2)}% ) : ( "—" )} {openMenuFor === acc.account_id && (
)}
); }