import { useRef, useState } from "react"; import { apiClient } from "@/shared/api"; type Criteria = { countryOfRisk: string; currency: string; maxLot: string; query: string; realExchange: string; sector: string; shareType: string; }; type ScreenerPage = Awaited>; type StocksPageVm = ReturnType; const emptyCriteria: Criteria = { countryOfRisk: "", currency: "", maxLot: "", query: "", realExchange: "", sector: "", shareType: "", }; async function createSearch(criteria: Criteria) { const { data } = await apiClient.POST("/api/v1/stocks/screener/search", { body: { ...(criteria.countryOfRisk && { countryOfRisk: criteria.countryOfRisk }), ...(criteria.currency && { currency: criteria.currency }), ...(criteria.maxLot && { maxLot: Number(criteria.maxLot) }), ...(criteria.query && { query: criteria.query }), ...(criteria.realExchange && { realExchange: criteria.realExchange }), ...(criteria.sector && { sector: criteria.sector }), ...(criteria.shareType && { shareType: criteria.shareType }), }, }); if (!data) throw new Error("Stock screener search failed"); return data; } async function readPage(searchId: string, page: number) { const { data } = await apiClient.GET( "/api/v1/stocks/screener/search/{searchId}", { params: { path: { searchId }, query: { page } } }, ); if (!data) throw new Error("Stock screener page failed"); return data; } function useStocksPageVm() { const [criteria, setCriteria] = useState(emptyCriteria); const [page, setPage] = useState(null); const [pending, setPending] = useState(false); const [error, setError] = useState(false); const requestIdRef = useRef(0); const search = async () => { const currentRequestId = requestIdRef.current + 1; requestIdRef.current = currentRequestId; setPending(true); setError(false); try { const result = await createSearch(criteria); if (requestIdRef.current === currentRequestId) setPage(result); } catch { if (requestIdRef.current === currentRequestId) setError(true); } finally { if (requestIdRef.current === currentRequestId) setPending(false); } }; const changePage = async (nextPage: number) => { if (!page) return; const currentRequestId = requestIdRef.current + 1; requestIdRef.current = currentRequestId; setPending(true); setError(false); try { const result = await readPage(page.searchId, nextPage); if (requestIdRef.current === currentRequestId) setPage(result); } catch { if (requestIdRef.current === currentRequestId) setError(true); } finally { if (requestIdRef.current === currentRequestId) setPending(false); } }; return { changePage, criteria, error, items: page?.items ?? [], loading: pending, onCriteriaChange: (name: keyof Criteria, value: string) => setCriteria(current => ({ ...current, [name]: value })), onPage: (nextPage: number) => void changePage(nextPage), onSearch: () => void search(), page: page?.page, resultTime: page?.asOf, // Test seam: settle the actual operation instead of the handler release. search, searched: page !== null, totalPages: page ? page.page + Number(page.hasNext) : 0, }; } export { type Criteria, type StocksPageVm, useStocksPageVm };