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 af4c332df8
commit 3994cb7392
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>

View File

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

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

After

Width:  |  Height:  |  Size: 265 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

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');
}
});
}

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>