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
This commit is contained in:
@ -17,7 +17,7 @@ import { useFavoritesStore } from '@/stores/favorites'
|
|||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop'
|
itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop' | 'shuttle'
|
||||||
itemId: string
|
itemId: string
|
||||||
itemName?: string
|
itemName?: string
|
||||||
itemImage?: string
|
itemImage?: string
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { supabase } from '@/supabase'
|
|||||||
export interface Favorite {
|
export interface Favorite {
|
||||||
id: string
|
id: string
|
||||||
user_id: string
|
user_id: string
|
||||||
item_type: 'coupon' | 'business' | 'taxi' | 'route' | 'stop'
|
item_type: 'coupon' | 'business' | 'taxi' | 'route' | 'stop' | 'shuttle'
|
||||||
item_id: string
|
item_id: string
|
||||||
item_name?: string
|
item_name?: string
|
||||||
item_image?: 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 taxis = computed(() => favorites.value.filter(f => f.item_type === 'taxi'))
|
||||||
const routes = computed(() => favorites.value.filter(f => f.item_type === 'route'))
|
const routes = computed(() => favorites.value.filter(f => f.item_type === 'route'))
|
||||||
const stops = computed(() => favorites.value.filter(f => f.item_type === 'stop'))
|
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() {
|
async function loadFavorites() {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
@ -50,7 +51,7 @@ export const useFavoritesStore = defineStore('favorites', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function addFavorite(
|
async function addFavorite(
|
||||||
itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop',
|
itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop' | 'shuttle',
|
||||||
itemId: string,
|
itemId: string,
|
||||||
itemName?: string,
|
itemName?: string,
|
||||||
itemImage?: string
|
itemImage?: string
|
||||||
@ -97,7 +98,7 @@ export const useFavoritesStore = defineStore('favorites', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function toggleFavorite(
|
async function toggleFavorite(
|
||||||
itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop',
|
itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop' | 'shuttle',
|
||||||
itemId: string,
|
itemId: string,
|
||||||
itemName?: string,
|
itemName?: string,
|
||||||
itemImage?: string
|
itemImage?: string
|
||||||
@ -121,7 +122,7 @@ export const useFavoritesStore = defineStore('favorites', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
favorites, isLoading, coupons, businesses, taxis, routes, stops,
|
favorites, isLoading, coupons, businesses, taxis, routes, stops, shuttles,
|
||||||
loadFavorites, addFavorite, removeFavorite, toggleFavorite, isFavorite
|
loadFavorites, addFavorite, removeFavorite, toggleFavorite, isFavorite
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -140,8 +140,10 @@ export interface Taxi {
|
|||||||
export interface Favorite {
|
export interface Favorite {
|
||||||
id: string
|
id: string
|
||||||
user_id: string
|
user_id: string
|
||||||
item_type: 'route' | 'stop' | 'taxi'
|
item_type: 'coupon' | 'business' | 'taxi' | 'route' | 'stop' | 'shuttle'
|
||||||
item_id: string
|
item_id: string
|
||||||
|
item_name?: string
|
||||||
|
item_image?: string
|
||||||
created_at?: string
|
created_at?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { businessService } from '@/services/businessService'
|
|||||||
import type { Business } from '@/types'
|
import type { Business } from '@/types'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import FavoriteButton from '@/components/FavoriteButton.vue'
|
|
||||||
import { analyticsService } from '@/services/analyticsService'
|
import { analyticsService } from '@/services/analyticsService'
|
||||||
import { getImageUrl } from '@/utils/imageUrl'
|
import { getImageUrl } from '@/utils/imageUrl'
|
||||||
import AuthGuard from '@/components/common/AuthGuard.vue'
|
import AuthGuard from '@/components/common/AuthGuard.vue'
|
||||||
@ -242,9 +241,6 @@ function resetFilters() {
|
|||||||
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')"
|
||||||
/>
|
/>
|
||||||
<div class="biz-fav">
|
|
||||||
<FavoriteButton item-type="business" :item-id="biz.id" :item-name="biz.name" :item-image="biz.image_url || undefined" />
|
|
||||||
</div>
|
|
||||||
<span class="biz-cat-badge">
|
<span class="biz-cat-badge">
|
||||||
<span class="material-icons" style="font-size:0.875rem">{{ catIcon(biz.category || '') }}</span>
|
<span class="material-icons" style="font-size:0.875rem">{{ catIcon(biz.category || '') }}</span>
|
||||||
{{ catName(biz.category || '') }}
|
{{ catName(biz.category || '') }}
|
||||||
@ -320,9 +316,6 @@ function resetFilters() {
|
|||||||
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')"
|
@error="(e) => (e.target as HTMLImageElement).src = getImageUrl(null, 'business')"
|
||||||
/>
|
/>
|
||||||
<div class="featured-gradient"></div>
|
<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" />
|
|
||||||
</div>
|
|
||||||
<div class="featured-info">
|
<div class="featured-info">
|
||||||
<span class="featured-cat">
|
<span class="featured-cat">
|
||||||
<span class="material-icons" style="font-size:0.8rem">{{ catIcon(biz.category || '') }}</span>
|
<span class="material-icons" style="font-size:0.8rem">{{ catIcon(biz.category || '') }}</span>
|
||||||
@ -358,9 +351,6 @@ function resetFilters() {
|
|||||||
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')"
|
||||||
/>
|
/>
|
||||||
<div class="biz-fav">
|
|
||||||
<FavoriteButton item-type="business" :item-id="biz.id" :item-name="biz.name" :item-image="biz.image_url || undefined" />
|
|
||||||
</div>
|
|
||||||
<span class="biz-cat-badge">
|
<span class="biz-cat-badge">
|
||||||
<span class="material-icons" style="font-size:0.875rem">{{ catIcon(biz.category || '') }}</span>
|
<span class="material-icons" style="font-size:0.875rem">{{ catIcon(biz.category || '') }}</span>
|
||||||
{{ catName(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%);
|
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 {
|
.featured-info {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -738,12 +721,6 @@ function resetFilters() {
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.biz-fav {
|
|
||||||
position: absolute;
|
|
||||||
top: 0.5rem;
|
|
||||||
right: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.biz-cat-badge {
|
.biz-cat-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0.5rem;
|
bottom: 0.5rem;
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { ref, computed, watch } from 'vue'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useFavoritesStore } from '@/stores/favorites'
|
import { useFavoritesStore } from '@/stores/favorites'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
|
import { useTaxiStore } from '@/stores/taxi'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { getImageUrl as utilGetImageUrl } from '@/utils/imageUrl'
|
import { getImageUrl as utilGetImageUrl } from '@/utils/imageUrl'
|
||||||
import LoadingBranded from '@/components/common/LoadingBranded.vue'
|
import LoadingBranded from '@/components/common/LoadingBranded.vue'
|
||||||
@ -11,11 +12,13 @@ const router = useRouter()
|
|||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const favoritesStore = useFavoritesStore()
|
const favoritesStore = useFavoritesStore()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
const taxiStore = useTaxiStore()
|
||||||
const selectedFilter = ref('all')
|
const selectedFilter = ref('all')
|
||||||
|
|
||||||
const filters = computed(() => [
|
const filters = computed(() => [
|
||||||
{ key: 'all', label: t('common.all'), icon: 'star' },
|
{ key: 'all', label: t('common.all'), icon: 'star' },
|
||||||
{ key: 'routes', label: t('favorites.tabs.routes'), icon: 'directions_bus' },
|
{ 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: 'taxis', label: t('favorites.tabs.taxis'), icon: 'local_taxi' },
|
||||||
{ key: 'businesses',label: t('favorites.tabs.businesses'), icon: 'store' },
|
{ key: 'businesses',label: t('favorites.tabs.businesses'), icon: 'store' },
|
||||||
{ key: 'coupons', label: t('favorites.tabs.coupons'), icon: 'confirmation_number' },
|
{ key: 'coupons', label: t('favorites.tabs.coupons'), icon: 'confirmation_number' },
|
||||||
@ -28,11 +31,17 @@ watch(
|
|||||||
(authenticated) => {
|
(authenticated) => {
|
||||||
if (authenticated) {
|
if (authenticated) {
|
||||||
favoritesStore.loadFavorites()
|
favoritesStore.loadFavorites()
|
||||||
|
taxiStore.silentReload()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
function getTaxiPhone(id: string) {
|
||||||
|
const taxi = taxiStore.taxis.find(t => t.id === id)
|
||||||
|
return taxi?.phone_number || ''
|
||||||
|
}
|
||||||
|
|
||||||
function getImageUrl(path?: string) {
|
function getImageUrl(path?: string) {
|
||||||
return utilGetImageUrl(path, 'business')
|
return utilGetImageUrl(path, 'business')
|
||||||
}
|
}
|
||||||
@ -44,10 +53,11 @@ async function removeFavorite(event: Event, itemType: string, itemId: string) {
|
|||||||
|
|
||||||
function navigateToItem(item: any) {
|
function navigateToItem(item: any) {
|
||||||
if (item.item_type === 'route') router.push({ path: '/schedules', query: { routeId: item.item_id } })
|
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 === 'business') router.push('/business/' + item.item_id)
|
||||||
else if (item.item_type === 'coupon') router.push('/coupons')
|
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 === '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(() =>
|
const visibleRoutes = computed(() =>
|
||||||
@ -65,12 +75,15 @@ const visibleCoupons = computed(() =>
|
|||||||
const visibleStops = computed(() =>
|
const visibleStops = computed(() =>
|
||||||
(selectedFilter.value === 'all' || selectedFilter.value === 'stops') ? favoritesStore.stops : []
|
(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 totalFavorites = computed(() => favoritesStore.favorites.length)
|
||||||
const hasVisibleItems = computed(() =>
|
const hasVisibleItems = computed(() =>
|
||||||
visibleRoutes.value.length + visibleTaxis.value.length +
|
visibleRoutes.value.length + visibleTaxis.value.length +
|
||||||
visibleBusinesses.value.length + visibleCoupons.value.length +
|
visibleBusinesses.value.length + visibleCoupons.value.length +
|
||||||
visibleStops.value.length > 0
|
visibleStops.value.length + visibleShuttles.value.length > 0
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -181,6 +194,15 @@ const hasVisibleItems = computed(() =>
|
|||||||
<p class="card-name">{{ item.item_name }}</p>
|
<p class="card-name">{{ item.item_name }}</p>
|
||||||
<p class="card-sub">{{ t('favorites.availability') }}</p>
|
<p class="card-sub">{{ t('favorites.availability') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<a
|
||||||
|
v-if="getTaxiPhone(item.item_id)"
|
||||||
|
:href="'tel:' + getTaxiPhone(item.item_id)"
|
||||||
|
class="call-btn"
|
||||||
|
@click.stop
|
||||||
|
title="Llamar ahora"
|
||||||
|
>
|
||||||
|
<span class="material-icons">phone_in_talk</span>
|
||||||
|
</a>
|
||||||
<button class="heart-btn heart-btn--active" @click.stop="removeFavorite($event, item.item_type, item.item_id)" :title="t('favorites.removeTitle')">
|
<button class="heart-btn heart-btn--active" @click.stop="removeFavorite($event, item.item_type, item.item_id)" :title="t('favorites.removeTitle')">
|
||||||
<span class="material-icons">favorite</span>
|
<span class="material-icons">favorite</span>
|
||||||
</button>
|
</button>
|
||||||
@ -188,6 +210,33 @@ const hasVisibleItems = computed(() =>
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- SHUTTLES -->
|
||||||
|
<section v-if="visibleShuttles.length > 0" class="fav-section">
|
||||||
|
<div class="section-label">
|
||||||
|
<span class="material-icons">airport_shuttle</span>
|
||||||
|
<span>Viajes Turísticos</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-list">
|
||||||
|
<div
|
||||||
|
v-for="item in visibleShuttles"
|
||||||
|
:key="item.id"
|
||||||
|
class="card card--row"
|
||||||
|
@click="navigateToItem(item)"
|
||||||
|
>
|
||||||
|
<div class="card-thumb card-thumb--img">
|
||||||
|
<img :src="getImageUrl(item.item_image)" :alt="item.item_name" />
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<p class="card-name">{{ item.item_name }}</p>
|
||||||
|
<p class="card-sub">Ver detalles</p>
|
||||||
|
</div>
|
||||||
|
<button class="heart-btn heart-btn--active" @click.stop="removeFavorite($event, item.item_type, item.item_id)">
|
||||||
|
<span class="material-icons">favorite</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- NEGOCIOS -->
|
<!-- NEGOCIOS -->
|
||||||
<section v-if="visibleBusinesses.length > 0" class="fav-section">
|
<section v-if="visibleBusinesses.length > 0" class="fav-section">
|
||||||
<div class="section-label">
|
<div class="section-label">
|
||||||
@ -524,6 +573,32 @@ const hasVisibleItems = computed(() =>
|
|||||||
transform: scale(1.1);
|
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 {
|
.heart-btn--overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.5rem;
|
top: 0.5rem;
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { getImageUrl } from '@/utils/imageUrl'
|
|||||||
import { analyticsService } from '@/services/analyticsService'
|
import { analyticsService } from '@/services/analyticsService'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import LoadingBranded from '@/components/common/LoadingBranded.vue'
|
import LoadingBranded from '@/components/common/LoadingBranded.vue'
|
||||||
|
import FavoriteButton from '@/components/FavoriteButton.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -73,6 +74,13 @@ const getTripTypeLabel = (type: string) => {
|
|||||||
<h1 class="font-bold text-[var(--text-primary)] text-lg truncate flex-1">
|
<h1 class="font-bold text-[var(--text-primary)] text-lg truncate flex-1">
|
||||||
{{ shuttle?.company_name || t('shuttle.detailTitle') }}
|
{{ shuttle?.company_name || t('shuttle.detailTitle') }}
|
||||||
</h1>
|
</h1>
|
||||||
|
<FavoriteButton
|
||||||
|
v-if="shuttle"
|
||||||
|
item-type="shuttle"
|
||||||
|
:item-id="shuttle.id"
|
||||||
|
:item-name="shuttle.company_name || shuttle.origin + ' - ' + shuttle.destination"
|
||||||
|
:item-image="shuttle.image_url || undefined"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Loading -->
|
<!-- Loading -->
|
||||||
|
|||||||
Reference in New Issue
Block a user