perf: optimization phases 3-5

This commit is contained in:
2026-02-26 12:50:12 -05:00
parent 7b3141e5e9
commit 2dd3384882
6 changed files with 41 additions and 4 deletions

View File

@ -7,6 +7,16 @@ const config: CapacitorConfig = {
server: { server: {
androidScheme: 'http', androidScheme: 'http',
cleartext: true cleartext: true
},
plugins: {
SplashScreen: {
launchShowDuration: 2000,
launchAutoHide: true,
backgroundColor: "#101820",
androidSplashResourceName: "splash",
androidScaleType: "CENTER_CROP",
showSpinner: false,
}
} }
}; };

View File

@ -9,9 +9,13 @@ export const useBusStopStore = defineStore('busStop', () => {
const busStops = ref<BusStop[]>([]) const busStops = ref<BusStop[]>([])
const isLoading = ref(false) const isLoading = ref(false)
const error = ref<string | null>(null) const error = ref<string | null>(null)
const lastFetched = ref<number>(0)
async function loadBusStops(force = false) { async function loadBusStops(force = false) {
if (!force && busStops.value.length > 0) { const CACHE_TIME = 1000 * 60 * 30; // 30 minutos
const now = Date.now();
if (!force && busStops.value.length > 0 && (now - lastFetched.value < CACHE_TIME)) {
return return
} }
@ -19,6 +23,7 @@ export const useBusStopStore = defineStore('busStop', () => {
error.value = null error.value = null
try { try {
busStops.value = await busStopsService.getAllBusStops() busStops.value = await busStopsService.getAllBusStops()
lastFetched.value = now;
} catch (e) { } catch (e) {
error.value = e instanceof Error ? e.message : 'Failed to load bus stops' error.value = e instanceof Error ? e.message : 'Failed to load bus stops'
console.error('Error loading bus stops:', e) console.error('Error loading bus stops:', e)

View File

@ -12,11 +12,16 @@ export const useRouteStore = defineStore('route', () => {
const isLoadingRoutes = ref(false) const isLoadingRoutes = ref(false)
const isLoadingStops = ref(false) const isLoadingStops = ref(false)
const error = ref<string | null>(null) const error = ref<string | null>(null)
const lastFetched = ref<number>(0) // ⚡ CACHÉ ESTÁTICO
const hasSelectedRoute = computed(() => selectedRouteId.value !== null && selectedRouteName.value !== null) const hasSelectedRoute = computed(() => selectedRouteId.value !== null && selectedRouteName.value !== null)
async function loadRoutes(filters?: { originCity?: string, destinationCity?: string }, force = false) { async function loadRoutes(filters?: { originCity?: string, destinationCity?: string }, force = false) {
if (!force && !filters && allRoutes.value.length > 0) { const CACHE_TIME = 1000 * 60 * 15; // 15 minutos
const now = Date.now();
// 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 return
} }
@ -24,6 +29,7 @@ export const useRouteStore = defineStore('route', () => {
error.value = null error.value = null
try { try {
allRoutes.value = await routesService.getAllRoutes(filters) allRoutes.value = await routesService.getAllRoutes(filters)
if (!filters) lastFetched.value = now; // Solo actualizar timer si es un request general limio
} catch (e) { } catch (e) {
error.value = e instanceof Error ? e.message : 'Failed to load routes' error.value = e instanceof Error ? e.message : 'Failed to load routes'
console.error('Error loading routes:', e) console.error('Error loading routes:', e)

View File

@ -200,6 +200,7 @@ function resetFilters() {
<div <div
v-for="biz in filteredBusinesses" v-for="biz in filteredBusinesses"
:key="biz.id" :key="biz.id"
v-memo="[biz.id]"
class="biz-card" class="biz-card"
@click="handleExplore(biz)" @click="handleExplore(biz)"
> >
@ -207,6 +208,8 @@ function resetFilters() {
<img <img
:src="getImageUrl(biz.image_url, 'business')" :src="getImageUrl(biz.image_url, 'business')"
:alt="biz.name" :alt="biz.name"
loading="lazy"
decoding="async"
class="biz-img" class="biz-img"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')" @error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')"
/> />
@ -271,12 +274,15 @@ function resetFilters() {
<div <div
v-for="biz in featuredBusinesses" v-for="biz in featuredBusinesses"
:key="biz.id" :key="biz.id"
v-memo="[biz.id]"
class="featured-card" class="featured-card"
@click="handleExplore(biz)" @click="handleExplore(biz)"
> >
<img <img
:src="getImageUrl(biz.image_url, 'business')" :src="getImageUrl(biz.image_url, 'business')"
:alt="biz.name" :alt="biz.name"
loading="lazy"
decoding="async"
class="featured-img" class="featured-img"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')" @error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')"
/> />
@ -306,6 +312,7 @@ function resetFilters() {
<div <div
v-for="biz in gridBusinesses" v-for="biz in gridBusinesses"
:key="biz.id" :key="biz.id"
v-memo="[biz.id]"
class="biz-card" class="biz-card"
@click="handleExplore(biz)" @click="handleExplore(biz)"
> >
@ -313,6 +320,8 @@ function resetFilters() {
<img <img
:src="getImageUrl(biz.image_url, 'business')" :src="getImageUrl(biz.image_url, 'business')"
:alt="biz.name" :alt="biz.name"
loading="lazy"
decoding="async"
class="biz-img" class="biz-img"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')" @error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')"
/> />

View File

@ -174,6 +174,7 @@ const getStatusClass = (status: string) => {
<div <div
v-for="route in routeStore.allRoutes" v-for="route in routeStore.allRoutes"
:key="route.id" :key="route.id"
v-memo="[route.id]"
class="bg-slate-50 dark:bg-card-dark p-5 rounded-[2rem] shadow-sm border border-slate-200 dark:border-white/5 flex flex-col gap-4 active:scale-[0.98] transition-all cursor-pointer" class="bg-slate-50 dark:bg-card-dark p-5 rounded-[2rem] shadow-sm border border-slate-200 dark:border-white/5 flex flex-col gap-4 active:scale-[0.98] transition-all cursor-pointer"
@click="goToSchedules(route)" @click="goToSchedules(route)"
> >
@ -219,12 +220,13 @@ const getStatusClass = (status: string) => {
<div <div
v-for="taxi in taxiStore.taxis" v-for="taxi in taxiStore.taxis"
:key="taxi.id" :key="taxi.id"
v-memo="[taxi.id]"
class="bg-slate-50 dark:bg-card-dark p-5 rounded-[2rem] shadow-sm border border-slate-200 dark:border-white/5 flex flex-col gap-4 active:scale-[0.98] transition-all" class="bg-slate-50 dark:bg-card-dark p-5 rounded-[2rem] shadow-sm border border-slate-200 dark:border-white/5 flex flex-col gap-4 active:scale-[0.98] transition-all"
> >
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="size-14 rounded-2xl bg-primary/10 flex items-center justify-center text-primary overflow-hidden"> <div class="size-14 rounded-2xl bg-primary/10 flex items-center justify-center text-primary overflow-hidden">
<img v-if="taxi.image_url" :src="taxi.image_url" class="w-full h-full object-cover"> <img v-if="taxi.image_url" :src="taxi.image_url" loading="lazy" decoding="async" class="w-full h-full object-cover">
<span v-else class="material-icons text-[32px]">local_taxi</span> <span v-else class="material-icons text-[32px]">local_taxi</span>
</div> </div>
<div> <div>

View File

@ -187,11 +187,13 @@ function getShiftLabel(shift: string) {
</div> </div>
<div v-else class="taxis-grid"> <div v-else class="taxis-grid">
<div v-for="taxi in filteredTaxis" :key="taxi.id" class="taxi-card-new"> <div v-for="taxi in filteredTaxis" :key="taxi.id" v-memo="[taxi.id]" class="taxi-card-new">
<div class="card-top"> <div class="card-top">
<div class="driver-avatar"> <div class="driver-avatar">
<img <img
:src="getImageUrl(taxi.image_url, 'taxi')" :src="getImageUrl(taxi.image_url, 'taxi')"
loading="lazy"
decoding="async"
alt="Driver" alt="Driver"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'taxi')" @error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'taxi')"
> >
@ -284,6 +286,7 @@ function getShiftLabel(shift: string) {
<div <div
v-for="shuttle in filteredShuttles" v-for="shuttle in filteredShuttles"
:key="shuttle.id" :key="shuttle.id"
v-memo="[shuttle.id]"
:ref="el => setShuttleRef(el, shuttle.id)" :ref="el => setShuttleRef(el, shuttle.id)"
class="shuttle-card" class="shuttle-card"
:class="{ expanded: expandedShuttleId === shuttle.id }" :class="{ expanded: expandedShuttleId === shuttle.id }"
@ -296,6 +299,8 @@ function getShiftLabel(shift: string) {
> >
<img <img
:src="getImageUrl(shuttle.image_url, 'shuttle')" :src="getImageUrl(shuttle.image_url, 'shuttle')"
loading="lazy"
decoding="async"
class="shuttle-card-bg" class="shuttle-card-bg"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'shuttle')" @error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'shuttle')"
/> />