Files
CollectFlow/docs/plan-optimisation-webui.md
T
Claude 220ca2263a feat(ui): lot 3 — toutes les pages lisibles, cohérentes et en français clair
Chaque page reprend le socle du lot 2 : en-tête avec une phrase
d'explication, couleurs du thème (plus de pages figées en blanc), textes
de 12 px minimum, noms de magasins, infobulles sur le jargon, retours par
toast / confirmation, états vides et d'erreur expliqués.

- Accueil : CA, tickets et panier moyen d'hier vs N-1, par magasin, top 10
  avec lien vers la fiche produit.
- Révision d'assortiment : un seul endroit pour enregistrer et exporter
  (menu Export unique, mêmes droits pour tous), annulation confirmée, sens
  des gammes affiché partout, magasin choisi en un clic, colonnes nommées
  en clair, bandeau de progression au lieu de la fenêtre floue, droits lus
  côté serveur, ligne sélectionnée opaque sur les colonnes figées.
- Recherche produit, Stocks à surveiller (export FF inchangé), Meilleures
  ventes, Ventes par mois (dernier mois complet par défaut), Publicités,
  Commandes fournisseurs, Historique, Paramètres (onglets), Synchronisation
  et Connexion migrés sur DataTable / Tabs / Card.
- Le nom de l'utilisateur est transmis à la session (affiché dans
  l'en-tête).
- Composants devenus inutiles supprimés (anciennes fenêtres modales,
  export-dropdown, financial-cell, supplier-selection).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AcA129WzGTH6zxPHFBGWmn
2026-10-03 21:05:46 +00:00

24 KiB
Raw Blame History

Plan — CollectFlow : interface simple, lisible et rapide

Contexte

Analyse des 13 pages (+ layout, menu, en-tête, API, accès BDD). Constats principaux :

  • Lisibilité : plus de 160 textes entre 8,5 et 11 px. Libellés en MAJUSCULES grasses gris pâle, sous le seuil de contraste AA. Jargon partout (Init., PCB, PRMP, Franco, Sync Qlik, CODEIN…). Libellés mi-anglais, mi-français (Dashboard, Analytics, Snapshots).
  • Incohérence :
    • Thème : 5 pages sont figées en blanc (bg-gray-50) dans une application sombre. Les classes dark: suivent le réglage de l'OS, pas le bouton de thème.
    • Composants : 7 styles d'en-tête de page, 13 tableaux codés à la main, formateurs de nombres copiés dans 12 fichiers.
    • Vocabulaire : magasins nommés de 8 façons. « Réseau » désigne tantôt les ~270 magasins, tantôt nos 2.
  • Ergonomie :
    • La recherche de l'en-tête ne filtre que la Grille, même depuis les autres pages.
    • L'utilisateur « Michael » est codé en dur dans l'en-tête.
    • La Grille affiche deux boutons « Valider » et deux exports, avec des droits différents.
    • Chaque chargement de page bloque tout l'écran (modale plein écran), et aucune page d'erreur n'existe.
  • Lenteur :
    • Hors tableau de bord, aucune donnée n'est mise en cache, alors que les données FF ne changent qu'une fois par nuit.
    • Une Grille froide se reconstruit en 10 à 43 s. Ce calcul a lieu après chaque enregistrement, après chaque changement de magasin, et à chaque retour par le menu après un clic sur « Rafraîchir » : le paramètre _refresh reste collé à l'URL.
    • La Grille rend tout le tableau à chaque paquet de 150 lignes, donc un coût quadratique.
    • exceljs et jspdf sont chargés pour tous les utilisateurs.
    • Plusieurs requêtes SQL ne peuvent pas utiliser d'index : TRIM(codein), TO_CHAR(datmvt).
  • Bugs relevés :
    • Après un filtre, la sélection multiple de la Grille vise d'autres produits.
    • Le filtre « passées » de Publicités ne s'applique pas (clé passees dans le filtre, passee dans les libellés).
    • L'erreur de chargement des Snapshots n'est jamais affichée.
    • La carte KPI du tableau de bord répète sa valeur principale.

Choix validés : thème clair par défaut ; menu regroupé et renommé, avec Snapshots et Exports fusionnés en « Historique » ; livraison par lots, une PR par lot, sur la branche claude/keen-maxwell-hbtho6.

Conventions du dépôt :

  • préfixe rtk sur toutes les commandes ;
  • commits en français ;
  • plan de chaque lot rangé selon BMAD (Business / Model / API / Data, .agents/rules/general.md).

Lot 1 — Vitesse et bugs, sans changement visuel (PR 1)

Risque faible, gain immédiat.

Grille (client)

  • src/features/grid/components/grid-client.tsx :
    • retirer _refresh de la requête mémorisée dans activeGridQuery (l.86-91), puis le supprimer de l'URL après usage avec router.replace ;
    • sortir code1 et code2 des dépendances du chargement : le serveur ne les lit pas.
  • grid-client.tsx:152-156 : accumuler les paquets reçus et ne mettre à jour le store que toutes les ~250 ms, plus une fois à la fin, au lieu de reconstruire à chaque paquet.
  • heatmap-grid.tsx :
    • ajouter getRowId: r => r.codein (corrige le bug de sélection) ;
    • mémoriser MONTHS_12 sur sa clé texte (l.628) ;
    • retirer transition-all sur les lignes et le flou du bandeau d'en-tête.
  • export-dropdown.tsx :
    • exceljs, jspdf et jspdf-autotable passent en await import() dans les gestionnaires ;
    • le composant est chargé via next/dynamic ;
    • remplacer useGridStore() sans sélecteur (l.12).
  • Créer src/lib/format.ts : instances Intl.NumberFormat créées une seule fois, plus fmtEur, fmtQte, fmtPct, fmtDate. Les cellules de la Grille l'utilisent ; les autres pages au lot 3.

Grille (serveur) — src/features/grid/api/get-product-rows.ts

  • Lancer la requête des métriques Qlik et celle du dernier snapshot dans le même Promise.all que la phase 1 (l.130-146, 410, 416).
  • qlik-network-cache.ts : ne sélectionner que les colonnes utiles, sans le JSONB metricsByMonth.
  • Cache mémoire :
    • le borner (LRU d'environ 30 entrées) ;
    • garder la déduplication des calculs en cours même en forceRefresh ;
    • transmettre request.signal depuis src/app/api/grid/rows/route.ts.
  • Après un enregistrement, mettre à jour en place le codeGamme des lignes en cache au lieu d'invalider tout le cache (save-draft-changes.ts:73). Nouvelle fonction patchGridRowsCache() à côté de invalidateGridRowsCache().
  • Supprimer les console.log des chemins chauds : get-product-rows.ts (l.83, 123, 148, 433, 444, 622) et pg-ff-client.ts (l.99, 300-303, 431, 516).

Cache des données FF jusqu'à la synchro nocturne

  • Créer src/lib/ff-cache.ts : enveloppes unstable_cache étiquetées ff-data, durée 6 h, sur le modèle déjà en place dans dashboard/page.tsx:4-8. Elles couvrent :
    • les 3 requêtes de stock ;
    • le hit-parade (clé : dates) ;
    • les CA par fournisseur et par nomenclature (clé : mois et mode) ;
    • pgGetFournisseurs ;
    • getFournisseurs de la Grille ;
    • la dernière réception par fournisseur ;
    • pgGetCommandesAuto.
  • Publicités : fetch avec next: { revalidate, tags } au lieu de no-store.
  • À la fin du cycle nocturne (src/features/admin/api/sync-scheduler.ts) : invalider l'étiquette ff-data, puis préchauffer le tableau de bord et le stock.
  • Points à vérifier :
    • la signature de revalidateTag en Next 16 ;
    • que les valeurs mises en cache passent en JSON (les Date deviennent des chaînes).

Base de données et API

  • src/db/index.ts :
    • pool rangé dans globalThis ;
    • options: "-c statement_timeout=60000 -c max_parallel_workers_per_gather=0" et application_name.
    • pgNoParallel (pg-ff-client.ts:25-33) n'a alors plus besoin de BEGIN/SET/COMMIT : 3 allers-retours de moins par requête.
  • AbortSignal.timeout sur tous les appels à l'API FF : api-ff-client.ts, publicites/page.tsx, appels du tableau de bord.
  • Index des tables applicatives, dans scripts/db-init.js et src/db/schema.ts :
    • session_snapshots(code_fournisseur, created_at DESC) ;
    • session_snapshots(user_id, type, created_at DESC).
  • Divers :
    • Commandes auto : supprimer le double rechargement, en retirant router.refresh() puisque revalidatePath est déjà appelé (cadencier-client.tsx:172-177).
    • Synchronisation : PATCH envoyé à la sortie du champ, plus à chaque frappe (admin/synchronisation/client.tsx:209-237).
    • Paramètres : un seul appel à getSavedDatabaseConfig.
    • log-capture.ts : sortie immédiate si aucune capture n'est ouverte.

Bugs fonctionnels

  • Publicités : aligner la clé passees/passee (publicites/client.tsx:141, 256).
  • Historique : afficher l'état d'erreur (snapshot-list.tsx:133).
  • Tableau de bord : la carte KPI répète sa valeur (dashboard/page.tsx:183-185).
  • Barre de résumé de la Grille : filters.magasin n'est jamais renseigné, donc les snapshots enregistrent toujours « TOTAL » (floating-summary-bar.tsx:104).

Lot 2 — Socle visuel, menu et vocabulaire (PR 2)

Thème et typographie — src/app/globals.css, theme-provider.tsx

  • defaultTheme="light".
  • Ajouter @custom-variant dark (&:where(.dark, .dark *)); pour que les classes dark: suivent le bouton de thème.
  • Ajouter @import "tw-animate-css";.
  • Faire correspondre les jetons shadcn (--color-background, --color-muted-foreground, --color-accent…) aux variables de l'application.
  • Contraste AA (≥ 4,5:1) pour --text-muted en clair et en sombre.
  • Échelle typographique :
    • rien sous 12 px ;
    • cellules de tableau à 13-14 px ;
    • libellés en casse normale, sans MAJUSCULES font-black tracking-widest.
  • Une seule famille de boutons (fusion de .apple-btn-* et .btn-action-*) ; définir ou retirer les classes inexistantes (apple-btn, --border-subtle, --surface-tertiary).

Composants partagés — dans src/components/ui/

  • PageHeader : titre, phrase d'explication, actions.
  • Card / Section, StatCard (KPI avec écart N-1).
  • Badge, DeltaBadge, StoreBadge, GammeBadge.
  • Tabs, avec l'onglet actif dans l'URL.
  • Select, SearchInput.
  • EmptyState, ErrorState, Skeleton.
  • ConfirmDialog sur ui/dialog. Il remplace window.confirm, alert() et les 3 modales faites à la main.
  • Notifications discrètes (sonner, standard shadcn) à la place de SuccessModal pour les succès.
  • DataTable<T> : généralisé depuis StockTable<T> (stock-negatif/client.tsx:168-384), qui gère déjà colonnes, tri, recherche, pagination et export. Ajouter en-tête collant, état vide, et infobulle sur les en-têtes.

Sources uniques

  • src/lib/magasins.ts : 292 → « Frouard (Nancy) », 579 → « Houdemont », TOTAL → « Nos 2 magasins ». Il remplace SITE_NAMES (dashboard), months.ts:91-94 et les 3 copies de SITES.
  • src/lib/glossaire.ts : libellé clair et infobulle pour chaque terme métier. Exemples :
    • Gamme A–Z avec son sens, à reprendre de bulk-action-toolbar.tsx:8-12 ;
    • Réseau = environ NB_MAGASINS_RESEAU magasins (network-trend.ts:13) ;
    • Franco, PCB, PRMP, GTIN, N-1, Init.
  • Règles de vocabulaire :
    • « Réseau » = les ~270 magasins uniquement ; nos 2 magasins = « Nos magasins » ;
    • Famille et Sous-famille identiques partout ;
    • vouvoiement partout.
  • Seuils de marge : une seule constante, au lieu de 25/15 % sur le tableau de bord et 40/25 % sur la fiche.

Menu — src/components/shared/sidebar.tsx

  • Menu déplié par défaut, en groupes :
    • Au quotidien : Accueil, Révision d'assortiment, Recherche produit, Stocks à surveiller, Commandes fournisseurs.
    • Analyses : Meilleures ventes, Ventes par mois, Publicités.
    • Historique : nouvelle page /historique qui fusionne Snapshots et Exports, avec un onglet par type ; l'onglet Exports est réservé aux admins. /snapshots et /exports y redirigent.
    • Administration : Synchronisation, Paramètres.
  • Icônes distinctes pour chaque entrée.
  • src/middleware.ts : protéger aussi côté serveur la partie admin de l'historique.

En-tête et coquille

  • header.tsx :
    • retirer la recherche globale, qui ne filtrait que la Grille ; la Grille garde la sienne ;
    • afficher le vrai utilisateur ;
    • réglages personnels (thème, densité de la Grille) dans un menu utilisateur accessible à tous.
  • Remplacer les 11 loading.tsx (modale plein écran) par des squelettes de page non bloquants.
  • Ajouter app/(dashboard)/error.tsx (message clair et bouton « Réessayer ») et not-found.tsx.
  • Titre de l'application en français (app/layout.tsx).

Lot 3 — Migration des pages, une à une (PR 3, découpable en 3a/3b)

Règle commune à chaque page :

  • PageHeader avec une phrase « à quoi sert cette page » ;
  • DataTable et composants partagés, jetons du thème ;
  • retrait des enveloppes min-h-screen bg-gray-50 p-6 (double marge) ;
  • noms de magasins depuis magasins.ts, infobulles depuis glossaire.ts ;
  • états vide et erreur explicites.
Page Changements spécifiques
Accueil (dashboard) Cartes StatCard. Section « Hit Parade » renommée « Top 10 d'hier ». Chaque produit mène à sa fiche. Couleurs codées en dur remplacées par les jetons. Message d'erreur compréhensible, sans trace technique.
Révision d'assortiment (grid) Barre d'outils : un seul « Enregistrer » (dans la barre de résumé), un seul menu Export, un seul « Actualiser », avec les mêmes droits partout (isAdmin calculé côté serveur via auth() dans grid/page.tsx). Les actions rares (Non travaillés, Vues, Colonnes, Synchro Qlik) passent dans « Plus d'options ». Libellés : gammes affichées avec leur sens (« A — Cœur ») ; menu Colonnes avec de vrais libellés, pas les identifiants ; « / 270 » remplacé par NB_MAGASINS_RESEAU. Chargement : la superposition floue est remplacée par une barre de progression légère. Accueil fournisseur : une phrase-guide.
Recherche produit (produits) Tailles ≥ 12 px dans fiche.tsx, results.tsx et opportunites.tsx. Infobulles PCB, PRMP, GTIN. Famille et Sous-famille alignées. Message de chargement sans « Qlik Sense ».
Meilleures ventes (hit-parade) DataTable paginé ; état vide ; « % Marge » triable. Export via exceljs en import dynamique, ce qui permet de retirer xlsx.
Stocks à surveiller (stock-negatif) Base du DataTable. En-têtes Excel lisibles (« Code article » au lieu de CODEIN, etc.). Noms de magasins.
Publicités Le tri et le filtre magasin s'appliquent à l'ensemble des données, pas seulement à la page de 50. Infobulles « Taux de sortie » et « % CA total ».
Commandes fournisseurs (commandes-auto) Onglets dans l'URL. Confirmation avant suppression. Erreurs affichées en notification. Retour visuel à l'enregistrement d'une fréquence. Infobulle « Franco ».
Ventes par mois (analytics) Dernier mois complet par défaut. Mois en clair (« oct. 2026 vs oct. 2025 »). Recherche, tri, export, état vide.
Historique Page fusionnée (lot 2), libellés de session lisibles.
Paramètres Onglets Connexions / Utilisateurs / Clés API / Journal. Un bouton « Enregistrer » par section (le bouton collant trompeur disparaît). Thème et densité déplacés dans le menu utilisateur.
Synchronisation Unités affichées (« jours », « heures »). Filtres en casse normale. Libellés sans jargon (« Tour bouclé » → « Tous les fournisseurs traités »).
Connexion Libellés lisibles. Slogan « Propulsée par l'IA » retiré. Rendu clair.

Lot 4 — Optimisations lourdes (PR 4)

Grille servie depuis l'instantané grid_rows (le plus gros gain)

  • La table est déjà remplie chaque nuit avec exactement les ProductRow du magasin TOTAL : persistGridSnapshot dans get-product-rows.ts, et sync-scheduler.ts:173.
  • Ajouter readGridRowsPayload(codeFournisseur) dans src/lib/grid-store.ts, qui lit payload::text et l'envoie tel quel, sans analyse ni réencodage.
  • Superposer ensuite la gamme INIT à jour (refreshGammeInit) et les gammes du dernier snapshot.
  • Si l'instantané est trop ancien, relancer le calcul en arrière-plan (stale-while-revalidate).
  • Ouverture à froid : de 10-43 s à environ 1-2 s.

Changement de magasin instantané

  • Toujours charger TOTAL : les lignes portent déjà le détail par magasin.
  • Bascule côté client avec setActiveMagasin et history.replaceState, sans aller-retour serveur (grid-filter-bar.tsx:100-105).
  • Le rattrapage via l'API FF (reconcileSelectedStoreFromMensuelApi) devient une petite requête séparée, /api/grid/rows/store-patch, appliquée quand elle arrive.

Rendu de la Grille

  • React.memo sur HeatmapGrid, GridFilterBar et FloatingSummaryBar.
  • useSaveDrafts déplacé dans le bouton Enregistrer.
  • En-tête de tableau mémorisé ; useFlushSync: false sur le virtualiseur.
  • Largeurs de colonnes en variables CSS.
  • persist de zustand sans les lignes, et limité en fréquence.
  • Tendance réseau et texte de recherche calculés une seule fois par ligne à la réception ; le filtre global ne porte plus que sur ce texte.
  • Lignes gardées en mémoire au retour sur la Grille (même fournisseur, moins de 10 min) : pas de setRows([]).

SQL sans index utilisable

À valider par EXPLAIN ANALYZE sur la base réelle avant fusion : le schéma des tables FF n'est pas dans le dépôt.

  • Fiche produit (get-produit-fiche.ts, pg-ff-client.ts:1737-2045) : résoudre no_id une fois, puis filtrer sur les clés ; lecture du cache Qlik dans le même Promise.all ; supprimer la double recherche du chemin ?cc=.
  • Ventes par mois (pg-ff-client.ts:827-885) : plages de dates au lieu de TO_CHAR.
  • Stocks : réécrire pgGetStockSansVente (l.1198-1235) en agrégeant d'abord par (article, site). Charger seulement l'onglet actif.
  • Meilleures ventes (l.922-991) : jointure sur artnoid / no_id.
  • Commandes : la dernière réception est limitée aux fournisseurs du cadencier et à une fenêtre de dates ; appels « franco » à concurrence bornée, avec délai maximal ; un Suspense par onglet.
  • Tableau de bord (l.623-797) : une seule requête SQL sur cube_stock, en réutilisant pgGetStockForCodeins, au lieu de ~60 appels HTTP. Cache par date, préchauffé après la synchro.
  • /api/v1/grid : pagination par curseur (keyset) au lieu d'OFFSET ; COUNT une seule fois.
  • api-auth.ts : last_used_at mis à jour au plus une fois par minute.

Nettoyage

  • Dépendances inutilisées : @tanstack/react-query, @google/genai, ai, react-markdown, et xlsx après le lot 3.
  • Code mort : supplier-selection.tsx, financial-cell.tsx, ui/textarea.tsx, getGridData et getAvailableNomenclature (grid/actions.ts).

Hors périmètre, à signaler

src/features/settings/actions.ts ne vérifie aucun droit. Tout utilisateur connecté peut lire l'URL de la base, le mot de passe Qlik et les clés IA, et réécrire la configuration. Ce point est proposé en tâche séparée.

Vérification (à chaque lot)

  1. Contrôles automatiques :
    • rtk tsc : aucune nouvelle erreur par rapport à tsc_errors.log, car ignoreBuildErrors est activé.
    • rtk lint.
    • rtk next build : comparer le JS de premier chargement de /grid avant et après (lot 1 : − exceljs / jspdf).
  2. Visuel :
    • rtk npm run dev, puis Playwright (Chromium préinstallé) pour capturer chaque page en clair et en sombre, en 1920 et 1366 px de large.
    • Un script dans la page signale tout texte calculé sous 12 px et tout élément bg-white en mode sombre.
  3. Vitesse :
    • mesurer le temps de réponse de /api/grid/rows (froid, chaud, après enregistrement), et de /stock-negatif et /hit-parade au 1er puis au 2e chargement ;
    • objectif : 2e visite en moins de 200 ms, Grille froide en moins de 2 s au lot 4.
  4. Recette manuelle sur l'environnement réel (ce conteneur n'a pas accès à PostgreSQL FF, à l'API FF ni à Qlik) :
    • gros fournisseur ;
    • changement de magasin instantané ;
    • enregistrer puis rouvrir sans attente ;
    • sélection multiple après filtre, sur les bons produits ;
    • après « Actualiser », un retour par le menu ne force plus le recalcul ;
    • exports Excel identiques.

Suivi — Lot 1 réalisé

Écarts par rapport au plan, décidés pendant l'implémentation :

  • Cache FF : un cache mémoire dédié (src/lib/ff-cache.ts) remplace unstable_cache. Deux raisons :

    • unstable_cache sérialise en JSON, ce qui casse les Map et les Date ;
    • revalidateTag ne peut pas être appelé depuis la synchro nocturne, qui tourne hors requête.

    Fonctionnement :

    • durée de vie de 30 min (FF_CACHE_TTL_S), et 10 min pour les publicités ;
    • les appels simultanés partagent une seule requête ;
    • les erreurs ne sont jamais gardées ;
    • le cache est vidé en fin de synchro nocturne.
  • statement_timeout : activé seulement si PG_STATEMENT_TIMEOUT_MS est défini, pour ne pas couper les longs calculs nocturnes.

  • Colonnes Qlik : non modifié. metricsByMonth sert à la courbe « nombre de magasins vendeurs ».

  • Carte KPI du tableau de bord : non modifiée. La ligne « Hier / N-1 » est une comparaison voulue, pas un doublon.

  • Préchauffage après la synchro : reporté. L'invalidation suffit, la première visite recharge.

Suivi — Lot 2 réalisé

  • Thème :
    • clair par défaut ; les classes dark: suivent le bouton de thème ;
    • animations (tw-animate-css) réellement importées ;
    • couleurs shadcn branchées sur les variables ;
    • libellés gris au contraste AA dans les deux thèmes ;
    • fond plein pour le menu et l'en-tête (le flou ne servait à rien).
  • Composants partagés, dans src/components/ui/ :
    • PageHeader, Card, Field, StatCard ;
    • Badge, DeltaBadge, StoreBadge, GammeBadge ;
    • Tabs, Segmented, useUrlTab ;
    • Select, SearchInput, Input ;
    • EmptyState, ErrorState, Skeleton, PageSkeleton ;
    • Tooltip, Terme (glossaire) ;
    • toast et confirmer (feedback.tsx) ;
    • Pagination, DataTable.
  • Sources uniques :
    • lib/magasins.ts (noms des magasins) ;
    • lib/gammes.ts (A à Z, avec leur sens et leurs couleurs) ;
    • lib/glossaire.ts ;
    • lib/marge.ts (seuils 40 / 25 %, ceux de la Grille) ;
    • lib/export-excel.ts (export unique, via exceljs).
  • Navigation :
    • menu groupé (Au quotidien / Analyses / Suivi / Administration), déplié par défaut, libellés en français ;
    • en-tête : nom de la page, thème, menu du compte (vrai nom, hauteur des lignes de la Grille pour tous, déconnexion) ;
    • la recherche globale, qui ne filtrait que la Grille, est retirée.
  • Historique : nouvelle page /historique (sessions et exports), et redirection des anciennes adresses /snapshots et /exports. Les deux onglets sont visibles par tous : chacun n'y voit que ses propres enregistrements.
  • Chargements et erreurs :
    • squelettes de page au lieu de la fenêtre plein écran ;
    • pages d'erreur (error.tsx) et « page introuvable » en français ;
    • après connexion, arrivée sur l'Accueil, comme pour /.
  • Écart : les en-têtes Excel de la page Stocks (CODEIN, GENCODE, QTE, CODMV, avec les codes 503 / 412) ne seront pas renommés. C'est vraisemblablement un format d'import de régularisation dans FF.

Suivi — Lot 3 réalisé

Toutes les pages utilisent désormais :

  • PageHeader avec une phrase d'explication ;
  • les couleurs du thème (plus de pages figées en blanc) ;
  • des textes d'au moins 12 px ;
  • les noms de magasins, toast et confirmer.

Vérifié dans un navigateur : sur les pages visitées, en clair et en sombre, aucun texte n'est sous 12 px.

Page Changements principaux
Accueil Cartes « Nos 2 magasins » : CA, tickets et panier moyen, avec l'évolution N-1. Une carte par magasin. Top 10 lisible, avec un lien vers la fiche produit.
Révision d'assortiment Barre du bas : seul endroit pour enregistrer et exporter, avec les mêmes droits pour tous. Le menu Export regroupe les deux anciens. Bouton « Annuler » avec confirmation. Filtres : sens des gammes affiché (A Cœur…), magasin choisi en un clic, menu « Colonnes » en clair. Chargement : bandeau de progression au lieu de la fenêtre floue. Droits : lus côté serveur.
Recherche produit Fiche lisible (Secteur / Rayon / Famille), infobulles PCB, PRMP, code-barres, réseau. Plus de mention technique de Qlik.
Stocks à surveiller DataTable avec trois onglets et une explication pour chacun. Export Excel inchangé (format d'import FF).
Meilleures ventes DataTable avec un tri sur toutes les colonnes et une ligne de totaux. Export via exceljs (xlsx n'est plus utilisé).
Ventes par mois Dernier mois complet par défaut, mois en clair, bascule Fournisseur / Famille, recherche, tri, export.
Publicités Onglets, statuts lisibles, part du CA calculée en pied de tableau. Le tri ne porte que sur la page affichée, et la page le signale.
Commandes fournisseurs Onglets dans l'URL, confirmation avant suppression, retours de toutes les actions.
Historique « Reprendre dans la Grille », chiffres visibles, états vides et d'erreur.
Paramètres Onglets Connexions / Utilisateurs / Accès API / Journal serveur, un bouton Enregistrer par section.
Synchronisation Vocabulaire clair, unités affichées, résumé en indicateurs.
Connexion Libellés lisibles, fond sobre.

Autres changements :

  • Composants retirés : SuccessModal, ConfirmModal, ChangePasswordModal, LoadingModal, export-dropdown, financial-cell et supplier-selection, qui n'étaient plus utilisés.
  • Correction : le nom de l'utilisateur est maintenant transmis à la session, et l'en-tête l'affiche.