420 lines
28 KiB
Markdown
420 lines
28 KiB
Markdown
# 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`](./design-system-audit.md), qui verrouille la charte.
|
||
|
||
---
|
||
|
||
## Sommaire
|
||
|
||
- [A. Synthèse — 12 problèmes transversaux](#a-synthèse--12-problèmes-transversaux)
|
||
- [B. Cartographie par zone](#b-cartographie-par-zone)
|
||
- [B1. Chrome applicatif](#b1-chrome-applicatif-layouts)
|
||
- [B2. Zone Dashboard (produit)](#b2-zone-dashboard--produit)
|
||
- [B3. Zone Admin](#b3-zone-admin)
|
||
- [B4. Zone Wiki](#b4-zone-wiki)
|
||
- [B5. Zone Auth](#b5-zone-auth)
|
||
- [B6. Portail Transporteur & Booking public](#b6-portail-transporteur--booking-public)
|
||
- [B7. Zone Marketing / Public](#b7-zone-marketing--public)
|
||
- [B8. Blog & Docs](#b8-blog--docs)
|
||
- [B9. Routes techniques / mortes](#b9-routes-techniques--mortes)
|
||
- [C. Composants à mutualiser](#c-composants-à-mutualiser)
|
||
- [D. Duplications identifiées](#d-duplications-identifiées)
|
||
- [E. Tableau de priorisation](#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, 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.
|