refactor(ui): adopt lions-* shared components (page-header, card, stat-card, empty-state) + add help page

- 12 pages (dashboard, users/list|create|edit|view|profile, roles/list, audit/logs, sync/dashboard, settings, admin/realm-assignments) migrated from custom layouts to shared lions-faces-layout components
- New help.xhtml (Base de connaissance opérationnelle LUM)
- user-data-table: drop resizableColumns+scrollable, replace 'Aucun rôle' span with p:tag
This commit is contained in:
2026-04-23 22:50:38 +00:00
parent 9891b5d921
commit 868f288381
13 changed files with 1019 additions and 1283 deletions

View File

@@ -12,95 +12,48 @@
<ui:define name="content">
<h:form id="formRealmAssignments">
<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-sitemap text-purple-500" style="font-size: 2rem"></i>
<div>
<h3 class="m-0 mb-1">Affectation des Realms</h3>
<p class="text-600 m-0">Gérer les permissions d'administration par realm (contrôle multi-tenant)</p>
</div>
</div>
<fr:commandButton value="Nouvelle Affectation"
icon="pi pi-plus"
severity="success"
onclick="PF('assignRealmDialog').show();"
type="button" />
</div>
</div>
</div>
<!-- En-tête -->
<ui:decorate template="/templates/components/shared/lions-page-header.xhtml">
<ui:param name="title" value="Affectation des Realms" />
<ui:param name="subtitle" value="Gérez les permissions d'administration par realm pour le contrôle multi-tenant." />
<ui:define name="actions">
<p:commandButton value="Nouvelle Affectation" icon="pi pi-plus"
styleClass="ui-button-success" onclick="PF('assignRealmDialog').show();"
type="button" />
</ui:define>
</ui:decorate>
<!-- ================================================================
STATISTIQUES
================================================================ -->
<div class="col-12 md:col-6 lg:col-4">
<div class="card">
<div class="flex align-items-start justify-content-between mb-3">
<div>
<div class="text-500 font-medium mb-1">Total Affectations</div>
<div class="text-900 font-bold text-2xl">#{realmAssignmentBean.totalAssignments}</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-sitemap text-blue-600 text-xl"></i>
</div>
</div>
<small class="text-500">Assignations configurées</small>
</div>
</div>
<!-- Statistiques -->
<ui:decorate template="/templates/components/shared/cards/lions-stat-card.xhtml">
<ui:param name="title" value="Total Affectations" />
<ui:param name="value" value="#{realmAssignmentBean.totalAssignments}" />
<ui:param name="icon" value="pi-sitemap" />
<ui:param name="iconColor" value="blue-600" />
<ui:param name="subtitle" value="Assignations configurées" />
</ui:decorate>
<div class="col-12 md:col-6 lg:col-4">
<div class="card">
<div class="flex align-items-start justify-content-between mb-3">
<div>
<div class="text-500 font-medium mb-1">Affectations Actives</div>
<div class="text-900 font-bold text-2xl">#{realmAssignmentBean.activeAssignmentsCount}</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>
<small class="text-500">En cours de validité</small>
</div>
</div>
<ui:decorate template="/templates/components/shared/cards/lions-stat-card.xhtml">
<ui:param name="title" value="Affectations Actives" />
<ui:param name="value" value="#{realmAssignmentBean.activeAssignmentsCount}" />
<ui:param name="icon" value="pi-check-circle" />
<ui:param name="iconColor" value="green-600" />
<ui:param name="subtitle" value="En cours de validité" />
</ui:decorate>
<div class="col-12 md:col-6 lg:col-4">
<div class="card">
<div class="flex align-items-start justify-content-between mb-3">
<div>
<div class="text-500 font-medium mb-1">Super Admins</div>
<div class="text-900 font-bold text-2xl">#{realmAssignmentBean.superAdminsCount}</div>
</div>
<div class="flex align-items-center justify-content-center bg-orange-100 border-circle"
style="width: 2.5rem; height: 2.5rem">
<i class="pi pi-star text-orange-600 text-xl"></i>
</div>
</div>
<small class="text-500">Peuvent gérer tous les realms</small>
</div>
</div>
<ui:decorate template="/templates/components/shared/cards/lions-stat-card.xhtml">
<ui:param name="title" value="Super Admins" />
<ui:param name="value" value="#{realmAssignmentBean.superAdminsCount}" />
<ui:param name="icon" value="pi-star" />
<ui:param name="iconColor" value="orange-600" />
<ui:param name="subtitle" value="Contrôle total des realms" />
</ui:decorate>
<!-- ================================================================
TABLEAU DES AFFECTATIONS
================================================================ -->
<div class="col-12">
<div class="card">
<div class="flex align-items-center justify-content-between mb-4">
<h5 class="m-0">Affectations Actuelles</h5>
<fr:commandButton value="Rafraîchir"
icon="pi pi-refresh"
outlined="true"
size="small"
action="#{realmAssignmentBean.loadAssignments}"
update=":formRealmAssignments" />
</div>
<!-- Tableau des Affectations -->
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Affectations Actuelles" />
<ui:param name="icon" value="pi pi-list text-primary" />
<fr:message id="messages" showDetail="true" closable="true">
<fr:message id="messages" showDetail="true" closable="true">
<p:autoUpdate />
</fr:message>
@@ -228,8 +181,7 @@
</div>
</p:column>
</p:dataTable>
</div>
</div>
</ui:decorate>
</div>
</h:form>

View File

@@ -10,27 +10,19 @@
<ui:define name="content">
<div class="grid">
<!-- En-tête -->
<div class="col-12">
<ui:decorate template="/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:decorate>
</div>
<ui:decorate template="/templates/components/shared/lions-page-header.xhtml">
<ui:param name="title" value="Registre de Gouvernance et d'Audit" />
<ui:param name="subtitle" value="Analyse de traçabilité et conformité des actions de sécurité sur le domaine LionsDev." />
<ui:define name="actions">
<h:form id="formActionsAudit">
<div class="flex gap-2">
<p:commandButton value="Exporter CSV" icon="pi pi-download"
action="#{auditConsultationBean.exportToCSV}" ajax="false"
styleClass="ui-button-success ui-button-outlined" />
</div>
</h:form>
</ui:define>
</ui:decorate>
<!-- Statistiques -->
<div class="col-12">
<ui:decorate template="/templates/components/shared/dashboard/kpi-group.xhtml">
@@ -158,78 +150,56 @@
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
currentPageReportTemplate="{startRecord}-{endRecord} sur {totalRecords}"
emptyMessage="Aucun log d'audit trouvé. Cliquez sur Rechercher pour lancer une requête."
styleClass="w-full" stripedRows="true" size="small">
stripedRows="true" size="small" responsiveLayout="scroll"
tableStyle="table-layout: fixed; width: 100%">
<!-- Colonne Statut -->
<p:column headerText="Statut" style="width: 6rem; text-align: center;">
<p:column headerText="Statut" style="width: 70px; text-align: center;">
<h:panelGroup rendered="#{log.successful}">
<p:tag value="Succès" severity="success" icon="pi pi-check" styleClass="text-xs" />
<i class="pi pi-check-circle text-green-500" title="Succès"></i>
</h:panelGroup>
<h:panelGroup rendered="#{not log.successful}">
<p:tag value="Échec" severity="danger" icon="pi pi-times" styleClass="text-xs" />
<i class="pi pi-times-circle text-red-500" title="Échec"></i>
</h:panelGroup>
</p:column>
<!-- Colonne Type d'action -->
<p:column headerText="Type d'action" sortBy="#{log.typeAction}" filterBy="#{log.typeAction}"
filterMatchMode="contains" style="width: 15%">
<span class="font-semibold text-900">#{log.typeAction}</span>
<!-- Colonne Action -->
<p:column headerText="Action" sortBy="#{log.typeAction}" style="width: 15%">
<span class="font-semibold text-900 text-xs">#{log.typeAction}</span>
</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>
<span class="text-xs">#{log.acteurUsername}</span>
</p:column>
<!-- Colonne Ressource -->
<p:column headerText="Ressource" style="width: 10%">
<h:panelGroup rendered="#{not empty log.ressourceType}">
<p:tag value="#{log.ressourceType}" severity="info" styleClass="text-xs" />
</h:panelGroup>
<h:panelGroup rendered="#{empty log.ressourceType}">
<span class="text-color-secondary">-</span>
</h:panelGroup>
<span class="text-xs text-500">#{log.ressourceType}</span>
</p:column>
<!-- Colonne Date -->
<p:column headerText="Date" sortBy="#{log.dateAction}" style="width: 14%">
<div class="flex align-items-center gap-2">
<i class="pi pi-calendar text-color-secondary text-sm"></i>
<h:outputText value="#{log.dateAction}" styleClass="text-sm">
<f:convertDateTime pattern="dd/MM/yyyy HH:mm" type="localDateTime" />
</h:outputText>
</div>
<p:column headerText="Date" sortBy="#{log.dateAction}" style="width: 100px">
<h:outputText value="#{log.dateAction}" styleClass="text-xs">
<f:convertDateTime pattern="dd/MM HH:mm" type="localDateTime" />
</h:outputText>
</p:column>
<!-- Colonne Description -->
<p:column headerText="Description" style="width: 22%">
<h:panelGroup rendered="#{not empty log.description}">
<span class="text-color-secondary text-sm line-clamp"
title="#{log.description}">#{log.description}</span>
</h:panelGroup>
<h:panelGroup rendered="#{empty log.description}">
<span class="text-color-secondary text-sm">-</span>
</h:panelGroup>
<p:column headerText="Description" style="width: 30%">
<span class="text-xs text-600 line-clamp" title="#{log.description}">#{log.description}</span>
</p:column>
<!-- Colonne IP -->
<p:column headerText="IP" style="width: 9%">
<h:panelGroup rendered="#{not empty log.ipAddress}">
<span class="text-color-secondary text-sm font-mono">#{log.ipAddress}</span>
</h:panelGroup>
<h:panelGroup rendered="#{empty log.ipAddress}">
<span class="text-color-secondary text-sm">-</span>
</h:panelGroup>
<p:column headerText="IP" style="width: 100px">
<span class="text-xs font-mono">#{log.ipAddress}</span>
</p:column>
<!-- Colonne Actions -->
<p:column headerText="" style="width: 4rem; text-align: center;">
<p:column style="width: 40px; text-align: center;">
<p:commandButton icon="pi pi-eye"
styleClass="p-button-text p-button-rounded p-button-sm"
title="Voir les détails" update=":dlgAuditLogDetails"
styleClass="p-button-text p-button-rounded p-button-sm p-0"
update=":dlgAuditLogDetails"
oncomplete="PF('auditLogDetailsDialog').show()">
<f:setPropertyActionListener target="#{auditConsultationBean.selectedLog}"
value="#{log}" />

View File

@@ -9,136 +9,99 @@
<p:outputPanel id="dashboardPanel">
<div class="grid">
<!-- En-tête -->
<div class="col-12">
<ui:decorate template="/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=":dashboardPanel" />
</h:form>
</ui:define>
</ui:decorate>
</div>
<!-- KPIs Principaux avec composant réutilisable -->
<div class="col-12">
<ui:decorate template="/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: Sessions Actives -->
<ui:include src="/templates/components/shared/cards/kpi-card.xhtml">
<ui:param name="title" value="Sessions Actives" />
<ui:param name="value" value="#{dashboardBean.activeSessionsDisplay}" />
<ui:param name="icon" value="pi-desktop" />
<ui:param name="iconColor" value="purple-600" />
<ui:param name="subtitle" value="#{dashboardBean.onlineUsersDisplay} utilisateur(s) en ligne" />
</ui:include>
</ui:define>
</ui:decorate>
</div>
<!-- Actions Rapides -->
<ui:decorate template="/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">
<p:button value="Nouvel 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">
<p:button value="Liste des Utilisateurs" icon="pi pi-users"
styleClass="w-full p-button-primary" outcome="/pages/user-manager/users/list" />
</div>
<div class="col-12 md:col-6">
<p:button value="Gestion des Rôles" icon="pi pi-shield"
styleClass="w-full p-button-info" outcome="/pages/user-manager/roles/list" />
</div>
<div class="col-12 md:col-6">
<p:button value="Journal d'Audit" icon="pi pi-history" styleClass="w-full p-button-help"
outcome="/pages/user-manager/audit/logs" />
</div>
</div>
<ui:decorate template="/templates/components/shared/lions-page-header.xhtml">
<ui:param name="title" value="Tableau de Bord" />
<ui:param name="subtitle" value="Vue d'ensemble de la gestion des utilisateurs et de l'annuaire Keycloak." />
<ui:define name="actions">
<h:form id="formDashboardHeader">
<p:commandButton value="Actualiser" icon="pi pi-refresh"
action="#{dashboardBean.refreshStatistics}" update=":dashboardPanel"
styleClass="ui-button-outlined" />
</h:form>
</ui:define>
</ui:decorate>
<!-- Stat Cards Row -->
<ui:decorate template="/templates/components/shared/cards/lions-stat-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:decorate>
<ui:decorate template="/templates/components/shared/cards/lions-stat-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:decorate>
<ui:decorate template="/templates/components/shared/cards/lions-stat-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:decorate>
<ui:decorate template="/templates/components/shared/cards/lions-stat-card.xhtml">
<ui:param name="title" value="Sessions Actives" />
<ui:param name="value" value="#{dashboardBean.activeSessionsDisplay}" />
<ui:param name="icon" value="pi-desktop" />
<ui:param name="iconColor" value="purple-600" />
<ui:param name="subtitle" value="#{dashboardBean.onlineUsersDisplay} utilisateur(s) en ligne" />
</ui:decorate>
<!-- Actions Rapides -->
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="colSize" value="col-12 lg:col-6" />
<ui:param name="title" value="Actions Rapides" />
<ui:param name="icon" value="pi pi-bolt text-yellow-600" />
<div class="grid mt-2">
<div class="col-12 md:col-6">
<p:button value="Nouvel Utilisateur" icon="pi pi-user-plus"
styleClass="w-full ui-button-success" outcome="/pages/user-manager/users/create" />
</div>
<div class="col-12 md:col-6">
<p:button value="Liste des Utilisateurs" icon="pi pi-users"
styleClass="w-full ui-button-primary" outcome="/pages/user-manager/users/list" />
</div>
<div class="col-12 md:col-6">
<p:button value="Gestion des Rôles" icon="pi pi-shield"
styleClass="w-full ui-button-info" outcome="/pages/user-manager/roles/list" />
</div>
<div class="col-12 md:col-6">
<p:button value="Journal d'Audit" icon="pi pi-history" styleClass="w-full ui-button-help"
outcome="/pages/user-manager/audit/logs" />
</div>
</div>
</ui:decorate>
<!-- Informations Système -->
<ui:decorate template="/templates/components/shared/dashboard/dashboard-section.xhtml">
<ui:param name="title" value="Informations Système" />
<ui:param name="icon" value="pi-info-circle" />
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<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>
<ui:param name="title" value="Informations Système" />
<ui:param name="icon" value="pi pi-info-circle text-blue-500" />
<div class="flex flex-column gap-3 py-2 mt-2">
<div class="flex align-items-center justify-content-between p-2 border-round hover:bg-emphasis transition-all">
<span class="text-600 font-medium">Version du Layout</span>
<span class="text-900 font-bold">1.0.4</span>
</div>
<div class="flex align-items-center justify-content-between">
<span class="text-600">Realm Keycloak</span>
<span class="font-semibold">#{dashboardBean.realmName}</span>
<div class="flex align-items-center justify-content-between p-2">
<span class="text-600 font-medium">Realm Keycloak</span>
<span class="text-900 font-bold">#{dashboardBean.realmName}</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 class="flex align-items-center justify-content-between p-2">
<span class="text-600 font-medium">Statut du Noyau</span>
<p:tag value="Opérationnel" severity="success" styleClass="px-3" />
</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 class="flex align-items-center justify-content-between p-2">
<span class="text-600 font-medium">Environnement</span>
<p:tag value="Développement" severity="warning" styleClass="px-3" />
</div>
</div>
</ui:define>
</ui:decorate>
</ui:decorate>
</div>
</p:outputPanel>
</ui:define>

View File

@@ -0,0 +1,105 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
template="/templates/main-template.xhtml">
<ui:define name="title">Base de Connaissance Opérationnelle</ui:define>
<ui:define name="content">
<div class="grid">
<div class="col-12">
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Procédures de Gestion des Identités" />
<ui:param name="icon" value="pi pi-book text-blue-500" />
<div class="p-4">
<div class="text-900 font-bold text-2xl mb-4">Support Opérationnel Lions User Manager (LUM)</div>
<p class="text-600 line-height-3 mb-5">
Cette plateforme centralise la gestion des accès pour l'ensemble de l'écosystème LionsDev.
En tant qu'administrateur ou gestionnaire, vos actions ont un impact direct sur la sécurité des SI.
</p>
<div class="grid">
<!-- Gestion des Agents -->
<div class="col-12 md:col-6">
<div class="flex gap-4 p-3 border-round hover:bg-emphasis transition-all">
<div class="flex-shrink-0 w-3rem h-3rem surface-50 flex align-items-center justify-content-center border-round border-1 surface-border">
<i class="pi pi-users text-blue-500 font-bold"></i>
</div>
<div>
<h5 class="text-900 font-bold mb-2">Cycle de vie d'un compte</h5>
<p class="text-600 text-sm mb-3">
La création d'un agent nécessite un email vérifié. Un compte inactif pendant plus de 90 jours est automatiquement désactivé par les politiques de purge.
</p>
<p:link value="Consulter la procédure de création" href="#" styleClass="text-sm font-semibold text-primary" />
</div>
</div>
</div>
<!-- Modèle de Rôles -->
<div class="col-12 md:col-6">
<div class="flex gap-4 p-3 border-round hover:bg-emphasis transition-all">
<div class="flex-shrink-0 w-3rem h-3rem surface-50 flex align-items-center justify-content-center border-round border-1 surface-border">
<i class="pi pi-briefcase text-purple-500 font-bold"></i>
</div>
<div>
<h5 class="text-900 font-bold mb-2">Attribution des Habilitations</h5>
<p class="text-600 text-sm mb-3">
LUM distingue les rôles de plateforme (admin, system) des rôles applicatifs. Les rôles applicatifs sont propagés via les claims OIDC vers les clients.
</p>
<p:link value="Matrice des responsabilités (RACCI)" href="#" styleClass="text-sm font-semibold text-primary" />
</div>
</div>
</div>
<!-- Réponses aux Incidents -->
<div class="col-12 md:col-6">
<div class="flex gap-4 p-3 border-round hover:bg-emphasis transition-all">
<div class="flex-shrink-0 w-3rem h-3rem surface-50 flex align-items-center justify-content-center border-round border-1 surface-border">
<i class="pi pi-exclamation-triangle text-orange-500 font-bold"></i>
</div>
<div>
<h5 class="text-900 font-bold mb-2">Analyse d'Audit et Sécurité</h5>
<p class="text-600 text-sm mb-3">
En cas de détection d'attaques brute-force, utilisez le tableau 'Audit History' pour identifier les Adresses IP sources et déclencher une suspension temporaire du Realm.
</p>
<p:link value="Guide de réponse aux incidents" href="#" styleClass="text-sm font-semibold text-primary" />
</div>
</div>
</div>
<!-- Réinitialisation forcée -->
<div class="col-12 md:col-6">
<div class="flex gap-4 p-3 border-round hover:bg-emphasis transition-all">
<div class="flex-shrink-0 w-3rem h-3rem surface-50 flex align-items-center justify-content-center border-round border-1 surface-border">
<i class="pi pi-undo text-green-500 font-bold"></i>
</div>
<div>
<h5 class="text-900 font-bold mb-2">Réinitialisation de Mot de Passe</h5>
<p class="text-600 text-sm mb-3">
L'administrateur peut déclencher une "Update Password" action OIDC. L'utilisateur devra obligatoirement changer son secret lors du prochain flux de connexion.
</p>
<p:link value="Voir les flux Keycloak Flow" href="#" styleClass="text-sm font-semibold text-primary" />
</div>
</div>
</div>
</div>
<div class="mt-6 p-4 surface-900 text-white border-round-xl">
<div class="flex align-items-center justify-content-between flex-wrap gap-4">
<div>
<div class="text-xl font-bold mb-2">Besoin d'une expertise complémentaire ?</div>
<div class="text-bluegray-200">L'équipe Support Sécurité est disponible pour vos questions sur l'IAM.</div>
</div>
<p:button value="Ouvrir un Ticket d'Expertise" icon="pi pi-envelope" styleClass="ui-button-info" />
</div>
</div>
</div>
</ui:decorate>
</div>
</div>
</ui:define>
</ui:composition>

View File

@@ -8,39 +8,36 @@
<ui:define name="title">Gestion des Rôles - Lions User Manager</ui:define>
<ui:define name="content">
<!-- En-tête — ui:decorate requis pour que ui:define name="actions" fonctionne -->
<ui:decorate template="/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">
<p:commandButton value="Nouveau Rôle Realm" icon="pi pi-plus"
styleClass="p-button-success"
type="button" onclick="PF('createRealmRoleDialog').show()" />
<p:commandButton value="Nouveau Rôle Client" icon="pi pi-plus-circle"
styleClass="p-button-info"
type="button" onclick="PF('createClientRoleDialog').show()" />
</div>
</h:form>
</ui:define>
</ui:decorate>
<div class="grid">
<!-- En-tête -->
<ui:decorate template="/templates/components/shared/lions-page-header.xhtml">
<ui:param name="title" value="Gestion des Rôles" />
<ui:param name="subtitle" value="Administrez les privilèges d'accès via les rôles Realm et Client dans l'écosystème Keycloak." />
<ui:define name="actions">
<h:form id="formActionsRoles">
<div class="flex gap-2">
<p:commandButton value="Nouveau Rôle Realm" icon="pi pi-plus"
styleClass="ui-button-success"
type="button" onclick="PF('createRealmRoleDialog').show()" />
<p:commandButton value="Nouveau Rôle Client" icon="pi pi-plus-circle"
styleClass="ui-button-info"
type="button" onclick="PF('createClientRoleDialog').show()" />
</div>
</h:form>
</ui:define>
</ui:decorate>
<!-- Messages globaux -->
<p:growl id="growlMessages" showDetail="true" life="5000" />
<div class="col-12">
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Filtres de Sélection" />
<ui:param name="icon" value="pi pi-filter text-purple-500" />
<!-- Filtres -->
<div class="card mb-3">
<h:form id="formFilters">
<div class="flex align-items-center mb-3">
<i class="pi pi-filter mr-2 text-color-secondary"></i>
<h5 class="m-0">Filtres</h5>
</div>
<div class="grid">
<div class="col-12 md:col-4">
<div class="field">
<p:outputLabel for="realmFilter" value="Realm" styleClass="font-medium" />
<h:form id="formFilters">
<div class="grid mt-2">
<div class="col-12 md:col-4">
<p:outputLabel for="realmFilter" value="Realm" styleClass="block mb-2 font-medium" />
<p:selectOneMenu id="realmFilter" value="#{roleGestionBean.realmName}" styleClass="w-full">
<f:selectItem itemLabel="Sélectionner un realm..." itemValue="" />
<f:selectItems value="#{roleGestionBean.availableRealms}" />
@@ -48,11 +45,9 @@
update=":formRealmRoles :formClientRoles :formFilters:clientFilter :growlMessages" />
</p:selectOneMenu>
</div>
</div>
<div class="col-12 md:col-4">
<div class="field">
<p:outputLabel for="clientFilter" value="Client" styleClass="font-medium" />
<div class="col-12 md:col-4">
<p:outputLabel for="clientFilter" value="Client" styleClass="block mb-2 font-medium" />
<p:selectOneMenu id="clientFilter" value="#{roleGestionBean.clientName}"
styleClass="w-full">
<f:selectItem itemLabel="Sélectionner un client..." itemValue="" />
@@ -61,11 +56,9 @@
update=":formClientRoles :growlMessages" />
</p:selectOneMenu>
</div>
</div>
<div class="col-12 md:col-4">
<div class="field">
<p:outputLabel for="typeFilter" value="Filtrer par type" styleClass="font-medium" />
<div class="col-12 md:col-4">
<p:outputLabel for="typeFilter" value="Filtrer par type" styleClass="block mb-2 font-medium" />
<p:selectOneMenu id="typeFilter" value="#{roleGestionBean.selectedTypeRole}"
styleClass="w-full">
<f:selectItem itemLabel="Tous les types" itemValue="#{null}" />
@@ -76,27 +69,18 @@
</p:selectOneMenu>
</div>
</div>
</div>
</h:form>
</div>
</h:form>
</ui:decorate>
</div>
<!-- Rôles Realm -->
<div class="col-12">
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Rôles Realm" />
<ui:param name="icon" value="pi pi-globe text-green-500" />
<!-- Rôles Realm -->
<div class="card mb-3">
<h:form id="formRealmRoles">
<p:panel id="realmRolesPanel" toggleable="true" collapsed="false">
<f:facet name="header">
<div class="flex align-items-center justify-content-between w-full">
<div class="flex align-items-center gap-2">
<i class="pi pi-globe text-green-500"></i>
<span class="font-semibold">Rôles Realm</span>
</div>
<p:tag value="#{roleGestionBean.filteredRealmRoles.size()}" severity="success"
styleClass="text-xs" rendered="#{not empty roleGestionBean.filteredRealmRoles}" />
</div>
</f:facet>
<div class="grid">
<h:form id="formRealmRoles">
<div class="grid mt-3">
<div class="col-12 md:col-6 lg:col-4">
<ui:repeat var="role" value="#{roleGestionBean.filteredRealmRoles}">
<div class="col-12 md:col-6 lg:col-4 xl:col-3">
<ui:include src="/templates/components/role-management/role-card.xhtml">
<ui:param name="role" value="#{role}" />
@@ -109,34 +93,26 @@
</div>
</ui:repeat>
<h:panelGroup layout="block" styleClass="col-12"
<div class="text-center text-color-secondary py-4">
rendered="#{empty roleGestionBean.filteredRealmRoles}">
<div class="text-center text-color-secondary py-5 surface-ground border-round">
<i class="pi pi-info-circle text-2xl block mb-2"></i>
<span>Aucun rôle Realm trouvé</span>
</div>
</h:panelGroup>
</p:panel>
</h:form>
</div>
</h:form>
</ui:decorate>
</div>
<!-- Rôles Client -->
<div class="card">
<h:form id="formClientRoles">
<p:panel id="clientRolesPanel" toggleable="true" collapsed="false">
<f:facet name="header">
<div class="flex align-items-center justify-content-between w-full">
<div class="flex align-items-center gap-2">
<i class="pi pi-desktop text-blue-500"></i>
<span class="font-semibold">Rôles Client</span>
</div>
<p:tag value="#{roleGestionBean.filteredClientRoles.size()}" severity="info"
styleClass="text-xs" rendered="#{not empty roleGestionBean.filteredClientRoles}" />
</div>
</f:facet>
<div class="grid">
<!-- Rôles Client -->
<div class="col-12">
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Rôles Client" />
<ui:param name="icon" value="pi pi-desktop text-blue-500" />
<h:form id="formClientRoles">
<div class="grid mt-3">
<div class="col-12 md:col-6 lg:col-4">
<ui:repeat var="role" value="#{roleGestionBean.filteredClientRoles}">
<div class="col-12 md:col-6 lg:col-4 xl:col-3">
<ui:include src="/templates/components/role-management/role-card.xhtml">
<ui:param name="role" value="#{role}" />
@@ -149,7 +125,7 @@
</div>
</ui:repeat>
<h:panelGroup layout="block" styleClass="col-12"
<div class="text-center text-color-secondary py-4">
rendered="#{empty roleGestionBean.filteredClientRoles}">
<div class="text-center text-color-secondary py-5 surface-ground border-round">
<i class="pi pi-info-circle text-2xl block mb-2"></i>
<h:panelGroup rendered="#{empty roleGestionBean.clientName}">
@@ -160,9 +136,9 @@
</h:panelGroup>
</div>
</h:panelGroup>
</p:panel>
</h:form>
</div>
</h:form>
</ui:decorate>
</div>
</div>

View File

@@ -1,131 +1,116 @@
<!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="title">Paramètres de Gouvernance</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" />
<div class="col-12">
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Configuration du Domaine de Sécurité" />
<ui:param name="icon" value="pi pi-shield text-primary" />
<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 class="grid p-3">
<!-- Gestion du Realm -->
<div class="col-12 lg:col-6">
<div class="surface-ground p-4 border-round-xl h-full border-1 surface-border">
<div class="flex align-items-center justify-content-between mb-4">
<h4 class="text-900 font-bold m-0">Identification du Realm</h4>
<p:tag value="Actif" severity="success" />
</div>
<div class="space-y-4">
<div class="flex justify-content-between py-2 border-bottom-1 surface-border">
<span class="text-600">Nom du Realm</span>
<span class="text-900 font-semibold">lions-user-manager</span>
</div>
<div class="flex justify-content-between py-2 border-bottom-1 surface-border">
<span class="text-600">Environnement</span>
<span class="text-900 font-semibold">Production Centralisée</span>
</div>
<div class="flex justify-content-between py-2">
<span class="text-600">Émetteur (Issuer)</span>
<span class="text-500 font-mono text-xs">#{userSessionBean.issuer}</span>
</div>
</div>
<div class="mt-4">
<p:link href="http://localhost:8180/admin/master/console/#/lions-user-manager"
target="_blank"
styleClass="flex align-items-center gap-2 text-blue-600 font-medium no-underline hover:underline">
<i class="pi pi-external-link"></i>
<span>Ouvrir la console d'administration Keycloak</span>
</p:link>
</div>
</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>
<!-- Politiques de Sécurité -->
<div class="col-12 lg:col-6">
<div class="surface-ground p-4 border-round-xl h-full border-1 surface-border">
<h4 class="text-900 font-bold mb-4">Politiques de Sécurité Appliquées</h4>
<div class="flex flex-column gap-3">
<div class="flex align-items-center gap-3">
<i class="pi pi-key text-orange-500"></i>
<div class="flex-1">
<div class="text-900 font-semibold">Politique de Mots de Passe</div>
<div class="text-600 text-sm">Lions Standard : 12 car. min, complexité élevée, rotation 90j.</div>
</div>
</div>
<div class="flex align-items-center gap-3">
<i class="pi pi-clock text-blue-500"></i>
<div class="flex-1">
<div class="text-900 font-semibold">Inactivité et Session</div>
<div class="text-600 text-sm">Expiration session idle : 30 min. Durée max : 10h.</div>
</div>
</div>
<div class="flex align-items-center gap-3">
<i class="pi pi-lock text-purple-500"></i>
<div class="flex-1">
<div class="text-900 font-semibold">Multi-Facteurs (MFA)</div>
<div class="text-600 text-sm">Activé pour les privilèges élevés (Roles: admin, user_manager).</div>
</div>
</div>
</div>
</div>
</div>
<!-- Audit & Rétention -->
<div class="col-12 mt-4">
<div class="surface-section p-4 border-round-xl border-1 surface-border">
<h4 class="text-900 font-bold mb-3">Conformité et Audit</h4>
<p class="text-600 mb-4">Tous les flux de gestion sont tracés conformément à la charte de sécurité LionsDev.</p>
<div class="grid">
<div class="col-12 md:col-4">
<div class="p-3 border-round surface-50">
<div class="text-500 text-sm mb-1 font-semibold uppercase">Rétention des logs</div>
<div class="text-900 text-xl font-bold">180 Jours</div>
<small class="text-500">Base de données d'audit locale</small>
</div>
</div>
<div class="col-12 md:col-4">
<div class="p-3 border-round surface-50">
<div class="text-500 text-sm mb-1 font-semibold uppercase">Fréquence de Sync</div>
<div class="text-900 text-xl font-bold">Temps Réel</div>
<small class="text-500">Synchronisation via SPI Keycloak</small>
</div>
</div>
<div class="col-12 md:col-4">
<div class="p-3 border-round surface-50">
<div class="text-500 text-sm mb-1 font-semibold uppercase">Niveau de Détail</div>
<div class="text-900 text-xl font-bold">Intégral</div>
<small class="text-500">Capture des payloads de modification</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</ui:decorate>
</div>
</div>
</ui:define>
</ui:composition>

View File

@@ -9,162 +9,197 @@
<h:form id="syncForm">
<p:growl id="growl" showDetail="true" />
<!-- En-tête -->
<div class="col-12">
<ui:decorate template="/templates/components/layout/page-header.xhtml">
<ui:param name="icon" value="pi pi-sync text-blue-500" />
<div class="grid">
<!-- En-tête -->
<ui:decorate template="/templates/components/shared/lions-page-header.xhtml">
<ui:param name="title" value="Synchronisation Keycloak" />
<ui:param name="description" value="Synchronisation et vérification de l'état de Keycloak" />
<ui:param name="subtitle" value="Analyse de cohérence et synchronisation des comptes entre Keycloak et la base locale." />
<ui:define name="actions">
<p:commandButton value="Actualiser l'état" icon="pi pi-refresh"
actionListener="#{syncDashboardBean.checkKeycloakStatus}" update="@form"
styleClass="p-button-outlined" />
styleClass="ui-button-outlined" />
</ui:define>
</ui:decorate>
</div>
<div class="grid">
<!-- Health Check Card -->
<div class="col-12 md:col-6">
<div class="card h-full">
<h5>État de Keycloak</h5>
<div class="flex align-items-center gap-3 mb-3">
<div class="flex align-items-center justify-content-center bg-blue-100 border-round p-3"
style="width: 3rem; height: 3rem">
<i class="pi pi-server text-blue-500 text-xl"></i>
</div>
<div class="flex flex-column">
<span class="text-900 font-medium text-xl">
#{syncDashboardBean.keycloakStatusLabel eq 'UP' ? 'En Ligne' : 'Hors Ligne'}
</span>
<span class="text-600">Statut du service</span>
</div>
<span class="ml-auto">
<p:tag value="#{syncDashboardBean.keycloakStatusLabel}"
severity="#{syncDashboardBean.keycloakStatusLabel eq 'UP' ? 'success' : 'danger'}" />
</span>
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="colSize" value="col-12 lg:col-4" />
<ui:param name="title" value="État du système" />
<ui:param name="icon" value="pi pi-heart-fill text-red-500" />
<div class="flex flex-column gap-3 py-2">
<div class="flex align-items-center justify-content-between">
<span class="text-900 font-bold text-2xl">
#{syncDashboardBean.keycloakStatusLabel eq 'UP' ? 'En Ligne' : 'Hors Ligne'}
</span>
<p:tag value="#{syncDashboardBean.keycloakStatusLabel}"
severity="#{syncDashboardBean.keycloakStatusLabel eq 'UP' ? 'success' : 'danger'}"
styleClass="px-3" />
</div>
<span class="text-600">Le serveur d'identité est opérationnel</span>
</div>
<p:divider />
<div class="flex flex-column gap-2">
<div class="flex justify-content-between">
<span class="text-600">Message:</span>
<span class="text-900">#{syncDashboardBean.keycloakStatusMessage}</span>
</div>
<div class="flex justify-content-between">
<span class="text-600">Version:</span>
<span class="text-900">#{syncDashboardBean.keycloakVersion}</span>
</div>
</div>
</div>
</div>
<!-- Sync Actions Card -->
<div class="col-12 md:col-6">
<div class="card h-full">
<h5>Actions de Synchronisation</h5>
<p class="text-600 mb-4">Lancez la synchronisation des données depuis Keycloak vers la base
locale.</p>
<p:divider styleClass="my-4" />
<div class="flex flex-column gap-3">
<div
class="flex align-items-center justify-content-between border-1 surface-border border-round p-3">
<div class="flex align-items-center gap-2">
<i class="pi pi-users text-primary text-xl"></i>
<span class="text-900 font-medium">Utilisateurs</span>
<div class="flex justify-content-between p-2 surface-section border-round">
<span class="text-600 font-medium">Message de retour</span>
<span class="text-900 font-semibold text-right">#{syncDashboardBean.keycloakStatusMessage}</span>
</div>
<div class="flex justify-content-between p-2">
<span class="text-600">Version du noyau</span>
<span class="text-900 font-bold text-blue-500">#{syncDashboardBean.keycloakVersion}</span>
</div>
</div>
</ui:decorate>
<!-- Sync Actions Card -->
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="colSize" value="col-12 md:col-6 lg:col-4" />
<ui:param name="title" value="Actions de Synchronisation" />
<ui:param name="icon" value="pi-sync" />
<p class="text-600 mb-4 line-height-3">
Lancez manuellement la récupération des données depuis Keycloak pour mettre à jour la base locale LUM.
</p>
<div class="flex flex-column gap-3">
<div class="flex align-items-center justify-content-between p-3 border-1 surface-border border-round hover:bg-emphasis transition-all transition-duration-200">
<div class="flex align-items-center gap-3">
<div class="w-2rem h-2rem bg-blue-100 border-circle flex align-items-center justify-content-center">
<i class="pi pi-users text-blue-600"></i>
</div>
<span class="text-900 font-semibold">Répertoire Utilisateurs</span>
</div>
<p:commandButton value="Synchroniser" icon="pi pi-sync"
<p:commandButton value="Synchroniser" icon="pi pi-refresh"
actionListener="#{syncDashboardBean.syncUsers}" update="growl"
styleClass="p-button-rounded p-button-text" />
styleClass="ui-button-sm ui-button-raised" />
</div>
<div
class="flex align-items-center justify-content-between border-1 surface-border border-round p-3">
<div class="flex align-items-center gap-2">
<i class="pi pi-shield text-primary text-xl"></i>
<span class="text-900 font-medium">Rôles</span>
<div class="flex align-items-center justify-content-between p-3 border-1 surface-border border-round hover:bg-emphasis transition-all transition-duration-200">
<div class="flex align-items-center gap-3">
<div class="w-2rem h-2rem bg-purple-100 border-circle flex align-items-center justify-content-center">
<i class="pi pi-shield text-purple-600"></i>
</div>
<span class="text-900 font-semibold">Référentiel des Rôles</span>
</div>
<p:commandButton value="Synchroniser" icon="pi pi-sync"
<p:commandButton value="Synchroniser" icon="pi pi-refresh"
actionListener="#{syncDashboardBean.syncRoles}" update="growl"
styleClass="p-button-rounded p-button-text p-button-secondary" />
styleClass="ui-button-sm ui-button-raised ui-button-secondary" />
</div>
</div>
</div>
</div>
</ui:decorate>
<!-- Dernier Statut de Sync Card -->
<div class="col-12 md:col-6">
<div class="card h-full">
<div class="flex align-items-center justify-content-between mb-3">
<h5 class="m-0">Dernière Synchronisation</h5>
<p:commandButton value="Sync complète" icon="pi pi-play"
<!-- Dernière Synchronisation Card -->
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="colSize" value="col-12 md:col-6 lg:col-4" />
<ui:param name="title" value="Dernière Synchronisation" />
<ui:param name="icon" value="pi-history" />
<ui:define name="options">
<p:commandButton value="Lancer Sync complète" icon="pi pi-play"
actionListener="#{syncDashboardBean.forceSyncRealm}"
update="@form" styleClass="p-button-sm p-button-warning"
title="Forcer une synchronisation complète utilisateurs + rôles" />
</div>
update="@form" styleClass="ui-button-sm ui-button-warning" />
</ui:define>
<div class="flex flex-column gap-2">
<div class="flex flex-column gap-4 py-2">
<div class="flex justify-content-between align-items-center">
<span class="text-600">Date:</span>
<span class="text-900 font-medium">#{syncDashboardBean.lastSyncDate}</span>
<div class="flex align-items-center gap-2">
<i class="pi pi-calendar text-500"></i>
<span class="text-700">Date et heure</span>
</div>
<span class="text-900 font-bold">#{syncDashboardBean.lastSyncDate}</span>
</div>
<div class="flex justify-content-between align-items-center">
<span class="text-600">Statut:</span>
<div class="flex align-items-center gap-2">
<i class="pi pi-info-circle text-500"></i>
<span class="text-700">Statut de sortie</span>
</div>
<p:tag value="#{syncDashboardBean.lastSyncStatusLabel}"
severity="#{syncDashboardBean.lastSyncStatusLabel eq 'SUCCESS' ? 'success' :
syncDashboardBean.lastSyncStatusLabel eq 'FAILURE' ? 'danger' : 'info'}" />
syncDashboardBean.lastSyncStatusLabel eq 'FAILURE' ? 'danger' : 'info'}"
styleClass="px-3" />
</div>
<div class="flex justify-content-between align-items-center">
<span class="text-600">Éléments traités:</span>
<span class="text-900">#{syncDashboardBean.lastSyncItemsProcessed}</span>
<div class="flex justify-content-between align-items-center pt-3 border-top-1 surface-border">
<span class="text-600">Objets traités avec succès</span>
<span class="text-2xl font-bold text-primary">#{syncDashboardBean.lastSyncItemsProcessed}</span>
</div>
</div>
</div>
</div>
</ui:decorate>
<!-- Cohérence des Données Card -->
<div class="col-12 md:col-6">
<div class="card h-full">
<div class="flex align-items-center justify-content-between mb-3">
<h5 class="m-0">Cohérence des Données</h5>
<p:commandButton value="Vérifier" icon="pi pi-check-circle"
actionListener="#{syncDashboardBean.checkDataConsistency}"
update="@form" styleClass="p-button-sm p-button-outlined" />
</div>
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="colSize" value="col-12 lg:col-8" />
<ui:param name="title" value="Cohérence des Données" />
<ui:param name="icon" value="pi pi-database text-blue-500" />
<p:panelGrid columns="1" styleClass="w-full" rendered="#{syncDashboardBean.consistencyResult ne null}">
<div class="flex justify-content-between align-items-center mb-2">
<span class="text-600">Résultat:</span>
<p:tag value="#{syncDashboardBean.consistencyStatusLabel}"
severity="#{syncDashboardBean.consistencyStatusLabel eq 'OK' ? 'success' :
syncDashboardBean.consistencyStatusLabel eq 'ERROR' ? 'danger' : 'warning'}" />
</div>
<div class="flex justify-content-between align-items-center mb-2">
<span class="text-600">Utilisateurs Keycloak:</span>
<span class="text-900">#{syncDashboardBean.consistencyResult.usersKeycloakCount}</span>
</div>
<div class="flex justify-content-between align-items-center mb-2">
<span class="text-600">Utilisateurs locaux:</span>
<span class="text-900">#{syncDashboardBean.consistencyResult.usersLocalCount}</span>
</div>
<div class="flex justify-content-between align-items-center">
<span class="text-600">Éléments manquants:</span>
<span class="text-900 #{syncDashboardBean.consistencyMissingCount gt 0 ? 'text-red-500 font-bold' : ''}">
#{syncDashboardBean.consistencyMissingCount}
</span>
</div>
</p:panelGrid>
<p:outputPanel id="consistency-panel">
<h:panelGroup rendered="#{syncDashboardBean.consistencyResult ne null}">
<div class="flex flex-column gap-3">
<!-- Résultat Global -->
<div class="flex justify-content-between align-items-center p-3 border-round bg-emphasis">
<span class="text-700 font-medium">État de Synchronisation</span>
<p:tag value="#{syncDashboardBean.consistencyStatusLabel}"
severity="#{syncDashboardBean.consistencyStatusLabel eq 'OK' ? 'success' :
syncDashboardBean.consistencyStatusLabel eq 'ERROR' ? 'danger' : 'warning'}"
styleClass="text-xs px-3" />
</div>
<p:outputPanel rendered="#{syncDashboardBean.consistencyResult eq null}">
<div class="flex align-items-center justify-content-center py-4 text-600">
<i class="pi pi-info-circle mr-2"></i>
Cliquez sur "Vérifier" pour analyser la cohérence des données.
</div>
<p:divider styleClass="m-0" />
<!-- Détails des Comptes -->
<div class="grid mt-2">
<div class="col-12 md:col-6">
<div class="surface-ground border-round p-3 flex align-items-center gap-3">
<div class="w-3rem h-3rem bg-blue-100 border-circle flex align-items-center justify-content-center">
<i class="pi pi-key text-blue-600 text-xl"></i>
</div>
<div class="flex flex-column">
<span class="text-500 text-sm font-medium">Keycloak</span>
<span class="text-900 font-bold text-2xl">#{syncDashboardBean.consistencyResult.usersKeycloakCount}</span>
</div>
</div>
</div>
<div class="col-12 md:col-6">
<div class="surface-ground border-round p-3 flex align-items-center gap-3">
<div class="w-3rem h-3rem bg-green-100 border-circle flex align-items-center justify-content-center">
<i class="pi pi-database text-green-600 text-xl"></i>
</div>
<div class="flex flex-column">
<span class="text-500 text-sm font-medium">Base Locale (LUM)</span>
<span class="text-900 font-bold text-2xl">#{syncDashboardBean.consistencyResult.usersLocalCount}</span>
</div>
</div>
</div>
</div>
<p:divider styleClass="my-3" />
<!-- Écarts -->
<div class="flex justify-content-between align-items-center p-3 border-1 border-round #{syncDashboardBean.consistencyMissingCount gt 0 ? 'border-red-200 bg-red-50' : 'surface-border bg-gray-50'}">
<div class="flex align-items-center gap-3">
<div class="w-2rem h-2rem border-circle flex align-items-center justify-content-center #{syncDashboardBean.consistencyMissingCount gt 0 ? 'bg-red-500' : 'bg-green-500'}">
<i class="pi pi-exclamation-triangle text-white"></i>
</div>
<span class="text-700 font-semibold">Écarts de synchronisation identifiés</span>
</div>
<span class="text-2xl font-bold #{syncDashboardBean.consistencyMissingCount gt 0 ? 'text-red-700' : 'text-green-700'}">
#{syncDashboardBean.consistencyMissingCount}
</span>
</div>
</div>
</h:panelGroup>
<h:panelGroup rendered="#{syncDashboardBean.consistencyResult eq null}">
<ui:decorate template="/templates/components/shared/lions-empty-state.xhtml">
<ui:param name="icon" value="pi-search-plus" />
<ui:param name="title" value="Analyse requise" />
<ui:param name="description" value="Comparez les comptes Keycloak avec la base locale pour identifier d'éventuelles incohérences." />
</ui:decorate>
</h:panelGroup>
</p:outputPanel>
</div>
</ui:decorate>
</div>
</div>
</h:form>
</ui:define>

View File

@@ -8,22 +8,17 @@
<ui:define name="content">
<div class="grid">
<!-- En-tête -->
<div class="col-12">
<ui:decorate template="/templates/components/layout/page-header.xhtml">
<ui:param name="icon" value="pi pi-user-plus text-green-500" />
<ui:param name="title" value="Nouvel Utilisateur" />
<ui:param name="description" value="Créer un nouvel utilisateur dans Keycloak" />
<ui:param name="breadcrumbParent" value="Utilisateurs" />
<ui:param name="breadcrumbParentLink" value="/pages/user-manager/users/list" />
<ui:define name="actions">
<h:link outcome="/pages/user-manager/users/list"
styleClass="p-button p-button-outlined p-button-secondary">
<i class="pi pi-arrow-left mr-2"></i>Retour à la liste
</h:link>
</ui:define>
</ui:decorate>
</div>
<!-- En-tête -->
<div class="col-12">
<ui:decorate template="/templates/components/shared/lions-page-header.xhtml">
<ui:param name="title" value="Nouvel Utilisateur" />
<ui:param name="subtitle" value="Enregistrez un nouveau profil dans l'annuaire Keycloak et configurez ses accès initiaux." />
<ui:define name="actions">
<p:button value="Retour à la liste" icon="pi pi-arrow-left" outcome="/pages/user-manager/users/list"
styleClass="ui-button-outlined ui-button-secondary" />
</ui:define>
</ui:decorate>
</div>
<!-- Formulaire -->
<h:form id="formUserCreation" styleClass="col-12 grid m-0 p-0">
@@ -47,164 +42,112 @@
</div>
</h:panelGroup>
<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="col-12 lg:col-8 xl:col-6 mx-auto flex flex-column gap-4">
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Identité et Accès" />
<ui:param name="icon" value="pi pi-user-plus text-blue-500" />
<div class="grid">
<!-- Colonne gauche -->
<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>
<div class="field mb-3">
<p:outputLabel for="username" value="Nom d'utilisateur"
styleClass="font-medium" />
<span class="text-red-500 ml-1">*</span>
<div class="flex flex-column gap-3 mt-3">
<!-- Informations de Base -->
<div class="surface-ground border-round p-3">
<h4 class="text-900 font-semibold mb-3 flex align-items-center gap-2 m-0">
<i class="pi pi-id-card text-blue-500"></i>
<span>Informations de Base</span>
</h4>
<div class="grid">
<div class="col-12 md:col-6 field mb-3">
<p:outputLabel for="username" value="Nom d'utilisateur" styleClass="font-medium mb-2 block" />
<p:inputText id="username" value="#{userCreationBean.newUser.username}"
styleClass="w-full" required="true"
requiredMessage="Le nom d'utilisateur est obligatoire"
placeholder="ex: jdupont">
<f:validateLength minimum="3" maximum="50" />
</p:inputText>
<p:message for="username" display="text" styleClass="mt-1" />
<small class="text-500 block mt-1">Identifiant unique de connexion</small>
styleClass="w-full" required="true" placeholder="ex: jdupont" />
<p:message for="username" display="text" />
</div>
<div class="field mb-3">
<p:outputLabel for="email" value="Adresse email" styleClass="font-medium" />
<span class="text-red-500 ml-1">*</span>
<div class="col-12 md:col-6 field mb-3">
<p:outputLabel for="email" value="Adresse email" styleClass="font-medium mb-2 block" />
<p:inputText id="email" value="#{userCreationBean.newUser.email}"
styleClass="w-full" required="true"
requiredMessage="L'adresse email est obligatoire"
placeholder="ex: jean.dupont@example.com">
<f:validateRegex pattern="^[A-Za-z0-9+_.-]+@(.+)$" />
</p:inputText>
<p:message for="email" display="text" styleClass="mt-1" />
styleClass="w-full" required="true" placeholder="ex: jean.dupont@lions.dev" />
<p:message for="email" display="text" />
</div>
<div class="field mb-3">
<p:outputLabel for="prenom" value="Prénom" styleClass="font-medium" />
<span class="text-red-500 ml-1">*</span>
<div class="col-12 md:col-6 field mb-3">
<p:outputLabel for="prenom" value="Prénom" styleClass="font-medium mb-2 block" />
<p:inputText id="prenom" value="#{userCreationBean.newUser.prenom}"
styleClass="w-full" required="true"
requiredMessage="Le prénom est obligatoire"
placeholder="ex: Jean">
<f:validateLength minimum="2" maximum="100" />
</p:inputText>
<p:message for="prenom" display="text" styleClass="mt-1" />
styleClass="w-full" required="true" placeholder="Jean" />
<p:message for="prenom" display="text" />
</div>
<div class="field mb-0">
<p:outputLabel for="nom" value="Nom" styleClass="font-medium" />
<span class="text-red-500 ml-1">*</span>
<div class="col-12 md:col-6 field mb-3">
<p:outputLabel for="nom" value="Nom" styleClass="font-medium mb-2 block" />
<p:inputText id="nom" value="#{userCreationBean.newUser.nom}"
styleClass="w-full" required="true"
requiredMessage="Le nom est obligatoire"
placeholder="ex: Dupont">
<f:validateLength minimum="2" maximum="100" />
</p:inputText>
<p:message for="nom" display="text" styleClass="mt-1" />
styleClass="w-full" required="true" placeholder="Dupont" />
<p:message for="nom" display="text" />
</div>
</div>
</div>
<!-- Colonne droite -->
<div class="col-12 lg:col-6">
<div class="surface-50 border-round p-3 mb-3 ui-fluid">
<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>
<div class="field mb-3">
<p:outputLabel for="password" value="Mot de passe"
styleClass="font-medium" />
<span class="text-red-500 ml-1">*</span>
<!-- Mot de Passe -->
<div class="surface-ground border-round p-3">
<h4 class="text-900 font-semibold mb-3 flex align-items-center gap-2 m-0">
<i class="pi pi-key text-orange-500"></i>
<span>Sécurité du Compte</span>
</h4>
<div class="grid">
<div class="col-12 md:col-6 field mb-3">
<p:outputLabel for="password" value="Mot de passe" styleClass="font-medium mb-2 block" />
<p:password id="password" value="#{userCreationBean.password}"
styleClass="w-full" required="true"
requiredMessage="Le mot de passe est obligatoire"
feedback="true" toggleMask="true"
placeholder="Minimum 8 caractères">
<f:validateLength minimum="8" maximum="100" />
</p:password>
<p:message for="password" display="text" styleClass="mt-1" />
<small class="text-500 block mt-1">Au moins 8 caractères</small>
styleClass="w-full" required="true" feedback="true" toggleMask="true" />
<p:message for="password" display="text" />
</div>
<div class="field mb-0">
<p:outputLabel for="passwordConfirm" value="Confirmer le mot de passe"
styleClass="font-medium" />
<span class="text-red-500 ml-1">*</span>
<p:password id="passwordConfirm"
value="#{userCreationBean.passwordConfirm}"
styleClass="w-full" required="true"
requiredMessage="Veuillez confirmer le mot de passe"
feedback="false" toggleMask="true"
placeholder="Confirmer le mot de passe" />
<p:message for="passwordConfirm" display="text" styleClass="mt-1" />
<div class="col-12 md:col-6 field mb-3">
<p:outputLabel for="passwordConfirm" value="Confirmation" styleClass="font-medium mb-2 block" />
<p:password id="passwordConfirm" value="#{userCreationBean.passwordConfirm}"
styleClass="w-full" required="true" feedback="false" toggleMask="true" />
<p:message for="passwordConfirm" display="text" />
</div>
</div>
</div>
<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>
<div class="field mb-3">
<p:outputLabel for="realm" value="Realm Keycloak"
styleClass="font-medium" />
<p:selectOneMenu id="realm" value="#{userCreationBean.realmName}"
styleClass="w-full">
<f:selectItems value="#{userCreationBean.availableRealms}" var="realm"
itemLabel="#{realm}" itemValue="#{realm}" />
</p:selectOneMenu>
<!-- Configuration -->
<div class="surface-ground border-round p-3">
<h4 class="text-900 font-semibold mb-3 flex align-items-center gap-2 m-0">
<i class="pi pi-cog text-purple-500"></i>
<span>Paramètres Système</span>
</h4>
<div class="field mb-3 mt-2">
<p:outputLabel for="realm" value="Realm Keycloak" styleClass="font-medium mb-2 block" />
<p:selectOneMenu id="realm" value="#{userCreationBean.realmName}" styleClass="w-full">
<f:selectItems value="#{userCreationBean.availableRealms}" />
</p:selectOneMenu>
</div>
<div class="flex flex-wrap gap-4 pt-2">
<div class="flex align-items-center gap-2">
<p:selectBooleanCheckbox id="enabled" value="#{userCreationBean.newUser.enabled}" />
<p:outputLabel for="enabled" value="Activer le compte immédiatement" />
</div>
<div class="flex flex-column gap-3">
<div class="flex align-items-center gap-2">
<p:selectBooleanCheckbox id="enabled"
value="#{userCreationBean.newUser.enabled}" />
<p:outputLabel for="enabled" value="Compte activé" />
</div>
<div class="flex align-items-center gap-2">
<p:selectBooleanCheckbox id="emailVerified"
value="#{userCreationBean.newUser.emailVerified}" />
<p:outputLabel for="emailVerified" value="Email vérifié" />
</div>
<div class="flex align-items-center gap-2">
<p:selectBooleanCheckbox id="emailVerified" value="#{userCreationBean.newUser.emailVerified}" />
<p:outputLabel for="emailVerified" value="Marquer l'email comme vérifié" />
</div>
</div>
</div>
</div>
</div>
</div>
</ui:decorate>
<!-- Actions -->
<div class="col-12">
<div class="card">
<!-- Actions -->
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<div class="flex flex-wrap gap-2 align-items-center">
<p:commandButton value="Créer l'utilisateur" icon="pi pi-check"
styleClass="p-button-success" action="#{userCreationBean.createUser}"
<p:commandButton value="Finaliser la Création" icon="pi pi-check"
styleClass="ui-button-success" action="#{userCreationBean.createUser}"
update=":formUserCreation" validateClient="true" />
<p:commandButton value="Réinitialiser" icon="pi pi-refresh"
styleClass="p-button-outlined p-button-secondary"
<p:commandButton value="Reset" icon="pi pi-refresh"
styleClass="ui-button-outlined ui-button-secondary"
action="#{userCreationBean.resetForm}"
process="@this" update=":formUserCreation" immediate="true">
<p:confirm header="Confirmation"
message="Voulez-vous vraiment réinitialiser le formulaire ?"
message="Réinitialiser tous les champs ?"
icon="pi pi-exclamation-triangle" />
</p:commandButton>
<p:commandButton value="Annuler" icon="pi pi-times"
styleClass="p-button-outlined"
styleClass="ui-button-outlined"
action="#{userCreationBean.cancel}" immediate="true"
ajax="false">
</p:commandButton>
@@ -212,14 +155,12 @@
<div class="flex-grow-1"></div>
<p:commandButton value="Aide" icon="pi pi-question-circle"
styleClass="p-button-outlined p-button-help" type="button"
styleClass="ui-button-outlined ui-button-help" type="button"
onclick="PF('helpDialog').show();" />
</div>
<p:messages id="messages" showDetail="true" closable="true" styleClass="mt-3">
<p:autoUpdate />
</p:messages>
</div>
<p:messages id="messages" showDetail="true" closable="true" styleClass="mt-3" />
</ui:decorate>
</div>
</h:form>
</div>

View File

@@ -13,36 +13,43 @@
<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 et gérer ses rôles" />
</ui:include>
<div class="grid">
<!-- En-tête -->
<ui:decorate template="/templates/components/shared/lions-page-header.xhtml">
<ui:param name="title" value="Modifier Utilisateur" />
<ui:param name="subtitle" value="Mettez à jour les informations du profil et gérez les habilitations d'accès pour cet utilisateur." />
<ui:define name="actions">
<p:button value="Retour" icon="pi pi-arrow-left" outcome="/pages/user-manager/users/list"
styleClass="ui-button-outlined ui-button-secondary" />
</ui:define>
</ui:decorate>
<div class="card">
<p:tabView id="userTabs">
<p:tab title="Profil Utilisateur">
<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:param name="useParentForm" value="false" />
</ui:include>
</p:tab>
<div class="col-12">
<div class="card">
<p:tabView id="userTabs">
<p:tab title="Profil Utilisateur">
<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:param name="useParentForm" value="false" />
</ui:include>
</p:tab>
<p:tab title="Gestion des Rôles" rendered="#{not empty userProfilBean.user.id}">
<h:form id="rolesForm">
<ui:include src="/templates/components/user-management/user-roles-manager.xhtml">
<ui:param name="user" value="#{userProfilBean.user}" />
<ui:param name="realm" value="#{userProfilBean.realmName}" />
<ui:param name="roleBean" value="#{roleGestionBean}" />
</ui:include>
</h:form>
</p:tab>
</p:tabView>
<p:tab title="Gestion des Rôles" rendered="#{not empty userProfilBean.user.id}">
<h:form id="rolesForm">
<ui:include src="/templates/components/user-management/user-roles-manager.xhtml">
<ui:param name="user" value="#{userProfilBean.user}" />
<ui:param name="realm" value="#{userProfilBean.realmName}" />
<ui:param name="roleBean" value="#{roleGestionBean}" />
</ui:include>
</h:form>
</p:tab>
</p:tabView>
</div>
</div>
</div>
</ui:define>

View File

@@ -8,18 +8,16 @@
<ui:define name="content">
<!-- En-tête -->
<ui:decorate template="/templates/components/layout/page-header.xhtml">
<ui:param name="icon" value="pi pi-users text-blue-500" />
<ui:decorate template="/templates/components/shared/lions-page-header.xhtml">
<ui:param name="title" value="Gestion des Utilisateurs" />
<ui:param name="description"
value="Gestion centralisée des utilisateurs Keycloak - Recherche, création, modification et suppression" />
<ui:param name="subtitle" value="Annuaire centralisé Keycloak. Gérez les comptes, les accès et la sécurité en temps réel." />
<ui:define name="actions">
<h:form id="formHeaderActions">
<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="Rafraîchir" icon="pi pi-refresh"
action="#{userListBean.refreshData}" update=":formUserList" styleClass="ui-button-outlined" />
<p:commandButton value="Nouvel Utilisateur" icon="pi pi-user-plus"
styleClass="p-button-success" outcome="/pages/user-manager/users/create" />
styleClass="ui-button-primary" outcome="/pages/user-manager/users/create" />
</div>
</h:form>
</ui:define>
@@ -33,115 +31,40 @@
<h:form id="formUserList">
<div class="grid">
<!-- ================================================================
STATISTIQUES KPI (4 CARTES)
================================================================ -->
<div class="col-12">
<h5 class="mb-3">Statistiques des Utilisateurs</h5>
</div>
<!-- Statistiques -->
<ui:decorate template="/templates/components/shared/cards/lions-stat-card.xhtml">
<ui:param name="title" value="Total Utilisateurs" />
<ui:param name="value" value="#{userListBean.kpiTotalUsers}" />
<ui:param name="icon" value="pi-users" />
<ui:param name="iconColor" value="blue-600" />
<ui:param name="subtitle" value="Utilisateurs dans le realm" />
</ui:decorate>
<!-- 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.kpiTotalUsers}</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>
<ui:decorate template="/templates/components/shared/cards/lions-stat-card.xhtml">
<ui:param name="title" value="Taux d'activation" />
<ui:param name="value" value="#{userListBean.activeUsersCount}" />
<ui:param name="icon" value="pi-check-circle" />
<ui:param name="iconColor" value="green-600" />
<ui:param name="subtitle" value="#{userListBean.activeUsersPercentage}% activés" />
</ui:decorate>
<ui:decorate template="/templates/components/shared/cards/lions-stat-card.xhtml">
<ui:param name="title" value="Désactivés" />
<ui:param name="value" value="#{userListBean.disabledUsersCount}" />
<ui:param name="icon" value="pi-times-circle" />
<ui:param name="iconColor" value="red-600" />
<ui:param name="subtitle" value="Comptes suspendus" />
</ui:decorate>
<!-- 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>
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Critères de Recherche" />
<ui:param name="icon" value="pi pi-search text-blue-500" />
<div class="grid">
<div class="grid mt-2">
<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}"
@@ -171,11 +94,11 @@
<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}"
styleClass="ui-button-outlined w-full" action="#{userListBean.resetSearch}"
update=":formUserList:userTable @form" />
</div>
</div>
</div>
</ui:decorate>
</div>
<!-- ================================================================
@@ -198,37 +121,33 @@
</ui:include>
</div>
<!-- ================================================================
ACTIONS RAPIDES
================================================================ -->
<!-- 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>
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Outils et Actions" />
<ui:param name="icon" value="pi pi-bolt text-orange-500" />
<div class="grid">
<div class="grid mt-2">
<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" />
<p:button value="Créer un Utilisateur" icon="pi pi-user-plus"
styleClass="w-full ui-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}"
<p:commandButton value="Exporter CSV" icon="pi pi-download"
styleClass="w-full ui-button-help" 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()"
<p:commandButton value="Importer JSON/CSV" icon="pi pi-upload"
styleClass="w-full ui-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" />
<p:button value="Gestion des Rôles" icon="pi pi-shield"
styleClass="w-full ui-button-primary" outcome="/pages/user-manager/roles/list" />
</div>
</div>
</div>
</ui:decorate>
</div>
</div>
</h:form>

View File

@@ -12,29 +12,21 @@
<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>
<!-- En-tête -->
<ui:decorate template="/templates/components/shared/lions-page-header.xhtml">
<ui:param name="title" value="Mon Profil" />
<ui:param name="subtitle" value="Gérez vos informations personnelles et consultez vos statistiques de session." />
<ui:define name="actions">
<p:button value="Dashboard" icon="pi pi-home" outcome="/pages/user-manager/dashboard"
styleClass="ui-button-outlined ui-button-secondary" />
</ui:define>
</ui:decorate>
<!-- ================================================================
CARTE PROFIL PRINCIPAL
================================================================ -->
<div class="col-12">
<div class="card">
<!-- Carte Profil Principal -->
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Vue d'ensemble du Profil" />
<ui:param name="icon" value="pi pi-user text-blue-500" />
<ui:param name="styleClass" value="mb-4" />
<div class="grid">
<!-- Photo de profil et informations principales -->
<div class="col-12 md:col-4">
@@ -47,25 +39,11 @@
<!-- 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>
<!-- Email supprimé pour éviter la redondance -->
<!-- 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 class="flex flex-column align-items-center gap-2">
<p:tag severity="success" icon="pi pi-circle-fill" value="CONNECTÉ" styleClass="px-3" />
<p:tag severity="info" value="#{userSessionBean.primaryRole}" styleClass="px-3" style="text-transform: uppercase; letter-spacing: 0.5px;" />
</div>
</div>
</div>
@@ -120,54 +98,29 @@
<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>
<p:tag value="#{role}" severity="info" styleClass="px-2 py-1"></p:tag>
</ui:repeat>
</div>
</div>
<div class="mb-3 pb-3 border-bottom-1 surface-border">
<div class="mb-3">
<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>
<p:tag value="#{userSessionBean.primaryRole}" severity="success" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</ui:decorate>
<!-- ================================================================
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>
<!-- Informations de Session -->
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Informations de Session OIDC" />
<ui:param name="icon" value="pi pi-shield text-orange-500" />
<ui:param name="styleClass" value="mb-4" />
<div class="p-3 surface-50 border-round">
<div class="grid">
<!-- Colonne gauche: Token Information -->
@@ -198,7 +151,7 @@
<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>
<p:tag value="Bearer" severity="info"></p:tag>
</div>
</div>
</div>
@@ -253,17 +206,13 @@
</div>
</div>
</div>
</div>
</ui:decorate>
<!-- ================================================================
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>
<!-- Statistiques d'Activité -->
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Statistiques d'Activité" />
<ui:param name="icon" value="pi pi-chart-line text-green-500" />
<ui:param name="styleClass" value="mb-4" />
<div class="grid">
<div class="col-12 md:col-6 lg:col-3">
@@ -310,18 +259,13 @@
</div>
</div>
</div>
</div>
</div>
</ui:decorate>
<!-- ================================================================
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>
<!-- Actions Rapides -->
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Actions Rapides de Sécurité" />
<ui:param name="icon" value="pi pi-cog text-gray-700" />
<ui:param name="styleClass" value="mb-4" />
<h:form id="formProfileActions">
<div class="grid">
@@ -364,36 +308,31 @@
<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="Voir mes sessions actives"
icon="pi pi-desktop"
styleClass="ui-button-outlined ui-button-info w-full mb-2"
disabled="true">
</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="Historique des connexions"
icon="pi pi-history"
styleClass="ui-button-outlined ui-button-secondary w-full mb-2"
disabled="true">
</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>
<p:commandButton value="Se déconnecter"
icon="pi pi-sign-out"
styleClass="ui-button-danger w-full"
action="#{userSessionBean.logout}">
<p:confirm header="Déconnexion"
message="Voulez-vous vraiment fermer votre session ?"
icon="pi pi-exclamation-triangle" />
</p:commandButton>
</div>
</div>
</div>
</h:form>
</div>
</div>
</ui:decorate>
</div>
<!-- ================================================================

View File

@@ -17,35 +17,28 @@
<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>
Profil Utilisateur
</h2>
<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>
<!-- En-tête -->
<ui:decorate template="/templates/components/shared/lions-page-header.xhtml">
<ui:param name="title" value="Profil Utilisateur" />
<ui:param name="subtitle" value="Consultez les informations détaillées et gérez l'état du compte de cet utilisateur." />
<ui:define name="actions">
<p:button value="Retour" icon="pi pi-arrow-left" outcome="/pages/user-manager/users/list"
styleClass="ui-button-outlined ui-button-secondary" />
</ui:define>
</ui:decorate>
<!-- ================================================================
CONTENU PRINCIPAL (si utilisateur trouvé)
================================================================ -->
<ui:fragment rendered="#{userProfilBean.user != null}">
<!-- ============================================================
CARTE PROFIL PRINCIPAL
============================================================ -->
<div class="col-12">
<div class="card">
<div class="grid">
<!-- Carte Profil Principal -->
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Vue d'ensemble du Profil" />
<ui:param name="icon" value="pi pi-user text-blue-500" />
<ui:param name="styleClass" value="mb-4" />
<div class="grid">
<!-- Photo de profil et informations principales -->
<div class="col-12 md:col-4">
<div class="text-center mb-4">
@@ -62,37 +55,16 @@
<!-- Username -->
<p class="text-500 mb-2">@#{userProfilBean.user.username}</p>
<!-- Email -->
<p class="text-600 mb-3 flex align-items-center justify-content-center gap-2">
<i class="pi pi-envelope"></i>
#{userProfilBean.user.email}
<h:panelGroup rendered="#{userProfilBean.user.emailVerified}">
<i class="pi pi-check-circle text-green-500" title="Email vérifié"></i>
</h:panelGroup>
</p>
<!-- Email (Supprimé car présent à droite) -->
<!-- Badge de statut -->
<div class="inline-flex align-items-center justify-content-center gap-2 mb-3">
<h:panelGroup rendered="#{userProfilBean.user.enabled}">
<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>Actif</span>
</span>
</h:panelGroup>
<h:panelGroup rendered="#{not userProfilBean.user.enabled}">
<span class="inline-flex align-items-center gap-2 bg-red-100 text-red-700 px-3 py-2 border-round font-semibold" style="font-size: 1rem;">
<i class="pi pi-circle-fill" style="font-size: 0.5rem;"></i>
<span>Inactif</span>
</span>
</h:panelGroup>
<div class="flex justify-content-center mb-3">
<p:tag severity="#{userProfilBean.user.enabled ? 'success' : 'danger'}"
icon="pi pi-circle-fill"
value="#{userProfilBean.user.enabled ? 'ACTIF' : 'INACTIF'}"
styleClass="px-3">
</p:tag>
</div>
<!-- Badge du rôle principal -->
<div class="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;">
#{userProfilBean.primaryRoleName}
</span>
</div>
</div>
</div>
@@ -101,60 +73,63 @@
<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="p-3 surface-50 border-round h-full">
<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">#{userProfilBean.user.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">Prénom</label>
<p class="text-900 font-semibold m-0">#{userProfilBean.user.prenom}</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</label>
<p class="text-900 font-semibold m-0">#{userProfilBean.user.nom}</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">#{userProfilBean.user.email}</p>
<h:panelGroup rendered="#{userProfilBean.user.emailVerified}">
<i class="pi pi-check-circle text-green-500" title="Email vérifié"></i>
</h:panelGroup>
<h:panelGroup rendered="#{not userProfilBean.user.emailVerified}">
<i class="pi pi-times-circle text-red-400" title="Email non vérifié"></i>
</h:panelGroup>
</div>
</div>
<h:panelGroup rendered="#{not empty userProfilBean.user.telephone}">
<div class="mb-3 pb-3 border-bottom-1 surface-border">
<label class="block text-600 font-medium mb-1 text-sm">Téléphone</label>
<p class="text-900 font-semibold m-0">#{userProfilBean.user.telephone}</p>
<label class="block text-600 font-medium mb-1 text-sm">Nom d'utilisateur</label>
<p class="text-900 font-semibold m-0">#{userProfilBean.user.username}</p>
</div>
</h:panelGroup>
<h:panelGroup rendered="#{not empty userProfilBean.user.langue}">
<div class="mb-3">
<label class="block text-600 font-medium mb-1 text-sm">Langue</label>
<p class="text-900 font-semibold m-0">#{userProfilBean.user.langue}</p>
<div class="mb-3 pb-3 border-bottom-1 surface-border">
<label class="block text-600 font-medium mb-1 text-sm">Prénom</label>
<p class="text-900 font-semibold m-0">#{userProfilBean.user.prenom}</p>
</div>
</h:panelGroup>
<div class="mb-3 pb-3 border-bottom-1 surface-border">
<label class="block text-600 font-medium mb-1 text-sm">Nom</label>
<p class="text-900 font-semibold m-0">#{userProfilBean.user.nom}</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">#{userProfilBean.user.email}</p>
<h:panelGroup rendered="#{userProfilBean.user.emailVerified}">
<i class="pi pi-check-circle text-green-500" title="Email vérifié"></i>
</h:panelGroup>
<h:panelGroup rendered="#{not userProfilBean.user.emailVerified}">
<i class="pi pi-times-circle text-red-400" title="Email non vérifié"></i>
</h:panelGroup>
</div>
</div>
<h:panelGroup rendered="#{not empty userProfilBean.user.telephone}">
<div class="mb-3 pb-3 border-bottom-1 surface-border">
<label class="block text-600 font-medium mb-1 text-sm">Téléphone</label>
<p class="text-900 font-semibold m-0">#{userProfilBean.user.telephone}</p>
</div>
</h:panelGroup>
<h:panelGroup rendered="#{not empty userProfilBean.user.langue}">
<div class="mb-3">
<label class="block text-600 font-medium mb-1 text-sm">Langue</label>
<p class="text-900 font-semibold m-0">#{userProfilBean.user.langue}</p>
</div>
</h:panelGroup>
</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="p-3 surface-50 border-round h-full">
<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>
@@ -162,7 +137,7 @@
<c:choose>
<c:when test="#{not empty userProfilBean.user.realmRoles and not userProfilBean.user.realmRoles.isEmpty()}">
<ui:repeat value="#{userProfilBean.user.realmRoles}" var="role">
<p:badge value="#{role}" severity="info" styleClass="text-sm"></p:badge>
<p:tag value="#{role}" severity="info" styleClass="px-2 py-1"></p:tag>
</ui:repeat>
</c:when>
<c:otherwise>
@@ -175,49 +150,33 @@
<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="#{userProfilBean.primaryRoleName}"
severity="success"
styleClass="text-sm">
</p:badge>
<p:tag value="#{userProfilBean.primaryRoleName}"
severity="success">
</p:tag>
</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">#{userProfilBean.primaryRoleName}</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">
<h:panelGroup rendered="#{userProfilBean.user.enabled}">
<p:badge value="Actif" severity="success" styleClass="text-sm"></p:badge>
</h:panelGroup>
<h:panelGroup rendered="#{not userProfilBean.user.enabled}">
<p:badge value="Inactif" severity="danger" styleClass="text-sm"></p:badge>
</h:panelGroup>
</div>
<!-- Redondances de statut et niveau d'accès supprimées -->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</ui:decorate>
<!-- ============================================================
INFORMATIONS DU COMPTE
============================================================ -->
<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-id-card text-teal-500"></i>
Informations du Compte
</h3>
<!-- Informations du Compte -->
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Détails de l'Organisation et Activité" />
<ui:param name="icon" value="pi pi-id-card text-teal-500" />
<ui:param name="styleClass" value="mb-4" />
<div class="grid">
<div class="grid p-2">
<div class="col-12 md:col-6">
<h4 class="text-900 font-semibold mb-3">Contact et Organisation</h4>
<div class="p-3 surface-50 border-round h-full">
<h4 class="text-900 font-semibold mb-3 flex align-items-center gap-2">
<i class="pi pi-building text-blue-500"></i>
<span>Contact et Organisation</span>
</h4>
<h:panelGroup rendered="#{not empty userProfilBean.user.organisation}">
<div class="mb-3">
@@ -259,10 +218,15 @@
<p class="text-900 m-0">#{userProfilBean.user.ville}</p>
</div>
</h:panelGroup>
</div>
</div>
<div class="col-12 md:col-6">
<h4 class="text-900 font-semibold mb-3">Activité et Sécurité</h4>
<div class="p-3 surface-50 border-round h-full">
<h4 class="text-900 font-semibold mb-3 flex align-items-center gap-2">
<i class="pi pi-history text-green-500"></i>
<span>Activité et Sécurité</span>
</h4>
<div class="mb-3">
<label class="block text-600 font-medium mb-1 text-sm">Realm Keycloak</label>
@@ -303,7 +267,7 @@
<label class="block text-600 font-medium mb-2 text-sm">Groupes</label>
<div class="flex flex-wrap gap-2">
<ui:repeat value="#{userProfilBean.user.groups}" var="group">
<p:badge value="#{group}" severity="warning" styleClass="text-sm"></p:badge>
<p:tag value="#{group}" severity="warning" styleClass="px-2 py-1"></p:tag>
</ui:repeat>
</div>
</div>
@@ -311,17 +275,13 @@
</div>
</div>
</div>
</div>
</ui:decorate>
<!-- ============================================================
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
</h3>
<!-- Actions de Gestion -->
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Opérations de Gestion du Compte" />
<ui:param name="icon" value="pi pi-cog text-gray-700" />
<ui:param name="styleClass" value="mb-4" />
<h:form id="formUserActions">
<div class="grid">
@@ -333,17 +293,17 @@
<span>Gestion du Profil</span>
</h4>
<div class="flex flex-column gap-2">
<h:link outcome="/pages/user-manager/users/edit"
styleClass="p-button p-button-outlined w-full justify-content-start">
<p:button outcome="/pages/user-manager/users/edit"
value="Modifier les informations"
icon="pi pi-user-edit"
styleClass="ui-button-outlined w-full">
<f:param name="userId" value="#{userProfilBean.userId}" />
<f:param name="realm" value="#{userProfilBean.realmName}" />
<i class="pi pi-pencil mr-2"></i>
Modifier l'utilisateur
</h:link>
</p:button>
<p:commandButton value="Réinitialiser le mot de passe"
icon="pi pi-key"
styleClass="p-button-outlined p-button-warning w-full justify-content-start"
styleClass="ui-button-outlined ui-button-warning w-full"
action="#{userProfilBean.resetPassword}"
update=":formUserActions"
oncomplete="PF('dlgResetPassword').show()">
@@ -370,74 +330,58 @@
<i class="pi pi-shield text-purple-500"></i>
<span>Gestion du Compte</span>
</h4>
<div class="flex flex-column gap-2">
<p:commandButton value="Activer le compte"
icon="pi pi-check-circle"
styleClass="p-button-outlined p-button-success w-full justify-content-start"
styleClass="ui-button-success w-full mb-2"
action="#{userProfilBean.activateUser}"
update="@form"
rendered="#{not userProfilBean.user.enabled}">
<p:confirm header="Confirmation d'activation"
message="Activer le compte de #{userProfilBean.user.prenom} #{userProfilBean.user.nom} ?"
icon="pi pi-check-circle" />
<p:confirm header="Confirmation" message="Voulez-vous activer ce compte utilisateur ?" />
</p:commandButton>
<p:commandButton value="Désactiver le compte"
icon="pi pi-ban"
styleClass="p-button-outlined p-button-warning w-full justify-content-start"
styleClass="ui-button-warning ui-button-outlined w-full mb-2"
action="#{userProfilBean.deactivateUser}"
update="@form"
rendered="#{userProfilBean.user.enabled}">
<p:confirm header="Confirmation de désactivation"
message="Désactiver le compte de #{userProfilBean.user.prenom} #{userProfilBean.user.nom} ?"
icon="pi pi-exclamation-triangle" />
<p:confirm header="Attention" message="Voulez-vous désactiver ce compte utilisateur ?" icon="pi pi-exclamation-triangle" />
</p:commandButton>
<p:commandButton value="Déconnecter toutes les sessions"
icon="pi pi-sign-out"
styleClass="p-button-outlined p-button-secondary w-full justify-content-start"
styleClass="ui-button-secondary ui-button-outlined w-full mb-2"
action="#{userProfilBean.logoutAllSessions}"
update="@form">
<p:confirm header="Confirmation"
message="Déconnecter toutes les sessions actives de #{userProfilBean.user.prenom} #{userProfilBean.user.nom} ?"
icon="pi pi-sign-out" />
<p:confirm header="Déconnexion forcée" message="Déconnecter toutes les sessions actives ?" icon="pi pi-sign-out" />
</p:commandButton>
<p:commandButton value="Supprimer l'utilisateur"
icon="pi pi-trash"
styleClass="p-button-danger w-full justify-content-start"
styleClass="ui-button-danger ui-button-flat w-full"
action="#{userProfilBean.deleteUser}"
update="@form">
<p:confirm header="Suppression définitive"
message="Supprimer définitivement #{userProfilBean.user.prenom} #{userProfilBean.user.nom} ? Cette action est irréversible."
icon="pi pi-exclamation-triangle" />
<p:confirm header="Suppression" message="Action IRRÉVERSIBLE. Supprimer cet utilisateur définitivement ?" icon="pi pi-exclamation-triangle" />
</p:commandButton>
</div>
</div>
</div>
</div>
</h:form>
</div>
</div>
</ui:fragment>
</ui:decorate>
</ui:fragment>
<!-- ================================================================
UTILISATEUR NON TROUVÉ
================================================================ -->
<!-- Utilisateur non trouvé -->
<ui:fragment rendered="#{userProfilBean.user == null}">
<div class="col-12">
<div class="card">
<div class="text-center p-5">
<i class="pi pi-exclamation-triangle text-orange-500" style="font-size: 3rem"></i>
<h3 class="text-900 font-semibold mt-3 mb-2">Utilisateur non trouvé</h3>
<p class="text-600">L'utilisateur demandé n'existe pas ou n'a pas pu être chargé.</p>
<h:link outcome="/pages/user-manager/users/list" styleClass="p-button mt-3">
<i class="pi pi-arrow-left mr-2"></i>
Retour à la liste
</h:link>
</div>
<ui:decorate template="/templates/components/shared/lions-card.xhtml">
<ui:param name="title" value="Avertissement" />
<ui:param name="icon" value="pi pi-exclamation-triangle text-orange-500" />
<div class="text-center p-5">
<h3 class="text-900 font-semibold mt-3 mb-2">Utilisateur non trouvé</h3>
<p class="text-600">L'utilisateur demandé n'existe pas ou n'a pas pu être chargé.</p>
<p:button outcome="/pages/user-manager/users/list" value="Retour à la liste" icon="pi pi-arrow-left"
styleClass="mt-3 ui-button-outlined" />
</div>
</div>
</ui:decorate>
</ui:fragment>
</div>

View File

@@ -77,7 +77,7 @@
rowsPerPageTemplate="10,20,50,100"
currentPageReportTemplate="Affichage {startRecord}-{endRecord} sur {totalRecords}"
emptyMessage="#{emptyMessage}" reflow="true" responsiveLayout="scroll" lazy="#{not empty lazy and lazy}"
sortMode="multiple" resizableColumns="true" scrollable="true">
sortMode="multiple">
<f:facet name="header">
<div class="flex align-items-center justify-content-between flex-wrap gap-2">
@@ -170,7 +170,7 @@
</c:if>
</c:when>
<c:otherwise>
<span class="text-400 text-xs font-italic">Aucun rôle</span>
<p:tag value="Aucun rôle" severity="secondary" styleClass="text-xs" style="opacity: 0.6" />
</c:otherwise>
</c:choose>
</div>