/**
* Équipes des mercredis et des vacances (`PTA.modules.teams`), écran teams.php.
*
* Une équipe rassemble des agents sur un lieu et une période, avec des règles
* d'encadrement propres à l'accueil collectif de mineurs (effectifs par tranche
* d'âge, taux d'encadrement, proportion d'agents diplômés). Le serveur calcule
* l'effectif requis et les contrôles ; ce module se contente de les afficher.
*
* Les contrôles suivent la forme commune {niveau, code, message}. AUCUN n'est
* bloquant : ils signalent, l'arbitrage reste humain.
*
* La page se recharge intégralement (`load()`) après chaque modification :
* ajouter ou retirer un membre change les contrôles de toute l'équipe.
*/
(() => {
'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);
/** Message dans le bandeau de la page. */
function message(text, type = 'info') {
const box = el('team-list-message');
if (!box) return;
box.className = `message message-${type}`;
box.textContent = text;
}
/**
* Appel d'API mutualisé : lit le JSON et transforme toute réponse non-OK en
* exception. Toutes les actions de la page passent par ici.
*/
async function api(url, options = {}) {
const response = await fetch(url, options);
const data = await readJsonResponse(response);
if (!response.ok) throw new Error(apiErrorMessage(data, 'Opération impossible.'));
return data;
}
/** Rend un contrôle, sa classe CSS dérivant de son niveau. */
function controlHtml(control) {
const level = String(control.niveau || 'INFO').toLowerCase();
return `
`;
}
/** Recharge toutes les équipes de la période filtrée. */
async function load() {
const start = el('team-filter-start')?.value || '';
const end = el('team-filter-end')?.value || '';
const query = new URLSearchParams();
if (start) query.set('date_debut', start);
if (end) query.set('date_fin', end);
message('Chargement des équipes…');
try {
const data = await api(`api/teams.php?${query.toString()}`);
el('team-list').innerHTML = (data.teams || []).map(teamHtml).join('') || '
Aucune équipe sur cette période.
';
message(`${(data.teams || []).length} équipe(s) affichée(s).`, 'success');
} catch (error) { message(error.message, 'error'); }
}
/** Crée une équipe (toujours en BROUILLON), puis recharge la liste. */
async function create(event) {
event.preventDefault();
const box = el('team-create-message');
try {
const data = await api('api/teams.php', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
csrf_token: window.PTA_CONFIG.csrfToken,
structure_id: Number(el('team-structure').value),
type_equipe: el('team-type').value,
libelle: el('team-label').value,
date_debut: el('team-start').value,
date_fin: el('team-end').value,
nombre_enfants_moins_6: Number(el('team-under-six').value),
nombre_enfants_6_plus: Number(el('team-over-six').value),
ratio_moins_6: Number(el('team-ratio-under-six').value),
ratio_6_plus: Number(el('team-ratio-over-six').value),
minimum_agents: Number(el('team-min-agents').value),
pourcentage_diplomes_min: Number(el('team-qualified-rate').value),
commentaire: el('team-comment').value,
}),
});
box.className = 'message message-success'; box.textContent = data.message;
event.currentTarget.reset();
el('team-ratio-under-six').value = '8'; el('team-ratio-over-six').value = '12'; el('team-min-agents').value = '1'; el('team-qualified-rate').value = '50';
await load();
} catch (error) { box.className = 'message message-error'; box.textContent = error.message; }
}
/**
* Ajout et retrait de membres, par délégation sur la liste des équipes —
* elle est reconstruite à chaque chargement.
*/
async function handle(event) {
const card = event.target.closest('.team-card');
if (!card) return;
const teamId = Number(card.dataset.teamId);
if (event.type === 'submit' && event.target.matches('.team-member-form')) {
event.preventDefault();
try {
await api('api/team_members.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({
csrf_token: window.PTA_CONFIG.csrfToken, team_id: teamId,
agent_id: Number(card.querySelector('.team-member-agent').value),
fonction_equipe: card.querySelector('.team-member-function').value,
commentaire: card.querySelector('.team-member-comment').value,
}) });
await load();
} catch (error) { message(error.message, 'error'); }
return;
}
const remove = event.target.closest('.remove-team-member');
if (remove) {
if (!window.confirm('Retirer cet agent de l’équipe ?')) return;
try {
await api('api/team_members.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({
csrf_token: window.PTA_CONFIG.csrfToken, action: 'REMOVE', team_id: teamId, agent_id: Number(remove.dataset.agentId),
}) });
await load();
} catch (error) { message(error.message, 'error'); }
}
}
/**
* Fait avancer une équipe dans son cycle de vie
* (BROUILLON → A_CONTROLER → VALIDEE). Aucune transition n'est imposée : le
* suivi est indicatif, pas un verrou.
*/
async function status(event) {
const select = event.target.closest('.team-status');
if (!select) return;
const card = select.closest('.team-card');
try {
await api('api/team_status.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({
csrf_token: window.PTA_CONFIG.csrfToken, team_id: Number(card.dataset.teamId), statut: select.value,
}) });
await load();
} catch (error) { message(error.message, 'error'); }
}
function init() {
const params = window.PTA_PAGE_CONFIG?.params || {};
if (params.dateDebut) el('team-filter-start').value = params.dateDebut;
if (params.dateFin) el('team-filter-end').value = params.dateFin;
el('team-create-form')?.addEventListener('submit', create);
el('load-teams')?.addEventListener('click', load);
el('team-list')?.addEventListener('submit', handle);
el('team-list')?.addEventListener('click', handle);
el('team-list')?.addEventListener('change', status);
load();
}
PTA.modules.teams = { init, load };
})();