feat: switch to spa
This commit is contained in:
@@ -1,36 +1,76 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useTitle } from "@vueuse/core";
|
||||
|
||||
interface ScopeOption {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
import { gamesApiListEventGames, submissionsApiListAvailableScopes } from "@/api";
|
||||
import type { ScopeOut } from "@/api/types.gen";
|
||||
|
||||
interface Props {
|
||||
eventPath: string;
|
||||
gamePath: string;
|
||||
gameName?: string;
|
||||
scopes?: ScopeOption[];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
gameName: "Game",
|
||||
scopes: () => [],
|
||||
});
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
useTitle(computed(() => t("submissionsSelect.title")));
|
||||
|
||||
const goToScope = (scopeId: string) => {
|
||||
window.location.href = `/${props.eventPath}/submissions/${props.gamePath}/${scopeId}`;
|
||||
const gameName = ref("");
|
||||
const scopes = ref<ScopeOut[]>([]);
|
||||
const loading = ref(true);
|
||||
const notAssigned = ref(false);
|
||||
|
||||
const scopeRoute = (scope: ScopeOut) => ({
|
||||
name: "submissions",
|
||||
params: { eventPath: props.eventPath, gamePath: props.gamePath, scopeId: scope.id },
|
||||
});
|
||||
|
||||
const goToScope = (scope: ScopeOut) => {
|
||||
router.push(scopeRoute(scope));
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
const [gamesResponse, scopesResponse] = await Promise.all([
|
||||
gamesApiListEventGames({ path: { event_path: props.eventPath } }),
|
||||
submissionsApiListAvailableScopes({
|
||||
path: { event_path: props.eventPath, game_path: props.gamePath },
|
||||
}),
|
||||
]);
|
||||
|
||||
gameName.value = gamesResponse.data?.find((game) => game.path === props.gamePath)?.name ?? "";
|
||||
|
||||
const availableScopes = scopesResponse.data ?? [];
|
||||
|
||||
// Mirrors the server-side behavior this replaced: skip the picker
|
||||
// straight to the only option, or bounce out if there's nothing to pick.
|
||||
if (availableScopes.length === 1) {
|
||||
router.replace(scopeRoute(availableScopes[0]));
|
||||
return;
|
||||
}
|
||||
if (availableScopes.length === 0) {
|
||||
notAssigned.value = true;
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
scopes.value = availableScopes;
|
||||
loading.value = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-1 bg-base-200 flex items-center justify-center p-4">
|
||||
<div class="card bg-base-100 shadow-lg w-full max-w-md">
|
||||
<div v-if="loading" class="flex justify-center py-20">
|
||||
<span class="loading loading-spinner loading-lg"></span>
|
||||
</div>
|
||||
<div v-else-if="notAssigned" class="text-center max-w-md">
|
||||
<i class="mdi mdi-account-alert-outline text-6xl text-warning"></i>
|
||||
<h2 class="text-2xl font-semibold mt-4">{{ t("submissionsSelect.notAssignedTitle") }}</h2>
|
||||
<p class="text-base-content/70 mt-2">{{ t("submissionsSelect.notAssignedMessage") }}</p>
|
||||
</div>
|
||||
<div v-else class="card bg-base-100 shadow-lg w-full max-w-md">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{{ t("submissionsSelect.title") }}</h2>
|
||||
<p class="text-base-content/70 mb-2">
|
||||
@@ -38,7 +78,7 @@ const goToScope = (scopeId: string) => {
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col gap-2 mt-2">
|
||||
<button v-for="scope in scopes" :key="scope.id" @click="goToScope(scope.id)"
|
||||
<button v-for="scope in scopes" :key="scope.id" @click="goToScope(scope)"
|
||||
class="btn btn-outline justify-start">
|
||||
<i class="mdi mdi-sword-cross mr-2"></i>
|
||||
{{ scope.name }}
|
||||
|
||||
Reference in New Issue
Block a user