(() => {
'use strict';
const PTA = window.PTA;
if (!PTA) throw new Error('PTA core doit être chargé avant vacation-calendar.js');
const { escapeHtml, readJsonResponse, apiErrorMessage, formatDate } = PTA.utils;
let previewPeriods = [];
const elements = () => ({
academy: document.querySelector('#vacation-academy'),
calendarYear: document.querySelector('#vacation-calendar-year'),
previewButton: document.querySelector('#preview-vacations'),
previewMessage: document.querySelector('#vacation-preview-message'),
previewPanel: document.querySelector('#vacation-preview-panel'),
previewBody: document.querySelector('#vacation-preview-body'),
selectAll: document.querySelector('#vacation-select-all'),
saveSelected: document.querySelector('#save-selected-vacations'),
savedBody: document.querySelector('#vacation-saved-body'),
savedMessage: document.querySelector('#vacation-saved-message'),
refresh: document.querySelector('#refresh-vacations'),
manualForm: document.querySelector('#manual-vacation-form'),
manualMessage: document.querySelector('#manual-vacation-message'),
});
function setMessage(element, text = '', type = 'info') {
if (!element) return;
element.className = text ? `message message-${type}` : 'message';
element.textContent = text;
}
function sourceLabel(source) {
return source === 'DATA_GOUV' ? 'API officielle' : 'Saisie manuelle';
}
function renderPreview() {
const el = elements();
if (!el.previewBody || !el.previewPanel) return;
if (!previewPeriods.length) {
el.previewBody.innerHTML = '
Aucune période proposée. |
';
el.previewPanel.hidden = false;
return;
}
el.previewBody.innerHTML = previewPeriods.map((period, index) => `
|
${escapeHtml(period.annee_scolaire || '')}
|
|
${period.date_reprise_api ? `Reprise API : ${escapeHtml(formatDate(period.date_reprise_api))}` : ''}
|
${escapeHtml([period.zone, period.academie].filter(Boolean).join(' · ') || 'Non précisé')} |
`).join('');
el.previewPanel.hidden = false;
if (el.selectAll) el.selectAll.checked = false;
}
async function preview() {
const el = elements();
const academy = el.academy?.value.trim() || '';
const calendarYear = String(el.calendarYear?.value || '').trim();
if (!academy || !/^\d{4}$/.test(calendarYear)) {
setMessage(el.previewMessage, 'Renseignez une académie et une année civile au format 2026.', 'warning');
return;
}
el.previewButton.disabled = true;
setMessage(el.previewMessage, 'Consultation de l’API officielle en cours…', 'info');
if (el.previewPanel) el.previewPanel.hidden = true;
try {
const params = new URLSearchParams({ academy, year: calendarYear });
const response = await fetch(`api/preview_school_holidays.php?${params.toString()}`);
const data = await readJsonResponse(response);
if (!response.ok) throw new Error(apiErrorMessage(data, 'Impossible de consulter le calendrier scolaire.'));
previewPeriods = Array.isArray(data.periods) ? data.periods : [];
renderPreview();
const warnings = Array.isArray(data.warnings) ? data.warnings.filter(Boolean) : [];
const baseMessage = data.message || `${previewPeriods.length} période(s) proposée(s). Aucune donnée n’a encore été enregistrée.`;
const fullMessage = warnings.length ? `${baseMessage} Attention : ${warnings.join(' ')}` : baseMessage;
setMessage(el.previewMessage, fullMessage, warnings.length ? 'warning' : (previewPeriods.length ? 'success' : 'warning'));
} catch (error) {
previewPeriods = [];
setMessage(el.previewMessage, error.message, 'error');
} finally {
el.previewButton.disabled = false;
}
}
function selectedPreviewPeriods() {
const el = elements();
return Array.from(el.previewBody?.querySelectorAll('.vacation-preview-row') || [])
.filter((row) => row.querySelector('.vacation-import-check')?.checked)
.map((row) => {
const source = previewPeriods[Number(row.dataset.index)] || {};
return {
libelle: row.querySelector('.vacation-preview-label')?.value.trim() || '',
date_debut: row.querySelector('.vacation-preview-start')?.value || '',
date_fin: row.querySelector('.vacation-preview-end')?.value || '',
annee_scolaire: source.annee_scolaire || '',
academie: source.academie || elements().academy?.value.trim() || '',
zone: source.zone || '',
source: 'DATA_GOUV',
};
});
}
async function savePeriods(periods, messageElement) {
const response = await fetch('api/save_vacation_periods.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
csrf_token: window.PTA_CONFIG.csrfToken,
periods,
}),
});
const data = await readJsonResponse(response);
if (!response.ok) throw new Error(apiErrorMessage(data, 'Impossible d’enregistrer les périodes.'));
setMessage(messageElement, data.message, 'success');
await loadSaved();
return data;
}
async function saveSelected() {
const el = elements();
const periods = selectedPreviewPeriods();
if (!periods.length) {
setMessage(el.previewMessage, 'Cochez au moins une période avant de l’enregistrer.', 'warning');
return;
}
el.saveSelected.disabled = true;
try {
await savePeriods(periods, el.previewMessage);
el.previewBody?.querySelectorAll('.vacation-import-check').forEach((checkbox) => { checkbox.checked = false; });
if (el.selectAll) el.selectAll.checked = false;
} catch (error) {
setMessage(el.previewMessage, error.message, 'error');
} finally {
el.saveSelected.disabled = false;
}
}
async function saveManual(event) {
event.preventDefault();
const el = elements();
const submit = el.manualForm?.querySelector('button[type="submit"]');
const period = {
libelle: document.querySelector('#manual-vacation-label')?.value.trim() || '',
date_debut: document.querySelector('#manual-vacation-start')?.value || '',
date_fin: document.querySelector('#manual-vacation-end')?.value || '',
annee_scolaire: document.querySelector('#manual-vacation-school-year')?.value.trim() || '',
academie: document.querySelector('#manual-vacation-academy')?.value.trim() || '',
zone: document.querySelector('#manual-vacation-zone')?.value.trim() || '',
source: 'MANUEL',
};
if (period.date_debut && period.date_fin && period.date_fin < period.date_debut) {
setMessage(el.manualMessage, 'La date de fin doit être postérieure ou égale à la date de début.', 'warning');
return;
}
if (submit) submit.disabled = true;
try {
await savePeriods([period], el.manualMessage);
document.querySelector('#manual-vacation-label').value = '';
document.querySelector('#manual-vacation-start').value = '';
document.querySelector('#manual-vacation-end').value = '';
document.querySelector('#manual-vacation-zone').value = '';
} catch (error) {
setMessage(el.manualMessage, error.message, 'error');
} finally {
if (submit) submit.disabled = false;
}
}
function renderSaved(periods) {
const el = elements();
if (!el.savedBody) return;
if (!periods.length) {
el.savedBody.innerHTML = 'Aucune période de vacances n’est encore enregistrée. |
';
return;
}
el.savedBody.innerHTML = periods.map((period) => `
| ${escapeHtml(period.libelle)} |
${escapeHtml(formatDate(period.date_debut))} → ${escapeHtml(formatDate(period.date_fin))} |
${escapeHtml(period.annee_scolaire || '')} |
${escapeHtml([period.academie, period.zone].filter(Boolean).join(' · ') || 'Non précisé')} |
${escapeHtml(sourceLabel(period.source))} |
|
`).join('');
}
async function loadSaved() {
const el = elements();
try {
const response = await fetch('api/vacation_periods.php');
const data = await readJsonResponse(response);
if (!response.ok) throw new Error(apiErrorMessage(data, 'Impossible de charger les périodes enregistrées.'));
renderSaved(Array.isArray(data.periods) ? data.periods : []);
setMessage(el.savedMessage, '', 'info');
} catch (error) {
setMessage(el.savedMessage, error.message, 'error');
}
}
async function deletePeriod(button) {
const row = button.closest('tr[data-period-id]');
const id = Number(row?.dataset.periodId || 0);
if (!id || !confirm('Supprimer cette période de vacances ? Les journées concernées seront à nouveau considérées comme périscolaires.')) return;
button.disabled = true;
try {
const response = await fetch('api/delete_vacation_period.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
csrf_token: window.PTA_CONFIG.csrfToken,
period_id: id,
}),
});
const data = await readJsonResponse(response);
if (!response.ok) throw new Error(apiErrorMessage(data, 'Suppression impossible.'));
setMessage(elements().savedMessage, data.message, 'success');
await loadSaved();
} catch (error) {
setMessage(elements().savedMessage, error.message, 'error');
button.disabled = false;
}
}
function init() {
const el = elements();
el.previewButton?.addEventListener('click', preview);
el.saveSelected?.addEventListener('click', saveSelected);
el.refresh?.addEventListener('click', loadSaved);
el.manualForm?.addEventListener('submit', saveManual);
el.selectAll?.addEventListener('change', () => {
el.previewBody?.querySelectorAll('.vacation-import-check').forEach((checkbox) => {
checkbox.checked = el.selectAll.checked;
});
});
el.savedBody?.addEventListener('click', (event) => {
const button = event.target.closest('.delete-vacation-period');
if (button) deletePeriod(button);
});
loadSaved();
}
PTA.modules.vacationCalendar = { init, loadSaved, preview };
})();