import { useQueryClient } from "@tanstack/react-query"; import { useState } from "react"; import { useParams, useSearchParams } from "react-router"; import { type BondPeriod, useBond, useBondCandles, useBondCoupons, useBondMetrics, useBondPosition, useBondQuote, } from "@/entities/bond"; import { brokerageOperationsQueryOptions, type OperationCategory, useBrokerageOperations, } from "@/entities/broker-operation"; import { section } from "@/features/instrument-details"; import { apiErrorCode } from "@/shared/api"; import { formatDate, instrumentLogoUrl } from "@/shared/lib"; const periods: BondPeriod[] = ["month", "halfYear", "year", "all"]; const filters = ["all", "trades", "coupons", "other"] as const; type BondEventFilter = (typeof filters)[number]; const categories: Record = { all: [], trades: ["trade"], coupons: ["income"], other: ["tax", "fee", "transfer", "other"], }; function useBondPageVm() { const { bondId = "" } = useParams(); const bond = useBond(bondId); const uid = bond.data?.instrumentUid ?? ""; const [params, setParams] = useSearchParams(); const period = periods.find((value) => value === params.get("period")) ?? "month"; const filter = filters.find((value) => value === params.get("events")) ?? "all"; const scope = `${uid}:${filter}`; const [pagination, setPagination] = useState<{ scope: string; cursors: string[]; }>({ scope, cursors: [] }); const cursors = pagination.scope === scope ? pagination.cursors : []; const client = useQueryClient(); const quote = useBondQuote(uid); const metrics = useBondMetrics(uid); const candles = useBondCandles(uid, period); const position = useBondPosition(uid); const coupons = useBondCoupons(uid); const firstPage = { instrumentUid: uid, categories: categories[filter], limit: 20, }; const operations = useBrokerageOperations(uid ? "all" : undefined, { ...firstPage, cursor: cursors.at(-1), }); const setParam = (key: string, value: string) => { const next = new URLSearchParams(params); next.set(key, value); setParams(next, { replace: true }); }; const retryOperations = () => { if (apiErrorCode(operations.error) === "INVALID_OPERATIONS_CURSOR") { setPagination({ scope, cursors: [] }); void client.invalidateQueries({ queryKey: brokerageOperationsQueryOptions("all", firstPage).queryKey, exact: true, }); } else void operations.refetch(); }; const chartData = candles.data?.candles.map((candle) => ({ ...candle, date: formatDate(candle.time), price: Number(candle.close), })) ?? []; return { canonicalTo: bond.data?.slug && bond.data.slug !== bondId ? `/bonds/${encodeURIComponent(bond.data.slug)}${params.size ? `?${params.toString()}` : ""}` : null, bond: bond.data, bondState: section(bond), code: apiErrorCode(bond.error), logoSrc: instrumentLogoUrl(bond.data?.logoName), metrics: metrics.data, metricsState: section(metrics), quote: quote.data, quoteState: section(quote), candles: candles.data, chartData, priceRows: chartData.toReversed(), candlesState: section(candles), period, setPeriod: (value: string) => setParam("period", value), averageLine: null, hasIncompleteCandle: candles.data?.candles.some((candle) => !candle.isComplete) ?? false, position: position.data, positionState: section(position), coupons: coupons.data, couponsState: section(coupons), upcoming: coupons.data?.coupons .filter((event) => event.timing === "upcoming") .sort((a, b) => (a.paymentDate ?? a.recordDate ?? "").localeCompare( b.paymentDate ?? b.recordDate ?? "", ), ) ?? [], pastCoupons: coupons.data?.coupons .filter((event) => event.timing !== "upcoming") .sort((a, b) => (b.paymentDate ?? b.recordDate ?? "").localeCompare( a.paymentDate ?? a.recordDate ?? "", ), ) ?? [], operations: operations.data, operationsState: { ...section(operations), retry: retryOperations }, eventRows: operations.data?.items.map((item) => ({ ...item, ticker: item.ticker ?? bond.data?.ticker ?? null, logoName: item.logoName ?? bond.data?.logoName ?? null, })) ?? [], filter, setFilter: (value: string) => setParam("events", value), hasPrevious: cursors.length > 0, onPrevious: () => setPagination({ scope, cursors: cursors.slice(0, -1) }), onNext: () => { if (operations.data?.nextCursor) setPagination({ scope, cursors: [...cursors, operations.data.nextCursor], }); }, }; } type BondPageVm = ReturnType; export { type BondPageVm, useBondPageVm };