Ajout des commentaires dans le code
This commit is contained in:
@@ -1,6 +1,37 @@
|
||||
/**
|
||||
* Socle JavaScript commun — À CHARGER EN PREMIER, tous les autres modules en
|
||||
* dépendent.
|
||||
*
|
||||
* Il n'y a ni bundler ni module ES dans ce projet : chaque fichier est une IIFE,
|
||||
* et la seule interface entre eux est l'objet global `window.PTA` publié en fin
|
||||
* de fichier. Un module s'enregistre en écrivant dans `PTA.modules` :
|
||||
*
|
||||
* PTA.modules.agentOverview = { init, load };
|
||||
*
|
||||
* `page-bootstrap.js` appelle ensuite `init()` sur chaque module déclaré par le
|
||||
* PageController, et les modules se rafraîchissent entre eux par des appels
|
||||
* optionnels : `PTA.modules.autre?.refresh?.({ silent: true })`.
|
||||
*
|
||||
* Chemin des données : PHP publie `window.PTA_CONFIG` (layouts/footer.php) et
|
||||
* `window.PTA_PAGE_CONFIG` ; `PTA.config` en est la copie, et contient
|
||||
* notamment `csrfToken`, `motifs` et `permissions`.
|
||||
*
|
||||
* Rappel de versionnement : après toute modification d'un fichier JS ou CSS, il
|
||||
* faut incrémenter le `?v=` dans layouts/header.php, layouts/footer.php ET
|
||||
* Views/pages/role.php, sinon nginx (expires 7d) et les navigateurs continuent
|
||||
* de servir l'ancienne version.
|
||||
*/
|
||||
(() => {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Références DOM résolues UNE FOIS au chargement.
|
||||
*
|
||||
* Le même objet sert à toutes les pages : la plupart des entrées valent donc
|
||||
* `null` sur un écran donné. C'est voulu — d'où les `if (!element) return;`
|
||||
* systématiques dans les modules. Ces sélecteurs concernent principalement
|
||||
* l'éditeur de planning, seul écran assez partagé pour justifier un cache.
|
||||
*/
|
||||
const elements = {
|
||||
structureSelect: document.querySelector('#structure'),
|
||||
agentSelect: document.querySelector('#agent'),
|
||||
@@ -27,6 +58,14 @@
|
||||
quotaRemaining: document.querySelector('#quota-remaining'),
|
||||
};
|
||||
|
||||
/**
|
||||
* État de l'éditeur de planning, PARTAGÉ ET MUTABLE.
|
||||
*
|
||||
* Plusieurs modules le lisent et l'écrivent (planning-editor, week-templates,
|
||||
* week-template-editor). C'est la contrepartie assumée de l'absence de
|
||||
* framework : avant de modifier une de ces clés, vérifier qui d'autre s'en
|
||||
* sert.
|
||||
*/
|
||||
const state = {
|
||||
entries: [],
|
||||
otherStructureEntries: [],
|
||||
@@ -39,10 +78,15 @@
|
||||
savedCurrentCountedMinutes: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* Cache de données brutes qu'un module expose aux autres, pour éviter de
|
||||
* refaire un appel réseau déjà effectué.
|
||||
*/
|
||||
const shared = {
|
||||
lastAgentOverviewData: null,
|
||||
};
|
||||
|
||||
/** Semaine ouvrée : le modèle métier s'arrête au vendredi. */
|
||||
const dayNames = ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi'];
|
||||
|
||||
/**
|
||||
@@ -62,18 +106,40 @@
|
||||
.replaceAll("'", ''');
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche un message dans le bandeau de la page.
|
||||
* @param {string} text
|
||||
* @param {'info'|'success'|'error'|'warning'} type Suffixe de la classe CSS.
|
||||
*/
|
||||
function showMessage(text, type = 'info') {
|
||||
if (!elements.messageBox) return;
|
||||
elements.messageBox.className = `message message-${type}`;
|
||||
elements.messageBox.textContent = text;
|
||||
}
|
||||
|
||||
/** Vide le bandeau de message. */
|
||||
function clearMessage() {
|
||||
if (!elements.messageBox) return;
|
||||
elements.messageBox.className = 'message';
|
||||
elements.messageBox.textContent = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Lit une réponse d'API en JSON, en produisant un message LISIBLE quand ce
|
||||
* n'en est pas.
|
||||
*
|
||||
* Le corps est lu en texte avant d'être analysé : `response.json()` échouerait
|
||||
* sur une page d'erreur HTML avec un message incompréhensible. Ici, le HTML
|
||||
* est dépouillé de ses balises pour faire remonter le message PHP réel.
|
||||
*
|
||||
* Ce filet ne devrait jamais servir — `JsonResponse::bootstrap()` garantit
|
||||
* du JSON côté serveur — mais il reste la seule façon de diagnostiquer une
|
||||
* erreur survenue AVANT ce bootstrap (erreur de syntaxe, page nginx).
|
||||
*
|
||||
* @param {Response} response
|
||||
* @returns {Promise<object>}
|
||||
* @throws {Error} Réponse vide ou non-JSON.
|
||||
*/
|
||||
async function readJsonResponse(response) {
|
||||
const raw = await response.text();
|
||||
|
||||
@@ -102,11 +168,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose le message d'erreur à afficher à partir d'une réponse d'API.
|
||||
* `details` n'est présent que si le mode debug est actif côté serveur.
|
||||
*/
|
||||
function apiErrorMessage(data, fallback) {
|
||||
const base = data?.error || fallback;
|
||||
return data?.details ? `${base} Détail : ${data.details}` : base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Date → « AAAA-MM-JJ » en heure LOCALE.
|
||||
* `toISOString()` convertirait en UTC et décalerait la date d'un jour selon
|
||||
* l'heure et le fuseau.
|
||||
*/
|
||||
function toIsoDate(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
@@ -114,6 +189,18 @@
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* « 2026-W08 » → les cinq jours ouvrés de cette semaine ISO.
|
||||
*
|
||||
* Le calcul s'appuie sur la définition ISO 8601 : la semaine 1 est celle qui
|
||||
* contient le 4 JANVIER. On recule donc jusqu'au lundi de cette semaine-là,
|
||||
* puis on avance de (semaine - 1) × 7 jours. `getDay() || 7` ramène dimanche
|
||||
* de 0 à 7, la numérotation ISO commençant au lundi.
|
||||
*
|
||||
* @param {string} value Semaine au format de l'input HTML `type="week"`.
|
||||
* @returns {Array<{date: string, label: string, display: string}>} Vide si
|
||||
* le format ne correspond pas.
|
||||
*/
|
||||
function parseIsoWeek(value) {
|
||||
const match = /^(\d{4})-W(\d{2})$/.exec(value);
|
||||
if (!match) return [];
|
||||
@@ -136,11 +223,16 @@
|
||||
});
|
||||
}
|
||||
|
||||
/** « 09:30 » → 570. Comparer des minutes évite les pièges de tri de chaînes. */
|
||||
function timeToMinutes(time) {
|
||||
const [hours, minutes] = String(time).split(':').map(Number);
|
||||
return hours * 60 + minutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 450 → « 7 h 30 ». Gère les valeurs négatives (dépassement de quota) : le
|
||||
* signe est extrait avant la division.
|
||||
*/
|
||||
function formatDuration(minutes) {
|
||||
const sign = minutes < 0 ? '-' : '';
|
||||
const absolute = Math.abs(Math.round(minutes));
|
||||
@@ -149,12 +241,22 @@
|
||||
return `${sign}${hours} h ${String(mins).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* « 2026-02-17 » → « 17/02/2026 ».
|
||||
* Découpage de chaîne plutôt que `new Date()`, qui interpréterait la valeur
|
||||
* en UTC et pourrait afficher la veille.
|
||||
*/
|
||||
function formatDate(value) {
|
||||
if (!value) return '';
|
||||
const [year, month, day] = String(value).split('-');
|
||||
return `${day}/${month}/${year}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrouve un motif dans le référentiel publié par PHP.
|
||||
* @returns {{id: number, code: string, label: string, quota: boolean}|null}
|
||||
* `quota` indique si le motif s'impute sur le quota annuel.
|
||||
*/
|
||||
function motifInfo(id) {
|
||||
const motif = (window.PTA_CONFIG?.motifs || []).find((item) => Number(item.id) === Number(id));
|
||||
return motif ? {
|
||||
@@ -165,6 +267,11 @@
|
||||
} : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface publique, unique point de contact entre les modules.
|
||||
* Ne jamais y ajouter de logique métier : `utils` ne contient que des
|
||||
* fonctions sans état.
|
||||
*/
|
||||
window.PTA = {
|
||||
config: window.PTA_CONFIG || {},
|
||||
elements,
|
||||
|
||||
Reference in New Issue
Block a user