fix(frontend): corrections workflow v3.0 — inscription événements, CreateMembreRequest, AJAX session expiry
Services:
- EvenementService: POST /inscriptions (sans membreId), DELETE /inscriptions, GET /recherche, GET /type/{type}
- MembreService: creer() accepte CreateMembreRequest au lieu de MembreResponse
- Nouveaux services: BackupService, EpargneService, FinanceApprovalService, LogsService, MessageService, OrganisationService, PaiementClientService
Beans:
- MembreInscriptionBean: construit CreateMembreRequest.builder() avec organisationId UUID
- EvenementsBean: inscrireParticipant(id) sans userId (backend infère depuis token)
- DashboardBean: checkAccessAndRedirect() SUPER_ADMIN en premier
Sécurité:
- AuthenticationFilter: gestion AJAX PrimeFaces (partial/ajax → XML partial-response redirect)
- PermissionChecker: vérification rôles côté bean
- k8s/: manifestes secrets SMTP et Wave (placeholders à remplir)
Pages XHTML: dashboards rôles, cotisations, membres, événements, organisations
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.organisation.response.OrganisationResponse;
|
||||
import dev.lions.unionflow.server.api.dto.organisation.response.OrganisationSummaryResponse;
|
||||
import dev.lions.unionflow.server.api.dto.common.PagedResponse;
|
||||
import dev.lions.unionflow.server.api.dto.abonnement.response.AbonnementResponse;
|
||||
import dev.lions.unionflow.server.api.dto.common.PagedResponse;
|
||||
import dev.lions.unionflow.server.api.dto.souscription.SouscriptionStatutResponse;
|
||||
import dev.lions.unionflow.client.service.AdminUserService;
|
||||
import dev.lions.unionflow.client.service.AssociationService;
|
||||
import dev.lions.unionflow.client.service.OrganisationService;
|
||||
import dev.lions.unionflow.client.service.AuditService;
|
||||
import dev.lions.unionflow.client.service.CotisationService;
|
||||
import dev.lions.unionflow.client.service.MembreService;
|
||||
import dev.lions.unionflow.client.service.MetricsService;
|
||||
import dev.lions.unionflow.client.service.SouscriptionService;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
@@ -23,6 +24,7 @@ import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
@@ -41,10 +43,12 @@ public class SuperAdminBean implements Serializable {
|
||||
private static final String OUTCOME_SUPER_ADMIN_CONFIGURATION = "superAdminConfigurationPage";
|
||||
private static final String OUTCOME_SUPER_ADMIN_ALERTES = "superAdminAlertesPage";
|
||||
private static final String OUTCOME_SUPER_ADMIN_ACTIVITE = "superAdminActivitePage";
|
||||
private static final String OUTCOME_SUPER_ADMIN_AUDIT = "/pages/admin/audit/journal";
|
||||
private static final String OUTCOME_SUPER_ADMIN_BACKUP = "/pages/secure/admin/sauvegarde";
|
||||
|
||||
@Inject
|
||||
@RestClient
|
||||
private AssociationService associationService;
|
||||
private OrganisationService organisationService;
|
||||
|
||||
@Inject
|
||||
@RestClient
|
||||
@@ -62,6 +66,10 @@ public class SuperAdminBean implements Serializable {
|
||||
@RestClient
|
||||
private SouscriptionService souscriptionService;
|
||||
|
||||
@Inject
|
||||
@RestClient
|
||||
private MembreService membreService;
|
||||
|
||||
@Inject
|
||||
private MetricsService metricsService;
|
||||
|
||||
@@ -116,6 +124,10 @@ public class SuperAdminBean implements Serializable {
|
||||
private RevenusData revenus;
|
||||
private String periodeEvolution = "12M";
|
||||
|
||||
// Caches transients des stats (non sérialisés — rechargés à chaque nouvelle session)
|
||||
private transient OrganisationService.StatistiquesOrganisationDTO orgStats;
|
||||
private transient MembreService.StatistiquesMembreDTO membreStats;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
initializeUserInfo();
|
||||
@@ -142,6 +154,8 @@ public class SuperAdminBean implements Serializable {
|
||||
}
|
||||
|
||||
private void initializeKPIs() {
|
||||
loadStatsOrganisations();
|
||||
loadStatsMembres();
|
||||
initializeAssociationKPIs();
|
||||
initializeAdminCount();
|
||||
initializeCotisationKPIs();
|
||||
@@ -151,22 +165,41 @@ public class SuperAdminBean implements Serializable {
|
||||
calculerPourcentagesJauges();
|
||||
}
|
||||
|
||||
private void initializeAssociationKPIs() {
|
||||
private void loadStatsOrganisations() {
|
||||
try {
|
||||
PagedResponse<OrganisationResponse> response = associationService.listerToutes(0, 1000);
|
||||
List<OrganisationResponse> associations = (response != null && response.getData() != null) ? response.getData()
|
||||
: new ArrayList<>();
|
||||
totalEntites = associations.size();
|
||||
totalMembres = associations.stream()
|
||||
.mapToInt(a -> a.getNombreMembres() != null ? a.getNombreMembres() : 0)
|
||||
.sum();
|
||||
croissanceEntites = "0";
|
||||
nouvellesEntites = 0;
|
||||
croissanceMembres = "0";
|
||||
orgStats = organisationService.obtenirStatistiques();
|
||||
} catch (Exception e) {
|
||||
LOG.debugf("Impossible de charger les KPIs associations: %s", e.getMessage());
|
||||
LOG.debugf("Impossible de charger les stats organisations: %s", e.getMessage());
|
||||
orgStats = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadStatsMembres() {
|
||||
try {
|
||||
membreStats = membreService.obtenirStatistiques();
|
||||
} catch (Exception e) {
|
||||
LOG.debugf("Impossible de charger les stats membres: %s", e.getMessage());
|
||||
membreStats = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeAssociationKPIs() {
|
||||
if (orgStats != null) {
|
||||
totalEntites = orgStats.totalAssociations != null ? orgStats.totalAssociations.intValue() : 0;
|
||||
nouvellesEntites = orgStats.nouvellesAssociations30Jours != null
|
||||
? orgStats.nouvellesAssociations30Jours.intValue() : 0;
|
||||
} else {
|
||||
totalEntites = 0;
|
||||
nouvellesEntites = 0;
|
||||
}
|
||||
croissanceEntites = nouvellesEntites > 0 ? "+" + nouvellesEntites : "0";
|
||||
if (membreStats != null) {
|
||||
totalMembres = membreStats.membresActifs != null ? membreStats.membresActifs.intValue() : 0;
|
||||
croissanceMembres = membreStats.nouveauxMembres30Jours != null
|
||||
? membreStats.nouveauxMembres30Jours.toString() : "0";
|
||||
} else {
|
||||
totalMembres = 0;
|
||||
croissanceMembres = "0";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,14 +222,20 @@ public class SuperAdminBean implements Serializable {
|
||||
try {
|
||||
Map<String, Object> stats = cotisationService.obtenirStatistiques();
|
||||
if (stats != null) {
|
||||
Object montantTotal = stats.get("montantTotal");
|
||||
Object montantTotal = stats.get("montantTotalPaye");
|
||||
if (montantTotal instanceof Number) {
|
||||
revenusGlobaux = String.format("%,.0f FCFA", ((Number) montantTotal).doubleValue());
|
||||
} else {
|
||||
revenusGlobaux = "0 FCFA";
|
||||
}
|
||||
Object croissance = stats.get("croissance");
|
||||
croissanceRevenus = croissance != null ? String.valueOf(croissance) : "0";
|
||||
// tauxPaiement = % de cotisations payées (proxy de la santé financière)
|
||||
Object croissance = stats.get("tauxPaiement");
|
||||
if (croissance instanceof Number) {
|
||||
double val = ((Number) croissance).doubleValue();
|
||||
croissanceRevenus = val == 0.0 ? "0" : String.format(Locale.US, "%.1f", val);
|
||||
} else {
|
||||
croissanceRevenus = "0";
|
||||
}
|
||||
} else {
|
||||
revenusGlobaux = "0 FCFA";
|
||||
croissanceRevenus = "0";
|
||||
@@ -212,10 +251,12 @@ public class SuperAdminBean implements Serializable {
|
||||
try {
|
||||
Map<String, Object> stats = auditService.getStatistiques();
|
||||
if (stats != null) {
|
||||
Object activitesJour = stats.get("activitesAujourdhui");
|
||||
activiteJournaliere = activitesJour instanceof Number ? ((Number) activitesJour).intValue() : 0;
|
||||
Object alertes = stats.get("alertes");
|
||||
alertesCount = alertes instanceof Number ? ((Number) alertes).intValue() : 0;
|
||||
// "total" = nombre total d'actions enregistrées (proxy activité)
|
||||
Object total = stats.get("total");
|
||||
activiteJournaliere = total instanceof Number ? ((Number) total).intValue() : 0;
|
||||
// "errors" = erreurs critiques enregistrées (proxy alertes système)
|
||||
Object errors = stats.get("errors");
|
||||
alertesCount = errors instanceof Number ? ((Number) errors).intValue() : 0;
|
||||
} else {
|
||||
activiteJournaliere = 0;
|
||||
alertesCount = 0;
|
||||
@@ -230,11 +271,11 @@ public class SuperAdminBean implements Serializable {
|
||||
|
||||
private void initializeSouscriptionKPIs() {
|
||||
try {
|
||||
List<AbonnementResponse> souscriptions = souscriptionService.listerToutes(null, 0, 1000);
|
||||
List<SouscriptionStatutResponse> souscriptions = souscriptionService.listerToutes(null, 0, 1000);
|
||||
if (souscriptions != null) {
|
||||
totalSouscriptions = souscriptions.size();
|
||||
souscriptionsActives = (int) souscriptions.stream()
|
||||
.filter(s -> s.getStatut() == dev.lions.unionflow.server.api.enums.abonnement.StatutAbonnement.ACTIF)
|
||||
.filter(s -> "ACTIVE".equals(s.getStatut()))
|
||||
.count();
|
||||
LocalDate dans30Jours = LocalDate.now().plusDays(30);
|
||||
souscriptionsExpirantSous30Jours = (int) souscriptions.stream()
|
||||
@@ -293,22 +334,22 @@ public class SuperAdminBean implements Serializable {
|
||||
private void initializeEntites() {
|
||||
topEntites = new ArrayList<>();
|
||||
try {
|
||||
PagedResponse<OrganisationResponse> response = associationService.listerToutes(0, 1000);
|
||||
List<OrganisationResponse> associations = (response != null && response.getData() != null) ? response.getData()
|
||||
PagedResponse<OrganisationSummaryResponse> response = organisationService.listerToutes(0, 50);
|
||||
List<OrganisationSummaryResponse> associations = (response != null && response.getData() != null) ? response.getData()
|
||||
: new ArrayList<>();
|
||||
topEntites = associations.stream()
|
||||
.sorted((a1, a2) -> {
|
||||
int m1 = a1.getNombreMembres() != null ? a1.getNombreMembres() : 0;
|
||||
int m2 = a2.getNombreMembres() != null ? a2.getNombreMembres() : 0;
|
||||
int m1 = a1.nombreMembres() != null ? a1.nombreMembres() : 0;
|
||||
int m2 = a2.nombreMembres() != null ? a2.nombreMembres() : 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.getTypeOrganisation());
|
||||
entite.setNombreMembres(a.getNombreMembres() != null ? a.getNombreMembres() : 0);
|
||||
entite.setId(a.id());
|
||||
entite.setNom(a.nom());
|
||||
entite.setTypeEntite(a.typeOrganisation());
|
||||
entite.setNombreMembres(a.nombreMembres() != null ? a.nombreMembres() : 0);
|
||||
return entite;
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
@@ -319,52 +360,47 @@ public class SuperAdminBean implements Serializable {
|
||||
|
||||
private void initializeRepartitionTypes() {
|
||||
repartitionTypes = new ArrayList<>();
|
||||
try {
|
||||
PagedResponse<OrganisationResponse> response = associationService.listerToutes(0, 1000);
|
||||
List<OrganisationResponse> associations = (response != null && response.getData() != null) ? response.getData()
|
||||
: new ArrayList<>();
|
||||
if (associations == null || associations.isEmpty())
|
||||
return;
|
||||
java.util.Map<String, Long> parType = associations.stream()
|
||||
.filter(a -> a.getTypeOrganisation() != null && !a.getTypeOrganisation().isBlank())
|
||||
.collect(java.util.stream.Collectors.groupingBy(OrganisationResponse::getTypeOrganisation,
|
||||
java.util.stream.Collectors.counting()));
|
||||
int total = associations.size();
|
||||
String[] couleurs = { "blue", "green", "orange", "purple", "teal" };
|
||||
int idx = 0;
|
||||
for (java.util.Map.Entry<String, Long> entry : parType.entrySet()) {
|
||||
TypeEntite typeEntite = new TypeEntite();
|
||||
typeEntite.setNom(entry.getKey());
|
||||
typeEntite.setDescription(entry.getKey());
|
||||
typeEntite.setNombre(entry.getValue().intValue());
|
||||
typeEntite.setPourcentage(total > 0 ? (entry.getValue().intValue() * 100) / total : 0);
|
||||
typeEntite.setIcone("pi-building");
|
||||
typeEntite.setCouleurBg(idx < couleurs.length ? couleurs[idx] : "secondary");
|
||||
typeEntite.setCouleurTexte("white");
|
||||
repartitionTypes.add(typeEntite);
|
||||
idx++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warnf(e, "Impossible de calculer la répartition des types");
|
||||
if (orgStats == null || orgStats.repartitionParType == null || orgStats.repartitionParType.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int total = orgStats.totalAssociations != null ? orgStats.totalAssociations.intValue() : 0;
|
||||
String[] couleurs = { "blue", "green", "orange", "purple", "teal" };
|
||||
int idx = 0;
|
||||
for (java.util.Map.Entry<String, Long> entry : orgStats.repartitionParType.entrySet()) {
|
||||
TypeEntite typeEntite = new TypeEntite();
|
||||
typeEntite.setNom(entry.getKey());
|
||||
typeEntite.setDescription(entry.getKey());
|
||||
typeEntite.setNombre(entry.getValue().intValue());
|
||||
typeEntite.setPourcentage(total > 0 ? (entry.getValue().intValue() * 100) / total : 0);
|
||||
typeEntite.setIcone("pi-building");
|
||||
typeEntite.setCouleurBg(idx < couleurs.length ? couleurs[idx] : "secondary");
|
||||
typeEntite.setCouleurTexte("white");
|
||||
repartitionTypes.add(typeEntite);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeActivites() {
|
||||
activitesRecentes = new ArrayList<>();
|
||||
try {
|
||||
Map<String, Object> auditResult = auditService.listerTous(0, 10, "date", "desc");
|
||||
if (auditResult != null && auditResult.containsKey("content")) {
|
||||
// sortBy=dateHeure (nom réel du champ), sortOrder=desc
|
||||
Map<String, Object> auditResult = auditService.listerTous(0, 10, "dateHeure", "desc");
|
||||
if (auditResult != null && auditResult.containsKey("data")) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> entries = (List<Map<String, Object>>) auditResult.get("content");
|
||||
List<Map<String, Object>> entries = (List<Map<String, Object>>) auditResult.get("data");
|
||||
if (entries != null) {
|
||||
for (Map<String, Object> entry : entries) {
|
||||
Activite activite = new Activite();
|
||||
activite.setId(UUID.randomUUID());
|
||||
activite.setDescription(entry.getOrDefault("action", "").toString());
|
||||
// typeAction = action métier, description = détail lisible
|
||||
Object desc = entry.get("description");
|
||||
Object typeAction = entry.get("typeAction");
|
||||
activite.setDescription(desc != null ? desc.toString()
|
||||
: (typeAction != null ? typeAction.toString() : ""));
|
||||
activite.setEntite(entry.getOrDefault("module", "").toString());
|
||||
activite.setUtilisateur(entry.getOrDefault("utilisateur", "").toString());
|
||||
activite.setDetails(entry.getOrDefault("details", "").toString());
|
||||
Object dateObj = entry.get("date");
|
||||
Object dateObj = entry.get("dateHeure");
|
||||
activite.setDate(dateObj != null ? dateObj.toString() : "");
|
||||
activite.setIcone("pi-history");
|
||||
activitesRecentes.add(activite);
|
||||
@@ -423,6 +459,14 @@ public class SuperAdminBean implements Serializable {
|
||||
return OUTCOME_SUPER_ADMIN_ACTIVITE + "?faces-redirect=true";
|
||||
}
|
||||
|
||||
public String auditSysteme() {
|
||||
return OUTCOME_SUPER_ADMIN_AUDIT + "?faces-redirect=true";
|
||||
}
|
||||
|
||||
public String backup() {
|
||||
return OUTCOME_SUPER_ADMIN_BACKUP + "?faces-redirect=true";
|
||||
}
|
||||
|
||||
public void exporterRapportFinancier() {
|
||||
LOG.info("Export du rapport financier généré");
|
||||
}
|
||||
@@ -615,15 +659,15 @@ public class SuperAdminBean implements Serializable {
|
||||
}
|
||||
|
||||
public String getTauxConversionFormat() {
|
||||
return String.format("%.1f%%", tauxConversion);
|
||||
return String.format(Locale.US, "%.1f%%", tauxConversion);
|
||||
}
|
||||
|
||||
public String getDisponibiliteSystemeFormat() {
|
||||
return String.format("%.1f%%", disponibiliteSysteme);
|
||||
return String.format(Locale.US, "%.1f%%", disponibiliteSysteme);
|
||||
}
|
||||
|
||||
public String getSatisfactionClientFormat() {
|
||||
return String.format("%.1f/5", satisfactionClient);
|
||||
return String.format(Locale.US, "%.1f/5", satisfactionClient);
|
||||
}
|
||||
|
||||
public List<Alerte> getAlertesRecentes() {
|
||||
|
||||
Reference in New Issue
Block a user