# Plan d'optimisation du webUI LogiFlow *État au 3 octobre 2026. Annexe : les constats détaillés de l'audit, dans [`docs/audit-webui/`](audit-webui/).* **Sommaire** : [Résumé](#résumé) · [Comment lire ce plan](#comment-lire-ce-plan) · [Feuille de route](#feuille-de-route) · [Décisions produit à prendre](#décisions-produit-à-prendre) · [P1 — Principes, navigation et design system](#p1--principes-navigation-et-design-system) · [P2 — Plan page par page](#p2--plan-page-par-page) · [P3 — Bugs et dette technique](#p3--bugs-et-dette-technique) · [P4 — Performance](#p4--performance) ## Résumé - **Constat.** L'audit de l'interface a vérifié 620 constats, dont 107 de gravité haute. Trois faiblesses dominent : des écrans qui affichent de fausses informations (« 0 » pendant le chargement, une panne présentée comme une liste vide, les données d'un autre magasin) ; une application qui ne parle pas le même langage sur PC et sur téléphone (mots, couleurs, droits, fonctions absentes ou en panne) ; une grande lenteur, parce que chaque écran téléchargeait bien plus que ce qu'il affiche. - **Déjà fait dans cette branche** (lot 1 de performance, pas encore en production, sans changement visible pour les équipes) : JavaScript avant l'écran de connexion 1,6 Mo → 180 Ko ; connexion → accueil 5,9 s → 0,16 s ; données reçues pour une visite des 23 pages 1,5 Go → 0,8 Mo ; appels en double 82 → 0 ; requêtes SQL −56 % ; mémoire du serveur 1,4 Go → 325 Mo ; plus aucune empreinte de mot de passe dans les réponses mesurées. Près de la moitié des constats graves (49 sur 107, surtout de performance) sont ainsi réglés en tout ou en partie. - **Trois chantiers prioritaires :** 1. **Corriger les 20 problèmes critiques** (Phase 1a, environ deux semaines) : données visibles d'un magasin à l'autre, failles d'impression, produits périmés qui disparaissent des alertes, commandes abîmées par une simple modification, SAV inutilisable sur téléphone. 2. **Poser un socle commun** (Phase 2) : un seul menu pour PC et téléphone, une seule table de droits, une palette unique des statuts et une dizaine de composants standards (chargement, erreur, liste vide, confirmation, tableau). 3. **Refaire les écrans du quotidien en magasin** (Phase 3), dans l'ordre d'usage : Accueil, DLC, Tâches, Livraisons, Commandes clients. - Douze décisions produit (qui voit quoi, vocabulaire des statuts) sont à prendre avant la Phase 2 ; chacune a une proposition par défaut. ## Comment lire ce plan **Pour qui.** Le résumé, la feuille de route et les décisions produit s'adressent à tous : direction, responsables de magasin, administrateur. Les parties P1 à P4 sont écrites pour les développeurs : chaque action y est précise (fichier, composant, route) et renvoie aux constats de l'audit. | Partie | Contenu | À quoi elle sert | |---|---|---| | [Feuille de route](#feuille-de-route) | Six phases, de ce qui est fait (Phase 0) à la dette (Phase 5), avec effort et critères de réussite | Savoir dans quel ordre avancer et comment vérifier chaque étape | | [Décisions produit](#décisions-produit-à-prendre) | Les questions à trancher par le métier, avec une proposition par défaut | Préparer la réunion de décision | | [P1](#p1--principes-navigation-et-design-system) | Dix principes, navigation, design system, accessibilité | Les règles communes à tous les écrans | | [P2](#p2--plan-page-par-page) | Les 20 écrans, un par un : usage, gênes, écran cible, libellés | Ce que chaque écran doit devenir | | [P3](#p3--bugs-et-dette-technique) | Les 136 bugs et les 51 constats de dette technique | Ce qu'il faut réparer, puis nettoyer | | [P4](#p4--performance) | Gains mesurés du lot 1, puis lots 2 et 3 | Ce qui a été fait et ce qui reste | Les renvois internes s'écrivent « P1 § 3.6 » (partie P1, paragraphe 3.6). « Principe 3 » désigne l'un des dix principes de P1 § 1. **L'annexe.** Le dossier [`docs/audit-webui/`](audit-webui/) contient 15 fichiers, un par zone de l'application, soit **620 constats vérifiés** (107 de gravité haute, 309 moyenne, 204 basse). En tête de chaque fichier, un tableau liste ses constats (identifiant, gravité, catégorie, effort, titre, fichier et ligne). Chaque constat a ensuite sa fiche : constat, preuve, impact, recommandation vérifiée et **verdict**. Quand une correction du plan s'écarte de la première recommandation de l'audit, c'est le verdict qui fait foi : il signale les pièges (régressions possibles, fonctions inexistantes). **Les identifiants.** Dans ce plan, les constats sont cités entre parenthèses, par exemple (DASH-22). La fiche se trouve dans le fichier indiqué ci-dessous, à l'ancre formée de l'identifiant en minuscules : DASH-22 → [`audit-webui/01-dashboard.md#dash-22`](audit-webui/01-dashboard.md#dash-22). | Préfixe de l'identifiant | Fichier de l'annexe | Constats (dont haute) | |---|---|---| | DASH | [`01-dashboard.md`](audit-webui/01-dashboard.md) | 46 (9) | | CAL, MCAL, ORD, LIV, MOD, MOB, SRV, BUNDLE, PERM-01 (droits des commandes) | [`02-calendar-orders-deliveries.md`](audit-webui/02-calendar-orders-deliveries.md) | 58 (7) | | RAPPRO, ECHE, NOCO-01 à NOCO-04 suivis de « (annexe 03) » | [`03-reconciliation-payment.md`](audit-webui/03-reconciliation-payment.md) | 40 (6) | | AVOIRS, AVOIRS-API, MOB-AVOIRS | [`04-avoirs.md`](audit-webui/04-avoirs.md) | 41 (8) | | TASKS | [`05-tasks.md`](audit-webui/05-tasks.md) | 36 (6) | | SAV, MSAV | [`06-sav.md`](audit-webui/06-sav.md) | 38 (8) | | CMDCLI | [`07-customer-orders.md`](audit-webui/07-customer-orders.md) | 43 (7) | | DLC | [`08-dlc.md`](audit-webui/08-dlc.md) | 51 (12) | | PUB | [`09-publicities.md`](audit-webui/09-publicities.md) | 35 (3) | | USERS, GROUPS, PERF-BUNDLE, PERM-01 (droits des utilisateurs), PERM-02 | [`10-users-groups.md`](audit-webui/10-users-groups.md) | 43 (7) | | SUPP, CONT, ANA, API, MAIL, SALES | [`11-suppliers-contacts-analytics.md`](audit-webui/11-suppliers-contacts-analytics.md) | 56 (11) | | ADMIN, AUTH, BACKUP, BAP, DEBUG, ERR, LAND, NF, SQL, UTIL, WEATHER, NOCO (sans mention ou avec « annexe 12 ») | [`12-admin-utilities-auth.md`](audit-webui/12-admin-utilities-auth.md) | 48 (4) | | SHELL | [`13-app-shell.md`](audit-webui/13-app-shell.md) | 30 (5) | | INFRA | [`14-server-infra.md`](audit-webui/14-server-infra.md) | 19 (6) | | DB | [`15-database.md`](audit-webui/15-database.md) | 36 (8) | Les identifiants NOCO-01 à NOCO-04 existent dans deux annexes : sans autre mention ou avec « (annexe 12) », ils renvoient à la page de configuration NocoDB ; avec « (annexe 03) », au rapprochement. PERM-01 existe aussi dans deux annexes, avec le même sujet (deux tables de droits divergentes). **Conventions.** - **Effort** (échelle commune à tout le plan) : **S** = moins d'une demi-journée, **M** = 1 à 3 jours, **L** = plus de 3 jours, à découper. Pour une page de P2, l'effort porte sur l'interface, une fois les composants de P1 disponibles ; les travaux serveur nécessaires sont signalés à part. - **Gravité** : celle de l'audit (haute, moyenne, basse). - **Lots.** Le *lot 1* est le premier lot de performance, déjà réalisé dans cette branche (P4 § 2) ; ce qu'il a réglé porte la mention *fait par le lot 1* dans P1 à P3. Les *lots 2 et 3* sont les lots de performance restants (P4 § 4). Les *lots A, B et C* sont les trois lots de corrections prioritaires de P3 § 1. ## Feuille de route Six phases. Les phases 1 à 3 se suivent. La phase 4 (performance, surtout côté serveur) avance en parallèle des phases 2 et 3. La dette (phase 5) est en partie traitée plus tôt, quand elle conditionne une autre phase. Les efforts sont donnés pour un développeur à plein temps ; un second développeur (l'un sur l'interface, l'autre sur le serveur) raccourcit nettement le calendrier. | Phase | Objectif | Effort | Prérequis | |---|---|---|---| | [0. Performance, lot 1](#phase-0--performance-lot-1-fait) | Une application rapide, sans rien changer à l'écran | **Fait** ; livraison : une demi-journée, puis une semaine de suivi | — | | [1. Corrections critiques](#phase-1--corrections-critiques) | Une application qui ne ment pas et ne laisse rien fuir | 3 à 4 semaines (1a : 2 semaines ; 1b : 1 à 2 semaines, en parallèle possible) | Phase 0 livrée | | [2. Socle](#phase-2--socle--navigation-design-system-composants-communs) | Les mêmes menus, mots, couleurs, droits et composants partout | 4 à 6 semaines | Phase 1a ; décisions D1 à D12 | | [3. Refonte des pages](#phase-3--refonte-des-pages-dans-lordre-dusage) | Chaque écran refait selon sa fiche, dans l'ordre d'usage | 10 à 12 semaines, page par page | Phase 2 | | [4. Performance, lots 2 et 3](#phase-4--performance-lots-2-et-3) | Des temps de réponse qui ne grandissent plus avec l'historique | 7 à 10 semaines, en parallèle des phases 2 et 3 | Phase 0 livrée ; décisions DP1 à DP10 | | [5. Dette](#phase-5--dette-technique) | Plus de copies divergentes, un seul serveur, une base protégée | 2 à 3 semaines pour ce qui reste | Phase 1 ; Phase 3 pour la fusion PC / téléphone | ### Phase 0 — Performance, lot 1 (fait) **Objectif.** Rendre l'application rapide sans rien changer à ce que voient les équipes. **Contenu.** Réalisé dans cette branche (64 fichiers modifiés), pas encore livré en production : code découpé par page, compressé et mis en cache ; utilisateur connecté lu une seule fois par session ; réponses de l'API allégées (plus de logo ni de configuration du magasin dans les listes, plus d'empreinte de mot de passe) ; 65 index créés automatiquement au démarrage ; appels inutiles et en double supprimés ; plus aucun rechargement complet de l'application. Détail en P4 § 2, mesures en P4 § 1. | Mesure (administrateur sur PC) | Avant | Après | |---|---|---| | JavaScript reçu avant l'écran de connexion | 1,6 Mo | 180 Ko | | Connexion → accueil affiché | 5,9 s, avec rechargement complet | 0,16 s | | Accueil : appels `/api`, données reçues | 20 appels, 361 Mo | 12 appels, 464 Ko | | Visite des 23 pages : données reçues, appels en double | 1,5 Go, 82 | 0,8 Mo, 0 | | Requêtes SQL pour un passage sur toute l'API | 425 | 189 | | Pic de mémoire du serveur | 1,4 Go | 325 Mo | | Réponses contenant une empreinte de mot de passe ou le mot de passe SMTP chiffré | 18 | 0 | **Pour clore la phase.** Les vérifications de livraison de P4 § 3 : reconstruire l'image Docker, livrer hors des heures d'ouverture (création des index en arrière-plan), contrôler la configuration nginx, tester un redéploiement avec un onglet resté ouvert, vérifier qu'aucun outil externe ne lisait les champs retirés des réponses. Dans la foulée, les quatre chantiers rapides du lot 3 (P4 § 4.2, n° 1 à 4). **Effort.** Réalisé. Livraison : une demi-journée, puis une semaine de relevé des index. **Critères de réussite** (mesurés au banc, à revérifier en production) : - JavaScript reçu avant l'écran de connexion ≤ 200 Ko compressés ; aucun fichier `/assets` retéléchargé lors d'une visite suivante. - `/api/user` appelé une seule fois par session ; 0 appel `/api` en double sur les 23 pages. - Nombre de requêtes `/api` à l'accueil (administrateur) ≤ 12, pour moins de 500 Ko reçus. - 0 réponse de l'API contenant une empreinte de mot de passe ou le mot de passe SMTP (sur les routes de lecture). - En production : `SELECT indexrelid::regclass FROM pg_index WHERE NOT indisvalid` ne renvoie rien ; après un redéploiement, un onglet resté ouvert se recharge une fois, sans écran blanc. - Aucune régression : statuts, valeurs et ordre des listes identiques sur les 272 réponses comparées (P4 § 1.6). ### Phase 1 — Corrections critiques **Objectif.** Que l'application ne mente plus et ne laisse plus rien fuir : chaque magasin ne voit que ses données, aucun chiffre n'est faux, aucune saisie n'est perdue, les fonctions du quotidien marchent, y compris sur téléphone. L'organisation des écrans ne change pas encore. **Contenu.** - **1a. Bugs prioritaires**, à faire avant toute refonte (P3 § 1 et § 5) : - *Jour 1, filets immédiats* : clé `password` retirée de toutes les réponses (P3, T1), échappement des impressions (T8), `requireAdmin` sur les magasins et la météo, journaux contenant des secrets supprimés, champ commentaire du SAV masqué, suppression du code mort sans importateur (P3 § 4.5, étape 1), avertissement « ne jamais lancer `db:push` sur la base de production » dans le README (DB-02). Vérifier aussi que la production utilise sa propre clé de session (`SESSION_SECRET`, complément en fin de P3). - *Les vingt problèmes de P3 § 1*, en trois lots. **Lot A, sécurité** : empreintes de mots de passe envoyées au navigateur (USERS-02), failles d'impression (CMDCLI-08, DLC-26), fichiers relayés sans contrôle vers une adresse fournie par le navigateur (RAPPRO-21, AVOIRS-04), secrets en clair (NOCO-01 annexe 12, GROUPS-14), comptes sans magasin qui voient toute l'enseigne (SAV-06, CONT-02, ANA-05, ANA-06), écritures sans contrôle du magasin ni du rôle (CONT-03, DLC-06, CMDCLI-07, TASKS-13), configuration d'un magasin effacée par un manager (GROUPS-01), téléchargement hors du dossier des sauvegardes et identifiants « admin / admin » affichés (BACKUP-09, AUTH-04). **Lot B, données fausses ou perdues** : produits DLC « traités » qui disparaissent des alertes (DLC-01, DLC-02), formulaire DLC qui écrase des produits (DLC-03, DLC-04, DLC-05), commandes clients abîmées par une modification (CMDCLI-03, CMDCLI-04), commentaires SAV jamais enregistrés (SAV-02), échéance effacée et TTC à 0,00 € (RAPPRO-07, ECHE-02), chiffres faux (DASH-26, CAL-11, ANA-04, DASH-28), sauvegarde ratée affichée « en cours » (BACKUP-02). **Lot C, fonctions cassées** : accueil et tâches vides pour les managers et les employés (DASH-01, DASH-03, TASKS-02), modification des commandes fournisseurs impossible (ORD-01), SAV et dates en panne sur téléphone (MSAV-01, MSAV-02, MOB-02), listes vides ou figées (DLC-10, MOB-AVOIRS-01, SAV-01), fenêtre d'alerte DLC qui se rouvre (DASH-02, DLC-08). - *Puis la liste « Juste après » de P3 § 1* : sessions perdues à chaque redéploiement (AUTH-06), filtres absents sur tablette (TASKS-09), « En retard de 0 jour » (TASKS-12), affectations de magasin en double (USERS-03), fournisseur n° 1 attribué en silence (DLC-11), etc. - Avec ce qu'a déjà fait le lot 1, ces corrections ferment les 49 bugs de gravité haute de l'audit, sauf PUB-01 (publicités de tous les magasins visibles par tous), qui dépend de la décision DP5 et se traite en Phase 2 avec le correctif T2. ADMIN-01 est à vérifier à l'écran : le lot 1 en a supprimé la cause (hook d'authentification non partagé). - **1b. Corrections rapides de l'interface**, sans changer l'organisation des écrans (en parallèle de 1a si un second développeur est disponible) : - Les actions immédiates de P1 (§ 4.1 et § 5, étape 1) : nom sur chaque bouton-icône (A2), gris lisibles (A5), messages d'erreur en français (A12), formats français des montants et des dates (A13), plus de texte technique à l'écran (A14), page introuvable en français (A15), tableaux qui défilent au lieu d'être coupés (ORD-11, PUB-22, USERS-14, DLC-43), rôle en français sur téléphone (SHELL-24). - Les lignes « D'abord (S) » de chaque fiche de P2 : elles retirent les fonctions factices (« Modifier » du SAV, SAV-03 ; « + » des publicités, PUB-11 ; « Exporter PDF » des DLC ; « Tester la configuration NocoDB »), protègent les gestes dangereux (suppression d'une sauvegarde sans confirmation, BACKUP-01 ; SQL brut en un clic, SQL-01 ; suppression d'un avoir d'un toucher sur téléphone, MOB-AVOIRS-02), débloquent les écrans (fenêtre d'envoi de facture qui ne se ferme jamais, RAPPRO-04 ; formulaire de commande client réduit à 90 % avec son bouton hors de l'écran, CMDCLI-20 ; employés bloqués sans magasin dans Contacts, CONT-04) et corrigent les libellés trompeurs (badge « Validé » d'un avoir non validé, AVOIRS-05). **Effort.** 1a : environ deux semaines (P3 § 5 : jour 1, semaines 1 et 2). 1b : 1 à 2 semaines. **Critères de réussite.** - 0 réponse de l'API contenant `password`, `smtpPassword` ou un jeton NocoDB en clair, sur toutes les routes et pour les 4 rôles, `POST /api/users` compris. - Un compte sans magasin reçoit 0 ticket SAV, 0 contact et 0 statistique ; un manager du magasin A reçoit un refus pour toute écriture sur le magasin B (contacts, SAV, DLC, commandes clients, tâches, magasins). - Une étiquette ou une liste DLC imprimée avec un nom contenant `` ou `&` affiche ce texte tel quel. - Les scénarios des 20 problèmes de P3 § 1 sont rejoués et passent : une commande fournisseur se modifie ; un produit DLC « traité » revient dans « Expirés » à sa date ; corriger le téléphone d'une commande client ne change ni son statut ni son magasin ; la fenêtre d'alerte DLC se ferme en un geste et ne revient pas dans la session ; une sauvegarde interrompue apparaît « Échec ». - Sur téléphone : 0 « Date inconnue » ; le SAV liste, crée et change de statut ; les listes DLC et avoirs ne sont jamais vides à tort (employé, « Tous les magasins »). - 0 bug de gravité haute ouvert, sauf PUB-01 (Phase 2). - Pour 1b : 0 bouton-icône sans nom accessible (contrôle automatique d'accessibilité) ; 0 code HTTP ni JSON dans les messages d'erreur ; `grep` : aucun `N/A`, `Required`, `toFixed(2)} €` ni identifiant technique `#12` affiché ; 0 fonction factice visible. ### Phase 2 — Socle : navigation, design system, composants communs **Objectif.** Que chaque employé retrouve les mêmes mots, couleurs, gestes et droits partout, sur PC comme sur téléphone, et que chaque écran sache dire « chargement », « erreur » et « vide ». C'est la base de la refonte : une page refaite avant le socle devrait l'être deux fois. **Prérequis.** Les décisions D1 à D11 (droits) et D12 (vocabulaire des statuts), prises en une réunion d'une heure avec un directeur et un responsable de magasin, tableau rôle × module de P1 § 2.2 en main. **Contenu.** - **Règles serveur communes à tous les modules** (P3 § 2) : accès par magasin (T2, qui règle aussi PUB-01 selon DP5), requêtes et clés de cache centralisées (T3), dates justes partout (T5), formulaires de modification qui n'écrasent rien (T6), retour juste après chaque action (T7). T4 (utilisateur chargé une seule fois) est fait par le lot 1. - **Droits** : une seule table `shared/permissions.ts`, alignée sur le tableau rôle × module (P1 § 2.2), lue par le menu, les routes (`ProtectedRoute`), les boutons et le serveur ; suppression de la table client (P3 § 4.2, D). - **Navigation** (P1 § 2) : menu regroupé par usage, identique sur PC et téléphone (`NAV_ITEMS`) ; magasin actif toujours visible et présélectionné ; cadre mobile unique qui rend au téléphone les 8 modules qui lui manquent (SHELL-10) ; PC ou téléphone choisi une fois au démarrage. - **Design system** (P1 § 3) : couleurs contrastées et survols qui fonctionnent (§ 3.1, A3, A4), cinq tonalités et palette unique des statuts (§ 3.2, § 3.3, avec la configuration unique de P3 § 4.2, F), typographie, espacements et arrondis (§ 3.4, § 3.5). - **Composants communs** (P1 § 3.6) : `PageHeader`, `StatusBadge`, `EmptyState`, `ErrorState`, squelettes, `ConfirmDialog`, `FilterBar`, `DataTable`, `IconButton`, `FormDialog`, `AccessDenied`, et un chargeur de données commun qui lève une erreur lisible (P3 § 4.2, G). - **Migration des états et des confirmations sur toutes les pages existantes**, sans attendre leur refonte : squelette au premier chargement, `ErrorState`, `EmptyState`, `ConfirmDialog` à la place des 7 `window.confirm` (P1 § 5, étape 5). C'est ce qui supprime les faux « 0 » de l'accueil (DASH-22) et des autres écrans. **Effort.** 4 à 6 semaines : correctifs T2 à T7 (environ deux semaines, P3 § 5), puis fondations, navigation et composants (P1 § 5, étapes 3 à 5, une dizaine de chantiers M). **Critères de réussite.** - Aucune page n'affiche « 0 » ni « Aucun… » pendant le chargement (test des 23 pages, PC et téléphone, avec un réseau ralenti dans le navigateur). - Réseau coupé : chaque écran affiche « Impossible de charger… — Réessayer » (aujourd'hui, 2 pages seulement lisent l'erreur). - Une seule table de droits : `client/src/lib/permissions.ts` supprimé, `grep "user?.role ==="` dans `client/src/pages` = 0. Avec un compte de chaque rôle : 0 « Accès refusé » en cliquant sur ce qui est affiché, et toute page interdite affiche « Vous n'avez pas accès à cette page ». - Même menu sur PC et téléphone (18 entrées au plus, chaque icône une seule fois) ; sur téléphone, 0 module inaccessible (8 aujourd'hui) et 0 page sans navigation. - Un employé mono-magasin, sur un navigateur vierge, voit son magasin et des listes remplies ; un seul sélecteur de magasin dans toute l'application. - `grep "confirm("` = 0 (7 aujourd'hui) ; `grep -E "text-\[1[01]px\]"` = 0 ; aucun statut affiché en code brut (`planned`, `attente_echange`) ; aucun `statusConfig` ni `getStatusColor` en dehors du module de statuts. - Contraste d'au moins 4,5:1 pour tout texte, bouton principal compris (3,2:1 aujourd'hui, 5,2:1 visé). ### Phase 3 — Refonte des pages, dans l'ordre d'usage **Objectif.** Construire, écran par écran, l'écran cible décrit dans sa fiche de P2 : une action principale, des listes lisibles sur PC et téléphone, plus de fonction factice ni de doublon. **Contenu.** Les fiches de P2, dans cet ordre : 1. **Écrans du quotidien en magasin** : Accueil (P2 § 1), DLC (§ 3), Tâches (§ 2), Livraisons (§ 6), Commandes clients (§ 4). 2. **Écrans de suivi** : SAV (§ 5), Commandes fournisseurs (§ 7), Calendrier (§ 8), Publicités (§ 12), Contacts (§ 13). 3. **Écrans de gestion** : Rapprochement BL / factures (§ 9), Avoirs (§ 10), Échéancier (§ 11), Statistiques (§ 15), Fiches fournisseurs (§ 14). 4. **Administration** : Utilisateurs (§ 16), Magasins (§ 17, dont la fenêtre d'environ 25 champs, GROUPS-05), Paramètres (§ 18), puis Connexion (§ 19) et page introuvable (§ 20). Pour chaque page : versions PC et téléphone fusionnées en un seul écran qui s'adapte (P3 § 4.2, B, E et H), ce qui donne enfin au téléphone les fiches et les actions qui lui manquent (ouvrir une fiche, valider une réception, MOB-01) ; suppression des écrans non visibles qui la concernent (P2 § 21) ; bugs de gravité basse restants corrigés ; liste paginée côté serveur au même moment (P4 § 5.1). Les prérequis serveur sont signalés dans chaque fiche ; la refonte de l'Accueil s'appuie sur la route de synthèse de la Phase 4 (DASH-05). **Effort.** 10 à 12 semaines pour un développeur (somme des fiches de P2 : une vingtaine de chantiers de 1 à 3 jours et trois de plus de 3 jours : DLC, Commandes clients, Rapprochement), découpables page par page ; chaque page livrée est utilisable seule. **Critères de réussite.** Pour chaque page livrée : - Elle respecte les dix principes de P1 § 1 et a été parcourue avec un compte de chaque rôle, sur PC et sur téléphone. - Un seul bouton plein dans l'en-tête ; sur chaque ligne, au plus une action avec du texte et un menu « ⋯ » (jusqu'à 6 boutons-icônes par ligne aujourd'hui). - Mêmes libellés, couleurs et icônes de statut sur PC, téléphone, calendrier et impression (tableau de P1 § 3.3). - Après une action en page 3, la liste reste en page 3 ; actualiser une liste filtrée conserve les filtres. - Une seule implémentation de l'écran : plus de paire PC / téléphone divergente. Et pour l'ensemble : depuis l'accueil, chaque urgence est à un clic de sa liste filtrée et au plus une fenêtre automatique s'ouvre par session ; sur téléphone, valider une réception, traiter un produit DLC, prévenir un client et suivre un ticket SAV se font de bout en bout (selon les droits D1, D7, D8 et D9) ; 0 écran non visible ni fonction factice dans le code. ### Phase 4 — Performance, lots 2 et 3 **Objectif.** Des temps de réponse qui ne grandissent plus avec l'historique, et aucun service externe qui bloque un écran. **Contenu.** P4 § 4.1 (lot 2), puis § 4.2 (lot 3), dans l'ordre de P4 § 5.1 : - Le banc de mesure versionné dans le dépôt (lot 2, n° 0), pour prouver chaque gain sans régression. - Logo et configuration du magasin retirés des réponses courantes et des fiches de détail (n° 1, GROUPS-02) ; contrôles d'accès légers (n° 2). - Synthèse de l'accueil calculée en SQL (n° 3, DASH-05, DB-08), à faire avec DASH-01 et la décision DP2. - Pagination côté serveur et sous-objets allégés, liste par liste (n° 4 et 5 : ORD-02, CMDCLI-02…), au moment où la page est refaite en Phase 3 et après la décision DP1. - État de vérification des factures renvoyé avec les listes (n° 6), échéancier en SQL (n° 7, ECHE-01), liste légère des commandes (n° 8, MOD-01), délais maximaux sur les appels externes (n° 9), historique des relances (n° 10). - Le lot 3 au fil de l'eau : caches, outils d'administration, journaux à niveaux, index déclarés dans le schéma. **Effort.** Lot 2 : 5 à 7 semaines ; lot 3 : 2 à 3 semaines. Ce travail, surtout côté serveur, avance en parallèle des phases 2 et 3. **Critères de réussite** (mesurés au banc, avant et après chaque chantier) : - Nombre de requêtes `/api` à l'accueil (administrateur) ≤ 5, pour quelques Ko, quel que soit l'historique (12 appels et 464 Ko après le lot 1). - `/api/groups` ≤ 10 Ko (148 Ko aujourd'hui) ; une fiche de détail ≤ 10 Ko compressés (111 Ko). - Chaque liste renvoie au plus 50 lignes par page : les livraisons de l'administrateur pèsent environ 75 Ko au lieu de 3,8 Mo. - Page Avoirs : ≤ 4 appels par visite (81 aujourd'hui). - Échéancier : quelques dizaines de millisecondes, même avec NocoDB. - Aucun appel externe sans délai maximal (webhook 5 s, météo 3 s, recherche d'article 5 s) ; `statement_timeout` en place sur la base. - 0 régression au banc : statuts, valeurs et ordre des listes identiques. ### Phase 5 — Dette technique **Objectif.** Que les corrections tiennent : plus de copies divergentes d'un même écran ou d'une même règle, un seul serveur, testé comme celui de production, une base qui ne peut pas perdre ses index. **Contenu.** P3 § 4, dans l'ordre de P3 § 4.5. Une partie est avancée dans les phases précédentes, parce qu'elle les conditionne : - en Phase 1 : suppression du code mort sans importateur (environ 3 700 lignes et 36 fichiers temporaires, étape 1), journaux contenant des secrets, avertissement `db:push` ; - en Phase 2 : table de droits unique (étape 4), requêtes centralisées, chargeur commun et configuration des statuts (étape 5) ; le hook d'authentification unique (étape 3) est fait pour l'essentiel par le lot 1 ; - en Phase 3 : fusion des versions PC et téléphone, module par module (étape 7). Restent pour la Phase 5 (les étapes 2 et 6 ne dépendent d'aucune autre phase : P3 § 5 les place dès les semaines 3 et 4, en parallèle de la Phase 2, si un développeur serveur est disponible) : - les journaux et routes mortes restants (étape 2) et le code mort à l'intérieur des fichiers (P3 § 4.1, seconde table : routes en double, `!important` du CSS, blocs 401 recopiés) ; - le point d'entrée serveur unique (`createApp`), puis la suppression de `localAuth.production.ts` (étape 6, P3 § 4.2, C) ; - la base de données (étape 8, P3 § 4.4) : index déclarés dans le schéma, unicité de `user_groups`, compteurs entiers, SQL paramétré, identifiants générés par le serveur. **Effort.** 2 à 3 semaines pour ce qui reste après les phases 1 à 3. **Critères de réussite.** - 0 fichier sans importateur dans `client/src` et `server` (vérifié par recherche dans tout le dépôt). - `npm start` et l'image Docker lancent le même code, avec les mêmes protections ; `localAuth.production.ts` et `db.production.ts` supprimés. - Un `db:push` sur une copie de la base de production ne supprime aucun index ; `user_groups` n'a plus aucun doublon et porte un index unique. - Une seule implémentation par module pour Tâches, Commandes clients, SAV, Avoirs, Publicités et DLC. - Plus aucun `console.log` dans `server/routes.ts` (environ 150 aujourd'hui), remplacés par le journal à niveaux de P4 (lot 3, n° 21). ### Règles communes à toutes les phases - **Aucune nouvelle erreur TypeScript** : `npx tsc --noEmit -p tsconfig.json` compte aujourd'hui 88 erreurs, toutes dans `server/storage.ts` ; ce nombre ne doit pas augmenter. - **Le build Docker passe** : `vite build`, puis la commande esbuild du `Dockerfile` sur `server/index.production.ts` (c'est elle qui échoue si un fichier encore importé est supprimé). - **Tester avec un compte de chaque rôle** : administrateur, directeur multi-magasins, manager, employé, et un compte sans magasin, sur PC, tablette et téléphone. Les bugs de cloisonnement (P3 § 3.2 et § 3.5) ne se voient qu'avec ces comptes. - **Mesurer chaque changement de performance** avec le banc (P4 § 5.2) : il ne doit modifier ni un statut, ni une valeur, ni l'ordre d'une liste. - **Lire le verdict de l'annexe avant de coder** : il signale les recommandations incomplètes ou risquées (par exemple RAPPRO-07, AVOIRS-API-06, TASKS-02, PERM-01, LAND-01, INFRA-11). - **Une correction = un ou plusieurs identifiants cités** dans le message de commit, pour pouvoir cocher l'annexe. - **Style** : code qui ressemble au code voisin, commentaires en français, sobres. ## Décisions produit à prendre Ces questions ne relèvent pas du développeur : la réponse change ce que voient ou font les équipes. Pour ne pas bloquer le chantier, chacune a une proposition par défaut, appliquée tant que rien d'autre n'est décidé. Les décisions D1 à D12 sont à prendre avant la Phase 2, en une réunion d'une heure avec un directeur et un responsable de magasin, tableau rôle × module de P1 § 2.2 en main. D13 et D14 peuvent attendre. DP1 à DP10 sont à prendre avant le chantier de performance concerné (Phase 4). ### Droits, vocabulaire et contenu (D1 à D14) | N° | Question | Situation actuelle | Proposition par défaut | Constats | |---|---|---|---|---| | D1 | L'employé voit-il les **commandes fournisseurs** et les **livraisons** ? Peut-il **valider une réception** depuis son téléphone ? | PC : masqué. Mobile : visible. Les deux tables de droits se contredisent. L'API renvoie les données. | Commandes fournisseurs : masqué. Livraisons : visible en lecture (savoir ce qui arrive aujourd'hui). Validation de réception : à accorder seulement si les employés réceptionnent les camions. | SHELL-11, PERM-01, MOB-01, DB-11 | | D2 | L'employé voit-il les **avoirs** ? Qui peut **envoyer le PDF** d'un avoir reçu ? | PC : masqué ; mobile : visible avec suppression ; envoi du PDF impossible pour directeur et employé. | Avoirs masqués pour l'employé. Envoi du PDF : admin et directeur. | SHELL-11, AVOIRS-04, MOB-AVOIRS-02 | | D3 | Le manager a-t-il accès au **rapprochement BL / factures** ? | Le texte de la page dit oui, le menu et le serveur disent non. | Non (admin et directeur). Corriger le texte de la page. | RAPPRO-17 | | D4 | L'employé voit-il les **statistiques** ? | Menu : non. Serveur : oui. | Non. | ANA-05, ANA-14 | | D5 | Le directeur gère-t-il les **comptes utilisateurs** de ses magasins ? | Menu : admin seul. Serveur : admin, directeur et manager peuvent lister les comptes. | Admin seul, serveur aligné. | USERS-02, USERS-20 | | D6 | L'employé peut-il **terminer** une tâche ? Le manager peut-il en **supprimer** ? | Mobile : « Terminer » pour tous. PC : « Supprimer » pour les managers. Table de droits : ni l'un ni l'autre. | Employé : oui pour terminer. Manager : pas de suppression. | TASKS-13, TASKS-26 | | D7 | L'employé peut-il marquer un produit **« Retiré du rayon »** ou **« Vérifié »** ? | Proposé sur mobile, refusé en silence par le serveur ; la fenêtre d'alerte pousse à « Stock épuisé » à la place. | Oui (c'est lui qui est en rayon), avec confirmation. Suppression réservée à partir du manager. | DLC-13, DLC-32, DLC-06 | | D8 | L'employé peut-il marquer **« Client prévenu »**, **« Retirée »**, **« Annulée »** sur une commande client ? | Interface : oui pour tout. Serveur : aucun contrôle. Table : création seule. | Oui pour « Client prévenu » et « Retirée » (gestes de comptoir). Non pour « Annulée » et la modification. | CMDCLI-07, CMDCLI-10, CMDCLI-26 | | D9 | L'employé peut-il **créer un ticket SAV** ? | PC : non. Mobile : bouton visible. Serveur : refus. | Oui pour la création (le client se présente à l'accueil). Statut et priorité : à partir du manager. | MSAV-04 | | D10 | Qui modifie une **fiche fournisseur** ? | Fiches fournisseurs : admin. Contacts : admin et directeur. Serveur : admin, directeur et manager. | Admin et directeur, depuis Fiches fournisseurs uniquement. | SUPP-02, SUPP-01 | | D11 | Les **outils avancés** (SQL brut, diagnostic de la base) restent-ils accessibles en production ? | Un clic depuis Utilitaires, sans confirmation. | Masqués par défaut (variable d'environnement), regroupés sous « Outils avancés » avec confirmation tapée. | SQL-01, DEBUG-02, UTIL-01 | | D12 | **Vocabulaire des statuts** de P1 § 3.3 (« À demander », « Retiré du rayon », « Vérifié », « Contrôle fait », féminin « Planifiée / Livrée ») : à valider avec un responsable de magasin. | Libellés différents selon l'écran. | Tableau de P1 § 3.3 (avec « À recevoir » pour les livraisons planifiées). | CMDCLI-27, DLC-32, AVOIRS-06, LIV-02, LIV-01 | | D13 | **Analyse des ventes** (page externe en iframe, non branchée) : on la branche ou on la supprime ? | Code présent mais inaccessible. | À trancher ; sans usage connu, supprimer. | SALES-01 | | D14 | **Mode sombre** : le maintient-on ? | Jamais activé (aucune classe `dark` posée, vérifié), mais 73 classes `dark:` dans le code. | Non pour l'instant : un seul thème clair, bien contrasté. | TASKS-17 | ### Performance et données (DP1 à DP10) | N° | Question | Pourquoi c'est une décision | Proposition par défaut | Constats | |---|---|---|---|---| | DP1 | **Quelle période afficher par défaut** dans les listes paginées ? | Avec une période, une ligne ancienne n'est plus visible sans action. | Commandes et livraisons : les 3 derniers mois, puis « Voir plus ». Tâches terminées, produits DLC validés, commandes clients retirées ou annulées : les 30 derniers jours. Tickets SAV fermés masqués par défaut. Avoirs finalisés chargés à l'ouverture de l'onglet. La recherche porte toujours sur tout l'historique. | ORD-02, TASKS-27, DLC-17, CMDCLI-02, SAV-13, AVOIRS-API-02 | | DP2 | **Que montre l'accueil**, et pour quel magasin ? | La route de synthèse fige la définition des compteurs. Corriger DASH-01 change les chiffres des managers et des directeurs. | Les compteurs de la fiche Accueil de P2 (§ 1), toujours calculés pour le magasin actif. | DASH-05, DB-08, DASH-01 | | DP3 | **Caches côté serveur** : accepte-t-on un léger décalage des chiffres ? | Un cache mal invalidé affiche un chiffre périmé. | Pas de cache pour les statistiques (5 ms aujourd'hui). Cache de 5 minutes pour la configuration NocoDB, vidé à sa modification. Cache météo seulement après WEATHER-01. | DASH-10, CAL-12, WEATHER-04, AVOIRS-API-09 | | DP4 | **« Meilleurs magasins »** dans l'export de Statistiques : le garder ? Lui appliquer la période choisie ? | Le résultat n'est affiché nulle part à l'écran. Appliquer la période change les chiffres exportés. | Le retirer de l'export (ANA-17), ce qui supprime la requête. | ANA-03, ANA-17, DB-16 | | DP5 | **Publicités** : chaque magasin ne voit-il que ses campagnes ? | Aujourd'hui, toutes les campagnes de l'année sont visibles par tous. | Oui pour le calendrier et l'accueil. Vue d'ensemble complète gardée pour l'admin. | CAL-15, DB-29 | | DP6 | **Logger à niveaux en production** : accord de l'exploitant ? | Change ce que contiennent les journaux Docker. | Niveau `info` par défaut, `debug` activable par variable d'environnement. | NOCO-04 (annexe 03), INFRA-14 | | DP7 | **Index en double** déjà présents sur certaines bases (créés à la fois par `init.sql` et par la migration manuelle) : les supprimer ? | Supprimer un index est une opération sur la base de production. | Oui, après une semaine de relevé de `pg_stat_user_indexes`, en supprimant celui qui n'est pas utilisé. | DB-37 | | DP8 | **Sauvegarde déclenchée à la connexion** : la garder ? | Elle n'est plus bloquante, mais elle fait doublon avec la sauvegarde automatique horaire. | La garder tant que BACKUP-02 n'est pas corrigé, puis la retirer. | BACKUP-04 | | DP9 | **« Vérifier toutes les factures »** : forcer NocoDB pour toutes les lignes, ou seulement pour celles en rouge ou pas encore vérifiées ? | Le comportement du bouton change. | Seulement les lignes rouges ou non vérifiées, avec une progression « 12 / 40 » et un récapitulatif. | RAPPRO-22 | | DP10 | **Échéancier** : que faire des livraisons dont l'échéance est inconnue ? | Ne plus appeler NocoDB pendant l'affichage, c'est accepter de les montrer « à compléter ». | Les afficher dans une ligne « Échéance à compléter », complétée par la vérification de facture. | ECHE-01 | | — | **Numérotation des tickets SAV** : corriger le calcul (« 5 » + 1 = « 51 ») et imposer l'unicité ? | Les numéros visibles changent. | Déjà inscrit dans P3. Le lot 1 n'a rendu que le filtre indexable. | DB-27 | ### Autres questions ouvertes relevées dans les fiches | N° | Question | Où | Proposition par défaut | Constats | |---|---|---|---|---| | Q1 | **Vue Kanban des tâches** : la retirer ? | P2 § 2 | Retirée : ses deux colonnes répètent les onglets « À faire » et « Terminées ». À défaut, la rendre utilisable sur tablette. À confirmer avec le propriétaire. | TASKS-09 | | Q2 | **« Modifier » du SAV** : brancher l'enregistrement ou retirer le bouton ? | P2 § 5 | Bouton retiré tant que l'enregistrement n'existe pas. | SAV-03 | | Q3 | **Bouton « Envoyer un bon à payer (BAP) »** : dans le Rapprochement ou dans l'Échéancier ? | P1 § 2.1 | En-tête du Rapprochement BL / factures, réservé à l'admin ; à confirmer avec la personne qui l'utilise. | BAP-01, SHELL-29 | | Q4 | **Restaurer une sauvegarde depuis l'interface** ? | P2 § 18.1 | Pas de proposition dans les fiches ; en attendant, pas de restauration depuis l'interface (situation actuelle). | — | | Q5 | **Téléphone du client obligatoire** dans une commande client ? | P2 § 4 | Oui, à confirmer avec le comptoir. | — | | Q6 | **Cases « Prix promotionnel » et « Client déjà notifié »** du formulaire sur téléphone : les faire fonctionner ou les retirer ? | P2 § 4, P3 § 3.3 | Les faire fonctionner (champs ajoutés au schéma, P3) ; les retirer si le comptoir ne s'en sert pas. | CMDCLI-06 | | Q7 | **Avoir validé** : l'admin peut-il encore changer son statut ? | P3 § 3.6 | Oui pour l'admin seul, comme la règle serveur d'AVOIRS-API-07 ; menu grisé pour les autres. | AVOIRS-18, AVOIRS-API-07 | | Q8 | **Publicités sans magasin participant**, en « Tous les magasins » : l'admin les voit-il ? | P3 § 3.2 | Oui : la vue d'ensemble complète reste à l'admin (DP5). | PUB-01 | | Q9 | **Sessions conservées entre deux déploiements** ? | P3 § 3.10 | Oui : sessions en base, plus de déconnexion générale à chaque mise à jour. | AUTH-06, INFRA-08 | | Q10 | **Protection CSRF** (décision technique) : la garder, avec l'en-tête envoyé par `apiRequest`, ou aligner `npm start` sur l'image Docker, qui n'en a pas ? | P3 § 3.10, § 4.2 C | À trancher avant l'unification des points d'entrée (dette, étape 6). | AUTH-07 | --- ## P1 — Principes, navigation et design system Cette section fixe les règles communes à tous les écrans : ce que l'on s'interdit, comment on navigue, quelles couleurs et quels composants on utilise. Les sections suivantes du plan (page par page) s'appuient dessus. Le but : qu'un employé, un manager ou un directeur retrouve **les mêmes mots, les mêmes couleurs et les mêmes gestes partout**, sur PC comme sur téléphone. Chaque action renvoie aux constats de l'audit entre parenthèses, par exemple (SHELL-12, DASH-21) ; le tableau des préfixes et des fichiers de l'annexe est dans [Comment lire ce plan](#comment-lire-ce-plan). --- ### 1. Dix principes pour toute l'application Chaque principe est une règle simple, avec les constats qui la justifient et un moyen concret de vérifier qu'elle est respectée. Une page n'est « terminée » que si elle respecte les dix. #### Principe 1. Une action principale par écran, une action principale par ligne - **Règle.** En haut de page : un titre et **un seul** bouton plein (ex. « Nouvelle livraison »). Sur chaque ligne ou carte : **un seul** bouton avec du texte pour le geste le plus fréquent (« Valider la réception », « Prévenir le client »), le reste dans un menu « ⋯ » dont chaque entrée a un libellé. Une seule fenêtre ouverte à la fois, jamais deux fenêtres empilées. - **Pourquoi.** Jusqu'à 5 ou 6 boutons-icônes sans texte par ligne, dont deux coches presque identiques (RAPPRO-14, AVOIRS-07, DLC-23, ORD-10, CMDCLI-24) ; en-têtes à 7 contrôles (PUB-21) ; deux fenêtres automatiques empilées à l'ouverture de l'accueil (DASH-24) ; fiches avec une fenêtre d'édition par-dessus (MOD-08, CAL-14) ; couleurs de bouton principal différentes d'un onglet à l'autre (BACKUP-10, MOD-09). - **Vérification.** Au plus un `} // 1 bouton plein + éventuellement un menu « ⋯ » /> ``` Empile titre et actions sous 640 px (`flex-col sm:flex-row flex-wrap`). Sur téléphone, le titre est déjà dans la barre du haut : `PageHeader` n'affiche que les actions. Variante `AdminSection` (titre h2 + description + actions) pour les onglets de Paramètres. **Pages** : les 18 modules du menu, les 6 onglets de Paramètres, la 404. **Constats** : SHELL-14, ECHE-04, BACKUP-10, UTIL-04, PUB-21, ANA-22, GROUPS-16. ##### `StatusBadge` et `PriorityIndicator` ```tsx // libellé, tonalité, icône lus dans la configuration Contrôle à faire // cas calculés // rien si priorité basse et compact ``` Configuration `STATUS[domain][code] = { label, tone, icon }` + `getStatusLabel(domain, code)` pour les exports, l'impression et le serveur. Badge non cliquable, sans effet au survol (PUB-26). Quand un statut se change d'un clic, on utilise un bouton ou une liste déroulante explicite, pas un badge cliquable (CMDCLI-25). **Pages** : Commandes fournisseurs, Livraisons, fiche commande/livraison, Calendrier (grille + légende), Accueil, Rapprochement, Avoirs, Tâches, SAV, Commandes clients (liste, détail, impression), DLC (+ fenêtre d'alerte), Publicités, et toutes leurs versions mobiles. **Constats** : ORD-12, CAL-06, CAL-07, MCAL-01, TASKS-18, MSAV-06, SAV-21, MOB-AVOIRS-09, CMDCLI-28, DLC-28, PUB-26, DASH-19, DASH-46. ##### `EmptyState` ```tsx Nouvelle livraison : undefined} // kind="empty" onClearFilters={resetFilters} // kind="no-results" : bouton « Effacer les filtres » /> ``` `no-store` affiche « Choisissez un magasin en haut de l'écran » (avec un bouton qui ouvre le sélecteur sur mobile). Le message s'adapte au rôle : pas de « Créez votre première publicité » pour qui ne peut pas créer. **Pages** : toutes les listes PC et mobile, chaque carte de l'accueil, chaque graphique des statistiques. **Constats** : TASKS-22, MSAV-09, CMDCLI-18, CMDCLI-31, PUB-12, PUB-26, PUB-33, SAV-23, CONT-04, DLC-10, SUPP-07. ##### `ErrorState` ```tsx query.refetch()} title="Impossible de charger les livraisons" // facultatif compact // version une ligne, pour une carte de l'accueil /> ``` Affiche le message de `getErrorMessage(error)` (français, jamais de JSON). Un refus de droit (403) affiche « Vous n'avez pas les droits pour voir ces données », sans bouton « Réessayer ». Les erreurs 403/404 ne sont plus réessayées automatiquement, ce qui évite 3 s d'attente (SHELL-08, *fait par le lot 1*). **Pages** : mêmes pages qu'`EmptyState`. **Constats** : DASH-22, MOB-03, TASKS-22, SAV-23, PUB-13, CONT-07, SUPP-07, ANA-18, DLC-22, CMDCLI-18, MSAV-09, ERR-01. ##### `TableSkeleton` (et `ListSkeleton`, `StatSkeleton`, `PageSkeleton`) ```tsx // même hauteur de ligne que DataTable // cartes mobiles // rangée de compteurs, même hauteur que les vrais // pendant le chargement d'une page à la demande (SHELL-03) ``` Affichés au **premier** chargement uniquement. Quand on change de filtre, de mois ou de magasin, on garde les données précédentes à l'écran avec un indicateur discret « Mise à jour… » (`placeholderData: keepPreviousData`) au lieu de tout remplacer par un spinner. **Pages** : les 29 pages qui utilisent aujourd'hui un spinner (`animate-spin`), en priorité Accueil, Calendrier, Statistiques, Échéancier, DLC, Utilisateurs. **Constats** : DASH-22, USERS-24, MOB-AVOIRS-09, SAV-23, ANA-18, CMDCLI-18, DASH-42. ##### `ConfirmDialog` (+ `useConfirm`) ```tsx deleteMutation.mutate(id, { onSuccess: () => setOpen(false) })} requireText="EXÉCUTER" // facultatif : saisie obligatoire (SQL) /> // Pour remplacer un window.confirm sans réécrire la page : const confirm = useConfirm(); if (await confirm({ title: "Dévalider ce rapprochement ?", confirmLabel: "Dévalider" })) { … } ``` Basé sur `AlertDialog`. Reste ouvert pendant l'action et en cas d'erreur, se ferme au succès (ORD-14, USERS-10). Nomme toujours l'objet, avec un repli si le nom est vide (USERS-13). Remplace `ConfirmationModal`, `ConfirmDeleteModal`, la confirmation propre à la fiche commande (MOD-07) et les 7 `window.confirm` (ReconciliationComments, BLReconciliation × 2, Users, Suppliers, Contacts, Groups). **Pages** : toutes les suppressions + les changements d'état listés au principe 6 + fermeture d'un formulaire modifié. **Constats** : USERS-13, RAPPRO-27, SAV-21, BACKUP-01, MOB-AVOIRS-02, AVOIRS-06, CMDCLI-26, SAV-24, MSAV-05, DLC-13, DASH-37, NOCO-04, SQL-01, GROUPS-13. ##### `FilterBar` ```tsx ``` Une ligne compacte au-dessus de la liste, sans carte ni titre « Recherche et filtres ». Sur téléphone : la recherche reste visible, les autres filtres s'ouvrent dans une feuille « Filtres (2) ». Les filtres sont lus et écrits dans l'adresse (`?status=planned`), ce qui permet aux compteurs de l'accueil d'ouvrir une liste déjà filtrée (DASH-23) et conserve les filtres à l'actualisation (principe 10). Les listes « tous les … » ont un libellé explicite (« Tous les statuts », pas « Status », CMDCLI-27). **Pages** : Commandes fournisseurs, Livraisons, Tâches, SAV, Commandes clients, DLC, Publicités, Avoirs, Rapprochement, Échéancier, Utilisateurs, Fiches fournisseurs, Contacts, Statistiques. **Constats** : DLC-27, CMDCLI-38, ECHE-07, ORD-15, PUB-23, SAV-25, ANA-13, TASKS-14, USERS-09. ##### `DataTable` responsive ```tsx d.id} columns={[ { id: "date", header: "Date prévue", cell: (d) => formatDate(d.scheduledDate) }, { id: "supplier", header: "Fournisseur", cell: (d) => d.supplier?.name }, { id: "store", header: "Magasin", cell: …, hideBelow: "lg", onlyWhenAllStores: true }, { id: "status", header: "Statut", cell: (d) => }, { id: "amount", header: "Montant", cell: (d) => formatEuro(d.blAmount), align: "right" }, ]} onRowClick={openDetail} primaryAction={(d) => d.status === "planned" && canValidate ? { label: "Valider la réception", onClick: … } : null} rowActions={(d) => [ { label: "Modifier", icon: Pencil, onClick: … }, { label: "Supprimer", icon: Trash2, tone: "danger", onClick: … }, // passe par ConfirmDialog ]} mobileCard={(d) => } // rendu sous 768 px query={deliveriesQuery} // gère squelette, erreur et vide empty={} pagination={{ page, pageSize, total, onPageChange }} /> ``` Ce que le composant garantit sans effort de la page : défilement horizontal au lieu de colonnes coupées (`overflow-x-auto`), colonnes secondaires masquées sur écran étroit, cartes sous 768 px, une seule action texte + menu « ⋯ » libellé (les boutons-icônes reçoivent automatiquement un nom accessible), lignes cliquables avec un vrai retour au survol, montants alignés à droite en chiffres tabulaires, colonne Magasin ajoutée en mode « Tous les magasins », page conservée après une action (la pagination ne revient en page 1 que si les filtres changent), états de chargement / erreur / vide intégrés. **Pages** : Commandes fournisseurs, Livraisons, Rapprochement (les deux onglets, mêmes colonnes et même règle d'écart), Échéancier, Avoirs, Commandes clients, DLC, Publicités, Utilisateurs, Fiches fournisseurs (au lieu de la grille de cartes), Tâches (vue liste), liste des sauvegardes. **Constats** : ORD-11, PUB-22, USERS-14, DLC-43, CMDCLI-24, RAPPRO-19, RAPPRO-14, RAPPRO-15, AVOIRS-07, DLC-23, ORD-10, TASKS-15, SAV-20, SUPP-05, BACKUP-07, ORD-14, RAPPRO-20, DLC-51, ECHE-05. ##### Trois petits composants complémentaires | Composant | API | Pourquoi | Constats | |---|---|---|---| | `IconButton` | `` : `label` **obligatoire**, devient `aria-label` + infobulle | Plus aucun bouton-icône muet ; 40 px PC / 44 px mobile | ORD-10, TASKS-15, DLC-41, PUB-27, USERS-23, SUPP-12, SHELL-21, BACKUP-07, NOCO-04, CMDCLI-25, SAV-20, RAPPRO-27 | | `FormDialog` | `` : `Dialog` sur PC, `Sheet` plein écran sur téléphone, pied normé, hauteur max, garde « Abandonner les modifications ? » | Fenêtres faites main sans Échap ni focus, boutons hors écran, saisie perdue | TASKS-16, CMDCLI-36, CMDCLI-20, PUB-31, SAV-19, MOD-09, GROUPS-13 | | `AccessDenied` | `` : « Vous n'avez pas accès à cette page » + « Retour à l'accueil » | Utilisé par `ProtectedRoute` | SHELL-11, RAPPRO-17 | --- ### 4. Accessibilité et lisibilité : actions concrètes #### 4.1 Actions Effort selon l'échelle commune (S, M). Les actions marquées « immédiat » sont sans risque et peuvent partir tout de suite. | N° | Action | Où | Constats | Effort | |---|---|---|---|---| | A1 (immédiat) | Déclarer la page en français : `` (*fait par le lot 1*, P4 § 2.1). Ensuite, quand tous les champs sur téléphone sont en 16 px, retirer `maximum-scale=1` pour autoriser le zoom. | `client/index.html` ; champs de `TasksPage.tsx`, formulaires mobiles | SHELL-20, DASH-34 | S | | A2 (immédiat) | Donner un nom à **chaque** bouton-icône (`aria-label` + `title`, libellés du § 3.6) en attendant `IconButton` ; transformer les `div` cliquables (« Plus », « BAP », badge de statut, bandeau d'appels) en `