diff --git a/src/features/grid/api/get-product-rows.ts b/src/features/grid/api/get-product-rows.ts index 85cc304..73063cd 100644 --- a/src/features/grid/api/get-product-rows.ts +++ b/src/features/grid/api/get-product-rows.ts @@ -34,28 +34,36 @@ export async function getProductRows(input: GetProductRowsInput): Promise(); + const storeParticipationMap = new Map>>(); // codein -> magasin -> Set of periods for (const row of rows) { // Ignorer les données qui ne font pas partie des 12 mois complets if (!allowedPeriods.has(row.periode)) continue; - const existing = productMap.get(row.codein); - const monthKey = row.periode; + const { codein, magasin, periode } = row; + + // Suivi de la participation par magasin + if (!storeParticipationMap.has(codein)) storeParticipationMap.set(codein, new Map()); + const productStores = storeParticipationMap.get(codein)!; + if (!productStores.has(magasin)) productStores.set(magasin, new Set()); + productStores.get(magasin)!.add(periode); + + const existing = productMap.get(codein); const qty = Math.abs(parseFloat(row.quantite?.toString() ?? "0")); const ca = Math.abs(parseFloat(row.montantMvt?.toString() ?? "0")); const marge = Math.abs(parseFloat(row.margeMvt?.toString() ?? "0")); if (existing) { - existing.sales12m[monthKey] = (existing.sales12m[monthKey] ?? 0) + qty; + existing.sales12m[periode] = (existing.sales12m[periode] ?? 0) + qty; existing.totalQuantite += qty; existing.totalCa += ca; existing.totalMarge += marge; existing.tauxMarge = existing.totalCa > 0 ? (existing.totalMarge / existing.totalCa) * 100 : 0; } else { - productMap.set(row.codein, { - codein: row.codein, + productMap.set(codein, { + codein, codeFournisseur: row.codeFournisseur ?? "", nomFournisseur: row.nomFournisseur ?? "", libelle1: row.libelle1 ?? "", @@ -65,16 +73,32 @@ export async function getProductRows(input: GetProductRowsInput): Promise 0 ? (marge / ca) * 100 : 0, score: 0, + workingStores: [], aiRecommendation: null, }); } } + // Finaliser la liste des magasins "actifs" (travaillant le produit : >= 3 mois de vente) + for (const [codein, product] of productMap.entries()) { + const productStores = storeParticipationMap.get(codein); + if (productStores) { + const workingStores: string[] = []; + for (const [magasin, periods] of productStores.entries()) { + if (magasin === "TOTAL") continue; + if (periods.size >= 3) { + workingStores.push(magasin); + } + } + product.workingStores = workingStores.sort(); + } + } + return computeProductScores(Array.from(productMap.values())); } diff --git a/src/features/grid/components/heatmap-grid.tsx b/src/features/grid/components/heatmap-grid.tsx index 905a6e9..9c2d03c 100644 --- a/src/features/grid/components/heatmap-grid.tsx +++ b/src/features/grid/components/heatmap-grid.tsx @@ -127,10 +127,29 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) { accessorKey: "libelle1", header: "Désignation", size: 280, - cell: ({ getValue }) => ( - ()} style={{ color: "var(--text-primary)" }}> - {getValue()} - + cell: ({ row }) => ( +
+ + {row.original.libelle1} + +
+ {row.original.workingStores.map((magasin) => ( +
+ {magasin.charAt(0).toUpperCase()} +
+ ))} +
+
), }, { diff --git a/src/types/grid.ts b/src/types/grid.ts index ac1f31f..e0f80bc 100644 --- a/src/types/grid.ts +++ b/src/types/grid.ts @@ -49,6 +49,7 @@ export interface ProductRow { tauxMarge: number; /** Combined performance score (0-10) */ score: number; + workingStores: string[]; aiRecommendation?: string | null; }