2026-09-01 11:16:18 +08:00
|
|
|
import {
|
|
|
|
|
useInfiniteQuery,
|
|
|
|
|
useQuery,
|
|
|
|
|
type InfiniteData,
|
|
|
|
|
} from "@tanstack/react-query"
|
2026-08-29 20:14:42 +08:00
|
|
|
|
2026-09-06 16:06:17 +08:00
|
|
|
import {
|
|
|
|
|
getSectorRadarDates,
|
|
|
|
|
getSectorRadarRankings,
|
|
|
|
|
getStockSectorMembership,
|
|
|
|
|
getSectorRadarHistory,
|
|
|
|
|
getSectorRadarDetail,
|
|
|
|
|
} from "./sector-radar.api"
|
2026-09-01 11:16:18 +08:00
|
|
|
import type {
|
|
|
|
|
RadarRankingsQuery,
|
|
|
|
|
RadarRankingsResponse,
|
2026-09-06 16:06:17 +08:00
|
|
|
RadarSectorQuery,
|
2026-09-01 11:16:18 +08:00
|
|
|
} from "./sector-radar.types"
|
2026-08-29 20:14:42 +08:00
|
|
|
|
|
|
|
|
export const sectorRadarDatesQueryKey = ["sectorRadar", "dates"] as const
|
|
|
|
|
|
2026-09-05 16:23:50 +08:00
|
|
|
export const stockSectorMembershipQueryKey = (
|
|
|
|
|
tsCode?: string,
|
|
|
|
|
tradeDate?: string,
|
|
|
|
|
) =>
|
|
|
|
|
[
|
|
|
|
|
"sectorRadar",
|
|
|
|
|
"stockMembership",
|
|
|
|
|
tsCode ?? "none",
|
|
|
|
|
tradeDate ?? "none",
|
|
|
|
|
] as const
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read one stock's point-in-time industries and concepts for a detail view.
|
|
|
|
|
*
|
|
|
|
|
* Disabled until both the stock code and target trade date are known; the
|
|
|
|
|
* query stays cached per code-date pair so switching selections refetches
|
|
|
|
|
* only unseen pairs.
|
|
|
|
|
*/
|
|
|
|
|
export function useStockSectorMembership(tsCode?: string, tradeDate?: string) {
|
|
|
|
|
return useQuery({
|
|
|
|
|
enabled: Boolean(tsCode && tradeDate),
|
|
|
|
|
queryFn: ({ signal }) =>
|
|
|
|
|
getStockSectorMembership(tsCode ?? "", tradeDate ?? "", signal),
|
|
|
|
|
queryKey: stockSectorMembershipQueryKey(tsCode, tradeDate),
|
|
|
|
|
staleTime: 5 * 60 * 1000,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-29 20:14:42 +08:00
|
|
|
export const sectorRadarRankingsQueryKey = (query: RadarRankingsQuery) =>
|
|
|
|
|
[
|
|
|
|
|
"sectorRadar",
|
|
|
|
|
"rankings",
|
|
|
|
|
query.tradeDate ?? "latest",
|
|
|
|
|
query.sectorType,
|
|
|
|
|
query.view,
|
|
|
|
|
query.rankChangeMetric,
|
|
|
|
|
query.rankChangeDays,
|
|
|
|
|
query.side,
|
|
|
|
|
query.search ?? "",
|
|
|
|
|
query.pageSize,
|
|
|
|
|
] as const
|
|
|
|
|
|
|
|
|
|
export function useSectorRadarDates() {
|
|
|
|
|
return useQuery({
|
|
|
|
|
queryFn: ({ signal }) => getSectorRadarDates(signal),
|
|
|
|
|
queryKey: sectorRadarDatesQueryKey,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useSectorRadarRankings(query: RadarRankingsQuery) {
|
2026-09-01 11:16:18 +08:00
|
|
|
return useInfiniteQuery<
|
|
|
|
|
RadarRankingsResponse,
|
|
|
|
|
Error,
|
|
|
|
|
InfiniteData<RadarRankingsResponse>,
|
|
|
|
|
ReturnType<typeof sectorRadarRankingsQueryKey>,
|
|
|
|
|
number
|
|
|
|
|
>({
|
|
|
|
|
getNextPageParam: (lastPage, pages, lastPageParam) => {
|
|
|
|
|
const loadedRowCount = pages.reduce(
|
|
|
|
|
(count, page) => count + page.rows.length,
|
|
|
|
|
0,
|
|
|
|
|
)
|
|
|
|
|
if (
|
|
|
|
|
lastPage.status !== "success" ||
|
|
|
|
|
lastPage.rows.length === 0 ||
|
|
|
|
|
lastPage.page !== lastPageParam ||
|
|
|
|
|
loadedRowCount >= lastPage.total
|
|
|
|
|
) {
|
|
|
|
|
return undefined
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const nextPageParam = lastPageParam + 1
|
|
|
|
|
return Number.isSafeInteger(nextPageParam) ? nextPageParam : undefined
|
|
|
|
|
},
|
|
|
|
|
initialPageParam: 1,
|
|
|
|
|
queryFn: ({ pageParam, signal }) =>
|
|
|
|
|
getSectorRadarRankings({ ...query, page: pageParam }, signal),
|
2026-08-29 20:14:42 +08:00
|
|
|
queryKey: sectorRadarRankingsQueryKey(query),
|
|
|
|
|
})
|
|
|
|
|
}
|
2026-09-06 16:06:17 +08:00
|
|
|
|
|
|
|
|
export const sectorRadarSectorQueryKey = (
|
|
|
|
|
resource: "history" | "detail",
|
|
|
|
|
query: RadarSectorQuery,
|
|
|
|
|
) =>
|
|
|
|
|
[
|
|
|
|
|
"sectorRadar",
|
|
|
|
|
resource,
|
|
|
|
|
query.sectorType,
|
|
|
|
|
query.sectorCode,
|
|
|
|
|
query.tradeDate,
|
|
|
|
|
] as const
|
|
|
|
|
|
|
|
|
|
/** Keep on-demand history isolated by sector type, code and requested date. */
|
|
|
|
|
export function useSectorRadarHistory(query: RadarSectorQuery) {
|
|
|
|
|
return useQuery({
|
|
|
|
|
queryKey: sectorRadarSectorQueryKey("history", query),
|
|
|
|
|
queryFn: ({ signal }) => getSectorRadarHistory(query, signal),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Mount only while the detail dialog is open; never reuse another date as placeholder data. */
|
|
|
|
|
export function useSectorRadarDetail(query: RadarSectorQuery) {
|
|
|
|
|
return useQuery({
|
|
|
|
|
queryKey: sectorRadarSectorQueryKey("detail", query),
|
|
|
|
|
queryFn: ({ signal }) => getSectorRadarDetail(query, signal),
|
|
|
|
|
})
|
|
|
|
|
}
|