178 lines
8.0 KiB
JavaScript
178 lines
8.0 KiB
JavaScript
(() => {
|
||
'use strict';
|
||
|
||
const PTA = window.PTA;
|
||
if (!PTA) throw new Error('PTA core doit être chargé avant structure-overview.js');
|
||
|
||
const { openStructurePdfButton } = PTA.elements;
|
||
const { escapeHtml, readJsonResponse, apiErrorMessage, parseIsoWeek, formatDate } = PTA.utils;
|
||
const typeLabel = (type) => type === 'EXTRASCOLAIRE' ? 'Extrascolaire' : 'Périscolaire';
|
||
|
||
function entryBadge(entry) {
|
||
return `
|
||
<div class="overview-entry motif-${escapeHtml(String(entry.motif_code || 'autre').toLowerCase())}">
|
||
<strong>${escapeHtml(entry.heure_debut)}–${escapeHtml(entry.heure_fin)}</strong>
|
||
<span>${escapeHtml(entry.motif_libelle)}</span>
|
||
${entry.statut === 'BROUILLON' ? '<em>Brouillon</em>' : ''}
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function coveragePanel(coverage) {
|
||
if (!coverage?.configured) {
|
||
return `
|
||
<div class="coverage-inline-notice coverage-inline-info">
|
||
<strong>Couverture non configurée</strong>
|
||
<span>Définissez les horaires de présence minimale dans Administration pour détecter automatiquement les créneaux vides.</span>
|
||
</div>`;
|
||
}
|
||
|
||
const gaps = Array.isArray(coverage.gaps) ? coverage.gaps : [];
|
||
if (!gaps.length) {
|
||
return `
|
||
<div class="coverage-inline-notice coverage-inline-success">
|
||
<strong>Couverture complète</strong>
|
||
<span>Aucun manque d’agent détecté sur les plages de couverture configurées.</span>
|
||
</div>`;
|
||
}
|
||
|
||
const items = gaps.slice(0, 8).map((gap) => `
|
||
<li><strong>${escapeHtml(gap.jour_libelle)} ${escapeHtml(gap.heure_debut)}–${escapeHtml(gap.heure_fin)}</strong> : ${Number(gap.agents_manquants)} agent${Number(gap.agents_manquants) > 1 ? 's' : ''} manquant${Number(gap.agents_manquants) > 1 ? 's' : ''}</li>
|
||
`).join('');
|
||
const more = gaps.length > 8 ? `<li>… et ${gaps.length - 8} autre${gaps.length - 8 > 1 ? 's' : ''} plage${gaps.length - 8 > 1 ? 's' : ''}.</li>` : '';
|
||
return `
|
||
<div class="coverage-inline-notice coverage-inline-danger">
|
||
<strong>⚠ ${gaps.length} plage${gaps.length > 1 ? 's' : ''} insuffisamment couverte${gaps.length > 1 ? 's' : ''}</strong>
|
||
<ul>${items}${more}</ul>
|
||
</div>`;
|
||
}
|
||
|
||
function render(data) {
|
||
const container = document.querySelector('#structure-overview-content');
|
||
const empty = document.querySelector('#structure-overview-empty');
|
||
const days = parseIsoWeek(data.week.value);
|
||
if (!container || !empty) return;
|
||
|
||
empty.hidden = true;
|
||
container.hidden = false;
|
||
|
||
if (!data.agents.length) {
|
||
container.innerHTML = `
|
||
<div class="overview-title-row">
|
||
<div>
|
||
<h3>${escapeHtml(data.structure.nom)}</h3>
|
||
<p>${typeLabel(data.structure.type_affectation)} · semaine du ${formatDate(days[0]?.date)} au ${formatDate(days[days.length - 1]?.date)}</p>
|
||
</div>
|
||
<span class="overview-count">0 agent</span>
|
||
</div>
|
||
${coveragePanel(data.coverage)}
|
||
<div class="empty-state">Aucun agent n’est rattaché ou planifié sur ce lieu pour cette semaine.</div>`;
|
||
return;
|
||
}
|
||
|
||
const headerDays = days.map((day) => `<th><strong>${day.label}</strong><span>${day.display}</span></th>`).join('');
|
||
const rows = data.agents.map((agent) => {
|
||
const cells = days.map((day) => {
|
||
const entries = agent.entries.filter((entry) => entry.date_jour === day.date);
|
||
return `<td>${entries.length ? entries.map((entry) => entryBadge(entry)).join('') : '<span class="cell-empty">—</span>'}</td>`;
|
||
}).join('');
|
||
|
||
return `
|
||
<tr>
|
||
<th class="agent-cell">
|
||
<strong>${escapeHtml(agent.prenom)} ${escapeHtml(agent.nom)}</strong>
|
||
<span>${escapeHtml(agent.matricule)}</span>
|
||
<small>${agent.lieu_principal ? 'Lieu principal' : 'Affectation ponctuelle'}</small>
|
||
</th>
|
||
${cells}
|
||
<td class="quota-cell">
|
||
<strong>${escapeHtml(agent.quota.restantes.libelle)}</strong>
|
||
<span>sur ${escapeHtml(agent.quota.quota_cible.libelle)}</span>
|
||
<small>${Number(agent.quota.quotite_travail).toLocaleString('fr-FR')} %</small>
|
||
</td>
|
||
</tr>
|
||
`;
|
||
}).join('');
|
||
|
||
container.innerHTML = `
|
||
<div class="overview-title-row">
|
||
<div>
|
||
<h3>${escapeHtml(data.structure.nom)}</h3>
|
||
<p>${typeLabel(data.structure.type_affectation)} · semaine du ${formatDate(days[0]?.date)} au ${formatDate(days[days.length - 1]?.date)}</p>
|
||
</div>
|
||
<span class="overview-count">${data.agents.length} agent${data.agents.length > 1 ? 's' : ''}</span>
|
||
</div>
|
||
${coveragePanel(data.coverage)}
|
||
<div class="table-scroll">
|
||
<table class="planning-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Agent</th>
|
||
${headerDays}
|
||
<th>Quota restant</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>${rows}</tbody>
|
||
</table>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function updatePdfButton() {
|
||
if (!openStructurePdfButton) return;
|
||
const structureId = document.querySelector('#overview-structure')?.value;
|
||
const week = document.querySelector('#overview-week')?.value;
|
||
openStructurePdfButton.disabled = !(structureId && week);
|
||
}
|
||
|
||
function openPdf() {
|
||
const structureId = document.querySelector('#overview-structure')?.value;
|
||
const week = document.querySelector('#overview-week')?.value;
|
||
if (!structureId || !week) return;
|
||
|
||
const params = new URLSearchParams({ structure_id: structureId, week });
|
||
window.open(`api/structure_planning_pdf.php?${params.toString()}`, '_blank', 'noopener');
|
||
}
|
||
|
||
async function load() {
|
||
const structureId = document.querySelector('#overview-structure')?.value;
|
||
const week = document.querySelector('#overview-week')?.value;
|
||
const container = document.querySelector('#structure-overview-content');
|
||
const empty = document.querySelector('#structure-overview-empty');
|
||
updatePdfButton();
|
||
if (!container || !empty) return;
|
||
|
||
if (!structureId || !week) {
|
||
empty.hidden = false;
|
||
empty.textContent = 'Choisissez un lieu d’affectation et une semaine.';
|
||
container.hidden = true;
|
||
return;
|
||
}
|
||
|
||
empty.hidden = false;
|
||
empty.textContent = 'Chargement du planning du lieu…';
|
||
container.hidden = true;
|
||
|
||
try {
|
||
const params = new URLSearchParams({ structure_id: structureId, week });
|
||
const response = await fetch(`api/structure_overview.php?${params.toString()}`);
|
||
const data = await readJsonResponse(response);
|
||
if (!response.ok) throw new Error(apiErrorMessage(data, 'Impossible de charger la vue du lieu.'));
|
||
render(data);
|
||
} catch (error) {
|
||
empty.hidden = false;
|
||
empty.textContent = error.message;
|
||
}
|
||
}
|
||
|
||
function init() {
|
||
document.querySelector('#load-structure-overview')?.addEventListener('click', load);
|
||
document.querySelector('#overview-structure')?.addEventListener('change', load);
|
||
document.querySelector('#overview-week')?.addEventListener('change', load);
|
||
openStructurePdfButton?.addEventListener('click', openPdf);
|
||
updatePdfButton();
|
||
}
|
||
|
||
PTA.modules.structureOverview = { init, load, render, updatePdfButton };
|
||
})();
|