Files
pichunt/app/pages/hunt/[huntSlug]/showcase.vue
T

316 lines
9.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { sluggy } from '#imports';
import type { DataTableColumnSource } from 'vuestic-ui';
import NumberCard from '~/components/showcase/NumberCard.vue';
import { fullDate } from '~~/server/utils/date';
definePageMeta({
layout: 'full'
});
const { t, d } = useI18n();
const localePath = useLocalePath();
const { tKey, hasKey, tSluggy } = useI18nKey();
const app = useNuxtApp();
const { name, id } = app.$slugData.huntSlug!;
const { data, error } = await useFetch(`/api/hunt/${id}/showcase`);
useHead({
title: t('layouts.two_title', {
title: tKey(data.value ?? { name_de: name, name_en: name }, 'name').value,
subtitle: t('page.showcase.title')
})
});
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>
<!-- eslint-disable-next-line vue/no-multiple-template-root -->
<article
v-if="data"
class="h-dvh w-full snap-y snap-mandatory overflow-y-scroll scroll-smooth"
>
<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">
{{ hasKey(data, 'name') ? tKey(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, fullDate) }}</span
><span v-if="data.start && data.end"> </span
><span v-if="data.end">{{ d(data.end, fullDate) }}</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"
:id="tSluggy(quest).value"
:key="quest.id"
>
<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">{{ tKey(quest, 'title') }}</h3>
<p v-if="hasKey(quest, 'question')" class="text-xl font-bold">
{{ tKey(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 v-if="data.leaderboard.length > 3" id="leaderboard">
<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] :key="col"
><span>{{ t(`page.showcase.table.${col}`) }}</span></template
>
<template #cell(place)="{ rowIndex }">
<strong>{{ rowIndex + 4 }}</strong>
</template>
</VaDataTable>
</div>
</section>
<section v-if="data.leaderboard[2]" id="leaderboard-third">
<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 v-if="data.leaderboard[1]" id="leaderboard-second">
<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 v-if="data.leaderboard[0]" id="leaderboard-winner-teaser">
<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-transparent">
{{ data.leaderboard[0].name }}
</h3>
<p class="bg-white text-transparent">
{{ 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 v-if="data.leaderboard[0]" id="leaderboard-winner">
<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-if="error"
class="flex h-dvh w-full flex-col items-center justify-center text-center text-2xl"
>
<VaAlert color="danger" class="w-[90%]">{{
t('page.common.not_revealed')
}}</VaAlert>
<VaAlert v-if="error.statusCode !== 403" color="danger" class="w-[90%]">{{
error
}}</VaAlert
><VaButton
icon="arrow_back"
:to="localePath(`/hunt/${sluggy($nuxt.$slugData.huntSlug!)}`)"
>{{ t('page.common.back_to_hunt') }}</VaButton
>
</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>