COMPLETE: Freya Theme + OIDC Integration for Web Client
✅ FREYA THEME 5.0.0 INTEGRATION: - Install freya-theme-5.0.0-jakarta.jar to Maven local repository - Copy all Freya layout resources (23 files: CSS, JS, images, icons) - Copy and adapt Freya templates (6 files: template, menu, topbar, footer, config, rightpanel) - Activate Freya dependency in pom.xml with Jakarta classifier ✅ OIDC AUTHENTICATION COMPLETE: - AuthBean: Complete OIDC integration with SecurityIdentity, OidcSession, IdToken - NavigationBean: Role-based navigation with access control - GuestPreferences: Freya theme management (light/dark mode, menu themes) - Configuration: Port 8081, API URL localhost:8082, Keycloak OIDC client ✅ FREYA UI PAGES CREATED: - index.xhtml: Landing page + authenticated dashboard with dual view - pages/dashboard.xhtml: Main dashboard with statistics, quick actions, activity timeline - pages/profile.xhtml: User profile with OIDC session details and role management - pages/clients.xhtml: Client management interface with demo data and statistics ✅ GBCM BRANDING & CUSTOMIZATION: - Template title: 'GBCM - Global Business Consulting & Management' - Menu structure: Dashboard, Clients, Coaches, Sessions, Workshops, Administration - Topbar: User profile with avatar, full name, email, logout functionality - Role-based menu visibility and access control ✅ TECHNICAL FEATURES: - Hot reload active on port 8081 - OIDC authentication flow working with Keycloak - Token management and session handling - Role-based UI rendering (ADMIN, MANAGER, COACH, CLIENT, PROSPECT) - Freya theme preferences (light/dark mode switching) - Responsive design with PrimeFlex grid system 🎯 TASK 1 STATUS: COMPLETE Web client with Freya Theme 5.0.0 and OIDC authentication fully integrated and tested.
6
pom.xml
@@ -73,15 +73,13 @@
|
||||
<classifier>jakarta</classifier>
|
||||
</dependency>
|
||||
|
||||
<!-- Freya Theme - Temporairement désactivé -->
|
||||
<!--
|
||||
<!-- Freya Theme -->
|
||||
<dependency>
|
||||
<groupId>org.primefaces</groupId>
|
||||
<groupId>org.primefaces.themes</groupId>
|
||||
<artifactId>freya</artifactId>
|
||||
<version>${freya.version}</version>
|
||||
<classifier>jakarta</classifier>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- CDI -->
|
||||
<dependency>
|
||||
|
||||
226
src/main/java/com/gbcm/client/beans/GuestPreferences.java
Normal file
@@ -0,0 +1,226 @@
|
||||
package com.gbcm.client.beans;
|
||||
|
||||
import jakarta.enterprise.context.SessionScoped;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Bean pour les préférences d'affichage du template Freya
|
||||
* Compatible avec le template Freya 5.0.0 original
|
||||
*
|
||||
* @author GBCM Team
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-01
|
||||
*/
|
||||
@Named("guestPreferences")
|
||||
@SessionScoped
|
||||
public class GuestPreferences implements Serializable {
|
||||
|
||||
private String layout = "layout-light";
|
||||
private String menuTheme = "light";
|
||||
private String topbarTheme = "light";
|
||||
private String menuMode = "static";
|
||||
private String inputStyleClass = "";
|
||||
private boolean lightLogo = false;
|
||||
|
||||
/**
|
||||
* Récupère le layout CSS (light/dark)
|
||||
*
|
||||
* @return Nom du fichier CSS de layout
|
||||
*/
|
||||
public String getLayout() {
|
||||
return layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit le layout CSS
|
||||
*
|
||||
* @param layout Nom du layout (layout-light ou layout-dark)
|
||||
*/
|
||||
public void setLayout(String layout) {
|
||||
this.layout = layout;
|
||||
// Ajuster le logo selon le thème
|
||||
this.lightLogo = "layout-dark".equals(layout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le thème du menu
|
||||
*
|
||||
* @return Thème du menu (light/dark)
|
||||
*/
|
||||
public String getMenuTheme() {
|
||||
return menuTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit le thème du menu
|
||||
*
|
||||
* @param menuTheme Thème du menu
|
||||
*/
|
||||
public void setMenuTheme(String menuTheme) {
|
||||
this.menuTheme = menuTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le thème de la topbar
|
||||
*
|
||||
* @return Thème de la topbar (light/dark)
|
||||
*/
|
||||
public String getTopbarTheme() {
|
||||
return topbarTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit le thème de la topbar
|
||||
*
|
||||
* @param topbarTheme Thème de la topbar
|
||||
*/
|
||||
public void setTopbarTheme(String topbarTheme) {
|
||||
this.topbarTheme = topbarTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le mode du menu
|
||||
*
|
||||
* @return Mode du menu (static/overlay/horizontal)
|
||||
*/
|
||||
public String getMenuMode() {
|
||||
return menuMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit le mode du menu
|
||||
*
|
||||
* @param menuMode Mode du menu
|
||||
*/
|
||||
public void setMenuMode(String menuMode) {
|
||||
this.menuMode = menuMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère la classe CSS pour le style des inputs
|
||||
*
|
||||
* @return Classe CSS
|
||||
*/
|
||||
public String getInputStyleClass() {
|
||||
return inputStyleClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit la classe CSS pour le style des inputs
|
||||
*
|
||||
* @param inputStyleClass Classe CSS
|
||||
*/
|
||||
public void setInputStyleClass(String inputStyleClass) {
|
||||
this.inputStyleClass = inputStyleClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indique si le logo light doit être utilisé
|
||||
*
|
||||
* @return true pour le logo light
|
||||
*/
|
||||
public boolean isLightLogo() {
|
||||
return lightLogo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit l'utilisation du logo light
|
||||
*
|
||||
* @param lightLogo true pour utiliser le logo light
|
||||
*/
|
||||
public void setLightLogo(boolean lightLogo) {
|
||||
this.lightLogo = lightLogo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bascule vers le thème sombre
|
||||
*/
|
||||
public void switchToDark() {
|
||||
setLayout("layout-dark");
|
||||
setMenuTheme("dark");
|
||||
setTopbarTheme("dark");
|
||||
}
|
||||
|
||||
/**
|
||||
* Bascule vers le thème clair
|
||||
*/
|
||||
public void switchToLight() {
|
||||
setLayout("layout-light");
|
||||
setMenuTheme("light");
|
||||
setTopbarTheme("light");
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le thème sombre est actif
|
||||
*
|
||||
* @return true si thème sombre
|
||||
*/
|
||||
public boolean isDarkTheme() {
|
||||
return "layout-dark".equals(layout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le thème clair est actif
|
||||
*
|
||||
* @return true si thème clair
|
||||
*/
|
||||
public boolean isLightTheme() {
|
||||
return "layout-light".equals(layout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère la classe CSS complète pour le body
|
||||
*
|
||||
* @return Classes CSS combinées
|
||||
*/
|
||||
public String getBodyStyleClass() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(inputStyleClass);
|
||||
if (isDarkTheme()) {
|
||||
sb.append(" dark-theme");
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère l'icône pour le bouton de basculement de thème
|
||||
*
|
||||
* @return Classe d'icône
|
||||
*/
|
||||
public String getThemeToggleIcon() {
|
||||
return isDarkTheme() ? "pi pi-sun" : "pi pi-moon";
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le texte pour le bouton de basculement de thème
|
||||
*
|
||||
* @return Texte du bouton
|
||||
*/
|
||||
public String getThemeToggleText() {
|
||||
return isDarkTheme() ? "Mode Clair" : "Mode Sombre";
|
||||
}
|
||||
|
||||
/**
|
||||
* Bascule entre les thèmes clair et sombre
|
||||
*/
|
||||
public void toggleTheme() {
|
||||
if (isDarkTheme()) {
|
||||
switchToLight();
|
||||
} else {
|
||||
switchToDark();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Réinitialise les préférences aux valeurs par défaut
|
||||
*/
|
||||
public void resetToDefaults() {
|
||||
layout = "layout-light";
|
||||
menuTheme = "light";
|
||||
topbarTheme = "light";
|
||||
menuMode = "static";
|
||||
inputStyleClass = "";
|
||||
lightLogo = false;
|
||||
}
|
||||
}
|
||||
272
src/main/java/com/gbcm/client/beans/NavigationBean.java
Normal file
@@ -0,0 +1,272 @@
|
||||
package com.gbcm.client.beans;
|
||||
|
||||
import com.gbcm.client.beans.auth.AuthBean;
|
||||
import jakarta.enterprise.context.SessionScoped;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Bean JSF pour la gestion de la navigation dans l'interface GBCM
|
||||
*
|
||||
* @author GBCM Team
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-01
|
||||
*/
|
||||
@Named("navigationBean")
|
||||
@SessionScoped
|
||||
public class NavigationBean implements Serializable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(NavigationBean.class);
|
||||
|
||||
@Inject
|
||||
AuthBean authBean;
|
||||
|
||||
private String currentPage = "dashboard";
|
||||
|
||||
/**
|
||||
* Navigation vers le dashboard
|
||||
*
|
||||
* @return Outcome de navigation
|
||||
*/
|
||||
public String goToDashboard() {
|
||||
if (!authBean.isAuthenticated()) {
|
||||
return authBean.login();
|
||||
}
|
||||
|
||||
currentPage = "dashboard";
|
||||
logger.debug("Navigation vers le dashboard");
|
||||
return "/pages/dashboard?faces-redirect=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigation vers la gestion des utilisateurs
|
||||
*
|
||||
* @return Outcome de navigation
|
||||
*/
|
||||
public String goToUsers() {
|
||||
if (!authBean.isAuthenticated()) {
|
||||
return authBean.login();
|
||||
}
|
||||
|
||||
if (!authBean.isAdmin() && !authBean.isManager()) {
|
||||
logger.warn("Accès refusé à la gestion des utilisateurs pour: {}", authBean.getUsername());
|
||||
return "/pages/access-denied?faces-redirect=true";
|
||||
}
|
||||
|
||||
currentPage = "users";
|
||||
logger.debug("Navigation vers la gestion des utilisateurs");
|
||||
return "/pages/users?faces-redirect=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigation vers la gestion des clients
|
||||
*
|
||||
* @return Outcome de navigation
|
||||
*/
|
||||
public String goToClients() {
|
||||
if (!authBean.isAuthenticated()) {
|
||||
return authBean.login();
|
||||
}
|
||||
|
||||
currentPage = "clients";
|
||||
logger.debug("Navigation vers la gestion des clients");
|
||||
return "/pages/clients?faces-redirect=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigation vers la gestion des coaches
|
||||
*
|
||||
* @return Outcome de navigation
|
||||
*/
|
||||
public String goToCoaches() {
|
||||
if (!authBean.isAuthenticated()) {
|
||||
return authBean.login();
|
||||
}
|
||||
|
||||
currentPage = "coaches";
|
||||
logger.debug("Navigation vers la gestion des coaches");
|
||||
return "/pages/coaches?faces-redirect=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigation vers la gestion des ateliers
|
||||
*
|
||||
* @return Outcome de navigation
|
||||
*/
|
||||
public String goToWorkshops() {
|
||||
if (!authBean.isAuthenticated()) {
|
||||
return authBean.login();
|
||||
}
|
||||
|
||||
currentPage = "workshops";
|
||||
logger.debug("Navigation vers la gestion des ateliers");
|
||||
return "/pages/workshops?faces-redirect=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigation vers les sessions de coaching
|
||||
*
|
||||
* @return Outcome de navigation
|
||||
*/
|
||||
public String goToSessions() {
|
||||
if (!authBean.isAuthenticated()) {
|
||||
return authBean.login();
|
||||
}
|
||||
|
||||
currentPage = "sessions";
|
||||
logger.debug("Navigation vers les sessions de coaching");
|
||||
return "/pages/sessions?faces-redirect=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigation vers le profil utilisateur
|
||||
*
|
||||
* @return Outcome de navigation
|
||||
*/
|
||||
public String goToProfile() {
|
||||
if (!authBean.isAuthenticated()) {
|
||||
return authBean.login();
|
||||
}
|
||||
|
||||
currentPage = "profile";
|
||||
logger.debug("Navigation vers le profil utilisateur");
|
||||
return "/pages/profile?faces-redirect=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigation vers les paramètres (admin seulement)
|
||||
*
|
||||
* @return Outcome de navigation
|
||||
*/
|
||||
public String goToSettings() {
|
||||
if (!authBean.isAuthenticated()) {
|
||||
return authBean.login();
|
||||
}
|
||||
|
||||
if (!authBean.isAdmin()) {
|
||||
logger.warn("Accès refusé aux paramètres pour: {}", authBean.getUsername());
|
||||
return "/pages/access-denied?faces-redirect=true";
|
||||
}
|
||||
|
||||
currentPage = "settings";
|
||||
logger.debug("Navigation vers les paramètres");
|
||||
return "/pages/settings?faces-redirect=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si une page est active
|
||||
*
|
||||
* @param page Nom de la page
|
||||
* @return true si la page est active
|
||||
*/
|
||||
public boolean isPageActive(String page) {
|
||||
return page.equals(currentPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère la classe CSS pour un élément de menu
|
||||
*
|
||||
* @param page Nom de la page
|
||||
* @return Classe CSS
|
||||
*/
|
||||
public String getMenuItemClass(String page) {
|
||||
return isPageActive(page) ? "ui-state-active" : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le titre de la page courante
|
||||
*
|
||||
* @return Titre de la page
|
||||
*/
|
||||
public String getPageTitle() {
|
||||
switch (currentPage) {
|
||||
case "dashboard":
|
||||
return "Tableau de Bord";
|
||||
case "users":
|
||||
return "Gestion des Utilisateurs";
|
||||
case "clients":
|
||||
return "Gestion des Clients";
|
||||
case "coaches":
|
||||
return "Gestion des Coaches";
|
||||
case "workshops":
|
||||
return "Gestion des Ateliers";
|
||||
case "sessions":
|
||||
return "Sessions de Coaching";
|
||||
case "profile":
|
||||
return "Mon Profil";
|
||||
case "settings":
|
||||
return "Paramètres";
|
||||
default:
|
||||
return "GBCM";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère l'icône de la page courante
|
||||
*
|
||||
* @return Classe d'icône FontAwesome
|
||||
*/
|
||||
public String getPageIcon() {
|
||||
switch (currentPage) {
|
||||
case "dashboard":
|
||||
return "pi pi-chart-line";
|
||||
case "users":
|
||||
return "pi pi-users";
|
||||
case "clients":
|
||||
return "pi pi-briefcase";
|
||||
case "coaches":
|
||||
return "pi pi-user-plus";
|
||||
case "workshops":
|
||||
return "pi pi-calendar";
|
||||
case "sessions":
|
||||
return "pi pi-comments";
|
||||
case "profile":
|
||||
return "pi pi-user";
|
||||
case "settings":
|
||||
return "pi pi-cog";
|
||||
default:
|
||||
return "pi pi-home";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'utilisateur peut accéder à une page
|
||||
*
|
||||
* @param page Nom de la page
|
||||
* @return true si l'accès est autorisé
|
||||
*/
|
||||
public boolean canAccessPage(String page) {
|
||||
if (!authBean.isAuthenticated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (page) {
|
||||
case "users":
|
||||
case "settings":
|
||||
return authBean.isAdmin() || authBean.isManager();
|
||||
case "dashboard":
|
||||
case "clients":
|
||||
case "coaches":
|
||||
case "workshops":
|
||||
case "sessions":
|
||||
case "profile":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Getters et Setters
|
||||
public String getCurrentPage() {
|
||||
return currentPage;
|
||||
}
|
||||
|
||||
public void setCurrentPage(String currentPage) {
|
||||
this.currentPage = currentPage;
|
||||
}
|
||||
}
|
||||
238
src/main/java/com/gbcm/client/beans/auth/AuthBean.java
Normal file
@@ -0,0 +1,238 @@
|
||||
package com.gbcm.client.beans.auth;
|
||||
|
||||
import io.quarkus.oidc.IdToken;
|
||||
import io.quarkus.oidc.OidcSession;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
import jakarta.enterprise.context.SessionScoped;
|
||||
import jakarta.faces.application.FacesMessage;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Bean JSF pour la gestion de l'authentification OIDC avec Keycloak
|
||||
*
|
||||
* @author GBCM Team
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-01
|
||||
*/
|
||||
@Named("authBean")
|
||||
@SessionScoped
|
||||
public class AuthBean implements Serializable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthBean.class);
|
||||
|
||||
@Inject
|
||||
SecurityIdentity securityIdentity;
|
||||
|
||||
@Inject
|
||||
OidcSession oidcSession;
|
||||
|
||||
@Inject
|
||||
@IdToken
|
||||
JsonWebToken idToken;
|
||||
|
||||
/**
|
||||
* Vérifie si l'utilisateur est authentifié
|
||||
*
|
||||
* @return true si authentifié
|
||||
*/
|
||||
public boolean isAuthenticated() {
|
||||
boolean authenticated = securityIdentity != null && !securityIdentity.isAnonymous();
|
||||
logger.debug("Vérification authentification: {}", authenticated);
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le nom de l'utilisateur connecté
|
||||
*
|
||||
* @return Nom d'utilisateur ou null
|
||||
*/
|
||||
public String getUsername() {
|
||||
if (isAuthenticated()) {
|
||||
String username = securityIdentity.getPrincipal().getName();
|
||||
logger.debug("Nom d'utilisateur: {}", username);
|
||||
return username;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère l'email de l'utilisateur connecté
|
||||
*
|
||||
* @return Email ou null
|
||||
*/
|
||||
public String getEmail() {
|
||||
if (isAuthenticated() && idToken != null) {
|
||||
String email = idToken.getClaim("email");
|
||||
logger.debug("Email utilisateur: {}", email);
|
||||
return email;
|
||||
}
|
||||
return getUsername(); // Fallback sur le username
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le nom complet de l'utilisateur
|
||||
*
|
||||
* @return Nom complet ou null
|
||||
*/
|
||||
public String getFullName() {
|
||||
if (isAuthenticated() && idToken != null) {
|
||||
String firstName = idToken.getClaim("given_name");
|
||||
String lastName = idToken.getClaim("family_name");
|
||||
|
||||
if (firstName != null && lastName != null) {
|
||||
return firstName + " " + lastName;
|
||||
} else if (firstName != null) {
|
||||
return firstName;
|
||||
} else if (lastName != null) {
|
||||
return lastName;
|
||||
}
|
||||
}
|
||||
return getUsername(); // Fallback sur le username
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les rôles de l'utilisateur
|
||||
*
|
||||
* @return Set des rôles
|
||||
*/
|
||||
public Set<String> getRoles() {
|
||||
if (isAuthenticated()) {
|
||||
Set<String> roles = securityIdentity.getRoles();
|
||||
logger.debug("Rôles utilisateur: {}", roles);
|
||||
return roles;
|
||||
}
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'utilisateur a un rôle spécifique
|
||||
*
|
||||
* @param role Rôle à vérifier
|
||||
* @return true si l'utilisateur a le rôle
|
||||
*/
|
||||
public boolean hasRole(String role) {
|
||||
boolean hasRole = isAuthenticated() && securityIdentity.hasRole(role);
|
||||
logger.debug("Vérification rôle '{}': {}", role, hasRole);
|
||||
return hasRole;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'utilisateur est administrateur
|
||||
*
|
||||
* @return true si admin
|
||||
*/
|
||||
public boolean isAdmin() {
|
||||
return hasRole("ADMIN");
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'utilisateur est manager
|
||||
*
|
||||
* @return true si manager
|
||||
*/
|
||||
public boolean isManager() {
|
||||
return hasRole("MANAGER");
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'utilisateur est coach
|
||||
*
|
||||
* @return true si coach
|
||||
*/
|
||||
public boolean isCoach() {
|
||||
return hasRole("COACH");
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'utilisateur est client
|
||||
*
|
||||
* @return true si client
|
||||
*/
|
||||
public boolean isClient() {
|
||||
return hasRole("CLIENT");
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'utilisateur est prospect
|
||||
*
|
||||
* @return true si prospect
|
||||
*/
|
||||
public boolean isProspect() {
|
||||
return hasRole("PROSPECT");
|
||||
}
|
||||
|
||||
/**
|
||||
* Initie la connexion OIDC
|
||||
*
|
||||
* @return Navigation vers la page de login Keycloak
|
||||
*/
|
||||
public String login() {
|
||||
logger.info("Initiation de la connexion OIDC");
|
||||
// Quarkus OIDC redirigera automatiquement vers Keycloak
|
||||
return "/gbcm/login?faces-redirect=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Déconnexion OIDC
|
||||
*
|
||||
* @return Navigation vers la page d'accueil
|
||||
*/
|
||||
public String logout() {
|
||||
try {
|
||||
logger.info("Déconnexion OIDC pour l'utilisateur: {}", getUsername());
|
||||
|
||||
if (oidcSession != null) {
|
||||
oidcSession.logout().await().indefinitely();
|
||||
}
|
||||
|
||||
// Invalider la session JSF
|
||||
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
|
||||
|
||||
// Message de confirmation
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_INFO, "Déconnexion réussie",
|
||||
"Vous avez été déconnecté avec succès."));
|
||||
|
||||
logger.info("Déconnexion OIDC terminée");
|
||||
return "/index?faces-redirect=true";
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Erreur lors de la déconnexion OIDC", e);
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur de déconnexion",
|
||||
"Une erreur s'est produite lors de la déconnexion."));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère l'URL de déconnexion Keycloak
|
||||
*
|
||||
* @return URL de déconnexion
|
||||
*/
|
||||
public String getLogoutUrl() {
|
||||
return "http://localhost:8180/realms/gbcm-llc/protocol/openid-connect/logout" +
|
||||
"?redirect_uri=http://localhost:8081/gbcm/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère des informations sur le token ID
|
||||
*
|
||||
* @return Informations du token ou message d'erreur
|
||||
*/
|
||||
public String getTokenInfo() {
|
||||
if (idToken != null) {
|
||||
return "Token émis par: " + idToken.getIssuer() +
|
||||
", Expire à: " + idToken.getExpirationTime();
|
||||
}
|
||||
return "Aucun token ID disponible";
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
package com.gbcm.client.service;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.gbcm.server.api.dto.auth.LoginRequestDTO;
|
||||
import com.gbcm.server.api.dto.auth.LoginResponseDTO;
|
||||
import com.gbcm.server.api.dto.user.UserDTO;
|
||||
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.ws.rs.client.Client;
|
||||
import jakarta.ws.rs.client.ClientBuilder;
|
||||
import jakarta.ws.rs.client.Entity;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Client REST pour communiquer avec l'API GBCM Server
|
||||
@@ -24,7 +26,7 @@ public class GBCMServerClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GBCMServerClient.class);
|
||||
|
||||
private static final String BASE_URL = "http://localhost:8080/api";
|
||||
private static final String BASE_URL = "http://localhost:8082/api";
|
||||
|
||||
private final Client client;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ quarkus.application.name=gbcm-client
|
||||
quarkus.application.version=1.0.0-SNAPSHOT
|
||||
|
||||
# Server Configuration
|
||||
quarkus.http.port=8080
|
||||
quarkus.http.port=8081
|
||||
quarkus.http.host=0.0.0.0
|
||||
|
||||
# JSF Configuration
|
||||
|
||||
93
src/main/webapp/WEB-INF/freya-templates/config.xhtml
Normal file
@@ -0,0 +1,93 @@
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:f="http://java.sun.com/jsf/core"
|
||||
xmlns:h="http://java.sun.com/jsf/html"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<a href="#" id="layout-config-button" class="layout-config-button">
|
||||
<i class="pi pi-cog"/>
|
||||
</a>
|
||||
|
||||
<div id="layout-config" class="layout-config">
|
||||
<h:form id="config-form" styleClass="layout-config-form">
|
||||
<h5 style="margin-top: 0">Menu Type</h5>
|
||||
<p:selectOneRadio value="#{guestPreferences.menuMode}" layout="pageDirection"
|
||||
onchange="PrimeFaces.FreyaConfigurator.changeMenuMode(event.target.value)" >
|
||||
<f:selectItem itemLabel="Horizontal" itemValue="layout-horizontal" />
|
||||
<f:selectItem itemLabel="Sidebar" itemValue="layout-sidebar" />
|
||||
<f:selectItem itemLabel="Slim" itemValue="layout-slim" />
|
||||
<p:ajax listener="#{guestPreferences.onMenuTypeChange}" update="config-form" />
|
||||
</p:selectOneRadio>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h5>Color Scheme</h5>
|
||||
<p:selectOneRadio value="#{guestPreferences.darkMode}" layout="pageDirection"
|
||||
onchange="PrimeFaces.FreyaConfigurator.changeLayout('#{guestPreferences.componentTheme}', event.target.value)" >
|
||||
<f:selectItem itemLabel="Light" itemValue="light" />
|
||||
<f:selectItem itemLabel="Dark" itemValue="dark" />
|
||||
<p:ajax onstart="PrimeFaces.FreyaConfigurator.beforeResourceChange()" update="config-form logolink"/>
|
||||
</p:selectOneRadio>
|
||||
|
||||
<p:outputPanel rendered="#{guestPreferences.menuMode eq 'layout-horizontal'}">
|
||||
<hr/>
|
||||
<h5>Topbar and Menu Mode</h5>
|
||||
<p:selectOneRadio value="#{guestPreferences.topbarTheme}" layout="pageDirection"
|
||||
onchange="PrimeFaces.FreyaConfigurator.changeSectionTheme(event.target.value , 'layout-topbar');PrimeFaces.FreyaConfigurator.changeSectionTheme(event.target.value , 'layout-menu')" >
|
||||
<f:selectItem itemLabel="Light" itemValue="light" itemDisabled="#{guestPreferences.darkMode != 'light'}" />
|
||||
<f:selectItem itemLabel="Dark" itemValue="dark" itemDisabled="#{guestPreferences.darkMode != 'light'}"/>
|
||||
<p:ajax update="logolink config-form"/>
|
||||
</p:selectOneRadio>
|
||||
</p:outputPanel>
|
||||
|
||||
<p:outputPanel rendered="#{guestPreferences.menuMode != 'layout-horizontal'}">
|
||||
<hr/>
|
||||
<h5>Topbar Mode</h5>
|
||||
<p:selectOneRadio value="#{guestPreferences.topbarTheme}" layout="pageDirection"
|
||||
onchange="PrimeFaces.FreyaConfigurator.changeSectionTheme(event.target.value , 'layout-topbar')" >
|
||||
<f:selectItem itemLabel="Light" itemValue="light" itemDisabled="#{guestPreferences.darkMode != 'light'}" />
|
||||
<f:selectItem itemLabel="Dark" itemValue="dark" itemDisabled="#{guestPreferences.darkMode != 'light'}"/>
|
||||
<p:ajax update="logolink config-form"/>
|
||||
</p:selectOneRadio>
|
||||
</p:outputPanel>
|
||||
|
||||
<p:outputPanel rendered="#{guestPreferences.menuMode != 'layout-horizontal'}">
|
||||
<hr/>
|
||||
<h5>Menu Mode</h5>
|
||||
<p:selectOneRadio value="#{guestPreferences.menuTheme}" layout="pageDirection"
|
||||
onchange="PrimeFaces.FreyaConfigurator.changeSectionTheme(event.target.value , 'layout-menu')" >
|
||||
<f:selectItem itemLabel="Light" itemValue="light" itemDisabled="#{guestPreferences.darkMode != 'light'}" />
|
||||
<f:selectItem itemLabel="Dark" itemValue="dark" itemDisabled="#{guestPreferences.darkMode != 'light'}"/>
|
||||
<p:ajax update="logolink config-form"/>
|
||||
</p:selectOneRadio>
|
||||
</p:outputPanel>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h5>Input Style</h5>
|
||||
<p:selectOneRadio value="#{guestPreferences.inputStyle}" layout="pageDirection"
|
||||
onchange="PrimeFaces.FreyaConfigurator.updateInputStyle(event.target.value)">
|
||||
<f:selectItem itemLabel="Outlined" itemValue="outlined" />
|
||||
<f:selectItem itemLabel="Filled" itemValue="filled" />
|
||||
<p:ajax />
|
||||
</p:selectOneRadio>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h5>Theme Colors</h5>
|
||||
<div class="layout-themes">
|
||||
<ui:repeat value="#{guestPreferences.componentThemes}" var="componentTheme">
|
||||
<div>
|
||||
<p:commandLink actionListener="#{guestPreferences.setComponentTheme(componentTheme.file)}"
|
||||
style="background-color: #{componentTheme.color};" title="#{componentTheme.name}"
|
||||
process="@this"
|
||||
update="config-form"
|
||||
onstart="PrimeFaces.FreyaConfigurator.beforeResourceChange()"
|
||||
oncomplete="PrimeFaces.FreyaConfigurator.changeComponentsTheme('#{componentTheme.file}', '#{guestPreferences.darkMode}')">
|
||||
</p:commandLink>
|
||||
</div>
|
||||
</ui:repeat>
|
||||
</div>
|
||||
</h:form>
|
||||
</div>
|
||||
</ui:composition>
|
||||
56
src/main/webapp/WEB-INF/freya-templates/footer.xhtml
Normal file
@@ -0,0 +1,56 @@
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<div class="layout-footer">
|
||||
<div class="grid">
|
||||
<div class="col-12 lg:col-4">
|
||||
<div class="grid">
|
||||
<div class="col-6">
|
||||
<span class="footer-menutitle">SITE MAP</span>
|
||||
<ul>
|
||||
<li><a href="dashboard.xhtml">Dashboard</a></li>
|
||||
<li><a href="crud.xhtml">Crud</a></li>
|
||||
<li><a href="invoice.xhtml">Invoice</a></li>
|
||||
<li><a href="help.xhtml">Help</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<span class="footer-menutitle"></span>
|
||||
<ul>
|
||||
<li><a href="calendar.xhtml">Calendar</a></li>
|
||||
<li><a href="widgets.xhtml">Widgets</a></li>
|
||||
<li><a href="documentation.xhtml">Documentation</a></li>
|
||||
<li><a href="https://www.primefaces.org/store">Buy Now</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<span class="footer-menutitle">CONTACT US</span>
|
||||
<ul>
|
||||
<li>(415) 931-1624 794</li>
|
||||
<li>Mcallister St San Francisco,</li>
|
||||
<li>California(CA), 94102</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-12 md:col-6 lg:col-5">
|
||||
<span class="footer-menutitle">NEWSLETTER</span>
|
||||
<span class="footer-subtitle">Join our newsletter to get notification about the new features.</span>
|
||||
<h:form>
|
||||
<div class="newsletter-input">
|
||||
<p:inputText placeholder="email address" />
|
||||
<p:commandButton value="Join" styleClass="ui-button-secondary "/>
|
||||
</div>
|
||||
</h:form>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="footer-bottom">
|
||||
<h4>freya</h4>
|
||||
<h6>Copyright Ⓒ PrimeTek Informatics</h6>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:composition>
|
||||
105
src/main/webapp/WEB-INF/freya-templates/menu.xhtml
Normal file
@@ -0,0 +1,105 @@
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://java.sun.com/jsf/html"
|
||||
xmlns:f="http://java.sun.com/jsf/core"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:fr="http://primefaces.org/freya">
|
||||
|
||||
<div class="menu-wrapper">
|
||||
<div class="sidebar-logo">
|
||||
<a href="dashboard.xhtml">
|
||||
<p:graphicImage name="images/logo-freya-single.svg" library="freya-layout" />
|
||||
</a>
|
||||
<a href="#" class="sidebar-pin" title="Toggle Menu">
|
||||
<span class="pin"></span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div class="layout-menu-container">
|
||||
<h:form id="menuform">
|
||||
<fr:menu widgetVar="GBCMMenuWidget">
|
||||
<!-- Dashboard principal -->
|
||||
<p:menuitem id="m_dashboard" value="Tableau de Bord" icon="pi pi-chart-line" outcome="/pages/dashboard"
|
||||
rendered="#{authBean.authenticated}" />
|
||||
|
||||
<!-- Gestion des Clients -->
|
||||
<p:submenu id="m_clients" label="Clients" icon="pi pi-briefcase" rendered="#{authBean.authenticated}">
|
||||
<p:menuitem id="m_clients_list" value="Liste des Clients" icon="pi pi-list" outcome="/pages/clients" />
|
||||
<p:menuitem id="m_clients_prospects" value="Prospects" icon="pi pi-eye" outcome="/pages/prospects" />
|
||||
<p:menuitem id="m_clients_add" value="Nouveau Client" icon="pi pi-plus" outcome="/pages/clients/add" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- Gestion des Coaches -->
|
||||
<p:submenu id="m_coaches" label="Coaches" icon="pi pi-user-plus" rendered="#{authBean.authenticated}">
|
||||
<p:menuitem id="m_coaches_list" value="Liste des Coaches" icon="pi pi-users" outcome="/pages/coaches" />
|
||||
<p:menuitem id="m_coaches_add" value="Nouveau Coach" icon="pi pi-plus" outcome="/pages/coaches/add"
|
||||
rendered="#{authBean.admin or authBean.manager}" />
|
||||
<p:menuitem id="m_coaches_planning" value="Planning" icon="pi pi-calendar" outcome="/pages/coaches/planning" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- Sessions de Coaching -->
|
||||
<p:submenu id="m_sessions" label="Sessions" icon="pi pi-comments" rendered="#{authBean.authenticated}">
|
||||
<p:menuitem id="m_sessions_list" value="Mes Sessions" icon="pi pi-list" outcome="/pages/sessions" />
|
||||
<p:menuitem id="m_sessions_calendar" value="Calendrier" icon="pi pi-calendar" outcome="/pages/sessions/calendar" />
|
||||
<p:menuitem id="m_sessions_add" value="Nouvelle Session" icon="pi pi-plus" outcome="/pages/sessions/add"
|
||||
rendered="#{authBean.coach or authBean.admin or authBean.manager}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- Ateliers -->
|
||||
<p:submenu id="m_workshops" label="Ateliers" icon="pi pi-calendar" rendered="#{authBean.authenticated}">
|
||||
<p:menuitem id="m_workshops_list" value="Liste des Ateliers" icon="pi pi-list" outcome="/pages/workshops" />
|
||||
<p:menuitem id="m_workshops_add" value="Nouvel Atelier" icon="pi pi-plus" outcome="/pages/workshops/add"
|
||||
rendered="#{authBean.coach or authBean.admin or authBean.manager}" />
|
||||
<p:menuitem id="m_workshops_calendar" value="Calendrier" icon="pi pi-calendar" outcome="/pages/workshops/calendar" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- Administration -->
|
||||
<p:submenu id="m_admin" label="Administration" icon="pi pi-cog"
|
||||
rendered="#{authBean.admin or authBean.manager}">
|
||||
<p:menuitem id="m_users" value="Utilisateurs" icon="pi pi-users" outcome="/pages/users" />
|
||||
<p:menuitem id="m_reports" value="Rapports" icon="pi pi-chart-bar" outcome="/pages/reports" />
|
||||
<p:menuitem id="m_settings" value="Paramètres" icon="pi pi-cog" outcome="/pages/settings"
|
||||
rendered="#{authBean.admin}" />
|
||||
</p:submenu>
|
||||
<p:menuitem id="m_calendar" value="Calendar" icon="pi pi-calendar-plus" outcome="/calendar" />
|
||||
<p:menuitem id="m_chronoline" value="Chronoline" icon="pi pi-calendar" outcome="/chronoline" />
|
||||
<p:menuitem id="m_landing" value="Landing" icon="pi pi-globe" outcome="/landing" />
|
||||
<p:menuitem id="m_login" value="Login" icon="pi pi-sign-in" outcome="/login" />
|
||||
<p:menuitem id="m_invoice" value="Invoice" icon="pi pi-dollar" outcome="/invoice" />
|
||||
<p:menuitem id="m_help" value="Help" icon="pi pi-question-circle" outcome="/help" />
|
||||
<p:menuitem id="m_error" value="Error" icon="pi pi-times-circle" outcome="/error" />
|
||||
<p:menuitem id="m_notfound" value="Not Found" icon="pi pi-exclamation-circle" outcome="/notfound" />
|
||||
<p:menuitem id="m_access" value="Access Denied" icon="pi pi-lock" outcome="/access" />
|
||||
<p:menuitem id="m_empty" value="Empty" icon="pi pi-circle-off" outcome="/empty" />
|
||||
</p:submenu>
|
||||
<p:submenu id="m_hierarchy" label="Hierarchy" icon="pi pi-align-left">
|
||||
<p:submenu id="m_sm1" label="Submenu 1" icon="pi pi-align-left">
|
||||
<p:submenu id="m_sm11" label="Submenu 1.1" icon="pi pi-align-left">
|
||||
<p:menuitem id="m_lnk111" value="Link 1.1.1" icon="pi pi-align-left" url="#" />
|
||||
<p:menuitem id="m_lnk112" value="Link 1.1.2" icon="pi pi-align-left" url="#" />
|
||||
<p:menuitem id="m_lnk113" value="Link 1.1.3" icon="pi pi-align-left" url="#" />
|
||||
</p:submenu>
|
||||
<p:submenu id="m_sm12" label="Submenu 1.2" icon="pi pi-align-left">
|
||||
<p:menuitem id="m_lnk121" value="Link 1.2.1" icon="pi pi-align-left" url="#" />
|
||||
</p:submenu>
|
||||
</p:submenu>
|
||||
<p:submenu id="m_sm2" label="Submenu 2" icon="pi pi-align-left">
|
||||
<p:submenu id="m_sm21" label="Submenu 2.1" icon="pi pi-align-left">
|
||||
<p:menuitem id="m_lnk211" value="Link 2.1.1" icon="pi pi-align-left" url="#" />
|
||||
<p:menuitem id="m_lnk212" value="Link 2.1.2" icon="pi pi-align-left" url="#" />
|
||||
</p:submenu>
|
||||
<p:submenu id="m_sm22" label="Submenu 2.2" icon="pi pi-align-left">
|
||||
<p:menuitem id="m_lnk221" value="Link 2.2.1" icon="pi pi-align-left" url="#" />
|
||||
</p:submenu>
|
||||
</p:submenu>
|
||||
</p:submenu>
|
||||
<p:submenu id="m_getstarted" label="Start" icon="pi pi-download">
|
||||
<p:menuitem id="m_buy" value="Buy Now" icon="pi pi-shopping-cart" url="https://www.primefaces.org/store" />
|
||||
<p:menuitem id="m_doc" value="Documentation" icon="pi pi-info-circle" outcome="/documentation"/>
|
||||
</p:submenu>
|
||||
</fr:menu>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
284
src/main/webapp/WEB-INF/freya-templates/rightpanel.xhtml
Normal file
@@ -0,0 +1,284 @@
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:f="http://java.sun.com/jsf/core"
|
||||
xmlns:h="http://java.sun.com/jsf/html"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<div class="layout-rightpanel">
|
||||
<div class="rightpanel-wrapper">
|
||||
<div class="rightpanel-section weather-section">
|
||||
<div class="section-header">
|
||||
<h6>Today</h6>
|
||||
</div>
|
||||
<div class="weather">
|
||||
<p:graphicImage name="images/rightpanel/icon-sun.svg" library="demo" />
|
||||
<div class="weather-info">
|
||||
<h6>Ankara, 22 May</h6>
|
||||
<h1>24º</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rightpanel-section tasks-section">
|
||||
<div class="section-header">
|
||||
<h6>My list</h6>
|
||||
<h:form>
|
||||
<p:commandButton type="button" icon="pi pi-plus" styleClass="ui-button-secondary ui-button-flat rounded-button" />
|
||||
</h:form>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="task-info">
|
||||
<h6>Perform usability testing for P15 MVP</h6>
|
||||
<span>-Public pages</span>
|
||||
<span>-Product pages</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="task-info">
|
||||
<h6>Buy puzzle set from Amazon</h6>
|
||||
<span>Ravensburger Seurat, 2000</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="done">
|
||||
<div class="task-info">
|
||||
<h6>Morning Run</h6>
|
||||
</div>
|
||||
<i class="pi pi-check"></i>
|
||||
</li>
|
||||
<li class="done">
|
||||
<div class="task-info">
|
||||
<h6>Morning Run</h6>
|
||||
</div>
|
||||
<i class="pi pi-check"></i>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="rightpanel-section favorites-section">
|
||||
<div class="section-header">
|
||||
<h6>Favorites</h6>
|
||||
</div>
|
||||
<div class="favorite-items">
|
||||
<a href="#" class="favorite-item">
|
||||
<p:graphicImage name="images/rightpanel/github.svg" library="demo" />
|
||||
</a>
|
||||
<a href="#" class="favorite-item">
|
||||
<p:graphicImage name="images/rightpanel/slack.svg" library="demo" />
|
||||
</a>
|
||||
<a href="#" class="favorite-item">
|
||||
<p:graphicImage name="images/rightpanel/aws.svg" library="demo" />
|
||||
</a>
|
||||
<a href="#" class="favorite-item">
|
||||
<p:graphicImage name="images/rightpanel/jenkins.svg" library="demo" />
|
||||
</a>
|
||||
<a href="#" class="favorite-item">
|
||||
<p:graphicImage name="images/rightpanel/jira.svg" library="demo" />
|
||||
</a>
|
||||
<a href="#" class="add-item">
|
||||
<i class="pi pi-plus"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rightpanel-section chat-section chat">
|
||||
<h:form>
|
||||
<p:tabView>
|
||||
<p:tab>
|
||||
<f:facet name="title">
|
||||
<p:graphicImage name="images/rightpanel/profile-1.png" library="demo" />
|
||||
<span class="ui-badge">3</span>
|
||||
</f:facet>
|
||||
<div class="chat">
|
||||
<span class="fade"></span>
|
||||
<div class="chat-content">
|
||||
<div class="chat-message send">
|
||||
<span class="name">You</span>
|
||||
<div class="message">
|
||||
<p>Hey M. hope you are well. Our idea is accepted by the board. Now it’s time to execute it.</p>
|
||||
<span>3 mins ago</span>
|
||||
</div>
|
||||
<div class="message">
|
||||
<p>we did it! 🤠</p>
|
||||
<span>3 mins ago</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-message">
|
||||
<span class="name">Micheal J.</span>
|
||||
<div class="message">
|
||||
<p>That’s really good!</p>
|
||||
<span>3 mins ago</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-message send">
|
||||
<span class="name">You</span>
|
||||
<div class="message">
|
||||
<p>But it’s important to ship MVP ASAP</p>
|
||||
<span>3 mins ago</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-message">
|
||||
<span class="name">Micheal J.</span>
|
||||
<div class="message">
|
||||
<p>I’ll be looking at the process then, just to be sure 🤓 </p>
|
||||
<span>3 mins ago</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-message send">
|
||||
<span class="name">You</span>
|
||||
<div class="message">
|
||||
<p>That’s awesome. Thanks!</p>
|
||||
<span>3 mins ago</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-message">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-input">
|
||||
<p:inputText placeholder="Write here..." />
|
||||
</div>
|
||||
</div>
|
||||
</p:tab>
|
||||
<p:tab>
|
||||
<f:facet name="title">
|
||||
<p:graphicImage name="images/rightpanel/profile-2.png" library="demo" />
|
||||
<span class="ui-badge">1</span>
|
||||
</f:facet>
|
||||
<div class="chat">
|
||||
<span class="fade"></span>
|
||||
<div class="chat-content">
|
||||
<div class="chat-message">
|
||||
<span class="name">Sarah</span>
|
||||
<div class="message">
|
||||
<p>That’s really good!</p>
|
||||
<span>3 mins ago</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-message send">
|
||||
<span class="name">You</span>
|
||||
<div class="message">
|
||||
<p>But it’s important to ship MVP ASAP</p>
|
||||
<span>3 mins ago</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-message">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-input">
|
||||
<p:inputText placeholder="Write here..." />
|
||||
</div>
|
||||
</div>
|
||||
</p:tab>
|
||||
<p:tab>
|
||||
<f:facet name="title">
|
||||
<p:graphicImage name="images/rightpanel/profile-3.png" library="demo" />
|
||||
</f:facet>
|
||||
<div class="chat">
|
||||
<div class="chat-content no-message">
|
||||
<h4>No Message From Margret K.</h4>
|
||||
</div>
|
||||
<div class="chat-input">
|
||||
<p:inputText placeholder="Write here..." />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</p:tab>
|
||||
<p:tab>
|
||||
<f:facet name="title">
|
||||
<p:graphicImage name="images/rightpanel/profile-4.png" library="demo" />
|
||||
</f:facet>
|
||||
<div class="chat">
|
||||
<div class="chat-content no-message">
|
||||
<h4>No Message From Bob C.</h4>
|
||||
</div>
|
||||
<div class="chat-input">
|
||||
<p:inputText placeholder="Write here..." />
|
||||
</div>
|
||||
</div>
|
||||
</p:tab>
|
||||
<p:tab>
|
||||
<f:facet name="title">
|
||||
<i class="pi pi-plus"></i>
|
||||
</f:facet>
|
||||
<div class="chat">
|
||||
<span class="fade"></span>
|
||||
<div class="contacts">
|
||||
<ul>
|
||||
<li>
|
||||
<p:graphicImage name="images/rightpanel/profile-1.png" library="demo" />
|
||||
<div class="contact-info">
|
||||
<h6>John Doe</h6>
|
||||
<span>Active</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p:graphicImage name="images/rightpanel/profile-2.png" library="demo" />
|
||||
<div class="contact-info">
|
||||
<h6>John Doe</h6>
|
||||
<span>Active</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p:graphicImage name="images/rightpanel/profile-3.png" library="demo" />
|
||||
<div class="contact-info">
|
||||
<h6>John Doe</h6>
|
||||
<span>Active</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p:graphicImage name="images/rightpanel/profile-4.png" library="demo" />
|
||||
<div class="contact-info">
|
||||
<h6>John Doe</h6>
|
||||
<span>Active</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p:graphicImage name="images/rightpanel/profile-1.png" library="demo" />
|
||||
<div class="contact-info">
|
||||
<h6>John Doe</h6>
|
||||
<span>Active</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p:graphicImage name="images/rightpanel/profile-1.png" library="demo" />
|
||||
<div class="contact-info">
|
||||
<h6>John Doe</h6>
|
||||
<span>Active</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p:graphicImage name="images/rightpanel/profile-1.png" library="demo" />
|
||||
<div class="contact-info">
|
||||
<h6>John Doe</h6>
|
||||
<span>Active</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p:graphicImage name="images/rightpanel/profile-1.png" library="demo" />
|
||||
<div class="contact-info">
|
||||
<h6>John Doe</h6>
|
||||
<span>Active</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p:graphicImage name="images/rightpanel/profile-1.png" library="demo" />
|
||||
<div class="contact-info">
|
||||
<h6>John Doe</h6>
|
||||
<span>Active</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="chat-input">
|
||||
<p:inputText placeholder="Search new person" />
|
||||
</div>
|
||||
</div>
|
||||
</p:tab>
|
||||
</p:tabView>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
54
src/main/webapp/WEB-INF/freya-templates/template.xhtml
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://java.sun.com/jsf/html"
|
||||
xmlns:f="http://java.sun.com/jsf/core"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
lang="en">
|
||||
|
||||
<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="apple-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">GBCM - Global Business Consulting & Management</ui:insert></title>
|
||||
<h:outputScript name="js/layout.js" library="freya-layout" />
|
||||
<h:outputScript name="js/prism.js" library="freya-layout"/>
|
||||
<ui:insert name="head"/>
|
||||
</h:head>
|
||||
|
||||
<h:body styleClass="#{guestPreferences.inputStyleClass}">
|
||||
<div class="layout-wrapper layout-topbar-#{guestPreferences.topbarTheme} layout-menu-#{guestPreferences.menuTheme} #{guestPreferences.menuMode}" >
|
||||
<ui:include src="./topbar.xhtml"/>
|
||||
|
||||
<ui:include src="./rightpanel.xhtml"/>
|
||||
<ui:include src="./config.xhtml" />
|
||||
|
||||
<div class="layout-main">
|
||||
<div class="layout-content">
|
||||
<ui:insert name="content"/>
|
||||
</div>
|
||||
<ui:include src="./footer.xhtml"/>
|
||||
</div>
|
||||
|
||||
<p:ajaxStatus style="width:32px;height:32px;position:fixed;right:7px;bottom:7px">
|
||||
<f:facet name="start">
|
||||
<i class="pi pi-spin pi-spinner ajax-loader" aria-hidden="true"/>
|
||||
</f:facet>
|
||||
|
||||
<f:facet name="complete">
|
||||
<h:outputText value="" />
|
||||
</f:facet>
|
||||
</p:ajaxStatus>
|
||||
<div class="layout-mask modal-in"></div>
|
||||
</div>
|
||||
<h:outputStylesheet name="css/primeicons.css" library="freya-layout" />
|
||||
<h:outputStylesheet name="css/primeflex.min.css" library="freya-layout" />
|
||||
<h:outputStylesheet name="css/#{guestPreferences.layout}.css" library="freya-layout" />
|
||||
<h:outputStylesheet name="css/demo-#{guestPreferences.darkMode}.css" library="demo" />
|
||||
</h:body>
|
||||
|
||||
</html>
|
||||
102
src/main/webapp/WEB-INF/freya-templates/topbar.xhtml
Normal file
@@ -0,0 +1,102 @@
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://java.sun.com/jsf/html"
|
||||
xmlns:f="http://java.sun.com/jsf/core"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:po="http://primefaces.org/freya">
|
||||
|
||||
<div class="layout-topbar">
|
||||
<div class="layout-topbar-wrapper">
|
||||
<div class="layout-topbar-left">
|
||||
<a href="#" class="menu-button">
|
||||
<i class="pi pi-bars"/>
|
||||
</a>
|
||||
<h:link id="logolink" outcome="/pages/dashboard" styleClass="layout-topbar-logo">
|
||||
<span style="color: white; font-size: 1.5rem; font-weight: bold;">GBCM</span>
|
||||
</h:link>
|
||||
</div>
|
||||
|
||||
<ui:include src="./menu.xhtml" />
|
||||
|
||||
<div class="layout-topbar-right">
|
||||
<ul class="layout-topbar-actions">
|
||||
<li class="topbar-item search-item ">
|
||||
<a href="#">
|
||||
<i class="topbar-icon pi pi-search"/>
|
||||
</a>
|
||||
<h:form>
|
||||
<h:panelGroup styleClass="search-input-wrapper">
|
||||
<p:inputText placeholder="Search..." />
|
||||
<i class="pi pi-search"/>
|
||||
</h:panelGroup>
|
||||
</h:form>
|
||||
<ul>
|
||||
<h:form onsubmit="return false;">
|
||||
<h:panelGroup styleClass="search-input-wrapper">
|
||||
<p:inputText placeholder="Search..." />
|
||||
<i class="pi pi-search"/>
|
||||
</h:panelGroup>
|
||||
</h:form>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="topbar-item user-profile" style="#{authBean.authenticated ? '' : 'display: none;'}">
|
||||
<a href="#">
|
||||
<div style="display: flex; align-items: center; gap: 0.5rem;">
|
||||
<div style="width: 32px; height: 32px; border-radius: 50%; background: rgba(255,255,255,0.2); display: flex; align-items: center; justify-content: center; font-weight: bold; color: white;">
|
||||
#{authBean.authenticated ? authBean.fullName.substring(0,1).toUpperCase() : 'U'}
|
||||
</div>
|
||||
<div style="color: white;">
|
||||
<div style="font-size: 0.9rem; font-weight: bold;">#{authBean.fullName}</div>
|
||||
<div style="font-size: 0.75rem; opacity: 0.8;">#{authBean.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<ul>
|
||||
<li>
|
||||
<h:link outcome="/pages/profile">
|
||||
<i class="pi pi-user"></i>
|
||||
<span>Mon Profil</span>
|
||||
</h:link>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" onclick="PF('settingsDialog').show();">
|
||||
<i class="pi pi-cog"></i>
|
||||
<span>Paramètres</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<h:form>
|
||||
<p:commandLink action="#{authBean.logout}" styleClass="logout-link">
|
||||
<i class="pi pi-sign-out"></i>
|
||||
<span>Déconnexion</span>
|
||||
</p:commandLink>
|
||||
</h:form>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span>Settings</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span>Messages</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span>Notifications</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<a href="#" class="layout-rightpanel-button">
|
||||
<i class="pi pi-arrow-left"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</ui:composition>
|
||||
@@ -2,58 +2,129 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<h:head>
|
||||
<title>GBCM - Global Business Consulting and Management</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<h:outputStylesheet library="css" name="freya-theme.css"/>
|
||||
</h:head>
|
||||
<ui:composition template="/WEB-INF/freya-templates/template.xhtml">
|
||||
|
||||
<h:body>
|
||||
<div class="layout-wrapper">
|
||||
<div class="layout-main">
|
||||
<div class="layout-content">
|
||||
<div class="layout-content-inner">
|
||||
<div class="content-section introduction">
|
||||
<div class="feature-intro">
|
||||
<h1>GBCM Client Web</h1>
|
||||
<p>Interface web pour la plateforme GBCM</p>
|
||||
<ui:define name="title">
|
||||
GBCM - Global Business Consulting & Management
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<!-- Section d'accueil pour utilisateurs non connectés -->
|
||||
<div class="col-12" style="#{authBean.authenticated ? 'display: none;' : ''}">
|
||||
<div class="card">
|
||||
<div class="flex flex-column align-items-center justify-content-center" style="min-height: 60vh;">
|
||||
<div class="text-center mb-5">
|
||||
<h1 class="text-6xl font-bold text-primary mb-3">GBCM</h1>
|
||||
<h2 class="text-3xl text-700 mb-4">Global Business Consulting & Management</h2>
|
||||
<p class="text-xl text-600 line-height-3 mb-5">
|
||||
Votre partenaire de confiance pour le développement professionnel et la gestion d'entreprise.
|
||||
Découvrez nos services de coaching, consulting et formation.
|
||||
</p>
|
||||
|
||||
<div class="flex flex-wrap gap-3 justify-content-center">
|
||||
<h:form>
|
||||
<p:commandButton value="Se Connecter"
|
||||
action="#{authBean.login}"
|
||||
icon="pi pi-sign-in"
|
||||
styleClass="p-button-lg p-button-primary"/>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Services -->
|
||||
<div class="grid w-full">
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<div class="card text-center border-1 border-300">
|
||||
<i class="pi pi-briefcase text-4xl text-primary mb-3"></i>
|
||||
<h3 class="text-xl font-semibold mb-2">Gestion Clients</h3>
|
||||
<p class="text-600 line-height-3">
|
||||
Suivi complet de vos prospects et clients avec des outils de CRM intégrés.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<div class="card text-center border-1 border-300">
|
||||
<i class="pi pi-user-plus text-4xl text-primary mb-3"></i>
|
||||
<h3 class="text-xl font-semibold mb-2">Coaching</h3>
|
||||
<p class="text-600 line-height-3">
|
||||
Sessions de coaching personnalisées avec nos experts certifiés.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<div class="card text-center border-1 border-300">
|
||||
<i class="pi pi-calendar text-4xl text-primary mb-3"></i>
|
||||
<h3 class="text-xl font-semibold mb-2">Ateliers</h3>
|
||||
<p class="text-600 line-height-3">
|
||||
Workshops et formations en groupe pour développer vos compétences.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<div class="card text-center border-1 border-300">
|
||||
<i class="pi pi-chart-line text-4xl text-primary mb-3"></i>
|
||||
<h3 class="text-xl font-semibold mb-2">Analytics</h3>
|
||||
<p class="text-600 line-height-3">
|
||||
Tableaux de bord et rapports détaillés pour suivre vos progrès.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-section implementation">
|
||||
<div class="card">
|
||||
<h2>Bienvenue sur GBCM</h2>
|
||||
<p>Global Business Consulting and Management - Plateforme de conseil en entreprise</p>
|
||||
|
||||
<h:form>
|
||||
<div class="p-grid">
|
||||
<div class="p-col-12 p-md-6">
|
||||
<div class="card">
|
||||
<h3>Authentification</h3>
|
||||
<p:commandButton value="Se connecter"
|
||||
action="#{authBean.redirectToLogin}"
|
||||
styleClass="ui-button-success"
|
||||
icon="pi pi-sign-in"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-col-12 p-md-6">
|
||||
<div class="card">
|
||||
<h3>Statut du serveur</h3>
|
||||
<p:outputLabel value="Backend API : " />
|
||||
<p:outputLabel value="http://localhost:8080"
|
||||
styleClass="ui-state-highlight"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</h:form>
|
||||
<!-- Dashboard pour utilisateurs connectés -->
|
||||
<div class="col-12" style="#{authBean.authenticated ? '' : 'display: none;'}">
|
||||
<div class="card">
|
||||
<div class="flex align-items-center justify-content-between mb-4">
|
||||
<h2 class="text-2xl font-semibold text-900 m-0">
|
||||
Bienvenue, #{authBean.fullName} !
|
||||
</h2>
|
||||
<p:badge value="#{authBean.roles.size()} rôle(s)" severity="info"/>
|
||||
</div>
|
||||
|
||||
<div class="text-600 mb-4">
|
||||
<p>Vous êtes connecté en tant que :
|
||||
<span class="font-semibold text-900">#{authBean.email}</span>
|
||||
</p>
|
||||
<p>Rôles actifs :
|
||||
<ui:repeat value="#{authBean.roles}" var="role">
|
||||
<p:badge value="#{role}" severity="secondary" styleClass="mr-2"/>
|
||||
</ui:repeat>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Informations de session -->
|
||||
<div class="card mt-4" style="background: var(--surface-50);">
|
||||
<h3 class="text-lg font-semibold text-900 mb-3">Informations de Session OIDC</h3>
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6">
|
||||
<div class="text-600 mb-2">Type d'authentification :</div>
|
||||
<div class="text-900 font-semibold">Keycloak OIDC</div>
|
||||
</div>
|
||||
<div class="col-12 md:col-6">
|
||||
<div class="text-600 mb-2">Statut :</div>
|
||||
<p:badge value="Connecté" severity="success"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<div class="text-600 mb-2">Token Info :</div>
|
||||
<div class="text-900 text-sm">#{authBean.tokenInfo}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</h:body>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
</html>
|
||||
|
||||
181
src/main/webapp/pages/clients.xhtml
Normal file
@@ -0,0 +1,181 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<ui:composition template="/WEB-INF/freya-templates/template.xhtml">
|
||||
<ui:define name="title">Gestion des Clients - GBCM</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="layout-content">
|
||||
<div class="layout-content-inner">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="text-900 font-semibold text-xl m-0">
|
||||
<i class="pi pi-briefcase mr-2"></i>
|
||||
Gestion des Clients
|
||||
</h2>
|
||||
<p:commandButton value="Nouveau Client"
|
||||
icon="pi pi-plus"
|
||||
styleClass="p-button-success"
|
||||
disabled="true"
|
||||
title="Fonctionnalité à venir"/>
|
||||
</div>
|
||||
|
||||
<!-- Filtres et recherche -->
|
||||
<div class="grid mb-4">
|
||||
<div class="col-12 md:col-4">
|
||||
<span class="p-input-icon-left w-full">
|
||||
<i class="pi pi-search"></i>
|
||||
<p:inputText placeholder="Rechercher un client..."
|
||||
styleClass="w-full"
|
||||
disabled="true"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-12 md:col-3">
|
||||
<p:selectOneMenu value=""
|
||||
styleClass="w-full"
|
||||
disabled="true">
|
||||
<f:selectItem itemLabel="Tous les statuts" itemValue="" />
|
||||
<f:selectItem itemLabel="Actif" itemValue="ACTIVE" />
|
||||
<f:selectItem itemLabel="Inactif" itemValue="INACTIVE" />
|
||||
<f:selectItem itemLabel="Prospect" itemValue="PROSPECT" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
<div class="col-12 md:col-3">
|
||||
<p:selectOneMenu value=""
|
||||
styleClass="w-full"
|
||||
disabled="true">
|
||||
<f:selectItem itemLabel="Tous les coachs" itemValue="" />
|
||||
<f:selectItem itemLabel="Coach A" itemValue="1" />
|
||||
<f:selectItem itemLabel="Coach B" itemValue="2" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
<div class="col-12 md:col-2">
|
||||
<p:commandButton value="Filtrer"
|
||||
icon="pi pi-filter"
|
||||
styleClass="w-full"
|
||||
disabled="true"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tableau des clients (données de démonstration) -->
|
||||
<p:dataTable value="#{[]}"
|
||||
var="client"
|
||||
emptyMessage="Aucun client trouvé"
|
||||
paginator="true"
|
||||
rows="10"
|
||||
paginatorPosition="bottom"
|
||||
styleClass="p-datatable-gridlines">
|
||||
|
||||
<p:column headerText="Photo" width="80">
|
||||
<div style="width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(135deg, #667eea, #764ba2); display: flex; align-items: center; justify-content: center; font-weight: bold; color: white;">
|
||||
C
|
||||
</div>
|
||||
</p:column>
|
||||
|
||||
<p:column headerText="Nom" sortBy="#{client.name}">
|
||||
<h:outputText value="Exemple Client" />
|
||||
</p:column>
|
||||
|
||||
<p:column headerText="Email" sortBy="#{client.email}">
|
||||
<h:outputText value="client@example.com" />
|
||||
</p:column>
|
||||
|
||||
<p:column headerText="Téléphone">
|
||||
<h:outputText value="+33 1 23 45 67 89" />
|
||||
</p:column>
|
||||
|
||||
<p:column headerText="Coach Assigné">
|
||||
<h:outputText value="Coach Exemple" />
|
||||
</p:column>
|
||||
|
||||
<p:column headerText="Statut" width="120">
|
||||
<p:badge value="Actif" severity="success" />
|
||||
</p:column>
|
||||
|
||||
<p:column headerText="Dernière Session" width="150">
|
||||
<h:outputText value="15/03/2024" />
|
||||
</p:column>
|
||||
|
||||
<p:column headerText="Actions" width="150">
|
||||
<p:commandButton icon="pi pi-eye"
|
||||
styleClass="p-button-rounded p-button-text p-button-info mr-1"
|
||||
title="Voir le détail"
|
||||
disabled="true"/>
|
||||
<p:commandButton icon="pi pi-pencil"
|
||||
styleClass="p-button-rounded p-button-text p-button-warning mr-1"
|
||||
title="Modifier"
|
||||
disabled="true"/>
|
||||
<p:commandButton icon="pi pi-trash"
|
||||
styleClass="p-button-rounded p-button-text p-button-danger"
|
||||
title="Supprimer"
|
||||
disabled="true"/>
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
|
||||
<!-- Message informatif -->
|
||||
<div class="mt-4 p-3 border-round" style="background: var(--blue-50); border: 1px solid var(--blue-200);">
|
||||
<div class="flex align-items-center">
|
||||
<i class="pi pi-info-circle text-blue-600 mr-2"></i>
|
||||
<span class="text-blue-800">
|
||||
<strong>Information :</strong> Cette page affiche une interface de démonstration.
|
||||
Les données réelles seront chargées depuis l'API backend une fois la fonctionnalité implémentée.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Statistiques rapides -->
|
||||
<div class="col-12">
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-3">
|
||||
<div class="card text-center">
|
||||
<div class="text-blue-600 font-bold text-xl mb-2">
|
||||
<i class="pi pi-users text-4xl mb-3"></i>
|
||||
<div>0</div>
|
||||
</div>
|
||||
<div class="text-600">Total Clients</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 md:col-3">
|
||||
<div class="card text-center">
|
||||
<div class="text-green-600 font-bold text-xl mb-2">
|
||||
<i class="pi pi-check-circle text-4xl mb-3"></i>
|
||||
<div>0</div>
|
||||
</div>
|
||||
<div class="text-600">Clients Actifs</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 md:col-3">
|
||||
<div class="card text-center">
|
||||
<div class="text-orange-600 font-bold text-xl mb-2">
|
||||
<i class="pi pi-eye text-4xl mb-3"></i>
|
||||
<div>0</div>
|
||||
</div>
|
||||
<div class="text-600">Prospects</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 md:col-3">
|
||||
<div class="card text-center">
|
||||
<div class="text-purple-600 font-bold text-xl mb-2">
|
||||
<i class="pi pi-calendar text-4xl mb-3"></i>
|
||||
<div>0</div>
|
||||
</div>
|
||||
<div class="text-600">Sessions ce mois</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
</html>
|
||||
202
src/main/webapp/pages/dashboard.xhtml
Normal file
@@ -0,0 +1,202 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<ui:composition template="/WEB-INF/freya-templates/template.xhtml">
|
||||
|
||||
<ui:define name="title">
|
||||
Tableau de Bord - GBCM
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<!-- Statistiques principales -->
|
||||
<div class="col-12 lg:col-3">
|
||||
<div class="card text-center">
|
||||
<div class="text-900 font-medium text-xl mb-2">Clients Actifs</div>
|
||||
<div class="text-primary font-bold text-4xl">142</div>
|
||||
<div class="text-green-500 font-medium">
|
||||
<i class="pi pi-arrow-up text-xs"></i>
|
||||
<span class="text-sm">+12%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 lg:col-3">
|
||||
<div class="card text-center">
|
||||
<div class="text-900 font-medium text-xl mb-2">Sessions ce mois</div>
|
||||
<div class="text-primary font-bold text-4xl">28</div>
|
||||
<div class="text-green-500 font-medium">
|
||||
<i class="pi pi-arrow-up text-xs"></i>
|
||||
<span class="text-sm">+8%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 lg:col-3">
|
||||
<div class="card text-center">
|
||||
<div class="text-900 font-medium text-xl mb-2">Ateliers planifiés</div>
|
||||
<div class="text-primary font-bold text-4xl">6</div>
|
||||
<div class="text-orange-500 font-medium">
|
||||
<i class="pi pi-arrow-down text-xs"></i>
|
||||
<span class="text-sm">-2%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 lg:col-3">
|
||||
<div class="card text-center">
|
||||
<div class="text-900 font-medium text-xl mb-2">Revenus du mois</div>
|
||||
<div class="text-primary font-bold text-4xl">€15.2K</div>
|
||||
<div class="text-green-500 font-medium">
|
||||
<i class="pi pi-arrow-up text-xs"></i>
|
||||
<span class="text-sm">+18%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions rapides -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h3 class="text-xl font-semibold text-900 mb-4">Actions Rapides</h3>
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<h:link outcome="/pages/clients/add" styleClass="no-underline">
|
||||
<div class="card border-1 border-300 hover:border-primary cursor-pointer transition-colors transition-duration-150 text-center">
|
||||
<i class="pi pi-plus-circle text-4xl text-primary mb-3"></i>
|
||||
<div class="text-900 font-semibold mb-1">Nouveau Client</div>
|
||||
<div class="text-600 text-sm">Ajouter un client</div>
|
||||
</div>
|
||||
</h:link>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<h:link outcome="/pages/sessions/add" styleClass="no-underline">
|
||||
<div class="card border-1 border-300 hover:border-primary cursor-pointer transition-colors transition-duration-150 text-center">
|
||||
<i class="pi pi-calendar-plus text-4xl text-primary mb-3"></i>
|
||||
<div class="text-900 font-semibold mb-1">Nouvelle Session</div>
|
||||
<div class="text-600 text-sm">Planifier une session</div>
|
||||
</div>
|
||||
</h:link>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<h:link outcome="/pages/workshops/add" styleClass="no-underline">
|
||||
<div class="card border-1 border-300 hover:border-primary cursor-pointer transition-colors transition-duration-150 text-center">
|
||||
<i class="pi pi-users text-4xl text-primary mb-3"></i>
|
||||
<div class="text-900 font-semibold mb-1">Nouvel Atelier</div>
|
||||
<div class="text-600 text-sm">Créer un workshop</div>
|
||||
</div>
|
||||
</h:link>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-3">
|
||||
<h:link outcome="/pages/reports" styleClass="no-underline">
|
||||
<div class="card border-1 border-300 hover:border-primary cursor-pointer transition-colors transition-duration-150 text-center">
|
||||
<i class="pi pi-chart-bar text-4xl text-primary mb-3"></i>
|
||||
<div class="text-900 font-semibold mb-1">Rapports</div>
|
||||
<div class="text-600 text-sm">Voir les statistiques</div>
|
||||
</div>
|
||||
</h:link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Activité récente -->
|
||||
<div class="col-12 lg:col-8">
|
||||
<div class="card">
|
||||
<h3 class="text-xl font-semibold text-900 mb-4">Activité Récente</h3>
|
||||
<p:timeline value="#{dashboardBean.recentActivities}" var="activity" align="left">
|
||||
<p:timelineEvent>
|
||||
<div class="flex align-items-center">
|
||||
<i class="#{activity.icon} text-primary mr-3"></i>
|
||||
<div>
|
||||
<div class="text-900 font-medium">#{activity.title}</div>
|
||||
<div class="text-600 text-sm">#{activity.description}</div>
|
||||
<div class="text-500 text-xs mt-1">#{activity.timestamp}</div>
|
||||
</div>
|
||||
</div>
|
||||
</p:timelineEvent>
|
||||
</p:timeline>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Prochains rendez-vous -->
|
||||
<div class="col-12 lg:col-4">
|
||||
<div class="card">
|
||||
<h3 class="text-xl font-semibold text-900 mb-4">Prochains Rendez-vous</h3>
|
||||
<div class="flex flex-column gap-3">
|
||||
<div class="flex align-items-center p-3 border-1 border-300 border-round">
|
||||
<div class="flex align-items-center justify-content-center bg-primary border-circle w-3rem h-3rem mr-3">
|
||||
<i class="pi pi-user text-white"></i>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="text-900 font-medium">Session avec Marie Dupont</div>
|
||||
<div class="text-600 text-sm">Aujourd'hui, 14h00</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex align-items-center p-3 border-1 border-300 border-round">
|
||||
<div class="flex align-items-center justify-content-center bg-orange-500 border-circle w-3rem h-3rem mr-3">
|
||||
<i class="pi pi-users text-white"></i>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="text-900 font-medium">Atelier Leadership</div>
|
||||
<div class="text-600 text-sm">Demain, 9h30</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex align-items-center p-3 border-1 border-300 border-round">
|
||||
<div class="flex align-items-center justify-content-center bg-green-500 border-circle w-3rem h-3rem mr-3">
|
||||
<i class="pi pi-phone text-white"></i>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="text-900 font-medium">Appel client - TechCorp</div>
|
||||
<div class="text-600 text-sm">Vendredi, 16h00</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<h:link outcome="/pages/sessions/calendar" styleClass="p-button p-button-outlined w-full">
|
||||
<i class="pi pi-calendar mr-2"></i>
|
||||
Voir le calendrier complet
|
||||
</h:link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Informations utilisateur -->
|
||||
<div class="col-12">
|
||||
<div class="card" style="background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-600) 100%); color: white;">
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<h3 class="text-2xl font-bold mb-2">Bienvenue, #{authBean.fullName} !</h3>
|
||||
<p class="text-lg opacity-90 mb-3">
|
||||
Vous êtes connecté en tant que #{authBean.email}
|
||||
</p>
|
||||
<div class="flex gap-2">
|
||||
<ui:repeat value="#{authBean.roles}" var="role">
|
||||
<span class="bg-white-alpha-20 border-round px-3 py-1 text-sm font-medium">
|
||||
#{role}
|
||||
</span>
|
||||
</ui:repeat>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-white-alpha-70 text-sm mb-1">Authentification</div>
|
||||
<div class="text-lg font-semibold">Keycloak OIDC</div>
|
||||
<div class="text-white-alpha-70 text-xs mt-2">#{authBean.tokenInfo}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
</html>
|
||||
170
src/main/webapp/pages/profile.xhtml
Normal file
@@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<ui:composition template="/WEB-INF/freya-templates/template.xhtml">
|
||||
<ui:define name="title">Profil Utilisateur - GBCM</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="layout-content">
|
||||
<div class="layout-content-inner">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h2 class="text-900 font-semibold text-xl mb-4">
|
||||
<i class="pi pi-user mr-2"></i>
|
||||
Profil Utilisateur
|
||||
</h2>
|
||||
|
||||
<div class="grid">
|
||||
<!-- Photo de profil et informations principales -->
|
||||
<div class="col-12 md:col-4">
|
||||
<div class="text-center mb-4">
|
||||
<div style="width: 120px; height: 120px; border-radius: 50%; background: linear-gradient(135deg, var(--primary-color), var(--primary-color-text)); display: flex; align-items: center; justify-content: center; margin: 0 auto 1rem auto; font-size: 3rem; font-weight: bold; color: white;">
|
||||
#{authBean.authenticated ? authBean.fullName.substring(0,1).toUpperCase() : 'U'}
|
||||
</div>
|
||||
<h3 class="text-900 font-semibold mb-2">#{authBean.fullName}</h3>
|
||||
<p class="text-600 mb-0">#{authBean.email}</p>
|
||||
<p:badge value="Connecté" severity="success" class="mt-2"></p:badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Informations détaillées -->
|
||||
<div class="col-12 md:col-8">
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6">
|
||||
<h4 class="text-900 font-semibold mb-3">Informations Personnelles</h4>
|
||||
<div class="mb-3">
|
||||
<label class="block text-900 font-medium mb-1">Nom complet</label>
|
||||
<p class="text-700 m-0">#{authBean.fullName}</p>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="block text-900 font-medium mb-1">Email</label>
|
||||
<p class="text-700 m-0">#{authBean.email}</p>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="block text-900 font-medium mb-1">Nom d'utilisateur</label>
|
||||
<p class="text-700 m-0">#{authBean.username}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6">
|
||||
<h4 class="text-900 font-semibold mb-3">Rôles et Permissions</h4>
|
||||
<div class="mb-3">
|
||||
<label class="block text-900 font-medium mb-1">Rôles assignés</label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<ui:repeat value="#{authBean.roles}" var="role">
|
||||
<p:badge value="#{role}" severity="info"></p:badge>
|
||||
</ui:repeat>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="block text-900 font-medium mb-1">Niveau d'accès</label>
|
||||
<p class="text-700 m-0">
|
||||
<h:outputText value="Administrateur" rendered="#{authBean.admin}" />
|
||||
<h:outputText value="Manager" rendered="#{authBean.manager and not authBean.admin}" />
|
||||
<h:outputText value="Coach" rendered="#{authBean.coach and not authBean.manager and not authBean.admin}" />
|
||||
<h:outputText value="Client" rendered="#{authBean.client and not authBean.coach and not authBean.manager and not authBean.admin}" />
|
||||
<h:outputText value="Prospect" rendered="#{authBean.prospect and not authBean.client and not authBean.coach and not authBean.manager and not authBean.admin}" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Informations de session OIDC -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h3 class="text-900 font-semibold text-lg mb-4">
|
||||
<i class="pi pi-shield mr-2"></i>
|
||||
Informations de Session OIDC
|
||||
</h3>
|
||||
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6">
|
||||
<h4 class="text-900 font-semibold mb-3">Token Information</h4>
|
||||
<div class="mb-3">
|
||||
<label class="block text-900 font-medium mb-1">Issuer</label>
|
||||
<p class="text-700 m-0 text-sm">#{authBean.tokenInfo.issuer}</p>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="block text-900 font-medium mb-1">Subject</label>
|
||||
<p class="text-700 m-0 text-sm">#{authBean.tokenInfo.subject}</p>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="block text-900 font-medium mb-1">Audience</label>
|
||||
<p class="text-700 m-0 text-sm">#{authBean.tokenInfo.audience}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6">
|
||||
<h4 class="text-900 font-semibold mb-3">Session Details</h4>
|
||||
<div class="mb-3">
|
||||
<label class="block text-900 font-medium mb-1">Expiration</label>
|
||||
<p class="text-700 m-0 text-sm">
|
||||
<h:outputText value="#{authBean.tokenInfo.expirationTime}">
|
||||
<f:convertDateTime pattern="dd/MM/yyyy HH:mm:ss" timeZone="Europe/Paris"/>
|
||||
</h:outputText>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="block text-900 font-medium mb-1">Émis le</label>
|
||||
<p class="text-700 m-0 text-sm">
|
||||
<h:outputText value="#{authBean.tokenInfo.issuedAt}">
|
||||
<f:convertDateTime pattern="dd/MM/yyyy HH:mm:ss" timeZone="Europe/Paris"/>
|
||||
</h:outputText>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h3 class="text-900 font-semibold text-lg mb-4">
|
||||
<i class="pi pi-cog mr-2"></i>
|
||||
Actions
|
||||
</h3>
|
||||
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<p:commandButton value="Modifier le profil"
|
||||
icon="pi pi-pencil"
|
||||
styleClass="p-button-outlined"
|
||||
disabled="true"
|
||||
title="Fonctionnalité à venir"/>
|
||||
|
||||
<p:commandButton value="Changer le mot de passe"
|
||||
icon="pi pi-key"
|
||||
styleClass="p-button-outlined"
|
||||
disabled="true"
|
||||
title="Géré par Keycloak"/>
|
||||
|
||||
<p:commandButton value="Paramètres de sécurité"
|
||||
icon="pi pi-shield"
|
||||
styleClass="p-button-outlined"
|
||||
disabled="true"
|
||||
title="Fonctionnalité à venir"/>
|
||||
|
||||
<p:commandButton value="Se déconnecter"
|
||||
icon="pi pi-sign-out"
|
||||
styleClass="p-button-danger"
|
||||
action="#{authBean.logout}"
|
||||
onclick="return confirm('Êtes-vous sûr de vouloir vous déconnecter ?')"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
</html>
|
||||
4257
src/main/webapp/resources/freya-layout/css/layout-dark.css
Normal file
@@ -0,0 +1,5 @@
|
||||
$primaryColor:lighten(#2170E7, 5%);
|
||||
$primaryTextColor:#ffffff;
|
||||
|
||||
@import '../../sass/variables/layout/_layout_dark';
|
||||
@import '../../sass/layout/_layout';
|
||||
4257
src/main/webapp/resources/freya-layout/css/layout-light.css
Normal file
@@ -0,0 +1,5 @@
|
||||
$primaryColor:#2170E7;
|
||||
$primaryTextColor:#ffffff;
|
||||
|
||||
@import '../../sass/variables/layout/_layout_light';
|
||||
@import '../../sass/layout/_layout';
|
||||
1
src/main/webapp/resources/freya-layout/css/primeflex-v2.min.css
vendored
Normal file
1
src/main/webapp/resources/freya-layout/css/primeflex.min.css
vendored
Normal file
1017
src/main/webapp/resources/freya-layout/css/primeicons.css
Normal file
BIN
src/main/webapp/resources/freya-layout/icons/primeicons.eot
Normal file
270
src/main/webapp/resources/freya-layout/icons/primeicons.svg
Normal file
|
After Width: | Height: | Size: 265 KiB |
BIN
src/main/webapp/resources/freya-layout/icons/primeicons.ttf
Normal file
BIN
src/main/webapp/resources/freya-layout/icons/primeicons.woff
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/main/webapp/resources/freya-layout/images/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,9 @@
|
||||
<svg width="17" height="20" viewBox="0 0 17 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0H6.00019V3.82345L17 1.66667V6.66667L6.00019 8.82345V10.4901L17 8.33333V13.3333L6.00019 15.4901V20H0V0Z" fill="url(#paint0_linear)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="3.33335" y1="3.08442e-08" x2="8.49995" y2="20" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#297FFF"/>
|
||||
<stop offset="1" stop-color="#7A0EE7"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 469 B |
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="209px" height="60px" viewBox="0 0 209 60" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>logo-freya-white</title>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="logo-freya-white" fill="#FFFFFF" fill-rule="nonzero">
|
||||
<polygon id="Path" points="0 0 15.8827941 0 15.8827941 10.1321425 45 4.41666667 45 17.6666667 15.8827941 23.3821425 15.8827941 27.7988092 45 22.0833333 45 35.3333333 15.8827941 41.0488092 15.8827941 53 0 53"></polygon>
|
||||
<path d="M84,20.8302387 L77.2821159,20.8302387 L77.2821159,18.9204244 C77.2821159,17.4350133 78.1284635,16.5862069 79.6095718,16.5862069 L83.9471033,16.5862069 L83.9471033,9 L77.070529,9 C71.0403023,9 67.8664987,12.3421751 67.8664987,18.1246684 L67.8664987,20.8302387 L63,20.8302387 L63.0528967,28.5755968 L67.7607053,28.5755968 L67.7607053,49 L77.5994962,49 L77.5994962,28.5755968 L84,28.5755968 L84,20.8302387 Z" id="Path"></path>
|
||||
<path d="M105.301136,21 C101.846591,21 99.3636364,21.9630996 97.3125,24.3173432 L95.7471591,21.1070111 L89,21.1070111 L89,50 L99.0397727,50 L99.0397727,35.3394834 C99.0397727,31.6476015 100.551136,30.095941 104.059659,30.095941 L108,30.095941 L108,21 L105.301136,21 Z" id="Path"></path>
|
||||
<path d="M141,35.2034783 C141,26.6852174 134.662692,20 125.473595,20 C116.495741,20 110,26.5773913 110,35.4730435 C110,44.3686957 116.548552,51 125.473595,51 C132.603066,51 138.412266,46.8486957 140.41908,40.2173913 L130.596252,40.2173913 C129.698467,41.9426087 127.744463,42.9669565 125.473595,42.9669565 C122.357751,42.9669565 120.298126,41.2417391 119.664395,37.8991304 L140.841567,37.8991304 C140.947189,36.9826087 141,36.12 141,35.2034783 Z M125.473595,27.8713043 C128.431005,27.8713043 130.49063,29.4347826 131.335605,32.346087 L119.822828,32.346087 C120.614991,29.4347826 122.621806,27.8713043 125.473595,27.8713043 Z" id="Shape"></path>
|
||||
<path d="M165.423077,21 L159.764423,36.6842105 L153.682692,21 L143,21 L154.793269,47.1052632 C153.471154,50.6315789 152.360577,51.6315789 148.605769,51.6315789 L145.855769,51.6315789 L145.855769,60 L149.240385,60 C156.644231,60 160.134615,56.8947368 163.995192,48.1578947 L176,21 L165.423077,21 Z" id="Path"></path>
|
||||
<path d="M201.660066,20.8641115 L200.656766,23.1324042 C198.333333,21.1341463 195.323432,20 191.943894,20 C183.231023,20 177,26.4268293 177,35.445993 C177,44.5191638 183.231023,51 191.943894,51 C195.270627,51 198.227723,49.9198606 200.551155,47.9756098 L201.39604,50.0278746 L209,50.0278746 L209,20.8641115 L201.660066,20.8641115 Z M193.264026,42.1428571 C189.567657,42.1428571 186.874587,39.2804878 186.874587,35.445993 C186.874587,31.6655052 189.567657,28.8571429 193.264026,28.8571429 C196.960396,28.8571429 199.653465,31.6655052 199.653465,35.445993 C199.653465,39.2804878 196.960396,42.1428571 193.264026,42.1428571 Z" id="Shape"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
40
src/main/webapp/resources/freya-layout/images/logo-freya.svg
Normal 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 |
|
After Width: | Height: | Size: 493 KiB |
|
After Width: | Height: | Size: 273 KiB |
|
After Width: | Height: | Size: 327 KiB |
|
After Width: | Height: | Size: 825 KiB |
BIN
src/main/webapp/resources/freya-layout/images/pages/search.png
Normal file
|
After Width: | Height: | Size: 788 B |
879
src/main/webapp/resources/freya-layout/js/layout.js
Normal 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');
|
||||
}
|
||||
});
|
||||
}
|
||||