pour prod

This commit is contained in:
Loic Masi
2026-08-07 16:13:43 +02:00
commit 5fbf76868f
157 changed files with 24085 additions and 0 deletions

158
assets/js/teams.js Normal file
View File

@@ -0,0 +1,158 @@
(() => {
'use strict';
const PTA = window.PTA;
if (!PTA) return;
const { readJsonResponse, apiErrorMessage, escapeHtml, formatDate } = PTA.utils;
const el = (id) => document.getElementById(id);
function message(text, type = 'info') {
const box = el('team-list-message');
if (!box) return;
box.className = `message message-${type}`;
box.textContent = text;
}
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;
}
function controlHtml(control) {
const level = String(control.niveau || 'INFO').toLowerCase();
return `<div class="control-item control-${escapeHtml(level)}"><strong>${escapeHtml(control.niveau)}</strong><span>${escapeHtml(control.message)}</span></div>`;
}
function teamHtml(team) {
const options = el('team-agent-options')?.innerHTML || '';
const members = (team.members || []).map((member) => `
<tr>
<td><strong>${escapeHtml(member.prenom)} ${escapeHtml(member.nom)}</strong><br><small>${escapeHtml(member.poste_libelle || 'Poste non renseigné')}</small></td>
<td>${member.est_diplome ? `Diplômé${member.diplome_libelle ? `${escapeHtml(member.diplome_libelle)}` : ''}` : 'Non diplômé'}</td>
<td>${escapeHtml(member.fonction_equipe)}</td>
<td>${escapeHtml(member.structure_principale_nom || 'Sans lieu principal')}</td>
<td><button type="button" class="button button-danger remove-team-member" data-agent-id="${Number(member.id_agent)}">Retirer</button></td>
</tr>
`).join('') || '<tr><td colspan="5">Aucun agent affecté.</td></tr>';
return `
<article class="card team-card" data-team-id="${Number(team.id_equipe)}">
<div class="section-heading">
<div><h3>${escapeHtml(team.libelle)}</h3><p class="section-subtitle">${escapeHtml(team.structure_nom)}${formatDate(team.date_debut)} au ${formatDate(team.date_fin)}${escapeHtml(team.type_equipe)}</p></div>
<select class="team-status" aria-label="Statut"><option value="BROUILLON" ${team.statut === 'BROUILLON' ? 'selected' : ''}>Brouillon</option><option value="A_CONTROLER" ${team.statut === 'A_CONTROLER' ? 'selected' : ''}>À contrôler</option><option value="VALIDEE" ${team.statut === 'VALIDEE' ? 'selected' : ''}>Validée</option></select>
</div>
<div class="summary-grid compact-summary">
<article class="summary-card"><span>Agents requis</span><strong>${Number(team.agents_requis)}</strong></article>
<article class="summary-card"><span>Agents affectés</span><strong>${Number(team.agents_affectes)}</strong></article>
<article class="summary-card"><span>Diplômés</span><strong>${Number(team.agents_diplomes || 0)}</strong></article>
<article class="summary-card"><span>Enfants</span><strong>${Number(team.nombre_enfants_moins_6) + Number(team.nombre_enfants_6_plus)}</strong></article>
</div>
<div class="control-list">${(team.controls || []).map(controlHtml).join('')}</div>
<form class="team-member-form inline-form">
<label class="field"><span>Agent</span><select class="team-member-agent" required><option value="">Choisir un agent</option>${options}</select></label>
<label class="field"><span>Fonction dans léquipe</span><select class="team-member-function"><option value="RESPONSABLE">Responsable</option><option value="ANIMATION">Animation</option><option value="RESTAURATION_ENTRETIEN">Restauration / entretien</option></select></label>
<label class="field"><span>Commentaire</span><input class="team-member-comment" type="text" maxlength="500"></label>
<button class="button button-primary" type="submit">Ajouter</button>
</form>
<div class="table-scroll"><table class="planning-table"><thead><tr><th>Agent</th><th>Qualification</th><th>Fonction</th><th>Lieu principal</th><th>Action</th></tr></thead><tbody>${members}</tbody></table></div>
</article>`;
}
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('') || '<div class="empty-state">Aucune équipe sur cette période.</div>';
message(`${(data.teams || []).length} équipe(s) affichée(s).`, 'success');
} catch (error) { message(error.message, 'error'); }
}
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; }
}
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'); }
}
}
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 };
})();