fix: critical bug fixes - routes UUID, image paths, favorites loading, bottom nav debounce

This commit is contained in:
2026-02-25 16:29:13 -05:00
parent c449083171
commit fd95df461b
14 changed files with 379 additions and 116 deletions

View File

@ -211,7 +211,8 @@ async function saveShuttle() {
<div class="preview-container">
<!-- PREVIEW CARD -->
<div class="shuttle-card-preview" :class="{ expanded: true }" :style="{ backgroundImage: `url(${previewImageUrl})` }">
<div class="shuttle-card-preview" :class="{ expanded: true }">
<img :src="previewImageUrl" class="shuttle-card-bg" @error="(e) => (e.target as HTMLImageElement).src = 'https://images.unsplash.com/photo-1449034446853-66c86144b0ad?q=80&w=2070&auto=format&fit=crop'" />
<div class="shuttle-main-info">
<div class="shuttle-header-mini">
<div class="company-badge">
@ -516,12 +517,11 @@ async function saveShuttle() {
border-radius: 20px;
overflow: hidden;
border: 1px solid rgba(255,255,255,0.12);
background-size: cover;
background-position: center;
position: relative;
display: flex;
flex-direction: column;
box-shadow: 0 30px 60px rgba(0,0,0,0.5);
background: #101820;
}
.shuttle-card-preview::before {
@ -534,7 +534,7 @@ async function saveShuttle() {
rgba(0, 0, 0, 0.65) 55%,
rgba(0, 0, 0, 0.30) 100%
);
z-index: 0;
z-index: 1;
}
.shuttle-card-preview.expanded {
@ -553,7 +553,7 @@ async function saveShuttle() {
.shuttle-main-info,
.shuttle-details {
position: relative;
z-index: 1;
z-index: 2;
padding: 18px 20px;
}

View File

@ -1,11 +1,11 @@
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { businessService } from '@/services/businessService'
import { API_URL } from '@/services/apiClient'
import type { Business } from '@/types'
import { useRouter } from 'vue-router'
import FavoriteButton from '@/components/FavoriteButton.vue'
import { analyticsService } from '@/services/analyticsService'
import { getImageUrl } from '@/utils/imageUrl'
const router = useRouter()
const businesses = ref<Business[]>([])
@ -98,11 +98,6 @@ function handleExplore(biz: Business) {
router.push('/business/' + biz.id)
}
function getImageUrl(path?: string | null) {
if (!path) return `https://ui-avatars.com/api/?name=Negocio&background=fee715&color=101820&size=200&bold=true`
if (path.startsWith('http')) return path
return `${API_URL}${path.startsWith('/') ? '' : '/'}${path}`
}
function resetFilters() {
selectedCategory.value = 'Todas'
@ -209,7 +204,12 @@ function resetFilters() {
@click="handleExplore(biz)"
>
<div class="biz-img-wrap">
<img :src="getImageUrl(biz.image_url)" :alt="biz.name" class="biz-img" />
<img
:src="getImageUrl(biz.image_url, 'business')"
:alt="biz.name"
class="biz-img"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')"
/>
<div class="biz-fav">
<FavoriteButton item-type="business" :item-id="biz.id" :item-name="biz.name" :item-image="biz.image_url || undefined" />
</div>
@ -274,7 +274,12 @@ function resetFilters() {
class="featured-card"
@click="handleExplore(biz)"
>
<img :src="getImageUrl(biz.image_url)" :alt="biz.name" class="featured-img" />
<img
:src="getImageUrl(biz.image_url, 'business')"
:alt="biz.name"
class="featured-img"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')"
/>
<div class="featured-gradient"></div>
<div class="featured-fav">
<FavoriteButton item-type="business" :item-id="biz.id" :item-name="biz.name" :item-image="biz.image_url || undefined" />
@ -305,7 +310,12 @@ function resetFilters() {
@click="handleExplore(biz)"
>
<div class="biz-img-wrap">
<img :src="getImageUrl(biz.image_url)" :alt="biz.name" class="biz-img" />
<img
:src="getImageUrl(biz.image_url, 'business')"
:alt="biz.name"
class="biz-img"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')"
/>
<div class="biz-fav">
<FavoriteButton item-type="business" :item-id="biz.id" :item-name="biz.name" :item-image="biz.image_url || undefined" />
</div>

View File

@ -7,9 +7,9 @@ import { useMapStore } from "@/stores/map";
import { useBusStopStore } from "@/stores/busStop";
import { useCouponStore } from "@/stores/coupon";
import { useGoogleMaps } from "@/composables/useGoogleMaps";
import { API_URL } from "@/services/apiClient";
import { telemetryService } from "@/services/telemetryService";
import { analyticsService } from "@/services/analyticsService";
import { getImageUrl } from "@/utils/imageUrl";
import BusStopInfoModal from "@/components/BusStopInfoModal.vue";
import type { BusStop } from '@/types'
@ -194,11 +194,6 @@ function closePromoModal() {
}
function getImageUrl(path: string | null | undefined) {
if (!path) return '/default-coupon.png'
if (path.startsWith('http')) return path
return `${API_URL}${path.startsWith('/') ? '' : '/'}${path}`
}
async function claimPromo() {
if (!selectedPromo.value) return
@ -1150,7 +1145,12 @@ function clearNavigation() {
>
<!-- Image -->
<div class="sheet-img-wrap">
<img :src="getImageUrl(currentPromo.image_url)" class="sheet-img" :alt="currentPromo.title" />
<img
:src="getImageUrl(currentPromo.image_url, 'coupon')"
class="sheet-img"
:alt="currentPromo.title"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'coupon')"
/>
<span v-if="currentPromo.discount_percentage" class="sheet-discount">
-{{ currentPromo.discount_percentage }}%
</span>
@ -1203,7 +1203,11 @@ function clearNavigation() {
<div v-if="showPromoModal && selectedPromo" class="promo-modal-overlay" @click="closePromoModal">
<div class="promo-modal-content" @click.stop>
<div class="promo-header-modal">
<img :src="getImageUrl(selectedPromo.image_url)" class="promo-img-modal" />
<img
:src="getImageUrl(selectedPromo.image_url, 'coupon')"
class="promo-img-modal"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'coupon')"
/>
<div class="promo-badge-modal">PROMO</div>
</div>
<div class="promo-body-modal">

View File

@ -4,9 +4,9 @@ import { useI18n } from 'vue-i18n'
import { useTaxiStore } from '@/stores/taxi'
import { useShuttleStore } from '@/stores/shuttle'
import { analyticsService } from '@/services/analyticsService'
import { API_URL } from '@/services/apiClient'
import type { Taxi, Shuttle } from '@/types'
import FavoriteButton from '@/components/FavoriteButton.vue'
import { getImageUrl } from '@/utils/imageUrl'
const { t } = useI18n()
const taxiStore = useTaxiStore()
@ -74,11 +74,6 @@ const filteredTaxis = computed(() => {
})
})
function getImageUrl(path?: string) {
if (!path) return `https://ui-avatars.com/api/?name=Taxi&background=fee715&color=101820`
if (path.startsWith('http')) return path
return `${API_URL}${path.startsWith('/') ? '' : '/'}${path}`
}
const handleCall = (taxi: Taxi) => {
analyticsService.logEvent({
@ -195,7 +190,11 @@ function getShiftLabel(shift: string) {
<div v-for="taxi in filteredTaxis" :key="taxi.id" class="taxi-card-new">
<div class="card-top">
<div class="driver-avatar">
<img :src="getImageUrl(taxi.image_url)" alt="Driver">
<img
:src="getImageUrl(taxi.image_url, 'taxi')"
alt="Driver"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'taxi')"
>
</div>
<div class="driver-info">
<h3>{{ taxi.owner_name }}</h3>
@ -288,7 +287,6 @@ function getShiftLabel(shift: string) {
:ref="el => setShuttleRef(el, shuttle.id)"
class="shuttle-card"
:class="{ expanded: expandedShuttleId === shuttle.id }"
:style="{ backgroundImage: `url(${getImageUrl(shuttle.image_url)})` }"
@click="() => {
expandedShuttleId = expandedShuttleId === shuttle.id ? null : shuttle.id;
if (expandedShuttleId === shuttle.id) {
@ -296,6 +294,11 @@ function getShiftLabel(shift: string) {
}
}"
>
<img
:src="getImageUrl(shuttle.image_url, 'shuttle')"
class="shuttle-card-bg"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'shuttle')"
/>
<!-- Collapsed info (always visible) -->
<div class="shuttle-main-info">
<div class="shuttle-header-mini">
@ -502,16 +505,24 @@ function getShiftLabel(shift: string) {
/* ---- La tarjeta base ---- */
.shuttle-card {
border-radius: 20px;
overflow: hidden;
border: 1px solid rgba(255,255,255,0.12);
background-size: cover;
background-position: center;
position: relative;
min-height: 170px;
display: flex;
flex-direction: column;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
overflow: hidden;
background: linear-gradient(135deg, #0d1b2a 0%, #1a2a40 50%, #101820 100%);
}
.shuttle-card-bg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 0;
}
/* Overlay oscuro base (compacto) */
@ -525,7 +536,7 @@ function getShiftLabel(shift: string) {
rgba(0, 0, 0, 0.65) 55%,
rgba(0, 0, 0, 0.30) 100%
);
z-index: 0;
z-index: 1;
transition: background 0.4s ease;
}
@ -550,7 +561,7 @@ function getShiftLabel(shift: string) {
.shuttle-main-info,
.shuttle-details {
position: relative;
z-index: 1;
z-index: 2;
padding: 18px 20px;
}