diff --git a/frontend/src/App.vue b/frontend/src/App.vue index faca183..c57ee7d 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -17,9 +17,7 @@ const favoritesStore = useFavoritesStore() const isSplashScreen = computed(() => route.name === 'splash') const isAuthScreen = computed(() => { - const name = route.name?.toString().toLowerCase() || '' - const path = route.path.toLowerCase() - return name.includes('auth') || path.includes('/login') || path.includes('/register') + return route.path === '/login' || route.path === '/register' || route.name === 'auth' }) onMounted(() => { diff --git a/frontend/src/components/map/ArrivalBanner.vue b/frontend/src/components/map/ArrivalBanner.vue new file mode 100644 index 0000000..1155b59 --- /dev/null +++ b/frontend/src/components/map/ArrivalBanner.vue @@ -0,0 +1,132 @@ + + + + + diff --git a/frontend/src/components/map/PromoCarousel.vue b/frontend/src/components/map/PromoCarousel.vue new file mode 100644 index 0000000..f40bd2a --- /dev/null +++ b/frontend/src/components/map/PromoCarousel.vue @@ -0,0 +1,268 @@ + + + + + diff --git a/frontend/src/components/map/SearchOverlay.vue b/frontend/src/components/map/SearchOverlay.vue new file mode 100644 index 0000000..733f797 --- /dev/null +++ b/frontend/src/components/map/SearchOverlay.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index f25069d..1f0ac59 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -177,8 +177,14 @@ router.beforeEach(async (to, _from, next) => { return next() } - // Verificar sesión activa - const { data: { session } } = await supabase.auth.getSession() + // Verificar sesión activa con seguridad + let session = null + try { + const { data } = await supabase.auth.getSession() + session = data.session + } catch (e) { + console.error('SIBU | Auth Check Error:', e) + } // Sin sesión en ruta protegida → login if (!session) { diff --git a/frontend/src/stores/busStop.ts b/frontend/src/stores/busStop.ts index 98ed5a3..ee43758 100644 --- a/frontend/src/stores/busStop.ts +++ b/frontend/src/stores/busStop.ts @@ -15,6 +15,7 @@ export const useBusStopStore = defineStore('busStop', () => { const CACHE_TIME = 1000 * 60 * 30; // 30 minutos const now = Date.now(); + if (isLoading.value) return; if (!force && busStops.value.length > 0 && (now - lastFetched.value < CACHE_TIME)) { return } @@ -33,6 +34,7 @@ export const useBusStopStore = defineStore('busStop', () => { } async function loadBusStopById(id: string, force = false) { + if (isLoading.value) return; // Buscar en cache primero if (!force && busStops.value.length > 0) { const cachedStop = busStops.value.find(s => s.id === id); diff --git a/frontend/src/stores/coupon.ts b/frontend/src/stores/coupon.ts index 2a6e9d4..8b1d770 100644 --- a/frontend/src/stores/coupon.ts +++ b/frontend/src/stores/coupon.ts @@ -12,6 +12,7 @@ export const useCouponStore = defineStore('coupon', () => { const filters = ref({}) async function loadCoupons(newFilters?: CouponFilters) { + if (isLoading.value) return; isLoading.value = true error.value = null if (newFilters) { diff --git a/frontend/src/stores/route.ts b/frontend/src/stores/route.ts index 463bc10..9a840bf 100644 --- a/frontend/src/stores/route.ts +++ b/frontend/src/stores/route.ts @@ -22,6 +22,9 @@ export const useRouteStore = defineStore('route', () => { const CACHE_TIME = 1000 * 60 * 15; // 15 minutos const now = Date.now(); + // Guard: Si ya se están cargando rutas, no iniciar otra petición + if (isLoadingRoutes.value) return; + // Si no forzamos, no hay filtros raros, ya tenemos rutas y aún no expira el caché, omitir llamada if (!force && !filters && allRoutes.value.length > 0 && (now - lastFetched.value < CACHE_TIME)) { return @@ -44,6 +47,7 @@ export const useRouteStore = defineStore('route', () => { const CACHE_TIME = 1000 * 60 * 15; // 15 minutos const now = Date.now(); + if (isLoadingStops.value) return; if (!force && stopsCache.value.has(routeId)) { const cacheEntry = stopsCache.value.get(routeId)!; if (now - cacheEntry.fetchedAt < CACHE_TIME) { diff --git a/frontend/src/supabase.ts b/frontend/src/supabase.ts index 9c81344..d5c94d9 100644 --- a/frontend/src/supabase.ts +++ b/frontend/src/supabase.ts @@ -1,7 +1,7 @@ import { createClient } from '@supabase/supabase-js' -export const SUPABASE_URL = 'https://bjgixlugjzsccazdfmph.supabase.co' -export const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJqZ2l4bHVnanpzY2NhemRmbXBoIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzIwNjQyMTAsImV4cCI6MjA4NzY0MDIxMH0.untLQoPi4yUr3cPnxo23wYSlg6xnNK0daKu9UHmFTp8' +const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL +const SUPABASE_ANON_KEY = import.meta.env.VITE_SUPABASE_ANON_KEY // SIBU | Hybrid Storage: Maneja persistencia según la voluntad del usuario const authStorage = { diff --git a/frontend/src/utils/geo.ts b/frontend/src/utils/geo.ts new file mode 100644 index 0000000..8041913 --- /dev/null +++ b/frontend/src/utils/geo.ts @@ -0,0 +1,39 @@ +/** + * Utility functions for geographical calculations + */ + +/** + * Calculates the Haversine distance between two points + */ +export function calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number): number { + const R = 6371e3; // Earth radius in meters + const f1 = lat1 * Math.PI / 180; + const f2 = lat2 * Math.PI / 180; + const df = (lat2 - lat1) * Math.PI / 180; + const dl = (lon2 - lon1) * Math.PI / 180; + + const a = Math.sin(df / 2) * Math.sin(df / 2) + + Math.cos(f1) * Math.cos(f2) * + Math.sin(dl / 2) * Math.sin(dl / 2); + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + + return R * c; // Distance in meters +} + +/** + * Formats distance in meters to a human-readable string + */ +export function formatDistance(meters: number): string { + if (meters < 1000) { + return `${Math.round(meters)}m`; + } + return `${(meters / 1000).toFixed(1)}km`; +} + +/** + * Estimates walking time in minutes + */ +export function estimateWalkingTime(meters: number): number { + const walkingSpeed = 1.4; // 1.4 m/s approx + return Math.ceil(meters / walkingSpeed / 60); +} diff --git a/frontend/src/views/AdminDashboard.vue b/frontend/src/views/AdminDashboard.vue deleted file mode 100644 index 0896d08..0000000 --- a/frontend/src/views/AdminDashboard.vue +++ /dev/null @@ -1,521 +0,0 @@ - - - - - diff --git a/frontend/src/views/DiscoverView.vue b/frontend/src/views/DiscoverView.vue index 0fc0986..900b75c 100644 --- a/frontend/src/views/DiscoverView.vue +++ b/frontend/src/views/DiscoverView.vue @@ -562,7 +562,7 @@ function resetFilters() { border-radius: 1.125rem; overflow: hidden; cursor: pointer; - aspect-ratio: 1/1; + aspect-ratio: 16/9; background: var(--bg-secondary); transition: transform 0.2s; } @@ -660,7 +660,7 @@ function resetFilters() { .biz-img-wrap { position: relative; - aspect-ratio: 16/10; + aspect-ratio: 4/3; overflow: hidden; } @@ -710,9 +710,9 @@ function resetFilters() { align-items: center; gap: 0.2rem; margin: 0; - font-size: 0.75rem; + font-size: 0.7rem; color: var(--text-secondary); - font-weight: 600; + font-weight: 500; } .biz-area-icon { font-size: 0.875rem; } diff --git a/frontend/src/views/MapView.vue b/frontend/src/views/MapView.vue index 5f5d6b4..f882bba 100644 --- a/frontend/src/views/MapView.vue +++ b/frontend/src/views/MapView.vue @@ -1,10 +1,9 @@