140 lines
3.7 KiB
TypeScript
140 lines
3.7 KiB
TypeScript
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 };
|