diff --git a/family-calendar.php b/family-calendar.php index 292607b..831bf24 100644 --- a/family-calendar.php +++ b/family-calendar.php @@ -63,7 +63,11 @@ require __DIR__ . '/header.php';
| Mois | -Semaine | -Lundi | -Mardi | -Mercredi | -Jeudi | -Vendredi | -# Off Carole | -# Extra off Carole | -#Centre | -#Avis | +Mois | +Semaine | +Lundi | +Mardi | +Mercredi | +Jeudi | +Vendredi | +# Off Carole | +# Extra off Carole | +# Centre | +# Avis | +# Pep malade | +# Pep Présence | + + +ALEX | +LAIA | +||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CP | +JRA | +JA | + +CP | +JRA | +JA | +||||||||||||||||||||||||||||||
| Av. | +Use | +Av. | +Use | +Av. | +Use | + + +Av. | +Use | +Av. | +Use | +Av. | +Use | ||||||||||||||||||||||||
Off Carole : ${totalOff} jours
Extra Off Carole : ${totalExtraOff} jours
+Pep malade : ${totalPepSick} jours
+Jours potentiels d'accueil Pep (année scolaire, hors fériés) : ${totalWorkingDays}
+Présence Pep (année scolaire) : ${totalPresencePep} jours
`; } @@ -294,20 +369,27 @@ document.addEventListener("DOMContentLoaded", () => { this.weeks.forEach((week) => { const tr = document.createElement("tr"); + // Mois if (!monthRowRendered[week.monthKey]) { monthRowRendered[week.monthKey] = true; const tdMonth = document.createElement("td"); tdMonth.rowSpan = monthSpans[week.monthKey] || 1; tdMonth.textContent = week.monthName; + tdMonth.classList.add("col-month"); tr.appendChild(tdMonth); } + // Semaine const tdWeek = document.createElement("td"); tdWeek.textContent = week.weekLabel; + tdWeek.classList.add("col-month"); tr.appendChild(tdWeek); + // Jours Lundi–Vendredi ["mon", "tue", "wed", "thu", "fri"].forEach((dayKey) => { const td = document.createElement("td"); + td.classList.add("col-day"); + const dayDate = week.dayDates[dayKey]; td.dataset.date = `${dayDate.getFullYear()}-${String( dayDate.getMonth() + 1 @@ -317,9 +399,9 @@ document.addEventListener("DOMContentLoaded", () => { "0" )}/${String(dayDate.getMonth() + 1).padStart(2, "0")}`; - td.className = ""; let hasGarde = false; let gardeType = null; + let hasPepSick = false; week.dayFlags[dayKey].eventsOnDay.forEach((evt) => { const classMap = { @@ -327,6 +409,7 @@ document.addEventListener("DOMContentLoaded", () => { PUBLIC_HOLIDAY: "fc-day--public-holiday", OFF_CAROLE: "fc-day--off-carole", EXTRA_OFF_CAROLE: "fc-day--extra-off-carole", + // pas de couleur pour PEP_SICK dans l'hebdo }; if (classMap[evt.type]) { @@ -337,6 +420,10 @@ document.addEventListener("DOMContentLoaded", () => { hasGarde = true; gardeType = evt.type; } + + if (evt.type === "PEP_SICK") { + hasPepSick = true; + } }); if (hasGarde) { @@ -345,7 +432,14 @@ document.addEventListener("DOMContentLoaded", () => { if (gardeType === "AVIS") td.classList.add("fc-day--avis"); } - // Indicateur congés Alex/Laia + if (hasPepSick) { + const pepSpan = document.createElement("span"); + pepSpan.className = "fc-pep-sick-emoji"; + pepSpan.textContent = "🤒"; + td.appendChild(pepSpan); + } + + // Congés Alex/Laia const isoDate = td.dataset.date; const leavesOnDay = this.leaves.filter( (lv) => lv.leave_date === isoDate @@ -355,8 +449,8 @@ document.addEventListener("DOMContentLoaded", () => { const container = document.createElement("div"); container.className = "fc-leaves-label-container"; - const hasAlex = leavesOnDay.some((lv) => lv.person_id === 2); // Alex - const hasLaia = leavesOnDay.some((lv) => lv.person_id === 3); // Laia + const hasAlex = leavesOnDay.some((lv) => lv.person_id === 2); + const hasLaia = leavesOnDay.some((lv) => lv.person_id === 3); if (hasAlex) { const alexSpan = document.createElement("span"); @@ -378,22 +472,53 @@ document.addEventListener("DOMContentLoaded", () => { tr.appendChild(td); }); + // Totaux const tdOff = document.createElement("td"); tdOff.textContent = formatTotal(week.totals.offCarole); + tdOff.classList.add("col-total"); tr.appendChild(tdOff); const tdExtra = document.createElement("td"); tdExtra.textContent = formatTotal(week.totals.extraOffCarole); + tdExtra.classList.add("col-total"); tr.appendChild(tdExtra); const tdCentre = document.createElement("td"); tdCentre.textContent = formatTotal(week.totals.centre); + tdCentre.classList.add("col-total"); tr.appendChild(tdCentre); const tdAvis = document.createElement("td"); tdAvis.textContent = formatTotal(week.totals.avis); + tdAvis.classList.add("col-total"); tr.appendChild(tdAvis); + const tdPepSick = document.createElement("td"); + tdPepSick.textContent = formatTotal(week.totals.pepSick); + tdPepSick.classList.add("col-total"); + tr.appendChild(tdPepSick); + + const tdPresencePep = document.createElement("td"); + tdPresencePep.textContent = formatTotal(week.totals.presencePep); + tdPresencePep.classList.add("col-total"); + tr.appendChild(tdPresencePep); + + // 6 colonnes ALEX + for (let i = 0; i < 6; i++) { + const td = document.createElement("td"); + td.textContent = ""; + td.classList.add("col-alex-sub"); + tr.appendChild(td); + } + + // 6 colonnes LAIA + for (let i = 0; i < 6; i++) { + const td = document.createElement("td"); + td.textContent = ""; + td.classList.add("col-laia-sub"); + tr.appendChild(td); + } + this.planningBody.appendChild(tr); }); } @@ -428,11 +553,6 @@ document.addEventListener("DOMContentLoaded", () => { this.monthSelectionMenu.addEventListener("click", (e) => this.handleMonthMenuClick(e) ); - - // Gestion des actions bulk (bulk-update*, bulk-delete*) via handleMenuClick - this.monthSelectionMenu.addEventListener("click", (e) => - this.handleMenuClick(e) - ); } } @@ -512,12 +632,14 @@ document.addEventListener("DOMContentLoaded", () => { ); const conge = eventsOnDay.find((ev) => CONGE_TYPES.includes(ev.type)); const garde = eventsOnDay.find((ev) => GUARDE_TYPES.includes(ev.type)); + const pep = eventsOnDay.find((ev) => PEP_TYPES.includes(ev.type)); - if (!conge && !garde) { + if (!conge && !garde && !pep) { this.showAddMenu(e); } else { - this.showEditMenuForDay(e, { conge, garde, date }); + this.showEditMenuForDay(e, { conge, garde, pep, date }); } + return; } @@ -662,11 +784,13 @@ document.addEventListener("DOMContentLoaded", () => { + + `; this.positionAndShowMenu(e); } - showEditMenuForDay(e, { conge, garde, date }) { + showEditMenuForDay(e, { conge, garde, pep, date }) { console.log( "[EDIT MENU] pour date", date, @@ -729,6 +853,24 @@ document.addEventListener("DOMContentLoaded", () => { `; } + // --- Section Pep malade --- + let pepSection = ""; + if (pep) { + pepSection = ` + + + `; + } else { + pepSection = ` + + + `; + } + // --- Section congés Alex / Laia --- console.log( "[EDIT MENU] this.leaves length =", @@ -770,7 +912,7 @@ document.addEventListener("DOMContentLoaded", () => { `; } - const fullHtml = congeSection + gardeSection + leavesSection; + const fullHtml = congeSection + gardeSection + pepSection + leavesSection; console.log("[EDIT MENU] full menu HTML length =", fullHtml.length); this.selectionMenu.innerHTML = fullHtml; @@ -835,17 +977,22 @@ document.addEventListener("DOMContentLoaded", () => { }; } - const res = await fetch("/api/manage-event.php", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); + const res = await fetch( + "/modules/family-calendar/includes/api/manage-event.php", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + } + ); if (!res.ok) { const errData = await res.json().catch(() => ({})); throw new Error(errData.message || "Erreur HTTP " + res.status); } - const resEvents = await fetch("/api/get-events.php"); + const resEvents = await fetch( + "/modules/family-calendar/includes/api/get-events.php" + ); if (!resEvents.ok) { throw new Error("Erreur lors du rechargement des événements."); } @@ -885,11 +1032,14 @@ document.addEventListener("DOMContentLoaded", () => { this.clearSelection(); try { - const response = await fetch("/api/save-leaves.php", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify([newLeave]), - }); + const response = await fetch( + "/modules/family-calendar/includes/api/save-leaves.php", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify([newLeave]), + } + ); if (!response.ok) { throw new Error("Erreur HTTP " + response.status); } @@ -917,11 +1067,14 @@ document.addEventListener("DOMContentLoaded", () => { this.clearSelection(); try { - const response = await fetch("/api/manage-leaf.php", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ action: "delete_day", date: leaveDate }), - }); + const response = await fetch( + "/modules/family-calendar/includes/api/manage-leaf.php", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ action: "delete_day", date: leaveDate }), + } + ); if (!response.ok) { const errData = await response.json().catch(() => ({})); throw new Error( @@ -985,17 +1138,22 @@ document.addEventListener("DOMContentLoaded", () => { this.clearSelection(); try { - const response = await fetch("/api/save-events.php", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(newEvents), - }); + const response = await fetch( + "/modules/family-calendar/includes/api/save-events.php", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(newEvents), + } + ); if (!response.ok) { throw new Error("Erreur HTTP " + response.status); } // Resync DB events - const resEvents = await fetch("/api/get-events.php"); + const resEvents = await fetch( + "/modules/family-calendar/includes/api/get-events.php" + ); if (!resEvents.ok) { throw new Error("Erreur lors du rechargement des événements."); } @@ -1050,16 +1208,21 @@ document.addEventListener("DOMContentLoaded", () => { this.clearSelection(); try { - const response = await fetch("/api/save-events.php", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify([newEvent]), - }); + const response = await fetch( + "/modules/family-calendar/includes/api/save-events.php", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify([newEvent]), + } + ); if (!response.ok) { throw new Error("Erreur HTTP " + response.status); } - const resEvents = await fetch("/api/get-events.php"); + const resEvents = await fetch( + "/modules/family-calendar/includes/api/get-events.php" + ); if (!resEvents.ok) { throw new Error("Erreur lors du rechargement des événements."); } @@ -1088,11 +1251,14 @@ document.addEventListener("DOMContentLoaded", () => { this.clearSelection(); try { - const response = await fetch("/api/manage-event.php", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ action: "delete", event_id: eventId }), - }); + const response = await fetch( + "/modules/family-calendar/includes/api/manage-event.php", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ action: "delete", event_id: eventId }), + } + ); if (!response.ok) { const errData = await response.json().catch(() => ({})); throw new Error( @@ -1100,7 +1266,9 @@ document.addEventListener("DOMContentLoaded", () => { ); } - const resEvents = await fetch("/api/get-events.php"); + const resEvents = await fetch( + "/modules/family-calendar/includes/api/get-events.php" + ); if (!resEvents.ok) { throw new Error("Erreur lors du rechargement des événements."); } @@ -1131,15 +1299,18 @@ document.addEventListener("DOMContentLoaded", () => { this.clearSelection(); try { - const response = await fetch("/api/manage-event.php", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - action: "update", - event_id: eventId, - new_type: newType, - }), - }); + const response = await fetch( + "/modules/family-calendar/includes/api/manage-event.php", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + action: "update", + event_id: eventId, + new_type: newType, + }), + } + ); if (!response.ok) { const errData = await response.json().catch(() => ({})); throw new Error( @@ -1147,7 +1318,9 @@ document.addEventListener("DOMContentLoaded", () => { ); } - const resEvents = await fetch("/api/get-events.php"); + const resEvents = await fetch( + "/modules/family-calendar/includes/api/get-events.php" + ); if (!resEvents.ok) { throw new Error("Erreur lors du rechargement des événements."); } @@ -1168,17 +1341,22 @@ document.addEventListener("DOMContentLoaded", () => { async manageEvent(payload) { try { - const response = await fetch("/api/manage-event.php", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); + const response = await fetch( + "/modules/family-calendar/includes/api/manage-event.php", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + } + ); if (!response.ok) { const errData = await response.json().catch(() => ({})); throw new Error(errData.message || "Erreur HTTP " + response.status); } - const resEvents = await fetch("/api/get-events.php"); + const resEvents = await fetch( + "/modules/family-calendar/includes/api/get-events.php" + ); if (!resEvents.ok) { throw new Error("Erreur lors du rechargement des événements."); } @@ -1276,31 +1454,38 @@ document.addEventListener("DOMContentLoaded", () => { } calculateMonthTotals(year, month) { - // Calculer le premier et dernier jour du mois (uniquement jours de semaine) - const firstDay = new Date(year, month, 1); const lastDay = new Date(year, month + 1, 0); const totals = { offCarole: 0, extraOffCarole: 0, centre: 0, avis: 0, + pepSick: 0, + presencePep: 0, + workingDays: 0, // jours potentiels d'accueil Pep }; - // Parcourir tous les jours du mois for (let day = 1; day <= lastDay.getDate(); day++) { const date = new Date(year, month, day); - const dayOfWeek = date.getDay(); + const dayOfWeek = date.getDay(); // 0=dim, 6=sam - // Ignorer les samedi (6) et dimanche (0) if (dayOfWeek === 0 || dayOfWeek === 6) { - continue; + continue; // on saute samedi/dimanche } const isoDate = `${year}-${String(month + 1).padStart(2, "0")}-${String( day ).padStart(2, "0")}`; - // Trouver les événements pour ce jour + // Si jour férié, ce n'est PAS un jour potentiel d'accueil Pep + const isPublicHoliday = + this.publicHolidayDates && this.publicHolidayDates.has(isoDate); + + if (!isPublicHoliday) { + totals.workingDays++; + } + + // Événements de base (pf_events) pour ce jour const dayEvents = this.dbEvents.filter((evt) => evt.date === isoDate); dayEvents.forEach((evt) => { @@ -1318,17 +1503,27 @@ document.addEventListener("DOMContentLoaded", () => { case "AVIS": totals.avis += dur; break; + case "PEP_SICK": + totals.pepSick += dur; + break; } }); } + const absencesPep = + (totals.offCarole || 0) + + (totals.extraOffCarole || 0) + + (totals.pepSick || 0); + + totals.presencePep = Math.max(0, totals.workingDays - absencesPep); + return totals; } generateMonthSummaryHTML(year, month) { const totals = this.calculateMonthTotals(year, month); const formatTotal = (total) => - total > 0 ? (Number.isInteger(total) ? total : total.toFixed(1)) : ""; + total > 0 ? (Number.isInteger(total) ? total : total.toFixed(1)) : "0"; const monthLabel = `${getMonthNameFr(month)} ${year}`; @@ -1338,19 +1533,21 @@ document.addEventListener("DOMContentLoaded", () => {