11 KiB
#154 — Refonte UI/UX de la visionneuse & éditeur XLSX (ruban, grille, inspecteur)
Item de roadmap : #154 — Refonte UI/UX tableur 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). 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
PaneManagerpour 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 pipelineputStructure). - A1.3 Badges d'état :
.xlsx-status-pilllecture 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-*(finiwindow.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-conflictavec 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
renderXlsxViewerdansfrontend/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 dansviewer.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_VERSIONincré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 <table> |
aucun | — | ✅ recommandé (conserve DOM, CSP, i18n, tests) |
- Performance : ne pas ré-écrire tout le DOM ; réutiliser le pipeline
appendWindow;content-visibility:auto; contain:strictsur les lignes ; garder la pagination serveur (500 × 40 = 20 000 cellules/feuille) plutôt qu'une virtualisation client complexe. - CSP :
main.pyautorise déjàesm.sh— une lib headless reste possible en Lot 4 si un vrai besoin de modèle de colonnes apparaît.
6. Critères d'acceptation (par lot)
- Lot 1 : une feuille unique affiche son onglet + « + » ; « + » ajoute une feuille via
PUT …/xlsx/structureet re-rend ;.xls/.odsmontrent le badge « lecture seule » (pas de « + », pas de structure, pas de dashboard) ; un.xlsxmontre le badge « formules non recalculées », un.csvnon ; les tests JSDOM/E2E existants restent verts + nouveaux tests. - Lots suivants : définis à leur ouverture.
7. Historique
| Date | Événement |
|---|---|
| 2026-09-29 | Audit UX (3 problèmes) + architecture cible + plan par lots ; Lot 1 livré (ruban groupé, onglets permanents + « + », badges d'état, tokens de grille) |
| 2026-09-29 | Lot 2 livré : dialogues thémés (showConfirm/showPrompt) pour la structure et la confirmation de perte, bandeau de conflit 409 non bloquant avec réessai, indicateur dirty (bouton + onglet) |
| 2026-09-29 | Lot 3 livré : le Tableau de bord passe dans un inspecteur droit repliable (grille toujours visible), en-tête d'inspecteur avec entrée Assistant IA et fermeture, responsive < 900 px |
| 2026-09-29 | Lot 4 livré : undo/redo (boutons + Ctrl+Z/Ctrl+Maj+Z/Ctrl+Y), chargement par IntersectionObserver, ARIA role="grid" ; le découpage modulaire est reporté en A5 |
| 2026-09-29 | Lot 5 livré (clôture #154) : extraction des unités sans état dans frontend/js/xlsx/* (refs.js, command-bar.js, dashboard.js), lien dashboard → grille, inspecteur redimensionnable, SW_VERSION v28 + pré-cache. Split view écarté (documenté) |