Ajout des commentaires dans le code
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user