feat(selection): rework results layout and add sector filter

- move the filter bar out of the list column into a full-width top section;
  list and detail stay side by side below on md+, stacking search > list >
  detail on small screens
- add sector select fed by the run's concept-board aggregates; options show
  per-sector stock counts ordered by count desc, stale selections reset when
  the aggregate list changes
- persist sector in the route search, results/runs query keys, and refresh
  sector aggregates when a selection run finishes
This commit is contained in:
yuxuanhui
2026-09-05 19:48:41 +08:00
parent 7e0f13d678
commit 12642f3c2d
8 changed files with 233 additions and 26 deletions
@@ -10,6 +10,7 @@ import { useEffect } from "react"
import {
getSelectionChart,
getSelectionResultSectors,
getSelectionResults,
getSelectionRun,
triggerSelectionRun,
@@ -19,6 +20,7 @@ import {
type SelectionResults,
type SelectionResultsQuery,
type SelectionRunRequest,
type SelectionSectors,
type SelectionStrategy,
} from "./selection.types"
@@ -55,6 +57,7 @@ export const selectionResultsQueryKey = (
query.search ?? "",
query.category ?? "all",
query.sort ?? "code",
query.sector ?? "",
] as const
export const selectionRunQueryKey = (
@@ -69,8 +72,15 @@ export const selectionRunQueryKey = (
query.search ?? "",
query.category ?? "all",
query.sort ?? "code",
query.sector ?? "",
] as const
export const selectionSectorsQueryKey = (
strategy: SelectionStrategy,
targetTradeDate?: string,
sectorType: "concept" | "industry" = "concept",
) => ["selection", "sectors", strategy, targetTradeDate, sectorType] as const
export function flattenSelectionResults(
data: InfiniteData<SelectionResults> | undefined,
): SelectionResults | undefined {
@@ -120,6 +130,18 @@ export function useSelectionChart(
})
}
export function useSelectionResultSectors(
strategy: SelectionStrategy,
targetTradeDate?: string,
sectorType: "concept" | "industry" = "concept",
) {
return useQuery<SelectionSectors, Error>({
queryFn: ({ signal }) =>
getSelectionResultSectors(strategy, targetTradeDate, sectorType, signal),
queryKey: selectionSectorsQueryKey(strategy, targetTradeDate, sectorType),
})
}
export function useSelectionRun(
runId: string | null,
resultQuery: SelectionResultsListQuery = defaultListQuery,
@@ -173,9 +195,14 @@ export function invalidateSelectionResults(
queryClient: QueryClient,
strategy: SelectionStrategy,
) {
return queryClient.invalidateQueries({
queryKey: ["selection", "results", strategy],
})
return Promise.all([
queryClient.invalidateQueries({
queryKey: ["selection", "results", strategy],
}),
queryClient.invalidateQueries({
queryKey: ["selection", "sectors", strategy],
}),
])
}
function nextSelectionPageParam(