mirror of
https://github.com/PhaseII-eAmusement-Network/PhaseWeb3-Vue.git
synced 2026-09-11 18:55:18 -05:00
Add first test web design for p2 rerun
This commit is contained in:
@@ -33,6 +33,10 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disableHover: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const tag = computed(() => {
|
||||
@@ -72,11 +76,16 @@ const cardStyle = `
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
`;
|
||||
|
||||
const hoverClass = props.disableHover
|
||||
? null
|
||||
: "hover:cursor-pointer hover:outline outline-blue-500/50";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="flex rounded-2xl hover:scale-[1.01] hover:shadow-xl hover:outline outline-blue-500/50 hover:bg-slate-500 hover:dark:bg-slate-950 hover:cursor-pointer transition-all duration-100"
|
||||
class="flex rounded-2xl hover:scale-[1.01] hover:shadow-xl hover:bg-slate-500 hover:dark:bg-slate-950 transition-all duration-100"
|
||||
:class="hoverClass"
|
||||
:style="cardStyle"
|
||||
@click="loadGamePage()"
|
||||
>
|
||||
|
||||
56
src/components/Charts/monthly.js
Normal file
56
src/components/Charts/monthly.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import { chartColors } from "@/components/Charts/chart.config";
|
||||
|
||||
const createDataset = (color, label, data) => ({
|
||||
fill: false,
|
||||
borderColor: chartColors.default[color],
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: chartColors.default[color],
|
||||
pointBorderColor: "rgba(255,255,255,0)",
|
||||
pointHoverBackgroundColor: chartColors.default[color],
|
||||
pointBorderWidth: 20,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBorderWidth: 15,
|
||||
pointRadius: 4,
|
||||
tension: 0.5,
|
||||
cubicInterpolationMode: "default",
|
||||
label,
|
||||
data,
|
||||
});
|
||||
|
||||
export const generateMonthlyChartData = () => {
|
||||
const labels = [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
];
|
||||
|
||||
return {
|
||||
labels,
|
||||
datasets: [
|
||||
createDataset(
|
||||
"info",
|
||||
"Plays",
|
||||
[145, 168, 192, 210, 185, 230, 244, 267, 289, 301, 315, 338],
|
||||
),
|
||||
createDataset(
|
||||
"danger",
|
||||
"Attempts",
|
||||
[85, 92, 108, 115, 101, 128, 132, 140, 156, 170, 181, 194],
|
||||
),
|
||||
createDataset(
|
||||
"primary",
|
||||
"Records",
|
||||
[14, 17, 13, 19, 16, 22, 24, 28, 26, 31, 35, 39],
|
||||
),
|
||||
],
|
||||
};
|
||||
};
|
||||
142
src/components/Rerun/DatesStory.vue
Normal file
142
src/components/Rerun/DatesStory.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
import { PhCalendar, PhChartLine } from "@phosphor-icons/vue";
|
||||
import SectionTitleLine from "@/components/SectionTitleLine.vue";
|
||||
import CardBox from "@/components/CardBox.vue";
|
||||
import LineChart from "@/components/Charts/LineChart.vue";
|
||||
import RerunLogo from "@/components/Rerun/RerunLogo.vue";
|
||||
import StatsBox from "@/components/Rerun/StatsBox.vue";
|
||||
import { generateMonthlyChartData } from "@/components/Charts/monthly";
|
||||
|
||||
defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
},
|
||||
});
|
||||
|
||||
const show = ref(false);
|
||||
const chartData = generateMonthlyChartData();
|
||||
|
||||
onMounted(() => {
|
||||
requestAnimationFrame(() => {
|
||||
show.value = true;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative flex h-full flex-col px-4 py-8 max-w-3xl md:min-w-lg">
|
||||
<div class="absolute left-4 top-4">
|
||||
<RerunLogo small :year="data.year" />
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-50 ease-out"
|
||||
enter-from-class="opacity-0 translate-y-4"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show" class="mt-12 -mb-6">
|
||||
<SectionTitleLine
|
||||
:icon="PhCalendar"
|
||||
color="text-orange-300"
|
||||
title="Notable Dates"
|
||||
main
|
||||
/>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<div class="mt-6 grid w-full gap-5">
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[200ms]"
|
||||
enter-from-class="opacity-0 translate-y-6"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<StatsBox
|
||||
header="Your first play..."
|
||||
footer="Started with DDR, and you never stopped."
|
||||
suffix="(14 plays, 30 scores, 10 records)"
|
||||
>
|
||||
1/1/2026
|
||||
</StatsBox>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[300ms]"
|
||||
enter-from-class="opacity-0 translate-y-6"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<StatsBox
|
||||
header="Your best day..."
|
||||
footer="What a day!"
|
||||
suffix="(14 plays, 30 scores, 10 records)"
|
||||
>
|
||||
3/3/2026
|
||||
</StatsBox>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[300ms]"
|
||||
enter-from-class="opacity-0 translate-y-6"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<StatsBox
|
||||
header="Your best month..."
|
||||
footer="You're truly dedicated."
|
||||
suffix="(14 plays, 30 scores, 10 records)"
|
||||
>
|
||||
December
|
||||
</StatsBox>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[400ms]"
|
||||
enter-from-class="opacity-0 translate-y-6"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<StatsBox
|
||||
header="Your latest play..."
|
||||
footer="Wrapping it up with IIDX."
|
||||
suffix="(14 plays, 30 scores, 10 records)"
|
||||
>
|
||||
12/25/2026
|
||||
</StatsBox>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[700ms] ease-out"
|
||||
enter-from-class="opacity-0 translate-y-4"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show" class="mt-6">
|
||||
<SectionTitleLine
|
||||
:icon="PhChartLine"
|
||||
color="text-sky-300"
|
||||
title="Monthly Breakdown"
|
||||
main
|
||||
/>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[800ms] ease-out"
|
||||
enter-from-class="opacity-0 translate-y-4"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<CardBox>
|
||||
<LineChart :data="chartData" class="h-50" />
|
||||
</CardBox>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
113
src/components/Rerun/FinalStory.vue
Normal file
113
src/components/Rerun/FinalStory.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<script setup>
|
||||
import { PhCalendar, PhJoystick, PhTrendUp } from "@phosphor-icons/vue";
|
||||
import SectionTitleLine from "@/components/SectionTitleLine.vue";
|
||||
import CardBoxWidget from "@/components/CardBoxWidget.vue";
|
||||
import GameTitleLine from "@/components/GameTitleLine.vue";
|
||||
import { GameConstants, getGameInfo } from "@/constants";
|
||||
|
||||
defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
},
|
||||
});
|
||||
|
||||
const gameInfo = getGameInfo(GameConstants.DDR);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<SectionTitleLine
|
||||
:icon="PhTrendUp"
|
||||
color="text-emerald-300"
|
||||
title="Overall Stats"
|
||||
main
|
||||
/>
|
||||
|
||||
<div
|
||||
class="grid grid-cols-2 sm:grid-cols-3 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 3xl:grid-cols-5 mb-6"
|
||||
>
|
||||
<CardBoxWidget label="Yearly Plays" :number="100" />
|
||||
<CardBoxWidget label="Unique Games" :number="3" />
|
||||
<CardBoxWidget label="Top Game">DDR</CardBoxWidget>
|
||||
<CardBoxWidget label="Top Song">Din Don Dan</CardBoxWidget>
|
||||
<CardBoxWidget label="Yearly Records" :number="3" />
|
||||
<CardBoxWidget label="Yearly Attempts" :number="3" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<SectionTitleLine
|
||||
:icon="PhCalendar"
|
||||
color="text-orange-300"
|
||||
title="Notable Days"
|
||||
main
|
||||
/>
|
||||
|
||||
<div
|
||||
class="grid grid-cols-2 sm:grid-cols-3 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 3xl:grid-cols-5 mb-6"
|
||||
>
|
||||
<CardBoxWidget label="Most Played Day">
|
||||
1/1/2026, with 30 plays
|
||||
</CardBoxWidget>
|
||||
<CardBoxWidget label="Most Games Day">
|
||||
1/1/2026, with 3 games
|
||||
</CardBoxWidget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<SectionTitleLine
|
||||
:icon="PhCalendar"
|
||||
color="text-rose-300"
|
||||
title="Monthly Breakdown"
|
||||
main
|
||||
/>
|
||||
|
||||
<div
|
||||
class="grid grid-cols-2 sm:grid-cols-3 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 3xl:grid-cols-5 mb-6"
|
||||
>
|
||||
<CardBoxWidget label="Most Played Month">
|
||||
January, with 300 plays
|
||||
</CardBoxWidget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<SectionTitleLine
|
||||
:icon="PhJoystick"
|
||||
color="text-cyan-300"
|
||||
title="Game Breakdown"
|
||||
main
|
||||
/>
|
||||
|
||||
<GameTitleLine :title="gameInfo.name" :path="gameInfo.icon" />
|
||||
|
||||
<div
|
||||
class="grid grid-cols-2 sm:grid-cols-3 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 3xl:grid-cols-5 mb-6"
|
||||
>
|
||||
<CardBoxWidget label="Most Played Song"> Din Don Dan </CardBoxWidget>
|
||||
<CardBoxWidget label="Yearly Plays" :number="25" />
|
||||
<CardBoxWidget label="Yearly Records" :number="3" />
|
||||
<CardBoxWidget label="Yearly Attempts" :number="3" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<SectionTitleLine
|
||||
:icon="PhJoystick"
|
||||
color="text-fuchsia-300"
|
||||
title="Location Statistics"
|
||||
main
|
||||
/>
|
||||
|
||||
<div
|
||||
class="grid grid-cols-2 sm:grid-cols-3 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 3xl:grid-cols-5 mb-6"
|
||||
>
|
||||
<CardBoxWidget label="Most Visited Arcade">
|
||||
Boss Battle Games
|
||||
</CardBoxWidget>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
128
src/components/Rerun/GameIntroStory.vue
Normal file
128
src/components/Rerun/GameIntroStory.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
import { PhJoystick, PhPlus } from "@phosphor-icons/vue";
|
||||
import SectionTitleLine from "@/components/SectionTitleLine.vue";
|
||||
import CardBoxGameStat from "@/components/CardBoxGameStat.vue";
|
||||
import RerunLogo from "@/components/Rerun/RerunLogo.vue";
|
||||
import StatsBox from "@/components/Rerun/StatsBox.vue";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
},
|
||||
});
|
||||
|
||||
const show = ref(false);
|
||||
onMounted(() => {
|
||||
requestAnimationFrame(() => {
|
||||
show.value = true;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative flex h-full flex-col px-4 py-8 max-w-3xl md:min-w-lg">
|
||||
<div class="absolute left-4 top-4">
|
||||
<RerunLogo small :year="data.year" />
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-50 ease-out"
|
||||
enter-from-class="opacity-0 translate-y-4"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show" class="mt-12 -mb-6">
|
||||
<SectionTitleLine
|
||||
:icon="PhJoystick"
|
||||
color="text-sky-300"
|
||||
title="Game Breakdowns"
|
||||
main
|
||||
/>
|
||||
<p class="mb-4 text-lg text-slate-300">
|
||||
Let's go through what had you hooked.
|
||||
</p>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<div class="mt-6 grid w-full gap-5">
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[200ms]"
|
||||
enter-from-class="opacity-0 translate-y-6"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<StatsBox
|
||||
header="You played..."
|
||||
footer="We're impressed!"
|
||||
suffix="unique games"
|
||||
:number="props.data.profiles?.length ?? 0"
|
||||
/>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[400ms] ease-out"
|
||||
enter-from-class="opacity-0 translate-y-4"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div
|
||||
v-if="show"
|
||||
class="grid grid-flow-row auto-rows-auto grid-cols-2 md:grid-cols-2 gap-5 my-6"
|
||||
>
|
||||
<CardBoxGameStat
|
||||
v-for="profile of props.data.profiles"
|
||||
:key="profile.game"
|
||||
:game="profile.game"
|
||||
:value="profile.data.total_plays"
|
||||
:profile-name="profile?.username"
|
||||
disable-local-click
|
||||
disable-hover
|
||||
type="plays"
|
||||
/>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[500ms] ease-out"
|
||||
enter-from-class="opacity-0 translate-y-4"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show" class="mt-2 -mb-6">
|
||||
<SectionTitleLine
|
||||
:icon="PhPlus"
|
||||
color="text-red-300"
|
||||
title="New Games"
|
||||
main
|
||||
/>
|
||||
<p class="mb-4 text-lg text-slate-300">
|
||||
These games were new to you this year.
|
||||
</p>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[700ms] ease-out"
|
||||
enter-from-class="opacity-0 translate-y-4"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div
|
||||
v-if="show"
|
||||
class="grid grid-flow-row auto-rows-auto grid-cols-2 md:grid-cols-2 gap-5 my-6"
|
||||
>
|
||||
<template v-for="profile of props.data.profiles" :key="profile.game">
|
||||
<CardBoxGameStat
|
||||
v-if="profile.data?.new"
|
||||
:game="profile.game"
|
||||
:value="profile.data.total_plays"
|
||||
:profile-name="profile?.username"
|
||||
disable-local-click
|
||||
disable-hover
|
||||
type="plays"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
134
src/components/Rerun/GameStory.vue
Normal file
134
src/components/Rerun/GameStory.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import NumberDynamic from "@/components/NumberDynamic.vue";
|
||||
import RerunLogo from "@/components/Rerun/RerunLogo.vue";
|
||||
import StatsBox from "@/components/Rerun/StatsBox.vue";
|
||||
import GameTitleLine from "@/components/GameTitleLine.vue";
|
||||
import { getGameInfo } from "@/constants";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
},
|
||||
});
|
||||
const profile = props.data?.profile ?? {};
|
||||
|
||||
const show = ref(false);
|
||||
const gameInfo = getGameInfo(profile?.game);
|
||||
|
||||
onMounted(() => {
|
||||
requestAnimationFrame(() => {
|
||||
show.value = true;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative flex h-full flex-col px-4 py-8 max-w-3xl md:min-w-lg">
|
||||
<div class="absolute left-4 top-4">
|
||||
<RerunLogo small :year="data.year" />
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-150 ease-out"
|
||||
enter-from-class="opacity-0 translate-y-8 scale-95"
|
||||
enter-to-class="opacity-100 translate-y-0 scale-100"
|
||||
>
|
||||
<GameTitleLine
|
||||
v-if="show"
|
||||
:title="gameInfo.name"
|
||||
:path="gameInfo.icon"
|
||||
class="mt-12 -mb-6"
|
||||
/>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-300 ease-out"
|
||||
enter-from-class="opacity-0 translate-y-4"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<div class="text-center">
|
||||
<div class="">
|
||||
<p class="mb-4 text-lg text-slate-300">Your #1 game this year</p>
|
||||
<h1 class="text-2xl">You had...</h1>
|
||||
<NumberDynamic
|
||||
class="text-8xl mb-2 font-black text-emerald-600"
|
||||
:value="1000"
|
||||
/>
|
||||
<h1 class="text-3xl font-bold">plays</h1>
|
||||
</div>
|
||||
|
||||
<hr class="border-t my-4 w-full" />
|
||||
|
||||
<div class="flex gap-4 w-full justify-center my-4">
|
||||
<div>
|
||||
<NumberDynamic
|
||||
class="text-4xl mb-2 font-black text-pink-300"
|
||||
:value="4000"
|
||||
/>
|
||||
<h1 class="text-3xl font-bold">scores</h1>
|
||||
</div>
|
||||
<div class="md:border-r" />
|
||||
<div>
|
||||
<NumberDynamic
|
||||
class="text-4xl mb-2 font-black text-amber-400"
|
||||
:value="1000"
|
||||
/>
|
||||
<h1 class="text-3xl font-bold">records</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<div class="mt-6 grid w-full gap-5">
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[500ms]"
|
||||
enter-from-class="opacity-0 translate-y-6"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<StatsBox
|
||||
header="You played..."
|
||||
footer="You were quite diverse!"
|
||||
suffix="unique games"
|
||||
:number="6"
|
||||
/>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[700ms]"
|
||||
enter-from-class="opacity-0 translate-y-6"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<StatsBox
|
||||
header="Your favorite was..."
|
||||
footer="You just kept coming back for more."
|
||||
suffix="(400 plays)"
|
||||
>
|
||||
DanceDanceRevolution
|
||||
</StatsBox>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[800ms]"
|
||||
enter-from-class="opacity-0 translate-y-6"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<StatsBox
|
||||
header="One song to rule them all..."
|
||||
footer="It must've been stuck in your head."
|
||||
suffix="(100 plays)"
|
||||
>
|
||||
Din Don Dan
|
||||
</StatsBox>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
135
src/components/Rerun/GeneralStory.vue
Normal file
135
src/components/Rerun/GeneralStory.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import NumberDynamic from "@/components/NumberDynamic.vue";
|
||||
import UserCard from "@/components/UserCard.vue";
|
||||
import RerunLogo from "@/components/Rerun/RerunLogo.vue";
|
||||
import StatsBox from "@/components/Rerun/StatsBox.vue";
|
||||
|
||||
defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
},
|
||||
});
|
||||
|
||||
const show = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
requestAnimationFrame(() => {
|
||||
show.value = true;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative flex h-full flex-col px-4 py-8 max-w-3xl md:min-w-lg">
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 ease-out"
|
||||
enter-from-class="opacity-0 -translate-y-6"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show" class="absolute left-4 top-4">
|
||||
<RerunLogo small :year="data.year" />
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-150 ease-out"
|
||||
enter-from-class="opacity-0 translate-y-8 scale-95"
|
||||
enter-to-class="opacity-100 translate-y-0 scale-100"
|
||||
>
|
||||
<UserCard v-if="show" class="mt-10 scale-[90%]" use-small even-smaller />
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-300 ease-out"
|
||||
enter-from-class="opacity-0 translate-y-4"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<div class="text-center">
|
||||
<div class="mt-4">
|
||||
<p class="mb-4 text-lg text-slate-300">
|
||||
From the first play on 1/1/2026, this is what your year looked
|
||||
like.
|
||||
</p>
|
||||
<h1 class="text-2xl">You had...</h1>
|
||||
<NumberDynamic
|
||||
class="text-8xl mb-2 font-black text-emerald-600"
|
||||
:value="1000"
|
||||
/>
|
||||
<h1 class="text-3xl font-bold">plays</h1>
|
||||
</div>
|
||||
|
||||
<hr class="border-t my-4 w-full" />
|
||||
|
||||
<div class="flex gap-4 w-full justify-center my-4">
|
||||
<div>
|
||||
<NumberDynamic
|
||||
class="text-4xl mb-2 font-black text-pink-300"
|
||||
:value="4000"
|
||||
/>
|
||||
<h1 class="text-3xl font-bold">scores</h1>
|
||||
</div>
|
||||
<div class="md:border-r" />
|
||||
<div>
|
||||
<NumberDynamic
|
||||
class="text-4xl mb-2 font-black text-amber-400"
|
||||
:value="1000"
|
||||
/>
|
||||
<h1 class="text-3xl font-bold">records</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<div class="mt-6 grid w-full gap-5">
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[500ms]"
|
||||
enter-from-class="opacity-0 translate-y-6"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<StatsBox
|
||||
header="You played..."
|
||||
footer="You were quite diverse!"
|
||||
suffix="unique games"
|
||||
:number="6"
|
||||
/>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[700ms]"
|
||||
enter-from-class="opacity-0 translate-y-6"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<StatsBox
|
||||
header="Your favorite was..."
|
||||
footer="You just kept coming back for more."
|
||||
suffix="(400 plays)"
|
||||
>
|
||||
DanceDanceRevolution
|
||||
</StatsBox>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-700 delay-[800ms]"
|
||||
enter-from-class="opacity-0 translate-y-6"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
>
|
||||
<div v-if="show">
|
||||
<StatsBox
|
||||
header="One song to rule them all..."
|
||||
footer="It must've been stuck in your head."
|
||||
suffix="(100 plays)"
|
||||
>
|
||||
Din Don Dan
|
||||
</StatsBox>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
19
src/components/Rerun/IntroStory.vue
Normal file
19
src/components/Rerun/IntroStory.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<script setup>
|
||||
import RerunLogo from "@/components/Rerun/RerunLogo.vue";
|
||||
|
||||
defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RerunLogo />
|
||||
<p class="text-lg">Let's run back your {{ data.year }}.</p>
|
||||
<hr class="border-t mt-6 w-full" />
|
||||
|
||||
<p class="my-2 font-light tracking-wide italic">
|
||||
You can navigate with your arrow keys
|
||||
</p>
|
||||
</template>
|
||||
37
src/components/Rerun/RerunLogo.vue
Normal file
37
src/components/Rerun/RerunLogo.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
import { PhRewind } from "@phosphor-icons/vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
|
||||
const props = defineProps({
|
||||
small: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
year: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const iconWidth = computed(() => (props.small ? "w-8" : "w-20"));
|
||||
const iconSize = computed(() => (props.small ? 24 : 45));
|
||||
const titleClass = computed(() =>
|
||||
props.small ? "text-xl font-bold" : "text-3xl font-bold",
|
||||
);
|
||||
const wrapperClass = computed(() =>
|
||||
props.small ? "flex items-center gap-2" : "text-center space-y-2",
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="wrapperClass">
|
||||
<BaseIcon
|
||||
:icon="PhRewind"
|
||||
color="text-amber-400"
|
||||
:w="iconWidth"
|
||||
:size="iconSize"
|
||||
/>
|
||||
<h1 :class="titleClass"><samp>PhaseII</samp> Rerun {{ year }}</h1>
|
||||
</div>
|
||||
</template>
|
||||
68
src/components/Rerun/StatsBox.vue
Normal file
68
src/components/Rerun/StatsBox.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<script setup>
|
||||
import NumberDynamic from "@/components/NumberDynamic.vue";
|
||||
|
||||
const props = defineProps({
|
||||
header: {
|
||||
type: String,
|
||||
},
|
||||
footer: {
|
||||
type: String,
|
||||
},
|
||||
number: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
icon: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
prefix: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
suffix: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: "text-gray-500 dark:text-slate-400",
|
||||
},
|
||||
numColor: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
iconColor: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="rounded-2xl border border-slate-400/10 bg-slate-400/5 hover:bg-slate-400/10 transition-colors px-6 py-5"
|
||||
>
|
||||
<div class="text-sm tracking-wider text-slate-400">
|
||||
{{ props.header }}
|
||||
</div>
|
||||
|
||||
<div class="text-2xl font-bold">
|
||||
<NumberDynamic
|
||||
v-if="props.number !== null"
|
||||
:value="props.number"
|
||||
:prefix="props.prefix"
|
||||
:suffix="props.suffix"
|
||||
/>
|
||||
|
||||
<div v-else class="grid md:flex gap-x-2 items-baseline">
|
||||
<slot />
|
||||
<div class="text-sm text-slate-300">
|
||||
{{ suffix }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-slate-400">{{ props.footer }}</div>
|
||||
</div>
|
||||
</template>
|
||||
82
src/components/Rerun/StoryCard.vue
Normal file
82
src/components/Rerun/StoryCard.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, onUnmounted } from "vue";
|
||||
import CardBox from "@/components/CardBox.vue";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import BaseButtons from "@/components/BaseButtons.vue";
|
||||
import { PhArrowLeft, PhArrowRight } from "@phosphor-icons/vue";
|
||||
|
||||
const props = defineProps({
|
||||
current: Number,
|
||||
total: Number,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["next", "previous"]);
|
||||
|
||||
const progress = computed(() => {
|
||||
return (props.current / (props.total - 1)) * 100;
|
||||
});
|
||||
|
||||
function onKeyDown(event) {
|
||||
switch (event.key) {
|
||||
case "ArrowRight":
|
||||
case " ":
|
||||
case "Enter":
|
||||
emit("next");
|
||||
break;
|
||||
|
||||
case "ArrowLeft":
|
||||
emit("previous");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener("keydown", onKeyDown);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("keydown", onKeyDown);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CardBox
|
||||
class="w-full md:w-auto rounded-none md:rounded-xl p-4"
|
||||
color-prop="bg-slate-800 dark:bg-slate-800"
|
||||
has-table
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<template v-if="current">
|
||||
<div class="space-y-2 mt-8">
|
||||
<p class="text-sm opacity-70">{{ current }} / {{ total - 1 }}</p>
|
||||
<div class="h-1 bg-gray-700">
|
||||
<div
|
||||
class="h-full bg-emerald-400 transition-all duration-500"
|
||||
:style="{ width: `${progress}%` }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<BaseButtons type="justify-center gap-4" class-addon="my-6">
|
||||
<BaseButton
|
||||
v-if="current"
|
||||
label="Back"
|
||||
color="info"
|
||||
:icon="PhArrowLeft"
|
||||
@click="$emit('previous')"
|
||||
/>
|
||||
|
||||
<BaseButton
|
||||
v-if="progress !== 100"
|
||||
:label="current ? 'Next' : `Here we go!`"
|
||||
color="success"
|
||||
:icon="current ? PhArrowRight : null"
|
||||
@click="$emit('next')"
|
||||
/>
|
||||
</BaseButtons>
|
||||
</CardBox>
|
||||
</template>
|
||||
@@ -1,11 +1,14 @@
|
||||
<script setup></script>
|
||||
|
||||
<template>
|
||||
<div class="bg-gray-950 dark:text-slate-100">
|
||||
<div
|
||||
class="relative min-h-screen bg-slate-800 md:bg-slate-950 dark:text-slate-100"
|
||||
>
|
||||
<div
|
||||
class="hidden md:block absolute inset-0 z-0 bg-[radial-gradient(125%_125%_at_50%_10%,#000000_40%,#0d1a36_100%)]"
|
||||
class="pointer-events-none absolute inset-0 hidden md:block z-0 md:bg-[radial-gradient(125%_125%_at_50%_10%,#000000_40%,#0d1a36_100%)]"
|
||||
/>
|
||||
<div class="animated animatedFadeInUp fadeInUp">
|
||||
|
||||
<div class="relative z-10 animated animatedFadeInUp fadeInUp">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -41,6 +41,7 @@ const routes = [
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
requiresGuest: true,
|
||||
title: "Login",
|
||||
},
|
||||
path: "/auth/login",
|
||||
@@ -49,6 +50,7 @@ const routes = [
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
requiresGuest: true,
|
||||
title: "Register",
|
||||
},
|
||||
path: "/auth/register",
|
||||
@@ -57,6 +59,7 @@ const routes = [
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
requiresGuest: true,
|
||||
title: "Reset Password",
|
||||
},
|
||||
path: "/auth/reset",
|
||||
@@ -182,6 +185,19 @@ const routes = [
|
||||
hotReload: true, // disables Hot Reload
|
||||
},
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
requiresAdmin: true,
|
||||
title: "Profile Rerun",
|
||||
},
|
||||
path: "/profile/rerun",
|
||||
name: "profile_rerun",
|
||||
component: () => import("@/views/Profile/RerunView.vue"),
|
||||
options: {
|
||||
hotReload: true, // disables Hot Reload
|
||||
},
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
@@ -488,6 +504,16 @@ router.beforeEach(async (to) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (to.meta.requiresGuest) {
|
||||
const validSession = await mainStore.loadUser();
|
||||
|
||||
if (validSession) {
|
||||
return {
|
||||
name: "dashboard",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (to.meta.requiresAdmin) {
|
||||
const validSession = await mainStore.loadUser();
|
||||
|
||||
|
||||
@@ -240,7 +240,12 @@ const cardBoxes = ref([
|
||||
</div>
|
||||
<CardBoxComponentEmpty v-if="!newsData || !newsData.length" />
|
||||
|
||||
<SectionTitleLine :icon="PhTrendUp" title="Quick Stats" main />
|
||||
<SectionTitleLine
|
||||
:icon="PhTrendUp"
|
||||
title="Quick Stats"
|
||||
color="text-emerald-300"
|
||||
main
|
||||
/>
|
||||
<div
|
||||
class="grid grid-cols-2 sm:grid-cols-3 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 3xl:grid-cols-5 mb-6"
|
||||
>
|
||||
|
||||
111
src/views/Profile/RerunView.vue
Normal file
111
src/views/Profile/RerunView.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<script setup>
|
||||
import { ref, computed } from "vue";
|
||||
import LayoutGuest from "@/layouts/LayoutGuest.vue";
|
||||
import StoryCard from "@/components/Rerun/StoryCard.vue";
|
||||
import IntroStory from "@/components/Rerun/IntroStory.vue";
|
||||
import GeneralStory from "@/components/Rerun/GeneralStory.vue";
|
||||
import DatesStory from "@/components/Rerun/DatesStory.vue";
|
||||
import GameIntroStory from "@/components/Rerun/GameIntroStory.vue";
|
||||
import GameStory from "@/components/Rerun/GameStory.vue";
|
||||
import FinalStory from "@/components/Rerun/FinalStory.vue";
|
||||
import { GameConstants } from "@/constants";
|
||||
|
||||
const ReRunYear = 2049;
|
||||
const current = ref(0);
|
||||
const profiles = [
|
||||
{
|
||||
game: GameConstants.DDR,
|
||||
username: "TRMAZI",
|
||||
data: {
|
||||
new: true,
|
||||
total_plays: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
game: GameConstants.DDROMNI,
|
||||
username: "TRMAZI",
|
||||
data: {
|
||||
total_plays: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
game: GameConstants.IIDX,
|
||||
username: "TRMAZI",
|
||||
data: {
|
||||
total_plays: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
game: GameConstants.SDVX,
|
||||
username: "TRMAZI",
|
||||
data: {
|
||||
total_plays: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
game: GameConstants.TSUMTSUM,
|
||||
username: "TRMAZI",
|
||||
data: {
|
||||
total_plays: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
game: GameConstants.REFLEC_BEAT,
|
||||
username: "TRMAZI",
|
||||
data: {
|
||||
total_plays: 100,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const baseData = {
|
||||
year: ReRunYear,
|
||||
};
|
||||
|
||||
var cards = [
|
||||
{ card: IntroStory },
|
||||
{ card: GeneralStory },
|
||||
{ card: DatesStory },
|
||||
{
|
||||
card: GameIntroStory,
|
||||
data: {
|
||||
...baseData,
|
||||
profiles: profiles,
|
||||
},
|
||||
},
|
||||
];
|
||||
for (const profile of profiles) {
|
||||
cards.push({
|
||||
card: GameStory,
|
||||
data: {
|
||||
...baseData,
|
||||
profile,
|
||||
},
|
||||
});
|
||||
}
|
||||
cards.push({ card: FinalStory });
|
||||
|
||||
const CurrentStory = computed(() => cards[current.value].card);
|
||||
const CurrentData = computed(() => cards[current.value].data ?? baseData);
|
||||
function next() {
|
||||
if (current.value < cards.length - 1) current.value++;
|
||||
}
|
||||
function previous() {
|
||||
if (current.value > 0) current.value--;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LayoutGuest>
|
||||
<div class="flex md:min-h-screen md:items-center md:justify-center md:p-6">
|
||||
<StoryCard
|
||||
:current="current"
|
||||
:total="cards.length"
|
||||
@next="next"
|
||||
@previous="previous"
|
||||
>
|
||||
<component :is="CurrentStory" :key="current" :data="CurrentData" />
|
||||
</StoryCard>
|
||||
</div>
|
||||
</LayoutGuest>
|
||||
</template>
|
||||
Reference in New Issue
Block a user