# kView INVESTOR — Fondation kView Vue, shell produit et cutovers métier phases 6–7

> Statut : phases 3 à 7 validées ; les sept index ERP sont en production Vue/Inertia
>
> Date : 20 juillet 2026
>
> Portée : socle kView, shell persistant, recherche, activité, consultations et formulaires métier

## 1. Résultat

`kview/ui-core` possède désormais deux APIs publiques alimentées par les mêmes
tokens, icônes, traductions et familles CSS :

```text
Blade : x-ui.*
Vue   : Kview*
```

L'entrée facultative `@kview/ui-core/vue` expose trente-et-un composants UI et
`KviewUiProvider`, soit trente-deux composants Vue publics, plus le composable
`useKviewToast`. Un projet Blade ne
charge toujours ni Vue, ni Inertia. Un projet Vue compile seulement l'entrée
source dont il a besoin dans son propre build Vite.

```ts
import {
    KviewAlert,
    KviewAvatar,
    KviewBreadcrumbs,
    KviewButton,
    KviewButtonGroup,
    KviewCard,
    KviewCombobox,
    KviewConfirmDialog,
    KviewDatePicker,
    KviewDialog,
    KviewDropdown,
    KviewEmptyState,
    KviewFormActions,
    KviewFormField,
    KviewIcon,
    KviewIconButton,
    KviewLoadingState,
    KviewMoneyInput,
    KviewPageHeader,
    KviewPercentInput,
    KviewPopover,
    KviewResourceHeader,
    KviewResourceHeading,
    KviewSectionHeader,
    KviewSegmentedControl,
    KviewTabs,
    KviewTextarea,
    KviewTextInput,
    KviewToggle,
    KviewToastProvider,
    KviewTooltip,
    KviewUiProvider,
    useKviewToast,
} from '@kview/ui-core/vue';
```

« Phase 3 terminée » signifie que le socle nécessaire à la première route
pilote est disponible et testé. La phase 4 l'utilise désormais sur `/search`.
Cela ne signifie pas que toutes les surfaces Blade, le tableau ERP ou les
graphiques possèdent déjà un équivalent Vue.

## 2. Catalogue des adaptateurs

| Contrat Blade | Adaptateur Vue | Contrat conservé |
| --- | --- | --- |
| `x-ui.icon` | `KviewIcon` | nom, alias, fallback, taille, stroke, image nommée ou décorative |
| boutons kView | `KviewButton` | variantes, lien ou bouton, disabled, loading, icônes |
| `x-ui.button-group` | `KviewButtonGroup` | groupe nommé, taille, sans faux état de sélection |
| `x-ui.card` | `KviewCard` | surface, header/body, padding default/compact, rayon, divider, ombre et contexte de container |
| `x-ui.icon-button` | `KviewIconButton` | taille stable, label accessible, disabled et politique de tooltip |
| `x-ui.tooltip` | `KviewTooltip` | `aria-describedby` sur le vrai contrôle, Teleport dans `body`, collision et Escape |
| avatar applicatif | `KviewAvatar` | image autorisée par l'hôte, libellé accessible et fallback de deux initiales |
| popover non modal | `KviewPopover` | bouton déclencheur nommé, Teleport, collision, focus initial, Escape et nettoyage |
| dialogue modal | `KviewDialog` | Teleport, contenu extérieur indisponible, piège de focus, Escape/backdrop et restitution du déclencheur |
| confirmation destructive | `KviewConfirmDialog` | `alertdialog`, Cancel initial, Escape/backdrop, pending, événement confirm et restitution du déclencheur |
| `x-ui.dropdown` dans `x-ui.form-field` | `KviewDropdown` | valeur formulaire, listbox, clavier/typeahead, options désactivées, erreurs ARIA et Teleport |
| autocomplete de relation | `KviewCombobox` | champ texte et valeur cachée distincts, listbox, clavier, sélection requise, clear, états ARIA et Teleport |
| `x-ui.date-picker` dans `x-ui.form-field` | `KviewDatePicker` | valeur ISO `YYYY-MM-DD`, fallback natif, calendrier jour/mois/année, bornes, clavier, Escape et restitution du focus |
| `x-ui.alert` | `KviewAlert` | types, live-region, message, fermeture Vue et réouverture sur un nouveau `resetKey` |
| `x-ui.loading-state` | `KviewLoadingState` | skeleton, spinner, progression et ARIA |
| `x-ui.empty-state` | `KviewEmptyState` | variantes, action native/Inertia explicite et tailles |
| `x-ui.form-field` | `KviewFormField` | label, hint, erreur, required, disabled et busy |
| `x-ui.text-input` | `KviewTextInput` | `v-model`, attributs natifs et composition des états/relations ARIA |
| `x-ui.textarea` | `KviewTextarea` | même shell de champ, lignes et attributs natifs |
| `x-ui.money-input` | `KviewMoneyInput` | valeur non transformée, monnaie visible et métadonnée de scaling |
| `x-ui.percent-input` | `KviewPercentInput` | valeur non transformée, suffixe `%` et saisie décimale |
| `x-ui.form-actions` | `KviewFormActions` | submit/cancel, alignement, sticky et loading |
| `x-ui.resource-heading` | `KviewResourceHeading` | ressource, icône, eyebrow, description et niveau sémantique |
| `x-ui.page-header` | `KviewPageHeader` | retour ou breadcrumbs, heading, actions, responsive et variant record `detail` |
| `x-ui.resource-header` | `KviewResourceHeader` | surface ressource, actions et heading explicite |
| `x-ui.section-header` | `KviewSectionHeader` | section compacte, actions et plan de titres |
| `x-ui.breadcrumbs` | `KviewBreadcrumbs` | landmark, page courante et liens fournis par l'hôte |
| `x-ui.tabs` | `KviewTabs` | `v-model`, panels, roving focus et Left/Right/Home/End |
| `x-ui.segmented-control` | `KviewSegmentedControl` | boutons `aria-pressed`, liens `aria-current` et valeurs typées |
| `x-ui.toggle` | `KviewToggle` | checkbox natif, `role="switch"`, description et valeur unchecked |

L'entrée facultative `@kview/visualization/vue` expose aussi
`KviewInfoCard`, `KviewMetricTrend` et `KviewPerformanceBadge`. Ces composants
réutilisent les classes Blade et restent des présentateurs : le serveur ou
l'hôte formate les nombres, dates et valeurs financières avant le rendu.

L'entrée facultative `@kview/ui-data/vue` expose `KviewTableCell`,
`KviewDetailTable`, `KviewRowActions`, `KviewPreviewDrawer` et
`KviewCrudTable`. Les quatre premiers couvrent les relations des fiches métier.
Le dernier est le moteur des sept index de phase 7 : recherche, tri et filtres
serveur, pagination, préférences de colonnes, sélection, exports,
prévisualisation et confirmation destructive. Il reste portable et n'importe
ni Inertia ni une classe `App\...`.

`KviewUiProvider` n'est pas une primitive visuelle. Il distribue le contexte
de l'hôte aux composants portables.

## 3. Catalogue d'icônes canonique

La géométrie SVG n'est plus synchronisée à la main entre Blade et Vue. L'unique
source de vérité est :

```text
packages/kview-ui-core/resources/icons/kview-icons.json
```

Le fichier contient actuellement 54 icônes, 35 alias historiques et le
fallback `external`.

```text
kview-icons.json
    -> Kview\UiCore\IconRegistry -> <x-ui.icon>
    -> icon-registry.ts          -> kview-icons.js / KviewIcon
```

`KviewIconName`, `KviewIconAlias` et `KviewIconIdentifier` sont dérivés du JSON
pour TypeScript. Un nom inconnu produit le même fallback dans les trois
runtimes. Toute nouvelle icône ou tout nouvel alias doit donc être ajouté au
JSON, jamais à un tableau PHP, TypeScript ou Vue séparé.

## 4. Dictionnaire UI typé

Le package ne doit importer ni les modèles kView INVESTOR, ni son traducteur, ni sa
configuration. L'application adapte ces données à sa frontière :

```text
traductions kview-ui-core + config/ui.php
    -> App\Support\KviewUiProps
    -> HandleInertiaRequests : prop `ui`
    -> SharedPageProps : KviewUiSerializedContext
    -> KviewUiHost
    -> KviewUiProvider
    -> useKviewUi() dans les primitives
```

Le contexte sérialisé contient :

- `messages` pour les alertes, actions communes et libellés de navigation ;
- `resourceIcons` pour résoudre une clé telle que `fund` ou
  `general_partner` sans lire `config/ui.php` dans Vue.

Ce dictionnaire couvre la copie réutilisable des primitives. Les libellés
propres à l'application, comme `Funds` ou `Settings`, restent dans
`ApplicationNavigation`; ils ne deviennent pas des traductions du package par
accident.

Le package conserve `defaultKviewUiMessages` en anglais pour fonctionner seul.
kView INVESTOR injecte les traductions Laravel actives. Les composants acceptent
toujours une prop explicite lorsqu'un écran doit remplacer un libellé précis.

Le contexte runtime contient également `linkComponent`, mais cette valeur
n'est pas sérialisable et n'est pas envoyée par Laravel. `KviewUiHost` lui
fournit localement `AppLink`.

## 5. Adaptateur de liens et navigation partagée

`resources/js/components/application/AppLink.vue` rend :

- un `Link` Inertia lorsque `inertia` vaut `true` ;
- un élément `<a>` natif lorsque `inertia` vaut `false` ou n'est pas fourni.

Cette distinction empêche une page Vue de traiter accidentellement une route
Blade comme une page Inertia. Les breadcrumbs, boutons, choix segmentés et
résultats de recherche peuvent utiliser le même point d'adaptation sans que le
package dépende d'Inertia. L'adaptateur conserve les attributs `aria-label` et
`aria-current` transmis par ces composants, sauf remplacement explicite. Il
relaie aussi le clic en phase de capture avant le handler Inertia : un contrôle
désactivé peut donc annuler la navigation. Enfin, son API `focus()` cible
toujours l'ancre réellement rendue, y compris lorsqu'elle est enveloppée par
le composant `Link`.

Le lien de la fondation publique vers le shell authentifié reste lui aussi un
`<a>` natif. Sans session, Laravel peut ainsi charger la page de login comme un
document normal au lieu de faire intercepter cette frontière d'authentification
par une visite ou une modale Inertia.

`App\Support\ApplicationNavigation` calcule désormais une seule structure
serveur avec la marque, les URLs, sections, permissions, icônes, états actifs,
modales, le mode de navigation, l'URL de données Settings et son URL de
comparaison Blade. Elle est consommée par :

- `resources/views/layouts/dashboard.blade.php` pour le shell Blade ;
- `HandleInertiaRequests` pour la prop typée `navigation` du shell Vue.

Les entrées de navigation métier actuelles déclarent encore `inertia: false`,
car leurs destinations servent du Blade. La route produit `/search` et l'action
de recherche complète déclarent `inertia: true`, car elles partagent maintenant
le shell Vue persistant. `ApplicationGlobalSearch` n'honore toutefois ce flag
que si son hôte active aussi `useInertia` ; un fallback conserve donc des
ancres natives. Un flag ne passe à `true` qu'après migration de la destination
et compatibilité de sa racine ; il n'est jamais déduit de l'URL.
Settings constitue une commande modale explicite, pas une destination Inertia :
son ancre reste native, mais le shell persistant intercepte seulement le clic
primaire ordinaire pour ouvrir `ApplicationSettingsDialog`. Ctrl/Cmd/clic
milieu et le lien de comparaison conservent une navigation serveur réelle.

Les sidebars Blade moderne et legacy honorent toutes deux le booléen `active`
calculé par cet adaptateur, y compris sur les sous-routes de consultation et
d'édition. L'entrée Investments couvre aussi les routes enfants
Call & Distribution et Capital Accounts : ces écrans ne perdent donc pas leur
repère actif.

## 6. Phase 3.5 : shell applicatif local

`resources/js/components/application/ApplicationShell.vue` prouve que ce
contrat suffit à composer une racine Vue authentifiée :

- sidebar groupée et état actif issu du serveur ;
- sidebar desktop ouverte à `240px` ou masquée à `0`, sans rail compact ;
- drawer mobile déclaré comme dialogue, arrière-plan inerte, piège Tab,
  verrouillage du scroll, backdrop, Escape et restauration du focus après le
  retrait de l'état inerte, y compris lorsque le viewport repasse en desktop ;
- topbar, recherche HTML focalisable avec `Ctrl/Cmd+K` après fermeture du
  drawer ou du menu compte, et disclosure de compte accessible ;
- skip link vers le contenu ;
- messages flash rendus avec `KviewAlert`; `flash.id` alimente `resetKey` afin
  qu'un nouveau message identique réapparaisse après une fermeture locale ;
- navigation complète vers chaque route Blade actuelle.

La démonstration est disponible uniquement en `local` et `testing` :

```text
GET /__kview/inertia/application-shell
```

Elle requiert une session authentifiée. Ce n'est pas une route métier. Son rôle
historique était de valider les fondations ; la clôture de phase 5 applique
désormais le même contrat de layout à `Search/Index`.

La phase 4 avait choisi une frontière plus prudente pour le produit : la carte Vue
`Search/Index` était montée par `inertia-dashboard.blade.php` à l'intérieur du
dashboard Blade historique. Il n'existait donc qu'une topbar et une sidebar
classiques sur `/search`. Le menu global de la topbar conservait ses suggestions
et actions rapides ; seul le formulaire de la carte pilotait le contenu local.

### Phase 5.1 : layout persistant technique

`resources/js/layouts/AuthenticatedLayout.vue` monte un seul
`ApplicationShell` autour des pages qui l'adoptent avec `defineOptions`. Deux
routes authentifiées `local/testing` valident l'aller-retour :

```text
GET /__kview/inertia/application-shell
GET /__kview/inertia/application-shell/secondary
```

Playwright vérifiait que la même instance DOM, l'état de sidebar, le focus, le
drawer mobile et l'accessibilité survivaient au changement de slot. À ce
checkpoint, cette preuve n'était pas encore le rollout du shell produit.

La route `/logout` accepte maintenant seulement `POST`. Blade utilise un
formulaire avec jeton CSRF ; Vue envoie `router.post` avec état pending et garde
anti-double clic. Un logout Inertia ou une session expirée renvoie `409` et
`X-Inertia-Location: /login` afin de charger proprement la page Blade. Le flux
natif non authentifié conserve la redirection Laravel ordinaire.

### Phase 5.2 : recherche globale du shell technique

`ApplicationGlobalSearch.vue` remplace le champ simple du banc technique par
le même service global que la topbar historique : presets, suggestions par
ressource et actions rapides. Il consomme toujours `/search/suggest`; Laravel
partage désormais l'URL, les presets et les actions par défaut via
`ApplicationNavigation` et `GlobalSearchConfiguration`.

Le composant conserve le focus dans la saisie et expose les options par
`aria-activedescendant`. Il gère ArrowUp/ArrowDown, Enter, Escape, le focus ou
clic extérieur, l'annonce live, le debounce, l'annulation immédiate et les
réponses désordonnées ; une nouvelle saisie retire immédiatement la sélection
précédente. Le panneau est téléporté dans `body`, collisionné sur le viewport
visuel — y compris lorsque le clavier mobile le réduit — et nettoyé au
démontage. Il n'utilise ni hook vanilla ni
`window.kviewFloating`.

Cette équivalence ne change pas la frontière produit : les résultats, actions
et soumissions restent natifs vers les écrans Blade, et `/search` conserve la
topbar Blade indépendante de son champ local Vue. Le composant historique et
le composant Vue ne possèdent jamais le même DOM.

### Phase 5.3 : surfaces globales du shell technique

`AuthenticatedLayout` enveloppe maintenant son shell persistant dans
`KviewToastProvider`. Les flashes et les résultats d'Excel Login passent par
`useKviewToast`, tandis qu'une seule instance de
`useApplicationExcelLogin` partage la requête, le pending et le protocole
entre la topbar et le menu utilisateur.

`ApplicationUserMenu` compose `KviewPopover` et `KviewAvatar` avec les URLs et
l'avatar fournis par Laravel. Il expose User Settings, Data Status, Workspace,
Excel et logout sans dupliquer ces routes dans Vue. Le popover non modal est
téléporté dans `body`, collisionné et refermé par Escape avec restitution du
focus. À la fin de ce checkpoint, le shell produit et la modale Settings
demeuraient Blade ; le lot 5.4 ci-dessous ajoute ensuite la comparaison Vue.

### Phase 5.4 : Settings Vue comparable au fallback Blade

`ApplicationSettingsDialog` porte maintenant les six sections du Settings
historique dans le shell technique : Account, Preferences, Workspace,
Integrations, System et Configuration pour les administrateurs. Il charge les
données à l'ouverture, garde la page montée derrière le dialog, possède ses
onglets, erreurs inline, focus de validation, pending, toasts et annulation de
requêtes, et réutilise l'instance Excel déjà détenue par le shell.

Laravel reste l'unique source de vérité. `App\Support\ApplicationSettings`
construit le contexte utilisé par la vue Blade et sérialise le `SettingsPayload`
typé pour les requêtes qui annoncent à la fois JSON et le header modale. Vue ne
reconstruit ni permissions, ni options, ni URLs d'actions. Les mêmes endpoints
de profil, mot de passe, préférences et champs dynamiques acceptent leurs
contrats historiques et renvoient erreurs `422` ou succès JSON au client Vue.

Le clic primaire ordinaire depuis la sidebar ou le menu ouvre la version Vue ;
l'ancre reste réelle pour un clic modifié ou une navigation native. Dans la
modale, « Compare Blade » ouvre `/preferences?tab=<onglet actif>` dans un
nouvel onglet. Le portage peut donc être comparé écran par écran et retiré sans
supprimer le propriétaire Blade pendant cette étape.

Les mécaniques invisibles proviennent de Reka uniquement à l'intérieur de
`KviewDialog` et `KviewDropdown`. kView INVESTOR importe ces deux noms depuis
`@kview/ui-core/vue`, réutilise les classes et tokens kView et ne dépend jamais
de composants Reka dans un panel métier.

## 7. Recherche migrée en phase 4

`SearchController@index` renvoie la page Inertia `Search/Index`. Son contrat
`SearchPageProps` reste volontairement petit :

- `query`, chaîne normalisée par Laravel ;
- `searchUrl`, URL canonique de soumission ;
- `groups`, trois groupes stables contenant uniquement les textes, icônes,
  identifiants et URLs nécessaires au rendu.

Laravel conserve les requêtes, l'ordre, la limite et la normalisation des
modèles. Vue possède le champ local, le pending, l'erreur réseau, la soumission
Inertia et la resynchronisation avec les props lors des retours historique.
`SearchResultGroup` reste un composant de rendu sans fetch ni connaissance du
routeur. Ses liens GP, fonds et investisseurs reçoivent explicitement
`inertia: false` et sortent donc par navigation native vers les pages Blade.

Au checkpoint phase 4, `inertia-dashboard.blade.php` embarquait cette carte
dans le dashboard historique avec `[data-frontend-owner="vue"]`. Cette
architecture reste utile uniquement comme référence mesurée : la clôture de
phase 5 supprime `inertia-dashboard.blade.php`, `inertia-dashboard.ts` et
`inertia-dashboard.css`. `/search` utilise maintenant `inertia.blade.php`,
`inertia.ts` et `AuthenticatedLayout`, tandis que `search.css` et
`search-results.css` sont importés par `inertia.css`.

Le contrat JSON `/search/suggest` reste inchangé et actif dans les deux shells.
Dans le shell Vue produit, l'action et la soumission vers `/search` utilisent
Inertia parce que le host active `useInertia`. Le panel se ferme et invalide les
requêtes en vol avant navigation. Les autres actions et résultats restent
natifs tant que leurs contrôleurs renvoient Blade.

Le même bootstrap pré-paint est chargé avant assets par les deux racines. La
préférence desktop de sidebar est correcte dès le premier rendu Vue ; le drawer
mobile reste transitoire. `#application-content` porte `scroll-region`, et le
shell conserve donc son identité pendant la sauvegarde/restauration du scroll.
`aria-busy` est immédiat ; le skeleton contenu attend `120 ms`, uniquement lors
d'un changement de pathname, sans masquer les recherches same-path ni ajouter
un second progress bar. La view transition reste limitée au contenu et respecte
la réduction des animations.

### Phase 6.1 : Activity dans le shell produit

`Activity/Index` est la première page métier ajoutée au layout persistant après
Search. Laravel garde filtres, compteurs, ordre, déduplication, historique
consulté et pagination ; `ActivityFeed` reçoit seulement des props typées de
présentation. Les filtres et la pagination restent same-root Inertia. À ce
checkpoint, Home et les fiches encore Blade étaient des sorties natives ; les
fiches portées dans les lots 6.3 à 6.5 utilisent maintenant Inertia.

Ce lot introduit l'entrée publique `@kview/visualization/vue` avec
`KviewPerformanceBadge`, sans déplacer le formatage financier ni importer une
source physique du package.

### Phase 6.2 : détail des taux de change

`ExchangeRates/Show` réutilise exactement le même `AuthenticatedLayout`. Son
payload contient une paire et cinq cartes déjà formatées par Laravel ; la page
ne connaît ni Eloquent, ni Carbon, ni les règles financières.

Le portage a fait émerger cinq contrats réutilisables plutôt que des copies
locales :

- `KviewCard` / `x-ui.card` pour la surface et ses insets ;
- `KviewPageHeader variant="detail"` pour Back, titre et actions ;
- `KviewInfoCard` et `KviewMetricTrend` dans le package de visualisation ;
- `KviewConfirmDialog` au-dessus de `KviewDialog` pour la décision
  destructive accessible.

À ce checkpoint, Back et Edit restaient des ancres natives car leur index et
leur formulaire étaient encore Blade. Le lot 6.4 a depuis porté Create/Edit sur
`ExchangeRates/Form`; la phase 7.12 porte l'index sur
`ExchangeRates/Index`. La suppression conserve un formulaire Laravel CSRF avec
méthode `DELETE` simulée et appelle `requestSubmit()` seulement après
confirmation ; le redirect cible désormais l'index Inertia. La vue Blade du
détail est retirée.

### Phase 6.3 : consultations Call/Distribution et Capital Account

`CallDistribs/Show` et `CapitalAccounts/Show` utilisent
`FinancialRecordPage`, le header detail, les cartes de métriques, le résumé et
les sections métier communs. `BusinessRecordPayload` et `RecordFieldsPayload`
conservent Laravel comme propriétaire des relations, libellés, routes et
formats financiers ; Vue ne reçoit que des chaînes et structures de
présentation typées.

Les actions Edit/Delete, le retour vers l'investissement parent et la
confirmation partagent les mêmes contrats que les autres pages Inertia. Les
anciennes vues Blade `show` sont retirées ; ce lot ne migrait encore aucun
index ERP. La phase 7.12 porte depuis les deux index sur `Business/Index`. La
métrique et les sections Capital Account utilisent la clé canonique
`unfunded_commitment`.

### Phase 6.4 : formulaires CRUD simples

`CallDistribs/Form`, `CapitalAccounts/Form`, `ExchangeRates/Form` et
`Investments/Form` sont des wrappers minces autour de `BusinessFormPage`.
`BusinessFormPayload` groupe les champs métier ; `DynamicFormSchema` adapte les
champs du workspace ; `DynamicRecordFields` choisit la primitive kView sans
dupliquer la présentation dans chaque page.

Le lot ajoute `KviewDatePicker` et `KviewCombobox` dans `ui-core`, puis
`RecordAutocomplete` à la frontière kView INVESTOR pour la recherche distante,
l'annulation des requêtes obsolètes et l'hydratation de la relation. Les erreurs
Laravel restent reliées au contrôle, le premier champ invalide reçoit le focus
et les valeurs financières restent non transformées. La création Investment
depuis un Investor ou un Fund préremplit et verrouille seulement la relation
d'origine ; la création autonome reste disponible.

Les routes API nommées de lookup Funds, GPs et Investors restent placées avant
leurs `apiResource`, afin que Laravel ne traite jamais `lookup` comme un ID de
ressource.

### Phase 6.5 : grandes entités métier

`Funds/Show`, `Gps/Show`, `Investors/Show` et `Investments/Show` réutilisent
`PortfolioRecordPage` ou `FinancialRecordPage`. `Funds/Form`, `Gps/Form` et
`Investors/Form` réutilisent le renderer de formulaire du lot 6.4. Les
compositions applicatives communes couvrent headers, résumés, champs,
métriques, onglets, alias, relations et suppression.

Les tables liées consomment la nouvelle entrée publique
`@kview/ui-data/vue` : `KviewTableCell`, `KviewDetailTable`,
`KviewRowActions` et `KviewPreviewDrawer`. Elles conservent le registre de types
et les formats déjà calculés par Laravel. Les vues Blade `show`, `create`,
`edit` et `_form` remplacées sont retirées. Les vues d'index ont depuis été
remplacées en production par la phase 7 puis retirées après le gate final et la
preuve de non-consommation.

Les previews de lignes Investments, cash flows, capital accounts, Funds et
Investors passent par ce drawer partagé et conservent Escape/restauration du
focus. La fiche Investor garde Cash flows avec ses sous-vues métier et expose
Performance/Exposures, sans agréger des montants de devises différentes.

### Phase 7 : index métier et `KviewCrudTable`

`KviewCrudTable` est le moteur public de la table d'index. Il reste dans
`kview/ui-data` et ne connaît ni Inertia, ni Laravel, ni les modèles kView INVESTOR.
Il rend les colonnes normalisées et émet des intentions de recherche, tri,
filtres, pagination et page size. Il possède l'état local des colonnes,
sélection, export CSV/XLSX, previews et confirmations accessibles.

kView INVESTOR fournit trois adaptateurs minces :

- `BusinessIndexPayload` sérialise les colonnes/cellules présentées, URLs,
  libellés, previews, confirmations et pagination ;
- `AbstractIndexListQuery` et une `*ListQuery` par ressource whitelistent les
  expressions SQL, opérateurs et valeurs ;
- `BusinessIndexPage` convertit les intentions en visites Inertia partielles
  `only: ['table']` et rend les formulaires Laravel natifs de suppression.

Les filtres texte, décimaux bornés, dates ISO `YYYY-MM-DD` et multisélections
bornées partagent ce contrat. Laravel garde la recherche, le tri et la
pagination côté serveur ; Vue ne charge que la page courante. Les casts des
valeurs JSON numériques/dates sont protégés avant PostgreSQL, et les clés,
opérateurs ou valeurs non déclarés sont ignorés.

Les routes de production GPs, Funds, Investors, Investments,
Calls/Distributions et Capital Accounts réutilisent `Business/Index`.
Exchange Rates réutilise le même `BusinessIndexPage` dans
`ExchangeRates/Index` et compose seulement la synchronisation ECB avec période
et Settings dans le slot `header-actions`. Les détails, Create et Edit sont des
visites Inertia ; les suppressions gardent CSRF et `_method=DELETE` natifs.
`return_index=1` distingue le retour depuis les index Investment,
Call/Distribution et Capital Account de leurs retours contextuels depuis une
fiche parent ; ce marqueur traverse Create/Show/Edit puis Update/Delete.
Un détour depuis une fiche Investment issue de l'index vers ses cash flows ou
capital accounts conserve cette même origine avec
`return_investment_index=1`.

Le touch masque la colonne terminale d'actions dont le hit-test est fragile
dans un scroller horizontal ; le tap de ligne ouvre la fiche et conserve le
parcours complet. Les préférences de colonnes utilisent le même contrat
localStorage que Blade, afin de survivre au cutover.

Les sept anciennes vues `index.blade.php` de l'application ont été retirées
après preuve de non-consommation et validation navigateur. Ce retrait
ne concerne jamais les APIs publiques Blade `x-erp.crud-table`,
`x-smart-table`, leurs registres, CSS, JavaScript, traductions ou fixtures de
consommation Laravel 11/12/13.

La clôture 7.11–7.12 du 20 juillet 2026 enregistre `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, consommateurs propres Laravel 11/12/13 et Composer sont verts. Le
benchmark reproductible conserve une pagination serveur et utilise
`funds_gp_id_index` (`Bitmap Index Scan`, environ `0,10 ms` localement).

## 8. Règles de propriété

- Une page métier importe l'entrée publique `@kview/ui-core/vue`, jamais un
  fichier physique du package.
- Aucun fichier sous `packages/` n'importe `App\...`, Inertia ou un modèle
  kView INVESTOR.
- kView INVESTOR possède `KviewUiProps`, `KviewUiHost`, `AppLink`,
  `ApplicationNavigation`, `ApplicationSettings`, `AuthenticatedLayout`,
  `ApplicationShell`, `ApplicationSettingsDialog`, `SearchResultGroup`,
  `Activity/Index`, `ActivityFeed`, les pages métier sous `resources/js/Pages`,
  `BusinessFormPage`, `FinancialRecordPage`, `PortfolioRecordPage`,
  `BusinessIndexPage`, les composants `business`, `forms` et `records`, ainsi
  que les serializers `BusinessFormPayload`, `DynamicFormSchema`,
  `BusinessRecordPayload`, `RecordFieldsPayload`, `BusinessIndexPayload` et les
  requêtes `*ListQuery`.
- Les composants Vue ne rendent aucun hook `data-ui-*` et n'appellent aucun
  `window.kview*`; Vue possède leur interaction.
- Les composants Blade conservent leurs hooks et leurs modules vanilla jusqu'à
  la migration de leur dernier consommateur.
- Une racine Vue volontairement embarquée dans un spécimen porte
  `[data-frontend-owner="vue"]`. Les routes produit migrées n'utilisent plus
  cette frontière : elles possèdent un document Inertia complet et partagent
  le layout persistant.
- Le shell Vue possède la recherche globale ; `Search/Index` possède son champ,
  ses erreurs et ses résultats locaux. Les shells Blade et Vue ne partagent
  jamais le même propriétaire DOM.
- La sidebar desktop, dans Blade comme dans Vue, n'a que deux états :
  ouverte à `240px` ou cachée à `0`.
- Les primitives Vue réutilisent les modules CSS kView. Le shell, les pages de
  recherche et métier ainsi que leurs compositions partagées gardent des
  modules applicatifs nommés.
  Le module du shell résout couleurs, ombres, rayons, espacements, typographies,
  transitions et couches avec les tokens kView plutôt qu'avec des valeurs
  visuelles parallèles.

## 9. Component Lab

La section `Parité Blade / Vue` de `/__kview` affiche les composants Blade
réels dans le document principal et les adaptateurs Vue réels dans un iframe
Inertia isolé. Ce choix reste utile pour le catalogue. Le produit `/search`
utilise, lui, la racine Inertia standard et le shell Vue persistant.

Les surfaces peuvent aussi être ouvertes directement :

```text
/__kview/inertia/components
/__kview/inertia/application-shell   # authentification requise
/__kview/inertia/application-shell/secondary   # authentification requise
```

## 10. Éléments volontairement différés

- Reka UI est interne aux seuls `KviewDialog`, `KviewDropdown` et
  `KviewCombobox` ;
  `KviewConfirmDialog` compose le premier sans exposer Reka ;
  `ApplicationGlobalSearch` reste une composition métier application-owned et
  `KviewPopover` conserve son implémentation package autonome. Toute extension
  de Reka doit rester derrière un nouveau contrat kView justifié par un vrai
  consommateur.
- `KviewViewSwitcher` reste à construire avec un écran consommateur réel.
  `KviewCombobox` et `KviewDatePicker` sont désormais publics et testés ;
  kView INVESTOR ajoute seulement `RecordAutocomplete` pour la recherche de relations
  distante propre à l'application.
- `kview/visualization` expose InfoCard, MetricTrend et PerformanceBadge ;
  `kview/ui-data` expose TableCell, DetailTable, RowActions, PreviewDrawer et
  le moteur d'index `KviewCrudTable`.
- Les sept index métier de phase 7 consomment désormais `KviewCrudTable`. Les
  APIs Blade ERP/smart-table restent supportées pour les routes et projets qui
  les consomment encore ; une migration produit n'est pas une suppression de
  package.
- Les graphiques et dashboards restent Blade/vanilla jusqu'à leur phase dédiée.
- Les contrôleurs produit migrés rendent `/search`, `/activity`, les détails,
  formulaires et index des taux de change, cash-flows, comptes de capital,
  investissements, fonds, General Partners et investisseurs avec Inertia. Les
  autres routes non migrées restent Blade.
- Le shell Vue est le layout produit de ces routes et possède sa recherche
  globale, ses toasts, Excel Login, le menu utilisateur complet et Settings.
  `/preferences` conserve la comparaison Blade ; les index ERP ont rejoint ce
  shell dans la phase 7.

Ces limites sont des frontières de séquençage, pas des oublis. Ajouter toutes
les primitives complexes avant un consommateur réel augmenterait le risque de
divergence sans accélérer une route réellement portée.

## 11. Vérification

Les contrôles pertinents sont :

```bash
php artisan test
./vendor/bin/pint --test
./vendor/bin/phpstan analyse
npm run typecheck
npm run lint:frontend
npm run test:frontend
npm run build
composer validate --no-check-publish
php artisan view:cache
npm run test:ui-release
npm run test:ui-consumer
npm run test:e2e
```

Les suites ciblées protègent notamment :

- le schéma et la parité PHP/TypeScript du registre d'icônes ;
- le dictionnaire Laravel et les valeurs par défaut du provider ;
- la fusion de `aria-describedby`, le clavier des choix et le tooltip porté ;
- le remplacement dynamique d'un ID de tooltip sans laisser l'ancien
  `aria-describedby` sur le contrôle ;
- la fermeture globale par Escape d'un tooltip ouvert au pointeur, ainsi que
  le nettoyage de son état et de ses listeners lors d'un changement dynamique
  de `disabled` ou de texte ;
- la normalisation de `aria-invalid` / `aria-busy` externes, y compris
  `grammar` et `spelling`, sans écraser l'erreur ou le loading du composant ;
- le choix explicite entre lien Inertia et lien natif, la conservation de ses
  attributs ARIA, l'annulation d'un lien désactivé et le focus via l'adaptateur ;
- la parité de `KviewCard`, du variant `detail` de `KviewPageHeader`, de
  `KviewInfoCard` et de `KviewMetricTrend` avec leurs contrats Blade ;
- le `alertdialog` de `KviewConfirmDialog`, Cancel initial, Escape/backdrop,
  pending, anti-double confirmation et restitution du focus ;
- le modèle/texte distinct, la validation native, le clavier, le clear et le
  Teleport de `KviewCombobox` ; le format ISO, les bornes, les vues
  jour/mois/année, le clavier et la restitution du focus de `KviewDatePicker` ;
- les types de cellule, lignes activables, actions, état vide et preview de
  `KviewTableCell`, `KviewDetailTable`, `KviewRowActions` et
  `KviewPreviewDrawer` ;
- l'action native/Inertia de `KviewEmptyState` et la réouverture de
  `KviewAlert` lorsque son `resetKey` reçoit un nouveau `flash.id` ;
- l'état actif commun aux sidebars Blade moderne et legacy ;
- l'état Investments sur les routes enfants Call & Distribution et Capital
  Accounts ;
- la structure de `ApplicationNavigation` et du shell authentifié ;
- l'URL de suggestions, les presets et les actions globales partagés par
  Laravel, puis le propriétaire Vue : Teleport, ARIA, clavier, presets,
  debounce, abort immédiat, réponse obsolète, erreur et nettoyage ;
- le composant `SearchResultGroup`, le payload minimal `Search/Index`, la
  racine Inertia standard, le layout persistant et les visites Inertia vers les
  fiches désormais migrées ;
- le payload minimal `ExchangeRates/Show`, les cinq cartes formatées, les
  sorties Back/index explicites, Edit Inertia et le formulaire Laravel DELETE
  natif après confirmation ;
- les payloads et pages Show/Form des ressources de phase 6, les schémas
  dynamiques, l'autocomplete distant, les erreurs Laravel, les alias, les
  créations imbriquées Investment et les navigations natives/Inertia
  explicites ;
- les sept payloads d'index Phase 7 et leurs réponses Inertia partielles, les
  whitelists recherche/tri/filtres des `*ListQuery`, les filtres texte,
  nombre, date et multiselect, la pagination serveur et les paramètres hostiles
  sans erreur serveur ;
- `BusinessIndexPage` et `KviewCrudTable` : persistance du shell et de l'état
  des colonnes, sélection mixed, export, actions clavier, popovers imbriqués,
  previews, confirmations, formulaires delete natifs et row tap mobile ;
- l'index Exchange Rates avec autorisation, sync ECB/période, Settings et
  retour après opérations ;
- l'historique précédent/suivant, le scroll, le chargement, l'erreur réseau et
  sa relance sur `/search` ;
- l'unique topbar/sidebar Vue, l'identité DOM du shell, le scroll-region,
  l'activation explicite de `useInertia`, le pré-paint sans flash, le skeleton
  différé et la sidebar ouverte à `240px` ou masquée à `0` ;
- l'installation et la compilation de l'entrée Vue depuis une archive propre
  sous Laravel 11, 12 et 13 ;
- les contrôles navigateur desktop/mobile, axe et débordement horizontal.

Résultat historique du checkpoint de fondation des phases 3–3.5 :

- suite PHP complète réussie : 146 tests et 1 643 assertions, y compris les
  nouveaux contrats navigation, dictionnaire, icônes et recherche ;
- 45 tests Vitest réussis ;
- 9 parcours Playwright réussis : cinq desktop et quatre mobile, dont le shell
  authentifié dans les deux viewports ;
- consommateurs propres Laravel 11, 12 et 13, contrat `test:ui-release` et
  `composer validate` réussis ;
- typecheck, ESLint, build Vite, cache des vues, Pint ciblé et PHPStan ciblé
  réussis ;
- la dette PHPStan globale reste la baseline historique de 137 erreurs hors de
  ce chantier.

Le contrat navigateur dédié de phase 4 ajoute la vérification clavier, focus
après rendu, double soumission rapide, indépendance entre menu global et champ
local, frontière de runtime, axe sans violation, absence de chevauchement de la topbar à
1 024 px, absence de débordement et cinq largeurs : desktop large, desktop
contraint à 1 024 px, régression signalée à 919 px, tablette à 768 px et mobile
à 390 px. Le parcours réécrit passe sur desktop et mobile ; les largeurs
intermédiaires sont exécutées une seule fois côté desktop.

Après vidage du cache par CDP, la médiane desktop locale cold finale est :
responseEnd `25 ms`, DCL `63 ms`, load `119 ms`, transfert brut `641 468 B`,
décodé `686 411 B`, LCP `108 ms`, CLS `0` et aucune tâche d'au moins `50 ms`.
Le serveur PHP local ne compresse pas les assets ; le budget de `700 000 B`
protège donc le payload brut du shell Blade et de la carte Vue.

À chaud, la visite Inertia naturelle desktop a une médiane de `76 ms`, une
requête, `4,2–7,7 kB` transférés, INP `16–24 ms`, CLS `0` et aucune longue
tâche. Le délai artificiel de `250 ms` reste exclu de ces mesures. Ces chiffres
sont locaux, sans compression, et ne représentent pas des percentiles
production. Le test navigateur authentifié requiert
`php artisan db:seed --class=CrudDemoSeeder`, `E2E_EMAIL` et `E2E_PASSWORD`,
dont l'absence fait échouer la CI.

Le parcours Phase 5.1 ajoute la conservation d'identité DOM et d'état du shell,
le vrai rechargement natif vers `/search`, le logout `POST` et la sortie propre
d'une session expirée sur desktop et mobile. Le lot 5.2 ajoute au même parcours
un panneau de suggestions Vue peuplé : clavier, géométrie desktop/mobile,
absence de débordement et axe sans violation pendant son ouverture.

Le lot 5.3 ajoute `KviewToastProvider`, `useKviewToast`, `KviewPopover` et
`KviewAvatar`, puis les consomme dans le shell technique pour les flashes,
Excel Login et le compte. Le protocole Excel, l'état pending, le timeout et les
messages restent partagés par une seule instance. Le menu Teleport dans
`body`, collisionne dans le viewport, restaure le focus après Escape et expose
les sorties natives User Settings, Data Status et Workspace, l'action Excel et
le logout `POST`.

La validation complète après le lot 5.2 réussit `153` tests PHP / `1 797`
assertions, `13` fichiers Vitest / `60` tests et `11` passages du parcours
Inertia pour `3` skips intentionnels. Typecheck, ESLint, build, Pint ciblé et
PHPStan ciblé sont verts.

Après le lot 5.3, la validation compte `156` tests PHP / `1 869` assertions et
`17` fichiers Vitest / `75` tests. La matrice Playwright complète compte `24`
passages et `6` skips intentionnels sur desktop/mobile : elle caractérise le
shell Blade, valide la parité Vue des surfaces globales et rejoue les parcours
Inertia/Search existants. Les consommateurs propres Laravel 11, 12 et 13 ainsi
que le manifeste de release du package sont également verts.

Le lot 5.4 ajoute les tests package de `KviewDialog`/`KviewDropdown` et la
caractérisation du payload Settings commun. 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.
Le parcours Playwright Settings Vue passe sur desktop et mobile : JSON Vue,
comparaison Blade HTML, restitution du focus et `Ctrl/Cmd+K`, dropdown,
confirmation imbriquée et axe sont contrôlés.

Le premier consommateur propre a détecté la résolution manquante des peers de
l'entrée Vue hors du monorepo. Le package déclare maintenant `vue` et
`reka-ui` en `peerDependencies`, et le fixture de consommation les résout depuis
le host. Les consommateurs Laravel 11, 12 et 13 sont verts. Un consommateur
Blade-only n'importe pas cette entrée facultative et n'a besoin ni de Vue ni de
Reka dans son build navigateur.

Le contrat exhaustif reste défini dans
`.agents/skills/kview_investor-ui-development/references/verification-contract.md`.

La clôture complète passe `159` tests PHP / `1 960` assertions, `19` fichiers
Vitest / `86` tests et `29` scénarios Playwright réussis pour `7` skips
intentionnels. Le root persistant mesure localement `526 709 B` cold, LCP
`84 ms` desktop / `72 ms` mobile, CLS `0` et aucune longue tâche.

Les lots 6.3 à 6.5 ajoutent des tests de fonctionnalités Laravel pour chaque
route Show/Create/Edit, des tests Vitest sur les adaptateurs et compositions
partagés, ainsi que des parcours navigateur desktop/mobile sur les formulaires,
relations, confirmations et limites de layout. Les chiffres historiques ci-dessus
restent ceux de leurs checkpoints respectifs ; le résultat courant doit être lu
dans la dernière exécution CI, pas déduit de ces compteurs.

La validation locale de clôture 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 et les consommateurs propres Laravel
11, 12 et 13 sont également verts.

La clôture de phase 7 du 20 juillet 2026 ajoute `224` tests PHP / `5 359`
assertions, `37` fichiers Vitest / `179` tests et `55` scénarios Playwright,
avec `23` skips responsive intentionnels. TypeScript, ESLint, build, Pint et
PHPStan ciblés, manifeste de release, consommateurs Laravel 11/12/13 et
Composer sont verts. Ces nombres proviennent du run agrégé final, pas de tests
ciblés déduits pendant le développement.

## 12. Étape suivante

La phase 7 est terminée jusqu'à 7.12 : les sept index utilisent le shell
persistant, `BusinessIndexPage`, les payloads/requêtes Laravel typés et le
moteur public `KviewCrudTable`. Le gate agrégé, la QA navigateur et le retrait
prouvé des sept vues index Blade historiques sont enregistrés. La prochaine
étape est la phase 8 (dashboards et flux avancés). Les composants Blade publics
restent supportés et ne font pas partie de ce nettoyage applicatif.
