mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-11 17:26:32 +02:00
feat: Introduce direct PostgreSQL client for FF Nancy data to replace HTTP API calls and add a new dashboard page.
This commit is contained in:
1 parent
d1bbe290da
commit
6ee0e3294d
2 files changed
+32
-22
No files matched your search
@@ -15,7 +15,7 @@ const SITE_NAMES: Record<string, string> = {
|
||||
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 }: {
|
||||
<div className="h-full rounded-full" style={{ width: `${barPct}%`, background: "var(--accent)" }} />
|
||||
</div>
|
||||
<span className="text-xs shrink-0" style={{ color: "var(--text-muted)" }}>
|
||||
{sortKey === "ca" && `${fmtQte(item.qte)} pcs · `}
|
||||
{sortKey !== "ca" && `CA ${fmtEur(item.ca)} · `}
|
||||
{fmtPct(txMarge, false)} marge
|
||||
</span>
|
||||
|
||||
+30
-21
@@ -539,31 +539,41 @@ export async function pgGetDashboardData(): Promise<DashboardData> {
|
||||
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<ApiPerfDashboard>;
|
||||
});
|
||||
// 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<ApiPerfDashboard>; }),
|
||||
fetch(`${apiBase}/dashboard?date=${dateN1Param}`, { cache: "no-store" })
|
||||
.then(r => r.ok ? r.json() as Promise<ApiPerfDashboard> : 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<string, { ca: number; trafic: number }>();
|
||||
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<DashboardData> {
|
||||
};
|
||||
});
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user