Refactoring

This commit is contained in:
dahoud
2026-03-01 22:00:28 +00:00
parent c0e2c4da45
commit 6b28cf751e
469 changed files with 26866 additions and 14768 deletions

View File

@@ -5,7 +5,7 @@
xmlns:p="http://primefaces.org/ui">
<!--
Fragment réutilisable pour le formulaire d'Organisation
Fragment réutilisable pour le formulaire d'Organisation - Version Elite
Paramètres attendus via <ui:param>:
- model: l'objet cible (ex: #{organisationsBean.nouvelleOrganisation})
- typesItems: la liste des SelectItem pour les types
@@ -15,18 +15,26 @@
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- SECTION 1 : INFORMATIONS GÉNÉRALES -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<p:fieldset legend="Informations générales" toggleable="true" collapsed="false" styleClass="mb-3">
<p:fieldset legend="📋 Informations générales" toggleable="true" collapsed="false" styleClass="mb-3">
<div class="formgrid grid">
<!-- Nom complet -->
<div class="field col-12 md:col-8">
<p:outputLabel for="nom" value="Nom complet *" styleClass="font-semibold" />
<p:outputLabel for="nom" value="Nom complet" styleClass="font-semibold">
<span class="text-red-500">*</span>
</p:outputLabel>
<p:inputText id="nom"
value="#{model.nom}"
required="true"
requiredMessage="Le nom de l'organisation est requis."
maxlength="200"
placeholder="Ex: Association pour le développement durable" />
placeholder="Ex: Association pour le développement durable">
<f:validateLength minimum="3" maximum="200" />
</p:inputText>
<p:message for="nom" />
<small class="text-500">
<i class="pi pi-info-circle mr-1"/>
Nom officiel complet de l'organisation (3-200 caractères)
</small>
</div>
<!-- Nom court / Sigle -->
@@ -35,53 +43,91 @@
<p:inputText id="nomCourt"
value="#{model.nomCourt}"
maxlength="100"
placeholder="Ex: ADD" />
placeholder="Ex: ADD">
<f:validateLength maximum="100" />
</p:inputText>
<small class="text-500">
<i class="pi pi-tag mr-1"/>
Acronyme ou nom abrégé
</small>
</div>
<!-- Type d'organisation -->
<div class="field col-12 md:col-6">
<p:outputLabel for="type" value="Type d'organisation *" styleClass="font-semibold" />
<p:selectOneMenu id="type"
value="#{model.typeAssociation}"
<p:outputLabel for="type" value="Type d'organisation" styleClass="font-semibold">
<span class="text-red-500">*</span>
</p:outputLabel>
<p:selectOneMenu id="type"
value="#{model.typeAssociation}"
required="true"
requiredMessage="Le type d'organisation est requis.">
<f:selectItem itemLabel="-- Sélectionnez un type --" itemValue="#{null}" noSelectionOption="true" />
<f:selectItems value="#{typesItems}" />
</p:selectOneMenu>
<p:message for="type" />
<p:tooltip for="type" value="Catégorie juridique ou fonctionnelle de l'organisation" position="top"/>
</div>
<!-- Statut de l'organisation -->
<div class="field col-12 md:col-3">
<p:outputLabel for="statut" value="Statut" styleClass="font-semibold">
<span class="text-red-500">*</span>
</p:outputLabel>
<p:selectOneMenu id="statut"
value="#{model.statut}"
required="true"
requiredMessage="Le statut de l'organisation est requis.">
<f:selectItem itemLabel="✅ Active" itemValue="ACTIVE" />
<f:selectItem itemLabel="🔨 En création" itemValue="EN_CREATION" />
<f:selectItem itemLabel="⏸️ Inactive" itemValue="INACTIVE" />
<f:selectItem itemLabel="⚠️ Suspendue" itemValue="SUSPENDUE" />
<f:selectItem itemLabel="🚫 Dissoute" itemValue="DISSOUTE" />
</p:selectOneMenu>
<p:message for="statut" />
<p:tooltip for="statut" value="État actuel de l'organisation - peut être modifié ultérieurement" position="top"/>
</div>
<!-- Date de fondation -->
<div class="field col-12 md:col-3">
<p:outputLabel for="dateFondation" value="Date de fondation" styleClass="font-semibold" />
<p:datePicker id="dateFondation"
value="#{model.dateFondation}"
pattern="dd/MM/yyyy"
<p:datePicker id="dateFondation"
value="#{model.dateFondation}"
pattern="dd/MM/yyyy"
showIcon="true"
yearNavigator="true"
yearRange="1900:2025"
maxdate="#{null}"
placeholder="jj/mm/aaaa" />
<p:tooltip for="dateFondation" value="Date officielle de création de l'organisation" position="top"/>
</div>
<!-- Numéro d'enregistrement -->
<div class="field col-12 md:col-3">
<p:outputLabel for="numEnreg" value="N° d'enregistrement" styleClass="font-semibold" />
<p:inputText id="numEnreg"
value="#{model.numeroRegistre}"
<div class="field col-12 md:col-6">
<p:outputLabel for="numEnreg" value="N° d'enregistrement / RCCM" styleClass="font-semibold" />
<p:inputText id="numEnreg"
value="#{model.numeroRegistre}"
maxlength="100"
placeholder="Ex: RNA-W123456789" />
placeholder="Ex: CI-ABJ-01-2024-B12-12345">
<f:validateLength maximum="100" />
</p:inputText>
<small class="text-500">
<i class="pi pi-shield mr-1"/>
Numéro d'immatriculation officiel (RCCM, RNA, etc.)
</small>
</div>
<!-- Description -->
<div class="field col-12">
<p:outputLabel for="description" value="Description" styleClass="font-semibold" />
<p:inputTextarea id="description"
value="#{model.description}"
rows="4"
<p:inputTextarea id="description"
value="#{model.description}"
rows="4"
maxlength="2000"
placeholder="Décrivez brièvement l'organisation..."
autoResize="false" />
<small class="text-muted">#{2000 - (empty model.description ? 0 : model.description.length())} caractères restants</small>
placeholder="Décrivez brièvement la mission, les objectifs et les activités principales de l'organisation..."
autoResize="false">
<f:validateLength maximum="2000" />
</p:inputTextarea>
<p:message for="description" />
<small class="text-500">Maximum 2000 caractères</small>
</div>
</div>
</p:fieldset>
@@ -89,76 +135,119 @@
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- SECTION 2 : COORDONNÉES -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<p:fieldset legend="Coordonnées" toggleable="true" collapsed="false" styleClass="mb-3">
<p:fieldset legend="📞 Coordonnées" toggleable="true" collapsed="false" styleClass="mb-3">
<div class="formgrid grid">
<!-- Email principal -->
<div class="field col-12 md:col-6">
<p:outputLabel for="email" value="Email principal" styleClass="font-semibold" />
<p:inputText id="email"
value="#{model.email}"
<p:inputText id="email"
value="#{model.email}"
maxlength="200"
type="email"
placeholder="contact@exemple.com" />
placeholder="contact@organisation.com">
<f:validateLength maximum="200" />
</p:inputText>
<p:message for="email" />
<small class="text-500">
<i class="pi pi-envelope mr-1"/>
Email principal de contact
</small>
</div>
<!-- Email secondaire -->
<div class="field col-12 md:col-6">
<p:outputLabel for="email2" value="Email secondaire" styleClass="font-semibold" />
<p:inputText id="email2"
value="#{model.emailSecondaire}"
<p:inputText id="email2"
value="#{model.emailSecondaire}"
maxlength="200"
type="email"
placeholder="admin@exemple.com" />
placeholder="admin@organisation.com">
<f:validateLength maximum="200" />
</p:inputText>
<p:message for="email2" />
<small class="text-500">
<i class="pi pi-envelope mr-1"/>
Email alternatif (optionnel)
</small>
</div>
<!-- Téléphone principal -->
<div class="field col-12 md:col-6">
<p:outputLabel for="telephone" value="Téléphone principal" styleClass="font-semibold" />
<p:inputText id="telephone"
value="#{model.telephone}"
maxlength="20"
placeholder="+225 XX XX XX XX XX" />
<p:inputText id="telephone"
value="#{model.telephone}"
maxlength="30"
placeholder="+225 07 00 00 00 00">
<f:validateLength maximum="30" />
</p:inputText>
<p:message for="telephone" />
<small class="text-500">
<i class="pi pi-phone mr-1"/>
Numéro principal avec indicatif pays
</small>
</div>
<!-- Téléphone secondaire -->
<div class="field col-12 md:col-6">
<p:outputLabel for="telephone2" value="Téléphone secondaire" styleClass="font-semibold" />
<p:inputText id="telephone2"
value="#{model.telephoneSecondaire}"
maxlength="20"
placeholder="+225 XX XX XX XX XX" />
<p:inputText id="telephone2"
value="#{model.telephoneSecondaire}"
maxlength="30"
placeholder="+221 77 00 00 00">
<f:validateLength maximum="30" />
</p:inputText>
<p:message for="telephone2" />
<small class="text-500">
<i class="pi pi-phone mr-1"/>
Numéro alternatif (optionnel)
</small>
</div>
<!-- Site web -->
<div class="field col-12 md:col-6">
<p:outputLabel for="siteWeb" value="Site web" styleClass="font-semibold" />
<p:inputText id="siteWeb"
value="#{model.siteWeb}"
<p:inputText id="siteWeb"
value="#{model.siteWeb}"
maxlength="500"
placeholder="https://www.exemple.com" />
placeholder="https://www.organisation.com">
<f:validateLength maximum="500" />
</p:inputText>
<p:message for="siteWeb" />
<small class="text-500">
<i class="pi pi-globe mr-1"/>
URL du site officiel (http:// ou https://)
</small>
</div>
<!-- Logo URL -->
<div class="field col-12 md:col-6">
<p:outputLabel for="logo" value="Logo (URL)" styleClass="font-semibold" />
<p:inputText id="logo"
value="#{model.logo}"
<p:inputText id="logo"
value="#{model.logo}"
maxlength="500"
placeholder="https://www.exemple.com/logo.png" />
placeholder="https://www.organisation.com/logo.png">
<f:validateLength maximum="500" />
</p:inputText>
<p:message for="logo" />
<small class="text-500">
<i class="pi pi-image mr-1"/>
Lien vers le logo de l'organisation (PNG, JPG, SVG)
</small>
</div>
<!-- Réseaux sociaux -->
<div class="field col-12">
<p:outputLabel for="reseauxSociaux" value="Réseaux sociaux" styleClass="font-semibold" />
<p:inputTextarea id="reseauxSociaux"
value="#{model.reseauxSociaux}"
rows="3"
<p:inputTextarea id="reseauxSociaux"
value="#{model.reseauxSociaux}"
rows="3"
maxlength="1000"
placeholder='Ex: {"facebook":"@pageOfficielle", "twitter":"@compte", "linkedin":"entreprise/nom"}'
autoResize="false" />
<small class="text-muted">Format JSON recommandé</small>
placeholder='{"facebook":"@page_officielle", "twitter":"@compte", "linkedin":"entreprise/nom", "instagram":"@profil"}'
autoResize="false">
<f:validateLength maximum="1000" />
</p:inputTextarea>
<small class="text-500">
<i class="pi pi-share-alt mr-1"/>
Format JSON recommandé - Maximum 1000 caractères
</small>
</div>
</div>
</p:fieldset>
@@ -166,80 +255,174 @@
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- SECTION 3 : LOCALISATION -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<p:fieldset legend="Localisation" toggleable="true" collapsed="false" styleClass="mb-3">
<p:fieldset legend="📍 Localisation" toggleable="true" collapsed="false" styleClass="mb-3">
<div class="formgrid grid">
<!-- Adresse complète -->
<div class="field col-12">
<p:outputLabel for="adresse" value="Adresse complète" styleClass="font-semibold" />
<p:inputTextarea id="adresse"
value="#{model.adresse}"
rows="2"
<p:inputTextarea id="adresse"
value="#{model.adresse}"
rows="2"
maxlength="500"
placeholder="N° rue, quartier..."
autoResize="false" />
placeholder="N° de la parcelle, nom de la rue, quartier..."
autoResize="false">
<f:validateLength maximum="500" />
</p:inputTextarea>
<small class="text-500">Maximum 500 caractères</small>
</div>
<!-- Quartier / Commune -->
<div class="field col-12 md:col-5">
<p:outputLabel for="quartier" value="Quartier / Commune" styleClass="font-semibold" />
<p:inputText id="quartier"
value="#{model.quartier}"
maxlength="100"
placeholder="Ex: Cocody, Hay Riad, Plateau...">
<f:validateLength maximum="100" />
</p:inputText>
<small class="text-500">
<i class="pi pi-home mr-1"/>
Quartier, commune ou arrondissement
</small>
</div>
<!-- Ville -->
<div class="field col-12 md:col-4">
<p:outputLabel for="ville" value="Ville" styleClass="font-semibold" />
<p:inputText id="ville"
value="#{model.ville}"
maxlength="100"
placeholder="Ex: Abidjan" />
<p:autoComplete id="ville"
value="#{model.ville}"
completeMethod="#{completionBean.completerVilles}"
minQueryLength="1"
queryDelay="200"
dropdown="true"
forceSelection="false"
placeholder="Ex: Abidjan"
maxlength="100">
<f:validateLength maximum="100" />
</p:autoComplete>
<p:tooltip for="ville" value="Commencez à taper pour voir les suggestions de villes de Côte d'Ivoire" position="top"/>
<small class="text-500">
<i class="pi pi-building mr-1"/>
Ville du siège social
</small>
</div>
<!-- Code postal -->
<div class="field col-12 md:col-2">
<p:outputLabel for="codePostal" value="Code postal" styleClass="font-semibold" />
<p:inputText id="codePostal"
value="#{model.codePostal}"
maxlength="10"
placeholder="Ex: 01 BP 123" />
<p:inputText id="codePostal"
value="#{model.codePostal}"
maxlength="20"
placeholder="Ex: 01 BP 123, 75001…">
<f:validateLength maximum="20" />
</p:inputText>
<p:message for="codePostal" />
<small class="text-500">
<i class="pi pi-map-marker mr-1"/>
Format : 3-10 caractères (chiffres, lettres, tiret)
</small>
</div>
<!-- Région -->
<div class="field col-12 md:col-3">
<p:outputLabel for="region" value="Région" styleClass="font-semibold" />
<p:inputText id="region"
value="#{model.region}"
maxlength="100"
placeholder="Ex: Lagunes" />
<p:autoComplete id="region"
value="#{model.region}"
completeMethod="#{completionBean.completerRegions}"
minQueryLength="1"
queryDelay="200"
dropdown="true"
forceSelection="false"
placeholder="Ex: Lagunes"
maxlength="100">
<f:validateLength maximum="100" />
</p:autoComplete>
<p:tooltip for="region" value="Commencez à taper pour voir les suggestions de régions de Côte d'Ivoire" position="top"/>
<small class="text-500">
<i class="pi pi-map mr-1"/>
Région administrative
</small>
</div>
<!-- Pays -->
<div class="field col-12 md:col-3">
<p:outputLabel for="pays" value="Pays" styleClass="font-semibold" />
<p:inputText id="pays"
value="#{model.pays}"
maxlength="100"
placeholder="Ex: Côte d'Ivoire" />
<p:selectOneMenu id="pays"
value="#{model.pays}">
<f:selectItem itemLabel="— Sélectionner —" itemValue="" />
<!-- Afrique de l'Ouest (UEMOA / XOF) -->
<f:selectItem itemLabel="🇨🇮 Côte d'Ivoire" itemValue="Côte d'Ivoire" />
<f:selectItem itemLabel="🇸🇳 Sénégal" itemValue="Sénégal" />
<f:selectItem itemLabel="🇲🇱 Mali" itemValue="Mali" />
<f:selectItem itemLabel="🇧🇫 Burkina Faso" itemValue="Burkina Faso" />
<f:selectItem itemLabel="🇬🇳 Guinée" itemValue="Guinée" />
<f:selectItem itemLabel="🇳🇪 Niger" itemValue="Niger" />
<f:selectItem itemLabel="🇹🇬 Togo" itemValue="Togo" />
<f:selectItem itemLabel="🇧🇯 Bénin" itemValue="Bénin" />
<f:selectItem itemLabel="🇬🇼 Guinée-Bissau" itemValue="Guinée-Bissau" />
<!-- Afrique Centrale (CEMAC / XAF) -->
<f:selectItem itemLabel="🇨🇲 Cameroun" itemValue="Cameroun" />
<f:selectItem itemLabel="🇬🇦 Gabon" itemValue="Gabon" />
<f:selectItem itemLabel="🇨🇬 Congo" itemValue="Congo" />
<f:selectItem itemLabel="🇨🇩 RD Congo" itemValue="RD Congo" />
<f:selectItem itemLabel="🇹🇩 Tchad" itemValue="Tchad" />
<f:selectItem itemLabel="🇨🇫 Centrafrique" itemValue="Centrafrique" />
<!-- Afrique du Nord -->
<f:selectItem itemLabel="🇲🇦 Maroc" itemValue="Maroc" />
<f:selectItem itemLabel="🇩🇿 Algérie" itemValue="Algérie" />
<f:selectItem itemLabel="🇹🇳 Tunisie" itemValue="Tunisie" />
<!-- Autres Afrique -->
<f:selectItem itemLabel="🇳🇬 Nigeria" itemValue="Nigeria" />
<f:selectItem itemLabel="🇬🇭 Ghana" itemValue="Ghana" />
<f:selectItem itemLabel="🇰🇪 Kenya" itemValue="Kenya" />
<f:selectItem itemLabel="🇿🇦 Afrique du Sud" itemValue="Afrique du Sud" />
<f:selectItem itemLabel="🇷🇼 Rwanda" itemValue="Rwanda" />
<f:selectItem itemLabel="🇪🇹 Éthiopie" itemValue="Éthiopie" />
<!-- Diaspora / Europe -->
<f:selectItem itemLabel="🇫🇷 France" itemValue="France" />
<f:selectItem itemLabel="🇧🇪 Belgique" itemValue="Belgique" />
<f:selectItem itemLabel="🌍 Autre" itemValue="Autre" />
</p:selectOneMenu>
<p:tooltip for="pays" value="Pays où l'organisation est enregistrée" position="top"/>
</div>
<!-- Coordonnées GPS -->
<div class="field col-12">
<p:outputLabel value="Coordonnées GPS" styleClass="font-semibold" />
<p:divider styleClass="my-3">
<span class="text-600 font-semibold">
<i class="pi pi-map-marker mr-2"/>
Coordonnées GPS (optionnel)
</span>
</p:divider>
</div>
<!-- Latitude -->
<div class="field col-12 md:col-6">
<p:outputLabel for="latitude" value="Latitude" />
<p:inputNumber id="latitude"
value="#{model.latitude}"
<p:outputLabel for="latitude" value="Latitude" styleClass="font-semibold"/>
<p:inputNumber id="latitude"
value="#{model.latitude}"
decimalPlaces="6"
minValue="-90"
maxValue="90"
placeholder="Ex: 5.316667" />
<small class="text-muted">Valeur entre -90 et 90</small>
<small class="text-500">
<i class="pi pi-compass mr-1"/>
Valeur entre -90 et 90 (ex: Abidjan ≈ 5.316667)
</small>
</div>
<!-- Longitude -->
<div class="field col-12 md:col-6">
<p:outputLabel for="longitude" value="Longitude" />
<p:inputNumber id="longitude"
value="#{model.longitude}"
<p:outputLabel for="longitude" value="Longitude" styleClass="font-semibold"/>
<p:inputNumber id="longitude"
value="#{model.longitude}"
decimalPlaces="6"
minValue="-180"
maxValue="180"
placeholder="Ex: -4.033333" />
<small class="text-muted">Valeur entre -180 et 180</small>
<small class="text-500">
<i class="pi pi-compass mr-1"/>
Valeur entre -180 et 180 (ex: Abidjan ≈ -4.033333)
</small>
</div>
</div>
</p:fieldset>
@@ -247,63 +430,103 @@
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- SECTION 4 : STRUCTURE & HIÉRARCHIE -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<p:fieldset legend="Structure &amp; Hiérarchie" toggleable="true" collapsed="true" styleClass="mb-3">
<p:fieldset legend="🏢 Structure &amp; Hiérarchie" toggleable="true" collapsed="true" styleClass="mb-3">
<div class="formgrid grid">
<!-- Statut juridique -->
<div class="field col-12">
<p:outputLabel value="Statut juridique" styleClass="font-semibold block mb-3" />
<p:outputLabel value="Statut juridique" styleClass="font-semibold block mb-3">
<i class="pi pi-building text-primary mr-2"/>
</p:outputLabel>
</div>
<div class="field col-12 md:col-4">
<div class="field-checkbox">
<p:selectBooleanCheckbox id="publique" value="#{model.organisationPublique}" />
<p:outputLabel for="publique" value="Organisation publique" styleClass="ml-2" />
<p:outputLabel for="publique" value="Organisation publique" styleClass="ml-2 font-semibold" />
</div>
<small class="text-muted block mt-1">Institution étatique ou gouvernementale</small>
<small class="text-500 block mt-1">
<i class="pi pi-shield mr-1"/>
Institution étatique ou gouvernementale
</small>
</div>
<div class="field col-12 md:col-4">
<div class="field-checkbox">
<p:selectBooleanCheckbox id="accepteMembres" value="#{model.accepteNouveauxMembres}" />
<p:outputLabel for="accepteMembres" value="Recrutement ouvert" styleClass="ml-2" />
<p:outputLabel for="accepteMembres" value="Recrutement ouvert" styleClass="ml-2 font-semibold" />
</div>
<small class="text-muted block mt-1">Accepte de nouveaux membres</small>
<small class="text-500 block mt-1">
<i class="pi pi-user-plus mr-1"/>
Accepte de nouveaux membres actuellement
</small>
</div>
<div class="field col-12 md:col-4">
<div class="field-checkbox">
<p:selectBooleanCheckbox id="cotisationObl" value="#{model.cotisationObligatoire}" />
<p:outputLabel for="cotisationObl" value="Cotisation obligatoire" styleClass="ml-2" />
<p:outputLabel for="cotisationObl" value="Cotisation obligatoire" styleClass="ml-2 font-semibold" />
</div>
<small class="text-muted block mt-1">Adhésion payante requise</small>
<small class="text-500 block mt-1">
<i class="pi pi-money-bill mr-1"/>
Adhésion payante requise pour les membres
</small>
</div>
<!-- Séparateur visuel -->
<div class="field col-12">
<p:divider />
<p:outputLabel value="Rattachement hiérarchique" styleClass="font-semibold block mb-3" />
<p:divider styleClass="my-3">
<span class="text-600 font-semibold">
<i class="pi pi-sitemap mr-2"/>
Rattachement hiérarchique
</span>
</p:divider>
</div>
<!-- Organisation parente -->
<div class="field col-12 md:col-9">
<p:outputLabel for="orgParente" value="Organisation parente" styleClass="font-semibold" />
<p:inputText id="orgParente"
value="#{model.organisationParenteId}"
maxlength="36"
placeholder="Sélectionner ou saisir l'identifiant de l'organisation mère" />
<small class="text-muted">Laisser vide si l'organisation est indépendante ou au sommet de la hiérarchie</small>
<p:autoComplete id="orgParente"
value="#{model.organisationParenteId}"
completeMethod="#{completionBean.rechercherOrganisations}"
var="org"
itemLabel="#{org.nom}"
itemValue="#{org.id}"
placeholder="Rechercher une organisation parente..."
minQueryLength="2"
queryDelay="300"
forceSelection="true"
dropdown="true"
scrollHeight="300"
emptyMessage="Aucune organisation trouvée">
<p:column headerText="Nom" style="width:60%">
<h:outputText value="#{org.nom}" styleClass="font-semibold"/>
</p:column>
<p:column headerText="Type" style="width:40%">
<h:outputText value="#{org.typeLibelle}" styleClass="text-500" />
</p:column>
</p:autoComplete>
<p:message for="orgParente" />
<p:tooltip for="orgParente" value="Recherchez et sélectionnez l'organisation mère si cette organisation est une sous-structure. Laissez vide si c'est une organisation racine." position="top"/>
<small class="text-500">
<i class="pi pi-sitemap mr-1"/>
Laisser vide si l'organisation est indépendante ou au sommet de la hiérarchie
</small>
</div>
<!-- Niveau hiérarchique -->
<div class="field col-12 md:col-3">
<p:outputLabel for="niveau" value="Niveau" styleClass="font-semibold" />
<p:inputNumber id="niveau"
value="#{model.niveauHierarchique}"
<p:outputLabel for="niveau" value="Niveau hiérarchique" styleClass="font-semibold" />
<p:inputNumber id="niveau"
value="#{model.niveauHierarchique}"
decimalPlaces="0"
minValue="0"
maxValue="10"
placeholder="0" />
<small class="text-muted">0 = Niveau national/racine<br/>1 = Niveau régional<br/>2+ = Niveaux locaux</small>
<small class="text-500">
0 = National/Racine<br/>
1 = Régional<br/>
2+ = Niveaux locaux
</small>
</div>
</div>
</p:fieldset>
@@ -311,26 +534,34 @@
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- SECTION 5 : MEMBRES & GOUVERNANCE -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<p:fieldset legend="Membres &amp; Gouvernance" toggleable="true" collapsed="true" styleClass="mb-3">
<p:fieldset legend="👥 Membres &amp; Gouvernance" toggleable="true" collapsed="true" styleClass="mb-3">
<div class="formgrid grid">
<!-- Nombre de membres -->
<div class="field col-12 md:col-6">
<p:outputLabel for="nbMembres" value="Nombre de membres" styleClass="font-semibold" />
<p:inputNumber id="nbMembres"
value="#{model.nombreMembres}"
<p:inputNumber id="nbMembres"
value="#{model.nombreMembres}"
decimalPlaces="0"
minValue="0"
placeholder="0" />
<small class="text-500">
<i class="pi pi-users mr-1"/>
Nombre total de membres actifs de l'organisation
</small>
</div>
<!-- Nombre d'administrateurs -->
<div class="field col-12 md:col-6">
<p:outputLabel for="nbAdmins" value="Nombre d'administrateurs" styleClass="font-semibold" />
<p:inputNumber id="nbAdmins"
value="#{model.nombreAdministrateurs}"
<p:inputNumber id="nbAdmins"
value="#{model.nombreAdministrateurs}"
decimalPlaces="0"
minValue="0"
placeholder="0" />
<small class="text-500">
<i class="pi pi-user-edit mr-1"/>
Nombre de membres du bureau exécutif ou du conseil d'administration
</small>
</div>
</div>
</p:fieldset>
@@ -338,68 +569,114 @@
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- SECTION 6 : BUDGET & FINANCES -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<p:fieldset legend="Budget &amp; Finances" toggleable="true" collapsed="true" styleClass="mb-3">
<p:fieldset legend="💰 Budget &amp; Finances" toggleable="true" collapsed="true" styleClass="mb-3">
<div class="formgrid grid">
<!-- Devise — placée EN PREMIER pour piloter les décimales des champs montants -->
<div class="field col-12 md:col-3">
<p:outputLabel for="devise" value="Devise" styleClass="font-semibold" />
<p:selectOneMenu id="devise"
value="#{model.devise}">
<!-- Devises africaines uniquement -->
<!-- Franc CFA : pas de centimes (0 décimale) -->
<f:selectItem itemLabel="XOF — Franc CFA (UEMOA)" itemValue="XOF" />
<f:selectItem itemLabel="XAF — Franc CFA (CEMAC)" itemValue="XAF" />
<!-- Afrique du Nord -->
<f:selectItem itemLabel="MAD — Dirham marocain" itemValue="MAD" />
<f:selectItem itemLabel="DZD — Dinar algérien" itemValue="DZD" />
<f:selectItem itemLabel="TND — Dinar tunisien" itemValue="TND" />
<!-- Autres devises africaines (2 décimales) -->
<f:selectItem itemLabel="NGN — Naira nigérian" itemValue="NGN" />
<f:selectItem itemLabel="GHS — Cedi ghanéen" itemValue="GHS" />
<f:selectItem itemLabel="KES — Shilling kényan" itemValue="KES" />
<f:selectItem itemLabel="ZAR — Rand sud-africain" itemValue="ZAR" />
<p:ajax event="change" update="panelBudget panelCotisation" />
</p:selectOneMenu>
<p:tooltip for="devise" value="Code ISO 4217 de la devise (3 lettres)" position="top"/>
<small class="text-500">
<i class="pi pi-info-circle mr-1"/>
XOF/XAF (Franc CFA) : entiers, sans centimes
</small>
</div>
<!-- Budget annuel -->
<div class="field col-12 md:col-5">
<p:outputLabel for="budget" value="Budget annuel" styleClass="font-semibold" />
<p:inputNumber id="budget"
value="#{model.budgetAnnuel}"
decimalPlaces="2"
minValue="0"
placeholder="0.00" />
</div>
<!-- Devise -->
<div class="field col-12 md:col-2">
<p:outputLabel for="devise" value="Devise" styleClass="font-semibold" />
<p:inputText id="devise"
value="#{model.devise}"
maxlength="3"
placeholder="XOF"
style="text-transform: uppercase;" />
<small class="text-muted">Code ISO (ex: XOF, EUR)</small>
<h:panelGroup id="panelBudget" layout="block">
<p:inputNumber id="budget"
value="#{model.budgetAnnuel}"
decimalPlaces="#{model.devise == 'XOF' or model.devise == 'XAF' ? 0 : 2}"
minValue="0"
placeholder="#{model.devise == 'XOF' or model.devise == 'XAF' ? '0' : '0,00'}"
decimalSeparator=","
thousandSeparator=" " />
</h:panelGroup>
<small class="text-500">
<i class="pi pi-chart-line mr-1"/>
Budget prévisionnel ou réalisé de l'année en cours
</small>
</div>
<!-- Montant cotisation -->
<div class="field col-12 md:col-5">
<p:outputLabel for="montantCotisation" value="Cotisation annuelle" styleClass="font-semibold" />
<p:inputNumber id="montantCotisation"
value="#{model.montantCotisationAnnuelle}"
decimalPlaces="2"
minValue="0"
placeholder="0.00" />
<div class="field col-12 md:col-4">
<p:outputLabel for="montantCotisation" value="Cotisation annuelle (membre)" styleClass="font-semibold" />
<h:panelGroup id="panelCotisation" layout="block">
<p:inputNumber id="montantCotisation"
value="#{model.montantCotisationAnnuelle}"
decimalPlaces="#{model.devise == 'XOF' or model.devise == 'XAF' ? 0 : 2}"
minValue="0"
placeholder="#{model.devise == 'XOF' or model.devise == 'XAF' ? '0' : '0,00'}"
decimalSeparator=","
thousandSeparator=" " />
</h:panelGroup>
<small class="text-500">
<i class="pi pi-wallet mr-1"/>
Montant annuel de la cotisation par membre (si applicable)
</small>
</div>
</div>
</p:fieldset>
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- SECTION 7 : MISSION & ACTIVITÉS -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<p:fieldset legend="Mission &amp; Activités" toggleable="true" collapsed="true" styleClass="mb-3">
<p:fieldset legend="🎯 Mission &amp; Activités" toggleable="true" collapsed="true" styleClass="mb-3">
<div class="formgrid grid">
<!-- Objectifs -->
<div class="field col-12">
<p:outputLabel for="objectifs" value="Objectifs stratégiques" styleClass="font-semibold" />
<p:inputTextarea id="objectifs"
value="#{model.objectifs}"
rows="4"
<p:inputTextarea id="objectifs"
value="#{model.objectifs}"
rows="4"
maxlength="2000"
placeholder="Décrivez les objectifs principaux de l'organisation..."
autoResize="false" />
<small class="text-muted">#{2000 - (empty model.objectifs ? 0 : model.objectifs.length())} caractères restants</small>
placeholder="Décrivez les objectifs principaux de l'organisation, sa vision et ses buts stratégiques..."
autoResize="false">
<f:validateLength maximum="2000" />
</p:inputTextarea>
<p:message for="objectifs" />
<small class="text-500">
<i class="pi pi-flag mr-1"/>
Maximum 2000 caractères
</small>
</div>
<!-- Activités principales -->
<div class="field col-12">
<p:outputLabel for="activites" value="Activités principales" styleClass="font-semibold" />
<p:inputTextarea id="activites"
value="#{model.activitesPrincipales}"
rows="4"
<p:inputTextarea id="activites"
value="#{model.activitesPrincipales}"
rows="4"
maxlength="2000"
placeholder="Décrivez les activités et programmes mis en œuvre..."
autoResize="false" />
<small class="text-muted">#{2000 - (empty model.activitesPrincipales ? 0 : model.activitesPrincipales.length())} caractères restants</small>
placeholder="Décrivez les activités, programmes et initiatives concrètes mis en œuvre par l'organisation..."
autoResize="false">
<f:validateLength maximum="2000" />
</p:inputTextarea>
<p:message for="activites" />
<small class="text-500">
<i class="pi pi-list mr-1"/>
Maximum 2000 caractères
</small>
</div>
</div>
</p:fieldset>
@@ -407,28 +684,40 @@
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- SECTION 8 : PARTENARIATS & CERTIFICATIONS -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<p:fieldset legend="Partenariats &amp; Certifications" toggleable="true" collapsed="true" styleClass="mb-3">
<p:fieldset legend="🤝 Partenariats &amp; Certifications" toggleable="true" collapsed="true" styleClass="mb-3">
<div class="formgrid grid">
<!-- Certifications / Labels -->
<div class="field col-12 md:col-6">
<p:outputLabel for="certifications" value="Certifications / Labels" styleClass="font-semibold" />
<p:inputTextarea id="certifications"
value="#{model.certifications}"
rows="3"
<p:inputTextarea id="certifications"
value="#{model.certifications}"
rows="3"
maxlength="500"
placeholder="Ex: ISO 9001, Label RSE..."
autoResize="false" />
placeholder="Ex: ISO 9001:2015, Label RSE, Certification Qualité..."
autoResize="false">
<f:validateLength maximum="500" />
</p:inputTextarea>
<small class="text-500">
<i class="pi pi-verified mr-1"/>
Maximum 500 caractères
</small>
</div>
<!-- Partenaires principaux -->
<div class="field col-12 md:col-6">
<p:outputLabel for="partenaires" value="Partenaires principaux" styleClass="font-semibold" />
<p:inputTextarea id="partenaires"
value="#{model.partenaires}"
rows="3"
<p:inputTextarea id="partenaires"
value="#{model.partenaires}"
rows="3"
maxlength="1000"
placeholder="Liste des partenaires stratégiques..."
autoResize="false" />
placeholder="Liste des partenaires stratégiques, bailleurs, organisations partenaires..."
autoResize="false">
<f:validateLength maximum="1000" />
</p:inputTextarea>
<small class="text-500">
<i class="pi pi-users mr-1"/>
Maximum 1000 caractères
</small>
</div>
</div>
</p:fieldset>
@@ -436,21 +725,26 @@
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- SECTION 9 : NOTES ADMINISTRATIVES -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<p:fieldset legend="Notes administratives" toggleable="true" collapsed="true" styleClass="mb-3">
<p:fieldset legend="📝 Notes administratives" toggleable="true" collapsed="true" styleClass="mb-3">
<div class="formgrid grid">
<div class="field col-12">
<p:outputLabel for="notes" value="Notes internes" styleClass="font-semibold" />
<p:inputTextarea id="notes"
value="#{model.notes}"
rows="4"
<p:inputTextarea id="notes"
value="#{model.notes}"
rows="4"
maxlength="1000"
placeholder="Notes réservées à l'usage administratif interne..."
autoResize="false" />
<small class="text-muted">Ces notes ne sont visibles que par les administrateurs</small>
placeholder="Notes réservées à l'usage administratif interne, remarques, historique..."
autoResize="false">
<f:validateLength maximum="1000" />
</p:inputTextarea>
<small class="text-500">
<i class="pi pi-lock mr-1"/>
Ces notes ne sont visibles que par les administrateurs - Maximum 1000 caractères
</small>
</div>
</div>
</p:fieldset>
</div>
</ui:fragment>
</ui:fragment>