Ajout des commentaires dans le code

This commit is contained in:
PTA
2026-08-20 13:22:49 +02:00
parent 4201894e28
commit 5d8c0993a6
154 changed files with 5071 additions and 47 deletions

View File

@@ -1,3 +1,24 @@
/**
* Éditeur de semaine type (`PTA.modules.weekTemplateEditor`, V7.10).
*
* Permet de COMPOSER un modèle lundi→vendredi directement, sans partir d'une
* semaine réellement planifiée. À distinguer de `week-templates.js`, qui
* enregistre et applique les modèles.
*
* MODES EXCLUSIFS DE planning.php — ce module pilote la bascule entre l'éditeur
* de semaine type (ouvert par défaut) et la saisie semaine par semaine, via
* `setMode()`. `page-bootstrap.js` force le second dès que l'URL porte `week` ou
* `focus` : un utilisateur venant corriger un planning identifié doit atterrir
* sur la saisie, pas sur l'éditeur de modèles.
*
* ÉTAT LOCAL — `draft` est une Map indexée par jour (1 à 5), contenant les
* plages en cours d'édition. Elle est indépendante de `PTA.state`, qui décrit la
* semaine réellement planifiée : un modèle n'est pas un planning.
*
* Le DOM fait autorité entre deux rendus : `syncDraftFromDom()` est appelé avant
* toute opération sur `draft`, car l'utilisateur peut avoir tapé dans les champs
* sans qu'aucun événement n'ait été traité.
*/
(() => {
'use strict';
@@ -40,16 +61,22 @@
let currentTemplateId = null;
let loading = false;
/** Message propre à l'éditeur, distinct du bandeau global de la page. */
function setMessage(text = '', type = 'info') {
if (!messageElement) return;
messageElement.className = text ? `message message-${type}` : 'message';
messageElement.textContent = text;
}
/** Agent auquel le modèle appartient ; un modèle est toujours nominatif. */
function agentId() {
return Number(agentSelect?.value || 0) || null;
}
/**
* Lieu proposé par défaut sur une nouvelle plage : celui sélectionné dans
* l'écran, sinon le premier disponible. Une plage sans lieu est refusée.
*/
function defaultStructureId() {
const selected = Number(structureSelect?.value || 0);
if (selected) return selected;
@@ -67,6 +94,7 @@
return Number(state.currentAgent?.droit_enveloppe_formation ?? 1) !== 0;
}
/** Motifs proposables à CET agent, « Formation » exclue s'il n'y a pas droit. */
function motifs() {
return window.PTA_CONFIG?.motifs || [];
}
@@ -79,6 +107,7 @@
return motif ? Number(motif.id) : null;
}
/** Options du sélecteur de motif d'une plage. */
function motifOptionsHtml(selectedMotifId = null) {
return motifs()
// Un motif interdit reste proposé s'il est déjà celui de la plage :
@@ -94,6 +123,11 @@
.join('');
}
/**
* Options du sélecteur de lieu.
* Un modèle peut porter des plages sur PLUSIEURS lieux : c'est ce qui le
* réserve au Service Enfance.
*/
function structureOptionsHtml(selectedStructureId = null) {
return [...(structureSelect?.querySelectorAll('option[value]:not([value=""])') || [])]
.map((option) => {
@@ -103,6 +137,11 @@
.join('');
}
/**
* Cibles de recopie d'un jour : chacun des autres jours, ou tous d'un coup.
* Un agent a souvent le même rythme du lundi au vendredi ; recopier évite de
* ressaisir cinq fois la même chose.
*/
function copyTargetOptionsHtml(weekday) {
const others = DAYS.filter((day) => day.weekday !== weekday);
return [
@@ -112,16 +151,19 @@
].join('');
}
/** Plages d'un jour du brouillon (tableau vide si le jour est libre). */
function rangesFor(weekday) {
return draft.get(weekday) || [];
}
/** Brouillon vierge : les cinq jours présents, tous sans plage. */
function emptyDraft() {
const next = new Map();
DAYS.forEach((day) => next.set(day.weekday, []));
return next;
}
/** Plage neuve, horaires vides mais motif et lieu déjà proposés. */
function newRange() {
return {
heure_debut: '',
@@ -131,15 +173,21 @@
};
}
/** Durée d'une plage ; 0 tant qu'elle est incomplète, jamais négatif. */
function rangeMinutes(range) {
if (!range.heure_debut || !range.heure_fin) return 0;
return Math.max(0, timeToMinutes(range.heure_fin) - timeToMinutes(range.heure_debut));
}
/** Total des minutes d'un jour. */
function dayMinutes(weekday) {
return rangesFor(weekday).reduce((total, range) => total + rangeMinutes(range), 0);
}
/**
* Totaux du modèle : toutes les minutes, et celles qui s'imputeraient sur le
* quota. La distinction compte, un modèle pouvant contenir des congés.
*/
function totals() {
let all = 0;
let counted = 0;
@@ -153,6 +201,7 @@
return { all, counted };
}
/** Balisage d'une plage : motif, lieu, horaires, suppression. */
function rangeHtml(weekday, index, range) {
const label = `${DAYS.find((day) => day.weekday === weekday)?.label || 'Jour'} plage ${index + 1}`;
return `
@@ -182,6 +231,11 @@
</div>`;
}
/**
* Redessine toute la grille depuis `draft`, puis met à jour les totaux.
* Rendu complet plutôt que ciblé : le nombre de plages par jour varie sans
* cesse, et une mise à jour fine serait plus fragile.
*/
function render() {
if (!gridElement) return;
@@ -224,10 +278,15 @@
refreshButtons();
}
/** Vrai si le brouillon contient au moins une plage. */
function hasAnyRange() {
return DAYS.some((day) => rangesFor(day.weekday).length > 0);
}
/**
* Active ou désactive les actions selon l'état : agent choisi, nom saisi,
* modèle non vide, absence d'appel réseau en cours.
*/
function refreshButtons() {
const ready = Boolean(agentId());
const named = Boolean(nameInput?.value.trim());
@@ -242,6 +301,7 @@
if (applyWeekButton) applyWeekButton.disabled = !ready || currentTemplateId === null || loading;
}
/** Remplit le sélecteur des modèles existants de l'agent. */
function renderTemplateSelect() {
if (!templateSelect) return;
const options = ['<option value="">➕ Nouvelle semaine type</option>'];
@@ -253,6 +313,7 @@
templateSelect.innerHTML = options.join('');
}
/** Recharge la liste des modèles de l'agent courant. */
async function loadTemplates() {
const agent = agentId();
if (!agent) {
@@ -274,6 +335,11 @@
}
}
/**
* Charge un modèle existant dans le brouillon pour le modifier.
* Les plages sont réparties par jour dans la Map ; les horaires arrivent en
* HH:MM, format attendu par les champs `type="time"`.
*/
async function openTemplate(templateId) {
const agent = agentId();
if (!agent || !templateId) {
@@ -361,6 +427,10 @@
return null;
}
/**
* Met à plat la Map en la liste attendue par l'API : un objet par plage,
* portant son numéro de jour.
*/
function flatEntries() {
const entries = [];
DAYS.forEach((day) => {
@@ -377,6 +447,12 @@
return entries;
}
/**
* Enregistre le modèle.
* Un 409 signifie qu'un modèle porte déjà ce nom : l'utilisateur se voit
* proposer le remplacement, ce qui rappelle cette fonction avec
* `replace = true`.
*/
async function save(replace = false) {
const agent = agentId();
const name = nameInput?.value.trim() || '';
@@ -430,6 +506,7 @@
}
}
/** Supprime le modèle ouvert, après confirmation. */
async function removeTemplate() {
const agent = agentId();
if (!agent || currentTemplateId === null) return;
@@ -466,6 +543,12 @@
}
}
/**
* Recopie les plages d'un jour vers un autre, ou vers tous les autres.
*
* Les plages sont CLONÉES (`{ ...range }`) : sans cette copie, les jours
* partageraient les mêmes objets et modifier l'un modifierait les autres.
*/
function copyDay(sourceWeekday, target) {
syncDraftFromDom();
const source = rangesFor(sourceWeekday);
@@ -487,6 +570,13 @@
render();
}
/**
* Bascule entre les deux modes exclusifs de planning.php.
* Appelée aussi par `page-bootstrap.js`, qui force le mode « week » sur les
* liens profonds portant une semaine ou un focus.
*
* @param {'template'|'week'} mode
*/
function setMode(mode) {
const isTemplate = mode === 'template';
if (panel) panel.hidden = !isTemplate;
@@ -497,6 +587,11 @@
modeWeekButton?.setAttribute('aria-selected', String(!isTemplate));
}
/**
* Changement d'agent : le brouillon est vidé et les modèles rechargés.
* Un modèle appartient à un seul agent, celui de l'agent précédent n'a plus
* de sens.
*/
function onAgentChanged() {
currentTemplateId = null;
draft = emptyDraft();
@@ -505,6 +600,7 @@
loadTemplates().then(render);
}
/** Point d'entrée appelé par page-bootstrap.js. */
function init() {
draft = emptyDraft();