"use client"; import { useState, useEffect } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Button } from "@/components/ui/button"; import { useToast } from "@/hooks/use-toast"; import { Users, ShoppingCart, DollarSign, Package, RefreshCw, Calendar, TrendingUp, } from "lucide-react"; import { getGrowthAnalyticsWithStore, type GrowthAnalytics, } from "@/lib/services/analytics-service"; import { formatGBP } from "@/utils/format"; import { ComposedChart, Bar, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell, BarChart, Legend, } from "recharts"; interface GrowthAnalyticsChartProps { hideNumbers?: boolean; } const SEGMENT_COLORS = { new: "#3b82f6", returning: "#10b981", loyal: "#f59e0b", vip: "#8b5cf6", }; const SEGMENT_LABELS = { new: "New (1 order)", returning: "Returning (2-3 orders)", loyal: "Loyal (4+ orders or £300+)", vip: "VIP (10+ orders or £1000+)", }; export default function GrowthAnalyticsChart({ hideNumbers = false, }: GrowthAnalyticsChartProps) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const { toast } = useToast(); const fetchData = async () => { try { setLoading(true); const response = await getGrowthAnalyticsWithStore(); setData(response); } catch (err) { console.error("Error fetching growth data:", err); toast({ title: "Error", description: "Failed to load growth analytics data.", variant: "destructive", }); } finally { setLoading(false); setRefreshing(false); } }; useEffect(() => { fetchData(); }, []); const handleRefresh = () => { setRefreshing(true); fetchData(); }; const formatCurrency = (value: number) => { if (hideNumbers) return "£***"; return new Intl.NumberFormat("en-GB", { style: "currency", currency: "GBP", maximumFractionDigits: 0, }).format(value); }; const formatNumber = (value: number) => { if (hideNumbers) return "***"; return value.toLocaleString(); }; const formatDate = (dateStr: string) => { const date = new Date(dateStr); return date.toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "numeric", }); }; const getDaysSinceLaunch = () => { if (!data?.launchDate) return 0; const launch = new Date(data.launchDate); const now = new Date(); return Math.floor( (now.getTime() - launch.getTime()) / (1000 * 60 * 60 * 24), ); }; if (loading && !data) { return (
); } if (!data) { return (
No growth data available. Complete your first sale to see analytics.
); } // Prepare chart data const recentDaily = data.daily.slice(-30); // Last 30 days for daily chart // Prepare segment pie chart data const segmentData = Object.entries(data.customers.segments) .filter(([_, value]) => value > 0) .map(([key, value]) => ({ name: key.charAt(0).toUpperCase() + key.slice(1), value, color: SEGMENT_COLORS[key as keyof typeof SEGMENT_COLORS], label: SEGMENT_LABELS[key as keyof typeof SEGMENT_LABELS], })); const CustomTooltip = ({ active, payload }: any) => { if (active && payload?.length) { const item = payload[0].payload; return (

{item.date || item.month}

Orders: {hideNumbers ? "***" : item.orders?.toLocaleString()}

Revenue: {hideNumbers ? "£***" : formatGBP(item.revenue)}

Customers: {hideNumbers ? "***" : item.customers}

{item.avgOrderValue && (

Avg Order: {hideNumbers ? "£***" : formatGBP(item.avgOrderValue)}

)}
); } return null; }; const MonthlyTooltip = ({ active, payload }: any) => { if (active && payload?.length) { const item = payload[0].payload; return (

{item.month}

Orders: {hideNumbers ? "***" : item.orders?.toLocaleString()}

Revenue: {hideNumbers ? "£***" : formatGBP(item.revenue)}

Customers: {hideNumbers ? "***" : item.customers}

New Customers: {hideNumbers ? "***" : item.newCustomers}

); } return null; }; return (
{/* Header */}

Growth Since First Sale

Started {formatDate(data.launchDate)} ({getDaysSinceLaunch()} days ago)

{/* Cumulative Summary Cards */}
Total Orders
{formatNumber(data.cumulative.orders)}
Total Revenue
{formatCurrency(data.cumulative.revenue)}
Total Customers
{formatNumber(data.cumulative.customers)}
Products
{formatNumber(data.cumulative.products)}
Avg Order
{formatCurrency(data.cumulative.avgOrderValue)}
{/* Tabbed Charts */} Daily (Last 30 Days) Monthly Growth Customer Segments {/* Daily Chart */} Daily Orders & Revenue Last 30 days of activity {loading || refreshing ? (
) : recentDaily.length > 0 ? (
{ const d = new Date(v); return `${d.getDate()}/${d.getMonth() + 1}`; }} /> (hideNumbers ? "***" : v)} /> hideNumbers ? "***" : `£${(v / 1000).toFixed(0)}k` } /> } />
) : (
No daily data available yet
)}
{/* Monthly Chart */} Monthly Growth Orders, revenue, and new customers by month {loading || refreshing ? (
) : data.monthly.length > 0 ? (
{ const [year, month] = v.split("-"); const date = new Date( parseInt(year), parseInt(month) - 1, ); return date.toLocaleDateString("en-GB", { month: "short", year: "2-digit", }); }} /> (hideNumbers ? "***" : v)} /> hideNumbers ? "***" : `£${(v / 1000).toFixed(0)}k` } /> } />
) : (
No monthly data available yet
)}
{/* Customer Segments */}
{/* Pie Chart */} Customer Segments Breakdown by purchase behavior {segmentData.length > 0 ? (
hideNumbers ? name : `${name} ${(percent * 100).toFixed(0)}%` } > {segmentData.map((entry, index) => ( ))} hideNumbers ? "***" : value } />
) : (
No customer data yet
)}
{/* Segment Details */} Segment Details Customer value by segment
{Object.entries(data.customers.segments).map( ([segment, count]) => { const details = data.customers.segmentDetails[segment] || {}; const percentage = data.customers.segmentPercentages[ segment as keyof typeof data.customers.segmentPercentages ] || 0; return (
{segment}
{SEGMENT_LABELS[ segment as keyof typeof SEGMENT_LABELS ] || segment}
{hideNumbers ? "***" : count} customers
{hideNumbers ? "***" : `${percentage}%`} |{" "} {hideNumbers ? "£***" : formatGBP(details.totalRevenue || 0)}{" "} revenue
); }, )}
{/* Summary Stats */}
{formatNumber(data.customers.total)}
Total Customers
{formatCurrency( data.cumulative.revenue / (data.customers.total || 1), )}
Avg Revenue/Customer
); }