import { useParams } from "react-router"; import type { Portfolio } from "@/entities/portfolio"; import type { InvestmentResourceMeta } from "@/shared/api"; import { useBrokerageOperations } from "@/entities/broker-operation"; import { type BrokerageAccount, useBrokerageAccounts, } from "@/entities/brokerage-account"; import { useBrokerageEvents, } from "@/entities/brokerage-event"; import { useIncomeSummary } from "@/entities/income"; import { usePortfolio } from "@/entities/portfolio"; import { useScreenSync } from "@/features/tbank-connection"; import { ApiErrorCode, apiErrorCode, isApiErrorCode } from "@/shared/api"; import { useInitialAvailability } from "@/shared/lib"; import { type AccountPagePositionSection, toAccountPagePositionSections, } from "../../model/positions"; import { type AccountPageIncomeSummary, type AccountPageRecentEvents, type AccountPageRecentOperations, toIncomeSummary, toRecentEvents, toRecentOperations, } from "../../model/previews"; type AccountPageContent = { account?: BrokerageAccount; accountId?: string; incomeSummary: AccountPageIncomeSummary; partial?: boolean; recentEvents: AccountPageRecentEvents; recentOperations: AccountPageRecentOperations; }; type AccountPageEmptyViewModel = AccountPageContent & { state: "empty"; }; type AccountPageLoadedViewModel = AccountPageContent & { portfolio: Portfolio; positionSections: AccountPagePositionSection[]; source?: InvestmentResourceMeta; state: "loaded"; }; type AccountPageViewModel = | AccountPageEmptyViewModel | AccountPageLoadedViewModel | { code?: string; state: "error" } | { state: "loading" }; function isPortfolioAccessFailure(code?: string) { return isApiErrorCode(code, ApiErrorCode.Forbidden) || isApiErrorCode(code, ApiErrorCode.InvalidTbankToken) || isApiErrorCode(code, ApiErrorCode.NotFound) || isApiErrorCode(code, ApiErrorCode.TbankNotConnected); } function useAccountPageViewModel(): AccountPageViewModel { const { accountId } = useParams(); const portfolio = usePortfolio(accountId); const operations = useBrokerageOperations(accountId, { limit: 5, }); const events = useBrokerageEvents(accountId); const income = useIncomeSummary(accountId); const accounts = useBrokerageAccounts(); useScreenSync({ accountId, screen: "account" }, [ accounts, portfolio, operations, events, income, ]); const code = apiErrorCode(portfolio.error); const isAccessFailure = isPortfolioAccessFailure(code); const availability = useInitialAvailability({ hasContent: !!portfolio.data, identity: accountId ?? "account", isError: portfolio.isError || portfolio.meta?.lastAttempt.state === "failed", isFetching: portfolio.isFetching, }); if (isAccessFailure) return { code, state: "error" as const }; if (!portfolio.data) return { state: availability === "unavailable" ? "error" : "loading", } as const; const account = accounts.data?.find(item => item.id === accountId); const recentOperations = toRecentOperations(accountId, operations); const recentEvents = toRecentEvents(accountId, events); const incomeSummary = toIncomeSummary(accountId, income); if ( portfolio.meta?.availability === "available" && portfolio.data.positions.length === 0 && portfolio.data.cashBalances.length === 0 ) return { account, accountId, incomeSummary, recentEvents, recentOperations, state: "empty" as const, }; const isPartial = portfolio.meta?.completeness === "incomplete"; return { account, accountId, incomeSummary, partial: isPartial, portfolio: portfolio.data, positionSections: toAccountPagePositionSections(portfolio.data), recentEvents, recentOperations, source: portfolio.meta, state: "loaded" as const, }; } export { useAccountPageViewModel };