feat: optimización de ETA, limpieza automática de rutas y smart location
This commit is contained in:
@ -276,10 +276,9 @@ function selectRouteAndClose(route: any) {
|
||||
}
|
||||
|
||||
async function updateActiveUnits() {
|
||||
if (!isLoaded.value) return;
|
||||
if (routeStore.selectedRouteId && paradaCercana.value) {
|
||||
await calcularETA(routeStore.selectedRouteId, paradaCercana.value as BusStop);
|
||||
}
|
||||
if (!isLoaded.value || !routeStore.selectedRouteId) return;
|
||||
// Llamamos a calcularETA incluso si no hay paradaCercana aún para un chequeo rápido de disponibilidad
|
||||
await calcularETA(routeStore.selectedRouteId, (paradaCercana.value as BusStop) || null);
|
||||
}
|
||||
|
||||
function locateUser(): Promise<void> {
|
||||
@ -333,24 +332,56 @@ const sonarHtml = `
|
||||
watch([etaCargando, () => busesActivos.value.length], ([loading, count]) => {
|
||||
if (!loading && count === 0 && routeStore.selectedRouteId && routeStore.wasSelectedFromMap) {
|
||||
showETACard.value = true;
|
||||
|
||||
// PROBLEMA 2 & 3: Limpieza automática cuando no hay buses
|
||||
// Reseteamos el estado de la ruta en el store para que el buscador se limpie
|
||||
// y el mapa se limpie a través de los watchers existentes.
|
||||
|
||||
// Pequeño delay para asegurar que ETACard capture los datos antes de limpiar el store
|
||||
setTimeout(() => {
|
||||
if (showETACard.value && busesActivos.value.length === 0 && routeStore.selectedRouteId) {
|
||||
routeStore.clearSelection();
|
||||
router.replace({ query: {} });
|
||||
console.log("SIBU | Ruta autolimpiada por falta de buses");
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
|
||||
// Watch for route selection changes
|
||||
watch(() => routeStore.selectedRouteId, (routeId) => {
|
||||
if (routeId) {
|
||||
if (routeStore.wasSelectedFromMap) {
|
||||
// OPTIMIZACIÓN PROBLEMA 1: Paralelismo Total
|
||||
// Iniciamos dibujo y búsqueda de disponibilidad en paralelo
|
||||
updateMapMarkers(false);
|
||||
updateActiveUnits();
|
||||
} else {
|
||||
clearMapMarkers();
|
||||
}
|
||||
} else {
|
||||
clearMapMarkers();
|
||||
showETACard.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Watch for paradaCercana to recalculate ETA as soon as it's identified
|
||||
watch(paradaCercana, (newStop) => {
|
||||
if (newStop && routeStore.selectedRouteId) {
|
||||
updateActiveUnits();
|
||||
}
|
||||
});
|
||||
|
||||
function handleImageError(event: Event) {
|
||||
(event.target as HTMLImageElement).src = getImageUrl(null, 'coupon');
|
||||
}
|
||||
|
||||
// AUTO-LOCATION: Watch for user profile to trigger location if preference is enabled
|
||||
watch(() => authStore.userProfile?.auto_location, (canLocate) => {
|
||||
if (canLocate && isLoaded.value && !userCoords.value) {
|
||||
locateUser();
|
||||
}
|
||||
}, { immediate: true });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -380,9 +411,20 @@ function handleImageError(event: Event) {
|
||||
<span v-if="couponStore.coupons.length > 0" class="offers-badge">{{ couponStore.coupons.length }}</span>
|
||||
</button>
|
||||
|
||||
<button v-if="isLoaded && (!authStore.userProfile?.auto_location || isMapMoved)" class="location-loader-btn" @click="locateUser">
|
||||
<span class="material-icons">my_location</span>
|
||||
</button>
|
||||
<!-- SMART LOCATION BUTTON: Hidden by default if auto-location is active, shows up with text when map moved -->
|
||||
<Transition name="fade-scale">
|
||||
<button
|
||||
v-if="isLoaded && (!authStore.userProfile?.auto_location || isMapMoved)"
|
||||
class="location-btn-smart"
|
||||
:class="{ 'moved': isMapMoved }"
|
||||
@click="locateUser"
|
||||
>
|
||||
<div class="btn-content">
|
||||
<span class="material-icons">my_location</span>
|
||||
<span v-if="isMapMoved" class="btn-text">Volver a Mi Ubicación</span>
|
||||
</div>
|
||||
</button>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -534,15 +576,54 @@ function handleImageError(event: Event) {
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
|
||||
.location-loader-btn {
|
||||
.location-btn-smart {
|
||||
background: var(--header-bg);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid var(--border-color);
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
border-radius: 25px;
|
||||
color: var(--active-color);
|
||||
box-shadow: var(--shadow);
|
||||
padding: 0 13px;
|
||||
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50px; /* Default circular */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.location-btn-smart.moved {
|
||||
width: auto; /* Expand for text */
|
||||
padding: 0 20px;
|
||||
background: var(--active-color);
|
||||
color: #000;
|
||||
border-color: #000;
|
||||
}
|
||||
|
||||
.btn-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.fade-scale-enter-active,
|
||||
.fade-scale-leave-active {
|
||||
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.fade-scale-enter-from,
|
||||
.fade-scale-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.5) translateY(20px);
|
||||
}
|
||||
|
||||
.promo-modal-overlay {
|
||||
|
||||
Reference in New Issue
Block a user