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,7 +1,23 @@
/**
* Vue annuelle d'un agent (`PTA.modules.annualOverview`), écran annual.php.
*
* Affiche les douze mois de l'année civile et permet de valider TOUTE l'année en
* une opération.
*
* PARTICULARITÉ — ce module ne calcule presque rien. Le calendrier arrive du
* serveur entièrement préparé (classes CSS, libellés, infobulles, totaux) :
* voir AnnualOverviewModel. Le rôle du JavaScript se limite à assembler le HTML
* et à gérer les interactions. C'est ce qui garantit un rendu identique à
* l'écran, à l'impression et en plein écran.
*
* Chaque jour est cliquable et ouvre l'écran de saisie sur la bonne semaine et
* le bon lieu.
*/
(() => {
'use strict';
const PTA = window.PTA;
// core.js absent : la page reste lisible, simplement non interactive.
if (!PTA) return;
const { readJsonResponse, apiErrorMessage, escapeHtml, formatDate } = PTA.utils;
const el = (id) => document.getElementById(id);
@@ -10,6 +26,7 @@
const escapeAttr = escapeHtml;
let current = null;
/** Message dans le bandeau de la page. */
function show(text, type = 'info') {
const box = el('annual-message');
if (!box) return;
@@ -17,17 +34,29 @@
box.textContent = text;
}
/**
* Horodatage SQL « 2026-02-17 09:30:00 » → date et heure en français.
* Le remplacement de l'espace par « T » rend la chaîne conforme à ISO 8601,
* seule forme que tous les navigateurs analysent de la même façon.
*/
function formatGenerated(value) {
const date = new Date(String(value).replace(' ', 'T'));
return Number.isNaN(date.getTime()) ? value : date.toLocaleString('fr-FR', { dateStyle: 'short', timeStyle: 'short' });
}
/** Libellé du type de contrat. */
function formatContract(value) {
if (value === 'PERMANENT') return 'Permanent';
if (value === 'TEMPORAIRE') return 'Temporaire';
return value || 'Non renseigné';
}
/**
* Lien profond d'une journée vers l'écran de saisie.
* Le lieu retenu est celui du premier créneau du jour, à défaut le
* rattachement de l'agent. `notice` rappelle d'où vient l'utilisateur, sans
* quoi il arriverait sur une semaine sans savoir pourquoi.
*/
function buildPlanningUrl(day) {
const agentId = Number(current?.agent?.id_agent || 0);
const structureId = Number(day.structure_id || current?.agent?.id_structure || 0);
@@ -38,6 +67,10 @@
return `planning.php?${params.toString()}`;
}
/**
* Une cellule du calendrier. Contenu, classe et infobulle viennent du
* serveur ; seule la marque « brouillon » est ajoutée ici.
*/
function cellHtml(cell, day) {
const content = cell.display ? escapeHtml(cell.display) : ' ';
const title = cell.title ? ` title="${escapeAttr(cell.title)}"` : '';
@@ -45,6 +78,7 @@
return `<td class="annual-data-cell ${escapeHtml(cell.class || '')}${draftClass}" data-date="${escapeHtml(day.date)}"${title}>${content}</td>`;
}
/** Tableau d'un mois : une ligne par jour, quatre colonnes AP/PP/AE/PE. */
function monthHtml(month) {
const rows = month.days.map((day) => {
const classes = [
@@ -80,6 +114,7 @@
</article>`;
}
/** Rappel du ou des quotas couvrant l'année affichée. */
function quotaPeriodsHtml(periods) {
return periods.map((period) => `
<article class="annual-quota-chip">
@@ -89,6 +124,7 @@
</article>`).join('');
}
/** Rend l'ensemble de la vue et mémorise les données dans `current`. */
function render(data) {
current = data;
const agent = data.agent || {};
@@ -123,6 +159,7 @@
if (el('annual-validate-year')) el('annual-validate-year').disabled = false;
}
/** Charge la vue annuelle de l'agent et de l'année sélectionnés. */
async function load(options = {}) {
const agentId = Number(el('annual-agent')?.value || 0);
const year = Number(el('annual-year')?.value || 0);
@@ -147,6 +184,7 @@
}
}
/** Lien de correction d'une semaine en erreur, ouvert dans un nouvel onglet. */
function validationPlanningUrl(planning) {
const agentId = Number(current?.agent?.id_agent || el('annual-agent')?.value || 0);
const week = `${Number(planning.annee)}-W${String(planning.numero_semaine).padStart(2, '0')}`;
@@ -160,6 +198,11 @@
return `planning.php?${params.toString()}`;
}
/**
* Rend l'échec de validation annuelle : AUCUNE semaine n'a été validée.
* Chaque semaine fautive est listée avec ses erreurs et un lien direct pour
* la corriger — c'est ce qui rend l'échec exploitable.
*/
function renderValidationIssues(data) {
const container = el('annual-validation-result');
if (!container) return;
@@ -188,6 +231,7 @@
</div>`;
}
/** Rend l'avertissement de quota dépassé, en attente de confirmation. */
function renderValidationWarning(data) {
const container = el('annual-validation-result');
if (!container) return;
@@ -198,6 +242,7 @@
</div>`;
}
/** Rend le compte rendu d'une validation annuelle réussie. */
function renderValidationSuccess(data) {
const container = el('annual-validation-result');
if (!container) return;
@@ -208,6 +253,17 @@
</div>`;
}
/**
* Lance la validation de l'année entière.
*
* Vérifie d'abord que la vue affichée correspond bien à l'agent et à l'année
* sélectionnés : sans ce contrôle, changer le sélecteur sans recharger
* validerait une année différente de celle qu'on regarde.
*
* Trois issues, alignées sur AnnualOverviewController::validateYear() :
* 422 erreurs, 409 quota dépassé (rappel avec `forceQuota = true` après
* confirmation), 200 validé.
*/
async function validateWholeYear(forceQuota = false) {
const agentId = Number(el('annual-agent')?.value || 0);
const year = Number(el('annual-year')?.value || 0);
@@ -266,12 +322,22 @@
}
}
/**
* Clic sur une ligne du calendrier : suit le lien porté par `data-url`.
* Délégation sur le conteneur, le calendrier étant redessiné à chaque
* chargement.
*/
function handleCalendarClick(event) {
const row = event.target.closest('tr[data-url]');
if (!row) return;
window.location.href = row.dataset.url;
}
/**
* Bascule le calendrier en plein écran — utile sur douze mois affichés d'un
* coup. L'API Fullscreen peut être refusée (navigateur, iframe, réglages) :
* l'échec est signalé sans conséquence pour le reste de la page.
*/
async function toggleFullscreen() {
const target = el('annual-content');
if (!target) return;