From b33c65093b9e9ef1084ae65c2b253d7ea025bd81 Mon Sep 17 00:00:00 2001 From: marsalva Date: Mon, 24 Aug 2026 14:08:20 +0000 Subject: [PATCH] =?UTF-8?q?Fix:=20GPS=20de=20"De=20Camino"=20solo=20se=20e?= =?UTF-8?q?nviaba=20una=20vez,=20ahora=20se=20env=C3=ADa=20cada=2025s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- calendario.html | 102 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/calendario.html b/calendario.html index 38c581d..1664c73 100644 --- a/calendario.html +++ b/calendario.html @@ -477,10 +477,16 @@ let localServices = []; let systemStatuses = []; let systemGuilds = []; - let currentWeekStart = new Date(); - let selectedDateStr = ""; + let currentWeekStart = new Date(); + let selectedDateStr = ""; let activeNotifications = []; // Variable global para guardar IDs con mensajes + // 🛰️ SEGUIMIENTO GPS EN VIVO MIENTRAS EL AVISO ESTÁ "DE CAMINO" + let gpsPingIntervalId = null; + const GPS_PING_KEY = 'active_camino_service'; + const GPS_PING_INTERVAL_MS = 25000; // cada 25s, mientras la página siga abierta + const GPS_PING_MAX_MS = 3 * 60 * 60 * 1000; // corte de seguridad: 3h sin cambiar de estado + async function applyTheme() { try { let theme = JSON.parse(localStorage.getItem('app_theme')); @@ -540,6 +546,15 @@ // Comprobar notificaciones al cargar y cada 30 segundos checkNotifications(); setInterval(checkNotifications, 30000); + + // Si veníamos de marcar "De Camino" y se recargó la página (o se reabrió la app), + // retomamos el envío periódico de posición para ese mismo aviso. + try { + const tracked = JSON.parse(localStorage.getItem(GPS_PING_KEY) || 'null'); + if (tracked && tracked.id && (Date.now() - tracked.startedAt) < GPS_PING_MAX_MS) { + startGpsTracking(tracked.id); + } + } catch (e) { /* localStorage corrupto o no disponible: seguimos sin reanudar */ } }); let lastUnreadCount = 0; // Para saber si el número ha subido y sonar @@ -1503,25 +1518,73 @@ body: JSON.stringify({ status_operativo: st.id }) }); - if(res.ok) { - showToast("¡Actualizado!"); - - if (action === 'camino' && navigator.geolocation) { - navigator.geolocation.getCurrentPosition(async (pos) => { - await fetch(`${API_URL}/services/${currentServiceId}/location`, { - method: 'POST', - headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, - body: JSON.stringify({ lat: pos.coords.latitude, lng: pos.coords.longitude }) - }); - }, () => {}, { enableHighAccuracy: false, timeout: 10000 }); + if(res.ok) { + showToast("¡Actualizado!"); + + if (action === 'camino') { + // Arranca (o retoma) el envío periódico de posición mientras dure el desplazamiento + startGpsTracking(currentServiceId); + } else { + // Cualquier otro cambio de estado (p.ej. "Trabajando") significa que ya ha llegado: + // paramos el seguimiento GPS de este aviso si estaba activo. + stopGpsTrackingIfMatches(currentServiceId); } - closeModal(); - refreshData(); + closeModal(); + refreshData(); } } catch (e) { alert("Error al actualizar"); } } + // Envía una posición puntual del operario para el aviso indicado. + async function sendLocationPing(serviceId) { + if (!navigator.geolocation) return; + navigator.geolocation.getCurrentPosition(async (pos) => { + try { + await fetch(`${API_URL}/services/${serviceId}/location`, { + method: 'POST', + headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify({ lat: pos.coords.latitude, lng: pos.coords.longitude }) + }); + } catch (e) { /* fallo puntual de red: lo reintentamos en el siguiente ciclo */ } + }, () => { /* permiso denegado o GPS lento: lo reintentamos en el siguiente ciclo */ }, { enableHighAccuracy: false, timeout: 10000, maximumAge: 15000 }); + } + + // Arranca el ciclo de posición en vivo para un aviso "De Camino" (o lo retoma si ya estaba en curso). + function startGpsTracking(serviceId) { + if (gpsPingIntervalId) clearInterval(gpsPingIntervalId); + + let tracked = null; + try { tracked = JSON.parse(localStorage.getItem(GPS_PING_KEY) || 'null'); } catch (e) {} + const startedAt = (tracked && tracked.id === serviceId) ? tracked.startedAt : Date.now(); + localStorage.setItem(GPS_PING_KEY, JSON.stringify({ id: serviceId, startedAt })); + + sendLocationPing(serviceId); // envío inmediato, sin esperar al primer ciclo + + gpsPingIntervalId = setInterval(() => { + let cur = null; + try { cur = JSON.parse(localStorage.getItem(GPS_PING_KEY) || 'null'); } catch (e) {} + if (!cur || cur.id !== serviceId || (Date.now() - cur.startedAt) > GPS_PING_MAX_MS) { + stopGpsTracking(); + return; + } + sendLocationPing(serviceId); + }, GPS_PING_INTERVAL_MS); + } + + function stopGpsTracking() { + if (gpsPingIntervalId) { clearInterval(gpsPingIntervalId); gpsPingIntervalId = null; } + localStorage.removeItem(GPS_PING_KEY); + } + + // Para el seguimiento solo si el aviso que se está rastreando es este mismo (evita cortar + // el rastreo de OTRO aviso "de camino" si el operario cambia el estado de uno distinto). + function stopGpsTrackingIfMatches(serviceId) { + let cur = null; + try { cur = JSON.parse(localStorage.getItem(GPS_PING_KEY) || 'null'); } catch (e) {} + if (cur && cur.id === serviceId) stopGpsTracking(); + } + async function askToFinish() { if(!currentServiceId) return; @@ -1540,10 +1603,11 @@ }) }); - if(res.ok) { - showToast(sendSurvey ? "Expediente finalizado y encuesta enviada." : "Expediente finalizado."); - closeModal(); - refreshData(); + if(res.ok) { + showToast(sendSurvey ? "Expediente finalizado y encuesta enviada." : "Expediente finalizado."); + stopGpsTrackingIfMatches(currentServiceId); + closeModal(); + refreshData(); } } catch (e) { alert("Error al finalizar."); } }