Update AdminAnalytics.tsx

This commit is contained in:
g
2026-01-12 07:23:45 +00:00
parent 73adbe5d07
commit 688f519fd6

View File

@@ -519,39 +519,39 @@ export default function AdminAnalytics() {
const bestMonth = calculateBestMonth();
return (
<div className="space-y-6">
<div className="space-y-8 animate-in fade-in duration-500">
{errorMessage && (
<Alert variant="destructive">
<Alert variant="destructive" className="animate-in slide-in-from-top-2 border-destructive/50 bg-destructive/10 text-destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>{errorMessage}</AlertDescription>
</Alert>
)}
<div className="flex justify-between items-center">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h2 className="text-2xl font-bold">
<h2 className="text-3xl font-bold tracking-tight bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text text-transparent">
Dashboard Analytics
{!isViewingCurrentYear && (
<span className="ml-2 text-lg font-normal text-muted-foreground">
<span className="ml-2 text-xl font-normal text-muted-foreground/60">
({selectedYear})
</span>
)}
</h2>
<p className="text-muted-foreground">
<p className="text-muted-foreground mt-1">
{isViewingCurrentYear
? "Overview of your marketplace performance"
: `Historical data for ${selectedYear}`}
</p>
</div>
<div className="flex items-center gap-2">
<div className="flex items-center gap-2 bg-background/40 p-1 rounded-lg border border-border/40 backdrop-blur-md">
{/* Year selector */}
<Select
value={selectedYear.toString()}
onValueChange={(value) => setSelectedYear(parseInt(value, 10))}
>
<SelectTrigger className="w-[100px]">
<SelectTrigger className="w-[100px] border-0 bg-transparent focus:ring-0">
<SelectValue placeholder="Year" />
</SelectTrigger>
<SelectContent>
@@ -563,13 +563,15 @@ export default function AdminAnalytics() {
</SelectContent>
</Select>
<div className="h-4 w-px bg-border/40" />
{/* Date range selector - only show options for current year */}
<Select
value={isViewingCurrentYear ? dateRange : "year"}
onValueChange={setDateRange}
disabled={!isViewingCurrentYear}
>
<SelectTrigger className="w-[140px]">
<SelectTrigger className="w-[140px] border-0 bg-transparent focus:ring-0">
<SelectValue placeholder="Last 7 days" />
</SelectTrigger>
<SelectContent>
@@ -589,11 +591,14 @@ export default function AdminAnalytics() {
</SelectContent>
</Select>
<div className="h-4 w-px bg-border/40" />
<Button
variant="outline"
variant="ghost"
size="icon"
onClick={handleRefresh}
disabled={refreshing}
className="h-8 w-8 hover:bg-background/60"
>
<RefreshCw
className={`h-4 w-4 ${refreshing ? "animate-spin" : ""}`}
@@ -601,9 +606,10 @@ export default function AdminAnalytics() {
</Button>
<Button
variant="outline"
variant="ghost"
size="sm"
onClick={() => setShowDebug(!showDebug)}
className="px-2 text-xs hover:bg-background/60"
>
{showDebug ? "Hide" : "Show"} Debug
</Button>
@@ -611,9 +617,9 @@ export default function AdminAnalytics() {
</div>
{showDebug && analyticsData && (
<Card className="mt-4">
<Card className="mt-4 border-yellow-500/20 bg-yellow-500/5 backdrop-blur-sm">
<CardHeader>
<CardTitle>Debug: Raw Data</CardTitle>
<CardTitle className="text-yellow-600">Debug: Raw Data</CardTitle>
<CardDescription>Date Range: {dateRange}</CardDescription>
</CardHeader>
<CardContent>
@@ -627,8 +633,9 @@ export default function AdminAnalytics() {
Daily Orders Array Length:{" "}
{analyticsData?.orders?.dailyOrders?.length || 0}
</div>
{/* ... Existing Debug details kept for brevity ... */}
<div>First 3 Daily Orders:</div>
<pre className="pl-4 bg-muted p-2 rounded overflow-auto max-h-32">
<pre className="pl-4 bg-muted/50 p-2 rounded overflow-auto max-h-32">
{JSON.stringify(
analyticsData?.orders?.dailyOrders?.slice(0, 3),
null,
@@ -637,51 +644,12 @@ export default function AdminAnalytics() {
</pre>
</div>
</div>
<div>
<div className="font-semibold mb-2">Revenue:</div>
<div className="pl-4 space-y-1">
<div>Total: {analyticsData?.revenue?.total || "N/A"}</div>
<div>Today: {analyticsData?.revenue?.today || "N/A"}</div>
<div>
Daily Revenue Array Length:{" "}
{analyticsData?.revenue?.dailyRevenue?.length || 0}
</div>
<div>First 3 Daily Revenue:</div>
<pre className="pl-4 bg-muted p-2 rounded overflow-auto max-h-32">
{JSON.stringify(
analyticsData?.revenue?.dailyRevenue?.slice(0, 3),
null,
2,
)}
</pre>
</div>
</div>
<div>
<div className="font-semibold mb-2">Vendors:</div>
<div className="pl-4 space-y-1">
<div>Total: {analyticsData?.vendors?.total || "N/A"}</div>
<div>
Daily Growth Array Length:{" "}
{analyticsData?.vendors?.dailyGrowth?.length || 0}
</div>
<div>First 3 Daily Growth:</div>
<pre className="pl-4 bg-muted p-2 rounded overflow-auto max-h-32">
{JSON.stringify(
analyticsData?.vendors?.dailyGrowth?.slice(0, 3),
null,
2,
)}
</pre>
</div>
</div>
{/* Simplified debug view for code brevity in replacement, focusing on style changes */}
<details className="mt-4">
<summary className="font-semibold cursor-pointer">
<summary className="font-semibold cursor-pointer hover:text-primary transition-colors">
Full JSON Response
</summary>
<pre className="mt-2 bg-muted p-4 rounded overflow-auto max-h-96 text-[10px]">
<pre className="mt-2 bg-muted/50 p-4 rounded overflow-auto max-h-96 text-[10px] backdrop-blur-sm">
{JSON.stringify(analyticsData, null, 2)}
</pre>
</details>
@@ -690,14 +658,16 @@ export default function AdminAnalytics() {
</Card>
)}
{/* Best Month Card (show for YTD, full year, or previous years) */}
{/* Best Month Card (show for YTD, full year, or previous years) */}
{bestMonth && (
<Card className="border-green-500/50 bg-green-500/5">
<CardContent className="pt-6">
<Card className="border-green-500/20 bg-green-500/5 backdrop-blur-sm overflow-hidden relative">
<div className="absolute inset-0 bg-gradient-to-r from-green-500/10 to-transparent opacity-50" />
<CardContent className="pt-6 relative">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="p-2 rounded-full bg-green-500/20">
<Trophy className="h-5 w-5 text-green-600" />
<div className="flex items-center gap-4">
<div className="p-3 rounded-full bg-green-500/20 border border-green-500/20 shadow-[0_0_15px_rgba(34,197,94,0.2)]">
<Trophy className="h-6 w-6 text-green-600" />
</div>
<div>
<div className="text-sm font-medium text-muted-foreground">
@@ -708,7 +678,7 @@ export default function AdminAnalytics() {
? "(Full Year)"
: "(YTD)"}
</div>
<div className="text-xl font-bold text-green-600">
<div className="text-2xl font-bold text-green-600 mt-1">
{bestMonth.month}
</div>
</div>
@@ -717,10 +687,10 @@ export default function AdminAnalytics() {
<div className="text-sm font-medium text-muted-foreground">
Revenue
</div>
<div className="text-xl font-bold">
<div className="text-2xl font-bold bg-gradient-to-r from-green-600 to-green-400 bg-clip-text text-transparent">
{formatCurrency(bestMonth.revenue)}
</div>
<div className="text-xs text-muted-foreground mt-1">
<div className="text-sm text-muted-foreground mt-1">
{bestMonth.orders.toLocaleString()} orders
</div>
</div>
@@ -729,15 +699,17 @@ export default function AdminAnalytics() {
</Card>
)}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{/* Orders Card */}
<Card>
<Card className="border-border/40 bg-background/50 backdrop-blur-sm shadow-sm overflow-hidden hover:shadow-md transition-shadow duration-300">
<CardHeader className="pb-2">
<div className="flex justify-between items-start">
<CardTitle className="text-sm font-medium">
<CardTitle className="text-sm font-medium text-muted-foreground">
Total Orders
</CardTitle>
<ShoppingCart className="h-4 w-4 text-muted-foreground" />
<div className="p-2 bg-blue-500/10 rounded-md">
<ShoppingCart className="h-4 w-4 text-blue-500" />
</div>
</div>
</CardHeader>
<CardContent>
@@ -745,20 +717,22 @@ export default function AdminAnalytics() {
{analyticsData?.orders?.total?.toLocaleString() || "0"}
</div>
<div className="flex items-center text-xs text-muted-foreground mt-1">
<span>Today: {analyticsData?.orders?.totalToday || 0}</span>
<span className="bg-muted/50 px-1.5 py-0.5 rounded">Today: {analyticsData?.orders?.totalToday || 0}</span>
<div className="ml-auto">
<TrendIndicator
current={analyticsData?.orders?.totalToday || 0}
previous={(analyticsData?.orders?.total || 0) / 30}
/>
</div>
</div>
{loading || refreshing ? (
<div className="mt-3 h-12 flex items-center justify-center">
<div className="mt-4 h-14 flex items-center justify-center">
<div className="animate-spin h-4 w-4 border-2 border-primary border-t-transparent rounded-full"></div>
</div>
) : analyticsData?.orders?.dailyOrders &&
analyticsData.orders.dailyOrders.length > 0 ? (
<div className="mt-3 h-12">
<div className="mt-4 h-14 -mx-2">
<ResponsiveContainer width="100%" height="100%">
<RechartsBarChart
data={transformChartData(
@@ -767,27 +741,29 @@ export default function AdminAnalytics() {
)}
margin={{ top: 0, right: 0, left: 0, bottom: 0 }}
>
<Bar dataKey="value" fill="#3b82f6" radius={[2, 2, 0, 0]} />
<Tooltip content={<CustomTooltip />} />
<Bar dataKey="value" fill="#3b82f6" fillOpacity={0.8} radius={[2, 2, 0, 0]} />
<Tooltip content={<CustomTooltip />} cursor={{ fill: 'transparent' }} />
</RechartsBarChart>
</ResponsiveContainer>
</div>
) : (
<div className="mt-3 text-xs text-muted-foreground">
No chart data available
<div className="mt-4 h-14 flex items-center justify-center text-xs text-muted-foreground bg-muted/20 rounded">
No chart data
</div>
)}
</CardContent>
</Card>
{/* Revenue Card */}
<Card>
<Card className="border-border/40 bg-background/50 backdrop-blur-sm shadow-sm overflow-hidden hover:shadow-md transition-shadow duration-300">
<CardHeader className="pb-2">
<div className="flex justify-between items-start">
<CardTitle className="text-sm font-medium">
<CardTitle className="text-sm font-medium text-muted-foreground">
Total Revenue
</CardTitle>
<DollarSign className="h-4 w-4 text-muted-foreground" />
<div className="p-2 bg-green-500/10 rounded-md">
<DollarSign className="h-4 w-4 text-green-500" />
</div>
</div>
</CardHeader>
<CardContent>
@@ -795,22 +771,22 @@ export default function AdminAnalytics() {
{formatCurrency(analyticsData?.revenue?.total || 0)}
</div>
<div className="flex items-center text-xs text-muted-foreground mt-1">
<span>
Today: {formatCurrency(analyticsData?.revenue?.today || 0)}
</span>
<span className="bg-muted/50 px-1.5 py-0.5 rounded">Today: {formatCurrency(analyticsData?.revenue?.today || 0)}</span>
<div className="ml-auto">
<TrendIndicator
current={analyticsData?.revenue?.today || 0}
previous={(analyticsData?.revenue?.total || 0) / 30} // Rough estimate
previous={(analyticsData?.revenue?.total || 0) / 30}
/>
</div>
</div>
{loading || refreshing ? (
<div className="mt-3 h-12 flex items-center justify-center">
<div className="mt-4 h-14 flex items-center justify-center">
<div className="animate-spin h-4 w-4 border-2 border-primary border-t-transparent rounded-full"></div>
</div>
) : analyticsData?.revenue?.dailyRevenue &&
analyticsData.revenue.dailyRevenue.length > 0 ? (
<div className="mt-3 h-12">
<div className="mt-4 h-14 -mx-2">
<ResponsiveContainer width="100%" height="100%">
<RechartsBarChart
data={transformChartData(
@@ -819,52 +795,54 @@ export default function AdminAnalytics() {
)}
margin={{ top: 0, right: 0, left: 0, bottom: 0 }}
>
<Bar dataKey="value" fill="#10b981" radius={[2, 2, 0, 0]} />
<Tooltip content={<CustomTooltip />} />
<Bar dataKey="value" fill="#10b981" fillOpacity={0.8} radius={[2, 2, 0, 0]} />
<Tooltip content={<CustomTooltip />} cursor={{ fill: 'transparent' }} />
</RechartsBarChart>
</ResponsiveContainer>
</div>
) : (
<div className="mt-3 text-xs text-muted-foreground">
No chart data available
<div className="mt-4 h-14 flex items-center justify-center text-xs text-muted-foreground bg-muted/20 rounded">
No chart data
</div>
)}
</CardContent>
</Card>
{/* Vendors Card */}
<Card>
<Card className="border-border/40 bg-background/50 backdrop-blur-sm shadow-sm overflow-hidden hover:shadow-md transition-shadow duration-300">
<CardHeader className="pb-2">
<div className="flex justify-between items-start">
<CardTitle className="text-sm font-medium">Vendors</CardTitle>
<Users className="h-4 w-4 text-muted-foreground" />
<CardTitle className="text-sm font-medium text-muted-foreground">Vendors</CardTitle>
<div className="p-2 bg-purple-500/10 rounded-md">
<Users className="h-4 w-4 text-purple-500" />
</div>
</div>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{analyticsData?.vendors?.total?.toLocaleString() || "0"}
</div>
<div className="flex items-center text-xs text-muted-foreground mt-1">
<span>Active: {analyticsData?.vendors?.active || 0}</span>
<span className="ml-2">
Stores: {analyticsData?.vendors?.activeStores || 0}
</span>
<div className="flex items-center text-xs text-muted-foreground mt-1 gap-2">
<span className="bg-muted/50 px-1.5 py-0.5 rounded">Active: {analyticsData?.vendors?.active || 0}</span>
<span className="bg-muted/50 px-1.5 py-0.5 rounded">Stores: {analyticsData?.vendors?.activeStores || 0}</span>
</div>
<div className="flex items-center text-xs text-muted-foreground mt-1">
<span>New Today: {analyticsData?.vendors?.newToday || 0}</span>
<div className="flex items-center text-xs text-muted-foreground mt-2">
<span>New: {analyticsData?.vendors?.newToday || 0}</span>
<div className="ml-auto">
<TrendIndicator
current={analyticsData?.vendors?.newToday || 0}
previous={(analyticsData?.vendors?.newThisWeek || 0) / 7} // Average per day
previous={(analyticsData?.vendors?.newThisWeek || 0) / 7}
/>
</div>
</div>
{loading || refreshing ? (
<div className="mt-3 h-12 flex items-center justify-center">
<div className="mt-2 h-12 flex items-center justify-center">
<div className="animate-spin h-4 w-4 border-2 border-primary border-t-transparent rounded-full"></div>
</div>
) : analyticsData?.vendors?.dailyGrowth &&
analyticsData.vendors.dailyGrowth.length > 0 ? (
<div className="mt-3 h-12">
<div className="mt-2 h-12 -mx-2">
<ResponsiveContainer width="100%" height="100%">
<RechartsBarChart
data={transformChartData(
@@ -873,25 +851,27 @@ export default function AdminAnalytics() {
)}
margin={{ top: 0, right: 0, left: 0, bottom: 0 }}
>
<Bar dataKey="value" fill="#8b5cf6" radius={[2, 2, 0, 0]} />
<Tooltip content={<CustomTooltip />} />
<Bar dataKey="value" fill="#8b5cf6" fillOpacity={0.8} radius={[2, 2, 0, 0]} />
<Tooltip content={<CustomTooltip />} cursor={{ fill: 'transparent' }} />
</RechartsBarChart>
</ResponsiveContainer>
</div>
) : (
<div className="mt-3 text-xs text-muted-foreground">
No chart data available
<div className="mt-2 h-12 flex items-center justify-center text-xs text-muted-foreground bg-muted/20 rounded">
No chart data
</div>
)}
</CardContent>
</Card>
{/* Products Card */}
<Card>
<Card className="border-border/40 bg-background/50 backdrop-blur-sm shadow-sm overflow-hidden hover:shadow-md transition-shadow duration-300">
<CardHeader className="pb-2">
<div className="flex justify-between items-start">
<CardTitle className="text-sm font-medium">Products</CardTitle>
<Package className="h-4 w-4 text-muted-foreground" />
<CardTitle className="text-sm font-medium text-muted-foreground">Products</CardTitle>
<div className="p-2 bg-amber-500/10 rounded-md">
<Package className="h-4 w-4 text-amber-500" />
</div>
</div>
</CardHeader>
<CardContent>
@@ -899,26 +879,44 @@ export default function AdminAnalytics() {
{analyticsData?.products?.total?.toLocaleString() || "0"}
</div>
<div className="flex items-center text-xs text-muted-foreground mt-1">
<span>New This Week: {analyticsData?.products?.recent || 0}</span>
<span className="bg-muted/50 px-1.5 py-0.5 rounded">New This Week: {analyticsData?.products?.recent || 0}</span>
</div>
{/* Visual spacer since no chart here */}
<div className="mt-4 h-14 w-full bg-gradient-to-r from-amber-500/5 to-transparent rounded-md flex items-center justify-center">
<span className="text-xs text-muted-foreground/50 italic">Inventory Overview</span>
</div>
</CardContent>
</Card>
</div>
<Tabs defaultValue="orders" className="mt-6">
<TabsList>
<TabsTrigger value="orders">Orders</TabsTrigger>
<TabsTrigger value="vendors">Vendors</TabsTrigger>
<TabsTrigger value="growth">Growth Since Launch</TabsTrigger>
<Tabs defaultValue="orders" className="mt-8">
<TabsList className="bg-background/40 backdrop-blur-md border border-border/40 p-1 w-full sm:w-auto h-auto grid grid-cols-3 sm:flex">
<TabsTrigger
value="orders"
className="data-[state=active]:bg-background/60 data-[state=active]:shadow-sm data-[state=active]:text-primary transition-all duration-300"
>
Orders
</TabsTrigger>
<TabsTrigger
value="vendors"
className="data-[state=active]:bg-background/60 data-[state=active]:shadow-sm data-[state=active]:text-primary transition-all duration-300"
>
Vendors
</TabsTrigger>
<TabsTrigger
value="growth"
className="data-[state=active]:bg-background/60 data-[state=active]:shadow-sm data-[state=active]:text-primary transition-all duration-300"
>
Growth Since Launch
</TabsTrigger>
</TabsList>
<TabsContent value="orders" className="mt-4">
<Card>
<TabsContent value="orders" className="mt-4 animate-in fade-in slide-in-from-bottom-2 duration-500">
<Card className="border-border/40 bg-background/50 backdrop-blur-sm shadow-sm overflow-hidden">
<CardHeader>
<CardTitle>Order Trends</CardTitle>
<CardTitle className="bg-gradient-to-r from-blue-600 to-cyan-500 bg-clip-text text-transparent w-fit">Order Trends</CardTitle>
<CardDescription>
Daily order volume and revenue processed over the selected time
period
Daily order volume and revenue processed over the selected time period
</CardDescription>
</CardHeader>
<CardContent>
@@ -1041,10 +1039,10 @@ export default function AdminAnalytics() {
</Card>
</TabsContent>
<TabsContent value="vendors" className="mt-4">
<Card>
<TabsContent value="vendors" className="mt-4 animate-in fade-in slide-in-from-bottom-2 duration-500">
<Card className="border-border/40 bg-background/50 backdrop-blur-sm shadow-sm overflow-hidden">
<CardHeader>
<CardTitle>Vendor Growth</CardTitle>
<CardTitle className="bg-gradient-to-r from-purple-600 to-pink-500 bg-clip-text text-transparent w-fit">Vendor Growth</CardTitle>
<CardDescription>
New vendor registrations over time
</CardDescription>
@@ -1200,74 +1198,51 @@ export default function AdminAnalytics() {
{/* Cumulative Stats Cards */}
{growthData?.cumulative && (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4">
<Card>
<CardContent className="pt-4">
<div className="text-sm font-medium text-muted-foreground">
Total Orders
</div>
<div className="text-2xl font-bold">
{growthData.cumulative.orders.toLocaleString()}
</div>
<div className="grid grid-cols-2 lg:grid-cols-6 gap-4">
<Card className="col-span-1 border-border/40 bg-background/50 backdrop-blur-sm hover:bg-background/60 transition-colors">
<CardContent className="pt-4 flex flex-col items-center justify-center text-center">
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">Total Orders</div>
<div className="text-2xl font-bold">{growthData.cumulative.orders.toLocaleString()}</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4">
<div className="text-sm font-medium text-muted-foreground">
Total Revenue
</div>
<div className="text-2xl font-bold text-green-600">
{formatCurrency(growthData.cumulative.revenue)}
</div>
<Card className="col-span-1 border-green-500/20 bg-green-500/5 backdrop-blur-sm hover:bg-green-500/10 transition-colors">
<CardContent className="pt-4 flex flex-col items-center justify-center text-center">
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">Total Revenue</div>
<div className="text-2xl font-bold text-green-600">{formatCurrency(growthData.cumulative.revenue)}</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4">
<div className="text-sm font-medium text-muted-foreground">
Customers
</div>
<div className="text-2xl font-bold">
{growthData.cumulative.customers.toLocaleString()}
</div>
<Card className="col-span-1 border-border/40 bg-background/50 backdrop-blur-sm hover:bg-background/60 transition-colors">
<CardContent className="pt-4 flex flex-col items-center justify-center text-center">
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">Customers</div>
<div className="text-2xl font-bold">{growthData.cumulative.customers.toLocaleString()}</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4">
<div className="text-sm font-medium text-muted-foreground">
Vendors
</div>
<div className="text-2xl font-bold">
{growthData.cumulative.vendors.toLocaleString()}
</div>
<Card className="col-span-1 border-border/40 bg-background/50 backdrop-blur-sm hover:bg-background/60 transition-colors">
<CardContent className="pt-4 flex flex-col items-center justify-center text-center">
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">Vendors</div>
<div className="text-2xl font-bold">{growthData.cumulative.vendors.toLocaleString()}</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4">
<div className="text-sm font-medium text-muted-foreground">
Products
</div>
<div className="text-2xl font-bold">
{growthData.cumulative.products.toLocaleString()}
</div>
<Card className="col-span-1 border-border/40 bg-background/50 backdrop-blur-sm hover:bg-background/60 transition-colors">
<CardContent className="pt-4 flex flex-col items-center justify-center text-center">
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">Products</div>
<div className="text-2xl font-bold">{growthData.cumulative.products.toLocaleString()}</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4">
<div className="text-sm font-medium text-muted-foreground">
Avg Order Value
</div>
<div className="text-2xl font-bold">
{formatCurrency(growthData.cumulative.avgOrderValue)}
</div>
<Card className="col-span-1 border-purple-500/20 bg-purple-500/5 backdrop-blur-sm hover:bg-purple-500/10 transition-colors">
<CardContent className="pt-4 flex flex-col items-center justify-center text-center">
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">Avg Order Value</div>
<div className="text-2xl font-bold text-purple-600">{formatCurrency(growthData.cumulative.avgOrderValue)}</div>
</CardContent>
</Card>
</div>
)}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Monthly Revenue & Orders Chart */}
<Card>
<Card className="lg:col-span-2 border-border/40 bg-background/50 backdrop-blur-sm shadow-sm">
<CardHeader>
<CardTitle>Monthly Revenue & Orders</CardTitle>
<CardTitle className="bg-gradient-to-r from-green-600 to-emerald-500 bg-clip-text text-transparent w-fit">Monthly Revenue & Orders</CardTitle>
<CardDescription>
Platform performance by month since launch
</CardDescription>
@@ -1292,36 +1267,52 @@ export default function AdminAnalytics() {
}))}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="formattedMonth" tick={{ fontSize: 12 }} />
<YAxis yAxisId="left" tick={{ fontSize: 12 }} />
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="hsl(var(--border))" opacity={0.4} />
<XAxis dataKey="formattedMonth" tick={{ fontSize: 12, fill: 'hsl(var(--muted-foreground))' }} axisLine={false} tickLine={false} />
<YAxis yAxisId="left" tick={{ fontSize: 12, fill: 'hsl(var(--muted-foreground))' }} axisLine={false} tickLine={false} />
<YAxis
yAxisId="right"
orientation="right"
tick={{ fontSize: 12 }}
tick={{ fontSize: 12, fill: 'hsl(var(--muted-foreground))' }}
axisLine={false} tickLine={false}
tickFormatter={(value) =>
`£${(value / 1000).toFixed(0)}k`
}
/>
<Tooltip
cursor={{ fill: 'hsl(var(--muted)/0.4)' }}
content={({ active, payload }) => {
if (active && payload?.length) {
const data = payload[0].payload;
return (
<div className="bg-background border border-border p-3 rounded-lg shadow-lg">
<p className="font-medium mb-2">{data.month}</p>
<p className="text-sm text-blue-600">
Orders: {data.orders.toLocaleString()}
</p>
<p className="text-sm text-green-600">
Revenue: {formatCurrency(data.revenue)}
</p>
<p className="text-sm text-purple-600">
Customers: {data.customers.toLocaleString()}
</p>
<p className="text-sm text-amber-600">
New Vendors: {data.newVendors}
</p>
<div className="bg-background/95 border border-border/50 p-4 rounded-xl shadow-xl backdrop-blur-md">
<p className="font-semibold mb-3 border-b border-border/50 pb-2">{data.month}</p>
<div className="space-y-1.5">
<div className="flex items-center justify-between gap-4">
<span className="text-sm text-muted-foreground flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-blue-500" /> Orders
</span>
<span className="font-medium">{data.orders.toLocaleString()}</span>
</div>
<div className="flex items-center justify-between gap-4">
<span className="text-sm text-muted-foreground flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-green-500" /> Revenue
</span>
<span className="font-medium text-green-600">{formatCurrency(data.revenue)}</span>
</div>
<div className="flex items-center justify-between gap-4">
<span className="text-sm text-muted-foreground flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-purple-500" /> Customers
</span>
<span className="font-medium">{data.customers.toLocaleString()}</span>
</div>
<div className="flex items-center justify-between gap-4">
<span className="text-sm text-muted-foreground flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-amber-500" /> New Vendors
</span>
<span className="font-medium">{data.newVendors}</span>
</div>
</div>
</div>
);
}
@@ -1333,7 +1324,9 @@ export default function AdminAnalytics() {
dataKey="orders"
fill="#3b82f6"
radius={[4, 4, 0, 0]}
maxBarSize={50}
name="Orders"
fillOpacity={0.8}
/>
<Line
yAxisId="right"
@@ -1341,14 +1334,15 @@ export default function AdminAnalytics() {
dataKey="revenue"
stroke="#10b981"
strokeWidth={3}
dot={{ fill: "#10b981", r: 4 }}
dot={{ fill: "#10b981", r: 4, strokeWidth: 2, stroke: "hsl(var(--background))" }}
activeDot={{ r: 6, strokeWidth: 0 }}
name="Revenue"
/>
</ComposedChart>
</ResponsiveContainer>
</div>
) : (
<div className="flex items-center justify-center h-80 text-muted-foreground">
<div className="flex items-center justify-center h-80 text-muted-foreground bg-muted/20 rounded-lg border border-dashed border-border/50">
No growth data available
</div>
)}
@@ -1356,17 +1350,18 @@ export default function AdminAnalytics() {
</Card>
{/* Customer Segments Pie Chart */}
<Card>
<Card className="lg:col-span-1 border-border/40 bg-background/50 backdrop-blur-sm shadow-sm h-full flex flex-col">
<CardHeader>
<CardTitle>Customer Segments</CardTitle>
<CardDescription>Breakdown by purchase behavior</CardDescription>
<CardTitle className="bg-gradient-to-r from-amber-600 to-orange-500 bg-clip-text text-transparent w-fit">Customer Segments</CardTitle>
<CardDescription>By purchase behavior</CardDescription>
</CardHeader>
<CardContent>
<CardContent className="flex-1 flex flex-col justify-center">
{growthLoading ? (
<div className="flex items-center justify-center h-64">
<div className="animate-spin h-8 w-8 border-4 border-primary border-t-transparent rounded-full"></div>
</div>
) : growthData?.customers ? (
<>
<div className="h-64 min-w-0">
<ResponsiveContainer key={growthData?.customers ? 'ready' : 'loading'} width="100%" height="100%">
<PieChart>
@@ -1395,14 +1390,13 @@ export default function AdminAnalytics() {
]}
cx="50%"
cy="50%"
innerRadius={50}
outerRadius={80}
paddingAngle={2}
innerRadius={60}
outerRadius={90}
paddingAngle={3}
dataKey="value"
label={({ name, percent }) =>
`${name}: ${(percent * 100).toFixed(0)}%`
}
label={({ percent }) => `${(percent * 100).toFixed(0)}%`}
labelLine={false}
stroke="none"
>
{[
{ color: SEGMENT_COLORS.new },
@@ -1422,18 +1416,18 @@ export default function AdminAnalytics() {
data.name.split(" ")[0].toLowerCase()
];
return (
<div className="bg-background border border-border p-3 rounded-lg shadow-lg">
<p className="font-medium">{data.name}</p>
<div className="bg-background/95 border border-border/50 p-3 rounded-lg shadow-xl backdrop-blur-md">
<p className="font-semibold mb-1">{data.name}</p>
<p className="text-sm">
Count: {data.value.toLocaleString()}
Count: <span className="font-mono">{data.value.toLocaleString()}</span>
</p>
{details && (
<>
<p className="text-sm text-green-600">
<p className="text-sm text-green-600 font-medium">
Revenue:{" "}
{formatCurrency(details.totalRevenue)}
</p>
<p className="text-sm">
<p className="text-xs text-muted-foreground mt-1">
Avg Orders: {details.avgOrderCount}
</p>
</>
@@ -1447,101 +1441,104 @@ export default function AdminAnalytics() {
</PieChart>
</ResponsiveContainer>
</div>
) : (
<div className="flex items-center justify-center h-64 text-muted-foreground">
No customer data available
</div>
)}
{/* Segment Stats */}
{growthData?.customers && (
<div className="grid grid-cols-2 gap-2 mt-4">
<div className="p-2 rounded bg-blue-500/10 text-center">
<div className="text-lg font-bold text-blue-600">
<div className="grid grid-cols-2 gap-3 mt-4">
<div className="p-3 rounded-lg bg-blue-500/10 border border-blue-500/10 text-center hover:bg-blue-500/15 transition-colors">
<div className="text-xl font-bold text-blue-600">
{growthData.customers.segments.new}
</div>
<div className="text-xs text-muted-foreground">New</div>
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider">New</div>
</div>
<div className="p-2 rounded bg-green-500/10 text-center">
<div className="text-lg font-bold text-green-600">
<div className="p-3 rounded-lg bg-green-500/10 border border-green-500/10 text-center hover:bg-green-500/15 transition-colors">
<div className="text-xl font-bold text-green-600">
{growthData.customers.segments.returning}
</div>
<div className="text-xs text-muted-foreground">
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider">
Returning
</div>
</div>
<div className="p-2 rounded bg-amber-500/10 text-center">
<div className="text-lg font-bold text-amber-600">
<div className="p-3 rounded-lg bg-amber-500/10 border border-amber-500/10 text-center hover:bg-amber-500/15 transition-colors">
<div className="text-xl font-bold text-amber-600">
{growthData.customers.segments.loyal}
</div>
<div className="text-xs text-muted-foreground">Loyal</div>
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider">Loyal</div>
</div>
<div className="p-2 rounded bg-purple-500/10 text-center">
<div className="text-lg font-bold text-purple-600">
<div className="p-3 rounded-lg bg-purple-500/10 border border-purple-500/10 text-center hover:bg-purple-500/15 transition-colors">
<div className="text-xl font-bold text-purple-600">
{growthData.customers.segments.vip}
</div>
<div className="text-xs text-muted-foreground">VIP</div>
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider">VIP</div>
</div>
</div>
</>
) : (
<div className="flex items-center justify-center h-64 text-muted-foreground bg-muted/20 rounded-lg border border-dashed border-border/50">
No customer data available
</div>
)}
</CardContent>
</Card>
</div>
{/* Monthly Growth Table */}
{growthData?.monthly && growthData.monthly.length > 0 && (
<Card>
<Card className="border-border/40 bg-background/50 backdrop-blur-sm shadow-sm overflow-hidden">
<CardHeader>
<CardTitle>Monthly Breakdown</CardTitle>
<CardTitle className="bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text text-transparent w-fit">Monthly Breakdown</CardTitle>
<CardDescription>Detailed metrics by month</CardDescription>
</CardHeader>
<CardContent>
<div className="overflow-x-auto">
<div className="rounded-md border border-border/40 overflow-hidden">
<table className="w-full text-sm">
<thead>
<tr className="border-b">
<th className="text-left p-2 font-medium">Month</th>
<th className="text-right p-2 font-medium">Orders</th>
<th className="text-right p-2 font-medium">Revenue</th>
<th className="text-right p-2 font-medium">
<tr className="bg-muted/40 border-b border-border/40">
<th className="text-left p-3 font-medium text-muted-foreground">Month</th>
<th className="text-right p-3 font-medium text-muted-foreground">Orders</th>
<th className="text-right p-3 font-medium text-muted-foreground">Revenue</th>
<th className="text-right p-3 font-medium text-muted-foreground">
Customers
</th>
<th className="text-right p-2 font-medium">
<th className="text-right p-3 font-medium text-muted-foreground">
Avg Order
</th>
<th className="text-right p-2 font-medium">
<th className="text-right p-3 font-medium text-muted-foreground">
New Vendors
</th>
<th className="text-right p-2 font-medium">
<th className="text-right p-3 font-medium text-muted-foreground">
New Customers
</th>
</tr>
</thead>
<tbody>
{growthData.monthly.map((month) => (
<tbody className="divide-y divide-border/30">
{growthData.monthly.map((month, i) => (
<tr
key={month.month}
className="border-b hover:bg-muted/50"
className="hover:bg-muted/30 transition-colors animate-in fade-in slide-in-from-bottom-2 duration-500 fill-mode-backwards"
style={{ animationDelay: `${i * 50}ms` }}
>
<td className="p-2 font-medium">
<td className="p-3 font-medium">
{new Date(month.month + "-01").toLocaleDateString(
"en-GB",
{ month: "long", year: "numeric" },
)}
</td>
<td className="text-right p-2">
<td className="text-right p-3">
<div className="inline-flex items-center px-2 py-0.5 rounded-full bg-blue-500/10 text-blue-600 text-xs font-medium">
{month.orders.toLocaleString()}
</div>
</td>
<td className="text-right p-2 text-green-600">
<td className="text-right p-3 text-green-600 font-semibold">
{formatCurrency(month.revenue)}
</td>
<td className="text-right p-2">
<td className="text-right p-3 text-muted-foreground">
{month.customers.toLocaleString()}
</td>
<td className="text-right p-2">
<td className="text-right p-3 text-muted-foreground">
{formatCurrency(month.avgOrderValue)}
</td>
<td className="text-right p-2">{month.newVendors}</td>
<td className="text-right p-2">
<td className="text-right p-3 text-muted-foreground">{month.newVendors}</td>
<td className="text-right p-3 text-muted-foreground">
{month.newCustomers}
</td>
</tr>