Update - Lions User Manager - Client (Quarkus PrimeFaces Freya)
181
target/classes/META-INF/faces-config.xml
Normal file
@@ -0,0 +1,181 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<faces-config xmlns="https://jakarta.ee/xml/ns/jakartaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
|
||||
https://jakarta.ee/xml/ns/jakartaee/web-facesconfig_4_0.xsd"
|
||||
version="4.0">
|
||||
|
||||
<name>Lions User Manager</name>
|
||||
|
||||
<application>
|
||||
<locale-config>
|
||||
<default-locale>fr</default-locale>
|
||||
<supported-locale>fr</supported-locale>
|
||||
<supported-locale>en</supported-locale>
|
||||
</locale-config>
|
||||
</application>
|
||||
|
||||
<navigation-rule>
|
||||
<from-view-id>*</from-view-id>
|
||||
|
||||
<!-- ================================================================
|
||||
DASHBOARD & ACCUEIL
|
||||
================================================================ -->
|
||||
<navigation-case>
|
||||
<description>Page d'accueil / Dashboard</description>
|
||||
<from-outcome>userManagerDashboardPage</from-outcome>
|
||||
<to-view-id>/pages/user-manager/dashboard.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Navigation directe vers dashboard</description>
|
||||
<from-outcome>/pages/user-manager/dashboard</from-outcome>
|
||||
<to-view-id>/pages/user-manager/dashboard.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<!-- ================================================================
|
||||
GESTION DES UTILISATEURS
|
||||
================================================================ -->
|
||||
<navigation-case>
|
||||
<description>Page de liste des utilisateurs</description>
|
||||
<from-outcome>userListPage</from-outcome>
|
||||
<to-view-id>/pages/user-manager/users/list.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Navigation directe vers liste utilisateurs</description>
|
||||
<from-outcome>/pages/user-manager/users/list</from-outcome>
|
||||
<to-view-id>/pages/user-manager/users/list.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Page de création d'utilisateur</description>
|
||||
<from-outcome>userCreatePage</from-outcome>
|
||||
<to-view-id>/pages/user-manager/users/create.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Navigation directe vers création utilisateur</description>
|
||||
<from-outcome>/pages/user-manager/users/create</from-outcome>
|
||||
<to-view-id>/pages/user-manager/users/create.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Page de profil utilisateur</description>
|
||||
<from-outcome>userProfilePage</from-outcome>
|
||||
<to-view-id>/pages/user-manager/users/profile.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Navigation directe vers profil utilisateur</description>
|
||||
<from-outcome>/pages/user-manager/users/profile</from-outcome>
|
||||
<to-view-id>/pages/user-manager/users/profile.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Page d'édition utilisateur</description>
|
||||
<from-outcome>userEditPage</from-outcome>
|
||||
<to-view-id>/pages/user-manager/users/edit.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Navigation directe vers édition utilisateur</description>
|
||||
<from-outcome>/pages/user-manager/users/edit</from-outcome>
|
||||
<to-view-id>/pages/user-manager/users/edit.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<!-- ================================================================
|
||||
GESTION DES RÔLES
|
||||
================================================================ -->
|
||||
<navigation-case>
|
||||
<description>Page de liste des rôles</description>
|
||||
<from-outcome>roleListPage</from-outcome>
|
||||
<to-view-id>/pages/user-manager/roles/list.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Navigation directe vers liste rôles</description>
|
||||
<from-outcome>/pages/user-manager/roles/list</from-outcome>
|
||||
<to-view-id>/pages/user-manager/roles/list.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Page d'attribution de rôles</description>
|
||||
<from-outcome>roleAssignPage</from-outcome>
|
||||
<to-view-id>/pages/user-manager/roles/assign.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Navigation directe vers attribution rôles</description>
|
||||
<from-outcome>/pages/user-manager/roles/assign</from-outcome>
|
||||
<to-view-id>/pages/user-manager/roles/assign.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<!-- ================================================================
|
||||
AUDIT
|
||||
================================================================ -->
|
||||
<navigation-case>
|
||||
<description>Page de journal d'audit</description>
|
||||
<from-outcome>auditLogsPage</from-outcome>
|
||||
<to-view-id>/pages/user-manager/audit/logs.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Navigation directe vers journal d'audit</description>
|
||||
<from-outcome>/pages/user-manager/audit/logs</from-outcome>
|
||||
<to-view-id>/pages/user-manager/audit/logs.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<!-- ================================================================
|
||||
SYNCHRONISATION
|
||||
================================================================ -->
|
||||
<navigation-case>
|
||||
<description>Page de dashboard synchronisation</description>
|
||||
<from-outcome>syncDashboardPage</from-outcome>
|
||||
<to-view-id>/pages/user-manager/sync/dashboard.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Navigation directe vers dashboard synchronisation</description>
|
||||
<from-outcome>/pages/user-manager/sync/dashboard</from-outcome>
|
||||
<to-view-id>/pages/user-manager/sync/dashboard.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<!-- ================================================================
|
||||
PARAMÈTRES & PROFIL
|
||||
================================================================ -->
|
||||
<navigation-case>
|
||||
<description>Page de paramètres utilisateur</description>
|
||||
<from-outcome>settingsPage</from-outcome>
|
||||
<to-view-id>/pages/user-manager/settings.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
<navigation-case>
|
||||
<description>Navigation directe vers paramètres</description>
|
||||
<from-outcome>/pages/user-manager/settings</from-outcome>
|
||||
<to-view-id>/pages/user-manager/settings.xhtml</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
|
||||
</navigation-rule>
|
||||
</faces-config>
|
||||
|
||||
3
target/classes/META-INF/quarkus-config.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
# Configuration Quarkus pour PrimeFaces
|
||||
# Exclusion de classes obsolètes de PrimeFaces 14.0.5
|
||||
|
||||
58
target/classes/META-INF/resources/index.xhtml
Normal file
@@ -0,0 +1,58 @@
|
||||
<!DOCTYPE html>
|
||||
<html 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"
|
||||
lang="fr">
|
||||
|
||||
<h:head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Lions User Manager - Gestion des Utilisateurs Keycloak</title>
|
||||
|
||||
<!-- PrimeFaces Freya Theme -->
|
||||
<h:outputStylesheet name="primefaces-freya/theme.css" />
|
||||
<h:outputStylesheet name="css/primeicons.css" library="freya-layout" />
|
||||
<h:outputStylesheet name="css/primeflex.min.css" library="freya-layout" />
|
||||
</h:head>
|
||||
|
||||
<h:body>
|
||||
<div class="flex align-items-center justify-content-center" style="min-height: 100vh; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
|
||||
<div class="card" style="width: 90%; max-width: 600px; text-align: center;">
|
||||
<div class="flex flex-column align-items-center gap-3 p-5">
|
||||
<i class="pi pi-users text-6xl text-primary"></i>
|
||||
<h1 class="text-4xl font-bold m-0">Lions User Manager</h1>
|
||||
<p class="text-xl text-600 m-0">Gestion centralisée des utilisateurs Keycloak</p>
|
||||
|
||||
<div class="flex flex-column gap-2 mt-4" style="width: 100%;">
|
||||
<h:link outcome="/pages/user-manager/users/list" styleClass="no-underline">
|
||||
<p:commandButton value="Accéder à la Gestion des Utilisateurs"
|
||||
icon="pi pi-users"
|
||||
styleClass="w-full p-button-lg" />
|
||||
</h:link>
|
||||
|
||||
<h:link outcome="/pages/user-manager/roles/list" styleClass="no-underline">
|
||||
<p:commandButton value="Gestion des Rôles"
|
||||
icon="pi pi-shield"
|
||||
styleClass="w-full p-button-lg p-button-outlined" />
|
||||
</h:link>
|
||||
|
||||
<h:link outcome="/pages/user-manager/audit/logs" styleClass="no-underline">
|
||||
<p:commandButton value="Journal d'Audit"
|
||||
icon="pi pi-history"
|
||||
styleClass="w-full p-button-lg p-button-outlined" />
|
||||
</h:link>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 text-600">
|
||||
<p class="m-0">Version 1.0.0</p>
|
||||
<p class="m-0 text-sm">Module réutilisable pour l'écosystème LionsDev</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</h:body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
<!DOCTYPE html>
|
||||
<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"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:param name="page" value="#{auditConsultationBean}"/>
|
||||
<ui:define name="title">Journal d'Audit - Lions User Manager</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<!-- En-tête -->
|
||||
<ui:include src="/templates/components/layout/page-header.xhtml">
|
||||
<ui:param name="icon" value="pi pi-history text-orange-500" />
|
||||
<ui:param name="title" value="Journal d'Audit" />
|
||||
<ui:param name="description" value="Consultation des logs d'audit et statistiques" />
|
||||
<ui:define name="actions">
|
||||
<h:form id="formActionsAudit">
|
||||
<div class="flex gap-2">
|
||||
<ui:include src="/templates/components/shared/buttons/button-user-action.xhtml">
|
||||
<ui:param name="value" value="Exporter CSV" />
|
||||
<ui:param name="icon" value="pi pi-download" />
|
||||
<ui:param name="hasAction" value="true" />
|
||||
<ui:param name="action" value="#{auditConsultationBean.exportToCSV}" />
|
||||
<ui:param name="severity" value="success" />
|
||||
</ui:include>
|
||||
</div>
|
||||
</h:form>
|
||||
</ui:define>
|
||||
</ui:include>
|
||||
|
||||
<!-- Statistiques avec composants réutilisables -->
|
||||
<ui:include src="/templates/components/shared/dashboard/kpi-group.xhtml">
|
||||
<ui:param name="title" value="Statistiques d'Audit" />
|
||||
<ui:param name="columns" value="4" />
|
||||
<ui:define name="kpi-content">
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Total Actions" />
|
||||
<ui:param name="value" value="#{auditConsultationBean.totalRecords}" />
|
||||
<ui:param name="icon" value="pi-history" />
|
||||
<ui:param name="iconColor" value="blue-600" />
|
||||
</ui:include>
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Actions Réussies" />
|
||||
<ui:param name="value" value="#{auditConsultationBean.successCount}" />
|
||||
<ui:param name="icon" value="pi-check-circle" />
|
||||
<ui:param name="iconColor" value="green-600" />
|
||||
</ui:include>
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Actions Échouées" />
|
||||
<ui:param name="value" value="#{auditConsultationBean.failureCount}" />
|
||||
<ui:param name="icon" value="pi-times-circle" />
|
||||
<ui:param name="iconColor" value="red-600" />
|
||||
</ui:include>
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Taux de Réussite" />
|
||||
<ui:param name="value" value="#{auditConsultationBean.totalRecords > 0 ? (auditConsultationBean.successCount * 100 / auditConsultationBean.totalRecords) : 0}%" />
|
||||
<ui:param name="icon" value="pi-percentage" />
|
||||
<ui:param name="iconColor" value="purple-600" />
|
||||
</ui:include>
|
||||
</ui:define>
|
||||
</ui:include>
|
||||
|
||||
<!-- Filtres de recherche -->
|
||||
<div class="card mb-3">
|
||||
<h:form id="formFilters">
|
||||
<p:panelGrid columns="3" styleClass="w-full" columnClasses="col-12 md:col-4">
|
||||
<p:outputLabel for="acteurFilter" value="Acteur" />
|
||||
<p:inputText id="acteurFilter"
|
||||
value="#{auditConsultationBean.acteurUsername}"
|
||||
placeholder="Nom d'utilisateur..."
|
||||
styleClass="w-full" />
|
||||
|
||||
<p:outputLabel for="typeActionFilter" value="Type d'action" />
|
||||
<p:selectOneMenu id="typeActionFilter"
|
||||
value="#{auditConsultationBean.selectedTypeAction}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemLabel="Tous les types" itemValue="" />
|
||||
<f:selectItems value="#{auditConsultationBean.typeActionOptions}" />
|
||||
</p:selectOneMenu>
|
||||
|
||||
<p:outputLabel for="succesFilter" value="Résultat" />
|
||||
<p:selectOneMenu id="succesFilter"
|
||||
value="#{auditConsultationBean.succes}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemLabel="Tous" itemValue="" />
|
||||
<f:selectItem itemLabel="Succès" itemValue="true" />
|
||||
<f:selectItem itemLabel="Échec" itemValue="false" />
|
||||
</p:selectOneMenu>
|
||||
|
||||
<p:outputLabel for="dateDebutFilter" value="Date début" />
|
||||
<p:calendar id="dateDebutFilter"
|
||||
value="#{auditConsultationBean.dateDebut}"
|
||||
pattern="dd/MM/yyyy"
|
||||
styleClass="w-full" />
|
||||
|
||||
<p:outputLabel for="dateFinFilter" value="Date fin" />
|
||||
<p:calendar id="dateFinFilter"
|
||||
value="#{auditConsultationBean.dateFin}"
|
||||
pattern="dd/MM/yyyy"
|
||||
styleClass="w-full" />
|
||||
|
||||
<p:outputLabel for="ressourceFilter" value="Type ressource" />
|
||||
<p:inputText id="ressourceFilter"
|
||||
value="#{auditConsultationBean.ressourceType}"
|
||||
placeholder="USER, ROLE..."
|
||||
styleClass="w-full" />
|
||||
</p:panelGrid>
|
||||
|
||||
<div class="flex gap-2 justify-content-end mt-3">
|
||||
<p:commandButton
|
||||
value="Rechercher"
|
||||
icon="pi pi-search"
|
||||
styleClass="p-button-primary"
|
||||
action="#{auditConsultationBean.searchLogs}"
|
||||
update="formAuditLogs:auditLogsTable" />
|
||||
<p:commandButton
|
||||
value="Réinitialiser"
|
||||
icon="pi pi-refresh"
|
||||
styleClass="p-button-secondary"
|
||||
action="#{auditConsultationBean.resetFilters}"
|
||||
update="formAuditLogs:auditLogsTable @form" />
|
||||
</div>
|
||||
</h:form>
|
||||
</div>
|
||||
|
||||
<!-- Liste des logs avec p:dataTable -->
|
||||
<div class="card">
|
||||
<h:form id="formAuditLogs">
|
||||
<h5>Logs d'Audit</h5>
|
||||
<p:dataTable
|
||||
id="auditLogsTable"
|
||||
value="#{auditConsultationBean.auditLogs}"
|
||||
var="log"
|
||||
rowKey="#{log.id}"
|
||||
paginator="true"
|
||||
rows="20"
|
||||
rowsPerPageTemplate="10,20,50,100"
|
||||
emptyMessage="Aucun log d'audit trouvé"
|
||||
styleClass="w-full">
|
||||
|
||||
<!-- Colonne Statut -->
|
||||
<p:column headerText="Statut" style="width: 5rem">
|
||||
<p:tag
|
||||
value="#{log.succes ? 'Succès' : 'Échec'}"
|
||||
severity="#{log.succes ? 'success' : 'danger'}"
|
||||
styleClass="text-xs" />
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne Type d'action -->
|
||||
<p:column headerText="Type d'action" sortBy="#{log.typeAction}" style="width: 15%">
|
||||
<strong class="text-900">#{log.typeAction}</strong>
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne Acteur -->
|
||||
<p:column headerText="Acteur" sortBy="#{log.acteurUsername}" style="width: 15%">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-user text-color-secondary"></i>
|
||||
<span>#{log.acteurUsername}</span>
|
||||
</div>
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne Ressource -->
|
||||
<p:column headerText="Ressource" style="width: 12%">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-database text-color-secondary"></i>
|
||||
<span>#{log.ressourceType}</span>
|
||||
</div>
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne Date -->
|
||||
<p:column headerText="Date" sortBy="#{log.dateAction}" style="width: 15%">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-calendar text-color-secondary"></i>
|
||||
<span>#{log.dateAction}</span>
|
||||
</div>
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne Détails -->
|
||||
<p:column headerText="Détails" style="width: 20%">
|
||||
<c:if test="#{not empty log.details}">
|
||||
<span class="text-color-secondary text-sm">#{log.details}</span>
|
||||
</c:if>
|
||||
<c:if test="#{empty log.details}">
|
||||
<span class="text-color-secondary text-sm">-</span>
|
||||
</c:if>
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne IP -->
|
||||
<p:column headerText="IP" style="width: 10%">
|
||||
<c:if test="#{not empty log.adresseIp}">
|
||||
<span class="text-color-secondary text-sm">#{log.adresseIp}</span>
|
||||
</c:if>
|
||||
<c:if test="#{empty log.adresseIp}">
|
||||
<span class="text-color-secondary text-sm">-</span>
|
||||
</c:if>
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne Actions -->
|
||||
<p:column headerText="Actions" style="width: 8%">
|
||||
<p:commandButton
|
||||
icon="pi pi-eye"
|
||||
styleClass="p-button-text p-button-sm"
|
||||
title="Voir les détails"
|
||||
onclick="PF('auditLogDetailsDialog').show()">
|
||||
<f:setPropertyActionListener target="#{auditConsultationBean.selectedLog}" value="#{log}" />
|
||||
</p:commandButton>
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
</h:form>
|
||||
</div>
|
||||
|
||||
<!-- Dialog de détails -->
|
||||
<p:dialog
|
||||
id="auditLogDetailsDialog"
|
||||
widgetVar="auditLogDetailsDialog"
|
||||
header="Détails du Log d'Audit"
|
||||
modal="true"
|
||||
resizable="false"
|
||||
styleClass="w-full md:w-30rem">
|
||||
<h:form id="formAuditLogDetails">
|
||||
<c:if test="#{not empty auditConsultationBean.selectedLog}">
|
||||
<div class="flex flex-column gap-3">
|
||||
<div>
|
||||
<strong>Type d'action:</strong>
|
||||
<p>#{auditConsultationBean.selectedLog.typeAction}</p>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Acteur:</strong>
|
||||
<p>#{auditConsultationBean.selectedLog.acteurUsername}</p>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Ressource:</strong>
|
||||
<p>#{auditConsultationBean.selectedLog.ressourceType} - #{auditConsultationBean.selectedLog.ressourceId}</p>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Date:</strong>
|
||||
<p>#{auditConsultationBean.selectedLog.dateAction}</p>
|
||||
</div>
|
||||
<c:if test="#{not empty auditConsultationBean.selectedLog.details}">
|
||||
<div>
|
||||
<strong>Détails:</strong>
|
||||
<p>#{auditConsultationBean.selectedLog.details}</p>
|
||||
</div>
|
||||
</c:if>
|
||||
<c:if test="#{not empty auditConsultationBean.selectedLog.adresseIp}">
|
||||
<div>
|
||||
<strong>Adresse IP:</strong>
|
||||
<p>#{auditConsultationBean.selectedLog.adresseIp}</p>
|
||||
</div>
|
||||
</c:if>
|
||||
<c:if test="#{not empty auditConsultationBean.selectedLog.userAgent}">
|
||||
<div>
|
||||
<strong>User Agent:</strong>
|
||||
<p>#{auditConsultationBean.selectedLog.userAgent}</p>
|
||||
</div>
|
||||
</c:if>
|
||||
<c:if test="#{not empty auditConsultationBean.selectedLog.messageErreur}">
|
||||
<div>
|
||||
<strong class="text-red-600">Message d'erreur:</strong>
|
||||
<p class="text-red-600">#{auditConsultationBean.selectedLog.messageErreur}</p>
|
||||
</div>
|
||||
</c:if>
|
||||
</div>
|
||||
</c:if>
|
||||
</h:form>
|
||||
</p:dialog>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,175 @@
|
||||
<!DOCTYPE html>
|
||||
<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"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:define name="title">Tableau de Bord - Lions User Manager</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<h:form id="formDashboard">
|
||||
<div class="grid">
|
||||
<!-- En-tête -->
|
||||
<div class="col-12">
|
||||
<ui:include src="/templates/components/layout/page-header.xhtml">
|
||||
<ui:param name="icon" value="pi pi-home text-blue-500" />
|
||||
<ui:param name="title" value="Tableau de Bord" />
|
||||
<ui:param name="description" value="Vue d'ensemble de la gestion des utilisateurs Keycloak" />
|
||||
<ui:define name="actions">
|
||||
<h:form id="formRefresh">
|
||||
<p:commandButton
|
||||
value="Rafraîchir"
|
||||
icon="pi pi-refresh"
|
||||
styleClass="p-button-secondary"
|
||||
action="#{dashboardBean.refreshStatistics}"
|
||||
update=":formDashboard" />
|
||||
</h:form>
|
||||
</ui:define>
|
||||
</ui:include>
|
||||
</div>
|
||||
|
||||
<!-- KPIs Principaux avec composant réutilisable -->
|
||||
<div class="col-12">
|
||||
<ui:include src="/templates/components/shared/dashboard/kpi-group.xhtml">
|
||||
<ui:param name="title" value="Statistiques Principales" />
|
||||
<ui:param name="columns" value="4" />
|
||||
<ui:define name="kpi-content">
|
||||
<!-- KPI 1: Utilisateurs Actifs -->
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Utilisateurs Actifs" />
|
||||
<ui:param name="value" value="#{dashboardBean.totalUsersDisplay}" />
|
||||
<ui:param name="icon" value="pi-users" />
|
||||
<ui:param name="iconColor" value="blue-600" />
|
||||
<ui:param name="subtitle" value="Total utilisateurs" />
|
||||
<ui:param name="clickable" value="true" />
|
||||
<ui:param name="clickOutcome" value="/pages/user-manager/users/list" />
|
||||
</ui:include>
|
||||
|
||||
<!-- KPI 2: Rôles Realm -->
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Rôles Realm" />
|
||||
<ui:param name="value" value="#{dashboardBean.totalRolesDisplay}" />
|
||||
<ui:param name="icon" value="pi-shield" />
|
||||
<ui:param name="iconColor" value="green-600" />
|
||||
<ui:param name="subtitle" value="Rôles configurés" />
|
||||
<ui:param name="clickable" value="true" />
|
||||
<ui:param name="clickOutcome" value="/pages/user-manager/roles/list" />
|
||||
</ui:include>
|
||||
|
||||
<!-- KPI 3: Actions Récentes -->
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Actions Récentes" />
|
||||
<ui:param name="value" value="#{dashboardBean.recentActionsDisplay}" />
|
||||
<ui:param name="icon" value="pi-history" />
|
||||
<ui:param name="iconColor" value="orange-600" />
|
||||
<ui:param name="subtitle" value="Dernières 24h" />
|
||||
<ui:param name="clickable" value="true" />
|
||||
<ui:param name="clickOutcome" value="/pages/user-manager/audit/logs" />
|
||||
</ui:include>
|
||||
|
||||
<!-- KPI 4: Rôles Client -->
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Rôles Client" />
|
||||
<ui:param name="value" value="-" />
|
||||
<ui:param name="icon" value="pi-key" />
|
||||
<ui:param name="iconColor" value="purple-600" />
|
||||
<ui:param name="subtitle" value="Rôles clients configurés" />
|
||||
<ui:param name="clickable" value="true" />
|
||||
<ui:param name="clickOutcome" value="/pages/user-manager/roles/list" />
|
||||
</ui:include>
|
||||
</ui:define>
|
||||
</ui:include>
|
||||
</div>
|
||||
|
||||
<!-- Actions Rapides -->
|
||||
<ui:include src="/templates/components/shared/dashboard/dashboard-section.xhtml">
|
||||
<ui:param name="title" value="Actions Rapides" />
|
||||
<ui:param name="icon" value="pi-bolt" />
|
||||
<ui:param name="colSize" value="col-12 lg:col-6" />
|
||||
<ui:define name="section-content">
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6">
|
||||
<h:form>
|
||||
<p:commandButton
|
||||
value="Nouvel Utilisateur"
|
||||
icon="pi pi-user-plus"
|
||||
styleClass="w-full p-button-success"
|
||||
outcome="/pages/user-manager/users/create" />
|
||||
</h:form>
|
||||
</div>
|
||||
<div class="col-12 md:col-6">
|
||||
<h:form>
|
||||
<p:commandButton
|
||||
value="Liste des Utilisateurs"
|
||||
icon="pi pi-users"
|
||||
styleClass="w-full p-button-primary"
|
||||
outcome="/pages/user-manager/users/list" />
|
||||
</h:form>
|
||||
</div>
|
||||
<div class="col-12 md:col-6">
|
||||
<h:form>
|
||||
<p:commandButton
|
||||
value="Gestion des Rôles"
|
||||
icon="pi pi-shield"
|
||||
styleClass="w-full p-button-info"
|
||||
outcome="/pages/user-manager/roles/list" />
|
||||
</h:form>
|
||||
</div>
|
||||
<div class="col-12 md:col-6">
|
||||
<h:form>
|
||||
<p:commandButton
|
||||
value="Journal d'Audit"
|
||||
icon="pi pi-history"
|
||||
styleClass="w-full p-button-help"
|
||||
outcome="/pages/user-manager/audit/logs" />
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:include>
|
||||
|
||||
<!-- Informations Système -->
|
||||
<ui:include src="/templates/components/shared/dashboard/dashboard-section.xhtml">
|
||||
<ui:param name="title" value="Informations Système" />
|
||||
<ui:param name="icon" value="pi-info-circle" />
|
||||
<ui:param name="colSize" value="col-12 lg:col-6" />
|
||||
<ui:define name="section-content">
|
||||
<div class="flex flex-column gap-2">
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<span class="text-600">Version</span>
|
||||
<span class="font-semibold">1.0.0</span>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<span class="text-600">Realm Keycloak</span>
|
||||
<span class="font-semibold">lions-user-manager</span>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<span class="text-600">Statut</span>
|
||||
<p:tag value="Opérationnel" severity="success" />
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<span class="text-600">Application</span>
|
||||
<span class="font-semibold">Lions User Manager</span>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<span class="text-600">Environnement</span>
|
||||
<span class="font-semibold">Développement</span>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<span class="text-600">Base de données</span>
|
||||
<span class="font-semibold">Keycloak Admin API</span>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<span class="text-600">Framework</span>
|
||||
<span class="font-semibold">Quarkus, PrimeFaces Freya</span>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:include>
|
||||
</div>
|
||||
</h:form>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<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"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:param name="page" value="#{userProfilBean}"/>
|
||||
<ui:define name="title">Attribution de Rôles - Lions User Manager</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<!-- En-tête -->
|
||||
<ui:include src="/templates/components/layout/page-header.xhtml">
|
||||
<ui:param name="icon" value="pi pi-key text-purple-500" />
|
||||
<ui:param name="title" value="Attribution de Rôles" />
|
||||
<ui:param name="description" value="Gérer les rôles de l'utilisateur" />
|
||||
</ui:include>
|
||||
|
||||
<!-- Attribution de rôles -->
|
||||
<div class="card">
|
||||
<ui:include src="/templates/components/role-management/role-assignment.xhtml">
|
||||
<ui:param name="user" value="#{userProfilBean.user}" />
|
||||
<ui:param name="availableRoles" value="#{roleGestionBean.allRoles}" />
|
||||
<ui:param name="userRoles" value="#{roleGestionBean.getUserRolesDTOs(userProfilBean.user)}" />
|
||||
<ui:param name="update" value="@form" />
|
||||
</ui:include>
|
||||
</div>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
<!DOCTYPE html>
|
||||
<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"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:param name="page" value="#{roleGestionBean}"/>
|
||||
<ui:define name="title">Gestion des Rôles - Lions User Manager</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<!-- En-tête -->
|
||||
<ui:include src="/templates/components/layout/page-header.xhtml">
|
||||
<ui:param name="icon" value="pi pi-shield text-purple-500" />
|
||||
<ui:param name="title" value="Gestion des Rôles" />
|
||||
<ui:param name="description" value="Gestion des rôles Realm et Client Keycloak" />
|
||||
<ui:define name="actions">
|
||||
<h:form id="formActionsRoles">
|
||||
<div class="flex gap-2">
|
||||
<ui:include src="/templates/components/shared/buttons/button-user-action.xhtml">
|
||||
<ui:param name="value" value="Nouveau Rôle Realm" />
|
||||
<ui:param name="icon" value="pi pi-plus" />
|
||||
<ui:param name="hasAction" value="false" />
|
||||
<ui:param name="hasOutcome" value="false" />
|
||||
<ui:param name="onclick" value="PF('createRealmRoleDialog').show()" />
|
||||
<ui:param name="severity" value="success" />
|
||||
</ui:include>
|
||||
<ui:include src="/templates/components/shared/buttons/button-user-action.xhtml">
|
||||
<ui:param name="value" value="Nouveau Rôle Client" />
|
||||
<ui:param name="icon" value="pi pi-plus-circle" />
|
||||
<ui:param name="hasAction" value="false" />
|
||||
<ui:param name="hasOutcome" value="false" />
|
||||
<ui:param name="onclick" value="PF('createClientRoleDialog').show()" />
|
||||
<ui:param name="severity" value="info" />
|
||||
</ui:include>
|
||||
</div>
|
||||
</h:form>
|
||||
</ui:define>
|
||||
</ui:include>
|
||||
|
||||
<!-- Filtres -->
|
||||
<div class="card mb-3">
|
||||
<h:form id="formFilters">
|
||||
<p:panelGrid columns="3" styleClass="w-full" columnClasses="col-12 md:col-4">
|
||||
<p:outputLabel for="realmFilter" value="Realm" />
|
||||
<p:selectOneMenu id="realmFilter"
|
||||
value="#{roleGestionBean.realmName}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemLabel="Sélectionner..." itemValue="" />
|
||||
<f:selectItems value="#{roleGestionBean.availableRealms}" />
|
||||
<p:ajax event="change"
|
||||
listener="#{roleGestionBean.loadRealmRoles}"
|
||||
update=":formRealmRoles:realmRolesPanel :formClientRoles:clientRolesPanel" />
|
||||
</p:selectOneMenu>
|
||||
|
||||
<p:outputLabel for="clientFilter" value="Client" />
|
||||
<p:selectOneMenu id="clientFilter"
|
||||
value="#{roleGestionBean.clientName}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemLabel="Sélectionner..." itemValue="" />
|
||||
<f:selectItems value="#{roleGestionBean.availableClients}" />
|
||||
<p:ajax event="change"
|
||||
listener="#{roleGestionBean.loadClientRoles}"
|
||||
update=":formClientRoles:clientRolesPanel" />
|
||||
</p:selectOneMenu>
|
||||
|
||||
<p:outputLabel for="typeFilter" value="Type" />
|
||||
<p:selectOneMenu id="typeFilter"
|
||||
value="#{roleGestionBean.selectedTypeRole}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemLabel="Tous les types" itemValue="" />
|
||||
<f:selectItems value="#{roleGestionBean.typeRoleOptions}" />
|
||||
</p:selectOneMenu>
|
||||
</p:panelGrid>
|
||||
</h:form>
|
||||
</div>
|
||||
|
||||
<!-- Rôles Realm -->
|
||||
<div class="card mb-3">
|
||||
<h:form id="formRealmRoles">
|
||||
<p:panel id="realmRolesPanel" header="Rôles Realm" toggleable="true" collapsed="false">
|
||||
<div class="grid">
|
||||
<c:forEach var="role" items="#{roleGestionBean.realmRoles}">
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<ui:include src="/templates/components/role-management/role-card.xhtml">
|
||||
<ui:param name="role" value="#{role}" />
|
||||
<ui:param name="showActions" value="true" />
|
||||
</ui:include>
|
||||
</div>
|
||||
</c:forEach>
|
||||
<c:if test="#{empty roleGestionBean.realmRoles}">
|
||||
<div class="col-12">
|
||||
<p class="text-center text-color-secondary">Aucun rôle Realm trouvé</p>
|
||||
</div>
|
||||
</c:if>
|
||||
</div>
|
||||
</p:panel>
|
||||
</h:form>
|
||||
</div>
|
||||
|
||||
<!-- Rôles Client -->
|
||||
<div class="card">
|
||||
<h:form id="formClientRoles">
|
||||
<p:panel id="clientRolesPanel" header="Rôles Client" toggleable="true" collapsed="false">
|
||||
<div class="grid">
|
||||
<c:forEach var="role" items="#{roleGestionBean.clientRoles}">
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<ui:include src="/templates/components/role-management/role-card.xhtml">
|
||||
<ui:param name="role" value="#{role}" />
|
||||
<ui:param name="showActions" value="true" />
|
||||
</ui:include>
|
||||
</div>
|
||||
</c:forEach>
|
||||
<c:if test="#{empty roleGestionBean.clientRoles}">
|
||||
<div class="col-12">
|
||||
<p class="text-center text-color-secondary">Aucun rôle Client trouvé</p>
|
||||
</div>
|
||||
</c:if>
|
||||
</div>
|
||||
</p:panel>
|
||||
</h:form>
|
||||
</div>
|
||||
|
||||
<!-- Dialog Création Rôle Realm -->
|
||||
<p:dialog id="createRealmRoleDialog"
|
||||
header="Nouveau Rôle Realm"
|
||||
widgetVar="createRealmRoleDialog"
|
||||
modal="true"
|
||||
styleClass="w-full md:w-6">
|
||||
<h:form id="formCreateRealmRole">
|
||||
<ui:include src="/templates/components/role-management/role-form.xhtml">
|
||||
<ui:param name="role" value="#{roleGestionBean.newRole}" />
|
||||
<ui:param name="mode" value="create" />
|
||||
<ui:param name="showClientSelector" value="false" />
|
||||
<ui:param name="submitAction" value="#{roleGestionBean.createRealmRole}" />
|
||||
<ui:param name="hasSubmitAction" value="true" />
|
||||
<ui:param name="update" value=":formRealmRoles:realmRolesPanel" />
|
||||
<ui:param name="useParentForm" value="true" />
|
||||
</ui:include>
|
||||
</h:form>
|
||||
</p:dialog>
|
||||
|
||||
<!-- Dialog Création Rôle Client -->
|
||||
<p:dialog id="createClientRoleDialog"
|
||||
header="Nouveau Rôle Client"
|
||||
widgetVar="createClientRoleDialog"
|
||||
modal="true"
|
||||
styleClass="w-full md:w-6">
|
||||
<h:form id="formCreateClientRole">
|
||||
<ui:include src="/templates/components/role-management/role-form.xhtml">
|
||||
<ui:param name="role" value="#{roleGestionBean.newRole}" />
|
||||
<ui:param name="mode" value="create" />
|
||||
<ui:param name="showClientSelector" value="true" />
|
||||
<ui:param name="submitAction" value="#{roleGestionBean.createClientRole}" />
|
||||
<ui:param name="hasSubmitAction" value="true" />
|
||||
<ui:param name="update" value=":formClientRoles:clientRolesPanel" />
|
||||
<ui:param name="useParentForm" value="true" />
|
||||
</ui:include>
|
||||
</h:form>
|
||||
</p:dialog>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
<!DOCTYPE html>
|
||||
<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"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:param name="page" value="#{settingsBean}"/>
|
||||
<ui:define name="title">Paramètres - Lions User Manager</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<!-- En-tête -->
|
||||
<ui:include src="/templates/components/layout/page-header.xhtml">
|
||||
<ui:param name="icon" value="pi pi-cog text-blue-500" />
|
||||
<ui:param name="title" value="Paramètres" />
|
||||
<ui:param name="description" value="Gérer vos préférences et paramètres de compte" />
|
||||
</ui:include>
|
||||
|
||||
<div class="grid">
|
||||
<!-- Informations du compte -->
|
||||
<div class="col-12 lg:col-8">
|
||||
<div class="card">
|
||||
<h5>Informations du compte</h5>
|
||||
<h:form id="formAccountInfo">
|
||||
<p:panelGrid columns="2" styleClass="w-full" columnClasses="col-12 md:col-4, col-12 md:col-8">
|
||||
<p:outputLabel for="username" value="Nom d'utilisateur" />
|
||||
<p:inputText id="username"
|
||||
value="#{userSessionBean.username}"
|
||||
readonly="true"
|
||||
styleClass="w-full" />
|
||||
|
||||
<p:outputLabel for="email" value="Email" />
|
||||
<p:inputText id="email"
|
||||
value="#{userSessionBean.email}"
|
||||
readonly="true"
|
||||
styleClass="w-full" />
|
||||
|
||||
<p:outputLabel for="fullName" value="Nom complet" />
|
||||
<p:inputText id="fullName"
|
||||
value="#{userSessionBean.fullName}"
|
||||
readonly="true"
|
||||
styleClass="w-full" />
|
||||
|
||||
<p:outputLabel for="mainRole" value="Rôle principal" />
|
||||
<p:inputText id="mainRole"
|
||||
value="#{userSessionBean.mainRole}"
|
||||
readonly="true"
|
||||
styleClass="w-full" />
|
||||
</p:panelGrid>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Préférences -->
|
||||
<div class="col-12 lg:col-4">
|
||||
<div class="card">
|
||||
<h5>Préférences</h5>
|
||||
<h:form id="formPreferences">
|
||||
<div class="flex flex-column gap-3">
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<span class="text-600">Thème des composants</span>
|
||||
<p:selectOneMenu value="#{guestPreferences.componentTheme}"
|
||||
styleClass="w-12rem">
|
||||
<f:selectItems value="#{guestPreferences.componentThemes}"
|
||||
var="theme"
|
||||
itemLabel="#{theme.name}"
|
||||
itemValue="#{theme.file}" />
|
||||
<p:ajax event="change" update="@form" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<span class="text-600">Mode sombre</span>
|
||||
<p:selectOneMenu value="#{guestPreferences.darkMode}"
|
||||
styleClass="w-12rem">
|
||||
<f:selectItem itemLabel="Clair" itemValue="light" />
|
||||
<f:selectItem itemLabel="Sombre" itemValue="dark" />
|
||||
<p:ajax event="change" update="@form" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<span class="text-600">Style d'input</span>
|
||||
<p:selectOneMenu value="#{guestPreferences.inputStyle}"
|
||||
styleClass="w-12rem">
|
||||
<f:selectItem itemLabel="Outlined" itemValue="outlined" />
|
||||
<f:selectItem itemLabel="Filled" itemValue="filled" />
|
||||
<p:ajax event="change" update="@form" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
</div>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h5>Actions</h5>
|
||||
<div class="flex gap-2">
|
||||
<h:form>
|
||||
<p:commandButton
|
||||
value="Rafraîchir les informations"
|
||||
icon="pi pi-refresh"
|
||||
styleClass="p-button-secondary"
|
||||
action="#{userSessionBean.loadUserInfo}"
|
||||
update="formAccountInfo" />
|
||||
</h:form>
|
||||
<h:form>
|
||||
<p:commandButton
|
||||
value="Changer le mot de passe"
|
||||
icon="pi pi-key"
|
||||
styleClass="p-button-info"
|
||||
outcome="/pages/user-manager/users/profile" />
|
||||
</h:form>
|
||||
<h:form>
|
||||
<p:commandButton
|
||||
value="Sauvegarder les préférences"
|
||||
icon="pi pi-save"
|
||||
styleClass="p-button-success"
|
||||
action="#{settingsBean.savePreferences}"
|
||||
update="@form" />
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<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"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:define name="title">Synchronisation Keycloak - Lions User Manager</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<!-- En-tête -->
|
||||
<ui:include src="/templates/components/layout/page-header.xhtml">
|
||||
<ui:param name="icon" value="pi pi-sync text-blue-500" />
|
||||
<ui:param name="title" value="Synchronisation Keycloak" />
|
||||
<ui:param name="description" value="Synchronisation et vérification de l'état de Keycloak" />
|
||||
</ui:include>
|
||||
|
||||
<!-- Health Checks -->
|
||||
<div class="grid mb-4">
|
||||
<div class="col-12 md:col-6">
|
||||
<div class="card">
|
||||
<h5>État de Keycloak</h5>
|
||||
<p:outputLabel value="Vérification de la connexion..." />
|
||||
<!-- TODO: Intégrer SyncServiceClient pour afficher le statut -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 md:col-6">
|
||||
<div class="card">
|
||||
<h5>Actions de Synchronisation</h5>
|
||||
<div class="flex flex-column gap-2">
|
||||
<ui:include src="/templates/components/shared/buttons/button-user-action.xhtml">
|
||||
<ui:param name="value" value="Synchroniser Utilisateurs" />
|
||||
<ui:param name="icon" value="pi pi-users" />
|
||||
<ui:param name="severity" value="primary" />
|
||||
</ui:include>
|
||||
<ui:include src="/templates/components/shared/buttons/button-user-action.xhtml">
|
||||
<ui:param name="value" value="Synchroniser Rôles" />
|
||||
<ui:param name="icon" value="pi pi-shield" />
|
||||
<ui:param name="severity" value="info" />
|
||||
</ui:include>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
<!DOCTYPE html>
|
||||
<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"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:param name="page" value="#{userCreationBean}"/>
|
||||
<ui:define name="title">Nouvel Utilisateur - Lions User Manager</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<!-- ================================================================
|
||||
EN-TÊTE DE LA PAGE
|
||||
================================================================ -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-user-plus text-green-500" style="font-size: 2rem"></i>
|
||||
<div>
|
||||
<h3 class="m-0 mb-1">Nouvel Utilisateur</h3>
|
||||
<p class="text-600 m-0">Créer un nouvel utilisateur dans Keycloak</p>
|
||||
</div>
|
||||
</div>
|
||||
<h:link outcome="/pages/user-manager/users/list" styleClass="p-button p-button-text">
|
||||
<i class="pi pi-arrow-left mr-2"></i>
|
||||
Retour à la liste
|
||||
</h:link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
FORMULAIRE DE CRÉATION
|
||||
================================================================ -->
|
||||
<h:form id="formUserCreation">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h3 class="text-900 font-semibold text-lg mb-4 flex align-items-center gap-2">
|
||||
<i class="pi pi-info-circle text-blue-500"></i>
|
||||
Informations de l'Utilisateur
|
||||
</h3>
|
||||
|
||||
<div class="grid">
|
||||
<!-- Colonne gauche: Informations de base -->
|
||||
<div class="col-12 lg:col-6">
|
||||
<div class="surface-50 border-round p-3 mb-3">
|
||||
<h4 class="text-900 font-semibold mb-3 flex align-items-center gap-2">
|
||||
<i class="pi pi-user text-blue-500"></i>
|
||||
<span>Informations de Base</span>
|
||||
</h4>
|
||||
|
||||
<!-- Nom d'utilisateur -->
|
||||
<div class="field mb-3">
|
||||
<label for="username" class="block text-900 font-medium mb-2">
|
||||
Nom d'utilisateur <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<p:inputText id="username"
|
||||
value="#{userCreationBean.newUser.username}"
|
||||
styleClass="w-full"
|
||||
required="true"
|
||||
placeholder="ex: jdupont">
|
||||
<f:validateLength minimum="3" maximum="50" />
|
||||
</p:inputText>
|
||||
<small class="text-500">Identifiant unique de connexion</small>
|
||||
</div>
|
||||
|
||||
<!-- Email -->
|
||||
<div class="field mb-3">
|
||||
<label for="email" class="block text-900 font-medium mb-2">
|
||||
Adresse email <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<p:inputText id="email"
|
||||
value="#{userCreationBean.newUser.email}"
|
||||
styleClass="w-full"
|
||||
required="true"
|
||||
type="email"
|
||||
placeholder="ex: jean.dupont@example.com">
|
||||
<f:validateRegex pattern="^[A-Za-z0-9+_.-]+@(.+)$" />
|
||||
</p:inputText>
|
||||
</div>
|
||||
|
||||
<!-- Prénom -->
|
||||
<div class="field mb-3">
|
||||
<label for="prenom" class="block text-900 font-medium mb-2">
|
||||
Prénom <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<p:inputText id="prenom"
|
||||
value="#{userCreationBean.newUser.prenom}"
|
||||
styleClass="w-full"
|
||||
required="true"
|
||||
placeholder="ex: Jean">
|
||||
<f:validateLength minimum="2" maximum="100" />
|
||||
</p:inputText>
|
||||
</div>
|
||||
|
||||
<!-- Nom -->
|
||||
<div class="field mb-0">
|
||||
<label for="nom" class="block text-900 font-medium mb-2">
|
||||
Nom <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<p:inputText id="nom"
|
||||
value="#{userCreationBean.newUser.nom}"
|
||||
styleClass="w-full"
|
||||
required="true"
|
||||
placeholder="ex: Dupont">
|
||||
<f:validateLength minimum="2" maximum="100" />
|
||||
</p:inputText>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Colonne droite: Mot de passe et Configuration -->
|
||||
<div class="col-12 lg:col-6">
|
||||
<!-- Section Mot de passe -->
|
||||
<div class="surface-50 border-round p-3 mb-3">
|
||||
<h4 class="text-900 font-semibold mb-3 flex align-items-center gap-2">
|
||||
<i class="pi pi-key text-orange-500"></i>
|
||||
<span>Mot de Passe</span>
|
||||
</h4>
|
||||
|
||||
<!-- Mot de passe -->
|
||||
<div class="field mb-3">
|
||||
<label for="password" class="block text-900 font-medium mb-2">
|
||||
Mot de passe <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<p:password id="password"
|
||||
value="#{userCreationBean.password}"
|
||||
styleClass="w-full"
|
||||
required="true"
|
||||
feedback="true"
|
||||
toggleMask="true"
|
||||
placeholder="Minimum 8 caractères">
|
||||
<f:validateLength minimum="8" maximum="100" />
|
||||
</p:password>
|
||||
<small class="text-500">Au moins 8 caractères</small>
|
||||
</div>
|
||||
|
||||
<!-- Confirmation mot de passe -->
|
||||
<div class="field mb-0">
|
||||
<label for="passwordConfirm" class="block text-900 font-medium mb-2">
|
||||
Confirmer le mot de passe <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<p:password id="passwordConfirm"
|
||||
value="#{userCreationBean.passwordConfirm}"
|
||||
styleClass="w-full"
|
||||
required="true"
|
||||
feedback="false"
|
||||
toggleMask="true"
|
||||
placeholder="Confirmer le mot de passe">
|
||||
</p:password>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section Configuration -->
|
||||
<div class="surface-50 border-round p-3">
|
||||
<h4 class="text-900 font-semibold mb-3 flex align-items-center gap-2">
|
||||
<i class="pi pi-cog text-purple-500"></i>
|
||||
<span>Configuration</span>
|
||||
</h4>
|
||||
|
||||
<!-- Realm -->
|
||||
<div class="field mb-3">
|
||||
<label for="realm" class="block text-900 font-medium mb-2">
|
||||
Realm Keycloak
|
||||
</label>
|
||||
<p:selectOneMenu id="realm"
|
||||
value="#{userCreationBean.realmName}"
|
||||
styleClass="w-full">
|
||||
<f:selectItems value="#{userCreationBean.availableRealms}"
|
||||
var="realm"
|
||||
itemLabel="#{realm}"
|
||||
itemValue="#{realm}" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
|
||||
<!-- Options de configuration -->
|
||||
<div class="flex flex-column gap-2">
|
||||
<!-- Compte activé -->
|
||||
<div class="field-checkbox mb-0">
|
||||
<p:selectBooleanCheckbox id="enabled"
|
||||
value="#{userCreationBean.newUser.enabled}">
|
||||
</p:selectBooleanCheckbox>
|
||||
<label for="enabled" class="ml-2">Compte activé</label>
|
||||
</div>
|
||||
|
||||
<!-- Email vérifié -->
|
||||
<div class="field-checkbox mb-0">
|
||||
<p:selectBooleanCheckbox id="emailVerified"
|
||||
value="#{userCreationBean.newUser.emailVerified}">
|
||||
</p:selectBooleanCheckbox>
|
||||
<label for="emailVerified" class="ml-2">Email vérifié</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
ACTIONS
|
||||
================================================================ -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h3 class="text-900 font-semibold text-lg mb-4 flex align-items-center gap-2">
|
||||
<i class="pi pi-check-circle text-green-500"></i>
|
||||
Actions
|
||||
</h3>
|
||||
|
||||
<div class="flex flex-wrap gap-2 align-items-center">
|
||||
<!-- Bouton Créer -->
|
||||
<p:commandButton value="Créer l'utilisateur"
|
||||
icon="pi pi-check"
|
||||
styleClass="p-button-success"
|
||||
action="#{userCreationBean.createUser}"
|
||||
update=":formUserCreation"
|
||||
validateClient="true">
|
||||
</p:commandButton>
|
||||
|
||||
<!-- Bouton Réinitialiser -->
|
||||
<p:commandButton value="Réinitialiser"
|
||||
icon="pi pi-refresh"
|
||||
styleClass="p-button-outlined p-button-secondary"
|
||||
action="#{userCreationBean.resetForm}"
|
||||
update=":formUserCreation"
|
||||
immediate="true">
|
||||
<p:confirm header="Confirmation"
|
||||
message="Voulez-vous vraiment réinitialiser le formulaire ?"
|
||||
icon="pi pi-exclamation-triangle" />
|
||||
</p:commandButton>
|
||||
|
||||
<!-- Bouton Annuler -->
|
||||
<p:commandButton value="Annuler"
|
||||
icon="pi pi-times"
|
||||
styleClass="p-button-outlined"
|
||||
action="#{userCreationBean.cancel}"
|
||||
immediate="true">
|
||||
<p:confirm header="Confirmation"
|
||||
message="Voulez-vous vraiment annuler la création ?"
|
||||
icon="pi pi-exclamation-triangle" />
|
||||
</p:commandButton>
|
||||
|
||||
<div class="flex-grow-1"></div>
|
||||
|
||||
<!-- Aide -->
|
||||
<p:commandButton value="Aide"
|
||||
icon="pi pi-question-circle"
|
||||
styleClass="p-button-outlined p-button-help"
|
||||
type="button"
|
||||
onclick="PF('helpDialog').show();">
|
||||
</p:commandButton>
|
||||
</div>
|
||||
|
||||
<!-- Message d'information -->
|
||||
<p:messages id="messages" showDetail="true" closable="true" styleClass="mt-3">
|
||||
<p:autoUpdate />
|
||||
</p:messages>
|
||||
</div>
|
||||
</div>
|
||||
</h:form>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
DIALOG DE CONFIRMATION
|
||||
================================================================ -->
|
||||
<p:confirmDialog global="true" showEffect="fade" hideEffect="fade"
|
||||
responsive="true" width="400">
|
||||
<p:commandButton value="Non" type="button"
|
||||
styleClass="p-button-text"
|
||||
icon="pi pi-times" />
|
||||
<p:commandButton value="Oui" type="button"
|
||||
styleClass="p-button-primary"
|
||||
icon="pi pi-check" />
|
||||
</p:confirmDialog>
|
||||
|
||||
<!-- ================================================================
|
||||
DIALOG D'AIDE
|
||||
================================================================ -->
|
||||
<p:dialog header="Aide - Création d'Utilisateur"
|
||||
widgetVar="helpDialog"
|
||||
modal="true"
|
||||
responsive="true"
|
||||
width="600"
|
||||
showEffect="fade"
|
||||
hideEffect="fade">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<h4 class="text-900 font-semibold flex align-items-center gap-2 mb-3">
|
||||
<i class="pi pi-info-circle text-blue-500"></i>
|
||||
Informations Requises
|
||||
</h4>
|
||||
<ul class="text-700 line-height-3 mb-4">
|
||||
<li><strong>Nom d'utilisateur</strong> : Identifiant unique (3-50 caractères)</li>
|
||||
<li><strong>Email</strong> : Adresse email valide</li>
|
||||
<li><strong>Mot de passe</strong> : Au moins 8 caractères</li>
|
||||
</ul>
|
||||
|
||||
<h4 class="text-900 font-semibold flex align-items-center gap-2 mb-3">
|
||||
<i class="pi pi-shield text-purple-500"></i>
|
||||
Sécurité
|
||||
</h4>
|
||||
<ul class="text-700 line-height-3 mb-4">
|
||||
<li>Le mot de passe doit contenir au moins 8 caractères</li>
|
||||
<li>Utilisez une combinaison de lettres, chiffres et caractères spéciaux</li>
|
||||
<li>L'utilisateur pourra modifier son mot de passe après la première connexion</li>
|
||||
</ul>
|
||||
|
||||
<h4 class="text-900 font-semibold flex align-items-center gap-2 mb-3">
|
||||
<i class="pi pi-cog text-orange-500"></i>
|
||||
Configuration
|
||||
</h4>
|
||||
<ul class="text-700 line-height-3 mb-0">
|
||||
<li><strong>Compte activé</strong> : L'utilisateur peut se connecter immédiatement</li>
|
||||
<li><strong>Email vérifié</strong> : L'email est considéré comme vérifié</li>
|
||||
<li><strong>Realm</strong> : Espace d'administration Keycloak</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<f:facet name="footer">
|
||||
<p:commandButton value="Fermer"
|
||||
icon="pi pi-times"
|
||||
styleClass="p-button-text"
|
||||
onclick="PF('helpDialog').hide();"
|
||||
type="button" />
|
||||
</f:facet>
|
||||
</p:dialog>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<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"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:param name="page" value="#{userProfilBean}"/>
|
||||
<ui:define name="title">Modifier Utilisateur - Lions User Manager</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<!-- En-tête -->
|
||||
<ui:include src="/templates/components/layout/page-header.xhtml">
|
||||
<ui:param name="icon" value="pi pi-pencil text-warning-500" />
|
||||
<ui:param name="title" value="Modifier Utilisateur" />
|
||||
<ui:param name="description" value="Modifier les informations de l'utilisateur" />
|
||||
</ui:include>
|
||||
|
||||
<!-- Formulaire d'édition -->
|
||||
<div class="card">
|
||||
<ui:include src="/templates/components/user-management/user-form.xhtml">
|
||||
<ui:param name="user" value="#{userProfilBean.user}" />
|
||||
<ui:param name="mode" value="edit" />
|
||||
<ui:param name="showPasswordFields" value="false" />
|
||||
<ui:param name="submitAction" value="#{userProfilBean.updateUser}" />
|
||||
<ui:param name="cancelOutcome" value="/pages/user-manager/users/list" />
|
||||
</ui:include>
|
||||
</div>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,429 @@
|
||||
<!DOCTYPE html>
|
||||
<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"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:param name="page" value="#{userListBean}"/>
|
||||
<ui:define name="title">Liste des Utilisateurs - Lions User Manager</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<h:form id="formUserList">
|
||||
<div class="grid">
|
||||
<!-- ================================================================
|
||||
EN-TÊTE DE LA PAGE
|
||||
================================================================ -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-users text-blue-500" style="font-size: 2rem"></i>
|
||||
<div>
|
||||
<h3 class="m-0 mb-1">Gestion des Utilisateurs</h3>
|
||||
<p class="text-600 m-0">Gestion centralisée des utilisateurs Keycloak - Recherche, création, modification et suppression</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<p:commandButton
|
||||
value="Rafraîchir"
|
||||
icon="pi pi-refresh"
|
||||
styleClass="p-button-secondary"
|
||||
action="#{userListBean.refreshData}"
|
||||
update=":formUserList"
|
||||
process="@this" />
|
||||
<p:commandButton
|
||||
value="Nouvel Utilisateur"
|
||||
icon="pi pi-user-plus"
|
||||
styleClass="p-button-success"
|
||||
outcome="/pages/user-manager/users/create" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
STATISTIQUES KPI (4 CARTES)
|
||||
================================================================ -->
|
||||
<div class="col-12">
|
||||
<h5 class="mb-3">Statistiques des Utilisateurs</h5>
|
||||
</div>
|
||||
|
||||
<!-- KPI 1: Total Utilisateurs -->
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<div class="card surface-0 border-round-lg">
|
||||
<div class="flex align-items-start justify-content-between mb-3">
|
||||
<div>
|
||||
<div class="text-500 font-medium mb-1">Total Utilisateurs</div>
|
||||
<div class="text-900 font-bold text-2xl">#{userListBean.totalRecords}</div>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center bg-blue-100 border-circle"
|
||||
style="width: 2.5rem; height: 2.5rem">
|
||||
<i class="pi pi-users text-blue-600 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-500 text-sm">
|
||||
<i class="pi pi-database text-600"></i>
|
||||
<span class="ml-2">Utilisateurs dans le realm</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- KPI 2: Utilisateurs Actifs -->
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<div class="card surface-0 border-round-lg">
|
||||
<div class="flex align-items-start justify-content-between mb-3">
|
||||
<div>
|
||||
<div class="text-500 font-medium mb-1">Utilisateurs Actifs</div>
|
||||
<div class="text-900 font-bold text-2xl">#{userListBean.activeUsersCount}</div>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center bg-green-100 border-circle"
|
||||
style="width: 2.5rem; height: 2.5rem">
|
||||
<i class="pi pi-check-circle text-green-600 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex align-items-center gap-2">
|
||||
<span class="text-green-600 font-semibold">
|
||||
<i class="pi pi-arrow-up text-xs"></i>
|
||||
#{userListBean.activeUsersPercentage}%
|
||||
</span>
|
||||
<span class="text-500 text-sm">Taux d'activation</span>
|
||||
</div>
|
||||
<p:progressBar value="#{userListBean.activeUsersPercentage}"
|
||||
styleClass="mt-2"
|
||||
style="height: 4px"
|
||||
showValue="false" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- KPI 3: Utilisateurs Désactivés -->
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<div class="card surface-0 border-round-lg">
|
||||
<div class="flex align-items-start justify-content-between mb-3">
|
||||
<div>
|
||||
<div class="text-500 font-medium mb-1">Utilisateurs Désactivés</div>
|
||||
<div class="text-900 font-bold text-2xl">#{userListBean.disabledUsersCount}</div>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center bg-red-100 border-circle"
|
||||
style="width: 2.5rem; height: 2.5rem">
|
||||
<i class="pi pi-times-circle text-red-600 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex align-items-center gap-2">
|
||||
<span class="text-red-600 font-semibold">
|
||||
<i class="pi pi-arrow-down text-xs"></i>
|
||||
#{userListBean.disabledUsersPercentage}%
|
||||
</span>
|
||||
<span class="text-500 text-sm">Taux de désactivation</span>
|
||||
</div>
|
||||
<p:progressBar value="#{userListBean.disabledUsersPercentage}"
|
||||
styleClass="mt-2"
|
||||
style="height: 4px"
|
||||
showValue="false" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- KPI 4: Realm Actuel -->
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<div class="card surface-0 border-round-lg">
|
||||
<div class="flex align-items-start justify-content-between mb-3">
|
||||
<div style="max-width: 150px;">
|
||||
<div class="text-500 font-medium mb-1">Realm Actuel</div>
|
||||
<div class="text-900 font-bold text-xl" style="word-break: break-word;">#{userListBean.realmName}</div>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center bg-purple-100 border-circle"
|
||||
style="width: 2.5rem; height: 2.5rem">
|
||||
<i class="pi pi-globe text-purple-600 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-500 text-sm">
|
||||
<i class="pi pi-server text-600"></i>
|
||||
<span class="ml-2">Realm Keycloak</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
SECTION RECHERCHE ET FILTRES
|
||||
================================================================ -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex align-items-center gap-2 mb-3">
|
||||
<i class="pi pi-search text-blue-500" style="font-size: 1.5rem"></i>
|
||||
<h5 class="m-0">Recherche et Filtres</h5>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<label for="searchText" class="block text-900 font-medium mb-2">Recherche</label>
|
||||
<p:inputText id="searchText"
|
||||
value="#{userListBean.searchText}"
|
||||
placeholder="Nom, email..."
|
||||
styleClass="w-full">
|
||||
<p:ajax event="keyup"
|
||||
delay="500"
|
||||
update=":formUserList:userTable"
|
||||
listener="#{userListBean.search}" />
|
||||
</p:inputText>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<label for="realmSelect" class="block text-900 font-medium mb-2">Realm</label>
|
||||
<p:selectOneMenu id="realmSelect"
|
||||
value="#{userListBean.realmName}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemLabel="lions-user-manager" itemValue="lions-user-manager" />
|
||||
<f:selectItem itemLabel="master" itemValue="master" />
|
||||
<p:ajax update=":formUserList:userTable"
|
||||
listener="#{userListBean.search}" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<label for="statusSelect" class="block text-900 font-medium mb-2">Statut</label>
|
||||
<p:selectOneMenu id="statusSelect"
|
||||
value="#{userListBean.selectedStatut}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemLabel="Tous" itemValue="#{null}" />
|
||||
<f:selectItems value="#{userListBean.statutOptions}" />
|
||||
<p:ajax update=":formUserList:userTable"
|
||||
listener="#{userListBean.search}" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
|
||||
<div class="col-12 lg:col-2 flex align-items-end">
|
||||
<p:commandButton
|
||||
value="Réinitialiser"
|
||||
icon="pi pi-refresh"
|
||||
styleClass="p-button-secondary w-full"
|
||||
action="#{userListBean.resetSearch}"
|
||||
update=":formUserList:userTable @form" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
TABLEAU DES UTILISATEURS
|
||||
================================================================ -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex align-items-center justify-content-between mb-3">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-list text-blue-500" style="font-size: 1.5rem"></i>
|
||||
<h5 class="m-0">Liste des Utilisateurs</h5>
|
||||
</div>
|
||||
<p:tag value="#{userListBean.totalRecords} utilisateur(s)"
|
||||
severity="info"
|
||||
icon="pi pi-users" />
|
||||
</div>
|
||||
|
||||
<p:dataTable
|
||||
id="userTable"
|
||||
value="#{userListBean.users}"
|
||||
var="user"
|
||||
rowKey="#{user.id}"
|
||||
paginator="true"
|
||||
rows="#{userListBean.pageSize}"
|
||||
rowsPerPageTemplate="10,20,50"
|
||||
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
|
||||
currentPageReportTemplate="Affichage {startRecord}-{endRecord} sur {totalRecords}"
|
||||
styleClass="w-full"
|
||||
emptyMessage="Aucun utilisateur trouvé"
|
||||
reflow="true">
|
||||
|
||||
<p:ajax event="page" listener="#{userListBean.onPageChange}" update=":formUserList:userTable" />
|
||||
|
||||
<!-- Colonne Avatar + Username -->
|
||||
<p:column headerText="Utilisateur" sortBy="#{user.username}" style="width: 250px">
|
||||
<div class="flex align-items-center gap-3">
|
||||
<div class="border-circle bg-primary text-white flex align-items-center justify-content-center"
|
||||
style="width: 42px; height: 42px; flex-shrink: 0;">
|
||||
<span class="font-bold">
|
||||
#{user.prenom != null ? user.prenom.substring(0,1).toUpperCase() : 'U'}#{user.nom != null ? user.nom.substring(0,1).toUpperCase() : 'U'}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-column">
|
||||
<span class="font-semibold text-900">#{user.username}</span>
|
||||
<span class="text-600 text-sm">#{user.prenom} #{user.nom}</span>
|
||||
</div>
|
||||
</div>
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne Email -->
|
||||
<p:column headerText="Email" sortBy="#{user.email}" style="width: 250px">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-envelope text-500"></i>
|
||||
<span class="text-900">#{user.email}</span>
|
||||
<p:outputPanel rendered="#{user.emailVerified}">
|
||||
<i class="pi pi-check-circle text-green-500" title="Email vérifié"></i>
|
||||
</p:outputPanel>
|
||||
</div>
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne Statut -->
|
||||
<p:column headerText="Statut" sortBy="#{user.enabled}" style="width: 120px; text-align: center">
|
||||
<p:tag value="#{user.enabled ? 'ACTIF' : 'INACTIF'}"
|
||||
severity="#{user.enabled ? 'success' : 'danger'}" />
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne Rôles -->
|
||||
<p:column headerText="Rôles" style="width: 250px">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<h:outputText value="Aucun rôle" styleClass="text-500 text-sm"
|
||||
rendered="#{user.realmRoles == null or user.realmRoles.size() == 0}" />
|
||||
|
||||
<ui:fragment rendered="#{user.realmRoles != null and user.realmRoles.size() > 0}">
|
||||
<ui:repeat value="#{user.realmRoles}" var="role" varStatus="status">
|
||||
<p:tag value="#{role}" severity="info"
|
||||
rendered="#{status.index lt 3}"
|
||||
styleClass="mr-1" />
|
||||
</ui:repeat>
|
||||
<p:tag value="+#{user.realmRoles.size() - 3}" severity="secondary"
|
||||
rendered="#{user.realmRoles.size() > 3}" />
|
||||
</ui:fragment>
|
||||
</div>
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne Actions -->
|
||||
<p:column headerText="Actions" style="width: 180px; text-align: center">
|
||||
<div class="flex gap-1 justify-content-center">
|
||||
<!-- Bouton Modifier -->
|
||||
<p:commandButton icon="pi pi-pencil"
|
||||
styleClass="p-button-rounded p-button-text p-button-sm"
|
||||
title="Modifier"
|
||||
outcome="/pages/user-manager/users/edit">
|
||||
<f:param name="userId" value="#{user.id}" />
|
||||
</p:commandButton>
|
||||
|
||||
<!-- Bouton Désactiver (si actif) -->
|
||||
<p:commandButton icon="pi pi-ban"
|
||||
styleClass="p-button-rounded p-button-text p-button-sm p-button-warning"
|
||||
title="Désactiver"
|
||||
action="#{userListBean.deactivateUser(user.id)}"
|
||||
update=":formUserList"
|
||||
rendered="#{user.enabled}">
|
||||
<p:confirm header="Confirmation"
|
||||
message="Voulez-vous vraiment désactiver cet utilisateur ?"
|
||||
icon="pi pi-exclamation-triangle" />
|
||||
</p:commandButton>
|
||||
|
||||
<!-- Bouton Activer (si inactif) -->
|
||||
<p:commandButton icon="pi pi-check"
|
||||
styleClass="p-button-rounded p-button-text p-button-sm p-button-success"
|
||||
title="Activer"
|
||||
action="#{userListBean.activateUser(user.id)}"
|
||||
update=":formUserList"
|
||||
rendered="#{not user.enabled}" />
|
||||
|
||||
<!-- Bouton Supprimer -->
|
||||
<p:commandButton icon="pi pi-trash"
|
||||
styleClass="p-button-rounded p-button-text p-button-sm p-button-danger"
|
||||
title="Supprimer"
|
||||
action="#{userListBean.deleteUser(user.id)}"
|
||||
update=":formUserList">
|
||||
<p:confirm header="Confirmation"
|
||||
message="Voulez-vous vraiment supprimer cet utilisateur ?"
|
||||
icon="pi pi-exclamation-triangle" />
|
||||
</p:commandButton>
|
||||
</div>
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
|
||||
<p:confirmDialog global="true" showEffect="fade" hideEffect="fade"
|
||||
responsive="true" width="350">
|
||||
<p:commandButton value="Non" type="button"
|
||||
styleClass="p-button-text"
|
||||
onclick="PF('confirmDialog').hide()" />
|
||||
<p:commandButton value="Oui" type="button"
|
||||
styleClass="p-button-primary"
|
||||
onclick="PF('confirmDialog').hide()" />
|
||||
</p:confirmDialog>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
ACTIONS RAPIDES
|
||||
================================================================ -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex align-items-center gap-2 mb-3">
|
||||
<i class="pi pi-bolt text-orange-500" style="font-size: 1.5rem"></i>
|
||||
<h5 class="m-0">Actions Rapides</h5>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<p:commandButton
|
||||
value="Créer un Utilisateur"
|
||||
icon="pi pi-user-plus"
|
||||
styleClass="w-full p-button-success"
|
||||
outcome="/pages/user-manager/users/create" />
|
||||
</div>
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<p:commandButton
|
||||
value="Exporter la Liste"
|
||||
icon="pi pi-download"
|
||||
styleClass="w-full p-button-secondary"
|
||||
action="#{userListBean.exportToCSV}"
|
||||
ajax="false" />
|
||||
</div>
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<p:commandButton
|
||||
value="Importer des Utilisateurs"
|
||||
icon="pi pi-upload"
|
||||
styleClass="w-full p-button-info"
|
||||
onclick="PF('importUsersDialog').show()"
|
||||
type="button" />
|
||||
</div>
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<p:commandButton
|
||||
value="Gestion des Rôles"
|
||||
icon="pi pi-shield"
|
||||
styleClass="w-full p-button-primary"
|
||||
outcome="/pages/user-manager/roles/list" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</h:form>
|
||||
|
||||
<!-- ================================================================
|
||||
DIALOG D'IMPORT
|
||||
================================================================ -->
|
||||
<p:dialog id="importUsersDialog"
|
||||
widgetVar="importUsersDialog"
|
||||
header="Importer des Utilisateurs"
|
||||
modal="true"
|
||||
resizable="false"
|
||||
styleClass="w-full md:w-30rem">
|
||||
<h:form id="formImportUsers">
|
||||
<div class="flex flex-column gap-3">
|
||||
<p class="text-600">
|
||||
Importez des utilisateurs depuis un fichier CSV ou JSON.
|
||||
</p>
|
||||
<p:fileUpload mode="simple"
|
||||
skinSimple="true"
|
||||
accept=".csv,.json"
|
||||
label="Sélectionner un fichier" />
|
||||
<div class="flex justify-content-end gap-2 mt-3">
|
||||
<p:commandButton value="Annuler"
|
||||
icon="pi pi-times"
|
||||
styleClass="p-button-secondary"
|
||||
onclick="PF('importUsersDialog').hide()"
|
||||
type="button" />
|
||||
<p:commandButton value="Importer"
|
||||
icon="pi pi-upload"
|
||||
styleClass="p-button-success"
|
||||
action="#{userListBean.importUsers}"
|
||||
update=":formUserList"
|
||||
oncomplete="PF('importUsersDialog').hide()" />
|
||||
</div>
|
||||
</div>
|
||||
</h:form>
|
||||
</p:dialog>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,421 @@
|
||||
<!DOCTYPE html>
|
||||
<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"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:param name="page" value="#{userSessionBean}"/>
|
||||
<ui:define name="title">Mon Profil - Lions User Manager</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<!-- ================================================================
|
||||
EN-TÊTE DE LA PAGE
|
||||
================================================================ -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<h2 class="text-900 font-semibold text-xl m-0">
|
||||
<i class="pi pi-user text-blue-500 mr-2"></i>
|
||||
Mon Profil
|
||||
</h2>
|
||||
<h:link outcome="/pages/user-manager/dashboard" styleClass="p-button p-button-text">
|
||||
<i class="pi pi-arrow-left mr-2"></i>
|
||||
Retour au tableau de bord
|
||||
</h:link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
CARTE PROFIL PRINCIPAL
|
||||
================================================================ -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="grid">
|
||||
<!-- Photo de profil et informations principales -->
|
||||
<div class="col-12 md:col-4">
|
||||
<div class="text-center mb-4">
|
||||
<!-- Avatar avec gradient -->
|
||||
<div style="width: 140px; height: 140px; border-radius: 50%; background: linear-gradient(135deg, var(--primary-color), var(--primary-600, #387FE9)); display: flex; align-items: center; justify-content: center; margin: 0 auto 1.5rem auto; font-size: 3.5rem; font-weight: bold; color: white; box-shadow: 0 8px 24px rgba(0,0,0,0.12);">
|
||||
#{userSessionBean.initials}
|
||||
</div>
|
||||
|
||||
<!-- Nom complet -->
|
||||
<h3 class="text-900 font-semibold text-2xl mb-2">#{userSessionBean.fullName}</h3>
|
||||
|
||||
<!-- Email -->
|
||||
<p class="text-600 mb-3 flex align-items-center justify-content-center gap-2">
|
||||
<i class="pi pi-envelope"></i>
|
||||
#{userSessionBean.email}
|
||||
</p>
|
||||
|
||||
<!-- Badge de statut -->
|
||||
<div class="inline-flex align-items-center justify-content-center gap-2">
|
||||
<span class="inline-flex align-items-center gap-2 bg-green-100 text-green-700 px-3 py-2 border-round font-semibold" style="font-size: 1rem;">
|
||||
<i class="pi pi-circle-fill" style="font-size: 0.5rem; animation: pulse 2s ease-in-out infinite;"></i>
|
||||
<span>Connecté</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Badge du rôle principal -->
|
||||
<div class="mt-3 flex justify-content-center">
|
||||
<span class="inline-flex align-items-center bg-blue-100 text-blue-700 px-3 py-1 border-round font-semibold text-sm" style="text-transform: uppercase; letter-spacing: 0.5px;">
|
||||
#{userSessionBean.primaryRole}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Informations détaillées -->
|
||||
<div class="col-12 md:col-8">
|
||||
<div class="grid">
|
||||
<!-- Colonne gauche: Informations personnelles -->
|
||||
<div class="col-12 md:col-6">
|
||||
<h4 class="text-900 font-semibold text-lg mb-3 flex align-items-center gap-2">
|
||||
<i class="pi pi-user text-blue-500"></i>
|
||||
Informations Personnelles
|
||||
</h4>
|
||||
|
||||
<div class="mb-3 pb-3 border-bottom-1 surface-border">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Nom d'utilisateur</label>
|
||||
<p class="text-900 font-semibold m-0">#{userSessionBean.username}</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 pb-3 border-bottom-1 surface-border">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Nom complet</label>
|
||||
<p class="text-900 font-semibold m-0">#{userSessionBean.fullName}</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 pb-3 border-bottom-1 surface-border">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Adresse email</label>
|
||||
<div class="flex align-items-center gap-2">
|
||||
<p class="text-900 font-semibold m-0">#{userSessionBean.email}</p>
|
||||
<i class="pi pi-check-circle text-green-500" title="Email vérifié"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Prénom</label>
|
||||
<p class="text-900 font-semibold m-0">#{userSessionBean.firstName}</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Nom</label>
|
||||
<p class="text-900 font-semibold m-0">#{userSessionBean.lastName}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Colonne droite: Rôles et permissions -->
|
||||
<div class="col-12 md:col-6">
|
||||
<h4 class="text-900 font-semibold text-lg mb-3 flex align-items-center gap-2">
|
||||
<i class="pi pi-shield text-purple-500"></i>
|
||||
Rôles et Permissions
|
||||
</h4>
|
||||
|
||||
<div class="mb-3 pb-3 border-bottom-1 surface-border">
|
||||
<label class="block text-600 font-medium mb-2 text-sm">Rôles assignés</label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<ui:repeat value="#{userSessionBean.roles}" var="role">
|
||||
<p:badge value="#{role}" severity="info" styleClass="text-sm"></p:badge>
|
||||
</ui:repeat>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 pb-3 border-bottom-1 surface-border">
|
||||
<label class="block text-600 font-medium mb-2 text-sm">Rôle principal</label>
|
||||
<div class="flex align-items-center">
|
||||
<p:badge value="#{userSessionBean.primaryRole}"
|
||||
severity="success"
|
||||
styleClass="text-sm">
|
||||
</p:badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 pb-3 border-bottom-1 surface-border">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Niveau d'accès</label>
|
||||
<p class="text-900 font-semibold m-0">
|
||||
<h:outputText value="Administrateur système" rendered="#{userSessionBean.hasRole('admin')}" />
|
||||
<h:outputText value="Gestionnaire utilisateurs" rendered="#{userSessionBean.hasRole('user_manager') and not userSessionBean.hasRole('admin')}" />
|
||||
<h:outputText value="Consultation utilisateurs" rendered="#{userSessionBean.hasRole('user_viewer') and not userSessionBean.hasRole('user_manager') and not userSessionBean.hasRole('admin')}" />
|
||||
<h:outputText value="Auditeur" rendered="#{userSessionBean.hasRole('auditor') and not userSessionBean.hasRole('user_viewer') and not userSessionBean.hasRole('user_manager') and not userSessionBean.hasRole('admin')}" />
|
||||
<h:outputText value="Utilisateur standard" rendered="#{not userSessionBean.hasRole('admin') and not userSessionBean.hasRole('user_manager') and not userSessionBean.hasRole('user_viewer') and not userSessionBean.hasRole('auditor')}" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="block text-600 font-medium mb-2 text-sm">Statut du compte</label>
|
||||
<div class="flex align-items-center">
|
||||
<p:badge value="Actif" severity="success" styleClass="text-sm"></p:badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
INFORMATIONS DE SESSION OIDC
|
||||
================================================================ -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h3 class="text-900 font-semibold text-lg mb-4 flex align-items-center gap-2">
|
||||
<i class="pi pi-shield text-orange-500"></i>
|
||||
Informations de Session OIDC
|
||||
</h3>
|
||||
|
||||
<div class="grid">
|
||||
<!-- Colonne gauche: Token Information -->
|
||||
<div class="col-12 md:col-6">
|
||||
<h4 class="text-900 font-semibold mb-3">Informations du Token</h4>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Issuer (Émetteur)</label>
|
||||
<p class="text-700 m-0 text-sm font-mono bg-bluegray-50 p-2 border-round">
|
||||
#{userSessionBean.issuer}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Subject (Identifiant)</label>
|
||||
<p class="text-700 m-0 text-sm font-mono bg-bluegray-50 p-2 border-round">
|
||||
#{userSessionBean.subject}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Audience</label>
|
||||
<p class="text-700 m-0 text-sm font-mono bg-bluegray-50 p-2 border-round">
|
||||
account
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="block text-600 font-medium mb-2 text-sm">Token Type</label>
|
||||
<div class="flex align-items-center">
|
||||
<p:badge value="Bearer" severity="info" styleClass="text-sm"></p:badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Colonne droite: Session Details -->
|
||||
<div class="col-12 md:col-6">
|
||||
<h4 class="text-900 font-semibold mb-3">Détails de la Session</h4>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Expiration du token</label>
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-calendar text-orange-500"></i>
|
||||
<p class="text-700 m-0 text-sm">
|
||||
<h:outputText value="#{userSessionBean.expirationTime}">
|
||||
<f:convertDateTime pattern="dd/MM/yyyy à HH:mm:ss" timeZone="Europe/Paris" type="both"/>
|
||||
</h:outputText>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Émis le</label>
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-clock text-blue-500"></i>
|
||||
<p class="text-700 m-0 text-sm">
|
||||
<h:outputText value="#{userSessionBean.issuedAt}">
|
||||
<f:convertDateTime pattern="dd/MM/yyyy à HH:mm:ss" timeZone="Europe/Paris" type="both"/>
|
||||
</h:outputText>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Realm Keycloak</label>
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-globe text-purple-500"></i>
|
||||
<p class="text-700 m-0 text-sm font-semibold">
|
||||
lions-user-manager
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="block text-600 font-medium mb-1 text-sm">Durée de validité</label>
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-hourglass text-green-500"></i>
|
||||
<p class="text-700 m-0 text-sm">
|
||||
Session active
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
STATISTIQUES D'ACTIVITÉ
|
||||
================================================================ -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h3 class="text-900 font-semibold text-lg mb-4 flex align-items-center gap-2">
|
||||
<i class="pi pi-chart-line text-green-500"></i>
|
||||
Statistiques d'Activité
|
||||
</h3>
|
||||
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<div class="surface-50 border-round p-3">
|
||||
<div class="flex align-items-center justify-content-between mb-2">
|
||||
<span class="text-600 font-medium text-sm">Connexions</span>
|
||||
<i class="pi pi-sign-in text-blue-500"></i>
|
||||
</div>
|
||||
<p class="text-900 font-bold text-2xl m-0">--</p>
|
||||
<small class="text-500">Total des connexions</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<div class="surface-50 border-round p-3">
|
||||
<div class="flex align-items-center justify-content-between mb-2">
|
||||
<span class="text-600 font-medium text-sm">Dernière connexion</span>
|
||||
<i class="pi pi-clock text-green-500"></i>
|
||||
</div>
|
||||
<p class="text-900 font-bold text-xl m-0">Aujourd'hui</p>
|
||||
<small class="text-500">Session en cours</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<div class="surface-50 border-round p-3">
|
||||
<div class="flex align-items-center justify-content-between mb-2">
|
||||
<span class="text-600 font-medium text-sm">Actions</span>
|
||||
<i class="pi pi-history text-orange-500"></i>
|
||||
</div>
|
||||
<p class="text-900 font-bold text-2xl m-0">--</p>
|
||||
<small class="text-500">Actions effectuées</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<div class="surface-50 border-round p-3">
|
||||
<div class="flex align-items-center justify-content-between mb-2">
|
||||
<span class="text-600 font-medium text-sm">Sessions</span>
|
||||
<i class="pi pi-desktop text-purple-500"></i>
|
||||
</div>
|
||||
<p class="text-900 font-bold text-2xl m-0">1</p>
|
||||
<small class="text-500">Session active</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
ACTIONS
|
||||
================================================================ -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h3 class="text-900 font-semibold text-lg mb-4 flex align-items-center gap-2">
|
||||
<i class="pi pi-cog text-gray-500"></i>
|
||||
Actions Rapides
|
||||
</h3>
|
||||
|
||||
<h:form id="formProfileActions">
|
||||
<div class="grid">
|
||||
<!-- Gestion du Profil -->
|
||||
<div class="col-12 md:col-6">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<h4 class="text-900 font-semibold mb-3 flex align-items-center gap-2">
|
||||
<i class="pi pi-user text-blue-500"></i>
|
||||
<span>Gestion du Profil</span>
|
||||
</h4>
|
||||
<div class="flex flex-column gap-2">
|
||||
<p:commandButton value="Modifier mon profil"
|
||||
icon="pi pi-pencil"
|
||||
styleClass="p-button-outlined w-full justify-content-start"
|
||||
disabled="true">
|
||||
<f:attribute name="data-tooltip" value="Fonctionnalité gérée par Keycloak"/>
|
||||
</p:commandButton>
|
||||
|
||||
<p:commandButton value="Changer mon mot de passe"
|
||||
icon="pi pi-key"
|
||||
styleClass="p-button-outlined w-full justify-content-start"
|
||||
disabled="true">
|
||||
<f:attribute name="data-tooltip" value="Utilisez le portail Keycloak"/>
|
||||
</p:commandButton>
|
||||
|
||||
<p:commandButton value="Paramètres de sécurité"
|
||||
icon="pi pi-shield"
|
||||
styleClass="p-button-outlined w-full justify-content-start"
|
||||
disabled="true">
|
||||
<f:attribute name="data-tooltip" value="Fonctionnalité à venir"/>
|
||||
</p:commandButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gestion des Sessions -->
|
||||
<div class="col-12 md:col-6">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<h4 class="text-900 font-semibold mb-3 flex align-items-center gap-2">
|
||||
<i class="pi pi-desktop text-purple-500"></i>
|
||||
<span>Sessions et Sécurité</span>
|
||||
</h4>
|
||||
<div class="flex flex-column gap-2">
|
||||
<p:commandButton value="Voir mes sessions actives"
|
||||
icon="pi pi-desktop"
|
||||
styleClass="p-button-outlined p-button-info w-full justify-content-start"
|
||||
disabled="true">
|
||||
<f:attribute name="data-tooltip" value="Fonctionnalité à venir"/>
|
||||
</p:commandButton>
|
||||
|
||||
<p:commandButton value="Historique des connexions"
|
||||
icon="pi pi-history"
|
||||
styleClass="p-button-outlined p-button-secondary w-full justify-content-start"
|
||||
disabled="true">
|
||||
<f:attribute name="data-tooltip" value="Fonctionnalité à venir"/>
|
||||
</p:commandButton>
|
||||
|
||||
<p:commandButton value="Se déconnecter"
|
||||
icon="pi pi-sign-out"
|
||||
styleClass="p-button-danger w-full justify-content-start"
|
||||
action="#{userSessionBean.logout}">
|
||||
<p:confirm header="Confirmation de déconnexion"
|
||||
message="Êtes-vous sûr de vouloir vous déconnecter ?"
|
||||
icon="pi pi-exclamation-triangle" />
|
||||
</p:commandButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================================================================
|
||||
DIALOG DE CONFIRMATION
|
||||
================================================================ -->
|
||||
<p:confirmDialog global="true" showEffect="fade" hideEffect="fade"
|
||||
responsive="true" width="400">
|
||||
<p:commandButton value="Non" type="button"
|
||||
styleClass="p-button-text"
|
||||
icon="pi pi-times" />
|
||||
<p:commandButton value="Oui" type="button"
|
||||
styleClass="p-button-danger"
|
||||
icon="pi pi-check" />
|
||||
</p:confirmDialog>
|
||||
|
||||
<!-- Animation CSS pour le badge "Connecté" -->
|
||||
<style>
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
</style>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,110 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
|
||||
|
||||
<composite:interface>
|
||||
<composite:attribute name="userId" type="java.lang.String" required="true"/>
|
||||
<composite:attribute name="userEnabled" type="java.lang.Boolean" required="false" default="true"/>
|
||||
<composite:attribute name="update" type="java.lang.String" required="false" default="@form"/>
|
||||
<composite:attribute name="showView" type="java.lang.Boolean" required="false" default="true"/>
|
||||
<composite:attribute name="showEdit" type="java.lang.Boolean" required="false" default="true"/>
|
||||
<composite:attribute name="showDelete" type="java.lang.Boolean" required="false" default="true"/>
|
||||
<composite:attribute name="showActivate" type="java.lang.Boolean" required="false" default="true"/>
|
||||
<composite:attribute name="showDeactivate" type="java.lang.Boolean" required="false" default="true"/>
|
||||
<composite:attribute name="showResetPassword" type="java.lang.Boolean" required="false" default="true"/>
|
||||
<composite:attribute name="viewPage" type="java.lang.String" required="false" default="/pages/user-manager/users/profile"/>
|
||||
<composite:attribute name="editPage" type="java.lang.String" required="false" default="/pages/user-manager/users/edit"/>
|
||||
<composite:attribute name="activateAction"
|
||||
method-signature="void activateAction(jakarta.faces.event.ActionEvent)"
|
||||
required="false"/>
|
||||
<composite:attribute name="deactivateAction"
|
||||
method-signature="void deactivateAction(jakarta.faces.event.ActionEvent)"
|
||||
required="false"/>
|
||||
<composite:attribute name="deleteAction"
|
||||
method-signature="void deleteAction(jakarta.faces.event.ActionEvent)"
|
||||
required="false"/>
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<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="#{cc.attrs.showView}">
|
||||
<p:menuitem
|
||||
value="Voir le profil"
|
||||
icon="pi pi-eye"
|
||||
outcome="#{cc.attrs.viewPage}">
|
||||
<f:param name="userId" value="#{cc.attrs.userId}" />
|
||||
</p:menuitem>
|
||||
</c:if>
|
||||
|
||||
<c:if test="#{cc.attrs.showEdit}">
|
||||
<p:menuitem
|
||||
value="Modifier"
|
||||
icon="pi pi-pencil"
|
||||
outcome="#{cc.attrs.editPage}">
|
||||
<f:param name="userId" value="#{cc.attrs.userId}" />
|
||||
</p:menuitem>
|
||||
</c:if>
|
||||
|
||||
<c:if test="#{cc.attrs.showResetPassword}">
|
||||
<p:menuitem
|
||||
value="Réinitialiser mot de passe"
|
||||
icon="pi pi-key"
|
||||
onclick="PF('resetPasswordDialog').show()" />
|
||||
</c:if>
|
||||
|
||||
<p:separator />
|
||||
|
||||
<c:if test="#{cc.attrs.showActivate and !cc.attrs.userEnabled}">
|
||||
<c:if test="#{not empty cc.attrs.activateAction}">
|
||||
<p:menuitem
|
||||
value="Activer"
|
||||
icon="pi pi-check"
|
||||
styleClass="text-green-600"
|
||||
actionListener="#{cc.attrs.activateAction}">
|
||||
<f:attribute name="userId" value="#{cc.attrs.userId}" />
|
||||
<p:ajax update="#{cc.attrs.update}" />
|
||||
</p:menuitem>
|
||||
</c:if>
|
||||
</c:if>
|
||||
|
||||
<c:if test="#{cc.attrs.showDeactivate and cc.attrs.userEnabled}">
|
||||
<c:if test="#{not empty cc.attrs.deactivateAction}">
|
||||
<p:menuitem
|
||||
value="Désactiver"
|
||||
icon="pi pi-times"
|
||||
styleClass="text-orange-600"
|
||||
actionListener="#{cc.attrs.deactivateAction}">
|
||||
<f:attribute name="userId" value="#{cc.attrs.userId}" />
|
||||
<p:ajax update="#{cc.attrs.update}" />
|
||||
</p:menuitem>
|
||||
</c:if>
|
||||
</c:if>
|
||||
|
||||
<c:if test="#{cc.attrs.showDelete}">
|
||||
<c:if test="#{not empty cc.attrs.deleteAction}">
|
||||
<p:separator />
|
||||
<p:menuitem
|
||||
value="Supprimer"
|
||||
icon="pi pi-trash"
|
||||
styleClass="text-red-600"
|
||||
actionListener="#{cc.attrs.deleteAction}">
|
||||
<f:attribute name="userId" value="#{cc.attrs.userId}" />
|
||||
<p:ajax update="#{cc.attrs.update}" />
|
||||
</p:menuitem>
|
||||
</c:if>
|
||||
</c:if>
|
||||
</p:menu>
|
||||
</p:commandButton>
|
||||
</composite:implementation>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,625 @@
|
||||
/* ============================================================================
|
||||
Lions User Manager - Enhanced Custom Topbar Styles
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 2.0.0
|
||||
Description: Styles améliorés pour la topbar avec intégration intelligente
|
||||
des patterns Freya layout pour un rendu parfait
|
||||
|
||||
Intégrations:
|
||||
- Freya Layout Variables & Patterns
|
||||
- Support Dark/Light Theme
|
||||
- Animations fluides (fadeInDown, modal-in)
|
||||
- PrimeFlex utility classes
|
||||
- Responsive design
|
||||
============================================================================ */
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
BASE TOPBAR LAYOUT OVERRIDES
|
||||
Améliore la structure de base de la topbar Freya
|
||||
---------------------------------------------------------------------------- */
|
||||
|
||||
.layout-topbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
width: 100%;
|
||||
height: 62px;
|
||||
transition: width 0.2s, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.layout-topbar .layout-topbar-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.layout-topbar .layout-topbar-wrapper .layout-topbar-right {
|
||||
height: 100%;
|
||||
flex-grow: 1;
|
||||
padding: 0 16px 0 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.layout-topbar .layout-topbar-wrapper .layout-topbar-right .layout-topbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-grow: 1;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.layout-topbar .layout-topbar-wrapper .layout-topbar-right .layout-topbar-actions > li {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
USER PROFILE LINK - Enhanced with Freya patterns
|
||||
---------------------------------------------------------------------------- */
|
||||
|
||||
.layout-topbar .user-profile-link {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s cubic-bezier(0.05, 0.74, 0.2, 0.99);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.layout-topbar .user-profile-link::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.layout-topbar .user-profile-link:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.layout-topbar .user-profile-link:hover {
|
||||
background-color: rgba(255, 255, 255, 0.12);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* User Avatar - Integration with Freya avatar patterns */
|
||||
.layout-topbar .user-profile-link .user-avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.layout-topbar .user-profile-link:hover .user-avatar {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* User Info Container */
|
||||
.layout-topbar .user-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
line-height: 1.2;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* User Name - Enhanced typography */
|
||||
.layout-topbar .user-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 0.125rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
/* User Email */
|
||||
.layout-topbar .user-email {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-color-secondary);
|
||||
opacity: 0.85;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
/* User Role Badge */
|
||||
.layout-topbar .user-role {
|
||||
font-size: 0.7rem;
|
||||
color: var(--primary-color);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.6px;
|
||||
background: rgba(var(--primary-color-rgb, 79, 142, 236), 0.1);
|
||||
padding: 0.125rem 0.375rem;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.layout-topbar .user-separator {
|
||||
color: var(--text-color-secondary);
|
||||
opacity: 0.5;
|
||||
font-weight: 300;
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
|
||||
/* Online Status Indicator */
|
||||
.layout-topbar .user-status {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.layout-topbar .user-status.online {
|
||||
background-color: #4CAF50;
|
||||
box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.3);
|
||||
animation: pulse-online 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-online {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 0 4px rgba(76, 175, 80, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
USER DROPDOWN MENU - Enhanced with Freya dropdown patterns
|
||||
---------------------------------------------------------------------------- */
|
||||
|
||||
.layout-topbar .user-dropdown-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 62px;
|
||||
right: 0;
|
||||
min-width: 280px;
|
||||
max-width: 320px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.08);
|
||||
border: 1px solid var(--surface-border);
|
||||
background: var(--surface-card);
|
||||
overflow: hidden;
|
||||
z-index: 1000;
|
||||
animation-duration: 0.2s;
|
||||
animation-timing-function: cubic-bezier(0.05, 0.74, 0.2, 0.99);
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
/* Show dropdown when parent is active */
|
||||
.layout-topbar .user-profile.active-topmenuitem > .user-dropdown-menu {
|
||||
display: block;
|
||||
animation-name: fadeInDown;
|
||||
}
|
||||
|
||||
/* Dropdown Header - Integration with Freya gradient patterns */
|
||||
.user-dropdown-header {
|
||||
padding: 1.25rem 1rem;
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--primary-600, #387FE9));
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.user-dropdown-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
|
||||
animation: shimmer 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0%, 100% { transform: translate(0, 0); }
|
||||
50% { transform: translate(-20%, -20%); }
|
||||
}
|
||||
|
||||
/* Dropdown Avatar */
|
||||
.user-dropdown-avatar {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.user-dropdown-avatar > div {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.user-status-indicator {
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 2px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
||||
.user-status-indicator.online {
|
||||
background-color: #4CAF50;
|
||||
animation: pulse-indicator 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-indicator {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.1); }
|
||||
}
|
||||
|
||||
/* Dropdown User Info */
|
||||
.user-dropdown-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.user-dropdown-name {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.25rem;
|
||||
color: white;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.user-dropdown-email {
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.95;
|
||||
margin-bottom: 0.25rem;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.user-dropdown-role {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 12px;
|
||||
display: inline-block;
|
||||
color: white;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
/* Dividers */
|
||||
.user-dropdown-divider {
|
||||
height: 1px;
|
||||
background-color: var(--surface-border);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Menu Sections */
|
||||
.user-dropdown-section {
|
||||
padding: 0.75rem 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--text-color-secondary);
|
||||
padding: 0 1rem 0.5rem 1rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.section-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Dropdown Items - Enhanced with Freya interaction patterns */
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 1rem;
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
transition: all 0.2s cubic-bezier(0.05, 0.74, 0.2, 0.99);
|
||||
border: none;
|
||||
background: none;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dropdown-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 3px;
|
||||
height: 100%;
|
||||
background: var(--primary-color);
|
||||
transform: scaleX(0);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.dropdown-item:hover::before {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background-color: var(--surface-hover);
|
||||
color: var(--primary-color);
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.dropdown-item:active {
|
||||
background-color: var(--surface-ground);
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.dropdown-item i {
|
||||
width: 1.25rem;
|
||||
text-align: center;
|
||||
color: var(--text-color-secondary);
|
||||
transition: all 0.2s ease;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.dropdown-item:hover i {
|
||||
color: var(--primary-color);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.dropdown-item span {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-arrow {
|
||||
margin-left: auto;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.dropdown-item:hover .item-arrow {
|
||||
opacity: 1;
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
/* Logout Item - Enhanced danger state */
|
||||
.logout-item {
|
||||
color: var(--red-500) !important;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.logout-item:hover {
|
||||
background-color: var(--red-50) !important;
|
||||
color: var(--red-600) !important;
|
||||
}
|
||||
|
||||
.logout-item i {
|
||||
color: var(--red-500) !important;
|
||||
}
|
||||
|
||||
.logout-item:hover i {
|
||||
color: var(--red-600) !important;
|
||||
transform: scale(1.1) rotate(-5deg);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
ANIMATIONS - Integration with Freya animation patterns
|
||||
---------------------------------------------------------------------------- */
|
||||
|
||||
@keyframes dropdownFadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px) scale(0.95);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.user-dropdown-menu {
|
||||
animation: dropdownFadeIn 0.3s ease-out;
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
DARK MODE SUPPORT - Integration with Freya dark theme
|
||||
---------------------------------------------------------------------------- */
|
||||
|
||||
.layout-wrapper.layout-topbar-dark .layout-topbar {
|
||||
background-color: #293241;
|
||||
box-shadow: 0 10px 40px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.layout-wrapper.layout-topbar-dark .user-profile-link:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.layout-wrapper.layout-topbar-dark .user-dropdown-menu {
|
||||
background: var(--surface-900, #1E1E1E);
|
||||
border-color: var(--surface-700, #383838);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.layout-wrapper.layout-topbar-dark .user-dropdown-divider {
|
||||
background-color: var(--surface-700, #383838);
|
||||
}
|
||||
|
||||
.layout-wrapper.layout-topbar-dark .section-title {
|
||||
color: var(--text-color-secondary);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.layout-wrapper.layout-topbar-dark .dropdown-item {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.layout-wrapper.layout-topbar-dark .dropdown-item:hover {
|
||||
background-color: var(--surface-800, #2A2A2A);
|
||||
}
|
||||
|
||||
.layout-wrapper.layout-topbar-dark .logout-item:hover {
|
||||
background-color: rgba(211, 47, 47, 0.1) !important;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
RESPONSIVE DESIGN - Integration with Freya responsive patterns
|
||||
---------------------------------------------------------------------------- */
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.layout-topbar .user-dropdown-menu {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
position: fixed;
|
||||
top: 62px;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.layout-topbar .user-dropdown-menu {
|
||||
min-width: 260px;
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
.user-dropdown-header {
|
||||
padding: 1rem 0.75rem;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
padding: 0.625rem 0.75rem;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
padding: 0 0.75rem 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
/* Hide user info on mobile */
|
||||
.layout-topbar .user-info {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-topbar .user-profile-link {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.layout-topbar .user-dropdown-menu {
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
ACCESSIBILITY ENHANCEMENTS
|
||||
---------------------------------------------------------------------------- */
|
||||
|
||||
.dropdown-item:focus,
|
||||
.user-profile-link:focus {
|
||||
outline: 2px solid var(--primary-color);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.layout-topbar .user-profile-link,
|
||||
.dropdown-item,
|
||||
.user-dropdown-menu,
|
||||
.user-avatar,
|
||||
.item-arrow {
|
||||
animation: none;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
UTILITY CLASSES - PrimeFlex integration
|
||||
---------------------------------------------------------------------------- */
|
||||
|
||||
.layout-topbar .flex {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.layout-topbar .align-items-center {
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.layout-topbar .justify-content-center {
|
||||
justify-content: center !important;
|
||||
}
|
||||
|
||||
.layout-topbar .gap-2 {
|
||||
gap: 0.5rem !important;
|
||||
}
|
||||
|
||||
.layout-topbar .text-white {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.layout-topbar .border-circle {
|
||||
border-radius: 50% !important;
|
||||
}
|
||||
|
||||
.layout-topbar .bg-primary {
|
||||
background-color: var(--primary-color) !important;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
$primaryColor:lighten(#2170E7, 5%);
|
||||
$primaryTextColor:#ffffff;
|
||||
|
||||
@import '../../sass/variables/layout/_layout_dark';
|
||||
@import '../../sass/layout/_layout';
|
||||
@@ -0,0 +1,5 @@
|
||||
$primaryColor:#2170E7;
|
||||
$primaryTextColor:#ffffff;
|
||||
|
||||
@import '../../sass/variables/layout/_layout_light';
|
||||
@import '../../sass/layout/_layout';
|
||||
1
target/classes/META-INF/resources/resources/freya-layout/css/primeflex-v2.min.css
vendored
Normal file
1
target/classes/META-INF/resources/resources/freya-layout/css/primeflex.min.css
vendored
Normal file
|
After Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,9 @@
|
||||
<svg width="17" height="20" viewBox="0 0 17 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0H6.00019V3.82345L17 1.66667V6.66667L6.00019 8.82345V10.4901L17 8.33333V13.3333L6.00019 15.4901V20H0V0Z" fill="url(#paint0_linear)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="3.33335" y1="3.08442e-08" x2="8.49995" y2="20" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#297FFF"/>
|
||||
<stop offset="1" stop-color="#7A0EE7"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 469 B |
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="209px" height="60px" viewBox="0 0 209 60" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>logo-freya-white</title>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="logo-freya-white" fill="#FFFFFF" fill-rule="nonzero">
|
||||
<polygon id="Path" points="0 0 15.8827941 0 15.8827941 10.1321425 45 4.41666667 45 17.6666667 15.8827941 23.3821425 15.8827941 27.7988092 45 22.0833333 45 35.3333333 15.8827941 41.0488092 15.8827941 53 0 53"></polygon>
|
||||
<path d="M84,20.8302387 L77.2821159,20.8302387 L77.2821159,18.9204244 C77.2821159,17.4350133 78.1284635,16.5862069 79.6095718,16.5862069 L83.9471033,16.5862069 L83.9471033,9 L77.070529,9 C71.0403023,9 67.8664987,12.3421751 67.8664987,18.1246684 L67.8664987,20.8302387 L63,20.8302387 L63.0528967,28.5755968 L67.7607053,28.5755968 L67.7607053,49 L77.5994962,49 L77.5994962,28.5755968 L84,28.5755968 L84,20.8302387 Z" id="Path"></path>
|
||||
<path d="M105.301136,21 C101.846591,21 99.3636364,21.9630996 97.3125,24.3173432 L95.7471591,21.1070111 L89,21.1070111 L89,50 L99.0397727,50 L99.0397727,35.3394834 C99.0397727,31.6476015 100.551136,30.095941 104.059659,30.095941 L108,30.095941 L108,21 L105.301136,21 Z" id="Path"></path>
|
||||
<path d="M141,35.2034783 C141,26.6852174 134.662692,20 125.473595,20 C116.495741,20 110,26.5773913 110,35.4730435 C110,44.3686957 116.548552,51 125.473595,51 C132.603066,51 138.412266,46.8486957 140.41908,40.2173913 L130.596252,40.2173913 C129.698467,41.9426087 127.744463,42.9669565 125.473595,42.9669565 C122.357751,42.9669565 120.298126,41.2417391 119.664395,37.8991304 L140.841567,37.8991304 C140.947189,36.9826087 141,36.12 141,35.2034783 Z M125.473595,27.8713043 C128.431005,27.8713043 130.49063,29.4347826 131.335605,32.346087 L119.822828,32.346087 C120.614991,29.4347826 122.621806,27.8713043 125.473595,27.8713043 Z" id="Shape"></path>
|
||||
<path d="M165.423077,21 L159.764423,36.6842105 L153.682692,21 L143,21 L154.793269,47.1052632 C153.471154,50.6315789 152.360577,51.6315789 148.605769,51.6315789 L145.855769,51.6315789 L145.855769,60 L149.240385,60 C156.644231,60 160.134615,56.8947368 163.995192,48.1578947 L176,21 L165.423077,21 Z" id="Path"></path>
|
||||
<path d="M201.660066,20.8641115 L200.656766,23.1324042 C198.333333,21.1341463 195.323432,20 191.943894,20 C183.231023,20 177,26.4268293 177,35.445993 C177,44.5191638 183.231023,51 191.943894,51 C195.270627,51 198.227723,49.9198606 200.551155,47.9756098 L201.39604,50.0278746 L209,50.0278746 L209,20.8641115 L201.660066,20.8641115 Z M193.264026,42.1428571 C189.567657,42.1428571 186.874587,39.2804878 186.874587,35.445993 C186.874587,31.6655052 189.567657,28.8571429 193.264026,28.8571429 C196.960396,28.8571429 199.653465,31.6655052 199.653465,35.445993 C199.653465,39.2804878 196.960396,42.1428571 193.264026,42.1428571 Z" id="Shape"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="446px" height="129px" viewBox="0 0 446 129" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>logo-freya</title>
|
||||
<defs>
|
||||
<linearGradient x1="28.4476672%" y1="1.54220833e-07%" x2="49.999861%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#297FFF" offset="0%"></stop>
|
||||
<stop stop-color="#7A0EE7" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="75.0238871%" y1="-21.5732317%" x2="40.5406822%" y2="100.001105%" id="linearGradient-2">
|
||||
<stop stop-color="#297FFF" offset="0%"></stop>
|
||||
<stop stop-color="#7616E8" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="37.9032258%" y1="-69.125861%" x2="-21.2207927%" y2="100.001538%" id="linearGradient-3">
|
||||
<stop stop-color="#297FFF" offset="0%"></stop>
|
||||
<stop stop-color="#7616E8" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="-55.9426462%" y1="-59.1651756%" x2="-141.121522%" y2="95.4614408%" id="linearGradient-4">
|
||||
<stop stop-color="#297FFF" offset="0%"></stop>
|
||||
<stop stop-color="#7616E8" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="-89.5003908%" y1="-50.8316014%" x2="-145.407143%" y2="72.8756185%" id="linearGradient-5">
|
||||
<stop stop-color="#297FFF" offset="0%"></stop>
|
||||
<stop stop-color="#7616E8" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="-319.994499%" y1="-50.1302194%" x2="-402.50275%" y2="91.589373%" id="linearGradient-6">
|
||||
<stop stop-color="#297FFF" offset="0%"></stop>
|
||||
<stop stop-color="#7616E8" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="logo-freya" fill-rule="nonzero">
|
||||
<polygon id="Path" fill="url(#linearGradient-1)" points="0 0 33.8832941 0 33.8832941 21.793665 96 9.5 96 38 33.8832941 50.293665 33.8832941 59.793665 96 47.5 96 76 33.8832941 88.293665 33.8832941 114 0 114"></polygon>
|
||||
<path d="M181,45.4350133 L166.604534,45.4350133 L166.604534,41.3289125 C166.604534,38.1352785 168.418136,36.3103448 171.59194,36.3103448 L180.88665,36.3103448 L180.88665,20 L166.151134,20 C153.229219,20 146.428212,27.1856764 146.428212,39.6180371 L146.428212,45.4350133 L136,45.4350133 L136.11335,62.0875332 L146.201511,62.0875332 L146.201511,106 L167.284635,106 L167.284635,62.0875332 L181,62.0875332 L181,45.4350133 Z" id="Path" fill="url(#linearGradient-2)"></path>
|
||||
<path d="M225.318182,44 C218.045455,44 212.818182,46.0590406 208.5,51.0922509 L205.204545,44.2287823 L191,44.2287823 L191,106 L212.136364,106 L212.136364,74.6568266 C212.136364,66.7638376 215.318182,63.4464945 222.704545,63.4464945 L231,63.4464945 L231,44 L225.318182,44 Z" id="Path" fill="url(#linearGradient-3)"></path>
|
||||
<path d="M304,73.8782609 C304,56.0173913 290.507666,42 270.943782,42 C251.829642,42 238,55.7913043 238,74.4434783 C238,93.0956522 251.942078,107 270.943782,107 C286.122658,107 298.49063,98.2956522 302.763203,84.3913043 L281.850085,84.3913043 C279.938671,88.0086957 275.778535,90.1565217 270.943782,90.1565217 C264.310051,90.1565217 259.925043,86.5391304 258.575809,79.5304348 L303.662692,79.5304348 C303.887564,77.6086957 304,75.8 304,73.8782609 Z M270.943782,58.5043478 C277.240204,58.5043478 281.625213,61.7826087 283.424191,67.8869565 L258.913118,67.8869565 C260.599659,61.7826087 264.872232,58.5043478 270.943782,58.5043478 Z" id="Shape" fill="url(#linearGradient-4)"></path>
|
||||
<path d="M353.24359,44 L341.06891,78.1835358 L327.983974,44 L305,44 L330.373397,100.896086 C327.528846,108.581646 325.139423,110.761134 317.060897,110.761134 L311.144231,110.761134 L311.144231,129 L318.426282,129 C334.355769,129 341.865385,122.232119 350.171474,103.190283 L376,44 L353.24359,44 Z" id="Path" fill="url(#linearGradient-5)"></path>
|
||||
<path d="M430.173267,43.8118467 L428.009901,48.5679443 C423,44.3780488 416.509901,42 409.222772,42 C390.435644,42 377,55.4756098 377,74.3867596 C377,93.4111498 390.435644,107 409.222772,107 C416.39604,107 422.772277,104.735192 427.782178,100.658537 L429.60396,104.961672 L446,104.961672 L446,43.8118467 L430.173267,43.8118467 Z M412.069307,88.4285714 C404.09901,88.4285714 398.292079,82.4268293 398.292079,74.3867596 C398.292079,66.4599303 404.09901,60.5714286 412.069307,60.5714286 C420.039604,60.5714286 425.846535,66.4599303 425.846535,74.3867596 C425.846535,82.4268293 420.039604,88.4285714 412.069307,88.4285714 Z" id="Shape" fill="url(#linearGradient-6)"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 493 KiB |
|
After Width: | Height: | Size: 273 KiB |
|
After Width: | Height: | Size: 327 KiB |
|
After Width: | Height: | Size: 825 KiB |
|
After Width: | Height: | Size: 788 B |
@@ -0,0 +1,879 @@
|
||||
/**
|
||||
* PrimeFaces Freya Layout
|
||||
*/
|
||||
PrimeFaces.widget.Freya = PrimeFaces.widget.BaseWidget.extend({
|
||||
|
||||
init: function(cfg) {
|
||||
this._super(cfg);
|
||||
this.wrapper = $(document.body).children('.layout-wrapper');
|
||||
var $this = this;
|
||||
|
||||
$(function() {
|
||||
$this._init();
|
||||
});
|
||||
|
||||
this.restoreMenuState();
|
||||
this.expandedMenuitems = this.expandedMenuitems||[];
|
||||
},
|
||||
|
||||
_init: function() {
|
||||
this.contentWrapper = this.wrapper.children('.layout-main');
|
||||
this.topbar = this.wrapper.find('.layout-topbar');
|
||||
this.topbarItems = this.topbar.find('.layout-topbar-actions > li.topbar-item');
|
||||
this.topbarLinks = this.topbarItems.children('a');
|
||||
this.topbarSearchItemMenu = this.topbar.find('.search-item');
|
||||
|
||||
this.menuWrapper = this.wrapper.find('.menu-wrapper');
|
||||
this.sidebarPin = this.menuWrapper.find('.sidebar-logo > .sidebar-pin');
|
||||
this.menu = this.menuWrapper.find('.layout-menu');
|
||||
this.menuButton = this.topbar.find('.menu-button');
|
||||
this.menulinks = this.menu.find('a');
|
||||
|
||||
this.rightpanel = this.wrapper.find('.layout-rightpanel');
|
||||
this.rightpanelButton = this.topbar.find('.layout-rightpanel-button');
|
||||
this.rightpanelExitButton = this.rightpanel.find('.rightpanel-exit-button');
|
||||
|
||||
this.configButton = $('#layout-config-button');
|
||||
this.configurator = this.wrapper.children('.layout-config');
|
||||
|
||||
this.bindEvents();
|
||||
},
|
||||
|
||||
toggleClass: function(el, className) {
|
||||
if (el.hasClass(className)) {
|
||||
el.removeClass(className);
|
||||
}
|
||||
else {
|
||||
el.addClass(className);
|
||||
}
|
||||
},
|
||||
|
||||
bindEvents: function() {
|
||||
var $this = this;
|
||||
|
||||
this.bindTopbarEvents();
|
||||
this.bindMenuEvents();
|
||||
this.bindRightPanelEvents();
|
||||
this.bindConfigEvents();
|
||||
|
||||
$(document.body).off('click.layoutBody').on('click.layoutBody', function() {
|
||||
if (!$this.menuClick) {
|
||||
$this.wrapper.removeClass('layout-sidebar-active layout-mobile-active');
|
||||
$(document.body).removeClass('blocked-scroll');
|
||||
|
||||
if ($this.isHorizontal() || $this.isSlim()) {
|
||||
$this.menu.find('.active-menuitem').removeClass('active-menuitem');
|
||||
$this.menu.find('ul:visible').hide();
|
||||
$this.menuActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this.topbarItemClicked) {
|
||||
$this.removeTopbarClassFromAllItems(null, 'active-topmenuitem', $this.topbarItems.filter('.active-topmenuitem'));
|
||||
}
|
||||
|
||||
if (!$this.rightpanelClicked) {
|
||||
$this.wrapper.removeClass('layout-rightpanel-active');
|
||||
}
|
||||
|
||||
if (!$this.configClicked && $this.configurator.hasClass('layout-config-active')) {
|
||||
$this.configurator.removeClass('layout-config-active');
|
||||
}
|
||||
|
||||
$this.horizontalMenuClick = false;
|
||||
$this.topbarItemClicked = false;
|
||||
$this.rightpanelClicked = false;
|
||||
$this.menuClick = false;
|
||||
$this.configClicked = false;
|
||||
});
|
||||
},
|
||||
|
||||
bindConfigEvents: function() {
|
||||
var $this = this;
|
||||
|
||||
this.configButton.off('click.configbutton').on('click.configbutton', function(e) {
|
||||
$this.configurator.toggleClass('layout-config-active');
|
||||
$this.configClicked = true;
|
||||
});
|
||||
|
||||
this.configurator.off('click.config').on('click.config', function() {
|
||||
$this.configClicked = true;
|
||||
});
|
||||
},
|
||||
|
||||
bindMenuEvents: function() {
|
||||
var $this = this;
|
||||
|
||||
this.menuButton.off('click.menu').on('click.menu', function(e) {
|
||||
$this.menuClick = true;
|
||||
|
||||
if ($this.isMobile()) {
|
||||
if ($this.wrapper.hasClass('layout-mobile-active')) {
|
||||
$this.wrapper.removeClass('layout-mobile-active');
|
||||
$(document.body).removeClass('blocked-scroll');
|
||||
}
|
||||
else {
|
||||
$this.wrapper.addClass('layout-mobile-active');
|
||||
$(document.body).addClass('blocked-scroll');
|
||||
}
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
this.menuWrapper.off('click.menuWrapper mouseenter.menuWrapper mouseleave.menuWrapper')
|
||||
.on('click.menuWrapper', function() {
|
||||
$this.menuClick = true;
|
||||
})
|
||||
.on('mouseenter.menuWrapper', function(e) {
|
||||
if(!$this.wrapper.hasClass('layout-sidebar-static')) {
|
||||
if($this.hideTimeout) {
|
||||
clearTimeout($this.hideTimeout);
|
||||
}
|
||||
|
||||
$this.menuWrapper.addClass('layout-sidebar-active');
|
||||
}
|
||||
if(!$this.wrapper.hasClass('layout-sidebar')) {
|
||||
if($this.hideTimeout) {
|
||||
clearTimeout($this.hideTimeout);
|
||||
}
|
||||
|
||||
$this.menuWrapper.removeClass('layout-sidebar-active');
|
||||
}
|
||||
})
|
||||
.on('mouseleave.menuWrapper', function(e) {
|
||||
if(!$this.wrapper.hasClass('layout-sidebar-static')) {
|
||||
$this.hideTimeout = setTimeout(function() {
|
||||
$this.menuWrapper.removeClass('layout-sidebar-active');
|
||||
}, $this.cfg.closeDelay);
|
||||
}
|
||||
});
|
||||
|
||||
this.sidebarPin.off('click.menuWrapper').on('click.menuWrapper', function(e) {
|
||||
$this.wrapper.removeClass('layout-static-restore');
|
||||
$this.wrapper.toggleClass('layout-static');
|
||||
$this.saveMenuState();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
this.menulinks.off('click.menuWrapper').on('click.menuWrapper', function(e) {
|
||||
var link = $(this),
|
||||
item = link.parent(),
|
||||
submenu = item.children('ul');
|
||||
horizontal = $this.isHorizontal();
|
||||
slim = $this.isSlim();
|
||||
$this.menuClick = true;
|
||||
|
||||
if (horizontal) {
|
||||
$this.horizontalMenuClick = true;
|
||||
}
|
||||
|
||||
if(item.hasClass('active-menuitem')) {
|
||||
if(submenu.length) {
|
||||
$this.removeMenuitem(item.attr('id'));
|
||||
item.removeClass('active-menuitem');
|
||||
|
||||
if(horizontal || slim) {
|
||||
if(item.parent().is($this.jq)) {
|
||||
$this.menuActive = false;
|
||||
}
|
||||
|
||||
submenu.hide();
|
||||
$this.removeMenuitem(item.attr('id'));
|
||||
item.removeClass('active-menuitem');
|
||||
}
|
||||
else {
|
||||
submenu.slideUp(function() {
|
||||
$this.removeMenuitem(item.attr('id'));
|
||||
item.removeClass('active-menuitem');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this.addMenuitem(item.attr('id'));
|
||||
|
||||
if(horizontal || slim) {
|
||||
$this.deactivateItems(item.siblings());
|
||||
item.addClass('active-menuitem');
|
||||
$this.menuActive = true;
|
||||
submenu.show();
|
||||
}
|
||||
else {
|
||||
$this.deactivateItems(item.siblings(), true);
|
||||
$this.activate(item);
|
||||
}
|
||||
}
|
||||
|
||||
if(submenu.length) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
this.menu.find('> li').off('mouseenter.menu').on('mouseenter.menu', function(e) {
|
||||
if ($this.isHorizontal() || $this.isSlim()) {
|
||||
var item = $(this);
|
||||
|
||||
if(!item.hasClass('active-menuitem')) {
|
||||
$this.menu.find('.active-menuitem').removeClass('active-menuitem');
|
||||
$this.menu.find('ul:visible').hide();
|
||||
|
||||
if($this.menuActive) {
|
||||
item.addClass('active-menuitem');
|
||||
item.children('ul').show();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
bindTopbarEvents: function() {
|
||||
var $this = this;
|
||||
|
||||
this.topbarLinks.off('click.topbar').on('click.topbar', function(e) {
|
||||
var link = $(this),
|
||||
item = link.parent(),
|
||||
submenu = item.children('ul');
|
||||
|
||||
if ($this.isMobile()) {
|
||||
$this.removeTopbarClassFromAllItems(null, 'active-topmenuitem', $this.topbarItems.filter('.active-topmenuitem').not(item));
|
||||
}
|
||||
else {
|
||||
$this.removeTopbarClassFromAllItems(item, 'active-topmenuitem');
|
||||
}
|
||||
$this.addTopbarClass(item, 'active-topmenuitem');
|
||||
|
||||
$this.topbarItemClicked = true;
|
||||
|
||||
if (submenu.length) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
this.topbarSearchItemMenu.off('click.topbar').on('click.topbar', function(e) {
|
||||
$this.topbarItemClicked = true;
|
||||
});
|
||||
},
|
||||
|
||||
bindRightPanelEvents: function() {
|
||||
var $this = this;
|
||||
var changeRightpanelState = function(e) {
|
||||
this.toggleClass(this.wrapper, 'layout-rightpanel-active');
|
||||
|
||||
this.rightpanelClicked = true;
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
this.rightpanelButton.off('click.rightpanel').on('click.rightpanel', changeRightpanelState.bind(this));
|
||||
this.rightpanelExitButton.off('click.rightpanel').on('click.rightpanel', changeRightpanelState.bind(this));
|
||||
|
||||
this.rightpanel.off('click.rightpanel').on('click.rightpanel', function() {
|
||||
$this.rightpanelClicked = true;
|
||||
});
|
||||
},
|
||||
|
||||
activate: function(item) {
|
||||
var submenu = item.children('ul');
|
||||
item.addClass('active-menuitem');
|
||||
|
||||
if(submenu.length) {
|
||||
submenu.slideDown();
|
||||
}
|
||||
},
|
||||
|
||||
deactivate: function(item) {
|
||||
var submenu = item.children('ul');
|
||||
item.removeClass('active-menuitem');
|
||||
|
||||
if(submenu.length) {
|
||||
submenu.hide();
|
||||
}
|
||||
},
|
||||
|
||||
deactivateItems: function(items, animate) {
|
||||
var $this = this;
|
||||
|
||||
for(var i = 0; i < items.length; i++) {
|
||||
var item = items.eq(i),
|
||||
submenu = item.children('ul');
|
||||
|
||||
if(submenu.length) {
|
||||
if(item.hasClass('active-menuitem')) {
|
||||
var activeSubItems = item.find('.active-menuitem');
|
||||
item.removeClass('active-menuitem');
|
||||
|
||||
if(animate) {
|
||||
submenu.slideUp('normal', function() {
|
||||
$(this).parent().find('.active-menuitem').each(function() {
|
||||
$this.deactivate($(this));
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
item.find('.active-menuitem').each(function() {
|
||||
$this.deactivate($(this));
|
||||
});
|
||||
}
|
||||
|
||||
$this.removeMenuitem(item.attr('id'));
|
||||
activeSubItems.each(function() {
|
||||
$this.removeMenuitem($(this).attr('id'));
|
||||
});
|
||||
}
|
||||
else {
|
||||
item.find('.active-menuitem').each(function() {
|
||||
var subItem = $(this);
|
||||
$this.deactivate(subItem);
|
||||
$this.removeMenuitem(subItem.attr('id'));
|
||||
});
|
||||
}
|
||||
}
|
||||
else if(item.hasClass('active-menuitem')) {
|
||||
$this.deactivate(item);
|
||||
$this.removeMenuitem(item.attr('id'));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
removeMenuitem: function (id) {
|
||||
this.expandedMenuitems = $.grep(this.expandedMenuitems, function (value) {
|
||||
return value !== id;
|
||||
});
|
||||
this.saveMenuState();
|
||||
},
|
||||
|
||||
addMenuitem: function (id) {
|
||||
if ($.inArray(id, this.expandedMenuitems) === -1) {
|
||||
this.expandedMenuitems.push(id);
|
||||
}
|
||||
this.saveMenuState();
|
||||
},
|
||||
|
||||
saveMenuState: function() {
|
||||
if(this.wrapper.hasClass('layout-static'))
|
||||
$.cookie('freya_menu_static', 'freya_menu_static', {path: '/'});
|
||||
else
|
||||
$.removeCookie('freya_menu_static', {path: '/'});
|
||||
|
||||
$.cookie('freya_expandeditems', this.expandedMenuitems.join(','), {path: '/'});
|
||||
},
|
||||
|
||||
clearMenuState: function() {
|
||||
this.expandedMenuitems = [];
|
||||
$.removeCookie('freya_expandeditems', {path: '/'});
|
||||
$.removeCookie('freya_menu_static', {path: '/'});
|
||||
},
|
||||
|
||||
clearActiveItems: function() {
|
||||
var activeItems = this.jq.find('li.active-menuitem'),
|
||||
subContainers = activeItems.children('ul');
|
||||
|
||||
activeItems.removeClass('active-menuitem');
|
||||
if(subContainers && subContainers.length) {
|
||||
subContainers.hide();
|
||||
}
|
||||
},
|
||||
|
||||
clearLayoutState: function() {
|
||||
this.clearMenuState();
|
||||
this.clearActiveItems();
|
||||
},
|
||||
|
||||
restoreMenuState: function() {
|
||||
var menuCookie = $.cookie('freya_expandeditems');
|
||||
if (!this.isSlim() && !this.isHorizontal() && menuCookie) {
|
||||
this.expandedMenuitems = menuCookie.split(',');
|
||||
for (var i = 0; i < this.expandedMenuitems.length; i++) {
|
||||
var id = this.expandedMenuitems[i];
|
||||
if (id) {
|
||||
var menuitem = $("#" + this.expandedMenuitems[i].replace(/:/g, "\\:"));
|
||||
menuitem.addClass('active-menuitem');
|
||||
|
||||
var submenu = menuitem.children('ul');
|
||||
if(submenu.length) {
|
||||
submenu.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sidebarCookie = $.cookie('freya_menu_static');
|
||||
if(sidebarCookie) {
|
||||
this.wrapper.addClass('layout-static');
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
removeTopbarClassFromAllItems: function(item, className, items) {
|
||||
var activeItems = item != null ? item.siblings('.' + className) : items;
|
||||
|
||||
activeItems.removeClass(className);
|
||||
activeItems.children('ul').removeClass('fadeInDown');
|
||||
},
|
||||
|
||||
addTopbarClass: function(item, className) {
|
||||
var submenu = item.children('ul');
|
||||
|
||||
if (submenu.length) {
|
||||
if (item.hasClass(className)) {
|
||||
submenu.removeClass('fadeInDown').addClass('fadeOutUp');
|
||||
|
||||
setTimeout(function() {
|
||||
item.removeClass(className);
|
||||
submenu.removeClass('fadeOutUp');
|
||||
}, 100);
|
||||
}
|
||||
else {
|
||||
item.addClass(className);
|
||||
submenu.addClass('fadeInDown');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
hideTopBar: function() {
|
||||
var $this = this;
|
||||
this.topbarMenu.addClass('fadeOutUp');
|
||||
|
||||
setTimeout(function() {
|
||||
$this.topbarMenu.removeClass('fadeOutUp topbar-menu-visible');
|
||||
},500);
|
||||
},
|
||||
|
||||
isMobile: function() {
|
||||
return window.innerWidth < 992;
|
||||
},
|
||||
isHorizontal: function() {
|
||||
return this.wrapper.hasClass('layout-horizontal') && !this.isMobile();
|
||||
},
|
||||
isSlim: function() {
|
||||
return this.wrapper.hasClass('layout-slim') && !this.isMobile();
|
||||
},
|
||||
isStatic: function() {
|
||||
return this.wrapper.hasClass('layout-static') && !this.isMobile();
|
||||
}
|
||||
});
|
||||
|
||||
PrimeFaces.FreyaConfigurator = {
|
||||
|
||||
changeLayout: function( componentTheme, darkMode ) {
|
||||
this.changeLayoutsTheme(darkMode);
|
||||
this.changeDemo(darkMode);
|
||||
this.changeComponentsTheme(componentTheme, darkMode);
|
||||
this.changeSectionTheme( darkMode, 'layout-menu');
|
||||
this.changeSectionTheme( darkMode , 'layout-topbar');
|
||||
},
|
||||
|
||||
changeLayoutsTheme: function(darkMode) {
|
||||
newLayout = '-' + darkMode;
|
||||
var linkElement = $('link[href*="layout-"]');
|
||||
var href = linkElement.attr('href');
|
||||
var startIndexOf = href.indexOf('layout-') + 6;
|
||||
var endIndexOf = href.indexOf('.css');
|
||||
var currentColor = href.substring(startIndexOf, endIndexOf);
|
||||
this.replaceLink(linkElement, href.replace(currentColor, newLayout));
|
||||
},
|
||||
|
||||
changeDemo: function(darkMode) {
|
||||
newLayout = '-' + darkMode;
|
||||
var linkElement = $('link[href*="demo-"]');
|
||||
var href = linkElement.attr('href');
|
||||
var startIndexOf = href.indexOf('demo-') + 4;
|
||||
var endIndexOf = href.indexOf('.css');
|
||||
var currentColor = href.substring(startIndexOf, endIndexOf);
|
||||
|
||||
this.replaceLink(linkElement, href.replace(currentColor, newLayout));
|
||||
},
|
||||
|
||||
changeComponentsTheme: function(themeColor, darkMode) {
|
||||
theme = this.getColor(themeColor, darkMode);
|
||||
var library = 'primefaces-freya';
|
||||
var linkElement = $('link[href*="theme.css"]');
|
||||
var href = linkElement.attr('href');
|
||||
var index = href.indexOf(library) + 1;
|
||||
var currentTheme = href.substring(index + library.length);
|
||||
|
||||
this.replaceLink(linkElement, href.replace(currentTheme, theme));
|
||||
},
|
||||
|
||||
changeSectionTheme: function(theme, section) {
|
||||
var wrapperElement = $('.layout-wrapper');
|
||||
|
||||
var styleClass = wrapperElement.attr('class');
|
||||
var tokens = styleClass.split(' ');
|
||||
var sectionClass;
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
if (tokens[i].indexOf(section + '-') > -1) {
|
||||
sectionClass = tokens[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
wrapperElement.attr('class', styleClass.replace(sectionClass, section + '-' + theme));
|
||||
},
|
||||
|
||||
changeMenuMode: function(menuMode) {
|
||||
var wrapper = $(document.body).children('.layout-wrapper');
|
||||
switch (menuMode) {
|
||||
case 'layout-sidebar':
|
||||
wrapper.addClass('layout-sidebar').removeClass('layout-slim layout-horizontal ');
|
||||
this.clearLayoutState();
|
||||
break;
|
||||
|
||||
case 'layout-horizontal':
|
||||
wrapper.addClass('layout-horizontal').removeClass('layout-static layout-slim layout-sidebar');
|
||||
this.clearLayoutState();
|
||||
break;
|
||||
|
||||
case 'layout-slim':
|
||||
wrapper.addClass('layout-slim').removeClass('layout-static layout-horizontal layout-sidebar');
|
||||
this.clearLayoutState();
|
||||
break;
|
||||
|
||||
default:
|
||||
wrapper.addClass('layout-sidebar').removeClass('layout-slim layout-horizontal ');
|
||||
this.clearLayoutState();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
beforeResourceChange: function() {
|
||||
PrimeFaces.ajax.RESOURCE = null; //prevent resource append
|
||||
},
|
||||
|
||||
replaceLink: function(linkElement, href) {
|
||||
PrimeFaces.ajax.RESOURCE = 'javax.faces.Resource';
|
||||
|
||||
var isIE = this.isIE();
|
||||
|
||||
if (isIE) {
|
||||
linkElement.attr('href', href);
|
||||
}
|
||||
else {
|
||||
var cloneLinkElement = linkElement.clone(false);
|
||||
|
||||
cloneLinkElement.attr('href', href);
|
||||
linkElement.after(cloneLinkElement);
|
||||
|
||||
cloneLinkElement.off('load').on('load', function() {
|
||||
linkElement.remove();
|
||||
});
|
||||
|
||||
// for dashboard
|
||||
setTimeout(function() {
|
||||
if (window['redrawChart']) {
|
||||
window.redrawChart();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
|
||||
getColor: function(name, darkMode) {
|
||||
return name + '-' + darkMode;
|
||||
},
|
||||
|
||||
isIE: function() {
|
||||
return /(MSIE|Trident\/|Edge\/)/i.test(navigator.userAgent);
|
||||
},
|
||||
|
||||
clearLayoutState: function() {
|
||||
var menu = PF('FreyaMenuWidget');
|
||||
|
||||
if (menu) {
|
||||
menu.clearLayoutState();
|
||||
}
|
||||
},
|
||||
|
||||
updateInputStyle: function(value) {
|
||||
if (value === 'filled')
|
||||
$(document.body).addClass('ui-input-filled');
|
||||
else
|
||||
$(document.body).removeClass('ui-input-filled');
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* jQuery Cookie Plugin v1.4.1
|
||||
* https://github.com/carhartl/jquery-cookie
|
||||
*
|
||||
* Copyright 2006, 2014 Klaus Hartl
|
||||
* Released under the MIT license
|
||||
*/
|
||||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD (Register as an anonymous module)
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node/CommonJS
|
||||
module.exports = factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals
|
||||
factory(jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
|
||||
var pluses = /\+/g;
|
||||
|
||||
function encode(s) {
|
||||
return config.raw ? s : encodeURIComponent(s);
|
||||
}
|
||||
|
||||
function decode(s) {
|
||||
return config.raw ? s : decodeURIComponent(s);
|
||||
}
|
||||
|
||||
function stringifyCookieValue(value) {
|
||||
return encode(config.json ? JSON.stringify(value) : String(value));
|
||||
}
|
||||
|
||||
function parseCookieValue(s) {
|
||||
if (s.indexOf('"') === 0) {
|
||||
// This is a quoted cookie as according to RFC2068, unescape...
|
||||
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
||||
}
|
||||
|
||||
try {
|
||||
// Replace server-side written pluses with spaces.
|
||||
// If we can't decode the cookie, ignore it, it's unusable.
|
||||
// If we can't parse the cookie, ignore it, it's unusable.
|
||||
s = decodeURIComponent(s.replace(pluses, ' '));
|
||||
return config.json ? JSON.parse(s) : s;
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
function read(s, converter) {
|
||||
var value = config.raw ? s : parseCookieValue(s);
|
||||
return $.isFunction(converter) ? converter(value) : value;
|
||||
}
|
||||
|
||||
var config = $.cookie = function (key, value, options) {
|
||||
|
||||
// Write
|
||||
|
||||
if (arguments.length > 1 && !$.isFunction(value)) {
|
||||
options = $.extend({}, config.defaults, options);
|
||||
|
||||
if (typeof options.expires === 'number') {
|
||||
var days = options.expires, t = options.expires = new Date();
|
||||
t.setMilliseconds(t.getMilliseconds() + days * 864e+5);
|
||||
}
|
||||
|
||||
return (document.cookie = [
|
||||
encode(key), '=', stringifyCookieValue(value),
|
||||
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
|
||||
options.path ? '; path=' + options.path : '',
|
||||
options.domain ? '; domain=' + options.domain : '',
|
||||
options.secure ? '; secure' : ''
|
||||
].join(''));
|
||||
}
|
||||
|
||||
// Read
|
||||
|
||||
var result = key ? undefined : {},
|
||||
// To prevent the for loop in the first place assign an empty array
|
||||
// in case there are no cookies at all. Also prevents odd result when
|
||||
// calling $.cookie().
|
||||
cookies = document.cookie ? document.cookie.split('; ') : [],
|
||||
i = 0,
|
||||
l = cookies.length;
|
||||
|
||||
for (; i < l; i++) {
|
||||
var parts = cookies[i].split('='),
|
||||
name = decode(parts.shift()),
|
||||
cookie = parts.join('=');
|
||||
|
||||
if (key === name) {
|
||||
// If second argument (value) is a function it's a converter...
|
||||
result = read(cookie, value);
|
||||
break;
|
||||
}
|
||||
|
||||
// Prevent storing a cookie that we couldn't decode.
|
||||
if (!key && (cookie = read(cookie)) !== undefined) {
|
||||
result[name] = cookie;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
config.defaults = {};
|
||||
|
||||
$.removeCookie = function (key, options) {
|
||||
// Must not alter options, thus extending a fresh object...
|
||||
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
|
||||
return !$.cookie(key);
|
||||
};
|
||||
|
||||
}));
|
||||
|
||||
if (PrimeFaces.widget.InputSwitch) {
|
||||
PrimeFaces.widget.InputSwitch = PrimeFaces.widget.InputSwitch.extend({
|
||||
|
||||
init: function (cfg) {
|
||||
this._super(cfg);
|
||||
|
||||
if (this.input.prop('checked')) {
|
||||
this.jq.addClass('ui-inputswitch-checked');
|
||||
}
|
||||
},
|
||||
|
||||
check: function () {
|
||||
var $this = this;
|
||||
|
||||
this.input.prop('checked', true).trigger('change');
|
||||
setTimeout(function () {
|
||||
$this.jq.addClass('ui-inputswitch-checked');
|
||||
}, 100);
|
||||
},
|
||||
|
||||
uncheck: function () {
|
||||
var $this = this;
|
||||
|
||||
this.input.prop('checked', false).trigger('change');
|
||||
setTimeout(function () {
|
||||
$this.jq.removeClass('ui-inputswitch-checked');
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (PrimeFaces.widget.AccordionPanel) {
|
||||
PrimeFaces.widget.AccordionPanel = PrimeFaces.widget.AccordionPanel.extend({
|
||||
|
||||
init: function (cfg) {
|
||||
this._super(cfg);
|
||||
|
||||
this.headers.last().addClass('ui-accordion-header-last');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Issue #924 is fixed for 5.3+ and 6.0. (compatibility with 5.3) */
|
||||
if(window['PrimeFaces'] && window['PrimeFaces'].widget.Dialog) {
|
||||
PrimeFaces.widget.Dialog = PrimeFaces.widget.Dialog.extend({
|
||||
|
||||
enableModality: function() {
|
||||
this._super();
|
||||
$(document.body).children(this.jqId + '_modal').addClass('ui-dialog-mask');
|
||||
},
|
||||
|
||||
syncWindowResize: function() {}
|
||||
});
|
||||
}
|
||||
|
||||
if (PrimeFaces.widget.SelectOneMenu) {
|
||||
PrimeFaces.widget.SelectOneMenu = PrimeFaces.widget.SelectOneMenu.extend({
|
||||
init: function (cfg) {
|
||||
this._super(cfg);
|
||||
|
||||
var $this = this;
|
||||
if (this.jq.parent().hasClass('ui-float-label')) {
|
||||
this.m_panel = $(this.jqId + '_panel');
|
||||
this.m_focusInput = $(this.jqId + '_focus');
|
||||
|
||||
this.m_panel.addClass('ui-input-overlay-panel');
|
||||
this.jq.addClass('ui-inputwrapper');
|
||||
|
||||
if (this.input.val() != '') {
|
||||
this.jq.addClass('ui-inputwrapper-filled');
|
||||
}
|
||||
|
||||
this.input.off('change').on('change', function () {
|
||||
$this.inputValueControl($(this));
|
||||
});
|
||||
|
||||
this.m_focusInput.on('focus.ui-selectonemenu', function () {
|
||||
$this.jq.addClass('ui-inputwrapper-focus');
|
||||
})
|
||||
.on('blur.ui-selectonemenu', function () {
|
||||
$this.jq.removeClass('ui-inputwrapper-focus');
|
||||
});
|
||||
|
||||
if (this.cfg.editable) {
|
||||
this.label.on('input', function (e) {
|
||||
$this.inputValueControl($(this));
|
||||
}).on('focus', function () {
|
||||
$this.jq.addClass('ui-inputwrapper-focus');
|
||||
}).on('blur', function () {
|
||||
$this.jq.removeClass('ui-inputwrapper-focus');
|
||||
$this.inputValueControl($(this));
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
inputValueControl: function (input) {
|
||||
if (input.val() != '')
|
||||
this.jq.addClass('ui-inputwrapper-filled');
|
||||
else
|
||||
this.jq.removeClass('ui-inputwrapper-filled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (PrimeFaces.widget.Chips) {
|
||||
PrimeFaces.widget.Chips = PrimeFaces.widget.Chips.extend({
|
||||
init: function (cfg) {
|
||||
this._super(cfg);
|
||||
|
||||
var $this = this;
|
||||
if (this.jq.parent().hasClass('ui-float-label')) {
|
||||
this.jq.addClass('ui-inputwrapper');
|
||||
|
||||
if ($this.jq.find('.ui-chips-token').length !== 0) {
|
||||
this.jq.addClass('ui-inputwrapper-filled');
|
||||
}
|
||||
|
||||
this.input.on('focus.ui-chips', function () {
|
||||
$this.jq.addClass('ui-inputwrapper-focus');
|
||||
}).on('input.ui-chips', function () {
|
||||
$this.inputValueControl();
|
||||
}).on('blur.ui-chips', function () {
|
||||
$this.jq.removeClass('ui-inputwrapper-focus');
|
||||
$this.inputValueControl();
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
inputValueControl: function () {
|
||||
if (this.jq.find('.ui-chips-token').length !== 0 || this.input.val() != '')
|
||||
this.jq.addClass('ui-inputwrapper-filled');
|
||||
else
|
||||
this.jq.removeClass('ui-inputwrapper-filled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (PrimeFaces.widget.DatePicker) {
|
||||
PrimeFaces.widget.DatePicker = PrimeFaces.widget.DatePicker.extend({
|
||||
init: function (cfg) {
|
||||
this._super(cfg);
|
||||
|
||||
var $this = this;
|
||||
if (this.jq.parent().hasClass('ui-float-label') && !this.cfg.inline) {
|
||||
if (this.input.val() != '') {
|
||||
this.jq.addClass('ui-inputwrapper-filled');
|
||||
}
|
||||
|
||||
this.jqEl.off('focus.ui-datepicker blur.ui-datepicker change.ui-datepicker')
|
||||
.on('focus.ui-datepicker', function () {
|
||||
$this.jq.addClass('ui-inputwrapper-focus');
|
||||
})
|
||||
.on('blur.ui-datepicker', function () {
|
||||
$this.jq.removeClass('ui-inputwrapper-focus');
|
||||
})
|
||||
.on('change.ui-datepicker', function () {
|
||||
$this.inputValueControl($(this));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
inputValueControl: function (input) {
|
||||
if (input.val() != '')
|
||||
this.jq.addClass('ui-inputwrapper-filled');
|
||||
else
|
||||
this.jq.removeClass('ui-inputwrapper-filled');
|
||||
}
|
||||
});
|
||||
}
|
||||
399
target/classes/META-INF/resources/templates/components/README.md
Normal file
@@ -0,0 +1,399 @@
|
||||
# 📦 Composants Réutilisables - Lions User Manager
|
||||
|
||||
**Version**: 1.0.0
|
||||
**Date**: 2025-01-29
|
||||
**Pattern**: WOU/DRY (Write Once Use / Don't Repeat Yourself)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Vue d'Ensemble
|
||||
|
||||
Ce répertoire contient tous les composants UI réutilisables pour le module **lions-user-manager**. Ces composants suivent les patterns établis dans **unionflow** et peuvent être utilisés dans n'importe quel projet de l'écosystème **lionsdev**.
|
||||
|
||||
---
|
||||
|
||||
## 📂 Structure des Composants
|
||||
|
||||
```
|
||||
templates/components/
|
||||
├── user-management/ # Composants spécifiques utilisateurs
|
||||
│ ├── user-card.xhtml
|
||||
│ ├── user-form.xhtml
|
||||
│ ├── user-search-bar.xhtml
|
||||
│ ├── user-actions.xhtml
|
||||
│ └── user-role-badge.xhtml
|
||||
├── role-management/ # Composants spécifiques rôles
|
||||
│ ├── role-card.xhtml
|
||||
│ ├── role-form.xhtml
|
||||
│ └── role-assignment.xhtml
|
||||
├── audit/ # Composants audit
|
||||
│ ├── audit-log-row.xhtml
|
||||
│ └── audit-stats-card.xhtml
|
||||
└── shared/ # Composants génériques réutilisables
|
||||
├── buttons/
|
||||
│ └── button-user-action.xhtml
|
||||
├── cards/
|
||||
│ └── user-stat-card.xhtml
|
||||
├── forms/
|
||||
│ └── user-form-field.xhtml
|
||||
└── tables/
|
||||
└── user-data-table.xhtml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Liste Complète des Composants
|
||||
|
||||
### 👤 User Management (5 composants)
|
||||
|
||||
#### 1. `user-card.xhtml`
|
||||
**Description**: Carte utilisateur avec informations principales et actions
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `user`: UserDTO (requis)
|
||||
- `showActions`: Boolean (défaut: true)
|
||||
- `showRoles`: Boolean (défaut: true)
|
||||
- `clickable`: Boolean (défaut: true)
|
||||
- `outcome`: String (optionnel)
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/user-management/user-card.xhtml">
|
||||
<ui:param name="user" value="#{userBean.selectedUser}" />
|
||||
<ui:param name="showActions" value="true" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
#### 2. `user-form.xhtml`
|
||||
**Description**: Formulaire complet pour créer/modifier un utilisateur
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `user`: UserDTO (requis)
|
||||
- `mode`: String (défaut: "create") - "create" ou "edit"
|
||||
- `showRealmSelector`: Boolean (défaut: false)
|
||||
- `showPasswordFields`: Boolean (défaut: true)
|
||||
- `readonly`: Boolean (défaut: false)
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<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>
|
||||
```
|
||||
|
||||
#### 3. `user-search-bar.xhtml`
|
||||
**Description**: Barre de recherche avancée pour utilisateurs
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `searchCriteria`: UserSearchCriteriaDTO (requis)
|
||||
- `searchAction`: String (requis)
|
||||
- `showAdvanced`: Boolean (défaut: false)
|
||||
- `showRealmFilter`: Boolean (défaut: true)
|
||||
- `showStatusFilter`: Boolean (défaut: true)
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<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>
|
||||
```
|
||||
|
||||
#### 4. `user-actions.xhtml`
|
||||
**Description**: Boutons d'action pour un utilisateur
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `user`: UserDTO (requis)
|
||||
- `showView`: Boolean (défaut: true)
|
||||
- `showEdit`: Boolean (défaut: true)
|
||||
- `showDelete`: Boolean (défaut: true)
|
||||
- `showActivate`: Boolean (défaut: true)
|
||||
- `layout`: String (défaut: "horizontal") - "horizontal", "vertical" ou "dropdown"
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<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>
|
||||
```
|
||||
|
||||
#### 5. `user-role-badge.xhtml`
|
||||
**Description**: Badge pour un rôle utilisateur
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `roleName`: String (requis)
|
||||
- `roleType`: String (optionnel) - "REALM_ROLE", "CLIENT_ROLE", "COMPOSITE_ROLE"
|
||||
- `severity`: String (optionnel) - "success", "info", "warning", "danger"
|
||||
- `clickable`: Boolean (défaut: false)
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/user-management/user-role-badge.xhtml">
|
||||
<ui:param name="roleName" value="ADMIN" />
|
||||
<ui:param name="roleType" value="REALM_ROLE" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🛡️ Role Management (3 composants)
|
||||
|
||||
#### 6. `role-card.xhtml`
|
||||
**Description**: Carte rôle avec informations principales
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `role`: RoleDTO (requis)
|
||||
- `showActions`: Boolean (défaut: true)
|
||||
- `showDescription`: Boolean (défaut: true)
|
||||
- `showCompositeInfo`: Boolean (défaut: true)
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/role-management/role-card.xhtml">
|
||||
<ui:param name="role" value="#{roleBean.selectedRole}" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
#### 7. `role-form.xhtml`
|
||||
**Description**: Formulaire pour créer/modifier un rôle
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `role`: RoleDTO (requis)
|
||||
- `mode`: String (défaut: "create")
|
||||
- `showRealmSelector`: Boolean (défaut: true)
|
||||
- `showClientSelector`: Boolean (défaut: false)
|
||||
- `showCompositeOptions`: Boolean (défaut: true)
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/role-management/role-form.xhtml">
|
||||
<ui:param name="role" value="#{roleBean.newRole}" />
|
||||
<ui:param name="mode" value="create" />
|
||||
<ui:param name="submitAction" value="#{roleBean.createRole}" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
#### 8. `role-assignment.xhtml`
|
||||
**Description**: Interface pour attribuer/révoquer des rôles
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `user`: UserDTO (requis)
|
||||
- `availableRoles`: List<RoleDTO> (requis)
|
||||
- `userRoles`: List<RoleDTO> (requis)
|
||||
- `assignAction`: String (requis)
|
||||
- `revokeAction`: String (requis)
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/role-management/role-assignment.xhtml">
|
||||
<ui:param name="user" value="#{userBean.selectedUser}" />
|
||||
<ui:param name="availableRoles" value="#{roleBean.availableRoles}" />
|
||||
<ui:param name="userRoles" value="#{userBean.userRoles}" />
|
||||
<ui:param name="assignAction" value="#{roleBean.assignRole}" />
|
||||
<ui:param name="revokeAction" value="#{roleBean.revokeRole}" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 📊 Audit (2 composants)
|
||||
|
||||
#### 9. `audit-log-row.xhtml`
|
||||
**Description**: Ligne de log d'audit avec informations détaillées
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `auditLog`: AuditLogDTO (requis)
|
||||
- `showDetails`: Boolean (défaut: false)
|
||||
- `showActions`: Boolean (défaut: false)
|
||||
- `compact`: Boolean (défaut: false)
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/audit/audit-log-row.xhtml">
|
||||
<ui:param name="auditLog" value="#{log}" />
|
||||
<ui:param name="showDetails" value="true" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
#### 10. `audit-stats-card.xhtml`
|
||||
**Description**: Carte statistiques d'audit
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `title`: String (requis)
|
||||
- `value`: Number (requis)
|
||||
- `icon`: String (requis)
|
||||
- `iconColor`: String (requis)
|
||||
- `trend`: Number (optionnel)
|
||||
- `trendLabel`: String (optionnel)
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/audit/audit-stats-card.xhtml">
|
||||
<ui:param name="title" value="Total Actions" />
|
||||
<ui:param name="value" value="#{auditBean.totalActions}" />
|
||||
<ui:param name="icon" value="pi-history" />
|
||||
<ui:param name="iconColor" value="blue-600" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🔧 Shared Components (4 composants)
|
||||
|
||||
#### 11. `button-user-action.xhtml`
|
||||
**Description**: Bouton générique pour actions utilisateur
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `value`: String (requis)
|
||||
- `icon`: String (optionnel)
|
||||
- `action`: String (optionnel)
|
||||
- `outcome`: String (optionnel)
|
||||
- `severity`: String (défaut: "primary")
|
||||
- `size`: String (défaut: "normal")
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/shared/buttons/button-user-action.xhtml">
|
||||
<ui:param name="value" value="Créer Utilisateur" />
|
||||
<ui:param name="icon" value="pi-user-plus" />
|
||||
<ui:param name="action" value="#{userBean.createUser}" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
#### 12. `user-stat-card.xhtml`
|
||||
**Description**: Carte statistique utilisateur
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `title`: String (requis)
|
||||
- `value`: String/Number (requis)
|
||||
- `icon`: String (requis)
|
||||
- `iconColor`: String (requis)
|
||||
- `trend`: Number (optionnel)
|
||||
- `clickable`: Boolean (défaut: false)
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/shared/cards/user-stat-card.xhtml">
|
||||
<ui:param name="title" value="Total Utilisateurs" />
|
||||
<ui:param name="value" value="#{userBean.totalUsers}" />
|
||||
<ui:param name="icon" value="pi-users" />
|
||||
<ui:param name="iconColor" value="blue-600" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
#### 13. `user-form-field.xhtml`
|
||||
**Description**: Champ de formulaire générique
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `id`: String (requis)
|
||||
- `label`: String (requis)
|
||||
- `value`: Object (requis)
|
||||
- `type`: String (défaut: "text") - "text", "email", "password", "number", "textarea", "select", "checkbox", "calendar"
|
||||
- `required`: Boolean (défaut: false)
|
||||
- `readonly`: Boolean (défaut: false)
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/shared/forms/user-form-field.xhtml">
|
||||
<ui:param name="id" value="username" />
|
||||
<ui:param name="label" value="Nom d'utilisateur" />
|
||||
<ui:param name="value" value="#{user.username}" />
|
||||
<ui:param name="required" value="true" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
#### 14. `user-data-table.xhtml`
|
||||
**Description**: Tableau de données pour utilisateurs
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `users`: List<UserDTO> (requis)
|
||||
- `var`: String (défaut: "user")
|
||||
- `paginator`: Boolean (défaut: true)
|
||||
- `rows`: Number (défaut: 20)
|
||||
- `showActions`: Boolean (défaut: true)
|
||||
- `showRoles`: Boolean (défaut: true)
|
||||
- `showSelection`: Boolean (défaut: false)
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/shared/tables/user-data-table.xhtml">
|
||||
<ui:param name="users" value="#{userBean.users}" />
|
||||
<ui:param name="update" value="userTable" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Patterns et Conventions
|
||||
|
||||
### Documentation Inline
|
||||
Chaque composant contient une documentation complète en commentaire avec:
|
||||
- Description du composant
|
||||
- Liste des paramètres avec types et valeurs par défaut
|
||||
- Exemples d'utilisation
|
||||
|
||||
### Paramètres Optionnels
|
||||
Tous les paramètres optionnels ont des valeurs par défaut définies avec `<c:set>`.
|
||||
|
||||
### Pattern WOU/DRY
|
||||
- **Write Once Use**: Chaque composant est écrit une fois et réutilisé partout
|
||||
- **Don't Repeat Yourself**: Pas de duplication de code
|
||||
|
||||
### Naming Convention
|
||||
- Noms de fichiers en `kebab-case`
|
||||
- Paramètres en `camelCase`
|
||||
- IDs de composants avec préfixe cohérent
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Utilisation dans d'Autres Projets
|
||||
|
||||
Ces composants peuvent être utilisés dans n'importe quel projet de l'écosystème **lionsdev** en ajoutant la dépendance Maven:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>dev.lions.user.manager</groupId>
|
||||
<artifactId>lions-user-manager-client-quarkus-primefaces-freya</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
Puis inclure les composants dans vos pages XHTML:
|
||||
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/user-management/user-card.xhtml">
|
||||
<ui:param name="user" value="#{userBean.selectedUser}" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Notes Importantes
|
||||
|
||||
1. **Dépendances**: Tous les composants nécessitent PrimeFaces 14.0.5+ et Quarkus PrimeFaces 3.13.3+
|
||||
2. **Thème**: Les composants utilisent le thème Freya (compatible avec unionflow)
|
||||
3. **Validation**: Les composants de formulaire incluent la validation JSF
|
||||
4. **Accessibilité**: Les composants suivent les bonnes pratiques d'accessibilité
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Maintenance
|
||||
|
||||
Pour ajouter un nouveau composant:
|
||||
|
||||
1. Créer le fichier dans le répertoire approprié
|
||||
2. Suivre le pattern de documentation inline
|
||||
3. Ajouter des exemples d'utilisation
|
||||
4. Mettre à jour ce README
|
||||
|
||||
---
|
||||
|
||||
**Dernière mise à jour**: 2025-01-29
|
||||
**Version**: 1.0.0
|
||||
**Auteur**: Lions User Manager Team
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<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: Ligne de Log d'Audit (WOU/DRY Pattern)
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Affiche une ligne de log d'audit avec informations détaillées
|
||||
|
||||
Paramètres:
|
||||
- auditLog: AuditLogDTO (requis) - Le log d'audit à afficher
|
||||
- showDetails: Boolean (défaut: false) - Afficher les détails complets
|
||||
- showActions: Boolean (défaut: false) - Afficher les actions possibles
|
||||
- compact: Boolean (défaut: false) - Mode compact
|
||||
- styleClass: String (optionnel) - Classes CSS supplémentaires
|
||||
|
||||
Exemples d'utilisation:
|
||||
|
||||
1. Ligne simple:
|
||||
<ui:include src="/templates/components/audit/audit-log-row.xhtml">
|
||||
<ui:param name="auditLog" value="#{log}" />
|
||||
</ui:include>
|
||||
|
||||
2. Ligne avec détails:
|
||||
<ui:include src="/templates/components/audit/audit-log-row.xhtml">
|
||||
<ui:param name="auditLog" value="#{log}" />
|
||||
<ui:param name="showDetails" value="true" />
|
||||
</ui:include>
|
||||
-->
|
||||
|
||||
<c:set var="showDetails" value="#{empty showDetails ? false : showDetails}" />
|
||||
<c:set var="showActions" value="#{empty showActions ? false : showActions}" />
|
||||
<c:set var="compact" value="#{empty compact ? false : compact}" />
|
||||
|
||||
<!-- Déterminer la severity selon le succès -->
|
||||
<c:set var="severity" value="#{auditLog.succes ? 'success' : 'danger'}" />
|
||||
<c:set var="icon" value="#{auditLog.succes ? 'pi-check-circle' : 'pi-times-circle'}" />
|
||||
|
||||
<div class="audit-log-row flex align-items-center gap-3 p-3 border-round surface-border border-1 #{styleClass}"
|
||||
style="#{compact ? 'padding: 0.5rem;' : ''}">
|
||||
|
||||
<!-- Icône de statut -->
|
||||
<div class="flex align-items-center justify-content-center"
|
||||
style="width: 2.5rem; height: 2.5rem; border-radius: 50%; background: var(--#{severity}-100);">
|
||||
<i class="pi #{icon} text-#{severity}-600"></i>
|
||||
</div>
|
||||
|
||||
<!-- Informations principales -->
|
||||
<div class="flex-1">
|
||||
<div class="flex align-items-center gap-2 mb-1">
|
||||
<strong class="text-900">#{auditLog.typeAction}</strong>
|
||||
<p:tag
|
||||
value="#{auditLog.succes ? 'Succès' : 'Échec'}"
|
||||
severity="#{severity}"
|
||||
styleClass="text-xs" />
|
||||
</div>
|
||||
|
||||
<div class="text-color-secondary text-sm">
|
||||
<c:if test="#{not empty auditLog.acteurUsername}">
|
||||
<span><i class="pi pi-user mr-1"></i>#{auditLog.acteurUsername}</span>
|
||||
</c:if>
|
||||
<c:if test="#{not empty auditLog.ressourceType}">
|
||||
<span class="ml-3"><i class="pi pi-database mr-1"></i>#{auditLog.ressourceType}</span>
|
||||
</c:if>
|
||||
<c:if test="#{not empty auditLog.dateAction}">
|
||||
<span class="ml-3"><i class="pi pi-calendar mr-1"></i>#{auditLog.dateAction}</span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<!-- Détails (si affichés) -->
|
||||
<c:if test="#{showDetails}">
|
||||
<div class="mt-2 text-color-secondary text-xs">
|
||||
<c:if test="#{not empty auditLog.ressourceId}">
|
||||
<div><strong>Ressource ID:</strong> #{auditLog.ressourceId}</div>
|
||||
</c:if>
|
||||
<c:if test="#{not empty auditLog.details}">
|
||||
<div><strong>Détails:</strong> #{auditLog.details}</div>
|
||||
</c:if>
|
||||
<c:if test="#{not empty auditLog.adresseIp}">
|
||||
<div><strong>IP:</strong> #{auditLog.adresseIp}</div>
|
||||
</c:if>
|
||||
<c:if test="#{not empty auditLog.userAgent}">
|
||||
<div><strong>User Agent:</strong> #{auditLog.userAgent}</div>
|
||||
</c:if>
|
||||
<c:if test="#{not empty auditLog.messageErreur}">
|
||||
<div class="text-red-600"><strong>Erreur:</strong> #{auditLog.messageErreur}</div>
|
||||
</c:if>
|
||||
</div>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<!-- Actions (si affichées) -->
|
||||
<c:if test="#{showActions}">
|
||||
<div class="flex gap-1">
|
||||
<p:commandButton
|
||||
icon="pi pi-eye"
|
||||
styleClass="p-button-text p-button-sm"
|
||||
title="Voir les détails"
|
||||
onclick="PF('auditLogDetailsDialog').show()" />
|
||||
</div>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
<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 Statistiques Audit (WOU/DRY Pattern)
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Affiche des statistiques d'audit dans une carte
|
||||
|
||||
Paramètres:
|
||||
- title: String (requis) - Titre de la carte
|
||||
- value: Number (requis) - Valeur à afficher
|
||||
- icon: String (requis) - Classe d'icône PrimeIcons
|
||||
- iconColor: String (requis) - Couleur de l'icône
|
||||
- subtitle: String (optionnel) - Sous-titre
|
||||
- trend: Number (optionnel) - Tendance (pourcentage)
|
||||
- trendLabel: String (optionnel) - Libellé de la tendance
|
||||
- colSize: String (défaut: "col-12 md:col-6 lg:col-3") - Taille de colonne
|
||||
- clickable: Boolean (défaut: false) - Rendre la carte cliquable
|
||||
- clickAction: String (optionnel) - Action au clic
|
||||
|
||||
Exemples d'utilisation:
|
||||
|
||||
1. Carte simple:
|
||||
<ui:include src="/templates/components/audit/audit-stats-card.xhtml">
|
||||
<ui:param name="title" value="Total Actions" />
|
||||
<ui:param name="value" value="#{auditBean.totalActions}" />
|
||||
<ui:param name="icon" value="pi-history" />
|
||||
<ui:param name="iconColor" value="blue-600" />
|
||||
</ui:include>
|
||||
|
||||
2. Carte avec tendance:
|
||||
<ui:include src="/templates/components/audit/audit-stats-card.xhtml">
|
||||
<ui:param name="title" value="Actions Réussies" />
|
||||
<ui:param name="value" value="#{auditBean.successCount}" />
|
||||
<ui:param name="icon" value="pi-check-circle" />
|
||||
<ui:param name="iconColor" value="green-600" />
|
||||
<ui:param name="trend" value="#{auditBean.successTrend}" />
|
||||
<ui:param name="trendLabel" value="vs mois dernier" />
|
||||
</ui:include>
|
||||
-->
|
||||
|
||||
<c:set var="colSize" value="#{empty colSize ? 'col-12 md:col-6 lg:col-3' : colSize}" />
|
||||
<c:set var="clickable" value="#{empty clickable ? false : clickable}" />
|
||||
|
||||
<div class="field #{colSize}">
|
||||
<c:choose>
|
||||
<c:when test="#{clickable and not empty clickAction}">
|
||||
<p:commandLink
|
||||
styleClass="card-link"
|
||||
action="#{clickAction}">
|
||||
<div class="card surface-0 hover:surface-100 border-round-lg transition-all transition-duration-200 p-4">
|
||||
<div class="flex align-items-center justify-content-between mb-3">
|
||||
<span class="block text-600 font-medium text-sm">#{title}</span>
|
||||
<div class="flex align-items-center justify-content-center surface-100 border-round-lg"
|
||||
style="width: 2.5rem; height: 2.5rem;">
|
||||
<i class="pi #{icon} text-#{iconColor} text-lg"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-900 font-bold text-2xl mb-2">#{value}</div>
|
||||
|
||||
<c:if test="#{not empty subtitle}">
|
||||
<div class="text-500 text-xs mb-2">#{subtitle}</div>
|
||||
</c:if>
|
||||
|
||||
<c:if test="#{not empty trend}">
|
||||
<div class="flex align-items-center">
|
||||
<i class="pi pi-arrow-#{trend >= 0 ? 'up' : 'down'} text-#{trend >= 0 ? 'green' : 'red'}-500 text-sm mr-2"></i>
|
||||
<span class="text-#{trend >= 0 ? 'green' : 'red'}-600 font-semibold text-sm mr-2">
|
||||
#{trend >= 0 ? '+' : ''}#{trend}%
|
||||
</span>
|
||||
<c:if test="#{not empty trendLabel}">
|
||||
<span class="text-500 text-xs">#{trendLabel}</span>
|
||||
</c:if>
|
||||
</div>
|
||||
</c:if>
|
||||
</div>
|
||||
</p:commandLink>
|
||||
</c:when>
|
||||
|
||||
<c:otherwise>
|
||||
<div class="card surface-0 border-round-lg p-4">
|
||||
<div class="flex align-items-center justify-content-between mb-3">
|
||||
<span class="block text-600 font-medium text-sm">#{title}</span>
|
||||
<div class="flex align-items-center justify-content-center surface-100 border-round-lg"
|
||||
style="width: 2.5rem; height: 2.5rem;">
|
||||
<i class="pi #{icon} text-#{iconColor} text-lg"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-900 font-bold text-2xl mb-2">#{value}</div>
|
||||
|
||||
<c:if test="#{not empty subtitle}">
|
||||
<div class="text-500 text-xs mb-2">#{subtitle}</div>
|
||||
</c:if>
|
||||
|
||||
<c:if test="#{not empty trend}">
|
||||
<div class="flex align-items-center">
|
||||
<i class="pi pi-arrow-#{trend >= 0 ? 'up' : 'down'} text-#{trend >= 0 ? 'green' : 'red'}-500 text-sm mr-2"></i>
|
||||
<span class="text-#{trend >= 0 ? 'green' : 'red'}-600 font-semibold text-sm mr-2">
|
||||
#{trend >= 0 ? '+' : ''}#{trend}%
|
||||
</span>
|
||||
<c:if test="#{not empty trendLabel}">
|
||||
<span class="text-500 text-xs">#{trendLabel}</span>
|
||||
</c:if>
|
||||
</div>
|
||||
</c:if>
|
||||
</div>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<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">
|
||||
|
||||
<!--
|
||||
Composant réutilisable: Footer (WOU/DRY Pattern)
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Pied de page avec informations
|
||||
-->
|
||||
|
||||
<div class="layout-footer">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="footer-bottom text-center">
|
||||
<h4>Lions User Manager</h4>
|
||||
<h6>Copyright © Lions Dev Team - Gestion centralisée des utilisateurs Keycloak</h6>
|
||||
<p class="text-600 text-sm mt-2">Version 1.0.0</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<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:fr="http://primefaces.org/freya">
|
||||
|
||||
<!--
|
||||
Composant réutilisable: Menu Navigation (WOU/DRY Pattern)
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Menu de navigation latéral pour Lions User Manager
|
||||
-->
|
||||
|
||||
<div class="menu-wrapper">
|
||||
<div class="sidebar-logo">
|
||||
<a href="/pages/user-manager/dashboard">
|
||||
<p:graphicImage name="images/logo-freya-single.svg" library="freya-layout" />
|
||||
</a>
|
||||
<a href="#" class="sidebar-pin" title="Toggle Menu">
|
||||
<span class="pin"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="layout-menu-container">
|
||||
<h:form id="menuform">
|
||||
<fr:menu widgetVar="FreyaMenuWidget">
|
||||
<!-- Dashboard -->
|
||||
<p:menuitem id="m_dashboard" value="Tableau de Bord" icon="pi pi-home" outcome="/pages/user-manager/dashboard" />
|
||||
|
||||
<!-- Gestion Utilisateurs -->
|
||||
<p:submenu id="m_users" label="Gestion Utilisateurs" icon="pi pi-users">
|
||||
<p:menuitem id="m_users_list" value="Liste des Utilisateurs" icon="pi pi-list" outcome="/pages/user-manager/users/list" />
|
||||
<p:menuitem id="m_users_create" value="Nouvel Utilisateur" icon="pi pi-user-plus" outcome="/pages/user-manager/users/create" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- Gestion Rôles -->
|
||||
<p:submenu id="m_roles" label="Gestion Rôles" icon="pi pi-shield">
|
||||
<p:menuitem id="m_roles_list" value="Liste des Rôles" icon="pi pi-list" outcome="/pages/user-manager/roles/list" />
|
||||
<p:menuitem id="m_roles_assign" value="Attribution Rôles" icon="pi pi-key" outcome="/pages/user-manager/roles/assign" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- Audit -->
|
||||
<p:submenu id="m_audit" label="Audit" icon="pi pi-history">
|
||||
<p:menuitem id="m_audit_logs" value="Journal d'Audit" icon="pi pi-file-o" outcome="/pages/user-manager/audit/logs" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- Synchronisation -->
|
||||
<p:submenu id="m_sync" label="Synchronisation" icon="pi pi-sync">
|
||||
<p:menuitem id="m_sync_dashboard" value="Dashboard" icon="pi pi-dashboard" outcome="/pages/user-manager/sync/dashboard" />
|
||||
</p:submenu>
|
||||
</fr:menu>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<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 en-tête de page réutilisable (WOU/DRY Pattern)
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: En-tête de page avec icône, titre, description et actions
|
||||
|
||||
Paramètres:
|
||||
- icon: String (optionnel) - Classe d'icône PrimeIcons (ex: "pi pi-users text-blue-500")
|
||||
- title: String (requis) - Titre de la page
|
||||
- description: String (optionnel) - Description de la page
|
||||
|
||||
Usage:
|
||||
<ui:include src="/templates/components/layout/page-header.xhtml">
|
||||
<ui:param name="icon" value="pi pi-users text-blue-500" />
|
||||
<ui:param name="title" value="Gestion des Utilisateurs" />
|
||||
<ui:param name="description" value="Gestion centralisée des utilisateurs Keycloak" />
|
||||
<ui:define name="actions">
|
||||
Boutons d'action ici
|
||||
</ui:define>
|
||||
</ui:include>
|
||||
-->
|
||||
|
||||
<div class="grid mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<h3 class="mb-2">
|
||||
<c:if test="#{not empty icon}">
|
||||
<i class="#{icon} mr-2"></i>
|
||||
</c:if>
|
||||
#{title}
|
||||
</h3>
|
||||
<p class="text-600 m-0" rendered="#{not empty description}">#{description}</p>
|
||||
</div>
|
||||
<div>
|
||||
<ui:insert name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
<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">
|
||||
|
||||
<!--
|
||||
Composant réutilisable: Topbar (WOU/DRY Pattern)
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Barre supérieure avec logo, menu et profil utilisateur
|
||||
-->
|
||||
|
||||
<div class="layout-topbar">
|
||||
<div class="layout-topbar-wrapper">
|
||||
<div class="layout-topbar-left">
|
||||
<a href="#" class="menu-button">
|
||||
<i class="pi pi-bars"/>
|
||||
</a>
|
||||
<h:link id="logolink" outcome="/pages/user-manager/dashboard" styleClass="layout-topbar-logo">
|
||||
<p:graphicImage name="images/#{guestPreferences.lightLogo ? 'logo-freya-white.svg' : 'logo-freya.svg'}" library="freya-layout" />
|
||||
</h:link>
|
||||
</div>
|
||||
|
||||
<ui:include src="/templates/components/layout/menu.xhtml" />
|
||||
|
||||
<div class="layout-topbar-right">
|
||||
<ul class="layout-topbar-actions">
|
||||
<li class="topbar-item user-profile">
|
||||
<a href="#" class="user-profile-link">
|
||||
<div class="bg-primary text-white border-circle flex align-items-center justify-content-center user-avatar"
|
||||
style="width: 36px; height: 36px; font-size: 14px; font-weight: bold; margin-right: 0.75rem;">
|
||||
#{userSessionBean.initials}
|
||||
</div>
|
||||
<span class="user-info">
|
||||
<span class="user-name">
|
||||
#{userSessionBean.fullName}
|
||||
<span class="user-status online"></span>
|
||||
<span class="user-separator">|</span>
|
||||
<span class="user-role">#{userSessionBean.primaryRole}</span>
|
||||
</span>
|
||||
<span class="user-email">#{userSessionBean.email}</span>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="user-dropdown-menu">
|
||||
<!-- En-tête du menu avec infos utilisateur -->
|
||||
<li class="user-dropdown-header">
|
||||
<div class="user-dropdown-avatar">
|
||||
<div class="bg-primary text-white border-circle flex align-items-center justify-content-center"
|
||||
style="width: 48px; height: 48px; font-size: 20px; font-weight: bold;">
|
||||
#{userSessionBean.initials}
|
||||
</div>
|
||||
<span class="user-status-indicator online"></span>
|
||||
</div>
|
||||
<div class="user-dropdown-info">
|
||||
<div class="user-dropdown-name text-900 font-semibold">#{userSessionBean.fullName}</div>
|
||||
<div class="user-dropdown-email text-600 text-sm">#{userSessionBean.email}</div>
|
||||
<div class="user-dropdown-role text-500 text-xs">#{userSessionBean.primaryRole}</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Séparateur -->
|
||||
<li class="user-dropdown-divider"></li>
|
||||
|
||||
<!-- Actions principales -->
|
||||
<li class="user-dropdown-section">
|
||||
<div class="section-title">Mon Compte</div>
|
||||
<div class="section-items">
|
||||
<h:link outcome="/pages/user-manager/users/profile" styleClass="dropdown-item">
|
||||
<i class="pi pi-user"></i>
|
||||
<span>Mon Profil</span>
|
||||
<i class="pi pi-angle-right item-arrow"></i>
|
||||
</h:link>
|
||||
<h:link outcome="/pages/user-manager/settings" styleClass="dropdown-item">
|
||||
<i class="pi pi-cog"></i>
|
||||
<span>Paramètres</span>
|
||||
<i class="pi pi-angle-right item-arrow"></i>
|
||||
</h:link>
|
||||
<a href="#" class="dropdown-item">
|
||||
<i class="pi pi-shield"></i>
|
||||
<span>Sécurité</span>
|
||||
<i class="pi pi-angle-right item-arrow"></i>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Séparateur -->
|
||||
<li class="user-dropdown-divider"></li>
|
||||
|
||||
<!-- Actions système -->
|
||||
<li class="user-dropdown-section">
|
||||
<div class="section-items">
|
||||
<a href="#" class="dropdown-item">
|
||||
<i class="pi pi-question-circle"></i>
|
||||
<span>Aide & Support</span>
|
||||
</a>
|
||||
<h:form>
|
||||
<p:commandLink action="#{userSessionBean.logout}" styleClass="dropdown-item logout-item">
|
||||
<i class="pi pi-sign-out"></i>
|
||||
<span>Déconnexion</span>
|
||||
</p:commandLink>
|
||||
</h:form>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<a href="#" class="layout-rightpanel-button">
|
||||
<i class="pi pi-arrow-left"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
<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: Attribution de Rôles (WOU/DRY Pattern)
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Interface pour attribuer/révoquer des rôles à un utilisateur
|
||||
|
||||
Paramètres:
|
||||
- user: UserDTO (requis) - L'utilisateur concerné
|
||||
- availableRoles: List<RoleDTO> (requis) - Liste des rôles disponibles
|
||||
- userRoles: List<RoleDTO> (requis) - Liste des rôles de l'utilisateur
|
||||
- roleBean: String (optionnel) - Nom du bean pour les actions (défaut: "roleGestionBean")
|
||||
- update: String (défaut: "@form") - Composants à mettre à jour
|
||||
- showRealmRoles: Boolean (défaut: true) - Afficher les rôles Realm
|
||||
- showClientRoles: Boolean (défaut: true) - Afficher les rôles Client
|
||||
- formId: String (défaut: "roleAssignmentForm") - ID du formulaire
|
||||
|
||||
Exemples d'utilisation:
|
||||
|
||||
1. Attribution simple:
|
||||
<ui:include src="/templates/components/role-management/role-assignment.xhtml">
|
||||
<ui:param name="user" value="#{userBean.selectedUser}" />
|
||||
<ui:param name="availableRoles" value="#{roleBean.availableRoles}" />
|
||||
<ui:param name="userRoles" value="#{userBean.userRoles}" />
|
||||
<ui:param name="assignAction" value="#{roleBean.assignRole}" />
|
||||
<ui:param name="revokeAction" value="#{roleBean.revokeRole}" />
|
||||
</ui:include>
|
||||
-->
|
||||
|
||||
<c:set var="formId" value="#{empty formId ? 'roleAssignmentForm' : formId}" />
|
||||
<c:set var="update" value="#{empty update ? '@form' : update}" />
|
||||
<c:set var="showRealmRoles" value="#{empty showRealmRoles ? true : showRealmRoles}" />
|
||||
<c:set var="showClientRoles" value="#{empty showClientRoles ? true : showClientRoles}" />
|
||||
<c:set var="roleBeanName" value="#{empty roleBean ? 'roleGestionBean' : roleBean}" />
|
||||
|
||||
<h:form id="#{formId}">
|
||||
<p:panel header="Attribution de rôles - #{user.username}" styleClass="w-full">
|
||||
|
||||
<!-- Rôles actuels de l'utilisateur -->
|
||||
<h3>Rôles actuels</h3>
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
<c:forEach var="userRole" items="#{userRoles}">
|
||||
<p:tag
|
||||
value="#{userRole.name}"
|
||||
severity="info"
|
||||
icon="pi pi-shield">
|
||||
<p:commandButton
|
||||
icon="pi pi-times"
|
||||
styleClass="p-button-text p-button-sm p-button-danger ml-2"
|
||||
action="#{roleGestionBean.revokeRoleFromParams}"
|
||||
update="#{update}"
|
||||
title="Révoquer le rôle">
|
||||
<f:param name="userId" value="#{user.id}" />
|
||||
<f:param name="roleName" value="#{userRole.name}" />
|
||||
</p:commandButton>
|
||||
</p:tag>
|
||||
</c:forEach>
|
||||
<c:if test="#{empty userRoles}">
|
||||
<span class="text-color-secondary">Aucun rôle attribué</span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<p:separator />
|
||||
|
||||
<!-- Rôles disponibles -->
|
||||
<h3>Rôles disponibles</h3>
|
||||
|
||||
<!-- Rôles Realm -->
|
||||
<c:if test="#{showRealmRoles}">
|
||||
<p:panel header="Rôles Realm" styleClass="mb-3">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<c:forEach var="role" items="#{availableRoles}">
|
||||
<c:if test="#{role.typeRole == 'REALM_ROLE'}">
|
||||
<c:set var="isAssigned" value="false" />
|
||||
<c:forEach var="userRole" items="#{userRoles}">
|
||||
<c:if test="#{userRole.id == role.id}">
|
||||
<c:set var="isAssigned" value="true" />
|
||||
</c:if>
|
||||
</c:forEach>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="#{isAssigned}">
|
||||
<p:tag
|
||||
value="#{role.name}"
|
||||
severity="success"
|
||||
icon="pi pi-check" />
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<p:tag
|
||||
value="#{role.name}"
|
||||
severity="info"
|
||||
icon="pi pi-shield">
|
||||
<p:commandButton
|
||||
icon="pi pi-plus"
|
||||
styleClass="p-button-text p-button-sm p-button-success ml-2"
|
||||
action="#{roleGestionBean.assignRoleFromParams}"
|
||||
update="#{update}"
|
||||
title="Attribuer le rôle">
|
||||
<f:param name="userId" value="#{user.id}" />
|
||||
<f:param name="roleName" value="#{role.name}" />
|
||||
</p:commandButton>
|
||||
</p:tag>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</c:if>
|
||||
</c:forEach>
|
||||
</div>
|
||||
</p:panel>
|
||||
</c:if>
|
||||
|
||||
<!-- Rôles Client -->
|
||||
<c:if test="#{showClientRoles}">
|
||||
<p:panel header="Rôles Client" styleClass="mb-3">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<c:forEach var="role" items="#{availableRoles}">
|
||||
<c:if test="#{role.typeRole == 'CLIENT_ROLE'}">
|
||||
<c:set var="isAssigned" value="false" />
|
||||
<c:forEach var="userRole" items="#{userRoles}">
|
||||
<c:if test="#{userRole.id == role.id}">
|
||||
<c:set var="isAssigned" value="true" />
|
||||
</c:if>
|
||||
</c:forEach>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="#{isAssigned}">
|
||||
<p:tag
|
||||
value="#{role.name}"
|
||||
severity="success"
|
||||
icon="pi pi-check" />
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<p:tag
|
||||
value="#{role.name}"
|
||||
severity="info"
|
||||
icon="pi pi-shield">
|
||||
<p:commandButton
|
||||
icon="pi pi-plus"
|
||||
styleClass="p-button-text p-button-sm p-button-success ml-2"
|
||||
action="#{roleGestionBean.assignRoleFromParams}"
|
||||
update="#{update}"
|
||||
title="Attribuer le rôle">
|
||||
<f:param name="userId" value="#{user.id}" />
|
||||
<f:param name="roleName" value="#{role.name}" />
|
||||
</p:commandButton>
|
||||
</p:tag>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</c:if>
|
||||
</c:forEach>
|
||||
</div>
|
||||
</p:panel>
|
||||
</c:if>
|
||||
|
||||
<!-- Recherche de rôles -->
|
||||
<p:separator />
|
||||
<h3>Rechercher un rôle</h3>
|
||||
<div class="flex gap-2 mb-3">
|
||||
<p:inputText
|
||||
value="#{roleGestionBean.roleSearchText}"
|
||||
placeholder="Rechercher un rôle..."
|
||||
styleClass="flex-1">
|
||||
<p:ajax event="keyup"
|
||||
delay="300"
|
||||
update="@parent" />
|
||||
</p:inputText>
|
||||
</div>
|
||||
|
||||
<div id="roleSearchResults" class="flex flex-wrap gap-2">
|
||||
<!-- Résultats de recherche -->
|
||||
</div>
|
||||
|
||||
</p:panel>
|
||||
</h:form>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
<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"
|
||||
xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions">
|
||||
|
||||
<!--
|
||||
Composant réutilisable: Carte Rôle (WOU/DRY Pattern)
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Affiche une carte rôle avec informations principales et actions
|
||||
|
||||
Paramètres:
|
||||
- role: RoleDTO (requis) - Le rôle à afficher
|
||||
- showActions: Boolean (défaut: true) - Afficher les boutons d'action
|
||||
- showDescription: Boolean (défaut: true) - Afficher la description
|
||||
- showCompositeInfo: Boolean (défaut: true) - Afficher les infos de rôle composite
|
||||
- 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/role-management/role-card.xhtml">
|
||||
<ui:param name="role" value="#{roleBean.selectedRole}" />
|
||||
</ui:include>
|
||||
|
||||
2. Carte avec actions:
|
||||
<ui:include src="/templates/components/role-management/role-card.xhtml">
|
||||
<ui:param name="role" value="#{roleBean.selectedRole}" />
|
||||
<ui:param name="showActions" value="true" />
|
||||
<ui:param name="outcome" value="/pages/user-manager/roles/details" />
|
||||
</ui:include>
|
||||
-->
|
||||
|
||||
<c:set var="showActions" value="#{empty showActions ? true : showActions}" />
|
||||
<c:set var="showDescription" value="#{empty showDescription ? true : showDescription}" />
|
||||
<c:set var="showCompositeInfo" value="#{empty showCompositeInfo ? true : showCompositeInfo}" />
|
||||
<c:set var="clickable" value="#{empty clickable ? true : clickable}" />
|
||||
|
||||
<p:card styleClass="role-card #{styleClass}" rendered="#{not empty role}">
|
||||
<f:facet name="header">
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-shield text-2xl text-primary"></i>
|
||||
<div class="flex flex-column">
|
||||
<h3 class="m-0">#{role.name}</h3>
|
||||
<span class="text-color-secondary text-sm">
|
||||
<c:choose>
|
||||
<c:when test="#{role.typeRole == 'REALM_ROLE'}">Rôle Realm</c:when>
|
||||
<c:when test="#{role.typeRole == 'CLIENT_ROLE'}">Rôle Client</c:when>
|
||||
<c:when test="#{role.typeRole == 'COMPOSITE_ROLE'}">Rôle Composite</c:when>
|
||||
<c:otherwise>Rôle</c:otherwise>
|
||||
</c:choose>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p:tag
|
||||
value="#{role.typeRole != null ? role.typeRole : 'ROLE'}"
|
||||
severity="#{role.typeRole == 'REALM_ROLE' ? 'success' : role.typeRole == 'CLIENT_ROLE' ? 'info' : 'warning'}" />
|
||||
</div>
|
||||
</f:facet>
|
||||
|
||||
<div class="role-card-content">
|
||||
<!-- Description -->
|
||||
<c:if test="#{showDescription and not empty role.description}">
|
||||
<p class="text-color-secondary mb-3">#{role.description}</p>
|
||||
</c:if>
|
||||
|
||||
<!-- Informations -->
|
||||
<div class="flex flex-column gap-2 mb-3">
|
||||
<c:if test="#{not empty role.realmName}">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-globe text-color-secondary"></i>
|
||||
<span>Realm: <strong>#{role.realmName}</strong></span>
|
||||
</div>
|
||||
</c:if>
|
||||
|
||||
<c:if test="#{not empty role.clientId}">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-desktop text-color-secondary"></i>
|
||||
<span>Client: <strong>#{role.clientId}</strong></span>
|
||||
</div>
|
||||
</c:if>
|
||||
|
||||
<!-- Rôle composite -->
|
||||
<c:if test="#{showCompositeInfo and role.composite}">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-sitemap text-color-secondary"></i>
|
||||
<span class="text-warning">Rôle composite</span>
|
||||
</div>
|
||||
</c:if>
|
||||
</div>
|
||||
</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 les détails"
|
||||
styleClass="p-button-text p-button-sm"
|
||||
outcome="#{not empty outcome ? outcome : '/pages/user-manager/roles/details'}"
|
||||
rendered="#{clickable}">
|
||||
<f:param name="roleId" value="#{role.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/roles/edit">
|
||||
<f:param name="roleId" value="#{role.id}" />
|
||||
</p:commandButton>
|
||||
|
||||
<p:commandButton
|
||||
icon="pi pi-trash"
|
||||
title="Supprimer"
|
||||
styleClass="p-button-text p-button-sm p-button-danger"
|
||||
onclick="PF('confirmDeleteRoleDialog_#{fn:replace(fn:replace(role.id != null ? role.id : role.name, '-', '_'), ':', '_')}').show()" />
|
||||
</div>
|
||||
</c:if>
|
||||
</f:facet>
|
||||
</p:card>
|
||||
|
||||
<!-- Dialog de confirmation de suppression avec ID unique basé sur l'ID ou le nom du rôle -->
|
||||
<c:if test="#{not empty role.name}">
|
||||
<c:set var="dialogId" value="confirmDeleteRoleDialog_#{fn:replace(fn:replace(role.id != null ? role.id : role.name, '-', '_'), ':', '_')}" />
|
||||
<p:confirmDialog
|
||||
id="#{dialogId}"
|
||||
widgetVar="#{dialogId}"
|
||||
message="Êtes-vous sûr de vouloir supprimer le rôle #{role.name} ?"
|
||||
header="Confirmation de suppression"
|
||||
severity="warn">
|
||||
<p:commandButton
|
||||
value="Oui"
|
||||
icon="pi pi-check"
|
||||
styleClass="p-button-danger"
|
||||
action="#{roleGestionBean.deleteRealmRole(role.name)}"
|
||||
update="@form"
|
||||
oncomplete="PF('#{dialogId}').hide()" />
|
||||
<p:commandButton
|
||||
value="Non"
|
||||
icon="pi pi-times"
|
||||
styleClass="p-button-secondary"
|
||||
onclick="PF('#{dialogId}').hide()" />
|
||||
</p:confirmDialog>
|
||||
</c:if>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
<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' ? 'Nouveau Rôle' : 'Modifier Rôle'}"
|
||||
styleClass="w-full">
|
||||
|
||||
<p:panelGrid columns="2" styleClass="w-full" columnClasses="col-12 md:col-6">
|
||||
|
||||
<!-- Nom du rôle -->
|
||||
<p:outputLabel for="roleName" value="Nom du rôle *" />
|
||||
<p:inputText id="roleName"
|
||||
value="#{role.name}"
|
||||
required="true"
|
||||
readonly="#{readonly}"
|
||||
placeholder="ADMIN, USER, MODERATOR..."
|
||||
styleClass="w-full">
|
||||
<f:validateLength minimum="2" maximum="100" />
|
||||
<f:validateRegex pattern="^[A-Z_]+$" />
|
||||
</p:inputText>
|
||||
|
||||
<!-- Description -->
|
||||
<p:outputLabel for="description" value="Description" />
|
||||
<p:inputTextarea id="description"
|
||||
value="#{role.description}"
|
||||
readonly="#{readonly}"
|
||||
placeholder="Description du rôle..."
|
||||
rows="3"
|
||||
styleClass="w-full" />
|
||||
|
||||
<!-- Type de rôle -->
|
||||
<p:outputLabel for="typeRole" value="Type de rôle *" />
|
||||
<p:selectOneMenu id="typeRole"
|
||||
value="#{role.typeRole}"
|
||||
required="true"
|
||||
readonly="#{readonly or mode == 'edit'}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemLabel="Sélectionner..." itemValue="" />
|
||||
<f:selectItem itemLabel="Rôle Realm" itemValue="REALM_ROLE" />
|
||||
<f:selectItem itemLabel="Rôle Client" itemValue="CLIENT_ROLE" />
|
||||
<f:selectItem itemLabel="Rôle Composite" itemValue="COMPOSITE_ROLE" />
|
||||
</p:selectOneMenu>
|
||||
|
||||
<!-- Realm (si affiché) -->
|
||||
<c:if test="#{showRealmSelector}">
|
||||
<p:outputLabel for="realmName" value="Realm *" />
|
||||
<p:selectOneMenu id="realmName"
|
||||
value="#{role.realmName}"
|
||||
required="#{showRealmSelector}"
|
||||
readonly="#{readonly}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemLabel="Sélectionner..." itemValue="" />
|
||||
<f:selectItems value="#{roleBean.availableRealms}" />
|
||||
</p:selectOneMenu>
|
||||
</c:if>
|
||||
|
||||
<!-- Client (si affiché) -->
|
||||
<c:if test="#{showClientSelector}">
|
||||
<p:outputLabel for="clientId" value="Client *" />
|
||||
<p:selectOneMenu id="clientId"
|
||||
value="#{role.clientId}"
|
||||
required="#{showClientSelector}"
|
||||
readonly="#{readonly}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemLabel="Sélectionner..." itemValue="" />
|
||||
<f:selectItems value="#{roleBean.availableClients}" />
|
||||
</p:selectOneMenu>
|
||||
</c:if>
|
||||
|
||||
<!-- Rôle composite -->
|
||||
<c:if test="#{showCompositeOptions}">
|
||||
<p:outputLabel for="composite" value="Rôle composite" />
|
||||
<p:selectBooleanCheckbox id="composite"
|
||||
value="#{role.composite}"
|
||||
readonly="#{readonly}" />
|
||||
</c:if>
|
||||
|
||||
</p:panelGrid>
|
||||
|
||||
<!-- 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"
|
||||
onclick="PF('createRealmRoleDialog').hide(); PF('createClientRoleDialog').hide();"
|
||||
immediate="true" />
|
||||
</div>
|
||||
</f:facet>
|
||||
</p:panel>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<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 Rôle (WOU/DRY Pattern)
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Formulaire complet pour créer/modifier un rôle
|
||||
|
||||
Paramètres:
|
||||
- role: RoleDTO (requis) - Le rôle à éditer (peut être null pour création)
|
||||
- formId: String (défaut: "roleForm") - ID du formulaire
|
||||
- mode: String (défaut: "create") - Mode: "create" ou "edit"
|
||||
- showRealmSelector: Boolean (défaut: true) - Afficher le sélecteur de realm
|
||||
- showClientSelector: Boolean (défaut: false) - Afficher le sélecteur de client
|
||||
- showCompositeOptions: Boolean (défaut: true) - Afficher les options composite
|
||||
- readonly: Boolean (défaut: false) - Mode lecture seule
|
||||
- submitAction: String (optionnel) - Action à exécuter à la soumission
|
||||
- submitOutcome: String (optionnel) - Page de redirection après soumission
|
||||
- update: String (optionnel) - Composants à mettre à jour après soumission
|
||||
|
||||
Exemples d'utilisation:
|
||||
|
||||
1. Formulaire de création rôle Realm:
|
||||
<ui:include src="/templates/components/role-management/role-form.xhtml">
|
||||
<ui:param name="role" value="#{roleBean.newRole}" />
|
||||
<ui:param name="mode" value="create" />
|
||||
<ui:param name="showClientSelector" value="false" />
|
||||
<ui:param name="submitAction" value="#{roleBean.createRole}" />
|
||||
</ui:include>
|
||||
|
||||
2. Formulaire de création rôle Client:
|
||||
<ui:include src="/templates/components/role-management/role-form.xhtml">
|
||||
<ui:param name="role" value="#{roleBean.newRole}" />
|
||||
<ui:param name="mode" value="create" />
|
||||
<ui:param name="showClientSelector" value="true" />
|
||||
<ui:param name="submitAction" value="#{roleBean.createClientRole}" />
|
||||
</ui:include>
|
||||
-->
|
||||
|
||||
<c:set var="formId" value="#{empty formId ? 'roleForm' : formId}" />
|
||||
<c:set var="mode" value="#{empty mode ? 'create' : mode}" />
|
||||
<c:set var="showRealmSelector" value="#{empty showRealmSelector ? true : showRealmSelector}" />
|
||||
<c:set var="showClientSelector" value="#{empty showClientSelector ? false : showClientSelector}" />
|
||||
<c:set var="showCompositeOptions" value="#{empty showCompositeOptions ? true : showCompositeOptions}" />
|
||||
<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 h:form ici) -->
|
||||
<ui:include src="/templates/components/role-management/role-form-content.xhtml">
|
||||
<ui:param name="role" value="#{role}" />
|
||||
<ui:param name="mode" value="#{mode}" />
|
||||
<ui:param name="showRealmSelector" value="#{showRealmSelector}" />
|
||||
<ui:param name="showClientSelector" value="#{showClientSelector}" />
|
||||
<ui:param name="showCompositeOptions" value="#{showCompositeOptions}" />
|
||||
<ui:param name="readonly" value="#{readonly}" />
|
||||
<ui:param name="hasSubmitAction" value="#{hasSubmitAction}" />
|
||||
<ui:param name="submitAction" value="#{submitAction}" />
|
||||
<ui:param name="submitOutcome" value="#{submitOutcome}" />
|
||||
<ui:param name="update" value="#{update}" />
|
||||
<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/role-management/role-form-content.xhtml">
|
||||
<ui:param name="role" value="#{role}" />
|
||||
<ui:param name="mode" value="#{mode}" />
|
||||
<ui:param name="showRealmSelector" value="#{showRealmSelector}" />
|
||||
<ui:param name="showClientSelector" value="#{showClientSelector}" />
|
||||
<ui:param name="showCompositeOptions" value="#{showCompositeOptions}" />
|
||||
<ui:param name="readonly" value="#{readonly}" />
|
||||
<ui:param name="hasSubmitAction" value="#{hasSubmitAction}" />
|
||||
<ui:param name="submitAction" value="#{submitAction}" />
|
||||
<ui:param name="submitOutcome" value="#{submitOutcome}" />
|
||||
<ui:param name="update" value="#{update}" />
|
||||
<ui:param name="cancelOutcome" value="#{cancelOutcome}" />
|
||||
</ui:include>
|
||||
</h:form>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
# 📊 Composants KPI Réutilisables - Écosystème LionsDev
|
||||
|
||||
**Version**: 2.0.0
|
||||
**Date**: 2025-01-29
|
||||
**Pattern**: WOU/DRY (Write Once Use / Don't Repeat Yourself)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Vue d'Ensemble
|
||||
|
||||
Ces composants KPI (Indicateurs de Performance) sont conçus pour être **100% réutilisables** dans tous les projets de l'écosystème **lionsdev**. Ils suivent les meilleures pratiques de PrimeFaces Freya et offrent une expérience utilisateur cohérente.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Composants Disponibles
|
||||
|
||||
### 1. **kpi-card.xhtml** - Carte KPI Individuelle
|
||||
|
||||
Composant principal pour afficher un indicateur de performance unique.
|
||||
|
||||
**Localisation**: `/templates/components/shared/cards/kpi-card.xhtml`
|
||||
|
||||
**Paramètres principaux**:
|
||||
- `title` (requis) - Titre du KPI
|
||||
- `value` (requis) - Valeur à afficher
|
||||
- `icon` (requis) - Icône PrimeIcons
|
||||
- `iconColor` (requis) - Couleur de l'icône
|
||||
- `growthValue` (optionnel) - Valeur de croissance
|
||||
- `growthLabel` (optionnel) - Libellé de croissance
|
||||
- `progressValue` (optionnel) - Barre de progression 0-100
|
||||
- `statusIcon`, `statusLabel`, `statusValue` (optionnel) - Mode statut
|
||||
- `clickable` (défaut: false) - Rendre cliquable
|
||||
- `clickOutcome` (optionnel) - Redirection au clic
|
||||
|
||||
**Exemple simple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Total Utilisateurs" />
|
||||
<ui:param name="value" value="#{bean.totalUsers}" />
|
||||
<ui:param name="icon" value="pi-users" />
|
||||
<ui:param name="iconColor" value="blue-600" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
**Exemple avec croissance**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Utilisateurs Actifs" />
|
||||
<ui:param name="value" value="#{bean.activeUsers}" />
|
||||
<ui:param name="icon" value="pi-user-check" />
|
||||
<ui:param name="iconColor" value="green-600" />
|
||||
<ui:param name="growthValue" value="#{bean.growthPercent}" />
|
||||
<ui:param name="growthLabel" value="ce mois" />
|
||||
<ui:param name="progressValue" value="#{bean.progressPercent}" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
**Exemple avec statut**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Sessions Actives" />
|
||||
<ui:param name="value" value="#{bean.activeSessions}" />
|
||||
<ui:param name="icon" value="pi-sign-in" />
|
||||
<ui:param name="iconColor" value="purple-600" />
|
||||
<ui:param name="statusIcon" value="pi-check-circle" />
|
||||
<ui:param name="statusLabel" value="En ligne" />
|
||||
<ui:param name="statusValue" value="#{bean.onlineUsers} actifs" />
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. **kpi-group.xhtml** - Groupe de KPI
|
||||
|
||||
Composant composite pour organiser plusieurs KPI dans une grille.
|
||||
|
||||
**Localisation**: `/templates/components/shared/dashboard/kpi-group.xhtml`
|
||||
|
||||
**Paramètres**:
|
||||
- `title` (optionnel) - Titre de la section
|
||||
- `columns` (défaut: 4) - Nombre de colonnes (1-6)
|
||||
- `colSize` (optionnel) - Taille personnalisée
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/shared/dashboard/kpi-group.xhtml">
|
||||
<ui:param name="title" value="Statistiques Utilisateurs" />
|
||||
<ui:param name="columns" value="4" />
|
||||
<ui:define name="kpi-content">
|
||||
<!-- KPI 1 -->
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Total" />
|
||||
<ui:param name="value" value="#{bean.total}" />
|
||||
<ui:param name="icon" value="pi-users" />
|
||||
<ui:param name="iconColor" value="blue-600" />
|
||||
</ui:include>
|
||||
<!-- KPI 2, 3, 4... -->
|
||||
</ui:define>
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. **dashboard-section.xhtml** - Section Dashboard
|
||||
|
||||
Composant composite pour créer des sections de dashboard réutilisables.
|
||||
|
||||
**Localisation**: `/templates/components/shared/dashboard/dashboard-section.xhtml`
|
||||
|
||||
**Paramètres**:
|
||||
- `title` (requis) - Titre de la section
|
||||
- `description` (optionnel) - Description
|
||||
- `icon` (optionnel) - Icône
|
||||
- `colSize` (défaut: "col-12") - Taille de colonne
|
||||
- `showCard` (défaut: true) - Envelopper dans une carte
|
||||
|
||||
**Exemple**:
|
||||
```xhtml
|
||||
<ui:include src="/templates/components/shared/dashboard/dashboard-section.xhtml">
|
||||
<ui:param name="title" value="Actions Rapides" />
|
||||
<ui:param name="icon" value="pi-bolt" />
|
||||
<ui:param name="colSize" value="col-12 lg:col-6" />
|
||||
<ui:define name="section-content">
|
||||
<!-- Contenu de la section -->
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6">
|
||||
<p:commandButton value="Action 1" />
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:include>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. **user-stat-card.xhtml** - Wrapper de Compatibilité
|
||||
|
||||
Wrapper de compatibilité ascendante qui délègue à `kpi-card.xhtml`.
|
||||
|
||||
**Note**: Pour de nouvelles implémentations, utilisez directement `kpi-card.xhtml`.
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Couleurs Disponibles
|
||||
|
||||
Utilisez les couleurs PrimeFlex pour `iconColor`:
|
||||
- `blue-600`, `blue-500`, `blue-400`
|
||||
- `green-600`, `green-500`, `green-400`
|
||||
- `purple-600`, `purple-500`, `purple-400`
|
||||
- `orange-600`, `orange-500`, `orange-400`
|
||||
- `red-600`, `red-500`, `red-400`
|
||||
- `cyan-600`, `cyan-500`, `cyan-400`
|
||||
- `pink-600`, `pink-500`, `pink-400`
|
||||
|
||||
---
|
||||
|
||||
## 📐 Tailles de Colonnes
|
||||
|
||||
Utilisez les classes PrimeFlex pour `colSize`:
|
||||
- `col-12` - Pleine largeur
|
||||
- `col-12 md:col-6` - 2 colonnes sur tablette+
|
||||
- `col-12 md:col-6 lg:col-3` - 4 colonnes sur desktop (défaut)
|
||||
- `col-12 md:col-4` - 3 colonnes
|
||||
- `col-12 md:col-6 lg:col-2` - 6 colonnes
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Réutilisabilité dans l'Écosystème LionsDev
|
||||
|
||||
Ces composants peuvent être utilisés dans:
|
||||
- ✅ **lions-user-manager** (module actuel)
|
||||
- ✅ **unionflow** (via dépendance Maven)
|
||||
- ✅ **btpxpress** (via dépendance Maven)
|
||||
- ✅ **Tout autre projet lionsdev** (via dépendance Maven)
|
||||
|
||||
**Avantages**:
|
||||
- Code DRY (Don't Repeat Yourself)
|
||||
- Interface utilisateur cohérente
|
||||
- Maintenance centralisée
|
||||
- Évolutivité garantie
|
||||
|
||||
---
|
||||
|
||||
## 📝 Bonnes Pratiques
|
||||
|
||||
1. **Toujours utiliser `kpi-card.xhtml`** pour de nouveaux KPI
|
||||
2. **Utiliser `kpi-group.xhtml`** pour organiser plusieurs KPI
|
||||
3. **Utiliser `dashboard-section.xhtml`** pour structurer les dashboards
|
||||
4. **Respecter les conventions de couleurs** PrimeFlex
|
||||
5. **Documenter les paramètres personnalisés** dans votre code
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Exemple Complet de Dashboard
|
||||
|
||||
```xhtml
|
||||
<div class="grid">
|
||||
<!-- En-tête -->
|
||||
<div class="col-12">
|
||||
<ui:include src="/templates/components/layout/page-header.xhtml">
|
||||
<ui:param name="title" value="Tableau de Bord" />
|
||||
</ui:include>
|
||||
</div>
|
||||
|
||||
<!-- KPIs -->
|
||||
<div class="col-12">
|
||||
<div class="grid">
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Total" />
|
||||
<ui:param name="value" value="#{bean.total}" />
|
||||
<ui:param name="icon" value="pi-users" />
|
||||
<ui:param name="iconColor" value="blue-600" />
|
||||
</ui:include>
|
||||
<!-- Autres KPI... -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sections -->
|
||||
<ui:include src="/templates/components/shared/dashboard/dashboard-section.xhtml">
|
||||
<ui:param name="title" value="Actions Rapides" />
|
||||
<ui:define name="section-content">
|
||||
<!-- Contenu -->
|
||||
</ui:define>
|
||||
</ui:include>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation Complète
|
||||
|
||||
Pour plus de détails, consultez:
|
||||
- `/templates/components/README.md` - Documentation générale
|
||||
- Code source des composants avec commentaires JSDoc
|
||||
|
||||
---
|
||||
|
||||
**Auteur**: Lions User Manager Team
|
||||
**Licence**: Écosystème LionsDev
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
<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: Bouton Action Utilisateur (WOU/DRY Pattern)
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Bouton générique pour actions utilisateur
|
||||
|
||||
Paramètres:
|
||||
- value: String (requis) - Texte du bouton
|
||||
- icon: String (optionnel) - Classe d'icône PrimeIcons
|
||||
- hasAction: Boolean (défaut: false) - Indique si une action est fournie
|
||||
- action: MethodExpression (optionnel) - Action à exécuter (requis si hasAction=true)
|
||||
- hasOutcome: Boolean (défaut: false) - Indique si un outcome est fourni
|
||||
- outcome: String (optionnel) - Page de redirection (requis si hasOutcome=true)
|
||||
- onclick: String (optionnel) - Code JavaScript à exécuter au clic
|
||||
- severity: String (défaut: "primary") - Severity: "primary", "success", "warning", "danger", "info", "secondary"
|
||||
- size: String (défaut: "normal") - Taille: "small", "normal", "large"
|
||||
- disabled: Boolean (défaut: false) - Désactiver le bouton
|
||||
- update: String (optionnel) - Composants à mettre à jour
|
||||
- process: String (défaut: "@this") - Composants à traiter
|
||||
- styleClass: String (optionnel) - Classes CSS supplémentaires
|
||||
|
||||
Exemples d'utilisation:
|
||||
|
||||
1. Bouton simple:
|
||||
<ui:include src="/templates/components/shared/buttons/button-user-action.xhtml">
|
||||
<ui:param name="value" value="Créer Utilisateur" />
|
||||
<ui:param name="icon" value="pi-user-plus" />
|
||||
<ui:param name="action" value="#{userBean.createUser}" />
|
||||
</ui:include>
|
||||
|
||||
2. Bouton avec redirection:
|
||||
<ui:include src="/templates/components/shared/buttons/button-user-action.xhtml">
|
||||
<ui:param name="value" value="Voir Profil" />
|
||||
<ui:param name="icon" value="pi-eye" />
|
||||
<ui:param name="outcome" value="/pages/user-manager/users/profile" />
|
||||
</ui:include>
|
||||
-->
|
||||
|
||||
<c:set var="severity" value="#{empty severity ? 'primary' : severity}" />
|
||||
<c:set var="size" value="#{empty size ? 'normal' : size}" />
|
||||
<c:set var="disabled" value="#{empty disabled ? false : disabled}" />
|
||||
<c:set var="process" value="#{empty process ? '@this' : process}" />
|
||||
<c:set var="hasAction" value="#{empty hasAction ? false : hasAction}" />
|
||||
<c:set var="hasOutcome" value="#{empty hasOutcome ? false : hasOutcome}" />
|
||||
|
||||
<!-- Déterminer la classe selon la severity -->
|
||||
<c:choose>
|
||||
<c:when test="#{severity == 'primary'}">
|
||||
<c:set var="buttonClass" value="p-button-primary" />
|
||||
</c:when>
|
||||
<c:when test="#{severity == 'success'}">
|
||||
<c:set var="buttonClass" value="p-button-success" />
|
||||
</c:when>
|
||||
<c:when test="#{severity == 'warning'}">
|
||||
<c:set var="buttonClass" value="p-button-warning" />
|
||||
</c:when>
|
||||
<c:when test="#{severity == 'danger'}">
|
||||
<c:set var="buttonClass" value="p-button-danger" />
|
||||
</c:when>
|
||||
<c:when test="#{severity == 'info'}">
|
||||
<c:set var="buttonClass" value="p-button-info" />
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<c:set var="buttonClass" value="p-button-secondary" />
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<!-- Ajouter la taille -->
|
||||
<c:if test="#{size == 'small'}">
|
||||
<c:set var="buttonClass" value="#{buttonClass} p-button-sm" />
|
||||
</c:if>
|
||||
<c:if test="#{size == 'large'}">
|
||||
<c:set var="buttonClass" value="#{buttonClass} p-button-lg" />
|
||||
</c:if>
|
||||
|
||||
<!-- Ajouter les classes personnalisées -->
|
||||
<c:if test="#{not empty styleClass}">
|
||||
<c:set var="buttonClass" value="#{buttonClass} #{styleClass}" />
|
||||
</c:if>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="#{hasAction}">
|
||||
<p:commandButton
|
||||
value="#{value}"
|
||||
icon="#{not empty icon ? icon : ''}"
|
||||
styleClass="#{buttonClass}"
|
||||
disabled="#{disabled}"
|
||||
action="#{action}"
|
||||
update="#{not empty update ? update : '@form'}"
|
||||
process="#{process}"
|
||||
onclick="#{not empty onclick ? onclick : ''}" />
|
||||
</c:when>
|
||||
<c:when test="#{hasOutcome}">
|
||||
<p:commandButton
|
||||
value="#{value}"
|
||||
icon="#{not empty icon ? icon : ''}"
|
||||
styleClass="#{buttonClass}"
|
||||
disabled="#{disabled}"
|
||||
outcome="#{outcome}"
|
||||
update="#{not empty update ? update : '@form'}"
|
||||
process="#{process}"
|
||||
onclick="#{not empty onclick ? onclick : ''}" />
|
||||
</c:when>
|
||||
<c:when test="#{not empty onclick}">
|
||||
<p:commandButton
|
||||
value="#{value}"
|
||||
icon="#{not empty icon ? icon : ''}"
|
||||
styleClass="#{buttonClass}"
|
||||
disabled="#{disabled}"
|
||||
type="button"
|
||||
onclick="#{onclick}" />
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<p:commandButton
|
||||
value="#{value}"
|
||||
icon="#{not empty icon ? icon : ''}"
|
||||
styleClass="#{buttonClass}"
|
||||
disabled="true"
|
||||
title="Aucune action définie" />
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
<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"
|
||||
xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions">
|
||||
|
||||
<div class="p-4" style="min-height: 9rem;">
|
||||
<!-- Header: Titre et Icône -->
|
||||
<div class="flex align-items-center justify-content-between mb-3">
|
||||
<span class="block text-600 font-medium text-sm">#{title}</span>
|
||||
<div class="flex align-items-center justify-content-center surface-100 border-round-lg"
|
||||
style="width: 2.5rem; height: 2.5rem;">
|
||||
<i class="pi #{icon} text-#{iconColor} text-lg"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Valeur principale -->
|
||||
<div class="text-900 font-bold text-2xl mb-2">
|
||||
<c:choose>
|
||||
<c:when test="#{not empty value}">
|
||||
<c:set var="valueStr" value="#{String.valueOf(value)}" />
|
||||
<c:choose>
|
||||
<c:when test="#{fn:startsWith(valueStr, '-') and fn:length(valueStr) == 1}">0</c:when>
|
||||
<c:when test="#{fn:startsWith(valueStr, '...')}">0</c:when>
|
||||
<c:otherwise>#{value}</c:otherwise>
|
||||
</c:choose>
|
||||
</c:when>
|
||||
<c:otherwise>0</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
|
||||
<!-- Sous-titre -->
|
||||
<c:if test="#{not empty subtitle}">
|
||||
<div class="text-500 text-xs mb-2">#{subtitle}</div>
|
||||
</c:if>
|
||||
|
||||
<!-- Section Croissance ou Statut -->
|
||||
<c:choose>
|
||||
<!-- Mode Statut (statusIcon fourni) -->
|
||||
<c:when test="#{not empty statusIcon}">
|
||||
<c:choose>
|
||||
<c:when test="#{not empty statusValue and statusValue != '0' and statusValue != '0.0'}">
|
||||
<div class="flex align-items-center mb-2">
|
||||
<i class="pi #{statusIcon} text-green-500 text-sm mr-2"></i>
|
||||
<span class="text-green-600 font-semibold text-sm mr-2">#{statusLabel}</span>
|
||||
<span class="text-500 text-xs">#{statusValue}</span>
|
||||
</div>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<div class="text-500 text-xs mb-2">Aucun #{statusLabel}</div>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</c:when>
|
||||
<!-- Mode Croissance -->
|
||||
<c:otherwise>
|
||||
<c:choose>
|
||||
<!-- Croissance en nombre -->
|
||||
<c:when test="#{growthType == 'number'}">
|
||||
<c:choose>
|
||||
<c:when test="#{showGrowth and not empty growthValue and growthValue != '0' and growthValue != '0.0'}">
|
||||
<div class="flex align-items-center mb-2">
|
||||
<i class="pi pi-arrow-up text-green-500 text-sm mr-2"></i>
|
||||
<span class="text-green-600 font-semibold text-sm mr-2">+#{growthValue}</span>
|
||||
<c:if test="#{not empty growthLabel}">
|
||||
<span class="text-500 text-xs">#{growthLabel}</span>
|
||||
</c:if>
|
||||
</div>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<div class="text-500 text-xs mb-2">#{noDataLabel}</div>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</c:when>
|
||||
<!-- Croissance en pourcentage (défaut) -->
|
||||
<c:otherwise>
|
||||
<c:choose>
|
||||
<c:when test="#{showGrowth and not empty growthValue and growthValue != '0' and growthValue != '0.0'}">
|
||||
<div class="flex align-items-center mb-2">
|
||||
<c:choose>
|
||||
<c:when test="#{growthValue >= 0}">
|
||||
<i class="pi pi-arrow-up text-green-500 text-sm mr-2"></i>
|
||||
<span class="text-green-600 font-semibold text-sm mr-2">+#{growthValue}%</span>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<i class="pi pi-arrow-down text-red-500 text-sm mr-2"></i>
|
||||
<span class="text-red-600 font-semibold text-sm mr-2">#{growthValue}%</span>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
<c:if test="#{not empty growthLabel}">
|
||||
<span class="text-500 text-xs">#{growthLabel}</span>
|
||||
</c:if>
|
||||
</div>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<div class="text-500 text-xs mb-2">#{noDataLabel}</div>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<!-- Barre de progression -->
|
||||
<c:if test="#{showProgress and not empty progressValue}">
|
||||
<p:progressBar value="#{progressValue}"
|
||||
showValue="false"
|
||||
styleClass="surface-200"
|
||||
style="height: 0.5rem; width: 100%;" />
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -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: Carte KPI (Indicateur de Performance) - Écosystème LionsDev
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 2.1.0
|
||||
Description: Carte KPI générique et réutilisable pour tous les projets de l'écosystème lionsdev
|
||||
-->
|
||||
|
||||
<c:set var="colSize" value="#{empty colSize ? 'col-12 md:col-6 lg:col-3' : colSize}" />
|
||||
<c:set var="clickable" value="#{empty clickable ? false : clickable}" />
|
||||
<c:set var="growthType" value="#{empty growthType ? 'percentage' : growthType}" />
|
||||
<c:set var="showGrowth" value="#{empty showGrowth ? (not empty growthValue) : showGrowth}" />
|
||||
<c:set var="showProgress" value="#{empty showProgress ? (not empty progressValue) : showProgress}" />
|
||||
<c:set var="noDataLabel" value="#{empty noDataLabel ? 'Données non disponibles' : noDataLabel}" />
|
||||
|
||||
<div class="#{colSize}">
|
||||
<c:choose>
|
||||
<c:when test="#{clickable and not empty clickOutcome}">
|
||||
<p:commandLink styleClass="card-link w-full #{styleClass}" outcome="#{clickOutcome}">
|
||||
<div class="card surface-0 hover:surface-100 border-round-lg transition-all transition-duration-200">
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card-content.xhtml">
|
||||
<ui:param name="title" value="#{title}" />
|
||||
<ui:param name="value" value="#{value}" />
|
||||
<ui:param name="icon" value="#{icon}" />
|
||||
<ui:param name="iconColor" value="#{iconColor}" />
|
||||
<ui:param name="subtitle" value="#{subtitle}" />
|
||||
<ui:param name="growthValue" value="#{growthValue}" />
|
||||
<ui:param name="growthLabel" value="#{growthLabel}" />
|
||||
<ui:param name="growthType" value="#{growthType}" />
|
||||
<ui:param name="showGrowth" value="#{showGrowth}" />
|
||||
<ui:param name="noDataLabel" value="#{noDataLabel}" />
|
||||
<ui:param name="progressValue" value="#{progressValue}" />
|
||||
<ui:param name="showProgress" value="#{showProgress}" />
|
||||
<ui:param name="statusIcon" value="#{statusIcon}" />
|
||||
<ui:param name="statusLabel" value="#{statusLabel}" />
|
||||
<ui:param name="statusValue" value="#{statusValue}" />
|
||||
</ui:include>
|
||||
</div>
|
||||
</p:commandLink>
|
||||
</c:when>
|
||||
<c:when test="#{clickable and not empty clickAction}">
|
||||
<p:commandLink styleClass="card-link w-full #{styleClass}" action="#{clickAction}">
|
||||
<div class="card surface-0 hover:surface-100 border-round-lg transition-all transition-duration-200">
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card-content.xhtml">
|
||||
<ui:param name="title" value="#{title}" />
|
||||
<ui:param name="value" value="#{value}" />
|
||||
<ui:param name="icon" value="#{icon}" />
|
||||
<ui:param name="iconColor" value="#{iconColor}" />
|
||||
<ui:param name="subtitle" value="#{subtitle}" />
|
||||
<ui:param name="growthValue" value="#{growthValue}" />
|
||||
<ui:param name="growthLabel" value="#{growthLabel}" />
|
||||
<ui:param name="growthType" value="#{growthType}" />
|
||||
<ui:param name="showGrowth" value="#{showGrowth}" />
|
||||
<ui:param name="noDataLabel" value="#{noDataLabel}" />
|
||||
<ui:param name="progressValue" value="#{progressValue}" />
|
||||
<ui:param name="showProgress" value="#{showProgress}" />
|
||||
<ui:param name="statusIcon" value="#{statusIcon}" />
|
||||
<ui:param name="statusLabel" value="#{statusLabel}" />
|
||||
<ui:param name="statusValue" value="#{statusValue}" />
|
||||
</ui:include>
|
||||
</div>
|
||||
</p:commandLink>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<div class="card surface-0 border-round-lg #{styleClass}">
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card-content.xhtml">
|
||||
<ui:param name="title" value="#{title}" />
|
||||
<ui:param name="value" value="#{value}" />
|
||||
<ui:param name="icon" value="#{icon}" />
|
||||
<ui:param name="iconColor" value="#{iconColor}" />
|
||||
<ui:param name="subtitle" value="#{subtitle}" />
|
||||
<ui:param name="growthValue" value="#{growthValue}" />
|
||||
<ui:param name="growthLabel" value="#{growthLabel}" />
|
||||
<ui:param name="growthType" value="#{growthType}" />
|
||||
<ui:param name="showGrowth" value="#{showGrowth}" />
|
||||
<ui:param name="noDataLabel" value="#{noDataLabel}" />
|
||||
<ui:param name="progressValue" value="#{progressValue}" />
|
||||
<ui:param name="showProgress" value="#{showProgress}" />
|
||||
<ui:param name="statusIcon" value="#{statusIcon}" />
|
||||
<ui:param name="statusLabel" value="#{statusLabel}" />
|
||||
<ui:param name="statusValue" value="#{statusValue}" />
|
||||
</ui:include>
|
||||
</div>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,67 @@
|
||||
<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 Statistique Utilisateur (WOU/DRY Pattern)
|
||||
Version 2.0.0 - Utilise maintenant kpi-card.xhtml en interne
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Description: Wrapper autour de kpi-card.xhtml pour compatibilité ascendante
|
||||
|
||||
Paramètres:
|
||||
- title: String (requis) - Titre de la carte
|
||||
- value: String/Number (requis) - Valeur à afficher
|
||||
- icon: String (requis) - Classe d'icône PrimeIcons
|
||||
- iconColor: String (requis) - Couleur de l'icône
|
||||
- subtitle: String (optionnel) - Sous-titre
|
||||
- trend: Number (optionnel) - Tendance (pourcentage) - Mappé vers growthValue
|
||||
- trendLabel: String (optionnel) - Libellé de la tendance - Mappé vers growthLabel
|
||||
- colSize: String (défaut: "col-12 md:col-6 lg:col-3") - Taille de colonne
|
||||
- clickable: Boolean (défaut: false) - Rendre la carte cliquable
|
||||
- clickOutcome: String (optionnel) - Page de redirection au clic
|
||||
|
||||
Note: Ce composant est un wrapper de compatibilité. Pour de nouvelles implémentations,
|
||||
utilisez directement kpi-card.xhtml qui offre plus de fonctionnalités.
|
||||
|
||||
Exemples d'utilisation:
|
||||
|
||||
1. Carte simple:
|
||||
<ui:include src="/templates/components/shared/cards/user-stat-card.xhtml">
|
||||
<ui:param name="title" value="Total Utilisateurs" />
|
||||
<ui:param name="value" value="#{userBean.totalUsers}" />
|
||||
<ui:param name="icon" value="pi-users" />
|
||||
<ui:param name="iconColor" value="blue-600" />
|
||||
</ui:include>
|
||||
|
||||
2. Carte avec tendance:
|
||||
<ui:include src="/templates/components/shared/cards/user-stat-card.xhtml">
|
||||
<ui:param name="title" value="Utilisateurs Actifs" />
|
||||
<ui:param name="value" value="#{userBean.activeUsers}" />
|
||||
<ui:param name="icon" value="pi-user-check" />
|
||||
<ui:param name="iconColor" value="green-600" />
|
||||
<ui:param name="trend" value="#{userBean.activeUsersTrend}" />
|
||||
<ui:param name="trendLabel" value="ce mois" />
|
||||
</ui:include>
|
||||
-->
|
||||
|
||||
<!-- Déléguer à kpi-card.xhtml -->
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="#{title}" />
|
||||
<ui:param name="value" value="#{value}" />
|
||||
<ui:param name="icon" value="#{icon}" />
|
||||
<ui:param name="iconColor" value="#{iconColor}" />
|
||||
<ui:param name="subtitle" value="#{subtitle}" />
|
||||
<ui:param name="growthValue" value="#{trend}" />
|
||||
<ui:param name="growthLabel" value="#{trendLabel}" />
|
||||
<ui:param name="growthType" value="percentage" />
|
||||
<ui:param name="colSize" value="#{colSize}" />
|
||||
<ui:param name="clickable" value="#{clickable}" />
|
||||
<ui:param name="clickOutcome" value="#{clickOutcome}" />
|
||||
</ui:include>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<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: Section Dashboard - Écosystème LionsDev
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Composant composite pour créer des sections de dashboard réutilisables
|
||||
|
||||
Paramètres:
|
||||
- title: String (requis) - Titre de la section
|
||||
- description: String (optionnel) - Description de la section
|
||||
- icon: String (optionnel) - Icône PrimeIcons
|
||||
- colSize: String (défaut: "col-12") - Taille de colonne
|
||||
- showCard: Boolean (défaut: true) - Envelopper dans une carte
|
||||
- styleClass: String (optionnel) - Classes CSS supplémentaires
|
||||
|
||||
Exemple:
|
||||
ui:include src="/templates/components/shared/dashboard/dashboard-section.xhtml"
|
||||
ui:param name="title" value="Actions Rapides"
|
||||
ui:param name="icon" value="pi-bolt"
|
||||
ui:param name="colSize" value="col-12 lg:col-6"
|
||||
ui:define name="section-content"
|
||||
Contenu de la section
|
||||
ui:define
|
||||
ui:include
|
||||
-->
|
||||
|
||||
<c:set var="colSize" value="#{empty colSize ? 'col-12' : colSize}" />
|
||||
<c:set var="showCard" value="#{empty showCard ? true : showCard}" />
|
||||
|
||||
<div class="#{colSize}">
|
||||
<c:choose>
|
||||
<c:when test="#{showCard}">
|
||||
<div class="card #{styleClass}">
|
||||
<c:if test="#{not empty title}">
|
||||
<div class="flex align-items-center mb-3">
|
||||
<c:if test="#{not empty icon}">
|
||||
<i class="pi #{icon} mr-2 text-primary"></i>
|
||||
</c:if>
|
||||
<h5 class="font-semibold mb-0">#{title}</h5>
|
||||
</div>
|
||||
</c:if>
|
||||
<c:if test="#{not empty description}">
|
||||
<p class="text-600 text-sm mb-3">#{description}</p>
|
||||
</c:if>
|
||||
<ui:insert name="section-content">
|
||||
<!-- Contenu de la section -->
|
||||
</ui:insert>
|
||||
</div>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<c:if test="#{not empty title}">
|
||||
<div class="flex align-items-center mb-3">
|
||||
<c:if test="#{not empty icon}">
|
||||
<i class="pi #{icon} mr-2 text-primary"></i>
|
||||
</c:if>
|
||||
<h5 class="font-semibold mb-0">#{title}</h5>
|
||||
</div>
|
||||
</c:if>
|
||||
<c:if test="#{not empty description}">
|
||||
<p class="text-600 text-sm mb-3">#{description}</p>
|
||||
</c:if>
|
||||
<ui:insert name="section-content">
|
||||
<!-- Contenu de la section -->
|
||||
</ui:insert>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<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: Groupe de KPI (Composite) - Écosystème LionsDev
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Composant composite pour afficher un groupe de KPI dans une grille
|
||||
|
||||
Paramètres:
|
||||
- title: String (optionnel) - Titre de la section
|
||||
- columns: Integer (défaut: 4) - Nombre de colonnes (1-12)
|
||||
- colSize: String (optionnel) - Taille de colonne personnalisée (ex: "col-12 md:col-6 lg:col-3")
|
||||
- showTitle: Boolean (défaut: true si title fourni) - Afficher le titre
|
||||
- styleClass: String (optionnel) - Classes CSS supplémentaires
|
||||
|
||||
Utilisation:
|
||||
Ce composant doit être utilisé avec des ui:param pour passer les KPI individuels.
|
||||
Chaque KPI doit être inclus avec kpi-card.xhtml.
|
||||
|
||||
Exemple:
|
||||
<ui:include src="/templates/components/shared/dashboard/kpi-group.xhtml">
|
||||
<ui:param name="title" value="Statistiques Utilisateurs" />
|
||||
<ui:param name="columns" value="4" />
|
||||
<ui:define name="kpi-content">
|
||||
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
|
||||
<ui:param name="title" value="Total" />
|
||||
<ui:param name="value" value="#{bean.total}" />
|
||||
<ui:param name="icon" value="pi-users" />
|
||||
<ui:param name="iconColor" value="blue-600" />
|
||||
</ui:include>
|
||||
Autres KPI à ajouter ici
|
||||
</ui:define>
|
||||
</ui:include>
|
||||
-->
|
||||
|
||||
<c:set var="columns" value="#{empty columns ? 4 : columns}" />
|
||||
<c:set var="showTitle" value="#{empty showTitle ? (not empty title) : showTitle}" />
|
||||
|
||||
<c:choose>
|
||||
<c:when test="#{columns == 1}">
|
||||
<c:set var="colSize" value="col-12" />
|
||||
</c:when>
|
||||
<c:when test="#{columns == 2}">
|
||||
<c:set var="colSize" value="col-12 md:col-6" />
|
||||
</c:when>
|
||||
<c:when test="#{columns == 3}">
|
||||
<c:set var="colSize" value="col-12 md:col-6 lg:col-4" />
|
||||
</c:when>
|
||||
<c:when test="#{columns == 4}">
|
||||
<c:set var="colSize" value="col-12 md:col-6 lg:col-3" />
|
||||
</c:when>
|
||||
<c:when test="#{columns == 6}">
|
||||
<c:set var="colSize" value="col-12 md:col-6 lg:col-2" />
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<c:set var="colSize" value="#{empty colSize ? 'col-12 md:col-6 lg:col-3' : colSize}" />
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<div class="mb-4 #{styleClass}">
|
||||
<c:if test="#{showTitle}">
|
||||
<h5 class="font-semibold mb-3">#{title}</h5>
|
||||
</c:if>
|
||||
<div class="grid">
|
||||
<ui:insert name="kpi-content">
|
||||
<!-- Les KPI seront insérés ici -->
|
||||
</ui:insert>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
<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: Champ Formulaire Utilisateur (WOU/DRY Pattern)
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Champ de formulaire générique pour utilisateur
|
||||
|
||||
Paramètres:
|
||||
- id: String (requis) - ID du champ
|
||||
- label: String (requis) - Label du champ
|
||||
- value: Object (requis) - Valeur du champ
|
||||
- type: String (défaut: "text") - Type: "text", "email", "password", "number", "textarea", "select", "checkbox", "calendar"
|
||||
- required: Boolean (défaut: false) - Champ requis
|
||||
- readonly: Boolean (défaut: false) - Mode lecture seule
|
||||
- placeholder: String (optionnel) - Placeholder
|
||||
- helpText: String (optionnel) - Texte d'aide
|
||||
- styleClass: String (optionnel) - Classes CSS supplémentaires
|
||||
- selectItems: List (optionnel) - Items pour select
|
||||
- rows: Number (optionnel, défaut: 3) - Nombre de lignes pour textarea
|
||||
|
||||
Exemples d'utilisation:
|
||||
|
||||
1. Champ texte:
|
||||
<ui:include src="/templates/components/shared/forms/user-form-field.xhtml">
|
||||
<ui:param name="id" value="username" />
|
||||
<ui:param name="label" value="Nom d'utilisateur" />
|
||||
<ui:param name="value" value="#{user.username}" />
|
||||
<ui:param name="required" value="true" />
|
||||
</ui:include>
|
||||
|
||||
2. Champ email:
|
||||
<ui:include src="/templates/components/shared/forms/user-form-field.xhtml">
|
||||
<ui:param name="id" value="email" />
|
||||
<ui:param name="label" value="Email" />
|
||||
<ui:param name="value" value="#{user.email}" />
|
||||
<ui:param name="type" value="email" />
|
||||
<ui:param name="required" value="true" />
|
||||
</ui:include>
|
||||
|
||||
3. Champ select:
|
||||
<ui:include src="/templates/components/shared/forms/user-form-field.xhtml">
|
||||
<ui:param name="id" value="statut" />
|
||||
<ui:param name="label" value="Statut" />
|
||||
<ui:param name="value" value="#{user.statut}" />
|
||||
<ui:param name="type" value="select" />
|
||||
<ui:param name="selectItems" value="#{userBean.statutOptions}" />
|
||||
</ui:include>
|
||||
-->
|
||||
|
||||
<c:set var="type" value="#{empty type ? 'text' : type}" />
|
||||
<c:set var="required" value="#{empty required ? false : required}" />
|
||||
<c:set var="readonly" value="#{empty readonly ? false : readonly}" />
|
||||
<c:set var="rows" value="#{empty rows ? 3 : rows}" />
|
||||
|
||||
<div class="field">
|
||||
<p:outputLabel for="#{id}" value="#{label}#{required ? ' *' : ''}" />
|
||||
|
||||
<c:choose>
|
||||
<!-- Champ texte -->
|
||||
<c:when test="#{type == 'text' or type == 'email'}">
|
||||
<p:inputText
|
||||
id="#{id}"
|
||||
value="#{value}"
|
||||
required="#{required}"
|
||||
readonly="#{readonly}"
|
||||
placeholder="#{placeholder}"
|
||||
type="#{type == 'email' ? 'email' : 'text'}"
|
||||
styleClass="w-full #{styleClass}" />
|
||||
</c:when>
|
||||
|
||||
<!-- Champ mot de passe -->
|
||||
<c:when test="#{type == 'password'}">
|
||||
<p:password
|
||||
id="#{id}"
|
||||
value="#{value}"
|
||||
required="#{required}"
|
||||
readonly="#{readonly}"
|
||||
placeholder="#{placeholder}"
|
||||
feedback="#{not empty feedback ? feedback : false}"
|
||||
styleClass="w-full #{styleClass}" />
|
||||
</c:when>
|
||||
|
||||
<!-- Champ nombre -->
|
||||
<c:when test="#{type == 'number'}">
|
||||
<p:inputNumber
|
||||
id="#{id}"
|
||||
value="#{value}"
|
||||
required="#{required}"
|
||||
readonly="#{readonly}"
|
||||
placeholder="#{placeholder}"
|
||||
styleClass="w-full #{styleClass}" />
|
||||
</c:when>
|
||||
|
||||
<!-- Champ textarea -->
|
||||
<c:when test="#{type == 'textarea'}">
|
||||
<p:inputTextarea
|
||||
id="#{id}"
|
||||
value="#{value}"
|
||||
required="#{required}"
|
||||
readonly="#{readonly}"
|
||||
placeholder="#{placeholder}"
|
||||
rows="#{rows}"
|
||||
styleClass="w-full #{styleClass}" />
|
||||
</c:when>
|
||||
|
||||
<!-- Champ select -->
|
||||
<c:when test="#{type == 'select'}">
|
||||
<p:selectOneMenu
|
||||
id="#{id}"
|
||||
value="#{value}"
|
||||
required="#{required}"
|
||||
readonly="#{readonly}"
|
||||
styleClass="w-full #{styleClass}">
|
||||
<f:selectItem itemLabel="Sélectionner..." itemValue="" />
|
||||
<f:selectItems value="#{selectItems}" />
|
||||
</p:selectOneMenu>
|
||||
</c:when>
|
||||
|
||||
<!-- Champ checkbox -->
|
||||
<c:when test="#{type == 'checkbox'}">
|
||||
<p:selectBooleanCheckbox
|
||||
id="#{id}"
|
||||
value="#{value}"
|
||||
readonly="#{readonly}" />
|
||||
</c:when>
|
||||
|
||||
<!-- Champ calendar -->
|
||||
<c:when test="#{type == 'calendar'}">
|
||||
<p:calendar
|
||||
id="#{id}"
|
||||
value="#{value}"
|
||||
required="#{required}"
|
||||
readonly="#{readonly}"
|
||||
pattern="dd/MM/yyyy"
|
||||
styleClass="w-full #{styleClass}" />
|
||||
</c:when>
|
||||
|
||||
<!-- Par défaut: champ texte -->
|
||||
<c:otherwise>
|
||||
<p:inputText
|
||||
id="#{id}"
|
||||
value="#{value}"
|
||||
required="#{required}"
|
||||
readonly="#{readonly}"
|
||||
placeholder="#{placeholder}"
|
||||
styleClass="w-full #{styleClass}" />
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<c:if test="#{not empty helpText}">
|
||||
<small class="text-color-secondary text-xs">#{helpText}</small>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
<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"
|
||||
xmlns:lum="http://xmlns.jcp.org/jsf/composite/components">
|
||||
|
||||
<!--
|
||||
Composant réutilisable: Tableau Utilisateurs (WOU/DRY Pattern)
|
||||
|
||||
Auteur: Lions User Manager
|
||||
Version: 1.0.0
|
||||
Description: Tableau de données pour afficher une liste d'utilisateurs
|
||||
|
||||
Paramètres:
|
||||
- users: List<UserDTO> (requis) - Liste des utilisateurs
|
||||
- var: String (défaut: "user") - Nom de la variable pour itération
|
||||
- tableId: String (défaut: "userTable") - ID du tableau
|
||||
- paginator: Boolean (défaut: true) - Activer la pagination
|
||||
- rows: Number (défaut: 20) - Nombre de lignes par page
|
||||
- showActions: Boolean (défaut: true) - Afficher la colonne actions
|
||||
- showRoles: Boolean (défaut: true) - Afficher la colonne rôles
|
||||
- showEmail: Boolean (défaut: true) - Afficher la colonne email
|
||||
- showStatus: Boolean (défaut: true) - Afficher la colonne statut
|
||||
- showSelection: Boolean (défaut: false) - Activer la sélection
|
||||
- selection: UserDTO (optionnel) - Utilisateur sélectionné
|
||||
- selectionMode: String (défaut: "single") - Mode: "single" ou "multiple"
|
||||
- totalRecords: Long (optionnel) - Nombre total d'enregistrements pour l'affichage
|
||||
- hasOnPageChange: Boolean (défaut: false) - Indique si un gestionnaire de pagination est fourni
|
||||
- onPageChange: MethodExpression (optionnel) - Méthode à appeler lors du changement de page (requis si hasOnPageChange=true)
|
||||
- lazy: Boolean (défaut: false) - Activer le chargement paresseux
|
||||
- update: String (optionnel) - Composants à mettre à jour
|
||||
- styleClass: String (optionnel) - Classes CSS supplémentaires
|
||||
|
||||
Exemples d'utilisation:
|
||||
|
||||
1. Tableau simple:
|
||||
<ui:include src="/templates/components/shared/tables/user-data-table.xhtml">
|
||||
<ui:param name="users" value="#{userBean.users}" />
|
||||
</ui:include>
|
||||
|
||||
2. Tableau avec sélection:
|
||||
<ui:include src="/templates/components/shared/tables/user-data-table.xhtml">
|
||||
<ui:param name="users" value="#{userBean.users}" />
|
||||
<ui:param name="showSelection" value="true" />
|
||||
<ui:param name="selection" value="#{userBean.selectedUser}" />
|
||||
</ui:include>
|
||||
-->
|
||||
|
||||
<c:set var="varName" value="#{empty var ? 'user' : var}" />
|
||||
<c:set var="tableId" value="#{empty tableId ? 'userTable' : tableId}" />
|
||||
<c:set var="paginator" value="#{empty paginator ? true : paginator}" />
|
||||
<c:set var="rows" value="#{empty rows ? 20 : rows}" />
|
||||
<c:set var="showActions" value="#{empty showActions ? true : showActions}" />
|
||||
<c:set var="showRoles" value="#{empty showRoles ? true : showRoles}" />
|
||||
<c:set var="showEmail" value="#{empty showEmail ? true : showEmail}" />
|
||||
<c:set var="showStatus" value="#{empty showStatus ? true : showStatus}" />
|
||||
<c:set var="showSelection" value="#{empty showSelection ? false : showSelection}" />
|
||||
<c:set var="selectionMode" value="#{empty selectionMode ? 'single' : selectionMode}" />
|
||||
<c:set var="hasOnPageChange" value="#{empty hasOnPageChange ? false : hasOnPageChange}" />
|
||||
|
||||
<p:dataTable
|
||||
id="#{tableId}"
|
||||
value="#{users}"
|
||||
var="user"
|
||||
rowKey="#{user.id}"
|
||||
paginator="#{paginator}"
|
||||
rows="#{rows}"
|
||||
rowCount="#{not empty totalRecords ? totalRecords : (users != null ? users.size() : 0)}"
|
||||
selection="#{selection}"
|
||||
selectionMode="#{selectionMode}"
|
||||
styleClass="p-datatable-sm p-datatable-gridlines p-datatable-striped w-full #{styleClass}"
|
||||
rowStyleClass="p-datatable-row"
|
||||
widgetVar="#{tableId}Widget"
|
||||
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
|
||||
rowsPerPageTemplate="10,20,50,100"
|
||||
currentPageReportTemplate="Affichage {startRecord}-{endRecord} sur {totalRecords}"
|
||||
emptyMessage="Aucun utilisateur trouvé"
|
||||
reflow="true"
|
||||
responsiveLayout="scroll"
|
||||
lazy="#{not empty lazy and lazy}">
|
||||
|
||||
<f:facet name="header">
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<span class="text-900 font-semibold text-xl">Utilisateurs</span>
|
||||
<c:if test="#{not empty totalRecords}">
|
||||
<span class="text-600 text-sm">Total: #{totalRecords}</span>
|
||||
</c:if>
|
||||
</div>
|
||||
</f:facet>
|
||||
|
||||
<!-- Gestionnaire d'événements pour la pagination -->
|
||||
<c:if test="#{hasOnPageChange}">
|
||||
<p:ajax event="page" listener="#{onPageChange}" update="#{not empty update ? update : tableId}" />
|
||||
</c:if>
|
||||
|
||||
<!-- Colonne de sélection -->
|
||||
<c:if test="#{showSelection}">
|
||||
<p:column selectionMode="#{selectionMode}" style="width: 50px" />
|
||||
</c:if>
|
||||
|
||||
<!-- Colonne Username -->
|
||||
<p:column headerText="Nom d'utilisateur" sortBy="#{user.username}" style="width: 200px">
|
||||
<div class="flex align-items-center py-2">
|
||||
<div class="border-circle overflow-hidden mr-2 flex-shrink-0" style="width: 36px; height: 36px;">
|
||||
<div class="bg-primary text-white flex align-items-center justify-content-center w-full h-full">
|
||||
<span style="font-size: 0.875rem; font-weight: bold;">
|
||||
#{user.prenom != null ? user.prenom.substring(0,1) : 'U'}#{user.nom != null ? user.nom.substring(0,1) : ''}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="font-semibold text-900">#{user.username}</span>
|
||||
</div>
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne Nom complet -->
|
||||
<p:column headerText="Nom complet" sortBy="#{user.nom}" style="width: 220px">
|
||||
<div class="flex flex-column py-2">
|
||||
<span class="font-medium text-900">#{user.prenom} #{user.nom}</span>
|
||||
<c:if test="#{not empty user.fonction}">
|
||||
<small class="text-600 text-xs mt-1">#{user.fonction}</small>
|
||||
</c:if>
|
||||
</div>
|
||||
</p:column>
|
||||
|
||||
<!-- Colonne Email -->
|
||||
<c:if test="#{showEmail}">
|
||||
<p:column headerText="Email" sortBy="#{user.email}" style="width: 250px">
|
||||
<div class="flex align-items-center py-2">
|
||||
<i class="pi pi-envelope text-500 mr-2"></i>
|
||||
<span class="text-900">#{user.email}</span>
|
||||
<c:if test="#{user.emailVerified}">
|
||||
<i class="pi pi-check-circle text-green-500 ml-2" title="Email vérifié"></i>
|
||||
</c:if>
|
||||
</div>
|
||||
</p:column>
|
||||
</c:if>
|
||||
|
||||
<!-- Colonne Statut -->
|
||||
<c:if test="#{showStatus}">
|
||||
<p:column headerText="Statut" sortBy="#{user.statut}" style="width: 130px">
|
||||
<div class="flex align-items-center py-2">
|
||||
<p:tag
|
||||
value="#{user.statut != null ? user.statut : 'INCONNU'}"
|
||||
severity="#{user.enabled ? 'success' : 'danger'}" />
|
||||
</div>
|
||||
</p:column>
|
||||
</c:if>
|
||||
|
||||
<!-- Colonne Rôles -->
|
||||
<c:if test="#{showRoles}">
|
||||
<p:column headerText="Rôles" style="width: 200px">
|
||||
<div class="flex flex-wrap gap-1 py-2 align-items-center">
|
||||
<c:choose>
|
||||
<c:when test="#{user.realmRoles != null and !user.realmRoles.isEmpty()}">
|
||||
<c:forEach var="role" items="#{user.realmRoles}" varStatus="status">
|
||||
<c:if test="#{status.index < 3}">
|
||||
<p:tag value="#{role}" severity="info" styleClass="text-xs" />
|
||||
</c:if>
|
||||
</c:forEach>
|
||||
<c:if test="#{user.realmRoles.size() > 3}">
|
||||
<p:tag value="+#{user.realmRoles.size() - 3}" severity="secondary" styleClass="text-xs" />
|
||||
</c:if>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<span class="text-500 text-xs">Aucun rôle</span>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
</p:column>
|
||||
</c:if>
|
||||
|
||||
<!-- Colonne Actions -->
|
||||
<c:if test="#{showActions}">
|
||||
<p:column headerText="Actions" style="width: 100px" exportable="false">
|
||||
<div class="flex justify-content-center align-items-center" style="min-height: 3rem;">
|
||||
<lum:user-action-dropdown
|
||||
userId="#{user.id}"
|
||||
userEnabled="#{user.enabled}"
|
||||
update="#{not empty update ? update : tableId}"
|
||||
activateAction="#{activateAction}"
|
||||
deactivateAction="#{deactivateAction}"
|
||||
deleteAction="#{deleteAction}" />
|
||||
</div>
|
||||
</p:column>
|
||||
</c:if>
|
||||
</p:dataTable>
|
||||
|
||||
</ui:composition>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html 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"
|
||||
lang="fr">
|
||||
|
||||
<h:head>
|
||||
<f:facet name="first">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<link rel="icon" href="#{request.contextPath}/resources/freya-layout/images/favicon.ico" type="image/x-icon"></link>
|
||||
</f:facet>
|
||||
<title><ui:insert name="title">Lions User Manager</ui:insert></title>
|
||||
<h:outputScript name="js/layout.js" library="freya-layout" />
|
||||
<h:outputScript name="js/prism.js" library="freya-layout"/>
|
||||
<ui:insert name="head"/>
|
||||
</h:head>
|
||||
|
||||
<h:body styleClass="#{guestPreferences.inputStyleClass}">
|
||||
<div class="layout-wrapper layout-topbar-#{guestPreferences.topbarTheme} layout-menu-#{guestPreferences.menuTheme} #{guestPreferences.menuMode}" >
|
||||
<ui:include src="/templates/components/layout/topbar.xhtml"/>
|
||||
|
||||
<div class="layout-main">
|
||||
<div class="layout-content">
|
||||
<p:messages id="messages" showDetail="true" closable="true" />
|
||||
<ui:insert name="content"/>
|
||||
</div>
|
||||
<ui:include src="/templates/components/layout/footer.xhtml"/>
|
||||
</div>
|
||||
|
||||
<p:ajaxStatus style="width:32px;height:32px;position:fixed;right:7px;bottom:7px">
|
||||
<f:facet name="start">
|
||||
<i class="pi pi-spin pi-spinner ajax-loader" aria-hidden="true"/>
|
||||
</f:facet>
|
||||
|
||||
<f:facet name="complete">
|
||||
<h:outputText value="" />
|
||||
</f:facet>
|
||||
</p:ajaxStatus>
|
||||
<div class="layout-mask modal-in"></div>
|
||||
</div>
|
||||
<h:outputStylesheet name="css/primeicons.css" library="freya-layout" />
|
||||
<h:outputStylesheet name="css/primeflex.min.css" library="freya-layout" />
|
||||
<h:outputStylesheet name="css/layout-#{guestPreferences.layout}.css" library="freya-layout" />
|
||||
<h:outputStylesheet name="primefaces-freya-#{guestPreferences.componentTheme}/theme.css" />
|
||||
<h:outputStylesheet name="css/custom-topbar.css" />
|
||||
</h:body>
|
||||
|
||||
</html>
|
||||
|
||||