xpeditis2.0/docs/ui-audit.md
2026-08-13 12:26:46 +02:00

28 KiB
Raw Permalink Blame History

UI Audit — Cartographie de l'application Xpeditis

Date : 2026-08-12 · Branche : ui_update · Périmètre : apps/frontend Volumétrie : 70 routes page.tsx · 126 fichiers .tsx · ~38 000 lignes · 51 pages 'use client'

Document d'observation. Aucune UI n'a été modifiée. À lire avec design-system-audit.md, qui verrouille la charte.


Sommaire


A. Synthèse — 12 problèmes transversaux

# Problème Mesure Gravité
A1 Deux identités visuelles. Le produit est bleu Tailwind générique ; la marque est navy + turquoise. 816 blue-* / 64 fichiers 🔴 Critique
A2 Design system non adopté. Les primitives existent mais ne sont pas utilisées. 299 <button> vs 17 <Button> · 175 <input> vs 16 <Input> · 26 <table> vs 2 <Table> 🔴 Critique
A3 Primitives cassées. 6 paquets Radix installés, 0 import Radix dans le code. Select n'affiche jamais sa valeur, Popover n'est pas portalisé, Dialog n'a ni focus trap ni Escape. ui/select.tsx, popover.tsx, dialog.tsx, command.tsx 🔴 Critique
A4 Aucun système de feedback. Pas de toast. Le retour utilisateur passe par alert() / confirm() natifs — modaux, bloquants, non traduits, non stylés. 58 appels natifs / 18 fichiers 🔴 Critique
A5 Titre dupliqué. La topbar affiche le nom de l'entrée de nav active et chaque page affiche son propre <h1>. Deux titres empilés sur chaque écran. dashboard/layout.tsx + toutes les pages 🟠 Majeur
A6 Pas de fil d'Ariane. Aucun composant Breadcrumb. Sur /dashboard/settings/users ou /dashboard/wiki/incoterms, aucun repère de profondeur ni retour. — 🟠 Majeur
A7 États non mutualisés. Loading, empty, error, skeleton réécrits à la main sur chaque page. 40 fichiers animate-spin · 27 empty states · 29 error states · 1 skeleton 🟠 Majeur
A8 Zéro adaptation grand écran. 2xl: n'apparaît jamais. Tout est plafonné à max-w-7xl (1280px). 0 occurrence 2xl: 🟠 Majeur
A9 Tablette non traitée. Palier md (768–1023px) 2× moins couvert que sm et lg. La sidebar reste un drawer alors que le contenu est déjà en grille desktop. 120 md: vs 287 sm: / 259 lg: 🟠 Majeur
A10 Tables non responsives. 21 fichiers gèrent le débordement par overflow-x-auto. Sur mobile : scroll horizontal, pas d'alternative en cartes. 21 fichiers 🟠 Majeur
A11 Accessibilité minimale. 29 aria-label pour 299 boutons. 9 role=. Focus focus: (454) au lieu de focus-visible: (13) → anneau visible à la souris. Aucun prefers-reduced-motion. — 🟠 Majeur
A12 Pages monolithiques. Aucune séparation composants/page. 25 useState dans un seul fichier de 1 169 lignes. documents/page.tsx 1169 l. · admin/blog 1341 l. · search-advanced 1067 l. 🟡 Moyen

B. Cartographie par zone

Légende des colonnes de métriques : L = lignes · B = occurrences blue-* · N = appels alert()/confirm() natifs · S = useState · PH = utilise PageHeader


B1. Chrome applicatif (layouts)

app/[locale]/layout.tsx — Root layout

Objectif : Injecter polices, i18n (next-intl), providers (React Query, Auth, Subscription, Cookie), métadonnées SEO.

Problème Type
<html> ne porte jamais class="dark" → le dark mode défini dans globals.css est mort Incohérence
Le favicon et l'image OpenGraph pointent vers logo-black.svg (1875×1699) — inadapté au format OG 1200×630 UI
Pas de <Toaster> monté (aucun système de toast) Manque

app/[locale]/dashboard/layout.tsx — App shell produit

Objectif : Sidebar + topbar + bottom-nav + garde d'authentification + gating par plan.

Composants : NotificationDropdown, LanguageSwitcher, StatusBadge, next/image, icônes lucide.

Problème Type
Item de sidebar actif en bg-blue-50 text-blue-700 — hors charte 🔴 UI
Avatar sidebar bg-blue-600 et avatar topbar bg-blue-600 — deux avatars du même utilisateur visibles simultanément 🔴 UI + UX
Bottom-nav active en text-blue-600 — hors charte 🔴 UI
Double titre : la topbar affiche le nom de nav actif, la page affiche son <h1> 🟠 UX
Sidebar w-64 fixe, non repliable — 256px perdus en permanence sur les vues denses (tables) 🟠 UX
Bottom-nav = 5 entrées sur 8. Wiki, Organisation, Clés API absents de la nav mobile primaire 🟠 UX
SVG burger et croix écrits à la main alors que Menu/X de lucide sont disponibles 🟡 Cohérence
Aucun état actif sur le sous-arbre /dashboard/settings/* : trois entrées de nav pointent vers des enfants de settings sans regroupement visuel 🟠 UX
Aucune recherche globale / command menu 🟠 Manque
logo-black.svg rendu en width=50 height=60 avec className="h-auto" → ratio non contrôlé 🟡 UI
Bouton logout text-red-700 bg-red-50 pleine largeur en pied de sidebar : poids visuel disproportionné pour une action rare 🟠 Hiérarchie
Overlay mobile bg-gray-600 bg-opacity-75 (syntaxe Tailwind v2 legacy) 🟡 Cohérence
Aucun focus trap dans le drawer mobile ouvert 🟠 A11y

app/[locale]/admin/layout.tsx — App shell admin

Objectif : Même shell, thème navy, garde de rôle ADMIN, exception pour /admin/login.

Problème Type
✅ Seule zone conforme à la charte (sidebar bg-brand-navy, logo blanc) —
Structure dupliquée à 80 % avec dashboard/layout.tsx (drawer, overlay, burger, garde, pied) sans mutualisation 🟠 Duplication
Pas de bottom-nav mobile (contrairement au dashboard) → incohérence de navigation entre les deux espaces 🟠 UX
Pas de breadcrumb 🟠 UX

B2. Zone Dashboard — produit

Route Objectif L B N S PH
/dashboard Vue d'ensemble KPI + graphiques 454 6 0 0 ❌
/dashboard/bookings Liste des réservations 908 9 0 9 ✅
/dashboard/bookings/[id] Détail réservation 284 4 1 0 ❌
/dashboard/bookings/new Création réservation 871 31 0 4 ❌
/dashboard/booking/new Création réservation (doublon) 736 18 0 7 ❌
/dashboard/booking/[id]/pay Paiement Stripe 621 20 0 12 ❌
/dashboard/booking/[id]/payment-success Confirmation paiement 146 9 2 3 ❌
/dashboard/documents Gestion documentaire 1169 27 9 25 ✅
/dashboard/search Recherche de taux 614 13 0 9 ❌
/dashboard/search-advanced Tunnel de recherche multi-étapes 1067 28 0 11 ❌
/dashboard/search-advanced/results Résultats de recherche 520 18 0 6 ❌
/dashboard/track-trace Suivi d'expédition 642 27 0 8 ❌
/dashboard/notifications Centre de notifications 410 13 1 3 ❌
/dashboard/profile Profil utilisateur 453 15 0 7 ❌
/dashboard/settings/organization Paramètres organisation 524 15 0 8 ❌
/dashboard/settings/users Gestion des membres 848 16 3 9 ✅
/dashboard/settings/api-keys Clés API 457 6 0 7 ✅
/dashboard/settings/subscription Abonnement (redirection) 27 1 0 0 ❌
/dashboard/docs Redirection docs 7 0 0 0 ❌
/dashboard/admin/[[...rest]] Redirection vers /admin 11 0 0 0 ❌

Détail des pages critiques

/dashboard — Vue d'ensemble Composants : Card, Button, Badge, ExportButton, Recharts (PieChart, BarChart).

  • 🔴 CTA principal bg-blue-600 hover:bg-blue-700 shadow-lg — hors charte, et shadow-lg sur un bouton est un poids d'ombre de modale.
  • 🔴 <Link><Button> → <a> contenant <button> : HTML invalide, Button n'a pas de asChild.
  • 🔴 Couleurs de graphiques hardcodées hors charte : #10b981 / #ef4444 / #f59e0b (Pie), #3b82f6 (Bar), et fill="#8884d8" — le violet par défaut de Recharts, resté par oubli.
  • 🟠 10 cartes KPI toutes identiques visuellement, sur 3 grilles séparées (4 + 2 + 3) : aucune hiérarchie, l'œil ne sait pas quoi lire en premier.
  • 🟠 Contenu KPI centré (items-center text-center) : casse le balayage vertical d'un tableau de bord ; l'alignement à gauche est la norme pour des chiffres à comparer.
  • 🟠 Pastilles d'icône colorées (bg-green-100, bg-red-100, bg-amber-100, bg-blue-100, bg-purple-100) — 5 teintes, dont violet sans justification sémantique.
  • 🟠 Skeletons inline bg-gray-100 animate-pulse rounded répétés 8 fois.
  • 🟠 <h1> de page en text-xl sm:text-3xl alors que la topbar affiche déjà un titre.
  • 🟡 Graphiques non responsives sous 640px : angle={-15} sur l'axe X écrase les libellés.
  • 🟡 Tooltip Recharts par défaut : ni police ni couleurs de marque.
  • 🟡 Le double min-h-screen (layout + page) crée un conteneur imbriqué inutile.

/dashboard/bookings — Liste des réservations (908 l.)

  • 🟠 Menu contextuel positionné manuellement via menuPos {top, right} + listener global — se désaligne au scroll, ne gère pas le clavier, sort du viewport en bas de liste.
  • 🟠 Table brute en overflow-x-auto : 10+ colonnes illisibles sous 1024px.
  • 🟠 Filtres (recherche, type de recherche, statut) sans état « filtres actifs » ni réinitialisation.
  • 🟠 Pagination réimplémentée localement.
  • 🟡 Bandeau showTransferBanner géré en useState local, sans persistance.

/dashboard/documents (1 169 l., 25 useState, 9 alert/confirm) — page la plus dégradée

  • 🔴 3 modales réimplémentées inline (ajout / remplacement / suppression) au lieu d'un Dialog partagé.
  • 🔴 9 alert()/confirm() natifs pour toute la confirmation de suppression et le retour d'upload.
  • 🟠 Deux zones de glisser-déposer distinctes (isDraggingAdd, isDraggingReplace) avec logique dupliquée.
  • 🟠 Aucune barre de progression d'upload : uploadingFiles est un booléen global.
  • 🟠 Pagination + itemsPerPage réimplémentés.
  • 🟠 25 useState dans un seul composant : re-renders larges, état non testable.

/dashboard/search-advanced (1 067 l.) + /results (520 l.)

  • 🔴 28 + 18 occurrences de blue-* (barre de progression du tunnel, étapes actives, boutons).
  • 🟠 Tunnel multi-étapes (currentStep) sans indicateur de progression accessible ni possibilité de revenir en arrière par l'URL — l'état vit uniquement en mémoire ; un rechargement perd la saisie.
  • 🟠 4 useState distincts pour les autocomplétions de ports (originSearch, showOriginDropdown, destinationSearch, showDestinationDropdown) → composant PortAutocomplete à extraire.
  • 🟠 Dropdowns d'autocomplétion non portalisés → clipping possible.
  • 🟠 Aucune navigation clavier dans les listes de suggestions.

/dashboard/bookings/new (871 l.) vs /dashboard/booking/new (736 l.)

  • 🔴 Deux tunnels de création de réservation coexistent sous des routes quasi identiques (bookings/new et booking/new). Duplication majeure, risque de divergence fonctionnelle. À trancher avant refonte.

/dashboard/track-trace (642 l., 27 blue-*)

  • 🟠 Timeline de suivi entièrement stylée en bleu générique.
  • 🟠 Carte Leaflet (PortRouteMap) : hauteur fixe, non responsive ; marqueurs aux couleurs hors charte.

/dashboard/settings/*

  • 🟠 Trois pages de paramètres sans conteneur commun : pas d'onglets, pas de sous-navigation. L'utilisateur doit repasser par la sidebar.
  • 🟠 subscription/page.tsx (27 l.) est une simple redirection — route fantôme dans la nav.
  • 🟠 settings/users : 2 tables brutes + 3 confirm() natifs pour la révocation d'accès (action destructive).

/dashboard/profile (453 l., 15 blue-*)

  • 🟠 Formulaire sans état de sauvegarde clair (pas de « modifications non enregistrées »).
  • 🟠 Aucun feedback de succès autre qu'un bandeau inline.

B3. Zone Admin

Route Objectif L B N S PH
/admin Redirection 8 0 0 0 ❌
/admin/login Connexion admin dédiée 141 0 0 6 ❌
/admin/users Gestion utilisateurs 507 15 3 11 ✅
/admin/organizations Gestion organisations 832 8 9 12 ✅
/admin/bookings Réservations globales 735 6 2 12 ❌
/admin/documents Documents globaux 684 10 2 13 ✅
/admin/csv-rates Import grilles tarifaires 209 1 3 4 ❌
/admin/blog Éditeur d'articles 1341 30 11 19 ✅
/admin/logs Journaux d'audit 547 2 0 9 ✅
  • 🔴 /admin/blog : 1 341 lignes, 19 useState, 11 alert/confirm, 30 blue-*, 24 <button> bruts. Le fichier le plus lourd du projet. Éditeur TipTap + liste + formulaire SEO + planification, tout dans un composant.
  • 🔴 /admin/organizations : 9 alert/confirm — dont la validation SIRET et la suspension d'organisation (actions à fort impact) confirmées par confirm() natif.
  • 🟠 Le shell admin est navy mais le contenu des pages reste en bleu générique : rupture visuelle entre chrome et contenu.
  • 🟠 5 tables brutes, aucune mutualisée, chacune avec ses propres filtres et sa propre pagination.
  • 🟠 /admin/csv-rates : import de fichier sans prévisualisation ni rapport d'erreur structuré (3 alert()).
  • 🟠 /admin/logs : 547 lignes de journal sans virtualisation (@tanstack/react-virtual est pourtant installé).
  • 🟡 /admin/login : page de connexion distincte de /login, avec un design différent — duplication de formulaire d'auth.

B4. Zone Wiki

13 routes : page (index) · incoterms · conteneurs · lcl-vs-fcl · transit-time · calcul-fret · documents-transport · douanes · imdg · vgm · assurance · lettre-credit · ports-routes.

Objectif : contenu pédagogique métier (fret maritime) accessible aux clients selon leur plan.

Constat Type
🟠 13 pages statiques rédigées en JSX en dur (110–244 lignes chacune) : le contenu éditorial est dans le code Architecture
🟠 93 occurrences blue-* cumulées sur la zone : encadrés d'information, tableaux, badges — tous en bleu générique 🔴 UI
🟠 10 tables brutes dispersées, chacune avec son propre style Duplication
🟠 Pas de sommaire, pas d'ancres, pas de « page suivante/précédente », pas de recherche dans le wiki UX
🟠 Pas de breadcrumb : impossible de savoir où l'on est dans l'arborescence UX
🟠 Aucune largeur de lecture contrainte (max-w-prose) sur certaines pages → lignes de 1 200px, illisibles Lisibilité
🟡 Le plugin @tailwindcss/typography est installé mais n'est pas utilisé pour ces pages Manque
🟡 Encadrés d'information (bg-blue-50 border-blue-200) réécrits ~30 fois → composant Callout évident Duplication

B5. Zone Auth

Route Objectif L B S
/login Connexion 523 0 10
/register Inscription (multi-étapes) 832 0 25
/forgot-password Demande de réinitialisation 264 0 5
/reset-password Réinitialisation 336 0 10
/verify-email Vérification e-mail 151 6 4
/admin/login Connexion admin 141 0 6
  • ✅ Zone la plus conforme à la charte (0 blue-* sauf /verify-email).
  • 🟠 /register : 832 lignes, 25 useState — tunnel multi-étapes (compte / organisation / SIRET / plan) sans machine à états ni validation par étape mutualisée. react-hook-form + zod sont installés mais non utilisés ici.
  • 🟠 4 mises en page d'auth distinctes (/login avec image de fond, /register pleine largeur, /forgot-password centré, /admin/login minimal) → aucun AuthLayout partagé.
  • 🟠 /verify-email : 6 blue-* — seule fuite de la zone.
  • 🟠 Pas de mesure de robustesse du mot de passe visible sur /register ni /reset-password.
  • 🟡 États d'erreur de formulaire en bandeaux inline, différents d'une page à l'autre.

B6. Portail Transporteur & Booking public

Route Objectif L B S
/carrier/accept/[token] Acceptation par magic link 144 3 4
/carrier/reject/[token] Refus par magic link 144 1 4
/carrier/documents/[token] Dépôt de documents transporteur 589 0 10
/booking/confirm/[token] Confirmation client 300 7 4
/booking/reject/[token] Refus client 363 4 8
  • 🟠 accept et reject sont deux fichiers de 144 lignes quasi identiques (même structure, même mise en page, variable d'action différente).
  • 🟠 Même remarque pour booking/confirm (300 l.) et booking/reject (363 l.).
  • 🟠 Aucun chrome : ces pages sont sans en-tête ni logo persistant — le transporteur ne sait pas sur quelle plateforme il se trouve.
  • 🟠 Pas d'état d'expiration de lien distinct de l'état d'erreur générique (le magic link expire à 1 h).
  • 🟠 /carrier/documents/[token] (589 l.) : upload sans progression, 1 alert() natif.
  • 🟡 Pas de version imprimable des pages de confirmation.

B7. Zone Marketing / Public

Route Objectif L B
/ Landing 1082 1
/pricing Grille tarifaire 334 0
/about À propos 414 1
/contact Contact 681 1
/careers Recrutement 574 0
/press Presse 509 0
/security Sécurité 274 0
/compliance Conformité 361 0
/privacy /terms /cookies Légal 182 / 190 / 313 0

Composants : LandingHeader (388 l.), LandingFooter, DemoVideoModal, CookieConsent, framer-motion.

  • ✅ Charte respectée. C'est la référence visuelle de la marque.
  • 🟠 / (1 082 l.) : page monolithique, aucune section extraite en composant.
  • 🟠 Rayons rounded-2xl / rounded-3xl et shadow-2xl employés ici uniquement → écart de langage visuel avec le produit (rounded-lg, shadow-sm).
  • 🟠 Dégradés navy avec nuances inventées (#1A2550, #1A2A5E) hors charte.
  • 🟠 8 pages légales/institutionnelles avec 8 mises en page rédactionnelles différentes → un LegalPageLayout s'impose.
  • 🟠 Logos partenaires : fichiers PNG aux noms contenant des espaces (Rectangle 4.png, TCC LOG 1.png) — fragile en URL, et Rectangle 4.png / image 1.png ne sont pas nommés.
  • 🟡 framer-motion (16 fichiers) concentré ici : animations d'apparition au scroll, sans prefers-reduced-motion.

B8. Blog & Docs

Route Objectif L
/blog Liste des articles 424
/blog/[slug] Article (+ BlogPostContent 350 l.) 152
/docs/api Documentation API 7
/docs/layout.tsx Shell docs —

Composants : DocsPageContent (1 506 lignes — le plus gros fichier du projet), CodeBlock, docsNav.ts, RichTextEditor (TipTap, 381 l.).

  • 🔴 DocsPageContent.tsx : 1 506 lignes, contenu de documentation API entièrement en JSX. Non maintenable, non recherchable.
  • 🟠 CodeBlock utilise un thème GitHub Dark hardcodé (#0D1117, #161B22) sans lien avec la charte.
  • 🟠 Le blog utilise .prose (@tailwindcss/typography) avec des overrides de marque dans globals.css — le seul endroit du projet où la typographie éditoriale est correctement traitée. Le wiki devrait suivre le même modèle.
  • 🟡 Pas de recherche ni d'ancrage dans les docs.

B9. Routes techniques / mortes

Route Statut
/test-image (49 l.) 🔴 Page de test à supprimer
/demo-carte (72 l.) 🔴 Démo Leaflet à supprimer (6 blue-*)
/dashboard/docs (7 l.) Redirection
/dashboard/admin/[[...rest]] (11 l.) Redirection legacy vers /admin
/dashboard/settings/subscription (27 l.) Redirection
src/app/rates/csv-search/page.tsx 🟠 Page hors app/[locale] — non internationalisée, non protégée par le middleware, hors du shell. Route orpheline.
src/legacy-pages/ (BookingsManagement, CarrierManagement, CarrierMonitoring) 🟠 Code mort (3 alert() dans CarrierManagement)
src/components/examples/DesignSystemShowcase.tsx (320 l.) 🟡 Vitrine du DS — à conserver et mettre à jour, ou supprimer

C. Composants à mutualiser

C1. À créer (n'existent pas)

Composant Remplace Occurrences éliminées
Toast / Toaster alert(), confirm() natifs, bandeaux inline 58 appels natifs
ConfirmDialog confirm() natif sur actions destructives ~14
Skeleton bg-gray-100 animate-pulse rounded inline ~30
EmptyState Blocs vides réécrits à la main 27
ErrorState Bandeaux d'erreur inline 29
Spinner animate-spin + Loader2 ad hoc 40 fichiers
Breadcrumb — 0 (manque total)
Pagination Paginations locales 7 implémentations
DataTable (sur @tanstack/react-table) <table> bruts + tri + filtres + responsive 26 tables
Drawer Panneaux latéraux ad hoc —
CommandMenu (⌘K) — 0 (manque total)
Callout Encadrés bg-blue-50 border-blue-200 du wiki ~30
AuthLayout 4 mises en page d'auth distinctes 4
SettingsLayout Sous-navigation des paramètres 3 pages
AppShell Base commune dashboard/admin 2 layouts à 80 % dupliqués
FormField <label> + <input> + erreur 175 inputs bruts
PortAutocomplete Autocomplétion de ports 2× dupliquée
FileDropzone Zones de glisser-déposer 4 implémentations
StatCard Cartes KPI 10 sur /dashboard, + admin

C2. À réparer (existent mais cassés ou non adoptés)

Composant Action
ui/button.tsx Réécrire sur cva + cn() + asChild (Radix Slot). Corriger hover:bg-accent (turquoise plein). Ajouter loading, active:.
ui/input.tsx Passer le focus sur le turquoise de marque. Ajouter états error, préfixe/suffixe, tailles.
ui/select.tsx Réécrire sur @radix-ui/react-select (déjà installé) — SelectValue est non fonctionnel.
ui/dialog.tsx Réécrire sur @radix-ui/react-dialog — portal, focus trap, Escape, scroll-lock, animations.
ui/popover.tsx Réécrire sur @radix-ui/react-popover — portalisation.
ui/command.tsx Compléter ou fonder le CommandMenu.
ui/card.tsx Ramener CardTitle à une taille réaliste (text-base/text-lg), padding par défaut p-4/p-5.
ui/table.tsx Base du DataTable ; densités compacte/confortable.
ui/badge.tsx Ajouter les variants sémantiques réellement utilisés (success, warning, info, neutral) avec les tokens de charte.
ui/PageHeader.tsx Adopté sur 9 pages seulement. Ajouter breadcrumb, onglets, description, actions secondaires. Généraliser.
ui/alert.tsx Aligner sur Callout ou fusionner.
ui/FeatureGate.tsx 0 usage — le gating est réimplémenté dans dashboard/layout.tsx. À adopter ou supprimer.

D. Duplications identifiées

# Duplication Fichiers Volume
D1 Deux tunnels de création de réservation dashboard/bookings/new (871 l.) + dashboard/booking/new (736 l.) 1 607 l.
D2 Shell applicatif dashboard/layout.tsx + admin/layout.tsx ~80 % commun
D3 Pages accept/reject transporteur carrier/accept/[token] + carrier/reject/[token] 2 × 144 l.
D4 Pages confirm/reject booking booking/confirm/[token] + booking/reject/[token] 663 l.
D5 Formulaires de connexion /login + /admin/login 664 l.
D6 Systèmes de bouton ui/button.tsx + .btn-* (globals.css) + 299 <button> bruts 3 systèmes
D7 Systèmes d'input ui/input.tsx + .input (globals.css) + 175 <input> bruts 3 systèmes
D8 Paginations 7 implémentations locales —
D9 Zones de glisser-déposer documents (×2), admin/documents, carrier/documents 4
D10 Encadrés d'information 13 pages wiki ~30
D11 Mises en page légales 8 pages institutionnelles 8
D12 Icônes burger/close en SVG inline 2 layouts doublon de lucide
D13 Échelles neutres concurrentes gray-* (2 485) vs neutral-* (92) —

E. Tableau de priorisation

Lot Contenu Impact Effort Ordre
L0 — Fondations Réparer les primitives (Button, Input, Select, Dialog, Popover sur Radix) · cn() partout · corriger --ring · ajouter Toast, Skeleton, EmptyState, ErrorState, Spinner, Pagination, Breadcrumb, Callout, FormField 🔴 Maximal M 1
L1 — App shell AppShell mutualisé · sidebar repliable · suppression du double titre · breadcrumb · bottom-nav complète · command menu ⌘K · restauration charte navy/turquoise sur le chrome 🔴 Maximal M 2
L2 — Tables & listes DataTable sur @tanstack/react-table · responsive carte-sur-mobile · tri/filtres/pagination mutualisés · virtualisation des logs 🔴 Fort L 3
L3 — Dashboard & réservations /dashboard (hiérarchie KPI, charts à la charte) · /bookings · arbitrage D1 · /bookings/[id] 🔴 Fort L 4
L4 — Documents & recherche /documents (démonolithisation, FileDropzone, modales Radix) · /search-advanced (état en URL, PortAutocomplete) · /track-trace 🔴 Fort L 5
L5 — Paramètres & profil SettingsLayout avec onglets · /profile · /settings/* 🟠 Moyen M 6
L6 — Admin Alignement chrome/contenu · démonolithisation de /admin/blog · suppression des alert/confirm 🟠 Moyen L 7
L7 — Wiki & docs Callout · .prose · sommaire + ancres · largeur de lecture · breadcrumb 🟠 Moyen M 8
L8 — Auth & portails AuthLayout · fusion accept/reject · états d'expiration de lien · robustesse mot de passe 🟠 Moyen S 9
L9 — Marketing Extraction en sections · alignement des rayons/ombres sur le produit · suppression des nuances inventées 🟡 Faible M 10
L10 — Nettoyage Supprimer /test-image, /demo-carte, src/legacy-pages/ · trancher src/app/rates/csv-search · gray-* → neutral-* 🟡 Faible S 11

Effort : S = < 1 j · M = 1–3 j · L = 3–5 j


F. Points nécessitant un arbitrage produit

Ces questions ne peuvent pas être tranchées par l'audit seul :

  1. D1 — Lequel des deux tunnels de réservation (bookings/new vs booking/new) est le tunnel actif ? L'autre peut-il être supprimé ?
  2. src/app/rates/csv-search — route orpheline hors i18n et hors middleware : à migrer sous app/[locale]/dashboard/ ou à supprimer ?
  3. src/legacy-pages/ — code mort confirmé ? Suppression autorisée ?
  4. Dark mode — l'implémenter réellement, ou retirer le scaffolding .dark mort ?
  5. Wiki — le contenu reste-t-il en JSX, ou passe-t-il en MDX / CMS ?
  6. §23 du design audit — validation explicite du remplacement blue-* → tokens de marque sur les 64 fichiers concernés.