Auto-commit for deploy to production - 2025-12-09 12:01:13
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
+142
-67
@@ -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
|
||||||
* Exemple de liste d'événements. Plus tard :
|
|
||||||
* - tu auras une UI pour ajouter/supprimer ces événements
|
|
||||||
* - tu les chargeras depuis/vers la DB.
|
|
||||||
*/
|
|
||||||
const events = [
|
|
||||||
// Exemple : Carole off le 15/09/2025
|
|
||||||
{ date: "2025-09-15", type: "OFF_CAROLE", duration: 1 },
|
|
||||||
// Exemple : Centre le 20/10/2025
|
|
||||||
{ date: "2025-10-20", type: "CENTRE", duration: 1 },
|
|
||||||
// etc.
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
async function fetchSchoolHolidays(anneeScolaire, zoneLabel) {
|
||||||
* Recalcule les totaux par semaine à partir des events.
|
const baseUrl =
|
||||||
*/
|
"https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records";
|
||||||
function recomputeTotalsFromEvents(weeks, events) {
|
const where = `annee_scolaire='${anneeScolaire}' AND zones LIKE '%${zoneLabel}%'`;
|
||||||
// reset
|
const params = new URLSearchParams({
|
||||||
weeks.forEach((w) => {
|
where,
|
||||||
w.totals.offCarole = 0;
|
limit: "100",
|
||||||
w.totals.extraOffCarole = 0;
|
order_by: "start_date",
|
||||||
w.totals.centre = 0;
|
|
||||||
w.totals.avis = 0;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
events.forEach((evt) => {
|
const url = `${baseUrl}?${params.toString()}`;
|
||||||
const [y, m, d] = evt.date.split("-").map(Number);
|
|
||||||
const evtDate = new Date(y, m - 1, d);
|
|
||||||
|
|
||||||
// trouver la semaine qui contient evtDate (entre lundi et vendredi)
|
const res = await fetch(url);
|
||||||
const week = weeks.find((w) => {
|
if (!res.ok) {
|
||||||
const md = w.dayDates.mon;
|
console.error("Erreur API vacances scolaires", res.status, res.statusText);
|
||||||
const fd = w.dayDates.fri;
|
return [];
|
||||||
return evtDate >= md && evtDate <= fd;
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
return data.results || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajoute les vacances scolaires comme événements de type VACANCES_SCOLAIRES
|
||||||
|
* ET remplit le tableau recap.
|
||||||
|
*/
|
||||||
|
function addSchoolHolidayEventsFromRecords(records, events) {
|
||||||
|
const tbody = document.querySelector("#schoolHolidaysTable tbody");
|
||||||
|
if (tbody) tbody.innerHTML = "";
|
||||||
|
|
||||||
|
records.forEach((record) => {
|
||||||
|
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,
|
||||||
});
|
});
|
||||||
if (!week) return;
|
|
||||||
|
|
||||||
const dur = evt.duration || 1;
|
current.setDate(current.getDate() + 1);
|
||||||
|
|
||||||
switch (evt.type) {
|
|
||||||
case "OFF_CAROLE":
|
|
||||||
week.totals.offCarole += dur;
|
|
||||||
break;
|
|
||||||
case "EXTRA_OFF_CAROLE":
|
|
||||||
week.totals.extraOffCarole += dur;
|
|
||||||
break;
|
|
||||||
case "CENTRE":
|
|
||||||
week.totals.centre += dur;
|
|
||||||
break;
|
|
||||||
case "AVIS":
|
|
||||||
week.totals.avis += dur;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// première agrégation
|
/**
|
||||||
recomputeTotalsFromEvents(weeks, events);
|
* Marque les jours & semaines de vacances scolaires dans weeks.
|
||||||
|
*/
|
||||||
|
function markSchoolHolidayDaysAndWeeks(weeks, events) {
|
||||||
|
// reset
|
||||||
|
weeks.forEach((w) => {
|
||||||
|
w.isSchoolHolidayWeek = false;
|
||||||
|
Object.keys(w.dayFlags).forEach((dayKey) => {
|
||||||
|
w.dayFlags[dayKey].schoolHoliday = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
events.forEach((evt) => {
|
||||||
|
if (evt.type !== "VACANCES_SCOLAIRES") return;
|
||||||
|
|
||||||
|
const [y, m, d] = evt.date.split("-").map(Number);
|
||||||
|
const evtDate = new Date(y, m - 1, d);
|
||||||
|
|
||||||
|
weeks.forEach((w) => {
|
||||||
|
const dayKeys = ["mon", "tue", "wed", "thu", "fri"];
|
||||||
|
let hasHolidayInWeek = false;
|
||||||
|
|
||||||
|
dayKeys.forEach((dayKey) => {
|
||||||
|
const dayDate = w.dayDates[dayKey];
|
||||||
|
if (
|
||||||
|
dayDate.getFullYear() === evtDate.getFullYear() &&
|
||||||
|
dayDate.getMonth() === evtDate.getMonth() &&
|
||||||
|
dayDate.getDate() === evtDate.getDate()
|
||||||
|
) {
|
||||||
|
w.dayFlags[dayKey].schoolHoliday = true;
|
||||||
|
hasHolidayInWeek = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasHolidayInWeek) {
|
||||||
|
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 l’instant) ===
|
// === 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));
|
||||||
|
});
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user