pour prod
This commit is contained in:
263
assets/js/vacation-calendar.js
Normal file
263
assets/js/vacation-calendar.js
Normal file
@@ -0,0 +1,263 @@
|
||||
(() => {
|
||||
'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 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 = '<tr><td colspan="6"><div class="empty-state">Aucune période de vacances n’est 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 };
|
||||
})();
|
||||
Reference in New Issue
Block a user