feat: implement product grid with comprehensive product data fetching, processing, and heatmap display.

This commit is contained in:
Michael committed 2026-03-19 12:41:03 +01:00
1 parent 2b7e7bc1f4
commit 8b7d81b20c
4 files changed
+127 -1

No files matched your search

+13 -1
View File
@@ -7,6 +7,7 @@ import {
getMensuelByArticles,
getReferentielByArticles,
getCommandesByFournisseur,
getRankingByArticles,
buildLast12MonthsRange,
} from "@/lib/api-ff-client";
import { db } from "@/db";
@@ -30,10 +31,11 @@ export async function getProductRows(input: GetProductRowsInput): Promise<Produc
// ─── Phase 2 : Mensuel + Commandes (en parallèle) ────────────────────
const { dateDebut, dateFin } = buildLast12MonthsRange();
const [mensuelMap, referentielMap, commandesMap] = await Promise.all([
const [mensuelMap, referentielMap, commandesMap, rankingMap] = await Promise.all([
getMensuelByArticles(articles, dateDebut, dateFin),
getReferentielByArticles(articles),
getCommandesByFournisseur(codeFournisseur),
getRankingByArticles(articles),
]);
console.log(`[getProductRows] mensuel data for ${mensuelMap.size}/${articles.length} articles`);
@@ -220,6 +222,16 @@ export async function getProductRows(input: GetProductRowsInput): Promise<Produc
}
console.log(`[getProductRows] Referentiel: ${refCount}/${articles.length} articles enrichis`);
// ─── Phase 6b : Ranking réseau + magasin ────────────────────────────
for (const [codein, ranking] of rankingMap.entries()) {
const product = productMap.get(codein);
if (!product) continue;
product.rankingCa = ranking.ranking_ca;
product.rankingQte = ranking.ranking_qte;
product.rankingMagCa = ranking.ranking_mag_ca;
product.rankingMagQte = ranking.ranking_mag_qte;
}
// ─── Phase 7 : Restaurer gammes depuis dernier snapshot ──────────────
try {
const snaps = await db
@@ -399,6 +399,58 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
);
},
},
{
accessorKey: "rankingCa",
header: () => <div className="text-center w-full">Rk CA<br/><span className="text-[9px] opacity-60">Réseau</span></div>,
size: 70,
cell: ({ getValue }) => {
const val = getValue<number | undefined>();
return (
<div className="text-center tabular-nums text-[12px] font-bold" style={{ color: "var(--text-secondary)" }}>
{val != null ? `#${val}` : "-"}
</div>
);
},
},
{
accessorKey: "rankingQte",
header: () => <div className="text-center w-full">Rk Qté<br/><span className="text-[9px] opacity-60">Réseau</span></div>,
size: 70,
cell: ({ getValue }) => {
const val = getValue<number | undefined>();
return (
<div className="text-center tabular-nums text-[12px] font-bold" style={{ color: "var(--text-secondary)" }}>
{val != null ? `#${val}` : "-"}
</div>
);
},
},
{
accessorKey: "rankingMagCa",
header: () => <div className="text-center w-full">Rk CA<br/><span className="text-[9px] opacity-60">Mag.</span></div>,
size: 70,
cell: ({ getValue }) => {
const val = getValue<number | undefined>();
return (
<div className="text-center tabular-nums text-[12px] font-bold" style={{ color: "var(--text-secondary)" }}>
{val != null ? `#${val}` : "-"}
</div>
);
},
},
{
accessorKey: "rankingMagQte",
header: () => <div className="text-center w-full">Rk Qté<br/><span className="text-[9px] opacity-60">Mag.</span></div>,
size: 70,
cell: ({ getValue }) => {
const val = getValue<number | undefined>();
return (
<div className="text-center tabular-nums text-[12px] font-bold" style={{ color: "var(--text-secondary)" }}>
{val != null ? `#${val}` : "-"}
</div>
);
},
},
...MONTHS_12.map((monthKey) => ({
id: `month_${monthKey}`,
header: () => <div className="text-center w-full">{formatMonthLabel(monthKey)}</div>,