Update - Lions User Manager - Client (Quarkus PrimeFaces Freya)

This commit is contained in:
dahoud
2025-12-06 22:07:05 +00:00
parent 53ea02ccad
commit 51d087e5be
189 changed files with 38558 additions and 1 deletions

View File

@@ -0,0 +1,387 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<!--
Composant réutilisable: Actions Utilisateur (WOU/DRY Pattern)
Auteur: Lions User Manager
Version: 1.0.0
Description: Boutons d'action pour un utilisateur (activate, deactivate, delete, etc.)
Paramètres:
- user: UserDTO (requis) - L'utilisateur concerné
- showView: Boolean (défaut: true) - Afficher le bouton "Voir"
- showEdit: Boolean (défaut: true) - Afficher le bouton "Modifier"
- showDelete: Boolean (défaut: true) - Afficher le bouton "Supprimer"
- showActivate: Boolean (défaut: true) - Afficher le bouton "Activer"
- showDeactivate: Boolean (défaut: true) - Afficher le bouton "Désactiver"
- showResetPassword: Boolean (défaut: true) - Afficher le bouton "Réinitialiser mot de passe"
- showLogoutSessions: Boolean (défaut: false) - Afficher le bouton "Déconnecter toutes les sessions"
- viewAction: String (optionnel) - Action pour "Voir"
- editAction: String (optionnel) - Action pour "Modifier"
- deleteAction: String (optionnel) - Action pour "Supprimer"
- activateAction: String (optionnel) - Action pour "Activer"
- deactivateAction: String (optionnel) - Action pour "Désactiver"
- resetPasswordAction: String (optionnel) - Action pour "Réinitialiser mot de passe"
- logoutSessionsAction: String (optionnel) - Action pour "Déconnecter sessions"
- viewOutcome: String (optionnel) - Page pour "Voir"
- editOutcome: String (optionnel) - Page pour "Modifier"
- update: String (défaut: "@form") - Composants à mettre à jour
- layout: String (défaut: "horizontal") - Layout: "horizontal" ou "vertical" ou "dropdown"
Exemples d'utilisation:
1. Actions horizontales (défaut):
<ui:include src="/templates/components/user-management/user-actions.xhtml">
<ui:param name="user" value="#{user}" />
<ui:param name="update" value="userTable" />
</ui:include>
2. Actions en dropdown:
<ui:include src="/templates/components/user-management/user-actions.xhtml">
<ui:param name="user" value="#{user}" />
<ui:param name="layout" value="dropdown" />
<ui:param name="update" value="userTable" />
</ui:include>
3. Actions limitées:
<ui:include src="/templates/components/user-management/user-actions.xhtml">
<ui:param name="user" value="#{user}" />
<ui:param name="showDelete" value="false" />
<ui:param name="showResetPassword" value="false" />
</ui:include>
-->
<c:set var="update" value="#{empty update ? '@form' : update}" />
<c:set var="layout" value="#{empty layout ? 'horizontal' : layout}" />
<c:set var="showView" value="#{empty showView ? true : showView}" />
<c:set var="showEdit" value="#{empty showEdit ? true : showEdit}" />
<c:set var="showDelete" value="#{empty showDelete ? true : showDelete}" />
<c:set var="showActivate" value="#{empty showActivate ? true : showActivate}" />
<c:set var="showDeactivate" value="#{empty showDeactivate ? true : showDeactivate}" />
<c:set var="showResetPassword" value="#{empty showResetPassword ? true : showResetPassword}" />
<c:set var="showLogoutSessions" value="#{empty showLogoutSessions ? false : showLogoutSessions}" />
<!-- Définir les actions par défaut si non fournies -->
<c:set var="defaultActivateAction" value="#{userBean.activateUser(user.id)}" />
<c:set var="defaultDeactivateAction" value="#{userBean.deactivateUser(user.id)}" />
<c:set var="defaultDeleteAction" value="#{userBean.deleteUser(user.id)}" />
<c:set var="defaultResetPasswordAction" value="#{userBean.resetPassword(user.id)}" />
<c:set var="defaultLogoutSessionsAction" value="#{userBean.logoutAllSessions(user.id)}" />
<c:choose>
<!-- Layout Dropdown -->
<c:when test="#{layout == 'dropdown'}">
<p:commandButton
icon="pi pi-ellipsis-v"
styleClass="p-button-text p-button-sm p-button-rounded p-button-plain"
type="button"
title="Actions"
style="width: 2rem; height: 2rem; padding: 0; margin: 0;">
<p:menu styleClass="w-12rem">
<c:if test="#{showView}">
<p:menuitem
value="Voir le profil"
icon="pi pi-eye"
outcome="#{not empty viewOutcome ? viewOutcome : '/pages/user-manager/users/profile'}">
<f:param name="userId" value="#{user.id}" />
</p:menuitem>
</c:if>
<c:if test="#{showEdit}">
<p:menuitem
value="Modifier"
icon="pi pi-pencil"
outcome="#{not empty editOutcome ? editOutcome : '/pages/user-manager/users/edit'}">
<f:param name="userId" value="#{user.id}" />
</p:menuitem>
</c:if>
<c:if test="#{showResetPassword}">
<p:menuitem
value="Réinitialiser mot de passe"
icon="pi pi-key"
onclick="PF('resetPasswordDialog').show()" />
</c:if>
<p:separator />
<c:if test="#{showActivate and not user.enabled}">
<c:choose>
<c:when test="#{not empty activateAction}">
<p:menuitem
value="Activer"
icon="pi pi-check"
styleClass="text-green-600"
action="#{activateAction}"
update="#{update}" />
</c:when>
<c:otherwise>
<p:menuitem
value="Activer"
icon="pi pi-check"
styleClass="text-green-600"
action="#{userBean.activateUser(user.id)}"
update="#{update}" />
</c:otherwise>
</c:choose>
</c:if>
<c:if test="#{showDeactivate and user.enabled}">
<c:choose>
<c:when test="#{not empty deactivateAction}">
<p:menuitem
value="Désactiver"
icon="pi pi-times"
styleClass="text-orange-600"
action="#{deactivateAction}"
update="#{update}" />
</c:when>
<c:otherwise>
<p:menuitem
value="Désactiver"
icon="pi pi-times"
styleClass="text-orange-600"
action="#{userBean.deactivateUser(user.id)}"
update="#{update}" />
</c:otherwise>
</c:choose>
</c:if>
<c:if test="#{showLogoutSessions}">
<c:choose>
<c:when test="#{not empty logoutSessionsAction}">
<p:menuitem
value="Déconnecter toutes les sessions"
icon="pi pi-sign-out"
styleClass="text-blue-600"
action="#{logoutSessionsAction}"
update="#{update}" />
</c:when>
<c:otherwise>
<p:menuitem
value="Déconnecter toutes les sessions"
icon="pi pi-sign-out"
styleClass="text-blue-600"
action="#{userBean.logoutAllSessions(user.id)}"
update="#{update}" />
</c:otherwise>
</c:choose>
</c:if>
<c:if test="#{showDelete}">
<p:separator />
<p:menuitem
value="Supprimer"
icon="pi pi-trash"
styleClass="text-red-600"
onclick="PF('confirmDeleteDialog').show()" />
</c:if>
</p:menu>
</p:commandButton>
</c:when>
<!-- Layout Horizontal (défaut) -->
<c:otherwise>
<div class="flex gap-1">
<c:if test="#{showView}">
<p:commandButton
icon="pi pi-eye"
title="Voir le profil"
styleClass="p-button-text p-button-sm p-button-info"
outcome="#{not empty viewOutcome ? viewOutcome : '/pages/user-manager/users/profile'}">
<f:param name="userId" value="#{user.id}" />
</p:commandButton>
</c:if>
<c:if test="#{showEdit}">
<p:commandButton
icon="pi pi-pencil"
title="Modifier"
styleClass="p-button-text p-button-sm p-button-warning"
outcome="#{not empty editOutcome ? editOutcome : '/pages/user-manager/users/edit'}">
<f:param name="userId" value="#{user.id}" />
</p:commandButton>
</c:if>
<c:if test="#{showResetPassword}">
<p:commandButton
icon="pi pi-key"
title="Réinitialiser mot de passe"
styleClass="p-button-text p-button-sm p-button-help"
onclick="PF('resetPasswordDialog').show()" />
</c:if>
<c:if test="#{showActivate and not user.enabled}">
<c:choose>
<c:when test="#{not empty activateAction}">
<p:commandButton
icon="pi pi-check"
title="Activer"
styleClass="p-button-text p-button-sm p-button-success"
action="#{activateAction}"
update="#{update}" />
</c:when>
<c:otherwise>
<p:commandButton
icon="pi pi-check"
title="Activer"
styleClass="p-button-text p-button-sm p-button-success"
action="#{userBean.activateUser(user.id)}"
update="#{update}" />
</c:otherwise>
</c:choose>
</c:if>
<c:if test="#{showDeactivate and user.enabled}">
<c:choose>
<c:when test="#{not empty deactivateAction}">
<p:commandButton
icon="pi pi-times"
title="Désactiver"
styleClass="p-button-text p-button-sm p-button-warning"
action="#{deactivateAction}"
update="#{update}" />
</c:when>
<c:otherwise>
<p:commandButton
icon="pi pi-times"
title="Désactiver"
styleClass="p-button-text p-button-sm p-button-warning"
action="#{userBean.deactivateUser(user.id)}"
update="#{update}" />
</c:otherwise>
</c:choose>
</c:if>
<c:if test="#{showLogoutSessions}">
<c:choose>
<c:when test="#{not empty logoutSessionsAction}">
<p:commandButton
icon="pi pi-sign-out"
title="Déconnecter toutes les sessions"
styleClass="p-button-text p-button-sm p-button-info"
action="#{logoutSessionsAction}"
update="#{update}" />
</c:when>
<c:otherwise>
<p:commandButton
icon="pi pi-sign-out"
title="Déconnecter toutes les sessions"
styleClass="p-button-text p-button-sm p-button-info"
action="#{userBean.logoutAllSessions(user.id)}"
update="#{update}" />
</c:otherwise>
</c:choose>
</c:if>
<c:if test="#{showDelete}">
<p:commandButton
icon="pi pi-trash"
title="Supprimer"
styleClass="p-button-text p-button-sm p-button-danger"
onclick="PF('confirmDeleteDialog').show()" />
</c:if>
</div>
</c:otherwise>
</c:choose>
<!-- Dialog de confirmation de suppression -->
<p:confirmDialog
id="confirmDeleteDialog"
widgetVar="confirmDeleteDialog"
message="Êtes-vous sûr de vouloir supprimer l'utilisateur #{user.username} ?"
header="Confirmation de suppression"
severity="warn">
<c:choose>
<c:when test="#{not empty deleteAction}">
<p:commandButton
value="Oui"
icon="pi pi-check"
styleClass="p-button-danger"
action="#{deleteAction}"
update="#{update}"
oncomplete="PF('confirmDeleteDialog').hide()" />
</c:when>
<c:otherwise>
<p:commandButton
value="Oui"
icon="pi pi-check"
styleClass="p-button-danger"
action="#{userBean.deleteUser(user.id)}"
update="#{update}"
oncomplete="PF('confirmDeleteDialog').hide()" />
</c:otherwise>
</c:choose>
<p:commandButton
value="Non"
icon="pi pi-times"
styleClass="p-button-secondary"
onclick="PF('confirmDeleteDialog').hide()" />
</p:confirmDialog>
<!-- Dialog de réinitialisation de mot de passe -->
<p:dialog
id="resetPasswordDialog"
widgetVar="resetPasswordDialog"
header="Réinitialiser le mot de passe"
modal="true"
styleClass="w-full md:w-4">
<h:form>
<p:panelGrid columns="2" styleClass="w-full">
<p:outputLabel for="newPassword" value="Nouveau mot de passe *" />
<p:password id="newPassword"
value="#{userBean.newPassword}"
feedback="true"
required="true"
styleClass="w-full">
<f:validateLength minimum="8" maximum="100" />
</p:password>
<p:outputLabel for="newPasswordConfirm" value="Confirmer *" />
<p:password id="newPasswordConfirm"
value="#{userBean.newPasswordConfirm}"
required="true"
styleClass="w-full" />
</p:panelGrid>
<f:facet name="footer">
<div class="flex gap-2 justify-content-end">
<c:choose>
<c:when test="#{not empty resetPasswordAction}">
<p:commandButton
value="Réinitialiser"
icon="pi pi-check"
styleClass="p-button-primary"
action="#{resetPasswordAction}"
update="#{update}"
oncomplete="PF('resetPasswordDialog').hide()"
process="@form" />
</c:when>
<c:otherwise>
<p:commandButton
value="Réinitialiser"
icon="pi pi-check"
styleClass="p-button-primary"
action="#{userBean.resetPassword(user.id)}"
update="#{update}"
oncomplete="PF('resetPasswordDialog').hide()"
process="@form" />
</c:otherwise>
</c:choose>
<p:commandButton
value="Annuler"
icon="pi pi-times"
styleClass="p-button-secondary"
onclick="PF('resetPasswordDialog').hide()" />
</div>
</f:facet>
</h:form>
</p:dialog>
</ui:composition>

View File

@@ -0,0 +1,130 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<!--
Composant réutilisable: Carte Utilisateur (WOU/DRY Pattern)
Auteur: Lions User Manager
Version: 1.0.0
Description: Affiche une carte utilisateur avec informations principales et actions
Paramètres:
- user: UserDTO (requis) - L'utilisateur à afficher
- showActions: Boolean (défaut: true) - Afficher les boutons d'action
- showRoles: Boolean (défaut: true) - Afficher les rôles de l'utilisateur
- clickable: Boolean (défaut: true) - Rendre la carte cliquable
- outcome: String (optionnel) - Page de destination au clic
- styleClass: String (optionnel) - Classes CSS supplémentaires
Exemples d'utilisation:
1. Carte simple:
<ui:include src="/templates/components/user-management/user-card.xhtml">
<ui:param name="user" value="#{userBean.selectedUser}" />
</ui:include>
2. Carte avec actions:
<ui:include src="/templates/components/user-management/user-card.xhtml">
<ui:param name="user" value="#{userBean.selectedUser}" />
<ui:param name="showActions" value="true" />
<ui:param name="outcome" value="/pages/user-manager/users/profile" />
</ui:include>
3. Carte sans rôles:
<ui:include src="/templates/components/user-management/user-card.xhtml">
<ui:param name="user" value="#{userBean.selectedUser}" />
<ui:param name="showRoles" value="false" />
</ui:include>
-->
<c:set var="showActions" value="#{empty showActions ? true : showActions}" />
<c:set var="showRoles" value="#{empty showRoles ? true : showRoles}" />
<c:set var="clickable" value="#{empty clickable ? true : clickable}" />
<p:card styleClass="user-card #{styleClass}" rendered="#{not empty user}">
<f:facet name="header">
<div class="flex align-items-center gap-2">
<p:avatar
label="#{user.prenom != null ? user.prenom.substring(0,1) : 'U'}#{user.nom != null ? user.nom.substring(0,1) : ''}"
styleClass="user-avatar"
size="large" />
<div class="flex flex-column">
<h3 class="m-0">#{user.prenom} #{user.nom}</h3>
<span class="text-color-secondary text-sm">@#{user.username}</span>
</div>
</div>
</f:facet>
<div class="user-card-content">
<!-- Informations principales -->
<div class="flex flex-column gap-2 mb-3">
<div class="flex align-items-center gap-2">
<i class="pi pi-envelope text-color-secondary"></i>
<span>#{user.email}</span>
</div>
<c:if test="#{not empty user.telephone}">
<div class="flex align-items-center gap-2">
<i class="pi pi-phone text-color-secondary"></i>
<span>#{user.telephone}</span>
</div>
</c:if>
<!-- Statut -->
<div class="flex align-items-center gap-2">
<i class="pi pi-circle-fill text-color-secondary"></i>
<p:tag
value="#{user.statut != null ? user.statut : 'INCONNU'}"
severity="#{user.enabled ? 'success' : 'danger'}" />
</div>
</div>
<!-- Rôles -->
<c:if test="#{showRoles and not empty user.roles}">
<div class="flex flex-column gap-2 mb-3">
<h5 class="m-0">Rôles</h5>
<div class="flex flex-wrap gap-1">
<c:forEach var="role" items="#{user.roles}">
<p:tag value="#{role.name}" severity="info" />
</c:forEach>
</div>
</div>
</c:if>
</div>
<f:facet name="footer">
<c:if test="#{showActions}">
<div class="flex gap-2 justify-content-end">
<p:commandButton
icon="pi pi-eye"
title="Voir le profil"
styleClass="p-button-text p-button-sm"
outcome="#{not empty outcome ? outcome : '/pages/user-manager/users/profile'}"
rendered="#{clickable}">
<f:param name="userId" value="#{user.id}" />
</p:commandButton>
<p:commandButton
icon="pi pi-pencil"
title="Modifier"
styleClass="p-button-text p-button-sm p-button-warning"
outcome="/pages/user-manager/users/edit">
<f:param name="userId" value="#{user.id}" />
</p:commandButton>
<p:commandButton
icon="pi pi-trash"
title="Supprimer"
styleClass="p-button-text p-button-sm p-button-danger"
onclick="PF('confirmDeleteDialog').show()" />
</div>
</c:if>
</f:facet>
</p:card>
</ui:composition>

View File

@@ -0,0 +1,216 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<p:panel header="#{mode == 'create' ? 'Nouvel Utilisateur' : 'Modifier Utilisateur'}"
styleClass="w-full">
<!-- Informations de base -->
<p:panelGrid columns="2" styleClass="w-full" columnClasses="col-12 md:col-6">
<!-- Username -->
<p:outputLabel for="username" value="Nom d'utilisateur *" />
<p:inputText id="username"
value="#{user.username}"
required="true"
readonly="#{readonly or mode == 'edit'}"
placeholder="jdupont"
styleClass="w-full">
<f:validateLength minimum="3" maximum="100" />
<f:validateRegex pattern="^[a-zA-Z0-9._-]+$" />
</p:inputText>
<!-- Email -->
<p:outputLabel for="email" value="Email *" />
<p:inputText id="email"
value="#{user.email}"
required="true"
readonly="#{readonly}"
placeholder="jean.dupont@lions.dev"
styleClass="w-full">
<f:validateLength minimum="5" maximum="255" />
</p:inputText>
<!-- Prénom -->
<p:outputLabel for="prenom" value="Prénom *" />
<p:inputText id="prenom"
value="#{user.prenom}"
required="true"
readonly="#{readonly}"
placeholder="Jean"
styleClass="w-full">
<f:validateLength minimum="2" maximum="100" />
</p:inputText>
<!-- Nom -->
<p:outputLabel for="nom" value="Nom *" />
<p:inputText id="nom"
value="#{user.nom}"
required="true"
readonly="#{readonly}"
placeholder="Dupont"
styleClass="w-full">
<f:validateLength minimum="2" maximum="100" />
</p:inputText>
<!-- Téléphone -->
<p:outputLabel for="telephone" value="Téléphone" />
<p:inputText id="telephone"
value="#{user.telephone}"
readonly="#{readonly}"
placeholder="+225 07 12 34 56 78"
styleClass="w-full" />
<!-- Organisation -->
<p:outputLabel for="organisation" value="Organisation" />
<p:inputText id="organisation"
value="#{user.organisation}"
readonly="#{readonly}"
placeholder="Lions Dev"
styleClass="w-full" />
<!-- Département -->
<p:outputLabel for="departement" value="Département" />
<p:inputText id="departement"
value="#{user.departement}"
readonly="#{readonly}"
placeholder="IT"
styleClass="w-full" />
<!-- Fonction -->
<p:outputLabel for="fonction" value="Fonction" />
<p:inputText id="fonction"
value="#{user.fonction}"
readonly="#{readonly}"
placeholder="Développeur Senior"
styleClass="w-full" />
<!-- Ville -->
<p:outputLabel for="ville" value="Ville" />
<p:inputText id="ville"
value="#{user.ville}"
readonly="#{readonly}"
placeholder="Abidjan"
styleClass="w-full" />
<!-- Pays -->
<p:outputLabel for="pays" value="Pays" />
<p:inputText id="pays"
value="#{user.pays}"
readonly="#{readonly}"
placeholder="Côte d'Ivoire"
styleClass="w-full" />
<!-- Statut -->
<p:outputLabel for="statut" value="Statut" />
<p:selectOneMenu id="statut"
value="#{user.statut}"
readonly="#{readonly}"
styleClass="w-full">
<f:selectItem itemLabel="Sélectionner..." itemValue="" />
<f:selectItems value="#{userBean.statutOptions}" />
</p:selectOneMenu>
<!-- Enabled -->
<p:outputLabel for="enabled" value="Compte activé" />
<p:selectBooleanCheckbox id="enabled"
value="#{user.enabled}"
readonly="#{readonly}" />
<!-- Email vérifié -->
<p:outputLabel for="emailVerified" value="Email vérifié" />
<p:selectBooleanCheckbox id="emailVerified"
value="#{user.emailVerified}"
readonly="#{readonly}" />
<!-- Realm (si affiché) -->
<c:if test="#{showRealmSelector}">
<p:outputLabel for="realmName" value="Realm *" />
<p:selectOneMenu id="realmName"
value="#{user.realmName}"
required="#{showRealmSelector}"
readonly="#{readonly}"
styleClass="w-full">
<f:selectItem itemLabel="Sélectionner..." itemValue="" />
<f:selectItems value="#{userBean.availableRealms}" />
</p:selectOneMenu>
</c:if>
</p:panelGrid>
<!-- Champs mot de passe (si affichés) -->
<c:if test="#{showPasswordFields and mode == 'create'}">
<p:separator />
<h3>Mot de passe</h3>
<p:panelGrid columns="2" styleClass="w-full" columnClasses="col-12 md:col-6">
<p:outputLabel for="password" value="Mot de passe *" />
<p:password id="password"
value="#{userBean.password}"
required="true"
feedback="true"
placeholder="Minimum 8 caractères"
styleClass="w-full">
<f:validateLength minimum="8" maximum="100" />
</p:password>
<p:outputLabel for="passwordConfirm" value="Confirmer le mot de passe *" />
<p:password id="passwordConfirm"
value="#{userBean.passwordConfirm}"
required="true"
placeholder="Répétez le mot de passe"
styleClass="w-full" />
</p:panelGrid>
</c:if>
<!-- Boutons d'action -->
<f:facet name="footer">
<div class="flex gap-2 justify-content-end">
<c:if test="#{not readonly}">
<c:choose>
<!-- Si hasSubmitAction est explicitement défini à true, utiliser action -->
<c:when test="#{hasSubmitAction == true}">
<p:commandButton
value="#{mode == 'create' ? 'Créer' : 'Modifier'}"
icon="pi pi-check"
styleClass="p-button-success"
action="#{submitAction}"
update="#{not empty update ? update : '@form'}"
process="@form" />
</c:when>
<!-- Si submitOutcome est fourni, utiliser outcome -->
<c:when test="#{not empty submitOutcome}">
<p:commandButton
value="#{mode == 'create' ? 'Créer' : 'Modifier'}"
icon="pi pi-check"
styleClass="p-button-success"
outcome="#{submitOutcome}"
update="#{not empty update ? update : '@form'}"
process="@form" />
</c:when>
<!-- Sinon, essayer d'utiliser submitAction si fourni -->
<c:otherwise>
<p:commandButton
value="#{mode == 'create' ? 'Créer' : 'Modifier'}"
icon="pi pi-check"
styleClass="p-button-success"
action="#{submitAction}"
update="#{not empty update ? update : '@form'}"
process="@form" />
</c:otherwise>
</c:choose>
</c:if>
<p:commandButton
value="Annuler"
icon="pi pi-times"
styleClass="p-button-secondary"
outcome="#{not empty cancelOutcome ? cancelOutcome : '/pages/user-manager/users/list'}"
immediate="true" />
</div>
</f:facet>
</p:panel>
</ui:composition>

View File

@@ -0,0 +1,95 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<!--
Composant réutilisable: Formulaire Utilisateur (WOU/DRY Pattern)
Auteur: Lions User Manager
Version: 1.0.0
Description: Formulaire complet pour créer/modifier un utilisateur
Paramètres:
- user: UserDTO (requis) - L'utilisateur à éditer (peut être null pour création)
- formId: String (défaut: "userForm") - ID du formulaire
- mode: String (défaut: "create") - Mode: "create" ou "edit"
- showRealmSelector: Boolean (défaut: false) - Afficher le sélecteur de realm
- showPasswordFields: Boolean (défaut: true) - Afficher les champs mot de passe
- readonly: Boolean (défaut: false) - Mode lecture seule
- submitAction: String (optionnel) - Expression de méthode JSF à exécuter (ex: "#{bean.method}")
- submitOutcome: String (optionnel) - Page de redirection après soumission
- update: String (optionnel) - Composants à mettre à jour après soumission
- hasSubmitAction: Boolean (optionnel) - Indicateur si submitAction est fourni (pour éviter l'évaluation)
- useParentForm: Boolean (défaut: false) - Utiliser le formulaire parent au lieu de créer un nouveau formulaire
Exemples d'utilisation:
1. Formulaire de création:
<ui:include src="/templates/components/user-management/user-form.xhtml">
<ui:param name="user" value="#{userBean.newUser}" />
<ui:param name="mode" value="create" />
<ui:param name="submitAction" value="#{userBean.createUser}" />
</ui:include>
2. Formulaire d'édition:
<ui:include src="/templates/components/user-management/user-form.xhtml">
<ui:param name="user" value="#{userBean.selectedUser}" />
<ui:param name="mode" value="edit" />
<ui:param name="showPasswordFields" value="false" />
<ui:param name="submitAction" value="#{userBean.updateUser}" />
</ui:include>
3. Formulaire en lecture seule:
<ui:include src="/templates/components/user-management/user-form.xhtml">
<ui:param name="user" value="#{userBean.selectedUser}" />
<ui:param name="readonly" value="true" />
</ui:include>
-->
<c:set var="formId" value="#{empty formId ? 'userForm' : formId}" />
<c:set var="mode" value="#{empty mode ? 'create' : mode}" />
<c:set var="showRealmSelector" value="#{empty showRealmSelector ? false : showRealmSelector}" />
<c:set var="showPasswordFields" value="#{empty showPasswordFields ? true : showPasswordFields}" />
<c:set var="readonly" value="#{empty readonly ? false : readonly}" />
<c:set var="useParentForm" value="#{empty useParentForm ? false : useParentForm}" />
<c:choose>
<c:when test="#{useParentForm}">
<!-- Utiliser le formulaire parent - pas de formulaire ici -->
<ui:include src="/templates/components/user-management/user-form-content.xhtml">
<ui:param name="user" value="#{user}" />
<ui:param name="mode" value="#{mode}" />
<ui:param name="showRealmSelector" value="#{showRealmSelector}" />
<ui:param name="showPasswordFields" value="#{showPasswordFields}" />
<ui:param name="readonly" value="#{readonly}" />
<ui:param name="submitAction" value="#{submitAction}" />
<ui:param name="submitOutcome" value="#{submitOutcome}" />
<ui:param name="update" value="#{update}" />
<ui:param name="hasSubmitAction" value="#{hasSubmitAction}" />
<ui:param name="cancelOutcome" value="#{cancelOutcome}" />
</ui:include>
</c:when>
<c:otherwise>
<!-- Créer son propre formulaire -->
<h:form id="#{formId}">
<ui:include src="/templates/components/user-management/user-form-content.xhtml">
<ui:param name="user" value="#{user}" />
<ui:param name="mode" value="#{mode}" />
<ui:param name="showRealmSelector" value="#{showRealmSelector}" />
<ui:param name="showPasswordFields" value="#{showPasswordFields}" />
<ui:param name="readonly" value="#{readonly}" />
<ui:param name="submitAction" value="#{submitAction}" />
<ui:param name="submitOutcome" value="#{submitOutcome}" />
<ui:param name="update" value="#{update}" />
<ui:param name="hasSubmitAction" value="#{hasSubmitAction}" />
<ui:param name="cancelOutcome" value="#{cancelOutcome}" />
</ui:include>
</h:form>
</c:otherwise>
</c:choose>
</ui:composition>

View File

@@ -0,0 +1,105 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<!--
Composant réutilisable: Badge de Rôle Utilisateur (WOU/DRY Pattern)
Auteur: Lions User Manager
Version: 1.0.0
Description: Affiche un badge pour un rôle utilisateur avec icône et couleur
Paramètres:
- roleName: String (requis) - Nom du rôle
- roleType: String (optionnel) - Type de rôle: "REALM_ROLE", "CLIENT_ROLE", "COMPOSITE_ROLE"
- severity: String (optionnel) - Severity PrimeFaces: "success", "info", "warning", "danger" (défaut: "info")
- showIcon: Boolean (défaut: true) - Afficher l'icône
- icon: String (optionnel) - Classe d'icône PrimeIcons (défaut: "pi-shield")
- size: String (optionnel) - Taille: "small", "normal", "large" (défaut: "normal")
- clickable: Boolean (défaut: false) - Rendre le badge cliquable
- clickAction: String (optionnel) - Action au clic
- styleClass: String (optionnel) - Classes CSS supplémentaires
Exemples d'utilisation:
1. Badge simple:
<ui:include src="/templates/components/user-management/user-role-badge.xhtml">
<ui:param name="roleName" value="ADMIN" />
</ui:include>
2. Badge avec type:
<ui:include src="/templates/components/user-management/user-role-badge.xhtml">
<ui:param name="roleName" value="USER" />
<ui:param name="roleType" value="REALM_ROLE" />
<ui:param name="severity" value="success" />
</ui:include>
3. Badge cliquable:
<ui:include src="/templates/components/user-management/user-role-badge.xhtml">
<ui:param name="roleName" value="MODERATOR" />
<ui:param name="clickable" value="true" />
<ui:param name="clickAction" value="#{roleBean.viewRole(roleName)}" />
</ui:include>
-->
<c:set var="showIcon" value="#{empty showIcon ? true : showIcon}" />
<c:set var="size" value="#{empty size ? 'normal' : size}" />
<c:set var="clickable" value="#{empty clickable ? false : clickable}" />
<c:set var="severity" value="#{empty severity ? 'info' : severity}" />
<c:set var="icon" value="#{empty icon ? 'pi-shield' : icon}" />
<!-- Déterminer la severity selon le type de rôle -->
<c:choose>
<c:when test="#{roleType == 'REALM_ROLE'}">
<c:set var="severity" value="success" />
</c:when>
<c:when test="#{roleType == 'CLIENT_ROLE'}">
<c:set var="severity" value="info" />
</c:when>
<c:when test="#{roleType == 'COMPOSITE_ROLE'}">
<c:set var="severity" value="warning" />
</c:when>
</c:choose>
<!-- Déterminer la taille -->
<c:choose>
<c:when test="#{size == 'small'}">
<c:set var="tagStyleClass" value="text-xs" />
</c:when>
<c:when test="#{size == 'large'}">
<c:set var="tagStyleClass" value="text-base" />
</c:when>
<c:otherwise>
<c:set var="tagStyleClass" value="text-sm" />
</c:otherwise>
</c:choose>
<c:choose>
<!-- Badge cliquable -->
<c:when test="#{clickable and not empty clickAction}">
<p:commandLink
styleClass="role-badge-link"
action="#{clickAction}">
<p:tag
value="#{roleName}"
severity="#{severity}"
icon="#{showIcon ? icon : ''}"
styleClass="#{tagStyleClass} #{styleClass}" />
</p:commandLink>
</c:when>
<!-- Badge simple -->
<c:otherwise>
<p:tag
value="#{roleName}"
severity="#{severity}"
icon="#{showIcon ? icon : ''}"
styleClass="#{tagStyleClass} #{styleClass}" />
</c:otherwise>
</c:choose>
</ui:composition>

View File

@@ -0,0 +1,181 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<p:panel styleClass="w-full mb-3">
<f:facet name="header">
<div class="flex align-items-center justify-content-between">
<span>Recherche d'utilisateurs</span>
<p:commandButton
icon="pi pi-filter"
styleClass="p-button-text p-button-sm"
onclick="PF('advancedSearchDialog').toggle()"
rendered="#{showAdvanced}"
title="Options avancées" />
</div>
</f:facet>
<!-- Recherche rapide -->
<div class="flex gap-2 align-items-end">
<div class="flex-1">
<p:outputLabel for="searchText" value="Rechercher" />
<p:inputText id="searchText"
value="#{searchCriteria.searchTerm}"
placeholder="Nom, prénom, email, username..."
styleClass="w-full">
<p:ajax event="keyup"
delay="500"
listener="#{searchAction}"
update="#{update}" />
</p:inputText>
</div>
<!-- Filtre Realm -->
<c:if test="#{showRealmFilter}">
<div style="width: 200px;">
<p:outputLabel for="realmFilter" value="Realm" />
<p:selectOneMenu id="realmFilter"
value="#{searchCriteria.realmName}"
styleClass="w-full">
<f:selectItem itemLabel="Tous les realms" itemValue="" />
<f:selectItems value="#{userBean.availableRealms}" />
</p:selectOneMenu>
</div>
</c:if>
<!-- Filtre Statut -->
<c:if test="#{showStatusFilter}">
<div style="width: 180px;">
<p:outputLabel for="statusFilter" value="Statut" />
<p:selectOneMenu id="statusFilter"
value="#{searchCriteria.statut}"
styleClass="w-full">
<f:selectItem itemLabel="Tous les statuts" itemValue="" />
<f:selectItems value="#{userBean.statutOptions}" />
</p:selectOneMenu>
</div>
</c:if>
<!-- Bouton Rechercher -->
<div>
<p:commandButton
value="Rechercher"
icon="pi pi-search"
styleClass="p-button-primary"
action="#{searchAction}"
update="#{update}"
process="@form" />
</div>
<!-- Bouton Réinitialiser -->
<div>
<p:commandButton
value="Réinitialiser"
icon="pi pi-refresh"
styleClass="p-button-secondary"
action="#{userBean.resetSearch}"
update="#{update}"
process="@form" />
</div>
</div>
</p:panel>
<!-- Options avancées (Dialog) -->
<c:if test="#{showAdvanced}">
<p:dialog id="advancedSearchDialog"
header="Options de recherche avancées"
widgetVar="advancedSearchDialog"
modal="true"
resizable="false"
styleClass="w-full md:w-6">
<p:panelGrid columns="2" styleClass="w-full" columnClasses="col-12 md:col-6">
<!-- Email -->
<p:outputLabel for="emailFilter" value="Email" />
<p:inputText id="emailFilter"
value="#{searchCriteria.email}"
placeholder="email@example.com"
styleClass="w-full" />
<!-- Téléphone -->
<p:outputLabel for="phoneFilter" value="Téléphone" />
<p:inputText id="phoneFilter"
value="#{searchCriteria.telephone}"
placeholder="+225 07 12 34 56 78"
styleClass="w-full" />
<!-- Organisation -->
<p:outputLabel for="orgFilter" value="Organisation" />
<p:inputText id="orgFilter"
value="#{searchCriteria.organisation}"
placeholder="Nom de l'organisation"
styleClass="w-full" />
<!-- Ville -->
<p:outputLabel for="cityFilter" value="Ville" />
<p:inputText id="cityFilter"
value="#{searchCriteria.ville}"
placeholder="Nom de la ville"
styleClass="w-full" />
<!-- Rôle (si affiché) -->
<c:if test="#{showRoleFilter}">
<p:outputLabel for="roleFilter" value="Rôle Realm" />
<p:selectManyMenu id="roleFilter"
value="#{searchCriteria.realmRoles}"
styleClass="w-full">
<f:selectItem itemLabel="Tous les rôles" itemValue="" />
<f:selectItems value="#{userBean.availableRoles}" />
</p:selectManyMenu>
</c:if>
<!-- Date de création (début) -->
<p:outputLabel for="dateDebut" value="Date de création (début)" />
<p:calendar id="dateDebut"
value="#{searchCriteria.dateCreationMin}"
pattern="dd/MM/yyyy"
styleClass="w-full" />
<!-- Date de création (fin) -->
<p:outputLabel for="dateFin" value="Date de création (fin)" />
<p:calendar id="dateFin"
value="#{searchCriteria.dateCreationMax}"
pattern="dd/MM/yyyy"
styleClass="w-full" />
<!-- Enabled -->
<p:outputLabel for="enabledFilter" value="Compte activé" />
<p:selectOneMenu id="enabledFilter"
value="#{searchCriteria.enabled}"
styleClass="w-full">
<f:selectItem itemLabel="Tous" itemValue="" />
<f:selectItem itemLabel="Activé" itemValue="true" />
<f:selectItem itemLabel="Désactivé" itemValue="false" />
</p:selectOneMenu>
</p:panelGrid>
<f:facet name="footer">
<div class="flex gap-2 justify-content-end">
<p:commandButton
value="Appliquer"
icon="pi pi-check"
styleClass="p-button-primary"
action="#{searchAction}"
update="#{update}"
onclick="PF('advancedSearchDialog').hide()"
process="@form" />
<p:commandButton
value="Annuler"
icon="pi pi-times"
styleClass="p-button-secondary"
onclick="PF('advancedSearchDialog').hide()" />
</div>
</f:facet>
</p:dialog>
</c:if>
</ui:composition>

View File

@@ -0,0 +1,83 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<!--
Composant réutilisable: Barre de Recherche Utilisateur (WOU/DRY Pattern)
Auteur: Lions User Manager
Version: 1.0.0
Description: Barre de recherche avancée pour utilisateurs
Paramètres:
- searchCriteria: UserSearchCriteriaDTO (requis) - Critères de recherche
- searchAction: String (requis) - Action à exécuter lors de la recherche
- update: String (défaut: "@form") - Composants à mettre à jour
- showAdvanced: Boolean (défaut: false) - Afficher les options avancées
- showRealmFilter: Boolean (défaut: true) - Afficher le filtre realm
- showStatusFilter: Boolean (défaut: true) - Afficher le filtre statut
- showRoleFilter: Boolean (défaut: true) - Afficher le filtre rôle
- formId: String (défaut: "searchForm") - ID du formulaire
- useParentForm: Boolean (défaut: false) - Si true, n'crée pas de formulaire (utilise le formulaire parent)
Exemples d'utilisation:
1. Recherche simple:
<ui:include src="/templates/components/user-management/user-search-bar.xhtml">
<ui:param name="searchCriteria" value="#{userBean.searchCriteria}" />
<ui:param name="searchAction" value="#{userBean.search}" />
<ui:param name="update" value="userTable" />
</ui:include>
2. Recherche avec formulaire parent:
<h:form id="formUsers">
<ui:include src="/templates/components/user-management/user-search-bar.xhtml">
<ui:param name="searchCriteria" value="#{userBean.searchCriteria}" />
<ui:param name="searchAction" value="#{userBean.search}" />
<ui:param name="update" value="userTable" />
<ui:param name="useParentForm" value="true" />
</ui:include>
</h:form>
-->
<c:set var="formId" value="#{empty formId ? 'searchForm' : formId}" />
<c:set var="update" value="#{empty update ? '@form' : update}" />
<c:set var="showAdvanced" value="#{empty showAdvanced ? false : showAdvanced}" />
<c:set var="showRealmFilter" value="#{empty showRealmFilter ? true : showRealmFilter}" />
<c:set var="showStatusFilter" value="#{empty showStatusFilter ? true : showStatusFilter}" />
<c:set var="showRoleFilter" value="#{empty showRoleFilter ? true : showRoleFilter}" />
<c:set var="useParentForm" value="#{empty useParentForm ? false : useParentForm}" />
<c:choose>
<c:when test="#{useParentForm}">
<!-- Utiliser le formulaire parent - pas de formulaire ici -->
<ui:include src="/templates/components/user-management/user-search-bar-content.xhtml">
<ui:param name="searchCriteria" value="#{searchCriteria}" />
<ui:param name="searchAction" value="#{searchAction}" />
<ui:param name="update" value="#{update}" />
<ui:param name="showAdvanced" value="#{showAdvanced}" />
<ui:param name="showRealmFilter" value="#{showRealmFilter}" />
<ui:param name="showStatusFilter" value="#{showStatusFilter}" />
<ui:param name="showRoleFilter" value="#{showRoleFilter}" />
</ui:include>
</c:when>
<c:otherwise>
<!-- Créer son propre formulaire -->
<h:form id="#{formId}">
<ui:include src="/templates/components/user-management/user-search-bar-content.xhtml">
<ui:param name="searchCriteria" value="#{searchCriteria}" />
<ui:param name="searchAction" value="#{searchAction}" />
<ui:param name="update" value="#{update}" />
<ui:param name="showAdvanced" value="#{showAdvanced}" />
<ui:param name="showRealmFilter" value="#{showRealmFilter}" />
<ui:param name="showStatusFilter" value="#{showStatusFilter}" />
<ui:param name="showRoleFilter" value="#{showRoleFilter}" />
</ui:include>
</h:form>
</c:otherwise>
</c:choose>
</ui:composition>