// Admin Analytics Dashboard - Real metrics and business insights
"use client";
import { useEffect, useEffectEvent, useCallback, useReducer } from "react";
import Link from "next/link";
import { formatDate } from "@/lib/format-date";
import { analytics } from "@/lib/analytics";
import {
getAnalyticsMetrics,
getRevenueChart,
getTopProducts,
getRecentOrders,
getCustomerGrowth,
getConversionFunnel,
type AnalyticsMetrics,
type RevenueDataPoint,
type ProductPerformance,
type RecentOrder,
type CustomerGrowth,
type ConversionFunnel,
} from "@/actions/analytics";
function getTrend(avgPrice: number) {
if (avgPrice > 10) return "up";
if (avgPrice < 5) return "down";
return "stable";
}
interface MetricCardProps {
title: string;
value: string | number;
change: number;
trend: "up" | "down" | "stable";
icon: React.ReactNode;
}
function MetricCard({ title, value, change, trend, icon }: MetricCardProps) {
return (
{title}
{typeof value === "number" ? value.toLocaleString() : value}
{icon}
{trend === "up" ? "↑" : trend === "down" ? "↓" : "→"}
{change > 0 ? "+" : ""}{change}% vs last period
);
}
interface RevenueChartProps {
data: RevenueDataPoint[];
totalRevenue: number;
avgOrder: number;
}
function RevenueChart({ data, totalRevenue, avgOrder }: RevenueChartProps) {
const maxRevenue = Math.max(...data.map(d => d.revenue), 1);
return (
Revenue Overview
{data.length > 0 ? (
<>
{data.map((item, i) => (
{new Date(item.date).toLocaleDateString("en-US", { month: "short", day: "numeric" })}
))}
Total Revenue
${totalRevenue.toLocaleString()}
Avg. Order
${avgOrder.toFixed(2)}
>
) : (
No revenue data available yet
)}
);
}
interface TopProductsTableProps {
products: ProductPerformance[];
}
function TopProductsTable({ products }: TopProductsTableProps) {
if (products.length === 0) {
return (
Top Products
No product data available yet
);
}
return (
Top Products
| Product |
Sales |
Revenue |
Trend |
{products.map((product, i) => (
|
|
{product.units_sold} |
${product.revenue.toFixed(2)} |
{getTrend(product.avg_price) === "up" ? "↑" : getTrend(product.avg_price) === "down" ? "↓" : "→"}
|
))}
);
}
interface RecentOrdersTableProps {
orders: RecentOrder[];
}
function RecentOrdersTable({ orders }: RecentOrdersTableProps) {
return (
Recent Orders
View all →
{orders.length > 0 ? (
| Order |
Customer |
Amount |
Status |
Date |
{orders.map((order, i) => (
|
{order.id.slice(0, 8)}
|
{order.customer_name || "Guest"} |
${order.subtotal.toFixed(2)} |
{order.status}
|
{formatDate(order.created_at)} |
))}
) : (
No orders yet
)}
);
}
interface CustomerGrowthChartProps {
data: CustomerGrowth;
}
function CustomerGrowthChart({ data }: CustomerGrowthChartProps) {
const growthPercent = data.growth_rate || 0;
const circumference = 2 * Math.PI * 56;
const offset = circumference - (Math.abs(growthPercent) / 100) * circumference;
return (
Customer Growth
{growthPercent > 0 ? "+" : ""}{growthPercent}%
vs last month
{data.total_customers}
Total
+{data.new_this_month}
New
{data.retention_rate}%
Retention
);
}
interface ConversionFunnelProps {
data: ConversionFunnel[];
}
function ConversionFunnel({ data }: ConversionFunnelProps) {
return (
Conversion Funnel
{data.length > 0 ? (
{data.map((step, i) => (
{step.stage}
{step.count.toLocaleString()}
({step.rate}%)
))}
) : (
No funnel data available
)}
);
}
// ---- useReducer state ----
type State = {
isLoading: boolean;
error: string | null;
metrics: AnalyticsMetrics;
revenueData: RevenueDataPoint[];
topProducts: ProductPerformance[];
recentOrders: RecentOrder[];
customerGrowth: CustomerGrowth;
conversionFunnel: ConversionFunnel[];
};
type Action =
| { type: "FETCH_START" }
| { type: "FETCH_SUCCESS"; metrics: AnalyticsMetrics; revenueData: RevenueDataPoint[]; topProducts: ProductPerformance[]; recentOrders: RecentOrder[]; customerGrowth: CustomerGrowth; conversionFunnel: ConversionFunnel[] }
| { type: "FETCH_ERROR"; error: string };
const initialMetrics: AnalyticsMetrics = {
total_revenue: 0,
revenue_change: 0,
total_orders: 0,
orders_change: 0,
active_customers: 0,
customers_change: 0,
avg_order_value: 0,
aov_change: 0,
};
const initialCustomerGrowth: CustomerGrowth = {
total_customers: 0,
new_this_month: 0,
retention_rate: 0,
growth_rate: 0,
};
const initialState: State = {
isLoading: true,
error: null,
metrics: initialMetrics,
revenueData: [],
topProducts: [],
recentOrders: [],
customerGrowth: initialCustomerGrowth,
conversionFunnel: [],
};
function reducer(state: State, action: Action): State {
switch (action.type) {
case "FETCH_START":
return { ...state, isLoading: true, error: null };
case "FETCH_SUCCESS":
return {
...state,
isLoading: false,
metrics: action.metrics,
revenueData: action.revenueData,
topProducts: action.topProducts,
recentOrders: action.recentOrders,
customerGrowth: action.customerGrowth,
conversionFunnel: action.conversionFunnel,
};
case "FETCH_ERROR":
return { ...state, isLoading: false, error: action.error };
}
}
export default function AnalyticsDashboard({ brandId }: { brandId?: string }) {
const [state, dispatch] = useReducer(reducer, initialState);
const fetchAllData = useCallback(async () => {
dispatch({ type: "FETCH_START" });
try {
const [
metricsData,
revenueChartData,
productsData,
ordersData,
growthData,
funnelData,
] = await Promise.all([
getAnalyticsMetrics(30),
getRevenueChart(30),
getTopProducts(5),
getRecentOrders(10),
getCustomerGrowth(),
getConversionFunnel(),
]);
dispatch({
type: "FETCH_SUCCESS",
metrics: metricsData,
revenueData: revenueChartData,
topProducts: productsData,
recentOrders: ordersData,
customerGrowth: growthData,
conversionFunnel: funnelData,
});
} catch (err) {
console.error("Failed to fetch analytics:", err);
dispatch({ type: "FETCH_ERROR", error: err instanceof Error ? err.message : "Failed to load analytics" });
}
}, []);
// useEffectEvent so we always call the latest fetchAllData without
// re-running the effect every time the parent re-renders.
const fetchAllDataEffect = useEffectEvent(() => {
fetchAllData();
});
useEffect(() => {
analytics.featureUsed("analytics_dashboard", { brand_id: brandId });
// Wrap in requestAnimationFrame to avoid sync setState
requestAnimationFrame(() => {
fetchAllDataEffect();
});
}, [brandId]);
if (state.isLoading) {
return ;
}
if (state.error) {
return ;
}
return (
);
}
function LoadingState() {
return (
);
}
function ErrorState({ error, onRetry }: { error: string; onRetry: () => void }) {
return (
);
}
function DashboardHeader({ onRefresh }: { onRefresh: () => void }) {
return (
Analytics
Track your business performance and growth
);
}
function MetricsRow({ metrics }: { metrics: AnalyticsMetrics }) {
return (
= 0 ? "up" : "down"}
icon={
}
/>
= 0 ? "up" : "down"}
icon={
}
/>
= 0 ? "up" : "down"}
icon={
}
/>
= 0 ? "up" : "down"}
icon={
}
/>
);
}
type ChartsRowProps = {
revenueData: RevenueDataPoint[];
totalRevenue: number;
avgOrder: number;
customerGrowth: CustomerGrowth;
};
function ChartsRow({ revenueData, totalRevenue, avgOrder, customerGrowth }: ChartsRowProps) {
return (
);
}
type TablesRowProps = {
topProducts: ProductPerformance[];
recentOrders: RecentOrder[];
};
function TablesRow({ topProducts, recentOrders }: TablesRowProps) {
return (
);
}