feat: limit to 2 routes and add slide animations to ETA card and banner

This commit is contained in:
2026-02-28 12:00:38 -05:00
parent 621da9e4c3
commit 25008054b3
2 changed files with 64 additions and 27 deletions

View File

@ -1,4 +1,5 @@
<template> <template>
<Transition name="sheet-ui">
<div v-if="isOpen" class="fixed inset-x-0 bottom-0 z-[9999] sm:max-w-md sm:mx-auto"> <div v-if="isOpen" class="fixed inset-x-0 bottom-0 z-[9999] sm:max-w-md sm:mx-auto">
<!-- Overlay transparente oscuro en fondo --> <!-- Overlay transparente oscuro en fondo -->
<div class="fixed inset-0 bg-black/40 transition-opacity" @click="closeCard"></div> <div class="fixed inset-0 bg-black/40 transition-opacity" @click="closeCard"></div>
@ -60,10 +61,10 @@
<p class="text-sm text-gray-500 dark:text-gray-400">No hay buses en ruta para hoy en esta línea.</p> <p class="text-sm text-gray-500 dark:text-gray-400">No hay buses en ruta para hoy en esta línea.</p>
</div> </div>
<!-- Lista de llegadas (Max 3) --> <!-- Lista de llegadas (Max 2) -->
<template v-else> <template v-else>
<div <div
v-for="(bus, index) in buses" v-for="(bus, index) in buses.slice(0, 2)"
:key="bus.horario_id" :key="bus.horario_id"
class="group bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-2xl p-4 flex items-center justify-between" class="group bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-2xl p-4 flex items-center justify-between"
:class="{ 'ring-2 ring-green-500/50 dark:ring-green-400/50 bg-green-50/30 dark:bg-green-900/10': bus.estado === 'en_camino' }" :class="{ 'ring-2 ring-green-500/50 dark:ring-green-400/50 bg-green-50/30 dark:bg-green-900/10': bus.estado === 'en_camino' }"
@ -134,6 +135,7 @@
</div> </div>
</div> </div>
</div> </div>
</Transition>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -214,11 +216,26 @@ onUnmounted(() => {
</script> </script>
<style scoped> <style scoped>
/* Transición extra refinada a lo material you */ /* Transición de entrada/salida (Slide up de abajo hacia arriba) */
.translate-y-full { .sheet-ui-enter-active,
transform: translateY(100%); .sheet-ui-leave-active {
transition: all 0.4s cubic-bezier(0.32, 0.72, 0, 1);
} }
.translate-y-0 {
transform: translateY(0%); .sheet-ui-enter-from,
.sheet-ui-leave-to {
transform: translateY(100%);
opacity: 0;
}
.sheet-ui-enter-to,
.sheet-ui-leave-from {
transform: translateY(0);
opacity: 1;
}
/* Transición extra para el overlay */
.fixed.inset-0 {
transition: opacity 0.4s ease;
} }
</style> </style>

View File

@ -58,7 +58,6 @@ const alturaNavbar = ref(64);
// Search state // Search state
const stopSearchQuery = ref(""); const stopSearchQuery = ref("");
const destinationQuery = ref(""); const destinationQuery = ref("");
const originQuery = ref("Mi ubicación");
const filteredSearchResults = ref<BusStop[]>([]); const filteredSearchResults = ref<BusStop[]>([]);
const showSearchDropdown = ref(false); const showSearchDropdown = ref(false);
const showUberSearch = ref(false); const showUberSearch = ref(false);
@ -87,15 +86,7 @@ watch([stopSearchQuery, destinationQuery], ([stopQuery, destQuery]) => {
} }
}); });
function selectStopFromSearch(stop: BusStop) { // selectStopFromSearch removed as it was unused
setCenter(stop.latitude, stop.longitude);
setZoom(17);
handleBusStopClick(stop);
stopSearchQuery.value = "";
destinationQuery.value = "";
showSearchDropdown.value = false;
showUberSearch.value = false;
}
function openUberSearch() { function openUberSearch() {
showPromos.value = false; // Cerramos ofertas para evitar solapamiento showPromos.value = false; // Cerramos ofertas para evitar solapamiento
@ -163,6 +154,14 @@ 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);
function animateAndReload() {
isBannerClosing.value = true;
setTimeout(() => {
reloadPage();
}, 450); // Mismo tiempo que la transición
}
// Close dropdown when clicking outside // Close dropdown when clicking outside
function handleClickOutside(event: MouseEvent) { function handleClickOutside(event: MouseEvent) {
@ -849,8 +848,9 @@ async function calculateWalkingPath(origin: { lat: number, lng: number }, target
</div> </div>
<!-- Nuevo Banner de Parada Cercana Alineado (Redimensionado y con ETA) --> <!-- Nuevo Banner de Parada Cercana Alineado (Redimensionado y con ETA) -->
<Transition name="banner-slide">
<div <div
v-if="paradaCercana && routeStore.selectedRouteId && !showETACard" v-if="paradaCercana && routeStore.selectedRouteId && !showETACard && !isBannerClosing"
class="best-stop-banner-compact" class="best-stop-banner-compact"
> >
<div class="banner-icon-bg"> <div class="banner-icon-bg">
@ -867,7 +867,7 @@ async function calculateWalkingPath(origin: { lat: number, lng: number }, target
<div class="eta-loader"></div> <div class="eta-loader"></div>
</template> </template>
<template v-else-if="busesActivos.length > 0"> <template v-else-if="busesActivos.length > 0">
<span class="eta-value">{{ busesActivos[0].etaMinutos > 0 ? busesActivos[0].etaMinutos : '0' }}</span> <span class="eta-value">{{ (busesActivos[0]?.etaMinutos ?? 0) > 0 ? busesActivos[0]?.etaMinutos : '0' }}</span>
<span class="eta-unit">min</span> <span class="eta-unit">min</span>
</template> </template>
<template v-else> <template v-else>
@ -875,10 +875,11 @@ async function calculateWalkingPath(origin: { lat: number, lng: number }, target
</template> </template>
</div> </div>
<button @click.stop="reloadPage" class="ml-2 p-1 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"> <button @click.stop="animateAndReload" class="ml-2 p-1 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors">
<span class="material-icons text-[18px] text-gray-400 hover:text-red-500">close</span> <span class="material-icons text-[18px] text-gray-400 hover:text-red-500">close</span>
</button> </button>
</div> </div>
</Transition>
</div> </div>
@ -1566,6 +1567,25 @@ html.light-theme .uber-search-trigger-compact {
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
max-width: none; max-width: none;
pointer-events: auto; pointer-events: auto;
z-index: 1200;
}
/* Animaciones del Banner (Slide de abajo hacia arriba per request) */
.banner-slide-enter-active,
.banner-slide-leave-active {
transition: all 0.45s cubic-bezier(0.32, 0.72, 0, 1);
}
.banner-slide-enter-from,
.banner-slide-leave-to {
transform: translateY(20px);
opacity: 0;
}
.banner-slide-enter-to,
.banner-slide-leave-from {
transform: translateY(0);
opacity: 1;
} }
.banner-icon-bg { .banner-icon-bg {