feat: simplify map by removing all stop markers and manual clear button
This commit is contained in:
@ -13,7 +13,6 @@ import { getImageUrl } from "@/utils/imageUrl";
|
|||||||
import { useDirectionsRoute } from "@/composables/useDirectionsRoute";
|
import { useDirectionsRoute } from "@/composables/useDirectionsRoute";
|
||||||
import { useParadaCercana } from "@/composables/useParadaCercana";
|
import { useParadaCercana } from "@/composables/useParadaCercana";
|
||||||
import { useETA } from "@/composables/useETA";
|
import { useETA } from "@/composables/useETA";
|
||||||
const BusStopInfoModal = defineAsyncComponent(() => import("@/components/BusStopInfoModal.vue"));
|
|
||||||
const ETACard = defineAsyncComponent(() => import("@/components/ETACard.vue"));
|
const ETACard = defineAsyncComponent(() => import("@/components/ETACard.vue"));
|
||||||
import type { BusStop } from '@/types'
|
import type { BusStop } from '@/types'
|
||||||
import { useFlujoPrincipal } from "@/composables/useFlujoPrincipal";
|
import { useFlujoPrincipal } from "@/composables/useFlujoPrincipal";
|
||||||
@ -27,7 +26,7 @@ const mapStore = useMapStore();
|
|||||||
const busStopStore = useBusStopStore();
|
const busStopStore = useBusStopStore();
|
||||||
const couponStore = useCouponStore();
|
const couponStore = useCouponStore();
|
||||||
|
|
||||||
const { map, isLoaded, error: mapsError, initMap, addCleanMarker, addHtmlMarker, setCenter, setZoom, addMarker } = useGoogleMaps();
|
const { map, isLoaded, error: mapsError, initMap, addHtmlMarker, setCenter, setZoom, addMarker } = useGoogleMaps();
|
||||||
const { estasCargando: estasCargandoRuta, errorRuta } = useDirectionsRoute();
|
const { estasCargando: estasCargandoRuta, errorRuta } = useDirectionsRoute();
|
||||||
const { encontrarParadaCercana, paradaCercana, distanciaMetros, duracionCaminata } = useParadaCercana();
|
const { encontrarParadaCercana, paradaCercana, distanciaMetros, duracionCaminata } = useParadaCercana();
|
||||||
const { calcularETA, busesActivos, cargando: etaCargando } = useETA();
|
const { calcularETA, busesActivos, cargando: etaCargando } = useETA();
|
||||||
@ -41,18 +40,18 @@ const showETACard = ref(false);
|
|||||||
const markers = ref<any[]>([]);
|
const markers = ref<any[]>([]);
|
||||||
const promoMarkers = ref<any[]>([]);
|
const promoMarkers = ref<any[]>([]);
|
||||||
const userMarker = ref<any>(null);
|
const userMarker = ref<any>(null);
|
||||||
const polyline = ref<google.maps.Polyline | null>(null);
|
const currentMarkerMode = ref<'dot' | 'pin' | null>(null);
|
||||||
const walkingPolyline = ref<google.maps.Polyline | null>(null);
|
|
||||||
const walkingPolylineBorder = ref<google.maps.Polyline | null>(null); // Borde blanco estilo Google Maps
|
|
||||||
const optimalStopPulse = ref<any>(null); // Radar para la parada óptima
|
|
||||||
const showRouteDropdown = ref(false);
|
|
||||||
const routeCardRef = ref<HTMLElement | null>(null);
|
|
||||||
const isUpdatingMarkers = ref(false);
|
const isUpdatingMarkers = ref(false);
|
||||||
const unitMarkers = ref<Map<string, google.maps.Marker>>(new Map());
|
const unitMarkers = ref<Map<string, google.maps.Marker>>(new Map());
|
||||||
const unitFetchInterval = ref<any>(null);
|
const unitFetchInterval = ref<any>(null);
|
||||||
const userCoords = ref<{ lat: number; lng: number } | null>(null); // Store last user location for internal navigation
|
const userCoords = ref<{ lat: number; lng: number } | null>(null);
|
||||||
const currentMarkerMode = ref<'dot' | 'pin' | null>(null);
|
const polyline = ref<google.maps.Polyline | null>(null);
|
||||||
const mappingSequenceId = ref(0); // Atomic ID to prevent race conditions
|
const walkingPolyline = ref<google.maps.Polyline | null>(null);
|
||||||
|
const walkingPolylineBorder = ref<google.maps.Polyline | null>(null);
|
||||||
|
const optimalStopPulse = ref<any>(null);
|
||||||
|
const showRouteDropdown = ref(false);
|
||||||
|
const routeCardRef = ref<HTMLElement | null>(null);
|
||||||
|
const mappingSequenceId = ref(0);
|
||||||
|
|
||||||
const alturaNavbar = ref(64);
|
const alturaNavbar = ref(64);
|
||||||
// Search state
|
// Search state
|
||||||
@ -90,59 +89,16 @@ function closeUberSearch() {
|
|||||||
destinationQuery.value = "";
|
destinationQuery.value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearAllMapData() {
|
|
||||||
console.log('🤖 JARVIS: Iniciando PURGA nuclear centralizada...');
|
|
||||||
|
|
||||||
// 1. UI inmediata
|
// clearAllMapData removed per request
|
||||||
showUberSearch.value = false;
|
|
||||||
showRoutesToggle.value = false;
|
|
||||||
destinationQuery.value = "";
|
|
||||||
stopSearchQuery.value = "";
|
|
||||||
showETACard.value = false;
|
|
||||||
paradaCercana.value = null; // Borrar banner superior
|
|
||||||
|
|
||||||
// 2. Invalidar hilos en curso
|
|
||||||
mappingSequenceId.value++;
|
|
||||||
|
|
||||||
try {
|
// Modal state removed per request (no more stop markers to click)
|
||||||
// 3. Resetear stores
|
|
||||||
routeStore.clearSelection();
|
|
||||||
lastProcessedRouteId.value = null;
|
|
||||||
|
|
||||||
// 4. LIMPIEZA CENTRALIZADA (useMapState)
|
function reloadPage() {
|
||||||
// Esto limpia markers, renderers, polylines, overlays HTML, etc.
|
window.location.reload();
|
||||||
limpiarTodoCentralizado();
|
|
||||||
|
|
||||||
// 5. Limpiar referencias locales del componente (aunque no tengan mapa asignado ya)
|
|
||||||
markers.value = [];
|
|
||||||
promoMarkers.value = [];
|
|
||||||
unitMarkers.value.clear();
|
|
||||||
polyline.value = null;
|
|
||||||
walkingPolyline.value = null;
|
|
||||||
walkingPolylineBorder.value = null;
|
|
||||||
optimalStopPulse.value = null;
|
|
||||||
|
|
||||||
// 6. Restaurar SOLO el marcador del usuario si tenemos ubicación
|
|
||||||
await nextTick();
|
|
||||||
if (userCoords.value) {
|
|
||||||
const { lat, lng } = userCoords.value;
|
|
||||||
userMarker.value = addHtmlMarker(
|
|
||||||
{ lat, lng },
|
|
||||||
sonarHtml,
|
|
||||||
{ x: -30, y: -30 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('🤖 JARVIS: Purga completada ✓');
|
|
||||||
} catch (err) {
|
|
||||||
console.error('❌ JARVIS: Error en purga:', err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modal state
|
|
||||||
const showBusStopModal = ref(false);
|
|
||||||
const selectedBusStop = ref<BusStop | null>(null);
|
|
||||||
|
|
||||||
const showPromoModal = ref(false);
|
const showPromoModal = ref(false);
|
||||||
const selectedPromo = ref<any>(null);
|
const selectedPromo = ref<any>(null);
|
||||||
const isBannerClosing = ref(false);
|
const isBannerClosing = ref(false);
|
||||||
@ -154,27 +110,6 @@ function animateAndReload() {
|
|||||||
}, 450); // Mismo tiempo que la transición
|
}, 450); // Mismo tiempo que la transición
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close dropdown when clicking outside
|
|
||||||
function handleClickOutside(event: MouseEvent) {
|
|
||||||
if (routeCardRef.value && !routeCardRef.value.contains(event.target as Node)) {
|
|
||||||
showRouteDropdown.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBusStopClick(stop: BusStop) {
|
|
||||||
selectedBusStop.value = stop;
|
|
||||||
showBusStopModal.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeBusStopModal() {
|
|
||||||
showBusStopModal.value = false;
|
|
||||||
selectedBusStop.value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reloadPage() {
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handlePromoClick(promo: any) {
|
function handlePromoClick(promo: any) {
|
||||||
selectedPromo.value = promo;
|
selectedPromo.value = promo;
|
||||||
showPromoModal.value = true;
|
showPromoModal.value = true;
|
||||||
@ -207,8 +142,6 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
analyticsService.logEvent({ event_name: 'screen_view', properties: { screen_name: 'Map' } })
|
analyticsService.logEvent({ event_name: 'screen_view', properties: { screen_name: 'Map' } })
|
||||||
// Add click outside listener
|
|
||||||
document.addEventListener('click', handleClickOutside);
|
|
||||||
|
|
||||||
// Load routes, bus stops and promos in parallel
|
// Load routes, bus stops and promos in parallel
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -226,19 +159,6 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle stopId if present
|
|
||||||
const queryStopId = router.currentRoute.value.query.stopId as string;
|
|
||||||
if (queryStopId) {
|
|
||||||
await busStopStore.loadBusStops();
|
|
||||||
const foundStop = busStopStore.busStops.find(s => s.id === queryStopId);
|
|
||||||
if (foundStop) {
|
|
||||||
selectedBusStop.value = foundStop;
|
|
||||||
showBusStopModal.value = true;
|
|
||||||
setCenter(foundStop.latitude, foundStop.longitude);
|
|
||||||
setZoom(17);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for Google Maps to load
|
// Wait for Google Maps to load
|
||||||
if (isLoaded.value) {
|
if (isLoaded.value) {
|
||||||
await initializeMap();
|
await initializeMap();
|
||||||
@ -294,7 +214,6 @@ function prevPromo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
document.removeEventListener('click', handleClickOutside);
|
|
||||||
if (unitFetchInterval.value) clearInterval(unitFetchInterval.value);
|
if (unitFetchInterval.value) clearInterval(unitFetchInterval.value);
|
||||||
if (carouselTimer.value) clearInterval(carouselTimer.value);
|
if (carouselTimer.value) clearInterval(carouselTimer.value);
|
||||||
// Clear all markers when component unmounts
|
// Clear all markers when component unmounts
|
||||||
@ -426,18 +345,7 @@ async function updateMapMarkers() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Agregar todos los stops como marcadores 'normales' para que se vean en el mapa
|
// All stop markers loop removed per request to avoid marking stops on map
|
||||||
stops.forEach(stop => {
|
|
||||||
// Evitar sobre dibujar si es la cercana (useFlujoPrincipal ya se encargó)
|
|
||||||
if (paradaCercana.value && stop.id === paradaCercana.value.id) return;
|
|
||||||
|
|
||||||
addCleanMarker(
|
|
||||||
{ lat: stop.latitude, lng: stop.longitude },
|
|
||||||
stop.name,
|
|
||||||
'normal',
|
|
||||||
() => handleBusStopClick(stop)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
isUpdatingMarkers.value = false;
|
isUpdatingMarkers.value = false;
|
||||||
}
|
}
|
||||||
@ -625,128 +533,9 @@ async function highlightOptimalStopForRoute() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Dibuja la ruta caminando en el mapa interno sin abrir apps externas
|
|
||||||
*/
|
|
||||||
function drawInternalWalkingRoute(targetStop: BusStop, originOverride?: { lat: number, lng: number }) {
|
|
||||||
const origin = originOverride || userCoords.value;
|
|
||||||
|
|
||||||
if (!origin) {
|
|
||||||
// Si no tenemos ubicación, la pedimos
|
|
||||||
navigator.geolocation.getCurrentPosition((pos) => {
|
|
||||||
userCoords.value = { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
|
||||||
calculateWalkingPath(userCoords.value, targetStop);
|
|
||||||
}, (_err) => {
|
|
||||||
alert("Necesitamos tu ubicación para trazar la ruta a pie.");
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
calculateWalkingPath(origin, targetStop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function calculateWalkingPath(origin: { lat: number, lng: number }, targetStop: BusStop) {
|
// walking route functions removed
|
||||||
// 1. Limpiar pulso anterior si existe
|
|
||||||
if (optimalStopPulse.value) {
|
|
||||||
if (typeof optimalStopPulse.value.setMap === 'function') {
|
|
||||||
optimalStopPulse.value.setMap(null);
|
|
||||||
}
|
|
||||||
optimalStopPulse.value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Añadir el PULSO NARANJA de "Parada Óptima"
|
|
||||||
optimalStopPulse.value = addHtmlMarker(
|
|
||||||
{ lat: targetStop.latitude, lng: targetStop.longitude },
|
|
||||||
optimalSonarHtml,
|
|
||||||
{ x: -30, y: -30 }
|
|
||||||
);
|
|
||||||
|
|
||||||
// 3. Resaltar la parada en verde (marcador estándar)
|
|
||||||
const targetStops = routeStore.selectedRouteId ? routeStore.selectedRouteStops : busStopStore.busStops;
|
|
||||||
const stopIndex = targetStops.findIndex(s => s.id === targetStop.id);
|
|
||||||
|
|
||||||
if (stopIndex !== -1 && markers.value[stopIndex]) {
|
|
||||||
const marker = markers.value[stopIndex];
|
|
||||||
marker.setIcon({
|
|
||||||
path: currentMarkerMode.value === 'pin'
|
|
||||||
? "M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z"
|
|
||||||
: google.maps.SymbolPath.CIRCLE,
|
|
||||||
fillColor: '#4CAF50',
|
|
||||||
fillOpacity: 1,
|
|
||||||
strokeColor: '#FFFFFF',
|
|
||||||
strokeWeight: 2,
|
|
||||||
scale: currentMarkerMode.value === 'pin' ? 2.5 : 9,
|
|
||||||
anchor: currentMarkerMode.value === 'pin' ? new google.maps.Point(12, 22) : null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Trazar línea de puntos verde siguiendo RED VIAL PRINCIPAL
|
|
||||||
try {
|
|
||||||
const { RoutesService } = await google.maps.importLibrary("routes") as any;
|
|
||||||
const routeService = new RoutesService();
|
|
||||||
const response = await routeService.computeRoutes({
|
|
||||||
origin: {
|
|
||||||
location: {
|
|
||||||
latLng: { lat: origin.lat, lng: origin.lng }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
location: {
|
|
||||||
latLng: { lat: targetStop.latitude, lng: targetStop.longitude }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
travelMode: 'DRIVE',
|
|
||||||
routingPreference: 'TRAFFIC_UNAWARE',
|
|
||||||
polylineQuality: 'HIGH_QUALITY',
|
|
||||||
polylineEncoding: 'ENCODED_POLYLINE',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.routes && response.routes.length > 0) {
|
|
||||||
const route = response.routes[0];
|
|
||||||
|
|
||||||
if (route.polyline && route.polyline.encodedPolyline) {
|
|
||||||
const path = google.maps.geometry.encoding.decodePath(route.polyline.encodedPolyline);
|
|
||||||
|
|
||||||
if (walkingPolyline.value) walkingPolyline.value.setMap(null);
|
|
||||||
if (walkingPolylineBorder.value) walkingPolylineBorder.value.setMap(null);
|
|
||||||
|
|
||||||
const { registrarPolyline: regPoly } = useMapState();
|
|
||||||
|
|
||||||
// CAPA 1: Borde blanco (Para dar contraste estilo Google Maps)
|
|
||||||
walkingPolylineBorder.value = new google.maps.Polyline({
|
|
||||||
path: path,
|
|
||||||
geodesic: true,
|
|
||||||
strokeColor: '#FFFFFF',
|
|
||||||
strokeOpacity: 0.9,
|
|
||||||
strokeWeight: 10,
|
|
||||||
map: map.value,
|
|
||||||
zIndex: 5
|
|
||||||
});
|
|
||||||
regPoly(walkingPolylineBorder.value);
|
|
||||||
|
|
||||||
// CAPA 2: Línea Indigo Central (La ruta principal)
|
|
||||||
walkingPolyline.value = new google.maps.Polyline({
|
|
||||||
path: path,
|
|
||||||
geodesic: true,
|
|
||||||
strokeColor: '#4285F4', // Azul Google Maps
|
|
||||||
strokeOpacity: 1.0,
|
|
||||||
strokeWeight: 5,
|
|
||||||
map: map.value,
|
|
||||||
zIndex: 10
|
|
||||||
});
|
|
||||||
regPoly(walkingPolyline.value);
|
|
||||||
|
|
||||||
// Ajustar zoom para mostrar toda la ruta de caminata
|
|
||||||
if (map.value) {
|
|
||||||
const bounds = new google.maps.LatLngBounds();
|
|
||||||
path.forEach(p => bounds.extend(p));
|
|
||||||
map.value.fitBounds(bounds, { top: 100, bottom: 200, left: 50, right: 50 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('SIBU | Error trazando ruta a pie con Routes API:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -886,14 +675,7 @@ async function calculateWalkingPath(origin: { lat: number, lng: number }, target
|
|||||||
|
|
||||||
<!-- Inputs and Toggle removed per request -->
|
<!-- Inputs and Toggle removed per request -->
|
||||||
<div class="search-actions-header">
|
<div class="search-actions-header">
|
||||||
<button
|
<!-- Limpiar Mapa button removed per request -->
|
||||||
v-if="routeStore.selectedRouteId || markers.length > 0"
|
|
||||||
class="clear-map-btn"
|
|
||||||
@click="clearAllMapData"
|
|
||||||
>
|
|
||||||
<span class="material-icons text-sm">layers_clear</span>
|
|
||||||
Limpiar Mapa
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Results -->
|
<!-- Results -->
|
||||||
@ -1004,13 +786,7 @@ async function calculateWalkingPath(origin: { lat: number, lng: number }, target
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal for details -->
|
<!-- Modal for details removed as per request to eliminate extra markings -->
|
||||||
<BusStopInfoModal
|
|
||||||
:is-open="showBusStopModal"
|
|
||||||
:bus-stop="selectedBusStop"
|
|
||||||
@close="closeBusStopModal"
|
|
||||||
@navigate="drawInternalWalkingRoute"
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
<Transition name="modal-fade">
|
<Transition name="modal-fade">
|
||||||
|
|||||||
@ -4,10 +4,9 @@ import { useScheduleStore } from '@/stores/schedule'
|
|||||||
import { useRouteStore } from '@/stores/route'
|
import { useRouteStore } from '@/stores/route'
|
||||||
import { formatTo12Hour } from '@/utils/timeFormatter'
|
import { formatTo12Hour } from '@/utils/timeFormatter'
|
||||||
import { analyticsService } from '@/services/analyticsService'
|
import { analyticsService } from '@/services/analyticsService'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
|
||||||
const scheduleStore = useScheduleStore()
|
const scheduleStore = useScheduleStore()
|
||||||
const routeStore = useRouteStore()
|
const routeStore = useRouteStore()
|
||||||
|
|
||||||
@ -126,11 +125,8 @@ function pickRoute(id: string, name: string) {
|
|||||||
scheduleStore.loadRouteSchedules(id)
|
scheduleStore.loadRouteSchedules(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
function goToMap() {
|
// goToMap removed per request
|
||||||
if (routeStore.selectedRouteId) {
|
|
||||||
router.push({ path: '/map', query: { routeId: routeStore.selectedRouteId } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Cierre del dropdown al hacer clic fuera
|
// ── Cierre del dropdown al hacer clic fuera
|
||||||
function handleOutsideClick(e: MouseEvent) {
|
function handleOutsideClick(e: MouseEvent) {
|
||||||
@ -256,11 +252,6 @@ onUnmounted(() => {
|
|||||||
<span class="live-dot"></span>
|
<span class="live-dot"></span>
|
||||||
<span>Próximas salidas</span>
|
<span>Próximas salidas</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- Botón ver en mapa -->
|
|
||||||
<button class="map-btn" @click="goToMap">
|
|
||||||
<span class="material-icons">map</span>
|
|
||||||
Ver en mapa
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Chips de filtro día -->
|
<!-- Chips de filtro día -->
|
||||||
|
|||||||
Reference in New Issue
Block a user