28 KiB
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
- B. Cartographie par zone
- C. Composants à mutualiser
- D. Duplications identifiées
- E. Tableau de priorisation
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, etshadow-lgsur un bouton est un poids d'ombre de modale. - 🔴
<Link><Button>→<a>contenant<button>: HTML invalide,Buttonn'a pas deasChild. - 🔴 Couleurs de graphiques hardcodées hors charte :
#10b981/#ef4444/#f59e0b(Pie),#3b82f6(Bar), etfill="#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 roundedrépétés 8 fois. - 🟠
<h1>de page entext-xl sm:text-3xlalors 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
showTransferBannergéré enuseStatelocal, 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
Dialogpartagé. - 🔴 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 :
uploadingFilesest un booléen global. - 🟠 Pagination +
itemsPerPageréimplémentés. - 🟠 25
useStatedans 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
useStatedistincts pour les autocomplétions de ports (originSearch,showOriginDropdown,destinationSearch,showDestinationDropdown) → composantPortAutocompleteà 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/newetbooking/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 + 3confirm()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, 19useState, 11alert/confirm, 30blue-*, 24<button>bruts. Le fichier le plus lourd du projet. Éditeur TipTap + liste + formulaire SEO + planification, tout dans un composant. - 🔴
/admin/organizations: 9alert/confirm— dont la validation SIRET et la suspension d'organisation (actions à fort impact) confirmées parconfirm()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é (3alert()). - 🟠
/admin/logs: 547 lignes de journal sans virtualisation (@tanstack/react-virtualest 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, 25useState— tunnel multi-étapes (compte / organisation / SIRET / plan) sans machine à états ni validation par étape mutualisée.react-hook-form+zodsont installés mais non utilisés ici. - 🟠 4 mises en page d'auth distinctes (
/loginavec image de fond,/registerpleine largeur,/forgot-passwordcentré,/admin/loginminimal) → aucunAuthLayoutpartagé. - 🟠
/verify-email: 6blue-*— seule fuite de la zone. - 🟠 Pas de mesure de robustesse du mot de passe visible sur
/registerni/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 |
- 🟠
acceptetrejectsont 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.) etbooking/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, 1alert()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-3xletshadow-2xlemployé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
LegalPageLayouts'impose. - 🟠 Logos partenaires : fichiers PNG aux noms contenant des espaces (
Rectangle 4.png,TCC LOG 1.png) — fragile en URL, etRectangle 4.png/image 1.pngne sont pas nommés. - 🟡
framer-motion(16 fichiers) concentré ici : animations d'apparition au scroll, sansprefers-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. - 🟠
CodeBlockutilise un thème GitHub Dark hardcodé (#0D1117,#161B22) sans lien avec la charte. - 🟠 Le blog utilise
.prose(@tailwindcss/typography) avec des overrides de marque dansglobals.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 :
- D1 — Lequel des deux tunnels de réservation (
bookings/newvsbooking/new) est le tunnel actif ? L'autre peut-il être supprimé ? src/app/rates/csv-search— route orpheline hors i18n et hors middleware : à migrer sousapp/[locale]/dashboard/ou à supprimer ?src/legacy-pages/— code mort confirmé ? Suppression autorisée ?- Dark mode — l'implémenter réellement, ou retirer le scaffolding
.darkmort ? - Wiki — le contenu reste-t-il en JSX, ou passe-t-il en MDX / CMS ?
- §23 du design audit — validation explicite du remplacement
blue-*→ tokens de marque sur les 64 fichiers concernés.