Files
unionflow-server-api/unionflow/docs/UX_MENU_PAR_ROLE.md
dahoud e8ad874015 feat: WebSocket temps réel + Finance Workflow + corrections
- 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
2026-03-15 02:12:17 +00:00

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 :

  1. Un membre simple d'une mutuelle n'a pas besoin de voir la liste de tous les membres
  2. Exposition de données personnelles (RGPD) sans raison métier
  3. La page /pages/secure/membre/liste.xhtml affiche des KPI administratifs :
    • Total Membres
    • Membres Actifs/Inactifs
    • Nouveaux Membres (30j)
  4. 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)

  1. Faciliter la communication entre membres
  2. Créer du lien social dans la mutuelle
  3. Trouver des contacts pour covoiturage aux événements
  4. Identifier des compétences (ex: trouver un plombier membre)

Arguments CONTRE (protection données)

  1. RGPD : Exposition non justifiée de données personnelles
  2. Sécurité : Risque de phishing/spam entre membres
  3. Pertinence limitée : Un membre n'a généralement pas besoin de la liste complète
  4. 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 exclure MEMBRE_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 de liste.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_organisation avec champ annuaire_membres_actif
  • Créer ConfigurationService.isAnnuaireMembresActive()
  • Modifier MenuBean.isAnnuaireMembresVisible() pour utiliser la config
  • Créer page admin /pages/admin/configuration/annuaire.xhtml pour 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 MenuBean en 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 menu
  • liste.xhtml - Page à conditionner