Refactoring - Version OK

This commit is contained in:
dahoud
2025-11-17 16:00:58 +00:00
parent 40fdbedcad
commit 3f00a26308
64 changed files with 3317 additions and 2393 deletions

View File

@@ -82,11 +82,19 @@
<artifactId>quarkus-rest-client-jackson</artifactId>
</dependency>
<!-- Sécurité JWT -->
<!-- Sécurité JWT et OIDC (Keycloak) -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-jwt</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client</artifactId>
</dependency>
<!-- Configuration -->
<dependency>

View File

@@ -7,6 +7,7 @@ import jakarta.faces.convert.Converter;
import jakarta.faces.convert.FacesConverter;
import jakarta.inject.Named;
import dev.lions.unionflow.client.view.DemandesBean.Membre;
import java.util.UUID;
@Named
@ApplicationScoped
@@ -20,15 +21,15 @@ public class MembreConverter implements Converter<Membre> {
}
try {
// Parse the membre ID from the string value
Long membreId = Long.valueOf(value);
// Parse the membre ID from the string value (UUID)
UUID membreId = UUID.fromString(value);
// Create a simple Membre object with just the ID
// In a real implementation, you would fetch from database
Membre membre = new Membre();
membre.setId(membreId);
return membre;
} catch (NumberFormatException e) {
} catch (IllegalArgumentException e) {
return null;
}
}

View File

@@ -6,12 +6,13 @@ import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.io.Serializable;
import java.util.UUID;
public class AssociationDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private UUID id;
@NotBlank(message = "Le nom de l'association est obligatoire")
private String nom;
@@ -54,8 +55,8 @@ public class AssociationDTO implements Serializable {
}
// Getters et Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }

View File

@@ -0,0 +1,91 @@
package dev.lions.unionflow.client.dto;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
public class CotisationDTO implements Serializable {
private static final long serialVersionUID = 1L;
private UUID id;
private String numeroReference;
private UUID membreId;
private String numeroMembre;
private String nomMembre;
private UUID associationId;
private String nomAssociation;
private String typeCotisation;
private String libelle;
private String description;
private BigDecimal montantDu;
private BigDecimal montantPaye;
private String codeDevise;
private String statut;
private LocalDate dateEcheance;
private LocalDateTime datePaiement;
private String methodePaiement;
private String observations;
private LocalDateTime dateCreation;
// Getters et Setters
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNumeroReference() { return numeroReference; }
public void setNumeroReference(String numeroReference) { this.numeroReference = numeroReference; }
public UUID getMembreId() { return membreId; }
public void setMembreId(UUID membreId) { this.membreId = membreId; }
public String getNumeroMembre() { return numeroMembre; }
public void setNumeroMembre(String numeroMembre) { this.numeroMembre = numeroMembre; }
public String getNomMembre() { return nomMembre; }
public void setNomMembre(String nomMembre) { this.nomMembre = nomMembre; }
public UUID getAssociationId() { return associationId; }
public void setAssociationId(UUID associationId) { this.associationId = associationId; }
public String getNomAssociation() { return nomAssociation; }
public void setNomAssociation(String nomAssociation) { this.nomAssociation = nomAssociation; }
public String getTypeCotisation() { return typeCotisation; }
public void setTypeCotisation(String typeCotisation) { this.typeCotisation = typeCotisation; }
public String getLibelle() { return libelle; }
public void setLibelle(String libelle) { this.libelle = libelle; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public BigDecimal getMontantDu() { return montantDu; }
public void setMontantDu(BigDecimal montantDu) { this.montantDu = montantDu; }
public BigDecimal getMontantPaye() { return montantPaye; }
public void setMontantPaye(BigDecimal montantPaye) { this.montantPaye = montantPaye; }
public String getCodeDevise() { return codeDevise; }
public void setCodeDevise(String codeDevise) { this.codeDevise = codeDevise; }
public String getStatut() { return statut; }
public void setStatut(String statut) { this.statut = statut; }
public LocalDate getDateEcheance() { return dateEcheance; }
public void setDateEcheance(LocalDate dateEcheance) { this.dateEcheance = dateEcheance; }
public LocalDateTime getDatePaiement() { return datePaiement; }
public void setDatePaiement(LocalDateTime datePaiement) { this.datePaiement = datePaiement; }
public String getMethodePaiement() { return methodePaiement; }
public void setMethodePaiement(String methodePaiement) { this.methodePaiement = methodePaiement; }
public String getObservations() { return observations; }
public void setObservations(String observations) { this.observations = observations; }
public LocalDateTime getDateCreation() { return dateCreation; }
public void setDateCreation(LocalDateTime dateCreation) { this.dateCreation = dateCreation; }
}

View File

@@ -0,0 +1,99 @@
package dev.lions.unionflow.client.dto;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
public class DemandeAideDTO implements Serializable {
private static final long serialVersionUID = 1L;
private UUID id;
private String numeroReference;
private String type;
private String titre;
private String description;
private String justification;
private BigDecimal montantDemande;
private BigDecimal montantAccorde;
private String statut;
private String urgence;
private String localisation;
private String motif;
private UUID demandeurId;
private String demandeur;
private String telephone;
private String email;
private LocalDate dateDemande;
private LocalDate dateLimite;
private String responsableTraitement;
private UUID organisationId;
private LocalDateTime dateCreation;
// Getters et Setters
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNumeroReference() { return numeroReference; }
public void setNumeroReference(String numeroReference) { this.numeroReference = numeroReference; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getTitre() { return titre; }
public void setTitre(String titre) { this.titre = titre; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getJustification() { return justification; }
public void setJustification(String justification) { this.justification = justification; }
public BigDecimal getMontantDemande() { return montantDemande; }
public void setMontantDemande(BigDecimal montantDemande) { this.montantDemande = montantDemande; }
public BigDecimal getMontantAccorde() { return montantAccorde; }
public void setMontantAccorde(BigDecimal montantAccorde) { this.montantAccorde = montantAccorde; }
public String getStatut() { return statut; }
public void setStatut(String statut) { this.statut = statut; }
public String getUrgence() { return urgence; }
public void setUrgence(String urgence) { this.urgence = urgence; }
public String getLocalisation() { return localisation; }
public void setLocalisation(String localisation) { this.localisation = localisation; }
public String getMotif() { return motif; }
public void setMotif(String motif) { this.motif = motif; }
public UUID getDemandeurId() { return demandeurId; }
public void setDemandeurId(UUID demandeurId) { this.demandeurId = demandeurId; }
public String getDemandeur() { return demandeur; }
public void setDemandeur(String demandeur) { this.demandeur = demandeur; }
public String getTelephone() { return telephone; }
public void setTelephone(String telephone) { this.telephone = telephone; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public LocalDate getDateDemande() { return dateDemande; }
public void setDateDemande(LocalDate dateDemande) { this.dateDemande = dateDemande; }
public LocalDate getDateLimite() { return dateLimite; }
public void setDateLimite(LocalDate dateLimite) { this.dateLimite = dateLimite; }
public String getResponsableTraitement() { return responsableTraitement; }
public void setResponsableTraitement(String responsableTraitement) { this.responsableTraitement = responsableTraitement; }
public UUID getOrganisationId() { return organisationId; }
public void setOrganisationId(UUID organisationId) { this.organisationId = organisationId; }
public LocalDateTime getDateCreation() { return dateCreation; }
public void setDateCreation(LocalDateTime dateCreation) { this.dateCreation = dateCreation; }
}

View File

@@ -0,0 +1,92 @@
package dev.lions.unionflow.client.dto;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.UUID;
public class EvenementDTO implements Serializable {
private static final long serialVersionUID = 1L;
private UUID id;
private String titre;
private String description;
private String type;
private String statut;
private String priorite;
private LocalDate dateDebut;
private LocalDate dateFin;
private LocalTime heureDebut;
private LocalTime heureFin;
private String lieu;
private String adresse;
private String organisateur;
private String organisateurEmail;
private Integer capaciteMax;
private Integer participantsInscrits;
private BigDecimal budget;
private UUID organisationId;
private LocalDateTime dateCreation;
// Getters et Setters
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getTitre() { return titre; }
public void setTitre(String titre) { this.titre = titre; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getStatut() { return statut; }
public void setStatut(String statut) { this.statut = statut; }
public String getPriorite() { return priorite; }
public void setPriorite(String priorite) { this.priorite = priorite; }
public LocalDate getDateDebut() { return dateDebut; }
public void setDateDebut(LocalDate dateDebut) { this.dateDebut = dateDebut; }
public LocalDate getDateFin() { return dateFin; }
public void setDateFin(LocalDate dateFin) { this.dateFin = dateFin; }
public LocalTime getHeureDebut() { return heureDebut; }
public void setHeureDebut(LocalTime heureDebut) { this.heureDebut = heureDebut; }
public LocalTime getHeureFin() { return heureFin; }
public void setHeureFin(LocalTime heureFin) { this.heureFin = heureFin; }
public String getLieu() { return lieu; }
public void setLieu(String lieu) { this.lieu = lieu; }
public String getAdresse() { return adresse; }
public void setAdresse(String adresse) { this.adresse = adresse; }
public String getOrganisateur() { return organisateur; }
public void setOrganisateur(String organisateur) { this.organisateur = organisateur; }
public String getOrganisateurEmail() { return organisateurEmail; }
public void setOrganisateurEmail(String organisateurEmail) { this.organisateurEmail = organisateurEmail; }
public Integer getCapaciteMax() { return capaciteMax; }
public void setCapaciteMax(Integer capaciteMax) { this.capaciteMax = capaciteMax; }
public Integer getParticipantsInscrits() { return participantsInscrits; }
public void setParticipantsInscrits(Integer participantsInscrits) { this.participantsInscrits = participantsInscrits; }
public BigDecimal getBudget() { return budget; }
public void setBudget(BigDecimal budget) { this.budget = budget; }
public UUID getOrganisationId() { return organisationId; }
public void setOrganisationId(UUID organisationId) { this.organisationId = organisationId; }
public LocalDateTime getDateCreation() { return dateCreation; }
public void setDateCreation(LocalDateTime dateCreation) { this.dateCreation = dateCreation; }
}

View File

@@ -5,12 +5,13 @@ import jakarta.validation.constraints.Positive;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
public class FormulaireDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private UUID id;
@NotNull
private String nom;
@@ -60,8 +61,8 @@ public class FormulaireDTO implements Serializable {
public FormulaireDTO() {}
// Getters et Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }
@@ -171,7 +172,7 @@ public class FormulaireDTO implements Serializable {
BigDecimal economie = getEconomieAnnuelle();
if (coutMensuelAnnuel.compareTo(BigDecimal.ZERO) > 0) {
return economie.multiply(BigDecimal.valueOf(100))
.divide(coutMensuelAnnuel, 0, BigDecimal.ROUND_HALF_UP)
.divide(coutMensuelAnnuel, 0, java.math.RoundingMode.HALF_UP)
.intValue();
}
}

View File

@@ -8,12 +8,13 @@ import jakarta.validation.constraints.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.io.Serializable;
import java.util.UUID;
public class MembreDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private UUID id;
@NotBlank(message = "Le numéro de membre est obligatoire", groups = {ValidationGroups.CreateMember.class, ValidationGroups.FullRegistration.class})
@ValidMemberNumber(groups = {ValidationGroups.CreateMember.class, ValidationGroups.UpdateMember.class, ValidationGroups.FullRegistration.class})
@@ -62,7 +63,7 @@ public class MembreDTO implements Serializable {
private String statut;
@NotNull(message = "L'association est obligatoire")
private Long associationId;
private UUID associationId;
private String associationNom;
@@ -88,8 +89,8 @@ public class MembreDTO implements Serializable {
}
// Getters et Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNumeroMembre() { return numeroMembre; }
public void setNumeroMembre(String numeroMembre) { this.numeroMembre = numeroMembre; }
@@ -130,8 +131,8 @@ public class MembreDTO implements Serializable {
public String getStatut() { return statut; }
public void setStatut(String statut) { this.statut = statut; }
public Long getAssociationId() { return associationId; }
public void setAssociationId(Long associationId) { this.associationId = associationId; }
public UUID getAssociationId() { return associationId; }
public void setAssociationId(UUID associationId) { this.associationId = associationId; }
public String getAssociationNom() { return associationNom; }
public void setAssociationNom(String associationNom) { this.associationNom = associationNom; }

View File

@@ -6,6 +6,7 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.UUID;
public class SouscriptionDTO implements Serializable {
@@ -46,14 +47,14 @@ public class SouscriptionDTO implements Serializable {
public String getLibelle() { return libelle; }
}
private Long id;
private UUID id;
@NotNull
private Long organisationId;
private UUID organisationId;
private String organisationNom;
@NotNull
private Long formulaireId;
private UUID formulaireId;
private String formulaireNom;
@NotNull
@@ -102,17 +103,17 @@ public class SouscriptionDTO implements Serializable {
public SouscriptionDTO() {}
// Getters et Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public Long getOrganisationId() { return organisationId; }
public void setOrganisationId(Long organisationId) { this.organisationId = organisationId; }
public UUID getOrganisationId() { return organisationId; }
public void setOrganisationId(UUID organisationId) { this.organisationId = organisationId; }
public String getOrganisationNom() { return organisationNom; }
public void setOrganisationNom(String organisationNom) { this.organisationNom = organisationNom; }
public Long getFormulaireId() { return formulaireId; }
public void setFormulaireId(Long formulaireId) { this.formulaireId = formulaireId; }
public UUID getFormulaireId() { return formulaireId; }
public void setFormulaireId(UUID formulaireId) { this.formulaireId = formulaireId; }
public String getFormulaireNom() { return formulaireNom; }
public void setFormulaireNom(String formulaireNom) { this.formulaireNom = formulaireNom; }

View File

@@ -2,6 +2,7 @@ package dev.lions.unionflow.client.dto.auth;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
public class LoginResponse {
@@ -79,7 +80,7 @@ public class LoginResponse {
}
public static class UserInfo {
private Long id;
private UUID id;
private String nom;
private String prenom;
private String email;
@@ -91,11 +92,11 @@ public class LoginResponse {
public UserInfo() {}
public Long getId() {
public UUID getId() {
return id;
}
public void setId(Long id) {
public void setId(UUID id) {
this.id = id;
}
@@ -172,7 +173,7 @@ public class LoginResponse {
}
public static class EntiteInfo {
private Long id;
private UUID id;
private String nom;
private String type;
private String pays;
@@ -180,11 +181,11 @@ public class LoginResponse {
public EntiteInfo() {}
public Long getId() {
public UUID getId() {
return id;
}
public void setId(Long id) {
public void setId(UUID id) {
this.id = id;
}

View File

@@ -1,7 +1,6 @@
package dev.lions.unionflow.client.exception;
import jakarta.faces.FacesException;
import jakarta.faces.application.NavigationHandler;
import jakarta.faces.application.ViewExpiredException;
import jakarta.faces.context.ExceptionHandler;
import jakarta.faces.context.ExceptionHandlerWrapper;
@@ -9,7 +8,6 @@ import jakarta.faces.context.FacesContext;
import jakarta.faces.event.ExceptionQueuedEvent;
import jakarta.faces.event.ExceptionQueuedEventContext;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -55,9 +53,9 @@ public class ViewExpiredExceptionHandler extends ExceptionHandlerWrapper {
LOG.log(Level.WARNING, "Impossible de stocker l'URL de redirection: {0}", e.getMessage());
}
// Rediriger vers la page de login avec paramètre expired
// Rediriger vers la racine qui déclenchera Keycloak
try {
String redirectURL = "/pages/public/login.xhtml?expired=true";
String redirectURL = "/";
facesContext.getExternalContext().redirect(
facesContext.getExternalContext().getRequestContextPath() + redirectURL
);
@@ -66,10 +64,10 @@ public class ViewExpiredExceptionHandler extends ExceptionHandlerWrapper {
LOG.log(Level.SEVERE, "Erreur lors de la redirection: {0}", e.getMessage());
// Fallback: essayer une redirection simple
try {
facesContext.getExternalContext().redirect("/pages/public/login.xhtml");
facesContext.getExternalContext().redirect("/");
facesContext.responseComplete();
} catch (Exception fallbackException) {
LOG.log(Level.SEVERE, "Impossible de rediriger vers login: {0}", fallbackException.getMessage());
LOG.log(Level.SEVERE, "Impossible de rediriger vers la racine: {0}", fallbackException.getMessage());
}
}
}

View File

@@ -9,6 +9,14 @@ import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.logging.Logger;
/**
* Filtre d'authentification pour vérifications supplémentaires
* Note: Avec Keycloak OIDC, l'authentification principale est gérée par Quarkus
* Ce filtre peut être utilisé pour des vérifications de permissions supplémentaires
*
* @author UnionFlow Team
* @version 2.0
*/
@WebFilter(urlPatterns = {"/pages/secure/*", "/pages/admin/*", "/pages/super-admin/*", "/pages/membre/*"})
public class AuthenticationFilter implements Filter {
@@ -17,9 +25,6 @@ public class AuthenticationFilter implements Filter {
@Inject
private UserSession userSession;
@Inject
private JwtTokenManager tokenManager;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
@@ -29,10 +34,13 @@ public class AuthenticationFilter implements Filter {
String requestURI = httpRequest.getRequestURI();
// Vérifier si l'utilisateur est authentifié
// Laisser Quarkus OIDC appliquer l'authentification (rediriger vers Keycloak si nécessaire)
// Ici, si l'utilisateur n'est pas encore authentifié, on ne force PAS une redirection custom
// pour éviter les boucles / conflits. On délègue au mécanisme Quarkus défini via
// quarkus.http.auth.permission.* et quarkus.oidc.*
if (!isAuthenticated()) {
LOGGER.warning("Accès non autorisé à: " + requestURI);
httpResponse.sendRedirect(httpRequest.getContextPath() + "/pages/public/login.xhtml");
LOGGER.fine("Requête non authentifiée sur " + requestURI + ", délégation à Quarkus OIDC.");
chain.doFilter(request, response);
return;
}
@@ -50,10 +58,8 @@ public class AuthenticationFilter implements Filter {
}
private boolean isAuthenticated() {
return userSession != null &&
userSession.isAuthenticated() &&
tokenManager != null &&
tokenManager.hasValidTokens();
// Avec Keycloak OIDC, UserSession vérifie automatiquement l'authentification via JsonWebToken
return userSession != null && userSession.isAuthenticated();
}
private boolean hasRequiredPermissions(String requestURI) {

View File

@@ -0,0 +1,50 @@
package dev.lions.unionflow.client.service;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.Map;
@RegisterRestClient(configKey = "unionflow-api")
@Path("/api/v1/analytics")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface AnalyticsService {
@GET
@Path("/dashboard/widgets")
Map<String, Object> getDashboardData(
@QueryParam("organisationId") String organisationId,
@QueryParam("utilisateurId") String utilisateurId
);
@GET
@Path("/tendances/{typeMetrique}")
Map<String, Object> getEvolutionMensuelle(
@PathParam("typeMetrique") String typeMetrique,
@QueryParam("organisationId") String organisationId,
@QueryParam("periode") String periode
);
@GET
@Path("/kpis")
Map<String, Object> getKPIs(
@QueryParam("organisationId") String organisationId,
@QueryParam("periode") String periode
);
@GET
@Path("/evolutions")
Map<String, Object> getEvolutions(
@QueryParam("organisationId") String organisationId,
@QueryParam("periode") String periode
);
@GET
@Path("/performance-globale")
Map<String, Object> getPerformanceGlobale(
@QueryParam("organisationId") String organisationId,
@QueryParam("periode") String periode
);
}

View File

@@ -5,6 +5,7 @@ import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.List;
import java.util.UUID;
@RegisterRestClient(configKey = "unionflow-api")
@Path("/api/associations")
@@ -17,7 +18,7 @@ public interface AssociationService {
@GET
@Path("/{id}")
AssociationDTO obtenirParId(@PathParam("id") Long id);
AssociationDTO obtenirParId(@PathParam("id") UUID id);
@GET
@Path("/search")
@@ -48,27 +49,27 @@ public interface AssociationService {
@PUT
@Path("/{id}")
AssociationDTO modifier(@PathParam("id") Long id, AssociationDTO association);
AssociationDTO modifier(@PathParam("id") UUID id, AssociationDTO association);
@DELETE
@Path("/{id}")
void supprimer(@PathParam("id") Long id);
void supprimer(@PathParam("id") UUID id);
@PUT
@Path("/{id}/activer")
AssociationDTO activer(@PathParam("id") Long id);
AssociationDTO activer(@PathParam("id") UUID id);
@PUT
@Path("/{id}/desactiver")
AssociationDTO desactiver(@PathParam("id") Long id);
AssociationDTO desactiver(@PathParam("id") UUID id);
@PUT
@Path("/{id}/suspendre")
AssociationDTO suspendre(@PathParam("id") Long id);
AssociationDTO suspendre(@PathParam("id") UUID id);
@PUT
@Path("/{id}/dissoudre")
AssociationDTO dissoudre(@PathParam("id") Long id);
AssociationDTO dissoudre(@PathParam("id") UUID id);
@GET
@Path("/statistiques")
@@ -76,11 +77,11 @@ public interface AssociationService {
@GET
@Path("/{id}/membres/count")
Long compterMembres(@PathParam("id") Long id);
Long compterMembres(@PathParam("id") UUID id);
@GET
@Path("/{id}/performance")
PerformanceAssociationDTO obtenirPerformance(@PathParam("id") Long id);
PerformanceAssociationDTO obtenirPerformance(@PathParam("id") UUID id);
// Classes DTO internes
class StatistiquesAssociationDTO {
@@ -127,7 +128,7 @@ public interface AssociationService {
}
class PerformanceAssociationDTO {
public Long associationId;
public UUID associationId;
public String nom;
public Integer scoreGlobal;
public Integer scoreMembres;
@@ -140,8 +141,8 @@ public interface AssociationService {
public PerformanceAssociationDTO() {}
// Getters et setters
public Long getAssociationId() { return associationId; }
public void setAssociationId(Long associationId) { this.associationId = associationId; }
public UUID getAssociationId() { return associationId; }
public void setAssociationId(UUID associationId) { this.associationId = associationId; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }

View File

@@ -9,6 +9,7 @@ import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import java.util.UUID;
import java.util.logging.Logger;
@ApplicationScoped
@@ -100,9 +101,15 @@ public class AuthenticationService {
private LoginResponse createDemoLoginResponse(LoginRequest request) {
LoginResponse.UserInfo userInfo = new LoginResponse.UserInfo();
// UUIDs fixes pour la démonstration (pour cohérence entre les sessions)
UUID superAdminId = UUID.fromString("00000000-0000-0000-0000-000000000001");
UUID adminId = UUID.fromString("00000000-0000-0000-0000-000000000002");
UUID membreId = UUID.fromString("00000000-0000-0000-0000-000000000003");
UUID entiteId = UUID.fromString("00000000-0000-0000-0000-000000000010");
switch (request.getUsername()) {
case "superadmin":
userInfo.setId(1L);
userInfo.setId(superAdminId);
userInfo.setNom("Diallo");
userInfo.setPrenom("Amadou");
userInfo.setEmail("amadou.diallo@unionflow.sn");
@@ -112,7 +119,7 @@ public class AuthenticationService {
break;
case "admin":
userInfo.setId(2L);
userInfo.setId(adminId);
userInfo.setNom("Traoré");
userInfo.setPrenom("Fatou");
userInfo.setEmail("fatou.traore@association-example.sn");
@@ -122,7 +129,7 @@ public class AuthenticationService {
// Entité de démonstration
LoginResponse.EntiteInfo entite = new LoginResponse.EntiteInfo();
entite.setId(1L);
entite.setId(entiteId);
entite.setNom("Association des Jeunes Entrepreneurs");
entite.setType("Association");
entite.setPays("Sénégal");
@@ -131,7 +138,7 @@ public class AuthenticationService {
break;
default:
userInfo.setId(3L);
userInfo.setId(membreId);
userInfo.setNom("Ndiaye");
userInfo.setPrenom("Moussa");
userInfo.setEmail("moussa.ndiaye@exemple.sn");
@@ -141,7 +148,7 @@ public class AuthenticationService {
// Entité de démonstration
LoginResponse.EntiteInfo entiteMembre = new LoginResponse.EntiteInfo();
entiteMembre.setId(1L);
entiteMembre.setId(entiteId);
entiteMembre.setNom("Association des Jeunes Entrepreneurs");
entiteMembre.setType("Association");
entiteMembre.setPays("Sénégal");
@@ -167,4 +174,4 @@ public class AuthenticationService {
super(message, cause);
}
}
}
}

View File

@@ -0,0 +1,46 @@
package dev.lions.unionflow.client.service;
import dev.lions.unionflow.client.dto.CotisationDTO;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.List;
import java.util.UUID;
@RegisterRestClient(configKey = "unionflow-api")
@Path("/api/cotisations")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface CotisationService {
@GET
List<CotisationDTO> listerToutes(
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("20") int size
);
@GET
@Path("/{id}")
CotisationDTO obtenirParId(@PathParam("id") UUID id);
@GET
@Path("/search")
List<CotisationDTO> rechercher(
@QueryParam("membreId") UUID membreId,
@QueryParam("statut") String statut,
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("20") int size
);
@POST
CotisationDTO creer(CotisationDTO cotisation);
@PUT
@Path("/{id}")
CotisationDTO modifier(@PathParam("id") UUID id, CotisationDTO cotisation);
@DELETE
@Path("/{id}")
void supprimer(@PathParam("id") UUID id);
}

View File

@@ -0,0 +1,55 @@
package dev.lions.unionflow.client.service;
import dev.lions.unionflow.client.dto.DemandeAideDTO;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.List;
import java.util.UUID;
@RegisterRestClient(configKey = "unionflow-api")
@Path("/api/demandes-aide")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface DemandeAideService {
@GET
List<DemandeAideDTO> listerToutes(
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("20") int size
);
@GET
@Path("/{id}")
DemandeAideDTO obtenirParId(@PathParam("id") UUID id);
@GET
@Path("/search")
List<DemandeAideDTO> rechercher(
@QueryParam("statut") String statut,
@QueryParam("type") String type,
@QueryParam("urgence") String urgence,
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("20") int size
);
@POST
DemandeAideDTO creer(DemandeAideDTO demande);
@PUT
@Path("/{id}")
DemandeAideDTO modifier(@PathParam("id") UUID id, DemandeAideDTO demande);
@DELETE
@Path("/{id}")
void supprimer(@PathParam("id") UUID id);
@PUT
@Path("/{id}/approuver")
DemandeAideDTO approuver(@PathParam("id") UUID id);
@PUT
@Path("/{id}/rejeter")
DemandeAideDTO rejeter(@PathParam("id") UUID id);
}

View File

@@ -0,0 +1,54 @@
package dev.lions.unionflow.client.service;
import dev.lions.unionflow.client.dto.EvenementDTO;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.List;
import java.util.UUID;
@RegisterRestClient(configKey = "unionflow-api")
@Path("/api/evenements")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface EvenementService {
@GET
List<EvenementDTO> listerTous(
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("20") int size
);
@GET
@Path("/{id}")
EvenementDTO obtenirParId(@PathParam("id") UUID id);
@GET
@Path("/search")
List<EvenementDTO> rechercher(
@QueryParam("titre") String titre,
@QueryParam("type") String type,
@QueryParam("statut") String statut,
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("20") int size
);
@GET
@Path("/a-venir")
List<EvenementDTO> listerAVenir(
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("20") int size
);
@POST
EvenementDTO creer(EvenementDTO evenement);
@PUT
@Path("/{id}")
EvenementDTO modifier(@PathParam("id") UUID id, EvenementDTO evenement);
@DELETE
@Path("/{id}")
void supprimer(@PathParam("id") UUID id);
}

View File

@@ -0,0 +1,50 @@
package dev.lions.unionflow.client.service;
import dev.lions.unionflow.client.dto.FormulaireDTO;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.List;
import java.util.UUID;
@RegisterRestClient(configKey = "unionflow-api")
@Path("/api/formulaires")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface FormulaireService {
@GET
List<FormulaireDTO> listerTous();
@GET
@Path("/{id}")
FormulaireDTO obtenirParId(@PathParam("id") UUID id);
@GET
@Path("/actifs")
List<FormulaireDTO> listerActifs();
@GET
@Path("/populaires")
List<FormulaireDTO> listerPopulaires();
@POST
FormulaireDTO creer(FormulaireDTO formulaire);
@PUT
@Path("/{id}")
FormulaireDTO modifier(@PathParam("id") UUID id, FormulaireDTO formulaire);
@DELETE
@Path("/{id}")
void supprimer(@PathParam("id") UUID id);
@PUT
@Path("/{id}/activer")
FormulaireDTO activer(@PathParam("id") UUID id);
@PUT
@Path("/{id}/desactiver")
FormulaireDTO desactiver(@PathParam("id") UUID id);
}

View File

@@ -5,6 +5,7 @@ import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.List;
import java.util.UUID;
@RegisterRestClient(configKey = "unionflow-api")
@Path("/api/membres")
@@ -17,7 +18,7 @@ public interface MembreService {
@GET
@Path("/{id}")
MembreDTO obtenirParId(@PathParam("id") Long id);
MembreDTO obtenirParId(@PathParam("id") UUID id);
@GET
@Path("/numero/{numeroMembre}")
@@ -31,14 +32,14 @@ public interface MembreService {
@QueryParam("email") String email,
@QueryParam("telephone") String telephone,
@QueryParam("statut") String statut,
@QueryParam("associationId") Long associationId,
@QueryParam("associationId") UUID associationId,
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("20") int size
);
@GET
@Path("/association/{associationId}")
List<MembreDTO> listerParAssociation(@PathParam("associationId") Long associationId);
List<MembreDTO> listerParAssociation(@PathParam("associationId") UUID associationId);
@GET
@Path("/actifs")
@@ -53,27 +54,27 @@ public interface MembreService {
@PUT
@Path("/{id}")
MembreDTO modifier(@PathParam("id") Long id, MembreDTO membre);
MembreDTO modifier(@PathParam("id") UUID id, MembreDTO membre);
@DELETE
@Path("/{id}")
void supprimer(@PathParam("id") Long id);
void supprimer(@PathParam("id") UUID id);
@PUT
@Path("/{id}/activer")
MembreDTO activer(@PathParam("id") Long id);
MembreDTO activer(@PathParam("id") UUID id);
@PUT
@Path("/{id}/desactiver")
MembreDTO desactiver(@PathParam("id") Long id);
MembreDTO desactiver(@PathParam("id") UUID id);
@PUT
@Path("/{id}/suspendre")
MembreDTO suspendre(@PathParam("id") Long id);
MembreDTO suspendre(@PathParam("id") UUID id);
@PUT
@Path("/{id}/radier")
MembreDTO radier(@PathParam("id") Long id);
MembreDTO radier(@PathParam("id") UUID id);
@GET
@Path("/statistiques")
@@ -84,7 +85,7 @@ public interface MembreService {
@Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
byte[] exporterExcel(
@QueryParam("format") @DefaultValue("EXCEL") String format,
@QueryParam("associationId") Long associationId,
@QueryParam("associationId") UUID associationId,
@QueryParam("statut") String statut
);
@@ -93,7 +94,7 @@ public interface MembreService {
@Consumes(MediaType.MULTIPART_FORM_DATA)
ResultatImportDTO importerDonnees(
@FormParam("file") java.io.InputStream fileInputStream,
@FormParam("associationId") Long associationId
@FormParam("associationId") UUID associationId
);
// Classes DTO internes pour les réponses spécialisées

View File

@@ -0,0 +1,46 @@
package dev.lions.unionflow.client.service;
import dev.lions.unionflow.client.dto.SouscriptionDTO;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.List;
import java.util.UUID;
@RegisterRestClient(configKey = "unionflow-api")
@Path("/api/souscriptions")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface SouscriptionService {
@GET
List<SouscriptionDTO> listerToutes(
@QueryParam("organisationId") UUID organisationId,
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("20") int size
);
@GET
@Path("/{id}")
SouscriptionDTO obtenirParId(@PathParam("id") UUID id);
@GET
@Path("/organisation/{organisationId}/active")
SouscriptionDTO obtenirActive(@PathParam("organisationId") UUID organisationId);
@POST
SouscriptionDTO creer(SouscriptionDTO souscription);
@PUT
@Path("/{id}")
SouscriptionDTO modifier(@PathParam("id") UUID id, SouscriptionDTO souscription);
@DELETE
@Path("/{id}")
void supprimer(@PathParam("id") UUID id);
@PUT
@Path("/{id}/renouveler")
SouscriptionDTO renouveler(@PathParam("id") UUID id);
}

View File

@@ -1,20 +1,30 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.FormulaireDTO;
import dev.lions.unionflow.client.dto.SouscriptionDTO;
import dev.lions.unionflow.client.service.FormulaireService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
@Named("adminFormulaireBean")
@SessionScoped
public class AdminFormulaireBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(AdminFormulaireBean.class.getName());
@Inject
@RestClient
private FormulaireService formulaireService;
private List<FormulaireDTO> formulaires;
private FormulaireDTO formulaireSelectionne;
@@ -27,106 +37,18 @@ public class AdminFormulaireBean implements Serializable {
private BigDecimal revenusFormulaires = BigDecimal.ZERO;
private String formulairePlusPopulaire = "";
public AdminFormulaireBean() {
@PostConstruct
public void init() {
initializeData();
}
private void initializeData() {
formulaires = new ArrayList<>();
// Formulaire STARTER
FormulaireDTO starter = new FormulaireDTO();
starter.setId(1L);
starter.setNom("Starter");
starter.setDescription("Parfait pour les petites associations débutantes");
starter.setQuotaMaxMembres(100);
starter.setPrixMensuel(new BigDecimal("2000"));
starter.setPrixAnnuel(new BigDecimal("20000"));
starter.setCouleurTheme("bg-blue-500");
starter.setIconeFormulaire("pi-star");
starter.setActif(true);
starter.setGestionMembres(true);
starter.setGestionCotisations(true);
starter.setNotificationsEmail(true);
starter.setDateCreation(LocalDateTime.now().minusMonths(6));
starter.setCreePar("Super Admin");
formulaires.add(starter);
// Formulaire STANDARD
FormulaireDTO standard = new FormulaireDTO();
standard.setId(2L);
standard.setNom("Standard");
standard.setDescription("Idéal pour les associations en croissance");
standard.setQuotaMaxMembres(200);
standard.setPrixMensuel(new BigDecimal("3000"));
standard.setPrixAnnuel(new BigDecimal("30000"));
standard.setCouleurTheme("bg-green-500");
standard.setIconeFormulaire("pi-users");
standard.setActif(true);
standard.setRecommande(true);
standard.setGestionMembres(true);
standard.setGestionCotisations(true);
standard.setGestionEvenements(true);
standard.setGestionAides(true);
standard.setNotificationsEmail(true);
standard.setGestionDocuments(true);
standard.setDateCreation(LocalDateTime.now().minusMonths(6));
standard.setCreePar("Super Admin");
formulaires.add(standard);
// Formulaire PREMIUM
FormulaireDTO premium = new FormulaireDTO();
premium.setId(3L);
premium.setNom("Premium");
premium.setDescription("Solution complète pour les grandes organisations");
premium.setQuotaMaxMembres(500);
premium.setPrixMensuel(new BigDecimal("4000"));
premium.setPrixAnnuel(new BigDecimal("40000"));
premium.setCouleurTheme("bg-purple-500");
premium.setIconeFormulaire("pi-crown");
premium.setActif(true);
premium.setGestionMembres(true);
premium.setGestionCotisations(true);
premium.setGestionEvenements(true);
premium.setGestionAides(true);
premium.setRapportsAvances(true);
premium.setSupportPrioritaire(true);
premium.setSauvegardeAutomatique(true);
premium.setPersonnalisationAvancee(true);
premium.setIntegrationPaiement(true);
premium.setNotificationsEmail(true);
premium.setNotificationsSMS(true);
premium.setGestionDocuments(true);
premium.setDateCreation(LocalDateTime.now().minusMonths(6));
premium.setCreePar("Super Admin");
formulaires.add(premium);
// Formulaire CRISTAL - Pour les très grandes organisations
FormulaireDTO cristal = new FormulaireDTO();
cristal.setId(4L);
cristal.setNom("Cristal");
cristal.setDescription("Solution premium pour les fédérations et grandes entités");
cristal.setQuotaMaxMembres(2000);
cristal.setPrixMensuel(new BigDecimal("5000"));
cristal.setPrixAnnuel(new BigDecimal("50000"));
cristal.setCouleurTheme("bg-indigo-500");
cristal.setIconeFormulaire("pi-diamond");
cristal.setActif(true);
cristal.setGestionMembres(true);
cristal.setGestionCotisations(true);
cristal.setGestionEvenements(true);
cristal.setGestionAides(true);
cristal.setRapportsAvances(true);
cristal.setSupportPrioritaire(true);
cristal.setSauvegardeAutomatique(true);
cristal.setPersonnalisationAvancee(true);
cristal.setIntegrationPaiement(true);
cristal.setNotificationsEmail(true);
cristal.setNotificationsSMS(true);
cristal.setGestionDocuments(true);
cristal.setDateCreation(LocalDateTime.now().minusMonths(6));
cristal.setCreePar("Super Admin");
formulaires.add(cristal);
try {
formulaires = formulaireService.listerTous();
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des formulaires: " + e.getMessage());
}
// Initialiser les statistiques
totalSouscriptions = 127; // Plus d'entités avec prix accessibles
@@ -154,12 +76,8 @@ public class AdminFormulaireBean implements Serializable {
public void sauvegarderFormulaire() {
if (modeCreation) {
// Générer un nouvel ID
Long nouvelId = formulaires.stream()
.mapToLong(FormulaireDTO::getId)
.max()
.orElse(0L) + 1;
nouveauFormulaire.setId(nouvelId);
// Générer un nouvel ID UUID
nouveauFormulaire.setId(UUID.randomUUID());
nouveauFormulaire.setDateCreation(LocalDateTime.now());
nouveauFormulaire.setCreePar("Admin");
@@ -197,7 +115,7 @@ public class AdminFormulaireBean implements Serializable {
public void dupliquerFormulaire(FormulaireDTO formulaire) {
FormulaireDTO copie = cloneFormulaire(formulaire);
copie.setId(null);
copie.setId(UUID.randomUUID());
copie.setNom(formulaire.getNom() + " (Copie)");
copie.setDateCreation(LocalDateTime.now());
copie.setCreePar("Admin");

View File

@@ -5,14 +5,15 @@ import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.logging.Logger;
@Named("configurationBean")
@SessionScoped
public class ConfigurationBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(ConfigurationBean.class.getName());
private ConfigurationGenerale general;
private ConfigurationSecurite securite;
@@ -178,84 +179,85 @@ public class ConfigurationBean implements Serializable {
// Actions générales
public void sauvegarderTout() {
System.out.println("Configuration complète sauvegardée à " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")));
LOGGER.info("Configuration complète sauvegardée à " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")));
}
public void reinitialiser() {
init();
System.out.println("Configuration réinitialisée aux valeurs par défaut");
LOGGER.info("Configuration réinitialisée aux valeurs par défaut");
}
public void exporterConfiguration() {
System.out.println("Export de la configuration généré");
LOGGER.info("Export de la configuration généré");
}
// Actions par section
public void sauvegarderGeneral() {
System.out.println("Configuration générale sauvegardée");
LOGGER.info("Configuration générale sauvegardée");
}
public void sauvegarderSecurite() {
System.out.println("Configuration sécurité sauvegardée");
LOGGER.info("Configuration sécurité sauvegardée");
}
public void sauvegarderEmail() {
System.out.println("Configuration email sauvegardée");
LOGGER.info("Configuration email sauvegardée");
}
// Actions pour la page système
public void sauvegarderConfiguration() {
System.out.println("Configuration système sauvegardée");
LOGGER.info("Configuration système sauvegardée");
}
public void restaurerDefauts() {
nomApplication = "UnionFlow";
versionSysteme = "1.0.0";
environnement = "DEV";
System.out.println("Configuration système restaurée aux valeurs par défaut");
LOGGER.info("Configuration système restaurée aux valeurs par défaut");
}
public void testerConnexionBDD() {
System.out.println("Test de connexion BDD: " + typeBDD + "://" + serveurBDD + ":" + portBDD + "/" + nomBDD);
// TODO: Implémenter le test réel
// Le test de connexion BDD sera implémenté via l'API backend
// Pour l'instant, log uniquement
LOGGER.info("Test de connexion BDD: " + typeBDD + "://" + serveurBDD + ":" + portBDD + "/" + nomBDD);
}
public void testerEmail() {
System.out.println("Test d'envoi d'email via " + serveurSMTP + ":" + portSMTP);
// TODO: Implémenter le test réel
System.out.println("Test email envoyé avec succès");
// Le test d'email sera implémenté via l'API backend
// Pour l'instant, log uniquement
LOGGER.info("Test d'envoi d'email via " + serveurSMTP + ":" + portSMTP);
}
public void forcerSauvegarde() {
System.out.println("Sauvegarde forcée du système");
// La sauvegarde sera déclenchée via l'API backend
LOGGER.info("Sauvegarde forcée du système");
derniereSauvegarde = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm"));
// TODO: Déclencher une sauvegarde immédiate
}
public void redemarrerServices() {
System.out.println("Redémarrage des services système en cours...");
// TODO: Redémarrer les services critiques
// Le redémarrage des services sera géré via l'API backend
LOGGER.info("Redémarrage des services système en cours...");
}
public void sauvegarderPaiements() {
System.out.println("Configuration paiements sauvegardée");
LOGGER.info("Configuration paiements sauvegardée");
}
public void sauvegarderSysteme() {
System.out.println("Configuration système sauvegardée");
LOGGER.info("Configuration système sauvegardée");
}
// Actions système
public void viderCache() {
System.out.println("Cache vidé avec succès");
LOGGER.info("Cache vidé avec succès");
}
public void optimiserBaseDonnees() {
System.out.println("Optimisation de la base de données en cours...");
LOGGER.info("Optimisation de la base de données en cours...");
}
public void sauvegarderBaseDonnees() {
System.out.println("Sauvegarde de la base de données initiée");
LOGGER.info("Sauvegarde de la base de données initiée");
}
public String voirLogsSysteme() {
@@ -665,35 +667,35 @@ public class ConfigurationBean implements Serializable {
// Méthodes d'actions
public void actualiserMonitoring() {
calculerMetriquesSysteme();
System.out.println("Monitoring actualisé");
LOGGER.info("Monitoring actualisé");
}
public void nettoyerCache() {
System.out.println("Cache système nettoyé");
LOGGER.info("Cache système nettoyé");
}
public void auditSysteme() {
System.out.println("Audit système lancé");
LOGGER.info("Audit système lancé");
}
public void appliquerConfigGenerale() {
System.out.println("Configuration générale appliquée");
LOGGER.info("Configuration générale appliquée");
}
public void appliquerConfigBDD() {
System.out.println("Configuration BDD appliquée");
LOGGER.info("Configuration BDD appliquée");
}
public void appliquerConfigEmail() {
System.out.println("Configuration email appliquée");
LOGGER.info("Configuration email appliquée");
}
public void appliquerConfigSecurite() {
System.out.println("Configuration sécurité appliquée");
LOGGER.info("Configuration sécurité appliquée");
}
public void sauvegarderAlertes() {
System.out.println("Configuration des alertes sauvegardée");
LOGGER.info("Configuration des alertes sauvegardée");
}
public static class ConfigurationSysteme {

View File

@@ -1,8 +1,12 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.CotisationDTO;
import dev.lions.unionflow.client.service.CotisationService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -10,14 +14,21 @@ import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.math.BigDecimal;
import java.util.logging.Logger;
@Named("cotisationsBean")
@SessionScoped
public class CotisationsBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(CotisationsBean.class.getName());
@Inject
@RestClient
private CotisationService cotisationService;
private List<Cotisation> toutesLesCotisations;
private List<Cotisation> cotisationsFiltrees;
@@ -33,8 +44,8 @@ public class CotisationsBean implements Serializable {
@PostConstruct
public void init() {
initializeFiltres();
initializeStatistiques();
initializeCotisations();
initializeStatistiques();
initializeNouvelleCotisation();
initializeEvolutionPaiements();
initializeRepartitionMethodes();
@@ -49,12 +60,40 @@ public class CotisationsBean implements Serializable {
private void initializeStatistiques() {
statistiques = new StatistiquesFinancieres();
statistiques.setTotalCollecte(new BigDecimal("45750000"));
statistiques.setObjectifAnnuel(new BigDecimal("60000000"));
statistiques.setTauxRecouvrement(76.25);
statistiques.setCotisationsEnRetard(23);
statistiques.setMontantRetard(new BigDecimal("3850000"));
statistiques.setMoyenneMensuelle(new BigDecimal("3812500"));
try {
List<CotisationDTO> cotisationsDTO = cotisationService.listerToutes(0, 1000);
BigDecimal totalCollecte = cotisationsDTO.stream()
.filter(c -> "PAYEE".equals(c.getStatut()) || "PARTIELLEMENT_PAYEE".equals(c.getStatut()))
.map(c -> c.getMontantPaye() != null ? c.getMontantPaye() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
statistiques.setTotalCollecte(totalCollecte);
statistiques.setObjectifAnnuel(totalCollecte.multiply(new BigDecimal("1.3")));
long total = cotisationsDTO.size();
long payees = cotisationsDTO.stream().filter(c -> "PAYEE".equals(c.getStatut())).count();
double tauxRecouvrement = total > 0 ? (double) payees / total * 100.0 : 0.0;
statistiques.setTauxRecouvrement(tauxRecouvrement);
long enRetard = cotisationsDTO.stream().filter(c -> "EN_RETARD".equals(c.getStatut())).count();
statistiques.setCotisationsEnRetard((int) enRetard);
BigDecimal montantRetard = cotisationsDTO.stream()
.filter(c -> "EN_RETARD".equals(c.getStatut()))
.map(c -> c.getMontantDu() != null ? c.getMontantDu() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
statistiques.setMontantRetard(montantRetard);
BigDecimal moyenneMensuelle = totalCollecte.divide(new BigDecimal("12"), 2, java.math.RoundingMode.HALF_UP);
statistiques.setMoyenneMensuelle(moyenneMensuelle);
} catch (Exception e) {
LOGGER.severe("Erreur lors du calcul des statistiques: " + e.getMessage());
statistiques.setTotalCollecte(BigDecimal.ZERO);
statistiques.setObjectifAnnuel(BigDecimal.ZERO);
statistiques.setTauxRecouvrement(0.0);
statistiques.setCotisationsEnRetard(0);
statistiques.setMontantRetard(BigDecimal.ZERO);
statistiques.setMoyenneMensuelle(BigDecimal.ZERO);
}
}
private void initializeEvolutionPaiements() {
@@ -142,65 +181,34 @@ public class CotisationsBean implements Serializable {
private void initializeCotisations() {
toutesLesCotisations = new ArrayList<>();
String[] membres = {
"Jean Kouassi", "Marie Traoré", "Ahmed Diallo", "Fatou Sanogo", "Paul Ouattara",
"Aissata Koné", "Ibrahim Touré", "Aminata Bakayoko", "Yves Koffi", "Mariam Coulibaly",
"Seydou Cissé", "Adjoa Mensah", "Kwame Asante", "Ama Gyamfi", "Kofi Adjei"
};
String[] clubs = {
"Association Alpha", "Club Beta", "Groupe Gamma",
"Association Delta", "Club Epsilon", "Groupe Zeta",
"Association Eta", "Club Theta", "Groupe Iota"
};
String[] types = {"MENSUELLE", "TRIMESTRIELLE", "ANNUELLE", "EXCEPTIONNELLE"};
String[] statuts = {"PAYEE", "EN_ATTENTE", "EN_RETARD", "PARTIELLEMENT_PAYEE"};
String[] methodes = {"WAVE_MONEY", "ESPECES", "CHEQUE", "VIREMENT"};
BigDecimal[] montants = {
new BigDecimal("150000"), new BigDecimal("125000"), new BigDecimal("100000"),
new BigDecimal("175000"), new BigDecimal("200000"), new BigDecimal("75000"),
new BigDecimal("120000"), new BigDecimal("180000"), new BigDecimal("95000")
};
for (int i = 0; i < 50; i++) {
Cotisation cotisation = new Cotisation();
cotisation.setId((long) (i + 1));
cotisation.setNumeroMembre("M" + String.format("%04d", (i % membres.length) + 1));
cotisation.setNomMembre(membres[i % membres.length]);
cotisation.setClub(clubs[i % clubs.length]);
cotisation.setTypeCotisation(types[i % types.length]);
cotisation.setMontantDu(montants[i % montants.length]);
if (i % 5 == 0) {
cotisation.setStatut("EN_RETARD");
cotisation.setMontantPaye(BigDecimal.ZERO);
} else if (i % 8 == 0) {
cotisation.setStatut("PARTIELLEMENT_PAYEE");
cotisation.setMontantPaye(cotisation.getMontantDu().multiply(new BigDecimal("0.5")));
} else if (i % 12 == 0) {
cotisation.setStatut("EN_ATTENTE");
cotisation.setMontantPaye(BigDecimal.ZERO);
} else {
cotisation.setStatut("PAYEE");
cotisation.setMontantPaye(cotisation.getMontantDu());
try {
List<CotisationDTO> cotisationsDTO = cotisationService.listerToutes(0, 1000);
for (CotisationDTO dto : cotisationsDTO) {
Cotisation cotisation = convertToCotisation(dto);
toutesLesCotisations.add(cotisation);
}
cotisation.setMethodePaiement(methodes[i % methodes.length]);
cotisation.setDateEcheance(LocalDate.now().minusDays(i * 2).plusMonths(1));
if (!cotisation.getStatut().equals("EN_ATTENTE")) {
cotisation.setDatePaiement(LocalDateTime.now().minusDays(i + 1));
}
cotisation.setObservations(i % 7 == 0 ? "Paiement en plusieurs fois autorisé" : "");
toutesLesCotisations.add(cotisation);
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des cotisations: " + e.getMessage());
}
}
private Cotisation convertToCotisation(CotisationDTO dto) {
Cotisation cotisation = new Cotisation();
cotisation.setId(dto.getId());
cotisation.setNumeroMembre(dto.getNumeroMembre());
cotisation.setNomMembre(dto.getNomMembre());
cotisation.setClub(dto.getNomAssociation());
cotisation.setTypeCotisation(dto.getTypeCotisation());
cotisation.setMontantDu(dto.getMontantDu());
cotisation.setMontantPaye(dto.getMontantPaye());
cotisation.setStatut(dto.getStatut());
cotisation.setMethodePaiement(dto.getMethodePaiement());
cotisation.setDateEcheance(dto.getDateEcheance());
cotisation.setDatePaiement(dto.getDatePaiement());
cotisation.setObservations(dto.getObservations());
return cotisation;
}
private void initializeNouvelleCotisation() {
nouvelleCotisation = new NouvelleCotisation();
}
@@ -269,7 +277,7 @@ public class CotisationsBean implements Serializable {
public void enregistrerCotisation() {
Cotisation nouvelleCot = new Cotisation();
nouvelleCot.setId((long) (toutesLesCotisations.size() + 1));
nouvelleCot.setId(UUID.randomUUID());
nouvelleCot.setNumeroMembre(nouvelleCotisation.getNumeroMembre());
nouvelleCot.setNomMembre(nouvelleCotisation.getNomMembre());
nouvelleCot.setClub(nouvelleCotisation.getClub());
@@ -283,7 +291,7 @@ public class CotisationsBean implements Serializable {
toutesLesCotisations.add(nouvelleCot);
appliquerFiltres();
System.out.println("Nouvelle cotisation enregistrée pour: " + nouvelleCot.getNomMembre());
LOGGER.info("Nouvelle cotisation enregistrée pour: " + nouvelleCot.getNomMembre());
initializeNouvelleCotisation();
}
@@ -292,7 +300,7 @@ public class CotisationsBean implements Serializable {
cotisationSelectionnee.setStatut("PAYEE");
cotisationSelectionnee.setMontantPaye(cotisationSelectionnee.getMontantDu());
cotisationSelectionnee.setDatePaiement(LocalDateTime.now());
System.out.println("Cotisation marquée comme payée: " + cotisationSelectionnee.getNomMembre());
LOGGER.info("Cotisation marquée comme payée: " + cotisationSelectionnee.getNomMembre());
}
}
@@ -300,26 +308,26 @@ public class CotisationsBean implements Serializable {
if (cotisationSelectionnee != null) {
cotisationSelectionnee.setStatut("PARTIELLEMENT_PAYEE");
cotisationSelectionnee.setDatePaiement(LocalDateTime.now());
System.out.println("Paiement partiel enregistré: " + cotisationSelectionnee.getNomMembre());
LOGGER.info("Paiement partiel enregistré: " + cotisationSelectionnee.getNomMembre());
}
}
public void envoyerRappel() {
if (cotisationSelectionnee != null) {
System.out.println("Rappel envoyé à: " + cotisationSelectionnee.getNomMembre());
LOGGER.info("Rappel envoyé à: " + cotisationSelectionnee.getNomMembre());
}
}
public void envoyerRappelsGroupes() {
System.out.println("Rappels envoyés à " + cotisationsSelectionnees.size() + " membres");
LOGGER.info("Rappels envoyés à " + cotisationsSelectionnees.size() + " membres");
}
public void exporterCotisations() {
System.out.println("Export de " + cotisationsFiltrees.size() + " cotisations");
LOGGER.info("Export de " + cotisationsFiltrees.size() + " cotisations");
}
public void genererRapportFinancier() {
System.out.println("Rapport financier généré");
LOGGER.info("Rapport financier généré");
}
// Getters et Setters
@@ -355,7 +363,7 @@ public class CotisationsBean implements Serializable {
// Classes internes
public static class Cotisation {
private Long id;
private UUID id;
private String numeroMembre;
private String nomMembre;
private String club;
@@ -369,8 +377,8 @@ public class CotisationsBean implements Serializable {
private String observations;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNumeroMembre() { return numeroMembre; }
public void setNumeroMembre(String numeroMembre) { this.numeroMembre = numeroMembre; }
@@ -474,7 +482,7 @@ public class CotisationsBean implements Serializable {
public int getPourcentagePaye() {
if (montantDu.equals(BigDecimal.ZERO)) return 0;
return montantPaye.multiply(new BigDecimal("100")).divide(montantDu, 0, BigDecimal.ROUND_HALF_UP).intValue();
return montantPaye.multiply(new BigDecimal("100")).divide(montantDu, 0, java.math.RoundingMode.HALF_UP).intValue();
}
public long getJoursRetard() {

View File

@@ -1,21 +1,38 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.CotisationDTO;
import dev.lions.unionflow.client.dto.AssociationDTO;
import dev.lions.unionflow.client.service.CotisationService;
import dev.lions.unionflow.client.service.AssociationService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.logging.Logger;
@Named("cotisationsGestionBean")
@SessionScoped
public class CotisationsGestionBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(CotisationsGestionBean.class.getName());
@Inject
@RestClient
private CotisationService cotisationService;
@Inject
@RestClient
private AssociationService associationService;
// Propriétés principales
private String periodeActuelle;
@@ -71,95 +88,157 @@ public class CotisationsGestionBean implements Serializable {
}
private void initializeKPIs() {
this.periodeActuelle = "Décembre 2024";
this.tauxRecouvrement = new BigDecimal("87.3");
this.totalMembresActifs = 18547;
this.montantCollecte = "2,845,000 FCFA";
this.objectifMensuel = "3,200,000 FCFA";
this.progressionMensuelle = 89;
this.membresAJour = "16,205";
this.pourcentageMembresAJour = 87;
this.montantEnAttente = "785,000 FCFA";
this.nombreCotisationsEnAttente = 1247;
this.montantImpayes = "425,000 FCFA";
this.joursRetardMoyen = 12;
this.revenus2024 = "34,200,000 FCFA";
this.croissanceAnnuelle = "+15.3%";
this.prelevementsActifs = "8,450";
this.montantPrelevementsPrevu = "42,250,000";
this.membresPrelevementActif = 8450;
this.montantPrelevementMensuel = "42,250,000 FCFA";
this.prochainPrelevement = "01/01/2025";
try {
List<CotisationDTO> cotisationsDTO = cotisationService.listerToutes(0, 1000);
BigDecimal totalCollecte = cotisationsDTO.stream()
.filter(c -> "PAYEE".equals(c.getStatut()) || "PARTIELLEMENT_PAYEE".equals(c.getStatut()))
.map(c -> c.getMontantPaye() != null ? c.getMontantPaye() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
long total = cotisationsDTO.size();
long payees = cotisationsDTO.stream().filter(c -> "PAYEE".equals(c.getStatut())).count();
double taux = total > 0 ? (double) payees / total * 100.0 : 0.0;
long enAttente = cotisationsDTO.stream().filter(c -> "EN_ATTENTE".equals(c.getStatut())).count();
BigDecimal montantAttente = cotisationsDTO.stream()
.filter(c -> "EN_ATTENTE".equals(c.getStatut()))
.map(c -> c.getMontantDu() != null ? c.getMontantDu() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
long enRetard = cotisationsDTO.stream().filter(c -> "EN_RETARD".equals(c.getStatut())).count();
BigDecimal montantImpayes = cotisationsDTO.stream()
.filter(c -> "EN_RETARD".equals(c.getStatut()))
.map(c -> c.getMontantDu() != null ? c.getMontantDu() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
this.periodeActuelle = LocalDate.now().format(DateTimeFormatter.ofPattern("MMMM yyyy"));
this.tauxRecouvrement = new BigDecimal(String.format("%.1f", taux));
this.totalMembresActifs = (int) total;
this.montantCollecte = formatMontant(totalCollecte);
this.objectifMensuel = formatMontant(totalCollecte.multiply(new BigDecimal("1.15")));
this.progressionMensuelle = (int) (taux);
this.membresAJour = String.valueOf(payees);
this.pourcentageMembresAJour = (int) taux;
this.montantEnAttente = formatMontant(montantAttente);
this.nombreCotisationsEnAttente = (int) enAttente;
this.montantImpayes = formatMontant(montantImpayes);
this.joursRetardMoyen = 12; // À calculer depuis les dates
this.revenus2024 = formatMontant(totalCollecte.multiply(new BigDecimal("12")));
this.croissanceAnnuelle = "+15.3%"; // À calculer
this.prelevementsActifs = "0";
this.montantPrelevementsPrevu = "0";
this.membresPrelevementActif = 0;
this.montantPrelevementMensuel = "0 FCFA";
this.prochainPrelevement = LocalDate.now().plusMonths(1).format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
} catch (Exception e) {
LOGGER.severe("Erreur lors du calcul des KPIs: " + e.getMessage());
this.periodeActuelle = LocalDate.now().format(DateTimeFormatter.ofPattern("MMMM yyyy"));
this.tauxRecouvrement = BigDecimal.ZERO;
this.totalMembresActifs = 0;
this.montantCollecte = "0 FCFA";
this.objectifMensuel = "0 FCFA";
this.progressionMensuelle = 0;
this.membresAJour = "0";
this.pourcentageMembresAJour = 0;
this.montantEnAttente = "0 FCFA";
this.nombreCotisationsEnAttente = 0;
this.montantImpayes = "0 FCFA";
this.joursRetardMoyen = 0;
this.revenus2024 = "0 FCFA";
this.croissanceAnnuelle = "0%";
this.prelevementsActifs = "0";
this.montantPrelevementsPrevu = "0";
this.membresPrelevementActif = 0;
this.montantPrelevementMensuel = "0 FCFA";
this.prochainPrelevement = LocalDate.now().plusMonths(1).format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
}
this.cotisationsSelectionnees = new ArrayList<>();
this.montantTotalSelectionne = "0 FCFA";
}
private String formatMontant(BigDecimal montant) {
if (montant == null) return "0 FCFA";
return String.format("%,.0f FCFA", montant.doubleValue());
}
private void initializeFiltres() {
this.filtres = new FiltresCotisations();
this.listeOrganisations = new ArrayList<>();
// Simulation d'organisations
for (int i = 1; i <= 127; i++) {
Organisation org = new Organisation();
org.setId((long) i);
org.setNom("Organisation " + i);
listeOrganisations.add(org);
try {
List<AssociationDTO> associations = associationService.listerToutes();
for (AssociationDTO assoc : associations) {
Organisation org = new Organisation();
org.setId(assoc.getId());
org.setNom(assoc.getNom());
listeOrganisations.add(org);
}
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des organisations: " + e.getMessage());
}
}
private void initializeData() {
this.cotisationsFiltrees = new ArrayList<>();
// Simulation de données de cotisations pour les 127 organisations
for (int i = 1; i <= 500; i++) {
CotisationAdmin cotisation = new CotisationAdmin();
cotisation.setId((long) i);
cotisation.setNomOrganisation("Organisation " + ((i % 127) + 1));
cotisation.setRegionOrganisation("Région " + ((i % 12) + 1));
cotisation.setIconeOrganisation("pi-building");
cotisation.setNomCompletMembre("Membre " + i);
cotisation.setNumeroMembre("M" + String.format("%06d", i));
cotisation.setInitialesMembre(getInitiales("Membre " + i));
cotisation.setTypeMembre(i % 3 == 0 ? "Membre Actif" : "Membre Associé");
cotisation.setType(getTypeCotisation(i));
cotisation.setPeriode(getperiode(i));
cotisation.setAnnee("2024");
cotisation.setMontant(new BigDecimal(getMontantCotisation(i)));
cotisation.setStatut(getStatutCotisation(i));
cotisation.setDateEcheance(LocalDate.now().minusDays(i % 60));
if (cotisation.getStatut().equals("PAYE")) {
cotisation.setDatePaiement(cotisation.getDateEcheance().plusDays(i % 10));
cotisation.setModePaiement(getModePaiement(i));
try {
List<CotisationDTO> cotisationsDTO = cotisationService.listerToutes(0, 1000);
for (CotisationDTO dto : cotisationsDTO) {
CotisationAdmin cotisation = convertToCotisationAdmin(dto);
cotisationsFiltrees.add(cotisation);
}
cotisationsFiltrees.add(cotisation);
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des cotisations: " + e.getMessage());
}
}
private CotisationAdmin convertToCotisationAdmin(CotisationDTO dto) {
CotisationAdmin cotisation = new CotisationAdmin();
cotisation.setId(dto.getId());
cotisation.setNomOrganisation(dto.getNomAssociation());
cotisation.setNomCompletMembre(dto.getNomMembre());
cotisation.setNumeroMembre(dto.getNumeroMembre());
cotisation.setType(dto.getTypeCotisation());
cotisation.setMontant(dto.getMontantDu());
cotisation.setStatut(dto.getStatut());
cotisation.setDateEcheance(dto.getDateEcheance());
cotisation.setDatePaiement(dto.getDatePaiement() != null ? dto.getDatePaiement().toLocalDate() : null);
cotisation.setModePaiement(dto.getMethodePaiement());
if (dto.getNomMembre() != null && !dto.getNomMembre().isEmpty()) {
cotisation.setInitialesMembre(getInitiales(dto.getNomMembre()));
}
return cotisation;
}
private void initializeTopOrganisations() {
this.topOrganisations = new ArrayList<>();
String[] noms = {"Lions Club Dakar Centre", "Association Thiès Nord", "Groupe Kaolack Est",
"Lions Club Saint-Louis", "Association Louga Centre"};
int[] taux = {98, 95, 92, 89, 87};
String[] montants = {"485K", "420K", "380K", "365K", "340K"};
int[] membres = {156, 134, 128, 145, 120};
int[] totaux = {160, 145, 140, 165, 135};
for (int i = 0; i < noms.length; i++) {
OrganisationPerformante org = new OrganisationPerformante();
org.setNom(noms[i]);
org.setTauxRecouvrement(taux[i]);
org.setMontantCollecte(montants[i]);
org.setNombreMembresAJour(membres[i]);
org.setTotalMembres(totaux[i]);
topOrganisations.add(org);
try {
List<AssociationDTO> associations = associationService.listerActives();
List<CotisationDTO> cotisationsDTO = cotisationService.listerToutes(0, 1000);
for (AssociationDTO assoc : associations.stream().limit(5).collect(Collectors.toList())) {
List<CotisationDTO> cotisationsOrg = cotisationsDTO.stream()
.filter(c -> c.getAssociationId() != null && c.getAssociationId().equals(assoc.getId()))
.collect(Collectors.toList());
long total = cotisationsOrg.size();
long payees = cotisationsOrg.stream().filter(c -> "PAYEE".equals(c.getStatut())).count();
int taux = total > 0 ? (int) ((double) payees / total * 100.0) : 0;
BigDecimal montantCollecte = cotisationsOrg.stream()
.filter(c -> "PAYEE".equals(c.getStatut()) || "PARTIELLEMENT_PAYEE".equals(c.getStatut()))
.map(c -> c.getMontantPaye() != null ? c.getMontantPaye() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
OrganisationPerformante org = new OrganisationPerformante();
org.setNom(assoc.getNom());
org.setTauxRecouvrement(taux);
org.setMontantCollecte(formatMontant(montantCollecte));
org.setNombreMembresAJour((int) payees);
org.setTotalMembres((int) total);
topOrganisations.add(org);
}
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des top organisations: " + e.getMessage());
}
}
@@ -217,95 +296,95 @@ public class CotisationsGestionBean implements Serializable {
// Actions principales
public void creerCampagne() {
System.out.println("Création de la campagne: " + nouvelleCampagne.getNom());
LOGGER.info("Création de la campagne: " + nouvelleCampagne.getNom());
// Logique de création de campagne
initializeNouvelleCampagne(); // Reset pour nouvelle campagne
}
public void relancesGroupees() {
System.out.println("Envoi de relances groupées");
LOGGER.info("Envoi de relances groupées");
}
public void exporterTout() {
System.out.println("Export global des cotisations");
LOGGER.info("Export global des cotisations");
}
public void appliquerFiltres() {
// Logique de filtrage
System.out.println("Application des filtres");
LOGGER.info("Application des filtres");
}
public void reinitialiserFiltres() {
this.filtres = new FiltresCotisations();
System.out.println("Réinitialisation des filtres");
LOGGER.info("Réinitialisation des filtres");
}
public void exporterExcel() {
System.out.println("Export Excel des cotisations filtrées");
LOGGER.info("Export Excel des cotisations filtrées");
}
// Actions sur cotisations individuelles
public void enregistrerPaiement(CotisationAdmin cotisation) {
System.out.println("Enregistrement paiement pour: " + cotisation.getNumeroMembre());
LOGGER.info("Enregistrement paiement pour: " + cotisation.getNumeroMembre());
}
public void genererRecu(CotisationAdmin cotisation) {
System.out.println("Génération reçu pour: " + cotisation.getNumeroMembre());
LOGGER.info("Génération reçu pour: " + cotisation.getNumeroMembre());
}
public void envoyerRappel(CotisationAdmin cotisation) {
System.out.println("Envoi rappel pour: " + cotisation.getNumeroMembre());
LOGGER.info("Envoi rappel pour: " + cotisation.getNumeroMembre());
}
public void voirDetails(CotisationAdmin cotisation) {
System.out.println("Affichage détails pour: " + cotisation.getNumeroMembre());
LOGGER.info("Affichage détails pour: " + cotisation.getNumeroMembre());
}
// Actions groupées
public void marquerPayeesGroupees() {
System.out.println("Marquage " + cotisationsSelectionnees.size() + " cotisations comme payées");
LOGGER.info("Marquage " + cotisationsSelectionnees.size() + " cotisations comme payées");
}
public void envoyerRelancesGroupees() {
System.out.println("Envoi relances pour " + cotisationsSelectionnees.size() + " cotisations");
LOGGER.info("Envoi relances pour " + cotisationsSelectionnees.size() + " cotisations");
}
public void genererRecusGroupes() {
System.out.println("Génération reçus pour " + cotisationsSelectionnees.size() + " cotisations");
LOGGER.info("Génération reçus pour " + cotisationsSelectionnees.size() + " cotisations");
}
public void annulerCotisationsGroupees() {
System.out.println("Annulation " + cotisationsSelectionnees.size() + " cotisations");
LOGGER.info("Annulation " + cotisationsSelectionnees.size() + " cotisations");
}
// Wave Money
public void lancerPrelevements() {
System.out.println("Lancement des prélèvements Wave Money");
LOGGER.info("Lancement des prélèvements Wave Money");
}
public void testerAPIWave() {
System.out.println("Test de l'API Wave Money");
LOGGER.info("Test de l'API Wave Money");
}
public void voirHistoriquePrelevements() {
System.out.println("Affichage historique des prélèvements");
LOGGER.info("Affichage historique des prélèvements");
}
// Actions rapides
public void genererRapportMensuel() {
System.out.println("Génération rapport mensuel");
LOGGER.info("Génération rapport mensuel");
}
public void configurerRelancesAuto() {
System.out.println("Configuration relances automatiques");
LOGGER.info("Configuration relances automatiques");
}
public void gererTypesCotisations() {
System.out.println("Gestion des types de cotisations");
LOGGER.info("Gestion des types de cotisations");
}
public void tableauDeBord() {
System.out.println("Affichage tableau de bord");
LOGGER.info("Affichage tableau de bord");
}
// Getters et Setters
@@ -415,7 +494,7 @@ public class CotisationsGestionBean implements Serializable {
// Classes internes pour les données
public static class CotisationAdmin {
private Long id;
private UUID id;
private String nomOrganisation;
private String regionOrganisation;
private String iconeOrganisation;
@@ -433,8 +512,8 @@ public class CotisationsGestionBean implements Serializable {
private String modePaiement;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNomOrganisation() { return nomOrganisation; }
public void setNomOrganisation(String nomOrganisation) { this.nomOrganisation = nomOrganisation; }
@@ -633,11 +712,11 @@ public class CotisationsGestionBean implements Serializable {
}
public static class Organisation {
private Long id;
private UUID id;
private String nom;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }

View File

@@ -1,22 +1,33 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.DemandeAideDTO;
import dev.lions.unionflow.client.service.DemandeAideService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.math.BigDecimal;
import java.util.logging.Logger;
@Named("demandesAideBean")
@SessionScoped
public class DemandesAideBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(DemandesAideBean.class.getName());
@Inject
@RestClient
private DemandeAideService demandeAideService;
private List<DemandeAide> toutesLesDemandes;
private List<DemandeAide> demandesFiltrees;
@@ -46,10 +57,25 @@ public class DemandesAideBean implements Serializable {
private void initializeStatistiques() {
statistiques = new StatistiquesDemandes();
statistiques.setTotalDemandes(156);
statistiques.setDemandesEnAttente(23);
statistiques.setDemandesApprouvees(89);
statistiques.setMontantTotalAide("12 850 000 FCFA");
try {
List<DemandeAideDTO> demandesDTO = demandeAideService.listerToutes(0, 1000);
statistiques.setTotalDemandes(demandesDTO.size());
long enAttente = demandesDTO.stream().filter(d -> "EN_ATTENTE".equals(d.getStatut())).count();
statistiques.setDemandesEnAttente((int) enAttente);
long approuvees = demandesDTO.stream().filter(d -> "APPROUVEE".equals(d.getStatut())).count();
statistiques.setDemandesApprouvees((int) approuvees);
BigDecimal montantTotal = demandesDTO.stream()
.filter(d -> d.getMontantAccorde() != null)
.map(DemandeAideDTO::getMontantAccorde)
.reduce(BigDecimal.ZERO, BigDecimal::add);
statistiques.setMontantTotalAide(montantTotal.toString() + " FCFA");
} catch (Exception e) {
LOGGER.severe("Erreur lors du calcul des statistiques: " + e.getMessage());
statistiques.setTotalDemandes(0);
statistiques.setDemandesEnAttente(0);
statistiques.setDemandesApprouvees(0);
statistiques.setMontantTotalAide("0 FCFA");
}
}
private void initializeEtapesWorkflow() {
@@ -101,83 +127,49 @@ public class DemandesAideBean implements Serializable {
private void initializeDemandes() {
toutesLesDemandes = new ArrayList<>();
String[] demandeurs = {
"Jean Kouassi", "Marie Traoré", "Ahmed Diallo", "Fatou Sanogo", "Paul Ouattara",
"Aissata Koné", "Ibrahim Touré", "Aminata Bakayoko", "Yves Koffi", "Mariam Coulibaly",
"Seydou Cissé", "Adjoa Mensah", "Kwame Asante", "Ama Gyamfi", "Kofi Adjei",
"Nana Akoto", "Akosua Boateng", "Emmanuel Ofori", "Joyce Owusu", "Stephen Asamoah"
};
String[] types = {
"AIDE_MEDICALE", "AIDE_ALIMENTAIRE", "AIDE_EDUCATIVE", "AIDE_LOGEMENT", "AIDE_URGENCE"
};
String[] statuts = {
"EN_ATTENTE", "EN_EVALUATION", "APPROUVEE", "REJETEE", "EN_COURS", "TERMINEE"
};
String[] urgences = {
"FAIBLE", "NORMALE", "ELEVEE", "CRITIQUE"
};
String[] localisations = {
"Zone Urbaine Centre", "Quartier Résidentiel Nord", "Zone Rurale Sud",
"Quartier Populaire Est", "Zone Industrielle Ouest", "Centre-Ville",
"Banlieue Nord", "Périphérie Sud", "Zone Commerciale", "Quartier Administratif"
};
String[] motifs = {
"Soins médicaux d'urgence", "Aide alimentaire famille nombreuse", "Frais de scolarité",
"Réparation logement suite inondation", "Urgence médicale enfant", "Soutien alimentaire mensuel",
"Achat fournitures scolaires", "Paiement loyer en retard", "Intervention chirurgicale",
"Aide nutritionnelle nourrisson", "Formation professionnelle", "Réhabilitation habitat",
"Traitement médical chronique", "Complément alimentaire", "Équipement scolaire"
};
for (int i = 0; i < 50; i++) {
DemandeAide demande = new DemandeAide();
demande.setId((long) (i + 1));
demande.setDemandeur(demandeurs[i % demandeurs.length]);
demande.setTelephone("77 123 45 " + String.format("%02d", (i % 99) + 1));
demande.setEmail(demandeurs[i % demandeurs.length].toLowerCase().replace(" ", ".") + "@email.com");
demande.setType(types[i % types.length]);
demande.setStatut(statuts[i % statuts.length]);
demande.setUrgence(urgences[i % urgences.length]);
demande.setLocalisation(localisations[i % localisations.length]);
demande.setMotif(motifs[i % motifs.length]);
demande.setDescription("Description détaillée pour " + motifs[i % motifs.length] + ". Situation nécessitant une évaluation approfondie.");
// Montants
BigDecimal montantBase = new BigDecimal(50000 + (i * 15000));
demande.setMontantDemande(montantBase);
if (demande.getStatut().equals("APPROUVEE") || demande.getStatut().equals("EN_COURS") || demande.getStatut().equals("TERMINEE")) {
demande.setMontantAccorde(montantBase.multiply(new BigDecimal("0.8")));
try {
List<DemandeAideDTO> demandesDTO = demandeAideService.listerToutes(0, 1000);
for (DemandeAideDTO dto : demandesDTO) {
DemandeAide demande = convertToDemandeAide(dto);
toutesLesDemandes.add(demande);
}
// Dates
demande.setDateDemande(LocalDate.now().minusDays(i + 1));
if (i % 4 == 0) {
demande.setDateLimite(LocalDate.now().plusDays(7 + (i % 14)));
}
// Responsable de traitement
if (!demande.getStatut().equals("EN_ATTENTE")) {
String[] responsables = {"Sarah Mensah", "David Konaté", "Grace Asante", "Michel Diallo", "Rita Kouassi"};
demande.setResponsableTraitement(responsables[i % responsables.length]);
}
toutesLesDemandes.add(demande);
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des demandes d'aide: " + e.getMessage());
}
}
private DemandeAide convertToDemandeAide(DemandeAideDTO dto) {
DemandeAide demande = new DemandeAide();
demande.setId(dto.getId());
demande.setDemandeur(dto.getDemandeur());
demande.setTelephone(dto.getTelephone());
demande.setEmail(dto.getEmail());
demande.setType(dto.getType());
demande.setStatut(dto.getStatut());
demande.setUrgence(dto.getUrgence());
demande.setLocalisation(dto.getLocalisation());
demande.setMotif(dto.getMotif() != null ? dto.getMotif() : dto.getTitre());
demande.setDescription(dto.getDescription());
demande.setMontantDemande(dto.getMontantDemande());
demande.setMontantAccorde(dto.getMontantAccorde());
demande.setDateDemande(dto.getDateDemande());
demande.setDateLimite(dto.getDateLimite());
demande.setResponsableTraitement(dto.getResponsableTraitement());
return demande;
}
private void initializeDemandesPrioritaires() {
demandesPrioritaires = toutesLesDemandes.stream()
.filter(d -> d.getUrgence().equals("CRITIQUE") || d.getUrgence().equals("ELEVEE"))
try {
List<DemandeAideDTO> demandesDTO = demandeAideService.rechercher("EN_ATTENTE", null, "CRITIQUE", 0, 6);
demandesPrioritaires = demandesDTO.stream()
.map(this::convertToDemandeAide)
.filter(d -> !d.getStatut().equals("TERMINEE") && !d.getStatut().equals("REJETEE"))
.sorted((d1, d2) -> d1.getDateDemande().compareTo(d2.getDateDemande()))
.limit(6)
.collect(Collectors.toList());
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des demandes prioritaires: " + e.getMessage());
demandesPrioritaires = new ArrayList<>();
}
}
private void initializeNouvelleDemande() {
@@ -250,7 +242,7 @@ public class DemandesAideBean implements Serializable {
public void creerDemande() {
DemandeAide nouvelleDem = new DemandeAide();
nouvelleDem.setId((long) (toutesLesDemandes.size() + 1));
nouvelleDem.setId(UUID.randomUUID());
nouvelleDem.setDemandeur(nouvelleDemande.getDemandeur());
nouvelleDem.setTelephone(nouvelleDemande.getTelephone());
nouvelleDem.setEmail(nouvelleDemande.getEmail());
@@ -268,7 +260,7 @@ public class DemandesAideBean implements Serializable {
appliquerFiltres();
initializeDemandesPrioritaires();
System.out.println("Nouvelle demande d'aide créée pour: " + nouvelleDem.getDemandeur());
LOGGER.info("Nouvelle demande d'aide créée pour: " + nouvelleDem.getDemandeur());
initializeNouvelleDemande();
}
@@ -278,7 +270,7 @@ public class DemandesAideBean implements Serializable {
if (demandeSelectionnee.getMontantAccorde() == null) {
demandeSelectionnee.setMontantAccorde(demandeSelectionnee.getMontantDemande().multiply(new BigDecimal("0.8")));
}
System.out.println("Demande approuvée pour: " + demandeSelectionnee.getDemandeur());
LOGGER.info("Demande approuvée pour: " + demandeSelectionnee.getDemandeur());
appliquerFiltres();
initializeDemandesPrioritaires();
}
@@ -287,7 +279,7 @@ public class DemandesAideBean implements Serializable {
public void rejeterDemande() {
if (demandeSelectionnee != null) {
demandeSelectionnee.setStatut("REJETEE");
System.out.println("Demande rejetée pour: " + demandeSelectionnee.getDemandeur());
LOGGER.info("Demande rejetée pour: " + demandeSelectionnee.getDemandeur());
appliquerFiltres();
initializeDemandesPrioritaires();
}
@@ -298,13 +290,13 @@ public class DemandesAideBean implements Serializable {
}
public void envoyerNotification() {
System.out.println("Notification envoyée pour la demande de: " + demandeSelectionnee.getDemandeur());
LOGGER.info("Notification envoyée pour la demande de: " + demandeSelectionnee.getDemandeur());
}
public void dupliquerDemande() {
if (demandeSelectionnee != null) {
DemandeAide copie = new DemandeAide();
copie.setId((long) (toutesLesDemandes.size() + 1));
copie.setId(UUID.randomUUID());
copie.setDemandeur(demandeSelectionnee.getDemandeur());
copie.setTelephone(demandeSelectionnee.getTelephone());
copie.setEmail(demandeSelectionnee.getEmail());
@@ -319,12 +311,12 @@ public class DemandesAideBean implements Serializable {
toutesLesDemandes.add(copie);
appliquerFiltres();
System.out.println("Demande dupliquée pour: " + copie.getDemandeur());
LOGGER.info("Demande dupliquée pour: " + copie.getDemandeur());
}
}
public void exporterDemandes() {
System.out.println("Export de " + demandesFiltrees.size() + " demandes d'aide");
LOGGER.info("Export de " + demandesFiltrees.size() + " demandes d'aide");
}
// Getters et Setters
@@ -357,7 +349,7 @@ public class DemandesAideBean implements Serializable {
// Classes internes
public static class DemandeAide {
private Long id;
private UUID id;
private String demandeur;
private String telephone;
private String email;
@@ -374,8 +366,8 @@ public class DemandesAideBean implements Serializable {
private String responsableTraitement;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getDemandeur() { return demandeur; }
public void setDemandeur(String demandeur) { this.demandeur = demandeur; }

View File

@@ -10,13 +10,16 @@ import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.logging.Logger;
@Named("demandeBean")
@SessionScoped
public class DemandesBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(DemandesBean.class.getName());
private List<Demande> demandes;
private List<Demande> selectedDemandes;
@@ -35,7 +38,7 @@ public class DemandesBean implements Serializable {
private LocalDate dateFilter;
// Assignation en lot
private Long gestionnaireAssignation;
private UUID gestionnaireAssignation;
private String commentaireAssignation;
// Statistiques
@@ -68,7 +71,7 @@ public class DemandesBean implements Serializable {
for (int i = 0; i < objets.length; i++) {
Demande demande = new Demande();
demande.setId((long) (i + 1));
demande.setId(UUID.fromString(String.format("00000000-0000-0000-0000-%012d", i + 1)));
demande.setReference("DEM-2024-" + String.format("%04d", i + 1));
demande.setObjet(objets[i]);
demande.setType(types[i]);
@@ -97,9 +100,9 @@ public class DemandesBean implements Serializable {
private void initializeGestionnaires() {
gestionnairesDisponibles = new ArrayList<>();
gestionnairesDisponibles.add(new Gestionnaire(1L, "Marie Gestionnaire"));
gestionnairesDisponibles.add(new Gestionnaire(2L, "Paul Superviseur"));
gestionnairesDisponibles.add(new Gestionnaire(3L, "Fatou Responsable"));
gestionnairesDisponibles.add(new Gestionnaire(UUID.fromString("00000000-0000-0000-0000-000000000500"), "Marie Gestionnaire"));
gestionnairesDisponibles.add(new Gestionnaire(UUID.fromString("00000000-0000-0000-0000-000000000600"), "Paul Superviseur"));
gestionnairesDisponibles.add(new Gestionnaire(UUID.fromString("00000000-0000-0000-0000-000000000700"), "Fatou Responsable"));
}
private void initializeNouvelleDemande() {
@@ -115,7 +118,7 @@ public class DemandesBean implements Serializable {
for (int i = 0; i < noms.length; i++) {
if (noms[i].toLowerCase().contains(query.toLowerCase())) {
Membre membre = new Membre();
membre.setId((long) (i + 1));
membre.setId(UUID.fromString(String.format("00000000-0000-0000-0000-%012d", i + 1)));
membre.setNomComplet(noms[i]);
membre.setNumeroMembre("M" + String.format("%06d", 1000 + i));
resultats.add(membre);
@@ -128,60 +131,60 @@ public class DemandesBean implements Serializable {
// Actions
public void voirDemande(Demande demande) {
this.demandeSelectionnee = demande;
System.out.println("Voir demande: " + demande.getObjet());
LOGGER.info("Voir demande: " + demande.getObjet());
}
public void traiterDemande(Demande demande) {
demande.setStatut("EN_COURS");
System.out.println("Traitement demande: " + demande.getObjet());
LOGGER.info("Traitement demande: " + demande.getObjet());
}
public void approuverDemande(Demande demande) {
demande.setStatut("APPROUVEE");
System.out.println("Demande approuvée: " + demande.getObjet());
LOGGER.info("Demande approuvée: " + demande.getObjet());
}
public void rejeterDemande(Demande demande) {
demande.setStatut("REJETEE");
System.out.println("Demande rejetée: " + demande.getObjet());
LOGGER.info("Demande rejetée: " + demande.getObjet());
}
public void assignerDemande(Demande demande) {
System.out.println("Assigner demande: " + demande.getObjet());
LOGGER.info("Assigner demande: " + demande.getObjet());
}
public void voirPiecesJointes(Demande demande) {
System.out.println("Voir pièces jointes: " + demande.getObjet());
LOGGER.info("Voir pièces jointes: " + demande.getObjet());
}
public void creerDemande() {
System.out.println("Créer nouvelle demande: " + nouvelleDemande.getObjet());
LOGGER.info("Créer nouvelle demande: " + nouvelleDemande.getObjet());
initializeNouvelleDemande();
}
public void effectuerAssignationLot() {
System.out.println("Assignation en lot à gestionnaire ID: " + gestionnaireAssignation);
LOGGER.info("Assignation en lot à gestionnaire ID: " + gestionnaireAssignation);
}
public void marquerTraitees() {
selectedDemandes.forEach(d -> d.setStatut("TRAITEE"));
System.out.println("Marquées comme traitées: " + selectedDemandes.size());
LOGGER.info("Marquées comme traitées: " + selectedDemandes.size());
}
public void exporterSelection() {
System.out.println("Export de " + selectedDemandes.size() + " demandes");
LOGGER.info("Export de " + selectedDemandes.size() + " demandes");
}
public void exporterDemandes() {
System.out.println("Export de toutes les demandes");
LOGGER.info("Export de toutes les demandes");
}
public void actualiser() {
System.out.println("Actualisation des données");
LOGGER.info("Actualisation des données");
}
public void filtrerUrgentes() {
System.out.println("Filtrer les demandes urgentes");
LOGGER.info("Filtrer les demandes urgentes");
}
// Getters et Setters
@@ -221,8 +224,8 @@ public class DemandesBean implements Serializable {
public LocalDate getDateFilter() { return dateFilter; }
public void setDateFilter(LocalDate dateFilter) { this.dateFilter = dateFilter; }
public Long getGestionnaireAssignation() { return gestionnaireAssignation; }
public void setGestionnaireAssignation(Long gestionnaireAssignation) { this.gestionnaireAssignation = gestionnaireAssignation; }
public UUID getGestionnaireAssignation() { return gestionnaireAssignation; }
public void setGestionnaireAssignation(UUID gestionnaireAssignation) { this.gestionnaireAssignation = gestionnaireAssignation; }
public String getCommentaireAssignation() { return commentaireAssignation; }
public void setCommentaireAssignation(String commentaireAssignation) { this.commentaireAssignation = commentaireAssignation; }
@@ -244,7 +247,7 @@ public class DemandesBean implements Serializable {
// Classes internes
public static class Demande {
private Long id;
private UUID id;
private String reference;
private String objet;
private String type;
@@ -261,8 +264,8 @@ public class DemandesBean implements Serializable {
private boolean hasPiecesJointes = false;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getReference() { return reference; }
public void setReference(String reference) { this.reference = reference; }
@@ -396,12 +399,12 @@ public class DemandesBean implements Serializable {
}
public static class Membre {
private Long id;
private UUID id;
private String nomComplet;
private String numeroMembre;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNomComplet() { return nomComplet; }
public void setNomComplet(String nomComplet) { this.nomComplet = nomComplet; }
@@ -422,7 +425,7 @@ public class DemandesBean implements Serializable {
private String priorite;
private String description;
private LocalDate dateEcheance;
private Long assigneA;
private UUID assigneA;
public String getObjet() { return objet; }
public void setObjet(String objet) { this.objet = objet; }
@@ -439,23 +442,23 @@ public class DemandesBean implements Serializable {
public LocalDate getDateEcheance() { return dateEcheance; }
public void setDateEcheance(LocalDate dateEcheance) { this.dateEcheance = dateEcheance; }
public Long getAssigneA() { return assigneA; }
public void setAssigneA(Long assigneA) { this.assigneA = assigneA; }
public UUID getAssigneA() { return assigneA; }
public void setAssigneA(UUID assigneA) { this.assigneA = assigneA; }
}
public static class Gestionnaire {
private Long id;
private UUID id;
private String nom;
public Gestionnaire() {}
public Gestionnaire(Long id, String nom) {
public Gestionnaire(UUID id, String nom) {
this.id = id;
this.nom = nom;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }

View File

@@ -10,6 +10,8 @@ import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@Named("documentsBean")
@@ -17,6 +19,7 @@ import java.util.stream.Collectors;
public class DocumentsBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(DocumentsBean.class.getName());
private List<Document> tousLesDocuments;
private List<Document> documentsFiltres;
@@ -32,7 +35,7 @@ public class DocumentsBean implements Serializable {
private StatistiquesDocuments statistiques;
private String modeAffichage = "GRID"; // GRID ou LIST
private Long dossierActuelId;
private UUID dossierActuelId;
@PostConstruct
public void init() {
@@ -52,94 +55,33 @@ public class DocumentsBean implements Serializable {
private void initializeStatistiques() {
statistiques = new StatistiquesDocuments();
statistiques.setTotalDocuments(1847);
statistiques.setTotalDossiers(23);
statistiques.setEspaceUtilise("2.3 GB");
statistiques.setPartagesMois(156);
try {
// Les statistiques seront calculées depuis les documents réels
// Pour l'instant, initialiser avec des valeurs par défaut
statistiques.setTotalDocuments(tousLesDocuments != null ? tousLesDocuments.size() : 0);
statistiques.setTotalDossiers(dossiersDisponibles != null ? dossiersDisponibles.size() : 0);
statistiques.setEspaceUtilise("0 GB");
statistiques.setPartagesMois(0);
} catch (Exception e) {
LOGGER.severe("Erreur lors du calcul des statistiques: " + e.getMessage());
statistiques.setTotalDocuments(0);
statistiques.setTotalDossiers(0);
statistiques.setEspaceUtilise("0 GB");
statistiques.setPartagesMois(0);
}
}
private void initializeDossiers() {
dossiersAffichage = new ArrayList<>();
dossiersDisponibles = new ArrayList<>();
String[] nomsDossiers = {
"Documents Administratifs", "Finances et Comptabilité", "Ressources Humaines",
"Communication", "Formations", "Projets et Actions", "Archives 2023",
"Modèles et Templates", "Rapports Mensuels", "Juridique et Conformité"
};
String[] couleurs = {
"blue-500", "green-500", "orange-500", "purple-500", "indigo-500",
"teal-500", "gray-500", "pink-500", "yellow-500", "red-500"
};
for (int i = 0; i < nomsDossiers.length; i++) {
Dossier dossier = new Dossier();
dossier.setId((long) (i + 1));
dossier.setNom(nomsDossiers[i]);
dossier.setCouleur(couleurs[i % couleurs.length]);
dossier.setNombreDocuments(15 + (i * 8));
dossier.setDerniereModification(LocalDateTime.now().minusDays(i + 1));
dossier.setCheminComplet("/" + nomsDossiers[i]);
dossiersAffichage.add(dossier);
dossiersDisponibles.add(dossier);
}
// Les dossiers seront chargés depuis l'API backend
// Pour l'instant, laisser les listes vides plutôt que des données mockées
}
private void initializeDocuments() {
tousLesDocuments = new ArrayList<>();
String[] noms = {
"Règlement Intérieur 2024.pdf", "Budget Prévisionnel Q1.xlsx", "Procès-Verbal AG Mars.docx",
"Guide Nouveau Membre.pdf", "Rapport Activités Janvier.docx", "Factures Fournisseurs Q4.xlsx",
"Statuts Association.pdf", "Plan Communication 2024.pptx", "Formation Leadership.pdf",
"Rapport Financier Annuel.pdf", "Convention Partenariat.docx", "Charte Graphique.pdf",
"Manuel Procédures.docx", "Bilan Social 2023.xlsx", "Certificat Conformité.pdf",
"Guide Utilisateur Plateforme.pdf", "Contrat Assurance.pdf", "Rapport Audit.docx",
"Plan Stratégique 3 ans.pptx", "Inventaire Matériel.xlsx", "Politique RGPD.pdf"
};
String[] types = {"PDF", "EXCEL", "WORD", "PDF", "WORD", "EXCEL", "PDF", "POWERPOINT", "PDF", "PDF", "WORD", "PDF", "WORD", "EXCEL", "PDF", "PDF", "PDF", "WORD", "POWERPOINT", "EXCEL", "PDF"};
String[] categories = {"ADMINISTRATIF", "FINANCIER", "ADMINISTRATIF", "FORMATION", "ADMINISTRATIF", "FINANCIER", "JURIDIQUE", "COMMUNICATION", "FORMATION", "FINANCIER", "JURIDIQUE", "COMMUNICATION", "ADMINISTRATIF", "FINANCIER", "JURIDIQUE", "FORMATION", "JURIDIQUE", "FINANCIER", "ADMINISTRATIF", "ADMINISTRATIF", "JURIDIQUE"};
String[] statuts = {"VALIDE", "VALIDE", "BROUILLON", "VALIDE", "VALIDE", "ARCHIVE", "VALIDE", "BROUILLON", "VALIDE", "VALIDE", "VALIDE", "VALIDE", "BROUILLON", "ARCHIVE", "VALIDE", "VALIDE", "VALIDE", "VALIDE", "BROUILLON", "VALIDE", "VALIDE"};
String[] auteurs = {"Marie Kouassi", "Paul Traoré", "Fatou Sanogo", "Jean Ouattara", "Aissata Koné", "Ibrahim Touré", "Aminata Bakayoko", "Yves Koffi", "Mariam Coulibaly", "Seydou Cissé", "Adjoa Mensah"};
long[] tailles = {2047000, 845000, 1250000, 3100000, 890000, 1450000, 567000, 2800000, 1900000, 3400000, 780000, 1200000, 1850000, 920000, 450000, 2200000, 890000, 1650000, 4200000, 680000, 750000};
for (int i = 0; i < noms.length; i++) {
Document document = new Document();
document.setId((long) (i + 1));
document.setNom(noms[i]);
document.setType(types[i]);
document.setCategorie(categories[i]);
document.setStatut(statuts[i]);
document.setAuteur(auteurs[i % auteurs.length]);
document.setTailleBytes(tailles[i]);
document.setDateCreation(LocalDateTime.now().minusDays(i * 3 + 1));
document.setDateModification(LocalDateTime.now().minusDays(i + 1));
document.setNombreVues(25 + (i * 7));
document.setNombreTelecharements(5 + (i * 2));
// Description automatique
document.setDescription("Document " + categories[i].toLowerCase() + " - " + noms[i]);
// Mots-clés automatiques
document.setMotsCles(generateMotsCles(categories[i], types[i]));
// Dossier parent (certains documents sont à la racine)
if (i % 3 != 0) {
document.setDossierId((long) ((i % dossiersDisponibles.size()) + 1));
}
tousLesDocuments.add(document);
}
}
private String generateMotsCles(String categorie, String type) {
String base = categorie.toLowerCase().replace("_", " ");
String typeClean = type.toLowerCase();
return base + ", " + typeClean + ", officiel, important";
// Les documents seront chargés depuis l'API backend
// Pour l'instant, laisser la liste vide plutôt que des données mockées
}
private void initializeNouveauDocument() {
@@ -270,7 +212,7 @@ public class DocumentsBean implements Serializable {
public void telechargerNouveauDocument() {
Document nouveau = new Document();
nouveau.setId((long) (tousLesDocuments.size() + 1));
nouveau.setId(UUID.randomUUID());
nouveau.setNom("Nouveau Document " + (tousLesDocuments.size() + 1));
nouveau.setCategorie(nouveauDocument.getCategorie());
nouveau.setDescription(nouveauDocument.getDescription());
@@ -288,25 +230,25 @@ public class DocumentsBean implements Serializable {
tousLesDocuments.add(nouveau);
appliquerFiltres();
System.out.println("Document téléchargé: " + nouveau.getNom());
LOGGER.info("Document téléchargé: " + nouveau.getNom());
initializeNouveauDocument();
}
public void telechargerDocument(Document document) {
document.setNombreTelecharements(document.getNombreTelecharements() + 1);
System.out.println("Téléchargement du document: " + document.getNom());
LOGGER.info("Téléchargement du document: " + document.getNom());
}
public void supprimerDocument(Document document) {
tousLesDocuments.remove(document);
appliquerFiltres();
System.out.println("Document supprimé: " + document.getNom());
LOGGER.info("Document supprimé: " + document.getNom());
}
public void dupliquerDocument() {
if (documentSelectionne != null) {
Document copie = new Document();
copie.setId((long) (tousLesDocuments.size() + 1));
copie.setId(UUID.randomUUID());
copie.setNom(documentSelectionne.getNom() + " (Copie)");
copie.setType(documentSelectionne.getType());
copie.setCategorie(documentSelectionne.getCategorie());
@@ -323,14 +265,14 @@ public class DocumentsBean implements Serializable {
tousLesDocuments.add(copie);
appliquerFiltres();
System.out.println("Document dupliqué: " + copie.getNom());
LOGGER.info("Document dupliqué: " + copie.getNom());
}
}
public void archiverDocument() {
if (documentSelectionne != null) {
documentSelectionne.setStatut("ARCHIVE");
System.out.println("Document archivé: " + documentSelectionne.getNom());
LOGGER.info("Document archivé: " + documentSelectionne.getNom());
appliquerFiltres();
}
}
@@ -339,7 +281,7 @@ public class DocumentsBean implements Serializable {
if (documentSelectionne != null) {
tousLesDocuments.remove(documentSelectionne);
appliquerFiltres();
System.out.println("Document supprimé définitivement: " + documentSelectionne.getNom());
LOGGER.info("Document supprimé définitivement: " + documentSelectionne.getNom());
}
}
@@ -396,12 +338,12 @@ public class DocumentsBean implements Serializable {
public String getModeAffichage() { return modeAffichage; }
public void setModeAffichage(String modeAffichage) { this.modeAffichage = modeAffichage; }
public Long getDossierActuelId() { return dossierActuelId; }
public void setDossierActuelId(Long dossierActuelId) { this.dossierActuelId = dossierActuelId; }
public UUID getDossierActuelId() { return dossierActuelId; }
public void setDossierActuelId(UUID dossierActuelId) { this.dossierActuelId = dossierActuelId; }
// Classes internes
public static class Document {
private Long id;
private UUID id;
private String nom;
private String description;
private String type;
@@ -409,7 +351,7 @@ public class DocumentsBean implements Serializable {
private String statut;
private String auteur;
private String motsCles;
private Long dossierId;
private UUID dossierId;
private long tailleBytes;
private LocalDateTime dateCreation;
private LocalDateTime dateModification;
@@ -418,8 +360,8 @@ public class DocumentsBean implements Serializable {
private boolean accesRestreint;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }
@@ -442,8 +384,8 @@ public class DocumentsBean implements Serializable {
public String getMotsCles() { return motsCles; }
public void setMotsCles(String motsCles) { this.motsCles = motsCles; }
public Long getDossierId() { return dossierId; }
public void setDossierId(Long dossierId) { this.dossierId = dossierId; }
public UUID getDossierId() { return dossierId; }
public void setDossierId(UUID dossierId) { this.dossierId = dossierId; }
public long getTailleBytes() { return tailleBytes; }
public void setTailleBytes(long tailleBytes) { this.tailleBytes = tailleBytes; }
@@ -557,7 +499,7 @@ public class DocumentsBean implements Serializable {
}
public static class Dossier {
private Long id;
private UUID id;
private String nom;
private String couleur;
private int nombreDocuments;
@@ -565,8 +507,8 @@ public class DocumentsBean implements Serializable {
private String cheminComplet;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }
@@ -596,7 +538,7 @@ public class DocumentsBean implements Serializable {
private String categorie;
private String description;
private String motsCles;
private Long dossierId;
private UUID dossierId;
private boolean accesRestreint;
// Getters et setters
@@ -609,8 +551,8 @@ public class DocumentsBean implements Serializable {
public String getMotsCles() { return motsCles; }
public void setMotsCles(String motsCles) { this.motsCles = motsCles; }
public Long getDossierId() { return dossierId; }
public void setDossierId(Long dossierId) { this.dossierId = dossierId; }
public UUID getDossierId() { return dossierId; }
public void setDossierId(UUID dossierId) { this.dossierId = dossierId; }
public boolean isAccesRestreint() { return accesRestreint; }
public void setAccesRestreint(boolean accesRestreint) { this.accesRestreint = accesRestreint; }
@@ -678,13 +620,13 @@ public class DocumentsBean implements Serializable {
public static class NiveauNavigation {
private String nom;
private Long dossierId;
private UUID dossierId;
// Getters et setters
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }
public Long getDossierId() { return dossierId; }
public void setDossierId(Long dossierId) { this.dossierId = dossierId; }
public UUID getDossierId() { return dossierId; }
public void setDossierId(UUID dossierId) { this.dossierId = dossierId; }
}
}

View File

@@ -1,8 +1,12 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.AssociationDTO;
import dev.lions.unionflow.client.service.AssociationService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -10,13 +14,20 @@ import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.logging.Logger;
@Named("entitesGestionBean")
@SessionScoped
public class EntitesGestionBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(EntitesGestionBean.class.getName());
@Inject
@RestClient
private AssociationService associationService;
private List<Entite> toutesLesEntites;
private List<Entite> entitesFiltrees;
@@ -42,102 +53,84 @@ public class EntitesGestionBean implements Serializable {
private void initializeStatistiques() {
statistiques = new Statistiques();
statistiques.setTotalEntites(127); // Nouvelle stratégie volume
statistiques.setEntitesActives(127);
statistiques.setTotalMembres(18547); // 146 membres/organisation en moyenne
statistiques.setMoyenneMembresParEntite(146); // Moyenne calculée
statistiques.setRevenus("363 000 FCFA");
statistiques.setSouscriptionsExpirantes(12);
statistiques.setEntitesQuotaAtteint(8);
statistiques.setFormulairePopulaire("Standard");
statistiques.setTauxRenouvellement(94.2f);
// Calcul dynamique des statistiques de souscription
try {
List<AssociationDTO> associations = associationService.listerToutes();
statistiques.setTotalEntites(associations.size());
long actives = associations.stream().filter(a -> "ACTIVE".equals(a.getStatut())).count();
statistiques.setEntitesActives((int) actives);
int totalMembres = associations.stream()
.mapToInt(a -> a.getNombreMembres() != null ? a.getNombreMembres() : 0)
.sum();
statistiques.setTotalMembres(totalMembres);
double moyenne = associations.isEmpty() ? 0 : (double) totalMembres / associations.size();
statistiques.setMoyenneMembresParEntite((int) moyenne);
statistiques.setRevenus("0 FCFA"); // À calculer depuis les souscriptions
statistiques.setSouscriptionsExpirantes(0); // À calculer
statistiques.setEntitesQuotaAtteint(0); // À calculer
statistiques.setFormulairePopulaire("Standard");
statistiques.setTauxRenouvellement(94.2f);
} catch (Exception e) {
LOGGER.severe("Erreur lors du calcul des statistiques: " + e.getMessage());
statistiques.setTotalEntites(0);
statistiques.setEntitesActives(0);
statistiques.setTotalMembres(0);
statistiques.setMoyenneMembresParEntite(0);
}
calculerStatistiquesSouscriptions();
}
private void initializeEntites() {
toutesLesEntites = new ArrayList<>();
// Génération de données de test
String[] noms = {
"Association Alpha Centrale", "Club Beta Régional", "Groupe Gamma Local",
"Association Delta Nord", "Club Epsilon Sud", "Groupe Zeta Jeunes",
"Association Eta Ouest", "Club Theta Est", "Groupe Iota Centre",
"Association Kappa Métropole", "Branche Féminine Lambda", "Club Mu Urbain",
"Groupe Jeunes Nu", "Association Xi Rural", "Club Omicron Mixte"
};
String[] types = {
"ASSOCIATION", "CLUB", "GROUPE", "ASSOCIATION", "CLUB", "GROUPE_JEUNES",
"ASSOCIATION", "CLUB", "GROUPE", "ASSOCIATION", "BRANCHE", "CLUB",
"GROUPE_JEUNES", "ASSOCIATION", "CLUB"
};
String[] regions = {
"REGION_1", "REGION_2", "REGION_3", "REGION_4", "REGION_5", "REGION_1",
"REGION_6", "REGION_7", "REGION_8", "REGION_9", "REGION_1", "REGION_10",
"REGION_2", "REGION_11", "REGION_12"
};
String[] statuts = {
"ACTIVE", "ACTIVE", "ACTIVE", "ACTIVE", "ACTIVE", "ACTIVE",
"ACTIVE", "SUSPENDUE", "ACTIVE", "ACTIVE", "ACTIVE", "INACTIVE",
"ACTIVE", "ACTIVE", "ACTIVE"
};
int[] nombresMembres = {156, 123, 98, 87, 73, 45, 67, 0, 54, 43, 32, 0, 28, 38, 51};
for (int i = 0; i < noms.length; i++) {
Entite entite = new Entite();
entite.setId((long) (i + 1));
entite.setNom(noms[i]);
entite.setCodeEntite("ENT" + String.format("%03d", i + 1));
entite.setType(types[i]);
entite.setRegion(regions[i]);
entite.setStatut(statuts[i]);
entite.setNombreMembres(nombresMembres[i]);
entite.setMembresUtilises(nombresMembres[i]);
entite.setAdresse("Adresse " + noms[i]);
entite.setTelephone("77 123 45 " + String.format("%02d", i + 1));
entite.setEmail(noms[i].toLowerCase().replace(" ", ".") + "@unionflow.app");
entite.setDescription("Description de " + noms[i]);
entite.setDerniereActivite(LocalDateTime.now().minusDays(i * 2 + 1));
// Définir le forfait selon le nombre de membres
if (nombresMembres[i] <= 100) {
entite.setForfaitSouscrit("Starter");
entite.setMembresQuota(100);
entite.setMontantMensuel("2 000 FCFA");
} else if (nombresMembres[i] <= 200) {
entite.setForfaitSouscrit("Standard");
entite.setMembresQuota(200);
entite.setMontantMensuel("3 000 FCFA");
} else if (nombresMembres[i] <= 500) {
entite.setForfaitSouscrit("Premium");
entite.setMembresQuota(500);
entite.setMontantMensuel("4 000 FCFA");
} else {
entite.setForfaitSouscrit("Cristal");
entite.setMembresQuota(2000);
entite.setMontantMensuel("5 000 FCFA");
try {
List<AssociationDTO> associations = associationService.listerToutes();
for (AssociationDTO dto : associations) {
Entite entite = convertToEntite(dto);
toutesLesEntites.add(entite);
}
// Date d'expiration (varie entre 1 mois et 11 mois)
entite.setDateExpirationSouscription(LocalDate.now().plusMonths(1 + (i % 11)));
// Administrateur
if (nombresMembres[i] > 0) {
Administrateur admin = new Administrateur();
admin.setNomComplet("Admin " + (i + 1));
admin.setEmail("admin" + (i + 1) + "@unionflow.app");
entite.setAdministrateur(admin);
}
toutesLesEntites.add(entite);
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des entités: " + e.getMessage());
}
}
private Entite convertToEntite(AssociationDTO dto) {
Entite entite = new Entite();
entite.setId(dto.getId());
entite.setNom(dto.getNom());
entite.setCodeEntite(dto.getNumeroRegistre());
entite.setType(dto.getTypeAssociation());
entite.setRegion(dto.getRegion());
entite.setStatut(dto.getStatut());
entite.setNombreMembres(dto.getNombreMembres() != null ? dto.getNombreMembres() : 0);
entite.setMembresUtilises(dto.getNombreMembres() != null ? dto.getNombreMembres() : 0);
entite.setAdresse(dto.getAdresse());
entite.setTelephone(dto.getTelephone());
entite.setEmail(dto.getEmail());
entite.setDescription(dto.getDescription());
entite.setDerniereActivite(dto.getDateDerniereActivite());
// Définir le forfait selon le nombre de membres
int nbMembres = dto.getNombreMembres() != null ? dto.getNombreMembres() : 0;
if (nbMembres <= 100) {
entite.setForfaitSouscrit("Starter");
entite.setMembresQuota(100);
entite.setMontantMensuel("2 000 FCFA");
} else if (nbMembres <= 200) {
entite.setForfaitSouscrit("Standard");
entite.setMembresQuota(200);
entite.setMontantMensuel("3 000 FCFA");
} else if (nbMembres <= 500) {
entite.setForfaitSouscrit("Premium");
entite.setMembresQuota(500);
entite.setMontantMensuel("4 000 FCFA");
} else {
entite.setForfaitSouscrit("Cristal");
entite.setMembresQuota(2000);
entite.setMontantMensuel("5 000 FCFA");
}
return entite;
}
private void initializeNouvelleEntite() {
nouvelleEntite = new Entite();
}
@@ -229,7 +222,7 @@ public class EntitesGestionBean implements Serializable {
}
public void creerEntite() {
nouvelleEntite.setId((long) (toutesLesEntites.size() + 1));
nouvelleEntite.setId(UUID.randomUUID());
nouvelleEntite.setCodeEntite("ENT" + String.format("%03d", toutesLesEntites.size() + 1));
nouvelleEntite.setStatut("ACTIVE");
nouvelleEntite.setNombreMembres(0);
@@ -238,7 +231,7 @@ public class EntitesGestionBean implements Serializable {
toutesLesEntites.add(nouvelleEntite);
appliquerFiltres();
System.out.println("Nouvelle entité créée: " + nouvelleEntite.getNom());
LOGGER.info("Nouvelle entité créée: " + nouvelleEntite.getNom());
initializeNouvelleEntite();
}
@@ -257,24 +250,24 @@ public class EntitesGestionBean implements Serializable {
public void suspendreEntite() {
entiteSelectionne.setStatut("SUSPENDUE");
System.out.println("Entité suspendue: " + entiteSelectionne.getNom());
LOGGER.info("Entité suspendue: " + entiteSelectionne.getNom());
appliquerFiltres();
}
public void reactiverEntite() {
entiteSelectionne.setStatut("ACTIVE");
System.out.println("Entité réactivée: " + entiteSelectionne.getNom());
LOGGER.info("Entité réactivée: " + entiteSelectionne.getNom());
appliquerFiltres();
}
public void supprimerEntite() {
toutesLesEntites.remove(entiteSelectionne);
System.out.println("Entité supprimée: " + entiteSelectionne.getNom());
LOGGER.info("Entité supprimée: " + entiteSelectionne.getNom());
appliquerFiltres();
}
public void exporterEntites() {
System.out.println("Export de " + entitesFiltrees.size() + " entités");
LOGGER.info("Export de " + entitesFiltrees.size() + " entités");
}
// Getters et Setters
@@ -325,7 +318,7 @@ public class EntitesGestionBean implements Serializable {
if (entiteSelectionne != null) {
entiteSelectionne.setDateExpirationSouscription(LocalDate.now().plusMonths(12));
entiteSelectionne.setStatutSouscription("ACTIVE");
System.out.println("Souscription renouvelée pour: " + entiteSelectionne.getNom());
LOGGER.info("Souscription renouvelée pour: " + entiteSelectionne.getNom());
appliquerFiltres();
}
}
@@ -350,24 +343,24 @@ public class EntitesGestionBean implements Serializable {
entiteSelectionne.setMontantMensuel("5 000 FCFA");
break;
}
System.out.println("Forfait upgradé pour: " + entiteSelectionne.getNom());
LOGGER.info("Forfait upgradé pour: " + entiteSelectionne.getNom());
appliquerFiltres();
}
}
public void gererQuotas() {
System.out.println("Gestion des quotas pour toutes les entités");
LOGGER.info("Gestion des quotas pour toutes les entités");
}
public void envoyerRelancesSouscriptions() {
int compteur = 0;
for (Entite entite : toutesLesEntites) {
if (entite.isExpirationProche()) {
System.out.println("Relance envoyée à: " + entite.getNom());
LOGGER.info("Relance envoyée à: " + entite.getNom());
compteur++;
}
}
System.out.println(compteur + " relances de souscription envoyées");
LOGGER.info(compteur + " relances de souscription envoyées");
}
// Actions groupées
@@ -378,7 +371,7 @@ public class EntitesGestionBean implements Serializable {
entite.setStatutSouscription("ACTIVE");
compteur++;
}
System.out.println(compteur + " souscriptions renouvelées en masse");
LOGGER.info(compteur + " souscriptions renouvelées en masse");
entitesSelectionnees.clear();
appliquerFiltres();
}
@@ -389,7 +382,7 @@ public class EntitesGestionBean implements Serializable {
entite.setStatut("SUSPENDUE");
compteur++;
}
System.out.println(compteur + " entités suspendues en masse");
LOGGER.info(compteur + " entités suspendues en masse");
entitesSelectionnees.clear();
appliquerFiltres();
}
@@ -400,7 +393,7 @@ public class EntitesGestionBean implements Serializable {
entite.setStatut("ACTIVE");
compteur++;
}
System.out.println(compteur + " entités réactivées en masse");
LOGGER.info(compteur + " entités réactivées en masse");
entitesSelectionnees.clear();
appliquerFiltres();
}
@@ -410,16 +403,16 @@ public class EntitesGestionBean implements Serializable {
for (Entite entite : entitesSelectionnees) {
if (entite.isQuotaProche()) {
// Simulation d'envoi de proposition d'upgrade
System.out.println("Proposition d'upgrade envoyée à: " + entite.getNom());
LOGGER.info("Proposition d'upgrade envoyée à: " + entite.getNom());
compteur++;
}
}
System.out.println(compteur + " propositions d'upgrade envoyées");
LOGGER.info(compteur + " propositions d'upgrade envoyées");
}
// Classes internes
public static class Entite {
private Long id;
private UUID id;
private String nom;
private String codeEntite;
private String type;
@@ -442,8 +435,8 @@ public class EntitesGestionBean implements Serializable {
private String montantMensuel = "3 000 FCFA";
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }

View File

@@ -1,24 +1,34 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.EvenementDTO;
import dev.lions.unionflow.client.service.EvenementService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.math.BigDecimal;
import java.util.logging.Logger;
@Named("evenementsBean")
@SessionScoped
public class EvenementsBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(EvenementsBean.class.getName());
@Inject
@RestClient
private EvenementService evenementService;
private List<Evenement> tousLesEvenements;
private List<Evenement> evenementsFiltres;
@@ -32,8 +42,8 @@ public class EvenementsBean implements Serializable {
@PostConstruct
public void init() {
initializeFiltres();
initializeStatistiques();
initializeEvenements();
initializeStatistiques();
initializeNouvelEvenement();
initializeEvenementsProchains();
appliquerFiltres();
@@ -46,98 +56,78 @@ public class EvenementsBean implements Serializable {
private void initializeStatistiques() {
statistiques = new StatistiquesEvenements();
statistiques.setTotalEvenements(42);
statistiques.setEvenementsActifs(18);
statistiques.setParticipantsTotal(1247);
statistiques.setBudgetTotal("3 450 000 FCFA");
statistiques.setMoyenneParticipants(69);
try {
List<EvenementDTO> evenementsDTO = evenementService.listerTous(0, 1000);
statistiques.setTotalEvenements(evenementsDTO.size());
long actifs = evenementsDTO.stream()
.filter(e -> "PLANIFIE".equals(e.getStatut()) || "EN_COURS".equals(e.getStatut()))
.count();
statistiques.setEvenementsActifs((int) actifs);
int totalParticipants = evenementsDTO.stream()
.mapToInt(e -> e.getParticipantsInscrits() != null ? e.getParticipantsInscrits() : 0)
.sum();
statistiques.setParticipantsTotal(totalParticipants);
BigDecimal totalBudget = evenementsDTO.stream()
.map(e -> e.getBudget() != null ? e.getBudget() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
statistiques.setBudgetTotal(totalBudget.toString() + " FCFA");
double moyenne = evenementsDTO.isEmpty() ? 0 : (double) totalParticipants / evenementsDTO.size();
statistiques.setMoyenneParticipants((int) moyenne);
} catch (Exception e) {
LOGGER.severe("Erreur lors du calcul des statistiques: " + e.getMessage());
statistiques.setTotalEvenements(0);
statistiques.setEvenementsActifs(0);
statistiques.setParticipantsTotal(0);
statistiques.setBudgetTotal("0 FCFA");
statistiques.setMoyenneParticipants(0);
}
}
private void initializeEvenements() {
tousLesEvenements = new ArrayList<>();
String[] titres = {
"Assemblée Générale Ordinaire", "Formation Leadership", "Action Sociale Quartier Nord",
"Réunion Mensuelle Comité", "Conférence Développement Durable", "Collecte de Fonds",
"Journée Portes Ouvertes", "Atelier Gestion Financière", "Campagne Sensibilisation",
"Séminaire Jeunes Leaders", "Action Humanitaire", "Réunion Conseil Administration",
"Formation Premiers Secours", "Festival Culturel", "Réunion Coordination Régionale"
};
String[] types = {
"ASSEMBLEE_GENERALE", "FORMATION", "ACTION_SOCIALE", "REUNION", "FORMATION",
"ACTION_SOCIALE", "EVENEMENT_FESTIF", "FORMATION", "ACTION_SOCIALE", "FORMATION",
"ACTION_SOCIALE", "REUNION", "FORMATION", "EVENEMENT_FESTIF", "REUNION"
};
String[] statuts = {
"PLANIFIE", "PLANIFIE", "EN_COURS", "TERMINE", "PLANIFIE",
"PLANIFIE", "TERMINE", "PLANIFIE", "EN_COURS", "PLANIFIE",
"TERMINE", "PLANIFIE", "PLANIFIE", "TERMINE", "PLANIFIE"
};
String[] priorites = {
"CRITIQUE", "ELEVEE", "NORMALE", "NORMALE", "ELEVEE",
"NORMALE", "FAIBLE", "NORMALE", "ELEVEE", "NORMALE",
"CRITIQUE", "NORMALE", "FAIBLE", "FAIBLE", "NORMALE"
};
String[] lieux = {
"Salle Principale", "Centre Formation", "Quartier Nord", "Salle Réunion A",
"Auditorium Central", "Place Publique", "Hall d'Accueil", "Salle Formation B",
"Centre-Ville", "Campus Universitaire", "Zone Rurale", "Salle Conseil",
"Centre Médical", "Parc Municipal", "Siège Régional"
};
String[] organisateurs = {
"Marie Kouassi", "Paul Traoré", "Fatou Sanogo", "Jean Ouattara", "Aissata Koné",
"Ibrahim Touré", "Aminata Bakayoko", "Yves Koffi", "Mariam Coulibaly", "Seydou Cissé",
"Adjoa Mensah", "Kwame Asante", "Ama Gyamfi", "Kofi Adjei", "Ahmed Diallo"
};
for (int i = 0; i < titres.length; i++) {
Evenement evenement = new Evenement();
evenement.setId((long) (i + 1));
evenement.setTitre(titres[i]);
evenement.setType(types[i]);
evenement.setStatut(statuts[i]);
evenement.setPriorite(priorites[i]);
evenement.setLieu(lieux[i]);
evenement.setAdresse("Adresse " + lieux[i] + ", Ville");
evenement.setOrganisateur(organisateurs[i]);
evenement.setOrganisateurEmail(organisateurs[i].toLowerCase().replace(" ", ".") + "@unionflow.app");
// Dates aléatoires
LocalDate baseDate = LocalDate.now().plusDays(i * 3 - 15);
evenement.setDateDebut(baseDate);
evenement.setDateFin(baseDate.plusDays(i % 3 == 0 ? 1 : 0));
evenement.setHeureDebut(LocalTime.of(9 + (i % 8), 0));
evenement.setHeureFin(LocalTime.of(11 + (i % 8), 30));
// Participants et capacité
int capacite = 50 + (i * 10);
int inscrits = (int) (capacite * (0.3 + (i * 0.05) % 0.7));
evenement.setCapaciteMax(capacite);
evenement.setParticipantsInscrits(inscrits);
// Budget
BigDecimal budget = new BigDecimal(100000 + (i * 25000));
evenement.setBudget(budget);
// Description
evenement.setDescription("Description détaillée pour " + titres[i] + " avec objectifs et programme.");
tousLesEvenements.add(evenement);
try {
List<EvenementDTO> evenementsDTO = evenementService.listerTous(0, 1000);
for (EvenementDTO dto : evenementsDTO) {
Evenement evenement = convertToEvenement(dto);
tousLesEvenements.add(evenement);
}
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des événements: " + e.getMessage());
}
}
private Evenement convertToEvenement(EvenementDTO dto) {
Evenement evenement = new Evenement();
evenement.setId(dto.getId());
evenement.setTitre(dto.getTitre());
evenement.setType(dto.getType());
evenement.setStatut(dto.getStatut());
evenement.setPriorite(dto.getPriorite());
evenement.setLieu(dto.getLieu());
evenement.setAdresse(dto.getAdresse());
evenement.setOrganisateur(dto.getOrganisateur());
evenement.setOrganisateurEmail(dto.getOrganisateurEmail());
evenement.setDateDebut(dto.getDateDebut());
evenement.setDateFin(dto.getDateFin());
evenement.setHeureDebut(dto.getHeureDebut());
evenement.setHeureFin(dto.getHeureFin());
evenement.setCapaciteMax(dto.getCapaciteMax());
evenement.setParticipantsInscrits(dto.getParticipantsInscrits());
evenement.setBudget(dto.getBudget());
evenement.setDescription(dto.getDescription());
return evenement;
}
private void initializeEvenementsProchains() {
evenementsProchains = tousLesEvenements.stream()
.filter(e -> e.getDateDebut().isAfter(LocalDate.now()) || e.getDateDebut().equals(LocalDate.now()))
.filter(e -> !e.getStatut().equals("ANNULE") && !e.getStatut().equals("TERMINE"))
.sorted((e1, e2) -> e1.getDateDebut().compareTo(e2.getDateDebut()))
.limit(6)
try {
List<EvenementDTO> evenementsDTO = evenementService.listerAVenir(0, 6);
evenementsProchains = evenementsDTO.stream()
.map(this::convertToEvenement)
.collect(Collectors.toList());
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des événements à venir: " + e.getMessage());
evenementsProchains = new ArrayList<>();
}
}
private void initializeNouvelEvenement() {
@@ -210,7 +200,7 @@ public class EvenementsBean implements Serializable {
public void creerEvenement() {
Evenement nouvelEvt = new Evenement();
nouvelEvt.setId((long) (tousLesEvenements.size() + 1));
nouvelEvt.setId(UUID.randomUUID());
nouvelEvt.setTitre(nouvelEvenement.getTitre());
nouvelEvt.setType(nouvelEvenement.getType());
nouvelEvt.setDateDebut(nouvelEvenement.getDateDebut());
@@ -231,7 +221,7 @@ public class EvenementsBean implements Serializable {
appliquerFiltres();
initializeEvenementsProchains();
System.out.println("Nouvel événement créé: " + nouvelEvt.getTitre());
LOGGER.info("Nouvel événement créé: " + nouvelEvt.getTitre());
initializeNouvelEvenement();
}
@@ -240,7 +230,7 @@ public class EvenementsBean implements Serializable {
}
public void envoyerInvitations() {
System.out.println("Invitations envoyées pour: " + evenementSelectionne.getTitre());
LOGGER.info("Invitations envoyées pour: " + evenementSelectionne.getTitre());
}
public String voirRapports() {
@@ -250,7 +240,7 @@ public class EvenementsBean implements Serializable {
public void annulerEvenement() {
if (evenementSelectionne != null) {
evenementSelectionne.setStatut("ANNULE");
System.out.println("Événement annulé: " + evenementSelectionne.getTitre());
LOGGER.info("Événement annulé: " + evenementSelectionne.getTitre());
appliquerFiltres();
initializeEvenementsProchains();
}
@@ -259,7 +249,7 @@ public class EvenementsBean implements Serializable {
public void dupliquerEvenement() {
if (evenementSelectionne != null) {
Evenement copie = new Evenement();
copie.setId((long) (tousLesEvenements.size() + 1));
copie.setId(UUID.randomUUID());
copie.setTitre(evenementSelectionne.getTitre() + " (Copie)");
copie.setType(evenementSelectionne.getType());
copie.setLieu(evenementSelectionne.getLieu());
@@ -278,16 +268,16 @@ public class EvenementsBean implements Serializable {
tousLesEvenements.add(copie);
appliquerFiltres();
System.out.println("Événement dupliqué: " + copie.getTitre());
LOGGER.info("Événement dupliqué: " + copie.getTitre());
}
}
public void exporterEvenements() {
System.out.println("Export de " + evenementsFiltres.size() + " événements");
LOGGER.info("Export de " + evenementsFiltres.size() + " événements");
}
public void exporterExcel() {
System.out.println("Export Excel de " + evenementsFiltres.size() + " événements");
LOGGER.info("Export Excel de " + evenementsFiltres.size() + " événements");
}
// Getters et Setters
@@ -317,7 +307,7 @@ public class EvenementsBean implements Serializable {
// Classes internes
public static class Evenement {
private Long id;
private UUID id;
private String titre;
private String description;
private String type;
@@ -336,8 +326,8 @@ public class EvenementsBean implements Serializable {
private BigDecimal budget;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getTitre() { return titre; }
public void setTitre(String titre) { this.titre = titre; }

View File

@@ -2,18 +2,28 @@ package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.FormulaireDTO;
import dev.lions.unionflow.client.dto.SouscriptionDTO;
import dev.lions.unionflow.client.service.FormulaireService;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
@Named("formulaireBean")
@RequestScoped
public class FormulaireBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(FormulaireBean.class.getName());
@Inject
@RestClient
private FormulaireService formulaireService;
private List<FormulaireDTO> formulaires;
private List<FormulaireDTO> formulairesPopulaires;
@@ -25,104 +35,21 @@ public class FormulaireBean implements Serializable {
private BigDecimal budgetMax;
private String categorieFiltre = "ALL";
public FormulaireBean() {
@PostConstruct
public void init() {
initializeFormulaires();
}
private void initializeFormulaires() {
formulaires = new ArrayList<>();
// Formulaire STARTER - Pour les petites associations
FormulaireDTO starter = new FormulaireDTO();
starter.setId(1L);
starter.setNom("Starter");
starter.setDescription("Parfait pour les associations débutantes");
starter.setQuotaMaxMembres(100);
starter.setPrixMensuel(new BigDecimal("2000")); // 2K FCFA
starter.setPrixAnnuel(new BigDecimal("20000")); // 20K FCFA (2 mois gratuits)
starter.setCouleurTheme("bg-blue-500");
starter.setIconeFormulaire("pi-star");
starter.setGestionMembres(true);
starter.setGestionCotisations(true);
starter.setGestionEvenements(false);
starter.setGestionAides(false);
starter.setRapportsAvances(false);
starter.setNotificationsEmail(true);
formulaires.add(starter);
// Formulaire STANDARD - Pour les associations moyennes
FormulaireDTO standard = new FormulaireDTO();
standard.setId(2L);
standard.setNom("Standard");
standard.setDescription("Idéal pour les associations en croissance");
standard.setQuotaMaxMembres(200);
standard.setPrixMensuel(new BigDecimal("3000")); // 3K FCFA
standard.setPrixAnnuel(new BigDecimal("30000")); // 30K FCFA (2 mois gratuits)
standard.setCouleurTheme("bg-green-500");
standard.setIconeFormulaire("pi-users");
standard.setRecommande(true);
standard.setGestionMembres(true);
standard.setGestionCotisations(true);
standard.setGestionEvenements(true);
standard.setGestionAides(true);
standard.setRapportsAvances(false);
standard.setNotificationsEmail(true);
standard.setNotificationsSMS(false);
standard.setGestionDocuments(true);
formulaires.add(standard);
// Formulaire PREMIUM - Pour les grandes associations
FormulaireDTO premium = new FormulaireDTO();
premium.setId(3L);
premium.setNom("Premium");
premium.setDescription("Solution complète pour les grandes organisations");
premium.setQuotaMaxMembres(500);
premium.setPrixMensuel(new BigDecimal("4000")); // 4K FCFA
premium.setPrixAnnuel(new BigDecimal("40000")); // 40K FCFA (2 mois gratuits)
premium.setCouleurTheme("bg-purple-500");
premium.setIconeFormulaire("pi-crown");
premium.setGestionMembres(true);
premium.setGestionCotisations(true);
premium.setGestionEvenements(true);
premium.setGestionAides(true);
premium.setRapportsAvances(true);
premium.setSupportPrioritaire(true);
premium.setSauvegardeAutomatique(true);
premium.setPersonnalisationAvancee(true);
premium.setIntegrationPaiement(true);
premium.setNotificationsEmail(true);
premium.setNotificationsSMS(true);
premium.setGestionDocuments(true);
formulaires.add(premium);
// Formulaire CRISTAL - Pour les très grandes organisations
FormulaireDTO cristal = new FormulaireDTO();
cristal.setId(4L);
cristal.setNom("Cristal");
cristal.setDescription("Solution premium pour les fédérations et grandes entités");
cristal.setQuotaMaxMembres(2000);
cristal.setPrixMensuel(new BigDecimal("5000")); // 5K FCFA
cristal.setPrixAnnuel(new BigDecimal("50000")); // 50K FCFA (2 mois gratuits)
cristal.setCouleurTheme("bg-indigo-500");
cristal.setIconeFormulaire("pi-diamond");
cristal.setGestionMembres(true);
cristal.setGestionCotisations(true);
cristal.setGestionEvenements(true);
cristal.setGestionAides(true);
cristal.setRapportsAvances(true);
cristal.setSupportPrioritaire(true);
cristal.setSauvegardeAutomatique(true);
cristal.setPersonnalisationAvancee(true);
cristal.setIntegrationPaiement(true);
cristal.setNotificationsEmail(true);
cristal.setNotificationsSMS(true);
cristal.setGestionDocuments(true);
formulaires.add(cristal);
// Définir les formulaires populaires (Standard et Premium)
formulairesPopulaires = new ArrayList<>();
formulairesPopulaires.add(standard);
formulairesPopulaires.add(premium);
try {
formulaires = formulaireService.listerActifs();
formulairesPopulaires = formulaireService.listerPopulaires();
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des formulaires: " + e.getMessage());
formulaires = new ArrayList<>();
formulairesPopulaires = new ArrayList<>();
}
}
public void selectionnerFormulaire(FormulaireDTO formulaire) {

View File

@@ -1,18 +1,21 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.auth.LoginRequest;
import dev.lions.unionflow.client.dto.auth.LoginResponse;
import dev.lions.unionflow.client.security.JwtTokenManager;
import dev.lions.unionflow.client.service.AuthenticationService;
import jakarta.enterprise.context.RequestScoped;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.validation.Valid;
import org.eclipse.microprofile.jwt.JsonWebToken;
import java.io.IOException;
import java.io.Serializable;
import java.util.logging.Logger;
/**
* Bean de gestion de l'authentification via Keycloak OIDC
*
* @author UnionFlow Team
* @version 2.0
*/
@Named("loginBean")
@RequestScoped
public class LoginBean implements Serializable {
@@ -20,104 +23,33 @@ public class LoginBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(LoginBean.class.getName());
@Inject
private JsonWebToken jwt;
@Inject
private UserSession userSession;
@Inject
private JwtTokenManager tokenManager;
@Inject
private AuthenticationService authService;
private String username;
private String password;
private String typeCompte;
private boolean rememberMe;
public String login() {
/**
* Redirige vers Keycloak pour l'authentification
* L'authentification est gérée automatiquement par Quarkus OIDC
*/
public void login() {
try {
if (username == null || username.trim().isEmpty() ||
password == null || password.trim().isEmpty() ||
typeCompte == null || typeCompte.trim().isEmpty()) {
addErrorMessage("Erreur de validation", "Tous les champs sont requis");
return null;
}
LoginRequest loginRequest = new LoginRequest(username, password, typeCompte);
loginRequest.setRememberMe(rememberMe);
LoginResponse response = authService.authenticate(loginRequest);
// Mettre à jour la session utilisateur
userSession.updateFromLoginResponse(response);
// Gérer les tokens JWT
tokenManager.setTokens(response);
LOGGER.info("Connexion réussie pour: " + username + " (Type: " + typeCompte + ")");
addSuccessMessage("Connexion réussie", "Bienvenue " + userSession.getCurrentUser().getNomComplet());
// Redirection selon le type de compte
return getRedirectUrlForUserType(response.getUser().getTypeCompte());
} catch (AuthenticationService.AuthenticationException e) {
LOGGER.warning("Échec de l'authentification: " + e.getMessage());
addErrorMessage("Erreur de connexion", e.getMessage());
return null;
// La redirection vers Keycloak est gérée automatiquement par Quarkus OIDC
// via la configuration dans application.properties
LOGGER.info("Redirection vers Keycloak pour l'authentification");
} catch (Exception e) {
LOGGER.severe("Erreur inattendue lors de la connexion: " + e.getMessage());
addErrorMessage("Erreur système", "Une erreur inattendue s'est produite. Veuillez réessayer.");
return null;
}
}
public String loginDemo(String demoType) {
try {
String demoUsername;
String demoPassword = "admin";
switch (demoType) {
case "SUPER_ADMIN":
demoUsername = "superadmin";
typeCompte = "SUPER_ADMIN";
break;
case "ADMIN":
demoUsername = "admin";
typeCompte = "ADMIN_ENTITE";
break;
case "MEMBRE":
demoUsername = "membre";
demoPassword = "membre";
typeCompte = "MEMBRE";
break;
default:
addErrorMessage("Erreur", "Type de démo invalide");
return null;
}
this.username = demoUsername;
this.password = demoPassword;
return login();
} catch (Exception e) {
LOGGER.severe("Erreur lors de la connexion démo: " + e.getMessage());
addErrorMessage("Erreur système", "Impossible de se connecter en mode démo");
return null;
LOGGER.severe("Erreur lors de la redirection vers Keycloak: " + e.getMessage());
}
}
/**
* Déconnexion de l'utilisateur
* Redirige vers l'endpoint de déconnexion Keycloak
*/
public String logout() {
try {
// Invalider le token côté serveur si possible
if (tokenManager.hasValidTokens()) {
authService.logout(tokenManager.getAccessToken());
}
// Nettoyer la session locale
tokenManager.clearTokens();
userSession.clearSession();
// Invalider la session JSF
@@ -125,77 +57,28 @@ public class LoginBean implements Serializable {
LOGGER.info("Déconnexion réussie");
return "/pages/public/login?faces-redirect=true";
// Redirection vers Keycloak pour la déconnexion complète
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
String logoutUrl = "/auth/logout";
externalContext.redirect(logoutUrl);
} catch (Exception e) {
return null; // La redirection est gérée par redirect()
} catch (IOException e) {
LOGGER.warning("Erreur lors de la déconnexion: " + e.getMessage());
// Même en cas d'erreur, invalider la session locale
tokenManager.clearTokens();
userSession.clearSession();
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "/pages/public/login?faces-redirect=true";
return "/?faces-redirect=true";
}
}
private String getRedirectUrlForUserType(String typeCompte) {
if (typeCompte == null) {
return "/pages/secure/dashboard?faces-redirect=true";
}
switch (typeCompte) {
case "SUPER_ADMIN":
return "/pages/super-admin/dashboard?faces-redirect=true";
case "ADMIN_ENTITE":
return "/pages/admin/dashboard?faces-redirect=true";
case "MEMBRE":
return "/pages/membre/dashboard?faces-redirect=true";
default:
return "/pages/secure/dashboard?faces-redirect=true";
}
}
private void addErrorMessage(String summary, String detail) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, summary, detail));
}
private void addSuccessMessage(String summary, String detail) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO, summary, detail));
}
// Getters et Setters
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getTypeCompte() {
return typeCompte;
}
public void setTypeCompte(String typeCompte) {
this.typeCompte = typeCompte;
}
public boolean isRememberMe() {
return rememberMe;
}
public void setRememberMe(boolean rememberMe) {
this.rememberMe = rememberMe;
/**
* Vérifie si l'utilisateur est authentifié
*/
public boolean isAuthenticated() {
return jwt != null && jwt.getName() != null;
}
}

View File

@@ -1,11 +1,9 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.MembreDTO;
import dev.lions.unionflow.client.dto.AssociationDTO;
import dev.lions.unionflow.client.service.MembreService;
import dev.lions.unionflow.client.service.AssociationService;
import dev.lions.unionflow.client.service.ValidationService;
import dev.lions.unionflow.client.view.SouscriptionBean;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Named;
import jakarta.inject.Inject;
@@ -18,13 +16,14 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
@Named("membreInscriptionBean")
@RequestScoped
public class MembreInscriptionBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(MembreInscriptionBean.class.getName());
@Inject
@RestClient
@@ -146,22 +145,22 @@ public class MembreInscriptionBean implements Serializable {
// Gestion de la photo si disponible
if (photoBase64 != null && !photoBase64.trim().isEmpty()) {
System.out.println("Photo cadrée reçue: " + photoBase64.length() + " caractères");
// TODO: Appeler service de sauvegarde de photo
LOGGER.info("Photo cadrée reçue: " + photoBase64.length() + " caractères");
// Note: La sauvegarde de la photo sera implémentée ultérieurement via un service dédié.
// Le service appellera l'API backend pour stocker la photo associée au membre.
}
System.out.println("Membre inscrit avec succès: " + membreCreee.getNomComplet());
LOGGER.info("Membre inscrit avec succès: " + membreCreee.getNomComplet());
// Message de succès
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO,
"Inscription soumise", "Votre inscription a été soumise avec succès. Elle sera validée par l'administrateur de votre organisation.");
FacesContext.getCurrentInstance().addMessage(null, message);
return "/pages/public/login?faces-redirect=true";
return "/?faces-redirect=true";
} catch (Exception e) {
System.err.println("Erreur lors de l'inscription: " + e.getMessage());
e.printStackTrace();
LOGGER.severe("Erreur lors de l'inscription: " + e.getMessage());
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Erreur", "Erreur lors de l'inscription: " + e.getMessage());
FacesContext.getCurrentInstance().addMessage(null, message);
@@ -174,7 +173,7 @@ public class MembreInscriptionBean implements Serializable {
if (nom != null && !nom.trim().isEmpty()) {
ValidationService.ValidationResult result = validationService.validateValue(MembreDTO.class, "nom", nom);
if (!result.isValid()) {
System.out.println("Erreur validation nom: " + result.getFirstErrorMessage());
LOGGER.info("Erreur validation nom: " + result.getFirstErrorMessage());
}
}
}
@@ -183,7 +182,7 @@ public class MembreInscriptionBean implements Serializable {
if (prenom != null && !prenom.trim().isEmpty()) {
ValidationService.ValidationResult result = validationService.validateValue(MembreDTO.class, "prenom", prenom);
if (!result.isValid()) {
System.out.println("Erreur validation prénom: " + result.getFirstErrorMessage());
LOGGER.info("Erreur validation prénom: " + result.getFirstErrorMessage());
}
}
}
@@ -192,7 +191,7 @@ public class MembreInscriptionBean implements Serializable {
if (email != null && !email.trim().isEmpty()) {
ValidationService.ValidationResult result = validationService.validateValue(MembreDTO.class, "email", email);
if (!result.isValid()) {
System.out.println("Erreur validation email: " + result.getFirstErrorMessage());
LOGGER.info("Erreur validation email: " + result.getFirstErrorMessage());
}
}
}
@@ -201,7 +200,7 @@ public class MembreInscriptionBean implements Serializable {
if (telephone != null && !telephone.trim().isEmpty()) {
ValidationService.ValidationResult result = validationService.validateValue(MembreDTO.class, "telephone", telephone);
if (!result.isValid()) {
System.out.println("Erreur validation téléphone: " + result.getFirstErrorMessage());
LOGGER.info("Erreur validation téléphone: " + result.getFirstErrorMessage());
}
}
}

View File

@@ -6,18 +6,22 @@ import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Named;
import jakarta.inject.Inject;
import jakarta.annotation.PostConstruct;
import jakarta.faces.context.FacesContext;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.IOException;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.logging.Logger;
@Named("membreListeBean")
@SessionScoped
public class MembreListeBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(MembreListeBean.class.getName());
@Inject
@RestClient
@@ -69,9 +73,8 @@ public class MembreListeBean implements Serializable {
chargerMembres();
chargerStatistiques();
} catch (Exception e) {
System.err.println("Erreur lors de l'initialisation: " + e.getMessage());
e.printStackTrace();
// Données de fallback
LOGGER.severe("Erreur lors de l'initialisation: " + e.getMessage());
// Pas de données mockées - initialiser à zéro
this.totalMembres = 0;
this.membresActifs = 0;
this.cotisationsAJour = 0;
@@ -86,35 +89,16 @@ public class MembreListeBean implements Serializable {
membres = membreService.listerTous();
membresFiltres = new ArrayList<>(membres);
System.out.println("Chargement de " + membres.size() + " membres depuis le serveur");
LOGGER.info("Chargement de " + membres.size() + " membres depuis le serveur");
} catch (Exception e) {
System.err.println("Impossible de charger les membres depuis le serveur: " + e.getMessage());
// Fallback avec données simulées
membres = genererDonneesSimulees();
membresFiltres = new ArrayList<>(membres);
LOGGER.severe("Impossible de charger les membres depuis le serveur: " + e.getMessage());
// Pas de données mockées - laisser la liste vide
membres = new ArrayList<>();
membresFiltres = new ArrayList<>();
}
}
private List<MembreDTO> genererDonneesSimulees() {
List<MembreDTO> membresSimules = new ArrayList<>();
for (int i = 1; i <= 20; i++) {
MembreDTO membre = new MembreDTO();
membre.setId((long) i);
membre.setNumeroMembre("M2024" + String.format("%03d", i));
membre.setNom("Membre" + i);
membre.setPrenom("Prénom" + i);
membre.setEmail("membre" + i + "@example.com");
membre.setTelephone("77 123 45 " + String.format("%02d", i));
membre.setStatut(i % 10 == 0 ? "INACTIF" : "ACTIF");
membre.setDateNaissance(LocalDate.now().minusYears(25 + i));
membre.setProfession("Profession " + (i % 10 + 1));
membre.setAdresse("Adresse " + i);
membresSimules.add(membre);
}
return membresSimules;
}
private void chargerStatistiques() {
try {
// Récupération des statistiques via le service REST
@@ -129,8 +113,8 @@ public class MembreListeBean implements Serializable {
this.cotisationsAJour = (int) (this.membresActifs * 0.85);
} catch (Exception e) {
System.err.println("Impossible de charger les statistiques: " + e.getMessage());
// Utiliser les valeurs de fallback définies dans init()
LOGGER.severe("Impossible de charger les statistiques: " + e.getMessage());
// Pas de données mockées - laisser les valeurs à zéro
}
}
@@ -149,25 +133,15 @@ public class MembreListeBean implements Serializable {
);
membresFiltres = resultats;
System.out.println("Recherche effectuée: " + membresFiltres.size() + " résultats");
LOGGER.info("Recherche effectuée: " + membresFiltres.size() + " résultats");
} catch (Exception e) {
System.err.println("Erreur lors de la recherche: " + e.getMessage());
// En cas d'erreur, appliquer le filtre localement
appliquerFiltreLocal();
LOGGER.severe("Erreur lors de la recherche: " + e.getMessage());
// En cas d'erreur, laisser la liste vide plutôt que des données mockées
membresFiltres = new ArrayList<>();
}
}
private void appliquerFiltreLocal() {
membresFiltres = membres.stream()
.filter(m -> searchFilter.isEmpty() ||
m.getNom().toLowerCase().contains(searchFilter.toLowerCase()) ||
m.getPrenom().toLowerCase().contains(searchFilter.toLowerCase()) ||
m.getEmail().toLowerCase().contains(searchFilter.toLowerCase()))
.filter(m -> statutFilter.isEmpty() || m.getStatut().equals(statutFilter))
.collect(Collectors.toList());
}
public void reinitialiserFiltres() {
searchFilter = "";
statutFilter = "";
@@ -188,18 +162,18 @@ public class MembreListeBean implements Serializable {
public void appliquerFiltresAvances() {
// Appliquer les filtres avancés
System.out.println("Application des filtres avancés");
LOGGER.info("Application des filtres avancés");
}
// Import/Export
public void importerMembres() {
// Logique d'import des membres
System.out.println("Import des membres");
LOGGER.info("Import des membres");
}
public void telechargerModele() {
// Télécharger modèle d'import
System.out.println("Téléchargement du modèle");
LOGGER.info("Téléchargement du modèle");
}
// Actions avec DTOs
@@ -211,9 +185,9 @@ public class MembreListeBean implements Serializable {
try {
membreService.activer(membre.getId());
membre.setStatut("ACTIF");
System.out.println("Membre activé: " + membre.getNomComplet());
LOGGER.info("Membre activé: " + membre.getNomComplet());
} catch (Exception e) {
System.err.println("Erreur lors de l'activation: " + e.getMessage());
LOGGER.severe("Erreur lors de l'activation: " + e.getMessage());
}
}
@@ -221,19 +195,35 @@ public class MembreListeBean implements Serializable {
try {
membreService.desactiver(membre.getId());
membre.setStatut("INACTIF");
System.out.println("Membre désactivé: " + membre.getNomComplet());
LOGGER.info("Membre désactivé: " + membre.getNomComplet());
} catch (Exception e) {
System.err.println("Erreur lors de la désactivation: " + e.getMessage());
LOGGER.severe("Erreur lors de la désactivation: " + e.getMessage());
}
}
public void exporterMembres() {
try {
byte[] excelData = membreService.exporterExcel(formatExport, null, statutFilter.isEmpty() ? null : statutFilter);
// TODO: Gérer le téléchargement du fichier Excel
System.out.println("Export Excel généré: " + excelData.length + " bytes");
// Téléchargement du fichier Excel via JSF
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
response.reset();
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=\"membres_export_" +
LocalDate.now() + "." + (formatExport != null ? formatExport.toLowerCase() : "xlsx") + "\"");
response.setContentLength(excelData.length);
response.getOutputStream().write(excelData);
response.getOutputStream().flush();
facesContext.responseComplete();
LOGGER.info("Export Excel généré et téléchargé: " + excelData.length + " bytes");
} catch (IOException e) {
LOGGER.severe("Erreur lors du téléchargement de l'export: " + e.getMessage());
} catch (Exception e) {
System.err.println("Erreur lors de l'export: " + e.getMessage());
LOGGER.severe("Erreur lors de l'export: " + e.getMessage());
}
}

View File

@@ -1,13 +1,19 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.MembreDTO;
import dev.lions.unionflow.client.service.MembreService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
import org.primefaces.event.FileUploadEvent;
@Named("membreProfilBean")
@@ -15,6 +21,11 @@ import org.primefaces.event.FileUploadEvent;
public class MembreProfilBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(MembreProfilBean.class.getName());
@Inject
@RestClient
private MembreService membreService;
private Membre membre;
private Membre membreEdit;
@@ -25,12 +36,13 @@ public class MembreProfilBean implements Serializable {
private DemandesData demandes;
private HistoriqueData historique;
private ContactData contact;
private Long membreId;
private UUID membreId;
@PostConstruct
public void init() {
if (membreId == null) {
membreId = 1L; // Par défaut pour le test
LOGGER.warning("Aucun membreId fourni, impossible de charger le profil");
return;
}
chargerMembre();
chargerStatistiques();
@@ -43,48 +55,36 @@ public class MembreProfilBean implements Serializable {
}
private void chargerMembre() {
membre = new Membre();
membre.setId(membreId);
membre.setNumeroMembre("M2024001");
membre.setPrenom("Jean");
membre.setNom("DIALLO");
membre.setEmail("jean.diallo@email.com");
membre.setTelephone("77 123 45 67");
membre.setDateNaissance(LocalDate.of(1985, 6, 15));
membre.setGenre("Masculin");
membre.setSituationFamiliale("Marié");
membre.setProfession("Ingénieur Informatique");
membre.setAdresse("Villa n°123, Cité Keur Gorgui");
membre.setVille("Dakar");
membre.setPays("Sénégal");
membre.setTypeMembre("ACTIF");
membre.setStatut("ACTIF");
membre.setEntite("LIONS CLUB Dakar Métropole");
membre.setDateAdhesion(LocalDate.of(2020, 3, 15));
membre.setCotisationStatut("À jour");
membre.setTauxParticipation(85);
// Famille
List<MembreFamille> famille = new ArrayList<>();
MembreFamille epouse = new MembreFamille();
epouse.setNomComplet("Awa DIALLO");
epouse.setRelation("Épouse");
epouse.setDateNaissance(LocalDate.of(1987, 9, 20));
epouse.setBeneficiaire(true);
famille.add(epouse);
MembreFamille enfant1 = new MembreFamille();
enfant1.setNomComplet("Amadou DIALLO");
enfant1.setRelation("Fils");
enfant1.setDateNaissance(LocalDate.of(2010, 12, 5));
enfant1.setBeneficiaire(true);
famille.add(enfant1);
membre.setFamille(famille);
// Copie pour l'édition
membreEdit = new Membre();
copierMembre(membre, membreEdit);
try {
MembreDTO dto = membreService.obtenirParId(membreId);
membre = convertToMembre(dto);
// Copie pour l'édition
membreEdit = new Membre();
copierMembre(membre, membreEdit);
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement du membre: " + e.getMessage());
membre = new Membre();
membre.setId(membreId);
}
}
private Membre convertToMembre(MembreDTO dto) {
Membre membre = new Membre();
membre.setId(dto.getId());
membre.setNumeroMembre(dto.getNumeroMembre());
membre.setPrenom(dto.getPrenom());
membre.setNom(dto.getNom());
membre.setEmail(dto.getEmail());
membre.setTelephone(dto.getTelephone());
membre.setDateNaissance(dto.getDateNaissance());
// Note: Genre, situation familiale, ville, pays, type membre ne sont pas disponibles dans MembreDTO client
// Ces champs seront ajoutés ultérieurement si nécessaire
membre.setProfession(dto.getProfession());
membre.setAdresse(dto.getAdresse());
membre.setStatut(dto.getStatut() != null ? dto.getStatut() : "ACTIF");
membre.setDateAdhesion(dto.getDateInscription() != null ? dto.getDateInscription().toLocalDate() : null);
return membre;
}
private void chargerStatistiques() {
@@ -223,7 +223,7 @@ public class MembreProfilBean implements Serializable {
// Actions
public void changerPhoto(FileUploadEvent event) {
// Logique de changement de photo
System.out.println("Photo changée: " + event.getFile().getFileName());
LOGGER.info("Photo changée: " + event.getFile().getFileName());
}
public String gererCotisations() {
@@ -232,35 +232,35 @@ public class MembreProfilBean implements Serializable {
public void sauvegarderModifications() {
copierMembre(membreEdit, membre);
System.out.println("Profil mis à jour pour: " + membre.getNomComplet());
LOGGER.info("Profil mis à jour pour: " + membre.getNomComplet());
}
public void envoyerMessage() {
System.out.println("Message envoyé: " + contact.getSujet() + " via " + contact.getCanaux());
LOGGER.info("Message envoyé: " + contact.getSujet() + " via " + contact.getCanaux());
contact = new ContactData();
contact.setCanaux(new ArrayList<>());
}
public void envoyerRappelCotisation() {
System.out.println("Rappel de cotisation envoyé à: " + membre.getEmail());
LOGGER.info("Rappel de cotisation envoyé à: " + membre.getEmail());
}
public void suspendre() {
membre.setStatut("SUSPENDU");
System.out.println("Membre suspendu: " + membre.getNomComplet());
LOGGER.info("Membre suspendu: " + membre.getNomComplet());
}
public void reactiver() {
membre.setStatut("ACTIF");
System.out.println("Membre réactivé: " + membre.getNomComplet());
LOGGER.info("Membre réactivé: " + membre.getNomComplet());
}
public void exporterDonnees() {
System.out.println("Export des données pour: " + membre.getNomComplet());
LOGGER.info("Export des données pour: " + membre.getNomComplet());
}
public String supprimer() {
System.out.println("Membre supprimé: " + membre.getNomComplet());
LOGGER.info("Membre supprimé: " + membre.getNomComplet());
return "/pages/secure/membre/liste?faces-redirect=true";
}
@@ -303,12 +303,12 @@ public class MembreProfilBean implements Serializable {
public ContactData getContact() { return contact; }
public void setContact(ContactData contact) { this.contact = contact; }
public Long getMembreId() { return membreId; }
public void setMembreId(Long membreId) { this.membreId = membreId; }
public UUID getMembreId() { return membreId; }
public void setMembreId(UUID membreId) { this.membreId = membreId; }
// Classes internes
public static class Membre {
private Long id;
private UUID id;
private String numeroMembre;
private String prenom;
private String nom;
@@ -331,8 +331,8 @@ public class MembreProfilBean implements Serializable {
private List<MembreFamille> famille = new ArrayList<>();
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNumeroMembre() { return numeroMembre; }
public void setNumeroMembre(String numeroMembre) { this.numeroMembre = numeroMembre; }

View File

@@ -1,19 +1,35 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.MembreDTO;
import dev.lions.unionflow.client.service.MembreService;
import dev.lions.unionflow.client.service.AssociationService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.logging.Logger;
@Named("membreRechercheBean")
@SessionScoped
public class MembreRechercheBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(MembreRechercheBean.class.getName());
@Inject
@RestClient
private MembreService membreService;
@Inject
@RestClient
private AssociationService associationService;
private Filtres filtres;
private Statistiques statistiques;
@@ -47,87 +63,72 @@ public class MembreRechercheBean implements Serializable {
private void initializeStatistiques() {
statistiques = new Statistiques();
statistiques.setTotalMembres(234);
try {
List<MembreDTO> membres = membreService.listerTous();
statistiques.setTotalMembres(membres.size());
} catch (Exception e) {
LOGGER.severe("Erreur lors du calcul des statistiques: " + e.getMessage());
statistiques.setTotalMembres(0);
}
statistiques.setResultatsActuels(0);
statistiques.setFiltresActifs(0);
statistiques.setTempsRecherche(25);
statistiques.setTempsRecherche(0);
}
private void initializeDonnees() {
tousLesMembres = new ArrayList<>();
selectedMembres = new ArrayList<>();
// Génération de données de test
String[] noms = {"DIALLO", "FALL", "NDIAYE", "DIOUF", "SARR", "BA", "SALL", "TOURE", "GUEYE", "NDOUR"};
String[] prenoms = {"Amadou", "Fatou", "Ousmane", "Awa", "Mamadou", "Aida", "Ibrahima", "Mariam", "Moussa", "Khady"};
String[] professions = {"Enseignant", "Médecin", "Ingénieur", "Commerçant", "Agriculteur", "Fonctionnaire", "Artisan", "Avocat"};
String[] villes = {"Dakar", "Thiès", "Kaolack", "Saint-Louis", "Ziguinchor", "Diourbel", "Tambacounda"};
String[] typesMembre = {"ACTIF", "ASSOCIE", "BIENFAITEUR", "HONORAIRE"};
String[] statuts = {"ACTIF", "INACTIF", "SUSPENDU"};
String[] entites = {"LIONS CLUB Dakar Métropole", "LIONS CLUB Thiès", "LIONS CLUB Kaolack"};
for (int i = 1; i <= 50; i++) {
Membre membre = new Membre();
membre.setId((long) i);
membre.setNumeroMembre("M2024" + String.format("%03d", i));
membre.setNom(noms[i % noms.length]);
membre.setPrenom(prenoms[i % prenoms.length]);
membre.setEmail(membre.getPrenom().toLowerCase() + "." + membre.getNom().toLowerCase() + "@email.com");
membre.setTelephone("77 123 45 " + String.format("%02d", i));
membre.setProfession(professions[i % professions.length]);
membre.setVille(villes[i % villes.length]);
membre.setTypeMembre(typesMembre[i % typesMembre.length]);
membre.setStatut(statuts[i % statuts.length]);
membre.setEntite(entites[i % entites.length]);
membre.setDateAdhesion(LocalDate.now().minusMonths(i));
membre.setCotisationStatut(i % 5 == 0 ? "En retard" : "À jour");
membre.setTauxParticipation(60 + (i % 40));
membre.setEvenementsAnnee(i % 15 + 2);
membre.setGenre(i % 2 == 0 ? "M" : "F");
membre.setAge(25 + (i % 40));
membre.setADesEnfants(i % 3 == 0);
membre.setARecuAides(i % 7 == 0);
tousLesMembres.add(membre);
try {
List<MembreDTO> membresDTO = membreService.listerTous();
for (MembreDTO dto : membresDTO) {
Membre membre = convertToMembre(dto);
tousLesMembres.add(membre);
}
resultats = new ArrayList<>(tousLesMembres);
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des membres: " + e.getMessage());
resultats = new ArrayList<>();
}
resultats = new ArrayList<>(tousLesMembres);
}
private Membre convertToMembre(MembreDTO dto) {
Membre membre = new Membre();
membre.setId(dto.getId());
membre.setNumeroMembre(dto.getNumeroMembre());
membre.setNom(dto.getNom());
membre.setPrenom(dto.getPrenom());
membre.setEmail(dto.getEmail());
membre.setTelephone(dto.getTelephone());
membre.setProfession(dto.getProfession());
membre.setVille(""); // Ville non disponible dans MembreDTO
membre.setTypeMembre("ACTIF"); // Type membre non disponible dans MembreDTO
if (dto.getStatut() != null) {
membre.setStatut(dto.getStatut());
} else {
membre.setStatut("ACTIF");
}
membre.setDateAdhesion(dto.getDateInscription() != null ? dto.getDateInscription().toLocalDate() : null);
return membre;
}
private void initializeEntites() {
entitesDisponibles = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
Entite entite = new Entite();
entite.setId((long) i);
entite.setNom("LIONS CLUB " + (i == 1 ? "Dakar Métropole" :
i == 2 ? "Thiès" :
i == 3 ? "Kaolack" :
i == 4 ? "Saint-Louis" : "Ziguinchor"));
entitesDisponibles.add(entite);
try {
List<dev.lions.unionflow.client.dto.AssociationDTO> associations = associationService.listerActives();
for (dev.lions.unionflow.client.dto.AssociationDTO assoc : associations) {
Entite entite = new Entite();
entite.setId(assoc.getId());
entite.setNom(assoc.getNom());
entitesDisponibles.add(entite);
}
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des entités: " + e.getMessage());
}
}
private void initializeRecherchesSauvegardees() {
recherchesSauvegardees = new ArrayList<>();
RechercheSauvegardee recherche1 = new RechercheSauvegardee();
recherche1.setId(1L);
recherche1.setNom("Membres actifs 2024");
recherche1.setDescription("Tous les membres actifs adhérés en 2024");
recherche1.setNombreCriteres(3);
recherche1.setDateCreation(LocalDate.now().minusDays(5));
recherche1.setPublique(true);
recherchesSauvegardees.add(recherche1);
RechercheSauvegardee recherche2 = new RechercheSauvegardee();
recherche2.setId(2L);
recherche2.setNom("Cotisations en retard");
recherche2.setDescription("Membres avec cotisations en retard");
recherche2.setNombreCriteres(2);
recherche2.setDateCreation(LocalDate.now().minusDays(2));
recherche2.setPublique(false);
recherchesSauvegardees.add(recherche2);
nouvelleRechercheSauvegardee = new RechercheSauvegardee();
}
@@ -140,9 +141,25 @@ public class MembreRechercheBean implements Serializable {
public void effectuerRecherche() {
long startTime = System.currentTimeMillis();
resultats = tousLesMembres.stream()
.filter(this::appliquerFiltres)
try {
List<MembreDTO> membresDTO = membreService.rechercher(
filtres.getNom(),
filtres.getPrenom(),
filtres.getEmail(),
filtres.getTelephone(),
filtres.getStatuts() != null && !filtres.getStatuts().isEmpty() ? filtres.getStatuts().get(0) : null,
null,
0,
100
);
resultats = membresDTO.stream()
.map(this::convertToMembre)
.collect(Collectors.toList());
} catch (Exception e) {
LOGGER.severe("Erreur lors de la recherche: " + e.getMessage());
resultats = new ArrayList<>();
}
long endTime = System.currentTimeMillis();
@@ -309,22 +326,22 @@ public class MembreRechercheBean implements Serializable {
}
public void contacterMembre(Membre membre) {
System.out.println("Contacter le membre: " + membre.getNomComplet());
LOGGER.info("Contacter le membre: " + membre.getNomComplet());
}
public void ajouterAuGroupe(Membre membre) {
System.out.println("Ajouter au groupe: " + membre.getNomComplet());
LOGGER.info("Ajouter au groupe: " + membre.getNomComplet());
}
// Gestion des recherches sauvegardées
public void sauvegarderRecherche() {
nouvelleRechercheSauvegardee.setId((long) (recherchesSauvegardees.size() + 1));
nouvelleRechercheSauvegardee.setId(UUID.randomUUID());
nouvelleRechercheSauvegardee.setNombreCriteres(compterFiltresActifs());
nouvelleRechercheSauvegardee.setDateCreation(LocalDate.now());
recherchesSauvegardees.add(nouvelleRechercheSauvegardee);
System.out.println("Recherche sauvegardée: " + nouvelleRechercheSauvegardee.getNom());
LOGGER.info("Recherche sauvegardée: " + nouvelleRechercheSauvegardee.getNom());
nouvelleRechercheSauvegardee = new RechercheSauvegardee();
}
@@ -341,17 +358,17 @@ public class MembreRechercheBean implements Serializable {
}
effectuerRecherche();
System.out.println("Recherche chargée: " + recherche.getNom());
LOGGER.info("Recherche chargée: " + recherche.getNom());
}
public void supprimerRecherche(RechercheSauvegardee recherche) {
recherchesSauvegardees.remove(recherche);
System.out.println("Recherche supprimée: " + recherche.getNom());
LOGGER.info("Recherche supprimée: " + recherche.getNom());
}
// Actions groupées
public void envoyerMessageGroupe() {
System.out.println("Message '" + messageGroupe.getSujet() + "' envoyé à " +
LOGGER.info("Message '" + messageGroupe.getSujet() + "' envoyé à " +
selectedMembres.size() + " membres via " + messageGroupe.getCanaux());
messageGroupe = new MessageGroupe();
@@ -359,7 +376,7 @@ public class MembreRechercheBean implements Serializable {
}
public void exporterSelection() {
System.out.println("Export de " + selectedMembres.size() + " membres sélectionnés");
LOGGER.info("Export de " + selectedMembres.size() + " membres sélectionnés");
}
// Méthodes d'autocomplétion
@@ -514,7 +531,7 @@ public class MembreRechercheBean implements Serializable {
}
public static class Membre {
private Long id;
private UUID id;
private String numeroMembre;
private String nom;
private String prenom;
@@ -536,8 +553,8 @@ public class MembreRechercheBean implements Serializable {
private boolean aRecuAides;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNumeroMembre() { return numeroMembre; }
public void setNumeroMembre(String numeroMembre) { this.numeroMembre = numeroMembre; }
@@ -661,18 +678,18 @@ public class MembreRechercheBean implements Serializable {
}
public static class Entite {
private Long id;
private UUID id;
private String nom;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }
}
public static class RechercheSauvegardee {
private Long id;
private UUID id;
private String nom;
private String description;
private int nombreCriteres;
@@ -680,8 +697,8 @@ public class MembreRechercheBean implements Serializable {
private boolean publique;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }

View File

@@ -1,6 +1,5 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.security.JwtTokenManager;
import jakarta.enterprise.context.RequestScoped;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
@@ -9,6 +8,12 @@ import java.io.IOException;
import java.io.Serializable;
import java.util.logging.Logger;
/**
* Bean de navigation avec authentification Keycloak OIDC
*
* @author UnionFlow Team
* @version 2.0
*/
@Named("navigationBean")
@RequestScoped
public class NavigationBean implements Serializable {
@@ -19,9 +24,6 @@ public class NavigationBean implements Serializable {
@Inject
private UserSession userSession;
@Inject
private JwtTokenManager tokenManager;
public void checkAuthentication() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
@@ -32,15 +34,16 @@ public class NavigationBean implements Serializable {
context.getExternalContext().getRequestContextPath() + dashboardUrl
);
} else {
// L'utilisateur n'est pas connecté, rediriger vers la page de login
// L'utilisateur n'est pas connecté, rediriger vers la racine qui déclenchera Keycloak
context.getExternalContext().redirect(
context.getExternalContext().getRequestContextPath() + "/pages/public/login.xhtml"
context.getExternalContext().getRequestContextPath() + "/"
);
}
}
public String redirectToLogin() {
return "/pages/public/login?faces-redirect=true";
// Redirection vers la racine qui déclenchera automatiquement Keycloak
return "/?faces-redirect=true";
}
public String goToDashboard() {
@@ -78,10 +81,8 @@ public class NavigationBean implements Serializable {
}
private boolean isUserAuthenticated() {
return userSession != null &&
userSession.isAuthenticated() &&
tokenManager != null &&
tokenManager.hasValidTokens();
// Avec Keycloak OIDC, UserSession vérifie automatiquement l'authentification via JsonWebToken
return userSession != null && userSession.isAuthenticated();
}
private String getDashboardUrlForUserType() {

View File

@@ -1,20 +1,57 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.service.AnalyticsService;
import dev.lions.unionflow.client.service.MembreService;
import dev.lions.unionflow.client.service.CotisationService;
import dev.lions.unionflow.client.service.EvenementService;
import dev.lions.unionflow.client.service.DemandeAideService;
import dev.lions.unionflow.client.service.AssociationService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.math.BigDecimal;
import java.util.logging.Logger;
@Named("rapportsBean")
@SessionScoped
public class RapportsBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(RapportsBean.class.getName());
@Inject
@RestClient
private AnalyticsService analyticsService;
@Inject
@RestClient
private MembreService membreService;
@Inject
@RestClient
private CotisationService cotisationService;
@Inject
@RestClient
private EvenementService evenementService;
@Inject
@RestClient
private DemandeAideService demandeAideService;
@Inject
@RestClient
private AssociationService associationService;
private String organisationId; // À injecter depuis la session
private String periodeRapide;
private LocalDate dateDebut;
@@ -57,260 +94,212 @@ public class RapportsBean implements Serializable {
private void initializeIndicateurs() {
indicateurs = new IndicateursGlobaux();
indicateurs.setTotalMembres(1247);
indicateurs.setCroissanceMembres(8.5);
indicateurs.setRevenus("45 750 000 FCFA");
indicateurs.setCroissanceRevenus(12.3);
indicateurs.setTotalEvenements(42);
indicateurs.setCroissanceEvenements(15.7);
indicateurs.setTotalAides("12 850 000 FCFA");
indicateurs.setCroissanceAides(22.1);
try {
int totalMembres = membreService.listerTous().size();
int totalEvenements = evenementService.listerTous(0, 1000).size();
BigDecimal totalRevenus = cotisationService.listerToutes(0, 1000).stream()
.filter(c -> "PAYEE".equals(c.getStatut()) || "PARTIELLEMENT_PAYEE".equals(c.getStatut()))
.map(c -> c.getMontantPaye() != null ? c.getMontantPaye() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal totalAides = demandeAideService.listerToutes(0, 1000).stream()
.filter(d -> d.getMontantAccorde() != null)
.map(d -> d.getMontantAccorde())
.reduce(BigDecimal.ZERO, BigDecimal::add);
indicateurs.setTotalMembres(totalMembres);
indicateurs.setCroissanceMembres(0.0); // À calculer depuis les données historiques
indicateurs.setRevenus(formatMontantCourt(totalRevenus) + " FCFA");
indicateurs.setCroissanceRevenus(0.0); // À calculer depuis les données historiques
indicateurs.setTotalEvenements(totalEvenements);
indicateurs.setCroissanceEvenements(0.0); // À calculer depuis les données historiques
indicateurs.setTotalAides(formatMontantCourt(totalAides) + " FCFA");
indicateurs.setCroissanceAides(0.0); // À calculer depuis les données historiques
} catch (Exception e) {
LOGGER.severe("Erreur lors du calcul des indicateurs: " + e.getMessage());
indicateurs.setTotalMembres(0);
indicateurs.setCroissanceMembres(0.0);
indicateurs.setRevenus("0 FCFA");
indicateurs.setCroissanceRevenus(0.0);
indicateurs.setTotalEvenements(0);
indicateurs.setCroissanceEvenements(0.0);
indicateurs.setTotalAides("0 FCFA");
indicateurs.setCroissanceAides(0.0);
}
}
private void initializeEvolutionMensuelle() {
evolutionMensuelle = new ArrayList<>();
String[] mois = {"Jan", "Fév", "Mar", "Avr", "Mai", "Jun"};
int[] membres = {1150, 1180, 1205, 1225, 1240, 1247};
double[] revenus = {3.2, 3.8, 4.1, 4.5, 3.9, 4.2};
for (int i = 0; i < mois.length; i++) {
EvolutionMensuelle evolution = new EvolutionMensuelle();
evolution.setLibelle(mois[i]);
evolution.setMembres(membres[i]);
evolution.setRevenus(revenus[i]);
evolution.setHauteurMembres((int) ((membres[i] - 1100) / 2));
evolution.setHauteurRevenus((int) (revenus[i] * 15));
evolutionMensuelle.add(evolution);
try {
if (organisationId != null && dateDebut != null && dateFin != null) {
String periode = "CUSTOM"; // À mapper depuis dateDebut/dateFin
analyticsService.getEvolutionMensuelle("NOMBRE_MEMBRES_ACTIFS", organisationId, periode);
// Traiter les données de l'API
// Pour l'instant, initialiser avec des données vides
}
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement de l'évolution mensuelle: " + e.getMessage());
}
// Si pas de données, laisser la liste vide plutôt que des données mockées
}
private void initializeObjectifs() {
objectifs = new ArrayList<>();
Objectif obj1 = new Objectif();
obj1.setLibelle("Nouveaux Membres");
obj1.setRealise("247");
obj1.setCible("300");
obj1.setPourcentage(82);
objectifs.add(obj1);
Objectif obj2 = new Objectif();
obj2.setLibelle("Revenus Cotisations");
obj2.setRealise("38.5M");
obj2.setCible("45M");
obj2.setPourcentage(86);
objectifs.add(obj2);
Objectif obj3 = new Objectif();
obj3.setLibelle("Événements Organisés");
obj3.setRealise("42");
obj3.setCible("50");
obj3.setPourcentage(84);
objectifs.add(obj3);
Objectif obj4 = new Objectif();
obj4.setLibelle("Aides Accordées");
obj4.setRealise("156");
obj4.setCible("200");
obj4.setPourcentage(78);
objectifs.add(obj4);
try {
int totalMembres = membreService.listerTous().size();
int totalEvenements = evenementService.listerTous(0, 1000).size();
int totalDemandes = demandeAideService.listerToutes(0, 1000).size();
BigDecimal totalRevenus = cotisationService.listerToutes(0, 1000).stream()
.filter(c -> "PAYEE".equals(c.getStatut()) || "PARTIELLEMENT_PAYEE".equals(c.getStatut()))
.map(c -> c.getMontantPaye() != null ? c.getMontantPaye() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Objectif obj1 = new Objectif();
obj1.setLibelle("Nouveaux Membres");
obj1.setRealise(String.valueOf(totalMembres));
obj1.setCible(String.valueOf((int) (totalMembres * 1.2))); // Objectif 20% supérieur
obj1.setPourcentage(totalMembres > 0 ? (int) ((double) totalMembres / (totalMembres * 1.2) * 100) : 0);
objectifs.add(obj1);
Objectif obj2 = new Objectif();
obj2.setLibelle("Revenus Cotisations");
obj2.setRealise(formatMontantCourt(totalRevenus));
obj2.setCible(formatMontantCourt(totalRevenus.multiply(new BigDecimal("1.2"))));
obj2.setPourcentage(83); // À calculer
objectifs.add(obj2);
Objectif obj3 = new Objectif();
obj3.setLibelle("Événements Organisés");
obj3.setRealise(String.valueOf(totalEvenements));
obj3.setCible(String.valueOf((int) (totalEvenements * 1.2)));
obj3.setPourcentage(totalEvenements > 0 ? (int) ((double) totalEvenements / (totalEvenements * 1.2) * 100) : 0);
objectifs.add(obj3);
Objectif obj4 = new Objectif();
obj4.setLibelle("Aides Accordées");
obj4.setRealise(String.valueOf(totalDemandes));
obj4.setCible(String.valueOf((int) (totalDemandes * 1.3)));
obj4.setPourcentage(totalDemandes > 0 ? (int) ((double) totalDemandes / (totalDemandes * 1.3) * 100) : 0);
objectifs.add(obj4);
} catch (Exception e) {
LOGGER.severe("Erreur lors du calcul des objectifs: " + e.getMessage());
}
}
private String formatMontantCourt(BigDecimal montant) {
if (montant == null) return "0";
double millions = montant.doubleValue() / 1_000_000.0;
if (millions >= 1) {
return String.format("%.1fM", millions);
}
return String.format("%.0fK", montant.doubleValue() / 1_000.0);
}
private void initializeRepartitions() {
repartitionMembres = new ArrayList<>();
RepartitionMembres actifs = new RepartitionMembres();
actifs.setLibelle("Membres Actifs");
actifs.setNombre(987);
actifs.setPourcentage(79.2);
actifs.setCouleur("green-500");
repartitionMembres.add(actifs);
RepartitionMembres inactifs = new RepartitionMembres();
inactifs.setLibelle("Membres Inactifs");
inactifs.setNombre(185);
inactifs.setPourcentage(14.8);
inactifs.setCouleur("orange-500");
repartitionMembres.add(inactifs);
RepartitionMembres nouveaux = new RepartitionMembres();
nouveaux.setLibelle("Nouveaux Membres");
nouveaux.setNombre(75);
nouveaux.setPourcentage(6.0);
nouveaux.setCouleur("blue-500");
repartitionMembres.add(nouveaux);
try {
List<dev.lions.unionflow.client.dto.MembreDTO> membres = membreService.listerTous();
long actifs = membres.stream().filter(m -> "ACTIF".equals(m.getStatut())).count();
long inactifs = membres.stream().filter(m -> "INACTIF".equals(m.getStatut())).count();
long total = membres.size();
if (total > 0) {
RepartitionMembres actifsRep = new RepartitionMembres();
actifsRep.setLibelle("Membres Actifs");
actifsRep.setNombre((int) actifs);
actifsRep.setPourcentage((double) actifs / total * 100.0);
actifsRep.setCouleur("green-500");
repartitionMembres.add(actifsRep);
RepartitionMembres inactifsRep = new RepartitionMembres();
inactifsRep.setLibelle("Membres Inactifs");
inactifsRep.setNombre((int) inactifs);
inactifsRep.setPourcentage((double) inactifs / total * 100.0);
inactifsRep.setCouleur("orange-500");
repartitionMembres.add(inactifsRep);
}
} catch (Exception e) {
LOGGER.severe("Erreur lors du calcul de la répartition des membres: " + e.getMessage());
}
sourceRevenus = new ArrayList<>();
SourceRevenus cotisations = new SourceRevenus();
cotisations.setLibelle("Cotisations");
cotisations.setMontant("28.5M FCFA");
cotisations.setPourcentage(62.3);
cotisations.setCouleur("blue-500");
cotisations.setIcon("pi-users");
sourceRevenus.add(cotisations);
SourceRevenus evenements = new SourceRevenus();
evenements.setLibelle("Événements");
evenements.setMontant("12.8M FCFA");
evenements.setPourcentage(28.0);
evenements.setCouleur("green-500");
evenements.setIcon("pi-calendar");
sourceRevenus.add(evenements);
SourceRevenus dons = new SourceRevenus();
dons.setLibelle("Dons & Subventions");
dons.setMontant("4.4M FCFA");
dons.setPourcentage(9.7);
dons.setCouleur("purple-500");
dons.setIcon("pi-heart");
sourceRevenus.add(dons);
try {
BigDecimal totalRevenus = cotisationService.listerToutes(0, 1000).stream()
.filter(c -> "PAYEE".equals(c.getStatut()) || "PARTIELLEMENT_PAYEE".equals(c.getStatut()))
.map(c -> c.getMontantPaye() != null ? c.getMontantPaye() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
if (totalRevenus.compareTo(BigDecimal.ZERO) > 0) {
SourceRevenus cotisations = new SourceRevenus();
cotisations.setLibelle("Cotisations");
cotisations.setMontant(formatMontantCourt(totalRevenus));
cotisations.setPourcentage(100.0); // Pour l'instant, uniquement cotisations
cotisations.setCouleur("blue-500");
cotisations.setIcon("pi-users");
sourceRevenus.add(cotisations);
}
} catch (Exception e) {
LOGGER.severe("Erreur lors du calcul des sources de revenus: " + e.getMessage());
}
}
private void initializeTopEntites() {
topEntites = new ArrayList<>();
String[] noms = {
"Association Alpha Centrale", "Club Beta Régional", "Groupe Gamma Local",
"Association Delta Nord", "Club Epsilon Sud"
};
String[] icons = {"pi-users", "pi-home", "pi-sitemap", "pi-users", "pi-home"};
int[] scores = {95, 89, 84, 78, 72};
String[] tendances = {"UP", "UP", "STABLE", "DOWN", "UP"};
for (int i = 0; i < noms.length; i++) {
TopEntite entite = new TopEntite();
entite.setRang(i + 1);
entite.setNom(noms[i]);
entite.setTypeIcon(icons[i]);
entite.setScore(scores[i]);
entite.setTendance(tendances[i]);
topEntites.add(entite);
try {
List<dev.lions.unionflow.client.dto.AssociationDTO> associations = associationService.listerActives();
topEntites = associations.stream()
.sorted((a1, a2) -> {
int m1 = a1.getNombreMembres() != null ? a1.getNombreMembres() : 0;
int m2 = a2.getNombreMembres() != null ? a2.getNombreMembres() : 0;
return Integer.compare(m2, m1);
})
.limit(5)
.map(a -> {
TopEntite entite = new TopEntite();
entite.setRang(0); // À calculer
entite.setNom(a.getNom());
entite.setTypeIcon("pi-users");
entite.setScore(0); // À calculer depuis les métriques
entite.setTendance("STABLE"); // À calculer
return entite;
})
.collect(java.util.stream.Collectors.toList());
// Assigner les rangs
for (int i = 0; i < topEntites.size(); i++) {
topEntites.get(i).setRang(i + 1);
}
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des top entités: " + e.getMessage());
}
}
private void initializeKPIs() {
kpis = new ArrayList<>();
KPI participation = new KPI();
participation.setLibelle("Taux de Participation");
participation.setValeur("84%");
participation.setProgression(84);
participation.setVariation(5.2);
participation.setTendance("UP");
participation.setIcon("pi-users");
participation.setCouleur("blue-500");
kpis.add(participation);
KPI satisfaction = new KPI();
satisfaction.setLibelle("Satisfaction Membres");
satisfaction.setValeur("92%");
satisfaction.setProgression(92);
satisfaction.setVariation(3.1);
satisfaction.setTendance("UP");
satisfaction.setIcon("pi-star");
satisfaction.setCouleur("green-500");
kpis.add(satisfaction);
KPI retention = new KPI();
retention.setLibelle("Taux de Rétention");
retention.setValeur("88%");
retention.setProgression(88);
retention.setVariation(-1.8);
retention.setTendance("DOWN");
retention.setIcon("pi-refresh");
retention.setCouleur("orange-500");
kpis.add(retention);
KPI croissance = new KPI();
croissance.setLibelle("Croissance Mensuelle");
croissance.setValeur("2.1%");
croissance.setProgression(75);
croissance.setVariation(0.8);
croissance.setTendance("UP");
croissance.setIcon("pi-chart-line");
croissance.setCouleur("purple-500");
kpis.add(croissance);
try {
if (organisationId != null && dateDebut != null && dateFin != null) {
String periode = "CUSTOM"; // À mapper depuis dateDebut/dateFin
analyticsService.getKPIs(organisationId, periode);
// Traiter les données de l'API
// Pour l'instant, laisser la liste vide plutôt que des données mockées
}
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des KPIs: " + e.getMessage());
}
}
private void initializeAlertes() {
alertes = new ArrayList<>();
Alerte baisseCotisations = new Alerte();
baisseCotisations.setTitre("Baisse des Cotisations");
baisseCotisations.setDescription("Les cotisations ont diminué de 8% ce mois-ci par rapport au mois précédent.");
baisseCotisations.setPriorite("HAUTE");
baisseCotisations.setSeverite("warning");
baisseCotisations.setSeveriteCouleur("orange-500");
baisseCotisations.setIcon("pi-exclamation-triangle");
baisseCotisations.setDateDetection("Il y a 2 jours");
alertes.add(baisseCotisations);
Alerte objectifManque = new Alerte();
objectifManque.setTitre("Objectif Membres Non Atteint");
objectifManque.setDescription("L'objectif de nouveaux membres risque de ne pas être atteint ce trimestre.");
objectifManque.setPriorite("MOYENNE");
objectifManque.setSeverite("info");
objectifManque.setSeveriteCouleur("blue-500");
objectifManque.setIcon("pi-info-circle");
objectifManque.setDateDetection("Il y a 5 jours");
alertes.add(objectifManque);
Alerte budgetDepasse = new Alerte();
budgetDepasse.setTitre("Budget Événements Dépassé");
budgetDepasse.setDescription("Le budget alloué aux événements a été dépassé de 15% ce mois-ci.");
budgetDepasse.setPriorite("CRITIQUE");
budgetDepasse.setSeverite("danger");
budgetDepasse.setSeveriteCouleur("red-500");
budgetDepasse.setIcon("pi-exclamation-circle");
budgetDepasse.setDateDetection("Hier");
alertes.add(budgetDepasse);
Alerte performance = new Alerte();
performance.setTitre("Performance Excellente");
performance.setDescription("Félicitations ! La satisfaction des membres a atteint un niveau record de 92%.");
performance.setPriorite("INFO");
performance.setSeverite("success");
performance.setSeveriteCouleur("green-500");
performance.setIcon("pi-check-circle");
performance.setDateDetection("Il y a 1 semaine");
alertes.add(performance);
// Les alertes seront calculées depuis les données réelles
// Pour l'instant, laisser la liste vide plutôt que des données mockées
}
private void initializeHistoriqueRapports() {
historiqueRapports = new ArrayList<>();
String[] types = {"FINANCIER", "MEMBRES", "ACTIVITES", "PERFORMANCE", "COMPLET"};
String[] typesLibelles = {"Rapport Financier", "Rapport Membres", "Rapport Activités", "Rapport Performance", "Rapport Complet"};
String[] typeIcons = {"pi-dollar", "pi-users", "pi-calendar", "pi-chart-bar", "pi-file"};
String[] typeCouleurs = {"green-500", "blue-500", "orange-500", "purple-500", "indigo-500"};
String[] generesPar = {"Marie Kouassi", "Paul Traoré", "Fatou Sanogo", "Jean Ouattara", "Aissata Koné"};
String[] statuts = {"GENERE", "EN_COURS", "GENERE", "GENERE", "PLANIFIE"};
for (int i = 0; i < 15; i++) {
HistoriqueRapport rapport = new HistoriqueRapport();
rapport.setId((long) (i + 1));
rapport.setType(types[i % types.length]);
rapport.setTypeLibelle(typesLibelles[i % typesLibelles.length]);
rapport.setTypeIcon(typeIcons[i % typeIcons.length]);
rapport.setTypeCouleur(typeCouleurs[i % typeCouleurs.length]);
rapport.setDateGeneration(LocalDate.now().minusDays(i * 7 + 1));
rapport.setPeriodeCouverte(getDescriptionPeriode(i));
rapport.setGenerePar(generesPar[i % generesPar.length]);
rapport.setStatut(statuts[i % statuts.length]);
historiqueRapports.add(rapport);
}
}
private String getDescriptionPeriode(int index) {
return switch (index % 5) {
case 0 -> "Dernières 4 semaines";
case 1 -> "Trimestre en cours";
case 2 -> "6 derniers mois";
case 3 -> "Année en cours";
default -> "Période personnalisée";
};
// L'historique des rapports sera chargé depuis la base de données
// Pour l'instant, laisser la liste vide plutôt que des données mockées
}
private void initializeNouveauRapport() {
@@ -322,10 +311,10 @@ public class RapportsBean implements Serializable {
// Actions
public void genererRapport() {
System.out.println("Génération du rapport " + nouveauRapport.getType() + " en format " + nouveauRapport.getFormat());
LOGGER.info("Génération du rapport " + nouveauRapport.getType() + " en format " + nouveauRapport.getFormat());
HistoriqueRapport nouveauHistorique = new HistoriqueRapport();
nouveauHistorique.setId((long) (historiqueRapports.size() + 1));
nouveauHistorique.setId(UUID.randomUUID());
nouveauHistorique.setType(nouveauRapport.getType());
nouveauHistorique.setTypeLibelle(getTypeLibelle(nouveauRapport.getType()));
nouveauHistorique.setTypeIcon(getTypeIcon(nouveauRapport.getType()));
@@ -389,11 +378,11 @@ public class RapportsBean implements Serializable {
}
public void telechargerRapport(HistoriqueRapport rapport) {
System.out.println("Téléchargement du rapport: " + rapport.getTypeLibelle());
LOGGER.info("Téléchargement du rapport: " + rapport.getTypeLibelle());
}
public void exporterDonnees() {
System.out.println("Export des données statistiques");
LOGGER.info("Export des données statistiques");
}
// Getters et Setters
@@ -656,7 +645,7 @@ public class RapportsBean implements Serializable {
}
public static class HistoriqueRapport {
private Long id;
private UUID id;
private String type;
private String typeLibelle;
private String typeIcon;
@@ -667,8 +656,8 @@ public class RapportsBean implements Serializable {
private String statut;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }

View File

@@ -1,20 +1,30 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.AssociationDTO;
import dev.lions.unionflow.client.dto.FormulaireDTO;
import dev.lions.unionflow.client.dto.SouscriptionDTO;
import dev.lions.unionflow.client.service.SouscriptionService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
@Named("souscriptionBean")
@SessionScoped
public class SouscriptionBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(SouscriptionBean.class.getName());
@Inject
@RestClient
private SouscriptionService souscriptionService;
private UUID organisationId; // À injecter depuis la session
private List<SouscriptionDTO> souscriptionsOrganisation;
private SouscriptionDTO souscriptionActive;
@@ -31,33 +41,31 @@ public class SouscriptionBean implements Serializable {
private boolean alerteQuotaProche = false;
private int joursAvantExpiration = 0;
public SouscriptionBean() {
initializeData();
@PostConstruct
public void init() {
if (organisationId != null) {
initializeData();
} else {
LOGGER.warning("Aucun organisationId fourni, impossible de charger les souscriptions");
souscriptionsOrganisation = new ArrayList<>();
}
}
private void initializeData() {
// Simulation d'une souscription active pour démonstration
souscriptionActive = new SouscriptionDTO();
souscriptionActive.setId(1L);
souscriptionActive.setOrganisationId(1L);
souscriptionActive.setOrganisationNom("LIONS CLUB Dakar Métropole");
souscriptionActive.setFormulaireId(2L);
souscriptionActive.setFormulaireNom("Standard");
souscriptionActive.setStatut(SouscriptionDTO.StatutSouscription.ACTIVE);
souscriptionActive.setTypeFacturation(SouscriptionDTO.TypeFacturation.ANNUEL);
souscriptionActive.setDateDebut(LocalDate.now().minusMonths(3));
souscriptionActive.setDateFin(LocalDate.now().plusMonths(9));
souscriptionActive.setQuotaMaxMembres(200);
souscriptionActive.setMembresActuels(87);
souscriptionActive.setMontantSouscription(new java.math.BigDecimal("30000"));
souscriptionActive.setNotificationExpiration(true);
souscriptionActive.setNotificationQuotaAtteint(true);
// Calculer les statistiques
updateStatistiques();
souscriptionsOrganisation = new ArrayList<>();
souscriptionsOrganisation.add(souscriptionActive);
try {
souscriptionsOrganisation = souscriptionService.listerToutes(organisationId, 0, 100);
souscriptionActive = souscriptionService.obtenirActive(organisationId);
if (souscriptionActive == null && !souscriptionsOrganisation.isEmpty()) {
souscriptionActive = souscriptionsOrganisation.stream()
.filter(s -> s.getStatut() == SouscriptionDTO.StatutSouscription.ACTIVE)
.findFirst()
.orElse(null);
}
updateStatistiques();
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des souscriptions: " + e.getMessage());
souscriptionsOrganisation = new ArrayList<>();
}
}
private void updateStatistiques() {

View File

@@ -1,8 +1,12 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.AssociationDTO;
import dev.lions.unionflow.client.service.AssociationService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
@@ -10,12 +14,19 @@ import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
@Named("superAdminBean")
@SessionScoped
public class SuperAdminBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(SuperAdminBean.class.getName());
@Inject
@RestClient
private AssociationService associationService;
private String nomComplet;
private String derniereConnexion;
@@ -71,31 +82,43 @@ public class SuperAdminBean implements Serializable {
}
private void initializeKPIs() {
totalEntites = 127; // Ajusté avec la stratégie volume
totalAdministrateurs = 127;
totalMembres = 18547; // Moyenne 146 membres par entité
revenusGlobaux = "363 000 FCFA";
alertesCount = 8;
croissanceEntites = "12";
activiteJournaliere = 1247;
// Initialiser les métriques de souscription
totalSouscriptions = 127;
souscriptionsActives = 127;
souscriptionsExpirantSous30Jours = 12;
tauxConversion = 68.5f;
// Revenus par forfait - nouvelle grille tarifaire
revenusStarter = new BigDecimal("88000"); // 44 organisations * 2000 FCFA
revenusStandard = new BigDecimal("180000"); // 60 organisations * 3000 FCFA
revenusPremmium = new BigDecimal("80000"); // 20 organisations * 4000 FCFA
revenusCristal = new BigDecimal("15000"); // 3 organisations * 5000 FCFA
// Métriques système
disponibiliteSysteme = 99.8f;
tempsReponsMoyen = 145; // ms
ticketsSupportOuverts = 8;
satisfactionClient = 4.7f; // /5
try {
List<AssociationDTO> associations = associationService.listerToutes();
totalEntites = associations.size();
totalAdministrateurs = associations.size(); // À calculer depuis les utilisateurs
int totalMembresCalc = associations.stream()
.mapToInt(a -> a.getNombreMembres() != null ? a.getNombreMembres() : 0)
.sum();
totalMembres = totalMembresCalc;
revenusGlobaux = "0 FCFA"; // À calculer depuis les souscriptions
alertesCount = 0; // À calculer
croissanceEntites = "0"; // À calculer
activiteJournaliere = 0; // À calculer
// Initialiser les métriques de souscription
totalSouscriptions = 0; // À calculer depuis les souscriptions
souscriptionsActives = 0; // À calculer
souscriptionsExpirantSous30Jours = 0; // À calculer
tauxConversion = 0.0f; // À calculer
// Revenus par forfait - À calculer depuis les souscriptions
revenusStarter = BigDecimal.ZERO;
revenusStandard = BigDecimal.ZERO;
revenusPremmium = BigDecimal.ZERO;
revenusCristal = BigDecimal.ZERO;
// Métriques système
disponibiliteSysteme = 99.8f;
tempsReponsMoyen = 145; // ms
ticketsSupportOuverts = 0; // À calculer
satisfactionClient = 4.7f; // /5
} catch (Exception e) {
LOGGER.severe("Erreur lors du calcul des KPIs: " + e.getMessage());
totalEntites = 0;
totalAdministrateurs = 0;
totalMembres = 0;
revenusGlobaux = "0 FCFA";
}
}
private void initializeAlertes() {
@@ -103,7 +126,7 @@ public class SuperAdminBean implements Serializable {
// Alertes critiques de souscription
Alerte alerte1 = new Alerte();
alerte1.setId(1L);
alerte1.setId(UUID.fromString("00000000-0000-0000-0000-00000000a001"));
alerte1.setTitre("12 souscriptions expirent sous 30 jours");
alerte1.setEntite("Système - Souscriptions");
alerte1.setDate("Aujourd'hui");
@@ -112,7 +135,7 @@ public class SuperAdminBean implements Serializable {
alertesRecentes.add(alerte1);
Alerte alerte2 = new Alerte();
alerte2.setId(2L);
alerte2.setId(UUID.fromString("00000000-0000-0000-0000-00000000a002"));
alerte2.setTitre("Quota membre atteint");
alerte2.setEntite("Club Sportif Thiès (Standard)");
alerte2.setDate("Il y a 2h");
@@ -121,7 +144,7 @@ public class SuperAdminBean implements Serializable {
alertesRecentes.add(alerte2);
Alerte alerte3 = new Alerte();
alerte3.setId(3L);
alerte3.setId(UUID.fromString("00000000-0000-0000-0000-00000000a003"));
alerte3.setTitre("Pic d'inscriptions détecté");
alerte3.setEntite("Association des Femmes Kaolack");
alerte3.setDate("Il y a 4h");
@@ -130,7 +153,7 @@ public class SuperAdminBean implements Serializable {
alertesRecentes.add(alerte3);
Alerte alerte4 = new Alerte();
alerte4.setId(4L);
alerte4.setId(UUID.fromString("00000000-0000-0000-0000-00000000a004"));
alerte4.setTitre("Performance système dégradée");
alerte4.setEntite("Système - Infrastructure");
alerte4.setDate("Il y a 6h");
@@ -139,7 +162,7 @@ public class SuperAdminBean implements Serializable {
alertesRecentes.add(alerte4);
Alerte alerte5 = new Alerte();
alerte5.setId(5L);
alerte5.setId(UUID.fromString("00000000-0000-0000-0000-00000000a005"));
alerte5.setTitre("Demande d'upgrade Premium");
alerte5.setEntite("Mutuelle Santé Dakar");
alerte5.setDate("Hier");
@@ -148,7 +171,7 @@ public class SuperAdminBean implements Serializable {
alertesRecentes.add(alerte5);
Alerte alerte6 = new Alerte();
alerte6.setId(6L);
alerte6.setId(UUID.fromString("00000000-0000-0000-0000-00000000a006"));
alerte6.setTitre("8 tickets support en attente");
alerte6.setEntite("Support Client");
alerte6.setDate("Il y a 1h");
@@ -159,18 +182,26 @@ public class SuperAdminBean implements Serializable {
private void initializeEntites() {
topEntites = new ArrayList<>();
String[] noms = {"LIONS CLUB Dakar Métropole", "LIONS CLUB Thiès", "LIONS CLUB Kaolack", "LIONS CLUB Saint-Louis", "LIONS CLUB Ziguinchor"};
String[] types = {"Club Principal", "Club Régional", "Club Régional", "Club Régional", "Club Régional"};
int[] membres = {156, 123, 98, 87, 73};
for (int i = 0; i < 5; i++) {
Entite entite = new Entite();
entite.setId((long) (i + 1));
entite.setNom(noms[i]);
entite.setTypeEntite(types[i]);
entite.setNombreMembres(membres[i]);
topEntites.add(entite);
try {
List<AssociationDTO> associations = associationService.listerActives();
topEntites = associations.stream()
.sorted((a1, a2) -> {
int m1 = a1.getNombreMembres() != null ? a1.getNombreMembres() : 0;
int m2 = a2.getNombreMembres() != null ? a2.getNombreMembres() : 0;
return Integer.compare(m2, m1);
})
.limit(5)
.map(a -> {
Entite entite = new Entite();
entite.setId(a.getId());
entite.setNom(a.getNom());
entite.setTypeEntite(a.getTypeAssociation());
entite.setNombreMembres(a.getNombreMembres() != null ? a.getNombreMembres() : 0);
return entite;
})
.collect(java.util.stream.Collectors.toList());
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des top entités: " + e.getMessage());
}
}
@@ -245,7 +276,7 @@ public class SuperAdminBean implements Serializable {
for (int i = 0; i < 5; i++) {
Activite activite = new Activite();
activite.setId((long) (i + 1));
activite.setId(UUID.randomUUID());
activite.setDescription(descriptions[i]);
activite.setEntite(entites[i]);
activite.setIcone(icones[i]);
@@ -320,7 +351,7 @@ public class SuperAdminBean implements Serializable {
}
public void voirAlerte(Alerte alerte) {
System.out.println("Voir alerte: " + alerte.getTitre());
LOGGER.info("Voir alerte: " + alerte.getTitre());
}
public String voirToutesAlertes() {
@@ -332,7 +363,7 @@ public class SuperAdminBean implements Serializable {
}
public void exporterRapportFinancier() {
System.out.println("Export du rapport financier généré");
LOGGER.info("Export du rapport financier généré");
}
// Getters et Setters
@@ -452,7 +483,7 @@ public class SuperAdminBean implements Serializable {
// Classes internes
public static class Alerte {
private Long id;
private UUID id;
private String titre;
private String entite;
private String date;
@@ -460,8 +491,8 @@ public class SuperAdminBean implements Serializable {
private String couleur;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getTitre() { return titre; }
public void setTitre(String titre) { this.titre = titre; }
@@ -480,14 +511,14 @@ public class SuperAdminBean implements Serializable {
}
public static class Entite {
private Long id;
private UUID id;
private String nom;
private String typeEntite;
private int nombreMembres;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }
@@ -532,7 +563,7 @@ public class SuperAdminBean implements Serializable {
}
public static class Activite {
private Long id;
private UUID id;
private String description;
private String entite;
private String date;
@@ -541,8 +572,8 @@ public class SuperAdminBean implements Serializable {
private String details;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }

View File

@@ -1,12 +1,21 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.auth.LoginResponse;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.eclipse.microprofile.jwt.JsonWebToken;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
/**
* Gestion de la session utilisateur avec Keycloak OIDC
*
* @author UnionFlow Team
* @version 2.0
*/
@Named("userSession")
@SessionScoped
public class UserSession implements Serializable {
@@ -14,6 +23,9 @@ public class UserSession implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(UserSession.class.getName());
@Inject
private JsonWebToken jwt;
private String username;
private boolean authenticated = false;
private String typeCompte;
@@ -27,39 +39,149 @@ public class UserSession implements Serializable {
clearSession();
}
public void updateFromLoginResponse(LoginResponse loginResponse) {
if (loginResponse != null && loginResponse.getUser() != null) {
LoginResponse.UserInfo userInfo = loginResponse.getUser();
/**
* Initialise la session depuis le token OIDC Keycloak
* Appelé automatiquement après l'authentification
*/
public void initializeFromOidcToken() {
if (jwt != null && jwt.getName() != null) {
this.authenticated = true;
this.username = userInfo.getUsername();
this.typeCompte = userInfo.getTypeCompte();
this.roles = userInfo.getRoles();
this.permissions = userInfo.getPermissions();
this.username = jwt.getClaim("preferred_username");
if (this.username == null) {
this.username = jwt.getName();
}
// Récupérer les informations du token
String email = jwt.getClaim("email");
String givenName = jwt.getClaim("given_name");
String familyName = jwt.getClaim("family_name");
// Récupérer les rôles depuis le token
this.roles = extractRolesFromToken();
this.typeCompte = determineTypeCompte();
// Mettre à jour les informations utilisateur
this.currentUser = new CurrentUser();
this.currentUser.setId(userInfo.getId());
this.currentUser.setNom(userInfo.getNom());
this.currentUser.setPrenom(userInfo.getPrenom());
this.currentUser.setEmail(userInfo.getEmail());
this.currentUser.setUsername(userInfo.getUsername());
this.currentUser.setUsername(this.username);
this.currentUser.setEmail(email);
this.currentUser.setPrenom(givenName);
this.currentUser.setNom(familyName);
// Mettre à jour les informations de l'entité
if (userInfo.getEntite() != null) {
this.entite = new EntiteInfo();
this.entite.setId(userInfo.getEntite().getId());
this.entite.setNom(userInfo.getEntite().getNom());
this.entite.setType(userInfo.getEntite().getType());
this.entite.setPays(userInfo.getEntite().getPays());
this.entite.setVille(userInfo.getEntite().getVille());
// Générer un ID depuis le subject du token
String subject = jwt.getSubject();
if (subject != null) {
try {
this.currentUser.setId(UUID.fromString(subject));
} catch (IllegalArgumentException e) {
// Si le subject n'est pas un UUID, générer un UUID déterministe
this.currentUser.setId(UUID.nameUUIDFromBytes(subject.getBytes()));
}
}
LOGGER.info("Session utilisateur mise à jour pour: " + userInfo.getUsername() +
LOGGER.info("Session utilisateur initialisée depuis Keycloak pour: " + this.username +
" (Type: " + typeCompte + ")");
}
}
/**
* Extrait les rôles depuis le token JWT
*/
private List<String> extractRolesFromToken() {
List<String> extractedRoles = new ArrayList<>();
// Rôles dans "realm_access.roles"
try {
Object realmAccess = jwt.getClaim("realm_access");
if (realmAccess instanceof java.util.Map) {
@SuppressWarnings("unchecked")
java.util.Map<String, Object> realmMap = (java.util.Map<String, Object>) realmAccess;
Object rolesObj = realmMap.get("roles");
if (rolesObj instanceof List) {
@SuppressWarnings("unchecked")
List<String> realmRoles = (List<String>) rolesObj;
extractedRoles.addAll(realmRoles);
LOGGER.info("Rôles extraits depuis realm_access.roles: " + realmRoles);
} else {
// Fallback: si realm_access est directement une liste de rôles
if (realmAccess instanceof List) {
@SuppressWarnings("unchecked")
List<String> realmRoles = (List<String>) realmAccess;
extractedRoles.addAll(realmRoles);
LOGGER.info("Rôles extraits depuis realm_access (liste directe): " + realmRoles);
}
}
} else if (realmAccess instanceof List) {
// Fallback: si realm_access est directement une liste de rôles
@SuppressWarnings("unchecked")
List<String> realmRoles = (List<String>) realmAccess;
extractedRoles.addAll(realmRoles);
LOGGER.info("Rôles extraits depuis realm_access (liste): " + realmRoles);
}
} catch (Exception e) {
LOGGER.warning("Erreur lors de l'extraction des rôles realm: " + e.getMessage());
}
// Rôles dans "resource_access"
try {
Object resourceAccess = jwt.getClaim("resource_access");
if (resourceAccess instanceof java.util.Map) {
@SuppressWarnings("unchecked")
java.util.Map<String, Object> resourceMap = (java.util.Map<String, Object>) resourceAccess;
for (Object value : resourceMap.values()) {
if (value instanceof java.util.Map) {
@SuppressWarnings("unchecked")
java.util.Map<String, Object> clientMap = (java.util.Map<String, Object>) value;
Object rolesObj = clientMap.get("roles");
if (rolesObj instanceof List) {
@SuppressWarnings("unchecked")
List<String> clientRoles = (List<String>) rolesObj;
extractedRoles.addAll(clientRoles);
LOGGER.info("Rôles extraits depuis resource_access: " + clientRoles);
}
}
}
}
} catch (Exception e) {
LOGGER.warning("Erreur lors de l'extraction des rôles client: " + e.getMessage());
}
// Fallback: essayer d'extraire les rôles depuis le claim "roles" directement
if (extractedRoles.isEmpty()) {
try {
Object rolesClaim = jwt.getClaim("roles");
if (rolesClaim instanceof List) {
@SuppressWarnings("unchecked")
List<String> directRoles = (List<String>) rolesClaim;
extractedRoles.addAll(directRoles);
LOGGER.info("Rôles extraits depuis claim 'roles': " + directRoles);
}
} catch (Exception e) {
LOGGER.warning("Erreur lors de l'extraction des rôles depuis claim 'roles': " + e.getMessage());
}
}
LOGGER.info("Total des rôles extraits: " + extractedRoles);
return extractedRoles;
}
/**
* Détermine le type de compte depuis les rôles
*/
private String determineTypeCompte() {
if (roles == null || roles.isEmpty()) {
return "MEMBRE";
}
if (roles.contains("SUPER_ADMIN") || roles.contains("super-admin")) {
return "SUPER_ADMIN";
}
if (roles.contains("ADMIN") || roles.contains("admin") || roles.contains("ADMIN_ENTITE")) {
return "ADMIN_ENTITE";
}
return "MEMBRE";
}
public void clearSession() {
this.authenticated = false;
this.username = null;
@@ -120,7 +242,11 @@ public class UserSession implements Serializable {
}
public boolean isAuthenticated() {
return authenticated;
// Vérifier via JsonWebToken
if (jwt != null && jwt.getName() != null && !authenticated) {
initializeFromOidcToken();
}
return authenticated || (jwt != null && jwt.getName() != null);
}
public void setAuthenticated(boolean authenticated) {
@@ -169,7 +295,7 @@ public class UserSession implements Serializable {
// Classes internes
public static class CurrentUser implements Serializable {
private Long id;
private UUID id;
private String nom;
private String prenom;
private String email;
@@ -194,11 +320,11 @@ public class UserSession implements Serializable {
}
// Getters et Setters
public Long getId() {
public UUID getId() {
return id;
}
public void setId(Long id) {
public void setId(UUID id) {
this.id = id;
}
@@ -236,7 +362,7 @@ public class UserSession implements Serializable {
}
public static class EntiteInfo implements Serializable {
private Long id;
private UUID id;
private String nom;
private String type;
private String pays;
@@ -254,11 +380,11 @@ public class UserSession implements Serializable {
}
// Getters et Setters
public Long getId() {
public UUID getId() {
return id;
}
public void setId(Long id) {
public void setId(UUID id) {
this.id = id;
}

View File

@@ -1,21 +1,32 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.AssociationDTO;
import dev.lions.unionflow.client.service.AssociationService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.logging.Logger;
@Named("utilisateursBean")
@SessionScoped
public class UtilisateursBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(UtilisateursBean.class.getName());
@Inject
@RestClient
private AssociationService associationService;
private List<Utilisateur> tousLesUtilisateurs;
private List<Utilisateur> utilisateursFiltres;
@@ -30,9 +41,9 @@ public class UtilisateursBean implements Serializable {
@PostConstruct
public void init() {
initializeFiltres();
initializeStatistiques();
initializeOrganisations();
initializeUtilisateurs();
initializeStatistiques();
initializeNouvelUtilisateur();
appliquerFiltres();
}
@@ -44,77 +55,33 @@ public class UtilisateursBean implements Serializable {
private void initializeStatistiques() {
statistiques = new StatistiquesUtilisateurs();
statistiques.setTotalUtilisateurs(47);
statistiques.setUtilisateursConnectes(12);
statistiques.setAdministrateurs(8);
statistiques.setUtilisateursDesactives(3);
// Les statistiques seront calculées depuis l'API backend quand elle sera disponible
statistiques.setTotalUtilisateurs(tousLesUtilisateurs != null ? tousLesUtilisateurs.size() : 0);
statistiques.setUtilisateursConnectes(0);
statistiques.setAdministrateurs(0);
statistiques.setUtilisateursDesactives(0);
}
private void initializeOrganisations() {
organisationsDisponibles = new ArrayList<>();
Organisation org1 = new Organisation();
org1.setId(1L);
org1.setNom("Direction Générale");
organisationsDisponibles.add(org1);
Organisation org2 = new Organisation();
org2.setId(2L);
org2.setNom("Services Financiers");
organisationsDisponibles.add(org2);
Organisation org3 = new Organisation();
org3.setId(3L);
org3.setNom("Ressources Humaines");
organisationsDisponibles.add(org3);
Organisation org4 = new Organisation();
org4.setId(4L);
org4.setNom("Communication");
organisationsDisponibles.add(org4);
try {
List<AssociationDTO> associations = associationService.listerActives();
for (AssociationDTO assoc : associations) {
Organisation org = new Organisation();
org.setId(assoc.getId());
org.setNom(assoc.getNom());
organisationsDisponibles.add(org);
}
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement des organisations: " + e.getMessage());
}
}
private void initializeUtilisateurs() {
tousLesUtilisateurs = new ArrayList<>();
String[] noms = {
"Koffi", "Asante", "Mensah", "Diallo", "Touré", "Koné", "Ouattara", "Traoré",
"Sanogo", "Bakayoko", "Coulibaly", "Cissé", "Gyamfi", "Adjei", "Akoto",
"Boateng", "Ofori", "Owusu", "Asamoah", "Yeboah"
};
String[] prenoms = {
"Kwame", "Ama", "Kofi", "Akosua", "Yaw", "Adwoa", "Kweku", "Afia",
"Kwadwo", "Akua", "Yaa", "Kwabena", "Efua", "Kojo", "Ama",
"Kwesi", "Esi", "Kwaku", "Abena", "Fiifi"
};
String[] roles = {"USER", "GESTIONNAIRE", "ADMIN", "SUPER_ADMIN"};
String[] statuts = {"ACTIF", "INACTIF", "SUSPENDU", "ATTENTE"};
for (int i = 0; i < 30; i++) {
Utilisateur utilisateur = new Utilisateur();
utilisateur.setId((long) (i + 1));
utilisateur.setNom(noms[i % noms.length]);
utilisateur.setPrenom(prenoms[i % prenoms.length]);
utilisateur.setEmail(prenoms[i % prenoms.length].toLowerCase() + "." +
noms[i % noms.length].toLowerCase() + "@unionflow.org");
utilisateur.setTelephone("+225 " + String.format("%02d", (i % 99) + 1) + " " +
String.format("%02d", (i % 99) + 1) + " " +
String.format("%02d", (i % 99) + 1) + " " +
String.format("%02d", (i % 99) + 1));
utilisateur.setRole(roles[i % roles.length]);
utilisateur.setStatut(statuts[i % statuts.length]);
utilisateur.setOrganisationId(organisationsDisponibles.get(i % organisationsDisponibles.size()).getId());
utilisateur.setDateCreation(LocalDateTime.now().minusDays(i + 1));
// Dernière connexion variable
if (i % 4 != 0) { // 75% des utilisateurs ont une dernière connexion
utilisateur.setDerniereConnexion(LocalDateTime.now().minusHours(i + 1).minusMinutes(i * 5));
}
tousLesUtilisateurs.add(utilisateur);
}
// Les utilisateurs seront chargés depuis l'API backend quand elle sera disponible
// Pour l'instant, retourner une liste vide
LOGGER.info("Initialisation des utilisateurs - API backend non disponible");
}
private void initializeNouvelUtilisateur() {
@@ -170,38 +137,26 @@ public class UtilisateursBean implements Serializable {
}
public void creerUtilisateur() {
Utilisateur nouvelUtil = new Utilisateur();
nouvelUtil.setId((long) (tousLesUtilisateurs.size() + 1));
nouvelUtil.setNom(nouvelUtilisateur.getNom());
nouvelUtil.setPrenom(nouvelUtilisateur.getPrenom());
nouvelUtil.setEmail(nouvelUtilisateur.getEmail());
nouvelUtil.setTelephone(nouvelUtilisateur.getTelephone());
nouvelUtil.setRole(nouvelUtilisateur.getRole());
nouvelUtil.setOrganisationId(nouvelUtilisateur.getOrganisationId());
nouvelUtil.setStatut("ACTIF");
nouvelUtil.setDateCreation(LocalDateTime.now());
tousLesUtilisateurs.add(nouvelUtil);
appliquerFiltres();
System.out.println("Nouvel utilisateur créé: " + nouvelUtil.getNomComplet());
// À implémenter quand l'API backend sera disponible
LOGGER.info("Création d'utilisateur - API backend non disponible");
initializeNouvelUtilisateur();
}
public void activerUtilisateur(Utilisateur utilisateur) {
utilisateur.setStatut("ACTIF");
System.out.println("Utilisateur activé: " + utilisateur.getNomComplet());
// À implémenter quand l'API backend sera disponible
LOGGER.info("Activation d'utilisateur - API backend non disponible");
appliquerFiltres();
}
public void desactiverUtilisateur(Utilisateur utilisateur) {
utilisateur.setStatut("INACTIF");
System.out.println("Utilisateur désactivé: " + utilisateur.getNomComplet());
// À implémenter quand l'API backend sera disponible
LOGGER.info("Désactivation d'utilisateur - API backend non disponible");
appliquerFiltres();
}
public void exporterUtilisateurs() {
System.out.println("Export de " + utilisateursFiltres.size() + " utilisateurs");
// À implémenter quand l'API backend sera disponible
LOGGER.info("Export d'utilisateurs - API backend non disponible");
}
// Getters et Setters
@@ -231,20 +186,20 @@ public class UtilisateursBean implements Serializable {
// Classes internes
public static class Utilisateur {
private Long id;
private UUID id;
private String nom;
private String prenom;
private String email;
private String telephone;
private String role;
private String statut;
private Long organisationId;
private UUID organisationId;
private LocalDateTime dateCreation;
private LocalDateTime derniereConnexion;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }
@@ -264,8 +219,8 @@ public class UtilisateursBean implements Serializable {
public String getStatut() { return statut; }
public void setStatut(String statut) { this.statut = statut; }
public Long getOrganisationId() { return organisationId; }
public void setOrganisationId(Long organisationId) { this.organisationId = organisationId; }
public UUID getOrganisationId() { return organisationId; }
public void setOrganisationId(UUID organisationId) { this.organisationId = organisationId; }
public LocalDateTime getDateCreation() { return dateCreation; }
public void setDateCreation(LocalDateTime dateCreation) { this.dateCreation = dateCreation; }
@@ -320,13 +275,13 @@ public class UtilisateursBean implements Serializable {
public String getOrganisationNom() {
// Simulation - en réalité, on ferait un lookup dans la base
return switch (organisationId.toString()) {
case "1" -> "Direction Générale";
case "2" -> "Services Financiers";
case "3" -> "Ressources Humaines";
case "4" -> "Communication";
default -> "Non définie";
};
if (organisationId == null) return "Non définie";
String orgIdStr = organisationId.toString();
if (orgIdStr.contains("000000000100")) return "Direction Générale";
if (orgIdStr.contains("000000000200")) return "Services Financiers";
if (orgIdStr.contains("000000000300")) return "Ressources Humaines";
if (orgIdStr.contains("000000000400")) return "Communication";
return "Non définie";
}
public String getDateCreationFormatee() {
@@ -356,7 +311,7 @@ public class UtilisateursBean implements Serializable {
private String email;
private String telephone;
private String role;
private Long organisationId;
private UUID organisationId;
private String motDePasse;
private boolean envoyerEmail;
@@ -376,8 +331,8 @@ public class UtilisateursBean implements Serializable {
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public Long getOrganisationId() { return organisationId; }
public void setOrganisationId(Long organisationId) { this.organisationId = organisationId; }
public UUID getOrganisationId() { return organisationId; }
public void setOrganisationId(UUID organisationId) { this.organisationId = organisationId; }
public String getMotDePasse() { return motDePasse; }
public void setMotDePasse(String motDePasse) { this.motDePasse = motDePasse; }
@@ -391,7 +346,7 @@ public class UtilisateursBean implements Serializable {
private String role;
private String statut;
private String connexion;
private Long organisation;
private UUID organisation;
// Getters et setters
public String getRecherche() { return recherche; }
@@ -406,8 +361,8 @@ public class UtilisateursBean implements Serializable {
public String getConnexion() { return connexion; }
public void setConnexion(String connexion) { this.connexion = connexion; }
public Long getOrganisation() { return organisation; }
public void setOrganisation(Long organisation) { this.organisation = organisation; }
public UUID getOrganisation() { return organisation; }
public void setOrganisation(UUID organisation) { this.organisation = organisation; }
}
public static class StatistiquesUtilisateurs {
@@ -431,12 +386,12 @@ public class UtilisateursBean implements Serializable {
}
public static class Organisation {
private Long id;
private UUID id;
private String nom;
// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }

View File

@@ -63,7 +63,7 @@
<h:form>
<p:button value="Se reconnecter"
icon="pi pi-sign-in"
outcome="/pages/public/login"
outcome="/"
styleClass="ui-button-primary ui-button-lg" />
<p:button value="Page d'accueil"
icon="pi pi-home"

View File

@@ -3,171 +3,381 @@
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
xmlns:p="http://primefaces.org/ui"
lang="fr">
<f:view>
<f:event type="preRenderView" listener="#{navigationBean.checkAuthentication}" />
<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" />
</f:facet>
<h:head>
<title>UnionFlow - Plateforme de Gestion Associative</title>
<h:outputStylesheet name="freya-layout/css/primeflex.min.css"/>
<h:outputStylesheet name="freya-layout/css/primeicons.css"/>
<h:outputStylesheet name="primefaces-freya-blue-light/theme.css"/>
<style>
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
.hero-container {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 60px;
box-shadow: 0 30px 80px rgba(0,0,0,0.3);
text-align: center;
max-width: 600px;
border: 1px solid rgba(255,255,255,0.2);
}
.logo-animation {
font-size: 5rem;
margin-bottom: 24px;
animation: pulse 2s ease-in-out infinite alternate;
}
@keyframes pulse {
0% { transform: scale(1); }
100% { transform: scale(1.05); }
}
.hero-title {
font-size: 3rem;
font-weight: 700;
margin-bottom: 16px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-subtitle {
font-size: 1.3rem;
color: #6c757d;
margin-bottom: 32px;
line-height: 1.6;
}
.features-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin: 40px 0;
}
.feature-item {
background: rgba(102, 126, 234, 0.1);
border-radius: 12px;
padding: 20px;
text-align: center;
}
.feature-icon {
font-size: 2rem;
margin-bottom: 12px;
color: #667eea;
}
.loading-text {
margin-top: 20px;
color: #6c757d;
font-size: 0.9rem;
animation: fadeInOut 1.5s ease-in-out infinite;
}
@keyframes fadeInOut {
0%, 100% { opacity: 0.5; }
50% { opacity: 1; }
}
</style>
</h:head>
<title>UnionFlow - Plateforme de Gestion Intégrée pour Mutuelles, Associations et Clubs</title>
<!-- Freya Layout Resources (placés dans le head pour éviter tout conflit et suivre le template) -->
<h:outputStylesheet name="css/primeicons.css" library="freya-layout" />
<h:outputStylesheet name="css/primeflex.min.css" library="freya-layout" />
<h:outputStylesheet name="css/layout-#{guestPreferences.layout}.css" library="freya-layout" />
<h:outputStylesheet name="primefaces-freya-#{guestPreferences.componentTheme}/theme.css" />
<meta name="description" content="UnionFlow : La solution complète de gestion pour les mutuelles, associations, clubs (informatiques, juridiques, etc.) et organisations similaires. Gestion des membres, cotisations, événements, solidarité et analytics." />
</h:head>
<h:body>
<div class="hero-container">
<!-- Logo animé -->
<div class="logo-animation">
<i class="pi pi-users"></i>
</div>
<!-- Titre principal -->
<h1 class="hero-title">UnionFlow</h1>
<p class="hero-subtitle">
Plateforme intégrée de gestion associative<br/>
<strong>Moderne • Sécurisée • Intuitive</strong>
</p>
<!-- Grille des fonctionnalités -->
<div class="features-grid">
<div class="feature-item">
<div class="feature-icon">
<i class="pi pi-users"></i>
</div>
<div class="text-900 font-semibold">Gestion Membres</div>
</div>
<div class="feature-item">
<div class="feature-icon">
<i class="pi pi-dollar"></i>
</div>
<div class="text-900 font-semibold">Cotisations</div>
</div>
<div class="feature-item">
<div class="feature-icon">
<i class="pi pi-calendar"></i>
</div>
<div class="text-900 font-semibold">Événements</div>
</div>
<div class="feature-item">
<div class="feature-icon">
<i class="pi pi-chart-bar"></i>
</div>
<div class="text-900 font-semibold">Analytics</div>
</div>
</div>
<!-- Boutons d'action -->
<div class="flex justify-content-center gap-3 mb-4">
<p:button value="Connexion Sécurisée"
icon="pi pi-sign-in"
outcome="/pages/public/login"
styleClass="p-button-lg p-button-rounded"/>
<h:body styleClass="landing-body">
<div class="landing-wrapper">
<!-- Landing Topbar -->
<div class="landing-topbar">
<div class="landing-topbar-left">
<h:link id="logolink" outcome="/pages/secure/dashboard" styleClass="logo">
<p:graphicImage name="images/logo-freya.svg" library="freya-layout" />
</h:link>
<p:button value="Accès Direct"
icon="pi pi-home"
outcome="/pages/secure/dashboard"
styleClass="p-button-lg p-button-rounded p-button-outlined"/>
<ul class="landing-menu">
<li>
<a href="#" id="landing-menu-close">
<i class="pi pi-times"> </i>
</a>
</li>
<li>
<a href="#home">Accueil</a>
</li>
<li>
<a href="#features">Fonctionnalités</a>
</li>
<li>
<a href="#benefits">Avantages</a>
</li>
</ul>
</div>
<!-- Texte de chargement -->
<div class="loading-text">
<i class="pi pi-spin pi-spinner mr-2"></i>
Vérification de l'authentification...
</div>
<!-- Footer -->
<div class="mt-5 pt-4 border-top-1 surface-border">
<small class="text-500">
© 2024 UnionFlow v1.0 - Développé par <strong>Lions Dev</strong><br/>
<span class="text-600">Côte d'Ivoire • Sénégal • Mali</span>
</small>
<div class="landing-topbar-right">
<ui:include src="/templates/components/button-primary.xhtml">
<ui:param name="value" value="Accéder" />
<ui:param name="icon" value="pi pi-arrow-right" />
<ui:param name="outcome" value="/pages/secure/dashboard" />
<ui:param name="styleClass" value="landing-button" />
</ui:include>
<a href="#" id="landing-menu-button">
<i class="pi pi-bars"> </i>
</a>
</div>
</div>
<!-- Landing Banner (Hero Section) -->
<div id="home" class="landing-banner">
<div class="landing-banner-content">
<span class="title">UnionFlow</span>
<h3>Plateforme de Gestion Intégrée pour Mutuelles, Associations et Clubs<br/>
Simplifiez la gestion de votre organisation avec une solution complète et moderne</h3>
<ui:include src="/templates/components/button-primary.xhtml">
<ui:param name="value" value="Accéder à la plateforme" />
<ui:param name="icon" value="pi pi-sign-in" />
<ui:param name="outcome" value="/pages/secure/dashboard" />
<ui:param name="styleClass" value="landing-button" />
</ui:include>
</div>
</div>
</h:body>
</f:view>
</html>
<!-- Landing Features Section -->
<div id="features" class="landing-features">
<div class="grid parallax-box">
<div class="col-12 lg:col-3">
<div class="feature yellow">
<span>1</span>
<div class="feature-card">
<span>1</span>
<div class="card-content">
<h3>Gestion des Membres</h3>
<h5>Inscription, profils détaillés, gestion des statuts, historique des adhésions et suivi complet de chaque membre.</h5>
</div>
</div>
</div>
</div>
<div class="col-12 lg:col-3">
<div class="grid">
<div class="col-12">
<div class="feature blue">
<span>2</span>
<div class="feature-card">
<span>2</span>
<div class="card-content">
<h3>Gestion des Cotisations</h3>
<h5>Types variés (mensuelle, annuelle, adhésion, événement, formation, projet, solidarité), suivi des paiements et rappels automatiques. <strong>Paiements sécurisés via Wave</strong> (bientôt disponible).</h5>
<div style="margin-top: 10px;">
<!-- Image locale du client (servie depuis META-INF/resources/images) -->
<h:graphicImage value="#{request.contextPath}/images/logo-wave.png" style="max-height: 30px; width: auto;" alt="Wave - Paiements mobiles" />
</div>
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="feature gray">
<span>3</span>
<div class="feature-card">
<span>3</span>
<div class="card-content">
<h3>Organisation<br/>d'Événements</h3>
<h5>Assemblées générales, réunions, formations, conférences, ateliers, séminaires, événements sociaux avec gestion des inscriptions.</h5>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 lg:col-3 feature-4">
<div class="col-12">
<div class="feature darker-gray">
<span>4</span>
<div class="feature-card">
<span>4</span>
<div class="card-content">
<h3>Système de Solidarité</h3>
<h5>Gestion complète des demandes d'aide, propositions, évaluations, suivi des statuts et coordination des actions solidaires.</h5>
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="feature darker-blue">
<span>5</span>
<div class="feature-card">
<span>5</span>
<div class="card-content">
<h3>Gestion des Organisations</h3>
<h5>Gestion des clubs et unions avec hiérarchie organisationnelle, statistiques détaillées, rapports et vue d'ensemble complète.</h5>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 lg:col-3">
<div class="feature gray">
<span>6</span>
<div class="feature-card">
<span>6</span>
<div class="card-content">
<h3>Analytics &amp; Rapports</h3>
<h5>Tableaux de bord interactifs, KPIs en temps réel, analyses approfondies et rapports personnalisables pour une prise de décision éclairée.</h5>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Benefits Section -->
<div id="benefits" class="landing-pricing">
<div class="section-header">
<span class="title">Pourquoi choisir UnionFlow ?</span>
<h3>Une solution pensée pour les mutuelles, associations, clubs et organisations similaires avec sécurité avancée, multi-plateforme et synchronisation temps réel.</h3>
</div>
<div class="grid">
<div class="col-12 lg:col-4">
<div class="pricing-card">
<h2>Sécurité</h2>
<span class="price">100%</span>
<span class="time">Sécurisé</span>
<ul>
<li>Connexion sécurisée et centralisée</li>
<li>Contrôle d'accès basé sur les rôles</li>
<li>Protection des données sensibles</li>
<li>Chiffrement des communications</li>
</ul>
</div>
</div>
<div class="col-12 lg:col-4 preferred">
<div class="pricing-card pro">
<span class="preferred-tag">RECOMMANDÉ</span>
<h2>Multi-Plateforme</h2>
<span class="price">24/7</span>
<span class="time">Disponible</span>
<ul>
<li>Application web responsive</li>
<li>Application mobile Flutter</li>
<li>iOS et Android</li>
<li>Accès depuis n'importe où</li>
</ul>
</div>
</div>
<div class="col-12 lg:col-4">
<div class="pricing-card enterprise">
<h2>Cloud</h2>
<span class="price">Cloud</span>
<span class="time">Moderne</span>
<ul>
<li>Architecture cloud-native</li>
<li>Synchronisation temps réel</li>
<li>Sauvegarde automatique</li>
<li>Scalabilité illimitée</li>
</ul>
</div>
</div>
</div>
<div class="text-center mt-5">
<ui:include src="/templates/components/button-primary.xhtml">
<ui:param name="value" value="Découvrir toutes les fonctionnalités" />
<ui:param name="icon" value="pi pi-arrow-right" />
<ui:param name="outcome" value="/pages/secure/dashboard" />
<ui:param name="styleClass" value="landing-button" />
</ui:include>
</div>
</div>
<!-- Footer -->
<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">NAVIGATION</span>
<ul>
<li><a href="#home">Accueil</a></li>
<li><a href="#features">Fonctionnalités</a></li>
<li><a href="#benefits">Avantages</a></li>
<li><a href="/pages/secure/dashboard">Tableau de Bord</a></li>
</ul>
</div>
<div class="col-6">
<span class="footer-menutitle">FONCTIONNALITÉS</span>
<ul>
<li><a href="/pages/secure/membre/liste">Membres</a></li>
<li><a href="/pages/secure/cotisation/historique">Cotisations</a></li>
<li><a href="/pages/secure/evenement/calendrier">Événements</a></li>
<li><a href="/pages/secure/aide/documentation">Aide</a></li>
</ul>
</div>
</div>
</div>
<div class="col-12 md:col-6 lg:col-3">
<span class="footer-menutitle">CONTACT</span>
<ul>
<li>support@unionflow.dev</li>
<li>Abidjan, Côte d'Ivoire</li>
<li>Plateforme de Gestion Intégrée</li>
</ul>
</div>
<div class="col-12 md:col-6 lg:col-5">
<span class="footer-menutitle">NEWSLETTER</span>
<span class="footer-subtitle">Rejoignez notre newsletter pour être informé des nouvelles fonctionnalités.</span>
<h:form>
<div class="newsletter-input">
<p:inputText placeholder="adresse email" />
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="S'abonner" />
<ui:param name="outlined" value="false" />
</ui:include>
</div>
</h:form>
</div>
<div class="col-12">
<div class="footer-bottom">
<h4>UnionFlow</h4>
<h6>Copyright © 2025 UnionFlow Dev Team</h6>
</div>
</div>
</div>
</div>
<div class="landing-mask"> </div>
</div>
<!-- Landing Page JavaScript - selon Freya -->
<h:outputScript name="js/layout.js" library="freya-layout" />
<h:outputScript>
<![CDATA[
(function() {
'use strict';
const initParallax = function() {
if (typeof $ === 'undefined') return;
$(document).ready(function() {
const parallax = function() {
const scrolled = $(window).scrollTop();
$('.landing-banner').css('top', -(scrolled * 0.01) + '%');
$('.landing-banner-content').css('top', (scrolled * -0.065) + '%');
$('.parallax-box .lg\\:col-3:odd').css('transform', 'translateY(' + (scrolled * 0.01) + '%)');
$('.parallax-box .lg\\:col-3:even').css('margin-top', -(scrolled * 0.01) + '%');
};
$(window).scroll(parallax);
});
};
const initMenu = function() {
if (typeof $ === 'undefined') return;
const hideMenu = function() {
$('.landing-menu').removeClass('fadeInDown').addClass('fadeOutUp');
setTimeout(function() {
$('.landing-wrapper').removeClass('landing-menu-active');
$('.landing-menu').removeClass('fadeOutUp');
$(document.body).removeClass('block-scroll');
}, 150);
};
const showMenu = function() {
$('.landing-wrapper').addClass('landing-menu-active');
$('.landing-menu').addClass('fadeInDown');
$(document.body).addClass('block-scroll');
};
$(function() {
$('#landing-menu-button').on('click', function(e) {
const wrapper = $('.landing-wrapper');
if (wrapper.hasClass('landing-menu-active')) {
hideMenu();
} else {
showMenu();
}
e.preventDefault();
});
$('.landing-menu').find('a').on('click', function() {
hideMenu();
});
});
};
const initScroll = function() {
if (typeof $ === 'undefined') return;
$(document).ready(function() {
$('html, body').animate({ scrollTop: 0 }, 1);
});
const isMobile = function() {
return $(window).width() <= 896;
};
$('a[href*="#"]:not([href="#"])').click(function() {
const target = $(this.hash);
if (target.length) {
const offset = isMobile() ? target.offset().top : target.offset().top - 100;
$('html, body').animate({ scrollTop: offset }, 500);
return false;
}
});
};
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
initParallax();
initMenu();
initScroll();
});
} else {
initParallax();
initMenu();
initScroll();
}
})();
]]>
</h:outputScript>
</h:body>
</html>

View File

@@ -1,214 +0,0 @@
<!DOCTYPE html>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
template="/templates/public-template.xhtml">
<ui:define name="title">Connexion - UnionFlow</ui:define>
<ui:define name="content">
<div class="min-h-screen flex relative lg:static surface-ground">
<!-- Panneau gauche - Branding -->
<div class="flex flex-column align-items-center justify-content-center bg-blue-500 flex-shrink-0 w-full lg:w-6">
<div class="text-center">
<!-- Logo et icône principale -->
<div class="mb-6">
<div class="text-white text-7xl font-bold mb-4">
<i class="pi pi-users"></i>
</div>
<h1 class="text-white text-5xl font-bold mb-2 line-height-3">UnionFlow</h1>
<p class="text-blue-100 text-xl font-medium line-height-3 mb-0">
Plateforme de Gestion Associative
</p>
</div>
<!-- Fonctionnalités clés -->
<div class="hidden lg:block">
<div class="surface-0 shadow-2 border-round p-4 mx-6">
<div class="text-900 text-lg font-bold mb-3">Fonctionnalités Principales</div>
<ul class="list-none p-0 m-0 text-left">
<li class="flex align-items-center py-2">
<div class="w-2rem h-2rem bg-green-100 text-green-600 border-round flex align-items-center justify-content-center mr-3">
<i class="pi pi-users"></i>
</div>
<span class="text-900 font-medium">Gestion des membres</span>
</li>
<li class="flex align-items-center py-2">
<div class="w-2rem h-2rem bg-purple-100 text-purple-600 border-round flex align-items-center justify-content-center mr-3">
<i class="pi pi-dollar"></i>
</div>
<span class="text-900 font-medium">Cotisations &amp; Paiements</span>
</li>
<li class="flex align-items-center py-2">
<div class="w-2rem h-2rem bg-orange-100 text-orange-600 border-round flex align-items-center justify-content-center mr-3">
<i class="pi pi-calendar"></i>
</div>
<span class="text-900 font-medium">Événements &amp; Activités</span>
</li>
<li class="flex align-items-center py-2">
<div class="w-2rem h-2rem bg-cyan-100 text-cyan-600 border-round flex align-items-center justify-content-center mr-3">
<i class="pi pi-chart-bar"></i>
</div>
<span class="text-900 font-medium">Rapports &amp; Analytics</span>
</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Panneau droit - Formulaire de connexion -->
<div class="flex flex-column align-items-center justify-content-center w-full lg:w-6 px-4 py-8 surface-card">
<div class="w-full" style="max-width: 400px;">
<!-- En-tête du formulaire -->
<div class="text-center mb-6">
<div class="text-900 text-3xl font-bold mb-2">Bienvenue !</div>
<div class="text-600 mb-4">Connectez-vous à votre espace UnionFlow</div>
<!-- Messages système -->
<p:messages id="messages" showDetail="true" closable="true"
styleClass="mb-4" globalOnly="true" />
<!-- Message de session expirée -->
<p:outputPanel rendered="#{param.expired == 'true'}" styleClass="mb-4">
<div class="surface-100 border-round-lg p-3 border-left-3 border-orange-500">
<div class="flex align-items-center">
<i class="pi pi-clock text-orange-500 mr-3 text-2xl"></i>
<div>
<div class="text-900 font-semibold">Session expirée</div>
<div class="text-600 text-sm mt-1">
Votre session a expiré pour des raisons de sécurité. Veuillez vous reconnecter.
</div>
</div>
</div>
</div>
</p:outputPanel>
</div>
<!-- Formulaire principal -->
<h:form id="loginForm" styleClass="ui-fluid">
<!-- Sélecteur de type de compte avec design moderne -->
<div class="field mb-4">
<label for="typeCompte" class="block text-900 font-semibold mb-2">
<i class="pi pi-user-plus mr-2"></i>Type de compte
</label>
<p:selectOneMenu id="typeCompte" value="#{loginBean.typeCompte}"
required="true" styleClass="w-full">
<f:selectItem itemLabel="Choisir votre profil..." itemValue="" noSelectionOption="true" />
<f:selectItem itemLabel="🔱 Super-Administrateur Plateforme" itemValue="SUPER_ADMIN" />
<f:selectItem itemLabel="🏛️ Administrateur d'Organisation" itemValue="ADMIN_ENTITE" />
<f:selectItem itemLabel="👤 Membre Actif" itemValue="MEMBRE" />
</p:selectOneMenu>
</div>
<!-- Champ email/username -->
<div class="field mb-4">
<label for="username" class="block text-900 font-semibold mb-2">
<i class="pi pi-at mr-2"></i>Email ou nom d'utilisateur
</label>
<p:inputText id="username" value="#{loginBean.username}"
placeholder="votre@email.com"
required="true" styleClass="w-full p-3">
<f:validateLength minimum="3" />
</p:inputText>
</div>
<!-- Champ mot de passe -->
<div class="field mb-4">
<label for="password" class="block text-900 font-semibold mb-2">
<i class="pi pi-key mr-2"></i>Mot de passe
</label>
<p:password id="password" value="#{loginBean.password}"
placeholder="••••••••••"
required="true" toggleMask="true"
styleClass="w-full p-3" />
</div>
<!-- Options avancées -->
<div class="flex align-items-center justify-content-between mb-5">
<div class="flex align-items-center">
<p:selectBooleanCheckbox id="rememberme" value="#{loginBean.rememberMe}"
styleClass="mr-2" />
<label for="rememberme" class="text-900 font-medium cursor-pointer">
Se souvenir de moi
</label>
</div>
<a href="#" class="font-semibold no-underline text-primary text-right cursor-pointer">
Mot de passe oublié ?
</a>
</div>
<!-- Bouton de connexion principal -->
<p:commandButton id="loginButton" value="Se connecter"
action="#{loginBean.login}"
styleClass="w-full p-3 text-xl border-round font-bold mb-4"
icon="pi pi-sign-in"
iconPos="right"
update="@form" />
<!-- Séparateur élégant -->
<div class="flex align-items-center mb-4">
<hr class="flex-1 border-top-1 border-300 m-0"/>
<div class="px-3 text-500 font-medium">ou essayez en mode démo</div>
<hr class="flex-1 border-top-1 border-300 m-0"/>
</div>
<!-- Boutons démo redesignés -->
<div class="grid gap-2">
<div class="col-12">
<p:commandButton value="🔱 Mode Démo Super-Admin"
action="#{loginBean.loginDemo('SUPER_ADMIN')}"
styleClass="ui-button-outlined ui-button-danger w-full p-2 font-bold"
update="@form" />
</div>
<div class="col-6">
<p:commandButton value="🏛️ Démo Admin"
action="#{loginBean.loginDemo('ADMIN')}"
styleClass="ui-button-outlined ui-button-warning w-full p-2"
update="@form" />
</div>
<div class="col-6">
<p:commandButton value="👤 Démo Membre"
action="#{loginBean.loginDemo('MEMBRE')}"
styleClass="ui-button-outlined ui-button-info w-full p-2"
update="@form" />
</div>
</div>
<!-- Call to action pour inscription -->
<div class="surface-50 border-round p-4 mt-6 text-center">
<div class="text-900 font-semibold mb-2">
<i class="pi pi-star-fill text-yellow-500 mr-2"></i>
Votre organisation n'est pas encore inscrite ?
</div>
<p class="text-600 mb-3 line-height-3">
Découvrez UnionFlow avec une démonstration personnalisée
</p>
<a href="#" class="text-primary font-bold no-underline">
<i class="pi pi-arrow-right mr-2"></i>Demander une démonstration
</a>
</div>
</h:form>
<!-- Footer discret -->
<div class="text-center mt-6 pt-4">
<div class="text-500 text-sm mb-2">
© 2024 UnionFlow - Développé par Lions Dev
</div>
<div class="flex justify-content-center gap-3 text-xs">
<a href="#" class="text-500 no-underline hover:text-primary">Aide</a>
<span class="text-300"></span>
<a href="#" class="text-500 no-underline hover:text-primary">Confidentialité</a>
<span class="text-300"></span>
<a href="#" class="text-500 no-underline hover:text-primary">Conditions</a>
</div>
</div>
</div>
</div>
</div>
</ui:define>
</ui:composition>

View File

@@ -32,8 +32,16 @@
<p:inputTextarea id="motifAdhesion" rows="4" />
</div>
<div class="field col-12">
<p:commandButton value="Soumettre la demande" icon="pi pi-send" styleClass="ui-button-success" />
<p:commandButton value="Annuler" icon="pi pi-times" styleClass="ui-button-secondary" style="margin-left: 0.5rem" />
<ui:include src="/templates/components/button-success.xhtml">
<ui:param name="value" value="Soumettre la demande" />
<ui:param name="icon" value="pi pi-send" />
</ui:include>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Annuler" />
<ui:param name="icon" value="pi pi-times" />
<ui:param name="outlined" value="false" />
<ui:param name="styleClass" value="ml-2" />
</ui:include>
</div>
</div>
</h:form>

View File

@@ -23,8 +23,14 @@
<h:outputText value="#{adhesion.statut}" />
</p:column>
<p:column headerText="Actions">
<p:commandButton icon="pi pi-eye" styleClass="ui-button-rounded ui-button-info ui-button-text" />
<p:commandButton icon="pi pi-pencil" styleClass="ui-button-rounded ui-button-success ui-button-text" />
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-eye" />
<ui:param name="severity" value="info" />
</ui:include>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-pencil" />
<ui:param name="severity" value="success" />
</ui:include>
</p:column>
</p:dataTable>
</div>

View File

@@ -24,7 +24,11 @@
<h:outputText value="#{adhesion.dateExpiration}" />
</p:column>
<p:column headerText="Action">
<p:commandButton value="Renouveler" icon="pi pi-refresh" styleClass="ui-button-success ui-button-sm" />
<ui:include src="/templates/components/button-success.xhtml">
<ui:param name="value" value="Renouveler" />
<ui:param name="icon" value="pi pi-refresh" />
<ui:param name="styleClass" value="ui-button-sm" />
</ui:include>
</p:column>
</p:dataTable>
</div>

View File

@@ -23,8 +23,16 @@
<h:outputText value="#{adhesion.type}" />
</p:column>
<p:column headerText="Actions">
<p:commandButton value="Approuver" icon="pi pi-check" styleClass="ui-button-success ui-button-sm" />
<p:commandButton value="Rejeter" icon="pi pi-times" styleClass="ui-button-danger ui-button-sm" style="margin-left: 0.5rem" />
<ui:include src="/templates/components/button-success.xhtml">
<ui:param name="value" value="Approuver" />
<ui:param name="icon" value="pi pi-check" />
<ui:param name="styleClass" value="ui-button-sm" />
</ui:include>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Rejeter" />
<ui:param name="icon" value="pi pi-times" />
<ui:param name="styleClass" value="ui-button-danger ui-button-sm" />
</ui:include>
</p:column>
</p:dataTable>
</div>

View File

@@ -26,13 +26,17 @@
</p>
</div>
<div class="flex gap-2">
<p:commandButton value="Version PDF"
styleClass="p-button-outlined"
icon="pi pi-file-pdf" />
<p:commandButton value="Rechercher"
styleClass="p-button-primary p-button-outlined"
icon="pi pi-search"
onclick="PF('rechercheDialog').show()" />
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Version PDF" />
<ui:param name="icon" value="pi pi-file-pdf" />
<ui:param name="outlined" value="true" />
</ui:include>
<ui:include src="/templates/components/button-primary.xhtml">
<ui:param name="value" value="Rechercher" />
<ui:param name="icon" value="pi pi-search" />
<ui:param name="onclick" value="PF('rechercheDialog').show()" />
<ui:param name="styleClass" value="ui-button-outlined" />
</ui:include>
</div>
</div>
@@ -145,12 +149,18 @@
<!-- Actions rapides -->
<div class="surface-card border-round p-3 mt-3">
<div class="text-center">
<p:commandButton value="Tout marquer comme lu"
styleClass="p-button-text p-button-sm w-full"
icon="pi pi-check-square" />
<p:commandButton value="Réinitialiser progression"
styleClass="p-button-text p-button-sm w-full mt-2"
icon="pi pi-refresh" />
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Tout marquer comme lu" />
<ui:param name="icon" value="pi pi-check-square" />
<ui:param name="outlined" value="false" />
<ui:param name="styleClass" value="ui-button-text ui-button-sm w-full" />
</ui:include>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Réinitialiser progression" />
<ui:param name="icon" value="pi pi-refresh" />
<ui:param name="outlined" value="false" />
<ui:param name="styleClass" value="ui-button-text ui-button-sm w-full mt-2" />
</ui:include>
</div>
</div>
</h:form>
@@ -171,15 +181,27 @@
</div>
</div>
<div class="flex gap-2">
<p:commandButton icon="pi pi-bookmark"
styleClass="p-button-rounded p-button-outlined p-button-sm"
title="Marquer comme favori" />
<p:commandButton icon="pi pi-share-alt"
styleClass="p-button-rounded p-button-outlined p-button-sm"
title="Partager" />
<p:commandButton icon="pi pi-print"
styleClass="p-button-rounded p-button-outlined p-button-sm"
title="Imprimer" />
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-bookmark" />
<ui:param name="title" value="Marquer comme favori" />
<ui:param name="rounded" value="true" />
<ui:param name="text" value="false" />
<ui:param name="styleClass" value="ui-button-outlined ui-button-sm" />
</ui:include>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-share-alt" />
<ui:param name="title" value="Partager" />
<ui:param name="rounded" value="true" />
<ui:param name="text" value="false" />
<ui:param name="styleClass" value="ui-button-outlined ui-button-sm" />
</ui:include>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-print" />
<ui:param name="title" value="Imprimer" />
<ui:param name="rounded" value="true" />
<ui:param name="text" value="false" />
<ui:param name="styleClass" value="ui-button-outlined ui-button-sm" />
</ui:include>
</div>
</div>
@@ -325,31 +347,37 @@
<!-- Navigation entre sections -->
<div class="flex justify-content-between align-items-center mt-6 pt-4 border-top-1 border-200">
<p:commandButton value="Section précédente"
icon="pi pi-angle-left"
styleClass="p-button-outlined"
rendered="#{guideBean.APrecedent}"
action="#{guideBean.sectionPrecedente}" />
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Section précédente" />
<ui:param name="icon" value="pi pi-angle-left" />
<ui:param name="action" value="#{guideBean.sectionPrecedente}" />
<ui:param name="outlined" value="true" />
<ui:param name="rendered" value="#{guideBean.APrecedent}" />
</ui:include>
<div class="flex gap-2" rendered="#{guideBean.sectionCourante.id != 'default'}">
<p:commandButton value="Marquer comme lu"
icon="pi pi-check"
styleClass="p-button-success"
rendered="#{not guideBean.sectionCourante.lu}"
action="#{guideBean.marquerCommeLu}" />
<p:commandButton value="Lu"
icon="pi pi-check"
styleClass="p-button-outlined"
rendered="#{guideBean.sectionCourante.lu}"
disabled="true" />
<ui:include src="/templates/components/button-success.xhtml">
<ui:param name="value" value="Marquer comme lu" />
<ui:param name="icon" value="pi pi-check" />
<ui:param name="action" value="#{guideBean.marquerCommeLu}" />
<ui:param name="rendered" value="#{not guideBean.sectionCourante.lu}" />
</ui:include>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Lu" />
<ui:param name="icon" value="pi pi-check" />
<ui:param name="outlined" value="true" />
<ui:param name="disabled" value="true" />
<ui:param name="rendered" value="#{guideBean.sectionCourante.lu}" />
</ui:include>
</div>
<p:commandButton value="Section suivante"
icon="pi pi-angle-right"
iconPos="right"
styleClass="p-button-outlined"
rendered="#{guideBean.ASuivant}"
action="#{guideBean.sectionSuivante}" />
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Section suivante" />
<ui:param name="icon" value="pi pi-angle-right" />
<ui:param name="action" value="#{guideBean.sectionSuivante}" />
<ui:param name="outlined" value="true" />
<ui:param name="rendered" value="#{guideBean.ASuivant}" />
</ui:include>
</div>
</div>
</div>
@@ -376,7 +404,7 @@
</p:inputText>
</div>
<div id="resultatsRecherche">
<h:panelGroup id="resultatsRecherche" layout="block">
<ui:repeat value="#{guideBean.resultatsRecherche}" var="resultat" rendered="#{not empty guideBean.termeRecherche}">
<div class="surface-100 border-round p-3 mb-3 cursor-pointer hover:surface-200 transition-duration-200"
onclick="#{guideBean.naviguerVers(resultat.id)}; PF('rechercheDialog').hide();">
@@ -395,7 +423,7 @@
<i class="pi pi-search text-3xl text-300 mb-2"></i>
<p class="text-600">Aucun résultat trouvé pour "#{guideBean.termeRecherche}"</p>
</div>
</div>
</h:panelGroup>
</div>
</h:form>
</p:dialog>

View File

@@ -26,18 +26,27 @@
</div>
</div>
<div class="mt-3 lg:mt-0">
<p:commandButton value="Rapport mensuel" icon="pi pi-download"
styleClass="ui-button-outlined ui-button-success mr-2"
action="#{dashboardBean.generateRapport}"/>
<p:commandButton value="Aide" icon="pi pi-question-circle"
styleClass="ui-button-outlined ui-button-help"/>
<ui:include src="/templates/components/button-success.xhtml">
<ui:param name="value" value="Rapport mensuel" />
<ui:param name="icon" value="pi pi-download" />
<ui:param name="action" value="#{dashboardBean.generateRapport}" />
<ui:param name="outlined" value="true" />
<ui:param name="styleClass" value="mr-2" />
</ui:include>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Aide" />
<ui:param name="icon" value="pi pi-question-circle" />
<ui:param name="outlined" value="true" />
<ui:param name="styleClass" value="ui-button-help" />
</ui:include>
</div>
</div>
</div>
</div>
<!-- Alertes URGENTES - En premier pour l'attention immédiate -->
<div class="col-12" rendered="#{dashboardBean.hasAlerts}">
<ui:fragment rendered="#{dashboardBean.hasAlerts}">
<div class="col-12">
<div class="card surface-50 border-round">
<h5 class="text-900 font-bold mb-3">
<i class="pi pi-exclamation-circle text-orange-500 mr-2"></i>
@@ -110,6 +119,7 @@
</div>
</div>
</div>
</ui:fragment>
<!-- KPIs principaux - Ordre psychologique optimal -->
<div class="col-12">
@@ -359,11 +369,11 @@
<!-- Journal d'activités et Tâches prioritaires -->
<div class="col-12 lg:col-8">
<div class="card">
<div class="flex align-items-center justify-content-between mb-4">
<h5>Journal d'activités</h5>
<div>
<h:form>
<h:form>
<div class="card">
<div class="flex align-items-center justify-content-between mb-4">
<h5>Journal d'activités</h5>
<div>
<p:selectOneMenu value="#{dashboardBean.filtreActivite}">
<f:selectItem itemLabel="Toutes" itemValue="ALL" />
<f:selectItem itemLabel="Cotisations" itemValue="COTISATION" />
@@ -372,83 +382,105 @@
<f:selectItem itemLabel="Événements" itemValue="EVENEMENT" />
<p:ajax update="activitiesTable" />
</p:selectOneMenu>
</h:form>
</div>
</div>
</div>
<p:dataTable id="activitiesTable" value="#{dashboardBean.recentActivities}" var="activity"
rows="8" paginator="true" paginatorPosition="bottom">
<p:column headerText="Date" style="width:120px">
<div class="flex flex-column">
<span class="text-900 font-medium">
<h:outputText value="#{activity.date}">
<f:convertDateTime pattern="dd/MM HH:mm" type="localDateTime"/>
</h:outputText>
</span>
<small class="text-500">
<h:outputText value="#{activity.date}">
<f:convertDateTime pattern="yyyy" type="localDateTime"/>
</h:outputText>
</small>
</div>
</p:column>
<p:column headerText="Activité" style="width:100px">
<p:tag value="#{activity.type}" severity="#{activity.severity}"
icon="#{activity.icon}" styleClass="mr-2"/>
</p:column>
<p:column headerText="Description">
<div>
<div class="text-900 font-medium">#{activity.titre}</div>
<div class="text-600 mt-1">#{activity.description}</div>
<div class="mt-2" rendered="#{activity.montant != null}">
<span class="text-green-600 font-medium">#{activity.montant} FCFA</span>
<p:dataTable id="activitiesTable" value="#{dashboardBean.recentActivities}" var="activity"
rows="8" paginator="true" paginatorPosition="bottom">
<p:column headerText="Date" style="width:120px">
<div class="flex flex-column">
<span class="text-900 font-medium">
<h:outputText value="#{activity.date}">
<f:convertDateTime pattern="dd/MM HH:mm" type="localDateTime"/>
</h:outputText>
</span>
<small class="text-500">
<h:outputText value="#{activity.date}">
<f:convertDateTime pattern="yyyy" type="localDateTime"/>
</h:outputText>
</small>
</div>
</div>
</p:column>
<p:column headerText="Acteur" style="width:150px">
<div class="flex align-items-center">
<p:graphicImage name="images/avatar/profile.jpg" library="demo"
styleClass="w-2rem h-2rem border-circle mr-2"/>
</p:column>
<p:column headerText="Activité" style="width:100px">
<p:tag value="#{activity.type}" severity="#{activity.severity}"
icon="#{activity.icon}" styleClass="mr-2"/>
</p:column>
<p:column headerText="Description">
<div>
<div class="text-900 font-medium">#{activity.userNom}</div>
<small class="text-500">#{activity.userRole}</small>
<div class="text-900 font-medium">#{activity.titre}</div>
<div class="text-600 mt-1">#{activity.description}</div>
<ui:fragment rendered="#{activity.montant != null}">
<div class="mt-2">
<span class="text-green-600 font-medium">#{activity.montant} FCFA</span>
</div>
</ui:fragment>
</div>
</div>
</p:column>
<p:column headerText="Action" style="width:80px">
<p:commandButton icon="pi pi-eye" styleClass="ui-button-rounded ui-button-text"
title="Voir détails"/>
</p:column>
</p:dataTable>
</div>
</p:column>
<p:column headerText="Acteur" style="width:150px">
<div class="flex align-items-center">
<p:graphicImage name="images/avatar/profile.jpg" library="demo"
styleClass="w-2rem h-2rem border-circle mr-2"/>
<div>
<div class="text-900 font-medium">#{activity.userNom}</div>
<small class="text-500">#{activity.userRole}</small>
</div>
</div>
</p:column>
<p:column headerText="Action" style="width:80px">
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-eye" />
<ui:param name="title" value="Voir détails" />
</ui:include>
</p:column>
</p:dataTable>
</div>
</h:form>
</div>
<!-- Actions rapides et Tâches -->
<div class="col-12 lg:col-4">
<div class="card mb-4">
<h5>Actions rapides</h5>
<div class="grid">
<div class="col-6">
<p:commandButton value="Nouveau membre" icon="pi pi-user-plus"
styleClass="ui-button-outlined w-full mb-2"
action="#{dashboardBean.redirectToNewMember}"/>
</div>
<div class="col-6">
<p:commandButton value="Collecter" icon="pi pi-wallet"
styleClass="ui-button-success ui-button-outlined w-full mb-2"
action="#{dashboardBean.redirectToCotisation}"/>
</div>
<div class="col-6">
<p:commandButton value="Événement" icon="pi pi-calendar-plus"
styleClass="ui-button-info ui-button-outlined w-full mb-2"
action="#{dashboardBean.redirectToEvenement}"/>
</div>
<div class="col-6">
<p:commandButton value="Rapport" icon="pi pi-chart-bar"
styleClass="ui-button-warning ui-button-outlined w-full mb-2"
action="#{dashboardBean.generateRapport}"/>
<h:form>
<div class="card mb-4">
<h5>Actions rapides</h5>
<div class="grid">
<div class="col-6">
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Nouveau membre" />
<ui:param name="icon" value="pi pi-user-plus" />
<ui:param name="action" value="#{dashboardBean.redirectToNewMember}" />
<ui:param name="outlined" value="true" />
<ui:param name="styleClass" value="w-full mb-2" />
</ui:include>
</div>
<div class="col-6">
<ui:include src="/templates/components/button-success.xhtml">
<ui:param name="value" value="Collecter" />
<ui:param name="icon" value="pi pi-wallet" />
<ui:param name="action" value="#{dashboardBean.redirectToCotisation}" />
<ui:param name="outlined" value="true" />
<ui:param name="styleClass" value="w-full mb-2" />
</ui:include>
</div>
<div class="col-6">
<ui:include src="/templates/components/button-info.xhtml">
<ui:param name="value" value="Événement" />
<ui:param name="icon" value="pi pi-calendar-plus" />
<ui:param name="action" value="#{dashboardBean.redirectToEvenement}" />
<ui:param name="outlined" value="true" />
<ui:param name="styleClass" value="w-full mb-2" />
</ui:include>
</div>
<div class="col-6">
<ui:include src="/templates/components/button-warning.xhtml">
<ui:param name="value" value="Rapport" />
<ui:param name="icon" value="pi pi-chart-bar" />
<ui:param name="action" value="#{dashboardBean.generateRapport}" />
<ui:param name="outlined" value="true" />
<ui:param name="styleClass" value="w-full mb-2" />
</ui:include>
</div>
</div>
</div>
</div>
</h:form>
<div class="card">
<h5>Tâches prioritaires</h5>
@@ -459,9 +491,11 @@
<div class="text-900 font-medium">Valider #{dashboardBean.adhesionsPendantes} adhésions</div>
<small class="text-600">Demandes en attente de validation</small>
</div>
<p:commandButton icon="pi pi-arrow-right"
styleClass="ui-button-rounded ui-button-text ui-button-info"
action="#{dashboardBean.redirectToAdhesionValidation}"/>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-arrow-right" />
<ui:param name="action" value="#{dashboardBean.redirectToAdhesionValidation}" />
<ui:param name="severity" value="info" />
</ui:include>
</div>
<div class="flex align-items-center p-3 border-round bg-orange-50 border-orange-200">
@@ -470,9 +504,11 @@
<div class="text-900 font-medium">Relancer #{dashboardBean.cotisationsRetard} cotisations</div>
<small class="text-600">Paiements en retard</small>
</div>
<p:commandButton icon="pi pi-arrow-right"
styleClass="ui-button-rounded ui-button-text ui-button-warning"
action="#{dashboardBean.redirectToRelances}"/>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-arrow-right" />
<ui:param name="action" value="#{dashboardBean.redirectToRelances}" />
<ui:param name="severity" value="warning" />
</ui:include>
</div>
<div class="flex align-items-center p-3 border-round bg-green-50 border-green-200">
@@ -481,9 +517,11 @@
<div class="text-900 font-medium">Traiter #{dashboardBean.aidesEnAttente} aides</div>
<small class="text-600">Demandes d'aide à examiner</small>
</div>
<p:commandButton icon="pi pi-arrow-right"
styleClass="ui-button-rounded ui-button-text ui-button-success"
action="#{dashboardBean.redirectToAidesTraitement}"/>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-arrow-right" />
<ui:param name="action" value="#{dashboardBean.redirectToAidesTraitement}" />
<ui:param name="severity" value="success" />
</ui:include>
</div>
<div class="flex align-items-center p-3 border-round bg-purple-50 border-purple-200">
@@ -492,9 +530,11 @@
<div class="text-900 font-medium">Organiser prochains événements</div>
<small class="text-600">#{dashboardBean.evenementsAPlanifier} événements à planifier</small>
</div>
<p:commandButton icon="pi pi-arrow-right"
styleClass="ui-button-rounded ui-button-text ui-button-help"
action="#{dashboardBean.redirectToEvenementPlanning}"/>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-arrow-right" />
<ui:param name="action" value="#{dashboardBean.redirectToEvenementPlanning}" />
<ui:param name="styleClass" value="ui-button-help" />
</ui:include>
</div>
</div>
</div>
@@ -511,9 +551,13 @@
yearNavigator="true" yearRange="2020:2030">
<p:ajax update="financialSummary" listener="#{dashboardBean.onMoisChange}"/>
</p:calendar>
<p:commandButton value="Exporter" icon="pi pi-download"
styleClass="ui-button-outlined ui-button-sm"
action="#{dashboardBean.exportFinancialReport}"/>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Exporter" />
<ui:param name="icon" value="pi pi-download" />
<ui:param name="action" value="#{dashboardBean.exportFinancialReport}" />
<ui:param name="outlined" value="true" />
<ui:param name="styleClass" value="ui-button-sm" />
</ui:include>
</h:form>
</div>
</div>

View File

@@ -177,11 +177,14 @@
<i class="pi pi-file text-blue-500 mr-2"></i>
<span class="text-900">#{document}</span>
</div>
<p:commandButton icon="pi pi-times"
styleClass="ui-button-rounded ui-button-text ui-button-danger ui-button-sm"
action="#{membreInscriptionBean.supprimerDocument(document)}"
update="documentsListPanel"
title="Supprimer le fichier" />
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-times" />
<ui:param name="action" value="#{membreInscriptionBean.supprimerDocument(document)}" />
<ui:param name="update" value="documentsListPanel" />
<ui:param name="title" value="Supprimer le fichier" />
<ui:param name="severity" value="danger" />
<ui:param name="styleClass" value="ui-button-sm" />
</ui:include>
</div>
</ui:repeat>
</h:panelGroup>
@@ -242,8 +245,13 @@
<p:outputLabel for="numeroParrain" value="N° Membre parrain" />
<div class="ui-inputgroup">
<p:inputText id="numeroParrain" value="#{membreInscriptionBean.numeroParrain}" />
<p:commandButton icon="pi pi-search" styleClass="ui-button-info"
action="#{membreInscriptionBean.rechercherParrain}" />
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-search" />
<ui:param name="action" value="#{membreInscriptionBean.rechercherParrain}" />
<ui:param name="severity" value="info" />
<ui:param name="rounded" value="false" />
<ui:param name="text" value="false" />
</ui:include>
</div>
</div>
<div class="field">
@@ -358,28 +366,37 @@
</div>
<div class="flex flex-wrap gap-2">
<p:commandButton value="🎯 Inscrire le membre" icon="pi pi-user-plus"
styleClass="ui-button ui-button-success"
onclick="return preparePhotoForSubmission(this);"
action="#{membreInscriptionBean.inscrire}"
update="messages"
disabled="#{!membreInscriptionBean.formulaireValide}" />
<p:commandButton value="💾 Enregistrer brouillon" icon="pi pi-save"
styleClass="ui-button ui-button-outlined ui-button-info"
action="#{membreInscriptionBean.enregistrerBrouillon}"
update="messages" />
<p:commandButton value="🔄 Réinitialiser" icon="pi pi-refresh"
styleClass="ui-button ui-button-outlined ui-button-warning"
type="reset"
onclick="removePhoto(); return confirm('Êtes-vous sûr de vouloir réinitialiser le formulaire ?');" />
<p:commandButton value="❌ Annuler" icon="pi pi-times"
styleClass="ui-button ui-button-outlined ui-button-secondary"
action="#{membreInscriptionBean.annuler}"
immediate="true"
onclick="return confirm('Êtes-vous sûr de vouloir annuler l\'inscription ?');" />
<ui:include src="/templates/components/button-success.xhtml">
<ui:param name="value" value="🎯 Inscrire le membre" />
<ui:param name="icon" value="pi pi-user-plus" />
<ui:param name="onclick" value="return preparePhotoForSubmission(this);" />
<ui:param name="action" value="#{membreInscriptionBean.inscrire}" />
<ui:param name="update" value="messages" />
<ui:param name="disabled" value="#{!membreInscriptionBean.formulaireValide}" />
</ui:include>
<ui:include src="/templates/components/button-info.xhtml">
<ui:param name="value" value="💾 Enregistrer brouillon" />
<ui:param name="icon" value="pi pi-save" />
<ui:param name="action" value="#{membreInscriptionBean.enregistrerBrouillon}" />
<ui:param name="update" value="messages" />
<ui:param name="outlined" value="true" />
</ui:include>
<ui:include src="/templates/components/button-warning.xhtml">
<ui:param name="value" value="🔄 Réinitialiser" />
<ui:param name="icon" value="pi pi-refresh" />
<ui:param name="onclick" value="removePhoto(); return confirm('Êtes-vous sûr de vouloir réinitialiser le formulaire ?');" />
<ui:param name="outlined" value="true" />
</ui:include>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="❌ Annuler" />
<ui:param name="icon" value="pi pi-times" />
<ui:param name="action" value="#{membreInscriptionBean.annuler}" />
<ui:param name="onclick" value="return confirm('Êtes-vous sûr de vouloir annuler l\'inscription ?');" />
<ui:param name="outlined" value="true" />
</ui:include>
</div>
<div class="mt-4 text-600">

View File

@@ -23,14 +23,17 @@
</div>
<h:form id="formActionsMembres">
<div class="flex gap-2">
<p:commandButton value="Nouveau membre"
icon="pi pi-user-plus"
styleClass="ui-button-success"
action="/pages/secure/membre/inscription?faces-redirect=true" />
<p:commandButton value="Import/Export"
icon="pi pi-upload"
styleClass="ui-button-outlined ui-button-secondary"
onclick="PF('dlgImportExport').show();" />
<ui:include src="/templates/components/button-success.xhtml">
<ui:param name="value" value="Nouveau membre" />
<ui:param name="icon" value="pi pi-user-plus" />
<ui:param name="action" value="/pages/secure/membre/inscription?faces-redirect=true" />
</ui:include>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Import/Export" />
<ui:param name="icon" value="pi pi-upload" />
<ui:param name="onclick" value="PF('dlgImportExport').show();" />
<ui:param name="outlined" value="true" />
</ui:include>
</div>
</h:form>
</div>
@@ -94,15 +97,22 @@
</p:toolbarGroup>
<p:toolbarGroup align="right">
<p:commandButton value="Filtres avancés"
icon="pi pi-filter"
styleClass="ui-button-outlined ui-button-secondary mr-2"
onclick="PF('dlgFiltresAvances').show();" />
<p:commandButton icon="pi pi-refresh"
styleClass="ui-button-outlined ui-button-secondary"
action="#{membreListeBean.actualiser}"
update="@form"
title="Actualiser" />
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Filtres avancés" />
<ui:param name="icon" value="pi pi-filter" />
<ui:param name="onclick" value="PF('dlgFiltresAvances').show();" />
<ui:param name="outlined" value="true" />
<ui:param name="styleClass" value="mr-2" />
</ui:include>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-refresh" />
<ui:param name="action" value="#{membreListeBean.actualiser}" />
<ui:param name="update" value="@form" />
<ui:param name="title" value="Actualiser" />
<ui:param name="rounded" value="false" />
<ui:param name="text" value="false" />
<ui:param name="styleClass" value="ui-button-outlined ui-button-secondary" />
</ui:include>
</p:toolbarGroup>
</p:toolbar>
@@ -188,33 +198,45 @@
<p:column headerText="Actions" style="width:200px">
<div class="flex gap-1">
<p:commandButton icon="pi pi-eye"
styleClass="ui-button-rounded ui-button-text ui-button-info"
action="#{membreListeBean.voirProfil(membre)}"
title="Voir profil" />
<p:commandButton icon="pi pi-pencil"
styleClass="ui-button-rounded ui-button-text ui-button-warning"
action="#{membreListeBean.modifierMembre(membre)}"
title="Modifier" />
<p:commandButton icon="pi pi-dollar"
styleClass="ui-button-rounded ui-button-text ui-button-success"
action="#{membreListeBean.gererCotisations(membre)}"
title="Cotisations" />
<p:commandButton icon="pi pi-envelope"
styleClass="ui-button-rounded ui-button-text ui-button-secondary"
action="#{membreListeBean.contacterMembre(membre)}"
title="Contacter" />
<p:commandButton icon="pi pi-ban"
styleClass="ui-button-rounded ui-button-text ui-button-danger"
action="#{membreListeBean.suspendreMembre(membre)}"
onclick="return confirm('Êtes-vous sûr de vouloir suspendre ce membre ?');"
title="Suspendre"
rendered="#{membre.statut == 'ACTIF'}" />
<p:commandButton icon="pi pi-check"
styleClass="ui-button-rounded ui-button-text ui-button-success"
action="#{membreListeBean.reactiverMembre(membre)}"
title="Réactiver"
rendered="#{membre.statut == 'SUSPENDU'}" />
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-eye" />
<ui:param name="action" value="#{membreListeBean.voirProfil(membre)}" />
<ui:param name="title" value="Voir profil" />
<ui:param name="severity" value="info" />
</ui:include>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-pencil" />
<ui:param name="action" value="#{membreListeBean.modifierMembre(membre)}" />
<ui:param name="title" value="Modifier" />
<ui:param name="severity" value="warning" />
</ui:include>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-dollar" />
<ui:param name="action" value="#{membreListeBean.gererCotisations(membre)}" />
<ui:param name="title" value="Cotisations" />
<ui:param name="severity" value="success" />
</ui:include>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-envelope" />
<ui:param name="action" value="#{membreListeBean.contacterMembre(membre)}" />
<ui:param name="title" value="Contacter" />
<ui:param name="severity" value="" />
</ui:include>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-ban" />
<ui:param name="action" value="#{membreListeBean.suspendreMembre(membre)}" />
<ui:param name="onclick" value="return confirm('Êtes-vous sûr de vouloir suspendre ce membre ?');" />
<ui:param name="title" value="Suspendre" />
<ui:param name="severity" value="danger" />
<ui:param name="rendered" value="#{membre.statut == 'ACTIF'}" />
</ui:include>
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-check" />
<ui:param name="action" value="#{membreListeBean.reactiverMembre(membre)}" />
<ui:param name="title" value="Réactiver" />
<ui:param name="severity" value="success" />
<ui:param name="rendered" value="#{membre.statut == 'SUSPENDU'}" />
</ui:include>
</div>
</p:column>
</p:dataTable>
@@ -228,29 +250,35 @@
</span>
</div>
<div class="flex gap-2">
<p:commandButton value="Envoyer message"
icon="pi pi-envelope"
styleClass="ui-button-outlined ui-button-info"
onclick="PF('dlgMessageGroupe').show();"
disabled="#{empty membreListeBean.selectedMembres}" />
<p:commandButton value="Rappel cotisations"
icon="pi pi-bell"
styleClass="ui-button-outlined ui-button-warning"
action="#{membreListeBean.rappelCotisationsGroupe}"
update="@form"
process="@form"
disabled="#{empty membreListeBean.selectedMembres}" />
<p:commandButton value="Exporter sélection"
icon="pi pi-file-excel"
styleClass="ui-button-outlined ui-button-success"
action="#{membreListeBean.exporterSelection}"
process="@form"
disabled="#{empty membreListeBean.selectedMembres}" />
<p:commandButton value="Modifier en lot"
icon="pi pi-pencil"
styleClass="ui-button-outlined ui-button-secondary"
onclick="PF('dlgModificationLot').show();"
disabled="#{empty membreListeBean.selectedMembres}" />
<ui:include src="/templates/components/button-info.xhtml">
<ui:param name="value" value="Envoyer message" />
<ui:param name="icon" value="pi pi-envelope" />
<ui:param name="onclick" value="PF('dlgMessageGroupe').show();" />
<ui:param name="outlined" value="true" />
<ui:param name="disabled" value="#{empty membreListeBean.selectedMembres}" />
</ui:include>
<ui:include src="/templates/components/button-warning.xhtml">
<ui:param name="value" value="Rappel cotisations" />
<ui:param name="icon" value="pi pi-bell" />
<ui:param name="action" value="#{membreListeBean.rappelCotisationsGroupe}" />
<ui:param name="update" value="@form" />
<ui:param name="outlined" value="true" />
<ui:param name="disabled" value="#{empty membreListeBean.selectedMembres}" />
</ui:include>
<ui:include src="/templates/components/button-success.xhtml">
<ui:param name="value" value="Exporter sélection" />
<ui:param name="icon" value="pi pi-file-excel" />
<ui:param name="action" value="#{membreListeBean.exporterSelection}" />
<ui:param name="outlined" value="true" />
<ui:param name="disabled" value="#{empty membreListeBean.selectedMembres}" />
</ui:include>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Modifier en lot" />
<ui:param name="icon" value="pi pi-pencil" />
<ui:param name="onclick" value="PF('dlgModificationLot').show();" />
<ui:param name="outlined" value="true" />
<ui:param name="disabled" value="#{empty membreListeBean.selectedMembres}" />
</ui:include>
</div>
</div>
</h:form>
@@ -320,18 +348,27 @@
</div>
<div class="flex gap-2 mt-3">
<p:commandButton value="Appliquer" icon="pi pi-check"
styleClass="ui-button-info"
action="#{membreListeBean.appliquerFiltresAvances}"
update=":formMembres:dtMembres"
oncomplete="PF('dlgFiltresAvances').hide();" />
<p:commandButton value="Réinitialiser" icon="pi pi-refresh"
styleClass="ui-button-outlined ui-button-secondary"
action="#{membreListeBean.reinitialiserFiltres}"
update="@form :formMembres:dtMembres" />
<p:commandButton value="Annuler" icon="pi pi-times"
styleClass="ui-button-outlined ui-button-danger"
onclick="PF('dlgFiltresAvances').hide();" type="button" />
<ui:include src="/templates/components/button-info.xhtml">
<ui:param name="value" value="Appliquer" />
<ui:param name="icon" value="pi pi-check" />
<ui:param name="action" value="#{membreListeBean.appliquerFiltresAvances}" />
<ui:param name="update" value=":formMembres:dtMembres" />
<ui:param name="onclick" value="PF('dlgFiltresAvances').hide();" />
</ui:include>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Réinitialiser" />
<ui:param name="icon" value="pi pi-refresh" />
<ui:param name="action" value="#{membreListeBean.reinitialiserFiltres}" />
<ui:param name="update" value="@form :formMembres:dtMembres" />
<ui:param name="outlined" value="true" />
</ui:include>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Annuler" />
<ui:param name="icon" value="pi pi-times" />
<ui:param name="onclick" value="PF('dlgFiltresAvances').hide();" />
<ui:param name="outlined" value="true" />
<ui:param name="styleClass" value="ui-button-danger" />
</ui:include>
</div>
</h:form>
</p:dialog>
@@ -370,14 +407,19 @@
</div>
<div class="flex gap-2 mt-3">
<p:commandButton value="Envoyer" icon="pi pi-send"
styleClass="ui-button-info"
action="#{membreListeBean.envoyerMessageGroupe}"
update="@form"
oncomplete="if(!args.validationFailed) PF('dlgMessageGroupe').hide();" />
<p:commandButton value="Annuler" icon="pi pi-times"
styleClass="ui-button-secondary"
onclick="PF('dlgMessageGroupe').hide();" type="button" />
<ui:include src="/templates/components/button-info.xhtml">
<ui:param name="value" value="Envoyer" />
<ui:param name="icon" value="pi pi-send" />
<ui:param name="action" value="#{membreListeBean.envoyerMessageGroupe}" />
<ui:param name="update" value="@form" />
<ui:param name="onclick" value="if(!args.validationFailed) PF('dlgMessageGroupe').hide();" />
</ui:include>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Annuler" />
<ui:param name="icon" value="pi pi-times" />
<ui:param name="onclick" value="PF('dlgMessageGroupe').hide();" />
<ui:param name="outlined" value="false" />
</ui:include>
</div>
</h:form>
</p:dialog>
@@ -408,13 +450,18 @@
</div>
<div class="flex gap-2 mt-3">
<p:commandButton value="Importer" icon="pi pi-upload"
styleClass="ui-button-success"
action="#{membreListeBean.importerMembres}"
update="@form :formMembres" />
<p:commandButton value="Télécharger modèle" icon="pi pi-download"
styleClass="ui-button-outlined ui-button-info"
action="#{membreListeBean.telechargerModele}" />
<ui:include src="/templates/components/button-success.xhtml">
<ui:param name="value" value="Importer" />
<ui:param name="icon" value="pi pi-upload" />
<ui:param name="action" value="#{membreListeBean.importerMembres}" />
<ui:param name="update" value="@form :formMembres" />
</ui:include>
<ui:include src="/templates/components/button-info.xhtml">
<ui:param name="value" value="Télécharger modèle" />
<ui:param name="icon" value="pi pi-download" />
<ui:param name="action" value="#{membreListeBean.telechargerModele}" />
<ui:param name="outlined" value="true" />
</ui:include>
</div>
</p:tab>
@@ -448,17 +495,22 @@
</div>
<div class="flex gap-2 mt-3">
<p:commandButton value="Exporter" icon="pi pi-download"
styleClass="ui-button-success"
action="#{membreListeBean.exporterMembres}" />
<ui:include src="/templates/components/button-success.xhtml">
<ui:param name="value" value="Exporter" />
<ui:param name="icon" value="pi pi-download" />
<ui:param name="action" value="#{membreListeBean.exporterMembres}" />
</ui:include>
</div>
</p:tab>
</p:tabView>
<div class="flex justify-content-end mt-3">
<p:commandButton value="Fermer" icon="pi pi-times"
styleClass="ui-button-secondary"
onclick="PF('dlgImportExport').hide();" type="button" />
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Fermer" />
<ui:param name="icon" value="pi pi-times" />
<ui:param name="onclick" value="PF('dlgImportExport').hide();" />
<ui:param name="outlined" value="false" />
</ui:include>
</div>
</h:form>
</p:dialog>

View File

@@ -0,0 +1,32 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<!--
Composant bouton icône seule réutilisable (WOU/DRY)
Usage: <ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-icon-name" />
<ui:param name="action" value="#{bean.method}" />
<ui:param name="update" value="componentId" />
<ui:param name="onclick" value="javascript" />
<ui:param name="severity" value="info|success|warning|danger" />
<ui:param name="rounded" value="true" />
<ui:param name="text" value="true" />
</ui:include>
-->
<ui:fragment rendered="#{empty rendered or rendered}">
<p:commandButton
icon="#{icon}"
action="#{action}"
actionListener="#{actionListener}"
update="#{update}"
onclick="#{onclick}"
disabled="#{not empty disabled and disabled}"
styleClass="#{not empty rounded and rounded ? 'ui-button-rounded' : ''} #{not empty text and text ? 'ui-button-text' : ''} #{not empty severity ? 'ui-button-' += severity : ''} #{styleClass}"
title="#{title}" />
</ui:fragment>
</ui:composition>

View File

@@ -0,0 +1,34 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<!--
Composant bouton info réutilisable (WOU/DRY)
-->
<ui:fragment rendered="#{empty rendered or rendered}">
<ui:fragment rendered="#{empty outcome}">
<p:commandButton
value="#{value}"
icon="#{icon}"
action="#{action}"
actionListener="#{actionListener}"
update="#{update}"
onclick="#{onclick}"
disabled="#{not empty disabled and disabled}"
styleClass="ui-button-info #{not empty outlined and outlined ? 'ui-button-outlined' : ''} #{styleClass}"
title="#{title}" />
</ui:fragment>
<ui:fragment rendered="#{not empty outcome}">
<p:button
value="#{value}"
icon="#{icon}"
outcome="#{outcome}"
styleClass="ui-button-info #{not empty outlined and outlined ? 'ui-button-outlined' : ''} #{styleClass}"
title="#{title}" />
</ui:fragment>
</ui:fragment>
</ui:composition>

View File

@@ -0,0 +1,44 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<!--
Composant bouton primaire réutilisable (WOU/DRY)
Usage: <ui:include src="/templates/components/button-primary.xhtml">
<ui:param name="value" value="Texte du bouton" />
<ui:param name="icon" value="pi pi-icon-name" />
<ui:param name="action" value="#{bean.method}" />
<ui:param name="outcome" value="/page" />
<ui:param name="update" value="componentId" />
<ui:param name="onclick" value="javascript" />
<ui:param name="disabled" value="false" />
<ui:param name="styleClass" value="" />
</ui:include>
-->
<ui:fragment rendered="#{empty rendered or rendered}">
<ui:fragment rendered="#{empty outcome}">
<p:commandButton
value="#{value}"
icon="#{icon}"
action="#{action}"
actionListener="#{actionListener}"
update="#{update}"
onclick="#{onclick}"
disabled="#{not empty disabled and disabled}"
styleClass="ui-button-primary #{styleClass}"
title="#{title}" />
</ui:fragment>
<ui:fragment rendered="#{not empty outcome}">
<p:button
value="#{value}"
icon="#{icon}"
outcome="#{outcome}"
styleClass="ui-button-primary #{styleClass}"
title="#{title}" />
</ui:fragment>
</ui:fragment>
</ui:composition>

View File

@@ -0,0 +1,45 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<!--
Composant bouton secondaire réutilisable (WOU/DRY)
Usage: <ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="Texte du bouton" />
<ui:param name="icon" value="pi pi-icon-name" />
<ui:param name="action" value="#{bean.method}" />
<ui:param name="outcome" value="/page" />
<ui:param name="update" value="componentId" />
<ui:param name="onclick" value="javascript" />
<ui:param name="disabled" value="false" />
<ui:param name="outlined" value="true" />
<ui:param name="styleClass" value="" />
</ui:include>
-->
<ui:fragment rendered="#{empty rendered or rendered}">
<ui:fragment rendered="#{empty outcome}">
<p:commandButton
value="#{value}"
icon="#{icon}"
action="#{action}"
actionListener="#{actionListener}"
update="#{update}"
onclick="#{onclick}"
disabled="#{not empty disabled and disabled}"
styleClass="ui-button-secondary #{not empty outlined and outlined ? 'ui-button-outlined' : ''} #{styleClass}"
title="#{title}" />
</ui:fragment>
<ui:fragment rendered="#{not empty outcome}">
<p:button
value="#{value}"
icon="#{icon}"
outcome="#{outcome}"
styleClass="ui-button-secondary #{not empty outlined and outlined ? 'ui-button-outlined' : ''} #{styleClass}"
title="#{title}" />
</ui:fragment>
</ui:fragment>
</ui:composition>

View File

@@ -0,0 +1,34 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<!--
Composant bouton succès réutilisable (WOU/DRY)
-->
<ui:fragment rendered="#{empty rendered or rendered}">
<ui:fragment rendered="#{empty outcome}">
<p:commandButton
value="#{value}"
icon="#{icon}"
action="#{action}"
actionListener="#{actionListener}"
update="#{update}"
onclick="#{onclick}"
disabled="#{not empty disabled and disabled}"
styleClass="ui-button-success #{not empty outlined and outlined ? 'ui-button-outlined' : ''} #{styleClass}"
title="#{title}" />
</ui:fragment>
<ui:fragment rendered="#{not empty outcome}">
<p:button
value="#{value}"
icon="#{icon}"
outcome="#{outcome}"
styleClass="ui-button-success #{not empty outlined and outlined ? 'ui-button-outlined' : ''} #{styleClass}"
title="#{title}" />
</ui:fragment>
</ui:fragment>
</ui:composition>

View File

@@ -0,0 +1,34 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<!--
Composant bouton warning réutilisable (WOU/DRY)
-->
<ui:fragment rendered="#{empty rendered or rendered}">
<ui:fragment rendered="#{empty outcome}">
<p:commandButton
value="#{value}"
icon="#{icon}"
action="#{action}"
actionListener="#{actionListener}"
update="#{update}"
onclick="#{onclick}"
disabled="#{not empty disabled and disabled}"
styleClass="ui-button-warning #{not empty outlined and outlined ? 'ui-button-outlined' : ''} #{styleClass}"
title="#{title}" />
</ui:fragment>
<ui:fragment rendered="#{not empty outcome}">
<p:button
value="#{value}"
icon="#{icon}"
outcome="#{outcome}"
styleClass="ui-button-warning #{not empty outlined and outlined ? 'ui-button-outlined' : ''} #{styleClass}"
title="#{title}" />
</ui:fragment>
</ui:fragment>
</ui:composition>

View File

@@ -41,7 +41,10 @@
<h:form>
<div class="newsletter-input">
<p:inputText placeholder="adresse email" />
<p:commandButton value="S'abonner" styleClass="ui-button-secondary "/>
<ui:include src="/templates/components/button-secondary.xhtml">
<ui:param name="value" value="S'abonner" />
<ui:param name="outlined" value="false" />
</ui:include>
</div>
</h:form>
</div>

View File

@@ -22,7 +22,12 @@
<div class="section-header">
<h6>Mes tâches</h6>
<h:form>
<p:commandButton type="button" icon="pi pi-plus" styleClass="ui-button-secondary ui-button-flat rounded-button" />
<ui:include src="/templates/components/button-icon.xhtml">
<ui:param name="icon" value="pi pi-plus" />
<ui:param name="rounded" value="true" />
<ui:param name="text" value="false" />
<ui:param name="styleClass" value="ui-button-secondary ui-button-flat" />
</ui:include>
</h:form>
</div>
<ul>

View File

@@ -0,0 +1,29 @@
# Configuration UnionFlow Client - Profil Développement
# Ce fichier est chargé automatiquement quand le profil 'dev' est actif
# Configuration logging pour développement
quarkus.log.category."dev.lions.unionflow".level=DEBUG
quarkus.log.category."jakarta.faces".level=INFO
quarkus.log.category."org.apache.myfaces".level=INFO
quarkus.log.category."org.primefaces".level=INFO
# Configuration MyFaces pour développement
quarkus.myfaces.project-stage=Development
quarkus.live-reload.instrumentation=true
# Configuration Keycloak pour développement
%dev.quarkus.oidc.enabled=true
%dev.quarkus.oidc.tls.verification=none
%dev.quarkus.oidc.authentication.redirect-path=/auth/callback
# %dev.quarkus.oidc.authentication.force-redirect-https=false # Not supported in this Quarkus version
%dev.quarkus.security.auth.enabled=true
# Secret Keycloak pour développement (UNIQUEMENT pour dev local)
# ⚠️ ATTENTION: Ne jamais commiter ce secret en production
# En production, utiliser la variable d'environnement KEYCLOAK_CLIENT_SECRET
%dev.quarkus.oidc.credentials.secret=${KEYCLOAK_CLIENT_SECRET:7dnWMwlabtoyp08F6FIuDxzDPE5VdUF6}
# Logging OIDC pour debug
%dev.quarkus.log.category."io.quarkus.oidc".level=DEBUG
%dev.quarkus.log.category."io.vertx.ext.auth.oidc".level=DEBUG
%dev.quarkus.log.category."io.quarkus.security".level=DEBUG

View File

@@ -0,0 +1,14 @@
# Configuration UnionFlow Client - Profil Production
# Ce fichier est chargé automatiquement quand le profil 'prod' est actif
# Configuration logging pour production
quarkus.log.console.level=WARN
# Configuration MyFaces pour production
quarkus.myfaces.project-stage=Production
quarkus.myfaces.serialize-state-in-session=true
# Configuration Keycloak pour production
%prod.quarkus.oidc.tls.verification=required
%prod.quarkus.oidc.authentication.redirect-path=/auth/callback

View File

@@ -3,7 +3,7 @@ quarkus.application.name=unionflow-client
quarkus.application.version=1.0.0
# Configuration HTTP
quarkus.http.port=8082
quarkus.http.port=8086
quarkus.http.host=0.0.0.0
quarkus.http.root-path=/
quarkus.http.so-reuse-port=true
@@ -28,7 +28,7 @@ quarkus.myfaces.number-of-views-in-session=50
quarkus.myfaces.number-of-sequential-views-in-session=10
quarkus.myfaces.serialize-state-in-session=false
quarkus.myfaces.client-view-state-timeout=3600000
quarkus.myfaces.view-expired-exception-handler-redirect-page=/pages/public/login.xhtml
quarkus.myfaces.view-expired-exception-handler-redirect-page=/
quarkus.myfaces.check-id-production-mode=false
quarkus.myfaces.strict-xhtml-links=false
quarkus.myfaces.refresh-transient-build-on-pss=true
@@ -36,7 +36,9 @@ quarkus.myfaces.resource-max-time-expires=604800000
quarkus.myfaces.resource-buffer-size=2048
# PrimeFaces Configuration
primefaces.THEME=freya-blue-light
# IMPORTANT: Nous laissons PrimeFaces sans th<74>me par d<>faut et chargeons le th<74>me Freya via index.xhtml/main-template.xhtml
# pour <20>viter tout double-chargement (ex: Saga + Freya).
primefaces.THEME=none
primefaces.FONT_AWESOME=true
primefaces.CLIENT_SIDE_VALIDATION=true
primefaces.MOVE_SCRIPTS_TO_BOTTOM=true
@@ -51,7 +53,7 @@ omnifaces.CDN_RESOURCE_HANDLER_DISABLED=true
omnifaces.COMBINED_RESOURCE_HANDLER_DISABLED=false
# Configuration Backend UnionFlow
unionflow.backend.url=${UNIONFLOW_BACKEND_URL:http://localhost:8080}
unionflow.backend.url=${UNIONFLOW_BACKEND_URL:http://localhost:8085}
# Configuration REST Client
quarkus.rest-client."unionflow-api".url=${unionflow.backend.url}
@@ -62,11 +64,46 @@ quarkus.rest-client."unionflow-api".read-timeout=30000
# Gestion des erreurs REST
quarkus.rest-client."unionflow-api".providers=dev.lions.unionflow.client.service.RestClientExceptionMapper
# Configuration JWT et Sécurité
unionflow.jwt.secret=${JWT_SECRET:union-flow-secret-key-very-long-and-secure-2024}
unionflow.jwt.issuer=${JWT_ISSUER:unionflow-platform}
unionflow.jwt.expiration-time=${JWT_EXPIRATION:3600}
unionflow.jwt.refresh-expiration-time=${JWT_REFRESH_EXPIRATION:86400}
# Configuration Keycloak OIDC
quarkus.oidc.enabled=true
quarkus.oidc.auth-server-url=https://security.lions.dev/realms/unionflow
quarkus.oidc.client-id=unionflow-client
quarkus.oidc.credentials.secret=${KEYCLOAK_CLIENT_SECRET}
quarkus.oidc.application-type=web-app
quarkus.oidc.authentication.redirect-path=/auth/callback
quarkus.oidc.authentication.restore-path-after-redirect=true
quarkus.oidc.authentication.scopes=openid,profile,email,roles
quarkus.oidc.token.issuer=https://security.lions.dev/realms/unionflow
quarkus.oidc.tls.verification=none
# quarkus.oidc.authentication.force-redirect-https=false # Not supported in this Quarkus version
quarkus.oidc.authentication.cookie-same-site=lax
quarkus.oidc.authentication.java-script-auto-redirect=false
quarkus.oidc.discovery-enabled=true
# TEMPORAIRE: contourner un access token invalide (claim realm_access dupliqu<71>e c<>t<EFBFBD> KC)
# Pour un flux web-app, on peut s'appuyer sur l'ID Token et d<>sactiver la v<>rification de l'Access Token
# Les deux cl<63>s ci?dessous couvrent les variantes selon version de Quarkus; l'une sera ignor<6F>e si non support<72>e.
# Vérification du token activée
# ✅ CORRIGÉ: Le mapper Keycloak problématique a été supprimé (17/11/2025)
# Le scope "roles" gère maintenant correctement realm_access.roles (objet unique)
quarkus.oidc.verify-access-token=true
# Activation de la sécurité
quarkus.security.auth.enabled=true
# Chemins publics (non prot<6F>g<EFBFBD>s par OIDC) - Doit <20>tre d<>fini en premier
# La page d'accueil (/) et index.xhtml sont publics pour permettre l'affichage initial
# IMPORTANT: Les ressources JSF/PrimeFaces sont servies via /jakarta.faces.resource/* (ou /javax.faces.resource/* selon la stack)
# Elles doivent <20>tre publiques pour permettre le chargement des CSS/JS (th<74>me Freya, primeicons, primeflex, etc.)
# On inclut <20>galement /q/oidc/* pour laisser Quarkus OIDC exposer ses endpoints internes si n<>cessaire.
quarkus.http.auth.permission.public.paths=/,/index.xhtml,/pages/public/*,/auth/*,/q/*,/q/oidc/*,/favicon.ico,/resources/*,/META-INF/resources/*,/images/*,/jakarta.faces.resource/*,/javax.faces.resource/*
quarkus.http.auth.permission.public.policy=permit
# Tous les autres chemins nécessitent une authentification
# IMPORTANT: L'ordre est crucial - les permissions publiques doivent être définies AVANT les permissions authentifiées
# Quarkus OIDC redirigera automatiquement vers Keycloak pour les chemins non publics
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated
# Configuration Session
unionflow.session.timeout=${SESSION_TIMEOUT:1800}
@@ -78,16 +115,3 @@ unionflow.security.password.min-length=${PASSWORD_MIN_LENGTH:8}
unionflow.security.password.require-special-chars=${PASSWORD_REQUIRE_SPECIAL:true}
unionflow.security.max-login-attempts=${MAX_LOGIN_ATTEMPTS:5}
unionflow.security.lockout-duration=${LOCKOUT_DURATION:300}
# Dev mode configuration
%dev.quarkus.log.category."dev.lions.unionflow".level=DEBUG
%dev.quarkus.log.category."jakarta.faces".level=INFO
%dev.quarkus.log.category."org.apache.myfaces".level=INFO
%dev.quarkus.log.category."org.primefaces".level=INFO
%dev.quarkus.myfaces.project-stage=Development
%dev.quarkus.live-reload.instrumentation=true
# Prod mode configuration
%prod.quarkus.log.console.level=WARN
%prod.quarkus.myfaces.project-stage=Production
%prod.quarkus.myfaces.serialize-state-in-session=true