---
suivi: 1449
date: 2026-09-04
sujet: Parcelle — refonte mise en page fiche (grille, nav, blocs repliés)
chantier: foncier / parcelles
type: feature
statut: poussé
hash: bc8e3a1b
fichiers:
  - app/Services/Foncier/ParcelleFicheNavigationService.php
  - app/Http/Controllers/Foncier/ParcelleFicheNavigationController.php
  - app/Http/Controllers/Foncier/ParcelleController.php
  - routes/web.php
  - resources/js/Components/Foncier/ParcelleFicheNavigation.vue
  - resources/js/Pages/Foncier/Parcelles/Show.vue
  - tests/Unit/Parcelle1449SourceTest.php
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - public/build/
  - docs/suivi/SUIVI_1449_refonte_fiche_parcelle.md
---

# SUIVI #1449 — Refonte mise en page fiche parcelle

## PROMPT ENVOYÉ

Refonte d’affichage `/foncier/parcelles/{id}` : paliers 860 / 1280, grille,
plan sur deux colonnes, en-tête compact, blocs Occupation/Bâti/Valorisations
repliés (dépliage sur place), actes masqués si vides, filiation en bas,
colonne de navigation à droite (sticky, recherche serveur, 3 niveaux, droits).
Ne pas défaire #1446 / #1447. Pas de `overflow-x-hidden`. Pas de fixture CSS.

## FICHES LUES

- `#1447` LEÇON — ne pas défaire mention, lieu-dit Identification, valo en
  Dialog, `AFFICHER_CHARGES_PORTAGE = false`, icône historique, jamais
  `overflow-x-hidden`. Masquage UI ≠ fermeture de route (#1428).
- `#1446` — fusion état/type, bornage, feu, bâti, échéance : champs et
  whitelist inchangés.
- `#1271` / `#1444` — `overflow-x-hidden` a déjà masqué du contenu et un
  bouton. Interdit ici. `min-width: 0` sur les cellules de grille à la place.
- `#1428` — ne pas ajouter une surface sans filtre de visibilité.
- `#1434` — `GS_GEMME_PDG` dans `RecordAcl::BYPASS_PARCELLE_VIEW_GROUPS`.
- `#1442` — pas de réutilisation de `$groups` dans le contrôleur (variables
  `texteRecherche`, `codeInseeDemande`, `navigationFichePayload`,
  `idsDossiersAccessibles`).

## PRÉREQUIS

`origin/main` contient #1447 (`8df835c6` / fiche `0ea7fb8e`) et #1448
(`8e32798a` / `5c5806fc`) avant commit. Confirmé ancêtre. #1448 = fiche
dossier, pas de collision parcelle.

## SYNTHÈSE

Lot d’affichage. Aucune migration. Données et whitelist #1446 inchangées.
Modale de division non touchée (filles EN RÉSERVE).

### Paliers (viewport)

- `min-width: 1280px` : coque `width: 1280px; max-width: 100%` centrée ;
  zone principale **3 colonnes** + nav 240 px à droite.
- `min-width: 860px` et `< 1280px` : largeur fluide 100 % ; **2 colonnes**
  de blocs + nav 240 px.
- `max-width: 859px` : colonne unique ; nav en `<details>` sélecteur en tête.

À viewport 1280, `max-width: 100%` évite le débordement dû au `p-8` de
`AppShell` `<main>` (leçon #1271 : ne pas cacher un overflow, le prévenir).

### Grille

Rang 1 : Identification · Surfaces & bornage · Urbanisme.
Rang 2 : Propriétaire · Plan (`grid-area` plan, 2 colonnes au palier 1280 ;
carte Leaflet 480 px au lieu de 320 px).
Rang 3 : Occupation · Bâti · Valorisations (repliés par défaut).
Puis blocs restants (`order: 1`), filiation en dernier (`order: 2`).

### Colonne fixe

`position: sticky; top: 0` dans le `<main class="overflow-y-auto">` de
l’AppShell (scrollport réel). `max-height: calc(100vh - 9rem); overflow-y: auto`
si le contenu nav dépasse la fenêtre. Pas de `overflow-x-hidden`. Le
défilement interne n’apparaît que si les trois niveaux dépliés dépassent
la hauteur ; le défilement principal reste celui de `main`.

### Navigation / droits

`Parcelle::query()` **avec** `FoncierParcelleVisibiliteScope` (#1288 R1).
Jamais `withoutGlobalScope` sur ce scope. `authorize('view')` sur la parcelle
contexte avant recherche. Bypass inchangé (`GS_DIRECTION_GENERALE`,
`GS_GEMME_PDG`, `GS_ADMIN_ERP`). Pivot `dossier_foncier_parcelle` puis
intersection via Eloquent scopé (les ids non visibles tombent).

Recherche `GET /foncier/parcelles/{parcelle}/navigation?q=` (section, numéro,
lieu-dit) et `?commune=` pour déplier un groupe. Serveur, pas filtre client.

4 requêtes bornées au chargement de la nav (pivot + dossier + même commune +
GROUP BY `parcelles.code_insee_commune, communes.nom`).

### SQL (chargement fiche)

MySQL local refusé (`SQLSTATE[HY000] [2002]`). Pas de mesure runtime.
Inventaire **source** de `show()` avant ce lot : `load()` 9 relations +
dossiers, count ACL, valorisations, détentions, historique, secteur ×2,
charges, valo service, filiation, géomètres, candidats réunion, entités,
users — ordre de grandeur 20–40 selon `foncier.gestion` (N historique déjà
là). **Après** : les mêmes + **4** requêtes nav bornées. Recherche = +1 à
la demande. Pas de N+1 par parcelle listée.

## DÉPLOIEMENT-TEST

```
git pull origin main
php artisan optimize:clear
php artisan view:clear
php artisan journal:sync
```

Pas de migration.

## LEÇON

Une colonne sticky doit se caler sur le scrollport réel (`main` overflow-y),
pas sur `100vh` seul. Plusieurs blocs `grid-area` identique se superposent :
`order` + `grid-column: 1 / -1` pour le bas de fiche. `max-width: 100%` avec
largeur cible 1280 évite une scrollbar horizontale née du padding parent —
mieux que `overflow-x-hidden`.
