(() => { 'use strict'; const PTA = window.PTA; if (!PTA) return; const { readJsonResponse, apiErrorMessage, escapeHtml, formatDate } = PTA.utils; const el = (id) => document.getElementById(id); const escapeAttr = (value) => String(value ?? '') .replaceAll('&', '&') .replaceAll('\"', '"') .replaceAll("'", ''') .replaceAll('<', '<') .replaceAll('>', '>'); let current = null; function show(text, type = 'info') { const box = el('annual-message'); if (!box) return; box.className = `message message-${type}`; box.textContent = text; } function formatGenerated(value) { const date = new Date(String(value).replace(' ', 'T')); return Number.isNaN(date.getTime()) ? value : date.toLocaleString('fr-FR', { dateStyle: 'short', timeStyle: 'short' }); } function formatContract(value) { if (value === 'PERMANENT') return 'Permanent'; if (value === 'TEMPORAIRE') return 'Temporaire'; return value || 'Non renseigné'; } function buildPlanningUrl(day) { const agentId = Number(current?.agent?.id_agent || 0); const structureId = Number(day.structure_id || current?.agent?.id_structure || 0); const week = `${Number(day.week_year)}-W${String(day.week).padStart(2, '0')}`; const params = new URLSearchParams({ agent_id: String(agentId), week, focus: 'annual' }); if (structureId) params.set('structure_id', String(structureId)); params.set('notice', `Ouverture depuis la vue annuelle — ${formatDate(day.date)}.`); return `planning.php?${params.toString()}`; } function cellHtml(cell, day) { const content = cell.display ? escapeHtml(cell.display) : ' '; const title = cell.title ? ` title="${escapeAttr(cell.title)}"` : ''; const draftClass = day.has_draft && cell.minutes > 0 ? ' annual-cell-draft' : ''; return `${content}`; } function monthHtml(month) { const rows = month.days.map((day) => { const classes = [ day.is_weekend ? 'annual-weekend' : '', day.is_vacation ? 'annual-vacation-row' : '', day.has_entries ? 'annual-has-entry' : '', ].filter(Boolean).join(' '); const vacationTitle = day.vacation_label ? ` title="${escapeAttr(day.vacation_label)}"` : ''; const weekText = day.show_week ? String(day.week).padStart(2, '0') : ''; return ` ${escapeHtml(day.weekday_short)}${day.day} ${weekText} ${cellHtml(day.cells.AP, day)} ${cellHtml(day.cells.PP, day)} ${cellHtml(day.cells.AE, day)} ${cellHtml(day.cells.PE, day)} `; }).join(''); return `
${rows}
${escapeHtml(month.name)}
JourSAPPPAEPE
Total${escapeHtml(month.totals.AP.compact || '0h')}${escapeHtml(month.totals.PP.compact || '0h')}${escapeHtml(month.totals.AE.compact || '0h')}${escapeHtml(month.totals.PE.compact || '0h')}
`; } function quotaPeriodsHtml(periods) { return periods.map((period) => `
${escapeHtml(formatDate(period.date_debut_periode))} → ${escapeHtml(formatDate(period.date_fin_periode))} ${escapeHtml(period.affectees?.libelle || '0 h 00')} affectées / ${escapeHtml(period.quota_cible?.libelle || '0 h 00')} Reste : ${escapeHtml(period.restantes?.libelle || '0 h 00')}
`).join(''); } function render(data) { current = data; const agent = data.agent || {}; const periods = data.quota_periods || []; const referencePeriod = periods[0] || {}; el('annual-content').hidden = false; el('annual-title').textContent = `ANNUALISATION ${data.year}`; el('annual-updated').textContent = `Mise à jour : ${formatGenerated(data.generated_at)}`; el('annual-agent-name').textContent = `${agent.prenom || ''} ${agent.nom || ''}`.trim() || '—'; el('annual-agent-number').textContent = agent.matricule || '—'; el('annual-rate').textContent = referencePeriod.quotite_travail !== undefined ? `${Number(referencePeriod.quotite_travail).toLocaleString('fr-FR')} %` : '—'; el('annual-target').textContent = referencePeriod.quota_cible?.libelle || '—'; el('annual-contract').textContent = formatContract(agent.type_contrat); el('annual-location').textContent = agent.structure_principale_nom || 'Sans lieu principal'; el('annual-post').textContent = agent.poste_libelle || 'Poste non renseigné'; el('annual-remaining').textContent = referencePeriod.restantes?.libelle || '—'; el('annual-quota-periods').innerHTML = quotaPeriodsHtml(periods); el('annual-calendar-total').textContent = data.calendar_totals?.duration || '0 h 00'; el('annual-validated-total').textContent = data.calendar_totals?.validated || '0 h 00'; el('annual-draft-total').textContent = data.calendar_totals?.draft || '0 h 00'; el('annual-year-label').textContent = `01/01/${data.year} → 31/12/${data.year}`; el('annual-months').innerHTML = (data.months || []).map(monthHtml).join(''); el('annual-legend').innerHTML = (data.legend || []).map((item) => ` ${escapeHtml(item.code)} ${escapeHtml(item.label)} `).join(''); el('annual-open-pta').href = `pta.php?agent_id=${Number(agent.id_agent)}&date=${data.year}-07-01`; el('annual-open-month').href = `agents.php?agent_id=${Number(agent.id_agent)}&month=${data.year}-01`; el('annual-print').disabled = false; el('annual-fullscreen').disabled = false; if (el('annual-validate-year')) el('annual-validate-year').disabled = false; } async function load(options = {}) { const agentId = Number(el('annual-agent')?.value || 0); const year = Number(el('annual-year')?.value || 0); if (!agentId || year < 2000 || year > 2100) { show('Choisissez un agent et une année valide.', 'error'); return; } if (options.clearValidation !== false && el('annual-validation-result')) { el('annual-validation-result').innerHTML = ''; } show('Construction de la vue annuelle…'); el('annual-content').hidden = true; try { const response = await fetch(`api/annual_overview.php?agent_id=${agentId}&year=${year}`); const data = await readJsonResponse(response); if (!response.ok) throw new Error(apiErrorMessage(data, 'Impossible de charger la vue annuelle.')); render(data); show('Vue annuelle actualisée.', 'success'); } catch (error) { show(error.message, 'error'); } } function validationPlanningUrl(planning) { const agentId = Number(current?.agent?.id_agent || el('annual-agent')?.value || 0); const week = `${Number(planning.annee)}-W${String(planning.numero_semaine).padStart(2, '0')}`; const params = new URLSearchParams({ agent_id: String(agentId), structure_id: String(Number(planning.id_structure)), week, focus: 'annual-validation', notice: `Correction demandée depuis la vue annuelle — semaine ${String(planning.numero_semaine).padStart(2, '0')}.`, }); return `planning.php?${params.toString()}`; } function renderValidationIssues(data) { const container = el('annual-validation-result'); if (!container) return; const invalid = (data.plannings || []).filter((planning) => planning.has_errors); container.innerHTML = `
Aucune semaine n’a été validée.
${escapeHtml(data.error || 'Des erreurs doivent être corrigées avant la validation annuelle.')}
${invalid.length} planning(s) à corriger.
${invalid.map((planning) => { const errors = (planning.controls || []).filter((control) => control.niveau === 'ERREUR'); return `

Semaine ${String(planning.numero_semaine).padStart(2, '0')} — ${escapeHtml(planning.structure_nom || 'Lieu non renseigné')}

Corriger cette semaine
${escapeHtml(formatDate(planning.date_debut))} → ${escapeHtml(formatDate(planning.date_fin))}
`; }).join('')}
`; } function renderValidationWarning(data) { const container = el('annual-validation-result'); if (!container) return; container.innerHTML = `
Avertissement avant validation.
${escapeHtml(data.message || 'Une confirmation est nécessaire.')}
`; } function renderValidationSuccess(data) { const container = el('annual-validation-result'); if (!container) return; container.innerHTML = `
Validation terminée.
${escapeHtml(data.message || 'Les plannings ont été validés.')}
`; } async function validateWholeYear(forceQuota = false) { const agentId = Number(el('annual-agent')?.value || 0); const year = Number(el('annual-year')?.value || 0); if (!agentId || !year || !current || Number(current.agent?.id_agent) !== agentId || Number(current.year) !== year) { show('Affichez d’abord la vue annuelle de l’agent à contrôler.', 'error'); return; } const button = el('annual-validate-year'); if (button) button.disabled = true; const container = el('annual-validation-result'); if (container) { container.innerHTML = '
Contrôle de toutes les semaines en brouillon…
'; } try { const response = await fetch('api/annual_validate.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ csrf_token: PTA.config.csrfToken, agent_id: agentId, year, force_quota: Boolean(forceQuota), }), }); const data = await readJsonResponse(response); if (response.status === 422 && data.status === 'errors') { renderValidationIssues(data); show('Des semaines doivent être corrigées avant la validation annuelle.', 'error'); return; } if (response.status === 409 && data.status === 'warning') { renderValidationWarning(data); const confirmed = window.confirm(`${data.message || 'Le quota annuel présente un avertissement.'}\n\nValider malgré cet avertissement ?`); if (confirmed) await validateWholeYear(true); return; } if (!response.ok) { throw new Error(apiErrorMessage(data, 'Impossible de valider l’année.')); } renderValidationSuccess(data); show(data.message || 'Validation annuelle terminée.', 'success'); await load({ clearValidation: false }); } catch (error) { if (container) { container.innerHTML = `
${escapeHtml(error.message)}
`; } show(error.message, 'error'); } finally { if (button) button.disabled = false; } } function handleCalendarClick(event) { const row = event.target.closest('tr[data-url]'); if (!row) return; window.location.href = row.dataset.url; } async function toggleFullscreen() { const target = el('annual-content'); if (!target) return; try { if (!document.fullscreenElement) { await target.requestFullscreen?.(); } else { await document.exitFullscreen?.(); } } catch (error) { show('Le mode plein écran n’est pas disponible dans ce navigateur.', 'error'); } } function init() { el('load-annual')?.addEventListener('click', load); el('annual-print')?.addEventListener('click', () => window.print()); el('annual-fullscreen')?.addEventListener('click', toggleFullscreen); el('annual-validate-year')?.addEventListener('click', () => validateWholeYear(false)); el('annual-months')?.addEventListener('click', handleCalendarClick); const params = window.PTA_PAGE_CONFIG?.params || {}; if (params.agentId) el('annual-agent').value = String(params.agentId); if (params.year) el('annual-year').value = String(params.year); if (params.agentId) load(); } PTA.modules.annualOverview = { init, load }; })();