perf: complete performance audit optimizations
This commit is contained in:
@ -5,14 +5,14 @@ import type { BusStop, Route } from '@/types'
|
||||
export const busStopsService = {
|
||||
/** Get all bus stops */
|
||||
async getAllBusStops(): Promise<BusStop[]> {
|
||||
const { data, error } = await supabase.from('bus_stops').select('*')
|
||||
const { data, error } = await supabase.from('bus_stops').select('id, name, latitude, longitude, city, address, parent_id, side, stop_type, has_shelter, has_seating, is_accessible, created_at, updated_at')
|
||||
if (error) throw new Error(error.message)
|
||||
return data as BusStop[]
|
||||
},
|
||||
|
||||
/** Get a single bus stop by ID */
|
||||
async getBusStopById(id: string): Promise<BusStop> {
|
||||
const { data, error } = await supabase.from('bus_stops').select('*').eq('id', id).single()
|
||||
const { data, error } = await supabase.from('bus_stops').select('id, name, latitude, longitude, city, address, parent_id, side, stop_type, has_shelter, has_seating, is_accessible, created_at, updated_at').eq('id', id).single()
|
||||
if (error) throw new Error(error.message)
|
||||
return data as BusStop
|
||||
},
|
||||
|
||||
@ -18,14 +18,14 @@ export const businessService = {
|
||||
|
||||
/** Get all businesses */
|
||||
async getAllBusinesses(): Promise<Business[]> {
|
||||
const { data, error } = await supabase.from('businesses').select('*')
|
||||
const { data, error } = await supabase.from('businesses').select('id, name, address, phone, image_url, social_media, category, latitude, longitude, area, updated_at')
|
||||
if (error) throw new Error(error.message)
|
||||
return data as Business[]
|
||||
},
|
||||
|
||||
/** Get a single business by ID */
|
||||
async getBusiness(id: string): Promise<Business> {
|
||||
const { data, error } = await supabase.from('businesses').select('*').eq('id', id).single()
|
||||
const { data, error } = await supabase.from('businesses').select('id, name, address, phone, image_url, social_media, category, latitude, longitude, area, updated_at').eq('id', id).single()
|
||||
if (error) throw new Error(error.message)
|
||||
return data as Business
|
||||
},
|
||||
|
||||
@ -5,7 +5,7 @@ import type { Route, BusStop } from '@/types'
|
||||
export const routesService = {
|
||||
/** Get all routes with optional filtering */
|
||||
async getAllRoutes(filters?: { originCity?: string, destinationCity?: string }): Promise<Route[]> {
|
||||
let query = supabase.from('routes').select('*')
|
||||
let query = supabase.from('routes').select('id, name, description, color, direction, origin_city, destination_city, distance_km, estimated_duration_minutes, average_speed_kmh, status, created_at, updated_at')
|
||||
|
||||
if (filters?.originCity) {
|
||||
query = query.eq('origin_city', filters.originCity)
|
||||
@ -21,7 +21,7 @@ export const routesService = {
|
||||
|
||||
/** Get a single route by ID */
|
||||
async getRouteById(id: string): Promise<Route> {
|
||||
const { data, error } = await supabase.from('routes').select('*').eq('id', id).single()
|
||||
const { data, error } = await supabase.from('routes').select('id, name, description, color, direction, origin_city, destination_city, distance_km, estimated_duration_minutes, average_speed_kmh, status, created_at, updated_at').eq('id', id).single()
|
||||
if (error) throw new Error(error.message)
|
||||
return data as Route
|
||||
},
|
||||
|
||||
@ -32,7 +32,16 @@ export const useBusStopStore = defineStore('busStop', () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadBusStopById(id: string) {
|
||||
async function loadBusStopById(id: string, force = false) {
|
||||
// Buscar en cache primero
|
||||
if (!force && busStops.value.length > 0) {
|
||||
const cachedStop = busStops.value.find(s => s.id === id);
|
||||
if (cachedStop) {
|
||||
selectedStop.value = cachedStop;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
|
||||
@ -12,7 +12,8 @@ export const useRouteStore = defineStore('route', () => {
|
||||
const isLoadingRoutes = ref(false)
|
||||
const isLoadingStops = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const lastFetched = ref<number>(0) // ⚡ CACHÉ ESTÁTICO
|
||||
const lastFetched = ref<number>(0) // ⚡ CACHÉ ESTÁTICO RUTAS
|
||||
const stopsCache = ref<Map<string, { fetchedAt: number, stops: BusStop[] }>>(new Map()) // ⚡ CACHÉ ESTÁTICO PARADAS
|
||||
|
||||
const hasSelectedRoute = computed(() => selectedRouteId.value !== null && selectedRouteName.value !== null)
|
||||
|
||||
@ -38,11 +39,24 @@ export const useRouteStore = defineStore('route', () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRouteStops(routeId: string) {
|
||||
async function loadRouteStops(routeId: string, force = false) {
|
||||
const CACHE_TIME = 1000 * 60 * 15; // 15 minutos
|
||||
const now = Date.now();
|
||||
|
||||
if (!force && stopsCache.value.has(routeId)) {
|
||||
const cacheEntry = stopsCache.value.get(routeId)!;
|
||||
if (now - cacheEntry.fetchedAt < CACHE_TIME) {
|
||||
selectedRouteStops.value = cacheEntry.stops;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
isLoadingStops.value = true
|
||||
error.value = null
|
||||
try {
|
||||
selectedRouteStops.value = await routesService.getRouteStops(routeId)
|
||||
const stops = await routesService.getRouteStops(routeId)
|
||||
selectedRouteStops.value = stops
|
||||
stopsCache.value.set(routeId, { fetchedAt: now, stops })
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to load route stops'
|
||||
console.error('Error loading route stops:', e)
|
||||
|
||||
@ -51,7 +51,8 @@ import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { busStopsService } from '@/services/busStopsService'
|
||||
import type { BusStop } from '@/types'
|
||||
import BusStopEditor from '@/components/BusStopEditor.vue'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
const BusStopEditor = defineAsyncComponent(() => import('@/components/BusStopEditor.vue'))
|
||||
|
||||
const router = useRouter()
|
||||
const stops = ref<BusStop[]>([])
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import LoginForm from '@/components/auth/LoginForm.vue'
|
||||
import RegisterForm from '@/components/auth/RegisterForm.vue'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
const LoginForm = defineAsyncComponent(() => import('@/components/auth/LoginForm.vue'))
|
||||
const RegisterForm = defineAsyncComponent(() => import('@/components/auth/RegisterForm.vue'))
|
||||
|
||||
const isLogin = ref(true)
|
||||
const toggleAuth = () => { isLogin.value = !isLogin.value }
|
||||
|
||||
@ -122,11 +122,12 @@ function getCategoryIcon(category?: string | null) {
|
||||
<div
|
||||
v-for="coupon in filteredCoupons"
|
||||
:key="coupon.id"
|
||||
v-memo="[coupon.id]"
|
||||
class="offer-card-new"
|
||||
@click="openCoupon(coupon)"
|
||||
>
|
||||
<div class="offer-image-wrapper">
|
||||
<img :src="getImageUrl(coupon.image_url)" :alt="coupon.title" class="offer-img">
|
||||
<img :src="getImageUrl(coupon.image_url)" :alt="coupon.title" loading="lazy" decoding="async" class="offer-img">
|
||||
<div class="status-badge" :class="{ 'tmr': coupon.title.toLowerCase().includes('mañana') || (coupon.description?.toLowerCase().includes('mañana') ?? false) }">
|
||||
<span class="material-icons">schedule</span>
|
||||
{{ coupon.title.toLowerCase().includes('mañana') ? t('coupons.tomorrow') : t('coupons.active') }}
|
||||
|
||||
@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useTaxiStore } from '@/stores/taxi'
|
||||
import { useShuttleStore } from '@/stores/shuttle'
|
||||
import { analyticsService } from '@/services/analyticsService'
|
||||
import type { Taxi, Shuttle } from '@/types'
|
||||
import type { Taxi } from '@/types'
|
||||
import FavoriteButton from '@/components/FavoriteButton.vue'
|
||||
import { getImageUrl } from '@/utils/imageUrl'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user