Initial commit: SIBU 2.0 MISSION
This commit is contained in:
140
frontend/src/components/FavoriteButton.vue
Normal file
140
frontend/src/components/FavoriteButton.vue
Normal file
@ -0,0 +1,140 @@
|
||||
<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'
|
||||
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>
|
||||
Reference in New Issue
Block a user