From 6ee0e3294db89ae1a94030bf5d22fc2d1cab4dbc Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Sat, 21 Mar 2026 14:37:02 +0100 Subject: [PATCH] feat: Introduce direct PostgreSQL client for FF Nancy data to replace HTTP API calls and add a new dashboard page. --- src/app/(dashboard)/dashboard/page.tsx | 3 +- src/lib/pg-ff-client.ts | 51 +++++++++++++++----------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/app/(dashboard)/dashboard/page.tsx b/src/app/(dashboard)/dashboard/page.tsx index 2531dcb..fd8d1d3 100644 --- a/src/app/(dashboard)/dashboard/page.tsx +++ b/src/app/(dashboard)/dashboard/page.tsx @@ -15,7 +15,7 @@ const SITE_NAMES: Record = { function fmtEur(n: number): string { const abs = Math.abs(n); if (abs >= 1_000_000) return `${(n / 1_000_000).toLocaleString("fr-FR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} M€`; - return `${Math.round(n).toLocaleString("fr-FR")} €`; + return `${n.toLocaleString("fr-FR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} €`; } function fmtQte(n: number): string { @@ -147,6 +147,7 @@ function Top10Table({ title, items, sortKey }: {
+ {sortKey === "ca" && `${fmtQte(item.qte)} pcs · `} {sortKey !== "ca" && `CA ${fmtEur(item.ca)} · `} {fmtPct(txMarge, false)} marge diff --git a/src/lib/pg-ff-client.ts b/src/lib/pg-ff-client.ts index dbb4615..cb8c531 100644 --- a/src/lib/pg-ff-client.ts +++ b/src/lib/pg-ff-client.ts @@ -539,31 +539,41 @@ export async function pgGetDashboardData(): Promise { yesterday.setDate(yesterday.getDate() - 1); const dateHier = yesterday.toISOString().split("T")[0]; - // Même jour de semaine N-1 : 364 jours = 52 semaines exactes (conserve le jour) + // N-1 : même jour de semaine, même semaine N-1 → 364 jours = 52 semaines exactes const n1Date = new Date(yesterday); n1Date.setDate(n1Date.getDate() - 364); const dateN1Param = n1Date.toISOString().split("T")[0]; const apiBase = "https://api.ffnancy.fr/api/performance"; - const apiResult = await fetch( - `${apiBase}/dashboard?date=${dateHier}&date_n1=${dateN1Param}`, - { cache: "no-store" } - ).then(r => { - if (!r.ok) throw new Error(`API dashboard ${r.status}: ${r.statusText}`); - return r.json() as Promise; - }); + // Deux appels parallèles : données hier + données même jour N-1 + const [apiResult, apiN1Result] = await Promise.all([ + fetch(`${apiBase}/dashboard?date=${dateHier}`, { cache: "no-store" }) + .then(r => { if (!r.ok) throw new Error(`API dashboard ${r.status}: ${r.statusText}`); return r.json() as Promise; }), + fetch(`${apiBase}/dashboard?date=${dateN1Param}`, { cache: "no-store" }) + .then(r => r.ok ? r.json() as Promise : null) + .catch(() => null), + ]); - const sites: DashboardSiteStats[] = (apiResult.sites ?? []).map(s => ({ - site: s.site, - ca_hier: Number(s.ca_ttc) || 0, - ca_n1: Number(s.ca_ttc_n1) || 0, - tickets_hier: Number(s.trafic) || 0, - tickets_n1: Number(s.trafic_n1) || 0, - qte_hier: 0, - marge_hier: 0, - lignes_hier: 0, - })); + // Construire map N-1 par site depuis l'appel séparé + const n1BySite = new Map(); + for (const s of apiN1Result?.sites ?? []) { + n1BySite.set(s.site, { ca: Number(s.ca_ttc) || 0, trafic: Number(s.trafic) || 0 }); + } + + const sites: DashboardSiteStats[] = (apiResult.sites ?? []).map(s => { + const n1 = n1BySite.get(s.site); + return { + site: s.site, + ca_hier: Number(s.ca_ttc) || 0, + ca_n1: n1?.ca ?? Number(s.ca_ttc_n1) || 0, + tickets_hier: Number(s.trafic) || 0, + tickets_n1: n1?.trafic ?? Number(s.trafic_n1) || 0, + qte_hier: 0, + marge_hier: 0, + lignes_hier: 0, + }; + }); // Top 10 par magasin : grouper les items par site, trier, garder top 10 const buildTop10 = (items: ApiPerfTopItem[], sortKey: "ca" | "qte" | "marge"): DashboardTopItem[] => @@ -595,12 +605,11 @@ export async function pgGetDashboardData(): Promise { }; }); - const dateN1 = apiResult.date_n1 ?? dateN1Param; - console.log(`[pg-ff] Dashboard API: ${sites.length} sites, date=${apiResult.date}, date_n1=${dateN1}`); + console.log(`[pg-ff] Dashboard: hier=${dateHier} n1=${dateN1Param} sites=${sites.length} n1_found=${n1BySite.size}`); return { dateHier: apiResult.date ?? dateHier, - dateN1, + dateN1: apiN1Result?.date ?? dateN1Param, sites, top10BySite, };