import type { FC } from "react"; import { Button, Group, Stack, Text, Title } from "@mantine/core"; import { useTranslation } from "react-i18next"; import { Link } from "react-router"; import type { InvestmentResourceMeta } from "@/shared/api"; import { type BrokerageAccountCard, BrokerageAccountList, } from "@/entities/brokerage-account"; import { formatMoney } from "@/shared/lib"; import { ReadRetry, type ReadRetryProps, SourceSyncStatus } from "@/shared/ui"; import classes from "./AccountsPageLoaded.module.css"; const DECIMAL_BASE = 10n; const DECIMAL_PART_COUNT = 2; const ZERO = 0n; type AccountsPageLoadedProps = { accounts: BrokerageAccountCard[]; meta: InvestmentResourceMeta; readRetry?: ReadRetryProps; }; type Money = { currency: string; value: string }; function accountTotals(accounts: BrokerageAccountCard[]) { const values = new Map(); let excludedCount = 0; for (const { summary } of accounts) { if (summary.state === "empty") continue; if (summary.state === "unavailable") { excludedCount += 1; continue; } for (const total of summary.portfolio.totalsByCurrency) { values.set(total.currency, [ ...values.get(total.currency) ?? [], total.value, ]); } } const totals: Money[] = Array.from(values, ([currency, amounts]) => ({ currency, value: sumDecimals(amounts), })); return { excludedCount, totals }; } function sumDecimals(values: string[]) { const scale = Math.max( 0, ...values.map(value => value.split(".", DECIMAL_PART_COUNT)[1]?.length ?? 0), ); const factor = DECIMAL_BASE ** BigInt(scale); let sum = ZERO; for (const value of values) { const isNegative = value.startsWith("-"); const [integer = "0", fraction = ""] = (isNegative ? value.slice(1) : value).split(".", DECIMAL_PART_COUNT); const units = BigInt(integer) * factor + BigInt(fraction.padEnd(scale, "0") || "0"); sum += isNegative ? -units : units; } const isNegative = sum < ZERO; const digits = (isNegative ? -sum : sum).toString().padStart(scale + 1, "0"); const integer = scale ? digits.slice(0, -scale) : digits; const fraction = scale ? `.${digits.slice(-scale)}` : ""; return `${isNegative ? "-" : ""}${integer}${fraction}`; } const AccountsPageLoaded: FC = (props) => { const { t } = useTranslation(); const { excludedCount, totals } = accountTotals(props.accounts); const primaryTotal = totals.find(total => total.currency === "RUB") ?? totals[0]; const otherTotals = totals.filter(total => total !== primaryTotal); return ( {t("accounts.title")} {t("accounts.currentValue")} {primaryTotal ? ( {formatMoney(primaryTotal, { withRoundedCents: true })} ) : ( {t("accounts.summaryNoTotals")} )} {otherTotals.length > 0 && ( {otherTotals.map(total => ( {formatMoney(total, { withRoundedCents: true })} ))} )} {t("accounts.count", { count: props.accounts.length })} {excludedCount > 0 && ( {t("accounts.summaryExcluded", { count: excludedCount })} )} {!!props.readRetry && } ); }; export { AccountsPageLoaded };