Auto-commit for deploy to production - 2025-12-09 12:01:13

This commit is contained in:
2025-12-09 12:01:14 +01:00
parent 035adcbced
commit dd41f0c6c0
3 changed files with 193 additions and 67 deletions
+29
View File
@@ -173,3 +173,32 @@ input[type="number"] {
.fc-row--bank-holiday { .fc-row--bank-holiday {
background: #d9f7be; background: #d9f7be;
} }
/* Ligne de semaine contenant au moins un jour de vacances scolaires */
.fc-row--school-holiday {
background-color: #fff5cc;
}
/* Jour individuel en vacances scolaires (cellule) */
.fc-day--school-holiday {
background-color: #ffe9a6;
}
/* Tableau recap vacances scolaires */
.fc-holidays-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
margin-top: 12px;
}
.fc-holidays-table th,
.fc-holidays-table td {
border: 1px solid #d9e2ec;
padding: 4px 6px;
text-align: left;
}
.fc-holidays-table th {
background-color: #f0f4f8;
}
+135 -60
View File
@@ -27,18 +27,17 @@ function getMonthNameFr(monthIndex) {
} }
/** /**
* Calcule le numéro de semaine dans l'année (1..52/53), * Calcule le n° de semaine dans l'année (1..),
* en prenant le lundi comme début de semaine. * Semaine 1 = semaine contenant le 1er janvier, début au lundi.
*/ */
function getWeekOfYear(date) { function getWeekOfYear(date) {
const year = date.getFullYear(); const year = date.getFullYear();
const startOfYear = new Date(year, 0, 1); const startOfYear = new Date(year, 0, 1);
// se placer au lundi de la semaine contenant le 1er janvier const dayOfWeek = startOfYear.getDay() || 7; // dimanche = 0 -> 7
const day = startOfYear.getDay() || 7; // dimanche=0 => 7 const diffToMonday = dayOfWeek === 1 ? 0 : dayOfWeek - 1;
const diffToMonday = day > 1 ? day - 1 : 0; const firstWeekMonday = new Date(year, 0, 1 - diffToMonday);
const firstMonday = new Date(year, 0, 1 - diffToMonday);
const diffMillis = date - firstMonday; const diffMillis = date - firstWeekMonday;
const diffDays = Math.floor(diffMillis / (1000 * 60 * 60 * 24)); const diffDays = Math.floor(diffMillis / (1000 * 60 * 60 * 24));
return Math.floor(diffDays / 7) + 1; return Math.floor(diffDays / 7) + 1;
} }
@@ -99,12 +98,20 @@ function generateWeeks() {
thu: thursday, thu: thursday,
fri: friday, fri: friday,
}, },
dayFlags: {
mon: { schoolHoliday: false },
tue: { schoolHoliday: false },
wed: { schoolHoliday: false },
thu: { schoolHoliday: false },
fri: { schoolHoliday: false },
},
totals: { totals: {
offCarole: 0, offCarole: 0,
extraOffCarole: 0, extraOffCarole: 0,
centre: 0, centre: 0,
avis: 0, avis: 0,
}, },
isSchoolHolidayWeek: false,
alex: { alex: {
total: 0, total: 0,
detail: "", detail: "",
@@ -123,68 +130,119 @@ function generateWeeks() {
const weeks = generateWeeks(); const weeks = generateWeeks();
// === 3. Événements calendrier (exemple, plus tard ce sera ton UI/DB) === // === 3. Événements & vacances scolaires ===
const events = []; // on pourra y ajouter OFF_CAROLE, CENTRE, etc. plus tard
async function fetchSchoolHolidays(anneeScolaire, zoneLabel) {
const baseUrl =
"https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records";
const where = `annee_scolaire='${anneeScolaire}' AND zones LIKE '%${zoneLabel}%'`;
const params = new URLSearchParams({
where,
limit: "100",
order_by: "start_date",
});
const url = `${baseUrl}?${params.toString()}`;
const res = await fetch(url);
if (!res.ok) {
console.error("Erreur API vacances scolaires", res.status, res.statusText);
return [];
}
const data = await res.json();
return data.results || [];
}
/** /**
* Exemple de liste d'événements. Plus tard : * Ajoute les vacances scolaires comme événements de type VACANCES_SCOLAIRES
* - tu auras une UI pour ajouter/supprimer ces événements * ET remplit le tableau recap.
* - tu les chargeras depuis/vers la DB.
*/ */
const events = [ function addSchoolHolidayEventsFromRecords(records, events) {
// Exemple : Carole off le 15/09/2025 const tbody = document.querySelector("#schoolHolidaysTable tbody");
{ date: "2025-09-15", type: "OFF_CAROLE", duration: 1 }, if (tbody) tbody.innerHTML = "";
// Exemple : Centre le 20/10/2025
{ date: "2025-10-20", type: "CENTRE", duration: 1 }, records.forEach((record) => {
// etc. const start = new Date(record.start_date);
]; const end = new Date(record.end_date);
// Remplir le tableau recap
if (tbody) {
const tr = document.createElement("tr");
const startStr = formatDayMonth(start) + "/" + start.getFullYear();
const endStr = formatDayMonth(end) + "/" + end.getFullYear();
tr.innerHTML = `
<td>${record.description || ""}</td>
<td>${startStr}</td>
<td>${endStr}</td>
<td>${record.zones || ""}</td>
<td>${record.location || ""}</td>
`;
tbody.appendChild(tr);
}
// Chaque jour de la plage -> event VACANCES_SCOLAIRES
let current = new Date(start);
while (current <= end) {
const year = current.getFullYear();
const month = String(current.getMonth() + 1).padStart(2, "0");
const day = String(current.getDate()).padStart(2, "0");
const isoDate = `${year}-${month}-${day}`;
events.push({
date: isoDate,
type: "VACANCES_SCOLAIRES",
duration: 1,
});
current.setDate(current.getDate() + 1);
}
});
}
/** /**
* Recalcule les totaux par semaine à partir des events. * Marque les jours & semaines de vacances scolaires dans weeks.
*/ */
function recomputeTotalsFromEvents(weeks, events) { function markSchoolHolidayDaysAndWeeks(weeks, events) {
// reset // reset
weeks.forEach((w) => { weeks.forEach((w) => {
w.totals.offCarole = 0; w.isSchoolHolidayWeek = false;
w.totals.extraOffCarole = 0; Object.keys(w.dayFlags).forEach((dayKey) => {
w.totals.centre = 0; w.dayFlags[dayKey].schoolHoliday = false;
w.totals.avis = 0; });
}); });
events.forEach((evt) => { events.forEach((evt) => {
if (evt.type !== "VACANCES_SCOLAIRES") return;
const [y, m, d] = evt.date.split("-").map(Number); const [y, m, d] = evt.date.split("-").map(Number);
const evtDate = new Date(y, m - 1, d); const evtDate = new Date(y, m - 1, d);
// trouver la semaine qui contient evtDate (entre lundi et vendredi) weeks.forEach((w) => {
const week = weeks.find((w) => { const dayKeys = ["mon", "tue", "wed", "thu", "fri"];
const md = w.dayDates.mon; let hasHolidayInWeek = false;
const fd = w.dayDates.fri;
return evtDate >= md && evtDate <= fd;
});
if (!week) return;
const dur = evt.duration || 1; dayKeys.forEach((dayKey) => {
const dayDate = w.dayDates[dayKey];
switch (evt.type) { if (
case "OFF_CAROLE": dayDate.getFullYear() === evtDate.getFullYear() &&
week.totals.offCarole += dur; dayDate.getMonth() === evtDate.getMonth() &&
break; dayDate.getDate() === evtDate.getDate()
case "EXTRA_OFF_CAROLE": ) {
week.totals.extraOffCarole += dur; w.dayFlags[dayKey].schoolHoliday = true;
break; hasHolidayInWeek = true;
case "CENTRE":
week.totals.centre += dur;
break;
case "AVIS":
week.totals.avis += dur;
break;
default:
break;
} }
}); });
}
// première agrégation if (hasHolidayInWeek) {
recomputeTotalsFromEvents(weeks, events); w.isSchoolHolidayWeek = true;
}
});
});
}
// === 4. Calcul des rowspans pour la colonne Mois === // === 4. Calcul des rowspans pour la colonne Mois ===
@@ -208,16 +266,23 @@ function renderTable() {
const showOnlyCaroleOff = const showOnlyCaroleOff =
document.getElementById("showOnlyCaroleOff")?.checked; document.getElementById("showOnlyCaroleOff")?.checked;
// showOnlySchoolHoliday est ignoré pour l'instant const showOnlySchoolHoliday = document.getElementById(
"showOnlySchoolHoliday"
)?.checked;
const monthRowRendered = {}; const monthRowRendered = {};
weeks.forEach((week) => { weeks.forEach((week) => {
if (showOnlyCaroleOff && week.totals.offCarole === 0) return; if (showOnlyCaroleOff && week.totals.offCarole === 0) return;
if (showOnlySchoolHoliday && !week.isSchoolHolidayWeek) return;
const tr = document.createElement("tr"); const tr = document.createElement("tr");
// Colonne Mois (rowspan sur toutes les semaines du mois) if (week.isSchoolHolidayWeek) {
tr.classList.add("fc-row--school-holiday");
}
// Colonne Mois
if (!monthRowRendered[week.monthKey]) { if (!monthRowRendered[week.monthKey]) {
monthRowRendered[week.monthKey] = true; monthRowRendered[week.monthKey] = true;
const tdMonth = document.createElement("td"); const tdMonth = document.createElement("td");
@@ -226,19 +291,22 @@ function renderTable() {
tr.appendChild(tdMonth); tr.appendChild(tdMonth);
} }
// Semaine // Colonne Semaine
const tdWeek = document.createElement("td"); const tdWeek = document.createElement("td");
tdWeek.textContent = week.weekLabel; tdWeek.textContent = week.weekLabel;
tr.appendChild(tdWeek); tr.appendChild(tdWeek);
// Jours // Jours (Lun -> Ven) avec coloration jours de vacances
["mon", "tue", "wed", "thu", "fri"].forEach((dayKey) => { ["mon", "tue", "wed", "thu", "fri"].forEach((dayKey) => {
const td = document.createElement("td"); const td = document.createElement("td");
td.textContent = week.days[dayKey]; td.textContent = week.days[dayKey];
if (week.dayFlags[dayKey].schoolHoliday) {
td.classList.add("fc-day--school-holiday");
}
tr.appendChild(td); tr.appendChild(td);
}); });
// Totaux # Off Carole, # Extra off, #Centre, #Avis // Totaux # Off / Extra / Centre / Avis (pour l'instant à 0)
const tdOff = document.createElement("td"); const tdOff = document.createElement("td");
tdOff.textContent = week.totals.offCarole.toFixed(2).replace(/\.00$/, ""); tdOff.textContent = week.totals.offCarole.toFixed(2).replace(/\.00$/, "");
tr.appendChild(tdOff); tr.appendChild(tdOff);
@@ -257,7 +325,7 @@ function renderTable() {
tdAvis.textContent = week.totals.avis.toFixed(2).replace(/\.00$/, ""); tdAvis.textContent = week.totals.avis.toFixed(2).replace(/\.00$/, "");
tr.appendChild(tdAvis); tr.appendChild(tdAvis);
// Alex total & détail (pour l'instant, juste placeholders) // Alex total & détail
const tdAlexTotal = document.createElement("td"); const tdAlexTotal = document.createElement("td");
tdAlexTotal.textContent = week.alex.total.toFixed(2).replace(/\.00$/, ""); tdAlexTotal.textContent = week.alex.total.toFixed(2).replace(/\.00$/, "");
tr.appendChild(tdAlexTotal); tr.appendChild(tdAlexTotal);
@@ -281,7 +349,7 @@ function renderTable() {
updateSummary(); updateSummary();
} }
// === 6. Résumé (encore 0 utilisés pour linstant) === // === 6. Résumé (toujours 0 utilisés pour l'instant) ===
function updateSummary() { function updateSummary() {
const summaryDiv = document.getElementById("summaryText"); const summaryDiv = document.getElementById("summaryText");
@@ -350,7 +418,7 @@ function updateSummary() {
// === 7. Init === // === 7. Init ===
function initFamilyCalendar() { async function initFamilyCalendar() {
if (!document.getElementById("planningBody")) return; if (!document.getElementById("planningBody")) return;
[ [
@@ -376,7 +444,14 @@ function initFamilyCalendar() {
showOnlySchoolHoliday.addEventListener("change", renderTable); showOnlySchoolHoliday.addEventListener("change", renderTable);
} }
// Charger vacances scolaires 2025-2026, Zone C
const holidaysRecords = await fetchSchoolHolidays("2025-2026", "Zone C");
addSchoolHolidayEventsFromRecords(holidaysRecords, events);
markSchoolHolidayDaysAndWeeks(weeks, events);
renderTable(); renderTable();
} }
document.addEventListener("DOMContentLoaded", initFamilyCalendar); document.addEventListener("DOMContentLoaded", () => {
initFamilyCalendar().catch((err) => console.error(err));
});
+22
View File
@@ -109,6 +109,28 @@ require __DIR__ . '/header.php';
</div> </div>
</section> </section>
<section class="pf-section">
<h2>Vacances scolaires - Zone C (2025-2026)</h2>
<p>Source : data.education.gouv.fr - calendrier officiel.</p>
<div class="pf-table-wrapper">
<table id="schoolHolidaysTable" class="fc-holidays-table">
<thead>
<tr>
<th>Période</th>
<th>Du</th>
<th>Au</th>
<th>Zones</th>
<th>Académies</th>
</tr>
</thead>
<tbody>
<!-- Rempli par JS -->
</tbody>
</table>
</div>
</section>
<script src="/assets/js/family-calendar.js"></script> <script src="/assets/js/family-calendar.js"></script>