Ajout des commentaires dans le code
This commit is contained in:
@@ -1,3 +1,21 @@
|
||||
/**
|
||||
* Écran d'administration des référentiels (`PTA.modules.administration`) :
|
||||
* agents, lieux d'affectation, postes et horaires de couverture.
|
||||
*
|
||||
* PRINCIPE DE MISE À JOUR — la page ne se recharge JAMAIS après une création ou
|
||||
* une modification. Les nouveaux éléments sont insérés à la main dans TOUS les
|
||||
* sélecteurs et tableaux concernés, d'où les fonctions `add*Everywhere()`. Un
|
||||
* lieu créé ici doit apparaître immédiatement dans les listes de l'écran de
|
||||
* saisie, de la vue par lieu, du formulaire agent et des règles de couverture.
|
||||
*
|
||||
* CONSÉQUENCE POUR LA MAINTENANCE : ajouter un sélecteur de lieu ou d'agent
|
||||
* ailleurs dans l'application impose de l'inscrire dans `addStructureEverywhere()`
|
||||
* ou `addAgentEverywhere()`, faute de quoi il restera périmé jusqu'au prochain
|
||||
* rechargement complet de la page.
|
||||
*
|
||||
* Cet écran charge aussi le module `coverage`, les règles de couverture étant
|
||||
* un référentiel de lieu.
|
||||
*/
|
||||
(() => {
|
||||
'use strict';
|
||||
|
||||
@@ -14,6 +32,11 @@
|
||||
|
||||
const typeLabel = (type) => type === 'EXTRASCOLAIRE' ? 'Extrascolaire' : 'Périscolaire';
|
||||
|
||||
/**
|
||||
* Enregistre le lieu de rattachement principal d'un agent depuis sa ligne de
|
||||
* tableau. Un lieu vide est valide : il détache l'agent, qui reste
|
||||
* planifiable ponctuellement ailleurs.
|
||||
*/
|
||||
async function saveAgentStructure(button) {
|
||||
const row = button.closest('tr[data-agent-id]');
|
||||
const select = row?.querySelector('.agent-structure-select');
|
||||
@@ -51,6 +74,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enregistre le type d'un lieu, puis répercute le nouveau libellé dans TOUS
|
||||
* les sélecteurs de la page — le type fait partie du texte affiché.
|
||||
*/
|
||||
async function saveStructureType(button) {
|
||||
const row = button.closest('tr[data-structure-id]');
|
||||
const select = row?.querySelector('.structure-type-select');
|
||||
@@ -91,6 +118,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une option à un `<select>`, ou met à jour son libellé si elle
|
||||
* existe déjà. Idempotent : appelable sans risque de doublon.
|
||||
*/
|
||||
function addOptionIfMissing(select, value, label, selected = false) {
|
||||
if (!select) return;
|
||||
let option = Array.from(select.options).find((item) => item.value === String(value));
|
||||
@@ -103,10 +134,18 @@
|
||||
if (selected) option.selected = true;
|
||||
}
|
||||
|
||||
/** Libellé unifié d'un lieu : « Nom — Type », identique partout. */
|
||||
function structureOptionLabel(structure) {
|
||||
return `${structure.nom} — ${typeLabel(structure.type_affectation)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insère un lieu nouvellement créé dans TOUS les sélecteurs de la page.
|
||||
*
|
||||
* La liste des sélecteurs est écrite en dur : tout nouvel écran ou modale
|
||||
* proposant un choix de lieu doit y être ajouté, sinon il ne verra pas les
|
||||
* lieux créés dans la session courante.
|
||||
*/
|
||||
function addStructureEverywhere(structure) {
|
||||
const id = Number(structure.id_structure);
|
||||
const label = structureOptionLabel(structure);
|
||||
@@ -126,6 +165,11 @@
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le libellé d'un lieu dans tous les `<select>` de la page après
|
||||
* un changement de type. Parcourt l'ensemble des sélecteurs plutôt qu'une
|
||||
* liste nommée : le libellé peut apparaître n'importe où.
|
||||
*/
|
||||
function updateStructureLabels(structureId, type) {
|
||||
const source = document.querySelector(`#structure-settings-body tr[data-structure-id="${Number(structureId)}"]`);
|
||||
const structureName = source?.querySelector('td strong')?.textContent?.trim();
|
||||
@@ -142,6 +186,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
/** Options de lieu, reconstruites depuis les lieux actuellement affichés. */
|
||||
function structureOptionsHtml(selectedStructureId = null) {
|
||||
const source = document.querySelector('#new-agent-structure');
|
||||
if (!source) return '<option value="">Sans lieu principal</option>';
|
||||
@@ -155,6 +200,7 @@
|
||||
return options.join('');
|
||||
}
|
||||
|
||||
/** Ajoute la ligne d'un lieu au tableau de paramétrage, sans doublon. */
|
||||
function addStructureSettingsRow(structure) {
|
||||
const body = document.querySelector('#structure-settings-body');
|
||||
if (!body || body.querySelector(`tr[data-structure-id="${Number(structure.id_structure)}"]`)) return;
|
||||
@@ -175,17 +221,27 @@
|
||||
body.appendChild(row);
|
||||
}
|
||||
|
||||
/** « Diplômé — Libellé », « Diplômé » ou « Non diplômé ». */
|
||||
function qualificationLabel(agent) {
|
||||
if (!Number(agent.est_diplome)) return 'Non diplômé';
|
||||
return agent.diplome_libelle ? `Diplômé — ${agent.diplome_libelle}` : 'Diplômé';
|
||||
}
|
||||
|
||||
/**
|
||||
* « 2026-02-17 » → « 17/02/2026 », « Non renseignée » si vide.
|
||||
* Découpage de chaîne plutôt que `new Date()`, qui décalerait la date.
|
||||
*/
|
||||
function formatContractDate(value) {
|
||||
if (!value) return 'Non renseignée';
|
||||
const [year, month, day] = String(value).split('-');
|
||||
return year && month && day ? `${day}/${month}/${year}` : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cellules d'une ligne d'agent.
|
||||
* Toutes les valeurs passent par escapeHtml() : elles viennent de la base et
|
||||
* sont insérées en HTML brut.
|
||||
*/
|
||||
function agentRowHtml(agent) {
|
||||
const structureLabel = agent.structure_nom || agent.structure_principale_nom || 'Sans lieu principal';
|
||||
return `
|
||||
@@ -200,6 +256,18 @@
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recopie la fiche de l'agent dans les `data-*` de sa ligne.
|
||||
*
|
||||
* Ces attributs sont la SOURCE de la modale d'édition : elle s'ouvre
|
||||
* uniquement à partir d'eux, sans nouvel appel réseau. Ajouter un champ
|
||||
* modifiable impose donc de l'ajouter ici ET dans
|
||||
* `openAgentProfileModal()`, sinon il s'ouvrira vide et sera écrasé à
|
||||
* l'enregistrement.
|
||||
*
|
||||
* Les noms sont en camelCase, conversion automatique des `data-*` par le DOM
|
||||
* (`data-date-debut-contrat` → `dataset.dateDebutContrat`).
|
||||
*/
|
||||
function setAgentRowData(row, agent) {
|
||||
row.dataset.agentId = String(Number(agent.id_agent));
|
||||
row.dataset.email = agent.email || '';
|
||||
@@ -216,6 +284,7 @@
|
||||
row.dataset.structureId = agent.id_structure ? String(Number(agent.id_structure)) : '';
|
||||
}
|
||||
|
||||
/** Insère un agent nouvellement créé dans les sélecteurs et le tableau. */
|
||||
function addAgentEverywhere(agent) {
|
||||
const overviewAgent = document.querySelector('#overview-agent');
|
||||
addOptionIfMissing(
|
||||
@@ -233,6 +302,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Le libellé du diplôme n'a de sens que si l'agent est diplômé : le champ
|
||||
* est désactivé et VIDÉ sinon, pour ne pas enregistrer une valeur résiduelle.
|
||||
*/
|
||||
function toggleDiplomaField(select, input) {
|
||||
if (!select || !input) return;
|
||||
const qualified = select.value === '1';
|
||||
@@ -240,6 +313,10 @@
|
||||
if (!qualified) input.value = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Ouvre la modale d'édition d'un agent, préremplie depuis les `data-*` de sa
|
||||
* ligne (voir `setAgentRowData()`).
|
||||
*/
|
||||
function openAgentProfileModal(row) {
|
||||
const modal = document.querySelector('#agent-profile-modal');
|
||||
if (!modal || !row) return;
|
||||
@@ -268,11 +345,17 @@
|
||||
modal.hidden = false;
|
||||
}
|
||||
|
||||
/** Ferme la modale d'édition d'un agent. */
|
||||
function closeAgentProfileModal() {
|
||||
const modal = document.querySelector('#agent-profile-modal');
|
||||
if (modal) modal.hidden = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enregistre la fiche d'un agent, puis met à jour sa ligne et ses `data-*`
|
||||
* avec la fiche renvoyée par le serveur — et non avec les valeurs saisies :
|
||||
* le serveur peut avoir normalisé ou refusé certains champs.
|
||||
*/
|
||||
async function saveAgentProfile(event) {
|
||||
event.preventDefault();
|
||||
const form = event.currentTarget;
|
||||
@@ -335,6 +418,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée un lieu d'affectation, puis le diffuse dans toute la page.
|
||||
* Un code déjà pris ressort en 409 et le message le dit explicitement.
|
||||
*/
|
||||
async function createStructure(event) {
|
||||
event.preventDefault();
|
||||
const form = event.currentTarget;
|
||||
@@ -376,6 +463,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée un agent, puis le diffuse dans toute la page.
|
||||
* Le serveur ouvre au passage son quota annuel et son PTA ; un matricule
|
||||
* déjà pris ressort en 409.
|
||||
*/
|
||||
async function createAgent(event) {
|
||||
event.preventDefault();
|
||||
const form = event.currentTarget;
|
||||
@@ -439,6 +531,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Point d'entrée appelé par page-bootstrap.js.
|
||||
* Les tableaux sont écoutés par DÉLÉGATION : leurs lignes sont ajoutées
|
||||
* dynamiquement et n'existent pas au moment où les écouteurs sont posés.
|
||||
*/
|
||||
function init() {
|
||||
document.querySelector('#agent-assignment-body')?.addEventListener('click', (event) => {
|
||||
const button = event.target.closest('.edit-agent-profile');
|
||||
|
||||
Reference in New Issue
Block a user