289 lines
8.5 KiB
Vue
289 lines
8.5 KiB
Vue
<script setup lang="ts">
|
||
import type { DataTableColumnSource } from 'vuestic-ui';
|
||
import NumberCard from '~/components/showcase/NumberCard.vue';
|
||
|
||
definePageMeta({
|
||
// TODO: re-enable perms
|
||
// middleware: ['admin'],
|
||
layout: 'full'
|
||
});
|
||
|
||
const app = useNuxtApp();
|
||
const { name, id } = app.$slugData.huntSlug!;
|
||
const { data } = await useFetch(`/api/admin/hunt/${id}/showcase`);
|
||
|
||
const { toggle, isFullscreen, isSupported } = useFullscreen();
|
||
|
||
const leaderboardColumns: DataTableColumnSource[] = [
|
||
'place',
|
||
'name',
|
||
'description',
|
||
{
|
||
key: 'score',
|
||
displayFormatFn: (data: number) => Number(data).toLocaleString('de')
|
||
},
|
||
'answers'
|
||
];
|
||
const columns = leaderboardColumns.map((c) =>
|
||
typeof c === 'string' ? c : c.key
|
||
);
|
||
// TODO: colors
|
||
</script>
|
||
|
||
<template>
|
||
<div
|
||
v-if="isSupported"
|
||
class="fixed left-2 top-2 opacity-25 transition-opacity hover:opacity-100"
|
||
>
|
||
<VaButton
|
||
round
|
||
border-color="primary"
|
||
color="onPrimary"
|
||
:icon="isFullscreen ? 'fullscreen_exit' : 'fullscreen'"
|
||
@click="toggle"
|
||
/>
|
||
</div>
|
||
<article
|
||
class="h-dvh w-full snap-y snap-mandatory overflow-y-scroll scroll-smooth"
|
||
v-if="data"
|
||
>
|
||
<section id="title">
|
||
<div
|
||
class="flex flex-col items-center justify-center gap-8 bg-green-300 text-center"
|
||
>
|
||
<h1 class="text-7xl">{{ $t('page.showcase.showcase_for') }}</h1>
|
||
<h1 class="text-9xl font-bold">{{ data.name || name }}</h1>
|
||
<p v-if="data.start || data.end" class="flex flex-row gap-4 text-4xl">
|
||
<span v-if="data.start">{{
|
||
$d(data.start, {
|
||
dateStyle: 'medium',
|
||
timeStyle: 'medium'
|
||
})
|
||
}}</span
|
||
><span v-if="data.start && data.end"> – </span
|
||
><span v-if="data.end">{{
|
||
$d(data.end, {
|
||
dateStyle: 'medium',
|
||
timeStyle: 'medium'
|
||
})
|
||
}}</span>
|
||
</p>
|
||
</div>
|
||
</section>
|
||
<section id="numbers">
|
||
<div class="grid grid-cols-3 gap-4 bg-red-100 p-4">
|
||
<NumberCard
|
||
:title="$t('page.showcase.crew')"
|
||
:count="data._count.members + 1"
|
||
icon="support_agent"
|
||
/>
|
||
<NumberCard
|
||
:title="$t('page.showcase.quests')"
|
||
:count="data._count.quests"
|
||
icon="home"
|
||
/>
|
||
<NumberCard
|
||
:title="$t('page.showcase.users')"
|
||
:count="data.userCount"
|
||
icon="person"
|
||
/>
|
||
<NumberCard
|
||
:title="$t('page.showcase.teams')"
|
||
:count="data._count.teams"
|
||
icon="groups"
|
||
/>
|
||
<NumberCard
|
||
:title="$t('page.showcase.total_points')"
|
||
:count="data.totalScore"
|
||
icon="star"
|
||
/>
|
||
<NumberCard
|
||
:title="$t('page.showcase.pictures')"
|
||
:count="data.pictureCount"
|
||
icon="photo"
|
||
/>
|
||
</div>
|
||
</section>
|
||
<section v-for="quest in data.quests" :key="quest.id" :id="sluggy(quest)">
|
||
<div class="flex flex-col gap-4 bg-yellow-100 p-4">
|
||
<div class="my-8 flex w-full flex-row justify-around">
|
||
<h3 class="text-3xl">{{ quest.title }}</h3>
|
||
<p class="text-xl font-bold">{{ quest.question }}</p>
|
||
<p v-if="quest.textSolution" class="text-3xl">
|
||
{{ quest.textSolution }}
|
||
</p>
|
||
</div>
|
||
<ClientOnly>
|
||
<VaCarousel
|
||
stateful
|
||
:items="quest.answers"
|
||
arrows
|
||
indicators
|
||
lazy
|
||
color="onPrimary"
|
||
indicator-trigger="hover"
|
||
class="w-full flex-grow"
|
||
style="--va-carousel-background: none"
|
||
>
|
||
<template #default="{ item }"
|
||
><VaImage :src="item.picture.url" fit="contain" lazy />
|
||
<p
|
||
class="absolute top-2 mx-auto rounded bg-gray-200/75 px-2 py-1 text-center text-xl"
|
||
>
|
||
{{ item.team.name }} — "{{ item.text }}" —
|
||
{{ item.picture.creator.name }}
|
||
</p></template
|
||
></VaCarousel
|
||
></ClientOnly
|
||
>
|
||
</div>
|
||
</section>
|
||
<section id="leaderboard" v-if="data.leaderboard.length > 3">
|
||
<div class="flex flex-col gap-4 bg-indigo-100">
|
||
<h1 class="mt-8 text-center text-6xl">
|
||
{{ $t('page.showcase.leaderboard') }}
|
||
</h1>
|
||
<VaDataTable
|
||
class="h-full text-2xl"
|
||
virtual-scroller
|
||
striped
|
||
sticky-header
|
||
:items="data.leaderboard.slice(3)"
|
||
:columns="leaderboardColumns"
|
||
>
|
||
<template v-for="col in columns" #[col]
|
||
><span>{{ $t(`page.showcase.table.${col}`) }}</span></template
|
||
>
|
||
<template #cell(place)="{ rowIndex }">
|
||
<strong>{{ rowIndex + 4 }}</strong>
|
||
</template>
|
||
</VaDataTable>
|
||
</div>
|
||
</section>
|
||
<section id="leaderboard-third" v-if="data.leaderboard[2]">
|
||
<div class="place bg-[#AD8A56]">
|
||
<div class="place-left">
|
||
<VaIcon name="looks_3" size="10rem" />
|
||
<h2>{{ $t('page.showcase.third_place') }}</h2>
|
||
</div>
|
||
<div class="place-right">
|
||
<h3>{{ data.leaderboard[2].name }}</h3>
|
||
<p>{{ data.leaderboard[2].description }}</p>
|
||
<NumberCard
|
||
:title="$t('page.showcase.points')"
|
||
:count="data.leaderboard[2].score"
|
||
icon="star"
|
||
/>
|
||
<NumberCard
|
||
:title="$t('page.showcase.answers')"
|
||
:count="data.leaderboard[2].answers"
|
||
icon="question_mark"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section id="leaderboard-second" v-if="data.leaderboard[1]">
|
||
<div class="place bg-[#B4B4B4]">
|
||
<div class="place-left">
|
||
<VaIcon name="looks_two" size="10rem" />
|
||
<h2>{{ $t('page.showcase.second_place') }}</h2>
|
||
</div>
|
||
<div class="place-right">
|
||
<h3>{{ data.leaderboard[1].name }}</h3>
|
||
<p>{{ data.leaderboard[1].description }}</p>
|
||
<NumberCard
|
||
:title="$t('page.showcase.points')"
|
||
:count="data.leaderboard[1].score"
|
||
icon="star"
|
||
/>
|
||
<NumberCard
|
||
:title="$t('page.showcase.answers')"
|
||
:count="data.leaderboard[1].answers"
|
||
icon="question_mark"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section id="leaderboard-winner-teaser" v-if="data.leaderboard[0]">
|
||
<div class="place bg-[#C9B037]">
|
||
<div class="place-left">
|
||
<VaIcon name="looks_one" size="10rem" />
|
||
<h2>{{ $t('page.showcase.first_place') }}</h2>
|
||
</div>
|
||
<div class="place-right">
|
||
<h3 class="bg-white text-white">{{ data.leaderboard[0].name }}</h3>
|
||
<p class="bg-white text-white">
|
||
{{ data.leaderboard[0].description }}
|
||
</p>
|
||
<NumberCard
|
||
:title="$t('page.showcase.points')"
|
||
:count="data.leaderboard[0].score"
|
||
icon="star"
|
||
/>
|
||
<NumberCard
|
||
:title="$t('page.showcase.answers')"
|
||
:count="data.leaderboard[0].answers"
|
||
icon="question_mark"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section id="leaderboard-winner" v-if="data.leaderboard[0]">
|
||
<div class="place bg-[#C9B037]">
|
||
<div class="place-left">
|
||
<VaIcon name="workspace_premium" size="10rem" />
|
||
<h2>{{ $t('page.showcase.first_place') }}</h2>
|
||
</div>
|
||
<div class="place-right">
|
||
<h3>{{ data.leaderboard[0].name }}</h3>
|
||
<p>{{ data.leaderboard[0].description }}</p>
|
||
<NumberCard
|
||
:title="$t('page.showcase.points')"
|
||
:count="data.leaderboard[0].score"
|
||
icon="star"
|
||
/>
|
||
<NumberCard
|
||
:title="$t('page.showcase.answers')"
|
||
:count="data.leaderboard[0].answers"
|
||
icon="question_mark"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</article>
|
||
<article v-else>pending...</article>
|
||
</template>
|
||
|
||
<style scoped>
|
||
section {
|
||
@apply mt-6 h-dvh snap-center p-6 first:mt-0;
|
||
}
|
||
|
||
section > div {
|
||
@apply h-full w-full rounded-lg;
|
||
}
|
||
|
||
.place {
|
||
@apply grid h-full w-full grid-cols-4 items-center p-4;
|
||
}
|
||
|
||
.place-left {
|
||
@apply flex h-full flex-col justify-center gap-4 text-center text-white;
|
||
}
|
||
|
||
.place-right {
|
||
@apply col-span-3 grid h-full grid-cols-2 gap-4 text-center text-white;
|
||
}
|
||
|
||
.place-right h3 {
|
||
@apply mt-auto text-6xl font-bold;
|
||
}
|
||
.place-right p {
|
||
@apply mt-auto text-3xl font-bold;
|
||
}
|
||
|
||
.place h2 {
|
||
@apply text-5xl font-bold;
|
||
}
|
||
</style>
|