Tendance : la calculer sur la QTE PAR MAGASIN, pas sur le volume brut

C'est le bon indicateur, et le volume brut ne pouvait pas le remplacer :
il confond la performance du produit et sa diffusion. Un produit monte en
volume des qu'on le reference ailleurs, sans mieux se vendre nulle part ;
un produit retire de 60 magasins s'effondre en volume tout en performant
mieux la ou il reste. Seule la qte/magasin repond a « ce produit est-il
tendance ».

- computeNetworkTrend prend le nombre de magasins par mois et calcule
  l'evolution sur qte/magasins vendeurs. Sans cette serie, repli sur le
  volume brut et `surQteParMagasin` a false, pour que l'UI le dise.
- La serie remonte jusqu'a tous les appelants : grille, fiche produit et
  recherche produits (nbMagByMonth ajoute au resultat de recherche).
- La carte affiche desormais trois bandes empilees sur le meme axe :
  qte/magasin en PRINCIPALE (couleur de la tendance, aire pleine),
  quantites vendues et magasins vendeurs en contexte. La teinte
  « tendance » ne designe plus qu'une seule chose dans le graphique ; les
  bandes de contexte ont des couleurs fixes.
- Le pourcentage de la carte precise « (qte/magasin) » : un chiffre de
  tendance sans son assiette se prete a tous les malentendus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfqUihgixw4K1AmJhizWiu
This commit is contained in:
Claude committed 2026-08-01 09:50:41 +00:00
1 parent e6e79dfe06
commit eaac945345
7 files changed
+234 -163

No files matched your search

+10 -5
View File
@@ -148,9 +148,8 @@ export function ProduitFicheView({ fiche, backQuery }: { fiche: ProduitFiche; ba
const serie = magasin === "TOTAL" ? fiche.mensuelTotal : (fiche.mensuelParSite[magasin] ?? {});
const totauxAffiches = magasin === "TOTAL" ? fiche.totaux : (fiche.totauxParSite[magasin] ?? { qte: 0, ca: 0, marge: 0, tauxMarge: 0 });
const trend = useMemo(() => computeNetworkTrend(reseau?.qteByMonth ?? null), [reseau]);
/** Nombre de magasins vendeurs par mois — deuxième courbe de la carte tendance. */
const magasinsParMois = useMemo(() => {
/** Magasins vendeurs par mois — dénominateur de la tendance ET 3e bande du graphique. */
const nbMagByMonth = useMemo(() => {
const detailMensuel = reseau?.metricsByMonth;
if (!detailMensuel) return null;
const parMois: Record<string, number> = {};
@@ -161,8 +160,13 @@ export function ProduitFicheView({ fiche, backQuery }: { fiche: ProduitFiche; ba
const nb = Number(mesures?.nbMag);
parMois[mois] = Number.isFinite(nb) ? nb : 0;
}
return computeStoresSeries(parMois);
return parMois;
}, [reseau]);
const trend = useMemo(
() => computeNetworkTrend(reseau?.qteByMonth ?? null, nbMagByMonth),
[reseau, nbMagByMonth],
);
const magasinsParMois = useMemo(() => computeStoresSeries(nbMagByMonth), [nbMagByMonth]);
const comparatif = useMemo(() => computeComparatif(fiche), [fiche]);
const verdict = indiceVerdict(comparatif.indiceQte);
@@ -292,13 +296,14 @@ export function ProduitFicheView({ fiche, backQuery }: { fiche: ProduitFiche; ba
{trend.hasData && (
<div className="mt-3">
<div className="text-[10px] font-semibold uppercase tracking-wide mb-1" style={{ color: "var(--text-muted)" }}>
Quantités vendues réseau, mois par mois
Tendance réseau — la qté/magasin d&apos;abord, les volumes en contexte
</div>
<NetworkLineChart
labels={trend.labels}
values={trend.values}
color={TREND_COLOR[trend.direction]}
stores={magasinsParMois?.values}
perStore={trend.perStore}
/>
</div>
)}
+1 -1
View File
@@ -313,7 +313,7 @@ function ResultTable({ rows, query }: { rows: ProduitRechercheRow[]; query: stri
</thead>
<tbody>
{rows.map((r) => {
const trend = computeNetworkTrend(r.qteByMonth);
const trend = computeNetworkTrend(r.qteByMonth, r.nbMagByMonth);
const href = r.codein
? `/produits?codein=${encodeURIComponent(r.codein)}&q=${encodeURIComponent(query)}`
: `/produits?cc=${encodeURIComponent(r.codeCentrale)}&q=${encodeURIComponent(query)}`;