feat(client): Creation page modification membre exhaustive et formulaire reutilisable

NOUVELLES PAGES:
- modifier.xhtml : Page dediee pour modifier un membre (exhaustive)
- membre-form.xhtml : Fragment reutilisable pour formulaire membre (DRY/WOU)

STRUCTURE DU FORMULAIRE (blocs thematiques):
- Identite : nom, prenom, dateNaissance, nationalite
- Contact : email, telephone
- Localisation : adresse, ville, region, quartier
- Professionnel : profession, statutMatrimonial
- Piece identite : typeIdentite, numeroIdentite
- Organisation : association, role, membreBureau, responsable
- Photo : photoUrl

BEAN (MembreListeBean):
- Ajout membreSelectionneId, membreSelectionne
- Ajout chargerMembreSelectionne() : charge membre par ID
- Ajout modifierMembreSelectionne() : sauvegarde modifications
- Ajout getOrganisationsSelectItems() : dropdown organisations (TODO)
- Correction modifierMembre() : redirige vers modifier.xhtml

BOUTON MODIFIER dans liste.xhtml:
- Navigation directe vers modifier.xhtml au lieu de popup
- Coherence avec inscription.xhtml

Respecte DRY/WOU avec formulaire reutilisable membre-form.xhtml
This commit is contained in:
dahoud
2025-11-29 22:45:39 +00:00
parent f8866feffe
commit a1f05441b0
4 changed files with 303 additions and 7 deletions

View File

@@ -191,7 +191,65 @@ public class MembreListeBean implements Serializable {
} }
public String modifierMembre(MembreDTO membre) { public String modifierMembre(MembreDTO membre) {
return "/pages/secure/membre/modification?id=" + membre.getId() + "&faces-redirect=true"; return "/pages/secure/membre/modifier?id=" + membre.getId() + "&faces-redirect=true";
}
// Propriétés pour la page de modification
private UUID membreSelectionneId;
private MembreDTO membreSelectionne;
public UUID getMembreSelectionneId() {
return membreSelectionneId;
}
public void setMembreSelectionneId(UUID membreSelectionneId) {
this.membreSelectionneId = membreSelectionneId;
}
public MembreDTO getMembreSelectionne() {
return membreSelectionne;
}
public void setMembreSelectionne(MembreDTO membreSelectionne) {
this.membreSelectionne = membreSelectionne;
}
public void chargerMembreSelectionne() {
if (membreSelectionneId != null) {
try {
membreSelectionne = membreService.obtenirParId(membreSelectionneId);
LOGGER.info("Membre chargé pour modification: " + membreSelectionne.getNomComplet());
} catch (Exception e) {
LOGGER.severe("Erreur lors du chargement du membre: " + e.getMessage());
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur",
"Impossible de charger le membre: " + e.getMessage()));
}
}
}
public String modifierMembreSelectionne() {
try {
membreService.modifier(membreSelectionne.getId(), membreSelectionne);
LOGGER.info("Membre modifié: " + membreSelectionne.getNomComplet());
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès",
"Le membre a été modifié avec succès"));
return "/pages/secure/membre/liste?faces-redirect=true";
} catch (Exception e) {
LOGGER.severe("Erreur lors de la modification: " + e.getMessage());
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur",
"Impossible de modifier le membre: " + e.getMessage()));
return null;
}
}
// Méthode pour obtenir la liste des organisations pour le dropdown
public List<jakarta.faces.model.SelectItem> getOrganisationsSelectItems() {
// TODO: Implémenter la récupération des organisations
// Pour l'instant, retourner une liste vide
return new java.util.ArrayList<>();
} }
public String gererCotisations(MembreDTO membre) { public String gererCotisations(MembreDTO membre) {

View File

@@ -195,12 +195,10 @@
<uf:action-button-view itemId="#{membre.id}" <uf:action-button-view itemId="#{membre.id}"
detailPage="/pages/secure/membre/profil.xhtml" detailPage="/pages/secure/membre/profil.xhtml"
iconOnly="true"/> iconOnly="true"/>
<ui:include src="/templates/components/buttons/button-icon.xhtml"> <p:commandButton icon="pi pi-pencil"
<ui:param name="icon" value="pi pi-pencil" /> title="Modifier"
<ui:param name="action" value="#{membreListeBean.modifierMembre(membre)}" /> styleClass="ui-button-rounded ui-button-warning"
<ui:param name="title" value="Modifier" /> onclick="window.location='#{request.contextPath}/pages/secure/membre/modifier.xhtml?id=#{membre.id}';return false;"/>
<ui:param name="severity" value="warning" />
</ui:include>
<ui:include src="/templates/components/buttons/button-icon.xhtml"> <ui:include src="/templates/components/buttons/button-icon.xhtml">
<ui:param name="icon" value="pi pi-dollar" /> <ui:param name="icon" value="pi pi-dollar" />
<ui:param name="action" value="#{membreListeBean.gererCotisations(membre)}" /> <ui:param name="action" value="#{membreListeBean.gererCotisations(membre)}" />

View File

@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<ui:composition template="/templates/main-template.xhtml">
<ui:define name="title">Modifier le Membre</ui:define>
<ui:define name="content">
<!-- Charger le membre à modifier -->
<f:metadata>
<f:viewParam name="id" value="#{membreListeBean.membreSelectionneId}" />
<f:viewAction action="#{membreListeBean.chargerMembreSelectionne}" />
</f:metadata>
<h:form id="formModifierMembre">
<p:messages id="messages" showDetail="true" closable="true"/>
<div class="card mb-3">
<div class="flex justify-content-between align-items-center flex-column md:flex-row">
<div class="mb-2 md:mb-0">
<h3 class="m-0">Modifier le Membre</h3>
<span class="text-600">
Modifiez les informations du membre.
</span>
</div>
<div class="flex gap-2">
<!-- DRY/WOU: button-secondary pour navigation -->
<ui:include src="/templates/components/buttons/button-secondary.xhtml">
<ui:param name="value" value="Annuler"/>
<ui:param name="icon" value="pi pi-times"/>
<ui:param name="outcome" value="/pages/secure/membre/liste"/>
</ui:include>
</div>
</div>
</div>
<div class="card">
<h5 class="mb-3">Informations du Membre</h5>
<ui:include src="/ui/includes/membre-form.xhtml">
<ui:param name="model" value="#{membreListeBean.membreSelectionne}" />
<ui:param name="organisationsItems" value="#{membreListeBean.organisationsSelectItems}" />
</ui:include>
</div>
<div class="mt-3 flex justify-content-end gap-2">
<!-- DRY/WOU: button-secondary pour navigation -->
<ui:include src="/templates/components/buttons/button-secondary.xhtml">
<ui:param name="value" value="Annuler"/>
<ui:param name="icon" value="pi pi-times"/>
<ui:param name="outcome" value="/pages/secure/membre/liste"/>
</ui:include>
<!-- Bouton Enregistrer : p:commandButton direct car action avec méthode backend -->
<p:commandButton value="Enregistrer"
icon="pi pi-check"
action="#{membreListeBean.modifierMembreSelectionne}"
update=":formModifierMembre:messages"
styleClass="ui-button-success" />
</div>
</h:form>
</ui:define>
</ui:composition>
</html>

View File

@@ -0,0 +1,174 @@
<ui:fragment 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">
<!--
Fragment de formulaire réutilisable pour les membres (DRY/WOU)
Utilisé pour création et modification
Paramètres:
- model : MembreDTO à éditer
- organisationsItems : Liste des organisations pour le dropdown
-->
<div class="ui-fluid">
<div class="formgrid grid">
<!-- Bloc Identité -->
<div class="col-12">
<div class="card p-fluid mb-3">
<h5 class="mb-3">Identité</h5>
<div class="grid formgrid">
<div class="field col-12 md:col-6">
<p:outputLabel for="nom" value="Nom *" />
<p:inputText id="nom" value="#{model.nom}" required="true" requiredMessage="Nom: une donnée est requise." maxlength="50" />
<p:message for="nom" />
</div>
<div class="field col-12 md:col-6">
<p:outputLabel for="prenom" value="Prénom *" />
<p:inputText id="prenom" value="#{model.prenom}" required="true" requiredMessage="Prénom: une donnée est requise." maxlength="50" />
<p:message for="prenom" />
</div>
<div class="field col-12 md:col-6">
<p:outputLabel for="dateNaissance" value="Date de naissance" />
<p:datePicker id="dateNaissance" value="#{model.dateNaissance}" pattern="yyyy-MM-dd" showIcon="true" />
</div>
<div class="field col-12 md:col-6">
<p:outputLabel for="nationalite" value="Nationalité" />
<p:inputText id="nationalite" value="#{model.nationalite}" maxlength="50" />
</div>
</div>
</div>
</div>
<!-- Bloc Contact -->
<div class="col-12">
<div class="card p-fluid mb-3">
<h5 class="mb-3">Contact</h5>
<div class="grid formgrid">
<div class="field col-12 md:col-6">
<p:outputLabel for="email" value="Email *" />
<p:inputText id="email" value="#{model.email}" required="true" requiredMessage="Email: une donnée est requise." maxlength="100" />
<p:message for="email" />
</div>
<div class="field col-12 md:col-6">
<p:outputLabel for="telephone" value="Téléphone" />
<p:inputText id="telephone" value="#{model.telephone}" maxlength="20" />
</div>
</div>
</div>
</div>
<!-- Bloc Localisation -->
<div class="col-12">
<div class="card p-fluid mb-3">
<h5 class="mb-3">Localisation</h5>
<div class="grid formgrid">
<div class="field col-12">
<p:outputLabel for="adresse" value="Adresse" />
<p:inputTextarea id="adresse" value="#{model.adresse}" rows="2" maxlength="200" />
</div>
<div class="field col-12 md:col-4">
<p:outputLabel for="ville" value="Ville" />
<p:inputText id="ville" value="#{model.ville}" maxlength="50" />
</div>
<div class="field col-12 md:col-4">
<p:outputLabel for="region" value="Région" />
<p:inputText id="region" value="#{model.region}" maxlength="50" />
</div>
<div class="field col-12 md:col-4">
<p:outputLabel for="quartier" value="Quartier" />
<p:inputText id="quartier" value="#{model.quartier}" maxlength="50" />
</div>
</div>
</div>
</div>
<!-- Bloc Professionnel -->
<div class="col-12">
<div class="card p-fluid mb-3">
<h5 class="mb-3">Informations professionnelles</h5>
<div class="grid formgrid">
<div class="field col-12 md:col-6">
<p:outputLabel for="profession" value="Profession" />
<p:inputText id="profession" value="#{model.profession}" maxlength="100" />
</div>
<div class="field col-12 md:col-6">
<p:outputLabel for="statutMatrimonial" value="Statut matrimonial" />
<p:inputText id="statutMatrimonial" value="#{model.statutMatrimonial}" maxlength="20" />
</div>
</div>
</div>
</div>
<!-- Bloc Identité administrative -->
<div class="col-12">
<div class="card p-fluid mb-3">
<h5 class="mb-3">Pièce d'identité</h5>
<div class="grid formgrid">
<div class="field col-12 md:col-6">
<p:outputLabel for="typeIdentite" value="Type de pièce" />
<p:selectOneMenu id="typeIdentite" value="#{model.typeIdentite}">
<f:selectItem itemLabel="Sélectionner..." itemValue="" />
<f:selectItem itemLabel="Carte Nationale d'Identité" itemValue="CNI" />
<f:selectItem itemLabel="Passeport" itemValue="PASSEPORT" />
<f:selectItem itemLabel="Permis de conduire" itemValue="PERMIS" />
<f:selectItem itemLabel="Autre" itemValue="AUTRE" />
</p:selectOneMenu>
</div>
<div class="field col-12 md:col-6">
<p:outputLabel for="numeroIdentite" value="Numéro de pièce" />
<p:inputText id="numeroIdentite" value="#{model.numeroIdentite}" maxlength="50" />
</div>
</div>
</div>
</div>
<!-- Bloc Organisation -->
<div class="col-12">
<div class="card p-fluid mb-3">
<h5 class="mb-3">Organisation &amp; Rôle</h5>
<div class="grid formgrid">
<div class="field col-12 md:col-6">
<p:outputLabel for="association" value="Organisation *" />
<p:selectOneMenu id="association" value="#{model.associationId}" required="true" requiredMessage="Organisation: une donnée est requise.">
<f:selectItems value="#{organisationsItems}" />
</p:selectOneMenu>
<p:message for="association" />
</div>
<div class="field col-12 md:col-6">
<p:outputLabel for="role" value="Rôle" />
<p:inputText id="role" value="#{model.role}" maxlength="50" />
</div>
<div class="field col-12 md:col-4">
<p:selectBooleanCheckbox id="membreBureau" value="#{model.membreBureau}" />
<p:outputLabel for="membreBureau" value="Membre du bureau" style="margin-left: 0.5rem;" />
</div>
<div class="field col-12 md:col-4">
<p:selectBooleanCheckbox id="responsable" value="#{model.responsable}" />
<p:outputLabel for="responsable" value="Responsable" style="margin-left: 0.5rem;" />
</div>
</div>
</div>
</div>
<!-- Bloc Photo -->
<div class="col-12">
<div class="card p-fluid mb-3">
<h5 class="mb-3">Photo de profil</h5>
<div class="grid formgrid">
<div class="field col-12">
<p:outputLabel for="photoUrl" value="URL de la photo" />
<p:inputText id="photoUrl" value="#{model.photoUrl}" maxlength="255" placeholder="https://..." />
<small class="text-600">Entrez l'URL d'une photo ou laissez vide pour utiliser l'avatar par défaut</small>
</div>
</div>
</div>
</div>
</div>
</div>
</ui:fragment>