fix css
Deploy HouseHub / deploy (push) Successful in 2s

This commit is contained in:
FeFeClochette committed 2026-09-03 16:15:53 +02:00
1 parent a2e0904132
commit 8a7b5b6644
6 files changed
+337 -119

No files matched your search

+10 -9
View File
@@ -85,7 +85,7 @@ $pageCss = "/modules/family-calendar/family-calendar.css";
require __DIR__ . '/header.php'; require __DIR__ . '/header.php';
?> ?>
<div class="pf-container" style="max-width:100%; padding:0;"> <div class="pf-container" style="--p1-main: <?= $parents[0]['color'] ?? '#0891b2' ?>; --p2-main: <?= $parents[1]['color'] ?? '#f59e0b' ?>; max-width:100%; padding:0;">
<div class="fc-header-row" style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px; margin-bottom: 24px;"> <div class="fc-header-row" style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px; margin-bottom: 24px;">
<!-- Bloc Titre + Roue crantée --> <!-- Bloc Titre + Roue crantée -->
@@ -422,7 +422,7 @@ require __DIR__ . '/header.php';
<?php endforeach; ?> <?php endforeach; ?>
<?php foreach ($parents as $index => $parent): <?php foreach ($parents as $index => $parent):
$parentClass = ($index % 2 === 0) ? 'col-alex' : 'col-laia'; // Couleurs alternées $parentClass = ($index % 2 === 0) ? 'col-p1' : 'col-p2';
$pLeaves = $leaveMatrix[$parent['id']] ?? []; $pLeaves = $leaveMatrix[$parent['id']] ?? [];
$colspan = count($pLeaves) * 2; $colspan = count($pLeaves) * 2;
?> ?>
@@ -433,7 +433,7 @@ require __DIR__ . '/header.php';
</tr> </tr>
<tr> <tr>
<?php foreach ($parents as $index => $parent): <?php foreach ($parents as $index => $parent):
$parentClass = ($index % 2 === 0) ? 'col-alex' : 'col-laia'; $parentClass = ($index % 2 === 0) ? 'col-p1' : 'col-p2';
$pLeaves = $leaveMatrix[$parent['id']] ?? []; $pLeaves = $leaveMatrix[$parent['id']] ?? [];
?> ?>
<?php foreach ($pLeaves as $type): ?> <?php foreach ($pLeaves as $type): ?>
@@ -443,7 +443,7 @@ require __DIR__ . '/header.php';
</tr> </tr>
<tr> <tr>
<?php foreach ($parents as $index => $parent): <?php foreach ($parents as $index => $parent):
$parentClass = ($index % 2 === 0) ? 'col-alex' : 'col-laia'; $parentClass = ($index % 2 === 0) ? 'col-p1' : 'col-p2';
$pLeaves = $leaveMatrix[$parent['id']] ?? []; $pLeaves = $leaveMatrix[$parent['id']] ?? [];
?> ?>
<?php foreach ($pLeaves as $type): ?> <?php foreach ($pLeaves as $type): ?>
@@ -482,16 +482,17 @@ require __DIR__ . '/header.php';
<div class="pf-legend-item"><div class="pf-legend-color fc-legend-public-holiday"></div><span><?= tr('leg_public_holiday') ?></span></div> <div class="pf-legend-item"><div class="pf-legend-color fc-legend-public-holiday"></div><span><?= tr('leg_public_holiday') ?></span></div>
<!-- Intervenants (Dynamique selon les congés paramétrés) --> <!-- Intervenants (Dynamique selon les congés paramétrés) -->
<?php foreach ($helpers as $helper): <?php
$helperColorIndex = 0;
foreach ($helpers as $helper):
$hLeaves = $leaveMatrix[$helper['id']] ?? []; $hLeaves = $leaveMatrix[$helper['id']] ?? [];
$colors = ['var(--warning)', 'var(--danger)', 'var(--primary)']; foreach ($hLeaves as $type):
foreach ($hLeaves as $index => $type):
?> ?>
<div class="pf-legend-item"> <div class="pf-legend-item">
<div class="pf-legend-color" style="background: <?= $colors[$index % count($colors)] ?>;"></div> <div class="pf-legend-color fc-bg-palette-<?= $helperColorIndex % 8 ?>"></div>
<span><?= htmlspecialchars($type) ?> <?= htmlspecialchars($helper['name']) ?></span> <span><?= htmlspecialchars($type) ?> <?= htmlspecialchars($helper['name']) ?></span>
</div> </div>
<?php endforeach; endforeach; ?> <?php $helperColorIndex++; endforeach; endforeach; ?>
<!-- Modes de garde (On masque "Nounou" si l'intervenant gère lui-même ses présences) --> <!-- Modes de garde (On masque "Nounou" si l'intervenant gère lui-même ses présences) -->
<?php foreach ($activeCareModes as $index => $mode): <?php foreach ($activeCareModes as $index => $mode):
+7
View File
@@ -351,6 +351,13 @@ return [
'fc_full_year' => 'Any sencer', 'fc_full_year' => 'Any sencer',
'fc_custom_period' => 'Període específic...', 'fc_custom_period' => 'Període específic...',
'fc_worked_days' => 'Treballats (Est.)', 'fc_worked_days' => 'Treballats (Est.)',
'fc_presence' => 'Presència',
'fc_working_days' => 'Feiners',
'fc_absences' => 'Abs.',
'fc_recap_title' => 'Resum',
'fc_worked_est' => 'Treballats (Est.)',
'fc_invalid_dates' => 'Selecciona dates vàlides.',
'fc_no_leaves_taken' => 'Cap permís sol·licitat.',
// --- CALENDARI : PARÀMETRES (MEMBRES I VISUALITZACIÓ) --- // --- CALENDARI : PARÀMETRES (MEMBRES I VISUALITZACIÓ) ---
'fc_tab_display' => '🖥️ Visualització', 'fc_tab_display' => '🖥️ Visualització',
+7
View File
@@ -352,6 +352,13 @@ return [
'fc_full_year' => 'Full year', 'fc_full_year' => 'Full year',
'fc_custom_period' => 'Custom period...', 'fc_custom_period' => 'Custom period...',
'fc_worked_days' => 'Worked (Est.)', 'fc_worked_days' => 'Worked (Est.)',
'fc_presence' => 'Presence',
'fc_working_days' => 'Working days',
'fc_absences' => 'Abs.',
'fc_recap_title' => 'Summary',
'fc_worked_est' => 'Worked (Est.)',
'fc_invalid_dates' => 'Please select valid dates.',
'fc_no_leaves_taken' => 'No leave taken.',
// --- CALENDAR: SETTINGS (MEMBERS & DISPLAY) --- // --- CALENDAR: SETTINGS (MEMBERS & DISPLAY) ---
'fc_tab_display' => '🖥️ Display', 'fc_tab_display' => '🖥️ Display',
+7
View File
@@ -356,6 +356,13 @@ return [
'fc_full_year' => 'Année complète', 'fc_full_year' => 'Année complète',
'fc_custom_period' => 'Période spécifique...', 'fc_custom_period' => 'Période spécifique...',
'fc_worked_days' => 'Travaillés (Est.)', 'fc_worked_days' => 'Travaillés (Est.)',
'fc_presence' => 'Présence',
'fc_working_days' => 'J. Ouvrés',
'fc_absences' => 'Abs.',
'fc_recap_title' => 'Récapitulatif',
'fc_worked_est' => 'Travaillés (Est.)',
'fc_invalid_dates' => 'Sélectionnez des dates valides.',
'fc_no_leaves_taken' => 'Aucun congé posé.',
// --- CALENDRIER : PARAMÈTRES (MEMBRES & AFFICHAGE) --- // --- CALENDRIER : PARAMÈTRES (MEMBRES & AFFICHAGE) ---
'fc_tab_display' => '🖥️ Affichage', 'fc_tab_display' => '🖥️ Affichage',
+71 -32
View File
@@ -10,15 +10,26 @@
#f1f5f9 8px, #f1f5f9 8px,
#f1f5f9 16px #f1f5f9 16px
); );
--c-off-carole: #fef3c7;
--c-extra-off: #fee2e2;
--c-selected: #dbeafe;
/* Thèmes Parents (Sync Budget) */ /* Palette dynamique pour les congés intervenants */
--bg-alex: #ecfeff; --fc-palette-0: #fef3c7;
--text-alex: #0891b2; --fc-palette-text-0: #b45309;
--bg-laia: #fffbeb; --fc-palette-1: #fee2e2;
--text-laia: #d97706; --fc-palette-text-1: #b91c1c;
--fc-palette-2: #dbeafe;
--fc-palette-text-2: #1d4ed8;
--fc-palette-3: #dcfce7;
--fc-palette-text-3: #15803d;
--fc-palette-4: #f3e8ff;
--fc-palette-text-4: #7e22ce;
--fc-palette-5: #ffedd5;
--fc-palette-text-5: #c2410c;
--fc-palette-6: #ccfbf1;
--fc-palette-text-6: #0f766e;
--fc-palette-7: #fce7f3;
--fc-palette-text-7: #be185d;
--c-selected: #dbeafe;
/* Rappel des variables Global Design System pour cohérence */ /* Rappel des variables Global Design System pour cohérence */
--pf-primary: #3b82f6; --pf-primary: #3b82f6;
@@ -301,11 +312,30 @@
background: var(--c-public-holiday) !important; background: var(--c-public-holiday) !important;
font-weight: 700; font-weight: 700;
} }
.fc-day--off-carole {
background: var(--c-off-carole) !important; .fc-bg-palette-0 {
background: var(--fc-palette-0) !important;
} }
.fc-day--extra-off-carole { .fc-bg-palette-1 {
background: var(--c-extra-off) !important; background: var(--fc-palette-1) !important;
}
.fc-bg-palette-2 {
background: var(--fc-palette-2) !important;
}
.fc-bg-palette-3 {
background: var(--fc-palette-3) !important;
}
.fc-bg-palette-4 {
background: var(--fc-palette-4) !important;
}
.fc-bg-palette-5 {
background: var(--fc-palette-5) !important;
}
.fc-bg-palette-6 {
background: var(--fc-palette-6) !important;
}
.fc-bg-palette-7 {
background: var(--fc-palette-7) !important;
} }
.fc-day--selected { .fc-day--selected {
@@ -314,8 +344,14 @@
z-index: 5; z-index: 5;
} }
.fc-day--off-carole.fc-day--school-holiday, .fc-bg-palette-0.fc-day--school-holiday,
.fc-day--extra-off-carole.fc-day--school-holiday, .fc-bg-palette-1.fc-day--school-holiday,
.fc-bg-palette-2.fc-day--school-holiday,
.fc-bg-palette-3.fc-day--school-holiday,
.fc-bg-palette-4.fc-day--school-holiday,
.fc-bg-palette-5.fc-day--school-holiday,
.fc-bg-palette-6.fc-day--school-holiday,
.fc-bg-palette-7.fc-day--school-holiday,
.fc-day--centre.fc-day--school-holiday, .fc-day--centre.fc-day--school-holiday,
.fc-day--avis.fc-day--school-holiday { .fc-day--avis.fc-day--school-holiday {
box-shadow: inset 0 -8px 0 0 var(--c-school-holiday) !important; box-shadow: inset 0 -8px 0 0 var(--c-school-holiday) !important;
@@ -409,15 +445,23 @@
z-index: 20; z-index: 20;
} }
.col-alex.header-group, .col-p1.header-group,
.col-alex-sub { .col-p1-sub {
background: var(--bg-alex) !important; background: color-mix(
color: var(--text-alex) !important; in srgb,
var(--p1-main, #0891b2) 8%,
#ffffff
) !important;
color: color-mix(in srgb, var(--p1-main, #0891b2) 85%, #000000) !important;
} }
.col-laia.header-group, .col-p2.header-group,
.col-laia-sub { .col-p2-sub {
background: var(--bg-laia) !important; background: color-mix(
color: var(--text-laia) !important; in srgb,
var(--p2-main, #f59e0b) 8%,
#ffffff
) !important;
color: color-mix(in srgb, var(--p2-main, #f59e0b) 85%, #000000) !important;
} }
#planningTable tbody td { #planningTable tbody td {
@@ -448,10 +492,10 @@ td.col-day {
} }
th.col-total, th.col-total,
td.col-total, td.col-total,
th.col-alex-sub, th.col-p1-sub,
td.col-alex-sub, td.col-p1-sub,
th.col-laia-sub, th.col-p2-sub,
td.col-laia-sub { td.col-p2-sub {
width: 32px !important; width: 32px !important;
min-width: 32px !important; min-width: 32px !important;
max-width: 32px !important; max-width: 32px !important;
@@ -594,12 +638,7 @@ td.col-laia-sub {
.fc-legend-public-holiday { .fc-legend-public-holiday {
background: var(--c-public-holiday); background: var(--c-public-holiday);
} }
.fc-legend-off-carole {
background: var(--c-off-carole);
}
.fc-legend-extra-off-carole {
background: var(--c-extra-off);
}
.fc-legend-centre::after { .fc-legend-centre::after {
content: "🏫"; content: "🏫";
} }
+235 -78
View File
@@ -682,6 +682,8 @@ document.addEventListener("DOMContentLoaded", () => {
this.leaves = []; this.leaves = [];
this.weeks = []; this.weeks = [];
this.helperColorMap = {};
this.parents = []; this.parents = [];
this.kids = []; this.kids = [];
this.helpers = []; this.helpers = [];
@@ -729,6 +731,17 @@ document.addEventListener("DOMContentLoaded", () => {
this.careModes = res.data.foyer.care_modes || []; this.careModes = res.data.foyer.care_modes || [];
this.leaveMatrix = res.data.leaves || {}; this.leaveMatrix = res.data.leaves || {};
let colorCounter = 0;
this.helpers.forEach((h) => {
const hLeaves =
this.leaveMatrix[h.id] || this.leaveMatrix[Number(h.id)] || [];
hLeaves.forEach((lt) => {
const typeName = typeof lt === "object" ? lt.type : lt;
this.helperColorMap[`${h.id}_${typeName}`] = colorCounter % 8;
colorCounter++;
});
});
const settings = res.data.calendar_settings || {}; const settings = res.data.calendar_settings || {};
if (settings.calendar_default_view) { if (settings.calendar_default_view) {
this.viewMode = settings.calendar_default_view; this.viewMode = settings.calendar_default_view;
@@ -902,14 +915,11 @@ document.addEventListener("DOMContentLoaded", () => {
const helperEvents = [...this.leaves, ...this.events] const helperEvents = [...this.leaves, ...this.events]
.filter((e) => { .filter((e) => {
const eDate = e.leave_date || e.date; const eDate = e.leave_date || e.date;
const eType = e.leave_type || e.type; const eType = String(e.leave_type || e.type).toUpperCase();
const isOldOff = const isOldEvent = eType.includes("OFF") || eType.includes("EXTRA");
eType === "HELPER_OFF" ||
eType === "HELPER_EXTRA" ||
eType.includes("OFF");
if (e.person_id != h.id) return false; if (parseInt(e.person_id) !== parseInt(h.id)) return false;
if (!e.leave_date && !isOldOff) return false; if (!e.leave_date && !isOldEvent) return false; // Ignore les événements standards sauf l'historique
if (isCustom && !hasValidDates) return false; if (isCustom && !hasValidDates) return false;
return eDate >= startDateStr && eDate <= endDateStr; return eDate >= startDateStr && eDate <= endDateStr;
@@ -929,12 +939,11 @@ document.addEventListener("DOMContentLoaded", () => {
let totalLeavesTaken = 0; let totalLeavesTaken = 0;
helperEvents.forEach((e) => { helperEvents.forEach((e) => {
let mappedType = const rawType = String(e.type).toUpperCase();
e.type === "HELPER_OFF" let mappedType = e.type;
? "OFF" if (rawType.includes("EXTRA")) mappedType = "EXTRA";
: e.type === "HELPER_EXTRA" else if (rawType.includes("OFF")) mappedType = "OFF";
? "EXTRA"
: e.type;
if (totals[mappedType] === undefined) totals[mappedType] = 0; if (totals[mappedType] === undefined) totals[mappedType] = 0;
totals[mappedType] += e.duration; totals[mappedType] += e.duration;
totalLeavesTaken += e.duration; totalLeavesTaken += e.duration;
@@ -967,22 +976,22 @@ document.addEventListener("DOMContentLoaded", () => {
const isCustomDisplay = isCustom ? "flex" : "none"; const isCustomDisplay = isCustom ? "flex" : "none";
const isYearDisplay = isCustom ? "none" : "block"; const isYearDisplay = isCustom ? "none" : "block";
// --- GÉNÉRATION DU HTML (Design Ultra-Compact + Micro Pilules) --- // --- GÉNÉRATION DU HTML (Design Ultra-Compact + Micro Pilules + Flexbox Corrigé) ---
let cardHtml = ` let cardHtml = `
<div style="background: var(--bg-panel); border: 1px solid var(--border-light); border-radius: 12px; padding: 12px 20px; margin-bottom: 20px; box-shadow: var(--pf-shadow-sm); display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 15px; width: 100%;"> <div style="background: var(--bg-panel); border: 1px solid var(--border-light); border-radius: 12px; padding: 12px 20px; margin-bottom: 20px; box-shadow: var(--pf-shadow-sm); display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 15px; width: 100%;">
<!-- Zone Gauche : Identité + KPI + Pilules --> <!-- Zone Gauche : Identité + KPI + Pilules -->
<div style="display: flex; align-items: center; gap: 20px; flex-wrap: wrap;"> <div style="display: flex; align-items: center; gap: 20px; flex-wrap: wrap; flex: 1;">
<!-- Identité --> <!-- Identité -->
<div style="display: flex; flex-direction: column; gap: 2px;"> <div style="display: flex; flex-direction: column; gap: 2px;">
<span style="font-size: 0.95rem; font-weight: 700; color: var(--text-main); display: flex; align-items: center; gap: 6px;">📅 ${h.name}</span> <span style="font-size: 0.95rem; font-weight: 700; color: var(--text-main); display: flex; align-items: center; gap: 6px;">📅 ${h.name}</span>
<span style="font-size: 0.65rem; color: var(--text-muted); text-transform: uppercase; font-weight: 700; letter-spacing: 0.05em;">Récapitulatif</span> <span style="font-size: 0.65rem; color: var(--text-muted); text-transform: uppercase; font-weight: 700; letter-spacing: 0.05em;">${window.I18N?.fc_recap_title || "Récapitulatif"}</span>
</div> </div>
<!-- Jours Travaillés --> <!-- Jours Travaillés -->
<div style="display: flex; align-items: baseline; gap: 6px; padding-right: 20px; border-right: 1px solid var(--border-light);"> <div style="display: flex; align-items: baseline; gap: 6px; padding-right: 20px; border-right: 1px solid var(--border-light);">
<span style="font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; font-weight: 700;">Travaillés (Est.)</span> <span style="font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; font-weight: 700;">${window.I18N?.fc_worked_est || "Travaillés (Est.)"}</span>
<span style="font-size: 1.4rem; font-weight: 800; color: var(--primary); line-height: 1;">${hasValidDates ? workedDays : "-"} <span style="font-size: 0.85rem;">j</span></span> <span style="font-size: 1.4rem; font-weight: 800; color: var(--primary); line-height: 1;">${hasValidDates ? workedDays : "-"} <span style="font-size: 0.85rem;">j</span></span>
</div> </div>
@@ -990,7 +999,7 @@ document.addEventListener("DOMContentLoaded", () => {
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">`; <div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">`;
if (!hasValidDates && isCustom) { if (!hasValidDates && isCustom) {
cardHtml += `<div style="font-size: 0.85rem; color: var(--danger); font-weight: bold;">Sélectionnez des dates valides.</div>`; cardHtml += `<div style="font-size: 0.85rem; color: var(--danger); font-weight: bold;">${window.I18N?.fc_invalid_dates || "Sélectionnez des dates valides."}</div>`;
} else { } else {
let hasLeaves = false; let hasLeaves = false;
for (const [t, val] of Object.entries(totals)) { for (const [t, val] of Object.entries(totals)) {
@@ -1001,22 +1010,31 @@ document.addEventListener("DOMContentLoaded", () => {
) )
) { ) {
hasLeaves = true; hasLeaves = true;
let label = const rawT = String(t).toUpperCase();
t === "HELPER_OFF" ? "Off" : t === "HELPER_EXTRA" ? "Extra" : t; let label = t;
if (rawT.includes("EXTRA"))
label = window.I18N?.btn_extra || "Extra";
else if (rawT.includes("OFF"))
label = window.I18N?.btn_off || "Off";
// 🎨 INJECTION DYNAMIQUE DE LA COULEUR CSS (La même que le calendrier !)
const colorIndex = this.helperColorMap[`${h.id}_${t}`] ?? 0;
const borderColor = `var(--fc-palette-text-${colorIndex})`;
cardHtml += ` cardHtml += `
<div style="display: flex; align-items: center; gap: 8px; background: var(--bg-page); border: 1px solid var(--border-light); border-left: 3px solid var(--warning); border-radius: 6px; padding: 4px 10px;"> <div style="display: flex; align-items: center; gap: 8px; background: var(--bg-page); border: 1px solid var(--border-light); border-left: 3px solid ${borderColor}; border-radius: 6px; padding: 4px 10px;">
<span style="font-size: 0.75rem; font-weight: 700; color: var(--text-muted); text-transform: uppercase;">${label}</span> <span style="font-size: 0.75rem; font-weight: 700; color: var(--text-muted); text-transform: uppercase;">${label}</span>
<span style="font-size: 1rem; font-weight: 800; color: var(--text-main); line-height: 1;">${val}<span style="font-size: 0.8rem; margin-left: 2px;">j</span></span> <span style="font-size: 1rem; font-weight: 800; color: var(--text-main); line-height: 1;">${val}<span style="font-size: 0.8rem; margin-left: 2px;">j</span></span>
</div>`; </div>`;
} }
} }
if (!hasLeaves) if (!hasLeaves)
cardHtml += `<div style="font-size: 0.85rem; color: var(--text-muted); font-style: italic;">Aucun congé posé.</div>`; cardHtml += `<div style="font-size: 0.85rem; color: var(--text-muted); font-style: italic;">${window.I18N?.fc_no_leaves_taken || "Aucun congé posé."}</div>`;
} }
cardHtml += ` cardHtml += `
</div> </div>
</div> </div> <!-- /Zone Gauche -->
<!-- Zone Droite : Sélecteurs de Période Dynamiques --> <!-- Zone Droite : Sélecteurs de Période Dynamiques -->
<div style="display: flex; gap: 8px; align-items: center; flex-wrap: wrap;"> <div style="display: flex; gap: 8px; align-items: center; flex-wrap: wrap;">
@@ -1260,15 +1278,28 @@ document.addEventListener("DOMContentLoaded", () => {
const dur = parseFloat(e.duration) || 1; const dur = parseFloat(e.duration) || 1;
if (e.type === "CHILD_SICK") if (e.type === "CHILD_SICK") {
w.totals["sick_" + e.person_id] = w.totals["sick_" + e.person_id] =
(w.totals["sick_" + e.person_id] || 0) + dur; (w.totals["sick_" + e.person_id] || 0) + dur;
if (e.type === "HELPER_OFF") } else {
w.totals["off_" + e.person_id] = const rawType = String(e.type).toUpperCase();
(w.totals["off_" + e.person_id] || 0) + dur; const isOldHelperEvent =
if (e.type === "HELPER_EXTRA") rawType.includes("OFF") || rawType.includes("EXTRA");
w.totals["extra_" + e.person_id] =
(w.totals["extra_" + e.person_id] || 0) + dur; if (isOldHelperEvent) {
const pId = this.helpers.some(
(h) => parseInt(h.id) === parseInt(e.person_id),
)
? e.person_id
: this.helpers[0]?.id || 0;
let mappedType = e.type;
if (rawType.includes("EXTRA")) mappedType = "EXTRA";
else if (rawType.includes("OFF")) mappedType = "OFF";
w.totals[`leave_${pId}_${mappedType}`] =
(w.totals[`leave_${pId}_${mappedType}`] || 0) + dur;
}
}
// Modes de garde dynamiques // Modes de garde dynamiques
if (upperCareModes.includes(e.type)) { if (upperCareModes.includes(e.type)) {
@@ -1448,8 +1479,7 @@ document.addEventListener("DOMContentLoaded", () => {
this.planningBody.innerHTML = ""; this.planningBody.innerHTML = "";
if (!this.weeks || this.weeks.length === 0) { if (!this.weeks || this.weeks.length === 0) {
this.planningBody.innerHTML = this.planningBody.innerHTML = `<tr><td colspan='15' style='text-align:center; padding: 20px; color: var(--text-muted);'>${window.I18N?.["fc_no_data"] || "Aucune donnée pour cette année scolaire."}</td></tr>`;
"<tr><td colspan='15' style='text-align:center; padding: 20px; color: var(--text-muted);'>Aucune donnée pour cette année scolaire.</td></tr>";
return; return;
} }
@@ -1466,33 +1496,39 @@ document.addEventListener("DOMContentLoaded", () => {
const upperCareModes = (this.careModes || []).map((m) => const upperCareModes = (this.careModes || []).map((m) =>
String(m).toUpperCase(), String(m).toUpperCase(),
); );
const helperIds = (this.helpers || []).map((h) => parseInt(h.id));
this.weeks.forEach((w, idx) => { this.weeks.forEach((w, idx) => {
const tr = document.createElement("tr"); const tr = document.createElement("tr");
tr.setAttribute("data-month", w.monthKey); tr.setAttribute("data-month", w.monthKey);
if (idx === 0 || this.weeks[idx - 1].monthKey !== w.monthKey) if (idx === 0 || this.weeks[idx - 1].monthKey !== w.monthKey) {
tr.classList.add("fc-month-first-week-row"); tr.classList.add("fc-month-first-week-row");
}
if ( if (
idx === this.weeks.length - 1 || idx === this.weeks.length - 1 ||
this.weeks[idx + 1].monthKey !== w.monthKey this.weeks[idx + 1].monthKey !== w.monthKey
) ) {
tr.classList.add("fc-month-last-week-row"); tr.classList.add("fc-month-last-week-row");
if (!processedMonths[w.monthKey]) {
processedMonths[w.monthKey] = true;
const td = document.createElement("td");
td.className = "col-month col-sticky-mois";
td.innerHTML = `<span class="fc-sticky-mois-label">${w.monthName || ""}</span>`;
td.rowSpan = monthSpans[w.monthKey];
tr.appendChild(td);
} }
// 1. Colonne Sticky MOIS
if (!processedMonths[w.monthKey]) {
processedMonths[w.monthKey] = true;
const tdMonth = document.createElement("td");
tdMonth.className = "col-month col-sticky-mois rotated-text";
tdMonth.innerHTML = `<span>${w.monthName || ""}</span>`;
tdMonth.rowSpan = monthSpans[w.monthKey];
tr.appendChild(tdMonth);
}
// 2. Colonne Sticky SEMAINE
const tdW = document.createElement("td"); const tdW = document.createElement("td");
tdW.className = "col-month col-sticky-sem"; tdW.className = "col-month col-sticky-sem rotated-text";
tdW.textContent = w.weekLabel || ""; tdW.innerHTML = `<span>${w.weekLabel || ""}</span>`;
tr.appendChild(tdW); tr.appendChild(tdW);
// 3. Jours de la semaine (Lundi à Vendredi)
["mon", "tue", "wed", "thu", "fri"].forEach((d) => { ["mon", "tue", "wed", "thu", "fri"].forEach((d) => {
const td = document.createElement("td"); const td = document.createElement("td");
const dateObj = w.dayDates[d]; const dateObj = w.dayDates[d];
@@ -1503,7 +1539,11 @@ document.addEventListener("DOMContentLoaded", () => {
td.className = "col-day"; td.className = "col-day";
const events = w.dayFlags?.[d]?.events || []; const events = w.dayFlags?.[d]?.events || [];
const dayLeaves = (this.leaves || []).filter(
(l) => (l.leave_date || l.date) === iso,
);
// A. Application des classes de coloration de fond CSS
events.forEach((evt) => { events.forEach((evt) => {
if (evt.type === "PUBLIC_HOLIDAY") if (evt.type === "PUBLIC_HOLIDAY")
td.classList.add("fc-day--public-holiday"); td.classList.add("fc-day--public-holiday");
@@ -1513,23 +1553,67 @@ document.addEventListener("DOMContentLoaded", () => {
td.classList.add("fc-day--has-guard"); td.classList.add("fc-day--has-guard");
}); });
// Détection dynamique et globale des congés de l'intervenant
let appliedColorIndex = -1;
const helperLeaf = dayLeaves.find((l) =>
helperIds.includes(parseInt(l.person_id)),
);
if (helperLeaf) {
appliedColorIndex =
this.helperColorMap[
`${helperLeaf.person_id}_${helperLeaf.leave_type}`
];
} else {
const helperEvent = events.find(
(e) =>
helperIds.includes(parseInt(e.person_id)) ||
String(e.type).toUpperCase().includes("OFF") ||
String(e.type).toUpperCase().includes("EXTRA"),
);
if (helperEvent) {
const pId = helperIds.includes(parseInt(helperEvent.person_id))
? helperEvent.person_id
: helperIds[0];
const rawType = String(helperEvent.type).toUpperCase();
let mappedType = helperEvent.type;
if (rawType.includes("EXTRA")) mappedType = "EXTRA";
else if (rawType.includes("OFF")) mappedType = "OFF";
appliedColorIndex =
this.helperColorMap[`${pId}_${mappedType}`] ??
this.helperColorMap[`${pId}_${helperEvent.type}`] ??
0;
}
}
if (appliedColorIndex !== -1 && appliedColorIndex !== undefined) {
td.classList.add(`fc-bg-palette-${appliedColorIndex}`);
}
// B. Contenu HTML de la cellule (Numéro du jour, Icônes de garde, Maladie Enfants, Initiales Parents)
let content = `<div style="position:relative; height:100%; width:100%; min-height:40px;"> let content = `<div style="position:relative; height:100%; width:100%; min-height:40px;">
<span style="display:block; padding:2px;">${String(dateObj.getDate()).padStart(2, "0")}</span>`; <span style="display:block; padding:2px;">${String(dateObj.getDate()).padStart(2, "0")}</span>`;
// Icônes des modes de garde
let iconsHtml = `<div style="position:absolute; top:2px; right:2px; display:flex; gap:2px;">`; let iconsHtml = `<div style="position:absolute; top:2px; right:2px; display:flex; gap:2px;">`;
events.forEach((evt) => { events.forEach((evt) => {
if (upperCareModes.includes(evt.type)) { if (upperCareModes.includes(evt.type)) {
const modeName = String(evt.type).toLowerCase(); const modeName = String(evt.type).toLowerCase();
if (modeName === "avis") if (modeName === "avis") {
iconsHtml += `<img src="/modules/family-calendar/assets/img/avis.svg" class="fc-icon-avis" title="Avis" style="width:14px; height:14px; object-fit:contain;">`; iconsHtml += `<img src="/modules/family-calendar/assets/img/avis.svg" class="fc-icon-avis" title="Avis" style="width:14px; height:14px; object-fit:contain;">`;
else if (modeName === "centre") } else if (modeName === "centre") {
iconsHtml += `<span class="fc-icon-centre" title="Centre" style="font-size:1.1rem; line-height:1;">🏫</span>`; iconsHtml += `<span class="fc-icon-centre" title="Centre" style="font-size:1.1rem; line-height:1;">🏫</span>`;
else } else {
iconsHtml += `<span style="background:var(--primary); color:#fff; border-radius:3px; padding:0 3px; font-size:9px;">${modeName.substring(0, 3)}</span>`; iconsHtml += `<span style="background:var(--primary); color:#fff; border-radius:3px; padding:0 3px; font-size:9px;">${modeName.substring(0, 3)}</span>`;
}
} }
}); });
content += iconsHtml + `</div>`; content += iconsHtml + `</div>`;
// Icônes Enfant Malade
let sickHtml = `<div style="position:absolute; bottom:2px; right:2px; display:flex; flex-direction:column; align-items:flex-end; gap:1px; font-size:11px; font-weight:bold; line-height:1;">`; let sickHtml = `<div style="position:absolute; bottom:2px; right:2px; display:flex; flex-direction:column; align-items:flex-end; gap:1px; font-size:11px; font-weight:bold; line-height:1;">`;
events.forEach((evt) => { events.forEach((evt) => {
if (evt.type === "CHILD_SICK") { if (evt.type === "CHILD_SICK") {
@@ -1543,11 +1627,9 @@ document.addEventListener("DOMContentLoaded", () => {
}); });
content += sickHtml + `</div>`; content += sickHtml + `</div>`;
const dayLeaves = (this.leaves || []).filter( // Initiales des Parents en congé
(l) => l.leave_date === iso || l.date === iso,
);
if (dayLeaves.length) { if (dayLeaves.length) {
let html = `<div style="position:absolute; bottom:0; left:0; width:100%; font-size:9px; display:flex; justify-content:center; gap:2px; pointer-events:none;">`; let parentLeavesHtml = `<div style="position:absolute; bottom:0; left:0; width:100%; font-size:9px; display:flex; justify-content:center; gap:2px; pointer-events:none;">`;
(this.parents || []).forEach((person) => { (this.parents || []).forEach((person) => {
if ( if (
dayLeaves.some( dayLeaves.some(
@@ -1555,16 +1637,17 @@ document.addEventListener("DOMContentLoaded", () => {
parseInt(l.person_id || l.person) === parseInt(person.id), parseInt(l.person_id || l.person) === parseInt(person.id),
) )
) { ) {
html += `<span style="color:${person.color || "#000"}; font-weight:800; margin: 0 1px;">${String(person.name).charAt(0).toUpperCase()}</span>`; parentLeavesHtml += `<span style="color:${person.color || "#000"}; font-weight:800; margin: 0 1px;">${String(person.name).charAt(0).toUpperCase()}</span>`;
} }
}); });
content += html + `</div>`; content += parentLeavesHtml + `</div>`;
} }
td.innerHTML = content + `</div>`; td.innerHTML = content + `</div>`;
tr.appendChild(td); tr.appendChild(td);
}); });
// 1. Congés intervenant (Dynamiques) // 4. Totaux : Congés de l'intervenant / Helper (Nombres bruts)
(this.helpers || []).forEach((h) => { (this.helpers || []).forEach((h) => {
const hLeaves = const hLeaves =
this.leaveMatrix[h.id] || this.leaveMatrix[Number(h.id)] || []; this.leaveMatrix[h.id] || this.leaveMatrix[Number(h.id)] || [];
@@ -1572,33 +1655,42 @@ document.addEventListener("DOMContentLoaded", () => {
const typeName = typeof lt === "object" ? lt.type : lt; const typeName = typeof lt === "object" ? lt.type : lt;
const td = document.createElement("td"); const td = document.createElement("td");
td.className = "col-total"; td.className = "col-total";
td.textContent = fmt(w.totals[`leave_${h.id}_${typeName}`] || 0);
const val = w.totals[`leave_${h.id}_${typeName}`] || 0;
if (val > 0) {
td.textContent = fmt(val);
} else {
td.textContent = "";
}
tr.appendChild(td); tr.appendChild(td);
}); });
}); });
// 2. Modes de garde (On ignore Nounou si Helper présent) // 5. Totaux : Modes de garde (On masque "Nounou" si un intervenant existe)
(this.careModes || []).forEach((mode) => { (this.careModes || []).forEach((mode) => {
if (mode.toLowerCase() === "nounou" && this.helpers.length > 0) if (mode.toLowerCase() === "nounou" && this.helpers.length > 0)
return; return;
const td = document.createElement("td"); const td = document.createElement("td");
td.className = "col-total"; td.className = "col-total";
td.textContent = fmt(w.totals["mode_" + mode] || 0); const val = w.totals["mode_" + mode] || 0;
td.textContent = fmt(val);
tr.appendChild(td); tr.appendChild(td);
}); });
// 3. Maladie enfants // 6. Totaux : Maladie par enfant
(this.kids || []).forEach((kid) => { (this.kids || []).forEach((kid) => {
const td = document.createElement("td"); const td = document.createElement("td");
td.className = "col-total"; td.className = "col-total";
td.textContent = fmt(w.totals["sick_" + kid.id] || 0); const val = w.totals["sick_" + kid.id] || 0;
td.textContent = fmt(val);
tr.appendChild(td); tr.appendChild(td);
}); });
// 7. Colonnes de suivi des soldes mensuels (Parents : Av. / Use)
if (!processedLeavesCols[w.monthKey]) { if (!processedLeavesCols[w.monthKey]) {
processedLeavesCols[w.monthKey] = true; processedLeavesCols[w.monthKey] = true;
(this.parents || []).forEach((parent, index) => { (this.parents || []).forEach((parent, index) => {
const cssPrefix = index % 2 === 0 ? "col-alex" : "col-laia"; const cssPrefix = index % 2 === 0 ? "col-p1" : "col-p2";
const matrix = const matrix =
this.leaveMatrix[String(parent.id)] || this.leaveMatrix[String(parent.id)] ||
this.leaveMatrix[Number(parent.id)] || this.leaveMatrix[Number(parent.id)] ||
@@ -1672,13 +1764,49 @@ document.addEventListener("DOMContentLoaded", () => {
cls += " fc-day--school-holiday"; cls += " fc-day--school-holiday";
if (dayEvts.some((e) => e.type === "PUBLIC_HOLIDAY")) if (dayEvts.some((e) => e.type === "PUBLIC_HOLIDAY"))
cls += " fc-day--public-holiday"; cls += " fc-day--public-holiday";
if (dayEvts.some((e) => e.type === "HELPER_OFF"))
cls += " fc-day--off-carole";
if (dayEvts.some((e) => e.type === "HELPER_EXTRA"))
cls += " fc-day--extra-off-carole";
if (dayEvts.some((e) => upperCareModes.includes(e.type))) if (dayEvts.some((e) => upperCareModes.includes(e.type)))
cls += " fc-day--has-guard"; cls += " fc-day--has-guard";
const helperIds = (this.helpers || []).map((h) => parseInt(h.id));
const dayLeaves = this.leaves.filter((l) => l.leave_date === iso);
let appliedColorIndex = -1;
const helperLeaf = dayLeaves.find((l) =>
helperIds.includes(parseInt(l.person_id)),
);
if (helperLeaf) {
appliedColorIndex =
this.helperColorMap[
`${helperLeaf.person_id}_${helperLeaf.leave_type}`
];
} else {
const helperEvent = dayEvts.find(
(e) =>
helperIds.includes(parseInt(e.person_id)) ||
e.type === "HELPER_OFF" ||
e.type === "HELPER_EXTRA",
);
if (helperEvent) {
const mappedType =
helperEvent.type === "HELPER_OFF"
? "OFF"
: helperEvent.type === "HELPER_EXTRA"
? "EXTRA"
: helperEvent.type;
appliedColorIndex =
this.helperColorMap[`${helperEvent.person_id}_${mappedType}`] ??
this.helperColorMap[
`${helperEvent.person_id}_${helperEvent.type}`
] ??
0;
}
}
if (appliedColorIndex !== -1 && appliedColorIndex !== undefined) {
cls += ` fc-bg-palette-${appliedColorIndex}`;
}
let content = `<div style="position:relative; height:100%; min-height:55px;"><span class="fc-day-number">${d}</span>`; let content = `<div style="position:relative; height:100%; min-height:55px;"><span class="fc-day-number">${d}</span>`;
let iconsHtml = `<div style="position:absolute; top:2px; right:2px; display:flex; gap:2px;">`; let iconsHtml = `<div style="position:absolute; top:2px; right:2px; display:flex; gap:2px;">`;
@@ -1708,7 +1836,6 @@ document.addEventListener("DOMContentLoaded", () => {
}); });
content += sickHtml + `</div>`; content += sickHtml + `</div>`;
const dayLeaves = this.leaves.filter((l) => l.leave_date === iso);
if (dayLeaves.length) { if (dayLeaves.length) {
content += `<div style="position:absolute; bottom:2px; left:2px; font-size:10px; font-weight:bold;">`; content += `<div style="position:absolute; bottom:2px; left:2px; font-size:10px; font-weight:bold;">`;
this.parents.forEach((parent) => { this.parents.forEach((parent) => {
@@ -1748,10 +1875,11 @@ document.addEventListener("DOMContentLoaded", () => {
// 2. Boucler sur chaque enfant concerné pour un calcul individuel // 2. Boucler sur chaque enfant concerné pour un calcul individuel
nounouKids.forEach((kid) => { nounouKids.forEach((kid) => {
let workingDays = 0; let workingDays = 0;
let off = 0; let absencesNounou = 0;
let extra = 0;
let sick = 0; let sick = 0;
const helperIds = this.helpers.map((h) => parseInt(h.id));
for (let d = 1; d <= daysInMonth; d++) { for (let d = 1; d <= daysInMonth; d++) {
const dateObj = new Date(year, month, d); const dateObj = new Date(year, month, d);
const dayOfWeek = dateObj.getDay(); const dayOfWeek = dateObj.getDay();
@@ -1768,12 +1896,20 @@ document.addEventListener("DOMContentLoaded", () => {
workingDays += 1; workingDays += 1;
const dayEvents = this.events.filter((e) => e.date === iso); const dayEvents = this.events.filter((e) => e.date === iso);
const dayLeaves = this.leaves.filter((l) => l.leave_date === iso);
dayEvents.forEach((e) => { dayEvents.forEach((e) => {
const dur = parseFloat(e.duration) || 1; const dur = parseFloat(e.duration) || 1;
const rawType = String(e.type).toUpperCase();
if (e.type === "HELPER_OFF") off += dur; // Si c'est un vieil événement historisé OFF/EXTRA
if (e.type === "HELPER_EXTRA") extra += dur; if (
rawType.includes("OFF") ||
rawType.includes("EXTRA") ||
helperIds.includes(parseInt(e.person_id))
) {
absencesNounou += dur;
}
// On vérifie que la maladie concerne BIEN cet enfant précis ! // On vérifie que la maladie concerne BIEN cet enfant précis !
if ( if (
@@ -1783,23 +1919,43 @@ document.addEventListener("DOMContentLoaded", () => {
sick += dur; sick += dur;
} }
}); });
// Prise en compte des nouveaux congés propres aux intervenants
dayLeaves.forEach((l) => {
const dur = parseFloat(l.duration) || 1;
if (helperIds.includes(parseInt(l.person_id))) {
absencesNounou += dur;
}
});
} }
// Calcul final strict : Jours ouvrés - Maladie (de l'enfant) - Extra - Off // Récupération dynamique du nom de l'intervenant avec i18n
const presence = Math.max(0, workingDays - sick - extra - off); const fallbackHelper = window.I18N?.fc_role_helper || "Intervenant";
const helperName =
this.helpers && this.helpers.length > 0
? this.helpers.map((h) => h.name).join(" & ")
: fallbackHelper;
// Calcul final strict : Jours ouvrés - Maladie (de l'enfant) - Absences Nounou
const presence = Math.max(0, workingDays - sick - absencesNounou);
// Textes traduits
const trPresence = window.I18N?.fc_presence || "Présence";
const trWorking = window.I18N?.fc_working_days || "Ouvrés";
const trAbs = window.I18N?.fc_absences || "Abs.";
const trSick = window.I18N?.fc_menu_sick || "Maladie";
// Rendu HTML individuel // Rendu HTML individuel
html += ` html += `
<div style="margin-top: 10px;"> <div style="margin-top: 10px;">
<div style="font-size: 0.8rem; font-weight: bold; color: var(--text-muted); margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.05em;"> <div style="font-size: 0.8rem; font-weight: bold; color: var(--text-muted); margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.05em;">
👧👦 Présence ${kid.name} (Nounou) 👧👦 ${trPresence} ${kid.name} (${helperName})
</div> </div>
<div class="fc-month-summary-inline" style="display:flex; justify-content:space-around; gap:10px; font-size:0.75rem; background: var(--bg-panel); padding: 8px; border-radius: 8px; border: 1px solid var(--border-light);"> <div class="fc-month-summary-inline" style="display:flex; justify-content:space-around; gap:10px; font-size:0.75rem; background: var(--bg-panel); padding: 8px; border-radius: 8px; border: 1px solid var(--border-light);">
<div class="fc-summ-pill" style="display:flex; flex-direction:column; align-items:center; flex:1;"><span>Ouvrés</span> <strong style="color:var(--text-main); font-size:0.95rem;">${workingDays} j</strong></div> <div class="fc-summ-pill" style="display:flex; flex-direction:column; align-items:center; flex:1;"><span>${trWorking}</span> <strong style="color:var(--text-main); font-size:0.95rem;">${workingDays} j</strong></div>
<div class="fc-summ-pill" style="display:flex; flex-direction:column; align-items:center; flex:1;"><span>Off</span> <strong style="color:var(--text-main); font-size:0.95rem;">${parseFloat(off.toFixed(1))} j</strong></div> <div class="fc-summ-pill" style="display:flex; flex-direction:column; align-items:center; flex:1;"><span>${trAbs} ${helperName}</span> <strong style="color:var(--text-main); font-size:0.95rem;">${parseFloat(absencesNounou.toFixed(1))} j</strong></div>
<div class="fc-summ-pill" style="display:flex; flex-direction:column; align-items:center; flex:1;"><span>Extra</span> <strong style="color:var(--text-main); font-size:0.95rem;">${parseFloat(extra.toFixed(1))} j</strong></div> <div class="fc-summ-pill" style="display:flex; flex-direction:column; align-items:center; flex:1;"><span>${trSick}</span> <strong style="color:var(--danger); font-size:0.95rem;">${parseFloat(sick.toFixed(1))} j</strong></div>
<div class="fc-summ-pill" style="display:flex; flex-direction:column; align-items:center; flex:1;"><span>Maladie</span> <strong style="color:var(--danger); font-size:0.95rem;">${parseFloat(sick.toFixed(1))} j</strong></div> <div class="fc-summ-pill pf-presence-pill" style="display:flex; flex-direction:column; align-items:center; flex:1;"><span>${trPresence}</span> <strong style="color:var(--primary); font-size:0.95rem;">${parseFloat(presence.toFixed(1))} j</strong></div>
<div class="fc-summ-pill" style="display:flex; flex-direction:column; align-items:center; flex:1;"><span>Présence</span> <strong style="color:var(--primary); font-size:0.95rem;">${parseFloat(presence.toFixed(1))} j</strong></div>
</div> </div>
</div> </div>
`; `;
@@ -2355,10 +2511,11 @@ document.addEventListener("DOMContentLoaded", () => {
const hLeaves = const hLeaves =
this.leaveMatrix[h.id] || this.leaveMatrix[Number(h.id)] || []; this.leaveMatrix[h.id] || this.leaveMatrix[Number(h.id)] || [];
if (hLeaves.length > 0) { if (hLeaves.length > 0) {
const hColor = h.color || "var(--warning)";
html += `<div class="fc-menu-section">${buildHeader(h.name, "clear-leaves-person", null, h.id)}<div class="fc-menu-grid">`; html += `<div class="fc-menu-section">${buildHeader(h.name, "clear-leaves-person", null, h.id)}<div class="fc-menu-grid">`;
hLeaves.forEach((lt) => { hLeaves.forEach((lt) => {
const lType = typeof lt === "object" ? lt.type : lt; const lType = typeof lt === "object" ? lt.type : lt;
const pIdx = this.helperColorMap[`${h.id}_${lType}`] ?? 0;
const hColor = `var(--fc-palette-text-${pIdx})`;
html += `<button class="fc-menu-btn" style="${getActiveStyleL(lType, h.id, hColor)}" data-action="add-leave" data-pid="${h.id}" data-type="${lType}">${lType}</button>`; html += `<button class="fc-menu-btn" style="${getActiveStyleL(lType, h.id, hColor)}" data-action="add-leave" data-pid="${h.id}" data-type="${lType}">${lType}</button>`;
}); });
html += `</div></div>`; html += `</div></div>`;