From f61a7e027655a36ebaf0ad2d1b78596614d31049 Mon Sep 17 00:00:00 2001 From: NotII <46204250+NotII@users.noreply.github.com> Date: Sun, 29 Jun 2025 03:05:42 +0100 Subject: [PATCH] Metrics --- app/dashboard/analytics/page.tsx | 40 ++++ components/analytics/AnalyticsDashboard.tsx | 248 ++++++++++++++++++++ components/analytics/MetricsCard.tsx | 66 ++++++ 3 files changed, 354 insertions(+) create mode 100644 app/dashboard/analytics/page.tsx create mode 100644 components/analytics/AnalyticsDashboard.tsx create mode 100644 components/analytics/MetricsCard.tsx diff --git a/app/dashboard/analytics/page.tsx b/app/dashboard/analytics/page.tsx new file mode 100644 index 0000000..bb94346 --- /dev/null +++ b/app/dashboard/analytics/page.tsx @@ -0,0 +1,40 @@ +import AnalyticsDashboard from "@/components/analytics/AnalyticsDashboard"; +import { fetchServer } from '@/lib/api'; + +interface AnalyticsOverview { + orders: { + total: number; + completed: number; + pending: number; + completionRate: string; + }; + revenue: { + total: number; + monthly: number; + weekly: number; + averageOrderValue: number; + }; + products: { + total: number; + }; + customers: { + unique: number; + }; +} + +export default async function AnalyticsPage() { + const analyticsData = await fetchServer("/analytics/overview"); + + return ( +
+
+

Analytics Dashboard

+

+ Comprehensive insights into your store performance, sales trends, and customer behavior. +

+
+ + +
+ ); +} \ No newline at end of file diff --git a/components/analytics/AnalyticsDashboard.tsx b/components/analytics/AnalyticsDashboard.tsx new file mode 100644 index 0000000..a4968ec --- /dev/null +++ b/components/analytics/AnalyticsDashboard.tsx @@ -0,0 +1,248 @@ +"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 { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { + TrendingUp, + ShoppingCart, + Users, + Package, + DollarSign, + BarChart3, + PieChart, + Activity, + RefreshCw +} from "lucide-react"; +import { clientFetch } from "@/lib/api"; +import { useToast } from "@/hooks/use-toast"; +import RevenueChart from "./RevenueChart"; +import ProductPerformanceChart from "./ProductPerformanceChart"; +import CustomerInsightsChart from "./CustomerInsightsChart"; +import OrderAnalyticsChart from "./OrderAnalyticsChart"; +import MetricsCard from "./MetricsCard"; + +interface AnalyticsOverview { + orders: { + total: number; + completed: number; + pending: number; + completionRate: string; + }; + revenue: { + total: number; + monthly: number; + weekly: number; + averageOrderValue: number; + }; + products: { + total: number; + }; + customers: { + unique: number; + }; +} + +interface AnalyticsDashboardProps { + initialData: AnalyticsOverview; +} + +export default function AnalyticsDashboard({ initialData }: AnalyticsDashboardProps) { + const [data, setData] = useState(initialData); + const [isLoading, setIsLoading] = useState(false); + const [timeRange, setTimeRange] = useState('30'); + const { toast } = useToast(); + + const refreshData = async () => { + try { + setIsLoading(true); + const newData = await clientFetch("/analytics/overview"); + setData(newData); + toast({ + title: "Data refreshed", + description: "Analytics data has been updated successfully.", + }); + } catch (error) { + toast({ + title: "Error", + description: "Failed to refresh analytics data.", + variant: "destructive", + }); + } finally { + setIsLoading(false); + } + }; + + const metrics = [ + { + title: "Total Revenue", + value: `$${data.revenue.total.toLocaleString()}`, + description: "All-time revenue", + icon: DollarSign, + trend: data.revenue.monthly > 0 ? "up" : "neutral", + trendValue: `$${data.revenue.monthly.toLocaleString()} this month` + }, + { + title: "Total Orders", + value: data.orders.total.toLocaleString(), + description: "All-time orders", + icon: ShoppingCart, + trend: data.orders.completed > 0 ? "up" : "neutral", + trendValue: `${data.orders.completed} completed` + }, + { + title: "Unique Customers", + value: data.customers.unique.toLocaleString(), + description: "Total customers", + icon: Users, + trend: "neutral", + trendValue: "Lifetime customers" + }, + { + title: "Products", + value: data.products.total.toLocaleString(), + description: "Active products", + icon: Package, + trend: "neutral", + trendValue: "In your store" + } + ]; + + return ( +
+ {/* Header with refresh button */} +
+
+

Performance Overview

+

+ Key metrics and insights about your store performance +

+
+ +
+ + {/* Key Metrics Cards */} +
+ {metrics.map((metric) => ( + + ))} +
+ + {/* Completion Rate Card */} + + + + + Order Completion Rate + + + Percentage of orders that have been successfully completed + + + +
+
+ {data.orders.completionRate}% +
+
+
+
+
+
+ + {data.orders.completed} / {data.orders.total} + +
+ + + + {/* Analytics Tabs */} + + + + + Revenue + + + + Products + + + + Customers + + + + Orders + + + + +
+
+

Revenue Trends

+

+ Track your revenue performance over time +

+
+ +
+ +
+ + +
+

Product Performance

+

+ Analyze which products are performing best +

+
+ +
+ + +
+

Customer Insights

+

+ Understand your customer base and behavior +

+
+ +
+ + +
+

Order Analytics

+

+ Detailed analysis of order patterns and status distribution +

+
+ +
+
+
+ ); +} \ No newline at end of file diff --git a/components/analytics/MetricsCard.tsx b/components/analytics/MetricsCard.tsx new file mode 100644 index 0000000..a2c4f6b --- /dev/null +++ b/components/analytics/MetricsCard.tsx @@ -0,0 +1,66 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { TrendingUp, TrendingDown, Minus } from "lucide-react"; +import { LucideIcon } from "lucide-react"; + +interface MetricsCardProps { + title: string; + value: string; + description: string; + icon: LucideIcon; + trend: "up" | "down" | "neutral"; + trendValue: string; +} + +export default function MetricsCard({ + title, + value, + description, + icon: Icon, + trend, + trendValue +}: MetricsCardProps) { + const getTrendIcon = () => { + switch (trend) { + case "up": + return ; + case "down": + return ; + default: + return ; + } + }; + + const getTrendColor = () => { + switch (trend) { + case "up": + return "text-green-600"; + case "down": + return "text-red-600"; + default: + return "text-gray-600"; + } + }; + + return ( + + + + {title} + + + + +
{value}
+

{description}

+
+ {getTrendIcon()} + + {trendValue} + +
+
+
+ ); +} \ No newline at end of file