refactor: clarify frontend model ownership
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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,5 +1,6 @@
|
||||
export {
|
||||
type IncomeAnalytics,
|
||||
type IncomeAnalyticsMeta,
|
||||
type IncomeSummary,
|
||||
useIncomeAnalytics,
|
||||
useIncomeSummary,
|
||||
|
||||
Reference in New Issue
Block a user