fix(ui): expose legacy menu as parallel admin navigation
Some checks are pending
CI - Tests et Syntax / lint-and-test (push) Waiting to run

This commit is contained in:
root 2026-08-23 09:38:23 +00:00
parent 26c02c0c92
commit db46ec6374
7 changed files with 70 additions and 27 deletions

View file

@ -90,12 +90,26 @@
<li><a class="dropdown-item" href="{{ url_for('admin.permissions') }}"><i class="bi bi-key"></i> Catalogue des droits</a></li> <li><a class="dropdown-item" href="{{ url_for('admin.permissions') }}"><i class="bi bi-key"></i> Catalogue des droits</a></li>
<li><a class="dropdown-item" href="{{ url_for('status.index') }}"><i class="bi bi-activity"></i> État du système</a></li> <li><a class="dropdown-item" href="{{ url_for('status.index') }}"><i class="bi bi-activity"></i> État du système</a></li>
{% if has_permission('watchdog_dnd.view') %}<li><a class="dropdown-item" href="{{ url_for('logs.index') }}"><i class="bi bi-journal-text"></i> Journaux</a></li>{% endif %} {% if has_permission('watchdog_dnd.view') %}<li><a class="dropdown-item" href="{{ url_for('logs.index') }}"><i class="bi bi-journal-text"></i> Journaux</a></li>{% endif %}
</ul>
</li>
{% endif %}
{# L'ancien menu est une rubrique principale parallèle. Il reprend les
groupes historiques de base.html avant la Phase 1.6, sans réorganiser
leurs fonctions. #}
{% if has_permission('system.admin') %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle {% if request.path in ['/messagerie'] or '/pronote' in request.path or '/gmao-config' in request.path %}active{% endif %}" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-list-columns-reverse"></i> Ancien menu</a>
<ul class="dropdown-menu dropdown-menu-dark" style="background:#34495e; min-width:18rem;" aria-label="Navigation historique temporaire">
<li class="px-2"><small class="text-muted">Référence historique — temporaire</small></li>
<li><hr class="dropdown-divider" style="border-color:rgba(255,255,255,0.15)"></li> <li><hr class="dropdown-divider" style="border-color:rgba(255,255,255,0.15)"></li>
<li> <li><details class="px-2 py-1"><summary class="text-light" style="cursor:pointer"><i class="bi bi-house"></i> Principal</summary><div class="mt-1">
<details class="px-2 py-1" id="ancien-menu"> <a class="dropdown-item" href="{{ url_for('dashboard.index') }}">Tableau de bord</a>
<summary class="text-light small" style="cursor:pointer"><i class="bi bi-list-columns-reverse"></i> Ancien menu <span class="text-muted">(temporaire)</span></summary> <a class="dropdown-item" href="/messagerie">Messagerie</a>
<div class="mt-1" aria-label="Ancien menu complet"> </div></details></li>
<a class="dropdown-item" href="{{ url_for('planning.index') }}">Interventions · Planning unifié</a> <li><details class="px-2 py-1"><summary class="text-light" style="cursor:pointer"><i class="bi bi-wrench-adjustable"></i> Interventions</summary><div class="mt-1">
<a class="dropdown-item" href="{{ url_for('interventions.index') }}">Toutes les interventions</a>
<a class="dropdown-item" href="{{ url_for('planning.index') }}">Planning unifié</a>
<a class="dropdown-item" href="{{ url_for('planning.room_schedules') }}">Planning des salles</a> <a class="dropdown-item" href="{{ url_for('planning.room_schedules') }}">Planning des salles</a>
<a class="dropdown-item" href="{{ url_for('planning.tasks') }}">Tâches préventives</a> <a class="dropdown-item" href="{{ url_for('planning.tasks') }}">Tâches préventives</a>
<a class="dropdown-item" href="{{ url_for('planning.scheduled') }}">Tâches planifiées</a> <a class="dropdown-item" href="{{ url_for('planning.scheduled') }}">Tâches planifiées</a>
@ -111,6 +125,8 @@
<a class="dropdown-item" href="{{ url_for('exports.export_interventions') }}">Export interventions</a> <a class="dropdown-item" href="{{ url_for('exports.export_interventions') }}">Export interventions</a>
<a class="dropdown-item" href="{{ url_for('exports.export_costs_csv') }}">Export coûts et stock</a> <a class="dropdown-item" href="{{ url_for('exports.export_costs_csv') }}">Export coûts et stock</a>
<a class="dropdown-item" href="{{ url_for('scheduler.index') }}">Génération automatique</a> <a class="dropdown-item" href="{{ url_for('scheduler.index') }}">Génération automatique</a>
</div></details></li>
<li><details class="px-2 py-1"><summary class="text-light" style="cursor:pointer"><i class="bi bi-pc-display"></i> Équipements</summary><div class="mt-1">
<a class="dropdown-item" href="{{ url_for('equipments.index') }}">Tous les équipements</a> <a class="dropdown-item" href="{{ url_for('equipments.index') }}">Tous les équipements</a>
<a class="dropdown-item" href="{{ url_for('equipments.categories') }}">Catégories</a> <a class="dropdown-item" href="{{ url_for('equipments.categories') }}">Catégories</a>
<a class="dropdown-item" href="{{ url_for('buildings.index') }}">Bâtiments</a> <a class="dropdown-item" href="{{ url_for('buildings.index') }}">Bâtiments</a>
@ -138,23 +154,25 @@
<a class="dropdown-item" href="{{ url_for('equipments.to_trash') }}">À jeter</a> <a class="dropdown-item" href="{{ url_for('equipments.to_trash') }}">À jeter</a>
<a class="dropdown-item" href="{{ url_for('equipments.lifecycle_dashboard') }}">Cycle de vie</a> <a class="dropdown-item" href="{{ url_for('equipments.lifecycle_dashboard') }}">Cycle de vie</a>
<a class="dropdown-item" href="{{ url_for('equipments.trashed') }}">Anciens équipements</a> <a class="dropdown-item" href="{{ url_for('equipments.trashed') }}">Anciens équipements</a>
</div></details></li>
<li><details class="px-2 py-1"><summary class="text-light" style="cursor:pointer"><i class="bi bi-folder2-open"></i> Documents</summary><div class="mt-1">
<a class="dropdown-item" href="{{ url_for('documents.index') }}">Centre documentaire</a>
<a class="dropdown-item" href="{{ url_for('documents.fds') }}">FDS accessibles</a>
</div></details></li>
<li><details class="px-2 py-1"><summary class="text-light" style="cursor:pointer"><i class="bi bi-gear"></i> Configuration</summary><div class="mt-1">
<a class="dropdown-item" href="{{ url_for('gmao_config.index') }}">Contexte GMAO</a> <a class="dropdown-item" href="{{ url_for('gmao_config.index') }}">Contexte GMAO</a>
<a class="dropdown-item" href="{{ url_for('ai_config.index') }}">Configuration IA</a> <a class="dropdown-item" href="{{ url_for('ai_config.index') }}">Configuration IA</a>
<a class="dropdown-item" href="{{ url_for('constraints.index') }}">Contraintes de zone</a> <a class="dropdown-item" href="{{ url_for('constraints.index') }}">Contraintes de zone</a>
<a class="dropdown-item" href="{{ url_for('outlook_pages.index') }}">Comptes Outlook</a> <a class="dropdown-item" href="{{ url_for('outlook_pages.index') }}">Comptes Outlook</a>
<a class="dropdown-item" href="{{ url_for('outlook_pages.add_account') }}">Ajouter un compte Outlook</a>
<a class="dropdown-item" href="{{ url_for('outlook_dashboard.processed_folders_config') }}">Dossiers Outlook surveillés</a>
<a class="dropdown-item" href="{{ url_for('ent.index') }}">ENT77</a> <a class="dropdown-item" href="{{ url_for('ent.index') }}">ENT77</a>
<a class="dropdown-item" href="{{ url_for('ent.messages') }}">Messages ENT</a>
<a class="dropdown-item" href="{{ url_for('ent.interpretations') }}">Interprétations ENT</a>
<a class="dropdown-item" href="{{ url_for('ent.staff') }}">Personnels ENT</a>
<a class="dropdown-item" href="{{ url_for('pronote.connect') }}">PRONOTE (QR)</a> <a class="dropdown-item" href="{{ url_for('pronote.connect') }}">PRONOTE (QR)</a>
<form method="post" action="{{ url_for('pronote.sync_salles') }}"><button class="dropdown-item" type="submit">PRONOTE (synchroniser les salles)</button></form>
<a class="dropdown-item" href="{{ url_for('pronote.planning') }}">PRONOTE (plannings)</a> <a class="dropdown-item" href="{{ url_for('pronote.planning') }}">PRONOTE (plannings)</a>
<a class="dropdown-item" href="{{ url_for('pronote.rooms') }}">Salles PRONOTE</a> <a class="dropdown-item" href="{{ url_for('pronote.rooms') }}">Salles PRONOTE</a>
<a class="dropdown-item" href="{{ url_for('pronote.personnels') }}">Personnels PRONOTE</a> <a class="dropdown-item" href="{{ url_for('pronote.personnels') }}">Personnels PRONOTE</a>
<a class="dropdown-item" href="{{ url_for('pronote.professeurs') }}">Professeurs PRONOTE</a> <a class="dropdown-item" href="{{ url_for('pronote.professeurs') }}">Professeurs PRONOTE</a>
<a class="dropdown-item" href="{{ url_for('yeastar.index') }}">Téléphone P520</a> <a class="dropdown-item" href="{{ url_for('yeastar.index') }}">Téléphone P520</a>
<a class="dropdown-item" href="{{ url_for('yeastar.config_page') }}">Config téléphone</a>
<a class="dropdown-item" href="{{ url_for('yeastar.dnd_config_page') }}">Watchdog DND</a>
<a class="dropdown-item" href="{{ url_for('status.index') }}">Status système</a> <a class="dropdown-item" href="{{ url_for('status.index') }}">Status système</a>
<a class="dropdown-item" href="{{ url_for('logs.index') }}">Logs watchdogs</a> <a class="dropdown-item" href="{{ url_for('logs.index') }}">Logs watchdogs</a>
<a class="dropdown-item" href="{{ url_for('chatbot.chatbot_interface') }}">Chatbot</a> <a class="dropdown-item" href="{{ url_for('chatbot.chatbot_interface') }}">Chatbot</a>
@ -164,9 +182,7 @@
<a class="dropdown-item" href="{{ url_for('admin.template_debug') }}">Audit des templates</a> <a class="dropdown-item" href="{{ url_for('admin.template_debug') }}">Audit des templates</a>
<a class="dropdown-item" href="{{ url_for('setup_wizard.admin_panel') }}">Gestion bases</a> <a class="dropdown-item" href="{{ url_for('setup_wizard.admin_panel') }}">Gestion bases</a>
<a class="dropdown-item" href="{{ url_for('setup_wizard.index') }}">Setup Wizard</a> <a class="dropdown-item" href="{{ url_for('setup_wizard.index') }}">Setup Wizard</a>
</div> </div></details></li>
</details>
</li>
</ul> </ul>
</li> </li>
{% endif %} {% endif %}

View file

@ -540,3 +540,6 @@ lookback hors périmètre et son erreur de teardown.
- Le test d'horaires exceptionnels dépendait de fermetures créées par d'autres - Le test d'horaires exceptionnels dépendait de fermetures créées par d'autres
scénarios ; son isolement a été corrigé. Aucun nouvel échec fonctionnel ne scénarios ; son isolement a été corrigé. Aucun nouvel échec fonctionnel ne
subsiste dans la suite complète. subsiste dans la suite complète.
- La première présentation de l'ancien menu sous Administration a été
corrigée : il est désormais une rubrique principale parallèle, sans impact
sur les permissions backend.

View file

@ -59,9 +59,14 @@ Entreprises
Entreprises, Contrats, Services, Pièces Entreprises, Contrats, Services, Pièces
Documents Documents
Centre documentaire, FDS accessibles Centre documentaire, FDS accessibles
Administration Administration
Utilisateurs, Rôles et permissions, État du système, Journaux Utilisateurs, Rôles et permissions, État du système, Journaux
Ancien menu (transitoire) Ancien menu (rubrique principale, transitoire, administrateurs uniquement)
Principal
Interventions
Équipements
Documents
Configuration
``` ```
Les rubriques sont conditionnées par les permissions effectives. Un parent Les rubriques sont conditionnées par les permissions effectives. Un parent
@ -70,14 +75,16 @@ sont pas remplacés par un contrôle de template.
## Ancien menu ## Ancien menu
**ANCIEN MENU CONSERVÉ : OUI** **ANCIEN MENU RUBRIQUE PRINCIPALE : OUI**
**ANCIEN MENU DANS ADMINISTRATION : NON**
**VISIBLE UNIQUEMENT ADMIN : OUI** **VISIBLE UNIQUEMENT ADMIN : OUI**
L'ancien inventaire complet est accessible dans `Administration → Ancien L'ancien inventaire complet est accessible dans une rubrique principale
menu`, sous forme de référentiel temporaire. Il inclut aussi les entrées `Ancien menu`, parallèle à `Administration`. Il reprend les cinq groupes
techniques et intégrations conservées pour comparaison. Il ne possède pas de historiques réels (`Principal`, `Interventions`, `Équipements`, `Documents`,
nouveau mécanisme d'autorisation et ne doit être retiré qu'après validation `Configuration`) sous forme de sous-menus repliables. Il inclut les 70 entrées
humaine. de la matrice, sans réorganisation métier. Il ne possède pas de nouveau
mécanisme d'autorisation et ne doit être retiré qu'après validation humaine.
## UX appliquée ## UX appliquée

View file

@ -6,7 +6,12 @@ réorganisation ne change pas les contrôles RBAC du backend. **Ancien menu est
transitoire et réservé aux administrateurs** ; aucune entrée n'est supprimée transitoire et réservé aux administrateurs** ; aucune entrée n'est supprimée
silencieusement. silencieusement.
| Ancien libellé | Endpoint | Permission | Ancien emplacement | Nouveau libellé | Nouveau menu | Statut | Commentaire | Les groupes historiques exacts sont : **Principal**, **Interventions**,
**Équipements**, **Documents** et **Configuration**. La colonne « Ancien
groupe historique / emplacement » permet de naviguer directement vers le
sous-menu correspondant.
| Ancien libellé | Endpoint | Permission | Ancien groupe historique / emplacement | Nouveau libellé | Nouveau menu | Statut | Commentaire |
|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|
| Tableau de bord | `dashboard.index` | `dashboard.view` | Principal | Tableau de bord | Accueil | MIGRÉ | Point d'entrée conservé | | Tableau de bord | `dashboard.index` | `dashboard.view` | Principal | Tableau de bord | Accueil | MIGRÉ | Point d'entrée conservé |
| Messagerie | `/messagerie` | `integration.ent.view` ou Outlook | Principal | Messagerie | Ancien menu | ADMIN UNIQUEMENT | Intégration hors menu métier | | Messagerie | `/messagerie` | `integration.ent.view` ou Outlook | Principal | Messagerie | Ancien menu | ADMIN UNIQUEMENT | Intégration hors menu métier |
@ -86,6 +91,7 @@ silencieusement.
- MIGRÉ : 25 ; REGROUPÉ : 20 ; ADMIN UNIQUEMENT : 25 ; DOUBLON : 0 ; - MIGRÉ : 25 ; REGROUPÉ : 20 ; ADMIN UNIQUEMENT : 25 ; DOUBLON : 0 ;
OBSOLÈTE À CONFIRMER : 0 ; NON MIGRÉ : 0 ; ROUTE CASSÉE : 0 constatée dans OBSOLÈTE À CONFIRMER : 0 ; NON MIGRÉ : 0 ; ROUTE CASSÉE : 0 constatée dans
le template après compilation. le template après compilation.
- Les liens techniques hors périmètre sont conservés dans le bloc - Les liens techniques hors périmètre sont conservés dans la rubrique
**Administration → Ancien menu**. Ce bloc est transitoire et ne doit être principale **Ancien menu**, sous les cinq groupes historiques. Cette
retiré qu'après validation humaine. rubrique est transitoire et ne doit être retirée qu'après validation
humaine.

View file

@ -445,3 +445,9 @@ Les tests ciblés Phase 1.6 + Phase 1/1.5 donnent **21 passed**. Après
isolation de la fixture de fermeture horaire, la suite complète donne isolation de la fixture de fermeture horaire, la suite complète donne
**132 passed, 1 failed, 1 error** ; seuls l'échec et le teardown historiques **132 passed, 1 failed, 1 error** ; seuls l'échec et le teardown historiques
du lookback/contexte GMAO restent hors périmètre. du lookback/contexte GMAO restent hors périmètre.
Correction de navigation : `Ancien menu` est maintenant une rubrique
principale parallèle à `Administration`, avec les groupes historiques
`Principal`, `Interventions`, `Équipements`, `Documents` et `Configuration`.
Les 70 entrées de la matrice sont conservées et la rubrique reste réservée
aux administrateurs.

View file

@ -321,7 +321,9 @@ réservées au référentiel administrateur. Le nouveau menu couvre Accueil, Mon
travail, Patrimoine, Produits & stocks, Entreprises, Documents et travail, Patrimoine, Produits & stocks, Entreprises, Documents et
Administration ; aucune entrée n'est perdue silencieusement. Administration ; aucune entrée n'est perdue silencieusement.
Le bloc `Administration → Ancien menu` est conservé, complet et transitoire. La rubrique principale `Ancien menu` est conservée, complète et transitoire,
parallèle à `Administration`. Elle expose les cinq groupes historiques et
reste réservée aux administrateurs.
Les pages Documents/FDS et Planning des salles disposent d'états vides, Les pages Documents/FDS et Planning des salles disposent d'états vides,
d'actions textuelles et de libellés de provenance plus clairs. La page RBAC d'actions textuelles et de libellés de provenance plus clairs. La page RBAC
présente désormais les libellés métier des permissions et explique le présente désormais les libellés métier des permissions et explique le

View file

@ -13,6 +13,9 @@ def test_admin_sees_new_menu_and_legacy_reference(authenticated_client):
assert label in body assert label in body
assert "Ancien menu" in body assert "Ancien menu" in body
assert "Référentiel temporaire" in body or "temporaire" in body assert "Référentiel temporaire" in body or "temporaire" in body
assert body.index("Administration") < body.index("Ancien menu")
for group in ("Principal", "Interventions", "Équipements", "Configuration"):
assert group in body
def test_non_admin_does_not_see_admin_navigation(client, app): def test_non_admin_does_not_see_admin_navigation(client, app):