From 621da9e4c3fe94d37a291f3d3b9b35bd1cfd23dc Mon Sep 17 00:00:00 2001 From: Hanzo_dev <2002samudiojohan@gmail.com> Date: Sat, 28 Feb 2026 10:39:20 -0500 Subject: [PATCH] Refactor: Map UI improvements, ETA metrics, Schedule fixes, and Transport Detail styling --- frontend/src/views/MapView.vue | 113 ++++++++++++++++-- frontend/src/views/SchedulesView.vue | 44 ++++--- frontend/src/views/TransporteLayout.vue | 9 +- .../src/views/transporte/ShuttleDetalle.vue | 56 ++++----- 4 files changed, 163 insertions(+), 59 deletions(-) diff --git a/frontend/src/views/MapView.vue b/frontend/src/views/MapView.vue index a784168..7775d11 100644 --- a/frontend/src/views/MapView.vue +++ b/frontend/src/views/MapView.vue @@ -98,6 +98,7 @@ function selectStopFromSearch(stop: BusStop) { } function openUberSearch() { + showPromos.value = false; // Cerramos ofertas para evitar solapamiento showUberSearch.value = true; showRoutesToggle.value = true; // Forzar que al abrir estemos en modo rutas } @@ -180,6 +181,10 @@ function closeBusStopModal() { selectedBusStop.value = null; } +function reloadPage() { + window.location.reload(); +} + function handlePromoClick(promo: any) { selectedPromo.value = promo; showPromoModal.value = true; @@ -503,7 +508,9 @@ async function updateActiveUnits() { if (!isLoaded.value) return; try { - // No-op for now. Backend is purely Supabase now. + if (routeStore.selectedRouteId && paradaCercana.value) { + await calcularETA(routeStore.selectedRouteId, paradaCercana.value as BusStop); + } } catch (e) { console.error('Failed to update active units', e); } @@ -841,21 +848,35 @@ async function calculateWalkingPath(origin: { lat: number, lng: number }, target ver rutas - +
- directions_bus -
- Parada cercana - {{ paradaCercana?.name }} + -
- {{ (distanciaMetros && distanciaMetros < 1000) ? Math.round(distanciaMetros) + 'm' : (distanciaMetros ? (distanciaMetros / 1000).toFixed(1) + 'km' : '') }} + +
+ Tiempo de llegada + {{ paradaCercana?.name }}
-
@@ -1531,6 +1552,76 @@ html.light-theme .uber-search-trigger-compact { max-width: none; } +.best-stop-banner-compact { + flex: 1; + background: var(--header-bg); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + height: 40px; /* Más compacto (de 44px a 40px) */ + border-radius: 10px; + display: flex; + align-items: center; + padding: 0 10px; + box-shadow: 0 4px 12px rgba(0,0,0,0.15); + border: 1px solid var(--border-color); + max-width: none; + pointer-events: auto; +} + +.banner-icon-bg { + background: #EAB308; /* yellow-500 */ + width: 24px; + height: 24px; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.trigger-text-compact { + color: var(--text-primary); + font-size: 0.95rem; + font-weight: 700; + letter-spacing: -0.01em; +} + +.eta-badge { + background: rgba(234, 179, 8, 0.1); /* yellow-500 with opacity */ + color: #EAB308; + padding: 2px 8px; + border-radius: 6px; + display: flex; + align-items: baseline; + gap: 2px; + font-weight: 800; + margin-left: 8px; + border: 1px solid rgba(234, 179, 8, 0.2); +} + +.eta-value { + font-size: 1.1rem; + line-height: 1; +} + +.eta-unit { + font-size: 0.7rem; + text-transform: uppercase; +} + +.eta-loader { + width: 14px; + height: 14px; + border: 2px solid #EAB308; + border-top-color: transparent; + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + .uber-search-panel { position: fixed; top: 70px; /* Debajo del header superior */ diff --git a/frontend/src/views/SchedulesView.vue b/frontend/src/views/SchedulesView.vue index e85d14c..ddb74bb 100644 --- a/frontend/src/views/SchedulesView.vue +++ b/frontend/src/views/SchedulesView.vue @@ -56,7 +56,7 @@ function getScheduleDay(schedule: any): 'today' | 'tomorrow' | 'other' { // Comparar con el tipo del horario // Nota: Si el horario es 'todos', cuenta para hoy y mañana (pero priorizamos hoy si pides hoy) - const type = schedule.schedule_type || 'todos' + const type = (schedule.schedule_type as string) || 'todos' const isToday = type === todayType || type === 'todos' const isTomorrow = type === tomorrowType || type === 'todos' @@ -81,27 +81,35 @@ function getDayLabel(schedule: any): string { // ── Filtrado de horarios const filteredSchedules = computed(() => { const now = new Date() + const tomorrow = new Date(now) + tomorrow.setDate(now.getDate() + 1) + const hhmmAhora = now.getHours() * 100 + now.getMinutes() return scheduleStore.schedules.filter(s => { - const d = getScheduleDay(s) - const [hStr, mStr] = (s.departure_time || '00:00').split(':') - const h = parseInt(hStr || '0') - const m = parseInt(mStr || '0') - const hhmmSched = h * 100 + m - const isPassed = hhmmSched < hhmmAhora - 2 // margen de 2 min + const type = (s.schedule_type as string) || 'todos' + const todayType = (now.getDay() === 0 || now.getDay() === 6) ? 'weekend' : 'weekday' + const tomorrowType = (tomorrow.getDay() === 0 || tomorrow.getDay() === 6) ? 'weekend' : 'weekday' + + const isActuallyToday = type === todayType || type === 'todos' + const isActuallyTomorrow = type === tomorrowType || type === 'todos' // Filtro Hoy: Es hoy Y no ha pasado (o es de los que dice salir en este rango) if (dayFilter.value === 'today') { - return d === 'today' && !isPassed + const [hStr, mStr] = (s.departure_time || '00:00').split(':') + const h = parseInt(hStr || '0') + const m = parseInt(mStr || '0') + const hhmmSched = h * 100 + m + const isPassed = hhmmSched < hhmmAhora - 2 // margen de 2 min + return isActuallyToday && !isPassed } - // Filtro Mañana: Es mañana + // Filtro Mañana: Es mañana (sin importar si pasó la hora hoy) if (dayFilter.value === 'tomorrow') { - return d === 'tomorrow' + return isActuallyTomorrow } - // Filtro Todos: Mostrar todo sin importar si pasó o es otro día + // Filtro Todos: Mostrar todo return true }) }) @@ -281,7 +289,7 @@ onUnmounted(() => { v-for="(schedule, i) in filteredSchedules" :key="schedule.id" class="schedule-card" - :class="`schedule-card--${getBusStatus(schedule.departure_time)}`" + :class="dayFilter === 'today' ? `schedule-card--${getBusStatus(schedule.departure_time)}` : 'schedule-card--upcoming'" >
@@ -295,8 +303,8 @@ onUnmounted(() => {
- - {{ getDayLabel(schedule) }} + + {{ dayFilter === 'tomorrow' ? 'Mañana' : (dayFilter === 'all' ? getDayLabel(schedule) : getDayLabel(schedule)) }} SALIENDO
@@ -308,11 +316,11 @@ onUnmounted(() => {
-
+
- {{ getBusStatus(schedule.departure_time) === 'departing' ? 'directions_run' : - getBusStatus(schedule.departure_time) === 'ontime' ? 'check_circle' : - getBusStatus(schedule.departure_time) === 'passed' ? 'history' : 'access_time' }} + {{ (dayFilter === 'today' && getBusStatus(schedule.departure_time) === 'departing') ? 'directions_run' : + (dayFilter === 'today' && getBusStatus(schedule.departure_time) === 'ontime') ? 'check_circle' : + (dayFilter === 'today' && getBusStatus(schedule.departure_time) === 'passed') ? 'history' : 'access_time' }}
diff --git a/frontend/src/views/TransporteLayout.vue b/frontend/src/views/TransporteLayout.vue index f15faa7..58ea4b2 100644 --- a/frontend/src/views/TransporteLayout.vue +++ b/frontend/src/views/TransporteLayout.vue @@ -1,10 +1,15 @@