Compare commits
10 Commits
b0d23a0003
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d04a625811 | ||
|
|
fcaac36a14 | ||
|
|
e936af7d39 | ||
|
|
11a1299bc7 | ||
|
|
cdcdb37459 | ||
|
|
917c8c5359 | ||
|
|
8f96fa4209 | ||
|
|
a7788036eb | ||
| 0c5a027ec3 | |||
| 0f360dab83 |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -7,6 +7,23 @@ et ce projet adhère au [Semantic Versioning](https://semver.org/lang/fr/).
|
||||
|
||||
---
|
||||
|
||||
## [3.1.0] - 2026-04-24 🚀 **Quarkus 3.27.3 LTS**
|
||||
|
||||
### Infrastructure
|
||||
|
||||
- ⬆️ **Quarkus** : 3.15.1 → **3.27.3 LTS** (Java 17 → **Java 21**)
|
||||
- ⬆️ **unionflow-server-api** : consommé en **1.0.7** (alignement DTOs + enrichissements `ville`/`pays`)
|
||||
- 🔄 **Pipeline lionsctl** : migration déploiement Helm → `lionsctl pipeline` non-Helm
|
||||
- Nouveau `Dockerfile` racine (`ubi8/openjdk-21:1.21`, UID 1001, `java -jar` direct)
|
||||
- `.dockerignore` adapté fast-jar (`target/*` + `!target/quarkus-app/**`)
|
||||
- 🔧 **Config Quarkus** : renommage clés dépréciées (5 props)
|
||||
|
||||
### Déploiement
|
||||
|
||||
- URL prod `https://unionflow.lions.dev` redéployée avec succès (selector Helm supprimé, svc repatch flat `app:`)
|
||||
|
||||
---
|
||||
|
||||
## [3.0.0] - 2026-01-04 🚀 **PRODUCTION-READY**
|
||||
|
||||
### 🎯 Migration Complète vers Architecture Production-Ready
|
||||
|
||||
22
Dockerfile
Normal file
22
Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
||||
# Dockerfile for unionflow-client-quarkus-primefaces-freya
|
||||
# Used by lionsctl pipeline. Expects `mvn clean package -Pprod` to have produced target/quarkus-app/ (fast-jar).
|
||||
FROM registry.access.redhat.com/ubi8/openjdk-21:1.21
|
||||
|
||||
ENV LANGUAGE='en_US:en'
|
||||
|
||||
COPY --chown=1001:1001 target/quarkus-app/lib/ /deployments/lib/
|
||||
COPY --chown=1001:1001 target/quarkus-app/*.jar /deployments/
|
||||
COPY --chown=1001:1001 target/quarkus-app/app/ /deployments/app/
|
||||
COPY --chown=1001:1001 target/quarkus-app/quarkus/ /deployments/quarkus/
|
||||
|
||||
USER 1001
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENV JAVA_OPTS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=30s --retries=3 \
|
||||
CMD curl -f http://localhost:8080/health || exit 1
|
||||
|
||||
ENTRYPOINT [ "java", "-jar", "/deployments/quarkus-run.jar" ]
|
||||
27
README.md
27
README.md
@@ -1,7 +1,7 @@
|
||||
# UnionFlow Client - Application Web de Gestion
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
@@ -35,7 +35,7 @@ Application web moderne de gestion pour organisations Lions Club, basée sur Qua
|
||||
|
||||
### Technologies Clés
|
||||
|
||||
- **Backend Framework**: Quarkus 3.15.1 (JVM optimisé, démarrage rapide)
|
||||
- **Backend Framework**: Quarkus 3.27.3 LTS (Java 21, JVM optimisé, démarrage rapide)
|
||||
- **UI Framework**: JSF 4.0 (MyFaces) + PrimeFaces 14.0.5
|
||||
- **UI Theme**: Freya 5.0.0 (design moderne et responsive)
|
||||
- **Sécurité**: Keycloak OIDC + JWT
|
||||
@@ -95,7 +95,7 @@ Application web moderne de gestion pour organisations Lions Club, basée sur Qua
|
||||
| Composant | Version | Rôle |
|
||||
|-----------|---------|------|
|
||||
| Java | 17 (LTS) | Langage |
|
||||
| Quarkus | 3.15.1 | Framework application |
|
||||
| Quarkus | 3.27.3 LTS | Framework application |
|
||||
| Jakarta EE | 10 | Standard entreprise |
|
||||
| JSF (MyFaces) | 4.0 | MVC web framework |
|
||||
| PrimeFaces | 14.0.5 | Composants riches |
|
||||
@@ -286,8 +286,8 @@ java -Dquarkus.profile=prod -jar target/quarkus-app/quarkus-run.jar
|
||||
### Docker (Optionnel)
|
||||
|
||||
```bash
|
||||
# Construction de l'image
|
||||
docker build -f src/main/docker/Dockerfile.jvm -t unionflow-client:latest .
|
||||
# Construction de l'image (Dockerfile à la racine — fast-jar, ubi8/openjdk-21:1.21, UID 1001)
|
||||
docker build -t unionflow-client:latest .
|
||||
|
||||
# Lancement du conteneur
|
||||
docker run -p 8080:8080 \
|
||||
@@ -296,6 +296,21 @@ docker run -p 8080:8080 \
|
||||
unionflow-client:latest
|
||||
```
|
||||
|
||||
### Déploiement prod — lionsctl pipeline
|
||||
|
||||
```bash
|
||||
lionsctl pipeline \
|
||||
-u https://git.lions.dev/lionsdev/unionflow-client-quarkus-primefaces-freya \
|
||||
-b main -j 21 -e production -c k1 -p prod
|
||||
```
|
||||
|
||||
**URL prod** : `https://unionflow.lions.dev`
|
||||
|
||||
**Pré-requis infrastructure** (migration Helm → lionsctl) :
|
||||
- Deployment Helm existant supprimé au préalable (selector immutable)
|
||||
- Service selector à repatcher après pipeline (retirer labels `app.kubernetes.io/*`)
|
||||
- OIDC : le pipeline hérite des secrets ESO `unionflow-client-oidc-eso` + `brevo-smtp-eso` (déjà référencés via `envFrom`)
|
||||
|
||||
## 🔒 Sécurité
|
||||
|
||||
### Authentification et Autorisation
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -142,7 +142,7 @@
|
||||
<dependency>
|
||||
<groupId>dev.lions.unionflow</groupId>
|
||||
<artifactId>unionflow-server-api</artifactId>
|
||||
<version>1.0.6</version>
|
||||
<version>1.0.10</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Lions User Manager Client - Module réutilisable de gestion d'utilisateurs Keycloak -->
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package dev.lions.unionflow.client.api.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Miroir local du record {@code ComplianceSnapshot} backend (P1-NEW-7).
|
||||
* @since 2026-04-25 (Sprint 8)
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record ComplianceSnapshotDto(
|
||||
UUID organisationId,
|
||||
String organisationNom,
|
||||
String referentielComptable,
|
||||
boolean complianceOfficerDesigne,
|
||||
ConformiteIndicateurDto agAnnuelle,
|
||||
ConformiteIndicateurDto rapportAirms,
|
||||
int dirigeantsAvecCmu,
|
||||
BigDecimal tauxKycAJourPct,
|
||||
BigDecimal tauxFormationLbcFtPct,
|
||||
ConformiteIndicateurDto commissaireAuxComptes,
|
||||
ConformiteIndicateurDto fomusCi,
|
||||
BigDecimal couvertureUboPct,
|
||||
int scoreGlobal
|
||||
) {
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record ConformiteIndicateurDto(String statut, String message) {}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package dev.lions.unionflow.client.api.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Miroir local du DTO {@code ReadinessReport} backend (P1-NEW-15).
|
||||
*
|
||||
* @since 2026-04-25 (Sprint 8)
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record PispiReadinessDto(
|
||||
String globalStatus, // READY, DEGRADED, BLOCKED
|
||||
String baseUrl,
|
||||
List<CheckResultDto> checks,
|
||||
List<String> blockingIssues,
|
||||
List<String> warnings
|
||||
) {
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record CheckResultDto(
|
||||
String name,
|
||||
String status, // PASS, FAIL
|
||||
String severity, // BLOCKING, WARNING
|
||||
String message
|
||||
) {}
|
||||
|
||||
public boolean estReady() { return "READY".equals(globalStatus); }
|
||||
public boolean estBlocked() { return "BLOCKED".equals(globalStatus); }
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package dev.lions.unionflow.client.api.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Miroir local de l'entité {@code RapportTrimestrielControleurInterne} backend (P2-NEW-3).
|
||||
*
|
||||
* <p>Champs JSONB et byte[] omis — récupérés via endpoint PDF dédié.
|
||||
*
|
||||
* @since 2026-04-25 (Sprint 8)
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record RapportTrimestrielDto(
|
||||
UUID id,
|
||||
UUID organisationId,
|
||||
Integer annee,
|
||||
Integer trimestre,
|
||||
LocalDateTime dateGeneration,
|
||||
String statut,
|
||||
Integer scoreConformite,
|
||||
UUID signataireId,
|
||||
LocalDateTime dateSignature,
|
||||
String hashSha256
|
||||
) {
|
||||
public boolean estDraft() { return "DRAFT".equals(statut); }
|
||||
public boolean estSigne() { return "SIGNE".equals(statut); }
|
||||
public boolean estArchive() { return "ARCHIVE".equals(statut); }
|
||||
}
|
||||
@@ -616,6 +616,61 @@ public class MenuBean implements Serializable {
|
||||
hasAnyRole("SUPER_ADMIN", "ADMIN_ORGANISATION");
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Pages conformité (Sprint 8 — backend P1-NEW-7 / P2-NEW-3 / P1-NEW-15)
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Tableau de bord conformité — Compliance Officer / Contrôleur Interne / direction.
|
||||
* @since 2026-04-25
|
||||
*/
|
||||
public boolean isConformiteDashboardVisible() {
|
||||
return hasAnyRole("SUPER_ADMIN", "ADMIN_ORGANISATION", "PRESIDENT",
|
||||
"TRESORIER", "COMPLIANCE_OFFICER", "CONTROLEUR_INTERNE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Rapports trimestriels Contrôleur Interne — restreint contrôleur + président + admin.
|
||||
* @since 2026-04-25
|
||||
*/
|
||||
public boolean isRapportsTrimestrielsVisible() {
|
||||
return hasAnyRole("SUPER_ADMIN", "ADMIN_ORGANISATION", "PRESIDENT",
|
||||
"CONTROLEUR_INTERNE");
|
||||
}
|
||||
|
||||
/**
|
||||
* PI-SPI Readiness — admin technique uniquement.
|
||||
* @since 2026-04-25
|
||||
*/
|
||||
public boolean isPispiReadinessVisible() {
|
||||
return hasAnyRole("SUPER_ADMIN", "COMPLIANCE_OFFICER");
|
||||
}
|
||||
|
||||
/**
|
||||
* Bénéficiaires Effectifs (UBO) — Instr. BCEAO 003-03-2025.
|
||||
* @since 2026-04-25 (Sprint 11)
|
||||
*/
|
||||
public boolean isBeneficiairesEffectifsVisible() {
|
||||
return hasAnyRole("SUPER_ADMIN", "ADMIN_ORGANISATION", "COMPLIANCE_OFFICER",
|
||||
"CONTROLEUR_INTERNE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Audit Trail viewer — compliance / contrôle interne.
|
||||
* @since 2026-04-25 (Sprint 11)
|
||||
*/
|
||||
public boolean isAuditTrailViewerVisible() {
|
||||
return hasAnyRole("SUPER_ADMIN", "COMPLIANCE_OFFICER", "CONTROLEUR_INTERNE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Délégations de rôles — admin org / président.
|
||||
* @since 2026-04-25 (Sprint 11)
|
||||
*/
|
||||
public boolean isRoleDelegationsVisible() {
|
||||
return hasAnyRole("SUPER_ADMIN", "ADMIN_ORGANISATION", "PRESIDENT");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne true si l'organisation active dispose d'au moins un module métier spécifique
|
||||
* (au-delà des modules communs toujours disponibles).
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
package dev.lions.unionflow.client.constants;
|
||||
|
||||
/**
|
||||
* Centralisation des chemins de vues JSF.
|
||||
*
|
||||
* <p>Lieu unique pour tous les outcomes de navigation. À utiliser depuis :
|
||||
* <ul>
|
||||
* <li>XHTML : {@code outcome="#{paths.uboList}"} via {@link
|
||||
* dev.lions.unionflow.client.view.ViewPathsBean}</li>
|
||||
* <li>Java beans : {@code return ViewPaths.UBO_LIST + ViewPaths.REDIRECT_SUFFIX;}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Convention de nommage : {@code MODULE_PAGE} en MAJ avec underscore
|
||||
* (ex: {@code COTISATION_PAIEMENT}, {@code SUPER_ADMIN_DASHBOARD}).
|
||||
*
|
||||
* @since 2026-04-25 (Sprint 12 — centralisation navigation DRY)
|
||||
*/
|
||||
public final class ViewPaths {
|
||||
|
||||
private ViewPaths() {
|
||||
// Classe utilitaire non instantiable
|
||||
}
|
||||
|
||||
// ─── Suffixes ────────────────────────────────────────────────────────────
|
||||
public static final String REDIRECT_SUFFIX = "?faces-redirect=true";
|
||||
|
||||
// ─── Index / racine / erreur ─────────────────────────────────────────────
|
||||
public static final String ROOT = "/";
|
||||
public static final String INDEX = "/index";
|
||||
|
||||
// ─── Dashboard ───────────────────────────────────────────────────────────
|
||||
public static final String DASHBOARD = "/pages/secure/dashboard";
|
||||
public static final String DASHBOARD_MEMBRE = "/pages/secure/dashboard-membre";
|
||||
|
||||
// ─── Profil personnel ────────────────────────────────────────────────────
|
||||
public static final String PROFILE = "/pages/secure/profile";
|
||||
public static final String PERSONNEL_PROFIL = "/pages/secure/personnel/profil";
|
||||
public static final String PERSONNEL_PARAMETRES = "/pages/secure/personnel/parametres";
|
||||
public static final String PERSONNEL_PREFERENCES = "/pages/secure/personnel/preferences";
|
||||
public static final String PERSONNEL_NOTIFICATIONS = "/pages/secure/personnel/notifications";
|
||||
|
||||
// ─── Membres ─────────────────────────────────────────────────────────────
|
||||
public static final String MEMBRE_LISTE = "/pages/secure/membre/liste";
|
||||
public static final String MEMBRE_INSCRIPTION = "/pages/secure/membre/inscription";
|
||||
public static final String MEMBRE_VALIDATION = "/pages/secure/membre/validation";
|
||||
public static final String MEMBRE_IMPORT = "/pages/secure/membre/import";
|
||||
public static final String MEMBRE_EXPORT = "/pages/secure/membre/export";
|
||||
public static final String MEMBRE_RECHERCHE = "/pages/secure/membre/recherche";
|
||||
public static final String MEMBRE_COTISATIONS = "/pages/secure/membre/cotisations";
|
||||
public static final String MEMBRE_PROFIL = "/pages/secure/membre/profil";
|
||||
public static final String MEMBRE_PAIEMENT_COTISATIONS = "/pages/secure/membre/paiement-mes-cotisations";
|
||||
|
||||
// ─── Adhésions ───────────────────────────────────────────────────────────
|
||||
public static final String ADHESION_LISTE = "/pages/secure/adhesion/liste";
|
||||
public static final String ADHESION_DEMANDE = "/pages/secure/adhesion/demande";
|
||||
public static final String ADHESION_VALIDATION = "/pages/secure/adhesion/validation";
|
||||
public static final String ADHESION_HISTORIQUE = "/pages/secure/adhesion/historique";
|
||||
public static final String ADHESION_RENOUVELLEMENT = "/pages/secure/adhesion/renouvellement";
|
||||
|
||||
// ─── Cotisations ─────────────────────────────────────────────────────────
|
||||
public static final String COTISATIONS_GESTION_ADMIN = "/pages/admin/cotisations/gestion";
|
||||
public static final String COTISATION_PAIEMENT = "/pages/secure/cotisation/paiement";
|
||||
public static final String COTISATION_HISTORIQUE = "/pages/secure/cotisation/historique";
|
||||
public static final String COTISATION_RELANCES = "/pages/secure/cotisation/relances";
|
||||
|
||||
// ─── Finance ─────────────────────────────────────────────────────────────
|
||||
public static final String FINANCE_TRESORERIE = "/pages/secure/finance/tresorerie";
|
||||
public static final String FINANCE_BUDGETS = "/pages/secure/finance/budgets";
|
||||
public static final String FINANCE_BILANS = "/pages/secure/finance/bilans";
|
||||
public static final String FINANCE_APPROBATIONS = "/pages/secure/finance/approbations";
|
||||
public static final String COMPTABILITE_GESTION = "/pages/secure/comptabilite/gestion";
|
||||
|
||||
// ─── Épargne / Crédit ────────────────────────────────────────────────────
|
||||
public static final String EPARGNE_COMPTES = "/pages/secure/epargne/comptes";
|
||||
public static final String CREDIT_DEMANDES = "/pages/secure/credit/demandes";
|
||||
public static final String CREDIT_EVALUATION = "/pages/secure/credit/evaluation";
|
||||
public static final String CREDIT_SUIVI = "/pages/secure/credit/suivi";
|
||||
public static final String CREDIT_REMBOURSEMENTS = "/pages/secure/credit/remboursements";
|
||||
public static final String CREDIT_STATISTIQUES = "/pages/secure/credit/statistiques";
|
||||
|
||||
// ─── Événements ──────────────────────────────────────────────────────────
|
||||
public static final String EVENEMENT_GESTION = "/pages/secure/evenement/gestion";
|
||||
public static final String EVENEMENT_CREATION = "/pages/secure/evenement/creation";
|
||||
public static final String EVENEMENT_CALENDRIER = "/pages/secure/evenement/calendrier";
|
||||
public static final String EVENEMENT_PARTICIPANTS = "/pages/secure/evenement/participants";
|
||||
public static final String EVENEMENT_PLANIFICATION = "/pages/secure/evenement/planification";
|
||||
public static final String EVENEMENT_LOGISTIQUE = "/pages/secure/evenement/logistique";
|
||||
public static final String EVENEMENT_BILAN = "/pages/secure/evenement/bilan";
|
||||
public static final String EVENEMENT_BILAN_DETAIL = "/pages/secure/evenement/bilan-detail";
|
||||
|
||||
// ─── Aide / support ──────────────────────────────────────────────────────
|
||||
public static final String AIDE_DEMANDE = "/pages/secure/aide/demande";
|
||||
public static final String AIDE_REQUESTS = "/pages/secure/aide/requests";
|
||||
public static final String AIDE_APPROVED = "/pages/secure/aide/approved";
|
||||
public static final String AIDE_TRAITEMENT = "/pages/secure/aide/traitement";
|
||||
public static final String AIDE_HISTORIQUE = "/pages/secure/aide/historique";
|
||||
public static final String AIDE_STATISTIQUES = "/pages/secure/aide/statistiques";
|
||||
public static final String AIDE_FAQ = "/pages/secure/aide/faq";
|
||||
public static final String AIDE_SUPPORT = "/pages/secure/aide/support";
|
||||
public static final String AIDE_APROPOS = "/pages/secure/aide/apropos";
|
||||
|
||||
// ─── Communication / Documents ───────────────────────────────────────────
|
||||
public static final String COMMUNICATION_CONVERSATIONS = "/pages/secure/communication/conversations";
|
||||
public static final String COMMUNICATION_NOTIFICATIONS = "/pages/secure/communication/notifications";
|
||||
public static final String DOCUMENTS_MES_DOCUMENTS = "/pages/secure/documents/mes-documents";
|
||||
|
||||
// ─── Organisation ────────────────────────────────────────────────────────
|
||||
public static final String ORGANISATION_LISTE = "/pages/secure/organisation/liste";
|
||||
public static final String ORGANISATION_DETAIL = "/pages/secure/organisation/detail";
|
||||
public static final String ORGANISATION_NOUVELLE = "/pages/secure/organisation/nouvelle";
|
||||
public static final String ORGANISATION_STATISTIQUES = "/pages/secure/organisation/statistiques";
|
||||
|
||||
// ─── Conformité (Sprints 8, 11) ──────────────────────────────────────────
|
||||
public static final String CONFORMITE_DASHBOARD = "/pages/secure/conformite/dashboard";
|
||||
public static final String CONFORMITE_RAPPORTS_TRIMESTRIELS = "/pages/secure/conformite/rapports-trimestriels";
|
||||
public static final String CONFORMITE_BENEFICIAIRES_EFFECTIFS = "/pages/secure/conformite/beneficiaires-effectifs";
|
||||
public static final String CONFORMITE_AUDIT_TRAIL = "/pages/secure/conformite/audit-trail";
|
||||
public static final String CONFORMITE_LIVE_FEED = "/pages/secure/conformite/live-feed";
|
||||
|
||||
// ─── Admin technique ─────────────────────────────────────────────────────
|
||||
public static final String ADMIN_PISPI_READINESS = "/pages/secure/admin/pispi-readiness";
|
||||
public static final String ADMIN_ROLE_DELEGATIONS = "/pages/secure/admin/role-delegations";
|
||||
public static final String ADMIN_SAUVEGARDE = "/pages/secure/admin/sauvegarde";
|
||||
public static final String ADMIN_AUDIT_JOURNAL = "/pages/admin/audit/journal";
|
||||
public static final String ADMIN_LOGS_SYSTEME = "/pages/admin/logs/systeme";
|
||||
public static final String ADMIN_PARAMETRES = "/pages/admin/parametres";
|
||||
public static final String MEMBRE_PARAMETRES = "/pages/membre/parametres";
|
||||
|
||||
// ─── Rapports ────────────────────────────────────────────────────────────
|
||||
public static final String RAPPORT_FINANCES = "/pages/secure/rapport/finances";
|
||||
public static final String RAPPORT_MEMBRES = "/pages/secure/rapport/membres";
|
||||
public static final String RAPPORT_ACTIVITES = "/pages/secure/rapport/activites";
|
||||
public static final String RAPPORT_TABLEAUX_BORD = "/pages/secure/rapport/tableaux-bord";
|
||||
public static final String RAPPORT_EXPORT = "/pages/secure/rapport/export";
|
||||
|
||||
// ─── Souscription ────────────────────────────────────────────────────────
|
||||
public static final String SOUSCRIPTION_DASHBOARD = "/pages/secure/souscription/dashboard";
|
||||
|
||||
// ─── Super-Admin ─────────────────────────────────────────────────────────
|
||||
public static final String SUPER_ADMIN_DASHBOARD = "/pages/super-admin/dashboard";
|
||||
public static final String SUPER_ADMIN_ROLES_GESTION = "/pages/super-admin/roles/gestion";
|
||||
public static final String SUPER_ADMIN_TYPES_ORGANISATIONS = "/pages/super-admin/types/organisations";
|
||||
public static final String SUPER_ADMIN_CONFIGURATION_SYSTEME = "/pages/super-admin/configuration/systeme";
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package dev.lions.unionflow.client.service;
|
||||
|
||||
import dev.lions.unionflow.client.security.AuthHeaderFactory;
|
||||
import dev.lions.unionflow.server.api.dto.audit.response.AuditTrailOperationResponse;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
/**
|
||||
* Client REST de lecture audit trail (Sprint 11 ⇄ backend Sprint 10 CQRS read).
|
||||
*/
|
||||
@RegisterRestClient(configKey = "unionflow-api")
|
||||
@RegisterClientHeaders(AuthHeaderFactory.class)
|
||||
@Path("/api/audit-trail")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public interface AuditTrailRestClient {
|
||||
|
||||
@GET
|
||||
@Path("/by-user/{userId}")
|
||||
List<AuditTrailOperationResponse> parUtilisateur(
|
||||
@PathParam("userId") UUID userId,
|
||||
@QueryParam("from") String from,
|
||||
@QueryParam("to") String to);
|
||||
|
||||
@GET
|
||||
@Path("/by-entity/{type}/{id}")
|
||||
List<AuditTrailOperationResponse> historique(
|
||||
@PathParam("type") String entityType, @PathParam("id") UUID entityId);
|
||||
|
||||
@GET
|
||||
@Path("/by-organisation/{orgId}")
|
||||
List<AuditTrailOperationResponse> parOrganisation(@PathParam("orgId") UUID orgId);
|
||||
|
||||
@GET
|
||||
@Path("/sod-violations")
|
||||
List<AuditTrailOperationResponse> violationsSod();
|
||||
|
||||
@GET
|
||||
@Path("/financial/{orgId}")
|
||||
List<AuditTrailOperationResponse> operationsFinancieres(
|
||||
@PathParam("orgId") UUID orgId,
|
||||
@QueryParam("from") String from,
|
||||
@QueryParam("to") String to);
|
||||
|
||||
/** Live Activity Feed (Sprint 15) — N opérations les plus récentes selon scope. */
|
||||
@GET
|
||||
@Path("/recent")
|
||||
List<AuditTrailOperationResponse> recent(
|
||||
@QueryParam("scope") String scope,
|
||||
@QueryParam("orgId") UUID orgId,
|
||||
@QueryParam("userId") UUID userId,
|
||||
@QueryParam("limit") int limit);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package dev.lions.unionflow.client.service;
|
||||
|
||||
import dev.lions.unionflow.client.security.AuthHeaderFactory;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.request.CreateBeneficiaireEffectifRequest;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.request.UpdateBeneficiaireEffectifRequest;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.response.BeneficiaireEffectifResponse;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
/**
|
||||
* Client REST des Bénéficiaires Effectifs (Sprint 11 ⇄ backend Sprint 10).
|
||||
* Réutilise les DTOs officiels de unionflow-server-api 1.0.8 — zéro duplication.
|
||||
*/
|
||||
@RegisterRestClient(configKey = "unionflow-api")
|
||||
@RegisterClientHeaders(AuthHeaderFactory.class)
|
||||
@Path("/api/kyc/beneficiaires-effectifs")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public interface BeneficiaireEffectifRestClient {
|
||||
|
||||
@GET
|
||||
List<BeneficiaireEffectifResponse> lister(
|
||||
@QueryParam("kycDossierId") UUID kycDossierId,
|
||||
@QueryParam("organisationCibleId") UUID organisationCibleId,
|
||||
@QueryParam("pep") Boolean pep);
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
BeneficiaireEffectifResponse trouverParId(@PathParam("id") UUID id);
|
||||
|
||||
@POST
|
||||
BeneficiaireEffectifResponse creer(CreateBeneficiaireEffectifRequest request);
|
||||
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
BeneficiaireEffectifResponse mettreAJour(
|
||||
@PathParam("id") UUID id, UpdateBeneficiaireEffectifRequest request);
|
||||
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
void desactiver(@PathParam("id") UUID id);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package dev.lions.unionflow.client.service;
|
||||
|
||||
import dev.lions.unionflow.client.api.dto.ComplianceSnapshotDto;
|
||||
import dev.lions.unionflow.client.security.AuthHeaderFactory;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
@RegisterRestClient(configKey = "unionflow-api")
|
||||
@RegisterClientHeaders(AuthHeaderFactory.class)
|
||||
@Path("/api/compliance/dashboard")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public interface ComplianceDashboardRestClient {
|
||||
|
||||
/** Snapshot pour l'organisation active (résolue par le backend via header X-Active-Organisation-Id). */
|
||||
@GET
|
||||
ComplianceSnapshotDto getSnapshotCurrent();
|
||||
|
||||
/** Snapshot d'une organisation arbitraire — restreint SUPER_ADMIN backend. */
|
||||
@GET
|
||||
@Path("/{organisationId}")
|
||||
ComplianceSnapshotDto getSnapshotOf(@PathParam("organisationId") UUID organisationId);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package dev.lions.unionflow.client.service;
|
||||
|
||||
import dev.lions.unionflow.client.api.dto.PispiReadinessDto;
|
||||
import dev.lions.unionflow.client.security.AuthHeaderFactory;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
@RegisterRestClient(configKey = "unionflow-api")
|
||||
@RegisterClientHeaders(AuthHeaderFactory.class)
|
||||
@Path("/api/admin/pispi/readiness")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public interface PispiReadinessRestClient {
|
||||
|
||||
@GET
|
||||
PispiReadinessDto getReadiness();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package dev.lions.unionflow.client.service;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.compliance.response.KpiPublicSnapshot;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
/**
|
||||
* Client REST public KPI (Sprint 17 — pas de header auth, token signé en query).
|
||||
*
|
||||
* <p>Note : pas de {@code @RegisterClientHeaders(AuthHeaderFactory.class)} car endpoint anonyme.
|
||||
*/
|
||||
@RegisterRestClient(configKey = "unionflow-api")
|
||||
@Path("/api/public/kpi")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public interface PublicKpiRestClient {
|
||||
|
||||
@GET
|
||||
KpiPublicSnapshot consulter(@QueryParam("token") String token);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package dev.lions.unionflow.client.service;
|
||||
|
||||
import dev.lions.unionflow.client.api.dto.RapportTrimestrielDto;
|
||||
import dev.lions.unionflow.client.security.AuthHeaderFactory;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
@RegisterRestClient(configKey = "unionflow-api")
|
||||
@RegisterClientHeaders(AuthHeaderFactory.class)
|
||||
@Path("/api/rapports/trimestriel")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public interface RapportTrimestrielRestClient {
|
||||
|
||||
@GET
|
||||
List<RapportTrimestrielDto> lister(
|
||||
@QueryParam("orgId") UUID orgId,
|
||||
@QueryParam("annee") Integer annee);
|
||||
|
||||
@POST
|
||||
@Path("/generer")
|
||||
RapportTrimestrielDto generer(
|
||||
@QueryParam("orgId") UUID orgId,
|
||||
@QueryParam("annee") int annee,
|
||||
@QueryParam("trimestre") int trimestre);
|
||||
|
||||
@POST
|
||||
@Path("/{id}/signer")
|
||||
RapportTrimestrielDto signer(
|
||||
@PathParam("id") UUID id,
|
||||
@QueryParam("signataireId") UUID signataireId);
|
||||
|
||||
@POST
|
||||
@Path("/{id}/archiver")
|
||||
RapportTrimestrielDto archiver(@PathParam("id") UUID id);
|
||||
|
||||
@GET
|
||||
@Path("/{id}/pdf")
|
||||
@Produces("application/pdf")
|
||||
byte[] telechargerPdf(@PathParam("id") UUID id);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package dev.lions.unionflow.client.service;
|
||||
|
||||
import dev.lions.unionflow.client.security.AuthHeaderFactory;
|
||||
import dev.lions.unionflow.server.api.dto.delegation.request.CreateRoleDelegationRequest;
|
||||
import dev.lions.unionflow.server.api.dto.delegation.response.RoleDelegationResponse;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
/**
|
||||
* Client REST des délégations de rôle (Sprint 11 ⇄ backend Sprint 10).
|
||||
*/
|
||||
@RegisterRestClient(configKey = "unionflow-api")
|
||||
@RegisterClientHeaders(AuthHeaderFactory.class)
|
||||
@Path("/api/role-delegations")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public interface RoleDelegationRestClient {
|
||||
|
||||
@GET
|
||||
@Path("/organisation/{orgId}")
|
||||
List<RoleDelegationResponse> listerParOrganisation(@PathParam("orgId") UUID orgId);
|
||||
|
||||
@POST
|
||||
RoleDelegationResponse creer(
|
||||
CreateRoleDelegationRequest request,
|
||||
@QueryParam("rolesDelegataire") String rolesDelegataireCsv);
|
||||
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
RoleDelegationResponse revoquer(@PathParam("id") UUID id);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.service.AuditTrailRestClient;
|
||||
import dev.lions.unionflow.server.api.dto.audit.response.AuditTrailOperationResponse;
|
||||
import jakarta.faces.application.FacesMessage;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Bean viewer audit trail (Sprint 11 ⇄ backend Sprint 10 CQRS read).
|
||||
* Pas d'écriture — la production des événements est dans le backend lifecycle.
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class AuditTrailViewerBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = Logger.getLogger(AuditTrailViewerBean.class);
|
||||
|
||||
@Inject @RestClient AuditTrailRestClient client;
|
||||
|
||||
/** Mode : USER, ENTITY, ORG, SOD_VIOLATIONS, FINANCIAL */
|
||||
private String mode = "ORG";
|
||||
|
||||
private UUID userId;
|
||||
private UUID orgId;
|
||||
private String entityType;
|
||||
private UUID entityId;
|
||||
private LocalDateTime from = LocalDateTime.now().minusDays(30);
|
||||
private LocalDateTime to = LocalDateTime.now();
|
||||
|
||||
private List<AuditTrailOperationResponse> operations = Collections.emptyList();
|
||||
private AuditTrailOperationResponse selection;
|
||||
private String erreur;
|
||||
|
||||
public void rechercher() {
|
||||
erreur = null;
|
||||
try {
|
||||
operations = switch (mode) {
|
||||
case "USER" -> userId != null
|
||||
? client.parUtilisateur(userId, from.toString(), to.toString())
|
||||
: Collections.emptyList();
|
||||
case "ENTITY" -> entityType != null && entityId != null
|
||||
? client.historique(entityType, entityId)
|
||||
: Collections.emptyList();
|
||||
case "ORG" -> orgId != null
|
||||
? client.parOrganisation(orgId)
|
||||
: Collections.emptyList();
|
||||
case "SOD_VIOLATIONS" -> client.violationsSod();
|
||||
case "FINANCIAL" -> orgId != null
|
||||
? client.operationsFinancieres(orgId, from.toString(), to.toString())
|
||||
: Collections.emptyList();
|
||||
default -> Collections.emptyList();
|
||||
};
|
||||
LOG.infof("Audit trail (%s) chargé : %d entrées", mode, operations.size());
|
||||
} catch (Exception e) {
|
||||
handleError("Recherche audit trail échouée", e);
|
||||
operations = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public String getCouleurAction(String actionType) {
|
||||
if (actionType == null) return "secondary";
|
||||
return switch (actionType) {
|
||||
case "DELETE", "PAYMENT_FAILED" -> "danger";
|
||||
case "VALIDATE", "PAYMENT_CONFIRMED", "AID_REQUEST_APPROVED" -> "success";
|
||||
case "UPDATE", "PAYMENT_INITIATED", "BUDGET_APPROVED" -> "info";
|
||||
case "CREATE" -> "primary";
|
||||
case "EXPORT" -> "warning";
|
||||
default -> "secondary";
|
||||
};
|
||||
}
|
||||
|
||||
public String getCouleurSod(Boolean sodCheckPassed) {
|
||||
if (sodCheckPassed == null) return "secondary";
|
||||
return sodCheckPassed ? "success" : "danger";
|
||||
}
|
||||
|
||||
private void handleError(String summary, Exception e) {
|
||||
LOG.warnf("%s : %s", summary, e.getMessage());
|
||||
erreur = summary + " — " + e.getMessage();
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", erreur));
|
||||
}
|
||||
|
||||
public String getMode() { return mode; }
|
||||
public void setMode(String mode) { this.mode = mode; }
|
||||
public UUID getUserId() { return userId; }
|
||||
public void setUserId(UUID userId) { this.userId = userId; }
|
||||
public UUID getOrgId() { return orgId; }
|
||||
public void setOrgId(UUID orgId) { this.orgId = orgId; }
|
||||
public String getEntityType() { return entityType; }
|
||||
public void setEntityType(String entityType) { this.entityType = entityType; }
|
||||
public UUID getEntityId() { return entityId; }
|
||||
public void setEntityId(UUID entityId) { this.entityId = entityId; }
|
||||
public LocalDateTime getFrom() { return from; }
|
||||
public void setFrom(LocalDateTime from) { this.from = from; }
|
||||
public LocalDateTime getTo() { return to; }
|
||||
public void setTo(LocalDateTime to) { this.to = to; }
|
||||
public List<AuditTrailOperationResponse> getOperations() { return operations; }
|
||||
public AuditTrailOperationResponse getSelection() { return selection; }
|
||||
public void setSelection(AuditTrailOperationResponse selection) { this.selection = selection; }
|
||||
public String getErreur() { return erreur; }
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.service.BeneficiaireEffectifRestClient;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.request.CreateBeneficiaireEffectifRequest;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.request.UpdateBeneficiaireEffectifRequest;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.response.BeneficiaireEffectifResponse;
|
||||
import jakarta.faces.application.FacesMessage;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Bean CRUD des Bénéficiaires Effectifs (Sprint 11 ⇄ backend Sprint 10).
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class BeneficiaireEffectifBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = Logger.getLogger(BeneficiaireEffectifBean.class);
|
||||
|
||||
@Inject @RestClient BeneficiaireEffectifRestClient client;
|
||||
|
||||
// Filtres
|
||||
private UUID kycDossierId;
|
||||
private UUID organisationCibleId;
|
||||
private Boolean filtrePep;
|
||||
|
||||
private List<BeneficiaireEffectifResponse> ubos = Collections.emptyList();
|
||||
private BeneficiaireEffectifResponse selection;
|
||||
private String erreur;
|
||||
|
||||
// Formulaire de création
|
||||
private String nom;
|
||||
private String prenoms;
|
||||
private String nationalite;
|
||||
private String paysResidence;
|
||||
private String numeroPieceIdentite;
|
||||
private String pourcentageCapital;
|
||||
private String natureControle = "DETENTION_CAPITAL";
|
||||
private boolean estPep;
|
||||
|
||||
public void rechercher() {
|
||||
erreur = null;
|
||||
try {
|
||||
ubos = client.lister(kycDossierId, organisationCibleId, filtrePep);
|
||||
LOG.infof("UBOs chargés : %d", ubos.size());
|
||||
} catch (Exception e) {
|
||||
handleError("Recherche UBO échouée", e);
|
||||
ubos = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public void creer() {
|
||||
if (kycDossierId == null && organisationCibleId == null) {
|
||||
addMessage(FacesMessage.SEVERITY_WARN, "Cible requise",
|
||||
"Sélectionnez d'abord un KycDossier ou une organisation cible");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
CreateBeneficiaireEffectifRequest req = CreateBeneficiaireEffectifRequest.builder()
|
||||
.kycDossierId(kycDossierId)
|
||||
.organisationCibleId(organisationCibleId)
|
||||
.nom(nom)
|
||||
.prenoms(prenoms)
|
||||
.nationalite(nationalite != null ? nationalite.toUpperCase() : null)
|
||||
.paysResidence(paysResidence != null ? paysResidence.toUpperCase() : null)
|
||||
.numeroPieceIdentite(numeroPieceIdentite)
|
||||
.pourcentageCapital(pourcentageCapital != null && !pourcentageCapital.isBlank()
|
||||
? new java.math.BigDecimal(pourcentageCapital) : null)
|
||||
.natureControle(natureControle)
|
||||
.estPep(estPep)
|
||||
.build();
|
||||
BeneficiaireEffectifResponse created = client.creer(req);
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "UBO créé",
|
||||
created.prenoms() + " " + created.nom());
|
||||
resetForm();
|
||||
rechercher();
|
||||
} catch (Exception e) {
|
||||
handleError("Création UBO échouée", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void desactiverSelection() {
|
||||
if (selection == null) return;
|
||||
try {
|
||||
client.desactiver(selection.id());
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "UBO désactivé",
|
||||
selection.prenoms() + " " + selection.nom());
|
||||
rechercher();
|
||||
} catch (Exception e) {
|
||||
handleError("Désactivation échouée", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void marquerPepSelection() {
|
||||
if (selection == null) return;
|
||||
try {
|
||||
UpdateBeneficiaireEffectifRequest req = UpdateBeneficiaireEffectifRequest.builder()
|
||||
.estPep(true).build();
|
||||
client.mettreAJour(selection.id(), req);
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "UBO marqué PEP", "");
|
||||
rechercher();
|
||||
} catch (Exception e) {
|
||||
handleError("Mise à jour PEP échouée", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetForm() {
|
||||
nom = null;
|
||||
prenoms = null;
|
||||
nationalite = null;
|
||||
paysResidence = null;
|
||||
numeroPieceIdentite = null;
|
||||
pourcentageCapital = null;
|
||||
natureControle = "DETENTION_CAPITAL";
|
||||
estPep = false;
|
||||
}
|
||||
|
||||
private void handleError(String summary, Exception e) {
|
||||
LOG.warnf("%s : %s", summary, e.getMessage());
|
||||
erreur = summary + " — " + e.getMessage();
|
||||
addMessage(FacesMessage.SEVERITY_ERROR, "Erreur", erreur);
|
||||
}
|
||||
|
||||
private void addMessage(FacesMessage.Severity sev, String summary, String detail) {
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(sev, summary, detail));
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
public UUID getKycDossierId() { return kycDossierId; }
|
||||
public void setKycDossierId(UUID kycDossierId) { this.kycDossierId = kycDossierId; }
|
||||
public UUID getOrganisationCibleId() { return organisationCibleId; }
|
||||
public void setOrganisationCibleId(UUID id) { this.organisationCibleId = id; }
|
||||
public Boolean getFiltrePep() { return filtrePep; }
|
||||
public void setFiltrePep(Boolean filtrePep) { this.filtrePep = filtrePep; }
|
||||
public List<BeneficiaireEffectifResponse> getUbos() { return ubos; }
|
||||
public BeneficiaireEffectifResponse getSelection() { return selection; }
|
||||
public void setSelection(BeneficiaireEffectifResponse selection) { this.selection = selection; }
|
||||
public String getErreur() { return erreur; }
|
||||
public String getNom() { return nom; }
|
||||
public void setNom(String nom) { this.nom = nom; }
|
||||
public String getPrenoms() { return prenoms; }
|
||||
public void setPrenoms(String prenoms) { this.prenoms = prenoms; }
|
||||
public String getNationalite() { return nationalite; }
|
||||
public void setNationalite(String nationalite) { this.nationalite = nationalite; }
|
||||
public String getPaysResidence() { return paysResidence; }
|
||||
public void setPaysResidence(String paysResidence) { this.paysResidence = paysResidence; }
|
||||
public String getNumeroPieceIdentite() { return numeroPieceIdentite; }
|
||||
public void setNumeroPieceIdentite(String n) { this.numeroPieceIdentite = n; }
|
||||
public String getPourcentageCapital() { return pourcentageCapital; }
|
||||
public void setPourcentageCapital(String pct) { this.pourcentageCapital = pct; }
|
||||
public String getNatureControle() { return natureControle; }
|
||||
public void setNatureControle(String nc) { this.natureControle = nc; }
|
||||
public boolean isEstPep() { return estPep; }
|
||||
public void setEstPep(boolean estPep) { this.estPep = estPep; }
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.service.MembreService;
|
||||
import dev.lions.unionflow.server.api.dto.membre.response.MembreResponse;
|
||||
import dev.lions.unionflow.server.api.dto.membre.response.MembreSummaryResponse;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Bean picker pour la sélection du Compliance Officer (Sprint 14 — Instr. BCEAO 001-03-2025).
|
||||
*
|
||||
* <p>Réutilise {@link MembreService} existant — DRY strict, aucun nouveau REST client.
|
||||
*
|
||||
* <p>Usage XHTML :
|
||||
* <pre>
|
||||
* <p:autoComplete value="#{model.complianceOfficerId}"
|
||||
* completeMethod="#{complianceOfficerPickerBean.suggest}"
|
||||
* var="m" itemLabel="#{complianceOfficerPickerBean.label(m)}"
|
||||
* itemValue="#{m.id}" forceSelection="true" />
|
||||
* </pre>
|
||||
*
|
||||
* @since 2026-04-25 (Sprint 14)
|
||||
*/
|
||||
@Named("complianceOfficerPickerBean")
|
||||
@ApplicationScoped
|
||||
public class ComplianceOfficerPickerBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = Logger.getLogger(ComplianceOfficerPickerBean.class);
|
||||
|
||||
@Inject @RestClient MembreService membreService;
|
||||
|
||||
/**
|
||||
* Suggère des membres correspondant à la requête (utilisé par {@code p:autoComplete.completeMethod}).
|
||||
* Recherche via nom OU prénom.
|
||||
*/
|
||||
public List<MembreSummaryResponse> suggest(String query) {
|
||||
if (query == null || query.isBlank()) return Collections.emptyList();
|
||||
try {
|
||||
// Recherche larges : nom OU prénom contenant la query
|
||||
List<MembreResponse> byNom = membreService.rechercher(
|
||||
query, null, null, null, null, null, 0, 10);
|
||||
List<MembreResponse> byPrenom = membreService.rechercher(
|
||||
null, query, null, null, null, null, 0, 10);
|
||||
|
||||
java.util.LinkedHashMap<UUID, MembreSummaryResponse> uniques = new java.util.LinkedHashMap<>();
|
||||
for (MembreResponse m : byNom) uniques.putIfAbsent(m.getId(), toSummary(m));
|
||||
for (MembreResponse m : byPrenom) uniques.putIfAbsent(m.getId(), toSummary(m));
|
||||
return new java.util.ArrayList<>(uniques.values());
|
||||
} catch (Exception e) {
|
||||
LOG.warnf("Suggest membres failed for query='%s' : %s", query, e.getMessage());
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/** Label de présentation : "Prénom NOM (numéro)". */
|
||||
public String label(MembreSummaryResponse m) {
|
||||
if (m == null) return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (m.getPrenom() != null) sb.append(m.getPrenom());
|
||||
if (m.getNom() != null) {
|
||||
if (sb.length() > 0) sb.append(' ');
|
||||
sb.append(m.getNom().toUpperCase());
|
||||
}
|
||||
if (m.getNumeroMembre() != null && !m.getNumeroMembre().isBlank()) {
|
||||
sb.append(" (").append(m.getNumeroMembre()).append(')');
|
||||
}
|
||||
return sb.length() == 0 ? "(membre " + m.getId() + ")" : sb.toString();
|
||||
}
|
||||
|
||||
/** Résolution UUID → membre pour affichage initial du form en mode édition. */
|
||||
public MembreSummaryResponse resoudre(UUID id) {
|
||||
if (id == null) return null;
|
||||
try {
|
||||
MembreResponse m = membreService.obtenirParId(id);
|
||||
return toSummary(m);
|
||||
} catch (Exception e) {
|
||||
LOG.warnf("Résolution membre id=%s échouée : %s", id, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Mapping interne ────────────────────────────────────────────────────
|
||||
|
||||
private MembreSummaryResponse toSummary(MembreResponse m) {
|
||||
if (m == null) return null;
|
||||
MembreSummaryResponse s = new MembreSummaryResponse();
|
||||
s.setId(m.getId());
|
||||
s.setNom(m.getNom());
|
||||
s.setPrenom(m.getPrenom());
|
||||
s.setEmail(m.getEmail());
|
||||
s.setTelephone(m.getTelephone());
|
||||
s.setNumeroMembre(m.getNumeroMembre());
|
||||
s.setStatutCompte(m.getStatutCompte());
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.api.dto.ComplianceSnapshotDto;
|
||||
import dev.lions.unionflow.client.service.ComplianceDashboardRestClient;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.faces.application.FacesMessage;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Bean du tableau de bord de conformité (Sprint 8 ⇄ backend P1-NEW-7).
|
||||
*
|
||||
* <p>Affiche le snapshot de l'organisation active : score global, indicateurs AG/AIRMS/CMU/KYC/UBO,
|
||||
* alertes critiques. Refresh manuel par bouton.
|
||||
*
|
||||
* @since 2026-04-25 (Sprint 8)
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class ConformiteDashboardBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = Logger.getLogger(ConformiteDashboardBean.class);
|
||||
|
||||
@Inject @RestClient
|
||||
ComplianceDashboardRestClient client;
|
||||
|
||||
private ComplianceSnapshotDto snapshot;
|
||||
private String erreur;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
rafraichir();
|
||||
}
|
||||
|
||||
public void rafraichir() {
|
||||
erreur = null;
|
||||
try {
|
||||
snapshot = client.getSnapshotCurrent();
|
||||
LOG.infof("Snapshot conformité chargé : score=%d", snapshot.scoreGlobal());
|
||||
} catch (Exception e) {
|
||||
LOG.warnf("Échec chargement compliance dashboard : %s", e.getMessage());
|
||||
erreur = "Impossible de charger le tableau de bord : " + e.getMessage();
|
||||
addMessage(FacesMessage.SEVERITY_ERROR, "Erreur", erreur);
|
||||
snapshot = null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getCouleurScore() {
|
||||
if (snapshot == null) return "secondary";
|
||||
int s = snapshot.scoreGlobal();
|
||||
if (s >= 80) return "success";
|
||||
if (s >= 60) return "warning";
|
||||
return "danger";
|
||||
}
|
||||
|
||||
public boolean hasAlertes() {
|
||||
if (snapshot == null) return false;
|
||||
return !snapshot.complianceOfficerDesigne()
|
||||
|| "RETARD".equals(snapshot.agAnnuelle().statut())
|
||||
|| snapshot.scoreGlobal() < 60;
|
||||
}
|
||||
|
||||
private void addMessage(FacesMessage.Severity sev, String summary, String detail) {
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(sev, summary, detail));
|
||||
}
|
||||
|
||||
public ComplianceSnapshotDto getSnapshot() { return snapshot; }
|
||||
public String getErreur() { return erreur; }
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.constants.ViewPaths;
|
||||
import dev.lions.unionflow.server.api.dto.cotisation.response.CotisationResponse;
|
||||
import dev.lions.unionflow.client.api.dto.MembreDashboardResponse;
|
||||
import dev.lions.unionflow.client.service.CotisationService;
|
||||
@@ -175,23 +176,23 @@ public class DashboardMembreBean implements Serializable {
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
public String allerAuxCotisations() {
|
||||
return "/pages/secure/membre/paiement-mes-cotisations.xhtml?faces-redirect=true";
|
||||
return ViewPaths.MEMBRE_PAIEMENT_COTISATIONS + ".xhtml" + ViewPaths.REDIRECT_SUFFIX;
|
||||
}
|
||||
|
||||
public String inscrireEvenement() {
|
||||
return "/pages/secure/evenement/calendrier.xhtml?faces-redirect=true";
|
||||
return ViewPaths.EVENEMENT_CALENDRIER + ".xhtml" + ViewPaths.REDIRECT_SUFFIX;
|
||||
}
|
||||
|
||||
public String demanderAide() {
|
||||
return "/pages/secure/aide/demande.xhtml?faces-redirect=true";
|
||||
return ViewPaths.AIDE_DEMANDE + ".xhtml" + ViewPaths.REDIRECT_SUFFIX;
|
||||
}
|
||||
|
||||
public String allerAMonProfil() {
|
||||
return "/pages/secure/membre/profil.xhtml?faces-redirect=true";
|
||||
return ViewPaths.MEMBRE_PROFIL + ".xhtml" + ViewPaths.REDIRECT_SUFFIX;
|
||||
}
|
||||
|
||||
public String allerAuxEvenements() {
|
||||
return "/pages/secure/evenement/calendrier.xhtml?faces-redirect=true";
|
||||
return ViewPaths.EVENEMENT_CALENDRIER + ".xhtml" + ViewPaths.REDIRECT_SUFFIX;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
104
src/main/java/dev/lions/unionflow/client/view/LiveFeedBean.java
Normal file
104
src/main/java/dev/lions/unionflow/client/view/LiveFeedBean.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.service.AuditTrailRestClient;
|
||||
import dev.lions.unionflow.server.api.dto.audit.response.AuditTrailOperationResponse;
|
||||
import jakarta.faces.application.FacesMessage;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Bean Live Activity Feed (Sprint 15 — transparency opérationnelle).
|
||||
*
|
||||
* <p>Polling 10s sur {@code /api/audit-trail/recent} avec choix du scope :
|
||||
* <ul>
|
||||
* <li>SELF — opérations de l'utilisateur courant (défaut)</li>
|
||||
* <li>ORG — opérations de l'organisation active</li>
|
||||
* <li>ALL — toutes opérations (compliance/contrôleur uniquement)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>L'auto-refresh est piloté côté UI via {@code <p:poll>} qui appelle {@link #rafraichir()}.
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class LiveFeedBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = Logger.getLogger(LiveFeedBean.class);
|
||||
|
||||
@Inject @RestClient AuditTrailRestClient client;
|
||||
|
||||
/** SELF (défaut, n'importe quel rôle), ORG (admin/officer), ALL (compliance/contrôleur). */
|
||||
private String scope = "SELF";
|
||||
private UUID orgId;
|
||||
private UUID userId;
|
||||
private int limit = 50;
|
||||
|
||||
private List<AuditTrailOperationResponse> operations = Collections.emptyList();
|
||||
private long compteur;
|
||||
private String erreur;
|
||||
|
||||
public void rafraichir() {
|
||||
erreur = null;
|
||||
try {
|
||||
operations = client.recent(scope, orgId, userId, limit);
|
||||
compteur++;
|
||||
LOG.debugf("LiveFeed (%s) refresh #%d : %d ops", scope, compteur,
|
||||
operations == null ? 0 : operations.size());
|
||||
} catch (Exception e) {
|
||||
LOG.warnf("LiveFeed refresh échoué : %s", e.getMessage());
|
||||
erreur = "Échec rafraîchissement : " + e.getMessage();
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_WARN, "Erreur LiveFeed", erreur));
|
||||
operations = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public String getCouleurAction(String actionType) {
|
||||
if (actionType == null) return "secondary";
|
||||
return switch (actionType) {
|
||||
case "DELETE", "PAYMENT_FAILED" -> "danger";
|
||||
case "VALIDATE", "PAYMENT_CONFIRMED", "AID_REQUEST_APPROVED" -> "success";
|
||||
case "UPDATE", "PAYMENT_INITIATED", "BUDGET_APPROVED" -> "info";
|
||||
case "CREATE" -> "primary";
|
||||
case "EXPORT" -> "warning";
|
||||
default -> "secondary";
|
||||
};
|
||||
}
|
||||
|
||||
public String getCouleurSod(Boolean sodCheckPassed) {
|
||||
if (sodCheckPassed == null) return "secondary";
|
||||
return sodCheckPassed ? "success" : "danger";
|
||||
}
|
||||
|
||||
/** Pour l'affichage relatif "il y a Xs". */
|
||||
public String tempsRelatif(java.time.LocalDateTime dt) {
|
||||
if (dt == null) return "—";
|
||||
long secondes = java.time.Duration.between(dt, java.time.LocalDateTime.now()).getSeconds();
|
||||
if (secondes < 0) return "à l'instant";
|
||||
if (secondes < 60) return "il y a " + secondes + "s";
|
||||
if (secondes < 3600) return "il y a " + (secondes / 60) + "m";
|
||||
if (secondes < 86400) return "il y a " + (secondes / 3600) + "h";
|
||||
return "il y a " + (secondes / 86400) + "j";
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
public String getScope() { return scope; }
|
||||
public void setScope(String scope) { this.scope = scope; }
|
||||
public UUID getOrgId() { return orgId; }
|
||||
public void setOrgId(UUID orgId) { this.orgId = orgId; }
|
||||
public UUID getUserId() { return userId; }
|
||||
public void setUserId(UUID userId) { this.userId = userId; }
|
||||
public int getLimit() { return limit; }
|
||||
public void setLimit(int limit) { this.limit = Math.max(1, Math.min(limit, 500)); }
|
||||
public List<AuditTrailOperationResponse> getOperations() { return operations; }
|
||||
public long getCompteur() { return compteur; }
|
||||
public String getErreur() { return erreur; }
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.constants.ViewPaths;
|
||||
import dev.lions.unionflow.server.api.dto.membre.response.MembreResponse;
|
||||
import dev.lions.unionflow.server.api.dto.cotisation.response.CotisationResponse;
|
||||
import dev.lions.unionflow.server.api.dto.evenement.response.EvenementResponse;
|
||||
@@ -363,7 +364,7 @@ public class MembreDashboardBean implements Serializable {
|
||||
}
|
||||
|
||||
public String voirEvenement(Evenement evenement) {
|
||||
return "/pages/secure/evenement/gestion.xhtml?faces-redirect=true";
|
||||
return ViewPaths.EVENEMENT_GESTION + ".xhtml" + ViewPaths.REDIRECT_SUFFIX;
|
||||
}
|
||||
|
||||
public void annulerInscription(Evenement evenement) {
|
||||
@@ -373,7 +374,7 @@ public class MembreDashboardBean implements Serializable {
|
||||
}
|
||||
|
||||
public String payerCotisations() {
|
||||
return "/pages/secure/cotisation/paiement.xhtml?faces-redirect=true";
|
||||
return ViewPaths.COTISATION_PAIEMENT + ".xhtml" + ViewPaths.REDIRECT_SUFFIX;
|
||||
}
|
||||
|
||||
public void actualiser() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.constants.ViewPaths;
|
||||
import jakarta.enterprise.context.RequestScoped;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.inject.Inject;
|
||||
@@ -62,7 +63,7 @@ public class NavigationBean implements Serializable {
|
||||
return redirectToLogin();
|
||||
}
|
||||
|
||||
return "/pages/secure/profile?faces-redirect=true";
|
||||
return ViewPaths.PROFILE + ViewPaths.REDIRECT_SUFFIX;
|
||||
}
|
||||
|
||||
public String goToSettings() {
|
||||
@@ -71,11 +72,11 @@ public class NavigationBean implements Serializable {
|
||||
}
|
||||
|
||||
if (userSession.isSuperAdmin()) {
|
||||
return "/pages/super-admin/configuration/systeme?faces-redirect=true";
|
||||
return ViewPaths.SUPER_ADMIN_CONFIGURATION_SYSTEME + ViewPaths.REDIRECT_SUFFIX;
|
||||
} else if (userSession.isAdmin()) {
|
||||
return "/pages/admin/parametres?faces-redirect=true";
|
||||
return ViewPaths.ADMIN_PARAMETRES + ViewPaths.REDIRECT_SUFFIX;
|
||||
} else {
|
||||
return "/pages/membre/parametres?faces-redirect=true";
|
||||
return ViewPaths.MEMBRE_PARAMETRES + ViewPaths.REDIRECT_SUFFIX;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,23 +88,21 @@ public class NavigationBean implements Serializable {
|
||||
|
||||
private String getDashboardUrlForUserType() {
|
||||
if (userSession == null || userSession.getTypeCompte() == null) {
|
||||
return "/pages/secure/dashboard.xhtml";
|
||||
return ViewPaths.DASHBOARD + ".xhtml";
|
||||
}
|
||||
|
||||
switch (userSession.getTypeCompte()) {
|
||||
case "SUPER_ADMIN":
|
||||
return "/pages/super-admin/dashboard.xhtml";
|
||||
return ViewPaths.SUPER_ADMIN_DASHBOARD + ".xhtml";
|
||||
case "ADMIN_ORGANISATION":
|
||||
return "/pages/secure/dashboard.xhtml";
|
||||
case "MODERATEUR":
|
||||
return "/pages/secure/dashboard.xhtml";
|
||||
return ViewPaths.DASHBOARD + ".xhtml";
|
||||
case "MEMBRE_ACTIF":
|
||||
return "/pages/secure/dashboard-membre.xhtml";
|
||||
case "MEMBRE":
|
||||
return "/pages/secure/dashboard-membre.xhtml";
|
||||
return ViewPaths.DASHBOARD_MEMBRE + ".xhtml";
|
||||
default:
|
||||
LOGGER.warning("Type de compte non reconnu: " + userSession.getTypeCompte());
|
||||
return "/pages/secure/dashboard.xhtml";
|
||||
return ViewPaths.DASHBOARD + ".xhtml";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.constants.StatutOrganisationConstants;
|
||||
import dev.lions.unionflow.client.constants.ViewPaths;
|
||||
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;
|
||||
@@ -245,7 +246,7 @@ public class OrganisationsBean implements Serializable {
|
||||
chargerOrganisations();
|
||||
chargerStatistiques();
|
||||
|
||||
return "/pages/secure/organisation/liste?faces-redirect=true";
|
||||
return ViewPaths.ORGANISATION_LISTE + ViewPaths.REDIRECT_SUFFIX;
|
||||
|
||||
} catch (RestClientExceptionMapper.ConflictException e) {
|
||||
errorHandler.handleException(e, "lors de la création d'une organisation",
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.api.dto.PispiReadinessDto;
|
||||
import dev.lions.unionflow.client.service.PispiReadinessRestClient;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.faces.application.FacesMessage;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Bean d'inspection PI-SPI Readiness (Sprint 8 ⇄ P1-NEW-15).
|
||||
*
|
||||
* @since 2026-04-25 (Sprint 8)
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class PispiReadinessBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = Logger.getLogger(PispiReadinessBean.class);
|
||||
|
||||
@Inject @RestClient
|
||||
PispiReadinessRestClient client;
|
||||
|
||||
private PispiReadinessDto readiness;
|
||||
private String erreur;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
rafraichir();
|
||||
}
|
||||
|
||||
public void rafraichir() {
|
||||
erreur = null;
|
||||
try {
|
||||
readiness = client.getReadiness();
|
||||
LOG.infof("PI-SPI readiness chargé : %s (%d blocking, %d warnings)",
|
||||
readiness.globalStatus(),
|
||||
readiness.blockingIssues() == null ? 0 : readiness.blockingIssues().size(),
|
||||
readiness.warnings() == null ? 0 : readiness.warnings().size());
|
||||
} catch (jakarta.ws.rs.WebApplicationException wae) {
|
||||
// Backend renvoie 503 quand BLOCKED — on récupère quand même le body
|
||||
try {
|
||||
readiness = wae.getResponse().readEntity(PispiReadinessDto.class);
|
||||
LOG.infof("PI-SPI readiness BLOCKED — body décodé");
|
||||
} catch (Exception parseEx) {
|
||||
handleError("Décodage réponse readiness échoué", parseEx);
|
||||
readiness = null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleError("Chargement PI-SPI readiness échoué", e);
|
||||
readiness = null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getCouleurStatus() {
|
||||
if (readiness == null) return "secondary";
|
||||
return switch (readiness.globalStatus()) {
|
||||
case "READY" -> "success";
|
||||
case "DEGRADED" -> "warning";
|
||||
case "BLOCKED" -> "danger";
|
||||
default -> "secondary";
|
||||
};
|
||||
}
|
||||
|
||||
public String getCouleurCheck(String severity, String status) {
|
||||
if ("PASS".equals(status)) return "success";
|
||||
return "BLOCKING".equals(severity) ? "danger" : "warning";
|
||||
}
|
||||
|
||||
private void handleError(String summary, Exception e) {
|
||||
LOG.warnf("%s : %s", summary, e.getMessage());
|
||||
erreur = summary + " — " + e.getMessage();
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", erreur));
|
||||
}
|
||||
|
||||
public PispiReadinessDto getReadiness() { return readiness; }
|
||||
public String getErreur() { return erreur; }
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.service.PublicKpiRestClient;
|
||||
import dev.lions.unionflow.server.api.dto.compliance.response.KpiPublicSnapshot;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Bean public consultation KPI (Sprint 17) — pas d'authentification requise.
|
||||
*
|
||||
* <p>Lit le query param {@code token} dès l'init via {@code @ViewScoped} + viewAction,
|
||||
* appelle l'endpoint public, expose le snapshot à la page.
|
||||
*
|
||||
* @since 2026-04-25 (Sprint 17)
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class PublicKpiBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = Logger.getLogger(PublicKpiBean.class);
|
||||
|
||||
@Inject @RestClient PublicKpiRestClient client;
|
||||
|
||||
private String token;
|
||||
private KpiPublicSnapshot snapshot;
|
||||
private String erreur;
|
||||
|
||||
/** Appelé via {@code <f:viewAction action="#{publicKpiBean.charger}" />}. */
|
||||
public void charger() {
|
||||
erreur = null;
|
||||
snapshot = null;
|
||||
|
||||
if (token == null || token.isBlank()) {
|
||||
// Token vient du query param via <f:viewParam>
|
||||
token = FacesContext.getCurrentInstance()
|
||||
.getExternalContext()
|
||||
.getRequestParameterMap()
|
||||
.get("token");
|
||||
}
|
||||
|
||||
if (token == null || token.isBlank()) {
|
||||
erreur = "Aucun token fourni dans l'URL.";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
snapshot = client.consulter(token);
|
||||
LOG.infof("PublicKpi chargé — score=%d", snapshot == null ? -1 : snapshot.scoreGlobal());
|
||||
} catch (jakarta.ws.rs.WebApplicationException wae) {
|
||||
int status = wae.getResponse().getStatus();
|
||||
if (status == 401) {
|
||||
erreur = "Token invalide ou expiré.";
|
||||
} else if (status == 404) {
|
||||
erreur = "Organisation introuvable.";
|
||||
} else {
|
||||
erreur = "Erreur HTTP " + status;
|
||||
}
|
||||
LOG.warnf("PublicKpi HTTP %d : %s", status, wae.getMessage());
|
||||
} catch (Exception e) {
|
||||
erreur = "Erreur de chargement : " + e.getMessage();
|
||||
LOG.errorf(e, "PublicKpi exception");
|
||||
}
|
||||
}
|
||||
|
||||
/** Couleur sémantique du score (success / warning / danger). */
|
||||
public String getCouleurScore() {
|
||||
if (snapshot == null) return "secondary";
|
||||
int s = snapshot.scoreGlobal();
|
||||
if (s >= 80) return "success";
|
||||
if (s >= 60) return "warning";
|
||||
return "danger";
|
||||
}
|
||||
|
||||
public String getCouleurStatut(String statut) {
|
||||
if (statut == null) return "secondary";
|
||||
return switch (statut) {
|
||||
case "OK" -> "success";
|
||||
case "RETARD" -> "danger";
|
||||
case "EN_ATTENTE" -> "warning";
|
||||
case "OBLIGATOIRE" -> "info";
|
||||
default -> "secondary";
|
||||
};
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
public String getToken() { return token; }
|
||||
public void setToken(String token) { this.token = token; }
|
||||
public KpiPublicSnapshot getSnapshot() { return snapshot; }
|
||||
public String getErreur() { return erreur; }
|
||||
public boolean isCharge() { return snapshot != null; }
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.api.dto.RapportTrimestrielDto;
|
||||
import dev.lions.unionflow.client.service.RapportTrimestrielRestClient;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.faces.application.FacesMessage;
|
||||
import jakarta.faces.context.ExternalContext;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.time.Year;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Bean liste/actions sur les rapports trimestriels Contrôleur Interne (Sprint 8 ⇄ P2-NEW-3).
|
||||
*
|
||||
* @since 2026-04-25 (Sprint 8)
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class RapportsTrimestrielsBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = Logger.getLogger(RapportsTrimestrielsBean.class);
|
||||
|
||||
@Inject @RestClient
|
||||
RapportTrimestrielRestClient client;
|
||||
|
||||
private List<RapportTrimestrielDto> rapports = Collections.emptyList();
|
||||
private int annee = Year.now().getValue();
|
||||
private int trimestreCible = 1;
|
||||
private RapportTrimestrielDto selection;
|
||||
private String erreur;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
rafraichir();
|
||||
}
|
||||
|
||||
public void rafraichir() {
|
||||
erreur = null;
|
||||
try {
|
||||
rapports = client.lister(null, annee);
|
||||
LOG.infof("Liste rapports trimestriels chargée pour année %d : %d entrée(s)",
|
||||
annee, rapports.size());
|
||||
} catch (Exception e) {
|
||||
handleError("Chargement des rapports échoué", e);
|
||||
rapports = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public void genererRapport() {
|
||||
erreur = null;
|
||||
try {
|
||||
var nouveau = client.generer(null, annee, trimestreCible);
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "Rapport généré",
|
||||
"Trimestre " + nouveau.trimestre() + "/" + nouveau.annee()
|
||||
+ " — score " + nouveau.scoreConformite());
|
||||
rafraichir();
|
||||
} catch (Exception e) {
|
||||
handleError("Génération du rapport échouée", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void signerSelection(UUID signataireId) {
|
||||
if (selection == null) {
|
||||
addMessage(FacesMessage.SEVERITY_WARN, "Aucune sélection",
|
||||
"Sélectionnez un rapport DRAFT à signer");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
client.signer(selection.id(), signataireId);
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "Signé",
|
||||
"Rapport " + selection.annee() + "/T" + selection.trimestre() + " signé");
|
||||
rafraichir();
|
||||
} catch (Exception e) {
|
||||
handleError("Signature échouée", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void archiverSelection() {
|
||||
if (selection == null) {
|
||||
addMessage(FacesMessage.SEVERITY_WARN, "Aucune sélection",
|
||||
"Sélectionnez un rapport SIGNE à archiver");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
client.archiver(selection.id());
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "Archivé",
|
||||
"Rapport " + selection.annee() + "/T" + selection.trimestre() + " archivé");
|
||||
rafraichir();
|
||||
} catch (Exception e) {
|
||||
handleError("Archivage échoué", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void telechargerPdf(RapportTrimestrielDto r) {
|
||||
if (r == null) return;
|
||||
try {
|
||||
byte[] pdf = client.telechargerPdf(r.id());
|
||||
String filename = String.format("rapport-trim-%d-T%d.pdf", r.annee(), r.trimestre());
|
||||
|
||||
FacesContext fc = FacesContext.getCurrentInstance();
|
||||
ExternalContext ec = fc.getExternalContext();
|
||||
ec.responseReset();
|
||||
ec.setResponseContentType("application/pdf");
|
||||
ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
|
||||
ec.setResponseContentLength(pdf.length);
|
||||
try (OutputStream os = ec.getResponseOutputStream()) {
|
||||
os.write(pdf);
|
||||
}
|
||||
fc.responseComplete();
|
||||
} catch (IOException e) {
|
||||
handleError("Téléchargement PDF échoué", e);
|
||||
} catch (Exception e) {
|
||||
handleError("Téléchargement PDF échoué", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleError(String summary, Exception e) {
|
||||
LOG.warnf("%s : %s", summary, e.getMessage());
|
||||
erreur = summary + " — " + e.getMessage();
|
||||
addMessage(FacesMessage.SEVERITY_ERROR, "Erreur", erreur);
|
||||
}
|
||||
|
||||
private void addMessage(FacesMessage.Severity sev, String summary, String detail) {
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(sev, summary, detail));
|
||||
}
|
||||
|
||||
public List<RapportTrimestrielDto> getRapports() { return rapports; }
|
||||
public int getAnnee() { return annee; }
|
||||
public void setAnnee(int annee) { this.annee = annee; }
|
||||
public int getTrimestreCible() { return trimestreCible; }
|
||||
public void setTrimestreCible(int trimestreCible) { this.trimestreCible = trimestreCible; }
|
||||
public RapportTrimestrielDto getSelection() { return selection; }
|
||||
public void setSelection(RapportTrimestrielDto selection) { this.selection = selection; }
|
||||
public String getErreur() { return erreur; }
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.service.RoleDelegationRestClient;
|
||||
import dev.lions.unionflow.server.api.dto.delegation.request.CreateRoleDelegationRequest;
|
||||
import dev.lions.unionflow.server.api.dto.delegation.response.RoleDelegationResponse;
|
||||
import jakarta.faces.application.FacesMessage;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Bean liste/création/révocation des délégations de rôle (Sprint 11 ⇄ backend Sprint 10).
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class RoleDelegationBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = Logger.getLogger(RoleDelegationBean.class);
|
||||
|
||||
@Inject @RestClient RoleDelegationRestClient client;
|
||||
|
||||
private UUID organisationId;
|
||||
private List<RoleDelegationResponse> delegations = Collections.emptyList();
|
||||
private RoleDelegationResponse selection;
|
||||
private String erreur;
|
||||
|
||||
// Formulaire création
|
||||
private UUID delegantUserId;
|
||||
private UUID delegataireUserId;
|
||||
private String roleDelegue = "TRESORIER";
|
||||
private LocalDateTime dateDebut = LocalDateTime.now().plusHours(1);
|
||||
private LocalDateTime dateFin = LocalDateTime.now().plusDays(14);
|
||||
private String motif;
|
||||
private String rolesDelegataireCsv;
|
||||
|
||||
public void rechercher() {
|
||||
erreur = null;
|
||||
if (organisationId == null) return;
|
||||
try {
|
||||
delegations = client.listerParOrganisation(organisationId);
|
||||
LOG.infof("Délégations chargées org=%s : %d", organisationId, delegations.size());
|
||||
} catch (Exception e) {
|
||||
handleError("Chargement délégations échoué", e);
|
||||
delegations = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public void creer() {
|
||||
if (organisationId == null || delegantUserId == null || delegataireUserId == null) {
|
||||
addMessage(FacesMessage.SEVERITY_WARN, "Champs requis",
|
||||
"Organisation, déléguant et délégataire sont obligatoires");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
CreateRoleDelegationRequest req = CreateRoleDelegationRequest.builder()
|
||||
.organisationId(organisationId)
|
||||
.delegantUserId(delegantUserId)
|
||||
.delegataireUserId(delegataireUserId)
|
||||
.roleDelegue(roleDelegue)
|
||||
.dateDebut(dateDebut)
|
||||
.dateFin(dateFin)
|
||||
.motif(motif)
|
||||
.build();
|
||||
RoleDelegationResponse created = client.creer(req, rolesDelegataireCsv);
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "Délégation créée",
|
||||
"Rôle " + created.roleDelegue() + " jusqu'au " + created.dateFin());
|
||||
resetForm();
|
||||
rechercher();
|
||||
} catch (Exception e) {
|
||||
handleError("Création délégation échouée", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void revoquerSelection() {
|
||||
if (selection == null) return;
|
||||
try {
|
||||
client.revoquer(selection.id());
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "Délégation révoquée",
|
||||
"Rôle " + selection.roleDelegue());
|
||||
rechercher();
|
||||
} catch (Exception e) {
|
||||
handleError("Révocation échouée", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getCouleurStatut(String statut) {
|
||||
if (statut == null) return "secondary";
|
||||
return switch (statut) {
|
||||
case "ACTIVE" -> "success";
|
||||
case "REVOQUEE" -> "danger";
|
||||
case "EXPIREE" -> "warning";
|
||||
default -> "secondary";
|
||||
};
|
||||
}
|
||||
|
||||
private void resetForm() {
|
||||
delegantUserId = null;
|
||||
delegataireUserId = null;
|
||||
roleDelegue = "TRESORIER";
|
||||
dateDebut = LocalDateTime.now().plusHours(1);
|
||||
dateFin = LocalDateTime.now().plusDays(14);
|
||||
motif = null;
|
||||
rolesDelegataireCsv = null;
|
||||
}
|
||||
|
||||
private void handleError(String summary, Exception e) {
|
||||
LOG.warnf("%s : %s", summary, e.getMessage());
|
||||
erreur = summary + " — " + e.getMessage();
|
||||
addMessage(FacesMessage.SEVERITY_ERROR, "Erreur", erreur);
|
||||
}
|
||||
|
||||
private void addMessage(FacesMessage.Severity sev, String summary, String detail) {
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(sev, summary, detail));
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
public UUID getOrganisationId() { return organisationId; }
|
||||
public void setOrganisationId(UUID organisationId) { this.organisationId = organisationId; }
|
||||
public List<RoleDelegationResponse> getDelegations() { return delegations; }
|
||||
public RoleDelegationResponse getSelection() { return selection; }
|
||||
public void setSelection(RoleDelegationResponse selection) { this.selection = selection; }
|
||||
public String getErreur() { return erreur; }
|
||||
public UUID getDelegantUserId() { return delegantUserId; }
|
||||
public void setDelegantUserId(UUID id) { this.delegantUserId = id; }
|
||||
public UUID getDelegataireUserId() { return delegataireUserId; }
|
||||
public void setDelegataireUserId(UUID id) { this.delegataireUserId = id; }
|
||||
public String getRoleDelegue() { return roleDelegue; }
|
||||
public void setRoleDelegue(String roleDelegue) { this.roleDelegue = roleDelegue; }
|
||||
public LocalDateTime getDateDebut() { return dateDebut; }
|
||||
public void setDateDebut(LocalDateTime dateDebut) { this.dateDebut = dateDebut; }
|
||||
public LocalDateTime getDateFin() { return dateFin; }
|
||||
public void setDateFin(LocalDateTime dateFin) { this.dateFin = dateFin; }
|
||||
public String getMotif() { return motif; }
|
||||
public void setMotif(String motif) { this.motif = motif; }
|
||||
public String getRolesDelegataireCsv() { return rolesDelegataireCsv; }
|
||||
public void setRolesDelegataireCsv(String csv) { this.rolesDelegataireCsv = csv; }
|
||||
}
|
||||
139
src/main/java/dev/lions/unionflow/client/view/ViewPathsBean.java
Normal file
139
src/main/java/dev/lions/unionflow/client/view/ViewPathsBean.java
Normal file
@@ -0,0 +1,139 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.constants.ViewPaths;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Bean exposant {@link ViewPaths} aux pages JSF via expressions EL.
|
||||
*
|
||||
* <p>Usage XHTML : {@code outcome="#{paths.uboList}"}.
|
||||
*
|
||||
* <p>Le bean est {@code @ApplicationScoped} car les chemins sont immuables et identiques pour
|
||||
* tous les utilisateurs — un seul instance suffit.
|
||||
*
|
||||
* @since 2026-04-25 (Sprint 12)
|
||||
*/
|
||||
@Named("paths")
|
||||
@ApplicationScoped
|
||||
public class ViewPathsBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// ─── Index / Dashboard ───────────────────────────────────────────────────
|
||||
public String getRoot() { return ViewPaths.ROOT; }
|
||||
public String getIndex() { return ViewPaths.INDEX; }
|
||||
public String getDashboard() { return ViewPaths.DASHBOARD; }
|
||||
public String getDashboardMembre() { return ViewPaths.DASHBOARD_MEMBRE; }
|
||||
|
||||
// ─── Profil personnel ────────────────────────────────────────────────────
|
||||
public String getProfile() { return ViewPaths.PROFILE; }
|
||||
public String getPersonnelProfil() { return ViewPaths.PERSONNEL_PROFIL; }
|
||||
public String getPersonnelParametres() { return ViewPaths.PERSONNEL_PARAMETRES; }
|
||||
public String getPersonnelPreferences() { return ViewPaths.PERSONNEL_PREFERENCES; }
|
||||
public String getPersonnelNotifications() { return ViewPaths.PERSONNEL_NOTIFICATIONS; }
|
||||
|
||||
// ─── Membres ─────────────────────────────────────────────────────────────
|
||||
public String getMembreListe() { return ViewPaths.MEMBRE_LISTE; }
|
||||
public String getMembreInscription() { return ViewPaths.MEMBRE_INSCRIPTION; }
|
||||
public String getMembreValidation() { return ViewPaths.MEMBRE_VALIDATION; }
|
||||
public String getMembreImport() { return ViewPaths.MEMBRE_IMPORT; }
|
||||
public String getMembreExport() { return ViewPaths.MEMBRE_EXPORT; }
|
||||
public String getMembreRecherche() { return ViewPaths.MEMBRE_RECHERCHE; }
|
||||
public String getMembreCotisations() { return ViewPaths.MEMBRE_COTISATIONS; }
|
||||
public String getMembreProfil() { return ViewPaths.MEMBRE_PROFIL; }
|
||||
public String getMembrePaiementCotisations() { return ViewPaths.MEMBRE_PAIEMENT_COTISATIONS; }
|
||||
|
||||
// ─── Adhésions ───────────────────────────────────────────────────────────
|
||||
public String getAdhesionListe() { return ViewPaths.ADHESION_LISTE; }
|
||||
public String getAdhesionDemande() { return ViewPaths.ADHESION_DEMANDE; }
|
||||
public String getAdhesionValidation() { return ViewPaths.ADHESION_VALIDATION; }
|
||||
public String getAdhesionHistorique() { return ViewPaths.ADHESION_HISTORIQUE; }
|
||||
public String getAdhesionRenouvellement() { return ViewPaths.ADHESION_RENOUVELLEMENT; }
|
||||
|
||||
// ─── Cotisations ─────────────────────────────────────────────────────────
|
||||
public String getCotisationsGestionAdmin() { return ViewPaths.COTISATIONS_GESTION_ADMIN; }
|
||||
public String getCotisationPaiement() { return ViewPaths.COTISATION_PAIEMENT; }
|
||||
public String getCotisationHistorique() { return ViewPaths.COTISATION_HISTORIQUE; }
|
||||
public String getCotisationRelances() { return ViewPaths.COTISATION_RELANCES; }
|
||||
|
||||
// ─── Finance ─────────────────────────────────────────────────────────────
|
||||
public String getFinanceTresorerie() { return ViewPaths.FINANCE_TRESORERIE; }
|
||||
public String getFinanceBudgets() { return ViewPaths.FINANCE_BUDGETS; }
|
||||
public String getFinanceBilans() { return ViewPaths.FINANCE_BILANS; }
|
||||
public String getFinanceApprobations() { return ViewPaths.FINANCE_APPROBATIONS; }
|
||||
public String getComptabiliteGestion() { return ViewPaths.COMPTABILITE_GESTION; }
|
||||
|
||||
// ─── Épargne / Crédit ────────────────────────────────────────────────────
|
||||
public String getEpargneComptes() { return ViewPaths.EPARGNE_COMPTES; }
|
||||
public String getCreditDemandes() { return ViewPaths.CREDIT_DEMANDES; }
|
||||
public String getCreditEvaluation() { return ViewPaths.CREDIT_EVALUATION; }
|
||||
public String getCreditSuivi() { return ViewPaths.CREDIT_SUIVI; }
|
||||
public String getCreditRemboursements() { return ViewPaths.CREDIT_REMBOURSEMENTS; }
|
||||
public String getCreditStatistiques() { return ViewPaths.CREDIT_STATISTIQUES; }
|
||||
|
||||
// ─── Événements ──────────────────────────────────────────────────────────
|
||||
public String getEvenementGestion() { return ViewPaths.EVENEMENT_GESTION; }
|
||||
public String getEvenementCreation() { return ViewPaths.EVENEMENT_CREATION; }
|
||||
public String getEvenementCalendrier() { return ViewPaths.EVENEMENT_CALENDRIER; }
|
||||
public String getEvenementParticipants() { return ViewPaths.EVENEMENT_PARTICIPANTS; }
|
||||
public String getEvenementPlanification() { return ViewPaths.EVENEMENT_PLANIFICATION; }
|
||||
public String getEvenementLogistique() { return ViewPaths.EVENEMENT_LOGISTIQUE; }
|
||||
public String getEvenementBilan() { return ViewPaths.EVENEMENT_BILAN; }
|
||||
public String getEvenementBilanDetail() { return ViewPaths.EVENEMENT_BILAN_DETAIL; }
|
||||
|
||||
// ─── Aide / Support ──────────────────────────────────────────────────────
|
||||
public String getAideDemande() { return ViewPaths.AIDE_DEMANDE; }
|
||||
public String getAideRequests() { return ViewPaths.AIDE_REQUESTS; }
|
||||
public String getAideApproved() { return ViewPaths.AIDE_APPROVED; }
|
||||
public String getAideTraitement() { return ViewPaths.AIDE_TRAITEMENT; }
|
||||
public String getAideHistorique() { return ViewPaths.AIDE_HISTORIQUE; }
|
||||
public String getAideStatistiques() { return ViewPaths.AIDE_STATISTIQUES; }
|
||||
public String getAideFaq() { return ViewPaths.AIDE_FAQ; }
|
||||
public String getAideSupport() { return ViewPaths.AIDE_SUPPORT; }
|
||||
public String getAideApropos() { return ViewPaths.AIDE_APROPOS; }
|
||||
|
||||
// ─── Communication / Documents ───────────────────────────────────────────
|
||||
public String getCommunicationConversations() { return ViewPaths.COMMUNICATION_CONVERSATIONS; }
|
||||
public String getCommunicationNotifications() { return ViewPaths.COMMUNICATION_NOTIFICATIONS; }
|
||||
public String getDocumentsMesDocuments() { return ViewPaths.DOCUMENTS_MES_DOCUMENTS; }
|
||||
|
||||
// ─── Organisation ────────────────────────────────────────────────────────
|
||||
public String getOrganisationListe() { return ViewPaths.ORGANISATION_LISTE; }
|
||||
public String getOrganisationDetail() { return ViewPaths.ORGANISATION_DETAIL; }
|
||||
public String getOrganisationNouvelle() { return ViewPaths.ORGANISATION_NOUVELLE; }
|
||||
public String getOrganisationStatistiques() { return ViewPaths.ORGANISATION_STATISTIQUES; }
|
||||
|
||||
// ─── Conformité (Sprints 8, 11) ──────────────────────────────────────────
|
||||
public String getConformiteDashboard() { return ViewPaths.CONFORMITE_DASHBOARD; }
|
||||
public String getConformiteRapportsTrimestriels() { return ViewPaths.CONFORMITE_RAPPORTS_TRIMESTRIELS; }
|
||||
public String getConformiteBeneficiairesEffectifs() { return ViewPaths.CONFORMITE_BENEFICIAIRES_EFFECTIFS; }
|
||||
public String getConformiteAuditTrail() { return ViewPaths.CONFORMITE_AUDIT_TRAIL; }
|
||||
public String getConformiteLiveFeed() { return ViewPaths.CONFORMITE_LIVE_FEED; }
|
||||
|
||||
// ─── Admin technique ─────────────────────────────────────────────────────
|
||||
public String getAdminPispiReadiness() { return ViewPaths.ADMIN_PISPI_READINESS; }
|
||||
public String getAdminRoleDelegations() { return ViewPaths.ADMIN_ROLE_DELEGATIONS; }
|
||||
public String getAdminSauvegarde() { return ViewPaths.ADMIN_SAUVEGARDE; }
|
||||
public String getAdminAuditJournal() { return ViewPaths.ADMIN_AUDIT_JOURNAL; }
|
||||
public String getAdminLogsSysteme() { return ViewPaths.ADMIN_LOGS_SYSTEME; }
|
||||
public String getAdminParametres() { return ViewPaths.ADMIN_PARAMETRES; }
|
||||
public String getMembreParametres() { return ViewPaths.MEMBRE_PARAMETRES; }
|
||||
|
||||
// ─── Rapports ────────────────────────────────────────────────────────────
|
||||
public String getRapportFinances() { return ViewPaths.RAPPORT_FINANCES; }
|
||||
public String getRapportMembres() { return ViewPaths.RAPPORT_MEMBRES; }
|
||||
public String getRapportActivites() { return ViewPaths.RAPPORT_ACTIVITES; }
|
||||
public String getRapportTableauxBord() { return ViewPaths.RAPPORT_TABLEAUX_BORD; }
|
||||
public String getRapportExport() { return ViewPaths.RAPPORT_EXPORT; }
|
||||
|
||||
// ─── Souscription ────────────────────────────────────────────────────────
|
||||
public String getSouscriptionDashboard() { return ViewPaths.SOUSCRIPTION_DASHBOARD; }
|
||||
|
||||
// ─── Super-Admin ─────────────────────────────────────────────────────────
|
||||
public String getSuperAdminDashboard() { return ViewPaths.SUPER_ADMIN_DASHBOARD; }
|
||||
public String getSuperAdminRolesGestion() { return ViewPaths.SUPER_ADMIN_ROLES_GESTION; }
|
||||
public String getSuperAdminTypesOrganisations() { return ViewPaths.SUPER_ADMIN_TYPES_ORGANISATIONS; }
|
||||
public String getSuperAdminConfigurationSysteme() { return ViewPaths.SUPER_ADMIN_CONFIGURATION_SYSTEME; }
|
||||
}
|
||||
@@ -63,11 +63,11 @@
|
||||
<h:form>
|
||||
<p:button value="Se reconnecter"
|
||||
icon="pi pi-sign-in"
|
||||
outcome="/"
|
||||
outcome="#{paths.root}"
|
||||
styleClass="ui-button-primary ui-button-lg" />
|
||||
<p:button value="Page d'accueil"
|
||||
icon="pi pi-home"
|
||||
outcome="/index"
|
||||
outcome="#{paths.index}"
|
||||
styleClass="ui-button-secondary ui-button-outlined ui-button-lg" />
|
||||
</h:form>
|
||||
</div>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<!-- Landing Topbar -->
|
||||
<div class="landing-topbar">
|
||||
<div class="landing-topbar-left">
|
||||
<h:link id="logolink" outcome="/pages/secure/dashboard" styleClass="logo unionflow-brand unionflow-brand--landing">
|
||||
<h:link id="logolink" outcome="#{paths.dashboard}" styleClass="logo unionflow-brand unionflow-brand--landing">
|
||||
<h:graphicImage value="#{request.contextPath}/resources/freya-layout/images/unionflow-logo.png"
|
||||
alt="Logo UnionFlow"
|
||||
styleClass="unionflow-brand-icon" />
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="card">
|
||||
<h2>Administration - Audit</h2>
|
||||
<p>Page d'administration en cours de développement...</p>
|
||||
<p:button value="Retour" icon="pi pi-arrow-left" outcome="/pages/secure/dashboard"/>
|
||||
<p:button value="Retour" icon="pi pi-arrow-left" outcome="#{paths.dashboard}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="card">
|
||||
<h2>Administration - Backup</h2>
|
||||
<p>Page d'administration en cours de développement...</p>
|
||||
<p:button value="Retour" icon="pi pi-arrow-left" outcome="/pages/secure/dashboard"/>
|
||||
<p:button value="Retour" icon="pi pi-arrow-left" outcome="#{paths.dashboard}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:define name="title">UnionFlow - Paramètres administrateur</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h2 class="text-900 font-medium text-3xl m-0">Paramètres administrateur</h2>
|
||||
<p class="text-600 mt-1 mb-4">
|
||||
Préférences personnelles de l'administrateur — interface, notifications, sécurité.
|
||||
</p>
|
||||
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-4">
|
||||
<p:commandButton value="Profil personnel"
|
||||
icon="pi pi-user"
|
||||
action="#{paths.personnelProfil}"
|
||||
styleClass="w-full p-button-outlined" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:commandButton value="Préférences"
|
||||
icon="pi pi-cog"
|
||||
action="#{paths.personnelPreferences}"
|
||||
styleClass="w-full p-button-outlined" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:commandButton value="Notifications"
|
||||
icon="pi pi-bell"
|
||||
action="#{paths.personnelNotifications}"
|
||||
styleClass="w-full p-button-outlined" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p:divider styleClass="my-4" />
|
||||
|
||||
<p:panel header="Page en construction" styleClass="surface-100">
|
||||
<p class="text-700">
|
||||
Cette page centralise les paramètres administrateur (configuration globale,
|
||||
module d'audit, logs, sauvegarde). Les sous-sections sont accessibles via
|
||||
les boutons ci-dessus en attendant la version complète.
|
||||
</p>
|
||||
</p:panel>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="card">
|
||||
<h2>Administration - Settings</h2>
|
||||
<p>Page d'administration en cours de développement...</p>
|
||||
<p:button value="Retour" icon="pi pi-arrow-left" outcome="/pages/secure/dashboard"/>
|
||||
<p:button value="Retour" icon="pi pi-arrow-left" outcome="#{paths.dashboard}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="card">
|
||||
<h2>Administration - Users</h2>
|
||||
<p>Page d'administration en cours de développement...</p>
|
||||
<p:button value="Retour" icon="pi pi-arrow-left" outcome="/pages/secure/dashboard"/>
|
||||
<p:button value="Retour" icon="pi pi-arrow-left" outcome="#{paths.dashboard}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:define name="title">UnionFlow - Mes paramètres</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h2 class="text-900 font-medium text-3xl m-0">Mes paramètres</h2>
|
||||
<p class="text-600 mt-1 mb-4">
|
||||
Gérez votre profil, vos notifications et vos préférences d'affichage.
|
||||
</p>
|
||||
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-4">
|
||||
<p:commandButton value="Mon profil"
|
||||
icon="pi pi-user"
|
||||
action="#{paths.personnelProfil}"
|
||||
styleClass="w-full p-button-outlined" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:commandButton value="Mes préférences"
|
||||
icon="pi pi-cog"
|
||||
action="#{paths.personnelPreferences}"
|
||||
styleClass="w-full p-button-outlined" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:commandButton value="Mes notifications"
|
||||
icon="pi pi-bell"
|
||||
action="#{paths.personnelNotifications}"
|
||||
styleClass="w-full p-button-outlined" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p:divider styleClass="my-4" />
|
||||
|
||||
<p:panel header="Page en construction" styleClass="surface-100">
|
||||
<p class="text-700">
|
||||
Vue centralisée des paramètres membre. Sous-sections accessibles via
|
||||
les boutons ci-dessus.
|
||||
</p>
|
||||
</p:panel>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
152
src/main/resources/META-INF/resources/pages/public/kpi.xhtml
Normal file
152
src/main/resources/META-INF/resources/pages/public/kpi.xhtml
Normal file
@@ -0,0 +1,152 @@
|
||||
<!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">UnionFlow - KPI publics</ui:define>
|
||||
|
||||
<ui:define name="metadata">
|
||||
<f:metadata>
|
||||
<f:viewParam name="token" value="#{publicKpiBean.token}" />
|
||||
<f:viewAction action="#{publicKpiBean.charger}" />
|
||||
</f:metadata>
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="text-center mb-4">
|
||||
<i class="pi pi-shield text-primary" style="font-size: 3rem;" />
|
||||
<h2 class="text-900 font-medium text-3xl mt-3 mb-2">
|
||||
Indicateurs publics de conformité
|
||||
</h2>
|
||||
<p class="text-600 text-lg m-0">
|
||||
Vue read-only destinée aux autorités externes (BCEAO, ARTCI, CENTIF).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ui:fragment rendered="#{publicKpiBean.erreur != null}">
|
||||
<p:messages closable="false" />
|
||||
<div class="surface-100 border-round p-4 text-center">
|
||||
<i class="pi pi-exclamation-triangle text-orange-500"
|
||||
style="font-size: 2rem;" />
|
||||
<h3 class="text-900 mt-2 mb-1">Accès impossible</h3>
|
||||
<p class="text-700 m-0">#{publicKpiBean.erreur}</p>
|
||||
</div>
|
||||
</ui:fragment>
|
||||
|
||||
<ui:fragment rendered="#{publicKpiBean.charge}">
|
||||
<!-- Score global -->
|
||||
<div class="grid mb-4">
|
||||
<div class="col-12 md:col-4">
|
||||
<div class="surface-100 border-round p-4 text-center">
|
||||
<div class="text-700 mb-2">Score conformité</div>
|
||||
<div class="text-#{publicKpiBean.couleurScore eq 'success' ? 'green' : (publicKpiBean.couleurScore eq 'warning' ? 'orange' : 'red')}-600 font-bold"
|
||||
style="font-size: 3.5rem; line-height: 1;">
|
||||
#{publicKpiBean.snapshot.scoreGlobal}
|
||||
</div>
|
||||
<div class="text-600 mt-1">/ 100</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 md:col-8">
|
||||
<div class="surface-100 border-round p-4 h-full">
|
||||
<div class="text-700 font-bold mb-2">Organisation</div>
|
||||
<div class="text-900 text-2xl">#{publicKpiBean.snapshot.organisationNom}</div>
|
||||
<div class="text-600 mt-3">
|
||||
Référentiel comptable :
|
||||
<span class="font-semibold">#{publicKpiBean.snapshot.referentielComptable}</span>
|
||||
</div>
|
||||
<div class="text-600 mt-2">
|
||||
Snapshot généré le : <span class="font-mono">#{publicKpiBean.snapshot.dateGeneration}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Indicateurs -->
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="flex justify-content-between">
|
||||
<span class="text-700">Compliance Officer</span>
|
||||
<p:tag value="#{publicKpiBean.snapshot.complianceOfficerDesigne ? 'Désigné' : 'ABSENT'}"
|
||||
severity="#{publicKpiBean.snapshot.complianceOfficerDesigne ? 'success' : 'danger'}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="flex justify-content-between">
|
||||
<span class="text-700">AG annuelle</span>
|
||||
<p:tag value="#{publicKpiBean.snapshot.agAnnuelleStatut}"
|
||||
severity="#{publicKpiBean.getCouleurStatut(publicKpiBean.snapshot.agAnnuelleStatut)}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="flex justify-content-between">
|
||||
<span class="text-700">Rapport AIRMS</span>
|
||||
<p:tag value="#{publicKpiBean.snapshot.rapportAirmsStatut}"
|
||||
severity="#{publicKpiBean.getCouleurStatut(publicKpiBean.snapshot.rapportAirmsStatut)}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="text-700">Dirigeants enrôlés CMU</div>
|
||||
<div class="text-900 text-2xl mt-1">#{publicKpiBean.snapshot.dirigeantsAvecCmu}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="text-700">Taux KYC à jour</div>
|
||||
<div class="text-900 text-2xl mt-1">#{publicKpiBean.snapshot.tauxKycAJourPct} %</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="text-700">Taux formation LBC/FT</div>
|
||||
<div class="text-900 text-2xl mt-1">#{publicKpiBean.snapshot.tauxFormationLbcFtPct} %</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="text-700">Couverture UBO</div>
|
||||
<div class="text-900 text-2xl mt-1">#{publicKpiBean.snapshot.couvertureUboPct} %</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="flex justify-content-between">
|
||||
<span class="text-700">Commissaire aux comptes</span>
|
||||
<p:tag value="#{publicKpiBean.snapshot.commissaireAuxComptesStatut}"
|
||||
severity="#{publicKpiBean.getCouleurStatut(publicKpiBean.snapshot.commissaireAuxComptesStatut)}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p:divider styleClass="my-4" />
|
||||
<div class="text-center text-500 text-sm">
|
||||
<i class="pi pi-info-circle mr-1" />
|
||||
Vue partagée temporairement via lien signé. Toute consultation est tracée
|
||||
dans l'audit trail UnionFlow.
|
||||
</div>
|
||||
</ui:fragment>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:define name="title">UnionFlow - PI-SPI Readiness</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="text-900 font-medium text-3xl m-0">PI-SPI Readiness</h2>
|
||||
<p class="text-600 mt-1 mb-0">
|
||||
Vérification des pré-requis intégration PI-SPI BCEAO avant activation production.
|
||||
</p>
|
||||
</div>
|
||||
<p:commandButton value="Rafraîchir"
|
||||
icon="pi pi-refresh"
|
||||
action="#{pispiReadinessBean.rafraichir}"
|
||||
update=":form-readiness"
|
||||
styleClass="p-button-outlined" />
|
||||
</div>
|
||||
|
||||
<h:form id="form-readiness">
|
||||
<p:messages closable="true" />
|
||||
|
||||
<ui:fragment rendered="#{pispiReadinessBean.readiness != null}">
|
||||
<!-- Statut global -->
|
||||
<div class="surface-100 border-round p-4 mb-4 text-center">
|
||||
<div class="text-700 mb-2">Statut global</div>
|
||||
<p:tag value="#{pispiReadinessBean.readiness.globalStatus}"
|
||||
severity="#{pispiReadinessBean.couleurStatus}"
|
||||
style="font-size: 1.5rem; padding: 0.5rem 1.5rem;" />
|
||||
<div class="text-600 mt-3">
|
||||
Base URL : <span class="font-mono">#{pispiReadinessBean.readiness.baseUrl}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Blocages -->
|
||||
<p:fieldset legend="Blocages critiques"
|
||||
rendered="#{not empty pispiReadinessBean.readiness.blockingIssues}"
|
||||
styleClass="mb-3 border-red-300">
|
||||
<ul class="m-0">
|
||||
<ui:repeat value="#{pispiReadinessBean.readiness.blockingIssues}" var="b">
|
||||
<li class="text-red-600">#{b}</li>
|
||||
</ui:repeat>
|
||||
</ul>
|
||||
</p:fieldset>
|
||||
|
||||
<!-- Warnings -->
|
||||
<p:fieldset legend="Avertissements (non bloquants)"
|
||||
rendered="#{not empty pispiReadinessBean.readiness.warnings}"
|
||||
styleClass="mb-3 border-orange-300">
|
||||
<ul class="m-0">
|
||||
<ui:repeat value="#{pispiReadinessBean.readiness.warnings}" var="w">
|
||||
<li class="text-orange-600">#{w}</li>
|
||||
</ui:repeat>
|
||||
</ul>
|
||||
</p:fieldset>
|
||||
|
||||
<!-- Détail des checks -->
|
||||
<p:dataTable value="#{pispiReadinessBean.readiness.checks}"
|
||||
var="c"
|
||||
emptyMessage="Aucun check disponible">
|
||||
<p:column headerText="Vérification">
|
||||
<h:outputText value="#{c.name}" styleClass="font-mono" />
|
||||
</p:column>
|
||||
<p:column headerText="Sévérité">
|
||||
<p:tag value="#{c.severity}"
|
||||
severity="#{c.severity eq 'BLOCKING' ? 'danger' : 'warning'}" />
|
||||
</p:column>
|
||||
<p:column headerText="Statut">
|
||||
<p:tag value="#{c.status}"
|
||||
severity="#{c.status eq 'PASS' ? 'success' : pispiReadinessBean.getCouleurCheck(c.severity, c.status)}"
|
||||
icon="#{c.status eq 'PASS' ? 'pi pi-check' : 'pi pi-times'}" />
|
||||
</p:column>
|
||||
<p:column headerText="Message">
|
||||
<h:outputText value="#{c.message}" />
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
</ui:fragment>
|
||||
|
||||
<ui:fragment rendered="#{pispiReadinessBean.readiness == null}">
|
||||
<div class="text-center text-600 p-5">
|
||||
Impossible de récupérer le rapport readiness.
|
||||
</div>
|
||||
</ui:fragment>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:define name="title">UnionFlow - Délégations de rôles</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h2 class="text-900 font-medium text-3xl m-0">Délégations temporaires de rôles</h2>
|
||||
<p class="text-600 mt-1 mb-3">
|
||||
Délégation d'un rôle pour absence prolongée. Vérification SoD à la création.
|
||||
</p>
|
||||
|
||||
<h:form id="form-deleg">
|
||||
<p:messages closable="true" />
|
||||
|
||||
<p:panel header="Recherche" styleClass="mb-3">
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-9">
|
||||
<p:outputLabel for="org" value="Organisation (UUID)" />
|
||||
<p:inputText id="org" value="#{roleDelegationBean.organisationId}"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-3 flex align-items-end">
|
||||
<p:commandButton value="Rechercher"
|
||||
icon="pi pi-search"
|
||||
action="#{roleDelegationBean.rechercher}"
|
||||
update="form-deleg"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</p:panel>
|
||||
|
||||
<p:dataTable value="#{roleDelegationBean.delegations}"
|
||||
var="d"
|
||||
selection="#{roleDelegationBean.selection}"
|
||||
rowKey="#{d.id}"
|
||||
selectionMode="single"
|
||||
emptyMessage="Aucune délégation">
|
||||
<p:column headerText="Rôle délégué">
|
||||
<h:outputText value="#{d.roleDelegue}" />
|
||||
</p:column>
|
||||
<p:column headerText="Déléguant">
|
||||
<h:outputText value="#{d.delegantUserId}" />
|
||||
</p:column>
|
||||
<p:column headerText="Délégataire">
|
||||
<h:outputText value="#{d.delegataireUserId}" />
|
||||
</p:column>
|
||||
<p:column headerText="Période">
|
||||
<h:outputText value="du #{d.dateDebut} au #{d.dateFin}" />
|
||||
</p:column>
|
||||
<p:column headerText="Motif">
|
||||
<h:outputText value="#{d.motif}" />
|
||||
</p:column>
|
||||
<p:column headerText="Statut">
|
||||
<p:tag value="#{d.statut}"
|
||||
severity="#{roleDelegationBean.getCouleurStatut(d.statut)}" />
|
||||
</p:column>
|
||||
<p:column headerText="Active">
|
||||
<p:tag value="#{d.estActive ? 'OUI' : 'NON'}"
|
||||
severity="#{d.estActive ? 'success' : 'secondary'}" />
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
|
||||
<div class="flex gap-2 mt-3">
|
||||
<p:commandButton value="Révoquer (sélection)"
|
||||
icon="pi pi-times"
|
||||
action="#{roleDelegationBean.revoquerSelection}"
|
||||
update="form-deleg"
|
||||
styleClass="p-button-danger" />
|
||||
</div>
|
||||
|
||||
<p:panel header="Nouvelle délégation" toggleable="true" collapsed="true" styleClass="mt-4">
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6">
|
||||
<p:outputLabel for="dgt" value="Déléguant (UUID utilisateur) *" />
|
||||
<p:inputText id="dgt" value="#{roleDelegationBean.delegantUserId}"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-6">
|
||||
<p:outputLabel for="dgtr" value="Délégataire (UUID utilisateur) *" />
|
||||
<p:inputText id="dgtr" value="#{roleDelegationBean.delegataireUserId}"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:outputLabel for="role" value="Rôle délégué *" />
|
||||
<p:selectOneMenu id="role" value="#{roleDelegationBean.roleDelegue}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemValue="TRESORIER" itemLabel="TRESORIER" />
|
||||
<f:selectItem itemValue="SECRETAIRE" itemLabel="SECRETAIRE" />
|
||||
<f:selectItem itemValue="ADMIN_ORGANISATION" itemLabel="ADMIN_ORGANISATION" />
|
||||
<f:selectItem itemValue="COMPLIANCE_OFFICER" itemLabel="COMPLIANCE_OFFICER" />
|
||||
<f:selectItem itemValue="CONTROLEUR_INTERNE" itemLabel="CONTROLEUR_INTERNE" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:outputLabel for="rolesDeleg" value="Rôles existants délégataire (CSV)" />
|
||||
<p:inputText id="rolesDeleg" value="#{roleDelegationBean.rolesDelegataireCsv}"
|
||||
styleClass="w-full" placeholder="MEMBRE_ACTIF,MEMBRE_BUREAU" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:outputLabel for="motif" value="Motif" />
|
||||
<p:inputText id="motif" value="#{roleDelegationBean.motif}"
|
||||
styleClass="w-full" placeholder="Congé, mission..." />
|
||||
</div>
|
||||
<div class="col-12 md:col-6">
|
||||
<p:outputLabel for="dd" value="Date début *" />
|
||||
<p:datePicker id="dd" value="#{roleDelegationBean.dateDebut}"
|
||||
showTime="true" styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-6">
|
||||
<p:outputLabel for="df" value="Date fin *" />
|
||||
<p:datePicker id="df" value="#{roleDelegationBean.dateFin}"
|
||||
showTime="true" styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<p:commandButton value="Créer délégation"
|
||||
icon="pi pi-plus"
|
||||
action="#{roleDelegationBean.creer}"
|
||||
update="form-deleg" />
|
||||
</div>
|
||||
</div>
|
||||
</p:panel>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,112 @@
|
||||
<!DOCTYPE html>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:define name="title">UnionFlow - Audit Trail</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h2 class="text-900 font-medium text-3xl m-0">Audit Trail</h2>
|
||||
<p class="text-600 mt-1 mb-3">
|
||||
Historique enrichi des opérations sensibles + détection violations SoD.
|
||||
</p>
|
||||
|
||||
<h:form id="form-audit">
|
||||
<p:messages closable="true" />
|
||||
|
||||
<p:panel header="Filtres" styleClass="mb-3">
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-3">
|
||||
<p:outputLabel for="mode" value="Mode" />
|
||||
<p:selectOneMenu id="mode" value="#{auditTrailViewerBean.mode}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemValue="ORG" itemLabel="Par organisation" />
|
||||
<f:selectItem itemValue="USER" itemLabel="Par utilisateur" />
|
||||
<f:selectItem itemValue="ENTITY" itemLabel="Historique entité" />
|
||||
<f:selectItem itemValue="SOD_VIOLATIONS" itemLabel="Violations SoD" />
|
||||
<f:selectItem itemValue="FINANCIAL" itemLabel="Opérations financières" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
<div class="col-12 md:col-3">
|
||||
<p:outputLabel for="org" value="Organisation (UUID)" />
|
||||
<p:inputText id="org" value="#{auditTrailViewerBean.orgId}" styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-3">
|
||||
<p:outputLabel for="usr" value="Utilisateur (UUID)" />
|
||||
<p:inputText id="usr" value="#{auditTrailViewerBean.userId}" styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-3 flex align-items-end">
|
||||
<p:commandButton value="Rechercher"
|
||||
icon="pi pi-search"
|
||||
action="#{auditTrailViewerBean.rechercher}"
|
||||
update="form-audit"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</p:panel>
|
||||
|
||||
<p:dataTable value="#{auditTrailViewerBean.operations}"
|
||||
var="o"
|
||||
selection="#{auditTrailViewerBean.selection}"
|
||||
rowKey="#{o.id}"
|
||||
selectionMode="single"
|
||||
emptyMessage="Aucune opération trouvée">
|
||||
<p:column headerText="Date">
|
||||
<h:outputText value="#{o.operationAt}" />
|
||||
</p:column>
|
||||
<p:column headerText="Utilisateur">
|
||||
<h:outputText value="#{o.userEmail}" />
|
||||
<h:outputText value=" (#{o.roleActif})" rendered="#{o.roleActif != null}"
|
||||
styleClass="text-600 text-sm" />
|
||||
</p:column>
|
||||
<p:column headerText="Action">
|
||||
<p:tag value="#{o.actionType}"
|
||||
severity="#{auditTrailViewerBean.getCouleurAction(o.actionType)}" />
|
||||
</p:column>
|
||||
<p:column headerText="Entité">
|
||||
<h:outputText value="#{o.entityType}" />
|
||||
</p:column>
|
||||
<p:column headerText="Description">
|
||||
<h:outputText value="#{o.description}" />
|
||||
</p:column>
|
||||
<p:column headerText="SoD">
|
||||
<p:tag value="#{o.sodCheckPassed ? 'OK' : 'VIOLATION'}"
|
||||
severity="#{auditTrailViewerBean.getCouleurSod(o.sodCheckPassed)}"
|
||||
rendered="#{o.sodCheckPassed != null}" />
|
||||
<h:outputText value="—" rendered="#{o.sodCheckPassed == null}" />
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
|
||||
<p:panel header="Détail opération" rendered="#{auditTrailViewerBean.selection != null}"
|
||||
styleClass="mt-3">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<strong>SoD violations :</strong>
|
||||
<h:outputText value="#{auditTrailViewerBean.selection.sodViolations}" />
|
||||
</div>
|
||||
<div class="col-12 md:col-6">
|
||||
<strong>Payload avant :</strong>
|
||||
<pre style="font-size: 0.85em; background:#f5f5f5; padding:8px;">#{auditTrailViewerBean.selection.payloadAvant}</pre>
|
||||
</div>
|
||||
<div class="col-12 md:col-6">
|
||||
<strong>Payload après :</strong>
|
||||
<pre style="font-size: 0.85em; background:#f5f5f5; padding:8px;">#{auditTrailViewerBean.selection.payloadApres}</pre>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Métadonnées :</strong>
|
||||
<pre style="font-size: 0.85em; background:#f5f5f5; padding:8px;">#{auditTrailViewerBean.selection.metadata}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</p:panel>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,147 @@
|
||||
<!DOCTYPE html>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:define name="title">UnionFlow - Bénéficiaires Effectifs (UBO)</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h2 class="text-900 font-medium text-3xl m-0">Bénéficiaires Effectifs (UBO)</h2>
|
||||
<p class="text-600 mt-1 mb-3">
|
||||
Instr. BCEAO 003-03-2025 — identification des personnes physiques contrôlant l'organisation.
|
||||
</p>
|
||||
|
||||
<h:form id="form-ubo">
|
||||
<p:messages closable="true" />
|
||||
|
||||
<p:panel header="Recherche" styleClass="mb-3">
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-4">
|
||||
<p:outputLabel for="kyc" value="KycDossier ID (UUID)" />
|
||||
<p:inputText id="kyc" value="#{beneficiaireEffectifBean.kycDossierId}"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:outputLabel for="org" value="Organisation cible (UUID)" />
|
||||
<p:inputText id="org" value="#{beneficiaireEffectifBean.organisationCibleId}"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-2 flex align-items-end">
|
||||
<p:selectBooleanCheckbox value="#{beneficiaireEffectifBean.filtrePep}"
|
||||
itemLabel="PEP uniquement" />
|
||||
</div>
|
||||
<div class="col-12 md:col-2 flex align-items-end">
|
||||
<p:commandButton value="Rechercher"
|
||||
icon="pi pi-search"
|
||||
action="#{beneficiaireEffectifBean.rechercher}"
|
||||
update="form-ubo"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</p:panel>
|
||||
|
||||
<p:dataTable value="#{beneficiaireEffectifBean.ubos}"
|
||||
var="u"
|
||||
selection="#{beneficiaireEffectifBean.selection}"
|
||||
rowKey="#{u.id}"
|
||||
selectionMode="single"
|
||||
emptyMessage="Aucun UBO trouvé">
|
||||
<p:column headerText="Nom complet">
|
||||
#{u.prenoms} #{u.nom}
|
||||
</p:column>
|
||||
<p:column headerText="Nationalité">
|
||||
<h:outputText value="#{u.nationalite}" />
|
||||
</p:column>
|
||||
<p:column headerText="% Capital">
|
||||
<h:outputText value="#{u.pourcentageCapital}" />
|
||||
</p:column>
|
||||
<p:column headerText="Nature contrôle">
|
||||
<h:outputText value="#{u.natureControle}" />
|
||||
</p:column>
|
||||
<p:column headerText="PEP">
|
||||
<p:tag value="PEP" severity="warning" rendered="#{u.estPep}" />
|
||||
<h:outputText value="—" rendered="#{not u.estPep}" />
|
||||
</p:column>
|
||||
<p:column headerText="Sanctions">
|
||||
<p:tag value="LISTÉ" severity="danger" rendered="#{u.presenceListesSanctions}" />
|
||||
<h:outputText value="—" rendered="#{not u.presenceListesSanctions}" />
|
||||
</p:column>
|
||||
<p:column headerText="Statut">
|
||||
<p:tag value="#{u.actif ? 'ACTIF' : 'INACTIF'}"
|
||||
severity="#{u.actif ? 'success' : 'secondary'}" />
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
|
||||
<div class="flex gap-2 mt-3">
|
||||
<p:commandButton value="Marquer PEP"
|
||||
icon="pi pi-flag"
|
||||
action="#{beneficiaireEffectifBean.marquerPepSelection}"
|
||||
update="form-ubo"
|
||||
styleClass="p-button-warning" />
|
||||
<p:commandButton value="Désactiver"
|
||||
icon="pi pi-times"
|
||||
action="#{beneficiaireEffectifBean.desactiverSelection}"
|
||||
update="form-ubo"
|
||||
styleClass="p-button-secondary" />
|
||||
</div>
|
||||
|
||||
<p:panel header="Nouveau UBO" toggleable="true" collapsed="true" styleClass="mt-4">
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-4">
|
||||
<p:outputLabel for="nom" value="Nom *" />
|
||||
<p:inputText id="nom" value="#{beneficiaireEffectifBean.nom}" styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:outputLabel for="prenoms" value="Prénoms *" />
|
||||
<p:inputText id="prenoms" value="#{beneficiaireEffectifBean.prenoms}" styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:outputLabel for="nat" value="Nationalité (ISO-3) *" />
|
||||
<p:inputText id="nat" value="#{beneficiaireEffectifBean.nationalite}"
|
||||
maxlength="3" styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:outputLabel for="pays" value="Pays résidence (ISO-3)" />
|
||||
<p:inputText id="pays" value="#{beneficiaireEffectifBean.paysResidence}"
|
||||
maxlength="3" styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:outputLabel for="pct" value="% Capital (0-100)" />
|
||||
<p:inputText id="pct" value="#{beneficiaireEffectifBean.pourcentageCapital}"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<p:outputLabel for="nat-ctl" value="Nature contrôle *" />
|
||||
<p:selectOneMenu id="nat-ctl" value="#{beneficiaireEffectifBean.natureControle}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemValue="DETENTION_CAPITAL" itemLabel="Détention capital" />
|
||||
<f:selectItem itemValue="DROITS_VOTE" itemLabel="Droits de vote" />
|
||||
<f:selectItem itemValue="CONTROLE_DE_FAIT" itemLabel="Contrôle de fait" />
|
||||
<f:selectItem itemValue="BENEFICIAIRE_ULTIME" itemLabel="Bénéficiaire ultime" />
|
||||
<f:selectItem itemValue="MANDAT_REPRESENTATION" itemLabel="Mandat" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<p:selectBooleanCheckbox value="#{beneficiaireEffectifBean.estPep}"
|
||||
itemLabel="Personne Politiquement Exposée (PEP)" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<p:commandButton value="Créer UBO"
|
||||
icon="pi pi-plus"
|
||||
action="#{beneficiaireEffectifBean.creer}"
|
||||
update="form-ubo" />
|
||||
</div>
|
||||
</div>
|
||||
</p:panel>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,164 @@
|
||||
<!DOCTYPE html>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:define name="title">UnionFlow - Tableau de bord conformité</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="text-900 font-medium text-3xl m-0">Tableau de bord conformité</h2>
|
||||
<p class="text-600 mt-1 mb-0">
|
||||
Indicateurs BCEAO Instr. 001-03-2025 / ARTCI / OHADA / AIRMS
|
||||
</p>
|
||||
</div>
|
||||
<p:commandButton value="Rafraîchir"
|
||||
icon="pi pi-refresh"
|
||||
action="#{conformiteDashboardBean.rafraichir}"
|
||||
update=":form-conformite"
|
||||
styleClass="p-button-outlined" />
|
||||
</div>
|
||||
|
||||
<h:form id="form-conformite">
|
||||
<p:messages closable="true" />
|
||||
|
||||
<ui:fragment rendered="#{conformiteDashboardBean.snapshot != null}">
|
||||
<!-- Score global -->
|
||||
<div class="grid mb-4">
|
||||
<div class="col-12 md:col-4">
|
||||
<div class="surface-100 border-round p-4 text-center">
|
||||
<div class="text-700 mb-2">Score conformité global</div>
|
||||
<div class="text-#{conformiteDashboardBean.couleurScore eq 'success' ? 'green' : (conformiteDashboardBean.couleurScore eq 'warning' ? 'orange' : 'red')}-600 font-bold"
|
||||
style="font-size: 3.5rem; line-height: 1;">
|
||||
#{conformiteDashboardBean.snapshot.scoreGlobal}
|
||||
</div>
|
||||
<div class="text-600 mt-1">/ 100</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-8">
|
||||
<div class="surface-100 border-round p-4 h-full">
|
||||
<div class="text-700 mb-2 font-bold">Organisation</div>
|
||||
<div class="text-900 text-xl">#{conformiteDashboardBean.snapshot.organisationNom}</div>
|
||||
<div class="text-600 mt-2">
|
||||
Référentiel comptable : <span class="font-semibold">#{conformiteDashboardBean.snapshot.referentielComptable}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Indicateurs détaillés -->
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="flex justify-content-between">
|
||||
<span class="text-700">Compliance Officer</span>
|
||||
<p:tag value="#{conformiteDashboardBean.snapshot.complianceOfficerDesigne ? 'Désigné' : 'ABSENT'}"
|
||||
severity="#{conformiteDashboardBean.snapshot.complianceOfficerDesigne ? 'success' : 'danger'}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="flex justify-content-between">
|
||||
<span class="text-700">AG annuelle</span>
|
||||
<p:tag value="#{conformiteDashboardBean.snapshot.agAnnuelle.statut}"
|
||||
severity="#{conformiteDashboardBean.snapshot.agAnnuelle.statut eq 'OK' ? 'success' : (conformiteDashboardBean.snapshot.agAnnuelle.statut eq 'RETARD' ? 'danger' : 'warning')}" />
|
||||
</div>
|
||||
<p class="text-600 text-sm mt-2 m-0">#{conformiteDashboardBean.snapshot.agAnnuelle.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="flex justify-content-between">
|
||||
<span class="text-700">Rapport AIRMS</span>
|
||||
<p:tag value="#{conformiteDashboardBean.snapshot.rapportAirms.statut}"
|
||||
severity="#{conformiteDashboardBean.snapshot.rapportAirms.statut eq 'OK' ? 'success' : 'warning'}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="text-700">Dirigeants enrôlés CMU</div>
|
||||
<div class="text-900 text-2xl mt-1">#{conformiteDashboardBean.snapshot.dirigeantsAvecCmu}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="text-700">Taux KYC à jour</div>
|
||||
<div class="text-900 text-2xl mt-1">#{conformiteDashboardBean.snapshot.tauxKycAJourPct} %</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="text-700">Taux formation LBC/FT</div>
|
||||
<div class="text-900 text-2xl mt-1">#{conformiteDashboardBean.snapshot.tauxFormationLbcFtPct} %</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="text-700">Couverture UBO</div>
|
||||
<div class="text-900 text-2xl mt-1">#{conformiteDashboardBean.snapshot.couvertureUboPct} %</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="text-700">Commissaire aux comptes</div>
|
||||
<p:tag value="#{conformiteDashboardBean.snapshot.commissaireAuxComptes.statut}"
|
||||
styleClass="mt-1" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<div class="surface-50 border-round p-3 h-full">
|
||||
<div class="text-700">FOMUS-CI</div>
|
||||
<p:tag value="#{conformiteDashboardBean.snapshot.fomusCi.statut}"
|
||||
styleClass="mt-1" />
|
||||
<p class="text-600 text-sm mt-2 m-0">#{conformiteDashboardBean.snapshot.fomusCi.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p:fieldset legend="Alertes critiques"
|
||||
rendered="#{conformiteDashboardBean.hasAlertes}"
|
||||
styleClass="mt-4 surface-100">
|
||||
<ul class="m-0">
|
||||
<li class="text-red-600" jsf:rendered="#{not conformiteDashboardBean.snapshot.complianceOfficerDesigne}">
|
||||
Compliance Officer non désigné — Instruction BCEAO 001-03-2025 obligatoire
|
||||
</li>
|
||||
<li class="text-red-600" jsf:rendered="#{conformiteDashboardBean.snapshot.agAnnuelle.statut eq 'RETARD'}">
|
||||
AG annuelle en retard — échéance 30 juin
|
||||
</li>
|
||||
<li class="text-orange-600" jsf:rendered="#{conformiteDashboardBean.snapshot.scoreGlobal lt 60}">
|
||||
Score global < 60 % — risque inspection BCEAO/ARTCI
|
||||
</li>
|
||||
</ul>
|
||||
</p:fieldset>
|
||||
</ui:fragment>
|
||||
|
||||
<ui:fragment rendered="#{conformiteDashboardBean.snapshot == null}">
|
||||
<p:messages />
|
||||
<div class="text-center text-600 p-5">
|
||||
Aucune donnée de conformité disponible.
|
||||
</div>
|
||||
</ui:fragment>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,117 @@
|
||||
<!DOCTYPE html>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:define name="title">UnionFlow - Live Activity Feed</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h2 class="text-900 font-medium text-3xl m-0">
|
||||
<i class="pi pi-bolt mr-2 text-yellow-500" />
|
||||
Live Activity Feed
|
||||
</h2>
|
||||
<p class="text-600 mt-1 mb-0">
|
||||
Transparency opérationnelle — les opérations sensibles défilent en temps réel.
|
||||
Refresh auto toutes les 10 secondes.
|
||||
</p>
|
||||
</div>
|
||||
<p:tag value="Refresh ##{liveFeedBean.compteur}" severity="info"
|
||||
icon="pi pi-refresh" />
|
||||
</div>
|
||||
|
||||
<h:form id="form-livefeed">
|
||||
<p:messages closable="true" />
|
||||
|
||||
<p:panel header="Scope" styleClass="mb-3">
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-3">
|
||||
<p:outputLabel for="scope" value="Périmètre" />
|
||||
<p:selectOneMenu id="scope" value="#{liveFeedBean.scope}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemValue="SELF" itemLabel="Mes opérations" />
|
||||
<f:selectItem itemValue="ORG" itemLabel="Mon organisation" />
|
||||
<f:selectItem itemValue="ALL" itemLabel="Toutes (compliance/contrôleur)" />
|
||||
<p:ajax event="change" listener="#{liveFeedBean.rafraichir}"
|
||||
update="form-livefeed" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
<div class="col-12 md:col-4" jsf:rendered="#{liveFeedBean.scope eq 'ORG'}">
|
||||
<p:outputLabel for="orgId" value="Organisation (UUID)" />
|
||||
<p:inputText id="orgId" value="#{liveFeedBean.orgId}"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-4" jsf:rendered="#{liveFeedBean.scope eq 'SELF'}">
|
||||
<p:outputLabel for="userId" value="Utilisateur (UUID)" />
|
||||
<p:inputText id="userId" value="#{liveFeedBean.userId}"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-2">
|
||||
<p:outputLabel for="lim" value="Limite (1-500)" />
|
||||
<p:inputNumber id="lim" value="#{liveFeedBean.limit}"
|
||||
minValue="1" maxValue="500"
|
||||
decimalPlaces="0" />
|
||||
</div>
|
||||
<div class="col-12 md:col-3 flex align-items-end">
|
||||
<p:commandButton value="Rafraîchir"
|
||||
icon="pi pi-refresh"
|
||||
action="#{liveFeedBean.rafraichir}"
|
||||
update="form-livefeed"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</p:panel>
|
||||
|
||||
<!-- Auto-refresh toutes les 10 secondes (10000 ms) -->
|
||||
<p:poll interval="10" listener="#{liveFeedBean.rafraichir}"
|
||||
update="form-livefeed" autoStart="true" />
|
||||
|
||||
<p:dataTable value="#{liveFeedBean.operations}" var="o"
|
||||
emptyMessage="Aucune opération récente"
|
||||
rowIndexVar="idx">
|
||||
<p:column headerText="#" style="width: 60px;">
|
||||
<h:outputText value="#{idx + 1}" />
|
||||
</p:column>
|
||||
<p:column headerText="Quand">
|
||||
<h:outputText value="#{liveFeedBean.tempsRelatif(o.operationAt)}"
|
||||
styleClass="text-700 font-semibold" />
|
||||
<br/>
|
||||
<small class="text-500">#{o.operationAt}</small>
|
||||
</p:column>
|
||||
<p:column headerText="Acteur">
|
||||
<h:outputText value="#{o.userEmail}" />
|
||||
<br/>
|
||||
<small class="text-500" jsf:rendered="#{o.roleActif != null}">
|
||||
#{o.roleActif}
|
||||
</small>
|
||||
</p:column>
|
||||
<p:column headerText="Action">
|
||||
<p:tag value="#{o.actionType}"
|
||||
severity="#{liveFeedBean.getCouleurAction(o.actionType)}" />
|
||||
</p:column>
|
||||
<p:column headerText="Entité">
|
||||
<h:outputText value="#{o.entityType}" />
|
||||
</p:column>
|
||||
<p:column headerText="Description">
|
||||
<h:outputText value="#{o.description}" />
|
||||
</p:column>
|
||||
<p:column headerText="SoD">
|
||||
<p:tag value="#{o.sodCheckPassed ? 'OK' : 'VIOLATION'}"
|
||||
severity="#{liveFeedBean.getCouleurSod(o.sodCheckPassed)}"
|
||||
rendered="#{o.sodCheckPassed != null}" />
|
||||
<h:outputText value="—" rendered="#{o.sodCheckPassed == null}" />
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,125 @@
|
||||
<!DOCTYPE html>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:define name="title">UnionFlow - Rapports trimestriels Contrôleur Interne</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="text-900 font-medium text-3xl m-0">Rapports trimestriels — Contrôleur Interne</h2>
|
||||
<p class="text-600 mt-1 mb-0">
|
||||
Source AG, inspections BCEAO/ARTCI. Cycle DRAFT → SIGNE → ARCHIVE.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h:form id="form-rapports">
|
||||
<p:messages closable="true" />
|
||||
|
||||
<!-- Filtres + génération -->
|
||||
<p:panel header="Génération rapport" styleClass="mb-3">
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-3">
|
||||
<p:outputLabel for="annee" value="Année" />
|
||||
<p:inputNumber id="annee"
|
||||
value="#{rapportsTrimestrielsBean.annee}"
|
||||
minValue="2024" maxValue="2099"
|
||||
thousandSeparator=""
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-3">
|
||||
<p:outputLabel for="trim" value="Trimestre" />
|
||||
<p:selectOneMenu id="trim"
|
||||
value="#{rapportsTrimestrielsBean.trimestreCible}"
|
||||
styleClass="w-full">
|
||||
<f:selectItem itemLabel="T1 (Jan-Mar)" itemValue="1" />
|
||||
<f:selectItem itemLabel="T2 (Avr-Jun)" itemValue="2" />
|
||||
<f:selectItem itemLabel="T3 (Jul-Sep)" itemValue="3" />
|
||||
<f:selectItem itemLabel="T4 (Oct-Déc)" itemValue="4" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
<div class="col-12 md:col-3 flex align-items-end">
|
||||
<p:commandButton value="Générer"
|
||||
icon="pi pi-plus"
|
||||
action="#{rapportsTrimestrielsBean.genererRapport}"
|
||||
update="form-rapports"
|
||||
styleClass="w-full" />
|
||||
</div>
|
||||
<div class="col-12 md:col-3 flex align-items-end">
|
||||
<p:commandButton value="Rafraîchir"
|
||||
icon="pi pi-refresh"
|
||||
action="#{rapportsTrimestrielsBean.rafraichir}"
|
||||
update="form-rapports"
|
||||
styleClass="p-button-outlined w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</p:panel>
|
||||
|
||||
<!-- Tableau -->
|
||||
<p:dataTable id="tbl-rapports"
|
||||
value="#{rapportsTrimestrielsBean.rapports}"
|
||||
var="r"
|
||||
selection="#{rapportsTrimestrielsBean.selection}"
|
||||
rowKey="#{r.id}"
|
||||
selectionMode="single"
|
||||
emptyMessage="Aucun rapport pour cette année"
|
||||
styleClass="mb-3">
|
||||
|
||||
<p:column headerText="Trimestre">
|
||||
<h:outputText value="T#{r.trimestre} / #{r.annee}" />
|
||||
</p:column>
|
||||
|
||||
<p:column headerText="Date génération">
|
||||
<h:outputText value="#{r.dateGeneration}" />
|
||||
</p:column>
|
||||
|
||||
<p:column headerText="Score">
|
||||
<p:tag value="#{r.scoreConformite}/100"
|
||||
severity="#{r.scoreConformite ge 80 ? 'success' : (r.scoreConformite ge 60 ? 'warning' : 'danger')}" />
|
||||
</p:column>
|
||||
|
||||
<p:column headerText="Statut">
|
||||
<p:tag value="#{r.statut}"
|
||||
severity="#{r.statut eq 'ARCHIVE' ? 'success' : (r.statut eq 'SIGNE' ? 'info' : 'warning')}" />
|
||||
</p:column>
|
||||
|
||||
<p:column headerText="Hash signature">
|
||||
<h:outputText value="#{r.hashSha256.substring(0, 16)}..." rendered="#{r.hashSha256 != null}" />
|
||||
<h:outputText value="—" rendered="#{r.hashSha256 == null}" />
|
||||
</p:column>
|
||||
|
||||
<p:column headerText="Actions" style="width: 120px;">
|
||||
<p:commandButton icon="pi pi-download"
|
||||
title="Télécharger PDF"
|
||||
action="#{rapportsTrimestrielsBean.telechargerPdf(r)}"
|
||||
ajax="false"
|
||||
styleClass="p-button-text" />
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<p:commandButton value="Signer (sélection)"
|
||||
icon="pi pi-check"
|
||||
action="#{rapportsTrimestrielsBean.signerSelection(userSession.currentUser != null ? userSession.currentUser.id : null)}"
|
||||
update="form-rapports"
|
||||
styleClass="p-button-success" />
|
||||
<p:commandButton value="Archiver (sélection)"
|
||||
icon="pi pi-lock"
|
||||
action="#{rapportsTrimestrielsBean.archiverSelection}"
|
||||
update="form-rapports"
|
||||
styleClass="p-button-secondary" />
|
||||
</div>
|
||||
</h:form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/templates/main-template.xhtml">
|
||||
|
||||
<ui:define name="title">UnionFlow - Bilan détaillé événement</ui:define>
|
||||
|
||||
<ui:define name="content">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="flex justify-content-between align-items-center mb-3">
|
||||
<h2 class="text-900 font-medium text-3xl m-0">Bilan détaillé</h2>
|
||||
<p:button value="Retour aux bilans" icon="pi pi-arrow-left"
|
||||
outcome="#{paths.evenementBilan}"
|
||||
styleClass="p-button-outlined" />
|
||||
</div>
|
||||
|
||||
<p:messages />
|
||||
|
||||
<p:panel header="Page en construction" styleClass="surface-100">
|
||||
<p class="text-700">
|
||||
La vue détaillée du bilan d'un événement (statistiques participants,
|
||||
recettes/dépenses, indicateurs satisfaction) est en cours de développement.
|
||||
</p>
|
||||
<p class="text-600 mt-2">
|
||||
En attendant, consulte la liste des bilans pour la vue synthétique.
|
||||
</p>
|
||||
</p:panel>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
@@ -170,7 +170,7 @@
|
||||
<p:commandButton value="Voir bilan"
|
||||
icon="pi pi-chart-bar"
|
||||
styleClass="ui-button-rounded ui-button-text ui-button-secondary"
|
||||
outcome="/pages/secure/evenement/bilan-detail" />
|
||||
outcome="#{paths.evenementBilanDetail}" />
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
</h:form>
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
<div class="flex gap-2">
|
||||
<p:commandButton value="Voir détails"
|
||||
icon="pi pi-eye"
|
||||
outcome="/pages/secure/evenement/gestion.xhtml"
|
||||
outcome="#{paths.evenementGestion}"
|
||||
styleClass="p-button-outlined" />
|
||||
<p:commandButton value="Fermer"
|
||||
icon="pi pi-times"
|
||||
|
||||
@@ -616,7 +616,7 @@
|
||||
</div>
|
||||
<div style="font-weight:700;color:var(--text-color);">
|
||||
<h:link value="#{organisationDetailBean.organisation.organisationParenteNom}"
|
||||
outcome="/pages/secure/organisation/detail"
|
||||
outcome="#{paths.organisationDetail}"
|
||||
styleClass="text-cyan-700 font-bold no-underline hover:underline">
|
||||
<f:param name="id" value="#{organisationDetailBean.organisation.organisationParenteId}" />
|
||||
</h:link>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<p:button value="Nouvelle organisation"
|
||||
icon="pi pi-plus"
|
||||
styleClass="ui-button-success"
|
||||
outcome="/pages/secure/organisation/nouvelle" />
|
||||
outcome="#{paths.organisationNouvelle}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<p:button value="Retour au tableau de bord"
|
||||
icon="pi pi-arrow-left"
|
||||
outcome="/pages/secure/dashboard"/>
|
||||
outcome="#{paths.dashboard}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -28,37 +28,37 @@
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- TABLEAU DE BORD -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:menuitem id="m_dashboard" value="Tableau de Bord" icon="pi pi-home" outcome="/pages/secure/dashboard" />
|
||||
<p:menuitem id="m_dashboard" value="Tableau de Bord" icon="pi pi-home" outcome="#{paths.dashboard}" />
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- SUPER ADMINISTRATION (SUPER_ADMIN uniquement) -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_super_admin" label="Super Administration" icon="pi pi-shield" rendered="#{menuBean.superAdminMenuVisible}">
|
||||
<p:menuitem id="m_super_dashboard" value="Dashboard Super-Admin" icon="pi pi-chart-bar" outcome="/pages/super-admin/dashboard" />
|
||||
<p:menuitem id="m_roles" value="Rôles Applicatifs" icon="pi pi-key" outcome="/pages/super-admin/roles/gestion" />
|
||||
<p:menuitem id="m_audit" value="Journal d'Audit" icon="pi pi-file-o" outcome="/pages/admin/audit/journal" />
|
||||
<p:menuitem id="m_logs_systeme" value="Logs Système" icon="pi pi-list" outcome="/pages/admin/logs/systeme" />
|
||||
<p:menuitem id="m_sauvegardes" value="Sauvegardes" icon="pi pi-save" outcome="/pages/secure/admin/sauvegarde" />
|
||||
<p:menuitem id="m_config_systeme" value="Configuration Système" icon="pi pi-cog" outcome="/pages/super-admin/configuration/systeme" />
|
||||
<p:menuitem id="m_super_dashboard" value="Dashboard Super-Admin" icon="pi pi-chart-bar" outcome="#{paths.superAdminDashboard}" />
|
||||
<p:menuitem id="m_roles" value="Rôles Applicatifs" icon="pi pi-key" outcome="#{paths.superAdminRolesGestion}" />
|
||||
<p:menuitem id="m_audit" value="Journal d'Audit" icon="pi pi-file-o" outcome="#{paths.adminAuditJournal}" />
|
||||
<p:menuitem id="m_logs_systeme" value="Logs Système" icon="pi pi-list" outcome="#{paths.adminLogsSysteme}" />
|
||||
<p:menuitem id="m_sauvegardes" value="Sauvegardes" icon="pi pi-save" outcome="#{paths.adminSauvegarde}" />
|
||||
<p:menuitem id="m_config_systeme" value="Configuration Système" icon="pi pi-cog" outcome="#{paths.superAdminConfigurationSysteme}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- ORGANISATIONS -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_organisations" label="Organisations" icon="pi pi-building" rendered="#{menuBean.superAdmin}">
|
||||
<p:menuitem id="m_liste_organisations" value="Liste des Organisations" icon="pi pi-list" outcome="/pages/secure/organisation/liste" />
|
||||
<p:menuitem id="m_nouvelle_organisation" value="Nouvelle Organisation" icon="pi pi-plus" outcome="/pages/secure/organisation/nouvelle" />
|
||||
<p:menuitem id="m_statistiques_orga" value="Statistiques" icon="pi pi-chart-bar" outcome="/pages/secure/organisation/statistiques" />
|
||||
<p:menuitem id="m_types_organisations" value="Types d'Organisation" icon="pi pi-tags" outcome="/pages/super-admin/types/organisations" />
|
||||
<p:menuitem id="m_liste_organisations" value="Liste des Organisations" icon="pi pi-list" outcome="#{paths.organisationListe}" />
|
||||
<p:menuitem id="m_nouvelle_organisation" value="Nouvelle Organisation" icon="pi pi-plus" outcome="#{paths.organisationNouvelle}" />
|
||||
<p:menuitem id="m_statistiques_orga" value="Statistiques" icon="pi pi-chart-bar" outcome="#{paths.organisationStatistiques}" />
|
||||
<p:menuitem id="m_types_organisations" value="Types d'Organisation" icon="pi pi-tags" outcome="#{paths.superAdminTypesOrganisations}" />
|
||||
</p:submenu>
|
||||
|
||||
<p:submenu id="m_mon_organisation" label="Mon Organisation" icon="pi pi-building" rendered="#{menuBean.adminOrganisation and not menuBean.superAdmin}">
|
||||
<p:menuitem id="m_detail_organisation" value="Détail & Paramètres" icon="pi pi-building"
|
||||
outcome="/pages/secure/organisation/detail">
|
||||
outcome="#{paths.organisationDetail}">
|
||||
<f:param name="id" value="#{userSession.activeOrganisationId}" />
|
||||
</p:menuitem>
|
||||
<p:menuitem id="m_mon_abonnement" value="Mon Abonnement" icon="pi pi-credit-card"
|
||||
outcome="/pages/secure/souscription/dashboard" />
|
||||
outcome="#{paths.souscriptionDashboard}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
@@ -66,64 +66,77 @@
|
||||
<!-- Un seul sous-menu, items conditionnels par rôle -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_membres" label="Membres" icon="pi pi-users" rendered="#{menuBean.annuaireMembresVisible}">
|
||||
<p:menuitem id="m_liste_membres" value="Annuaire des Membres" icon="pi pi-list" outcome="/pages/secure/membre/liste" />
|
||||
<p:menuitem id="m_recherche_membres" value="Rechercher un Membre" icon="pi pi-search" outcome="/pages/secure/membre/recherche" />
|
||||
<p:menuitem id="m_inscription" value="Nouvelle Inscription" icon="pi pi-user-plus" outcome="/pages/secure/membre/inscription" rendered="#{menuBean.gestionMembresMenuVisible}" />
|
||||
<p:menuitem id="m_validation_membres" value="Validation Inscriptions" icon="pi pi-check-circle" outcome="/pages/secure/membre/validation" rendered="#{menuBean.gestionMembresMenuVisible}" />
|
||||
<p:menuitem id="m_import_membres" value="Import en Masse" icon="pi pi-upload" outcome="/pages/secure/membre/import" rendered="#{menuBean.gestionMembresMenuVisible}" />
|
||||
<p:menuitem id="m_export_membres" value="Export Membres" icon="pi pi-download" outcome="/pages/secure/membre/export" rendered="#{menuBean.gestionMembresMenuVisible}" />
|
||||
<p:menuitem id="m_liste_membres" value="Annuaire des Membres" icon="pi pi-list" outcome="#{paths.membreListe}" />
|
||||
<p:menuitem id="m_recherche_membres" value="Rechercher un Membre" icon="pi pi-search" outcome="#{paths.membreRecherche}" />
|
||||
<p:menuitem id="m_inscription" value="Nouvelle Inscription" icon="pi pi-user-plus" outcome="#{paths.membreInscription}" rendered="#{menuBean.gestionMembresMenuVisible}" />
|
||||
<p:menuitem id="m_validation_membres" value="Validation Inscriptions" icon="pi pi-check-circle" outcome="#{paths.membreValidation}" rendered="#{menuBean.gestionMembresMenuVisible}" />
|
||||
<p:menuitem id="m_import_membres" value="Import en Masse" icon="pi pi-upload" outcome="#{paths.membreImport}" rendered="#{menuBean.gestionMembresMenuVisible}" />
|
||||
<p:menuitem id="m_export_membres" value="Export Membres" icon="pi pi-download" outcome="#{paths.membreExport}" rendered="#{menuBean.gestionMembresMenuVisible}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- ADHÉSIONS -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_adhesions" label="Adhésions" icon="pi pi-bookmark" rendered="#{menuBean.adhesionsMenuVisible}">
|
||||
<p:menuitem id="m_demande_adhesion" value="Nouvelle Demande" icon="pi pi-plus-circle" outcome="/pages/secure/adhesion/demande" />
|
||||
<p:menuitem id="m_liste_adhesions" value="Toutes les Adhésions" icon="pi pi-list" outcome="/pages/secure/adhesion/liste" />
|
||||
<p:menuitem id="m_validation_adhesion" value="Validation des Demandes" icon="pi pi-check-circle" outcome="/pages/secure/adhesion/validation" rendered="#{menuBean.validationAdhesionVisible}" />
|
||||
<p:menuitem id="m_renouvellement" value="Renouvellements" icon="pi pi-refresh" outcome="/pages/secure/adhesion/renouvellement" />
|
||||
<p:menuitem id="m_historique_adhesions" value="Historique" icon="pi pi-history" outcome="/pages/secure/adhesion/historique" />
|
||||
<p:menuitem id="m_demande_adhesion" value="Nouvelle Demande" icon="pi pi-plus-circle" outcome="#{paths.adhesionDemande}" />
|
||||
<p:menuitem id="m_liste_adhesions" value="Toutes les Adhésions" icon="pi pi-list" outcome="#{paths.adhesionListe}" />
|
||||
<p:menuitem id="m_validation_adhesion" value="Validation des Demandes" icon="pi pi-check-circle" outcome="#{paths.adhesionValidation}" rendered="#{menuBean.validationAdhesionVisible}" />
|
||||
<p:menuitem id="m_renouvellement" value="Renouvellements" icon="pi pi-refresh" outcome="#{paths.adhesionRenouvellement}" />
|
||||
<p:menuitem id="m_historique_adhesions" value="Historique" icon="pi pi-history" outcome="#{paths.adhesionHistorique}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- MES FINANCES (perso — tous les membres) -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_mes_finances" label="Mes Finances" icon="pi pi-wallet" rendered="#{menuBean.mesFinancesMenuVisible}">
|
||||
<p:menuitem id="m_mes_cotisations" value="Mes Cotisations" icon="pi pi-credit-card" outcome="/pages/secure/membre/cotisations" />
|
||||
<p:menuitem id="m_payer_cotisations" value="Payer mes Cotisations" icon="pi pi-dollar" outcome="/pages/secure/cotisation/paiement" />
|
||||
<p:menuitem id="m_historique_finances" value="Historique" icon="pi pi-history" outcome="/pages/secure/cotisation/historique" />
|
||||
<p:menuitem id="m_mes_cotisations" value="Mes Cotisations" icon="pi pi-credit-card" outcome="#{paths.membreCotisations}" />
|
||||
<p:menuitem id="m_payer_cotisations" value="Payer mes Cotisations" icon="pi pi-dollar" outcome="#{paths.cotisationPaiement}" />
|
||||
<p:menuitem id="m_historique_finances" value="Historique" icon="pi pi-history" outcome="#{paths.cotisationHistorique}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- GESTION FINANCIÈRE (admin — trésorier, admin org) -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_gestion_finances" label="Gestion Financière" icon="pi pi-dollar" rendered="#{menuBean.gestionFinancesMenuVisible}">
|
||||
<p:menuitem id="m_gestion_cotisations" value="Gestion Cotisations" icon="pi pi-dollar" outcome="/pages/admin/cotisations/gestion" />
|
||||
<p:menuitem id="m_tresorerie" value="Trésorerie" icon="pi pi-wallet" outcome="/pages/secure/finance/tresorerie" />
|
||||
<p:menuitem id="m_budgets" value="Gestion des Budgets" icon="pi pi-chart-pie" outcome="/pages/secure/finance/budgets" />
|
||||
<p:menuitem id="m_approbations_finance" value="Approbations" icon="pi pi-check-square" outcome="/pages/secure/finance/approbations" />
|
||||
<p:menuitem id="m_comptabilite" value="Comptabilité" icon="pi pi-calculator" outcome="/pages/secure/comptabilite/gestion" />
|
||||
<p:menuitem id="m_relances" value="Relances Cotisations" icon="pi pi-bell" outcome="/pages/secure/cotisation/relances" />
|
||||
<p:menuitem id="m_bilans" value="Bilans Financiers" icon="pi pi-chart-line" outcome="/pages/secure/finance/bilans" />
|
||||
<p:menuitem id="m_gestion_cotisations" value="Gestion Cotisations" icon="pi pi-dollar" outcome="#{paths.cotisationsGestionAdmin}" />
|
||||
<p:menuitem id="m_tresorerie" value="Trésorerie" icon="pi pi-wallet" outcome="#{paths.financeTresorerie}" />
|
||||
<p:menuitem id="m_budgets" value="Gestion des Budgets" icon="pi pi-chart-pie" outcome="#{paths.financeBudgets}" />
|
||||
<p:menuitem id="m_approbations_finance" value="Approbations" icon="pi pi-check-square" outcome="#{paths.financeApprobations}" />
|
||||
<p:menuitem id="m_comptabilite" value="Comptabilité" icon="pi pi-calculator" outcome="#{paths.comptabiliteGestion}" />
|
||||
<p:menuitem id="m_relances" value="Relances Cotisations" icon="pi pi-bell" outcome="#{paths.cotisationRelances}" />
|
||||
<p:menuitem id="m_bilans" value="Bilans Financiers" icon="pi pi-chart-line" outcome="#{paths.financeBilans}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- CONFORMITÉ (Sprint 8 — BCEAO / ARTCI / OHADA) -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_conformite" label="Conformité" icon="pi pi-verified" rendered="#{menuBean.conformiteDashboardVisible}">
|
||||
<p:menuitem id="m_conformite_dashboard" value="Tableau de bord" icon="pi pi-chart-bar" outcome="#{paths.conformiteDashboard}" />
|
||||
<p:menuitem id="m_rapports_trimestriels" value="Rapports trimestriels" icon="pi pi-file-pdf" outcome="#{paths.conformiteRapportsTrimestriels}" rendered="#{menuBean.rapportsTrimestrielsVisible}" />
|
||||
<p:menuitem id="m_ubo" value="Bénéficiaires Effectifs" icon="pi pi-users" outcome="#{paths.conformiteBeneficiairesEffectifs}" rendered="#{menuBean.beneficiairesEffectifsVisible}" />
|
||||
<p:menuitem id="m_audit_trail" value="Audit Trail" icon="pi pi-history" outcome="#{paths.conformiteAuditTrail}" rendered="#{menuBean.auditTrailViewerVisible}" />
|
||||
<p:menuitem id="m_live_feed" value="Live Activity Feed" icon="pi pi-bolt" outcome="#{paths.conformiteLiveFeed}" />
|
||||
<p:menuitem id="m_role_delegations" value="Délégations de rôles" icon="pi pi-share-alt" outcome="#{paths.adminRoleDelegations}" rendered="#{menuBean.roleDelegationsVisible}" />
|
||||
<p:menuitem id="m_pispi_readiness" value="PI-SPI Readiness" icon="pi pi-cog" outcome="#{paths.adminPispiReadiness}" rendered="#{menuBean.pispiReadinessVisible}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- ÉPARGNE (module EPARGNE — mutuelle, coopérative) -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_epargne" label="Épargne & Crédit" icon="pi pi-wallet" rendered="#{menuBean.epargneMenuVisible}">
|
||||
<p:menuitem id="m_epargne_comptes" value="Comptes Épargne" icon="pi pi-wallet" outcome="/pages/secure/epargne/comptes" />
|
||||
<p:menuitem id="m_demandes_credit" value="Demandes de Crédit" icon="pi pi-inbox" outcome="/pages/secure/credit/demandes" rendered="#{menuBean.creditMenuVisible}" />
|
||||
<p:menuitem id="m_evaluation_credit" value="Évaluation Solvabilité" icon="pi pi-search" outcome="/pages/secure/credit/evaluation" rendered="#{menuBean.creditMenuVisible}" />
|
||||
<p:menuitem id="m_suivi_credits" value="Suivi des Crédits" icon="pi pi-eye" outcome="/pages/secure/credit/suivi" rendered="#{menuBean.creditMenuVisible}" />
|
||||
<p:menuitem id="m_remboursements" value="Remboursements" icon="pi pi-replay" outcome="/pages/secure/credit/remboursements" rendered="#{menuBean.creditMenuVisible}" />
|
||||
<p:menuitem id="m_stats_credit" value="Statistiques Crédit" icon="pi pi-chart-bar" outcome="/pages/secure/credit/statistiques" rendered="#{menuBean.creditMenuVisible}" />
|
||||
<p:menuitem id="m_epargne_comptes" value="Comptes Épargne" icon="pi pi-wallet" outcome="#{paths.epargneComptes}" />
|
||||
<p:menuitem id="m_demandes_credit" value="Demandes de Crédit" icon="pi pi-inbox" outcome="#{paths.creditDemandes}" rendered="#{menuBean.creditMenuVisible}" />
|
||||
<p:menuitem id="m_evaluation_credit" value="Évaluation Solvabilité" icon="pi pi-search" outcome="#{paths.creditEvaluation}" rendered="#{menuBean.creditMenuVisible}" />
|
||||
<p:menuitem id="m_suivi_credits" value="Suivi des Crédits" icon="pi pi-eye" outcome="#{paths.creditSuivi}" rendered="#{menuBean.creditMenuVisible}" />
|
||||
<p:menuitem id="m_remboursements" value="Remboursements" icon="pi pi-replay" outcome="#{paths.creditRemboursements}" rendered="#{menuBean.creditMenuVisible}" />
|
||||
<p:menuitem id="m_stats_credit" value="Statistiques Crédit" icon="pi pi-chart-bar" outcome="#{paths.creditStatistiques}" rendered="#{menuBean.creditMenuVisible}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- Mon Épargne (membre — visible si pas admin épargne) -->
|
||||
<p:submenu id="m_mon_epargne" label="Mon Épargne" icon="pi pi-wallet" rendered="#{menuBean.epargneMemberVisible and not menuBean.epargneMenuVisible}">
|
||||
<p:menuitem id="m_mon_epargne_compte" value="Mon Compte Épargne" icon="pi pi-wallet" outcome="/pages/secure/epargne/comptes" />
|
||||
<p:menuitem id="m_demande_pret" value="Demander un Prêt" icon="pi pi-plus" outcome="/pages/secure/credit/demandes" rendered="#{menuBean.creditMemberVisible}" />
|
||||
<p:menuitem id="m_mes_credits" value="Mes Crédits en Cours" icon="pi pi-list" outcome="/pages/secure/credit/suivi" rendered="#{menuBean.creditMemberVisible}" />
|
||||
<p:menuitem id="m_mon_epargne_compte" value="Mon Compte Épargne" icon="pi pi-wallet" outcome="#{paths.epargneComptes}" />
|
||||
<p:menuitem id="m_demande_pret" value="Demander un Prêt" icon="pi pi-plus" outcome="#{paths.creditDemandes}" rendered="#{menuBean.creditMemberVisible}" />
|
||||
<p:menuitem id="m_mes_credits" value="Mes Crédits en Cours" icon="pi pi-list" outcome="#{paths.creditSuivi}" rendered="#{menuBean.creditMemberVisible}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
@@ -163,70 +176,70 @@
|
||||
<!-- AIDE SOCIALE -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_mes_aides" label="Aide Sociale" icon="pi pi-heart" rendered="#{menuBean.mesAidesSocialesMenuVisible}">
|
||||
<p:menuitem id="m_demande_aide" value="Faire une Demande" icon="pi pi-plus" outcome="/pages/secure/aide/demande" />
|
||||
<p:menuitem id="m_mes_demandes_aide" value="Mes Demandes" icon="pi pi-list" outcome="/pages/secure/aide/requests" />
|
||||
<p:menuitem id="m_historique_aides" value="Historique" icon="pi pi-clock" outcome="/pages/secure/aide/historique" />
|
||||
<p:menuitem id="m_traitement_aide" value="Traitement des Demandes" icon="pi pi-cog" outcome="/pages/secure/aide/traitement" rendered="#{menuBean.gestionAidesSocialesMenuVisible}" />
|
||||
<p:menuitem id="m_suivi_aide" value="Suivi des Bénéficiaires" icon="pi pi-eye" outcome="/pages/secure/aide/approved" rendered="#{menuBean.gestionAidesSocialesMenuVisible}" />
|
||||
<p:menuitem id="m_statistiques_aides" value="Statistiques Sociales" icon="pi pi-chart-line" outcome="/pages/secure/aide/statistiques" rendered="#{menuBean.gestionAidesSocialesMenuVisible}" />
|
||||
<p:menuitem id="m_demande_aide" value="Faire une Demande" icon="pi pi-plus" outcome="#{paths.aideDemande}" />
|
||||
<p:menuitem id="m_mes_demandes_aide" value="Mes Demandes" icon="pi pi-list" outcome="#{paths.aideRequests}" />
|
||||
<p:menuitem id="m_historique_aides" value="Historique" icon="pi pi-clock" outcome="#{paths.aideHistorique}" />
|
||||
<p:menuitem id="m_traitement_aide" value="Traitement des Demandes" icon="pi pi-cog" outcome="#{paths.aideTraitement}" rendered="#{menuBean.gestionAidesSocialesMenuVisible}" />
|
||||
<p:menuitem id="m_suivi_aide" value="Suivi des Bénéficiaires" icon="pi pi-eye" outcome="#{paths.aideApproved}" rendered="#{menuBean.gestionAidesSocialesMenuVisible}" />
|
||||
<p:menuitem id="m_statistiques_aides" value="Statistiques Sociales" icon="pi pi-chart-line" outcome="#{paths.aideStatistiques}" rendered="#{menuBean.gestionAidesSocialesMenuVisible}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- ÉVÉNEMENTS -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_evenements" label="Événements" icon="pi pi-calendar" rendered="#{menuBean.mesEvenementsMenuVisible}">
|
||||
<p:menuitem id="m_calendrier" value="Calendrier" icon="pi pi-calendar-plus" outcome="/pages/secure/evenement/calendrier" />
|
||||
<p:menuitem id="m_mes_inscriptions_events" value="Mes Inscriptions" icon="pi pi-list" outcome="/pages/secure/evenement/participants" />
|
||||
<p:menuitem id="m_creation_evenement" value="Nouvel Événement" icon="pi pi-plus" outcome="/pages/secure/evenement/creation" rendered="#{menuBean.gestionEvenementsMenuVisible}" />
|
||||
<p:menuitem id="m_planification" value="Planification" icon="pi pi-clock" outcome="/pages/secure/evenement/planification" rendered="#{menuBean.gestionEvenementsMenuVisible}" />
|
||||
<p:menuitem id="m_logistique" value="Logistique" icon="pi pi-truck" outcome="/pages/secure/evenement/logistique" rendered="#{menuBean.gestionEvenementsMenuVisible}" />
|
||||
<p:menuitem id="m_gestion_generale_evenements" value="Gestion Générale" icon="pi pi-cog" outcome="/pages/secure/evenement/gestion" rendered="#{menuBean.gestionEvenementsMenuVisible}" />
|
||||
<p:menuitem id="m_bilan_evenements" value="Bilans" icon="pi pi-chart-bar" outcome="/pages/secure/evenement/bilan" rendered="#{menuBean.gestionEvenementsMenuVisible}" />
|
||||
<p:menuitem id="m_calendrier" value="Calendrier" icon="pi pi-calendar-plus" outcome="#{paths.evenementCalendrier}" />
|
||||
<p:menuitem id="m_mes_inscriptions_events" value="Mes Inscriptions" icon="pi pi-list" outcome="#{paths.evenementParticipants}" />
|
||||
<p:menuitem id="m_creation_evenement" value="Nouvel Événement" icon="pi pi-plus" outcome="#{paths.evenementCreation}" rendered="#{menuBean.gestionEvenementsMenuVisible}" />
|
||||
<p:menuitem id="m_planification" value="Planification" icon="pi pi-clock" outcome="#{paths.evenementPlanification}" rendered="#{menuBean.gestionEvenementsMenuVisible}" />
|
||||
<p:menuitem id="m_logistique" value="Logistique" icon="pi pi-truck" outcome="#{paths.evenementLogistique}" rendered="#{menuBean.gestionEvenementsMenuVisible}" />
|
||||
<p:menuitem id="m_gestion_generale_evenements" value="Gestion Générale" icon="pi pi-cog" outcome="#{paths.evenementGestion}" rendered="#{menuBean.gestionEvenementsMenuVisible}" />
|
||||
<p:menuitem id="m_bilan_evenements" value="Bilans" icon="pi pi-chart-bar" outcome="#{paths.evenementBilan}" rendered="#{menuBean.gestionEvenementsMenuVisible}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- COMMUNICATION -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_communications" label="Communication" icon="pi pi-envelope" rendered="#{menuBean.mesCommunicationsMenuVisible}">
|
||||
<p:menuitem id="m_messagerie" value="Messagerie" icon="pi pi-comments" outcome="/pages/secure/communication/conversations" />
|
||||
<p:menuitem id="m_mes_notifications" value="Mes Notifications" icon="pi pi-bell" outcome="/pages/secure/communication/notifications" />
|
||||
<p:menuitem id="m_messagerie" value="Messagerie" icon="pi pi-comments" outcome="#{paths.communicationConversations}" />
|
||||
<p:menuitem id="m_mes_notifications" value="Mes Notifications" icon="pi pi-bell" outcome="#{paths.communicationNotifications}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- DOCUMENTS -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_documents" label="Documents" icon="pi pi-folder" rendered="#{menuBean.documentsMenuVisible}">
|
||||
<p:menuitem id="m_mes_documents" value="Mes Documents" icon="pi pi-file" outcome="/pages/secure/documents/mes-documents" />
|
||||
<p:menuitem id="m_mes_documents" value="Mes Documents" icon="pi pi-file" outcome="#{paths.documentsMesDocuments}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- RAPPORTS ET ANALYSES -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_rapports" label="Rapports" icon="pi pi-chart-bar" rendered="#{menuBean.rapportsMenuVisible}">
|
||||
<p:menuitem id="m_tableaux_bord" value="Tableaux de Bord" icon="pi pi-chart-line" outcome="/pages/secure/rapport/tableaux-bord" />
|
||||
<p:menuitem id="m_rapport_membres" value="Rapport Membres" icon="pi pi-users" outcome="/pages/secure/rapport/membres" />
|
||||
<p:menuitem id="m_rapport_finances" value="Rapport Financier" icon="pi pi-dollar" outcome="/pages/secure/rapport/finances" rendered="#{menuBean.rapportFinancierVisible}" />
|
||||
<p:menuitem id="m_rapport_activites" value="Rapport d'Activités" icon="pi pi-chart-line" outcome="/pages/secure/rapport/activites" />
|
||||
<p:menuitem id="m_export" value="Exports" icon="pi pi-download" outcome="/pages/secure/rapport/export" rendered="#{menuBean.exportsPersonnalisesVisible}" />
|
||||
<p:menuitem id="m_tableaux_bord" value="Tableaux de Bord" icon="pi pi-chart-line" outcome="#{paths.rapportTableauxBord}" />
|
||||
<p:menuitem id="m_rapport_membres" value="Rapport Membres" icon="pi pi-users" outcome="#{paths.rapportMembres}" />
|
||||
<p:menuitem id="m_rapport_finances" value="Rapport Financier" icon="pi pi-dollar" outcome="#{paths.rapportFinances}" rendered="#{menuBean.rapportFinancierVisible}" />
|
||||
<p:menuitem id="m_rapport_activites" value="Rapport d'Activités" icon="pi pi-chart-line" outcome="#{paths.rapportActivites}" />
|
||||
<p:menuitem id="m_export" value="Exports" icon="pi pi-download" outcome="#{paths.rapportExport}" rendered="#{menuBean.exportsPersonnalisesVisible}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- MON ESPACE PERSONNEL -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_personnel" label="Mon Espace" icon="pi pi-user">
|
||||
<p:menuitem id="m_mon_profil" value="Mon Profil" icon="pi pi-user-edit" outcome="/pages/secure/personnel/profil" />
|
||||
<p:menuitem id="m_mes_preferences" value="Mes Préférences" icon="pi pi-cog" outcome="/pages/secure/personnel/preferences" />
|
||||
<p:menuitem id="m_personnel_notifications" value="Mes Notifications" icon="pi pi-bell" outcome="/pages/secure/personnel/notifications" />
|
||||
<p:menuitem id="m_parametres_compte" value="Paramètres Compte" icon="pi pi-lock" outcome="/pages/secure/personnel/parametres" />
|
||||
<p:menuitem id="m_mon_profil" value="Mon Profil" icon="pi pi-user-edit" outcome="#{paths.personnelProfil}" />
|
||||
<p:menuitem id="m_mes_preferences" value="Mes Préférences" icon="pi pi-cog" outcome="#{paths.personnelPreferences}" />
|
||||
<p:menuitem id="m_personnel_notifications" value="Mes Notifications" icon="pi pi-bell" outcome="#{paths.personnelNotifications}" />
|
||||
<p:menuitem id="m_parametres_compte" value="Paramètres Compte" icon="pi pi-lock" outcome="#{paths.personnelParametres}" />
|
||||
</p:submenu>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- AIDE ET SUPPORT -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<p:submenu id="m_aide_documentation" label="Aide" icon="pi pi-question-circle">
|
||||
<p:menuitem id="m_faq" value="Questions Fréquentes" icon="pi pi-question" outcome="/pages/secure/aide/faq" />
|
||||
<p:menuitem id="m_support" value="Contacter le Support" icon="pi pi-phone" outcome="/pages/secure/aide/support" />
|
||||
<p:menuitem id="m_apropos" value="À Propos" icon="pi pi-info" outcome="/pages/secure/aide/apropos" />
|
||||
<p:menuitem id="m_faq" value="Questions Fréquentes" icon="pi pi-question" outcome="#{paths.aideFaq}" />
|
||||
<p:menuitem id="m_support" value="Contacter le Support" icon="pi pi-phone" outcome="#{paths.aideSupport}" />
|
||||
<p:menuitem id="m_apropos" value="À Propos" icon="pi pi-info" outcome="#{paths.aideApropos}" />
|
||||
</p:submenu>
|
||||
|
||||
</fr:menu>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<a href="#" class="menu-button">
|
||||
<i class="pi pi-bars"/>
|
||||
</a>
|
||||
<h:link id="logolink" outcome="/pages/secure/dashboard" styleClass="layout-topbar-logo" style="display:none"/>
|
||||
<h:link id="logolink" outcome="#{paths.dashboard}" styleClass="layout-topbar-logo" style="display:none"/>
|
||||
</div>
|
||||
|
||||
<!-- CENTER - Menu -->
|
||||
|
||||
@@ -650,6 +650,55 @@
|
||||
</div>
|
||||
</p:fieldset>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||||
<!-- SECTION 6.5 : CONFORMITÉ (Sprint 1 — Instr. BCEAO + OHADA) -->
|
||||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||||
<p:fieldset legend="🛡️ Conformité réglementaire" toggleable="true" collapsed="true" styleClass="mb-3">
|
||||
<div class="formgrid grid">
|
||||
|
||||
<!-- Référentiel comptable -->
|
||||
<div class="field col-12 md:col-6">
|
||||
<p:outputLabel for="referentiel" value="Référentiel comptable" styleClass="font-semibold" />
|
||||
<p:selectOneMenu id="referentiel"
|
||||
value="#{model.referentielComptable}">
|
||||
<f:selectItem itemLabel="-- Auto (selon type d'organisation) --" itemValue="" />
|
||||
<f:selectItem itemLabel="SYSCOHADA — Système comptable OHADA (associations, coopératives)" itemValue="SYSCOHADA" />
|
||||
<f:selectItem itemLabel="SYCEBNL — Entités à but non lucratif (mutuelles santé, ONG)" itemValue="SYCEBNL" />
|
||||
<f:selectItem itemLabel="PCSFD_UMOA — Plan comptable SFD UEMOA (microfinance art. 44)" itemValue="PCSFD_UMOA" />
|
||||
</p:selectOneMenu>
|
||||
<p:tooltip for="referentiel" position="top"
|
||||
value="Détermine bilan, compte de résultat et annexes (Acte uniforme OHADA + BCEAO)" />
|
||||
<small class="text-500">
|
||||
<i class="pi pi-info-circle mr-1"/>
|
||||
Si vide : appliqué automatiquement selon typeOrganisation
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- Compliance Officer (picker autoComplete) -->
|
||||
<div class="field col-12 md:col-6">
|
||||
<p:outputLabel for="complianceOfficer" value="Compliance Officer"
|
||||
styleClass="font-semibold" />
|
||||
<p:autoComplete id="complianceOfficer"
|
||||
value="#{model.complianceOfficerId}"
|
||||
completeMethod="#{complianceOfficerPickerBean.suggest}"
|
||||
var="m"
|
||||
itemLabel="#{complianceOfficerPickerBean.label(m)}"
|
||||
itemValue="#{m.id}"
|
||||
forceSelection="true"
|
||||
minQueryLength="2"
|
||||
queryDelay="300"
|
||||
placeholder="Tapez 2+ lettres du nom ou prénom..." />
|
||||
<p:tooltip for="complianceOfficer" position="top"
|
||||
value="Désignation obligatoire selon Instr. BCEAO 001-03-2025 (LBC/FT). Membre rattaché à la direction, distinct du trésorier (séparation des pouvoirs)." />
|
||||
<small class="text-500">
|
||||
<i class="pi pi-shield mr-1"/>
|
||||
Instr. BCEAO 001-03-2025 — LBC/FT
|
||||
</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</p:fieldset>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||||
<!-- SECTION 7 : MISSION & ACTIVITÉS -->
|
||||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package dev.lions.unionflow.client.api.dto;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class RapportTrimestrielDtoTest {
|
||||
|
||||
private RapportTrimestrielDto build(String statut) {
|
||||
return new RapportTrimestrielDto(
|
||||
UUID.randomUUID(), UUID.randomUUID(), 2026, 1,
|
||||
LocalDateTime.now(), statut, 75, null, null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("estDraft / estSigne / estArchive — DRAFT")
|
||||
void draft() {
|
||||
var r = build("DRAFT");
|
||||
assertTrue(r.estDraft());
|
||||
assertFalse(r.estSigne());
|
||||
assertFalse(r.estArchive());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("estDraft / estSigne / estArchive — SIGNE")
|
||||
void signe() {
|
||||
var r = build("SIGNE");
|
||||
assertFalse(r.estDraft());
|
||||
assertTrue(r.estSigne());
|
||||
assertFalse(r.estArchive());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("estDraft / estSigne / estArchive — ARCHIVE")
|
||||
void archive() {
|
||||
var r = build("ARCHIVE");
|
||||
assertFalse(r.estDraft());
|
||||
assertFalse(r.estSigne());
|
||||
assertTrue(r.estArchive());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Statut inconnu → tous false")
|
||||
void inconnu() {
|
||||
var r = build("AUTRE");
|
||||
assertFalse(r.estDraft());
|
||||
assertFalse(r.estSigne());
|
||||
assertFalse(r.estArchive());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package dev.lions.unionflow.client.constants;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Vérifie que chaque constante {@link ViewPaths} pointe vers un fichier xhtml existant
|
||||
* sous {@code META-INF/resources/}.
|
||||
*
|
||||
* <p>Garantie de non-régression : tout outcome utilisé en navigation existe physiquement.
|
||||
*/
|
||||
class ViewPathsConsistencyTest {
|
||||
|
||||
/** Constantes auxquelles aucun fichier physique n'est attaché (suffixes / racines techniques). */
|
||||
private static final java.util.Set<String> NON_FILE_CONSTANTS = java.util.Set.of(
|
||||
"REDIRECT_SUFFIX", "ROOT", "INDEX");
|
||||
|
||||
/**
|
||||
* Pages référencées par le code mais pas encore implémentées.
|
||||
* Sprint 13.B a créé les 3 stubs détectés par Sprint 12 — set vide.
|
||||
*/
|
||||
private static final java.util.Set<String> KNOWN_MISSING_PAGES = java.util.Set.of();
|
||||
|
||||
@Test
|
||||
@DisplayName("Chaque constante ViewPaths pointe vers un .xhtml existant en classpath")
|
||||
void chaquePathExisteCommeFichier() throws Exception {
|
||||
List<String> manquants = new ArrayList<>();
|
||||
|
||||
for (Field f : ViewPaths.class.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(f.getModifiers())
|
||||
|| !Modifier.isFinal(f.getModifiers())
|
||||
|| !f.getType().equals(String.class)) {
|
||||
continue;
|
||||
}
|
||||
String name = f.getName();
|
||||
if (NON_FILE_CONSTANTS.contains(name)) continue;
|
||||
if (KNOWN_MISSING_PAGES.contains(name)) continue; // dette pré-Sprint 12
|
||||
|
||||
String path = (String) f.get(null);
|
||||
String resourcePath = "META-INF/resources" + path + ".xhtml";
|
||||
URL url = Thread.currentThread().getContextClassLoader().getResource(resourcePath);
|
||||
if (url == null) {
|
||||
manquants.add(name + " → " + resourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!manquants.isEmpty()) {
|
||||
fail("Paths sans fichier xhtml correspondant :\n " + String.join("\n ", manquants));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("REDIRECT_SUFFIX commence par '?'")
|
||||
void redirectSuffixFormat() {
|
||||
assertNotNull(ViewPaths.REDIRECT_SUFFIX);
|
||||
assertTrue(ViewPaths.REDIRECT_SUFFIX.startsWith("?"));
|
||||
assertTrue(ViewPaths.REDIRECT_SUFFIX.contains("faces-redirect=true"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Aucune constante n'est null ou blanche")
|
||||
void aucuneConstanteVide() throws Exception {
|
||||
for (Field f : ViewPaths.class.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(f.getModifiers())
|
||||
|| !f.getType().equals(String.class)) continue;
|
||||
String value = (String) f.get(null);
|
||||
assertNotNull(value, "Constante " + f.getName() + " null");
|
||||
assertTrue(!value.isBlank(), "Constante " + f.getName() + " vide");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class AuditTrailViewerBeanTest {
|
||||
|
||||
private AuditTrailViewerBean bean;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
bean = new AuditTrailViewerBean();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleurAction — DELETE → danger")
|
||||
void couleurDelete() {
|
||||
assertEquals("danger", bean.getCouleurAction("DELETE"));
|
||||
assertEquals("danger", bean.getCouleurAction("PAYMENT_FAILED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleurAction — VALIDATE / payment confirmé / aide approuvée → success")
|
||||
void couleurSuccess() {
|
||||
assertEquals("success", bean.getCouleurAction("VALIDATE"));
|
||||
assertEquals("success", bean.getCouleurAction("PAYMENT_CONFIRMED"));
|
||||
assertEquals("success", bean.getCouleurAction("AID_REQUEST_APPROVED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleurAction — UPDATE / payment initié / budget approuvé → info")
|
||||
void couleurInfo() {
|
||||
assertEquals("info", bean.getCouleurAction("UPDATE"));
|
||||
assertEquals("info", bean.getCouleurAction("PAYMENT_INITIATED"));
|
||||
assertEquals("info", bean.getCouleurAction("BUDGET_APPROVED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleurAction — CREATE → primary")
|
||||
void couleurCreate() {
|
||||
assertEquals("primary", bean.getCouleurAction("CREATE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleurAction — EXPORT → warning")
|
||||
void couleurExport() {
|
||||
assertEquals("warning", bean.getCouleurAction("EXPORT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleurAction — null/inconnu → secondary")
|
||||
void couleurDefault() {
|
||||
assertEquals("secondary", bean.getCouleurAction(null));
|
||||
assertEquals("secondary", bean.getCouleurAction("AUTRE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleurSod — true → success, false → danger, null → secondary")
|
||||
void couleurSod() {
|
||||
assertEquals("success", bean.getCouleurSod(true));
|
||||
assertEquals("danger", bean.getCouleurSod(false));
|
||||
assertEquals("secondary", bean.getCouleurSod(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Defaults — mode = ORG, plage = 30 derniers jours")
|
||||
void defaults() {
|
||||
assertEquals("ORG", bean.getMode());
|
||||
// plage from 30 jours avant maintenant — vérifié pas null
|
||||
org.junit.jupiter.api.Assertions.assertNotNull(bean.getFrom());
|
||||
org.junit.jupiter.api.Assertions.assertNotNull(bean.getTo());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.membre.response.MembreSummaryResponse;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ComplianceOfficerPickerBeanTest {
|
||||
|
||||
private ComplianceOfficerPickerBean bean;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
bean = new ComplianceOfficerPickerBean();
|
||||
}
|
||||
|
||||
// ── label ────────────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
@DisplayName("label — null → vide")
|
||||
void labelNull() {
|
||||
assertEquals("", bean.label(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("label — Prénom NOM (numéro)")
|
||||
void labelComplet() {
|
||||
MembreSummaryResponse m = new MembreSummaryResponse();
|
||||
m.setPrenom("Jean Pierre");
|
||||
m.setNom("dupont");
|
||||
m.setNumeroMembre("MBR-2026-0042");
|
||||
assertEquals("Jean Pierre DUPONT (MBR-2026-0042)", bean.label(m));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("label — sans numéro membre")
|
||||
void labelSansNumero() {
|
||||
MembreSummaryResponse m = new MembreSummaryResponse();
|
||||
m.setPrenom("Marie");
|
||||
m.setNom("Kone");
|
||||
assertEquals("Marie KONE", bean.label(m));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("label — uniquement nom")
|
||||
void labelNomSeul() {
|
||||
MembreSummaryResponse m = new MembreSummaryResponse();
|
||||
m.setNom("Diallo");
|
||||
assertEquals("DIALLO", bean.label(m));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("label — uniquement prénom")
|
||||
void labelPrenomSeul() {
|
||||
MembreSummaryResponse m = new MembreSummaryResponse();
|
||||
m.setPrenom("Aminata");
|
||||
assertEquals("Aminata", bean.label(m));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("label — entité minimaliste sans nom/prénom → fallback id")
|
||||
void labelMinimaliste() {
|
||||
MembreSummaryResponse m = new MembreSummaryResponse();
|
||||
m.setId(java.util.UUID.fromString("00000000-0000-0000-0000-000000000001"));
|
||||
String label = bean.label(m);
|
||||
assertTrue(label.contains("00000000-0000-0000-0000-000000000001"));
|
||||
assertTrue(label.startsWith("(membre"));
|
||||
}
|
||||
|
||||
// ── suggest avec query vide/null ──────────────────────────────────────
|
||||
|
||||
@Test
|
||||
@DisplayName("suggest — query null → liste vide")
|
||||
void suggestNull() {
|
||||
// pas besoin de mock REST : retour direct sans appel réseau
|
||||
assertTrue(bean.suggest(null).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("suggest — query blank → liste vide")
|
||||
void suggestBlank() {
|
||||
assertTrue(bean.suggest(" ").isEmpty());
|
||||
}
|
||||
|
||||
// ── resoudre id null ──────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
@DisplayName("resoudre — UUID null → null")
|
||||
void resoudreNull() {
|
||||
assertNull(bean.resoudre(null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import dev.lions.unionflow.client.api.dto.ComplianceSnapshotDto;
|
||||
import dev.lions.unionflow.client.api.dto.ComplianceSnapshotDto.ConformiteIndicateurDto;
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests purs logique du bean conformité — pas d'invocation REST réelle.
|
||||
*
|
||||
* @since 2026-04-25 (Sprint 8)
|
||||
*/
|
||||
class ConformiteDashboardBeanTest {
|
||||
|
||||
private ConformiteDashboardBean bean;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
bean = new ConformiteDashboardBean();
|
||||
}
|
||||
|
||||
private void setSnapshot(ConformiteDashboardBean b, ComplianceSnapshotDto s) throws Exception {
|
||||
Field f = ConformiteDashboardBean.class.getDeclaredField("snapshot");
|
||||
f.setAccessible(true);
|
||||
f.set(b, s);
|
||||
}
|
||||
|
||||
private ComplianceSnapshotDto snapshotAvec(int score, boolean officer, String agStatut) {
|
||||
return new ComplianceSnapshotDto(
|
||||
UUID.randomUUID(), "Test Mutuelle", "SYSCOHADA",
|
||||
officer,
|
||||
new ConformiteIndicateurDto(agStatut, ""),
|
||||
new ConformiteIndicateurDto("OK", ""),
|
||||
3, new BigDecimal("80"), new BigDecimal("70"),
|
||||
new ConformiteIndicateurDto("OPTIONNEL", ""),
|
||||
new ConformiteIndicateurDto("EN_VEILLE", ""),
|
||||
new BigDecimal("60"),
|
||||
score);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurScore — score >=80 → success")
|
||||
void couleurSuccess() throws Exception {
|
||||
setSnapshot(bean, snapshotAvec(85, true, "OK"));
|
||||
assertEquals("success", bean.getCouleurScore());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurScore — 60..79 → warning")
|
||||
void couleurWarning() throws Exception {
|
||||
setSnapshot(bean, snapshotAvec(70, true, "OK"));
|
||||
assertEquals("warning", bean.getCouleurScore());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurScore — <60 → danger")
|
||||
void couleurDanger() throws Exception {
|
||||
setSnapshot(bean, snapshotAvec(45, true, "OK"));
|
||||
assertEquals("danger", bean.getCouleurScore());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurScore — snapshot null → secondary")
|
||||
void couleurSecondaire() {
|
||||
assertEquals("secondary", bean.getCouleurScore());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasAlertes — score 85 + officer OK + AG OK → false")
|
||||
void pasDAlertes() throws Exception {
|
||||
setSnapshot(bean, snapshotAvec(85, true, "OK"));
|
||||
assertFalse(bean.hasAlertes());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasAlertes — officer absent → true")
|
||||
void alerteOfficerAbsent() throws Exception {
|
||||
setSnapshot(bean, snapshotAvec(85, false, "OK"));
|
||||
assertTrue(bean.hasAlertes());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasAlertes — AG en RETARD → true")
|
||||
void alerteAgRetard() throws Exception {
|
||||
setSnapshot(bean, snapshotAvec(85, true, "RETARD"));
|
||||
assertTrue(bean.hasAlertes());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasAlertes — score <60 → true")
|
||||
void alerteScoreFaible() throws Exception {
|
||||
setSnapshot(bean, snapshotAvec(50, true, "OK"));
|
||||
assertTrue(bean.hasAlertes());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasAlertes — snapshot null → false")
|
||||
void hasAlertesNull() {
|
||||
assertFalse(bean.hasAlertes());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class LiveFeedBeanTest {
|
||||
|
||||
private LiveFeedBean bean;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
bean = new LiveFeedBean();
|
||||
}
|
||||
|
||||
// ── couleurAction (mêmes règles que AuditTrailViewerBean) ────────────
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurAction — DELETE/PAYMENT_FAILED → danger")
|
||||
void couleurDanger() {
|
||||
assertEquals("danger", bean.getCouleurAction("DELETE"));
|
||||
assertEquals("danger", bean.getCouleurAction("PAYMENT_FAILED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurAction — VALIDATE/PAYMENT_CONFIRMED/AID_REQUEST_APPROVED → success")
|
||||
void couleurSuccess() {
|
||||
assertEquals("success", bean.getCouleurAction("VALIDATE"));
|
||||
assertEquals("success", bean.getCouleurAction("PAYMENT_CONFIRMED"));
|
||||
assertEquals("success", bean.getCouleurAction("AID_REQUEST_APPROVED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurAction — UPDATE/PAYMENT_INITIATED/BUDGET_APPROVED → info")
|
||||
void couleurInfo() {
|
||||
assertEquals("info", bean.getCouleurAction("UPDATE"));
|
||||
assertEquals("info", bean.getCouleurAction("PAYMENT_INITIATED"));
|
||||
assertEquals("info", bean.getCouleurAction("BUDGET_APPROVED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurAction — CREATE → primary, EXPORT → warning, autres → secondary")
|
||||
void couleurAutres() {
|
||||
assertEquals("primary", bean.getCouleurAction("CREATE"));
|
||||
assertEquals("warning", bean.getCouleurAction("EXPORT"));
|
||||
assertEquals("secondary", bean.getCouleurAction(null));
|
||||
assertEquals("secondary", bean.getCouleurAction("INCONNU"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurSod — true→success, false→danger, null→secondary")
|
||||
void couleurSod() {
|
||||
assertEquals("success", bean.getCouleurSod(true));
|
||||
assertEquals("danger", bean.getCouleurSod(false));
|
||||
assertEquals("secondary", bean.getCouleurSod(null));
|
||||
}
|
||||
|
||||
// ── tempsRelatif ─────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
@DisplayName("tempsRelatif — null → '—'")
|
||||
void tempsRelatifNull() {
|
||||
assertEquals("—", bean.tempsRelatif(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("tempsRelatif — 30s ago → 'il y a 30s'")
|
||||
void tempsRelatifSecondes() {
|
||||
LocalDateTime past = LocalDateTime.now().minusSeconds(30);
|
||||
assertTrue(bean.tempsRelatif(past).startsWith("il y a "));
|
||||
assertTrue(bean.tempsRelatif(past).endsWith("s"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("tempsRelatif — 5min ago → 'il y a 5m'")
|
||||
void tempsRelatifMinutes() {
|
||||
LocalDateTime past = LocalDateTime.now().minusMinutes(5);
|
||||
assertEquals("il y a 5m", bean.tempsRelatif(past));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("tempsRelatif — 2h ago → 'il y a 2h'")
|
||||
void tempsRelatifHeures() {
|
||||
LocalDateTime past = LocalDateTime.now().minusHours(2);
|
||||
assertEquals("il y a 2h", bean.tempsRelatif(past));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("tempsRelatif — 3 jours ago → 'il y a 3j'")
|
||||
void tempsRelatifJours() {
|
||||
LocalDateTime past = LocalDateTime.now().minusDays(3);
|
||||
assertEquals("il y a 3j", bean.tempsRelatif(past));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("tempsRelatif — futur → 'à l'instant'")
|
||||
void tempsRelatifFutur() {
|
||||
LocalDateTime futur = LocalDateTime.now().plusMinutes(1);
|
||||
assertEquals("à l'instant", bean.tempsRelatif(futur));
|
||||
}
|
||||
|
||||
// ── Limit clamping ───────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
@DisplayName("setLimit clamp [1, 500]")
|
||||
void setLimitClamp() {
|
||||
bean.setLimit(0);
|
||||
assertEquals(1, bean.getLimit());
|
||||
bean.setLimit(1000);
|
||||
assertEquals(500, bean.getLimit());
|
||||
bean.setLimit(50);
|
||||
assertEquals(50, bean.getLimit());
|
||||
bean.setLimit(-100);
|
||||
assertEquals(1, bean.getLimit());
|
||||
}
|
||||
|
||||
// ── Defaults ─────────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
@DisplayName("Defaults — scope=SELF, limit=50, compteur=0")
|
||||
void defaults() {
|
||||
assertEquals("SELF", bean.getScope());
|
||||
assertEquals(50, bean.getLimit());
|
||||
assertEquals(0, bean.getCompteur());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import dev.lions.unionflow.client.api.dto.PispiReadinessDto;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class PispiReadinessBeanTest {
|
||||
|
||||
private PispiReadinessBean bean;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
bean = new PispiReadinessBean();
|
||||
}
|
||||
|
||||
private void setReadiness(PispiReadinessDto r) throws Exception {
|
||||
Field f = PispiReadinessBean.class.getDeclaredField("readiness");
|
||||
f.setAccessible(true);
|
||||
f.set(bean, r);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurStatus — null → secondary")
|
||||
void couleurNull() {
|
||||
assertEquals("secondary", bean.getCouleurStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurStatus — READY → success")
|
||||
void couleurReady() throws Exception {
|
||||
setReadiness(new PispiReadinessDto("READY", "url", List.of(), List.of(), List.of()));
|
||||
assertEquals("success", bean.getCouleurStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurStatus — DEGRADED → warning")
|
||||
void couleurDegraded() throws Exception {
|
||||
setReadiness(new PispiReadinessDto("DEGRADED", "url", List.of(), List.of(), List.of()));
|
||||
assertEquals("warning", bean.getCouleurStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurStatus — BLOCKED → danger")
|
||||
void couleurBlocked() throws Exception {
|
||||
setReadiness(new PispiReadinessDto("BLOCKED", "url", List.of(), List.of(), List.of()));
|
||||
assertEquals("danger", bean.getCouleurStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurCheck — PASS → success quel que soit severity")
|
||||
void couleurCheckPass() {
|
||||
assertEquals("success", bean.getCouleurCheck("BLOCKING", "PASS"));
|
||||
assertEquals("success", bean.getCouleurCheck("WARNING", "PASS"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurCheck — FAIL BLOCKING → danger")
|
||||
void couleurCheckFailBlocking() {
|
||||
assertEquals("danger", bean.getCouleurCheck("BLOCKING", "FAIL"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurCheck — FAIL WARNING → warning")
|
||||
void couleurCheckFailWarning() {
|
||||
assertEquals("warning", bean.getCouleurCheck("WARNING", "FAIL"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("DTO estReady / estBlocked")
|
||||
void dtoStateHelpers() {
|
||||
var ready = new PispiReadinessDto("READY", "u", List.of(), List.of(), List.of());
|
||||
var blocked = new PispiReadinessDto("BLOCKED", "u", List.of(), List.of(), List.of());
|
||||
assertTrue(ready.estReady());
|
||||
assertFalse(ready.estBlocked());
|
||||
assertFalse(blocked.estReady());
|
||||
assertTrue(blocked.estBlocked());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.compliance.response.KpiPublicSnapshot;
|
||||
import java.lang.reflect.Field;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class PublicKpiBeanTest {
|
||||
|
||||
private PublicKpiBean bean;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
bean = new PublicKpiBean();
|
||||
}
|
||||
|
||||
private void setSnapshot(KpiPublicSnapshot s) throws Exception {
|
||||
Field f = PublicKpiBean.class.getDeclaredField("snapshot");
|
||||
f.setAccessible(true);
|
||||
f.set(bean, s);
|
||||
}
|
||||
|
||||
// ── couleurScore ─────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurScore — score >= 80 → success")
|
||||
void couleurSuccess() throws Exception {
|
||||
setSnapshot(KpiPublicSnapshot.builder().scoreGlobal(85).build());
|
||||
assertEquals("success", bean.getCouleurScore());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurScore — 60..79 → warning")
|
||||
void couleurWarning() throws Exception {
|
||||
setSnapshot(KpiPublicSnapshot.builder().scoreGlobal(70).build());
|
||||
assertEquals("warning", bean.getCouleurScore());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurScore — < 60 → danger")
|
||||
void couleurDanger() throws Exception {
|
||||
setSnapshot(KpiPublicSnapshot.builder().scoreGlobal(50).build());
|
||||
assertEquals("danger", bean.getCouleurScore());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurScore — null snapshot → secondary")
|
||||
void couleurNull() {
|
||||
assertEquals("secondary", bean.getCouleurScore());
|
||||
}
|
||||
|
||||
// ── couleurStatut ────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
@DisplayName("couleurStatut — règles")
|
||||
void couleurStatut() {
|
||||
assertEquals("success", bean.getCouleurStatut("OK"));
|
||||
assertEquals("danger", bean.getCouleurStatut("RETARD"));
|
||||
assertEquals("warning", bean.getCouleurStatut("EN_ATTENTE"));
|
||||
assertEquals("info", bean.getCouleurStatut("OBLIGATOIRE"));
|
||||
assertEquals("secondary", bean.getCouleurStatut(null));
|
||||
assertEquals("secondary", bean.getCouleurStatut("AUTRE"));
|
||||
}
|
||||
|
||||
// ── État charge ──────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
@DisplayName("isCharge — false par défaut, true quand snapshot présent")
|
||||
void isCharge() throws Exception {
|
||||
assertFalse(bean.isCharge());
|
||||
setSnapshot(KpiPublicSnapshot.builder().scoreGlobal(75).build());
|
||||
assertTrue(bean.isCharge());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class RoleDelegationBeanTest {
|
||||
|
||||
private RoleDelegationBean bean;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
bean = new RoleDelegationBean();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleurStatut — ACTIVE/REVOQUEE/EXPIREE/null/inconnu")
|
||||
void couleur() {
|
||||
assertEquals("success", bean.getCouleurStatut("ACTIVE"));
|
||||
assertEquals("danger", bean.getCouleurStatut("REVOQUEE"));
|
||||
assertEquals("warning", bean.getCouleurStatut("EXPIREE"));
|
||||
assertEquals("secondary", bean.getCouleurStatut(null));
|
||||
assertEquals("secondary", bean.getCouleurStatut("AUTRE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Defaults — rôle TRESORIER, dates initialisées")
|
||||
void defaults() {
|
||||
assertEquals("TRESORIER", bean.getRoleDelegue());
|
||||
assertNotNull(bean.getDateDebut());
|
||||
assertNotNull(bean.getDateFin());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user