feat: adiciona opcao de favoritar a partir de ranking por plataforma

This commit is contained in:
2026-05-29 20:23:47 -05:00
parent eaadbbc7de
commit 8ed08e2869

View File

@@ -80,6 +80,14 @@
class="inline-flex items-center justify-center rounded-full bg-[#292524] px-2 py-0.5 text-[12px] font-semibold leading-none text-white"> class="inline-flex items-center justify-center rounded-full bg-[#292524] px-2 py-0.5 text-[12px] font-semibold leading-none text-white">
{{ game.score }} {{ game.score }}
</span> </span>
<button type="button" :disabled="togglingId === game.name"
:aria-label="favoriteIds.has(game.name) ? 'Remover dos favoritos' : 'Adicionar aos favoritos'"
@click="toggleFavorite(game)"
class="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full transition hover:bg-[#f0efed] disabled:cursor-not-allowed disabled:opacity-50">
<Icon
:name="togglingId === game.name ? 'mdi:loading' : favoriteIds.has(game.name) ? 'mdi:heart' : 'mdi:heart-outline'"
:class="['text-sm', togglingId === game.name ? 'animate-spin text-[#777169]' : favoriteIds.has(game.name) ? 'text-rose-500' : 'text-[#777169]']" />
</button>
</div> </div>
</div> </div>
</li> </li>
@@ -108,6 +116,14 @@
<strong class="min-w-0 break-words text-base font-medium leading-6 tracking-[0.16px] text-[#0c0a09]"> <strong class="min-w-0 break-words text-base font-medium leading-6 tracking-[0.16px] text-[#0c0a09]">
{{ game.name }} {{ game.name }}
</strong> </strong>
<button type="button" :disabled="togglingId === game.name"
:aria-label="favoriteIds.has(game.name) ? 'Remover dos favoritos' : 'Adicionar aos favoritos'"
@click="toggleFavorite(game)"
class="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full transition hover:bg-[#f0efed] disabled:cursor-not-allowed disabled:opacity-50">
<Icon
:name="togglingId === game.name ? 'mdi:loading' : favoriteIds.has(game.name) ? 'mdi:heart' : 'mdi:heart-outline'"
:class="['text-base', togglingId === game.name ? 'animate-spin text-[#777169]' : favoriteIds.has(game.name) ? 'text-rose-500' : 'text-[#777169]']" />
</button>
</div> </div>
<span <span
:class="['inline-flex items-center rounded-full px-2.5 py-1 text-[12px] font-semibold uppercase leading-none tracking-[0.8px] text-[#0c0a09]', platformBadgeClass(game.platform)]"> :class="['inline-flex items-center rounded-full px-2.5 py-1 text-[12px] font-semibold uppercase leading-none tracking-[0.8px] text-[#0c0a09]', platformBadgeClass(game.platform)]">
@@ -154,6 +170,7 @@ useHead({
}) })
const COMPARE_API_BASE_URL = 'https://ranking-plataforma.onrender.com' const COMPARE_API_BASE_URL = 'https://ranking-plataforma.onrender.com'
const WISHLIST_API_BASE_URL = 'https://gameverse-wishlist-production.up.railway.app'
const platformOptions = [ const platformOptions = [
{ label: 'Todas as plataformas', value: 'all' }, { label: 'Todas as plataformas', value: 'all' },
@@ -182,6 +199,8 @@ const selectedPlatform = ref('all')
const rankings = ref([]) const rankings = ref([])
const isLoading = ref(false) const isLoading = ref(false)
const errorMessage = ref('') const errorMessage = ref('')
const favoriteIds = ref(new Set())
const togglingId = ref(null)
const filteredRankings = computed(() => { const filteredRankings = computed(() => {
if (selectedPlatform.value === 'all') return rankings.value if (selectedPlatform.value === 'all') return rankings.value
@@ -232,7 +251,68 @@ async function fetchRanking() {
} }
} }
async function fetchFavorites() {
if (!token.value) return
try {
const data = await $fetch(`${WISHLIST_API_BASE_URL}/api/wishlist`, {
headers: { Authorization: `Bearer ${token.value}` }
})
favoriteIds.value = new Set(
(data?.data ?? [])
.filter((item) => item.is_favorite === 1 || item.is_favorite === true)
.map((item) => item.game_id)
)
} catch (error) {
console.warn('Erro ao carregar os favoritos. Tente novamente.', error)
}
}
async function toggleFavorite(game) {
if (!token.value) {
await navigateTo('/login')
return
}
const gameId = game.name
togglingId.value = gameId
try {
if (favoriteIds.value.has(gameId)) {
await $fetch(`${WISHLIST_API_BASE_URL}/api/wishlist/${encodeURIComponent(gameId)}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token.value}` }
})
favoriteIds.value = new Set([...favoriteIds.value].filter((id) => id !== gameId))
$toast.success(`${gameId} removido dos favoritos.`, { duration: 4000 })
} else {
await $fetch(`${WISHLIST_API_BASE_URL}/api/wishlist`, {
method: 'POST',
headers: { Authorization: `Bearer ${token.value}` },
body: { game_id: gameId, is_wishlist: false, is_favorite: true, price_alert: false }
})
favoriteIds.value = new Set([...favoriteIds.value, gameId])
$toast.success(`${gameId} adicionado aos favoritos!`, { duration: 4000 })
}
} catch (error) {
const statusCode = error?.statusCode ?? error?.response?.status ?? error?.data?.statusCode
if (statusCode === 401) {
clearToken()
$toast.error('Sua sessão expirou. Faça login novamente.', { duration: 8000 })
await navigateTo('/login')
return
}
$toast.error('Erro ao atualizar favoritos. Tente novamente.', { duration: 6000 })
} finally {
togglingId.value = null
}
}
onMounted(() => { onMounted(() => {
fetchRanking() fetchRanking()
fetchFavorites()
}) })
</script> </script>