From fe39eb8f2c278ab564e8b3ad5d0d6cd4544a4512 Mon Sep 17 00:00:00 2001 From: "fefe.clochette" Date: Fri, 17 Apr 2026 16:51:05 +0200 Subject: [PATCH] weather --- family-calendar.php | 23 ++++++ includes/lang/ca.php | 17 +++++ includes/lang/fr.php | 17 +++++ modules/budget/views/budget_prev.php | 48 ++++++------- modules/budget/views/epargne.php | 67 +++++++---------- modules/family-calendar/family-calendar.css | 20 ++++-- modules/family-calendar/family-calendar.js | 8 +-- modules/holidays/holidays.css | 23 ++++++ modules/holidays/holidays.js | 71 +++++++++++++++++-- modules/holidays/includes/api/get_weather.php | 63 ++++++++++++++++ modules/holidays/views/detail.php | 68 +++++++++++++----- modules/holidays/views/list.php | 32 ++++++++- 12 files changed, 360 insertions(+), 97 deletions(-) create mode 100644 modules/holidays/includes/api/get_weather.php diff --git a/family-calendar.php b/family-calendar.php index ea6554d..c791353 100644 --- a/family-calendar.php +++ b/family-calendar.php @@ -196,5 +196,28 @@ require __DIR__ . '/header.php'; + \ No newline at end of file diff --git a/includes/lang/ca.php b/includes/lang/ca.php index 0932224..04210d6 100644 --- a/includes/lang/ca.php +++ b/includes/lang/ca.php @@ -206,6 +206,20 @@ return [ 'hdl_js_missing_dates_title'=> 'Dates mancants', 'hdl_js_missing_dates_msg' => 'Cal definir una data d’arribada i sortida per organitzar la planificació.', 'hdl_to_place' => 'Per situar', + 'hdl_from' => 'Des del', + 'hdl_to' => 'Fins al', + 'hdl_btn_add_step' => 'Afegir una etapa', + 'hdl_quick_edit_title' => 'Modificació ràpida', + 'hdl_paid' => 'Pagat', + 'weather_forecast' => 'Previsions meteorològiques', + 'weather_not_available' => 'Temps no disponible', + 'temp_max' => 'Temp. Màxima', + 'weather_loading' => 'Carregant el temps...', + 'weather_error' => 'Temps no disponible', + 'weather_sunny' => 'Assolellat', + 'weather_cloudy' => 'Ennuvolat', + 'weather_rainy' => 'Plujós', + 'weather_snowy' => 'Nevat', // Anciennes clés Holidays non préfixées (conservades) 'outbound_trip' => 'Anada', @@ -373,6 +387,9 @@ return [ 'bud_reg_none' => 'Cap', 'bud_recap_confirm_delete' => 'Segur que vols suprimir aquest element?', 'bud_err_delete' => 'Error en suprimir.', + 'bud_sav_modal_title_add' => 'Introduir un mes', + 'bud_sav_ph_name' => 'Nom (ex: Vacances)', + 'bud_prev_label_name' => 'Nom', // --- BUDGET : EPARGNE --- 'bud_sav_add_month' => 'Introduir un mes', diff --git a/includes/lang/fr.php b/includes/lang/fr.php index 1937c87..416ed2b 100644 --- a/includes/lang/fr.php +++ b/includes/lang/fr.php @@ -206,6 +206,20 @@ return [ 'hdl_js_missing_dates_title'=> 'Dates manquantes', 'hdl_js_missing_dates_msg' => 'Veuillez définir une date d’arrivée et de départ pour organiser le planning.', 'hdl_to_place' => 'À placer', + 'hdl_from' => 'Du', + 'hdl_to' => 'Au', + 'hdl_btn_add_step' => 'Ajouter une étape', + 'hdl_quick_edit_title' => 'Modification rapide', + 'hdl_paid' => 'Payé', + 'weather_forecast' => 'Prévisions météo', + 'weather_not_available' => 'Météo indisponible', + 'temp_max' => 'Temp. Max', + 'weather_loading' => 'Chargement météo...', + 'weather_error' => 'Météo indisponible', + 'weather_sunny' => 'Ensoleillé', + 'weather_cloudy' => 'Nuageux', + 'weather_rainy' => 'Pluvieux', + 'weather_snowy' => 'Neigeux', // Anciennes clés Holidays non préfixées (conservées pour compatibilité si utilisées) 'outbound_trip' => 'Aller', @@ -438,6 +452,9 @@ return [ 'bud_prev_err_no_history' => 'Impossible : pas d\'historique disponible pour copier.', 'bud_prev_confirm_copy' => "Voulez-vous copier les données de %s vers %t ?\n\n⚠️ Cela écrasera toutes les valeurs déjà présentes pour %t.", 'bud_prev_confirm_transfers' => "Confirmer que %p a bien effectué tous ses virements pour %m ?\n\nCela mettra à jour l'Épargne automatiquement.", + 'bud_sav_modal_title_add' => 'Saisir un mois', + 'bud_sav_ph_name' => 'Nom (ex: Vacances)', + 'bud_prev_label_name' => 'Nom', // ========================================== // CADEAUX (GIFTS) diff --git a/modules/budget/views/budget_prev.php b/modules/budget/views/budget_prev.php index 4f5b3a6..6ab128d 100644 --- a/modules/budget/views/budget_prev.php +++ b/modules/budget/views/budget_prev.php @@ -571,19 +571,22 @@ body.sum-mode-active .sum-target { - +// --- 1. SÉCURISATION TRADUCTIONS ET LANGUE --- +window.appLang = document.documentElement.lang === "ca" ? "ca-ES" : "fr-FR"; +window.I18N = { + ...(window.I18N || {}), + 'bud_sav_modal_title_add': "", + 'bud_sav_modal_title_edit': "", + 'bud_sav_ph_name': "", + 'bud_sav_confirm_delete_month': "", + 'bud_sav_prompt_duplicate': "", + 'bud_err_tech': "", + 'bud_err_server': "", + 'bud_err_network_dup': "", + 'bud_sav_saving': "", + 'bud_err_delete': "" +}; - \ No newline at end of file diff --git a/modules/family-calendar/family-calendar.css b/modules/family-calendar/family-calendar.css index 047e153..81067ef 100644 --- a/modules/family-calendar/family-calendar.css +++ b/modules/family-calendar/family-calendar.css @@ -168,7 +168,6 @@ min-width: 1000px; border-collapse: separate; border-spacing: 0; - table-layout: fixed; } /* Sticky Header */ @@ -303,12 +302,25 @@ width: auto !important; border-right: 2px solid var(--border-color) !important; } -.col-total, -.col-alex-sub, -.col-laia-sub { +th.col-total, +td.col-total, +th.col-alex-sub, +td.col-alex-sub, +th.col-laia-sub, +td.col-laia-sub { width: 32px !important; min-width: 32px !important; max-width: 32px !important; + box-sizing: border-box; + padding: 0 2px !important; /* On réduit le padding pour que les chiffres respirent */ + overflow: hidden; +} + +th.col-day, +td.col-day { + width: auto !important; + min-width: 35px !important; + border-right: 2px solid var(--border-color) !important; } .rotated-text span { diff --git a/modules/family-calendar/family-calendar.js b/modules/family-calendar/family-calendar.js index 79acda8..0514536 100644 --- a/modules/family-calendar/family-calendar.js +++ b/modules/family-calendar/family-calendar.js @@ -944,10 +944,10 @@ document.addEventListener("DOMContentLoaded", () => { }); div.innerHTML = ` -
Off Carole${parseFloat(stats.off.toFixed(1))} jours
-
Extra Off Carole${parseFloat(stats.extra.toFixed(1))} jours
-
Pep Malade${parseFloat(stats.sick.toFixed(1))} jours
-
Présence Pep${parseFloat(stats.pep.toFixed(1))} jours
+
${tr("leg_off_carole")}${parseFloat(stats.off.toFixed(1))} ${tr("fc_unit_days")}
+
${tr("leg_extra_off")}${parseFloat(stats.extra.toFixed(1))} ${tr("fc_unit_days")}
+
${tr("leg_pep_sick")}${parseFloat(stats.sick.toFixed(1))} ${tr("fc_unit_days")}
+
${tr("leg_presence")} Pep${parseFloat(stats.pep.toFixed(1))} ${tr("fc_unit_days")}
`; } diff --git a/modules/holidays/holidays.css b/modules/holidays/holidays.css index 94c4751..10125af 100644 --- a/modules/holidays/holidays.css +++ b/modules/holidays/holidays.css @@ -1196,3 +1196,26 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover { box-shadow: 0 0 0 3px var(--primary); transform: scale(1.02); } + +/* Style du badge météo injecté par le JS */ +.hol-weather-info { + display: inline-flex; +} + +.pf-weather-badge { + display: flex; + align-items: center; + gap: 4px; + background: #f8fafc; /* Slate 50 */ + border: 1px solid #e2e8f0; /* Slate 200 */ + padding: 1px 6px; + border-radius: 6px; + color: #475569; /* Slate 600 */ + font-weight: 600; + font-size: 0.7rem; + line-height: 1; +} + +.pf-weather-icon { + font-size: 0.9rem; +} diff --git a/modules/holidays/holidays.js b/modules/holidays/holidays.js index f3aed12..99a6abf 100644 --- a/modules/holidays/holidays.js +++ b/modules/holidays/holidays.js @@ -5,8 +5,60 @@ function tr(key) { return window.I18N && window.I18N[key] ? window.I18N[key] : key; } -// On détecte la langue de la page (définie dans la balise du header) -const currentLang = document.documentElement.lang === "ca" ? "ca-ES" : "fr-FR"; +// On utilise 'var' au lieu de 'const/let' pour éviter les crashs si le fichier est lu 2 fois +var currentLang = document.documentElement.lang === "ca" ? "ca-ES" : "fr-FR"; +var selectedItemIdForMove = null; // Déplacé ici pour plus de clarté + +// ============================================================================ +// UTILITAIRES MÉTÉO +// ============================================================================ +function getWeatherInfo(code) { + // Transformation en conditions pour regrouper les codes WMO + if (code === 0) return { icon: "☀️", label: tr("weather_sunny") }; + if ([1, 2].includes(code)) return { icon: "🌤️", label: tr("weather_sunny") }; + if ([3, 45, 48].includes(code)) + return { icon: "☁️", label: tr("weather_cloudy") }; + // Les codes 51 à 67 et 80 à 82 couvrent toutes les formes de pluie et bruine + if ([51, 53, 55, 56, 57, 61, 63, 65, 66, 67, 80, 81, 82].includes(code)) + return { icon: "🌧️", label: tr("weather_rainy") }; + // Les codes neigeux + if ([71, 73, 75, 77, 85, 86].includes(code)) + return { icon: "❄️", label: tr("weather_snowy") }; + // Orages + if ([95, 96, 99].includes(code)) + return { icon: "⛈️", label: tr("weather_rainy") }; + + return { icon: "🌡️", label: tr("weather_forecast") }; +} + +async function loadWeatherForStep(pt) { + if (!pt.step_start_date || !pt.lat || !pt.lng) return; + + const container = document.querySelector( + `#step-card-${pt.sort_order} .hol-weather-info`, + ); + if (!container) return; + + try { + const resp = await fetch( + `/modules/holidays/includes/api/get_weather.php?lat=${pt.lat}&lng=${pt.lng}&date=${pt.step_start_date}`, + ); + const res = await resp.json(); + + console.log(`Météo pour ${pt.location_name} :`, res); + + if (res.success) { + const info = getWeatherInfo(res.data.code); + container.innerHTML = ` +
+ ${info.icon} + ${Math.round(res.data.temp_min)}° / ${Math.round(res.data.temp_max)}°C +
`; + } + } catch (e) { + console.error("Weather error", e); + } +} // ============================================================================ // FERMETURE UNIVERSELLE DES MODALES @@ -150,7 +202,7 @@ function deleteHoliday() { // --- 3. GESTION DE LA CARTE --- -let map = null; +var map = null; function toggleMap() { const modal = document.getElementById("hol-map-modal"); @@ -197,7 +249,7 @@ function initMap() { // 4. GESTION DE LA CARTE DÉTAILLÉE (ROADTRIP) ET GÉOCODAGE // ============================================================================ -let detailMap = null; +var detailMap = null; document.addEventListener("DOMContentLoaded", () => { if (document.getElementById("tripMap")) { @@ -208,6 +260,11 @@ document.addEventListener("DOMContentLoaded", () => { function initDetailMap() { if (typeof L === "undefined" || typeof MAP_POINTS === "undefined") return; + const mapContainer = L.DomUtil.get("tripMap"); + if (mapContainer !== null && mapContainer._leaflet_id) { + mapContainer._leaflet_id = null; + } + detailMap = L.map("tripMap"); L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19, @@ -323,6 +380,11 @@ function initDetailMap() { } }); }); + detailMap.fitBounds(bounds, { padding: [50, 50] }); + // Lancement de la météo pour chaque étape + if (typeof MAP_POINTS !== "undefined") { + MAP_POINTS.forEach((pt) => loadWeatherForStep(pt)); + } } function drawFallbackLine(coords, color, weight) { @@ -582,7 +644,6 @@ document.addEventListener("DOMContentLoaded", () => { // ============================================================================ // MOTEUR DRAG & DROP DU PLANNING // ============================================================================ -let selectedItemIdForMove = null; function closePlanningModal() { document.getElementById("planningModal").style.display = "none"; diff --git a/modules/holidays/includes/api/get_weather.php b/modules/holidays/includes/api/get_weather.php new file mode 100644 index 0000000..c6914b8 --- /dev/null +++ b/modules/holidays/includes/api/get_weather.php @@ -0,0 +1,63 @@ + false, 'message' => 'Paramètres manquants']); + exit; +} + +$date = substr($date, 0, 10); +$today = new DateTime(); +$targetDate = new DateTime($date); +$interval = $today->diff($targetDate); +$daysDiff = (int)$interval->format('%R%a'); + +// 4. Choix du bon modèle météo (Prévisions vs Historique) +if ($daysDiff < -2) { + $baseUrl = "https://archive-api.open-meteo.com/v1/archive"; +} else { + $baseUrl = "https://api.open-meteo.com/v1/forecast"; +} + +$url = "$baseUrl?latitude=$lat&longitude=$lng&daily=weather_code,temperature_2m_max,temperature_2m_min&timezone=auto&start_date=$date&end_date=$date"; + +// 5. Interrogation d'Open-Meteo +$res = @file_get_contents($url); + +if (!$res) { + echo json_encode(['success' => false, 'message' => 'Erreur API Open-Meteo']); + exit; +} + +$data = json_decode($res, true); + +// 6. Formatage et envoi au Javascript +if (isset($data['daily']['weather_code'][0])) { + echo json_encode([ + 'success' => true, + 'data' => [ + 'code' => $data['daily']['weather_code'][0], + 'temp_max' => $data['daily']['temperature_2m_max'][0], + 'temp_min' => $data['daily']['temperature_2m_min'][0] + ] + ]); +} else { + echo json_encode(['success' => false, 'message' => 'Aucune donnée pour cette date']); +} \ No newline at end of file diff --git a/modules/holidays/views/detail.php b/modules/holidays/views/detail.php index cd401f8..510527b 100644 --- a/modules/holidays/views/detail.php +++ b/modules/holidays/views/detail.php @@ -204,8 +204,11 @@ $pctSaved = $cost > 0 ? min(100 - $pctPaid, ($saved / $cost) * 100) : 0; 🏁 -
- +
+ + + +
@@ -352,19 +355,52 @@ $pctSaved = $cost > 0 ? min(100 - $pctPaid, ($saved / $cost) * 100) : 0; + + \ No newline at end of file + // --- NOUVELLES CLÉS MÉTÉO ICI --- + 'weather_sunny': "", + 'weather_cloudy': "", + 'weather_rainy': "", + 'weather_snowy': "", + 'weather_forecast': "" +}; + +// Fallback de sécurité pour s'assurer que les modales peuvent toujours se fermer +window.closeCheckpointModal = window.closeCheckpointModal || function() { + const modal = document.getElementById('checkpointModal'); + if(modal) modal.style.display = 'none'; + document.body.classList.remove('no-scroll'); +}; + +window.closePlanningModal = window.closePlanningModal || function() { + const modal = document.getElementById('planningModal'); + if(modal) modal.style.display = 'none'; + document.body.classList.remove('no-scroll'); +}; + + + \ No newline at end of file diff --git a/modules/holidays/views/list.php b/modules/holidays/views/list.php index ef28719..9f317b7 100644 --- a/modules/holidays/views/list.php +++ b/modules/holidays/views/list.php @@ -90,6 +90,37 @@ foreach ($active as $h) { + + + + prepare("SELECT * FROM pf_holidays_items WHERE holiday_id = ?"); @@ -119,7 +150,6 @@ function renderHolidayCard($h, $pdo) { $pctPaid = $cost > 0 ? min(100, ($paid / $cost) * 100) : 0; $pctSaved = $cost > 0 ? min(100 - $pctPaid, ($saved / $cost) * 100) : 0; - // Traduction dynamique du statut $statusLabel = tr('hdl_status_' . $h['status']); echo "