141 lines
2.8 KiB
Vue
141 lines
2.8 KiB
Vue
<template>
|
|
<button
|
|
class="favorite-btn"
|
|
:class="{ 'is-favorite': isFavorited, 'is-loading': isLoading }"
|
|
@click.stop="handleToggle"
|
|
:title="isFavorited ? 'Quitar de favoritos' : 'Agregar a favoritos'"
|
|
>
|
|
<span class="material-icons heart-icon">
|
|
{{ isFavorited ? 'favorite' : 'favorite_border' }}
|
|
</span>
|
|
</button>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import { useFavoritesStore } from '@/stores/favorites'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
|
|
const props = defineProps<{
|
|
itemType: 'coupon' | 'business' | 'taxi' | 'route' | 'stop'
|
|
itemId: string
|
|
itemName?: string
|
|
itemImage?: string
|
|
}>()
|
|
|
|
const favoritesStore = useFavoritesStore()
|
|
const authStore = useAuthStore()
|
|
const isLoading = ref(false)
|
|
|
|
const isFavorited = computed(() => {
|
|
return favoritesStore.isFavorite(props.itemType, props.itemId)
|
|
})
|
|
|
|
onMounted(() => {
|
|
// Load favorites if authenticated and not loaded yet
|
|
if (authStore.isAuthenticated && favoritesStore.favorites.length === 0) {
|
|
favoritesStore.loadFavorites()
|
|
}
|
|
})
|
|
|
|
async function handleToggle() {
|
|
if (!authStore.isAuthenticated) {
|
|
// Optionally redirect to login or show message
|
|
alert('Debes iniciar sesión para agregar favoritos')
|
|
return
|
|
}
|
|
|
|
isLoading.value = true
|
|
try {
|
|
await favoritesStore.toggleFavorite(
|
|
props.itemType,
|
|
props.itemId,
|
|
props.itemName,
|
|
props.itemImage
|
|
)
|
|
} catch (error) {
|
|
console.error('Error toggling favorite:', error)
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.favorite-btn {
|
|
background: rgba(255, 255, 255, 0.9);
|
|
backdrop-filter: blur(10px);
|
|
border: none;
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.favorite-btn:hover {
|
|
transform: scale(1.1);
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.favorite-btn:active {
|
|
transform: scale(0.95);
|
|
}
|
|
|
|
.heart-icon {
|
|
color: #e91e63;
|
|
font-size: 22px;
|
|
transition: all 0.3s;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.favorite-btn.is-favorite .heart-icon {
|
|
animation: heartBeat 0.5s ease;
|
|
}
|
|
|
|
.favorite-btn.is-loading {
|
|
pointer-events: none;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.favorite-btn.is-loading .heart-icon {
|
|
animation: pulse 1s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes heartBeat {
|
|
0%, 100% {
|
|
transform: scale(1);
|
|
}
|
|
25% {
|
|
transform: scale(1.3);
|
|
}
|
|
50% {
|
|
transform: scale(1.1);
|
|
}
|
|
75% {
|
|
transform: scale(1.2);
|
|
}
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%, 100% {
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
opacity: 0.5;
|
|
}
|
|
}
|
|
|
|
/* Dark mode support */
|
|
.dark .favorite-btn {
|
|
background: rgba(30, 30, 30, 0.9);
|
|
}
|
|
</style>
|