refactor: clarify frontend model ownership
Frontend verify / commits (pull_request) Successful in 27s
Frontend verify / security (pull_request) Successful in 9s
Frontend verify / verify (pull_request) Successful in 8m20s

This commit is contained in:
2026-09-28 21:44:53 +03:00
parent 7e976c330c
commit 0865b35f93
44 changed files with 685 additions and 710 deletions
+6
View File
@@ -2,6 +2,10 @@ import { apiQueryClient, type paths, useResourceQuery } from "@/shared/api";
const MIN_BOND_SEARCH_LENGTH = 2;
type BondCoupons = NonNullable<paths["/api/v1/bonds/{uid}/coupons"]["get"]["responses"][200]["content"]["application/json"]["data"]>;
type BondMetrics = NonNullable<paths["/api/v1/bonds/{uid}/metrics"]["get"]["responses"][200]["content"]["application/json"]["data"]>;
type BondPeriod = NonNullable<
NonNullable<
paths["/api/v1/bonds/{uid}/candles"]["get"]["parameters"]["query"]
@@ -104,6 +108,8 @@ function useBondSearch(query: string) {
}
export {
type BondCoupons,
type BondMetrics,
type BondPeriod,
MIN_BOND_SEARCH_LENGTH,
useBond,
+2
View File
@@ -1,4 +1,6 @@
export {
type BondCoupons,
type BondMetrics,
type BondPeriod,
MIN_BOND_SEARCH_LENGTH,
useBond,
@@ -68,6 +68,7 @@ function useBrokerageOperations(
export {
type BrokerageOperation,
type BrokerageOperationsQuery,
brokerageOperationsQueryOptions,
brokerageOperationsQueryParameters as brokerageOperationsQueryParams,
operationCategories,
+1
View File
@@ -5,4 +5,5 @@ export {
type OperationCategory,
useBrokerageOperations,
} from "./api/useBrokerageOperations";
export { useBrokerageOperationHistory } from "./model/useBrokerageOperationHistory";
export { LedgerEntryRow } from "./ui/LedgerEntryRow";
@@ -0,0 +1,66 @@
import { hashKey, useQueryClient } from "@tanstack/react-query";
import { useState } from "react";
import { ApiErrorCode, apiErrorCode, isApiErrorCode } from "@/shared/api";
import {
type BrokerageOperationsQuery,
brokerageOperationsQueryOptions,
useBrokerageOperations,
} from "../api/useBrokerageOperations";
function useBrokerageOperationHistory(
accountId: string | undefined,
firstPage: Omit<BrokerageOperationsQuery, "cursor">,
) {
const { queryKey } = brokerageOperationsQueryOptions(accountId, firstPage);
const scope = hashKey(queryKey);
const [pagination, setPagination] = useState<{
cursors: string[];
scope: string;
}>({ cursors: [], scope });
const cursors = pagination.scope === scope ? pagination.cursors : [];
const client = useQueryClient();
const operations = useBrokerageOperations(accountId, {
...firstPage,
cursor: cursors.at(-1),
});
const reset = () => setPagination({ cursors: [], scope });
const retry = (retryScreen: () => unknown) => {
if (
isApiErrorCode(
apiErrorCode(operations.error),
ApiErrorCode.InvalidOperationsCursor,
)
) {
reset();
void client.invalidateQueries({
exact: true,
queryKey,
});
}
else void retryScreen();
};
return {
hasPrevious: cursors.length > 0,
onNext: () => {
if (operations.data?.nextCursor)
setPagination({
cursors: [...cursors, operations.data.nextCursor],
scope,
});
},
onPrevious: () => setPagination({ cursors: cursors.slice(0, -1), scope }),
operations,
reset,
retry,
};
}
export { useBrokerageOperationHistory };
@@ -1,6 +1,7 @@
import type { InvestmentResourceMeta } from "@/shared/api";
import type {
BrokerageAccount,
useBrokerageAccounts,
} from "../api/useBrokerageAccounts";
const VISIBLE_CURRENCY_LIMIT = 2;
@@ -9,11 +10,7 @@ type BrokerageAccountCard = {
account: BrokerageAccount;
positionCounts: null
| { bonds: number; stocks: number };
source?: NonNullable<
ReturnType<typeof useBrokerageAccounts>["meta"]
>["dependencies"] extends (infer T)[] | undefined
? T
: never;
source?: NonNullable<InvestmentResourceMeta["dependencies"]>[number];
summary:
| { remainingCurrencyCount: number; state: "available"; totals: Money[] }
| { state: "empty" }
@@ -30,9 +27,7 @@ function accountTypeKey(type: BrokerageAccount["type"]) {
function brokerageAccountCards(
accounts: BrokerageAccount[],
dependencies: NonNullable<
ReturnType<typeof useBrokerageAccounts>["meta"]
>["dependencies"] = [],
dependencies: InvestmentResourceMeta["dependencies"] = [],
): BrokerageAccountCard[] {
return accounts.map((account) => {
const source = dependencies.find(
+4
View File
@@ -4,6 +4,9 @@ type IncomeAnalytics = NonNullable<
paths["/api/v1/brokerage-accounts/{accountId}/income-analytics"]["get"]["responses"][200]["content"]["application/json"]["data"]
>;
type IncomeAnalyticsMeta
= paths["/api/v1/brokerage-accounts/{accountId}/income-analytics"]["get"]["responses"][200]["content"]["application/json"]["meta"];
type IncomeSummary = NonNullable<
paths["/api/v1/brokerage-accounts/{accountId}/income-summary"]["get"]["responses"][200]["content"]["application/json"]["data"]
>;
@@ -44,6 +47,7 @@ function useIncomeSummary(accountId?: string) {
export {
type IncomeAnalytics,
type IncomeAnalyticsMeta,
type IncomeSummary,
useIncomeAnalytics,
useIncomeSummary,
+1
View File
@@ -1,5 +1,6 @@
export {
type IncomeAnalytics,
type IncomeAnalyticsMeta,
type IncomeSummary,
useIncomeAnalytics,
useIncomeSummary,