---
title: "Hub 'Santé de l'app' — Documentation fonctionnelle"
module: sante
type: functional
status: active
updated: 2026-07-08
---

# Hub "Santé de l'app" — Documentation fonctionnelle

> Tableau de bord d'audit technique d'ISI-APP, réservé aux super-développeurs ISI.
> Centralise cinq indicateurs de santé du codebase et des données, accessibles sans impacter les tenants clients.

---

## Vue d'ensemble

Le Hub Santé de l'app est une page d'administration (`/admin/app-health`) qui permet aux super-développeurs ISI d'auditer l'état interne d'ISI-APP : cohérence des données, qualité du code UI, fraîcheur des dépendances. Chaque audit est lancé manuellement, s'exécute en arrière-plan, et le résultat est persisté en base pour une lecture instantanée à tout moment.

---

## Accès

- **URL :** `/admin/app-health`
- **Restriction :** `isSuperDeveloper()` — super-développeurs ISI uniquement. Toute tentative d'accès sans ce droit retourne un 403.
- Aucun module client ni droit tenant requis.

---

## Les 3 onglets

| Onglet | Rôle |
|--------|------|
| **Vue d'ensemble** | Grille de 5 cartes résumant le statut et la valeur principale de chaque type d'audit. Permet de lancer ou relancer un scan. Cliquer sur une carte (en dehors du bouton d'action) ouvre directement le détail correspondant dans l'onglet **Intégrité des données** ou **Audit Onyx**. |
| **Intégrité des données** | Détail des résultats des 4 indicateurs d'intégrité (carte des relations, orphelins, normalisation, fraîcheur). Navigation par sous-onglet. |
| **Audit Onyx** | Résultat de l'audit de conformité UI : statistiques d'usage des composants Onyx et liste des anti-patterns Bootstrap/global détectés dans les vues Blade. |

---

## Lancer un scan

1. Aller sur l'onglet **Vue d'ensemble**.
2. Sur la carte souhaitée, cliquer sur **Lancer** (premier scan) ou **Relancer** (résultat déjà disponible).
3. Le temps que la requête parte au serveur, le bouton se désactive et affiche "Envoi en cours…" (retour visuel immédiat). Une fois le scan créé, le bouton passe à "Analyse en cours…" et la page se rafraîchit automatiquement toutes les 3 secondes via polling Livewire tant qu'un scan est actif.
4. Une fois terminé, la carte affiche la valeur principale et la date de génération.

Si un scan du même type est déjà en cours, un second clic est ignoré silencieusement (protection anti-doublon).

---

## Les 5 indicateurs

### Carte des relations

Construit une carte de toutes les relations entre tables depuis trois sources complémentaires : les clés étrangères déclarées en base (information_schema), les relations Eloquent parsées dans `app/Models/`, et les jointures explicites détectées par grep dans `app/`. La valeur principale affichée est le nombre total de relations dédupliquées.

### Détection des orphelins

S'appuie sur la carte des relations (dernière disponible) pour détecter les enregistrements enfants sans parent valide. **Un scan « Carte des relations » réussi doit exister au préalable** — sans lui, le scan des orphelins échoue explicitement avec un message invitant à lancer d'abord la cartographie, plutôt que de renvoyer un faux résultat « 0 orphelin » qui donnerait une confiance trompeuse.

Quatre niveaux de sévérité sont distingués, chacun détecté par une requête SQL dédiée et mutuellement exclusive (une ligne ne peut matcher qu'une seule des quatre) :

| Sévérité | Signification |
|----------|---------------|
| **FK null** | La colonne FK elle-même n'est pas renseignée — absence de relation, pas une relation brisée |
| **Orphelin vrai** | FK renseignée mais parent totalement absent de la table, dans aucun tenant |
| **Parent soft-deleted** | Parent présent mais archivé (`dtdel` non nul) |
| **Fuite cross-tenant** | Parent présent et actif, mais `idc` différent de l'enfant |

La valeur principale affichée est le nombre total d'orphelins (toutes sévérités confondues), calculé
de façon exacte (aucun plafonnement) via un `COUNT` SQL par catégorie. Toutes les relations de la
carte sont analysées à chaque scan (aucune limite par défaut).

Une même relation physique détectée par plusieurs sources de la carte des relations (ex. une clé
étrangère à la fois déclarée en base et devinée par l'analyse du code Eloquent) est dédupliquée en
amont : sans cette déduplication, ses orphelins auraient été comptés plusieurs fois dans le total
affiché.

Le détail de **tous** les enregistrements orphelins (aucun plafond par relation) est affiché dans un
tableau filtrable et triable côté serveur — deux filtres sont disponibles :
- **Sévérité** : FK null, parent absent, fuite cross-tenant, ou parent archivé
- **Table** : liste des tables enfants concernées par le dernier scan

Seule la dernière génération d'échantillons est conservée (un nouveau scan remplace intégralement la
précédente) — l'historique des scans eux-mêmes reste consultable, mais pas le détail ligne par ligne
des scans antérieurs.

### Conformité normalisation

Vérifie que les données stockées en base respectent les règles de normalisation appliquées par les triggers SQL (migration `2026_06_05`). Les règles couvrent : `trim`, `trim_collapse`, `email`, `phone`, `siret`, `postal`. Un échantillon de 500 lignes est analysé par colonne concernée. La valeur principale affichée est le taux de conformité global en pourcentage. Le détail par colonne (« Violations par colonne ») est trié par taux de conformité croissant : les colonnes les plus problématiques (0 % en premier) apparaissent en tête.

Un bouton « Voir » sur chaque colonne comportant des violations ouvre une fenêtre listant jusqu'à 3 exemples concrets — la valeur brute en base et sa version normalisée attendue — pour comprendre en un coup d'œil ce qui ne respecte pas la règle, sans avoir à interroger la base manuellement.

### Fraîcheur des dépendances

Appelle `composer outdated` et `npm outdated` pour comparer les versions installées aux dernières disponibles. Classe chaque package en `a_jour`, `retard_mineur` (patch/minor) ou `retard_majeur` (major). La valeur principale affichée est le nombre de packages obsolètes. La note globale est : rouge (au moins un retard majeur), orange (retard mineur uniquement), vert (tout à jour) — un sous-texte précise systématiquement sa composition (ex. « 2 paquet(s) en retard majeur »).

### Audit Onyx UI

Scanne toutes les vues Blade de `resources/views/` pour détecter :
- Les composants `x-onyx.*` utilisés (inventaire de l'adoption Onyx)
- Les anti-patterns à migrer : éléments HTML natifs (`<input>`, `<select>`, `<textarea>`), classes Bootstrap (`btn btn-*`, `badge bg-*`, `alert alert-*`, `card`, etc.), anciens composants `x-global.*`

La valeur principale affichée est le nombre total d'occurrences d'anti-patterns. L'onglet **Audit Onyx** détaille par règle, fichier et ligne.

---

## Mécanique asynchrone

Les scans ne s'exécutent jamais de façon synchrone. Le flux est :

1. **Déclenchement** : `AppHealthHub::startScan()` crée un enregistrement `AppHealthScan` en base avec le statut `pending`, puis dispatche `AppHealthScanJob` sur la queue `default`.
2. **Exécution** : le worker prend le job, passe le scan en `running`, exécute le service correspondant, puis enregistre le résultat JSON dans `jsresult` et passe le statut à `done` (ou `failed` avec message d'erreur).
3. **Lecture** : `AppHealthHub::render()` lit uniquement les derniers scans `done` depuis la base — il n'exécute aucun calcul. Le polling Livewire (3 s) rafraîchit la vue jusqu'à disparition des scans actifs.

Ce design garantit qu'un scan long (plusieurs minutes) n'impacte pas le temps de réponse HTTP et que le résultat reste consultable indéfiniment après la fin du job.

---

## Contournement cross-tenant délibéré

La détection des fuites cross-tenant dans `DataIntegrityService` nécessite de lire les colonnes `idc` de tables qui appartiennent à des tenants différents, sans scoper la requête. Ce contournement est intentionnel et documenté dans le code. Il est réservé aux super-développeurs ISI, qui ont précisément besoin d'une vision cross-tenant pour identifier les anomalies. Aucune écriture n'est effectuée.

---

## Fonctionnalité différée

Le sous-lot **suppression d'orphelins** (§6.8 de la spec) est volontairement non implémenté. Le hub est en lecture seule : il signale les anomalies mais ne les corrige pas automatiquement. La suppression d'orphelins fera l'objet d'un développement séparé avec des gardes-fous adaptés.
