diff --git a/components/analytics/AnalyticsDashboard.tsx b/components/analytics/AnalyticsDashboard.tsx index 5a3d858..4e37f52 100644 --- a/components/analytics/AnalyticsDashboard.tsx +++ b/components/analytics/AnalyticsDashboard.tsx @@ -49,8 +49,6 @@ import type { DateRange as ProfitDateRange } from "@/lib/services/profit-analyti import { MotionWrapper } from "@/components/ui/motion-wrapper"; import { motion } from "framer-motion"; -// Lazy load chart components - already handled individually below - const RevenueChart = dynamic(() => import("./RevenueChart"), { loading: () => , }); diff --git a/components/analytics/PredictionsChart.tsx b/components/analytics/PredictionsChart.tsx index 75b93e6..f749c7b 100644 --- a/components/analytics/PredictionsChart.tsx +++ b/components/analytics/PredictionsChart.tsx @@ -82,6 +82,12 @@ export default function PredictionsChart({ const [baselinePredictions, setBaselinePredictions] = useState( null, ); + // Batch data holds all pre-cached predictions for instant switching + const [batchData, setBatchData] = useState<{ + [horizon: string]: { + [simulationFactor: string]: PredictionsOverview; + }; + } | null>(null); const [stockPredictions, setStockPredictions] = useState(null); const [loading, setLoading] = useState(true); @@ -92,16 +98,33 @@ export default function PredictionsChart({ const [committedSimulationFactor, setCommittedSimulationFactor] = useState(0); const { toast } = useToast(); - // Fetch baseline predictions (simulation factor = 0) - const fetchBaseline = async () => { + // Fetch all predictions in batch (for instant client-side switching) + const fetchBatchData = async () => { try { setLoading(true); - const [overview, stock] = await Promise.all([ - getPredictionsOverviewWithStore(daysAhead, timeRange, 0), + const { getBatchPredictionsWithStore } = await import("@/lib/services/analytics-service"); + const [batchResponse, stock] = await Promise.all([ + getBatchPredictionsWithStore(timeRange), getStockPredictionsWithStore(timeRange), ]); - setBaselinePredictions(overview); - setPredictions(overview); + + if (batchResponse.success && batchResponse.predictions) { + setBatchData(batchResponse.predictions); + // Set initial predictions from batch + const horizonData = batchResponse.predictions[daysAhead.toString()]; + if (horizonData) { + const baseline = horizonData["0"]; + if (baseline) { + setBaselinePredictions(baseline); + setPredictions(baseline); + } + } + } else { + // Fallback to single request if batch not available + const overview = await getPredictionsOverviewWithStore(daysAhead, timeRange, 0); + setBaselinePredictions(overview); + setPredictions(overview); + } setStockPredictions(stock); } catch (error) { console.error("Error fetching predictions:", error); @@ -115,43 +138,46 @@ export default function PredictionsChart({ } }; - // Fetch simulated predictions (without full reload) - const fetchSimulation = async (factor: number) => { - if (factor === 0) { - // Use cached baseline - setPredictions(baselinePredictions); - return; - } + // Switch predictions from batch data (no API call!) + const switchPredictions = useCallback((horizon: number, simFactor: number) => { + if (!batchData) return; - try { - setIsSimulating(true); - const overview = await getPredictionsOverviewWithStore(daysAhead, timeRange, factor / 100); - setPredictions(overview); - } catch (error) { - console.error("Error fetching simulation:", error); - toast({ - title: "Error", - description: "Failed to load simulation", - variant: "destructive", - }); - } finally { - setIsSimulating(false); - } - }; + const horizonData = batchData[horizon.toString()]; + if (!horizonData) return; - // Fetch baseline on initial load or when daysAhead/timeRange changes + // Simulation factor is stored as decimal (e.g., 0.1 for 10%) + const simKey = (simFactor / 100).toString(); + const newPrediction = horizonData[simKey]; + + if (newPrediction) { + setPredictions(newPrediction); + if (simFactor === 0) { + setBaselinePredictions(newPrediction); + } + } + }, [batchData]); + + // Fetch batch data on initial load or when timeRange changes useEffect(() => { - fetchBaseline(); + fetchBatchData(); setCommittedSimulationFactor(0); setSimulationFactor(0); - }, [daysAhead, timeRange]); + }, [timeRange]); - // Fetch simulation when committed slider value changes + // Switch predictions when daysAhead changes (instant, from batch) useEffect(() => { - if (baselinePredictions) { - fetchSimulation(committedSimulationFactor); + if (batchData) { + switchPredictions(daysAhead, committedSimulationFactor); } - }, [committedSimulationFactor]); + }, [daysAhead, batchData, switchPredictions]); + + // Switch predictions when simulation factor changes (instant, from batch) + useEffect(() => { + if (batchData) { + switchPredictions(daysAhead, committedSimulationFactor); + } + }, [committedSimulationFactor, batchData, switchPredictions]); + const getConfidenceColor = (confidence: string) => { switch (confidence) { @@ -305,7 +331,7 @@ export default function PredictionsChart({ - Predictions generated using a Deep Learning Ensemble Model (TensorFlow.js) + Predictions generated using a Deep Learning Ensemble Model )}
Predictions generated using a Deep Learning Ensemble Model (TensorFlow.js)
Predictions generated using a Deep Learning Ensemble Model