/** * É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 { apiJson, 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; } /** Rend un contrôle, sa classe CSS dérivant de son niveau. */ function controlHtml(control) { const level = String(control.niveau || 'INFO').toLowerCase(); return `
${escapeHtml(control.niveau)}${escapeHtml(control.message)}
`; } /** Carte complète d'une équipe : paramètres, membres, contrôles, actions. */ function teamHtml(team) { const options = el('team-agent-options')?.innerHTML || ''; const members = (team.members || []).map((member) => ` ${escapeHtml(member.prenom)} ${escapeHtml(member.nom)}
${escapeHtml(member.poste_libelle || 'Poste non renseigné')} ${member.est_diplome ? `Diplômé${member.diplome_libelle ? ` — ${escapeHtml(member.diplome_libelle)}` : ''}` : 'Non diplômé'} ${escapeHtml(member.fonction_equipe)} ${escapeHtml(member.structure_principale_nom || 'Sans lieu principal')} `).join('') || 'Aucun agent affecté.'; return `

${escapeHtml(team.libelle)}

${escapeHtml(team.structure_nom)} — ${formatDate(team.date_debut)} au ${formatDate(team.date_fin)} — ${escapeHtml(team.type_equipe)}

Agents requis${Number(team.agents_requis)}
Agents affectés${Number(team.agents_affectes)}
Diplômés${Number(team.agents_diplomes || 0)}
Enfants${Number(team.nombre_enfants_moins_6) + Number(team.nombre_enfants_6_plus)}
${(team.controls || []).map(controlHtml).join('')}
${members}
AgentQualificationFonctionLieu principalAction
`; } /** 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 apiJson(`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 apiJson('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 apiJson('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 apiJson('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 apiJson('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 }; })();