weather
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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']);
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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 "
|
||||
|
||||
Reference in New Issue
Block a user