diff --git a/app/dashboard/analytics/page.tsx b/app/dashboard/analytics/page.tsx index 8189982..45e5161 100644 --- a/app/dashboard/analytics/page.tsx +++ b/app/dashboard/analytics/page.tsx @@ -3,6 +3,7 @@ import { cookies } from 'next/headers'; import { redirect } from 'next/navigation'; import Dashboard from "@/components/dashboard/dashboard"; import AnalyticsDashboard from '@/components/analytics/AnalyticsDashboard'; +import AnalyticsDashboardSkeleton from '@/components/analytics/AnalyticsDashboardSkeleton'; import StoreSelector from '@/components/analytics/StoreSelector'; import { getAnalyticsOverviewServer } from '@/lib/server-api'; import { fetchServer } from '@/lib/api'; @@ -65,14 +66,7 @@ export default async function AnalyticsPage({
{/* Analytics Content */} - -
-
-

Loading analytics...

-
-
- }> + }> diff --git a/components/analytics/AnalyticsDashboard.tsx b/components/analytics/AnalyticsDashboard.tsx index a08df86..13cec0a 100644 --- a/components/analytics/AnalyticsDashboard.tsx +++ b/components/analytics/AnalyticsDashboard.tsx @@ -25,6 +25,7 @@ import OrderAnalyticsChart from "./OrderAnalyticsChart"; import MetricsCard from "./MetricsCard"; import { getAnalyticsOverviewWithStore, type AnalyticsOverview } from "@/lib/services/analytics-service"; import { formatGBP } from "@/utils/format"; +import { MetricsCardSkeleton } from './SkeletonLoaders'; interface AnalyticsDashboardProps { initialData: AnalyticsOverview; @@ -95,9 +96,15 @@ export default function AnalyticsDashboard({ initialData }: AnalyticsDashboardPr
{/* Key Metrics Cards */}
- {metrics.map((metric) => ( - - ))} + {isLoading ? ( + [...Array(4)].map((_, i) => ( + + )) + ) : ( + metrics.map((metric) => ( + + )) + )}
{/* Completion Rate Card */} @@ -112,25 +119,55 @@ export default function AnalyticsDashboard({ initialData }: AnalyticsDashboardPr -
-
- {data.orders.completionRate}% -
-
-
-
+ {isLoading ? ( +
+
+
+
+
- - {data.orders.completed} / {data.orders.total} - -
+ ) : ( +
+
+ {data.orders.completionRate}% +
+
+
+
+
+
+ + {data.orders.completed} / {data.orders.total} + +
+ )} + {/* Time Period Selector */} +
+
+

Time period:

+

+ Revenue and Orders tabs use time filtering. Products and Customers show all-time data. +

+
+ +
+ {/* Analytics Tabs */}
diff --git a/components/analytics/AnalyticsDashboardSkeleton.tsx b/components/analytics/AnalyticsDashboardSkeleton.tsx new file mode 100644 index 0000000..20fdefe --- /dev/null +++ b/components/analytics/AnalyticsDashboardSkeleton.tsx @@ -0,0 +1,204 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { MetricsCardSkeleton } from './SkeletonLoaders'; +import { + TrendingUp, + Package, + Users, + BarChart3, + Activity +} from "lucide-react"; + +export default function AnalyticsDashboardSkeleton() { + return ( +
+ {/* Key Metrics Cards */} +
+ {[...Array(4)].map((_, i) => ( + + ))} +
+ + {/* Completion Rate Card */} + + + + + Order Completion Rate + + + Percentage of orders that have been successfully completed + + + +
+ +
+ +
+ +
+
+
+ + + + {/* Analytics Tabs */} +
+ + + + + Revenue + + + + Products + + + + Customers + + + + Orders + + + + + + + + + Revenue Trends + + + Revenue performance over the selected time period + + + +
+ {/* Chart area */} +
+ + {/* Summary stats */} +
+ {[...Array(3)].map((_, i) => ( +
+ + +
+ ))} +
+
+ + + + + + + + + + Product Performance + + + Top performing products by revenue and sales + + + +
+ {/* Table header */} +
+ {[...Array(5)].map((_, i) => ( + + ))} +
+ + {/* Table rows */} + {[...Array(8)].map((_, rowIndex) => ( +
+ {[...Array(5)].map((_, colIndex) => ( +
+ {colIndex === 0 && ( + + )} + +
+ ))} +
+ ))} +
+
+
+
+ + + + + + + Customer Insights + + + Customer segmentation and behavior analysis + + + +
+ {/* Customer segments */} +
+ {[...Array(4)].map((_, i) => ( +
+ + +
+ ))} +
+ + {/* Top customers table */} +
+ + {[...Array(5)].map((_, i) => ( +
+
+ + +
+
+ + +
+
+ ))} +
+
+
+
+
+ + + + + + + Order Analytics + + + Order status distribution and trends + + + +
+ {/* Chart area */} +
+
+ + + + +
+
+ ); +} \ No newline at end of file diff --git a/components/analytics/CustomerInsightsChart.tsx b/components/analytics/CustomerInsightsChart.tsx index e7c22e5..715bbe1 100644 --- a/components/analytics/CustomerInsightsChart.tsx +++ b/components/analytics/CustomerInsightsChart.tsx @@ -8,6 +8,7 @@ import { Skeleton } from "@/components/ui/skeleton"; import { Users, Crown, UserPlus, UserCheck, Star } from "lucide-react"; import { getCustomerInsightsWithStore, type CustomerInsights } from "@/lib/services/analytics-service"; import { formatGBP } from "@/utils/format"; +import { CustomerInsightsSkeleton } from './SkeletonLoaders'; export default function CustomerInsightsChart() { const [data, setData] = useState(null); @@ -85,26 +86,11 @@ export default function CustomerInsightsChart() { if (isLoading) { return ( - - - - - Customer Insights - - - Customer segmentation and behavior analysis - - - -
- -
- - -
-
-
-
+ ); } diff --git a/components/analytics/OrderAnalyticsChart.tsx b/components/analytics/OrderAnalyticsChart.tsx index a852399..6dad5de 100644 --- a/components/analytics/OrderAnalyticsChart.tsx +++ b/components/analytics/OrderAnalyticsChart.tsx @@ -8,6 +8,7 @@ import { Skeleton } from "@/components/ui/skeleton"; import { BarChart3, Clock, CheckCircle, XCircle, AlertCircle } from "lucide-react"; import { getOrderAnalyticsWithStore, type OrderAnalytics } from "@/lib/services/analytics-service"; import { formatGBP } from "@/utils/format"; +import { ChartSkeleton } from './SkeletonLoaders'; interface OrderAnalyticsChartProps { timeRange: string; @@ -105,23 +106,12 @@ export default function OrderAnalyticsChart({ timeRange }: OrderAnalyticsChartPr if (isLoading) { return ( - - - - - Order Analytics - - - Order status distribution and trends - - - -
- - -
-
-
+ ); } diff --git a/components/analytics/ProductPerformanceChart.tsx b/components/analytics/ProductPerformanceChart.tsx index 1b14dc3..f7858c8 100644 --- a/components/analytics/ProductPerformanceChart.tsx +++ b/components/analytics/ProductPerformanceChart.tsx @@ -9,6 +9,7 @@ import { Skeleton } from "@/components/ui/skeleton"; import { Package } from "lucide-react"; import { getProductPerformanceWithStore, type ProductPerformance } from "@/lib/services/analytics-service"; import { formatGBP } from "@/utils/format"; +import { TableSkeleton } from './SkeletonLoaders'; export default function ProductPerformanceChart() { const [data, setData] = useState([]); @@ -41,33 +42,13 @@ export default function ProductPerformanceChart() { if (isLoading) { return ( - - - - - Product Performance - - - Top performing products by revenue and sales - - - -
- - {[...Array(5)].map((_, i) => ( -
- -
- - -
- - -
- ))} -
-
-
+ ); } diff --git a/components/analytics/RevenueChart.tsx b/components/analytics/RevenueChart.tsx index 7e47fa0..bb82f36 100644 --- a/components/analytics/RevenueChart.tsx +++ b/components/analytics/RevenueChart.tsx @@ -8,6 +8,7 @@ import { TrendingUp, DollarSign } from "lucide-react"; import { getRevenueTrendsWithStore, type RevenueData } from "@/lib/services/analytics-service"; import { formatGBP } from "@/utils/format"; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, BarChart, Bar } from 'recharts'; +import { ChartSkeleton } from './SkeletonLoaders'; interface RevenueChartProps { timeRange: string; @@ -95,27 +96,12 @@ export default function RevenueChart({ timeRange }: RevenueChartProps) { if (isLoading) { return ( - - - - - Revenue Trends - - - Revenue performance over the selected time period - - - -
- -
- - - -
-
-
-
+ ); } diff --git a/components/analytics/SkeletonLoaders.tsx b/components/analytics/SkeletonLoaders.tsx new file mode 100644 index 0000000..8415177 --- /dev/null +++ b/components/analytics/SkeletonLoaders.tsx @@ -0,0 +1,170 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; + +// Chart skeleton for revenue trends and order analytics +export function ChartSkeleton({ + title, + description, + icon: Icon, + showStats = false +}: { + title: string; + description: string; + icon: any; + showStats?: boolean; +}) { + return ( + + + + + {title} + + {description} + + +
+ {/* Chart area */} +
+ + {/* Summary stats if applicable */} + {showStats && ( +
+ {[...Array(3)].map((_, i) => ( +
+ + +
+ ))} +
+ )} +
+ + + ); +} + +// Table skeleton for product performance +export function TableSkeleton({ + title, + description, + icon: Icon, + rows = 5, + columns = 5 +}: { + title: string; + description: string; + icon: any; + rows?: number; + columns?: number; +}) { + return ( + + + + + {title} + + {description} + + +
+ {/* Table header */} +
+ {[...Array(columns)].map((_, i) => ( + + ))} +
+ + {/* Table rows */} + {[...Array(rows)].map((_, rowIndex) => ( +
+ {[...Array(columns)].map((_, colIndex) => ( +
+ {colIndex === 0 && ( + + )} + +
+ ))} +
+ ))} +
+
+
+ ); +} + +// Customer insights skeleton with segments +export function CustomerInsightsSkeleton({ + title, + description, + icon: Icon +}: { + title: string; + description: string; + icon: any; +}) { + return ( + + + + + {title} + + {description} + + +
+ {/* Customer segments */} +
+ {[...Array(4)].map((_, i) => ( +
+ + +
+ ))} +
+ + {/* Top customers table */} +
+ + {[...Array(5)].map((_, i) => ( +
+
+ + +
+
+ + +
+
+ ))} +
+
+
+
+ ); +} + +// Metrics card skeleton +export function MetricsCardSkeleton() { + return ( + + +
+
+ + +
+ +
+
+ + +
+
+
+ ); +} \ No newline at end of file