Stabilize the decision workspace data boundaries
The scan terminal had grown into overlapping CSS, request-state, AI-provider, and city-card data responsibilities. This refactor separates those boundaries without changing product behavior: CSS modules are split by surface, city AI prompt/provider/fallback logic is isolated, and scan terminal request state now has reusable RemoteData adapters plus business-state tests. Constraint: Preserve existing global scan-terminal class names and API responses during the refactor Constraint: No new dependencies; keep this as a file-boundary cleanup Rejected: Introduce React Query now | higher migration risk than the requested lightweight query-client path Rejected: Rewrite AI stream behavior | progressive/fallback states are product-sensitive and were only adapter-split Confidence: high Scope-risk: moderate Reversibility: clean Directive: Keep AI stream state changes covered by business snapshots before changing fallback/cache wording Tested: npm run test:business; npx tsc --noEmit; npm run build; python pytest -q; ruff check; py_compile targeted city AI modules Not-tested: Live DeepSeek provider network replay and browser visual QA
This commit is contained in:
@@ -29,6 +29,11 @@ export type AiCityStreamProgress = {
|
||||
raw_length?: number | null;
|
||||
};
|
||||
|
||||
export const scanTerminalQueryPolicy = {
|
||||
autoRefreshMs: 10 * 60_000,
|
||||
manualForceRefreshCooldownMs: 2 * 60_000,
|
||||
} as const;
|
||||
|
||||
type AiCityStreamEvent = {
|
||||
data: Record<string, unknown>;
|
||||
event: string;
|
||||
@@ -100,6 +105,32 @@ export function toRemoteError<T>(
|
||||
};
|
||||
}
|
||||
|
||||
export function shouldSkipManualTerminalRefresh({
|
||||
hasCurrentData,
|
||||
lastForcedRefreshAt,
|
||||
now = Date.now(),
|
||||
}: {
|
||||
hasCurrentData: boolean;
|
||||
lastForcedRefreshAt: number;
|
||||
now?: number;
|
||||
}) {
|
||||
return (
|
||||
hasCurrentData &&
|
||||
lastForcedRefreshAt > 0 &&
|
||||
now - lastForcedRefreshAt < scanTerminalQueryPolicy.manualForceRefreshCooldownMs
|
||||
);
|
||||
}
|
||||
|
||||
export function shouldRunAutoTerminalRefresh({
|
||||
documentHidden,
|
||||
isLoading,
|
||||
}: {
|
||||
documentHidden: boolean;
|
||||
isLoading: boolean;
|
||||
}) {
|
||||
return !documentHidden && !isLoading;
|
||||
}
|
||||
|
||||
async function readJsonOrThrow<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const response = await fetchBackendApi(path, init);
|
||||
if (response.ok) return response.json() as Promise<T>;
|
||||
|
||||
Reference in New Issue
Block a user