# kView INVESTOR — Stack cible et roadmap de migration frontend

> Statut : phase 9 terminée — la migration Blade → Inertia/Vue est complète.
> Toutes les routes kView INVESTOR utilisent Inertia/Vue, et le vieux shell Blade
> (`layouts.dashboard`/`layouts.app`, bundle `app.js`/`app.css`, une
> quinzaine de modules vanilla) a été retiré après vérification
> consommateur par consommateur
>
> Mise à jour : 22 juillet 2026
>
> Public : produit, développeurs intermédiaires et développeurs confirmés

## 1. Objet du document

Ce document fixe une direction technique simple à expliquer et une méthode de
migration progressive pour kView INVESTOR.

Les objectifs sont les suivants :

- conserver Laravel, PHP et le métier existant ;
- introduire Vue 3 et TypeScript sans réécriture globale ;
- remplacer progressivement la navigation partielle maison par Inertia ;
- préserver kView comme design system et actif réutilisable ;
- faire évoluer le tableau ERP sans perdre ses contrats financiers ;
- améliorer l'évolutivité, l'accessibilité, les tests et l'expérience de
  développement ;
- mesurer les performances au lieu de supposer qu'une nouvelle technologie est
  automatiquement plus rapide.

Ce plan n'est pas un redesign visuel. Il organise un changement de moteur
frontend tout en conservant le produit, ses règles métier et son identité.

## 2. Résumé en trente secondes

### Stack actuelle validée

```text
PHP 8.4 + Laravel 13
+ routes historiques : Blade + JavaScript vanilla + navigation partielle maison
+ routes produit migrées : Inertia 3 + Vue 3 + TypeScript
+ consultations et formulaires métier : taux de change, cash-flows, comptes de capital,
  investissements, fonds, General Partners et investisseurs
+ shell Vue/Inertia persistant sur les routes migrées
+ adaptateurs kView Vue + dictionnaire UI typé + shell Blade conservé sur les routes restantes
+ Tailwind 3 + CSS kView + Vite 8
```

### Stack cible

```text
PHP 8.4 + Laravel 13 + Inertia 3 + Vue 3 + TypeScript
+ Vite + Tailwind + kView
+ Reka UI uniquement sous certains composants kView
```

### Principe directeur

Laravel garde le backend, le métier, les permissions et la validation. Inertia
relie Laravel à Vue sans API frontend séparée. Vue gère l'interface. TypeScript
sécurise les contrats de données. kView reste l'unique design system visible.
Reka UI peut fournir certains comportements accessibles invisibles, sans
imposer de style.

La migration se fait route par route. Les versions Blade et Vue coexistent
jusqu'à atteindre la parité. Aucun composant fonctionnel n'est supprimé avant
que son remplaçant soit testé.

## 3. Situation actuelle

kView INVESTOR est aujourd'hui une application Laravel dont **toutes les routes
applicatives utilisent Inertia et Vue 3** ; il ne reste plus aucune route
kView INVESTOR rendue en Blade avec navigation partielle maison. `/search` a été la
première route produit rendue par Inertia, Vue 3 et TypeScript ; `/activity` a
ouvert le cutover métier de la phase 6, ensuite étendu aux consultations et
formulaires. La phase 7 a porté les sept index de production — General
Partners, Funds, Investors, Investments, Calls/Distributions, Capital Accounts
et Exchange Rates — dans le même shell Inertia/Vue (clôturée le 20 juillet
2026). La phase 8 a ensuite porté les dashboards (`/`, `/dashboard`) et la
phase 8.5 a traité tout le résidu Blade identifié hors périmètre de la phase
8 — Admin validate-capital-accounts, FlexImport, RayDoc imports, le doublon
Settings, puis `/login` et `/admin/dropdowns` (contrôleur du package vendor
`lncshared` remplacé par un contrôleur kView INVESTOR qui réutilise son service et
sa form request sans modifier le submodule) — clôturée le 22 juillet 2026.
Conséquence directe : le vieux layout Blade (`layouts.dashboard`/`layouts.app`)
et son bundle `app.js`/`app.css` n'avaient plus de route qui les charge ; la
phase 9 les a retirés le même jour, avec la cascade qu'elle a mise au jour en
chemin (voir cette phase pour le détail). **La migration Blade → Inertia/Vue
est terminée.**

### Points forts à conserver

- logique métier, authentification, permissions et validation dans Laravel ;
- composants publics `x-ui.*` et `x-erp.*` ;
- trois packages kView séparant le core, la visualisation et les données ;
- tokens, typographies, icônes et langage visuel kView ;
- catalogue d'icônes JSON canonique partagé entre PHP, JavaScript et Vue ;
- trente-et-un composants UI kView Vue dans `ui-core`, complétés par un
  provider typé, quatre adaptateurs Vue ciblés dans `ui-data` et trois dans
  `visualization` ;
- formats financiers, statuts et préférences d'affichage ;
- registre de colonnes et registre de cellules typées du tableau ERP ;
- contrats d'accessibilité déjà documentés ;
- Component Lab Laravel disponible sur `/__kview` en local et en test ;
- suite fonctionnelle automatisée, complétée par Vitest, Playwright et les
  tests de contrats des packages.

### Limites qui motivent l'évolution

Les limites historiques liées au routeur SPA maison (`ui-page-loading.js`) et
aux initialiseurs `kviewInit*` répliqués manuellement n'existent plus : ce
code a été retiré en phase 9, avec le reste du vieux shell Blade. Ce qui
reste :

- le tableau ERP pilote beaucoup de comportements en recherchant et modifiant
  directement des éléments du DOM ;
- Vue, TypeScript et Inertia servent désormais l'intégralité du produit :
  `/search`, `/activity`, les consultations, les formulaires métier, les sept
  index de production, les dashboards, l'administration (y compris
  `/admin/dropdowns`), les imports, les paramètres et `/login` ;
- les primitives simples nécessaires au pilote sont couvertes ; carte,
  confirmation, combobox, date picker, info card, tendance, badge de
  performance, cellule typée, table de détail, actions de ligne et drawer de
  prévisualisation possèdent leurs adaptateurs Vue. `KviewCrudTable` porte
  maintenant le CRUD d'index complet ; les autres surfaces de visualisation
  restent à porter ;
- l'analyse statique et le formatage PHP ne sont pas encore entièrement verts.

Ces limites ne signifient pas que le système actuel est mauvais. Il est déjà
riche et cohérent. Elles indiquent surtout que son coût de maintenance va
augmenter à mesure que le SaaS et le nombre de projets réutilisant kView
grandissent.

## 4. Explication simple de chaque technologie

| Brique | Rôle simple | Ce qu'elle ne fait pas |
| --- | --- | --- |
| PHP | Langage principal du backend. | Ne construit pas à lui seul une interface interactive moderne. |
| Laravel | Gère les routes, utilisateurs, sécurité, base de données, validation et métier. | Ne remplace pas Vue pour l'état interactif du navigateur. |
| Inertia | Fait le pont entre une route Laravel et une page Vue. | Ce n'est ni un backend ni un design system. |
| Vue 3 | Construit les écrans et garde leur état interactif cohérent. | Ne remplace pas Laravel ni les règles métier. |
| TypeScript | Vérifie la forme des données et beaucoup d'erreurs avant exécution. | N'améliore pas directement la vitesse du serveur. |
| Vite | Compile et découpe le code frontend pour le navigateur. | Ne définit ni le design ni l'architecture métier. |
| Tailwind | Fournit des utilitaires pour composer rapidement les layouts et ajustements. | Ne remplace pas les composants et contrats kView. |
| kView | Porte l'identité, les composants, les formats financiers et les règles UI. | Ne doit pas dépendre directement des modèles de kView INVESTOR. |
| Reka UI | Fournit des comportements Vue accessibles et non stylés pour certains composants complexes. | N'impose ni couleurs, ni police, ni padding, ni arrondis. |

### Analogie

- Laravel est le moteur et le back-office.
- Vue est le tableau de bord visible par l'utilisateur.
- Inertia est la transmission entre les deux.
- TypeScript est le contrôle qualité des pièces échangées.
- kView est l'identité et l'ergonomie du véhicule.
- Reka peut fournir des mécanismes invisibles, comme la fermeture centralisée
  ou l'assistance de direction, sans choisir l'apparence.

## 5. Architecture technique cible

```mermaid
flowchart LR
    U["Utilisateur"] --> V["Pages Vue 3 + TypeScript"]
    V --> K["Composants kView Vue"]
    K --> R["Reka UI optionnel et interne"]
    V <--> I["Inertia 3"]
    I <--> L["Laravel 13"]
    L --> M["Métier, permissions et validation"]
    L --> D["Base de données et services"]
    L --> P["Contrats kView PHP"]
    P --> I
```

### Répartition des responsabilités

#### Laravel possède

- authentification et autorisation ;
- routes et contrôleurs ;
- validation serveur ;
- requêtes, tris, filtres et pagination des gros volumes ;
- règles financières et métier ;
- imports, exports lourds et traitements asynchrones ;
- normalisation PHP des colonnes et données du tableau ;
- source de vérité des données persistées.

#### Inertia possède

- protocole de navigation entre Laravel et Vue ;
- historique navigateur et visites ;
- envoi des données de page ;
- rechargements partiels ;
- états de soumission et erreurs Laravel ;
- prefetch et conservation explicite de certains états.

#### Vue possède

- rendu des pages migrées ;
- état éphémère de l'interface ;
- composants et composition des écrans ;
- sélection, ouverture, fermeture et interactions locales ;
- mise à jour ciblée de l'interface.

#### kView possède

- tokens, typographies, icônes, couleurs, densité et responsive ;
- composants publics `Kview*` en Vue et `x-ui.*` / `x-erp.*` en Blade ;
- formatage des dates, montants, pourcentages et multiples ;
- statuts et sémantique financière ;
- contrats d'accessibilité ;
- tableau ERP et cellules typées ;
- Component Lab et tests de contrat.

### Packaging recommandé

À court et moyen terme, les packages Composer existants restent la méthode de
distribution principale pour les projets Laravel :

```text
kview/ui-core
kview/visualization
kview/ui-data
```

Chaque package peut exposer une entrée Vue/TypeScript compilée par Vite en plus
de son API Blade. Cela évite d'introduire immédiatement un registre NPM privé.
Ces entrées restent optionnelles : un projet qui consomme uniquement les
composants Blade ne charge ni Vue, ni Inertia, ni Reka dans son navigateur.

Nommage cible possible :

```text
@kview/ui-core/vue
@kview/visualization/vue
@kview/ui-data/vue
```

Un package NPM indépendant ne devient nécessaire que lorsqu'un second produit
non-Laravel doit consommer kView. La décision doit être basée sur un vrai
consommateur, pas sur une anticipation abstraite.

## 6. Pourquoi cette stack

### Pourquoi Laravel + Inertia

kView INVESTOR reste un monolithe : un dépôt, une authentification et un déploiement
principal. Inertia apporte l'expérience d'une application Vue sans imposer :

- une API REST supplémentaire pour chaque écran ;
- un second projet Nuxt séparé ;
- une duplication de la validation et de l'authentification ;
- une couche de mapping systématique entre API et interface.

Inertia remplace progressivement la plomberie de navigation maison. Il ne
remplace pas les contrôleurs Laravel ni les services métier.

### Pourquoi ne pas ajouter Nuxt ou le SSR immédiatement

kView INVESTOR est principalement un SaaS authentifié : le référencement public
n'est pas le besoin directeur. Ajouter Nuxt ou un serveur de rendu séparé
augmenterait le nombre de couches, les choix de routage et le déploiement sans
bénéfice immédiat démontré. Le SSR pourra être réévalué pour de futures pages
publiques ou si une mesure de performance le justifie.

### Pourquoi Vue 3

- cible souhaitée pour kView INVESTOR ;
- composants réutilisables et explicites ;
- état interactif plus prévisible que des recherches DOM dispersées ;
- intégration naturelle avec Inertia ;
- bonne adaptation aux tables, formulaires et dashboards d'un SaaS dense.

### Pourquoi TypeScript

- type les données reçues de Laravel ;
- sécurise les props et événements des composants ;
- rend les contrats du tableau et des cellules plus explicites ;
- facilite la contribution de nouveaux développeurs ;
- rend les refactors plus sûrs.

TypeScript n'est pas la source de vérité métier. Laravel reste responsable des
validations réelles et des permissions.

### Pourquoi ne pas utiliser Pinia immédiatement

Inertia et l'état local Vue couvrent d'abord la majorité des besoins. Pinia ne
sera ajouté que si plusieurs pages ont réellement besoin d'un état client
partagé, persistant et complexe. Éviter un store global prématuré limite le
couplage.

### Pourquoi Tailwind et du CSS kView

Tailwind sert principalement à :

- composer les layouts ;
- gérer les variantes responsive ;
- appliquer des espacements et ajustements simples ;
- réduire le CSS spécifique aux pages.

Le CSS sémantique kView reste pertinent pour :

- les primitives stables ;
- le tableau ERP dense ;
- les états complexes ;
- les animations et surfaces flottantes ;
- les composants distribués dans plusieurs projets.

L'objectif n'est pas d'obtenir 100 % de Tailwind. L'objectif est d'avoir une
seule source de vérité par comportement et par famille visuelle.

La migration vers une nouvelle version majeure de Tailwind doit rester un
chantier séparé de l'introduction d'Inertia et Vue.

> **Mise à jour du 22 juillet 2026** : dans la pratique, aucune page Vue ni
> aucun composant kView n'a jamais consommé de classe utilitaire Tailwind
> brute — le CSS sémantique kView a suffi seul. Confirmé à la clôture de la
> phase 9 puis en suivi immédiat, `tailwind.config.js` et le plugin PostCSS
> sont retirés du build kView INVESTOR. La dépendance npm `tailwindcss` elle-même
> reste en devDependency : `scripts/verify-ui-package-consumer.mjs` (le
> harness CI qui prouve que les packages kView fonctionnent chez un
> consommateur Blade-only) réutilise le binaire `node_modules/.bin/tailwindcss`
> de la racine pour construire sa fixture Tailwind, une dépendance de test,
> pas du build applicatif. La section ci-dessus reste comme trace de
> l'intention d'origine ; elle ne décrit plus l'état
> réel du dépôt.

### Pourquoi Reka UI reste optionnel

Reka UI n'est pas le design system. Il peut être utilisé sous un composant
kView pour éviter de reconstruire :

- navigation clavier ;
- rôles et relations ARIA ;
- focus initial, focus trap et restauration ;
- fermeture par Escape ou interaction extérieure ;
- comportements de combobox, menu, dialog ou select.

Règle d'architecture :

> Une page kView INVESTOR importe des composants kView, jamais directement Reka UI.

Usages potentiellement pertinents : `Dialog`, `AlertDialog`, `Combobox`,
`Select`, `DropdownMenu` et `Popover`.

Usages sans intérêt direct : boutons, cartes, badges, titres, cellules
financières, structure principale du tableau ERP et navigation Inertia.

Un composant existant, accessible et correctement testé n'est pas remplacé
uniquement pour adopter Reka.

## 7. Contrat de migration de kView

### Ce qui doit rester stable

- tokens et typographies ;
- noms et sens des variantes ;
- icônes ;
- formats financiers ;
- comportement responsive ;
- labels et traductions ;
- contrats clavier et ARIA ;
- API Blade tant que des consommateurs existent ;
- frontières entre `ui-core`, `visualization` et `ui-data`.

### Double adaptateur temporaire

Pendant la migration, kView expose deux formes de composants :

```text
Blade : <x-ui.*> et <x-erp.*>
Vue   : <Kview*>
```

Les deux formes partagent le même vocabulaire, les mêmes tokens et les mêmes
contrats. Elles peuvent avoir des implémentations internes différentes.

Exemple :

```text
<x-ui.confirm-dialog>  -> implémentation Blade/vanilla historique
<KviewConfirmDialog>   -> implémentation Vue, éventuellement basée sur Reka
```

### Source de vérité

- PHP reste la source de vérité pour la normalisation métier et serveur.
- TypeScript décrit la forme des données reçues et l'état client.
- Les tokens CSS restent la source de vérité visuelle.
- Des fixtures et tests de parité protègent les deux implémentations.

### Dictionnaire UI et navigation

Les textes par défaut et les icônes de ressources ne doivent pas être copiés
dans chaque composant Vue. Le chemin établi en phase 3 est :

```text
traductions Laravel + config/ui.php
    -> App\Support\KviewUiProps
    -> props Inertia `ui`
    -> KviewUiHost
    -> KviewUiProvider
    -> composants Kview* via useKviewUi()
```

Le package conserve des valeurs anglaises explicites lorsqu'il est utilisé
sans provider. kView INVESTOR injecte les traductions de l'hôte et le mapping
ressource-vers-icône. Un composant Vue n'accède donc ni au traducteur Laravel,
ni à `config/ui.php`.

Le dictionnaire concerne les primitives portables. Les labels propres au
produit et les destinations restent dans `ApplicationNavigation`, à la
frontière kView INVESTOR.

Les liens suivent une règle tout aussi explicite : `AppLink` rend un `Link`
Inertia uniquement lorsque la destination est marquée `inertia: true`; il rend
sinon un lien HTML natif. Ce composant appartient à kView INVESTOR. Le package kView
ne connaît que le point d'adaptation `linkComponent` injecté localement dans
le provider ;
elle n'est jamais sérialisée par Laravel.

Le Component Lab doit présenter les composants réels, jamais des copies HTML
qui pourraient diverger.

## 8. Stratégie spécifique au tableau ERP

Le tableau ERP doit être migré vers Vue à terme. Il n'est pas choisi comme
premier pilote parce qu'il concentre beaucoup de valeur et de risques.

### Ce qui reste côté Laravel/PHP

- `TableColumnRegistry` ;
- règles de permissions ;
- requêtes et gros volumes ;
- tri, recherche, filtres et pagination serveur lorsque nécessaire ;
- exports complets ou longs ;
- valeurs brutes nécessaires aux tris, filtres et exports.

### Ce qui passe côté Vue/TypeScript

- rendu du tableau et des cellules ;
- état de tri, filtre, sélection et colonnes visibles ;
- largeur et ordre des colonnes ;
- interactions de toolbar ;
- actualisation Inertia ;
- états de chargement ;
- drawers et surfaces locales.

### API cible

```vue
<KviewCrudTable
    :columns="columns"
    :rows="rows"
    :pagination="pagination"
/>
```

La façade Vue doit conserver les types canoniques existants :

```text
text, entity, link, status, date, amount, currency, number,
scaled-number, percentage, multiple, boolean, tag, multiselect,
quarter, year, label, sparkline
```

Reka peut aider pour les popovers de filtre ou le dialogue d'export. Il ne
remplace ni le tableau, ni ses cellules, ni son contrat financier.

### Règles de performance du tableau

- ne pas envoyer un dataset complet massif au navigateur ;
- conserver la pagination serveur pour les volumes importants ;
- conserver tri et filtre serveur lorsqu'ils portent sur l'ensemble des
  données ;
- virtualiser uniquement si une mesure prouve le besoin et après validation
  d'accessibilité ;
- traiter les exports lourds côté serveur ;
- préserver des lignes de hauteur stable et des skeletons de même géométrie.

## 9. Performance, accessibilité et qualité

### Performance : bénéfices attendus

- moins de documents HTML complets téléchargés et analysés ;
- layout persistant entre les pages ;
- rechargements partiels des données ;
- moins de destruction et de réinitialisation manuelle du DOM ;
- chargement différé des données secondaires ;
- découpage du bundle par route ou fonctionnalité.

### Performance : risques

- bundle initial plus lourd avec Vue et Inertia ;
- composants Vue trop génériques ou trop imbriqués ;
- trop de données envoyées au navigateur ;
- duplication Blade/Vue temporaire ;
- tableaux plus lents si tout est déplacé côté client sans pagination.

### Scénarios de mesure obligatoires

Avant chaque migration importante, mesurer l'ancien écran puis le nouveau :

1. premier chargement à froid de Home ;
2. navigation chaude Funds vers Investors ;
3. filtrage, tri et pagination d'un tableau ERP ;
4. soumission valide et invalide d'un formulaire ;
5. retour navigateur vers un écran avec scroll et filtres ;
6. affichage contraint avec sidebar ouverte et affichage mobile.

Mesures minimales : poids transféré, durée serveur, durée de navigation, LCP,
INP, longues tâches JavaScript et stabilité visuelle. Une migration ne doit pas
introduire de régression non documentée.

### Accessibilité cible

kView INVESTOR vise WCAG 2.2 niveau AA pour les parcours migrés.

Chaque composant ou écran doit vérifier :

- utilisation complète au clavier ;
- focus visible, initial et restauré ;
- titres, labels et relations ARIA ;
- Escape et fermeture extérieure adaptés au type de surface ;
- erreurs de formulaire reliées à leurs champs ;
- aucun sens transmis uniquement par la couleur ;
- contrastes suffisants ;
- respect de `prefers-reduced-motion` ;
- tailles tactiles et absence de débordement mobile ;
- lecteur d'écran sur les flux critiques.

Reka réduit certains risques mais ne remplace ni les tests manuels ni la
qualité du contenu accessible.

### Outillage qualité cible

| Couche | Outil ou contrôle |
| --- | --- |
| Backend | PHPUnit, tests de fonctionnalités Laravel |
| Qualité PHP | Pint, Larastan/PHPStan |
| Contrats packages | tests kView et fixture consommateur |
| Types frontend | TypeScript et `vue-tsc` |
| Qualité frontend | ESLint |
| Composants Vue | Vitest et Vue Test Utils |
| Parcours navigateur | Playwright |
| Accessibilité automatisée | axe-core dans les tests navigateur |
| Build | Vite en mode production |
| Visuel | Component Lab et captures de référence ciblées |

## 10. Roadmap de migration

### Vue d'ensemble

| Phase | Objectif | Charge relative | Dépend de |
| --- | --- | --- | --- |
| 0 | Baseline, décisions et garde-fous | S | aucune |
| 1 | Laravel 11 vers 12 puis 13 | S à M | phase 0 |
| 2 | Fondation Inertia, Vue et TypeScript | M | phase 1 |
| 3 | Adaptateur kView Vue | M, incrémental | phase 2 |
| 3.5 | Dictionnaire, liens et shell applicatif local | M | phase 3 |
| 4 | Première route pilote | S à M | phase 3.5 |
| 5 | Shell et navigation principale | L | phase 4 validée |
| 6 | Pages simples, détails et formulaires | L, incrémental | phase 5 |
| 7 | Tableau ERP Vue | XL | socle kView stable |
| 8 | Dashboards, graphiques et flux avancés | L | phases 5 à 7 selon écran |
| 8.5 | Résidu Blade post-phase 8 : décisions explicites et derniers écrans | S à M | phase 8 close |
| 9 | Retrait contrôlé du legacy | M, progressif | parité atteinte |

`S`, `M`, `L` et `XL` indiquent une complexité relative, pas un engagement de
calendrier.

Statut au 22 juillet 2026 : **la migration est terminée, phases 0 à 9
closes.** Le jalon frontend de phase 5 et les lots 6.1 à 6.5 sont terminés ;
les pages de consultation et les formulaires métier visés par la phase 6
utilisent le shell Inertia/Vue. Les lots 7.11–7.12 ont connecté les sept
index métier au même shell et la phase 7 est clôturée. La phase 8 a migré les
dashboards (`/`, `/dashboard`) ; la phase 8.5 a ensuite traité tout le résidu
Blade identifié hors périmètre de la phase 8 — Admin
validate-capital-accounts, FlexImport, RayDoc imports, le doublon Settings,
puis `/login` et `/admin/dropdowns` — si bien qu'il ne restait plus aucune
route kView INVESTOR sur le vieux shell Blade. La phase 9 a retiré ce shell
devenu mort — les deux layouts, son bundle `app.js`/`app.css` et sa
quinzaine de modules vanilla, plus la cascade découverte pendant l'exécution
(la branche fragment de Settings et ses composants de formulaire déjà
orphelins depuis la phase 6.4) — après vérification consommateur par
consommateur de chaque fichier retiré.
La validation UAT externe de la phase 0 reste une porte de promotion séparée.

### Phase 0 — Baseline et décisions

#### Objectif

Créer un point de comparaison fiable et empêcher la migration de devenir une
réécriture diffuse.

#### Actions

- adopter ce document comme décision d'architecture ;
- inventorier les routes Blade, modules JavaScript et composants kView ;
- identifier les parcours critiques : login, Passport, CRUD, imports,
  dashboard, tableau ERP et paramètres ;
- enregistrer les mesures de performance des scénarios de référence ;
- ajouter les premiers tests navigateur sur les parcours critiques ;
- documenter les erreurs PHPStan et Pint existantes comme baseline, puis ne pas
  en ajouter ;
- décider explicitement UUIDv4 ou UUIDv7 pour `RayDocImport` ;
- geler les nouvelles fonctionnalités de `ui-page-loading.js` aux corrections
  nécessaires.

#### Livrables

- inventaire des routes et statut `Blade`, `pilote`, `Inertia` ou `migrée` ;
- rapport de performance initial ;
- matrice de tests critiques ;
- décisions consignées.

Le relevé vivant de ces livrables est tenu dans
`dev_guide/frontend-migration-baseline.md`.

#### Avancement au 17 juillet 2026

- inventaire des routes, vues, modules JavaScript et composants : réalisé ;
- baseline PHPUnit, Pint, PHPStan, Composer et Vite : réalisée ;
- matrice des parcours critiques et décisions initiales : réalisées ;
- gel de l'extension de `ui-page-loading.js` : protégé par un test ;
- baseline navigateur : réalisée avec le fallback Playwright autorisé, sur
  Chromium desktop et mobile émulé ;
- quatre scénarios login/navigation réussis, sans erreur console pertinente,
  débordement horizontal ni violation axe ;
- défaut critique du nom accessible du menu compte mobile : corrigé et couvert ;
- dette de contraste axe `serious` : corrigée dans les tokens kView et protégée
  par un test WCAG AA ;
- timings de chargement, navigation chaude, poids des ressources, LCP, INP et
  longues tâches : enregistrés avec des seuils de non-régression ;
- tests backend Passport nécessaires à l'upgrade : réalisés ;
- validation d'un client Passport et de PostgreSQL réels : automatisée par
  `php artisan kview_investor:verify-uat` et maintenue comme porte du déploiement UAT.

#### Critère de sortie

La migration peut démontrer qu'un écran nouveau est au moins aussi fiable que
l'écran remplacé.

### Phase 1 — Laravel 11 vers Laravel 13

#### Objectif

Mettre le backend sur une version supportée avant de modifier le frontend.

#### Actions

1. Migrer Laravel 11 vers 12 dans un commit dédié.
2. Faire passer les tests et tester Passport, stockage et imports.
3. Migrer Laravel 12 vers 13 dans un second commit.
4. Mettre à jour `laravel/tinker`, PHPUnit et les dépendances associées.
5. Élargir les contraintes `illuminate/*` des trois packages kView pour
   Laravel 11, 12 et 13 tant que cette compatibilité est voulue.
6. Revoir la protection CSRF et les requêtes partielles maison.
7. Ajouter la configuration sécurisée de désérialisation du cache.
8. Mettre à jour la CI et la fixture consommateur pour tester Laravel 13 —
   réalisé dans le checkpoint CI Bitbucket du 18 juillet 2026.

#### Vérifications ciblées

- login et logout ;
- password grant, refresh token et API Passport ;
- `ProtocolGrant` personnalisé ;
- imports Flex et Raydocs ;
- stockage privé et avatars ;
- pagination ;
- commandes et queues ;
- base PostgreSQL de préproduction.

#### Checkpoint Laravel 12 — 17 juillet 2026

Le premier palier de la phase est validé localement :

- application migrée de Laravel 11.54 à Laravel 12.64 ;
- trois packages kView compatibles `^11.31 || ^12.0` et vérifiés comme archives
  propres dans un consommateur Laravel 11 puis Laravel 12 ;
- noms des routes API isolés sous `api.v1.*`, sans changement d'URL, afin de
  supprimer 29 collisions avec les routes web ;
- `route:cache` désormais fonctionnel et protégé par un test d'unicité ;
- nouveaux `RayDocImport` vérifiés en UUIDv7 ;
- disque `local` déjà explicitement fixé à `storage/app/private` ;
- validation d'avatar déjà limitée à JPG, JPEG, PNG et WebP, donc aucun SVG
  nouvellement accepté ou refusé ;
- cache de vues Laravel 11 à purger avec `php artisan view:clear` pendant le
  déploiement, car Laravel 12 change le hash interne des composants Blade
  anonymes ;
- 105 tests et 731 assertions réussis ;
- Passport, `ProtocolGrant`, refresh, imports, stockage et packages réussis ;
- dette PHPStan inchangée à 137 erreurs ;
- build Vite réussi, sans variation JavaScript et avec +0,36 % environ sur le
  CSS applicatif ;
- quatre scénarios Playwright desktop/mobile réussis, sans franchir les seuils
  de non-régression ;
- audit Composer sans vulnérabilité après mise à jour de `phpseclib/phpseclib`
  vers 3.0.55.

Ce checkpoint reste l'état de retour isolable entre les deux versions majeures.

#### Checkpoint Laravel 13 — 17 juillet 2026

Le second palier est validé localement sans introduire Inertia, Vue ni
TypeScript :

- application migrée de Laravel 12.64 à Laravel 13.20 ;
- `laravel/tinker` 3 et PHPUnit 12 installés conformément au guide officiel ;
- option de cache `serializable_classes` fixée à `false` : kView INVESTOR stocke des
  scalaires et tableaux, pas des objets PHP arbitraires ;
- préfixe Redis et cookie de session UAT explicités pour éviter un changement
  implicite de nom pendant le déploiement ;
- protection antifalsification Laravel 13 validée par le login invalide, le
  login valide et la navigation partielle réelle ;
- 111 tests et 748 assertions réussis sous PHPUnit 12 ;
- dette PHPStan inchangée à 137 erreurs ;
- caches de routes, vues et configuration construits puis supprimés avec
  succès ; toutes les migrations PostgreSQL locales sont appliquées ;
- packages kView installés depuis des archives propres, rendus avec Blade et
  compilés avec Vite sous Laravel 11.55, 12.64 et 13.20 ;
- build Vite réussi : CSS applicatif 198,57 kB, JavaScript applicatif
  112,01 kB ;
- quatre scénarios Playwright réussis en desktop et mobile, sans erreur console,
  débordement horizontal ni violation axe ;
- LCP observé entre 144 et 624 ms selon la page et le viewport, sous le seuil
  local de 2 500 ms ; INP de la navigation Fonds entre 24 et 32 ms, sous le
  seuil local de 200 ms ;
- audit Composer sans vulnérabilité connue.

La commande `kview_investor:verify-uat` contrôle ensuite, sur l'environnement réel,
PostgreSQL, les migrations, le client Passport, le password grant, l'appel API,
le refresh et la révocation des jetons de smoke. Son exécution requiert les
secrets UAT externes au dépôt et reste donc la porte de déploiement, pas une
incertitude sur la migration locale. Le runbook est
`dev_guide/uat-smoke-check.md`.

#### Checkpoint CI Bitbucket — 18 juillet 2026

Le dépôt possède désormais un `bitbucket-pipelines.yml` qui, une fois
Pipelines activé dans Bitbucket, s'exécute à chaque push. Il reconstruit PHP
8.4, Node 22 et PostgreSQL 16 dans un environnement isolé, puis contrôle
PHPUnit, les fichiers PHP modifiés avec Pint/PHPStan, TypeScript, ESLint,
Vitest, les consommateurs kView Laravel 11 à 13, le build Vite et la matrice
Playwright desktop/mobile. La base, les données de démonstration et le compte
navigateur sont éphémères ; aucun secret UAT ou production n'est requis.

La dette Pint/PHPStan globale antérieure reste explicite et n'autorise aucune
nouvelle dette sur les fichiers touchés. L'activation de Pipelines et la règle
Bitbucket imposant un build vert avant merge sur `master` sont documentées dans
`dev_guide/bitbucket-ci.md`.

#### Critère de sortie

Laravel 13 fonctionne en préproduction sans changement de frontend et sans
régression sur les parcours critiques.

### Phase 2 — Fondation Inertia, Vue 3 et TypeScript

#### Objectif

Ajouter la nouvelle stack à côté de Blade sans migrer encore les fonctionnalités
complexes.

#### Actions

- installer l'adaptateur Laravel Inertia et `@inertiajs/vue3` ;
- installer Vue 3 et TypeScript ;
- créer l'entrée Vite Vue et la page racine Inertia ;
- configurer `tsconfig`, alias kView, `vue-tsc` et ESLint ;
- ajouter Vitest et un test navigateur minimal ;
- définir les types de props partagés : utilisateur, permissions, flash,
  préférences et locale ;
- configurer le découpage de code par page ;
- définir la frontière stricte entre racines Blade et racines Vue.

#### Checkpoint phase 2 — 17 juillet 2026

La fondation est validée localement sans migrer de route métier :

- Inertia Laravel 3.1, Inertia Vue/Vite 3.6, Vue 3.5, TypeScript 5.9 et Vite 8
  sont verrouillés dans les manifests ;
- `resources/views/inertia.blade.php` est la racine serveur minimale des pages
  Vue, tandis que `resources/views/layouts/app.blade.php` reste propriétaire
  des écrans Blade ;
- `resources/js/inertia.ts` ne charge ni `app.js`, ni
  `ui-page-loading.js` ; un test d'architecture protège cette séparation ;
- le middleware partage un contrat typé stable pour utilisateur, permissions,
  flash, préférences d'affichage et locale, sans créer de configuration
  utilisateur pendant une simple lecture ;
- deux pages locales et de test, sous `/__kview/inertia`, prouvent navigation
  partielle, historique, état mémorisé, formulaire et validation Laravel ;
- le découpage Vite produit des chunks séparés pour `Index` et `Navigation` ;
- `vue-tsc`, ESLint, Vitest et Vue Test Utils sont configurés ;
- les tests Playwright passent sur Chromium desktop et mobile, conservent le
  même document pendant une navigation Inertia, ne débordent pas
  horizontalement et ne signalent aucune violation axe critique ;
- la suite PHP complète réussit avec 117 tests et 818 assertions ;
- PHPStan reste à 137 erreurs historiques, sans erreur dans les nouveaux
  contrôleur et middleware ;
- le build production et les audits Composer/npm réussissent sans
  vulnérabilité connue ;
- le cache des champs dynamiques est désormais invalidé après leur seeding,
  ce qui rend le seeder financier et le test Flex Investment déterministes.

Le guide d'implémentation et d'exploitation du socle est
`dev_guide/frontend-inertia-foundation.md`.

#### Règle de coexistence

`ui-page-loading.js` ne doit jamais remplacer ou intercepter le DOM à
l'intérieur d'une application Vue montée. Les racines Vue embarquées portent
`[data-frontend-owner="vue"]`; le runtime Blade peut rester actif autour, mais
il cède sur les liens, formulaires et l'historique de cette zone. Une route non
migrée utilise une navigation complète ou le mécanisme Blade historique ; une
route Inertia autonome utilise sa racine dédiée.

#### Critère de sortie

Une page Inertia minimale se charge, navigue, affiche une erreur Laravel et est
testée, tandis que toutes les routes Blade continuent de fonctionner.

### Phase 3 — Fondation kView Vue

#### Objectif

Créer les adaptateurs Vue de kView sans dupliquer son identité.

#### Ordre recommandé

1. tokens, thèmes, typographies et catalogue d'icônes canonique ;
2. dictionnaire UI typé et provider indépendant de kView INVESTOR ;
3. boutons, icon buttons, feedback, chargement et états vides ;
4. labels, champs texte, textarea, champs financiers et actions de formulaire ;
5. headers, breadcrumbs et structures de page ;
6. tabs, segmented controls, toggle et tooltip ;
7. point d'adaptation de liens fourni par l'hôte ;
8. dialog, dropdown, popover, combobox et date picker ;
9. toasts et confirmation ;
10. primitives de visualisation et de données.

Les étapes 1 à 7 constituent le socle nécessaire à la première route pilote et
sont terminées. Les lots 5.3–5.4 ont ensuite livré le popover, les toasts, le
dialog et le dropdown parce que le shell et Settings en sont devenus les vrais
consommateurs. `/activity` ajoute ensuite `KviewPerformanceBadge`. Le détail
des taux de change justifie `KviewCard`, `KviewConfirmDialog`, le variant
`detail` de `KviewPageHeader`, `KviewInfoCard` et `KviewMetricTrend`.
Les formulaires de phase 6 ajoutent ensuite `KviewCombobox` et
`KviewDatePicker`; les fiches relationnelles ajoutent la tranche ciblée
`KviewTableCell` / `KviewDetailTable` / `KviewRowActions` /
`KviewPreviewDrawer`. Les autres visualisations et la façade CRUD data restent
des extensions incrémentales : elles ne doivent pas être implémentées sans
écran consommateur.

#### Politique Reka

- import autorisé uniquement dans les implémentations internes kView ;
- aucun import Reka dans une page métier ;
- adoption composant par composant ;
- parité visuelle et comportementale avant remplacement ;
- pas de mélange entre un contrôleur Reka et `kviewFloating` sur la même
  surface ;
- tests de focus, clavier et lecteur d'écran pour chaque surface complexe.

#### Checkpoint incrément 1 — 17 juillet 2026

- `kview/ui-core` expose l’entrée facultative `@kview/ui-core/vue` ;
- dix adaptateurs typés couvrent icônes, actions, feedback, chargement, champs
  texte et page headers ;
- les composants Vue réutilisent directement les tokens et CSS kView ;
- le registre d’icônes est partagé avec le JavaScript historique ;
- aucun hook vanilla `data-ui-*` n’est rendu dans la racine Vue ;
- le Component Lab compare les implémentations Blade et Vue réelles dans deux
  documents isolés ;
- TypeScript, ESLint, Vitest et le build production sont verts ;
- aucun composant Reka ni store global n’a été ajouté ;
- à ce checkpoint, aucune route produit n’était encore migrée.

Le contrat et les limites du lot sont documentés dans
`dev_guide/frontend-kview-vue-foundation.md`.

#### Checkpoint incrément 2 — 17 juillet 2026

- à ce checkpoint, `kview/ui-core` expose quatorze adaptateurs Vue typés ;
- `KviewTabs` possède ses panels, son `v-model`, le roving focus et
  Left/Right/Home/End sans déléguer au JavaScript Blade ;
- `KviewSegmentedControl` conserve les sémantiques bouton/lien du design
  system, sans faux pattern radio ;
- `KviewToggle` conserve un checkbox natif, `role="switch"`, la description et
  la valeur cachée nécessaire aux formulaires Laravel ;
- `KviewButtonGroup` évite aux pages de recréer le groupe de commandes ;
- le Component Lab compare ces choix dans les implémentations Blade et Vue ;
- un garde-fou interdit les hooks vanilla `data-ui-*` et `window.kview*` dans
  tous les adaptateurs Vue ;
- aucun CSS de primitive n’a été dupliqué et, à ce checkpoint, aucune route
  produit n’avait été migrée ;
- Reka reste différé jusqu’au lot dropdown/listbox, où son comportement porté,
  clavier et focus pourra apporter une valeur mesurable ;
- les tests PHP, Vue, TypeScript, ESLint, Vite et Playwright sont verts, avec
  contrôle visuel à 1440, 1024, 768 et 390 px ;
- le package s’installe et compile ses entrées Blade et Vue depuis une archive
  propre sous Laravel 11, 12 et 13 ; la dette PHPStan reste inchangée à 137
  erreurs historiques.

#### Checkpoint final du socle phase 3 — 17 juillet 2026

- `packages/kview-ui-core/resources/icons/kview-icons.json` est l'unique
  propriétaire de 54 géométries, 35 alias historiques et du fallback
  `external` ; `Kview\UiCore\IconRegistry` en PHP et
  `packages/kview-ui-core/resources/js/icon-registry.ts` en TypeScript adaptent
  ce même fichier pour Blade, JavaScript historique et Vue ;
- l'entrée `@kview/ui-core/vue` expose vingt-deux adaptateurs UI ainsi que
  `KviewUiProvider`, soit vingt-trois composants Vue publics ;
- le lot final ajoute tooltip porté dans `body`, breadcrumbs, resource et
  section headers, actions de formulaire, textarea, money input et percent
  input ; l'icon button applique désormais le même contrat de tooltip que
  Blade ;
- `KviewEmptyState` porte explicitement le mode de son action et `KviewAlert`
  utilise un `resetKey` pour afficher un nouveau flash même si son texte ne
  change pas ;
- `KviewUiProps` construit depuis Laravel un dictionnaire sérialisable de
  traductions et d'icônes de ressources ; `KviewUiHost` l'injecte une fois dans
  `KviewUiProvider`, et les primitives utilisent `useKviewUi()` ;
- les relations `aria-describedby` externes sont fusionnées avec les hints,
  erreurs et tooltips au lieu d'être remplacées ;
- les états `aria-invalid` et `aria-busy` fournis par l'hôte sont normalisés et
  combinés avec l'erreur et le loading internes ;
- le provider accepte un composant de lien fourni par l'hôte, sans importer
  Inertia ni une classe `App\...` dans le package ;
- le Component Lab et ses pages Inertia locales exercent les adaptateurs réels,
  sans copie HTML ni hooks vanilla dans la racine Vue ;
- la validation finale réussit 146 tests PHP / 1 643 assertions, 45 tests
  Vitest et 9 parcours Playwright desktop/mobile, dont le shell authentifié ;
- les consommateurs propres Laravel 11, 12 et 13, `test:ui-release` et
  `composer validate` sont verts ;
- à ce checkpoint, Reka, dropdown, combobox, dialog, date picker, toast,
  confirmation, visualisation et `ui-data` étaient volontairement différés
  jusqu'à leur chantier consommateur ; les lots 5 et 6 n'ont ajouté que les
  tranches devenues nécessaires.

#### Critère de sortie

Atteint pour le socle du pilote : le Component Lab compare les composants
Blade et Vue réels, et les primitives nécessaires à `/search` possèdent une API
Vue testable. « Phase 3 terminée » ne signifie pas que chaque composant Blade a
déjà un équivalent Vue ; les surfaces complexes suivent la roadmap par besoin.

### Phase 3.5 — Fondation du shell et des liens

#### Objectif

Supprimer les ambiguïtés de navigation avant de migrer `/search`, sans basculer
encore le shell de production ni une route métier vers Inertia.

#### Livrables — 17 juillet 2026

- `App\Support\ApplicationNavigation` calcule une seule structure de marque,
  URLs, sections, permissions, états actifs et types de navigation ; le layout
  Blade historique, y compris sa variante legacy, et les props Inertia
  consomment cette même structure. Investments reste actif sur ses routes
  Call & Distribution et Capital Accounts ;
- `AppLink` choisit explicitement une visite Inertia ou une navigation complète
  selon `inertia`; à ce checkpoint, tous les liens métier restaient à `false`.
  Il conserve l'ARIA transmis, relaie les clics avant Inertia pour permettre
  leur annulation et expose le focus sur l'ancre réellement rendue ;
- le lien de la fondation publique vers le shell authentifié reste natif afin
  que la redirection login ne soit pas interceptée comme une visite/modale
  Inertia ;
- `resources/js/components/application/ApplicationShell.vue` démontre sidebar,
  topbar, recherche `Ctrl/Cmd+K`, compte, flash, skip link, drawer mobile modal,
  arrière-plan inerte, piège Tab, verrouillage du scroll, Escape, restauration
  du focus après retrait de l'état inerte — fermeture ou passage au breakpoint
  desktop — et sidebar ouverte à `240px` ou masquée à `0`, sans rail compact ;
- les alertes du shell reçoivent `flash.id` comme `resetKey`, et le CSS du
  spécimen réutilise les tokens kView au lieu de créer une seconde couche de
  couleurs, ombres, rayons ou espacements ;
- la démonstration est disponible uniquement en `local` et `testing` sur
  `GET /__kview/inertia/application-shell`, avec authentification ;
- `resources/js/components/search/SearchResultGroup.vue` fournit le groupe de
  résultats typé nécessaire au pilote et utilisait alors le même module
  `resources/css/modules/search-results.css` que la vue Blade ;
- au checkpoint de phase 3.5, `/search` était encore Blade. Sa vue avait été
  normalisée avec `page-header`, `text-input`, `form-actions`, `section-header`
  et `empty-state`; des tests caractérisaient authentification, requête vide,
  recherche nom/nom complet/alias, ordre, limite, URLs et payload de
  suggestions.

#### Frontière avec les phases suivantes

La phase 3.5 reste l'origine du banc d'essai authentifié. La phase 4 a réutilisé
ses primitives et contrats en embarquant seulement la carte `/search` dans le
shell Blade historique. Les lots 5.1–5.4 ont ensuite transformé le spécimen en
layout persistant et porté les fonctions globales ; la clôture de phase 5 a
finalement adopté ce shell sur `/search`.

#### Critère de sortie

Atteint localement : Blade et Vue reçoivent la même navigation serveur, chaque
lien déclare son moteur, et un shell authentifié peut accueillir la page pilote
sans prendre le contrôle des routes Blade.

### Phase 4 — Route pilote

#### Objectif

Valider l'architecture sur un écran à faible risque avant le déploiement
général du shell et avant l'ERP.

#### Livrables validés — 17 juillet 2026

- `SearchController@index` a commencé à renvoyer `Search/Index` avec un payload
  minimal et typé : `query`, `searchUrl` et trois groupes de résultats déjà
  normalisés par Laravel ; aucun modèle Eloquent brut n'est sérialisé ;
- au checkpoint phase 4, `SearchController@index` sélectionnait
  `inertia-dashboard.blade.php`. Cette
  racine étendait le dashboard Blade historique et conservait exactement une topbar
  et une sidebar classiques ;
- `resources/js/Pages/Search/Index.vue` composait uniquement la carte locale avec
  les primitives kView Vue et `SearchResultGroup`, sans `ApplicationShell` ni
  hook vanilla ;
- `[data-frontend-owner="vue"]` délimitait la carte et obligeait
  `ui-page-loading.js` à céder au runtime Vue pour ses liens, formulaires et son
  historique ;
- le menu de topbar Blade conservait recherche globale, suggestions et actions
  rapides. Il est indépendant du champ de carte, seul responsable du contenu
  local, de son URL, de ses chargements et de ses erreurs ;
- la sidebar desktop avait déjà deux états : ouverte à `240px` ou cachée à `0`,
  jamais un rail d'icônes ;
- les liens de résultats vers GP, fonds et investisseurs restaient des ancres
  natives, car ces pages étaient encore Blade à ce checkpoint ; ils utilisent
  maintenant Inertia depuis leur portage en phase 6.5 ;
- la vue `resources/views/search/index.blade.php` a été supprimée après mesure
  et validation. Sa référence reproductible reste isolée au commit `b276b8a` ;
- le contrat JSON de `/search/suggest` n'a pas été absorbé par la carte : il
  alimentait le menu global historique. Le lot 5.2 a préparé son propriétaire
  Vue et la clôture de phase 5 l'a rendu actif sur la route produit.

#### Vérification fonctionnelle, responsive et accessible

Les tests Laravel, Vitest et Playwright couvrent le payload typé, la requête
vide, les résultats, les sorties natives, la synchronisation de l'URL et du
champ, les retours précédent/suivant, la remise à zéro puis la restauration du
scroll, l'état de chargement, l'erreur réseau conservant les résultats et sa
relance, le focus après rendu et la course d'une double soumission rapide. Le
contrat navigateur contrôle aussi une seule topbar/sidebar, l'indépendance du
menu global et de la carte, la frontière Vue face à la navigation partielle, la
sidebar `240px`/`0`, le clavier, axe sans violation, l'absence de chevauchement
dans la topbar à 1 024 px, l'absence de débordement horizontal et cinq
largeurs : desktop large, desktop contraint à 1 024 px, régression signalée à
919 px, tablette à 768 px et mobile à 390 px. Le filtre console n'ignore que
les erreurs `ERR_FAILED` portant le marqueur d'URL exact `__network_error__`,
émises par la requête intentionnellement interrompue pour tester l'erreur
réseau de la carte.

Le parcours authentifié exige une base préparée avec
`php artisan db:seed --class=CrudDemoSeeder`, ainsi que `E2E_EMAIL` et
`E2E_PASSWORD`. Il peut être ignoré localement si ces variables manquent, mais
la CI échoue explicitement dans ce cas.

Le délai artificiel de `250 ms` n'est utilisé que pour rendre l'état de
chargement observable et testable. Les artefacts sont nommés
`search-warm-delayed` : `365 ms` desktop avec INP `16 ms`, et `353 ms` mobile
avec INP `24 ms`. Ils sont exclus de toutes les mesures de performance
naturelles ci-dessous.

#### Mesures locales avant/après

La comparaison utilise la version Blade isolée au commit `b276b8a` et la
version Inertia finale, dans le même environnement local. Le cache navigateur
est explicitement vidé par CDP avant chacun des trois chargements Inertia à
froid. La valeur de référence est la médiane, pas un run choisi.

| Référence Blade (`b276b8a`) | Ressources transférées | DCL | Load | LCP | CLS |
| --- | ---: | ---: | ---: | ---: | ---: |
| Desktop | 407 831 B | 89 ms | 189 ms | 132 ms | 0,0002 |
| Mobile | 407 831 B | 96 ms | 183 ms | 132 ms | 0,0009 |

| Cold Inertia | responseEnd, 3 runs → médiane | DCL, 3 runs → médiane | Load, 3 runs → médiane | Transféré, chaque run → médiane | Décodé, chaque run → médiane | LCP, 3 runs → médiane | CLS par run | Tâches >= 50 ms par run |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Desktop | 24 / 25 / 28 → 25 ms | 59 / 67 / 63 → 63 ms | 119 / 124 / 118 → 119 ms | 641 468 / 641 468 / 641 468 → 641 468 B | 686 411 / 686 411 / 686 411 → 686 411 B | 116 / 108 / 108 → 108 ms | 0 / 0 / 0 | 0 / 0 / 0 |
| Mobile | 20 / 26 / 23 → 23 ms | 58 / 67 / 66 → 66 ms | 108 / 118 / 117 → 117 ms | 641 468 / 641 468 / 641 468 → 641 468 B | 686 411 / 686 411 / 686 411 → 686 411 B | 92 / 96 / 92 → 92 ms | 0 / 0 / 0 | 0 / 0 / 0 |

Le harness tourne localement sous Chromium, sans throttling. Les octets
additionnent les `transferSize` des ressources secondaires exposées par la
Performance API ; ils n'incluent pas le document HTML principal. Le serveur PHP
local ne compresse pas les assets. Le montage transitoire charge à la fois le
shell Blade et Vue : son transfert brut est donc supérieur à la référence
Blade, même si les timings DCL/load/LCP locaux restent plus courts. Cette
mesure ne prédit ni le transfert gzip/Brotli ni les percentiles terrain. Le CLS
Blade était déjà négligeable ; il est nul dans les échantillons finaux.

Chaque échantillon cold, avant calcul de la médiane, respecte les budgets du
harness : ressources entre 1 et `700 000 B`, LCP présent et <= `2 500 ms`, CLS
<= `0,1`, et aucune tâche d'au moins `50 ms`. La médiane ne peut donc pas
masquer un run hors budget.

| Visites Inertia naturelles à chaud | Trois runs | Médiane | Ressources | Requêtes | CLS | Tâches >= 50 ms | Interaction observée |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |
| Desktop | 86 / 76 / 73 ms | 76 ms | 4 161–7 715 B | 1 | 0 | 0 | 16 / 24 / 16 ms |
| Mobile | 69 / 70 / 73 ms | 70 ms | 4 161–7 715 B | 1 | 0 | 0 | `<16` / `<16` / 16 ms |

La navigation Blade complète observée transférait `397 069 B`. Aucune durée
Blade n'est comparée aux médianes X-Inertia, car DCL/load d'un document complet
et durée d'une requête Inertia ne mesurent pas la même chose. Ces résultats
locaux ne remplacent pas de futures données terrain au 75e centile.

La validation finale réussit 147 tests PHP / 1 744 assertions et 11 fichiers
Vitest / 53 tests. Le parcours Search Playwright compte 3 passages et 1 skip
intentionnel pour ne pas dupliquer les largeurs intermédiaires sur le projet
mobile. La dette PHPStan globale finale est de 136 erreurs historiques.

#### Critère de sortie

Atteint : parité fonctionnelle, visuelle et accessible, tests et mesures à froid
avec cache vidé, et visites naturelles à chaud. Le délai de chargement
artificiel demeure strictement hors benchmark.

### Phase 5 — Shell et navigation

#### Objectif

Faire d'Inertia le propriétaire de la navigation des pages migrées.

Le composant local de phase 3.5 sert de preuve et de contrat. La phase 5 est le
déploiement réel du shell sur les routes produit après validation du pilote.
Ses lots techniques jusqu'à 5.4 ont validé chaque fonction globale, puis le lot
de clôture a composé `ApplicationShell` sur `/search`. Les futures pages Inertia
réutilisent ce layout persistant ; les routes métier non migrées conservent le
shell Blade jusqu'à leur propre cutover.

Sa frontière comprend le shell persistant et la migration des fonctions encore
possédées par Blade — recherche globale avec suggestions/actions, Excel Login
et modale Settings — puis le cutover de `/search`. Les consultations et
formulaires produit appartiennent à la phase 6 ; les index ERP à la phase 7. La
déconnexion `POST` et la sortie explicite vers login lors d'une expiration de
session Inertia sont désormais sécurisées par le lot 5.1.

#### Phase 5.1 — fondation technique validée le 17 juillet 2026

- `resources/js/layouts/AuthenticatedLayout.vue` monte un seul
  `ApplicationShell` autour du slot des pages qui l'adoptent explicitement ;
- `Foundation/ApplicationShell` et
  `Foundation/ApplicationShellSecondary` partagent ce layout sur deux routes
  authentifiées `local/testing` ; Playwright conserve un marqueur sur la même
  instance DOM et l'état replié de la sidebar pendant l'aller-retour ;
- à ce checkpoint, la recherche du shell technique restait un `GET` natif vers
  `/search`, car la route produit employait encore la racine hybride
  `inertia-dashboard.blade.php` ;
- `/logout` accepte désormais seulement `POST`. Le shell Blade envoie un
  formulaire CSRF et le shell Vue utilise `router.post` avec garde anti-double
  soumission ;
- logout volontaire et session expirée renvoient une redirection externe
  Inertia `409 / X-Inertia-Location` vers `/login`, tandis qu'une requête native
  conserve la redirection Laravel classique ;
- le responsive, le clavier, axe, l'aller-retour persistant, le logout et la
  frontière de session sont validés sur Chromium desktop et mobile.

La validation globale du checkpoint réussit `152` tests PHP / `1 777`
assertions et `12` fichiers Vitest / `56` tests. Le parcours Inertia dédié
compte `11` passages et `3` skips intentionnels pour ne pas dupliquer sur mobile
les contrôles de frontière exécutés une fois sur desktop. PHPStan est vert sur
les fichiers 5.1 ; sa dette globale reste stable à `136` erreurs historiques.
Pint est également vert sur les fichiers du lot, tandis que la dette de format
globale antérieure reste hors de ce checkpoint. La matrice Playwright complète
du dépôt réussit `18` parcours avec `4` skips intentionnels.

Ce checkpoint ne migre aucune nouvelle route produit. À sa clôture, il ne
portait pas encore les suggestions/actions globales, Excel Login, Settings, les
toasts globaux ni le pré-paint de la préférence sidebar ; `/search` restait la
carte Vue de phase 4 dans le shell Blade.

#### Phase 5.2 — recherche globale du shell technique validée le 17 juillet 2026

- `App\Support\GlobalSearchConfiguration` centralise les trois presets et les
  actions rapides. `SearchController` et `ApplicationNavigation` consomment le
  même adaptateur : les URLs de recherche/création ne sont plus recopiées dans
  le frontend Vue ;
- `ApplicationNavigation` partage `searchSuggestUrl`, `searchPresets` et
  `searchDefaultActions` sous un contrat TypeScript explicite ;
- `ApplicationGlobalSearch.vue` porte dans le layout persistant technique le
  menu déjà présent dans la topbar Blade : suggestions GP/fonds/investisseurs,
  actions rapides et raccourci `Ctrl/Cmd+K` ;
- le composant est application-owned, pas une nouvelle primitive générique. Il
  rend du texte Vue sans `innerHTML`, hook `data-ui-*` ni appel à
  `window.kviewFloating` ;
- son champ suit le contrat combobox/listbox : nom accessible,
  `aria-expanded`, `aria-controls`, `aria-activedescendant`, navigation
  ArrowUp/ArrowDown, Enter et Escape, annonce live et focus conservé dans la
  saisie ;
- chaque nouvelle saisie invalide immédiatement la séquence précédente, annule
  la requête en vol, retire l'ancienne sélection et empêche une réponse ou un
  lien obsolète de rester activable ;
- le panneau est téléporté dans `body`, repositionné sur scroll/resize et sur
  les changements du viewport visuel, collisionné dans l'espace restant même
  avec un clavier mobile, élargi quand la topbar est étroite,
  puis nettoyé avec timers, requêtes, observers et listeners au démontage ;
- les suggestions, actions et la soumission restent des navigations natives,
  car `/search`, les fiches et les formulaires de création utilisent encore le
  shell Blade ou la racine hybride ;
- la topbar Blade produit conserve son contrat visuel et son propriétaire
  JavaScript. Sa configuration et son écoute interne sont ensuite alignées sur
  le contrat partagé dans la maintenance post-5.2 ci-dessous. Le nouveau
  propriétaire Vue ne s'exécute que sur les deux routes techniques : il prépare
  le cutover sans créer deux contrôleurs sur le même DOM.

Les tests ciblés réussissent les 17 tests PHP du contrat navigation/recherche
et les 12 tests Vitest du composant et du shell. La validation complète réussit
`153` tests PHP / `1 797` assertions, `13` fichiers Vitest / `60` tests, le
typecheck, ESLint et le build. Le parcours Inertia Chromium compte `11`
passages et `3` skips intentionnels ; il ouvre le panneau sur desktop et mobile,
vérifie clavier, collision, axe sans violation, persistance du shell et absence
de débordement.

Ce lot ne bascule toujours aucune route produit et ne synchronise jamais la
recherche globale avec le champ local de la carte `/search`.

#### Maintenance post-5.2 — parité de la topbar Blade

La topbar produit historique consomme désormais les mêmes presets et actions
rapides que le shell Vue technique via `ApplicationNavigation` et
`GlobalSearchConfiguration`. Son panneau restant portalisé dans la couche
globale, les clics internes sont écoutés sur le panneau lui-même plutôt que sur
le formulaire qui n'est plus son parent DOM. Les trois presets et l'action
« Create new investor » sont couverts par les tests PHP, Vitest et Playwright.

#### Phase 5.3 — feedback global, Excel et compte validés le 18 juillet 2026

- un parcours Playwright caractérise d'abord les propriétaires Blade réels :
  Excel Login, pile de toasts, menu utilisateur, ouverture de Settings,
  fermeture avec Escape et restauration du focus ; ce filet reste actif tant
  que le shell produit historique existe ;
- `KviewToastProvider` et `useKviewToast` portent dans `kview/ui-core` la pile
  Vue réutilisable : Teleport direct dans `body`, rôles `status`/`alert`, IDs
  stables, mise à jour, promesse, durée nulle persistante, fermeture et
  nettoyage des timers. Le provider entoure seulement le layout persistant,
  afin de ne pas dupliquer le viewport Blade de la carte `/search` hybride ;
- `HandleInertiaRequests` partage désormais `success`, `error`, `warning`,
  `info`, `status` et la collection `toasts`. Un `flash.id` unique empêche le
  layout persistant de rejouer le même feedback pendant une navigation ;
- `useApplicationExcelLogin` possède une seule requête partagée entre la
  topbar et le menu : état occupé commun, garde anti-double appel, timeout de
  dix secondes, en-têtes same-origin, messages toast et URL
  `lncom://token=...&url=...&encoded_url=...` encodée comme dans le runtime
  historique ;
- Laravel sérialise l'URL privée et cache-bustée de l'avatar ainsi que toutes
  les URLs du compte. Vue ne devine aucune route ;
- `KviewAvatar` porte l'image ou le fallback de deux initiales et
  `KviewPopover` porte le vrai cycle de vie non modal : Teleport, collision,
  focus initial, Escape avec restitution, clic extérieur sans vol de focus et
  nettoyage scroll/resize/viewport ;
- le menu technique atteint la parité demandée : photo/avatar, User Settings,
  Data Status, Workspace, Excel et logout `POST`. À la clôture de 5.3, les
  trois destinations Settings restaient des ancres natives vers leurs onglets
  Blade ; le lot 5.4 ci-dessous ajoute ensuite le dialog Vue comparable.

Ce lot ne bascule encore aucune route produit. Les validations du checkpoint
réussissent `156` tests PHP / `1 869` assertions, `17` fichiers Vitest / `75`
tests, typecheck, ESLint, build, Pint et PHPStan ciblé. La matrice Playwright
complète est verte avec `24` passages et `6` skips intentionnels sur desktop et
mobile. Les archives propres du package passent aussi sous Laravel 11, 12 et
13, et le manifeste de release est cohérent.

#### Phase 5.4 — Settings Vue comparable à Blade, validée le 18 juillet 2026

- `App\Support\ApplicationSettings` devient le présentateur unique de
  Settings. Il construit une fois les onglets, permissions, préférences,
  options, statuts, champs dynamiques et URLs, puis alimente soit la vue Blade,
  soit un payload JSON sûr demandé explicitement par le client Vue ;
- `ApplicationSettingsDialog` porte Account, Preferences, Workspace,
  Integrations, System et Configuration admin dans le shell persistant
  technique. Le chargement reste paresseux et annulable ; les formulaires
  réutilisent les endpoints Laravel existants, affichent les erreurs `422`
  inline, remettent le focus sur le premier champ en erreur et publient les
  succès/échecs par le provider de toasts du shell ;
- `KviewDialog` et `KviewDropdown` rejoignent l'entrée publique
  `@kview/ui-core/vue`. Reka UI 2.10.1 ne fournit que leurs mécaniques internes
  de dialog et listbox : aucun panel kView INVESTOR ne l'importe, et toutes les
  formes, couleurs, espacements, arrondis, ombres et typographies restent kView ;
- un clic primaire ordinaire sur Settings dans la sidebar ou le menu ouvre la
  modale Vue sans démonter la page. Les éléments restent de vraies ancres pour
  les clics modifiés et navigations natives ;
- chaque onglet propose « Compare Blade » vers
  `/preferences?tab=<onglet actif>` dans un nouvel onglet. La version Blade
  n'est donc ni supprimée ni masquée pendant la comparaison et reste le
  fallback du shell produit ;
- `application-settings.css` est l'unique module applicatif de la composition,
  importé par les entrées Blade et Inertia. Le package possède uniquement les
  styles génériques du dialog/dropdown.

La suite PHP complète passe `158` tests / `1 919` assertions. Typecheck,
ESLint, Vitest complet (`19` fichiers / `81` tests), build Vite,
`composer validate`, `test:ui-release` et `test:ui-consumer` sont verts.
Settings Playwright est vert sur desktop et mobile : le scénario contrôle la
réponse JSON Vue, la comparaison Blade HTML, la restitution du focus et `Ctrl/Cmd+K`, le dropdown,
la confirmation imbriquée et axe sans violation.

Le premier consommateur d'archive propre a révélé que la résolution des peers
de l'entrée Vue dépendait encore du monorepo. `kview/ui-core` publie désormais
`vue` et `reka-ui` dans ses `peerDependencies`, et le fixture les résout depuis
le host. Les consommateurs propres Laravel 11, 12 et 13 sont verts. Un
consommateur Blade-only n'importe pas cette entrée facultative et n'installe ni
ne livre Vue ou Reka au navigateur. Ce lot ne bascule toujours aucune route
produit.

#### Maintenance post-5.4 — parité visuelle du shell Blade/Vue

Le shell technique Vue reprend désormais le contrat visuel du shell produit
Blade sans importer son monolithe CSS : rail extérieur `3px`, sidebar
`240px`/`0`, topbar `50px`, logo `28px`, recherche `520 × 38px`, contrôles
topbar `36px` et lignes de navigation `42px`. Le drawer mobile occupe tout le
viewport et la recherche compacte s'élargit dès le premier clic ou le premier
focus.

Les presets typographiques historiques sont conservés explicitement dans le
module applicatif : titre de marque `750`, byline `550`, navigation `600`,
avec `DM Sans` et une base de shell à `14px`. Il ne s'agit pas de nouveaux
tokens génériques du package : ce sont les valeurs de compatibilité du shell
kView INVESTOR pendant le cutover.

Blade et Vue utilisent maintenant le même pictogramme canonique `excel` du
registre `kview/ui-core`. Le contenu du shell n'impose plus de largeur maximale
globale : il possède uniquement le scroll, tandis que chaque page possède son
espacement. Les deux pages techniques gardent leur marge via
`application-shell__specimen-page`.

`tests/e2e/application-shell-parity.spec.ts` compare les deux implémentations à
`±1px` sur desktop et mobile et verrouille aussi les trois font-weights, le
drawer plein écran, l'absence de rail compact et l'ouverture de Search au
premier clic. Les tests d'interaction Phase 5 existants restent la référence
pour focus, Escape, Settings, Excel, toasts et menu utilisateur.

#### Clôture de phase 5 — cutover du shell produit

- `/search` utilise désormais la racine standard `inertia.blade.php` et déclare
  `AuthenticatedLayout`. Le shell Vue persiste pendant les recherches et les
  futurs changements entre pages Inertia ; seul le slot de contenu change ;
- l'ancienne racine, l'entrée TypeScript et l'entrée CSS
  `inertia-dashboard.*` sont supprimées du dépôt et de Vite. Le marqueur
  `[data-frontend-owner="vue"]` n'est plus nécessaire sur cette route produit ;
- un composant Blade synchrone et sans dépendance applique les préférences avant
  les assets dans `layouts/app.blade.php` comme dans `inertia.blade.php`. La
  préférence desktop de sidebar est donc correcte au premier paint ; le drawer
  mobile reste transitoire et ne remplace jamais cette préférence ;
- `ApplicationGlobalSearch` suit `item.inertia: true` uniquement lorsque son
  hôte active `useInertia`. Le shell produit l'active pour l'action et la
  soumission vers `/search` ; un fallback ou une destination Blade conserve une
  ancre native. Le panneau téléporté est fermé et ses requêtes invalidées avant
  le changement de page ;
- `#application-content` porte `scroll-region` : Inertia sauvegarde et restaure
  le scroll du contenu lors des visites et de l'historique, sans déplacer le
  shell ;
- toute visite pertinente expose `aria-busy` immédiatement. Un skeleton
  structurel limité au contenu apparaît après `120 ms` seulement lorsque le
  pathname change. Une recherche same-path conserve les résultats visibles ;
  prefetch et `showProgress: false` sont ignorés. Inertia garde son unique
  indicateur de progression et une courte view transition ne concerne que le
  contenu, avec respect de `prefers-reduced-motion` ;
- une sortie vers GP, fonds, investisseurs, création rapide ou toute autre
  route encore Blade reste une navigation document native. Elle ne tente jamais
  une visite partielle entre deux racines différentes.

La validation de clôture passe `159` tests PHP / `1 960` assertions, `19`
fichiers Vitest / `86` tests, le typecheck, ESLint, le build Vite, PHPStan
ciblé, `composer validate`, le manifeste de release et les consommateurs
propres Laravel 11, 12 et 13. La matrice Playwright complète compte `29`
passages et `7` skips intentionnels sur desktop/mobile ; axe ne remonte aucune
violation sur les surfaces contrôlées. Le contrôle a aussi renforcé le token
destructif commun afin que le texte blanc des boutons danger atteigne WCAG AA.
La nouvelle médiane cold locale du root persistant est `526 709 B` transférés,
LCP `84 ms` desktop / `72 ms` mobile, CLS `0` et aucune longue tâche ; les
visites Inertia naturelles ont une médiane de `144 ms` desktop / `95 ms`
mobile dans ce harness local non compressé.

À la fin de la phase 5, « shell migré » signifie donc : le shell Vue/Inertia est
le propriétaire de `/search` et des futures routes Inertia. Cela ne signifie
pas que Blade a disparu : le shell Blade reste le propriétaire des routes
métier non migrées jusqu'à leur lot dédié, notamment les index en phase 7.

#### Ordre de migration actualisé

La phase 5 est terminée : layout persistant, pré-paint, sidebar, topbar,
recherche globale, toasts, Excel Login, menu utilisateur, Settings, logout,
frontière de session, scroll, transitions et skeletons sont actifs sur
`/search`. Les lots 6.1 à 6.5 réemploient ce contrat sur `/activity`, les
consultations et les formulaires métier, sans second shell. La suite porte les
index ERP de phase 7, route par route.

`KviewAlert` reste la primitive des messages persistants dans le flux de page ;
les flashes globaux du layout Vue passent désormais par les toasts.

#### Actions

- réutiliser le layout Vue persistant sur chaque nouvelle route Inertia sans
  remonter un second shell ;
- conserver les permissions et routes calculées par Laravel ;
- faire évoluer `ApplicationNavigation` route par route et passer
  `inertia: true` uniquement lorsque la destination est réellement migrée ;
- définir les liens qui déclenchent encore une navigation complète vers
  Blade ;
- maintenir la déconnexion `POST` protégée par CSRF et ses redirections externes
  Inertia validées en phase 5.1 ;
- conserver `#application-content` comme région de scroll Inertia ;
- arrêter d'ajouter des responsabilités à `ui-page-loading.js` ;
- retirer son contrôle uniquement des routes effectivement migrées.

#### Critère de sortie

Atteint : les navigations de `/search` et des futures routes Inertia n'utilisent
plus la navigation partielle maison. Les routes Blade restantes demeurent
accessibles par navigation native, sans double contrôle du DOM.

### Phase 6 — Pages métier et formulaires

#### Phase 6.1 — activité, validée le 19 juillet 2026

`/activity` est la deuxième route produit dans la racine Inertia standard et la
première page métier portée après le shell. `Activity/Index` déclare
`AuthenticatedLayout` ; la sidebar, la topbar, la recherche globale, le compte,
Excel, Settings et les toasts restent donc montés pendant les filtres et la
pagination.

Laravel reste propriétaire du contrat métier : normalisation d'un filtre
invalide vers `all`, compteurs, tri décroissant, déduplication, historique
consulté en session, pagination à 25 éléments et libellés temporels. Le payload
ne sérialise ni modèle Eloquent, ni paginator complet, ni objet Carbon brut.
`ActivityFeed` reçoit uniquement des props de présentation typées.

Les filtres et la pagination sont des visites Inertia same-root. À ce
checkpoint, le retour Home et les fiches métier étaient encore des ancres
natives ; les lots 6.3 à 6.5 ont depuis basculé les destinations de fiche
concernées sur Inertia. L'ancienne vue
`resources/views/activity/index.blade.php` a été supprimée après preuve de
non-utilisation ; le composant Blade
`x-ui.latest-updates` reste conservé pour la Home qui ne sera portée qu'en
phase 8.

Le style historique du feed a été extrait dans `dashboard-panels.css`, partagé
par la Home Blade et Activity Vue. `activity.css` ne possède que la composition
de page. Le statut emploie le nouvel adaptateur public
`@kview/visualization/vue` au lieu de recopier la primitive Blade, et l'entrée
Inertia ne charge que son CSS `metrics` ciblé. La vérification couvre le
backend, les composants Vue, la persistance du shell, l'historique, le clavier,
les sorties natives, les géométries desktop et mobile et axe. Cette tranche a
aussi corrigé le contraste du compteur du
filtre actif et rendu explicite le compte administrateur Playwright de la CI.
Le checkpoint complet passe `168` tests PHP / `2 130` assertions, `22` fichiers
Vitest / `93` tests et `32` scénarios Playwright, avec `8` skips intentionnels.

#### Phase 6.2 — détail d'un taux de change, portage validé

`ExchangeRateController@show` renvoie désormais `ExchangeRates/Show` dans la
racine Inertia standard. Laravel garde l'autorisation et le formatage financier
et sérialise un contrat de présentation strict : identifiant, paire, cinq
cartes déjà formatées et URLs d'actions. Aucun modèle Eloquent, objet Carbon ou
contrat de formulaire interne n'est exposé brut.

La page recompose fidèlement le détail historique avec des briques réutilisables :

- `KviewCard` / `x-ui.card` possèdent la surface, le rayon, l'ombre et les
  insets `default` ou `compact` ; Search l'utilise aussi et ne duplique plus
  cette famille de styles ;
- le variant `detail` de `KviewPageHeader` conserve Back, titre et actions dans
  le même ordre et avec le même responsive que la fiche Blade ;
- `KviewInfoCard` et `KviewMetricTrend`, dans
  `@kview/visualization/vue`, portent les équivalents des cartes de
  visualisation sans reprendre le formatage métier ;
- `KviewConfirmDialog` compose le dialog package en `alertdialog`, place
  d'abord le focus sur Cancel, gère Escape/backdrop, pending et restauration
  du déclencheur sans modale locale.

À ce checkpoint, la frontière de navigation restait volontairement mixte :
l'index, la création et l'édition des taux de change étaient encore Blade. Le
lot 6.4 porte depuis Create/Edit sur `ExchangeRates/Form`, puis 7.12 porte Back
et le redirect de suppression vers `ExchangeRates/Index`. La suppression
conserve un formulaire Laravel avec CSRF et
`_method=DELETE`, soumis une seule fois via `requestSubmit()` après
confirmation ; le redirect et son flash ciblent désormais l'index Inertia.
Les liens d'entrée Blade portent la sortie explicite de la navigation partielle
historique afin qu'un seul document Inertia soit chargé.

L'ancienne vue `resources/views/exchange_rates/show.blade.php` est retirée
après tests de contrôleur et de composant. Le parcours Playwright desktop/mobile
valide l'entrée Blade, la géométrie, les cinq cartes, les sorties natives,
Escape, la restitution du focus, les marges du dialogue, axe et l'absence
d'erreur console. Les suites globales passent `174` tests PHP / `2 243`
assertions, `25` fichiers Vitest / `105` tests et `35` scénarios Playwright,
avec `9` skips responsive intentionnels ; le consommateur archive Laravel 13
compile aussi les nouvelles APIs et l'entrée CSS ciblée.

#### Phase 6.3 — consultations financières, terminée

`call-distribs.show` et `capital-accounts.show` utilisent désormais
`CallDistribs/Show` et `CapitalAccounts/Show` dans le layout persistant. Les
deux pages composent la même architecture : `FinancialRecordPage`,
`BusinessRecordHeader`, `BusinessRecordSummary`, `BusinessRecordSections`,
`KviewInfoCard` et la confirmation destructive commune. Laravel conserve le
choix des rubriques, les relations, les URLs et le formatage des montants,
pourcentages, multiples et dates grâce à `BusinessRecordPayload`,
`RecordFieldsPayload` et `FinanceFormatter`.

Le lien retour privilégie l'investissement parent lorsqu'il existe. Edit et les
liens vers les entités déjà portées sont des visites Inertia. Les vues Blade
`show` correspondantes ont été retirées après les tests de parité ; les index
ne sont pas déclarés migrés. Le Capital Account lit désormais partout la clé
canonique `unfunded_commitment`, y compris dans sa métrique et ses sections.

#### Phase 6.4 — formulaires CRUD simples, terminée

Les pages Create/Edit suivantes utilisent le composant applicatif partagé
`BusinessFormPage` :

- Calls & Distributions ;
- Capital Accounts ;
- Exchange Rates ;
- Investments, y compris la création imbriquée depuis un investisseur ou un
  fonds avec relation préremplie et verrouillée.

`BusinessFormPayload` et `DynamicFormSchema` sérialisent un schéma typé sans
exposer Eloquent. `DynamicRecordFields` choisit la primitive selon le champ et
conserve les noms, valeurs et erreurs Laravel. Le lot ajoute seulement les
adaptateurs nécessaires : `KviewDatePicker`, `KviewCombobox`,
`RecordAutocomplete`, dropdown, texte, textarea, money, percent, fichier/image
et champs dynamiques. Le formulaire Inertia garde les erreurs inline, place le
focus sur le premier champ invalide, protège la double soumission et laisse
Laravel valider et parser les données.

Les routes API nommées de lookup Funds, GPs et Investors sont déclarées avant
les `apiResource` correspondantes. Ainsi `lookup` rejoint toujours
l'autocomplete et n'est jamais interprété comme l'identifiant d'une ressource.

#### Phase 6.5 — grandes entités métier, terminée

Les pages Show/Create/Edit de Funds, General Partners et Investors, ainsi que
la page Show d'Investments, sont maintenant Inertia/Vue. Investment Create/Edit
avait déjà été porté dans le lot 6.4. Les pages de détail réutilisent
`PortfolioRecordPage` ou `FinancialRecordPage`, les résumés, onglets,
informations, alias, métriques et tables liées. Les pages de formulaire restent
des wrappers minces autour de `BusinessFormPage`.

Pour les relations, le lot 6.5 a exposé dans `@kview/ui-data/vue` les briques
ciblées `KviewTableCell`, `KviewDetailTable`, `KviewRowActions` et
`KviewPreviewDrawer`. Le CRUD complet — colonnes configurables, filtres,
sélection, pagination et exports des index ERP — était alors réservé à la
phase 7. Depuis la clôture 7.12, la même entrée publique expose aussi
`KviewCrudTable` ; les deux familles restent séparées par leur usage, tables de
détail d'un côté et moteur d'index de l'autre.

Les actions de lignes Investments, cash flows, capital accounts, Funds et
Investors ouvrent leur aperçu dans ce drawer commun, avec Escape et restitution
du focus. La fiche Investor conserve Cash flows en sous-vues Calls &
Distributions / Capital Accounts et ajoute les sous-vues Analytics Performance
et Exposures. Ces analytics comparent des observations et des répartitions sans
additionner de montants libellés dans des devises différentes.

Les résultats de recherche, suggestions globales, activités et actions de
création pointant vers ces contrôleurs portent désormais explicitement
`inertia: true`. À la fin de la phase 6, les retours vers les index étaient
encore natifs ; la phase 7 les a depuis basculés sur le même root Inertia. Les
anciennes vues Blade `show`, `create`, `edit` et `_form` des ressources portées
ont été retirées ; les sept vues `index.blade.php` historiques ont ensuite été
retirées après le gate final de phase 7 et la preuve de non-consommation.

La clôture locale du 19 juillet 2026 passe `204` tests PHP / `4 176`
assertions, `35` fichiers Vitest / `152` tests et `38` scénarios Playwright,
avec `12` skips responsive intentionnels. TypeScript, ESLint, Vite, Pint,
PHPStan sur les fichiers modifiés, les manifestes de release et les
consommateurs propres Laravel 11, 12 et 13 sont verts.

#### Phase 7 engagée après ce checkpoint

La phase 7 a depuis migré les index ERP vers Vue en conservant le contrat PHP
et les sémantiques financières existantes. Elle porte la façade CRUD, les
cellules, le tri, les filtres, la sélection, la pagination, les exports et les
drawers sans réécrire les payloads de détail stabilisés en phase 6. Le détail
de la bascule 7.11–7.12 et sa checklist de clôture figurent ci-dessous.

#### Séquençage appliqué en phase 6

1. Activity et détail simple du taux de change ;
2. consultations financières Calls/Distributions et Capital Accounts ;
3. formulaires CRUD simples et adaptateurs strictement nécessaires ;
4. détails et formulaires des grandes entités métier ;
5. retrait des vues Blade remplacées après les preuves backend, composant et
   navigateur.

#### Contrats à conserver

- validation et parsing serveur ;
- noms et sens des champs ;
- permissions ;
- erreurs inline reliées aux champs ;
- flash après redirection ;
- confirmations destructives ;
- formats monétaires et pourcentages ;
- responsive et touch targets.

#### Critère de sortie du lot et de chaque route

- tests backend sur chaque route et réponse Inertia partielle ;
- tests Vue des composants principaux et adaptateurs partagés ;
- parcours Playwright valides et invalides représentatifs de chaque famille
  d'interaction, sans recopier le même scénario sur chaque wrapper de route ;
- contrôle clavier et accessibilité ;
- comparaison desktop et mobile ;
- ancienne route ou vue supprimée seulement après preuve de non-utilisation.

### Phase 7 — Tableau ERP Vue

#### Objectif

Changer le moteur interactif du tableau sans refaire son contrat métier.

#### Statut au 20 juillet 2026

L'implémentation et son gate global sont terminés jusqu'à 7.12. Le pilote 7.11
a connecté `gps.index`; 7.12 réutilise exactement le
même moteur pour Funds, Investors, Investments, Calls/Distributions, Capital
Accounts et Exchange Rates. Il n'existe pas sept moteurs :

```text
contrôleur Laravel
    -> BusinessIndexPayload
    -> *ListQuery (whitelist tri/recherche/filtres)
    -> Business/Index ou ExchangeRates/Index
    -> BusinessIndexPage
    -> KviewCrudTable
```

`BusinessIndexPage` ne contient que l'adaptation Inertia et les formulaires
Laravel natifs de suppression. Le package émet des intentions et rend la
table ; Laravel reste propriétaire des données, permissions, routes,
formatages et requêtes. Les filtres texte, nombre, date ISO et multisélection
sont déclarés par ressource et assainis avant SQL.

Exchange Rates utilise le slot d'actions du header commun pour conserver la
synchronisation ECB (avec période) et Settings. Les suppressions Investment,
Call/Distribution et Capital Account transportent `return_index=1` lorsqu'elles
partent de la liste, afin de revenir à l'index migré.

#### Sous-phases

1. figer les fixtures et tests du contrat actuel ;
2. typer le schéma de colonnes et les données Inertia ;
3. migrer les cellules typées vers Vue/TypeScript ;
4. construire une version en lecture seule ;
5. ajouter pagination, tri et recherche serveur ;
6. ajouter filtres ;
7. ajouter ordre, largeur et visibilité des colonnes ;
8. ajouter sélection et actions groupées ;
9. ajouter export et preview drawer ;
10. valider accessibilité et performances ;
11. piloter sur la table ERP la moins personnalisée ;
12. migrer les autres tables une par une.

Les douze sous-phases sont implémentées. Les étapes 1–10 restent couvertes par
le spécimen `/__kview/inertia/crud-table`; 7.11 couvre la route GP réelle et
7.12 les six autres index. Calls/Distributions et Capital Accounts remplacent
notamment leurs anciens écrans `x-smart-table`; Exchange Rates conserve ses
actions métier sans variante locale du moteur.

#### Critères de sortie

- tous les types de cellules conservent leur rendu et leur sémantique ;
- `aria-sort`, labels, checkboxes et activation clavier sont préservés ;
- les préférences de colonnes survivent à la navigation ;
- les gros volumes restent paginés côté serveur ;
- les exports complets ne bloquent pas le navigateur ;
- les versions Blade et Vue passent les mêmes fixtures de contrat ;
- le pilote ne régresse pas sur les mesures définies en phase 0.

#### Nettoyage et compatibilité

Les sept anciennes vues d'application suivantes ne servent plus les routes de
production et ont été supprimées après preuve de non-consommation :

- `resources/views/gps/index.blade.php` ;
- `resources/views/funds/index.blade.php` ;
- `resources/views/investors/index.blade.php` ;
- `resources/views/investments/index.blade.php` ;
- `resources/views/call_distribs/index.blade.php` ;
- `resources/views/capital_accounts/index.blade.php` ;
- `resources/views/exchange_rates/index.blade.php`.

Cette suppression applicative ne supprime pas les contrats publics Blade
`x-erp.crud-table`, `x-smart-table`, leurs registres, styles, modules vanilla,
traductions ou fixtures consommateurs Laravel 11/12/13. Ils restent une API
kView réutilisable et une compatibilité pour les autres projets.

#### Gate final enregistré

Le 20 juillet 2026, la suite complète passe `224` tests PHP / `5 359`
assertions, `37` fichiers Vitest / `179` tests et `55` scénarios Playwright,
avec `23` skips de projets responsive intentionnels et aucun échec. Pint et
PHPStan sur les fichiers modifiés, TypeScript, ESLint, build Vite, manifeste de
release, Composer et consommateurs propres Laravel 11/12/13 sont verts.
Playwright couvre les sept routes en desktop et mobile : shell persistant,
recherche/tri/filtres partiels, multiselect/date, sélection, export, preview,
confirmation, Escape/focus, actions Exchange Rates, row tap mobile et absence
d'overflow. Le benchmark reproductible confirme la pagination serveur et le
`Bitmap Index Scan` sur `funds_gp_id_index` (environ `0,10 ms` localement).

#### Incident de stabilisation ouvert — page blanche intermittente

Le 21 juillet 2026, une page entièrement vide a été observée de manière
intermittente sur `/funds` à `1006 × 942`. Le document possédait encore
`body.inertia-body`, mais aucune interface n'était visible. L'action précédant
l'incident n'a pas été identifiée et la capture seule ne permet pas de
l'attribuer à Inertia, à Vue, aux assets ou à la migration. Le défaut reste donc
**ouvert, non reproduit et de cause indéterminée** ; il ne remet pas à lui seul
en cause la clôture fonctionnelle 7.12, mais doit être levé avant une promotion
en production réelle parce que son impact utilisateur est bloquant lorsqu'il
survient.

À la prochaine occurrence, conserver avant rechargement : la route de départ,
l'action effectuée, l'URL finale, le contenu et les attributs de `#app`, les
erreurs console, les requêtes réseau en échec, le statut de la réponse Inertia
et les éventuelles erreurs d'assets ou d'expiration de session. Le correctif
sera considéré validé lorsque des navigations répétées entre routes migrées,
retours navigateur et rechargements ne peuvent plus laisser une racine vide et
qu'une erreur de session, de réponse ou d'asset déclenche une récupération
visible plutôt qu'une page blanche.

### Phase 8 — Dashboards et flux avancés

#### Objectif

Migrer les zones à données différées ou fortement interactives après
stabilisation du socle.

#### Cibles

- dashboards ;
- graphiques financiers ;
- vues trimestrielles ;
- imports longs ;
- recherche et autocomplétion avancées ;
- paramètres globaux ;
- activités nécessitant polling ou actualisation partielle.

#### Stratégie

- afficher d'abord la structure et les données essentielles ;
- différer les graphiques et métriques secondaires ;
- conserver le contenu précédent pendant une actualisation ;
- annuler les requêtes devenues obsolètes ;
- ne pas recalculer côté Vue ce que Laravel doit garantir ;
- charger Chart.js et les modules lourds uniquement sur les routes utiles.

#### Reconnaissance au 21 juillet 2026

Avant le lot 8.1, un audit du périmètre réel a resserré la liste des cibles :

- **recherche et autocomplétion** : déjà en Vue (`ApplicationGlobalSearch.vue`,
  `Pages/Search/Index.vue`, `RecordAutocomplete.vue`). Le seul écart est côté
  Laravel (`SearchController`/`GlobalSearchConfiguration` ne couvrent que
  GP/Fund/Investor par nom/alias, sans facettes) — ce n'est pas un chantier UI ;
- **paramètres globaux** : déjà en Vue (`ApplicationSettingsDialog` couvre les
  six onglets, y compris l'admin Dynamic Fields). Restent en Blade et hors
  périmètre 8.1 : `admin/validate-capital-accounts.blade.php`, les dropdowns
  admin (contrôleur `LncShared` vendor) et le doublon `user_config/edit.blade.php`
  conservé en comparaison ;
- **imports** (`FlexImport`, RayDoc) : réellement encore Blade, mais
  entièrement synchrones aujourd'hui — aucune file d'attente/aucun polling
  n'existe à porter ; « imports longs » nécessiterait une infrastructure
  asynchrone neuve, pas une simple migration ;
- **dashboards** : le vrai chantier. `/` (Home, `HomeController::index()`,
  42 lignes de vue) et `/dashboard` (`DashboardController`, 1124 lignes,
  6 onglets, tableau SVG `x-ui.fund-performance-chart` + Chart.js chargé par
  CDN pour l'historique trimestriel) sont tous deux encore Blade. Aucune
  librairie de graphiques n'est un dépendance npm ; le seul équivalent Vue
  existant est le sparkline SVG de `KviewTableCell`. Investor Performance/
  Exposures (Phase 6.5) n'a que des cartes et tableaux, pas de graphique.

#### Phase 8 lot 1 (8.1) — Home, validé le 21 juillet 2026

`/` migre vers `Home/Index` sur le shell Inertia persistant, en réutilisant
exactement les mêmes constructeurs d'activité que `/activity`
(`activityItemProps`, `buildActivityFilters`, tronqués à dix éléments côté
serveur). Les quatre cartes métriques passent par `KviewInfoCard`
(`variant="metric"`) et le panneau d'activité réutilise `ActivityFeed` via
trois props optionnelles (`title`/`subtitle`/`actionLabel`/`actionHref`) —
aucun nouveau composant de rendu n'a été créé. L'élément de navigation `home`
et le lien retour `homeLink` d'Activity passent tous les deux `inertia: true`.
`resources/views/home.blade.php` est supprimé après preuve; `/dashboard`
reste la route de référence Blade pour toute caractérisation du vieux shell
(topbar, recherche legacy) qui utilisait auparavant Home.

Piège CSS découvert et documenté : `.application-shell__page` est
`display: flex`; un enfant direct sans `width: 100%` explicite (ici
`KviewCard`) s'effondre à quelques pixels de large dès que son contenu utilise
une grille `minmax(0, 1fr)`, exactement comme `.exchange-rate-page__card` et
`.activity-page > .ui-dashboard-panel` le documentaient déjà — vérifié par
interaction réelle (le lien "Tout voir" n'était plus cliquable) avant
correction.

Gate 8.1 (jeu de démo local, 50 GPs / 61 investments) : 228 tests PHP
(5 524 assertions), 211 tests Vitest, TypeScript/ESLint/build/Pint/PHPStan
verts, et la matrice Playwright applicable verte à l'exception de quatre
échecs préexistants reproduits à l'identique sur le commit pré-8.1 (aucun ne
touche Home) : le focus-visible programmatique d'un filtre `/activity`, deux
vérifications de timing sur la combobox de recherche globale, et la
visibilité mobile du formulaire de recherche Blade dans un scénario
`search-pilot`.

#### Phase 8 lot 2 (8.2) — Dashboard et primitives de graphiques, validé le 22 juillet 2026

`/dashboard` (`DashboardController`, 6 onglets) porte vers `Dashboard/Index` sur
le shell Inertia persistant. `App\Support\DashboardPayload` (nouveau) formate
chaque onglet en chaînes d'affichage déjà scaledées/converties ; tout le
calcul métier (conversion de devise, bissection IRR trimestrielle, agrégation
annuelle/trimestrielle) reste inchangé dans les méthodes privées existantes du
contrôleur — `DashboardPayload` n'est qu'une couche de présentation, à
l'image de `BusinessRecordPayload`. Les onglets Commitment, Call Distri & NAV
et IRR partagent `DashboardGroupedTable` (regroupement par investisseur avec
ligne de sous-total ; IRR affiche son sous-total en ligne d'en-tête plutôt
qu'en pied, à l'identique du Blade) ; Yearly et Quarterly history partagent
`DashboardPeriodTable`, un composant Vue générique (`<script setup
generic="TGroup">`) exposant un slot `after-group` que l'onglet trimestriel
seul utilise pour injecter ses graphiques.

Deux primitives de graphique nouvelles dans `@kview/visualization/vue`,
conformes à la stratégie de la phase (« charger Chart.js uniquement sur les
routes utiles ») :

- `KviewChart` : primitive Chart.js (line/bar/combo, axe secondaire,
  datasets typés, palette par jetons CSS `var(--token)`) chargée par
  `import('chart.js')` dynamique déclenché au premier rendu avec données —
  jamais au chargement de la page. Utilisée uniquement par l'onglet
  Quarterly history (drawdown/distribution/TVPI en combo à double axe, net
  investment/NAV), remplaçant le Chart.js chargé par CDN de
  `dashboard-quarterly.js` (supprimé).
- `KviewPerformanceChart` : port Vue fidèle, sans dépendance externe, de
  `x-ui.fund-performance-chart` (SVG fait main, même feuille de style
  `fund-performance-chart.css` partagée à l'identique) pour le graphique NAV
  vs Paid-in de l'onglet Overview — resté volontairement en dehors de
  `KviewChart` à la demande explicite du produit (pas de librairie externe
  sur ce graphique). L'algorithme de domaine « nice-step », les tendances par
  point et le tableau de données `sr-only` sont un port direct de l'algorithme
  Blade, vérifié par des assertions Vitest sur des points fixes. L'activation
  de l'infobulle utilise l'état réactif Vue (`pointerenter`/`focus` sur une
  seule clé active) au lieu du repositionnement DOM impératif du composant
  Blade, en conservant le même contrat visuel (une seule infobulle visible à
  la fois, toujours peinte au-dessus des points voisins).

Piège de résolution CSS découvert et documenté : les alias Vite de
`@kview/*` sont de simples alias de chemin (substitution de chaîne +
extension `.css` automatique), **pas** une résolution consciente du champ
`exports` de `package.json` — un sous-chemin `./css/x` doit pointer vers un
fichier littéralement nommé `x.css`, sinon PostCSS échoue avec un
`ENOENT` lors du `build`. Piège d'ordre `watch`/`immediate` découvert dans
`KviewChart` : un `watch(..., { immediate: true })` s'exécute pendant
`setup()`, avant que les refs de template soient liées — le premier rendu
avec données au chargement échouait silencieusement ; corrigé en démarrant
le rendu initial depuis `onMounted` et en gardant le `watch` (non immédiat)
pour les changements ultérieurs.

Correction post-clôture initiale : le sélecteur de vue à 6 entrées avait
d'abord été porté avec `KviewTabs` (bande d'onglets visible), une dérive de
redesign non intentionnelle — `x-ui.view-switcher` est en réalité
`x-ui.dropdown` (icône + préfixe + description par option), pas une bande
d'onglets, et `KviewTabs` reste partagé par `PortfolioRecordPage`/
`FinancialRecordPage`/`BusinessRecordGroup` donc ne devait pas être modifié
pour ce seul besoin. Remplacé par `KviewDropdown` directement dans
`Dashboard/Index.vue`, avec `switcherLabel`/`description` par onglet ajoutés
à `DashboardPayload::tabs()` (le libellé du sélecteur diffère volontairement
du titre de section — ex. « Cash flows » au menu contre « Call Distri & NAV »
en en-tête de tableau, comme dans le Blade d'origine) et les six panneaux
convertis de slots `KviewTabs` en `<section role="region" v-show="...">`
simples. Piège a11y découvert : `KviewDropdown` utilise `role="combobox"`
(Reka Select), dont le nom accessible ne se calcule **pas** de façon fiable
depuis le contenu visible (icône + préfixe + valeur) contrairement à un
`<button>` natif — axe signale `button-name` sans `aria-label`/`aria-labelledby`
explicite ; corrigé avec `v-bind="{ 'aria-label': '...' }"` (une liaison
directe `aria-label="..."` sur le tag échoue au typecheck, `aria-label`
n'étant pas un prop déclaré du composant).

`/dashboard` était la dernière route de caractérisation du vieux shell Blade
dans `AuthenticationFlowTest`/`SearchTest` (formulaire de logout CSRF, topbar
Blade avec recherche/actions de création) ; ces deux tests pointent
désormais vers `/imports` (`flex-import.index`), qui étend le même
`layouts.dashboard` et reste Blade. `dashboard/index.blade.php`,
`dashboard-quarterly.js` et `dashboard-quarterly.css` sont supprimés après
preuve (aucun autre consommateur) ; l'entrée `dashboard` de
`ApplicationNavigation` passe `inertia: true`. `layouts/dashboard.blade.php`
reste : c'est le layout Blade partagé par les routes encore natives
(imports, admin, paramètres). Nettoyage final : `x-ui.top-performers` et
`x-ui.latest-updates` (`resources/views/components/ui/`) n'avaient plus
aucun consommateur — le premier depuis que l'Overview de Dashboard compose
sa propre section `ui-top-performers` en Vue, le second depuis qu'`ActivityFeed.vue`
(Phase 6.1) réutilise les mêmes classes `ui-latest-updates`/`ui-update-*` de
`dashboard-panels.css` — supprimés après vérification (zéro occurrence dans
`resources/views`, les packages ou la suite de tests).

Gate 8.2 final (jeu de démo local, 50 GPs / 61 investments) : 230 tests PHP
(5 547 assertions), 215 tests Vitest, TypeScript/ESLint/build/Pint verts sur
les fichiers touchés, PHPStan sans nouvelle erreur sur `DashboardPayload`
(0 erreur) — `DashboardController` ne reçoit aucune nouvelle erreur sur les
méthodes modifiées (`index`/`quarterly`), les erreurs pré-existantes de ses
méthodes de calcul non touchées (accès de propriété de relation Eloquent non
résolu sous `checkModelProperties: false`) restent une dette antérieure au
lot 8.2, axe (wcag2a/2aa/21aa/22aa) sans violation critique/sérieuse sur
`/dashboard` desktop et mobile (y compris le sélecteur de vue en dropdown),
et Playwright vert à l'exception des mêmes échecs préexistants déjà
documentés en 8.1 sur la combobox de recherche globale (aucun ne touche
Dashboard).

**Phase 8 close le 22 juillet 2026.** Le périmètre réel (cf. reconnaissance
ci-dessus) se limitait aux dashboards ; recherche, paramètres globaux et
imports restaient explicitement hors chantier à cette date (imports
nécessiterait une infrastructure asynchrone neuve — une décision
d'architecture distincte, pas une migration). Ce résidu volontairement laissé
de côté est repris et clôturé en phase 8.5 ci-dessous, à l'exception de la
décision d'architecture sur les imports asynchrones, qui reste ouverte.

#### Critère de sortie

Les dashboards restent lisibles pendant les chargements, ne bloquent pas la
navigation et respectent les formats financiers kView.

### Phase 8.5 — Résidu Blade post-phase 8

#### Objectif

Traiter explicitement chaque route encore Blade laissée de côté par la phase
8 : soit la migrer si elle ne dépend d'aucune infrastructure nouvelle, soit
consigner une décision explicite de la garder en Blade, plutôt que de la
laisser en simple oubli non documenté.

#### Cibles migrées

- `admin/validate-capital-accounts.blade.php` → `Admin/ValidateCapitalAccounts`
  (Inertia/Vue) ; la logique de validation reste inchangée dans
  `CapitalAccountValidationService` ;
- `flex_import/index.blade.php` (`/imports`) → `Imports/FlexImport` ; les
  méthodes `preview()`/`apply()`/`clear()` du contrôleur restent inchangées
  (redirections + flash Inertia, déjà partagées globalement) ;
- `raydocs/imports/index.blade.php` (`/imports/raydocs`) → `Imports/RayDocs` ;
  `store()` reste inchangé. `App\Support\PaginationPayload` est extrait à
  cette occasion (déjà utilisé par `/activity`, désormais partagé) et
  `resources/js/components/imports/FileDropzone.vue` remplace les deux
  scripts vanilla JS de dropzone quasi identiques des deux pages d'import ;
- doublon Settings (`user_config/edit.blade.php` rendu en page complète) :
  retiré. `UserConfigController::edit()` redirige désormais vers `route('home',
  ['settings' => ...])` lorsque la requête n'est ni le fragment de modale ni
  l'appel JSON de la modale Vue ; `ApplicationShell.vue` détecte le paramètre
  `?settings=<tab>` au montage et rouvre la modale après l'événement
  `router.on('navigate', ...)` d'Inertia (un `history.replaceState` plus tôt
  est écrasé par la resynchronisation d'historique initiale d'Inertia). Les
  props `bladeUrl`/`comparisonEnabled` de comparaison sont retirées du contrat
  Settings.

Gate de la phase 8.5 (jeu de démo local) : 236 tests PHP (5 623 assertions),
215 tests Vitest, TypeScript/ESLint/build/Pint verts, PHPStan sans nouvelle
erreur sur les fichiers touchés. `AuthenticationFlowTest`/`SearchTest`
pointaient à ce stade vers `admin.dropdowns.index` (`lncshared`) comme route
de caractérisation stable du vieux shell, après deux repointages successifs
vers des routes kView INVESTOR qui ont fini par migrer elles aussi (`/dashboard`
puis `/imports`). Ce choix n'a tenu qu'un instant : `/admin/dropdowns` migre
à son tour ci-dessous, dans la même journée.

#### Cibles migrées (suite, le jour même)

Les deux routes volontairement laissées de côté au moment de la clôture
initiale de cette phase ont finalement été migrées elles aussi, à la demande
explicite du produit :

- `/login` → `Auth/Login` (Inertia/Vue). `LoginController::showLoginForm()`
  rend la page via `Inertia::render()` au lieu de `view('auth.login')` ;
  `login()`/`logout()` restent inchangés (redirections classiques, déjà
  compatibles Inertia). La page n'utilise aucun layout persistant (route
  invité, avant authentification) et compose directement `KviewTextInput`
  (email/mot de passe), `KviewToggle` (remember me) et `KviewButton`
  (submit) ; le tooltip d'aide au login est remplacé par un `hint` toujours
  visible plutôt qu'une infobulle au survol, faute de slot dédié dans
  `KviewFormField` pour composer les deux — une simplification volontaire,
  légèrement plus accessible que l'original ;
- `/admin/dropdowns` (les trois routes index/edit/update) → `Admin/DropdownsIndex`
  et `Admin/DropdownsEdit` (Inertia/Vue), via un nouveau contrôleur kView INVESTOR
  `App\Http\Controllers\Web\AdminDropdownController` qui **remplace** le
  contrôleur vendor `LncShared\...\DropdownAdminController` dans le routage
  applicatif, sans toucher au submodule `lncshared/` : le service
  `LncShared\App\Services\DropdownFileService` (accès fichier JSON pur, sans
  couplage vue) et la form request `LncShared\App\Http\Requests\UpdateDropdownFileRequest`
  (validation pure) sont réutilisés tels quels. Le formulaire d'édition à
  lignes dynamiques (ajout/suppression d'options) suit le patron déjà établi
  par `AliasEditor.vue` (ligne = `KviewTextInput` par champ avec label
  visible + `KviewIconButton` "trash", pas de table HTML) plutôt que
  d'inventer un nouveau composant tableau éditable.

Gate de complément (jeu de démo local) : 242 tests PHP (5 658 assertions),
215 tests Vitest, TypeScript/ESLint/build/Pint verts, PHPStan sans nouvelle
erreur sur les fichiers touchés.

#### Conséquence : le vieux shell Blade n'a plus aucun consommateur

Une fois `/login` et `/admin/dropdowns` partis, plus aucune route kView INVESTOR
n'utilise `layouts/dashboard.blade.php` ni, par transitivité,
`layouts/app.blade.php` (que `layouts/dashboard.blade.php` étendait, et que
`/login` étendait aussi directement) : `resources/views/status/index.blade.php`
était déjà mort (`StatusController::index()` ne fait que rediriger), et le
`@extends($settingsLayout ?? 'layouts.dashboard')` de
`user_config/edit.blade.php` retombe toujours sur la branche fragment
explicite désormais. Le bundle `resources/js/app.js` /
`resources/css/app.css` — topbar, recherche et menu compte en JavaScript
vanilla, `ui-page-loading.js`, `settings-modal.js`, etc. — n'est donc plus
chargé par aucune route atteignable. **Ce constat est fait ici mais son
retrait n'est pas exécuté dans cette phase** : c'est un chantier plus large
et plus risqué (une bonne quinzaine de modules JS, deux layouts, un bundle
CSS complet) qui recoupe directement l'objectif de la phase 9 ci-dessous et
mérite sa propre validation plutôt qu'un effet de bord silencieux d'une
migration de deux routes.

`AuthenticationFlowTest`/`SearchTest` n'ont plus de route Blade à
caractériser : leurs tests `test_blade_shell_renders_a_csrf_post_logout_form`
et `test_blade_topbar_renders_the_shared_search_presets_and_default_actions`
sont retirés (leur prémisse — une route authentifiée encore rendue par le
vieux shell — n'existe plus) plutôt que repointés une troisième fois.
`AuthenticationFlowTest` gagne à la place une caractérisation directe de
`/login` (forme Inertia, identifiants invalides, identifiants valides).

#### Décision d'architecture ouverte

Les imports (`FlexImport`, RayDoc) sont sur Inertia/Vue mais restent
**synchrones** : aucune file d'attente ni polling n'a été ajouté. Si le
produit veut un jour des imports longs (gros classeurs, traitement différé),
cela demande une infrastructure asynchrone neuve (job, file d'attente, suivi
de progression côté Vue) — une décision d'architecture séparée, pas une
suite mécanique de cette phase.

#### Critère de sortie

Chaque route qui reste en Blade a une raison explicite consignée dans ce
document ; aucune route ne reste Blade par simple oubli. **Au 22 juillet
2026, plus aucune route kView INVESTOR ne reste sur ce critère : `/login` et
`/admin/dropdowns` sont partis, il ne reste que le legacy mort identifié
ci-dessus, à retirer en phase 9.**

### Phase 9 — Retrait du legacy

#### Objectif

Supprimer la dette uniquement lorsqu'elle n'a plus de consommateur.

#### Actions

- maintenir une liste des routes encore dépendantes de Blade/vanilla ;
- retirer les initialiseurs `kviewInit*` après migration de leurs derniers
  consommateurs ;
- supprimer les branches devenues inutiles de `ui-page-loading.js` ;
- retirer la navigation partielle maison lorsque toutes les routes concernées
  utilisent Inertia ;
- nettoyer le CSS par propriétaire, sans migration esthétique opportuniste ;
- conserver les adaptateurs Blade kView si d'autres projets les utilisent ;
- versionner les changements de packages selon SemVer ;
- documenter toute rupture publique.

#### Exécutée le 22 juillet 2026

Le constat hérité de la phase 8.5 (`layouts/dashboard.blade.php`,
`layouts/app.blade.php`, le bundle `app.js`/`app.css`, une quinzaine de
modules vanilla) a été traité intégralement, avec la même discipline
« recherche de consommateurs + suite verte » que le reste de cette phase —
pas un simple constat cette fois.

**Méthode.** Source de vérité : un grep exhaustif de `return view(`/`->view(`
sur tout `app/Http/Controllers/` (seuls deux contrôleurs restaient :
`UiStorybookController` — Component Lab, racine HTML autonome, jamais liée à
`app.js`/`app.css` — et `UserConfigController::edit()`, dont la branche
fragment HTML n'était atteignable que par `settings-modal.js`). À partir de
là, remontée systématique de chaque dépendance (composants Blade inclus,
sélecteurs `data-*` ciblés par chaque module vanilla, imports CSS partagés
avec `inertia.css`) avant toute suppression, plutôt qu'une suppression en
bloc suivie d'une réparation.

**Cascade complète retirée** (29 fichiers), au-delà de la liste initiale de
la phase 8.5 :

- Les deux layouts : `layouts/dashboard.blade.php`, `layouts/app.blade.php`.
- Leurs partiels propres à kView INVESTOR, désormais sans aucun autre
  consommateur : `components/layout/app-sidebar.blade.php`,
  `components/layout/legacy-sidebar.blade.php`,
  `components/layout/settings-modal-host.blade.php`.
- Le bundle `resources/js/app.js` / `resources/css/app.css` et ses quinze
  modules : `bootstrap.js`, `ui-page-loading.js`, `settings-modal.js`,
  `global-search.js`, `user-menu.js`, `ui-app-sidebar.js`, `excel-login.js`,
  `call-distrib-form.js`, `alias-fields.js`, `settings-account.js`,
  `dynamic-fields.js`, `mobile-home.js`. `@tailwind base/components/utilities`
  ne vivait que dans `app.css` — vérifié qu'aucune page Vue ni aucun composant
  kView (Blade ou Vue) ne consomme une classe utilitaire Tailwind brute avant
  de laisser partir les directives avec le fichier. `tailwind.config.js`
  laissé en place le jour même dans l'attente d'une décision, puis retiré le
  22 juillet 2026 en suivi direct (voir plus bas) une fois confirmé qu'aucune
  classe utilitaire brute ne survivait nulle part dans le repo.
- **Cascade settings-modal, découverte pendant l'exécution** : la branche
  fragment HTML de `UserConfigController::edit()` n'était atteignable que par
  `settings-modal.js`, lui-même mort avec `app.js`. Simplifiée à deux branches
  (JSON pour la modale Vue, redirection sinon) ; `layouts/fragment.blade.php`
  et `user_config/edit.blade.php` retirés avec elle. Ce dernier référençait
  des composants de formulaire déjà orphelins depuis la migration Vue de la
  phase 6.4 et jamais nettoyés à l'époque : `components/alias-fields.blade.php`,
  `components/dynamic-fields.blade.php`, `components/dynamic-fields-display.blade.php`
  (zéro consommateur via balise de composant, vérifié).
- **Trouvailles annexes, zéro consommateur confirmé indépendamment** :
  `resources/views/welcome.blade.php` (scaffold Laravel par défaut, jamais
  routé), `resources/views/status/index.blade.php` (`StatusController::index()`
  ne fait que rediriger depuis toujours), `components/mobile/workspace-switcher.blade.php`
  (le réglage « mobile home » qu'il exposait est déjà entièrement porté côté
  Vue par `SettingsWorkspacePanel.vue`, qui pilote son propre toggle
  `localStorage` indépendamment — ce toggle a lui-même été retiré le jour même,
  voir plus bas).
- `config('app.sidebar_variant')` retiré (ne servait que le variant
  `legacy-sidebar` du layout mort) ; entrées `app.css`/`app.js` retirées de
  `vite.config.js`.
- **Conservé sans y toucher**, conformément à la gouvernance : tout `x-ui.*`/
  `x-erp.*` sous `packages/*/resources/views` (API Blade publique kView — le
  produit réel des packages design-system, démontré par le Component Lab et
  couvert par `PackageContractTest`/`TokenAccessibilityTest`, sans rapport
  avec le shell kView INVESTOR mort) et `components/layout/prepaint-preferences.blade.php`
  (partagé avec `inertia.blade.php`, toujours actif côté Vue pour éviter un
  flash de layout sur l'état replié de la sidebar).

**Suivi exécuté le 22 juillet 2026, dans la foulée** : le toggle « mobile
home » de `SettingsWorkspacePanel.vue`, repéré vestigial plus haut (la classe
CSS `mobile-home-root` qu'il active n'est plus posée sur aucune page Vue,
donc sans effet visuel constatable), a été retiré entièrement plutôt que
laissé en l'état : le composant Vue perd son toggle, `mobileHomeStorageKey`
disparaît du type `SettingsPayload['workspace']` et du payload PHP
(`ApplicationSettings::payload()`), et `prepaint-preferences.blade.php` perd
sa branche `kview_investor.mobileHome.enabled` — la condition combinée `(mobile &&
!mobileHome) || (desktop && sidebarHidden)` se simplifiait algébriquement en
`mobile || sidebarHidden` puisque `mobileHome` valait toujours `false` en
pratique (identité `A ∨ (¬A ∧ B) ≡ A ∨ B`), confirmé équivalent au
comportement de montage de `ApplicationShell.vue` (`mobileNavigationOpen`
démarre à `false`, donc la nav est masquée par défaut sur mobile
indépendamment de tout état « mobile home »). Cascade : les quatre
composants Blade sous `resources/views/components/mobile/` restants
(`bottom-search`, `dev-return`, `nav-list`, `nav-row`) n'avaient plus aucun
consommateur externe depuis la suppression de `workspace-switcher.blade.php`
et du dashboard Blade — supprimés avec le reste du dossier. Dans le même
mouvement, `tailwind.config.js` a été retiré et le plugin `tailwindcss` sorti
de `postcss.config.js` : la phase 9 avait déjà confirmé zéro directive
`@tailwind` et zéro classe utilitaire brute restante nulle part dans le repo
ou les packages kView, donc plus aucune raison de le garder câblé dans le
build de kView INVESTOR.

**Correctif le jour même** : la suppression initiale de la dépendance npm
`tailwindcss` (pas seulement du plugin PostCSS) cassait
`npm run test:ui-consumer` — ce harness reconstruit une fixture Laravel
jetable pour prouver que les packages kView fonctionnent chez un consommateur
Blade-only qui utilise réellement Tailwind, et réutilise pour cela le binaire
`node_modules/.bin/tailwindcss` de la racine kView INVESTOR plutôt que d'installer
Tailwind séparément dans la fixture (`scripts/verify-ui-package-consumer.mjs`,
exécuté en CI par `.github/workflows/ui-packages.yml`). `tailwindcss` est donc
réinstallé comme devDependency — outil de test uniquement, toujours absent du
build applicatif de kView INVESTOR (`postcss.config.js`/`tailwind.config.js` restent
sans Tailwind). Trouvé dans le même passage, sans rapport avec ce chantier :
`tests/Fixtures/ui-package-consumer/vite.config.mjs` n'aliasait pas
`chart.js` pour la fixture, un oubli datant de l'ajout de `KviewChart.vue` en
phase 8 — corrigé au passage (même pattern que les alias `reka-ui`/`vue`
existants). Les trois consommateurs Laravel 11/12/13 repassent au vert.
**Gate re-vérifié : 230 PHP / 5 480 assertions, 214 Vitest, TypeScript/ESLint/
build verts, Pint et PHPStan sans nouvelle erreur imputable à ce changement,
vérification Playwright manuelle de l'onglet Workspace de la modale
Settings.**
- **Tests retirés ou adaptés en conséquence** : `tests/Unit/FrontendMigrationGuardrailTest.php`
  supprimé (chaque assertion caractérisait un fichier désormais absent) ;
  `tests/frontend/GlobalSearch.spec.ts` supprimé (testait `global-search.js`
  directement) ; `UiDataComponentsTest.php` perd sept tests qui rendaient
  `<x-layout.*>` ou lisaient les modules vanilla, et
  `test_application_views_keep_behavior_and_styles_out_of_blade` est réduit à
  son scan générique (plus d'assertion sur `layouts/app.blade.php`) ;
  `UiPackageArchitectureTest.php` repointé de `app.css`/`app.js` vers
  `ui-storybook.css`/`ui-storybook.js` — le Component Lab est désormais le
  seul point réel qui instancie encore le JS vanilla des packages kView (pour
  démontrer `x-ui.*`/`x-erp.*` à côté de leurs équivalents Vue), donc la
  bonne preuve « un vrai consommateur câble ces packages » ;
  `UserProfileSettingsTest.php` perd le test de la branche fragment HTML
  disparue. `tests/e2e/application-global-surfaces.spec.ts` avait aussi
  dérivé indépendamment de cette phase : un test entier caractérisait encore
  `/dashboard` comme Blade (obsolète depuis la phase 8.2) et un autre
  vérifiait un lien « Compare Blade » déjà retiré pendant la phase 8.5 — les
  deux corrigés au passage.

Gate (jeu de démo local) : 230 tests PHP (5 492 assertions, contre 236 avant
— sept tests obsolètes en moins, aucune régression), 214 tests Vitest (contre
215 — `GlobalSearch.spec.ts` en moins), TypeScript/ESLint/build verts, Pint et
PHPStan sans nouvelle erreur sur les fichiers touchés. Vérification manuelle
Playwright : connexion, les onze routes migrées, ouverture de la modale
Settings (onglets Account et Configuration) et le Component Lab — zéro erreur
console, zéro requête en échec sur l'ensemble du parcours.

#### Critère de sortie

Chaque suppression est justifiée par une recherche de consommateurs et une
suite de tests verte. Aucune suppression massive n'est réalisée sur la seule
base d'un objectif de réduction de lignes. **Satisfait au 22 juillet 2026** :
plus aucun fichier kView INVESTOR-propre lié au vieux shell Blade ne subsiste ; ce
qui reste sur disque est soit l'API Blade publique kView (conservée par
design), soit du code activement utilisé par au moins une route ou un test
réel.

## 11. Règles de gouvernance

### À faire

- une phase ou un composant partagé par changement relisible ;
- migrations route par route ;
- mesures avant et après ;
- contrats publics stables ;
- décisions documentées ;
- tests de parité ;
- composants kView avant composants locaux ;
- logique de données dans sa couche propriétaire.

### À ne pas faire

- réécrire tout kView INVESTOR en une branche ;
- mélanger la migration Laravel et le redesign frontend ;
- importer Reka directement dans les pages ;
- déplacer toute la logique du tableau dans le navigateur ;
- remplacer kView par une bibliothèque générique ;
- chercher 100 % de Tailwind au prix de composants incohérents ;
- supprimer Blade avant la fin des consommateurs ;
- masquer les régressions d'accessibilité derrière des tests uniquement
  visuels.

## 12. Checklist globale de validation

Avant de considérer une phase terminée :

- [ ] `php artisan test` réussit ;
- [ ] PHPUnit cible uniquement la base PostgreSQL dédiée `kview_investor_testing`
  conformément à `dev_guide/testing-database-safety.md` ;
- [ ] Pint ne reçoit pas de nouvelle dette ;
- [ ] PHPStan ne reçoit pas de nouvelle erreur ;
- [ ] le build Vite de production réussit ;
- [ ] `vue-tsc` et ESLint réussissent pour le code migré ;
- [ ] les tests Vitest ciblés réussissent ;
- [ ] les parcours Playwright critiques réussissent ;
- [ ] les contrôles axe ne détectent pas de violation critique ;
- [ ] le clavier, Escape, le focus et le lecteur d'écran sont contrôlés ;
- [ ] desktop, sidebar contrainte, tablette et mobile sont vérifiés ;
- [ ] les performances ont été comparées à la baseline ;
- [ ] aucun package kView n'importe `App\...` ;
- [ ] aucun écran métier n'importe directement Reka ;
- [ ] les changements de contrats sont documentés dans le Component Lab ;
- [ ] les anciens fichiers ne sont supprimés qu'après vérification des
  consommateurs.

## 13. Réponse courte à donner à un développeur

> kView INVESTOR est aujourd'hui un monolithe Laravel 13. Laravel garde
> l'authentification, les
> permissions, la validation et le métier. Inertia 3 relie Laravel à Vue sans
> API frontend séparée et remplace progressivement notre navigation AJAX
> maison. Vue 3 et TypeScript gèrent l'interface et ses contrats. kView reste
> notre design system avec ses composants, ses formats financiers et son
> tableau ERP. Tailwind sert principalement à la composition des layouts.
> Reka UI est optionnel et uniquement utilisé sous certains composants kView
> pour des comportements accessibles complexes. La migration se fait route
> par route avec coexistence Blade/Vue et validation de parité avant toute
> suppression. Les textes, icônes de ressources et liens passent par des
> adaptateurs explicites : Laravel fournit le dictionnaire et la navigation,
> tandis que l'hôte choisit lien Inertia ou navigation complète.

## 14. Décisions enregistrées

| Sujet | Décision |
| --- | --- |
| Backend | Conserver Laravel/PHP et migrer vers Laravel 13. |
| Architecture frontend | Utiliser Inertia plutôt qu'une SPA et une API séparées. |
| Framework UI | Vue 3 avec TypeScript. |
| Design system | kView reste l'unique API visuelle publique. |
| Icônes | Un catalogue JSON kView alimente PHP, JavaScript historique et Vue. |
| Textes UI | Laravel sérialise le dictionnaire de l'hôte ; `KviewUiProvider` le distribue aux primitives. |
| Liens | `AppLink` choisit explicitement `Link` Inertia ou `<a>` natif ; aucun lien n'est deviné. |
| Reka UI | Optionnel, interne à kView et adopté composant par composant. |
| Blade | Maintenu pendant la transition et pour les consommateurs qui en ont besoin. |
| Tailwind | Conservé pour la composition ; pas d'objectif 100 % utilitaire. |
| Store global | Pas de Pinia sans besoin transversal démontré. |
| ERP | Migration Vue prévue, contrat PHP et sémantique financière conservés. |
| Données volumineuses | Tri, filtre, pagination et exports lourds côté serveur. |
| Migration | Progressive, mesurée et sans big bang. |
| Phase 3.5 | Shell authentifié local et contrats partagés ; aucun rollout produit avant le pilote. |
| Phase 4 | `/search` est une carte Inertia/Vue embarquée dans l'unique shell Blade historique ; topbar globale et recherche locale restent indépendantes, et l'ancienne vue Blade reste reproductible au commit `b276b8a`. |
| Phase 5.1 | Un layout Vue authentifié persiste entre deux routes techniques ; logout est `POST`, les sorties de session Inertia sont explicites, et le shell produit reste Blade jusqu'au portage de ses fonctions globales. |
| Phase 5.2 | Le shell Vue technique possède une recherche globale typée et accessible alimentée par l'API existante ; son fallback produit Blade et toutes les sorties natives restent actifs jusqu'au cutover. |
| Phase 5.3 | Le shell Vue technique possède les toasts globaux, Excel Login et un menu utilisateur complet, téléporté et accessible ; à ce checkpoint Settings est encore une sortie Blade native. |
| Phase 5.4 | Le shell Vue technique possède Settings avec un payload serveur unique Blade/JSON ; `/preferences?tab=...` garde la comparaison Blade et Reka reste interne à `KviewDialog`/`KviewDropdown`. |
| Clôture phase 5 | `/search` utilise le shell Vue/Inertia persistant standard. Pré-paint, scroll-region, navigation same-root, skeleton différé et sorties natives Blade fixent le contrat des futures pages ; les routes métier Blade passent en phase 6. |
| Phase 6.1 | `/activity` est la première route métier portée dans ce shell. Laravel conserve filtres, compteurs, tri, déduplication et pagination ; à ce checkpoint, les destinations encore Blade restaient natives. |
| Phase 6.2 | `exchange-rates.show` porte un détail simple sans changer son apparence ni ses contrats serveur. Carte, header detail, info cards et confirmation deviennent des APIs kView Vue ; l'index reste une frontière native. |
| Phase 6.3 | `call-distribs.show` et `capital-accounts.show` partagent `FinancialRecordPage`, les payloads de présentation, les formatters financiers et la confirmation commune ; Capital Account emploie la clé canonique `unfunded_commitment`. |
| Phase 6.4 | Calls/Distributions, Capital Accounts, Exchange Rates et Investments partagent `BusinessFormPage`, le schéma dynamique, le date picker et la recherche de relation ; la création Investment fonctionne aussi depuis un Investor ou un Fund et les routes lookup précèdent les `apiResource`. |
| Phase 6.5 | Funds, General Partners et Investors portent leurs fiches et formulaires ; Investment porte sa fiche. Les tables relationnelles et leurs previews utilisent l'entrée Vue ciblée de `ui-data`; Investor garde Cash flows et ajoute Performance/Exposures sans agrégation monétaire inter-devises. Les index ERP ne sont pas déclarés migrés. |
| Phase 7 lot 1 (7.1–7.5) | Le moteur d'index Vue `KviewCrudTable` (`@kview/ui-data/vue`) rend le schéma de colonnes normalisé serveur en lecture seule avec recherche, tri (cycle asc/desc/none) et pagination **côté serveur** via `App\Support\BusinessIndexPayload` + `App\Queries\GpListQuery` (whitelist de tri, champs dynamiques jsonb, `items_per_page` assaini). Le composant émet des intentions ; la page hôte fait la visite Inertia same-path (`preserveState`, `only: ['table']`). Spécimen authentifié local `/__kview/inertia/crud-table` sur les vraies données GP ; parité visuelle vérifiée desktop/contraint/mobile contre `/gps` (CSS du package partagé, dont le collapse container-query du bouton de création). `resources/css/inertia.css` importe désormais `@kview/ui-data/css/index.css`. Aucune route de production basculée. |
| Phase 7 lot 2 (7.6–7.9) | Le moteur `KviewCrudTable` porte les filtres de colonnes à opérateurs (texte : contains/equals/starts/ends/not_* ; nombre : `=` `!=` `>` `>=` `<` `<=`) **côté serveur** avec whitelist par colonne (`GpListQuery::FILTERS`, jsonb + sous-requête `funds_count`), le column manager (visibilité, réordonnancement par drag, reset), le redimensionnement pointer et la redistribution proportionnelle des largeurs (port de `fitColumnWidths`), la sélection en colonne sticky + suppression groupée, l'export CSV/XLSX client et les actions de ligne + preview drawer. Factorisations partagées Blade/Vue extraites d'`erp-table.js` : `table-export.js` (builder CSV/XLSX/zip) et `table-view-state.js` (localStorage `kview_investor.erp-table.<id>` → `{hidden, order, widths}` — les vues sauvegardées survivent à la bascule). Confirmations destructives via `KviewConfirmDialog` + `requestSubmit()` sur les formulaires Laravel natifs rendus par la page hôte (`<id>_bulk_delete`, `<id>_row_delete`). Panneaux positionnés par `KviewPopover` avec classes visuelles `erp-vue-panel*` ; rails Blade remplacés par des cellules sticky CSS (amélioration documentée, même UX). Vérifié visuellement : filtre appliqué serveur, manager, sélection, export, hover actions, mobile. |
| Phase 7 durcissement post-audit | Audit externe pris en compte, vérifié par interaction réelle (clics, scroll, marker SPA). Corrigés : actions de ligne réellement cliquables (`pointer-events` restauré au hover/focus de ligne, toujours actives en `@media (hover: none)` pour le tactile) ; défilement horizontal mobile (`overflow-x: auto` scopé `erp-table-card--vue`, l'équivalent CSS du switch JS Blade) ; filtre numérique durci (décimal borné sans exposant côté payload + cast `::numeric` de l'expression côté SQL — `bigint = '0.5'` était un 500) ; filtres/tri/recherche des champs dynamiques alignés sur la forme affichée (`replace('_',' ')` — `equals "United Kingdom"` matche) ; création et action d'état vide en visite Inertia (`createInertia`) ; checkboxes de sélection nommées par ligne ; index `funds(gp_id)` (la FK Postgres n'indexe pas seule). Restent notés pour 7.10 : artefact cosmétique de gouttière sticky au scroll mobile, h1 du spécimen, clavier pour resize/drag-reorder, passes axe. |
| Phase 7 lot 3 (7.10) | Accessibilité et perfs. `h1` de page via `headingLevel` (le payload passe 1), poignées de resize `role="separator"` + `aria-valuenow/min/max` pilotables au clavier (flèches, Shift=pas large, Home=reset, sur la largeur *préférée* pas rendue), column manager réordonnable au clavier (Alt+flèches) + boutons monter/descendre, checkboxes de sélection nommées par ligne. **axe wcag2a/2aa/21aa : 0 violation sérieuse/critique** (page + panneau ouvert), vérifié par script réel. Correctif de scoping clé : les popovers téléportés vers `<body>` se stylent sous `.erp-vue-panel`, pas `.erp-table-card--vue`. Séparateur sticky mobile ajouté. Tests : 2 spécs Vitest clavier + `tests/e2e/phase7-crud-table.spec.ts` (heading+axe, reorder/resize clavier, drawer desktop, scroll+visibilité mobile), verts desktop et mobile. **Limitation assumée** : sur tactile la colonne d'actions en fin de tableau (~353px) passe en défilement normal (sticky de table clippe) ; le tap fiable d'une action par ligne sur mobile est une décision produit renvoyée au pilote 7.11 (tap ligne = navigation ; envisager les actions dans le drawer/détail). Perfs : tout paginé serveur (`LIMIT`), index `funds(gp_id)` présent (seq scan tant que la table est minuscule) ; la sous-requête `funds_count` s'évalue 2× au filtrage — candidate à un lateral join plus tard. |
| Phase 7 durcissement post-audit 2 | Deuxième audit externe traité, vérifié par interaction réelle. **Sécurité** : neutralisation OWASP de l'injection de formules CSV (`neutralizeFormula` dans `table-export`, partagé Blade+Vue) ; normalisation stricte des paramètres GET (`scalarQuery` : arrays ignorés, bornage 200 car., UTF-8 invalide rejeté — plus de cast Array-to-string ni 500). **Clavier** : `handleRowKeydown` sort avant `preventDefault` si la cible est un contrôle → Space coche la case, Enter suit le lien GP (prouvé navigateur). **Popovers imbriqués** : `KviewPopover` reconnaît les couches Reka enfants (`data-reka-popper-content-wrapper`) → choisir un opérateur ne ferme plus le filtre, Escape ferme en cascade (dropdown puis filtre). **Mobile** : toolbar empilée sans chevauchement après sélection (delete replié en icône), colonnes d'actions non-sticky sur tactile. **A11y** : case select-all `indeterminate`/`aria-checked=mixed` en sélection partielle. **i18n** : `KviewCrudTable` pleinement localisable via prop `labels` (défauts EN, placeholders `:name`/`:count`/`:format`) ; `BusinessIndexPayload` passe le dictionnaire `kview-ui-data::table` → suit la locale (test fr). **Contrat package** : le fixture consumer importe et vérifie réellement `KviewCrudTable` + entrées `@kview/ui-data/vue`. **Perf** : mesure au volume (5050 funds) — `Bitmap Index Scan on funds_gp_id_index` dès filtre sélectif (0.19ms), pagination serveur confirmée. Reste noté : tap fiable d'action de ligne sur tablette étroite (décision UX 7.11). |
| Phase 7 durcissement post-audit 3 | Troisième audit traité puis assertions corrigées précisément. **CSV** : mitigation conservatrice partagée Blade/Vue — déclencheurs directs (tab/CR/LF inclus), formules après whitespace/BOM et variantes pleine largeur (＝＋－＠) préfixés ; toutes les cellules CSV sont entre guillemets pour empêcher un délimiteur alternatif de recréer une cellule. Aucune promesse universelle CSV : XLSX (inline strings) est préféré pour un usage tableur. **i18n complète** : chaque texte de contrôle générique (recherche, réglages, export, état vide, sélection, filtres, ordre/resize, pagination, confirmations) passe par `KviewCrudTableLabelOverrides`; les props explicites restent des overrides hôte. Le payload FR verrouille l'ensemble des clés, pas un échantillon de 11. **Serveur** : recherche inclut `funds_count` (parité Blade) ; pagination construite via `appends()` sur les valeurs assainies (plus de `withQueryString` brut — URLs bornées). **Mobile** : décision tranchée — sur tactile la colonne d'actions est masquée (mini-boutons non fiables dans une table qui défile ET redondants), `row tap → détail` couvre le parcours (prouvé) ; le handler d'action fonctionne mais le hit-test était capturé par le conteneur, donc pas d'affordance cassée livrée. **Docs** : `component-map`/`package-boundaries`/README alignés (entrée Vue = detail **+ crud-table engine**, public mais consommé seulement par le spécimen), changelog + bump `0.2.0` + manifeste de release. **Tests durables** : e2e Escape-cascade, `aria mixed`, non-chevauchement toolbar, mobile masqué + row-tap ; `scripts/phase7-crud-table-benchmark.php` reproductible (rollback) — `Bitmap Index Scan on funds_gp_id_index`, ~0.04ms. |
| Phase 7.11–7.12 | Les sept index de production utilisent le root persistant : GP pilote, puis Funds, Investors, Investments, Calls/Distributions, Capital Accounts et Exchange Rates. Tous réutilisent `BusinessIndexPage` + `KviewCrudTable`, `BusinessIndexPayload` et une `*ListQuery` whitelistée ; filtres texte/nombre/date/multiselect et pagination restent serveur. Les valeurs multiselect sont intersectées avec l'allow-list avant SQL, props et liens de pagination. Exchange Rates conserve sync ECB + Settings via le slot du header. Les parcours Investment, Call/Distribution et Capital Account issus d'un index conservent `return_index=1` de Create/Show/Edit jusqu'à Update/Delete ; `return_investment_index=1` préserve aussi cette origine lors d'un détour par un cash flow ou un capital account lié. Les suppressions restent des formulaires Laravel natifs avec CSRF/method spoofing. Les sept vues `index.blade.php` historiques ont été retirées après preuve ; les APIs package Blade restent publiques. **Gate final : 224 PHP / 5 359 assertions, 179 Vitest, 55 Playwright, 0 échec ; packages Laravel 11/12/13 verts. Phase close le 20 juillet 2026.** |
| UUID RayDocImport | UUIDv7 pour les nouveaux enregistrements à partir de Laravel 12 ; les UUIDv4 existants restent inchangés. |
| Phase 8 reconnaissance | La recherche/autocomplétion et les paramètres globaux sont déjà en Vue ; les imports sont synchrones (pas d'infra à porter) ; le vrai périmètre Phase 8 est les dashboards (`/`, `/dashboard`) et l'absence totale de librairie de graphiques Vue. |
| Phase 8 lot 1 (8.1) | `/` (Home) porte vers `Home/Index` sur le shell Inertia persistant, en réutilisant les constructeurs d'activité de `/activity` et les composants `KviewInfoCard`/`ActivityFeed` existants sans nouveau composant de rendu. `/dashboard` reste Blade et devient la route de référence pour la caractérisation du vieux shell. **Gate : 228 PHP / 5 524 assertions, 211 Vitest, Playwright vert hors quatre échecs préexistants reproduits sur le commit pré-8.1. Clos le 21 juillet 2026.** |
| Phase 8 lot 2 (8.2) | `/dashboard` porte vers `Dashboard/Index` (6 vues). `App\Support\DashboardPayload` formate l'affichage ; tout le calcul (conversion devise, bissection IRR, agrégations) reste dans le contrôleur. Deux primitives Chart.js/SVG nouvelles dans `@kview/visualization/vue` : `KviewChart` (Chart.js chargé dynamiquement, jamais au chargement de page, utilisé seulement par Quarterly history) et `KviewPerformanceChart` (port SVG sans dépendance du graphique Overview, volontairement hors `KviewChart`). Le sélecteur de vue est un `KviewDropdown` (fidèle à `x-ui.view-switcher`, qui est lui-même `x-ui.dropdown`), pas une bande d'onglets — corrigé après une première dérive vers `KviewTabs`. `dashboard/index.blade.php`, `dashboard-quarterly.js`/`.css`, et les composants orphelins `x-ui.top-performers`/`x-ui.latest-updates` sont supprimés après preuve ; `/imports` reprend le rôle de route de référence Blade pour `AuthenticationFlowTest`/`SearchTest`. **Gate : 230 PHP / 5 547 assertions, 215 Vitest, axe 0 violation critique/sérieuse, Playwright vert hors les mêmes échecs préexistants documentés en 8.1. Phase 8 close le 22 juillet 2026 — périmètre réel limité aux dashboards ; recherche/paramètres/imports restent hors chantier.** |
| Phase 8.5 | Le résidu Blade laissé de côté par la phase 8 est traité route par route : Admin validate-capital-accounts, FlexImport et RayDoc imports migrent vers Inertia/Vue (`PaginationPayload` et `FileDropzone.vue` partagés entre Home/RayDocs et FlexImport/RayDocs respectivement) ; le doublon Settings est retiré, la navigation directe redirigeant désormais vers la modale Vue via un paramètre `?settings=<tab>` rouvert après l'événement `navigate` d'Inertia. Le même jour, `/login` (`Auth/Login`, sans layout persistant, `KviewTextInput`/`KviewToggle`/`KviewButton`) et `/admin/dropdowns` (`Admin/DropdownsIndex`/`Admin/DropdownsEdit`, via un contrôleur kView INVESTOR `AdminDropdownController` qui remplace le contrôleur vendor `lncshared` dans le routage tout en réutilisant tel quel son service et sa form request, sans modifier le submodule) sont migrés à leur tour, sur demande explicite. Conséquence : plus aucune route kView INVESTOR n'utilise `layouts.dashboard`/`layouts.app` ni le bundle `app.js`/`app.css` — constatation consignée pour la phase 9, retrait non exécuté ici. `AuthenticationFlowTest`/`SearchTest` perdent leurs deux tests de caractérisation du vieux shell (prémisse disparue) ; `AuthenticationFlowTest` gagne une caractérisation directe de `/login`. La décision d'infrastructure asynchrone pour des imports longs reste ouverte et distincte de cette phase. **Gate final : 242 PHP / 5 658 assertions, 215 Vitest, TypeScript/ESLint/build/Pint verts, PHPStan sans nouvelle erreur. Phase close le 22 juillet 2026.** |
| Phase 9 | Le shell Blade mort constaté en phase 8.5 est retiré, 29 fichiers : les deux layouts, leurs partiels kView INVESTOR (`app-sidebar`, `legacy-sidebar`, `settings-modal-host`), le bundle `app.js`/`app.css` et ses quinze modules vanilla, `tailwind.config.js` laissé en place mais plus invoqué nulle part. Cascade découverte pendant l'exécution : la branche fragment HTML de `UserConfigController::edit()` n'était atteignable que par `settings-modal.js` (mort avec `app.js`) — simplifiée à deux branches (JSON, redirection) — entraînant le retrait de `layouts/fragment.blade.php`, `user_config/edit.blade.php` et trois composants de formulaire déjà orphelins depuis la migration Vue de la phase 6.4 (`alias-fields`, `dynamic-fields`, `dynamic-fields-display`). Trouvailles annexes à zéro consommateur : `welcome.blade.php` (scaffold jamais routé), `status/index.blade.php` (contrôleur qui ne fait que rediriger), `workspace-switcher.blade.php` (son réglage vit déjà côté Vue dans `SettingsWorkspacePanel.vue`, repéré vestigial mais non corrigé — signalé, hors périmètre). API Blade publique kView et `prepaint-preferences.blade.php` (partagé avec `inertia.blade.php`) conservés sans y toucher. `UiPackageArchitectureTest` repointé de `app.css`/`app.js` vers `ui-storybook.css`/`ui-storybook.js`, désormais le seul point qui instancie encore le JS vanilla des packages kView. Corrigé au passage : deux caractérisations obsolètes dans `application-global-surfaces.spec.ts` qui avaient dérivé indépendamment de cette phase (un test testait encore `/dashboard` comme Blade, un autre un lien « Compare Blade » retiré en phase 8.5). **Gate : 230 PHP / 5 492 assertions, 214 Vitest, TypeScript/ESLint/build/Pint verts, PHPStan sans nouvelle erreur ; vérification Playwright manuelle sur les onze routes migrées + modale Settings + Component Lab, zéro erreur console, zéro requête en échec. Phase close le 22 juillet 2026 — migration Blade → Inertia/Vue terminée.** |
| Phase 9 (suivi, même jour) | Les deux points laissés ouverts par la phase 9 sont tranchés : le toggle « mobile home » vestigial de `SettingsWorkspacePanel.vue` est retiré (composant Vue, type `SettingsPayload`, payload `ApplicationSettings::payload()`, `prepaint-preferences.blade.php` simplifié par identité booléenne `A ∨ (¬A ∧ B) ≡ A ∨ B` puisque `mobileHome` valait toujours `false`), entraînant la suppression des quatre derniers composants orphelins de `components/mobile/` (`bottom-search`, `dev-return`, `nav-list`, `nav-row`) ; et `tailwind.config.js` + le plugin PostCSS sont retirés du build kView INVESTOR, confirmé inutilisé à 100 % depuis la phase 9 — la dépendance npm `tailwindcss` elle-même reste en devDependency, car `scripts/verify-ui-package-consumer.mjs` (CI) en a besoin pour prouver que les packages kView fonctionnent chez un consommateur Blade+Tailwind externe. Corrigé au passage, sans rapport : un alias `chart.js` manquant dans la fixture de ce même harness (oubli de la phase 8). API Blade publique kView et `prepaint-preferences.blade.php` confirmés conservés (produit réel du design system / toujours actif côté Vue). **Gate : 230 PHP / 5 480 assertions, 214 Vitest, TypeScript/ESLint/build verts, Pint et PHPStan sans nouvelle erreur imputable, les trois consommateurs Laravel 11/12/13 repassent au vert ; vérification Playwright manuelle de l'onglet Workspace.** |

## 15. Références

### Internes

- `dev_guide/kview-design-system.md`
- `dev_guide/kview-ui-packages.md`
- `dev_guide/kview-component-lab.md`
- `dev_guide/frontend-migration-baseline.md`
- `dev_guide/frontend-inertia-foundation.md`
- `dev_guide/frontend-kview-vue-foundation.md`
- `dev_guide/bitbucket-ci.md`
- `dev_guide/uat-smoke-check.md`
- `.agents/skills/kview_investor-ui-development/references/package-boundaries.md`
- `.agents/skills/kview_investor-ui-development/references/table-contract.md`
- `.agents/skills/kview_investor-ui-development/references/verification-contract.md`

### Officielles

- Laravel 12 : <https://laravel.com/docs/12.x>
- Migration Laravel 12 : <https://laravel.com/docs/12.x/upgrade>
- Laravel 13 : <https://laravel.com/docs/13.x>
- Migration Laravel 13 : <https://laravel.com/docs/13.x/upgrade>
- Inertia 3 : <https://inertiajs.com/docs/v3>
- Vue 3 et TypeScript : <https://vuejs.org/guide/typescript/overview.html>
- TypeScript : <https://www.typescriptlang.org/docs/>
- Vite : <https://vite.dev/guide/>
- Tailwind CSS : <https://tailwindcss.com/docs>
- Reka UI : <https://www.reka-ui.com/docs/overview/introduction>
- WCAG 2.2 : <https://www.w3.org/TR/WCAG22/>
- Largest Contentful Paint : <https://web.dev/articles/lcp?hl=fr>
- Interaction to Next Paint : <https://web.dev/articles/inp?hl=fr>
