This commit is contained in:
2026-04-17 16:51:05 +02:00
parent 0db6a71fd7
commit fe39eb8f2c
12 changed files with 360 additions and 97 deletions
+23
View File
@@ -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;
}
+66 -5
View File
@@ -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 <html lang="..."> 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 = `
<div class="pf-weather-badge" title="${info.label}">
<span class="pf-weather-icon">${info.icon}</span>
<span>${Math.round(res.data.temp_min)}° / ${Math.round(res.data.temp_max)}°C</span>
</div>`;
}
} 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";
@@ -0,0 +1,63 @@
<?php
// modules/holidays/includes/api/get_weather.php
// 1. Désactiver les erreurs HTML pour ne pas corrompre le JSON
ini_set('display_errors', 0);
error_reporting(0);
// 2. Inclusion de tes fichiers vitaux (C'est auth.php qui sécurise l'accès !)
$basePath = '../../../../';
require_once $basePath . 'includes/db.php';
require_once $basePath . 'includes/auth.php';
// Si on arrive à cette ligne, c'est que auth.php a validé la session.
header('Content-Type: application/json');
// 3. Récupération des paramètres GPS et Date
$lat = filter_input(INPUT_GET, 'lat', FILTER_VALIDATE_FLOAT);
$lng = filter_input(INPUT_GET, 'lng', FILTER_VALIDATE_FLOAT);
$date = $_GET['date'] ?? null;
if (!$lat || !$lng || !$date) {
echo json_encode(['success' => 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']);
}
+52 -16
View File
@@ -204,8 +204,11 @@ $pctSaved = $cost > 0 ? min(100 - $pctPaid, ($saved / $cost) * 100) : 0;
<span style="background: #fff7ed; color: #ea580c; padding: 2px 6px; border-radius: 4px; font-size: 0.7rem; font-weight: bold; margin-left: 5px; border: 1px solid #ffedd5; vertical-align: middle;">🏁 <?= tr('hdl_return') ?></span>
<?php endif; ?>
<?php if (!empty($step['step_start_date']) && !empty($step['step_end_date'])): ?>
<div style="font-size:0.75rem; color:#64748b; font-weight:normal; margin-top:2px;">
<?= tr('hdl_from') ?> <?= date('d/m', strtotime($step['step_start_date'])) ?> <?= tr('hdl_to') ?> <?= date('d/m', strtotime($step['step_end_date'])) ?>
<div style="font-size:0.75rem; color:#64748b; font-weight:normal; margin-top:2px; display: flex; align-items: center; gap: 10px;">
<span>
<?= tr('hdl_from') ?> <?= date('d/m', strtotime($step['step_start_date'])) ?> <?= tr('hdl_to') ?> <?= date('d/m', strtotime($step['step_end_date'])) ?>
</span>
<div class="hol-weather-info"></div>
</div>
<?php endif; ?>
</div>
@@ -352,19 +355,52 @@ $pctSaved = $cost > 0 ? min(100 - $pctPaid, ($saved / $cost) * 100) : 0;
</div>
</div>
<?php include __DIR__ . '/modal.php'; ?>
<script>
const MAP_POINTS = <?= json_encode($mapPoints) ?>;
// --- 1. SÉCURISATION TRADUCTIONS ET VARIABLES ---
window.MAP_POINTS = <?= json_encode($mapPoints ?? []) ?>;
window.appLang = document.documentElement.lang === "ca" ? "ca-ES" : "fr-FR";
window.I18N = {
...(window.I18N || {}),
'hdl_js_search_loading': "<?= tr('hdl_js_search_loading') ?>",
'hdl_js_no_result': "<?= tr('hdl_js_no_result') ?>",
'hdl_js_confirm_del_trip': "<?= tr('hdl_js_confirm_del_trip') ?>",
'hdl_js_confirm_del_step': "<?= tr('hdl_js_confirm_del_step') ?>",
'hdl_js_step_label': "<?= tr('hdl_js_step_label') ?>",
'hdl_js_ph_expense_name': "<?= tr('hdl_js_ph_expense_name') ?>",
'hdl_js_delete_line': "<?= tr('btn_delete') ?>",
'hdl_planning_title': "<?= tr('hdl_planning_title') ?>",
'hdl_to_place': "<?= tr('hdl_to_place') ?>",
'hdl_js_missing_dates_title': "<?= tr('hdl_js_missing_dates_title') ?>",
'hdl_js_missing_dates_msg': "<?= tr('hdl_js_missing_dates_msg') ?>",
'hdl_modal_title': "<?= tr('hdl_modal_title') ?>",
'hdl_js_edit_step': "<?= tr('hdl_js_edit_step') ?>",
'hdl_ph_notes': "<?= tr('hdl_ph_notes') ?>",
'hdl_btn_add_step': "<?= tr('hdl_btn_add_step') ?>",
'hdl_quick_edit_title': "<?= tr('hdl_quick_edit_title') ?>",
// Le pont i18n pour Javascript (Holidays specific)
window.I18N = {
...window.I18N,
'hdl_js_search_loading': "<?= tr('hdl_js_search_loading') ?>",
'hdl_js_no_result': "<?= tr('hdl_js_no_result') ?>",
'hdl_js_confirm_del_trip': "<?= tr('hdl_js_confirm_del_trip') ?>",
'hdl_js_confirm_del_step': "<?= tr('hdl_js_confirm_del_step') ?>",
'hdl_js_step_label': "<?= tr('hdl_js_step_label') ?>",
'hdl_js_ph_expense_name': "<?= tr('hdl_js_ph_expense_name') ?>",
'hdl_js_ph_amount': "0.00",
'hdl_js_delete_line': "<?= tr('btn_delete') ?>"
};
</script>
// --- NOUVELLES CLÉS MÉTÉO ICI ---
'weather_sunny': "<?= tr('weather_sunny') ?>",
'weather_cloudy': "<?= tr('weather_cloudy') ?>",
'weather_rainy': "<?= tr('weather_rainy') ?>",
'weather_snowy': "<?= tr('weather_snowy') ?>",
'weather_forecast': "<?= tr('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');
};
</script>
<script src="/modules/holidays/holidays.js"></script>
+31 -1
View File
@@ -90,6 +90,37 @@ foreach ($active as $h) {
<?php include __DIR__ . '/modal.php'; ?>
<script>
// --- 1. SÉCURISATION TRADUCTIONS ET LANGUE ---
window.appLang = document.documentElement.lang === "ca" ? "ca-ES" : "fr-FR";
window.I18N = {
...(window.I18N || {}),
'hdl_js_search_loading': "<?= tr('hdl_js_search_loading') ?>",
'hdl_js_no_result': "<?= tr('hdl_js_no_result') ?>",
'hdl_js_confirm_del_trip': "<?= tr('hdl_js_confirm_del_trip') ?>",
'hdl_js_confirm_del_step': "<?= tr('hdl_js_confirm_del_step') ?>",
'hdl_js_step_label': "<?= tr('hdl_js_step_label') ?>",
'hdl_js_ph_expense_name': "<?= tr('hdl_js_ph_expense_name') ?>",
'hdl_js_delete_line': "<?= tr('btn_delete') ?>",
'hdl_planning_title': "<?= tr('hdl_planning_title') ?>",
'hdl_to_place': "<?= tr('hdl_to_place') ?>",
'hdl_js_missing_dates_title': "<?= tr('hdl_js_missing_dates_title') ?>",
'hdl_js_missing_dates_msg': "<?= tr('hdl_js_missing_dates_msg') ?>",
'hdl_modal_title': "<?= tr('hdl_modal_title') ?>",
'hdl_quick_edit_title': "<?= tr('hdl_quick_edit_title') ?>"
};
// Fallback de sécurité pour s'assurer que la modale peut toujours se fermer
window.closeHolidayModal = window.closeHolidayModal || function() {
const modal = document.getElementById('holidayModal');
if(modal) modal.style.display = 'none';
document.body.classList.remove('no-scroll');
};
</script>
<script src="/modules/holidays/holidays.js"></script>
<?php
function renderHolidayCard($h, $pdo) {
$stmt = $pdo->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 "