diff --git a/family-calendar.php b/family-calendar.php
index 831bf24..f978d07 100644
--- a/family-calendar.php
+++ b/family-calendar.php
@@ -202,20 +202,19 @@ require __DIR__ . '/header.php';
- | Av. |
- Use |
- Av. |
- Use |
- Av. |
- Use |
-
-
- Av. |
- Use |
- Av. |
- Use |
- Av. |
- Use |
+ Av. |
+ Use |
+ Av. |
+ Use |
+ Av. |
+ Use |
+
+ Av. |
+ Use |
+ Av. |
+ Use |
+ Av. |
+ Use |
diff --git a/modules/family-calendar/family-calendar.css b/modules/family-calendar/family-calendar.css
index d6cf2d9..c54ddc7 100644
--- a/modules/family-calendar/family-calendar.css
+++ b/modules/family-calendar/family-calendar.css
@@ -38,6 +38,22 @@
text-overflow: ellipsis;
}
+/* Colonnes "Av." non modifiables : fond gris clair + texte un peu atténué */
+#planningHeaderTable th.col-alex-av,
+#planningTable td.col-alex-av,
+#planningHeaderTable th.col-laia-av,
+#planningTable td.col-laia-av {
+ background-color: #e5e7eb;
+ color: #4b5563;
+ font-weight: 600;
+}
+
+/* Optionnel : curseur "not-allowed" pour marquer le côté non éditable */
+#planningTable td.col-alex-av,
+#planningTable td.col-laia-av {
+ cursor: not-allowed;
+}
+
/* Mois / Semaine */
#planningHeaderTable .col-month,
#planningTable .col-month {
@@ -175,48 +191,125 @@
cursor: pointer;
}
-/* Le menu contextuel flottant */
+/* Conteneur du menu de sélection (hebdo & mensuel) */
.fc-selection-menu {
display: none;
position: absolute;
z-index: 100;
- background-color: white;
- border: 1px solid #ccc;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
- border-radius: 6px;
- padding: 8px;
+ background-color: #ffffff;
+ border: 1px solid #d1d5db; /* gris clair */
+ box-shadow: 0 4px 12px rgba(15, 23, 42, 0.18);
+ border-radius: 8px;
+ padding: 8px 10px;
font-size: 12px;
+ min-width: 220px;
+ max-width: 360px;
}
-.fc-selection-menu .fc-menu-section {
+/* Sections internes avec titre */
+.fc-menu-section {
margin-bottom: 8px;
}
-.fc-selection-menu .fc-menu-section:last-child {
- margin-bottom: 0;
-}
-
-.fc-selection-menu strong {
+.fc-menu-section > strong {
display: block;
margin-bottom: 4px;
font-weight: 600;
+ color: #111827; /* gris très foncé */
}
-.fc-selection-menu button {
+/* Grille pour disposer proprement les boutons dans une section */
+.fc-menu-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 4px;
+}
+
+/* Boutons génériques du menu */
+.fc-selection-menu button.fc-menu-btn,
+.fc-selection-menu button.fc-menu-leave-btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ border-radius: 4px;
+ border: 1px solid #e5e7eb;
+ padding: 4px 6px;
+ font-size: 12px;
+ background: #f9fafb;
+ color: #111827;
+ cursor: pointer;
+ transition: background-color 0.15s ease, border-color 0.15s ease,
+ box-shadow 0.15s ease;
+}
+
+.fc-selection-menu button.fc-menu-btn:hover,
+.fc-selection-menu button.fc-menu-leave-btn:hover {
+ background-color: #e5f0ff; /* léger bleu */
+ border-color: #93c5fd;
+ box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.3);
+}
+
+/* Variantes par type pour un léger hint visuel
+.fc-menu-btn--conge {
+ congés Carole
+}
+
+.fc-menu-btn--garde {
+ mode de garde
+}
+
+.fc-menu-btn--pep {
+ Pep malade
+} */
+
+/* Boutons "danger" (suppression) */
+.fc-selection-menu button.fc-menu-danger {
display: block;
width: 100%;
- background: none;
- border: 1px solid #ddd;
+ margin-top: 6px;
+ padding: 4px 6px;
border-radius: 4px;
- padding: 4px 8px;
- margin-top: 4px;
- text-align: left;
+ border: 1px solid #fecaca;
+ background-color: #fef2f2;
+ color: #b91c1c;
+ font-size: 12px;
cursor: pointer;
+ text-align: left;
}
-.fc-selection-menu button:hover {
- background-color: #f0f4f8;
- border-color: #b0c4de;
+.fc-selection-menu button.fc-menu-danger:hover {
+ background-color: #fee2e2;
+ border-color: #fca5a5;
+}
+
+/* Tableau des congés Alex / Laia dans le menu */
+.fc-menu-leaves-table {
+ margin-top: 4px;
+}
+
+.fc-menu-leaves-table table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 11px;
+}
+
+.fc-menu-leaves-table th,
+.fc-menu-leaves-table td {
+ padding: 2px;
+ text-align: center;
+}
+
+.fc-menu-leaves-table thead th {
+ font-weight: 600;
+ color: #374151;
+ border-bottom: 1px solid #e5e7eb;
+}
+
+.fc-menu-leaves-table tbody td:first-child {
+ text-align: left;
+ font-weight: 500;
+ color: #4b5563;
}
/* --- Couleurs de base pour la légende et les jours --- */
@@ -422,6 +515,7 @@
.fc-calendar-and-summary {
display: block;
+ position: relative;
}
.fc-month-header {
@@ -678,6 +772,15 @@
line-height: 1;
}
+/* Bordure épaisse pour délimiter les mois dans le planning hebdo */
+#planningTable tr.fc-month-first-week-row td {
+ border-top: 2px solid #243b53; /* couleur sombre cohérente avec ta charte */
+}
+
+#planningTable tr.fc-month-last-week-row td {
+ border-bottom: 2px solid #243b53;
+}
+
/* === TABLEAU RÉCAPITULATIF === */
.fc-month-summary {
diff --git a/modules/family-calendar/family-calendar.js b/modules/family-calendar/family-calendar.js
index 2a84756..b5d7994 100644
--- a/modules/family-calendar/family-calendar.js
+++ b/modules/family-calendar/family-calendar.js
@@ -208,6 +208,7 @@ document.addEventListener("DOMContentLoaded", () => {
}
reprocessEvents() {
+ // Réinitialisation des semaines
this.weeks.forEach((w) => {
w.totals = {
offCarole: 0,
@@ -216,25 +217,35 @@ document.addEventListener("DOMContentLoaded", () => {
avis: 0,
pepSick: 0,
presencePep: 0,
+ // Nouveaux totaux pour Alex / Laia
+ alexCP: 0,
+ alexJRA: 0,
+ alexJA: 0,
+ laiaCP: 0,
+ laiaJRA: 0,
+ laiaJA: 0,
};
- Object.values(w.dayFlags).forEach((df) => (df.eventsOnDay = []));
+ Object.values(w.dayFlags).forEach((df) => {
+ df.eventsOnDay = [];
+ });
});
-
+ // ================== Événements Carole / garde / Pep ==================
this.events.forEach((evt) => {
const evtDate = new Date(evt.date + "T00:00:00");
+ // Trouver la semaine correspondant à la date de l'événement
const week = this.weeks.find(
(w) => evtDate >= w.dayDates.mon && evtDate <= w.dayDates.fri
);
if (!week) return;
-
+ // Identifier le jour (mon, tue, wed, thu, fri)
const dayKey = Object.keys(week.dayDates).find(
- (k) => week.dayDates[k].toDateString() === evtDate.toDateString()
+ (key) => week.dayDates[key].toDateString() === evtDate.toDateString()
);
if (dayKey) {
week.dayFlags[dayKey].eventsOnDay.push(evt);
}
-
const dur = parseFloat(evt.duration) || 1;
+ // Mettre à jour les totaux hebdo selon le type
switch (evt.type) {
case "OFF_CAROLE":
week.totals.offCarole += dur;
@@ -251,6 +262,29 @@ document.addEventListener("DOMContentLoaded", () => {
case "PEP_SICK":
week.totals.pepSick += dur;
break;
+ default:
+ break;
+ }
+ });
+ // ================== Congés Alex / Laia (CP / JRA / JA) ==================
+ (this.leaves || []).forEach((lv) => {
+ const lvDate = new Date(lv.leave_date + "T00:00:00");
+ const week = this.weeks.find(
+ (w) => lvDate >= w.dayDates.mon && lvDate <= w.dayDates.fri
+ );
+ if (!week) return;
+ const isAlex = lv.person_id === 2;
+ const isLaia = lv.person_id === 3;
+ const type = lv.leave_type; // "CP", "JRA" ou "JA"
+ const dur = parseFloat(lv.duration) || 1;
+ if (isAlex) {
+ if (type === "CP") week.totals.alexCP += dur;
+ if (type === "JRA") week.totals.alexJRA += dur;
+ if (type === "JA") week.totals.alexJA += dur;
+ } else if (isLaia) {
+ if (type === "CP") week.totals.laiaCP += dur;
+ if (type === "JRA") week.totals.laiaJRA += dur;
+ if (type === "JA") week.totals.laiaJA += dur;
}
});
@@ -366,10 +400,21 @@ document.addEventListener("DOMContentLoaded", () => {
const formatTotal = (total) =>
total > 0 ? (Number.isInteger(total) ? total : total.toFixed(1)) : "";
- this.weeks.forEach((week) => {
+ this.weeks.forEach((week, index) => {
const tr = document.createElement("tr");
-
- // Mois
+ // Déterminer si c'est la première ou la dernière semaine du mois
+ const isFirstWeekOfMonth =
+ index === 0 || this.weeks[index - 1].monthKey !== week.monthKey;
+ const isLastWeekOfMonth =
+ index === this.weeks.length - 1 ||
+ this.weeks[index + 1].monthKey !== week.monthKey;
+ if (isFirstWeekOfMonth) {
+ tr.classList.add("fc-month-first-week-row");
+ }
+ if (isLastWeekOfMonth) {
+ tr.classList.add("fc-month-last-week-row");
+ }
+ // Colonne Mois (avec rowSpan)
if (!monthRowRendered[week.monthKey]) {
monthRowRendered[week.monthKey] = true;
const tdMonth = document.createElement("td");
@@ -503,19 +548,63 @@ document.addEventListener("DOMContentLoaded", () => {
tdPresencePep.classList.add("col-total");
tr.appendChild(tdPresencePep);
- // 6 colonnes ALEX
+ // 6 colonnes ALEX : [CP Av., CP Use, JRA Av., JRA Use, JA Av., JA Use]
for (let i = 0; i < 6; i++) {
const td = document.createElement("td");
- td.textContent = "";
td.classList.add("col-alex-sub");
+
+ if (i % 2 === 0) {
+ // colonnes Av. (pour l'instant vides, on remplira plus tard si besoin)
+ td.classList.add("col-alex-av");
+ td.textContent = ""; // ex: futur "solde" CP/JRA/JA
+ } else {
+ // colonnes Use : remplir selon le couple (index, type)
+ td.classList.add("col-alex-use");
+
+ let value = "";
+ if (i === 1) {
+ // CP Use
+ value = formatTotal(week.totals.alexCP);
+ } else if (i === 3) {
+ // JRA Use
+ value = formatTotal(week.totals.alexJRA);
+ } else if (i === 5) {
+ // JA Use
+ value = formatTotal(week.totals.alexJA);
+ }
+
+ td.textContent = value;
+ }
+
tr.appendChild(td);
}
- // 6 colonnes LAIA
+ // 6 colonnes LAIA : [CP Av., CP Use, JRA Av., JRA Use, JA Av., JA Use]
for (let i = 0; i < 6; i++) {
const td = document.createElement("td");
- td.textContent = "";
td.classList.add("col-laia-sub");
+
+ if (i % 2 === 0) {
+ td.classList.add("col-laia-av");
+ td.textContent = ""; // futur solde
+ } else {
+ td.classList.add("col-laia-use");
+
+ let value = "";
+ if (i === 1) {
+ // CP Use
+ value = formatTotal(week.totals.laiaCP);
+ } else if (i === 3) {
+ // JRA Use
+ value = formatTotal(week.totals.laiaJRA);
+ } else if (i === 5) {
+ // JA Use
+ value = formatTotal(week.totals.laiaJA);
+ }
+
+ td.textContent = value;
+ }
+
tr.appendChild(td);
}
@@ -624,8 +713,8 @@ document.addEventListener("DOMContentLoaded", () => {
if (this.selectedCells.length === 0) return;
+ // ===== Cas 1 seule cellule =====
if (this.selectedCells.length === 1) {
- // Cas 1 jour : logique habituelle
const date = this.selectedCells[0].dataset.date;
const eventsOnDay = this.events.filter(
(evt) => evt.date === date && MODIFIABLE_TYPES.includes(evt.type)
@@ -634,12 +723,7 @@ document.addEventListener("DOMContentLoaded", () => {
const garde = eventsOnDay.find((ev) => GUARDE_TYPES.includes(ev.type));
const pep = eventsOnDay.find((ev) => PEP_TYPES.includes(ev.type));
- if (!conge && !garde && !pep) {
- this.showAddMenu(e);
- } else {
- this.showEditMenuForDay(e, { conge, garde, pep, date });
- }
-
+ this.showEditMenuForDay(e, { conge, garde, pep, date });
return;
}
@@ -666,83 +750,200 @@ document.addEventListener("DOMContentLoaded", () => {
const allDatesHaveGarde =
gardes.length === selectedDates.length && uniqueGardeTypes.size === 1;
- console.log("Multi-jours:");
- console.log("selectedDates:", selectedDates);
- console.log("eventsOnDates:", eventsOnDates);
- console.log("conges:", conges);
- console.log("gardes:", gardes);
- console.log("uniqueCongeTypes:", Array.from(uniqueCongeTypes));
- console.log("uniqueGardeTypes:", Array.from(uniqueGardeTypes));
- console.log("allDatesHaveConge:", allDatesHaveConge);
- console.log("allDatesHaveGarde:", allDatesHaveGarde);
+ // On construit quand même un bulkInfo partiel (même si pas homogène)
+ const bulkInfo = {
+ selectedDates,
+ conges,
+ gardes,
+ congeType: allDatesHaveConge ? conges[0].type : null,
+ gardeType: allDatesHaveGarde ? gardes[0].type : null,
+ };
- if (allDatesHaveConge || allDatesHaveGarde) {
- const bulkInfo = {
- selectedDates,
- conges,
- gardes,
- congeType: allDatesHaveConge ? conges[0].type : null,
- gardeType: allDatesHaveGarde ? gardes[0].type : null,
- };
- this.showBulkMenu(e, bulkInfo);
- } else {
- this.showAddMenu(e);
- }
+ // On affiche toujours le menu bulk pour permettre Alex/Laia,
+ // et éventuellement les actions Carole/garde si congeType/gardeType sont définis.
+ this.showBulkMenu(e, bulkInfo);
}
showBulkMenu(e, bulkInfo) {
- const { selectedDates, conges, gardes, congeType, gardeType } = bulkInfo;
+ const { selectedDates } = bulkInfo;
const nbDays = selectedDates.length;
- let html =
- '';
+ let html = `
+
+ `;
- if (congeType) {
- const oppositeConge =
- congeType === "OFF_CAROLE" ? "EXTRA_OFF_CAROLE" : "OFF_CAROLE";
- html += `
-
+ // Section Congés Carole
+ html += `
+
+`;
- if (gardeType) {
- const oppositeGarde = gardeType === "CENTRE" ? "AVIS" : "CENTRE";
- html += `
-
+ // Section Mode de garde – toujours visible
+ html += `
+
+`;
+
+ // Section bulk congés Alex / Laia – tableau identique au single
+ html += `
+
+`;
+
+ // Après le tableau CP/JRA/JA
+ html += `
+
+
+`;
this.selectionMenu.innerHTML = html;
- // On stocke bulkInfo pour réutilisation dans handleMenuClick
this._currentBulkInfo = bulkInfo;
this.positionAndShowMenu(e);
}
@@ -778,15 +979,39 @@ document.addEventListener("DOMContentLoaded", () => {
showAddMenu(e) {
this.selectionMenu.innerHTML = `
-
-
-
-
-
-
-
-
- `;
+
+
+
+
+
+ `;
this.positionAndShowMenu(e);
}
@@ -806,25 +1031,52 @@ document.addEventListener("DOMContentLoaded", () => {
const oppositeConge =
conge.type === "OFF_CAROLE" ? "EXTRA_OFF_CAROLE" : "OFF_CAROLE";
congeSection = `
-
-
-
+
`;
} else {
congeSection = `
-
-
-
+
`;
}
@@ -833,23 +1085,50 @@ document.addEventListener("DOMContentLoaded", () => {
if (garde) {
const oppositeGarde = garde.type === "CENTRE" ? "AVIS" : "CENTRE";
gardeSection = `
-
-
-
+
`;
} else {
gardeSection = `
-
-
-
+
`;
}
@@ -857,17 +1136,30 @@ document.addEventListener("DOMContentLoaded", () => {
let pepSection = "";
if (pep) {
pepSection = `
-
-
+
`;
} else {
pepSection = `
-
-
+
`;
}
@@ -883,30 +1175,114 @@ document.addEventListener("DOMContentLoaded", () => {
console.log("[EDIT MENU] leavesOnDay =", leavesOnDay);
let leavesSection = `
-
-
-
-
-
-
-
+
`;
+ // Bouton de suppression si congés existants ce jour-là
if (leavesOnDay.length > 0) {
leavesSection += `
-