Commit Graph
8 Commits
Author SHA1 Message Date
Claude 150baeef9a Carte tendance : hauteur figee, bascule Graphique/Tableau, cellules copiables
1. LA CARTE CHANGEAIT DE TAILLE PENDANT LE SURVOL.
   La lecture du mois vivait sous le graphique, en HTML : selon la longueur
   des valeurs elle passait sur deux lignes et la carte sautait. Elle est
   desormais dessinee DANS le SVG, dont la geometrie est figee par le
   viewBox — infobulle ancree au reticule, qui bascule a gauche quand elle
   deborderait a droite.

   Deux causes residuelles trouvees a la mesure, pas a la lecture :
   - le SVG etait dimensionne en pourcentage, donc sa hauteur dependait de
     la largeur : il porte maintenant une hauteur en pixels ;
   - un SVG en ligne traine l'espace sous la ligne de base (6 px mesures),
     ce qui decalait la vue tableau : `display:block`.

   Verifie en pilotant le composant reellement monte : 382,5 px au repos,
   sur quatre positions de survol et dans les deux vues, en clair comme en
   sombre.

2. LES VALEURS MOIS PAR MOIS SANS AGRANDIR LA CARTE.
   Le deplaint aurait fait grandir la carte a l'ouverture — exactement ce
   qu'on venait de corriger. Le tableau occupe donc la place du graphique
   dans un panneau de MEME HAUTEUR et defile a l'interieur.

3. REFERENCE ET EAN COPIABLES, comme le code interne.
   La copie etait ecrite en ligne dans la colonne « Code interne », avec un
   `eslint-disable react-hooks/rules-of-hooks` : un useState dans le corps
   d'un cell() de TanStack n'est pas rendu au meme endroit d'un rendu a
   l'autre. Extraite en composant, la derogation disparait et les trois
   colonnes partagent le meme comportement.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfqUihgixw4K1AmJhizWiu
2026-08-01 10:40:37 +00:00
Claude 5ad222431d Carte tendance : refonte du graphique selon les regles data-viz
Petits multiples empiles sur un axe des mois commun, palette validee par
script, etiquetage selectif, survol et vue tableau.

FORME. Trois mesures d'ordres de grandeur differents (qte/magasin,
volumes, magasins) ne peuvent pas partager un cadre : il faudrait deux ou
trois echelles verticales dont l'alignement serait arbitraire, ce qui
inventerait des correlations absentes des donnees. Trois facettes, une
echelle chacune, le meme axe horizontal. La facette PRINCIPALE est la
qte/magasin.

COULEUR. Slots 1-3 de la palette categorielle de reference, en variables
CSS, avec un jeu de pas CHOISI pour le fond sombre (pas un eclaircissement
automatique). Palette passee au validateur dans les deux modes, toutes
paires : bande de clarte, plancher de chroma, separation daltonienne
(pire paire ΔE 9,2 clair / 9,4 sombre) et contraste. En mode clair l'aqua
tombe a 2,82:1 — la regle de relief s'applique, d'ou etiquettes visibles
ET vue tableau, toutes deux presentes.

La teinte de tendance (vert/rouge/gris) ne vit plus que dans la tuile de
verdict, avec fleche et libelle : un etat ne se signale jamais par la
couleur seule, et la meme couleur ne veut pas dire deux choses.

LISIBILITE. Etiquetage selectif — l'extreme et le dernier mois, pas les 36
valeurs. Le texte ne porte jamais la couleur d'une serie (illisible en
teinte claire) : l'identite vient du repere pose a cote. Filets de base
pleins et discrets, jamais pointilles. Reticule au survol reliant les
trois facettes au meme mois, et vue tableau depliable : aucune valeur
n'est accessible au seul survol.

En-tete refait en tuiles : verdict, qte/magasin du dernier mois, magasins
vendeurs. Chiffres proportionnels sur les valeurs mises en avant,
tabulaires seulement dans le tableau ou les colonnes s'alignent.

Rendu verifie en image dans les deux themes avant livraison : la version
precedente faisait passer l'etiquette du pic par-dessus le titre de la
facette — elle bascule desormais sous le point quand elle toucherait le
bandeau.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfqUihgixw4K1AmJhizWiu
2026-08-01 09:58:31 +00:00
Claude eaac945345 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
2026-08-01 09:50:41 +00:00
Claude e6e79dfe06 Carte tendance : desencombrer le graphique, il etait illisible
La densite etait le vrai probleme. La version precedente imprimait les 24
valeurs (12 quantites + 12 magasins) et titrait chaque bande DANS le SVG.
Resultat sur ecran : les titres passaient par-dessus les courbes, les
nombres des extremites etaient coupes par le bord du cadre, et les
etiquettes des magasins se collaient au trait pointille.

- Les titres sortent du dessin : legende HTML au-dessus, plus aucun
  recouvrement possible quelle que soit la forme des courbes.
- La bande des magasins ne porte plus que sa DERNIERE valeur ; son niveau
  se lit a la forme et au maximum rappele dans la legende.
- Les etiquettes des bords sont ancrees vers l'interieur (start / end au
  lieu de middle) : plus rien n'est tronque.
- Chaque mois expose une infobulle native avec les valeurs exactes, ce qui
  rend inutile d'imprimer tous les nombres.
- Cadre elargi (520 de large, marge haute pour les etiquettes du sommet) et
  modale passee en max-w-xl.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfqUihgixw4K1AmJhizWiu
2026-08-01 09:46:15 +00:00
Claude bf6fef6a14 Carte tendance : deux bandes separees, indicateur lisible, courbe magasins reparee
Trois defauts signales, trois causes distinctes.

1. LA COURBE DES MAGASINS N'APPARAISSAIT PRESQUE JAMAIS.
   L'extracteur ecrivait `{ qte: 0 }` sur les mois sans vente, sans
   `nbMag`. La serie exigeant les 12 mois, elle disparaissait des qu'un
   produit avait un mois creux — c'est-a-dire presque toujours. Un mois
   PRESENT dans le detail est un mois extrait : s'il n'a pas de nbMag,
   c'est que personne n'a vendu, donc zero. Corrige a la source ET a la
   lecture, pour que les caches deja ecrits repartent sans re-sync.

2. LES DEUX COURBES ETAIENT ILLISIBLES SUPERPOSEES.
   Deux echelles independantes placaient la courbe des magasins AU-DESSUS
   de celle des quantites : cela se lit spontanement comme « il y a plus
   de magasins que de ventes », un contresens. Les series sont desormais
   tracees dans DEUX BANDES distinctes alignees sur le meme axe des mois.
   On compare la forme de l'une avec la forme de l'autre, sans jamais
   suggerer un rapport de grandeur.

3. LES POURCENTAGES N'AVAIENT PAS DE SENS.
   La pente d'une regression rapportee a la moyenne donnait « -124 % »
   (une baisse ne peut pas depasser -100 %), « +1 062 % » pour 2 unites
   vendues une fois dans l'annee, et surtout « +508 % » sur des dizaines
   de lignes d'affilee — la signature arithmetique d'une serie nulle
   partout sauf le dernier mois, ou le rapport ne depend meme plus des
   quantites. On compare maintenant deux moyennes de 4 mois : variation
   qui se lit comme telle, plancher a -100 %, insensible au bruit d'un
   mois isole. Sans base de comparaison, aucun pourcentage n'est
   invente — le produit est marque « Nouveau », et le tri le remonte en
   tete au lieu de l'enterrer.

Le graphique accepte aussi des quantites negatives (retours superieurs
aux ventes sur un mois) : l'echelle part du minimum reel, le point ne
sort plus du cadre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfqUihgixw4K1AmJhizWiu
2026-08-01 09:41:25 +00:00
Claude 28fe78a646 Carte tendance : garantir que la courbe magasins se distingue des quantites
La courbe des quantites prend la couleur de la TENDANCE : vert, rouge, ou
gris-bleu (#94a3b8) quand elle est stable. L'indigo des magasins se
detache du vert et du rouge, mais il est trop proche du gris-bleu — or
« stable » est le cas le plus frequent.

storesColorFor() bascule sur l'ambre (#f59e0b) dans ce cas precis : gris
froid contre orange chaud, impossible a confondre.

Les deux series se distinguent desormais par TROIS canaux, pas seulement
la couleur — qui ne suffit ni en impression noir et blanc, ni pour un
daltonien :
- teinte garantie differente,
- trait pointille contre trait plein,
- points evides contre points pleins.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfqUihgixw4K1AmJhizWiu
2026-08-01 09:10:31 +00:00
Claude abc704a363 Carte tendance : deuxieme courbe du nombre de magasins vendeurs
Une quantite qui monte parce que le produit est diffuse dans plus de
magasins ne raconte pas la meme histoire qu'une quantite qui monte a
diffusion constante. La courbe des quantites seule ne permettait pas de
distinguer un succes produit d'un simple elargissement.

- computeStoresSeries() : serie « magasins vendeurs » sur la MEME fenetre
  12 mois que la tendance, avec la meme regle du tout ou rien — les 12
  cles doivent etre presentes, sinon null. Un mois non extrait ne doit
  pas se lire comme « zero magasin », ce qui simulerait un arret de
  diffusion.
- nbMagReseauByMonth remonte du cache (metricsByMonth, deja stocke par la
  sync) jusqu'a la ligne de grille.
- NetworkLineChart accepte une serie `stores` optionnelle.

DEUX ECHELLES INDEPENDANTES : les quantites se comptent en milliers, les
magasins plafonnent a ~270. Sur une echelle commune la courbe des
magasins serait ecrasee sur l'axe et ne montrerait rien. Chaque serie est
normalisee sur son propre maximum, rappele dans la legende — c'est la
FORME des deux courbes qu'on compare, pas leurs hauteurs. La courbe des
magasins est pointillee et indigo, pour ne jamais entrer en collision
avec le vert/rouge/gris de la tendance.

Branchee sur la carte de la Grille et sur celle de la fiche produit. La
sparkline de la colonne reste a une seule courbe : 46x18 px, deux traces
y seraient illisibles.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfqUihgixw4K1AmJhizWiu
2026-08-01 09:07:39 +00:00
Claude eeaae09282 feat(produits): page de recherche produit + fiche 360° local/réseau
Nouvelle page /produits : recherche par libellé ou par code centrale, puis
fiche complète d'un produit — identité, prix, stock par magasin, ventes sur
12 mois glissants, et l'intégralité des métriques réseau Qlik.

Jusqu'ici les données produit n'étaient accessibles que par fournisseur
(la Grille) ou par classement (Hit Parade) : aucune route n'acceptait un
codein. Les données réseau Qlik n'étaient visibles que dans six colonnes
de la grille d'arbitrage.

Deux analyses ajoutées :
- comparatif local vs réseau, ramené au magasin moyen (indice de
  performance sur les quantités, seule base commune fiable — le CA local
  est TTC et la base du CA Qlik n'est pas documentée, donc indicatif) ;
- opportunités de la même famille : références que le réseau vend bien et
  que nous vendons peu, écart calculé par magasin.

Détail mensuel réseau enrichi : le cube Qlik renvoyait déjà CA, nb de
magasins, CA/magasin et marge % par (article, mois) mais seule la quantité
était conservée. Ces mesures sont désormais stockées dans une nouvelle
colonne metrics_by_month, sans requête Qlik supplémentaire. qte_by_month
reste inchangée — la colonne « Tendance / Réseau » de la Grille en dépend.

L'extraction Qlik peut désormais cibler un seul code centrale
(/api/qlik/sync?codeCentrale=…), ouverte à tout utilisateur connecté et
plafonnée à 3 extractions simultanées ; le mode fournisseur reste
réservé aux admins.

Refactor sans changement de comportement : les helpers de mois, le calcul
de tendance et les graphes SVG quittent heatmap-grid.tsx pour des modules
partagés, et le polling des jobs Qlik devient le hook useQlikSyncJob,
réutilisé par la Grille et par la fiche.

Les calculs de ventes reprennent le SQL canonique de la Grille (genremvt=3
avec quantités niées pour déduire les retours, stock de fin de mois pris
sur le dernier mouvement, report sur les mois sans mouvement), afin que
les chiffres des deux pages soient identiques par construction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XR8cMbsdjXWChX8seL9NCP
2026-07-27 14:26:08 +00:00