feat(client): Réorganisation des composants réutilisables par type et correction des erreurs EL

- Réorganisation de la structure des composants dans des sous-dossiers :
  * buttons/ : tous les composants de boutons
  * cards/ : card-header, card-simple, filter-bar, stat-card
  * columns/ : composants de colonnes pour datatables
  * dialogs/ : confirm-dialog, form-dialog
  * forms/ : tous les composants de formulaires
  * layout/ : menu, topbar, footer, config, rightpanel, page-header, organisation-logo
  * tables/ : data-table

- Correction des erreurs EL dans les composants :
  * Suppression des expressions EL dans les commentaires XML
  * Protection des paramètres optionnels (styleClass) avec 'not empty'
  * Simplification de confirm-dialog avec valeurs fixes
  * Correction de organisation-logo (rendered sur balise HTML)

- Ajout de nouvelles pages pour les organisations :
  * detail.xhtml : consultation exhaustive d'une organisation
  * nouvelle.xhtml : création complète d'une organisation

- Mise à jour de toutes les références vers les nouveaux chemins (37+ fichiers)
- Maintien de l'approche DRY/WOU avec composants réutilisables
This commit is contained in:
dahoud
2025-11-29 19:10:01 +00:00
parent 952141662b
commit c877065500
94 changed files with 2776 additions and 486 deletions

View File

@@ -5,9 +5,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.io.Serializable;
import java.util.UUID;
/**
@@ -26,12 +27,17 @@ public class AssociationDTO implements Serializable {
@NotBlank(message = "Le nom de l'association est obligatoire")
private String nom;
// Aligné sur OrganisationDTO.nomCourt
private String nomCourt;
private String description;
private String adresse;
private String telephone;
private String email;
private String siteWeb;
// Aligné sur OrganisationDTO.logo (URL ou chemin du logo)
private String logo;
@NotNull(message = "Le type d'association est obligatoire")
@JsonProperty("typeOrganisation")
@@ -47,6 +53,8 @@ public class AssociationDTO implements Serializable {
private String numeroRegistre;
private String statut;
private Integer nombreMembres;
// Aligné sur OrganisationDTO.nombreAdministrateurs
private Integer nombreAdministrateurs;
private String responsablePrincipal;
private String telephoneResponsable;
private String emailResponsable;
@@ -70,6 +78,39 @@ public class AssociationDTO implements Serializable {
private String pays;
// Aligné sur OrganisationDTO.codePostal
private String codePostal;
// Aligné sur OrganisationDTO.activitesPrincipales
private String activitesPrincipales;
// Aligné sur OrganisationDTO.objectifs / partenaires / certifications / reseauxSociaux / notes
private String objectifs;
private String partenaires;
private String certifications;
private String reseauxSociaux;
private String notes;
// Aligné sur OrganisationDTO.organisationPublique / accepteNouveauxMembres / cotisationObligatoire
private Boolean organisationPublique;
private Boolean accepteNouveauxMembres;
private Boolean cotisationObligatoire;
// Aligné sur OrganisationDTO.budgetAnnuel / devise / montantCotisationAnnuelle
private BigDecimal budgetAnnuel;
private String devise;
private BigDecimal montantCotisationAnnuelle;
// Aligné sur OrganisationDTO.telephoneSecondaire / emailSecondaire
private String telephoneSecondaire;
private String emailSecondaire;
// Aligné sur OrganisationDTO.organisationParenteId / nomOrganisationParente / niveauHierarchique
private UUID organisationParenteId;
private String nomOrganisationParente;
private Integer niveauHierarchique;
// Aligné sur OrganisationDTO.latitude / longitude
private BigDecimal latitude;
private BigDecimal longitude;
// Constructeurs
public AssociationDTO() {}
@@ -88,6 +129,9 @@ public class AssociationDTO implements Serializable {
public String getNom() { return nom; }
public void setNom(String nom) { this.nom = nom; }
public String getNomCourt() { return nomCourt; }
public void setNomCourt(String nomCourt) { this.nomCourt = nomCourt; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
@@ -103,6 +147,9 @@ public class AssociationDTO implements Serializable {
public String getSiteWeb() { return siteWeb; }
public void setSiteWeb(String siteWeb) { this.siteWeb = siteWeb; }
public String getLogo() { return logo; }
public void setLogo(String logo) { this.logo = logo; }
public String getTypeAssociation() { return typeAssociation; }
public void setTypeAssociation(String typeAssociation) { this.typeAssociation = typeAssociation; }
@@ -118,6 +165,9 @@ public class AssociationDTO implements Serializable {
public Integer getNombreMembres() { return nombreMembres; }
public void setNombreMembres(Integer nombreMembres) { this.nombreMembres = nombreMembres; }
public Integer getNombreAdministrateurs() { return nombreAdministrateurs; }
public void setNombreAdministrateurs(Integer nombreAdministrateurs) { this.nombreAdministrateurs = nombreAdministrateurs; }
public String getResponsablePrincipal() { return responsablePrincipal; }
public void setResponsablePrincipal(String responsablePrincipal) { this.responsablePrincipal = responsablePrincipal; }
@@ -146,6 +196,63 @@ public class AssociationDTO implements Serializable {
public String getCodePostal() { return codePostal; }
public void setCodePostal(String codePostal) { this.codePostal = codePostal; }
public String getActivitesPrincipales() { return activitesPrincipales; }
public void setActivitesPrincipales(String activitesPrincipales) { this.activitesPrincipales = activitesPrincipales; }
public String getObjectifs() { return objectifs; }
public void setObjectifs(String objectifs) { this.objectifs = objectifs; }
public String getPartenaires() { return partenaires; }
public void setPartenaires(String partenaires) { this.partenaires = partenaires; }
public String getCertifications() { return certifications; }
public void setCertifications(String certifications) { this.certifications = certifications; }
public String getReseauxSociaux() { return reseauxSociaux; }
public void setReseauxSociaux(String reseauxSociaux) { this.reseauxSociaux = reseauxSociaux; }
public String getNotes() { return notes; }
public void setNotes(String notes) { this.notes = notes; }
public Boolean getOrganisationPublique() { return organisationPublique; }
public void setOrganisationPublique(Boolean organisationPublique) { this.organisationPublique = organisationPublique; }
public Boolean getAccepteNouveauxMembres() { return accepteNouveauxMembres; }
public void setAccepteNouveauxMembres(Boolean accepteNouveauxMembres) { this.accepteNouveauxMembres = accepteNouveauxMembres; }
public Boolean getCotisationObligatoire() { return cotisationObligatoire; }
public void setCotisationObligatoire(Boolean cotisationObligatoire) { this.cotisationObligatoire = cotisationObligatoire; }
public BigDecimal getBudgetAnnuel() { return budgetAnnuel; }
public void setBudgetAnnuel(BigDecimal budgetAnnuel) { this.budgetAnnuel = budgetAnnuel; }
public String getDevise() { return devise; }
public void setDevise(String devise) { this.devise = devise; }
public BigDecimal getMontantCotisationAnnuelle() { return montantCotisationAnnuelle; }
public void setMontantCotisationAnnuelle(BigDecimal montantCotisationAnnuelle) { this.montantCotisationAnnuelle = montantCotisationAnnuelle; }
public String getTelephoneSecondaire() { return telephoneSecondaire; }
public void setTelephoneSecondaire(String telephoneSecondaire) { this.telephoneSecondaire = telephoneSecondaire; }
public String getEmailSecondaire() { return emailSecondaire; }
public void setEmailSecondaire(String emailSecondaire) { this.emailSecondaire = emailSecondaire; }
public UUID getOrganisationParenteId() { return organisationParenteId; }
public void setOrganisationParenteId(UUID organisationParenteId) { this.organisationParenteId = organisationParenteId; }
public String getNomOrganisationParente() { return nomOrganisationParente; }
public void setNomOrganisationParente(String nomOrganisationParente) { this.nomOrganisationParente = nomOrganisationParente; }
public Integer getNiveauHierarchique() { return niveauHierarchique; }
public void setNiveauHierarchique(Integer niveauHierarchique) { this.niveauHierarchique = niveauHierarchique; }
public BigDecimal getLatitude() { return latitude; }
public void setLatitude(BigDecimal latitude) { this.latitude = latitude; }
public BigDecimal getLongitude() { return longitude; }
public void setLongitude(BigDecimal longitude) { this.longitude = longitude; }
public LocalDateTime getDateCreation() { return dateCreation; }
public void setDateCreation(LocalDateTime dateCreation) { this.dateCreation = dateCreation; }

View File

@@ -0,0 +1,87 @@
package dev.lions.unionflow.client.view;
import dev.lions.unionflow.client.dto.AssociationDTO;
import dev.lions.unionflow.client.service.AssociationService;
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.IOException;
import java.io.Serializable;
import java.util.UUID;
import java.util.logging.Logger;
import org.eclipse.microprofile.rest.client.inject.RestClient;
/**
* Bean de consultation d'une organisation (fiche détaillée en lecture seule).
*/
@Named("organisationDetailBean")
@ViewScoped
public class OrganisationDetailBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(OrganisationDetailBean.class.getName());
@Inject
@RestClient
AssociationService associationService;
private AssociationDTO organisation;
private UUID organisationId;
@PostConstruct
public void init() {
// Récupérer l'ID depuis les paramètres de requête
String idParam = FacesContext.getCurrentInstance()
.getExternalContext()
.getRequestParameterMap()
.get("id");
if (idParam != null && !idParam.isBlank()) {
try {
organisationId = UUID.fromString(idParam);
chargerOrganisation();
} catch (IllegalArgumentException e) {
LOGGER.severe("ID d'organisation invalide: " + idParam);
ajouterMessageErreur("Organisation introuvable", "Identifiant invalide.");
}
} else {
ajouterMessageErreur("Organisation introuvable", "Aucun identifiant fourni.");
}
}
public void chargerOrganisation() {
if (organisationId == null) {
return;
}
try {
organisation = associationService.obtenirParId(organisationId);
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement de l'organisation: " + e.getMessage());
ajouterMessageErreur("Organisation introuvable",
"Impossible de charger les détails de l'organisation.");
}
}
public void revenirAListe() throws IOException {
FacesContext.getCurrentInstance()
.getExternalContext()
.redirect(FacesContext.getCurrentInstance()
.getExternalContext()
.getRequestContextPath() + "/pages/secure/organisation/liste.xhtml");
}
private void ajouterMessageErreur(String resume, String detail) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, resume, detail));
}
public AssociationDTO getOrganisation() {
return organisation;
}
}

View File

@@ -250,6 +250,40 @@ public class OrganisationsBean implements Serializable {
}
}
/**
* Recherche les organisations dont le nom contient la requête fournie.
* Méthode utilitaire côté client qui délègue au service REST backend.
*
* @param query terme de recherche (partie du nom)
* @return liste d'organisations correspondant au critère, ou liste vide en cas d'erreur
*/
public List<AssociationDTO> rechercherOrganisations(String query) {
if (query == null || query.trim().isEmpty()) {
return organisations; // rien saisi : on renvoie la liste actuelle
}
try {
// On délègue au endpoint /api/organisations/recherche avec uniquement le nom rempli.
List<AssociationDTO> resultats = associationService.rechercher(
query, // nom
null, // type
null, // statut
null, // region
null, // ville
0, // page
100 // size
);
LOGGER.info("Recherche d'organisations pour '" + query + "': " +
(resultats != null ? resultats.size() : 0) + " résultat(s)");
return resultats != null ? resultats : List.of();
} catch (Exception e) {
LOGGER.severe("Erreur lors de la recherche d'organisations pour '" + query + "': " + e.getMessage());
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Erreur", "Impossible de rechercher les organisations: " + e.getMessage()));
return List.of();
}
}
/**
* Bascule le statut d'une organisation entre ACTIVE et INACTIVE
* Cette méthode est utilisée pour éviter l'utilisation d'expressions ternaires dans les expressions EL