pep malade

This commit is contained in:
2026-01-07 09:36:11 +01:00
parent 3b895f3f0c
commit ab46c105f7
10 changed files with 632 additions and 161 deletions
+113 -14
View File
@@ -65,6 +65,10 @@ require __DIR__ . '/header.php';
<div class="pf-legend-color fc-legend-avis"></div> <div class="pf-legend-color fc-legend-avis"></div>
<span>Avis</span> <span>Avis</span>
</div> </div>
<div class="pf-legend-item">
<div class="pf-legend-color fc-legend-pep-sick"></div>
<span>Pep malade</span>
</div>
</div> </div>
</div> </div>
@@ -134,26 +138,121 @@ require __DIR__ . '/header.php';
<!-- ===================================================================== --> <!-- ===================================================================== -->
<section class="pf-section"> <section class="pf-section">
<h2>Planning par semaine</h2> <h2>Planning par semaine</h2>
<div class="pf-table-wrapper"> <div class="pf-table-wrapper" id="planningTable-wrapper">
<table id="planningTable" class="pf-table pf-table--compact"> <table id="planningHeaderTable" class="pf-table pf-table--compact">
<colgroup>
<col class="col-month"> <!-- Mois -->
<col class="col-month"> <!-- Semaine -->
<col class="col-day"> <!-- Lundi -->
<col class="col-day"> <!-- Mardi -->
<col class="col-day"> <!-- Mercredi -->
<col class="col-day"> <!-- Jeudi -->
<col class="col-day"> <!-- Vendredi -->
<col class="col-total"> <!-- # Off -->
<col class="col-total"> <!-- # Extra -->
<col class="col-total"> <!-- # Centre -->
<col class="col-total"> <!-- # Avis -->
<col class="col-total"> <!-- # Pep malade -->
<col class="col-total"> <!-- # Pep Présence -->
<!-- ALEX (6 colonnes) -->
<col class="col-alex-sub">
<col class="col-alex-sub">
<col class="col-alex-sub">
<col class="col-alex-sub">
<col class="col-alex-sub">
<col class="col-alex-sub">
<!-- LAIA (6 colonnes) -->
<col class="col-laia-sub">
<col class="col-laia-sub">
<col class="col-laia-sub">
<col class="col-laia-sub">
<col class="col-laia-sub">
<col class="col-laia-sub">
</colgroup>
<thead> <thead>
<tr> <tr>
<th>Mois</th> <th rowspan="3" class="col-month">Mois</th>
<th>Semaine</th> <th rowspan="3" class="col-month">Semaine</th>
<th>Lundi</th> <th rowspan="3" class="col-day">Lundi</th>
<th>Mardi</th> <th rowspan="3" class="col-day">Mardi</th>
<th>Mercredi</th> <th rowspan="3" class="col-day">Mercredi</th>
<th>Jeudi</th> <th rowspan="3" class="col-day">Jeudi</th>
<th>Vendredi</th> <th rowspan="3" class="col-day">Vendredi</th>
<th># Off Carole</th> <th rowspan="3" class="col-total"># Off Carole</th>
<th># Extra off Carole</th> <th rowspan="3" class="col-total"># Extra off Carole</th>
<th>#Centre</th> <th rowspan="3" class="col-total"># Centre</th>
<th>#Avis</th> <th rowspan="3" class="col-total"># Avis</th>
<th rowspan="3" class="col-total"># Pep malade</th>
<th rowspan="3" class="col-total"># Pep Présence</th>
<!-- Ligne 1 : ALEX / LAIA -->
<th colspan="6" class="col-alex">ALEX</th>
<th colspan="6" class="col-laia">LAIA</th>
</tr>
<tr>
<!-- Ligne 2 : CP / JRA / JA (regroupement) -->
<th colspan="2" class="col-alex-sub">CP</th>
<th colspan="2" class="col-alex-sub">JRA</th>
<th colspan="2" class="col-alex-sub">JA</th>
<th colspan="2" class="col-laia-sub">CP</th>
<th colspan="2" class="col-laia-sub">JRA</th>
<th colspan="2" class="col-laia-sub">JA</th>
</tr>
<tr>
<!-- Ligne 3 : Available / Use pour chaque type -->
<!-- ALEX -->
<th class="col-alex-sub">Av.</th>
<th class="col-alex-sub">Use</th>
<th class="col-alex-sub">Av.</th>
<th class="col-alex-sub">Use</th>
<th class="col-alex-sub">Av.</th>
<th class="col-alex-sub">Use</th>
<!-- LAIA -->
<th class="col-laia-sub">Av.</th>
<th class="col-laia-sub">Use</th>
<th class="col-laia-sub">Av.</th>
<th class="col-laia-sub">Use</th>
<th class="col-laia-sub">Av.</th>
<th class="col-laia-sub">Use</th>
</tr> </tr>
</thead> </thead>
</table>
<table id="planningTable" class="pf-table pf-table--compact">
<tbody id="planningBody"> <tbody id="planningBody">
<!-- Le contenu de ce tableau est entièrement généré par family-calendar.js --> <colgroup>
<col class="col-month"> <!-- Mois -->
<col class="col-month"> <!-- Semaine -->
<col class="col-day"> <!-- Lundi -->
<col class="col-day"> <!-- Mardi -->
<col class="col-day"> <!-- Mercredi -->
<col class="col-day"> <!-- Jeudi -->
<col class="col-day"> <!-- Vendredi -->
<col class="col-total"> <!-- # Off -->
<col class="col-total"> <!-- # Extra -->
<col class="col-total"> <!-- # Centre -->
<col class="col-total"> <!-- # Avis -->
<col class="col-total"> <!-- # Pep malade -->
<col class="col-total"> <!-- # Pep Présence -->
<!-- ALEX (6 colonnes) -->
<col class="col-alex-sub">
<col class="col-alex-sub">
<col class="col-alex-sub">
<col class="col-alex-sub">
<col class="col-alex-sub">
<col class="col-alex-sub">
<!-- LAIA (6 colonnes) -->
<col class="col-laia-sub">
<col class="col-laia-sub">
<col class="col-laia-sub">
<col class="col-laia-sub">
<col class="col-laia-sub">
<col class="col-laia-sub">
</colgroup>
<!-- lignes générées par JS -->
</tbody> </tbody>
</table> </table>
<!-- Le menu contextuel est caché par défaut et son contenu est généré par JS --> <!-- Le menu contextuel est caché par défaut et son contenu est généré par JS -->
+127 -28
View File
@@ -29,44 +29,106 @@
#planningTable th, #planningTable th,
#planningTable td { #planningTable td {
height: 32px;
min-height: 32px;
position: relative;
text-align: center; text-align: center;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
/* Colonnes "Mois" et "Semaine" plus étroites */ /* Mois / Semaine */
#planningTable th:nth-child(1), #planningHeaderTable .col-month,
#planningTable td:nth-child(1), #planningTable .col-month {
#planningTable th:nth-child(2), width: 60px;
#planningTable td:nth-child(2) { max-width: 60px;
width: 2%;
} }
/* Colonnes jours (Lundi à Vendredi) */ /* Jours */
#planningTable th:nth-child(3), #planningHeaderTable .col-day,
#planningTable td:nth-child(3), #planningTable .col-day {
#planningTable th:nth-child(4), width: 70px;
#planningTable td:nth-child(4), max-width: 70px;
#planningTable th:nth-child(5),
#planningTable td:nth-child(5),
#planningTable th:nth-child(6),
#planningTable td:nth-child(6),
#planningTable th:nth-child(7),
#planningTable td:nth-child(7) {
width: 2%;
} }
/* Colonnes de totaux */ /* Totaux */
#planningTable th:nth-child(8), #planningHeaderTable .col-total,
#planningTable td:nth-child(8), #planningTable .col-total {
#planningTable th:nth-child(9), width: 50px;
#planningTable td:nth-child(9), max-width: 50px;
#planningTable th:nth-child(10), white-space: nowrap;
#planningTable td:nth-child(10), }
#planningTable th:nth-child(11),
#planningTable td:nth-child(11) { /* Sous-colonnes Alex/Laia */
width: 4%; #planningHeaderTable .col-alex-sub,
#planningHeaderTable .col-laia-sub,
#planningTable .col-alex-sub,
#planningTable .col-laia-sub {
width: 40px;
max-width: 40px;
font-size: 11px;
}
/* Autoriser le wrap dans les TH des sous-colonnes pour garder lisible */
#planningTable thead th.col-total,
#planningTable thead th.col-alex-sub,
#planningTable thead th.col-laia-sub {
white-space: normal;
}
#planningTable-wrapper {
max-height: 500px;
overflow-y: auto;
overflow-x: auto;
position: relative;
}
/* Header sticky */
#planningHeaderTable {
width: 100%;
border-collapse: collapse;
}
/* Empêcher le header de bouger/contracter */
#planningHeaderTable th {
background: #f0f4f8;
}
/* Optionnel : aligner visuellement les deux tableaux (bordures, etc.) */
#planningHeaderTable,
#planningTable {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
#planningTable thead th {
top: 0;
z-index: 3;
background: #f0f4f8;
}
/* Ligne 1 */
#planningTable thead tr:nth-child(1) th {
position: sticky;
top: 0;
z-index: 3;
background: #f0f4f8;
}
/* Ligne 2 */
#planningTable thead tr:nth-child(2) th {
position: sticky;
top: 24px; /* hauteur de la 1ère ligne */
z-index: 3;
background: #f0f4f8;
}
/* Ligne 3 */
#planningTable thead tr:nth-child(3) th {
position: sticky;
top: 48px; /* 1ère + 2e ligne (22 + 22) */
z-index: 3;
background: #f0f4f8;
} }
/* === COLOR CODING / LÉGENDE === */ /* === COLOR CODING / LÉGENDE === */
@@ -189,6 +251,16 @@
line-height: 1; line-height: 1;
z-index: 2; z-index: 2;
} }
/* Planning hebdo : picto Centre en haut à droite */
#planningTable .fc-day--has-guard.fc-day--centre::after {
content: "🏫";
position: absolute;
top: 2px;
right: 2px;
font-size: 12px;
line-height: 1;
z-index: 2;
}
/* Combinaisons avec dégradés */ /* Combinaisons avec dégradés */
.fc-day--school-holiday.fc-day--off-carole { .fc-day--school-holiday.fc-day--off-carole {
@@ -247,6 +319,33 @@
content: "🏫"; content: "🏫";
} }
.fc-legend-pep-sick {
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
background: none;
border: none;
}
.fc-legend-pep-sick::before {
content: "🤒";
}
.fc-pep-sick-emoji {
position: absolute;
right: 2px;
bottom: 2px;
font-size: 14px;
line-height: 1;
}
#planningTable .fc-pep-sick-emoji {
font-size: 12px;
right: 2px;
bottom: 2px;
}
/* Avis indicateur */ /* Avis indicateur */
.fc-legend-avis { .fc-legend-avis {
border-radius: 2px; border-radius: 2px;
+359 -112
View File
@@ -5,7 +5,8 @@
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const CONGE_TYPES = ["OFF_CAROLE", "EXTRA_OFF_CAROLE"]; const CONGE_TYPES = ["OFF_CAROLE", "EXTRA_OFF_CAROLE"];
const GUARDE_TYPES = ["CENTRE", "AVIS"]; const GUARDE_TYPES = ["CENTRE", "AVIS"];
const MODIFIABLE_TYPES = [...CONGE_TYPES, ...GUARDE_TYPES]; const PEP_TYPES = ["PEP_SICK"];
const MODIFIABLE_TYPES = [...CONGE_TYPES, ...GUARDE_TYPES, ...PEP_TYPES];
class FamilyCalendar { class FamilyCalendar {
constructor() { constructor() {
@@ -58,6 +59,13 @@ document.addEventListener("DOMContentLoaded", () => {
this.events = [...this.dbEvents, ...this.fixedEvents]; this.events = [...this.dbEvents, ...this.fixedEvents];
this.leaves = await this.fetchLeaves(); this.leaves = await this.fetchLeaves();
// Nouveau : set des jours fériés (dates ISO)
this.publicHolidayDates = new Set(
this.fixedEvents
.filter((e) => e.type === "PUBLIC_HOLIDAY")
.map((e) => e.date)
);
this.reprocessAndRender(); this.reprocessAndRender();
this.renderMonthCalendar(); this.renderMonthCalendar();
} }
@@ -77,7 +85,9 @@ document.addEventListener("DOMContentLoaded", () => {
async fetchLeaves() { async fetchLeaves() {
try { try {
const res = await fetch("/api/get-leaves.php"); const res = await fetch(
"/modules/family-calendar/includes/api/get-leaves.php"
);
if (!res.ok) { if (!res.ok) {
throw new Error("Erreur HTTP " + res.status); throw new Error("Erreur HTTP " + res.status);
} }
@@ -199,7 +209,14 @@ document.addEventListener("DOMContentLoaded", () => {
reprocessEvents() { reprocessEvents() {
this.weeks.forEach((w) => { this.weeks.forEach((w) => {
w.totals = { offCarole: 0, extraOffCarole: 0, centre: 0, avis: 0 }; w.totals = {
offCarole: 0,
extraOffCarole: 0,
centre: 0,
avis: 0,
pepSick: 0,
presencePep: 0,
};
Object.values(w.dayFlags).forEach((df) => (df.eventsOnDay = [])); Object.values(w.dayFlags).forEach((df) => (df.eventsOnDay = []));
}); });
@@ -231,15 +248,44 @@ document.addEventListener("DOMContentLoaded", () => {
case "AVIS": case "AVIS":
week.totals.avis += dur; week.totals.avis += dur;
break; break;
case "PEP_SICK":
week.totals.pepSick += dur;
break;
} }
}); });
// Calcul présence Pep par semaine
this.weeks.forEach((w) => {
// Calcul du nombre de jours potentiels d'accueil Pep dans la semaine
let workingDays = 0;
["mon", "tue", "wed", "thu", "fri"].forEach((dayKey) => {
const d = w.dayDates[dayKey];
const isoDate = `${d.getFullYear()}-${String(
d.getMonth() + 1
).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
const isPublicHoliday =
this.publicHolidayDates && this.publicHolidayDates.has(isoDate);
if (!isPublicHoliday) {
workingDays++;
}
});
const absencesPep =
(w.totals.offCarole || 0) +
(w.totals.extraOffCarole || 0) +
(w.totals.pepSick || 0);
w.totals.presencePep = Math.max(0, workingDays - absencesPep);
w.totals.workingDays = workingDays; // si tu veux l'afficher un jour
});
} }
updateGlobalSummary() { updateGlobalSummary() {
const summaryDiv = document.getElementById("globalSummary"); const summaryDiv = document.getElementById("globalSummary");
if (!summaryDiv) return; if (!summaryDiv) return;
// On filtre uniquement sur les événements "DB" pour le récap (pas les vacances/fériés)
const allEvents = this.dbEvents || []; const allEvents = this.dbEvents || [];
const totalOff = allEvents const totalOff = allEvents
@@ -250,9 +296,38 @@ document.addEventListener("DOMContentLoaded", () => {
.filter((e) => e.type === "EXTRA_OFF_CAROLE") .filter((e) => e.type === "EXTRA_OFF_CAROLE")
.reduce((sum, e) => sum + Number(e.duration || 1), 0); .reduce((sum, e) => sum + Number(e.duration || 1), 0);
const totalPepSick = allEvents
.filter((e) => e.type === "PEP_SICK")
.reduce((sum, e) => sum + Number(e.duration || 1), 0);
// Calculer les jours ouvrés & présence Pep sur l'année scolaire
let totalWorkingDays = 0;
let totalPresencePep = 0;
// On parcourt l'année scolaire en mois, en réutilisant calculateMonthTotals
const start = new Date(2025, 8, 1); // 1 sept 2025
const end = new Date(2026, 7, 31); // 31 août 2026
const current = new Date(start);
while (current <= end) {
const y = current.getFullYear();
const m = current.getMonth();
const monthTotals = this.calculateMonthTotals(y, m);
totalWorkingDays += monthTotals.workingDays || 0;
totalPresencePep += monthTotals.presencePep || 0;
// Passer au 1er du mois suivant
current.setMonth(current.getMonth() + 1);
current.setDate(1);
}
summaryDiv.innerHTML = ` summaryDiv.innerHTML = `
<p><strong>Off Carole :</strong> ${totalOff} jours</p> <p><strong>Off Carole :</strong> ${totalOff} jours</p>
<p><strong>Extra Off Carole :</strong> ${totalExtraOff} jours</p> <p><strong>Extra Off Carole :</strong> ${totalExtraOff} jours</p>
<p><strong>Pep malade :</strong> ${totalPepSick} jours</p>
<p><strong>Jours potentiels d'accueil Pep (année scolaire, hors fériés) :</strong> ${totalWorkingDays}</p>
<p><strong>Présence Pep (année scolaire) :</strong> ${totalPresencePep} jours</p>
`; `;
} }
@@ -294,20 +369,27 @@ document.addEventListener("DOMContentLoaded", () => {
this.weeks.forEach((week) => { this.weeks.forEach((week) => {
const tr = document.createElement("tr"); const tr = document.createElement("tr");
// 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");
tdMonth.rowSpan = monthSpans[week.monthKey] || 1; tdMonth.rowSpan = monthSpans[week.monthKey] || 1;
tdMonth.textContent = week.monthName; tdMonth.textContent = week.monthName;
tdMonth.classList.add("col-month");
tr.appendChild(tdMonth); tr.appendChild(tdMonth);
} }
// Semaine
const tdWeek = document.createElement("td"); const tdWeek = document.createElement("td");
tdWeek.textContent = week.weekLabel; tdWeek.textContent = week.weekLabel;
tdWeek.classList.add("col-month");
tr.appendChild(tdWeek); tr.appendChild(tdWeek);
// Jours LundiVendredi
["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.classList.add("col-day");
const dayDate = week.dayDates[dayKey]; const dayDate = week.dayDates[dayKey];
td.dataset.date = `${dayDate.getFullYear()}-${String( td.dataset.date = `${dayDate.getFullYear()}-${String(
dayDate.getMonth() + 1 dayDate.getMonth() + 1
@@ -317,9 +399,9 @@ document.addEventListener("DOMContentLoaded", () => {
"0" "0"
)}/${String(dayDate.getMonth() + 1).padStart(2, "0")}`; )}/${String(dayDate.getMonth() + 1).padStart(2, "0")}`;
td.className = "";
let hasGarde = false; let hasGarde = false;
let gardeType = null; let gardeType = null;
let hasPepSick = false;
week.dayFlags[dayKey].eventsOnDay.forEach((evt) => { week.dayFlags[dayKey].eventsOnDay.forEach((evt) => {
const classMap = { const classMap = {
@@ -327,6 +409,7 @@ document.addEventListener("DOMContentLoaded", () => {
PUBLIC_HOLIDAY: "fc-day--public-holiday", PUBLIC_HOLIDAY: "fc-day--public-holiday",
OFF_CAROLE: "fc-day--off-carole", OFF_CAROLE: "fc-day--off-carole",
EXTRA_OFF_CAROLE: "fc-day--extra-off-carole", EXTRA_OFF_CAROLE: "fc-day--extra-off-carole",
// pas de couleur pour PEP_SICK dans l'hebdo
}; };
if (classMap[evt.type]) { if (classMap[evt.type]) {
@@ -337,6 +420,10 @@ document.addEventListener("DOMContentLoaded", () => {
hasGarde = true; hasGarde = true;
gardeType = evt.type; gardeType = evt.type;
} }
if (evt.type === "PEP_SICK") {
hasPepSick = true;
}
}); });
if (hasGarde) { if (hasGarde) {
@@ -345,7 +432,14 @@ document.addEventListener("DOMContentLoaded", () => {
if (gardeType === "AVIS") td.classList.add("fc-day--avis"); 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 isoDate = td.dataset.date;
const leavesOnDay = this.leaves.filter( const leavesOnDay = this.leaves.filter(
(lv) => lv.leave_date === isoDate (lv) => lv.leave_date === isoDate
@@ -355,8 +449,8 @@ document.addEventListener("DOMContentLoaded", () => {
const container = document.createElement("div"); const container = document.createElement("div");
container.className = "fc-leaves-label-container"; container.className = "fc-leaves-label-container";
const hasAlex = leavesOnDay.some((lv) => lv.person_id === 2); // Alex const hasAlex = leavesOnDay.some((lv) => lv.person_id === 2);
const hasLaia = leavesOnDay.some((lv) => lv.person_id === 3); // Laia const hasLaia = leavesOnDay.some((lv) => lv.person_id === 3);
if (hasAlex) { if (hasAlex) {
const alexSpan = document.createElement("span"); const alexSpan = document.createElement("span");
@@ -378,22 +472,53 @@ document.addEventListener("DOMContentLoaded", () => {
tr.appendChild(td); tr.appendChild(td);
}); });
// Totaux
const tdOff = document.createElement("td"); const tdOff = document.createElement("td");
tdOff.textContent = formatTotal(week.totals.offCarole); tdOff.textContent = formatTotal(week.totals.offCarole);
tdOff.classList.add("col-total");
tr.appendChild(tdOff); tr.appendChild(tdOff);
const tdExtra = document.createElement("td"); const tdExtra = document.createElement("td");
tdExtra.textContent = formatTotal(week.totals.extraOffCarole); tdExtra.textContent = formatTotal(week.totals.extraOffCarole);
tdExtra.classList.add("col-total");
tr.appendChild(tdExtra); tr.appendChild(tdExtra);
const tdCentre = document.createElement("td"); const tdCentre = document.createElement("td");
tdCentre.textContent = formatTotal(week.totals.centre); tdCentre.textContent = formatTotal(week.totals.centre);
tdCentre.classList.add("col-total");
tr.appendChild(tdCentre); tr.appendChild(tdCentre);
const tdAvis = document.createElement("td"); const tdAvis = document.createElement("td");
tdAvis.textContent = formatTotal(week.totals.avis); tdAvis.textContent = formatTotal(week.totals.avis);
tdAvis.classList.add("col-total");
tr.appendChild(tdAvis); 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); this.planningBody.appendChild(tr);
}); });
} }
@@ -428,11 +553,6 @@ document.addEventListener("DOMContentLoaded", () => {
this.monthSelectionMenu.addEventListener("click", (e) => this.monthSelectionMenu.addEventListener("click", (e) =>
this.handleMonthMenuClick(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 conge = eventsOnDay.find((ev) => CONGE_TYPES.includes(ev.type));
const garde = eventsOnDay.find((ev) => GUARDE_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); this.showAddMenu(e);
} else { } else {
this.showEditMenuForDay(e, { conge, garde, date }); this.showEditMenuForDay(e, { conge, garde, pep, date });
} }
return; return;
} }
@@ -662,11 +784,13 @@ document.addEventListener("DOMContentLoaded", () => {
<div class="fc-menu-section"><strong>Mode de Garde</strong></div> <div class="fc-menu-section"><strong>Mode de Garde</strong></div>
<button data-action="add" data-type="CENTRE">Centre</button> <button data-action="add" data-type="CENTRE">Centre</button>
<button data-action="add" data-type="AVIS">Avis</button> <button data-action="add" data-type="AVIS">Avis</button>
<div class="fc-menu-section"><strong>Pep</strong></div>
<button data-action="add" data-type="PEP_SICK">Pep malade</button>
`; `;
this.positionAndShowMenu(e); this.positionAndShowMenu(e);
} }
showEditMenuForDay(e, { conge, garde, date }) { showEditMenuForDay(e, { conge, garde, pep, date }) {
console.log( console.log(
"[EDIT MENU] pour date", "[EDIT MENU] pour date",
date, date,
@@ -729,6 +853,24 @@ document.addEventListener("DOMContentLoaded", () => {
`; `;
} }
// --- Section Pep malade ---
let pepSection = "";
if (pep) {
pepSection = `
<div class="fc-menu-section"><strong>Pep</strong></div>
<button data-action="delete" data-event-id="${pep.id}">
Supprimer "Pep malade"
</button>
`;
} else {
pepSection = `
<div class="fc-menu-section"><strong>Pep</strong></div>
<button data-action="add-single" data-type="PEP_SICK" data-date="${date}">
Marquer Pep malade
</button>
`;
}
// --- Section congés Alex / Laia --- // --- Section congés Alex / Laia ---
console.log( console.log(
"[EDIT MENU] this.leaves length =", "[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); console.log("[EDIT MENU] full menu HTML length =", fullHtml.length);
this.selectionMenu.innerHTML = fullHtml; this.selectionMenu.innerHTML = fullHtml;
@@ -835,17 +977,22 @@ document.addEventListener("DOMContentLoaded", () => {
}; };
} }
const res = await fetch("/api/manage-event.php", { const res = await fetch(
method: "POST", "/modules/family-calendar/includes/api/manage-event.php",
headers: { "Content-Type": "application/json" }, {
body: JSON.stringify(payload), method: "POST",
}); headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}
);
if (!res.ok) { if (!res.ok) {
const errData = await res.json().catch(() => ({})); const errData = await res.json().catch(() => ({}));
throw new Error(errData.message || "Erreur HTTP " + res.status); 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) { if (!resEvents.ok) {
throw new Error("Erreur lors du rechargement des événements."); throw new Error("Erreur lors du rechargement des événements.");
} }
@@ -885,11 +1032,14 @@ document.addEventListener("DOMContentLoaded", () => {
this.clearSelection(); this.clearSelection();
try { try {
const response = await fetch("/api/save-leaves.php", { const response = await fetch(
method: "POST", "/modules/family-calendar/includes/api/save-leaves.php",
headers: { "Content-Type": "application/json" }, {
body: JSON.stringify([newLeave]), method: "POST",
}); headers: { "Content-Type": "application/json" },
body: JSON.stringify([newLeave]),
}
);
if (!response.ok) { if (!response.ok) {
throw new Error("Erreur HTTP " + response.status); throw new Error("Erreur HTTP " + response.status);
} }
@@ -917,11 +1067,14 @@ document.addEventListener("DOMContentLoaded", () => {
this.clearSelection(); this.clearSelection();
try { try {
const response = await fetch("/api/manage-leaf.php", { const response = await fetch(
method: "POST", "/modules/family-calendar/includes/api/manage-leaf.php",
headers: { "Content-Type": "application/json" }, {
body: JSON.stringify({ action: "delete_day", date: leaveDate }), method: "POST",
}); headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action: "delete_day", date: leaveDate }),
}
);
if (!response.ok) { if (!response.ok) {
const errData = await response.json().catch(() => ({})); const errData = await response.json().catch(() => ({}));
throw new Error( throw new Error(
@@ -985,17 +1138,22 @@ document.addEventListener("DOMContentLoaded", () => {
this.clearSelection(); this.clearSelection();
try { try {
const response = await fetch("/api/save-events.php", { const response = await fetch(
method: "POST", "/modules/family-calendar/includes/api/save-events.php",
headers: { "Content-Type": "application/json" }, {
body: JSON.stringify(newEvents), method: "POST",
}); headers: { "Content-Type": "application/json" },
body: JSON.stringify(newEvents),
}
);
if (!response.ok) { if (!response.ok) {
throw new Error("Erreur HTTP " + response.status); throw new Error("Erreur HTTP " + response.status);
} }
// Resync DB events // 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) { if (!resEvents.ok) {
throw new Error("Erreur lors du rechargement des événements."); throw new Error("Erreur lors du rechargement des événements.");
} }
@@ -1050,16 +1208,21 @@ document.addEventListener("DOMContentLoaded", () => {
this.clearSelection(); this.clearSelection();
try { try {
const response = await fetch("/api/save-events.php", { const response = await fetch(
method: "POST", "/modules/family-calendar/includes/api/save-events.php",
headers: { "Content-Type": "application/json" }, {
body: JSON.stringify([newEvent]), method: "POST",
}); headers: { "Content-Type": "application/json" },
body: JSON.stringify([newEvent]),
}
);
if (!response.ok) { if (!response.ok) {
throw new Error("Erreur HTTP " + response.status); 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) { if (!resEvents.ok) {
throw new Error("Erreur lors du rechargement des événements."); throw new Error("Erreur lors du rechargement des événements.");
} }
@@ -1088,11 +1251,14 @@ document.addEventListener("DOMContentLoaded", () => {
this.clearSelection(); this.clearSelection();
try { try {
const response = await fetch("/api/manage-event.php", { const response = await fetch(
method: "POST", "/modules/family-calendar/includes/api/manage-event.php",
headers: { "Content-Type": "application/json" }, {
body: JSON.stringify({ action: "delete", event_id: eventId }), method: "POST",
}); headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action: "delete", event_id: eventId }),
}
);
if (!response.ok) { if (!response.ok) {
const errData = await response.json().catch(() => ({})); const errData = await response.json().catch(() => ({}));
throw new Error( 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) { if (!resEvents.ok) {
throw new Error("Erreur lors du rechargement des événements."); throw new Error("Erreur lors du rechargement des événements.");
} }
@@ -1131,15 +1299,18 @@ document.addEventListener("DOMContentLoaded", () => {
this.clearSelection(); this.clearSelection();
try { try {
const response = await fetch("/api/manage-event.php", { const response = await fetch(
method: "POST", "/modules/family-calendar/includes/api/manage-event.php",
headers: { "Content-Type": "application/json" }, {
body: JSON.stringify({ method: "POST",
action: "update", headers: { "Content-Type": "application/json" },
event_id: eventId, body: JSON.stringify({
new_type: newType, action: "update",
}), event_id: eventId,
}); new_type: newType,
}),
}
);
if (!response.ok) { if (!response.ok) {
const errData = await response.json().catch(() => ({})); const errData = await response.json().catch(() => ({}));
throw new Error( 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) { if (!resEvents.ok) {
throw new Error("Erreur lors du rechargement des événements."); throw new Error("Erreur lors du rechargement des événements.");
} }
@@ -1168,17 +1341,22 @@ document.addEventListener("DOMContentLoaded", () => {
async manageEvent(payload) { async manageEvent(payload) {
try { try {
const response = await fetch("/api/manage-event.php", { const response = await fetch(
method: "POST", "/modules/family-calendar/includes/api/manage-event.php",
headers: { "Content-Type": "application/json" }, {
body: JSON.stringify(payload), method: "POST",
}); headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}
);
if (!response.ok) { if (!response.ok) {
const errData = await response.json().catch(() => ({})); const errData = await response.json().catch(() => ({}));
throw new Error(errData.message || "Erreur HTTP " + response.status); 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) { if (!resEvents.ok) {
throw new Error("Erreur lors du rechargement des événements."); throw new Error("Erreur lors du rechargement des événements.");
} }
@@ -1276,31 +1454,38 @@ document.addEventListener("DOMContentLoaded", () => {
} }
calculateMonthTotals(year, month) { 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 lastDay = new Date(year, month + 1, 0);
const totals = { const totals = {
offCarole: 0, offCarole: 0,
extraOffCarole: 0, extraOffCarole: 0,
centre: 0, centre: 0,
avis: 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++) { for (let day = 1; day <= lastDay.getDate(); day++) {
const date = new Date(year, month, 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) { if (dayOfWeek === 0 || dayOfWeek === 6) {
continue; continue; // on saute samedi/dimanche
} }
const isoDate = `${year}-${String(month + 1).padStart(2, "0")}-${String( const isoDate = `${year}-${String(month + 1).padStart(2, "0")}-${String(
day day
).padStart(2, "0")}`; ).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); const dayEvents = this.dbEvents.filter((evt) => evt.date === isoDate);
dayEvents.forEach((evt) => { dayEvents.forEach((evt) => {
@@ -1318,17 +1503,27 @@ document.addEventListener("DOMContentLoaded", () => {
case "AVIS": case "AVIS":
totals.avis += dur; totals.avis += dur;
break; 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; return totals;
} }
generateMonthSummaryHTML(year, month) { generateMonthSummaryHTML(year, month) {
const totals = this.calculateMonthTotals(year, month); const totals = this.calculateMonthTotals(year, month);
const formatTotal = (total) => 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}`; const monthLabel = `${getMonthNameFr(month)} ${year}`;
@@ -1338,19 +1533,21 @@ document.addEventListener("DOMContentLoaded", () => {
<thead> <thead>
<tr> <tr>
<th>Mois</th> <th>Mois</th>
<th>Jours ouvrés</th>
<th># Off Carole</th> <th># Off Carole</th>
<th># Extra off Carole</th> <th># Extra off Carole</th>
<th># Centre</th> <th># Pep malade</th>
<th># Avis</th> <th># Présence Pep</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>${monthLabel}</td> <td>${monthLabel}</td>
<td>${formatTotal(totals.workingDays)}</td>
<td>${formatTotal(totals.offCarole)}</td> <td>${formatTotal(totals.offCarole)}</td>
<td>${formatTotal(totals.extraOffCarole)}</td> <td>${formatTotal(totals.extraOffCarole)}</td>
<td>${formatTotal(totals.centre)}</td> <td>${formatTotal(totals.pepSick)}</td>
<td>${formatTotal(totals.avis)}</td> <td>${formatTotal(totals.presencePep)}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -1602,6 +1799,7 @@ document.addEventListener("DOMContentLoaded", () => {
let classes = "fc-month-day"; let classes = "fc-month-day";
let hasGarde = false; let hasGarde = false;
let gardeType = null; let gardeType = null;
let hasPepSick = false; // <- ajouté
dayEvents.forEach((evt) => { dayEvents.forEach((evt) => {
const classMap = { const classMap = {
@@ -1609,6 +1807,7 @@ document.addEventListener("DOMContentLoaded", () => {
PUBLIC_HOLIDAY: "fc-day--public-holiday", PUBLIC_HOLIDAY: "fc-day--public-holiday",
OFF_CAROLE: "fc-day--off-carole", OFF_CAROLE: "fc-day--off-carole",
EXTRA_OFF_CAROLE: "fc-day--extra-off-carole", EXTRA_OFF_CAROLE: "fc-day--extra-off-carole",
// PEP_SICK: "fc-day--pep-sick",
}; };
if (classMap[evt.type]) { if (classMap[evt.type]) {
@@ -1619,6 +1818,10 @@ document.addEventListener("DOMContentLoaded", () => {
hasGarde = true; hasGarde = true;
gardeType = evt.type; gardeType = evt.type;
} }
if (evt.type === "PEP_SICK") {
hasPepSick = true;
}
}); });
if (hasGarde) { if (hasGarde) {
@@ -1635,6 +1838,10 @@ document.addEventListener("DOMContentLoaded", () => {
let cellInnerHTML = `${day}`; let cellInnerHTML = `${day}`;
if (hasPepSick) {
cellInnerHTML += `<span class="fc-pep-sick-emoji">🤒</span>`;
}
if (leavesOnDay.length > 0) { if (leavesOnDay.length > 0) {
const hasAlex = leavesOnDay.some((lv) => lv.person_id === 2); // Alex const hasAlex = leavesOnDay.some((lv) => lv.person_id === 2); // Alex
const hasLaia = leavesOnDay.some((lv) => lv.person_id === 3); // Laia const hasLaia = leavesOnDay.some((lv) => lv.person_id === 3); // Laia
@@ -1696,11 +1903,11 @@ document.addEventListener("DOMContentLoaded", () => {
); );
const conge = eventsOnDay.find((e) => CONGE_TYPES.includes(e.type)); const conge = eventsOnDay.find((e) => CONGE_TYPES.includes(e.type));
const garde = eventsOnDay.find((e) => GUARDE_TYPES.includes(e.type)); const garde = eventsOnDay.find((e) => GUARDE_TYPES.includes(e.type));
const pep = eventsOnDay.find((e) => PEP_TYPES.includes(e.type));
if (!conge && !garde) { if (!conge && !garde && !pep) {
this.showMonthAddMenu(e); this.showMonthAddMenu(e);
} else { } else {
this.showMonthEditMenuForDay(e, { conge, garde, date }); this.showMonthEditMenuForDay(e, { conge, garde, pep, date });
} }
return; return;
} }
@@ -1761,11 +1968,13 @@ document.addEventListener("DOMContentLoaded", () => {
<div class="fc-menu-section"><strong>Mode de Garde</strong></div> <div class="fc-menu-section"><strong>Mode de Garde</strong></div>
<button data-action="add" data-type="CENTRE">Centre</button> <button data-action="add" data-type="CENTRE">Centre</button>
<button data-action="add" data-type="AVIS">Avis</button> <button data-action="add" data-type="AVIS">Avis</button>
<div class="fc-menu-section"><strong>Pep</strong></div>
<button data-action="add" data-type="PEP_SICK">Pep malade</button>
`; `;
this.positionAndShowMonthMenu(e); this.positionAndShowMonthMenu(e);
} }
showMonthEditMenuForDay(e, { conge, garde, date }) { showMonthEditMenuForDay(e, { conge, garde, pep, date }) {
if (!this.monthSelectionMenu) return; if (!this.monthSelectionMenu) return;
// --- Section congé Carole --- // --- Section congé Carole ---
@@ -1821,6 +2030,24 @@ document.addEventListener("DOMContentLoaded", () => {
`; `;
} }
// --- Section Pep malade ---
let pepSection = "";
if (pep) {
pepSection = `
<div class="fc-menu-section"><strong>Pep</strong></div>
<button data-action="delete" data-event-id="${pep.id}">
Supprimer "Pep malade"
</button>
`;
} else {
pepSection = `
<div class="fc-menu-section"><strong>Pep</strong></div>
<button data-action="add-single" data-type="PEP_SICK" data-date="${date}">
Marquer Pep malade
</button>
`;
}
// --- Section congés Alex / Laia --- // --- Section congés Alex / Laia ---
const leavesOnDay = (this.leaves || []).filter( const leavesOnDay = (this.leaves || []).filter(
(lv) => lv.leave_date === date (lv) => lv.leave_date === date
@@ -1858,7 +2085,7 @@ document.addEventListener("DOMContentLoaded", () => {
// --- Assemblage du menu --- // --- Assemblage du menu ---
this.monthSelectionMenu.innerHTML = this.monthSelectionMenu.innerHTML =
congeSection + gardeSection + leavesSection; congeSection + gardeSection + pepSection + leavesSection;
this.positionAndShowMonthMenu(e); this.positionAndShowMonthMenu(e);
} }
@@ -1978,16 +2205,21 @@ document.addEventListener("DOMContentLoaded", () => {
this.clearMonthSelection(); this.clearMonthSelection();
try { try {
const response = await fetch("/api/save-events.php", { const response = await fetch(
method: "POST", "/modules/family-calendar/includes/api/save-events.php",
headers: { "Content-Type": "application/json" }, {
body: JSON.stringify(newEvents), method: "POST",
}); headers: { "Content-Type": "application/json" },
body: JSON.stringify(newEvents),
}
);
if (!response.ok) { if (!response.ok) {
throw new Error("Erreur HTTP " + response.status); 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) { if (!resEvents.ok) {
throw new Error("Erreur lors du rechargement des événements."); throw new Error("Erreur lors du rechargement des événements.");
} }
@@ -2041,16 +2273,21 @@ document.addEventListener("DOMContentLoaded", () => {
this.clearMonthSelection(); this.clearMonthSelection();
try { try {
const response = await fetch("/api/save-events.php", { const response = await fetch(
method: "POST", "/modules/family-calendar/includes/api/save-events.php",
headers: { "Content-Type": "application/json" }, {
body: JSON.stringify([newEvent]), method: "POST",
}); headers: { "Content-Type": "application/json" },
body: JSON.stringify([newEvent]),
}
);
if (!response.ok) { if (!response.ok) {
throw new Error("Erreur HTTP " + response.status); 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) { if (!resEvents.ok) {
throw new Error("Erreur lors du rechargement des événements."); throw new Error("Erreur lors du rechargement des événements.");
} }
@@ -2077,11 +2314,14 @@ document.addEventListener("DOMContentLoaded", () => {
this.clearMonthSelection(); this.clearMonthSelection();
try { try {
const response = await fetch("/api/manage-event.php", { const response = await fetch(
method: "POST", "/modules/family-calendar/includes/api/manage-event.php",
headers: { "Content-Type": "application/json" }, {
body: JSON.stringify({ action: "delete", event_id: eventId }), method: "POST",
}); headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action: "delete", event_id: eventId }),
}
);
if (!response.ok) { if (!response.ok) {
const errData = await response.json().catch(() => ({})); const errData = await response.json().catch(() => ({}));
throw new Error( throw new Error(
@@ -2089,7 +2329,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) { if (!resEvents.ok) {
throw new Error("Erreur lors du rechargement des événements."); throw new Error("Erreur lors du rechargement des événements.");
} }
@@ -2116,15 +2358,18 @@ document.addEventListener("DOMContentLoaded", () => {
this.clearMonthSelection(); this.clearMonthSelection();
try { try {
const response = await fetch("/api/manage-event.php", { const response = await fetch(
method: "POST", "/modules/family-calendar/includes/api/manage-event.php",
headers: { "Content-Type": "application/json" }, {
body: JSON.stringify({ method: "POST",
action: "update", headers: { "Content-Type": "application/json" },
event_id: eventId, body: JSON.stringify({
new_type: newType, action: "update",
}), event_id: eventId,
}); new_type: newType,
}),
}
);
if (!response.ok) { if (!response.ok) {
const errData = await response.json().catch(() => ({})); const errData = await response.json().catch(() => ({}));
throw new Error( throw new Error(
@@ -2132,7 +2377,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) { if (!resEvents.ok) {
throw new Error("Erreur lors du rechargement des événements."); throw new Error("Erreur lors du rechargement des événements.");
} }
@@ -0,0 +1,17 @@
[2026-01-06T14:39:28+01:00] RAW INPUT: []
[2026-01-06T14:39:28+01:00] RAW INPUT: [{"date":"2025-09-08","type":"PEP_SICK","duration":1}]
[2026-01-06T14:40:04+01:00] RAW INPUT: [{"date":"2025-09-30","type":"OFF_CAROLE","person":"Carole","duration":1}]
[2026-01-06T14:40:04+01:00] RAW INPUT: []
[2026-01-06T14:40:52+01:00] RAW INPUT: [{"date":"2025-09-08","type":"PEP_SICK","duration":1}]
[2026-01-06T14:40:52+01:00] RAW INPUT: []
[2026-01-06T14:46:35+01:00] RAW INPUT: [{"date":"2025-09-22","type":"PEP_SICK","duration":1}]
[2026-01-06T14:47:20+01:00] RAW INPUT: [{"date":"2025-09-29","type":"PEP_SICK","duration":1}]
[2026-01-06T14:48:43+01:00] RAW INPUT: [{"date":"2025-09-30","type":"PEP_SICK","duration":1}]
[2026-01-06T15:00:55+01:00] RAW INPUT: [{"date":"2025-09-29","type":"PEP_SICK","duration":1}]
[2026-01-06T15:04:46+01:00] RAW INPUT: [{"date":"2025-10-17","type":"PEP_SICK","duration":1}]
[2026-01-06T15:10:42+01:00] RAW INPUT: [{"date":"2025-10-21","type":"PEP_SICK","duration":1}]
[2026-01-06T15:11:00+01:00] RAW INPUT: [{"date":"2025-10-24","type":"PEP_SICK","duration":1}]
[2026-01-06T15:11:10+01:00] RAW INPUT: [{"date":"2025-12-02","type":"PEP_SICK","duration":1}]
[2026-01-06T15:11:57+01:00] RAW INPUT: [{"date":"2025-09-23","type":"PEP_SICK","duration":1}]
[2026-01-06T15:17:03+01:00] RAW INPUT: [{"date":"2025-09-10","type":"CENTRE","duration":1}]
[2026-01-06T15:26:35+01:00] RAW INPUT: [{"date":"2025-10-06","type":"PEP_SICK","duration":1},{"date":"2025-10-07","type":"PEP_SICK","duration":1},{"date":"2025-10-08","type":"PEP_SICK","duration":1}]
@@ -1,7 +1,7 @@
<?php <?php
header('Content-Type: application/json'); header('Content-Type: application/json');
require __DIR__ . '/../includes/db.php'; require __DIR__ . '/../../../../includes/db.php';
try { try {
$stmt = $pdo->query("SELECT id, event_date AS date, event_type AS type, person_id, duration FROM pf_events ORDER BY event_date"); $stmt = $pdo->query("SELECT id, event_date AS date, event_type AS type, person_id, duration FROM pf_events ORDER BY event_date");
@@ -2,7 +2,7 @@
// api/get-leaves.php // api/get-leaves.php
header('Content-Type: application/json'); header('Content-Type: application/json');
require __DIR__ . '/../includes/db.php'; require __DIR__ . '/../../../../includes/db.php';
try { try {
$stmt = $pdo->query(" $stmt = $pdo->query("
@@ -1,6 +1,6 @@
<?php <?php
header('Content-Type: application/json'); header('Content-Type: application/json');
require __DIR__ . '/../includes/db.php'; require __DIR__ . '/../../../../includes/db.php';
$input = json_decode(file_get_contents('php://input'), true); $input = json_decode(file_get_contents('php://input'), true);
@@ -1,7 +1,7 @@
<?php <?php
// api/manage-leaf.php // api/manage-leaf.php
header('Content-Type: application/json'); header('Content-Type: application/json');
require __DIR__ . '/../includes/db.php'; require __DIR__ . '/../../../../includes/db.php';
$input = json_decode(file_get_contents('php://input'), true); $input = json_decode(file_get_contents('php://input'), true);
@@ -1,8 +1,17 @@
<?php <?php
header('Content-Type: application/json'); header('Content-Type: application/json');
require __DIR__ . '/../includes/db.php'; require __DIR__ . '/../../../../includes/db.php';
$eventsToSave = json_decode(file_get_contents('php://input'), true); $eventsToSave = json_decode(file_get_contents('php://input'), true);
$rawInput = file_get_contents('php://input');
file_put_contents(
__DIR__ . '/events-debug.log',
"[" . date('c') . "] RAW INPUT: " . $rawInput . PHP_EOL,
FILE_APPEND
);
$eventsToSave = json_decode($rawInput, true);
if (empty($eventsToSave) || !is_array($eventsToSave)) { if (empty($eventsToSave) || !is_array($eventsToSave)) {
http_response_code(400); http_response_code(400);
@@ -2,7 +2,7 @@
// api/save-leaves.php // api/save-leaves.php
header('Content-Type: application/json'); header('Content-Type: application/json');
require __DIR__ . '/../includes/db.php'; require __DIR__ . '/../../../../includes/db.php';
$input = json_decode(file_get_contents('php://input'), true); $input = json_decode(file_get_contents('php://input'), true);