- Task #6: WebSocket /ws/dashboard + Kafka events (5 topics) * Backend: KafkaEventProducer, KafkaEventConsumer * Mobile: WebSocketService (reconnection, heartbeat, typed events) * DashboardBloc: Auto-refresh depuis WebSocket events - Finance Workflow: approbations + budgets (backend + mobile) * Backend: entities, services, resources, migrations Flyway V6 * Mobile: features finance_workflow complète avec BLoC - Corrections DI: interfaces IRepository partout * IProfileRepository, IOrganizationRepository, IMembreRepository * GetIt configuré avec @injectable - Spec-Kit: constitution + templates mis à jour * .specify/memory/constitution.md enrichie * Templates agent, plan, spec, tasks, checklist - Nettoyage: fichiers temporaires supprimés Signed-off-by: lions dev Team
15 KiB
Révision UX - Menu et Pages par Rôle
Date: 2026-03-02 Système: UnionFlow - Navigation et Accès par Rôle Principe: Chaque membre ne voit que ce qui est pertinent pour SON rôle
Problème Identifié
État Actuel ❌
Ligne 48-52 de menu.xhtml : "Annuaire des Membres" est visible pour TOUS incluant MEMBRE_ACTIF
<!-- Annuaire des Membres (MEMBRE_ACTIF et plus - Consultation) -->
<p:submenu id="m_annuaire" label="Annuaire des Membres" icon="pi pi-users"
rendered="#{menuBean.annuaireMembresVisible}">
<p:menuitem id="m_liste_membres_lecture" value="Liste des Membres"
icon="pi pi-list" outcome="/pages/secure/membre/liste" />
<p:menuitem id="m_recherche_membres" value="Rechercher un Membre"
icon="pi pi-search" outcome="/pages/secure/membre/recherche" />
</p:submenu>
Problèmes UX :
- ❌ Un membre simple d'une mutuelle n'a pas besoin de voir la liste de tous les membres
- ❌ Exposition de données personnelles (RGPD) sans raison métier
- ❌ La page
/pages/secure/membre/liste.xhtmlaffiche des KPI administratifs :- Total Membres
- Membres Actifs/Inactifs
- Nouveaux Membres (30j)
- ❌ Actions administratives non pertinentes :
- Nouveau Membre
- Import/Export
- Suspendre/Réactiver
- Rappel Cotisations Groupé
Question Métier Fondamentale
Pour un MEMBRE_ACTIF d'une mutuelle, quels sont ses besoins réels ?
✅ Besoins légitimes :
- SON dashboard personnel
- SES cotisations
- SON compte épargne
- SES inscriptions aux événements
- SES demandes d'aide sociale
- Consulter les événements publics (pour s'inscrire)
- Voir ses notifications personnelles
- Accéder à SON profil
❌ Besoins NON légitimes (rôles admin) :
- Voir la liste complète des membres
- Rechercher d'autres membres
- Voir les statistiques globales de l'organisation
- Créer/Modifier/Suspendre des membres
- Importer/Exporter des membres
- Envoyer des rappels de cotisations
- Voir la trésorerie globale
Solution Recommandée
1. Révision du Menu par Rôle
A. Menu pour MEMBRE_ACTIF (Minimal)
<!-- MEMBRE_ACTIF - Menu Personnel Uniquement -->
<fr:menu widgetVar="FreyaMenuWidget">
<!-- Dashboard Personnel -->
<p:menuitem value="Mon Espace" icon="pi pi-home"
outcome="/pages/secure/dashboard-membre" />
<!-- Mes Finances -->
<p:submenu label="Mes Finances" icon="pi pi-wallet">
<p:menuitem value="Mes Cotisations" icon="pi pi-credit-card"
outcome="/pages/secure/membre/cotisations" />
<p:menuitem value="Payer mes Cotisations" icon="pi pi-dollar"
outcome="/pages/secure/cotisation/paiement" />
<p:menuitem value="Mon Épargne" icon="pi pi-money-bill"
outcome="/pages/secure/epargne/mon-compte" />
<p:menuitem value="Mes Prêts" icon="pi pi-briefcase"
outcome="/pages/secure/credit/mes-prets"
rendered="#{config.moduleCredit}" />
</p:submenu>
<!-- Événements -->
<p:submenu label="Événements" icon="pi pi-calendar">
<p:menuitem value="Calendrier" icon="pi pi-calendar-plus"
outcome="/pages/secure/evenement/calendrier" />
<p:menuitem value="Mes Inscriptions" icon="pi pi-list"
outcome="/pages/secure/evenement/mes-inscriptions" />
</p:submenu>
<!-- Aide Sociale -->
<p:submenu label="Aide Sociale" icon="pi pi-heart">
<p:menuitem value="Faire une Demande" icon="pi pi-plus"
outcome="/pages/secure/aide/demande" />
<p:menuitem value="Mes Demandes" icon="pi pi-list"
outcome="/pages/secure/aide/mes-demandes" />
</p:submenu>
<!-- Communication -->
<p:submenu label="Communication" icon="pi pi-envelope">
<p:menuitem value="Mes Notifications" icon="pi pi-bell"
outcome="/pages/secure/communication/notifications" />
<p:menuitem value="Annonces" icon="pi pi-megaphone"
outcome="/pages/secure/communication/annonces" />
</p:submenu>
<!-- Mon Profil -->
<p:menuitem value="Mon Profil" icon="pi pi-user"
outcome="/pages/secure/membre/mon-profil" />
<!-- Aide -->
<p:menuitem value="Aide" icon="pi pi-question-circle"
outcome="/pages/secure/aide/support" />
</fr:menu>
Total items : ~10 items pertinents (vs ~50+ actuellement)
B. Menu pour SECRETAIRE (Gestion Administrative)
<!-- SECRETAIRE - Administration + Personnel -->
<fr:menu>
<!-- Tous les items MEMBRE_ACTIF + -->
<!-- Gestion des Membres -->
<p:submenu label="Gestion des Membres" icon="pi pi-users-cog">
<p:menuitem value="Nouvelle Inscription" outcome="/pages/secure/membre/inscription" />
<p:menuitem value="Liste Complète" outcome="/pages/secure/membre/liste" />
<p:menuitem value="Validation Inscriptions" outcome="/pages/secure/membre/validation" />
<p:menuitem value="Import/Export" outcome="/pages/secure/membre/import" />
</p:submenu>
<!-- Gestion Événements -->
<p:submenu label="Gestion Événements" icon="pi pi-calendar-clock">
<p:menuitem value="Nouvel Événement" outcome="/pages/secure/evenement/creation" />
<p:menuitem value="Planification" outcome="/pages/secure/evenement/planification" />
<p:menuitem value="Gestion Participations" outcome="/pages/secure/evenement/participation" />
</p:submenu>
<!-- Communication -->
<p:submenu label="Communication" icon="pi pi-megaphone">
<p:menuitem value="Envoyer SMS/Email" outcome="/pages/secure/communication/envoi" />
<p:menuitem value="Annonces Officielles" outcome="/pages/secure/communication/annonces-admin" />
</p:submenu>
</fr:menu>
C. Menu pour TRESORIER (Gestion Financière)
<!-- TRESORIER - Finances + Personnel -->
<fr:menu>
<!-- Tous les items MEMBRE_ACTIF + -->
<!-- Gestion Financière -->
<p:submenu label="Gestion Financière" icon="pi pi-dollar">
<p:menuitem value="Trésorerie" outcome="/pages/secure/finance/tresorerie" />
<p:menuitem value="Cotisations Globales" outcome="/pages/admin/cotisations/gestion" />
<p:menuitem value="Comptabilité" outcome="/pages/secure/comptabilite/gestion" />
<p:menuitem value="Relances Cotisations" outcome="/pages/secure/cotisation/relances" />
<p:menuitem value="Rapports Financiers" outcome="/pages/secure/finance/rapports" />
</p:submenu>
<!-- Épargne et Crédit (si module actif) -->
<p:submenu label="Épargne et Crédit" icon="pi pi-money-bill">
<p:menuitem value="Demandes de Crédit" outcome="/pages/secure/credit/demandes" />
<p:menuitem value="Suivi des Crédits" outcome="/pages/secure/credit/suivi" />
<p:menuitem value="Remboursements" outcome="/pages/secure/credit/remboursements" />
</p:submenu>
</fr:menu>
D. Menu pour ADMIN_ORGANISATION (Tout)
<!-- ADMIN_ORGANISATION - Tous les menus -->
<fr:menu>
<!-- Dashboard Admin -->
<p:menuitem value="Dashboard Admin" outcome="/pages/secure/dashboard" />
<!-- Tous les menus de gestion -->
<!-- + Tous les items personnels -->
</fr:menu>
2. Révision des Pages par Rôle
A. /pages/secure/membre/liste.xhtml
État actuel : Une seule page pour tous (admin + membres)
Solution : Conditionner l'affichage selon le rôle
<!-- KPI Statistiques - Visible uniquement pour SECRETAIRE, ADMIN -->
<h:panelGroup id="panelStatistiques" layout="block" styleClass="grid mb-3"
rendered="#{menuBean.gestionMembresMenuVisible}">
<div class="col-12 md:col-3">
<ui:decorate template="/templates/components/cards/stat-card.xhtml">
<ui:param name="value" value="#{membreListeBean.totalMembres}" />
<ui:param name="label" value="Total Membres" />
</ui:decorate>
</div>
<!-- Autres KPI... -->
</h:panelGroup>
<!-- Actions Admin - Visible uniquement pour SECRETAIRE, ADMIN -->
<ui:define name="actions">
<p:button value="Nouveau Membre" icon="pi pi-user-plus"
outcome="membreInscriptionPage"
rendered="#{menuBean.gestionMembresMenuVisible}"
styleClass="ui-button-success mr-2" />
<p:commandButton value="Import / Export" icon="pi pi-file-excel"
rendered="#{menuBean.gestionMembresMenuVisible}"
onclick="PF('dlgImportExport').show();" />
</ui:define>
<!-- Actions en DataTable - Visible uniquement pour SECRETAIRE, ADMIN -->
<ui:define name="actions">
<!-- Voir le profil - TOUS -->
<p:button icon="pi pi-user" outcome="membreProfilPage" title="Profil" />
<!-- Éditer - ADMIN SEULEMENT -->
<p:button icon="pi pi-pencil" outcome="membreModifierPage" title="Modifier"
rendered="#{menuBean.gestionMembresMenuVisible}" />
<!-- Contacter - TOUS -->
<p:commandButton icon="pi pi-envelope" title="Contacter" />
<!-- Suspendre - ADMIN SEULEMENT -->
<p:commandButton icon="pi pi-ban" title="Suspendre"
rendered="#{menuBean.gestionMembresMenuVisible and membre.statut == 'ACTIF'}" />
</ui:define>
Alternative : Créer 2 pages séparées
/pages/secure/membre/liste.xhtml→ Admin seulement (avec KPI et actions)/pages/secure/membre/annuaire.xhtml→ Tous (lecture seule, pas de KPI)
B. /pages/secure/dashboard.xhtml vs /pages/secure/dashboard-membre.xhtml
Actuellement : Bien séparés ✅
dashboard.xhtml→ ADMIN, RESPONSABLES (KPI globaux)dashboard-membre.xhtml→ MEMBRE_ACTIF (données personnelles)
À conserver tel quel.
3. Modification de MenuBean.java
Réviser isAnnuaireMembresVisible()
Avant (ligne 135-139) :
public boolean isAnnuaireMembresVisible() {
return hasAnyRole("SUPER_ADMIN", "ADMIN_ORGANISATION", "SECRETAIRE", "TRESORIER",
"RESPONSABLE_SOCIAL", "RESPONSABLE_EVENEMENTS", "RESPONSABLE_CREDIT",
"MEMBRE_BUREAU", "MEMBRE_ACTIF"); // ← PROBLÈME
}
Après (Option 1 - Restrictif) :
/**
* Annuaire des Membres - Consultation limitée
* Visible pour les responsables et bureau SEULEMENT (pas MEMBRE_ACTIF)
*
* Raison métier: Un membre simple n'a pas besoin de voir la liste complète
* des autres membres (RGPD, pertinence métier limitée)
*/
public boolean isAnnuaireMembresVisible() {
return hasAnyRole("SUPER_ADMIN", "ADMIN_ORGANISATION", "SECRETAIRE", "TRESORIER",
"RESPONSABLE_SOCIAL", "RESPONSABLE_EVENEMENTS", "RESPONSABLE_CREDIT",
"MEMBRE_BUREAU");
// MEMBRE_ACTIF retiré intentionnellement
}
Après (Option 2 - Configurable par Organisation) :
@Inject
ConfigurationService configService; // Service qui lit config de l'organisation
/**
* Annuaire des Membres - Consultation limitée
* Visible selon configuration de l'organisation
*/
public boolean isAnnuaireMembresVisible() {
// Toujours visible pour les admins
if (hasAnyRole("SUPER_ADMIN", "ADMIN_ORGANISATION", "SECRETAIRE", "TRESORIER",
"RESPONSABLE_SOCIAL", "RESPONSABLE_EVENEMENTS", "RESPONSABLE_CREDIT",
"MEMBRE_BUREAU")) {
return true;
}
// Pour MEMBRE_ACTIF: vérifier si l'organisation autorise l'annuaire
if (hasAnyRole("MEMBRE_ACTIF")) {
return configService.isAnnuaireMembresActive(); // false par défaut
}
return false;
}
Cas d'Usage Métier - Annuaire pour MEMBRE_ACTIF ?
✅ Arguments POUR (lien social)
- Faciliter la communication entre membres
- Créer du lien social dans la mutuelle
- Trouver des contacts pour covoiturage aux événements
- Identifier des compétences (ex: trouver un plombier membre)
❌ Arguments CONTRE (protection données)
- RGPD : Exposition non justifiée de données personnelles
- Sécurité : Risque de phishing/spam entre membres
- Pertinence limitée : Un membre n'a généralement pas besoin de la liste complète
- Surcharge cognitive : Menu trop chargé pour un usage quotidien limité
💡 Recommandation
Option privilégiée : Désactiver par défaut, rendre configurable par organisation
// Configuration dans table `configuration_organisation`
{
"annuaire_membres_actif": false, // Par défaut : désactivé
"annuaire_membres_champs_visibles": ["nom", "prenom", "telephone"], // Pas email
"annuaire_membres_recherche_avancee": false // Recherche simple seulement
}
Si l'organisation active l'annuaire pour MEMBRE_ACTIF :
- ✅ Afficher une version limitée (pas de KPI, pas d'actions admin)
- ✅ Masquer certains champs sensibles (email optionnel, pas d'adresse)
- ✅ Limiter la recherche (nom/prénom seulement, pas de filtres avancés)
Plan d'Action
Phase 1 : Menu ✅ (Immédiat)
- Modifier
MenuBean.isAnnuaireMembresVisible()pour exclureMEMBRE_ACTIF - Tester que le menu "Annuaire des Membres" n'apparaît plus pour MEMBRE_ACTIF
- Vérifier que les autres menus sont bien visibles selon les rôles
Phase 2 : Pages Conditionnelles 🔧 (Court terme)
- Ajouter
rendered="#{menuBean.gestionMembresMenuVisible}"sur les KPI deliste.xhtml - Ajouter
rendered="#{menuBean.gestionMembresMenuVisible}"sur les actions admin - Conditionner les actions du DataTable (Éditer, Suspendre) selon le rôle
- Tester avec un utilisateur MEMBRE_ACTIF : pas de KPI, pas d'actions admin
Phase 3 : Configuration Optionnelle 🚀 (Moyen terme)
- Créer table
configuration_organisationavec champannuaire_membres_actif - Créer
ConfigurationService.isAnnuaireMembresActive() - Modifier
MenuBean.isAnnuaireMembresVisible()pour utiliser la config - Créer page admin
/pages/admin/configuration/annuaire.xhtmlpour activer/désactiver - Si activé : créer page
/pages/secure/membre/annuaire.xhtml(version simplifiée)
Phase 4 : Révision Complète Menu 📋 (Long terme)
- Créer des fichiers menu séparés par rôle :
menu-membre-actif.xhtml(10 items)menu-secretaire.xhtml(20 items)menu-tresorier.xhtml(15 items)menu-admin.xhtml(50+ items)
- Charger le bon menu selon le rôle dans
main-template.xhtml - Simplifier
MenuBeanen supprimant les méthodes deprecated
Checklist de Validation UX
Avant de déployer un menu ou une page, vérifier :
- Pertinence métier : L'utilisateur a-t-il besoin de cette fonction pour SON rôle ?
- Moindre privilège : La fonction n'expose-t-elle que les données nécessaires ?
- Clarté : L'intitulé du menu est-il explicite ? ("Mes Cotisations" vs "Cotisations")
- Cohérence : Les fonctions "MES" vs "GESTION" sont-elles bien séparées ?
- Simplicité : Le menu n'est-il pas surchargé ? (max 10-15 items pour MEMBRE_ACTIF)
Contact
Documentation :
docs/UX_MENU_PAR_ROLE.md(ce fichier)docs/KPI_DASHBOARD_PAR_ROLE.md(matrice KPI)docs/PERMISSIONS_MATRIX.md(permissions pages)
Code :
MenuBean.java- Logique de visibilitémenu.xhtml- Structure du menuliste.xhtml- Page à conditionner