diff --git a/frontend/components/dashboard/ScanTerminalDashboard.tsx b/frontend/components/dashboard/ScanTerminalDashboard.tsx index ad5acd1f..47a5c790 100644 --- a/frontend/components/dashboard/ScanTerminalDashboard.tsx +++ b/frontend/components/dashboard/ScanTerminalDashboard.tsx @@ -46,7 +46,11 @@ import { scanRootClass } from "@/components/dashboard/scan-root-styles"; import { useRelativeTime } from "@/hooks/useRelativeTime"; import { Panel } from "@/components/dashboard/scan-terminal/Panel"; import { UsageGuideDashboard } from "@/components/dashboard/scan-terminal/UsageGuideDashboard"; -import { LiveTemperatureThresholdChart, clearCityDetailCache } from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart"; +import { + LiveTemperatureThresholdChart, + clearCityDetailCache, + preloadTemperatureChartCanvas, +} from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart"; import { KoyfinRowsTable } from "@/components/dashboard/scan-terminal/KoyfinRowsTable"; import { rowName, pct, money, temp, edgeClass } from "@/components/dashboard/scan-terminal/utils"; import { CitySelectorDropdown } from "@/components/dashboard/scan-terminal/CitySelectorDropdown"; @@ -428,6 +432,11 @@ function PolyWeatherTerminal({ const [activeNavKey, setActiveNavKey] = useState("thresholds"); const [onlineCount, setOnlineCount] = useState(null); + useEffect(() => { + if (activeNavKey !== "thresholds") return; + void preloadTemperatureChartCanvas(); + }, [activeNavKey]); + useEffect(() => { const fetchOnline = () => { if (typeof document !== "undefined" && document.visibilityState === "hidden") return; diff --git a/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx b/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx index 80735141..6c8c3ee4 100644 --- a/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx +++ b/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx @@ -63,9 +63,24 @@ const DEFERRED_DETAIL_LOAD_DELAY_MS = 1_200; const DEFERRED_DETAIL_LOAD_GROUP_SIZE = 3; const DEFERRED_DETAIL_LOAD_WAVE_STEP_MS = 900; +let temperatureChartCanvasModulePromise: Promise< + typeof import("@/components/dashboard/scan-terminal/TemperatureChartCanvas") +> | null = null; + +function loadTemperatureChartCanvasModule() { + temperatureChartCanvasModulePromise ??= import( + "@/components/dashboard/scan-terminal/TemperatureChartCanvas" + ); + return temperatureChartCanvasModulePromise; +} + +export function preloadTemperatureChartCanvas() { + return loadTemperatureChartCanvasModule().then(() => undefined, () => undefined); +} + const TemperatureChartCanvas = dynamic( () => - import("@/components/dashboard/scan-terminal/TemperatureChartCanvas").then( + loadTemperatureChartCanvasModule().then( (mod) => mod.TemperatureChartCanvas, ), { diff --git a/frontend/components/dashboard/scan-terminal/__tests__/refreshCadencePolicy.test.ts b/frontend/components/dashboard/scan-terminal/__tests__/refreshCadencePolicy.test.ts index ae49c4dd..a08f0cd4 100644 --- a/frontend/components/dashboard/scan-terminal/__tests__/refreshCadencePolicy.test.ts +++ b/frontend/components/dashboard/scan-terminal/__tests__/refreshCadencePolicy.test.ts @@ -72,6 +72,17 @@ export async function runTests() { chartSource.includes("2 * 60_000"), "selected city chart should consume SSE patches and use a 2-minute no-patch fallback", ); + assert( + chartSource.includes("loadTemperatureChartCanvasModule") && + chartSource.includes("preloadTemperatureChartCanvas"), + "terminal chart canvas should expose a preload hook for the dynamic chart chunk", + ); + assert( + dashboardSource.includes("preloadTemperatureChartCanvas") && + dashboardSource.includes("void preloadTemperatureChartCanvas()") && + dashboardSource.includes('activeNavKey !== "thresholds"'), + "terminal screen should preload the chart chunk once access is confirmed on the chart tab", + ); assert( chartSource.includes("fetchHourlyForecastForCity(city, { ignoreCache: true, resolution: targetResolution })") && chartSource.includes("setHourly(data)"),