diff --git a/components/admin/AdminAnalytics.tsx b/components/admin/AdminAnalytics.tsx index b747158..dd1426e 100644 --- a/components/admin/AdminAnalytics.tsx +++ b/components/admin/AdminAnalytics.tsx @@ -29,6 +29,7 @@ import { Package, Trophy, PieChart as PieChartIcon, + Zap, } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/common/alert"; import { fetchClient } from "@/lib/api/api-client"; @@ -46,7 +47,7 @@ import { AreaChart, Area, } from "recharts"; -import { formatGBP, formatNumber } from "@/lib/utils/format"; +import { formatGBP, formatNumber, formatCurrency } from "@/lib/utils/format"; import { PieChart, Pie, Cell, Legend, Sector } from "recharts"; import { AdminStatCard } from "./AdminStatCard"; import { TrendIndicator } from "./TrendIndicator"; @@ -132,6 +133,168 @@ const renderActiveShape = (props: any) => { ); }; +const getSmartInsights = (data?: AnalyticsData | null, growth?: GrowthData | null) => { + const insights: Array<{ + type: 'positive' | 'neutral' | 'information'; + message: string; + icon: any; + color: string; + bg: string; + }> = []; + + if (!data) return insights; + + // 1. AI Forecast (Always high priority if available) + if (data.predictions && data.predictions.predicted > 0) { + insights.push({ + type: 'positive', + message: `AI Forecast: Projected ${formatCurrency(data.predictions.predicted)} revenue over the next 7 days.`, + icon: Zap, + color: 'text-purple-500', + bg: 'bg-purple-500/10' + }); + } + + // 2. Comprehensive Comparisons Data + if (data.comparisons) { + const rawInsights: Array<{ + score: number; // Importance score + type: 'positive' | 'neutral' | 'information'; + message: string; + icon: any; + color: string; + bg: string; + }> = []; + + const periods = Object.entries(data.comparisons); + + periods.forEach(([label, metrics]) => { + const timeframeName = label === '1w' ? 'last week' : + label === '2w' ? 'last 2 weeks' : + label === '1m' ? 'last month' : + label === '3m' ? 'last 3 months' : + label === '6m' ? 'last 6 months' : + label === '1y' ? 'last year' : 'last 6 months'; + + // --- Revenue Insight --- + if (metrics.revenue.previous > 0) { + const revDiff = metrics.revenue.current - metrics.revenue.previous; + const revPercent = (revDiff / metrics.revenue.previous) * 100; + + if (Math.abs(revPercent) > 5) { + rawInsights.push({ + score: Math.abs(revPercent) * (label === '1w' ? 1.5 : 1), // Weight recent changes higher + type: revPercent > 0 ? 'positive' : 'neutral', + message: `Revenue is ${revPercent > 0 ? 'up' : 'down'} ${Math.abs(revPercent).toFixed(1)}% vs ${timeframeName} (${formatCurrency(Math.abs(revDiff))} difference).`, + icon: revPercent > 0 ? TrendingUp : TrendingDown, + color: revPercent > 0 ? 'text-green-500' : 'text-orange-500', + bg: revPercent > 0 ? 'bg-green-500/10' : 'bg-orange-500/10' + }); + } + } + + // --- Order Volume Insight --- + if (metrics.orders.previous > 0) { + const ordDiff = metrics.orders.current - metrics.orders.previous; + const ordPercent = (ordDiff / metrics.orders.previous) * 100; + + if (Math.abs(ordPercent) > 10) { + rawInsights.push({ + score: Math.abs(ordPercent) * 0.8, + type: ordPercent > 0 ? 'positive' : 'neutral', + message: `Order volume has ${ordPercent > 0 ? 'increased' : 'decreased'} by ${Math.abs(ordPercent).toFixed(0)}% compared to ${timeframeName}.`, + icon: ShoppingCart, + color: ordPercent > 0 ? 'text-blue-500' : 'text-slate-400', + bg: ordPercent > 0 ? 'bg-blue-500/10' : 'bg-slate-400/10' + }); + } + } + + // --- New Customer Insight --- + if (metrics.customers.previous > 0) { + const custDiff = metrics.customers.current - metrics.customers.previous; + const custPercent = (custDiff / metrics.customers.previous) * 100; + + if (custPercent > 5) { + rawInsights.push({ + score: custPercent * 1.2, + type: 'positive', + message: `New customer acquisition is up ${custPercent.toFixed(1)}% vs ${timeframeName}.`, + icon: Users, + color: 'text-emerald-500', + bg: 'bg-emerald-500/10' + }); + } + } + }); + + // Sort by importance (score) and pick top ones + rawInsights.sort((a, b) => b.score - a.score); + + // Add up to 3 most interesting comparison insights + rawInsights.slice(0, 3).forEach(ri => { + insights.push({ + type: ri.type, + message: ri.message, + icon: ri.icon, + color: ri.color, + bg: ri.bg + }); + }); + } + + // 3. Fallback / AI Trends if we have space + if (insights.length < 3 && data.predictions?.features?.trends) { + const { growthRate } = data.predictions.features.trends; + if (growthRate > 0.05) { + insights.push({ + type: 'positive', + message: `AI confirms a steady growth trend of ${(growthRate * 100).toFixed(1)}% in the current market.`, + icon: Zap, + color: 'text-purple-500', + bg: 'bg-purple-500/10' + }); + } + } + + // 4. Customer Loyalty Insight (from growth data) + if (insights.length < 4 && growth?.customers?.segments) { + const segments = growth.customers.segments; + const totalActive = segments.new + segments.returning + segments.loyal + segments.vip; + if (totalActive > 0) { + const returningRate = (segments.returning + segments.loyal + segments.vip) / totalActive; + if (returningRate > 0.4) { + insights.push({ + type: 'information', + message: `${(returningRate * 100).toFixed(0)}% of your audience consists of returning customers.`, + icon: Trophy, + color: 'text-amber-500', + bg: 'bg-amber-500/10' + }); + } + } + } + + // Ensure we have at least one message + if (insights.length === 0) { + insights.push({ + type: 'neutral', + message: 'Dashboard data calibrated. Marketplace performance is stable.', + icon: RefreshCw, + color: 'text-muted-foreground', + bg: 'bg-muted/10' + }); + } + + return insights.slice(0, 4); +}; + +interface ComparisonPeriod { + orders: { current: number; previous: number }; + revenue: { current: number; previous: number }; + customers: { current: number; previous: number }; +} + interface GrowthData { launchDate: string; generatedAt: string; @@ -213,6 +376,9 @@ interface AnalyticsData { total?: number; totalToday?: number; totalThisWeek?: number; + totalPreviousWeek?: number; + totalThisMonth?: number; + totalLastMonth?: number; pending?: number; completed?: number; dailyOrders?: { date: string; count: number }[]; @@ -221,6 +387,9 @@ interface AnalyticsData { total?: number; today?: number; thisWeek?: number; + previousWeek?: number; + thisMonth?: number; + lastMonth?: number; dailyRevenue?: { date: string; amount: number; @@ -246,6 +415,27 @@ interface AnalyticsData { total?: number; active?: number; }; + comparisons?: { + [key: string]: ComparisonPeriod; + }; + predictions?: { + predicted: number; + confidence: "high" | "medium" | "low"; + confidenceScore: number; + dailyPredictions: Array<{ date: string; predicted: number; confidence: string }>; + message?: string; + features?: { + trends: { + growthRate: number; + momentum: number; + volatility: number; + }; + seasonality: { + weekly: number[]; + monthly: number[]; + }; + }; + }; } export default function AdminAnalytics() { @@ -266,6 +456,8 @@ export default function AdminAnalytics() { const [growthData, setGrowthData] = useState(null); const [growthLoading, setGrowthLoading] = useState(false); + const insights = React.useMemo(() => getSmartInsights(analyticsData, growthData), [analyticsData, growthData]); + const [activeIndex, setActiveIndex] = useState(0); const onPieEnter = (_: any, index: number) => { @@ -865,6 +1057,30 @@ export default function AdminAnalytics() { /> + {/* AI Performance Insights */} + {insights.length > 0 && !loading && !refreshing && ( +
+ {insights.map((insight, index) => ( + + +
+ +
+
+

+ Insight + {insight.type === 'positive' && } +

+

+ {insight.message} +

+
+
+
+ ))} +
+ )} +