From 35e2a6d632754908b618e27ef3fe6a74a56cfd0a Mon Sep 17 00:00:00 2001
From: Hanzo_dev <2002samudiojohan@gmail.com>
Date: Wed, 4 Mar 2026 16:34:47 -0500
Subject: [PATCH] fix(favorites): adjust favorite button visibility and
functionality
- removed favorite button from Discover cards
- added call button for taxis in Favorites view
- added favorite button in Shuttle details
- added Shuttles category in Favorites view
---
frontend/src/components/FavoriteButton.vue | 2 +-
frontend/src/stores/favorites.ts | 9 ++-
frontend/src/types/index.ts | 4 +-
frontend/src/views/DiscoverView.vue | 23 ------
frontend/src/views/FavoritesView.vue | 79 ++++++++++++++++++-
.../src/views/transporte/ShuttleDetalle.vue | 8 ++
6 files changed, 94 insertions(+), 31 deletions(-)
diff --git a/frontend/src/components/FavoriteButton.vue b/frontend/src/components/FavoriteButton.vue
index 55ddbb0..cf67b41 100644
--- a/frontend/src/components/FavoriteButton.vue
+++ b/frontend/src/components/FavoriteButton.vue
@@ -17,7 +17,7 @@ import { useFavoritesStore } from '@/stores/favorites'
import { useAuthStore } from '@/stores/auth'
const props = defineProps<{
- itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop'
+ itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop' | 'shuttle'
itemId: string
itemName?: string
itemImage?: string
diff --git a/frontend/src/stores/favorites.ts b/frontend/src/stores/favorites.ts
index a163a1c..2530c8a 100644
--- a/frontend/src/stores/favorites.ts
+++ b/frontend/src/stores/favorites.ts
@@ -5,7 +5,7 @@ import { supabase } from '@/supabase'
export interface Favorite {
id: string
user_id: string
- item_type: 'coupon' | 'business' | 'taxi' | 'route' | 'stop'
+ item_type: 'coupon' | 'business' | 'taxi' | 'route' | 'stop' | 'shuttle'
item_id: string
item_name?: string
item_image?: string
@@ -21,6 +21,7 @@ export const useFavoritesStore = defineStore('favorites', () => {
const taxis = computed(() => favorites.value.filter(f => f.item_type === 'taxi'))
const routes = computed(() => favorites.value.filter(f => f.item_type === 'route'))
const stops = computed(() => favorites.value.filter(f => f.item_type === 'stop'))
+ const shuttles = computed(() => favorites.value.filter(f => f.item_type === 'shuttle'))
async function loadFavorites() {
isLoading.value = true
@@ -50,7 +51,7 @@ export const useFavoritesStore = defineStore('favorites', () => {
}
async function addFavorite(
- itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop',
+ itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop' | 'shuttle',
itemId: string,
itemName?: string,
itemImage?: string
@@ -97,7 +98,7 @@ export const useFavoritesStore = defineStore('favorites', () => {
}
async function toggleFavorite(
- itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop',
+ itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop' | 'shuttle',
itemId: string,
itemName?: string,
itemImage?: string
@@ -121,7 +122,7 @@ export const useFavoritesStore = defineStore('favorites', () => {
}
return {
- favorites, isLoading, coupons, businesses, taxis, routes, stops,
+ favorites, isLoading, coupons, businesses, taxis, routes, stops, shuttles,
loadFavorites, addFavorite, removeFavorite, toggleFavorite, isFavorite
}
})
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
index 1d3fe86..b3a817d 100644
--- a/frontend/src/types/index.ts
+++ b/frontend/src/types/index.ts
@@ -140,8 +140,10 @@ export interface Taxi {
export interface Favorite {
id: string
user_id: string
- item_type: 'route' | 'stop' | 'taxi'
+ item_type: 'coupon' | 'business' | 'taxi' | 'route' | 'stop' | 'shuttle'
item_id: string
+ item_name?: string
+ item_image?: string
created_at?: string
}
diff --git a/frontend/src/views/DiscoverView.vue b/frontend/src/views/DiscoverView.vue
index 359e1ea..32e6ae2 100644
--- a/frontend/src/views/DiscoverView.vue
+++ b/frontend/src/views/DiscoverView.vue
@@ -4,7 +4,6 @@ import { businessService } from '@/services/businessService'
import type { Business } from '@/types'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
-import FavoriteButton from '@/components/FavoriteButton.vue'
import { analyticsService } from '@/services/analyticsService'
import { getImageUrl } from '@/utils/imageUrl'
import AuthGuard from '@/components/common/AuthGuard.vue'
@@ -242,9 +241,6 @@ function resetFilters() {
class="biz-img"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')"
/>
-
-
-
{{ catIcon(biz.category || '') }}
{{ catName(biz.category || '') }}
@@ -320,9 +316,6 @@ function resetFilters() {
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')"
/>
-
-
-
{{ catIcon(biz.category || '') }}
@@ -358,9 +351,6 @@ function resetFilters() {
class="biz-img"
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')"
/>
-
-
-
{{ catIcon(biz.category || '') }}
{{ catName(biz.category || '') }}
@@ -648,13 +638,6 @@ function resetFilters() {
background: linear-gradient(to top, rgba(0,0,0,0.75) 40%, transparent 70%);
}
-.featured-fav {
- position: absolute;
- top: 0.625rem;
- right: 0.625rem;
- z-index: 2;
-}
-
.featured-info {
position: absolute;
bottom: 0;
@@ -738,12 +721,6 @@ function resetFilters() {
object-fit: cover;
}
-.biz-fav {
- position: absolute;
- top: 0.5rem;
- right: 0.5rem;
-}
-
.biz-cat-badge {
position: absolute;
bottom: 0.5rem;
diff --git a/frontend/src/views/FavoritesView.vue b/frontend/src/views/FavoritesView.vue
index 0f40b9f..5e5c4f3 100644
--- a/frontend/src/views/FavoritesView.vue
+++ b/frontend/src/views/FavoritesView.vue
@@ -3,6 +3,7 @@ import { ref, computed, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useFavoritesStore } from '@/stores/favorites'
import { useAuthStore } from '@/stores/auth'
+import { useTaxiStore } from '@/stores/taxi'
import { useI18n } from 'vue-i18n'
import { getImageUrl as utilGetImageUrl } from '@/utils/imageUrl'
import LoadingBranded from '@/components/common/LoadingBranded.vue'
@@ -11,11 +12,13 @@ const router = useRouter()
const { t } = useI18n()
const favoritesStore = useFavoritesStore()
const authStore = useAuthStore()
+const taxiStore = useTaxiStore()
const selectedFilter = ref('all')
const filters = computed(() => [
{ key: 'all', label: t('common.all'), icon: 'star' },
{ key: 'routes', label: t('favorites.tabs.routes'), icon: 'directions_bus' },
+ { key: 'shuttles', label: 'Viajes TurĂsticos', icon: 'airport_shuttle' },
{ key: 'taxis', label: t('favorites.tabs.taxis'), icon: 'local_taxi' },
{ key: 'businesses',label: t('favorites.tabs.businesses'), icon: 'store' },
{ key: 'coupons', label: t('favorites.tabs.coupons'), icon: 'confirmation_number' },
@@ -28,11 +31,17 @@ watch(
(authenticated) => {
if (authenticated) {
favoritesStore.loadFavorites()
+ taxiStore.silentReload()
}
},
{ immediate: true }
)
+function getTaxiPhone(id: string) {
+ const taxi = taxiStore.taxis.find(t => t.id === id)
+ return taxi?.phone_number || ''
+}
+
function getImageUrl(path?: string) {
return utilGetImageUrl(path, 'business')
}
@@ -44,10 +53,11 @@ async function removeFavorite(event: Event, itemType: string, itemId: string) {
function navigateToItem(item: any) {
if (item.item_type === 'route') router.push({ path: '/schedules', query: { routeId: item.item_id } })
- else if (item.item_type === 'taxi') router.push('/taxi')
+ else if (item.item_type === 'taxi') router.push('/transporte/taxis')
else if (item.item_type === 'business') router.push('/business/' + item.item_id)
else if (item.item_type === 'coupon') router.push('/coupons')
else if (item.item_type === 'stop') router.push({ path: '/map', query: { stopId: item.item_id } })
+ else if (item.item_type === 'shuttle') router.push({ name: 'ShuttleDetalle', params: { id: item.item_id } })
}
const visibleRoutes = computed(() =>
@@ -65,12 +75,15 @@ const visibleCoupons = computed(() =>
const visibleStops = computed(() =>
(selectedFilter.value === 'all' || selectedFilter.value === 'stops') ? favoritesStore.stops : []
)
+const visibleShuttles = computed(() =>
+ (selectedFilter.value === 'all' || selectedFilter.value === 'shuttles') ? favoritesStore.shuttles : []
+)
const totalFavorites = computed(() => favoritesStore.favorites.length)
const hasVisibleItems = computed(() =>
visibleRoutes.value.length + visibleTaxis.value.length +
visibleBusinesses.value.length + visibleCoupons.value.length +
- visibleStops.value.length > 0
+ visibleStops.value.length + visibleShuttles.value.length > 0
)
@@ -181,6 +194,15 @@ const hasVisibleItems = computed(() =>
{{ item.item_name }}
{{ t('favorites.availability') }}
+
+ phone_in_talk
+
@@ -188,6 +210,33 @@ const hasVisibleItems = computed(() =>
+
+
+
+ airport_shuttle
+ Viajes TurĂsticos
+
+
+
+
+
![]()
+
+
+
{{ item.item_name }}
+
Ver detalles
+
+
+
+
+
+
@@ -524,6 +573,32 @@ const hasVisibleItems = computed(() =>
transform: scale(1.1);
}
+.call-btn {
+ background: var(--active-color);
+ border: none;
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #101820;
+ cursor: pointer;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ flex-shrink: 0;
+ margin-right: -4px;
+}
+
+.call-btn:hover {
+ transform: scale(1.1);
+ box-shadow: 0 4px 15px rgba(254, 231, 21, 0.3);
+}
+
+.call-btn .material-icons {
+ font-size: 20px;
+}
+
.heart-btn--overlay {
position: absolute;
top: 0.5rem;
diff --git a/frontend/src/views/transporte/ShuttleDetalle.vue b/frontend/src/views/transporte/ShuttleDetalle.vue
index f27345c..47b3d13 100644
--- a/frontend/src/views/transporte/ShuttleDetalle.vue
+++ b/frontend/src/views/transporte/ShuttleDetalle.vue
@@ -7,6 +7,7 @@ import { getImageUrl } from '@/utils/imageUrl'
import { analyticsService } from '@/services/analyticsService'
import { useI18n } from 'vue-i18n'
import LoadingBranded from '@/components/common/LoadingBranded.vue'
+import FavoriteButton from '@/components/FavoriteButton.vue'
const route = useRoute()
const router = useRouter()
@@ -73,6 +74,13 @@ const getTripTypeLabel = (type: string) => {
{{ shuttle?.company_name || t('shuttle.detailTitle') }}
+