Files
pta/assets/js/vacation-calendar.js
Loic Masi 5fbf76868f pour prod
2026-08-07 16:13:43 +02:00

264 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(() => {
'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 = '<tr><td colspan="5"><div class="empty-state">Aucune période proposée.</div></td></tr>';
el.previewPanel.hidden = false;
return;
}
el.previewBody.innerHTML = previewPeriods.map((period, index) => `
<tr class="vacation-preview-row" data-index="${index}">
<td><input class="vacation-import-check" type="checkbox" aria-label="Importer ${escapeHtml(period.libelle)}"></td>
<td>
<input class="vacation-preview-label" type="text" maxlength="150" value="${escapeHtml(period.libelle)}">
<small>${escapeHtml(period.annee_scolaire || '')}</small>
</td>
<td><input class="vacation-preview-start" type="date" value="${escapeHtml(period.date_debut)}"></td>
<td>
<input class="vacation-preview-end" type="date" value="${escapeHtml(period.date_fin)}">
${period.date_reprise_api ? `<small>Reprise API : ${escapeHtml(formatDate(period.date_reprise_api))}</small>` : ''}
</td>
<td>${escapeHtml([period.zone, period.academie].filter(Boolean).join(' · ') || 'Non précisé')}</td>
</tr>
`).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 lAPI 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 na 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 denregistrer 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 lenregistrer.', '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 = '<tr><td colspan="6"><div class="empty-state">Aucune période de vacances nest encore enregistrée.</div></td></tr>';
return;
}
el.savedBody.innerHTML = periods.map((period) => `
<tr data-period-id="${Number(period.id_periode)}">
<td><strong>${escapeHtml(period.libelle)}</strong></td>
<td>${escapeHtml(formatDate(period.date_debut))}${escapeHtml(formatDate(period.date_fin))}</td>
<td>${escapeHtml(period.annee_scolaire || '')}</td>
<td>${escapeHtml([period.academie, period.zone].filter(Boolean).join(' · ') || 'Non précisé')}</td>
<td><span class="vacation-source-badge">${escapeHtml(sourceLabel(period.source))}</span></td>
<td><button type="button" class="button button-danger delete-vacation-period">Supprimer</button></td>
</tr>
`).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 };
})();