feat: switch to spa

This commit is contained in:
2026-09-17 00:56:17 +02:00
parent 15590e5162
commit d104480188
23 changed files with 327 additions and 171 deletions
+22 -21
View File
@@ -1,25 +1,25 @@
<script setup lang="ts">
import { ref, onMounted, computed } from "vue";
import { useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
import { useTitle } from "@vueuse/core";
import { gamesApiListEventGames } from "./api";
import { gamesApiListEventGames, polylanSubmitterApiGetConfig } from "./api";
import type { GamesApiListEventGamesResponse } from "./api/types.gen";
interface Props {
eventPath: string;
marketEnabled?: string | boolean;
}
const props = withDefaults(defineProps<Props>(), {
marketEnabled: true,
});
const props = defineProps<Props>();
const router = useRouter();
const { t } = useI18n();
useTitle(computed(() => t("home.title")));
const games = ref<GamesApiListEventGamesResponse | undefined>();
const loading = ref(true);
const imageErrors = ref<Set<number>>(new Set());
const marketEnabled = ref(false);
const getHeaderImage = (game: GamesApiListEventGamesResponse[number]) => {
if (game.image) {
@@ -35,24 +35,25 @@ const onImageError = (gameId: number) => {
imageErrors.value.add(gameId);
};
const navigate = (path: string) => {
window.location.href = `/${props.eventPath}${path}`;
const goToMarket = () => {
router.push(`/${props.eventPath}/market`);
};
const isMarketEnabled = () => {
if (typeof props.marketEnabled === 'string') {
return props.marketEnabled === 'true';
}
return Boolean(props.marketEnabled);
const goToGame = (game: GamesApiListEventGamesResponse[number]) => {
const path = game.custom_path ? `/${game.path}` : `/submissions/${game.path}`;
router.push(`/${props.eventPath}${path}`);
};
onMounted(async () => {
// Fetch games enabled for this event
const response = await gamesApiListEventGames({
path: { event_path: props.eventPath },
});
if (response.data) {
games.value = response.data;
const [gamesResponse, configResponse] = await Promise.all([
gamesApiListEventGames({ path: { event_path: props.eventPath } }),
polylanSubmitterApiGetConfig(),
]);
if (gamesResponse.data) {
games.value = gamesResponse.data;
}
if (configResponse.data) {
marketEnabled.value = configResponse.data.market_enabled;
}
loading.value = false;
});
@@ -77,7 +78,7 @@ onMounted(async () => {
<!-- Cards Grid -->
<div v-else class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- Market Card -->
<div v-if="isMarketEnabled()" @click="navigate('/market')"
<div v-if="marketEnabled" @click="goToMarket"
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-gradient-to-br from-purple-600 to-blue-600 flex items-center justify-center">
<i class="mdi mdi-chart-box text-6xl text-white opacity-80"></i>
@@ -97,7 +98,7 @@ onMounted(async () => {
<!-- Game Cards -->
<div v-for="game in games" :key="game.id"
@click="navigate(game.custom_path ? `/${game.path}` : `/submissions/${game.path}`)"
@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"
@@ -111,7 +112,7 @@ onMounted(async () => {
<div class="card-body">
<h2 class="card-title text-2xl">{{ game.name }}</h2>
<div class="card-actions justify-end mt-4">
<button @click="navigate(game.path)" class="btn btn-primary">
<button class="btn btn-primary">
<i class="mdi mdi-arrow-right mr-2"></i>
{{ t("home.submitResults") }}
</button>