// SnapshotEditor — groups the active accounts by balance category and // renders one `SnapshotLineRow` per account. // // Both `simple` and `priced` variants are dispatched by `account.category_kind` // inside `SnapshotLineRow`. The editor itself only carries the values down // and the change handlers up. import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import type { BalanceAccountWithCategory, BalanceCategory, } from "../../shared/types"; import type { PricedEntry } from "../../hooks/useSnapshotEditor"; import SnapshotLineRow from "./SnapshotLineRow"; interface Props { accounts: BalanceAccountWithCategory[]; categories: BalanceCategory[]; /** account_id → string-typed value (simple kind). */ values: Record; /** account_id → {quantity, unit_price} strings (priced kind). */ pricedValues: Record; onValueChange: (accountId: number, next: string) => void; onQuantityChange: (accountId: number, next: string) => void; onUnitPriceChange: (accountId: number, next: string) => void; disabled?: boolean; } export default function SnapshotEditor({ accounts, categories, values, pricedValues, onValueChange, onQuantityChange, onUnitPriceChange, disabled, }: Props) { const { t } = useTranslation(); // Group accounts by their category, preserving the categories' sort_order // first then the account name within each group. const groups = useMemo(() => { const byCategory = new Map(); for (const acc of accounts) { const list = byCategory.get(acc.balance_category_id) ?? []; list.push(acc); byCategory.set(acc.balance_category_id, list); } const sortedCategories = [...categories].sort( (a, b) => a.sort_order - b.sort_order || a.key.localeCompare(b.key) ); return sortedCategories .map((cat) => ({ category: cat, accounts: (byCategory.get(cat.id) ?? []).sort((a, b) => a.name.localeCompare(b.name) ), })) .filter((group) => group.accounts.length > 0); }, [accounts, categories]); if (accounts.length === 0) { return (
{t("balance.snapshot.editor.empty")}
); } return (
{groups.map(({ category, accounts: catAccounts }) => (

{t(category.i18n_key, { defaultValue: category.key })}

{catAccounts.map((acc) => { const priced = pricedValues[acc.id]; return ( onValueChange(acc.id, next)} onQuantityChange={(next) => onQuantityChange(acc.id, next)} onUnitPriceChange={(next) => onUnitPriceChange(acc.id, next)} disabled={disabled} /> ); })}
))}
); }