From a4debe992be07a10eba7fffc0a458d8b87979c65 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Fri, 3 Apr 2026 07:12:15 +0200 Subject: [PATCH] feat: implement dashboard page and add loading skeletons for all dashboard modules --- .claude/settings.json | 4 ++- src/app/(dashboard)/analytics/loading.tsx | 30 +++++++++++++++++++ src/app/(dashboard)/dashboard/loading.tsx | 27 +++++++++++++++++ src/app/(dashboard)/dashboard/page.tsx | 9 +++++- src/app/(dashboard)/exports/loading.tsx | 21 +++++++++++++ src/app/(dashboard)/hit-parade/loading.tsx | 34 ++++++++++++++++++++++ src/app/(dashboard)/settings/loading.tsx | 17 +++++++++++ src/app/(dashboard)/snapshots/loading.tsx | 18 ++++++++++++ 8 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 src/app/(dashboard)/analytics/loading.tsx create mode 100644 src/app/(dashboard)/dashboard/loading.tsx create mode 100644 src/app/(dashboard)/exports/loading.tsx create mode 100644 src/app/(dashboard)/hit-parade/loading.tsx create mode 100644 src/app/(dashboard)/settings/loading.tsx create mode 100644 src/app/(dashboard)/snapshots/loading.tsx diff --git a/.claude/settings.json b/.claude/settings.json index 4d783f8..dbb71b5 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -27,7 +27,9 @@ "Bash(curl -i -s \"https://api.ffnancy.fr/api/performance/ca/nomenclature?dateDebut=2025-03-01&dateFin=2025-03-31&site=&niveau=1\")", "Bash(curl -i -s \"https://api.ffnancy.fr/api/performance/ca?dateDebut=2025-03-01&dateFin=2025-03-31&groupBy=mois\")", "WebFetch(domain:collect.ffnancy.fr)", - "Bash(curl -s \"https://collect.ffnancy.fr/api/diag-mvtart\")" + "Bash(curl -s \"https://collect.ffnancy.fr/api/diag-mvtart\")", + "Read(//c/Users/Michael/VSCODE/CollectFlow/**)", + "Bash(xargs wc:*)" ] } } diff --git a/src/app/(dashboard)/analytics/loading.tsx b/src/app/(dashboard)/analytics/loading.tsx new file mode 100644 index 0000000..dd99d6d --- /dev/null +++ b/src/app/(dashboard)/analytics/loading.tsx @@ -0,0 +1,30 @@ +export default function AnalyticsLoading() { + return ( +
+ {/* Header */} +
+
+
+
+
+
+
+ + {/* Filter bar */} +
+ + {/* Table skeleton */} +
+
+ {Array.from({ length: 8 }).map((_, i) => ( +
+
+
+
+
+
+ ))} +
+
+ ); +} diff --git a/src/app/(dashboard)/dashboard/loading.tsx b/src/app/(dashboard)/dashboard/loading.tsx new file mode 100644 index 0000000..89b212a --- /dev/null +++ b/src/app/(dashboard)/dashboard/loading.tsx @@ -0,0 +1,27 @@ +export default function DashboardLoading() { + return ( +
+ {/* Header skeleton */} +
+
+
+
+ + {/* KPI cards row */} +
+ {Array.from({ length: 4 }).map((_, i) => ( +
+ ))} +
+ + {/* Main content area */} +
+
+
+
+ + {/* Bottom tables */} +
+
+ ); +} diff --git a/src/app/(dashboard)/dashboard/page.tsx b/src/app/(dashboard)/dashboard/page.tsx index aebbad4..bca535d 100644 --- a/src/app/(dashboard)/dashboard/page.tsx +++ b/src/app/(dashboard)/dashboard/page.tsx @@ -1,4 +1,11 @@ import { pgGetDashboardData, type DashboardTopItem, type DashboardSiteStats, type DashboardSiteTop10 } from "@/lib/pg-ff-client"; +import { unstable_cache } from "next/cache"; + +const getCachedDashboardData = unstable_cache( + pgGetDashboardData, + ["dashboard-data"], + { revalidate: 600 } // 10 minutes +); import { TrendingUp, TrendingDown, Minus, Store, Euro, Users, ShoppingCart, Award, BarChart3 } from "lucide-react"; // --------------------------------------------------------------------------- @@ -722,7 +729,7 @@ function SectionHeader({ label, sub }: { label: string; sub?: string }) { export default async function DashboardPage() { let data; try { - data = await pgGetDashboardData(); + data = await getCachedDashboardData(); } catch (e) { const msg = e instanceof Error ? e.message : String(e); console.error("[Dashboard] pgGetDashboardData failed:", e); diff --git a/src/app/(dashboard)/exports/loading.tsx b/src/app/(dashboard)/exports/loading.tsx new file mode 100644 index 0000000..6c6c9ab --- /dev/null +++ b/src/app/(dashboard)/exports/loading.tsx @@ -0,0 +1,21 @@ +export default function ExportsLoading() { + return ( +
+ {/* Header */} +
+ + {/* Table */} +
+
+ {Array.from({ length: 8 }).map((_, i) => ( +
+
+
+
+
+
+ ))} +
+
+ ); +} diff --git a/src/app/(dashboard)/hit-parade/loading.tsx b/src/app/(dashboard)/hit-parade/loading.tsx new file mode 100644 index 0000000..d85f2ae --- /dev/null +++ b/src/app/(dashboard)/hit-parade/loading.tsx @@ -0,0 +1,34 @@ +export default function HitParadeLoading() { + return ( +
+ {/* Header */} +
+
+
+
+
+
+
+ + {/* Podium / top cards */} +
+ {Array.from({ length: 3 }).map((_, i) => ( +
+ ))} +
+ + {/* Rankings table */} +
+
+ {Array.from({ length: 10 }).map((_, i) => ( +
+
+
+
+
+
+ ))} +
+
+ ); +} diff --git a/src/app/(dashboard)/settings/loading.tsx b/src/app/(dashboard)/settings/loading.tsx new file mode 100644 index 0000000..0d0039b --- /dev/null +++ b/src/app/(dashboard)/settings/loading.tsx @@ -0,0 +1,17 @@ +export default function SettingsLoading() { + return ( +
+ {/* Header */} +
+ + {/* Settings sections */} + {Array.from({ length: 3 }).map((_, i) => ( +
+
+
+
+
+ ))} +
+ ); +} diff --git a/src/app/(dashboard)/snapshots/loading.tsx b/src/app/(dashboard)/snapshots/loading.tsx new file mode 100644 index 0000000..4a4e705 --- /dev/null +++ b/src/app/(dashboard)/snapshots/loading.tsx @@ -0,0 +1,18 @@ +export default function SnapshotsLoading() { + return ( +
+ {/* Header */} +
+
+
+
+ + {/* Cards grid */} +
+ {Array.from({ length: 6 }).map((_, i) => ( +
+ ))} +
+
+ ); +}