diff --git a/family-calendar.php b/family-calendar.php index b448fc1..39aeca3 100644 --- a/family-calendar.php +++ b/family-calendar.php @@ -121,9 +121,10 @@ require __DIR__ . '/header.php';
+
- +
diff --git a/includes/lang/ca.php b/includes/lang/ca.php index 7451beb..caa39b0 100644 --- a/includes/lang/ca.php +++ b/includes/lang/ca.php @@ -134,8 +134,10 @@ return [ 'fc_err_fetch_gov' => 'Error en la connexió amb l’API governamental.', 'fc_err_no_data_gov' => 'No s’han trobat dades a l’API del govern per a aquest any.', 'fc_err_action' => 'Error en l’acció: ', + 'fc_today_short' => 'Avui', + 'fc_today_title' => 'Anar a avui', + 'leg_presence' => 'Presència Pep', - 'leg_school_holidays' => 'Vacances Escolars', 'leg_public_holiday' => 'Festiu', 'leg_off_carole' => 'Off Carole', diff --git a/includes/lang/fr.php b/includes/lang/fr.php index 5b093c1..bb19709 100644 --- a/includes/lang/fr.php +++ b/includes/lang/fr.php @@ -137,8 +137,10 @@ return [ 'fc_err_fetch_gov' => 'Erreur lors de la connexion à l’API gouvernementale.', 'fc_err_no_data_gov' => 'Aucune donnée trouvée sur l’API du gouvernement pour cette année.', 'fc_err_action' => 'Erreur lors de l’action : ', + 'fc_today_short' => 'Auj.', + 'fc_today_title' => "Aujourd'hui", + 'leg_presence' => 'Présence Pep', - 'leg_school_holidays' => 'Vacances Scolaires', 'leg_public_holiday' => 'Férié', 'leg_off_carole' => 'Off Carole', diff --git a/modules/family-calendar/family-calendar.css b/modules/family-calendar/family-calendar.css index e09d7a4..c159ae8 100644 --- a/modules/family-calendar/family-calendar.css +++ b/modules/family-calendar/family-calendar.css @@ -740,6 +740,15 @@ td.col-laia-sub { gap: 4px; } +/* --- BOUTON DE MENU ACTIF (Élément déjà enregistré) --- */ +.fc-menu-btn--active { + background: #eff6ff; + border-color: var(--pf-primary); + color: var(--pf-primary); + /* Un léger inset shadow pour rendre la bordure un peu plus épaisse sans casser la taille */ + box-shadow: inset 0 0 0 1px var(--pf-primary); +} + /* Table specifique modale vacances */ .fc-holidays-table { width: 100%; @@ -827,6 +836,55 @@ td.col-laia-sub { box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15); } +/* --- BOUTON AUJOURD'HUI & CONTROLES --- */ +.fc-view-controls { + align-items: center; /* Pour bien centrer le nouveau bouton */ +} + +.fc-today-button { + background: white; + border: 1px solid var(--pf-border); + border-radius: 8px; + padding: 6px 12px; + font-size: 0.85rem; + font-weight: 700; + color: var(--pf-primary); + cursor: pointer; + transition: all 0.2s; + box-shadow: var(--pf-shadow-sm); +} + +.fc-today-button:hover { + background: #eff6ff; + border-color: var(--pf-primary); +} + +.fc-view-divider { + width: 1px; + height: 20px; + background: #cbd5e1; + margin: 0 8px; +} + +/* --- MISE EN ÉVIDENCE DU JOUR EN COURS --- */ +.fc-day--today { + background: #eff6ff !important; + box-shadow: inset 0 0 0 1px #bfdbfe !important; +} + +.fc-day--today .fc-day-number { + display: inline; + background: none; + width: auto; + height: auto; + box-shadow: none; + border-radius: 0; + margin: 0; + color: var(--pf-primary); + font-weight: 900; + font-size: 1.1em; +} + @media (max-width: 768px) { .fc-smart-select { font-size: 1rem; @@ -890,4 +948,10 @@ td.col-laia-sub { border-radius: 10px; margin: 0 auto 15px auto; } + .fc-two-months-container, + .fc-three-months-container { + display: flex !important; + flex-direction: column !important; + gap: 20px !important; + } } diff --git a/modules/family-calendar/family-calendar.js b/modules/family-calendar/family-calendar.js index a28e879..3f8db42 100644 --- a/modules/family-calendar/family-calendar.js +++ b/modules/family-calendar/family-calendar.js @@ -940,7 +940,10 @@ document.addEventListener("DOMContentLoaded", () => { } const iso = this.getLocalIsoDate(dateObj); + const todayIso = this.getLocalIsoDate(new Date()); // Date du jour + let cls = "fc-month-day"; + if (iso === todayIso) cls += " fc-day--today"; // On applique la classe ! const dayEvts = this.events.filter((e) => e.date === iso); if (dayEvts.some((e) => e.type === "VACANCES_SCOLAIRES")) @@ -958,7 +961,7 @@ document.addEventListener("DOMContentLoaded", () => { if (dayEvts.some((e) => e.type === "AVIS")) cls += " fc-day--avis"; } - let content = `
${dayCounter}`; + let content = `
${dayCounter}`; if (dayEvts.some((e) => e.type === "PEP_SICK")) content += `🤒`; const dayLeaves = this.leaves.filter((l) => l.leave_date === iso); @@ -1357,6 +1360,66 @@ document.addEventListener("DOMContentLoaded", () => { this.currentMonth.setMonth(this.currentMonth.getMonth() + 1); this.renderMonthCalendar(); }); + // --- BOUTON AUJOURD'HUI --- + document.getElementById("fc-today-btn")?.addEventListener("click", () => { + this.currentMonth = new Date(); + this.currentMonth.setDate(1); // Force au 1er du mois pour éviter les bugs de fins de mois + this.renderMonthCalendar(); + }); + + // --- GESTION DU SWIPE MENSUEL (MOBILE) --- + if (this.monthCalendar) { + let touchStartX = 0; + let touchStartY = 0; + + // On écoute le début du toucher + this.monthCalendar.addEventListener( + "touchstart", + (e) => { + touchStartX = e.changedTouches[0].screenX; + touchStartY = e.changedTouches[0].screenY; + }, + { passive: true }, + ); + + // On écoute la fin du toucher + this.monthCalendar.addEventListener( + "touchend", + (e) => { + const touchEndX = e.changedTouches[0].screenX; + const touchEndY = e.changedTouches[0].screenY; + + const deltaX = touchEndX - touchStartX; + const deltaY = touchEndY - touchStartY; + + // Détection d'un swipe horizontal (on exclut les swipes verticaux de défilement) + // Seuil de 50px pour éviter les déclenchements par erreur + if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 50) { + const numMonths = + this.viewMode === "2months" + ? 2 + : this.viewMode === "3months" + ? 3 + : 1; + + if (deltaX < 0) { + // Swipe vers la GAUCHE = Aller dans le FUTUR (Mois suivants) + this.currentMonth.setMonth( + this.currentMonth.getMonth() + numMonths, + ); + } else { + // Swipe vers la DROITE = Revenir dans le PASSÉ (Mois précédents) + this.currentMonth.setMonth( + this.currentMonth.getMonth() - numMonths, + ); + } + + this.renderMonthCalendar(); // Rafraîchit l'UI + } + }, + { passive: true }, + ); + } document .getElementById("fc-prev-school-year") ?.addEventListener("click", () => this.changeSchoolYear(-1)); @@ -1466,16 +1529,43 @@ document.addEventListener("DOMContentLoaded", () => { this._currentBulkInfo = { dates }; + // --- 🔍 DÉTECTION DES ÉVÉNEMENTS & CONGÉS EXISTANTS --- + const activeEvents = new Set(); + this.events.forEach((e) => { + if (dates.includes(e.date)) activeEvents.add(e.type); + }); + + const activeLeaves = { 2: new Set(), 3: new Set() }; + this.leaves.forEach((l) => { + if (dates.includes(l.leave_date)) { + if (activeLeaves[l.person_id]) + activeLeaves[l.person_id].add(l.leave_type); + } + }); + + // Petites fonctions utilitaires pour gérer l'UI du bouton + const getBtnClass = (type, personId = null) => { + let isActive = personId + ? activeLeaves[personId] && activeLeaves[personId].has(type) + : activeEvents.has(type); + return isActive ? "fc-menu-btn--active" : ""; + }; + const getBtnIcon = (type, personId = null) => { + let isActive = personId + ? activeLeaves[personId] && activeLeaves[personId].has(type) + : activeEvents.has(type); + return isActive ? "✓ " : ""; + }; + // Gestion de la date affichée (Singulier/Pluriel) const dateLabel = dates.length > 1 ? `${dates.length} ${tr("fc_unit_days")}` : new Date(dates[0]).toLocaleDateString(window.I18N_LANG || "fr-FR"); - // Icône SVG Poubelle (Uniformisée) + // Icône SVG Poubelle const trashSvg = ``; - // Fonction utilitaire interne pour les en-têtes de section avec suppression const buildHeader = (title, action, cat) => `
${title} @@ -1483,36 +1573,35 @@ document.addEventListener("DOMContentLoaded", () => {
`; - // 1. En-tête du menu (Date ou nombre de jours) let html = `
${dateLabel}
`; - // 2. Section Carole (Congés) + // 2. Section Carole html += `
${buildHeader(tr("fc_menu_carole"), "clear-type", "CONGE")}
- - + +
`; - // 3. Section Garde (Centre / Avis) + // 3. Section Garde html += `
${buildHeader(tr("leg_centre"), "clear-type", "GARDE")}
- - + +
`; - // 4. Section Pep (Maladie) + // 4. Section Pep html += `
${buildHeader("Pep", "clear-type", "PEP")} - +
`; - // 5. Section Enfants (Alex & Laia) + // 5. Section Enfants html += `
${buildHeader(tr("fc_menu_kids_leaves"), null, null)}
@@ -1521,14 +1610,12 @@ document.addEventListener("DOMContentLoaded", () => {
- Alex - + Alex
- Laia - + Laia
@@ -1537,8 +1624,8 @@ document.addEventListener("DOMContentLoaded", () => { ["CP", "JRA", "JA"].forEach((t) => { html += ` - - + + `; }); diff --git a/modules/family-calendar/includes/api/events-debug.log b/modules/family-calendar/includes/api/events-debug.log index b7b3782..27e6e5e 100644 --- a/modules/family-calendar/includes/api/events-debug.log +++ b/modules/family-calendar/includes/api/events-debug.log @@ -46,3 +46,8 @@ [2026-05-06T10:14:34+02:00] RAW INPUT: [{"date":"2026-02-23","type":"CENTRE","duration":1,"person":"Carole"}] [2026-05-06T10:16:01+02:00] RAW INPUT: [{"date":"2026-01-19","type":"PEP_SICK","duration":1,"person":"Carole"}] [2026-05-06T10:16:57+02:00] RAW INPUT: [{"date":"2026-03-19","type":"PEP_SICK","duration":1,"person":"Carole"}] +[2026-05-06T20:38:19+02:00] RAW INPUT: [{"date":"2026-05-08","type":"AVIS","duration":1,"person":"Carole"}] +[2026-05-06T20:38:22+02:00] RAW INPUT: [{"date":"2026-05-08","type":"CENTRE","duration":1,"person":"Carole"}] +[2026-05-06T20:38:24+02:00] RAW INPUT: [{"date":"2026-05-08","type":"AVIS","duration":1,"person":"Carole"}] +[2026-05-06T20:43:43+02:00] RAW INPUT: [{"date":"2026-05-06","type":"AVIS","duration":1,"person":"Carole"}] +[2026-05-06T20:44:36+02:00] RAW INPUT: [{"date":"2026-05-06","type":"PEP_SICK","duration":1,"person":"Carole"}]