Files
pta/assets/js/agent-access.js
2026-08-25 15:12:11 +02:00

159 lines
6.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Compte d'accès depuis la fiche des agents (`PTA.modules.agentAccess`),
* écran manage_agents.php — V7.28.
*
* Pose sur chaque ligne du tableau des agents un bouton qui COUPE ou RÉTABLIT
* l'accès de l'agent à l'application. C'est le geste courant — un départ, un
* arrêt long, un retour — là où l'écran Administration porte la distribution des
* jetons elle-même.
*
* CE QUE LE BOUTON NE FAIT PAS : il ne touche ni au jeton (désactiver puis
* réactiver rend le MÊME jeton valable), ni à la fiche de l'agent, qui reste
* planifiable. Il ne crée pas non plus de compte : un agent qui n'en a pas
* encore l'obtient depuis Administration, seul écran qui sache afficher un jeton
* fraîchement produit.
*
* Il réutilise les endpoints de la délégation V7.27 (api/acces_comptes.php et
* api/acces_compte_statut.php) : mêmes gardes serveur, même liste blanche de
* rôles, même journalisation. Rien de neuf côté droits.
*
* L'état des comptes N'EST PAS rendu par PHP mais chargé par ce module : le HTML
* servi ne porte ainsi aucune information sur les comptes, comme sur l'écran
* DSI.
*/
(() => {
'use strict';
const PTA = window.PTA;
if (!PTA) return;
const { readJsonResponse, apiErrorMessage } = PTA.utils;
const corps = () => document.querySelector('#agent-assignment-body');
/** Comptes d'accès des agents, indexés par identifiant d'agent. */
let comptesParAgent = new Map();
let chargement = false;
function message(texte, type = 'info') {
const boite = document.querySelector('#assignment-message');
if (!boite) return;
boite.className = texte ? `message message-${type}` : 'message';
boite.textContent = texte;
}
/** Appel d'API mutualisé : toute réponse non-OK devient une exception. */
async function api(url, options = {}) {
const reponse = await fetch(url, options);
const donnees = await readJsonResponse(reponse);
if (!reponse.ok) throw new Error(apiErrorMessage(donnees, 'Opération impossible.'));
return donnees;
}
/**
* Met un bouton de ligne à l'état du compte correspondant.
*
* Trois états, et un seul est actionnable :
* - compte actif → « Couper l'accès » ;
* - compte coupé → « Rétablir l'accès » ;
* - aucun compte → bouton inerte, qui dit où en créer un.
*/
function decorerLigne(ligne) {
const bouton = ligne.querySelector('.agent-access-toggle');
if (!bouton) return;
const agentId = Number(ligne.dataset.agentId);
const compte = comptesParAgent.get(agentId);
if (!compte) {
bouton.disabled = true;
bouton.textContent = 'Sans compte d’accès';
bouton.title = 'Cet agent n’a pas encore de jeton. Créez son compte depuis l’onglet Administration, section « Jetons d’accès ».';
delete bouton.dataset.compteId;
delete bouton.dataset.actif;
return;
}
bouton.disabled = false;
bouton.dataset.compteId = String(compte.id_compte);
bouton.dataset.actif = compte.actif ? '1' : '0';
bouton.textContent = compte.actif ? 'Couper l’accès' : 'Rétablir l’accès';
bouton.title = compte.actif
? 'Désactive le compte : l’agent ne pourra plus se connecter. Son jeton reste valable et resservira au rétablissement.'
: 'Réactive le compte : l’agent pourra de nouveau se connecter avec le jeton qu’il détient déjà.';
bouton.classList.toggle('button-danger', compte.actif);
bouton.classList.toggle('button-secondary', !compte.actif);
}
/** Applique l'état chargé à toutes les lignes présentes. */
function decorerToutesLesLignes() {
corps()?.querySelectorAll('tr[data-agent-id]').forEach(decorerLigne);
}
/**
* Recharge l'état des comptes agents.
*
* L'échec est SILENCIEUX sur le tableau : les boutons restent inertes avec
* leur libellé d'attente, et la page des agents continue de fonctionner. Une
* commodité ne doit pas empêcher de consulter ou de modifier une fiche.
*/
async function refresh() {
if (!corps() || chargement) return;
chargement = true;
try {
const donnees = await api('api/acces_comptes.php?role=AGENT');
comptesParAgent = new Map(
(donnees.comptes || [])
.filter((compte) => compte.id_agent !== null)
.map((compte) => [Number(compte.id_agent), compte])
);
decorerToutesLesLignes();
} catch (erreur) {
message(`État des comptes d’accès indisponible : ${erreur.message}`, 'warning');
} finally {
chargement = false;
}
}
/** Coupe ou rétablit l'accès de l'agent d'une ligne. */
async function basculer(evenement) {
const bouton = evenement.target.closest('.agent-access-toggle');
if (!bouton || bouton.disabled) return;
const ligne = bouton.closest('tr[data-agent-id]');
const compteId = Number(bouton.dataset.compteId);
const actif = bouton.dataset.actif === '1';
const titulaire = ligne?.querySelector('.agent-row-name')?.textContent?.trim() || 'cet agent';
if (actif && !window.confirm(`Couper l’accès de ${titulaire} ? Il ne pourra plus se connecter, et sa session en cours se refermera à sa prochaine action. Son jeton reste valable pour un rétablissement.`)) {
return;
}
bouton.disabled = true;
try {
const donnees = await api('api/acces_compte_statut.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
csrf_token: window.PTA_CONFIG.csrfToken,
compte_id: compteId,
action: actif ? 'DESACTIVER' : 'ACTIVER',
}),
});
message(donnees.message, 'success');
await refresh();
} catch (erreur) {
message(erreur.message, 'error');
bouton.disabled = false;
}
}
function init() {
// Le module ne sert que sur l'écran des agents : ailleurs, il se retire.
if (!corps()) return;
corps().addEventListener('click', basculer);
refresh();
}
PTA.modules.agentAccess = { init, refresh };
})();