Files
polylan-submitter/polylan_submitter/src/Home.vue
T
2026-10-16 05:20:52 +02:00

110 lines
4.2 KiB
Vue

<script setup lang="ts">
import { ref, computed } from "vue";
import { useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
import { useTitle } from "@vueuse/core";
import { useQuery } from "@tanstack/vue-query";
import { eventGamesQuery, eventPlanningQuery } from "@/queries";
import type { GamesApiListEventGamesResponse } from "./api/types.gen";
interface Props {
eventPath: string;
}
const props = defineProps<Props>();
const router = useRouter();
const { t } = useI18n();
useTitle(computed(() => t("home.title")));
const { data: games, isPending: loading } = useQuery(eventGamesQuery(props.eventPath));
const hasSteamGames = computed(() => games.value?.some((game) => game.steam_app_id) ?? false);
const { data: planning } = useQuery(eventPlanningQuery(props.eventPath));
const hasPlanning = computed(() => !!planning.value?.length);
const imageErrors = ref<Set<number>>(new Set());
const getHeaderImage = (game: GamesApiListEventGamesResponse[number]) => {
if (game.image) {
return game.image;
}
if (game.steam_app_id) {
return `https://cdn.akamai.steamstatic.com/steam/apps/${game.steam_app_id}/header.jpg`;
}
return null;
};
const onImageError = (gameId: number) => {
imageErrors.value.add(gameId);
};
const goToGame = (game: GamesApiListEventGamesResponse[number]) => {
const path = game.custom_path ? `/${game.path}` : `/submissions/${game.path}`;
router.push(`/${props.eventPath}${path}`);
};
</script>
<template>
<div class="flex-1 bg-base-300 flex items-center justify-center px-4">
<div class="w-full max-w-7xl">
<!-- Header -->
<div class="text-center mb-12">
<h1 class="text-5xl font-bold mb-4">{{ t("home.title") }}</h1>
<p class="text-xl text-base-content/70">
{{ t("home.subtitle") }}
</p>
</div>
<!-- Loading -->
<div v-if="loading" class="flex justify-center py-20">
<span class="loading loading-spinner loading-lg"></span>
</div>
<!-- Cards Grid -->
<div v-else class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- Game Cards -->
<div v-for="game in games" :key="game.id"
@click="goToGame(game)"
class="card card-xl bg-base-200 shadow-xl hover:shadow-2xl transition-all cursor-pointer transform hover:-translate-y-2 hover:scale-[1.05] hover:bg-base-100 overflow-hidden">
<figure class="relative h-60 bg-base-300 overflow-hidden">
<img v-if="getHeaderImage(game) && !imageErrors.has(game.id)" :src="getHeaderImage(game)!" :alt="game.name"
@error="onImageError(game.id)" class="w-full h-full object-cover" />
<div v-else
class="w-full h-full bg-gradient-to-br from-blue-600 to-blue-400 flex items-center justify-center text-white">
<i class="mdi mdi-gamepad-variant text-5xl"></i>
</div>
<div class="absolute inset-0 bg-black/30 group-hover:bg-black/20 transition-colors"></div>
</figure>
<div class="card-body">
<h2 class="card-title text-2xl">{{ game.name }}</h2>
<p v-if="game.description" class="text-sm text-base-content/70 line-clamp-2" :title="game.description">{{ game.description }}</p>
<div class="card-actions justify-end mt-4">
<button class="btn btn-primary">
<i class="mdi mdi-arrow-right mr-2"></i>
{{ t("home.submitResults") }}
</button>
</div>
</div>
</div>
</div>
<!-- Links to the planning and the Steam games page -->
<div v-if="!loading && (hasPlanning || hasSteamGames)" class="flex flex-wrap justify-center gap-4 mt-12">
<RouterLink v-if="hasPlanning" :to="{ name: 'planning', params: { eventPath } }" class="btn btn-outline">
<i class="mdi mdi-calendar-clock mr-2"></i>
{{ t("home.planning") }}
</RouterLink>
<RouterLink v-if="hasSteamGames" :to="{ name: 'games', params: { eventPath } }" class="btn btn-outline">
<i class="mdi mdi-steam mr-2"></i>
{{ t("home.getTheGames") }}
</RouterLink>
</div>
<!-- Footer -->
<div class="text-center mt-12 text-base-content/50">
<p>{{ t("home.footer") }}</p>
</div>
</div>
</div>
</template>