Files
DADProject/frontend/src/pages/home/HomePage.vue
T
2025-12-25 01:49:02 +00:00

242 lines
10 KiB
Vue

<script setup>
import { ref, onMounted, nextTick } from 'vue'
import { Button } from '@/components/ui/button'
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle
} from '@/components/ui/card'
import { useAPIStore } from '@/stores/api'
import { useRouter } from 'vue-router'
const apiStore = useAPIStore()
const router = useRouter()
const openGames = ref([])
const selectedMode = ref('9')
const observerTarget = ref(null);
const observer = ref(null);
const isLoading = ref(false);
const pageCounter = ref(1);
const showHostModal = ref(false)
const showJoinModal = ref(false)
const selectedGame = ref(null)
const isReady = ref(false)
const setupObserver = () => {
if (observer.value) observer.value.disconnect();
observer.value = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && !isLoading.value && openGames.value.length > 0) {
getPendingGames();
}
}, { threshold: 0.1 });
if (observerTarget.value) {
observer.value.observe(observerTarget.value);
}
}
const getPendingGames = async (mode = selectedMode.value) => {
if (isLoading.value) return;
isLoading.value = true;
apiStore.gameQueryParameters.page = pageCounter.value++;
apiStore.gameQueryParameters.filters.sort_direction = "asc"
apiStore.gameQueryParameters.filters.type = mode
apiStore.gameQueryParameters.filters.status = 'Pending'
try {
const response = await apiStore.getGames();
const newGames = response.data.data;
openGames.value = [...openGames.value, ...newGames];
} finally {
isLoading.value = false;
}
}
const clickMode = async (mode) => {
if (selectedMode.value === mode) return;
openGames.value = [];
selectedMode.value = mode;
pageCounter.value = 1;
await getPendingGames();
setupObserver();
}
const startSingle = () => {
router.push({ name: 'bisca' + selectedMode.value });
}
const JoinGameModal = (game) => {
selectedGame.value = game
isReady.value = false
showJoinModal.value = true
}
const hostGameModal = () => {
showHostModal.value = true
}
const toggleReady = () => {
isReady.value = !isReady.value
}
onMounted(async () => {
await getPendingGames();
await nextTick();
setupObserver();
})
</script>
<template>
<div class="min-h-screen bg-cover bg-center transition-all duration-700">
<div class="flex flex-col justify-center items-center gap-5 pt-10 px-4">
<div class="relative w-full max-w-md bg-card rounded-lg border shadow-sm overflow-hidden">
<div class="relative h-14 flex items-center">
<button @click="clickMode('9')"
class="h-full flex items-center justify-center font-semibold transition-all duration-500 ease-in-out border-r last:border-r-0"
:class="{
'w-2/3 z-10 text-purple-600 dark:text-purple-400 bg-purple-500/10': selectedMode === '9',
'w-1/3 z-0 text-muted-foreground/40 bg-transparent': selectedMode !== '9'
}">
<span>Bisca de 9</span>
</button>
<button @click="clickMode('3')"
class="h-full flex items-center justify-center font-semibold transition-all duration-500 ease-in-out"
:class="{
'w-2/3 z-10 text-purple-600 dark:text-purple-400 bg-purple-500/10': selectedMode === '3',
'w-1/3 z-0 text-muted-foreground/40 bg-transparent': selectedMode !== '3'
}">
<span>Bisca de 3</span>
</button>
</div>
</div>
<div class="flex flex-col items-center gap-5 w-[100vw]">
<Card class="w-full max-w-2xl">
<CardHeader>
<CardTitle class="text-3xl font-bold text-center">
Single Player
</CardTitle>
<CardDescription class="text-center">
Go against one of our bots!
</CardDescription>
</CardHeader>
<CardContent class="space-y-6">
<div class="flex justify-center">
<Button @click="startSingle()" size="lg" variant="secondary"
class="hover:bg-purple-500 hover:text-slate-200">
Start Game
</Button>
</div>
</CardContent>
</Card>
<Card class="w-full max-w-2xl">
<CardHeader>
<CardTitle class="text-3xl font-bold text-center">
MultiPlayer
</CardTitle>
<CardDescription class="text-center">
Go one on one with another player!
</CardDescription>
</CardHeader>
<CardContent class="space-y-6">
<label class="text-sm font-medium">Open games (oldest first)</label>
<div class="rounded-lg border bg-card text-card-foreground shadow-sm">
<div v-if="openGames.length === 0" class="p-6 text-center text-sm text-muted-foreground">
No open games yet. Try hosting one!
</div>
<div v-else class="max-h-[350px] overflow-y-auto custom-scrollbar p-1">
<div v-for="(game, index) in openGames" :key="game.id" @click="JoinGameModal(game)"
class="flex items-center justify-between p-3 mb-2 border border-transparent rounded-lg transition-all hover:bg-muted/50 hover:border-purple-500">
<div class="flex items-center gap-3">
<div
class="flex h-8 w-8 items-center justify-center rounded-full text-xs font-bold bg-green-100 text-green-700">
{{ index + 1 }}
</div>
<div>
<div class="font-medium text-sm">Started: {{ game.began_at }}</div>
<div class="text-xs text-muted-foreground">host: {{
game.player1?.nickname || 'Anonymous' }}</div>
</div>
</div>
</div>
<div ref="observerTarget" class="h-10 flex items-center justify-center">
<span v-if="isLoading" class="text-xs text-purple-500 animate-pulse">Loading
more...</span>
</div>
</div>
</div>
<div class="flex justify-center">
<Button @click="hostGameModal()" size="lg" variant="secondary"
class="hover:bg-purple-500 hover:text-slate-200">
Host Game
</Button>
</div>
</CardContent>
</Card>
</div>
</div>
<div v-if="showHostModal"
class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
<Card class="w-full max-w-sm border-purple-500 shadow-2xl">
<CardHeader>
<CardTitle class="text-center">Hosting Game</CardTitle>
</CardHeader>
<CardContent class="flex flex-col items-center gap-6 py-10">
<div class="relative">
<div
class="h-16 w-16 animate-spin rounded-full border-4 border-purple-500 border-t-transparent">
</div>
</div>
<p class="animate-pulse text-lg font-medium text-muted-foreground">Waiting for opponents...</p>
<Button variant="outline" @click="showHostModal = false">Cancel Hosting</Button>
</CardContent>
</Card>
</div>
<div v-if="showJoinModal"
class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
<Card class="w-full max-w-md border-purple-500 shadow-2xl">
<CardHeader>
<CardTitle>Join Game</CardTitle>
<CardDescription v-if="selectedGame">
Hosted by {{ selectedGame.player1?.nickname || 'Anonymous' }}
</CardDescription>
</CardHeader>
<CardContent class="space-y-4">
<div class="rounded-lg bg-muted p-4 text-sm">
<p><strong>Game Type:</strong> Bisca de {{ selectedMode }}</p>
<p><strong>Started at:</strong> {{ selectedGame?.began_at }}</p>
</div>
<div v-if="isReady"
class="flex items-center justify-center rounded-md bg-green-500/10 p-3 text-green-600 dark:text-green-400">
<span class="animate-pulse font-semibold">Waiting for the host to start...</span>
</div>
<div class="flex gap-3 pt-4">
<Button variant="destructive" class="flex-1" @click="showJoinModal = false">
Leave
</Button>
<Button class="flex-1 transition-all duration-300" :variant="isReady ? 'outline' : 'default'"
:class="!isReady ? 'bg-purple-600 hover:bg-purple-700' : ''" @click="toggleReady">
{{ isReady ? 'Cancel Ready' : 'Ready up' }}
</Button>
</div>
</CardContent>
</Card>
</div>
</div>
</template>