"use client"; import { useState, useEffect, memo, useMemo, useCallback } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { TrendingUp, TrendingDown, Package, DollarSign, AlertTriangle, RefreshCw, Calendar, BarChart3, Brain, Layers, Zap, Info, Download, } from "lucide-react"; import { useToast } from "@/hooks/use-toast"; import { Skeleton } from "@/components/ui/skeleton"; import CountUp from "react-countup"; import { getPredictionsOverviewWithStore, getStockPredictionsWithStore, type PredictionsOverview, type StockPredictionsResponse, } from "@/lib/services/analytics-service"; import { formatGBP } from "@/utils/format"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { format } from "date-fns"; import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip as RechartsTooltip, ResponsiveContainer, } from "recharts"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Slider } from "@/components/ui/slider"; interface PredictionsChartProps { timeRange?: number; } export default function PredictionsChart({ timeRange = 90, }: PredictionsChartProps) { const [predictions, setPredictions] = useState( null, ); const [baselinePredictions, setBaselinePredictions] = useState( null, ); const [stockPredictions, setStockPredictions] = useState(null); const [loading, setLoading] = useState(true); const [isSimulating, setIsSimulating] = useState(false); const [daysAhead, setDaysAhead] = useState(7); const [activeTab, setActiveTab] = useState<"overview" | "stock">("overview"); const [simulationFactor, setSimulationFactor] = useState(0); const [committedSimulationFactor, setCommittedSimulationFactor] = useState(0); const { toast } = useToast(); // Fetch baseline predictions (simulation factor = 0) const fetchBaseline = async () => { try { setLoading(true); const [overview, stock] = await Promise.all([ getPredictionsOverviewWithStore(daysAhead, timeRange, 0), getStockPredictionsWithStore(timeRange), ]); setBaselinePredictions(overview); setPredictions(overview); setStockPredictions(stock); } catch (error) { console.error("Error fetching predictions:", error); toast({ title: "Error", description: "Failed to load predictions", variant: "destructive", }); } finally { setLoading(false); } }; // Fetch simulated predictions (without full reload) const fetchSimulation = async (factor: number) => { if (factor === 0) { // Use cached baseline setPredictions(baselinePredictions); 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); } }; // Fetch baseline on initial load or when daysAhead/timeRange changes useEffect(() => { fetchBaseline(); setCommittedSimulationFactor(0); setSimulationFactor(0); }, [daysAhead, timeRange]); // Fetch simulation when committed slider value changes useEffect(() => { if (baselinePredictions) { fetchSimulation(committedSimulationFactor); } }, [committedSimulationFactor]); const getConfidenceColor = (confidence: string) => { switch (confidence) { case "very_high": return "bg-emerald-600/20 text-emerald-700 dark:text-emerald-400 border-emerald-600/30"; case "high": return "bg-green-500/10 text-green-700 dark:text-green-400"; case "medium": return "bg-yellow-500/10 text-yellow-700 dark:text-yellow-400"; case "low": return "bg-red-500/10 text-red-700 dark:text-red-400"; default: return "bg-gray-500/10 text-gray-700 dark:text-gray-400"; } }; const getConfidenceLabel = (confidence: string) => { switch (confidence) { case "very_high": return "Very High"; case "high": return "High"; case "medium": return "Medium"; case "low": return "Low"; default: return confidence; } }; // Combine baseline and simulated data for overlay chart const chartData = useMemo(() => { if (!predictions?.sales?.dailyPredictions) return []; const baselineData = baselinePredictions?.sales?.dailyPredictions || []; const simulatedDailyData = predictions.sales.dailyPredictions; return simulatedDailyData.map((d: any, idx: number) => ({ ...d, formattedDate: format(new Date(d.date), "MMM d"), simulated: d.predicted, baseline: baselineData[idx]?.predicted ?? d.predicted, orders: d.predictedOrders || 0, })); }, [predictions, baselinePredictions]); // Keep simulatedData for export compatibility const simulatedData = chartData; const handleExportCSV = () => { if (!simulatedData.length) return; // Create CSV headers const headers = ["Date", "Baseline Revenue", "Simulated Revenue", "Orders", "Confidence"]; // Create CSV rows const rows = simulatedData.map(d => [ format(new Date(d.date), "yyyy-MM-dd"), d.baseline?.toFixed(2) || "", d.simulated?.toFixed(2) || "", d.orders || "", predictions?.sales?.confidence || "unknown" ]); // Combine headers and rows const csvContent = [ headers.join(","), ...rows.map(row => row.join(",")) ].join("\n"); // Create download link const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.setAttribute("href", url); link.setAttribute("download", `predictions_export_${format(new Date(), "yyyyMMdd")}.csv`); link.style.visibility = "hidden"; document.body.appendChild(link); link.click(); document.body.removeChild(link); }; if (loading) { return (
); } if (!predictions) { return ( Predictions Forecast future sales, demand, and stock levels

No prediction data available. Need more historical data.

); } return (
Predictions & Forecasting {predictions.sales.aiModel?.used ? "AI neural network + statistical models for sales, demand, and inventory" : "AI-powered predictions for sales, demand, and inventory"}
{activeTab === "overview" && (
{/* Sales Predictions */}
Revenue Prediction {predictions.sales.predicted !== null ? (

Predicted daily average revenue for the next {daysAhead} days

{getConfidenceLabel(predictions.sales.confidence)} Confidence {predictions.sales.confidenceScore !== undefined && ( ({Math.round(predictions.sales.confidenceScore * 100)}%) )}

Based on data consistency, historical accuracy, and model agreement

{predictions.sales.aiModel?.used && ( 🤖 AI Powered {predictions.sales.aiModel.modelAccuracy !== undefined && ( ({Math.round(predictions.sales.aiModel.modelAccuracy * 100)}%) )}

Predictions generated using a Deep Learning Ensemble Model (TensorFlow.js)

)} {predictions.sales.trend && ( {predictions.sales.trend.direction === "up" && ( )} {predictions.sales.trend.direction === "down" && ( )} {predictions.sales.trend.direction === "up" ? "Trending Up" : predictions.sales.trend.direction === "down" ? "Trending Down" : "Stable"}

Direction of the recent sales trend (slope analysis)

)} Next {daysAhead} days
{predictions.sales.predictedOrders && (
~{Math.round(predictions.sales.predictedOrders)}{" "} orders
)} {!predictions.sales.confidenceIntervals && predictions.sales.minPrediction && predictions.sales.maxPrediction && (
Range: {formatGBP(predictions.sales.minPrediction)} -{" "} {formatGBP(predictions.sales.maxPrediction)}
)}
) : (
{predictions.sales.message || "Insufficient data for prediction"}
)}
{/* Model Intelligence Card */} Model Intelligence

Technical details about the active prediction model

Architecture Hybrid Ensemble (Deep Learning)

Combines LSTM Neural Networks with Statistical Methods (Holt-Winters, ARIMA)

{stockPredictions?.predictions && (
Features Multi-Feature Enabled
)}
Optimization Performance Tuned
Model automatically retrains with new sales data.
Prediction Accuracy Warning These predictions are estimates based on historical sales data. Actual results may vary due to external factors, market conditions, and unforeseen events. Use these insights as a guide, not a guarantee. {/* Daily Predictions Chart */} {predictions.sales.dailyPredictions && predictions.sales.dailyPredictions.length > 0 && ( Daily Revenue Forecast
Simulate Traffic:{" "} 0 ? "text-green-600" : simulationFactor < 0 ? "text-red-600" : ""}> {simulationFactor > 0 ? "+" : ""} {simulationFactor}% setSimulationFactor(val[0])} onValueCommit={(val) => setCommittedSimulationFactor(val[0])} className="w-[150px] mt-1.5" />
{simulationFactor !== 0 && ( )}
{isSimulating && (
)} `£${value}`} /> [ formatGBP(value), name === "baseline" ? "Baseline" : "Simulated" ]} /> {/* Always show baseline as solid line */} {/* Show simulated line when simulation is active */} {committedSimulationFactor !== 0 && ( )}
)}
)} {activeTab === "stock" && (
{stockPredictions && stockPredictions.predictions.length > 0 ? ( <>

{stockPredictions.totalProducts} products tracked

{stockPredictions.productsNeedingRestock > 0 && (

{stockPredictions.productsNeedingRestock} products need restocking soon

)}
Product Current Stock Days Until Out Estimated Date Confidence Status {stockPredictions.predictions.map((prediction) => ( {prediction.productName} {prediction.currentStock} {prediction.unitType} {prediction.prediction.daysUntilOutOfStock !== null ? (
{prediction.prediction.daysUntilOutOfStock} days
{prediction.prediction.optimisticDays !== null && prediction.prediction.pessimisticDays && (
{prediction.prediction.optimisticDays} -{" "} {prediction.prediction.pessimisticDays} days
)}
) : ( "N/A" )}
{prediction.prediction.estimatedDate ? (
{format( new Date( prediction.prediction.estimatedDate, ), "MMM d, yyyy", )}
{prediction.prediction.optimisticDate && prediction.prediction.pessimisticDate && (
{format( new Date( prediction.prediction.optimisticDate, ), "MMM d", )}{" "} -{" "} {format( new Date( prediction.prediction.pessimisticDate, ), "MMM d", )}
)}
) : ( "N/A" )}
{getConfidenceLabel(prediction.prediction.confidence)} {prediction.needsRestock ? ( Restock Soon ) : ( OK )}
))}
) : (

No stock predictions available

Enable stock tracking on products to get predictions

)}
)}
); }