# #154 — Refonte UI/UX de la visionneuse & éditeur XLSX (ruban, grille, inspecteur) > **Item de roadmap :** [#154 — Refonte UI/UX tableur](../ROADMAP.md) > **Origine :** #152 / #153 (visionneuse XLSX fonctionnelle mais peu conviviale) > **Statut :** ✅ **terminé** — Lots 1 → 5 livrés le 2026-09-29 (A1-A5) > **Effort estimé :** 6-9 jours (Lot 1 ✅ · Lot 2 ✅ · Lot 3 ✅ · Lot 4 ✅ · Lot 5 ✅) > **Règle de maintenance :** la Roadmap porte les cases à cocher (suivi), cette fiche porte > l'analyse, l'architecture cible et le plan par lots. **Ne pas dupliquer le détail.** --- ## 1. Objectif Rendre la vue tableur d'ObsiGate **intuitive, moderne et hautement utilisable** en s'inspirant des standards du marché (Excel, Google Sheets, Airtable), **sans renier les contraintes du dépôt** : thème sombre, `vanilla JS`, **zéro framework, zéro build npm** ([`AGENTS.md`](../../AGENTS.md)). La refonte est **organique** : on améliore la coquille existante (`frontend/js/viewer.js::renderXlsxViewer`, `frontend/style.css`), on ne réécrit pas la grille ni le backend. ## 2. Audit UX — les 3 problèmes majeurs | # | Problème | Constat | Résolution | |---|---|---|---| | **P1** | **Aucune hiérarchie ni regroupement des commandes** | Rangée plate de boutons de poids identique (`viewer.js` toolbar historique) ; « Tableau de bord » *prependé* au runtime ; barre de formule réduite à un `input`. | **Barre de commandes groupée** (Formules · Insertion · Vue · Fichier), bouton **Enregistrer primaire**, état *dirty*. | | **P2** | **Grille sans affordances : en-têtes = cellules** | Contraste faible entre `th` et `td`, pas de zébrage, pas de survol lisible, cellule active peu marquée. | **Tokens de grille** + en-têtes plus clairs/interactifs, zébrage, survol, cellule active en bordure accent. | | **P3** | **États avancés traités comme du contenu** | Dashboard *inline* qui pousse la grille, troncature/lecture seule/formules non recalculées sans emplacement dédié, `confirm()`/`prompt()` natifs. | **Couche UI dédiée** : bandeaux d'état + **inspecteur droit** (Lot 3) + dialogues thémés (Lot 2). | ## 3. Architecture cible de l'écran ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ BARRE APP (globale, existante) │ ├─────────────┬────────────────────────────────────────────────────────────┤ │ │ A. RUBAN — groupes Formules · Insertion · Vue · Fichier │ │ EXPLORATEUR│ B. BARRE DE FORMULE — [ A1 ] fx [ … ] │ │ DE FICHIERS│ C. BANDEAUX D'ÉTAT — lecture seule · formules non recalculées│ │ (sidebar) ├──────────────────────────────────────────────┬─────────────┤ │ │ D. GRILLE (en-têtes clairs, zébrage, survol) │ E. INSPECTEUR│ │ │ │ (dashboard + │ │ │ │ IA, repliable)│ │ ├───────────────────────────────────────────────┤ │ │ │ F. ONGLETS FEUILLES + « + » · 500/522 │ │ └─────────────┴───────────────────────────────────────────────┴─────────────┘ ``` - **A. Ruban** : groupes d'actions avec séparateurs ; actions de style désactivées (styles lus, pas écrits). Bouton **Enregistrer** en accent, désactivé si rien de *dirty*. - **B. Barre de formule** : zone nom + champ + badge de session `f(x)`. - **C. Bandeaux d'état** : empilables, non bloquants ; portent lecture seule et « formules non recalculées ». - **D. Grille** : rendue côté serveur (`backend/xlsx_reader.py`), habillée et câblée par le front. - **E. Inspecteur** : **à venir (Lot 3)** — Tableau de bord + Assistant IA dans un panneau droit repliable (réutilise `PaneManager` pour le détachement), au lieu du dashboard *inline* actuel. - **F. Onglets feuilles** : permanents (même à une seule feuille) + bouton « + ». ## 4. Plan par lots (incréments livrables) ### Lot 1 — Coquille : ruban groupé, onglets permanents, badges d'état ✅ *(2026-09-29)* - **A1.1** Barre de commandes groupée (`.xlsx-cmdbar`, `.xlsx-cmd-group`, `.xlsx-cmd-sep`, `.xlsx-save-primary`), IDs existants conservés (compatibilité tests JSDOM/E2E). - **A1.2** Onglets de feuilles **toujours rendus** (non-CSV) + bouton **`+`** `.xlsx-tab-add` → `sheet_add` (même pipeline `putStructure`). - **A1.3** Badges d'état : `.xlsx-status-pill` **lecture seule** (`.xls`/`.ods`) et **formules non recalculées** (non-CSV). - **A1.4** Tokens de grille (`--grid-bg`, `--grid-header-bg`, `--grid-header-text`, `--grid-border`, `--grid-zebra`) déclinés dark/light + affordances (en-têtes clairs, zébrage, survol, cellule active solide, cellule *dirty* prioritaire au survol). ### Lot 2 — Dialogues thémés & feedback ✅ *(2026-09-29)* - **A2.1** Helpers génériques **`showConfirm()` / `showPrompt()`** (`frontend/js/ui.js`), promise-based, réutilisant les classes `.obsigate-modal-*` (fini `window.confirm()` / `window.prompt()`). - **A2.2** La visionneuse XLSX utilise ces dialogues pour les actions de structure (ajouter / renommer / dupliquer / supprimer feuille, insérer / supprimer ligne et colonne) et pour la confirmation de perte (409 `xlsx_lossy_content`). - **A2.3** **Conflit de sauvegarde (409 `conflict`)** : bandeau **non bloquant** `.xlsx-banner-conflict` avec bouton **Réessayer** — les modifications sont conservées. - **A2.4** Indicateur *dirty* sur le bouton **Enregistrer** et sur l'onglet de la feuille concernée. ### Lot 3 — Inspecteur droit ✅ *(2026-09-29)* - **A3.1** Le **Tableau de bord** quitte le flux de la grille pour un **panneau droit repliable** (`.xlsx-inspector`) : la grille reste visible à côté (`.xlsx-body` = `.xlsx-main` + inspecteur). - **A3.2** En-tête d'inspecteur : titre, bouton **Assistant IA** (ouvre le panneau latéral global existant) et bouton de fermeture. - **A3.3** Responsive : sous 900 px, l'inspecteur passe sous la grille. ### Lot 4 — Interactions : undo/redo, défilement, accessibilité ✅ *(2026-09-29)* - **A4.1** **Undo/redo** local (pile de commandes) pour les éditions de cellules : boutons **Annuler / Rétablir** dans le ruban + raccourcis `Ctrl+Z`, `Ctrl+Maj+Z`, `Ctrl+Y`. - **A4.2** Chargement des fenêtres via **`IntersectionObserver`** (repli sur l'écouteur de défilement pour les environnements sans IO). - **A4.3** Sémantique **ARIA** : `role="grid"` / `row` / `gridcell` / `columnheader` / `rowheader`. ### Lot 5 — Découpage modulaire & finitions ✅ *(2026-09-29)* - **A5.1** Extraction des parties pures/sans état du monolithe `renderXlsxViewer` dans `frontend/js/xlsx/` : **`refs.js`** (`parseRef`, `columnName`, `findTd`, `sheetOfRef`, `firstCellOfRange`), **`command-bar.js`** (`buildCommandBar` : onglets + ruban + pastilles), **`dashboard.js`** (`renderDashboardLoading`, `renderDashboardHtml`). Le noyau **avec état** (orchestration DOM, édition, écouteurs) reste dans `viewer.js` : l'extraction est volontairement limitée aux unités sans état, à comportement constant et sous couvert des tests. - **A5.2** Lien **dashboard → grille** : cliquer une plage nommée sélectionne et révèle sa première cellule (change d'onglet si la plage est sur une autre feuille). - **A5.3** Inspecteur **redimensionnable** (poignée gauche, largeur 260–640 px, restaurée par session via `localStorage`). - **A5.4** `SW_VERSION` incrémenté (`v28`) et nouveaux modules ajoutés au pré-cache du service worker. > **Hors périmètre (documenté) :** le détachement de l'inspecteur en split view > (`PaneManager.splitRight()`) n'est pas retenu — l'inspecteur est intrinsèquement lié à la > visionneuse d'un document ; un split générique ouvrirait un second contexte sans le classeur. > À réévaluer si un usage concret apparaît. ## 5. Recommandations techniques (contrainte « zéro build ») | Option Data Grid | Build | Licence | Verdict | |---|---|---|---| | AG Grid Community | npm + bundler | MIT | ❌ viole « zéro build », réécrit le DOM, casse les tests | | Handsontable | npm + bundler | **commerciale** | ❌ licence non libre | | TanStack Table | headless (importable esm.sh) | MIT | ⚠ possible sans build, mais *headless* → gain limité | | **Grille maison sur `