feat(client): Intégration thème Freya et création dashboard

- Intégration complète thème Freya (CSS, JS, images, icons)
- Création GuestPreferences bean pour gestion thème
- Mise à jour main-template.xhtml avec structure Freya
- Création dashboard.xhtml avec statistiques et actions rapides
- Correction menu et topbar pour navigation
This commit is contained in:
lionsdev
2025-12-05 16:23:47 +00:00
parent 5ec22f2a6a
commit 873bef5145
29 changed files with 11146 additions and 49 deletions

View File

@@ -0,0 +1,147 @@
package dev.lions.user.manager.client.view;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Named;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Named("guestPreferences")
@SessionScoped
public class GuestPreferences implements Serializable {
private static final long serialVersionUID = 1L;
private String theme = "blue-light";
private String layout = "light";
private String componentTheme = "blue-light";
private String darkMode = "light";
private String menuMode = "layout-sidebar";
private String topbarTheme = "light";
private String menuTheme = "light";
private String inputStyle = "outlined";
private boolean lightLogo = false;
public String getTheme() {
return theme;
}
public void setTheme(String theme) {
this.theme = theme;
}
public String getLayout() {
return layout;
}
public void setLayout(String layout) {
this.layout = layout;
}
public String getComponentTheme() {
return componentTheme;
}
public void setComponentTheme(String componentTheme) {
this.componentTheme = componentTheme;
}
public String getDarkMode() {
return darkMode;
}
public void setDarkMode(String darkMode) {
this.darkMode = darkMode;
this.lightLogo = "dark".equals(darkMode);
}
public String getMenuMode() {
return menuMode;
}
public void setMenuMode(String menuMode) {
this.menuMode = menuMode;
}
public String getTopbarTheme() {
return topbarTheme;
}
public void setTopbarTheme(String topbarTheme) {
this.topbarTheme = topbarTheme;
}
public String getMenuTheme() {
return menuTheme;
}
public void setMenuTheme(String menuTheme) {
this.menuTheme = menuTheme;
}
public String getInputStyle() {
return inputStyle;
}
public void setInputStyle(String inputStyle) {
this.inputStyle = inputStyle;
}
public boolean isLightLogo() {
return lightLogo;
}
public void setLightLogo(boolean lightLogo) {
this.lightLogo = lightLogo;
}
public String getInputStyleClass() {
return "p-input-" + inputStyle;
}
public String getLayoutClass() {
return "layout-" + layout + " layout-theme-" + theme;
}
public List<ComponentTheme> getComponentThemes() {
List<ComponentTheme> themes = new ArrayList<>();
themes.add(new ComponentTheme("blue-light", "Blue", "#007ad9"));
themes.add(new ComponentTheme("green-light", "Green", "#28a745"));
themes.add(new ComponentTheme("orange-light", "Orange", "#fd7e14"));
themes.add(new ComponentTheme("purple-light", "Purple", "#6f42c1"));
themes.add(new ComponentTheme("pink-light", "Pink", "#e83e8c"));
themes.add(new ComponentTheme("indigo-light", "Indigo", "#6610f2"));
themes.add(new ComponentTheme("teal-light", "Teal", "#20c997"));
themes.add(new ComponentTheme("cyan-light", "Cyan", "#17a2b8"));
return themes;
}
public void onMenuTypeChange() {
// Called when menu type changes
}
public static class ComponentTheme {
private String file;
private String name;
private String color;
public ComponentTheme(String file, String name, String color) {
this.file = file;
this.name = name;
this.color = color;
}
public String getFile() {
return file;
}
public String getName() {
return name;
}
public String getColor() {
return color;
}
}
}

View File

@@ -0,0 +1,160 @@
<!DOCTYPE html>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
template="/templates/main-template.xhtml">
<ui:define name="title">Tableau de Bord - Lions User Manager</ui:define>
<ui:define name="content">
<div class="grid">
<!-- En-tête -->
<div class="col-12">
<ui:include src="/templates/components/layout/page-header.xhtml">
<ui:param name="icon" value="pi pi-home text-blue-500" />
<ui:param name="title" value="Tableau de Bord" />
<ui:param name="description" value="Vue d'ensemble de la gestion des utilisateurs Keycloak" />
</ui:include>
</div>
<!-- KPIs Principaux -->
<div class="col-12">
<div class="grid">
<!-- 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="#{empty dashboardBean ? '-' : dashboardBean.totalUsers}" />
<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="#{empty dashboardBean ? '-' : dashboardBean.totalRoles}" />
<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="#{empty dashboardBean ? '-' : dashboardBean.recentActions}" />
<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="#{empty dashboardBean ? '-' : dashboardBean.activeSessions}" />
<ui:param name="icon" value="pi-sign-in" />
<ui:param name="iconColor" value="purple-600" />
<ui:param name="subtitle" value="Utilisateurs connectés" />
<ui:param name="statusIcon" value="pi-check-circle" />
<ui:param name="statusLabel" value="En ligne" />
<ui:param name="statusValue" value="#{empty dashboardBean ? '0' : dashboardBean.onlineUsers} actifs" />
</ui:include>
</div>
</div>
<!-- Actions Rapides -->
<ui:include src="/templates/components/shared/dashboard/dashboard-section.xhtml">
<ui:param name="title" value="Actions Rapides" />
<ui:param name="icon" value="pi-bolt" />
<ui:param name="colSize" value="col-12 lg:col-6" />
<ui:define name="section-content">
<div class="grid">
<div class="col-12 md:col-6">
<h:form>
<p:commandButton
value="Nouvel Utilisateur"
icon="pi pi-user-plus"
styleClass="w-full p-button-success"
outcome="/pages/user-manager/users/create" />
</h:form>
</div>
<div class="col-12 md:col-6">
<h:form>
<p:commandButton
value="Liste des Utilisateurs"
icon="pi pi-users"
styleClass="w-full p-button-primary"
outcome="/pages/user-manager/users/list" />
</h:form>
</div>
<div class="col-12 md:col-6">
<h:form>
<p:commandButton
value="Gestion des Rôles"
icon="pi pi-shield"
styleClass="w-full p-button-info"
outcome="/pages/user-manager/roles/list" />
</h:form>
</div>
<div class="col-12 md:col-6">
<h:form>
<p:commandButton
value="Journal d'Audit"
icon="pi pi-history"
styleClass="w-full p-button-help"
outcome="/pages/user-manager/audit/logs" />
</h:form>
</div>
</div>
</ui:define>
</ui:include>
<!-- Informations Système -->
<ui:include src="/templates/components/shared/dashboard/dashboard-section.xhtml">
<ui:param name="title" value="Informations Système" />
<ui:param name="icon" value="pi-info-circle" />
<ui:param name="colSize" value="col-12 lg:col-6" />
<ui:define name="section-content">
<div class="flex flex-column gap-2">
<div class="flex align-items-center justify-content-between">
<span class="text-600">Version</span>
<span class="font-semibold">1.0.0</span>
</div>
<div class="flex align-items-center justify-content-between">
<span class="text-600">Realm Keycloak</span>
<span class="font-semibold">lions-user-manager</span>
</div>
<div class="flex align-items-center justify-content-between">
<span class="text-600">Statut</span>
<p:tag value="Opérationnel" severity="success" />
</div>
<div class="flex align-items-center justify-content-between">
<span class="text-600">Application</span>
<span class="font-semibold">Lions User Manager</span>
</div>
<div class="flex align-items-center justify-content-between">
<span class="text-600">Environnement</span>
<span class="font-semibold">Développement</span>
</div>
<div class="flex align-items-center justify-content-between">
<span class="text-600">Base de données</span>
<span class="font-semibold">Keycloak Admin API</span>
</div>
<div class="flex align-items-center justify-content-between">
<span class="text-600">Framework</span>
<span class="font-semibold">Quarkus, PrimeFaces Freya</span>
</div>
</div>
</ui:define>
</ui:include>
</div>
</ui:define>
</ui:composition>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
$primaryColor:lighten(#2170E7, 5%);
$primaryTextColor:#ffffff;
@import '../../sass/variables/layout/_layout_dark';
@import '../../sass/layout/_layout';

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
$primaryColor:#2170E7;
$primaryTextColor:#ffffff;
@import '../../sass/variables/layout/_layout_light';
@import '../../sass/layout/_layout';

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,9 @@
<svg width="17" height="20" viewBox="0 0 17 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0H6.00019V3.82345L17 1.66667V6.66667L6.00019 8.82345V10.4901L17 8.33333V13.3333L6.00019 15.4901V20H0V0Z" fill="url(#paint0_linear)"/>
<defs>
<linearGradient id="paint0_linear" x1="3.33335" y1="3.08442e-08" x2="8.49995" y2="20" gradientUnits="userSpaceOnUse">
<stop stop-color="#297FFF"/>
<stop offset="1" stop-color="#7A0EE7"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 469 B

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="209px" height="60px" viewBox="0 0 209 60" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>logo-freya-white</title>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="logo-freya-white" fill="#FFFFFF" fill-rule="nonzero">
<polygon id="Path" points="0 0 15.8827941 0 15.8827941 10.1321425 45 4.41666667 45 17.6666667 15.8827941 23.3821425 15.8827941 27.7988092 45 22.0833333 45 35.3333333 15.8827941 41.0488092 15.8827941 53 0 53"></polygon>
<path d="M84,20.8302387 L77.2821159,20.8302387 L77.2821159,18.9204244 C77.2821159,17.4350133 78.1284635,16.5862069 79.6095718,16.5862069 L83.9471033,16.5862069 L83.9471033,9 L77.070529,9 C71.0403023,9 67.8664987,12.3421751 67.8664987,18.1246684 L67.8664987,20.8302387 L63,20.8302387 L63.0528967,28.5755968 L67.7607053,28.5755968 L67.7607053,49 L77.5994962,49 L77.5994962,28.5755968 L84,28.5755968 L84,20.8302387 Z" id="Path"></path>
<path d="M105.301136,21 C101.846591,21 99.3636364,21.9630996 97.3125,24.3173432 L95.7471591,21.1070111 L89,21.1070111 L89,50 L99.0397727,50 L99.0397727,35.3394834 C99.0397727,31.6476015 100.551136,30.095941 104.059659,30.095941 L108,30.095941 L108,21 L105.301136,21 Z" id="Path"></path>
<path d="M141,35.2034783 C141,26.6852174 134.662692,20 125.473595,20 C116.495741,20 110,26.5773913 110,35.4730435 C110,44.3686957 116.548552,51 125.473595,51 C132.603066,51 138.412266,46.8486957 140.41908,40.2173913 L130.596252,40.2173913 C129.698467,41.9426087 127.744463,42.9669565 125.473595,42.9669565 C122.357751,42.9669565 120.298126,41.2417391 119.664395,37.8991304 L140.841567,37.8991304 C140.947189,36.9826087 141,36.12 141,35.2034783 Z M125.473595,27.8713043 C128.431005,27.8713043 130.49063,29.4347826 131.335605,32.346087 L119.822828,32.346087 C120.614991,29.4347826 122.621806,27.8713043 125.473595,27.8713043 Z" id="Shape"></path>
<path d="M165.423077,21 L159.764423,36.6842105 L153.682692,21 L143,21 L154.793269,47.1052632 C153.471154,50.6315789 152.360577,51.6315789 148.605769,51.6315789 L145.855769,51.6315789 L145.855769,60 L149.240385,60 C156.644231,60 160.134615,56.8947368 163.995192,48.1578947 L176,21 L165.423077,21 Z" id="Path"></path>
<path d="M201.660066,20.8641115 L200.656766,23.1324042 C198.333333,21.1341463 195.323432,20 191.943894,20 C183.231023,20 177,26.4268293 177,35.445993 C177,44.5191638 183.231023,51 191.943894,51 C195.270627,51 198.227723,49.9198606 200.551155,47.9756098 L201.39604,50.0278746 L209,50.0278746 L209,20.8641115 L201.660066,20.8641115 Z M193.264026,42.1428571 C189.567657,42.1428571 186.874587,39.2804878 186.874587,35.445993 C186.874587,31.6655052 189.567657,28.8571429 193.264026,28.8571429 C196.960396,28.8571429 199.653465,31.6655052 199.653465,35.445993 C199.653465,39.2804878 196.960396,42.1428571 193.264026,42.1428571 Z" id="Shape"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="446px" height="129px" viewBox="0 0 446 129" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>logo-freya</title>
<defs>
<linearGradient x1="28.4476672%" y1="1.54220833e-07%" x2="49.999861%" y2="100%" id="linearGradient-1">
<stop stop-color="#297FFF" offset="0%"></stop>
<stop stop-color="#7A0EE7" offset="100%"></stop>
</linearGradient>
<linearGradient x1="75.0238871%" y1="-21.5732317%" x2="40.5406822%" y2="100.001105%" id="linearGradient-2">
<stop stop-color="#297FFF" offset="0%"></stop>
<stop stop-color="#7616E8" offset="100%"></stop>
</linearGradient>
<linearGradient x1="37.9032258%" y1="-69.125861%" x2="-21.2207927%" y2="100.001538%" id="linearGradient-3">
<stop stop-color="#297FFF" offset="0%"></stop>
<stop stop-color="#7616E8" offset="100%"></stop>
</linearGradient>
<linearGradient x1="-55.9426462%" y1="-59.1651756%" x2="-141.121522%" y2="95.4614408%" id="linearGradient-4">
<stop stop-color="#297FFF" offset="0%"></stop>
<stop stop-color="#7616E8" offset="100%"></stop>
</linearGradient>
<linearGradient x1="-89.5003908%" y1="-50.8316014%" x2="-145.407143%" y2="72.8756185%" id="linearGradient-5">
<stop stop-color="#297FFF" offset="0%"></stop>
<stop stop-color="#7616E8" offset="100%"></stop>
</linearGradient>
<linearGradient x1="-319.994499%" y1="-50.1302194%" x2="-402.50275%" y2="91.589373%" id="linearGradient-6">
<stop stop-color="#297FFF" offset="0%"></stop>
<stop stop-color="#7616E8" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="logo-freya" fill-rule="nonzero">
<polygon id="Path" fill="url(#linearGradient-1)" points="0 0 33.8832941 0 33.8832941 21.793665 96 9.5 96 38 33.8832941 50.293665 33.8832941 59.793665 96 47.5 96 76 33.8832941 88.293665 33.8832941 114 0 114"></polygon>
<path d="M181,45.4350133 L166.604534,45.4350133 L166.604534,41.3289125 C166.604534,38.1352785 168.418136,36.3103448 171.59194,36.3103448 L180.88665,36.3103448 L180.88665,20 L166.151134,20 C153.229219,20 146.428212,27.1856764 146.428212,39.6180371 L146.428212,45.4350133 L136,45.4350133 L136.11335,62.0875332 L146.201511,62.0875332 L146.201511,106 L167.284635,106 L167.284635,62.0875332 L181,62.0875332 L181,45.4350133 Z" id="Path" fill="url(#linearGradient-2)"></path>
<path d="M225.318182,44 C218.045455,44 212.818182,46.0590406 208.5,51.0922509 L205.204545,44.2287823 L191,44.2287823 L191,106 L212.136364,106 L212.136364,74.6568266 C212.136364,66.7638376 215.318182,63.4464945 222.704545,63.4464945 L231,63.4464945 L231,44 L225.318182,44 Z" id="Path" fill="url(#linearGradient-3)"></path>
<path d="M304,73.8782609 C304,56.0173913 290.507666,42 270.943782,42 C251.829642,42 238,55.7913043 238,74.4434783 C238,93.0956522 251.942078,107 270.943782,107 C286.122658,107 298.49063,98.2956522 302.763203,84.3913043 L281.850085,84.3913043 C279.938671,88.0086957 275.778535,90.1565217 270.943782,90.1565217 C264.310051,90.1565217 259.925043,86.5391304 258.575809,79.5304348 L303.662692,79.5304348 C303.887564,77.6086957 304,75.8 304,73.8782609 Z M270.943782,58.5043478 C277.240204,58.5043478 281.625213,61.7826087 283.424191,67.8869565 L258.913118,67.8869565 C260.599659,61.7826087 264.872232,58.5043478 270.943782,58.5043478 Z" id="Shape" fill="url(#linearGradient-4)"></path>
<path d="M353.24359,44 L341.06891,78.1835358 L327.983974,44 L305,44 L330.373397,100.896086 C327.528846,108.581646 325.139423,110.761134 317.060897,110.761134 L311.144231,110.761134 L311.144231,129 L318.426282,129 C334.355769,129 341.865385,122.232119 350.171474,103.190283 L376,44 L353.24359,44 Z" id="Path" fill="url(#linearGradient-5)"></path>
<path d="M430.173267,43.8118467 L428.009901,48.5679443 C423,44.3780488 416.509901,42 409.222772,42 C390.435644,42 377,55.4756098 377,74.3867596 C377,93.4111498 390.435644,107 409.222772,107 C416.39604,107 422.772277,104.735192 427.782178,100.658537 L429.60396,104.961672 L446,104.961672 L446,43.8118467 L430.173267,43.8118467 Z M412.069307,88.4285714 C404.09901,88.4285714 398.292079,82.4268293 398.292079,74.3867596 C398.292079,66.4599303 404.09901,60.5714286 412.069307,60.5714286 C420.039604,60.5714286 425.846535,66.4599303 425.846535,74.3867596 C425.846535,82.4268293 420.039604,88.4285714 412.069307,88.4285714 Z" id="Shape" fill="url(#linearGradient-6)"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 493 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 273 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

View File

@@ -0,0 +1,879 @@
/**
* PrimeFaces Freya Layout
*/
PrimeFaces.widget.Freya = PrimeFaces.widget.BaseWidget.extend({
init: function(cfg) {
this._super(cfg);
this.wrapper = $(document.body).children('.layout-wrapper');
var $this = this;
$(function() {
$this._init();
});
this.restoreMenuState();
this.expandedMenuitems = this.expandedMenuitems||[];
},
_init: function() {
this.contentWrapper = this.wrapper.children('.layout-main');
this.topbar = this.wrapper.find('.layout-topbar');
this.topbarItems = this.topbar.find('.layout-topbar-actions > li.topbar-item');
this.topbarLinks = this.topbarItems.children('a');
this.topbarSearchItemMenu = this.topbar.find('.search-item');
this.menuWrapper = this.wrapper.find('.menu-wrapper');
this.sidebarPin = this.menuWrapper.find('.sidebar-logo > .sidebar-pin');
this.menu = this.menuWrapper.find('.layout-menu');
this.menuButton = this.topbar.find('.menu-button');
this.menulinks = this.menu.find('a');
this.rightpanel = this.wrapper.find('.layout-rightpanel');
this.rightpanelButton = this.topbar.find('.layout-rightpanel-button');
this.rightpanelExitButton = this.rightpanel.find('.rightpanel-exit-button');
this.configButton = $('#layout-config-button');
this.configurator = this.wrapper.children('.layout-config');
this.bindEvents();
},
toggleClass: function(el, className) {
if (el.hasClass(className)) {
el.removeClass(className);
}
else {
el.addClass(className);
}
},
bindEvents: function() {
var $this = this;
this.bindTopbarEvents();
this.bindMenuEvents();
this.bindRightPanelEvents();
this.bindConfigEvents();
$(document.body).off('click.layoutBody').on('click.layoutBody', function() {
if (!$this.menuClick) {
$this.wrapper.removeClass('layout-sidebar-active layout-mobile-active');
$(document.body).removeClass('blocked-scroll');
if ($this.isHorizontal() || $this.isSlim()) {
$this.menu.find('.active-menuitem').removeClass('active-menuitem');
$this.menu.find('ul:visible').hide();
$this.menuActive = false;
}
}
if (!$this.topbarItemClicked) {
$this.removeTopbarClassFromAllItems(null, 'active-topmenuitem', $this.topbarItems.filter('.active-topmenuitem'));
}
if (!$this.rightpanelClicked) {
$this.wrapper.removeClass('layout-rightpanel-active');
}
if (!$this.configClicked && $this.configurator.hasClass('layout-config-active')) {
$this.configurator.removeClass('layout-config-active');
}
$this.horizontalMenuClick = false;
$this.topbarItemClicked = false;
$this.rightpanelClicked = false;
$this.menuClick = false;
$this.configClicked = false;
});
},
bindConfigEvents: function() {
var $this = this;
this.configButton.off('click.configbutton').on('click.configbutton', function(e) {
$this.configurator.toggleClass('layout-config-active');
$this.configClicked = true;
});
this.configurator.off('click.config').on('click.config', function() {
$this.configClicked = true;
});
},
bindMenuEvents: function() {
var $this = this;
this.menuButton.off('click.menu').on('click.menu', function(e) {
$this.menuClick = true;
if ($this.isMobile()) {
if ($this.wrapper.hasClass('layout-mobile-active')) {
$this.wrapper.removeClass('layout-mobile-active');
$(document.body).removeClass('blocked-scroll');
}
else {
$this.wrapper.addClass('layout-mobile-active');
$(document.body).addClass('blocked-scroll');
}
}
e.preventDefault();
});
this.menuWrapper.off('click.menuWrapper mouseenter.menuWrapper mouseleave.menuWrapper')
.on('click.menuWrapper', function() {
$this.menuClick = true;
})
.on('mouseenter.menuWrapper', function(e) {
if(!$this.wrapper.hasClass('layout-sidebar-static')) {
if($this.hideTimeout) {
clearTimeout($this.hideTimeout);
}
$this.menuWrapper.addClass('layout-sidebar-active');
}
if(!$this.wrapper.hasClass('layout-sidebar')) {
if($this.hideTimeout) {
clearTimeout($this.hideTimeout);
}
$this.menuWrapper.removeClass('layout-sidebar-active');
}
})
.on('mouseleave.menuWrapper', function(e) {
if(!$this.wrapper.hasClass('layout-sidebar-static')) {
$this.hideTimeout = setTimeout(function() {
$this.menuWrapper.removeClass('layout-sidebar-active');
}, $this.cfg.closeDelay);
}
});
this.sidebarPin.off('click.menuWrapper').on('click.menuWrapper', function(e) {
$this.wrapper.removeClass('layout-static-restore');
$this.wrapper.toggleClass('layout-static');
$this.saveMenuState();
e.preventDefault();
});
this.menulinks.off('click.menuWrapper').on('click.menuWrapper', function(e) {
var link = $(this),
item = link.parent(),
submenu = item.children('ul');
horizontal = $this.isHorizontal();
slim = $this.isSlim();
$this.menuClick = true;
if (horizontal) {
$this.horizontalMenuClick = true;
}
if(item.hasClass('active-menuitem')) {
if(submenu.length) {
$this.removeMenuitem(item.attr('id'));
item.removeClass('active-menuitem');
if(horizontal || slim) {
if(item.parent().is($this.jq)) {
$this.menuActive = false;
}
submenu.hide();
$this.removeMenuitem(item.attr('id'));
item.removeClass('active-menuitem');
}
else {
submenu.slideUp(function() {
$this.removeMenuitem(item.attr('id'));
item.removeClass('active-menuitem');
});
}
}
}
else {
$this.addMenuitem(item.attr('id'));
if(horizontal || slim) {
$this.deactivateItems(item.siblings());
item.addClass('active-menuitem');
$this.menuActive = true;
submenu.show();
}
else {
$this.deactivateItems(item.siblings(), true);
$this.activate(item);
}
}
if(submenu.length) {
e.preventDefault();
}
});
this.menu.find('> li').off('mouseenter.menu').on('mouseenter.menu', function(e) {
if ($this.isHorizontal() || $this.isSlim()) {
var item = $(this);
if(!item.hasClass('active-menuitem')) {
$this.menu.find('.active-menuitem').removeClass('active-menuitem');
$this.menu.find('ul:visible').hide();
if($this.menuActive) {
item.addClass('active-menuitem');
item.children('ul').show();
}
}
}
});
},
bindTopbarEvents: function() {
var $this = this;
this.topbarLinks.off('click.topbar').on('click.topbar', function(e) {
var link = $(this),
item = link.parent(),
submenu = item.children('ul');
if ($this.isMobile()) {
$this.removeTopbarClassFromAllItems(null, 'active-topmenuitem', $this.topbarItems.filter('.active-topmenuitem').not(item));
}
else {
$this.removeTopbarClassFromAllItems(item, 'active-topmenuitem');
}
$this.addTopbarClass(item, 'active-topmenuitem');
$this.topbarItemClicked = true;
if (submenu.length) {
e.preventDefault();
}
});
this.topbarSearchItemMenu.off('click.topbar').on('click.topbar', function(e) {
$this.topbarItemClicked = true;
});
},
bindRightPanelEvents: function() {
var $this = this;
var changeRightpanelState = function(e) {
this.toggleClass(this.wrapper, 'layout-rightpanel-active');
this.rightpanelClicked = true;
e.preventDefault();
};
this.rightpanelButton.off('click.rightpanel').on('click.rightpanel', changeRightpanelState.bind(this));
this.rightpanelExitButton.off('click.rightpanel').on('click.rightpanel', changeRightpanelState.bind(this));
this.rightpanel.off('click.rightpanel').on('click.rightpanel', function() {
$this.rightpanelClicked = true;
});
},
activate: function(item) {
var submenu = item.children('ul');
item.addClass('active-menuitem');
if(submenu.length) {
submenu.slideDown();
}
},
deactivate: function(item) {
var submenu = item.children('ul');
item.removeClass('active-menuitem');
if(submenu.length) {
submenu.hide();
}
},
deactivateItems: function(items, animate) {
var $this = this;
for(var i = 0; i < items.length; i++) {
var item = items.eq(i),
submenu = item.children('ul');
if(submenu.length) {
if(item.hasClass('active-menuitem')) {
var activeSubItems = item.find('.active-menuitem');
item.removeClass('active-menuitem');
if(animate) {
submenu.slideUp('normal', function() {
$(this).parent().find('.active-menuitem').each(function() {
$this.deactivate($(this));
});
});
}
else {
item.find('.active-menuitem').each(function() {
$this.deactivate($(this));
});
}
$this.removeMenuitem(item.attr('id'));
activeSubItems.each(function() {
$this.removeMenuitem($(this).attr('id'));
});
}
else {
item.find('.active-menuitem').each(function() {
var subItem = $(this);
$this.deactivate(subItem);
$this.removeMenuitem(subItem.attr('id'));
});
}
}
else if(item.hasClass('active-menuitem')) {
$this.deactivate(item);
$this.removeMenuitem(item.attr('id'));
}
}
},
removeMenuitem: function (id) {
this.expandedMenuitems = $.grep(this.expandedMenuitems, function (value) {
return value !== id;
});
this.saveMenuState();
},
addMenuitem: function (id) {
if ($.inArray(id, this.expandedMenuitems) === -1) {
this.expandedMenuitems.push(id);
}
this.saveMenuState();
},
saveMenuState: function() {
if(this.wrapper.hasClass('layout-static'))
$.cookie('freya_menu_static', 'freya_menu_static', {path: '/'});
else
$.removeCookie('freya_menu_static', {path: '/'});
$.cookie('freya_expandeditems', this.expandedMenuitems.join(','), {path: '/'});
},
clearMenuState: function() {
this.expandedMenuitems = [];
$.removeCookie('freya_expandeditems', {path: '/'});
$.removeCookie('freya_menu_static', {path: '/'});
},
clearActiveItems: function() {
var activeItems = this.jq.find('li.active-menuitem'),
subContainers = activeItems.children('ul');
activeItems.removeClass('active-menuitem');
if(subContainers && subContainers.length) {
subContainers.hide();
}
},
clearLayoutState: function() {
this.clearMenuState();
this.clearActiveItems();
},
restoreMenuState: function() {
var menuCookie = $.cookie('freya_expandeditems');
if (!this.isSlim() && !this.isHorizontal() && menuCookie) {
this.expandedMenuitems = menuCookie.split(',');
for (var i = 0; i < this.expandedMenuitems.length; i++) {
var id = this.expandedMenuitems[i];
if (id) {
var menuitem = $("#" + this.expandedMenuitems[i].replace(/:/g, "\\:"));
menuitem.addClass('active-menuitem');
var submenu = menuitem.children('ul');
if(submenu.length) {
submenu.show();
}
}
}
}
var sidebarCookie = $.cookie('freya_menu_static');
if(sidebarCookie) {
this.wrapper.addClass('layout-static');
}
},
removeTopbarClassFromAllItems: function(item, className, items) {
var activeItems = item != null ? item.siblings('.' + className) : items;
activeItems.removeClass(className);
activeItems.children('ul').removeClass('fadeInDown');
},
addTopbarClass: function(item, className) {
var submenu = item.children('ul');
if (submenu.length) {
if (item.hasClass(className)) {
submenu.removeClass('fadeInDown').addClass('fadeOutUp');
setTimeout(function() {
item.removeClass(className);
submenu.removeClass('fadeOutUp');
}, 100);
}
else {
item.addClass(className);
submenu.addClass('fadeInDown');
}
}
},
hideTopBar: function() {
var $this = this;
this.topbarMenu.addClass('fadeOutUp');
setTimeout(function() {
$this.topbarMenu.removeClass('fadeOutUp topbar-menu-visible');
},500);
},
isMobile: function() {
return window.innerWidth < 992;
},
isHorizontal: function() {
return this.wrapper.hasClass('layout-horizontal') && !this.isMobile();
},
isSlim: function() {
return this.wrapper.hasClass('layout-slim') && !this.isMobile();
},
isStatic: function() {
return this.wrapper.hasClass('layout-static') && !this.isMobile();
}
});
PrimeFaces.FreyaConfigurator = {
changeLayout: function( componentTheme, darkMode ) {
this.changeLayoutsTheme(darkMode);
this.changeDemo(darkMode);
this.changeComponentsTheme(componentTheme, darkMode);
this.changeSectionTheme( darkMode, 'layout-menu');
this.changeSectionTheme( darkMode , 'layout-topbar');
},
changeLayoutsTheme: function(darkMode) {
newLayout = '-' + darkMode;
var linkElement = $('link[href*="layout-"]');
var href = linkElement.attr('href');
var startIndexOf = href.indexOf('layout-') + 6;
var endIndexOf = href.indexOf('.css');
var currentColor = href.substring(startIndexOf, endIndexOf);
this.replaceLink(linkElement, href.replace(currentColor, newLayout));
},
changeDemo: function(darkMode) {
newLayout = '-' + darkMode;
var linkElement = $('link[href*="demo-"]');
var href = linkElement.attr('href');
var startIndexOf = href.indexOf('demo-') + 4;
var endIndexOf = href.indexOf('.css');
var currentColor = href.substring(startIndexOf, endIndexOf);
this.replaceLink(linkElement, href.replace(currentColor, newLayout));
},
changeComponentsTheme: function(themeColor, darkMode) {
theme = this.getColor(themeColor, darkMode);
var library = 'primefaces-freya';
var linkElement = $('link[href*="theme.css"]');
var href = linkElement.attr('href');
var index = href.indexOf(library) + 1;
var currentTheme = href.substring(index + library.length);
this.replaceLink(linkElement, href.replace(currentTheme, theme));
},
changeSectionTheme: function(theme, section) {
var wrapperElement = $('.layout-wrapper');
var styleClass = wrapperElement.attr('class');
var tokens = styleClass.split(' ');
var sectionClass;
for (var i = 0; i < tokens.length; i++) {
if (tokens[i].indexOf(section + '-') > -1) {
sectionClass = tokens[i];
break;
}
}
wrapperElement.attr('class', styleClass.replace(sectionClass, section + '-' + theme));
},
changeMenuMode: function(menuMode) {
var wrapper = $(document.body).children('.layout-wrapper');
switch (menuMode) {
case 'layout-sidebar':
wrapper.addClass('layout-sidebar').removeClass('layout-slim layout-horizontal ');
this.clearLayoutState();
break;
case 'layout-horizontal':
wrapper.addClass('layout-horizontal').removeClass('layout-static layout-slim layout-sidebar');
this.clearLayoutState();
break;
case 'layout-slim':
wrapper.addClass('layout-slim').removeClass('layout-static layout-horizontal layout-sidebar');
this.clearLayoutState();
break;
default:
wrapper.addClass('layout-sidebar').removeClass('layout-slim layout-horizontal ');
this.clearLayoutState();
break;
}
},
beforeResourceChange: function() {
PrimeFaces.ajax.RESOURCE = null; //prevent resource append
},
replaceLink: function(linkElement, href) {
PrimeFaces.ajax.RESOURCE = 'javax.faces.Resource';
var isIE = this.isIE();
if (isIE) {
linkElement.attr('href', href);
}
else {
var cloneLinkElement = linkElement.clone(false);
cloneLinkElement.attr('href', href);
linkElement.after(cloneLinkElement);
cloneLinkElement.off('load').on('load', function() {
linkElement.remove();
});
// for dashboard
setTimeout(function() {
if (window['redrawChart']) {
window.redrawChart();
}
}, 100);
}
},
getColor: function(name, darkMode) {
return name + '-' + darkMode;
},
isIE: function() {
return /(MSIE|Trident\/|Edge\/)/i.test(navigator.userAgent);
},
clearLayoutState: function() {
var menu = PF('FreyaMenuWidget');
if (menu) {
menu.clearLayoutState();
}
},
updateInputStyle: function(value) {
if (value === 'filled')
$(document.body).addClass('ui-input-filled');
else
$(document.body).removeClass('ui-input-filled');
}
};
/*!
* jQuery Cookie Plugin v1.4.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2006, 2014 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD (Register as an anonymous module)
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function encode(s) {
return config.raw ? s : encodeURIComponent(s);
}
function decode(s) {
return config.raw ? s : decodeURIComponent(s);
}
function stringifyCookieValue(value) {
return encode(config.json ? JSON.stringify(value) : String(value));
}
function parseCookieValue(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape...
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
// Replace server-side written pluses with spaces.
// If we can't decode the cookie, ignore it, it's unusable.
// If we can't parse the cookie, ignore it, it's unusable.
s = decodeURIComponent(s.replace(pluses, ' '));
return config.json ? JSON.parse(s) : s;
} catch (e) { }
}
function read(s, converter) {
var value = config.raw ? s : parseCookieValue(s);
return $.isFunction(converter) ? converter(value) : value;
}
var config = $.cookie = function (key, value, options) {
// Write
if (arguments.length > 1 && !$.isFunction(value)) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setMilliseconds(t.getMilliseconds() + days * 864e+5);
}
return (document.cookie = [
encode(key), '=', stringifyCookieValue(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// Read
var result = key ? undefined : {},
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling $.cookie().
cookies = document.cookie ? document.cookie.split('; ') : [],
i = 0,
l = cookies.length;
for (; i < l; i++) {
var parts = cookies[i].split('='),
name = decode(parts.shift()),
cookie = parts.join('=');
if (key === name) {
// If second argument (value) is a function it's a converter...
result = read(cookie, value);
break;
}
// Prevent storing a cookie that we couldn't decode.
if (!key && (cookie = read(cookie)) !== undefined) {
result[name] = cookie;
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return !$.cookie(key);
};
}));
if (PrimeFaces.widget.InputSwitch) {
PrimeFaces.widget.InputSwitch = PrimeFaces.widget.InputSwitch.extend({
init: function (cfg) {
this._super(cfg);
if (this.input.prop('checked')) {
this.jq.addClass('ui-inputswitch-checked');
}
},
check: function () {
var $this = this;
this.input.prop('checked', true).trigger('change');
setTimeout(function () {
$this.jq.addClass('ui-inputswitch-checked');
}, 100);
},
uncheck: function () {
var $this = this;
this.input.prop('checked', false).trigger('change');
setTimeout(function () {
$this.jq.removeClass('ui-inputswitch-checked');
}, 100);
}
});
}
if (PrimeFaces.widget.AccordionPanel) {
PrimeFaces.widget.AccordionPanel = PrimeFaces.widget.AccordionPanel.extend({
init: function (cfg) {
this._super(cfg);
this.headers.last().addClass('ui-accordion-header-last');
}
});
}
/* Issue #924 is fixed for 5.3+ and 6.0. (compatibility with 5.3) */
if(window['PrimeFaces'] && window['PrimeFaces'].widget.Dialog) {
PrimeFaces.widget.Dialog = PrimeFaces.widget.Dialog.extend({
enableModality: function() {
this._super();
$(document.body).children(this.jqId + '_modal').addClass('ui-dialog-mask');
},
syncWindowResize: function() {}
});
}
if (PrimeFaces.widget.SelectOneMenu) {
PrimeFaces.widget.SelectOneMenu = PrimeFaces.widget.SelectOneMenu.extend({
init: function (cfg) {
this._super(cfg);
var $this = this;
if (this.jq.parent().hasClass('ui-float-label')) {
this.m_panel = $(this.jqId + '_panel');
this.m_focusInput = $(this.jqId + '_focus');
this.m_panel.addClass('ui-input-overlay-panel');
this.jq.addClass('ui-inputwrapper');
if (this.input.val() != '') {
this.jq.addClass('ui-inputwrapper-filled');
}
this.input.off('change').on('change', function () {
$this.inputValueControl($(this));
});
this.m_focusInput.on('focus.ui-selectonemenu', function () {
$this.jq.addClass('ui-inputwrapper-focus');
})
.on('blur.ui-selectonemenu', function () {
$this.jq.removeClass('ui-inputwrapper-focus');
});
if (this.cfg.editable) {
this.label.on('input', function (e) {
$this.inputValueControl($(this));
}).on('focus', function () {
$this.jq.addClass('ui-inputwrapper-focus');
}).on('blur', function () {
$this.jq.removeClass('ui-inputwrapper-focus');
$this.inputValueControl($(this));
});
}
}
},
inputValueControl: function (input) {
if (input.val() != '')
this.jq.addClass('ui-inputwrapper-filled');
else
this.jq.removeClass('ui-inputwrapper-filled');
}
});
}
if (PrimeFaces.widget.Chips) {
PrimeFaces.widget.Chips = PrimeFaces.widget.Chips.extend({
init: function (cfg) {
this._super(cfg);
var $this = this;
if (this.jq.parent().hasClass('ui-float-label')) {
this.jq.addClass('ui-inputwrapper');
if ($this.jq.find('.ui-chips-token').length !== 0) {
this.jq.addClass('ui-inputwrapper-filled');
}
this.input.on('focus.ui-chips', function () {
$this.jq.addClass('ui-inputwrapper-focus');
}).on('input.ui-chips', function () {
$this.inputValueControl();
}).on('blur.ui-chips', function () {
$this.jq.removeClass('ui-inputwrapper-focus');
$this.inputValueControl();
});
}
},
inputValueControl: function () {
if (this.jq.find('.ui-chips-token').length !== 0 || this.input.val() != '')
this.jq.addClass('ui-inputwrapper-filled');
else
this.jq.removeClass('ui-inputwrapper-filled');
}
});
}
if (PrimeFaces.widget.DatePicker) {
PrimeFaces.widget.DatePicker = PrimeFaces.widget.DatePicker.extend({
init: function (cfg) {
this._super(cfg);
var $this = this;
if (this.jq.parent().hasClass('ui-float-label') && !this.cfg.inline) {
if (this.input.val() != '') {
this.jq.addClass('ui-inputwrapper-filled');
}
this.jqEl.off('focus.ui-datepicker blur.ui-datepicker change.ui-datepicker')
.on('focus.ui-datepicker', function () {
$this.jq.addClass('ui-inputwrapper-focus');
})
.on('blur.ui-datepicker', function () {
$this.jq.removeClass('ui-inputwrapper-focus');
})
.on('change.ui-datepicker', function () {
$this.inputValueControl($(this));
});
}
},
inputValueControl: function (input) {
if (input.val() != '')
this.jq.addClass('ui-inputwrapper-filled');
else
this.jq.removeClass('ui-inputwrapper-filled');
}
});
}

File diff suppressed because one or more lines are too long

View File

@@ -15,8 +15,8 @@
<div class="menu-wrapper">
<div class="sidebar-logo">
<a href="/pages/user-manager/users/list">
<span class="font-bold text-xl">Lions User Manager</span>
<a href="/pages/user-manager/dashboard">
<p:graphicImage name="images/logo-freya-single.svg" library="freya-layout" />
</a>
<a href="#" class="sidebar-pin" title="Toggle Menu">
<span class="pin"></span>
@@ -32,27 +32,22 @@
<p:submenu id="m_users" label="Gestion Utilisateurs" icon="pi pi-users">
<p:menuitem id="m_users_list" value="Liste des Utilisateurs" icon="pi pi-list" outcome="/pages/user-manager/users/list" />
<p:menuitem id="m_users_create" value="Nouvel Utilisateur" icon="pi pi-user-plus" outcome="/pages/user-manager/users/create" />
<p:menuitem id="m_users_search" value="Recherche Avancée" icon="pi pi-search" outcome="/pages/user-manager/users/search" />
</p:submenu>
<!-- Gestion Rôles -->
<p:submenu id="m_roles" label="Gestion Rôles" icon="pi pi-shield">
<p:menuitem id="m_roles_list" value="Liste des Rôles" icon="pi pi-list" outcome="/pages/user-manager/roles/list" />
<p:menuitem id="m_roles_create_realm" value="Nouveau Rôle Realm" icon="pi pi-plus" outcome="/pages/user-manager/roles/create-realm" />
<p:menuitem id="m_roles_create_client" value="Nouveau Rôle Client" icon="pi pi-plus-circle" outcome="/pages/user-manager/roles/create-client" />
<p:menuitem id="m_roles_assign" value="Attribution Rôles" icon="pi pi-key" outcome="/pages/user-manager/roles/assign" />
</p:submenu>
<!-- Audit -->
<p:submenu id="m_audit" label="Audit" icon="pi pi-history">
<p:menuitem id="m_audit_logs" value="Journal d'Audit" icon="pi pi-file-o" outcome="/pages/user-manager/audit/logs" />
<p:menuitem id="m_audit_stats" value="Statistiques" icon="pi pi-chart-bar" outcome="/pages/user-manager/audit/stats" />
</p:submenu>
<!-- Synchronisation -->
<p:submenu id="m_sync" label="Synchronisation" icon="pi pi-sync">
<p:menuitem id="m_sync_dashboard" value="Dashboard" icon="pi pi-dashboard" outcome="/pages/user-manager/sync/dashboard" />
<p:menuitem id="m_sync_health" value="Health Check" icon="pi pi-heart" outcome="/pages/user-manager/sync/health" />
</p:submenu>
</fr:menu>
</h:form>

View File

@@ -23,7 +23,7 @@
<ui:param name="title" value="Gestion des Utilisateurs" />
<ui:param name="description" value="Gestion centralisée des utilisateurs Keycloak" />
<ui:define name="actions">
<!-- Boutons d'action -->
Boutons d'action ici
</ui:define>
</ui:include>
-->

View File

@@ -18,11 +18,13 @@
<a href="#" class="menu-button">
<i class="pi pi-bars"/>
</a>
<h:link id="logolink" outcome="/pages/user-manager/users/list" styleClass="layout-topbar-logo">
<span class="font-bold text-xl">Lions User Manager</span>
<h:link id="logolink" outcome="/pages/user-manager/dashboard" styleClass="layout-topbar-logo">
<p:graphicImage name="images/#{guestPreferences.lightLogo ? 'logo-freya-white.svg' : 'logo-freya.svg'}" library="freya-layout" />
</h:link>
</div>
<ui:include src="/templates/components/layout/menu.xhtml" />
<div class="layout-topbar-right">
<ul class="layout-topbar-actions">
<li class="topbar-item user-profile">

View File

@@ -4,49 +4,50 @@
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:fr="http://primefaces.org/freya">
lang="fr">
<h:head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><ui:insert name="title">Lions User Manager</ui:insert></title>
<!-- PrimeFaces Freya Theme -->
<h:outputStylesheet name="primefaces-freya/theme.css" />
</h:head>
<h:head>
<f:facet name="first">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
<meta name="mobile-web-app-capable" content="yes" />
<link rel="icon" href="#{request.contextPath}/resources/freya-layout/images/favicon.ico" type="image/x-icon"></link>
</f:facet>
<title><ui:insert name="title">Lions User Manager</ui:insert></title>
<h:outputScript name="js/layout.js" library="freya-layout" />
<h:outputScript name="js/prism.js" library="freya-layout"/>
<ui:insert name="head"/>
</h:head>
<h:body styleClass="freya-layout-wrapper">
<!-- Layout Principal Freya -->
<div class="freya-layout">
<!-- Topbar -->
<ui:include src="/templates/components/layout/topbar.xhtml" />
<!-- Menu Sidebar -->
<ui:include src="/templates/components/layout/menu.xhtml" />
<!-- Contenu Principal -->
<div class="freya-main-content">
<div class="freya-content-wrapper">
<!-- Messages -->
<p:messages id="messages" showDetail="true" closable="true" />
<!-- Contenu de la page -->
<ui:insert name="content">
<!-- Le contenu de chaque page sera inséré ici -->
</ui:insert>
<h:body styleClass="#{guestPreferences.inputStyleClass}">
<div class="layout-wrapper layout-topbar-#{guestPreferences.topbarTheme} layout-menu-#{guestPreferences.menuTheme} #{guestPreferences.menuMode}" >
<ui:include src="/templates/components/layout/topbar.xhtml"/>
<div class="layout-main">
<div class="layout-content">
<p:messages id="messages" showDetail="true" closable="true" />
<ui:insert name="content"/>
</div>
<ui:include src="/templates/components/layout/footer.xhtml"/>
</div>
<!-- Footer -->
<ui:include src="/templates/components/layout/footer.xhtml" />
<p:ajaxStatus style="width:32px;height:32px;position:fixed;right:7px;bottom:7px">
<f:facet name="start">
<i class="pi pi-spin pi-spinner ajax-loader" aria-hidden="true"/>
</f:facet>
<f:facet name="complete">
<h:outputText value="" />
</f:facet>
</p:ajaxStatus>
<div class="layout-mask modal-in"></div>
</div>
</div>
</h:body>
<h:outputStylesheet name="css/primeicons.css" library="freya-layout" />
<h:outputStylesheet name="css/primeflex.min.css" library="freya-layout" />
<h:outputStylesheet name="css/layout-#{guestPreferences.layout}.css" library="freya-layout" />
<h:outputStylesheet name="primefaces-freya-#{guestPreferences.componentTheme}/theme.css" />
</h:body>
</html>