Add play trends to dashboard
Some checks failed
Build / build (push) Has been cancelled

This commit is contained in:
Trenton Zimmer
2025-01-26 01:56:35 -05:00
parent 24dd3a67df
commit f0c7cf6bb7
4 changed files with 117 additions and 48 deletions

View File

@@ -1,9 +1,9 @@
<script setup>
import CardBox from "@/components/CardBox.vue";
import NumberDynamic from "@/components/NumberDynamic.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import BaseLevel from "@/components/BaseLevel.vue";
import PillTagTrend from "@/components/PillTagTrend.vue";
import IconRounded from "@/components/IconRounded.vue";
defineProps({
number: {
@@ -34,6 +34,14 @@ defineProps({
type: String,
default: "",
},
iconColor: {
type: String,
default: "",
},
iconBGColor: {
type: String,
default: null,
},
trend: {
type: String,
default: null,
@@ -47,35 +55,38 @@ defineProps({
<template>
<CardBox>
<BaseLevel v-if="trend" class="mb-3" mobile>
<PillTagTrend :trend="trend" :trend-type="trendType" small />
</BaseLevel>
<BaseLevel mobile>
<div>
<h3 :class="color" class="text-lg leading-tight mb-1.5">
{{ label }}
</h3>
<h1 class="text-3xl leading-tight font-semibold">
<NumberDynamic
v-if="number"
:class="numColor"
:value="number"
:prefix="prefix"
:suffix="suffix"
/>
<div :class="numColor">
<slot />
</div>
</h1>
</div>
<BaseIcon
<div class="flex gap-4">
<IconRounded
v-if="icon"
:path="icon"
size="48"
w=""
h="h-16"
:class="color"
:icon="icon"
size="30"
:class="iconColor"
bg-color="bg-gray-800"
/>
</BaseLevel>
<div>
<BaseLevel v-if="trend" class="mb-3" mobile>
<PillTagTrend :trend="trend" :trend-type="trendType" small />
</BaseLevel>
<BaseLevel mobile>
<div>
<h3 :class="color" class="text-lg leading-tight mb-1.5">
{{ label }}
</h3>
<h1 class="text-3xl leading-tight font-semibold">
<NumberDynamic
v-if="number"
:class="numColor"
:value="number"
:prefix="prefix"
:suffix="suffix"
/>
<div :class="numColor">
<slot />
</div>
</h1>
</div>
</BaseLevel>
</div>
</div>
</CardBox>
</template>

View File

@@ -6,16 +6,6 @@ export const chartColors = {
},
};
const randomChartData = (n) => {
const data = [];
for (let i = 0; i < n; i++) {
data.push(Math.round(Math.random() * 200));
}
return data;
};
const datasetObject = (color, points) => {
return {
fill: false,
@@ -30,21 +20,65 @@ const datasetObject = (color, points) => {
pointHoverRadius: 4,
pointHoverBorderWidth: 15,
pointRadius: 4,
data: randomChartData(points),
data: points,
tension: 0.5,
cubicInterpolationMode: "default",
label: "Plays",
};
};
export const sampleChartData = (points = 7) => {
const labels = [];
const parseArcadeHistory = (users) => {
const groupByDay = (timestamps) => {
const dayMap = {};
timestamps.forEach((timestamp) => {
const day = new Date(timestamp * 1000).toISOString().split("T")[0];
dayMap[day] = (dayMap[day] || 0) + 1;
});
return dayMap;
};
for (let i = 1; i <= points; i++) {
labels.push(`0${i}`);
const generateLast14Days = () => {
const today = new Date();
const dates = [];
for (let i = 13; i >= 0; i--) {
const date = new Date(today);
date.setDate(today.getDate() - i);
dates.push(date.toISOString().split("T")[0]); // Format: YYYY-MM-DD
}
return dates;
};
const allTimestamps = [];
users.forEach((user) => {
const arcadeHistory = user.data?.arcade_history || {};
Object.values(arcadeHistory).forEach((machines) => {
Object.values(machines).forEach((timestamps) => {
allTimestamps.push(...timestamps);
});
});
});
const playsByDay = groupByDay(allTimestamps);
const last14Days = generateLast14Days();
// Populate data for the last 14 days, filling 0 for days without plays
const playCounts = last14Days.map((day) => playsByDay[day] || 0);
return { labels: last14Days, data: playCounts };
};
export const generateChartData = (users) => {
if (!Array.isArray(users) || users.length === 0) {
return {
labels: [],
datasets: [],
};
}
const { labels, data } = parseArcadeHistory(users);
return {
labels,
datasets: [datasetObject("info", points)],
datasets: [datasetObject("info", data)],
};
};

View File

@@ -23,7 +23,10 @@ const props = defineProps({
type: String,
default: "h-12",
},
bg: Boolean,
bgColor: {
type: String,
default: "bg-slate-900",
},
});
const spanColor = computed(() => props.iconColor);
@@ -36,6 +39,7 @@ const spanColor = computed(() => props.iconColor);
:h="h"
:color="spanColor"
size="24"
class="rounded-full bg-slate-900"
class="rounded-full"
:class="props.bgColor"
/>
</template>

View File

@@ -4,13 +4,19 @@ import {
mdiGamepad,
mdiNewspaperVariant,
mdiChartTimelineVariant,
mdiCounter,
mdiGamepadOutline,
mdiFire,
mdiTrendingUp,
} from "@mdi/js";
import UserCard from "@/components/UserCard.vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import CardBoxWidget from "@/components/CardBoxWidget.vue";
import CardBoxGameStat from "@/components/CardBoxGameStat.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
import LineChart from "@/components/Charts/LineChart.vue";
import { useMainStore } from "@/stores/main";
const mainStore = useMainStore();
@@ -18,6 +24,7 @@ const mainStore = useMainStore();
import CardBoxNews from "@/components/Cards/CardBoxNews.vue";
import CardBoxComponentEmpty from "@/components/CardBoxComponentEmpty.vue";
import { getGameInfo } from "@/constants";
import { generateChartData } from "@/components/Charts/chart.config";
var newsData = ref([]);
onMounted(async () => {
@@ -129,22 +136,28 @@ function filterUserProfiles(userProfiles) {
main
/>
<div
class="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 3xl:grid-cols-6 mb-6"
class="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 3xl:grid-cols-5 mb-6"
>
<CardBoxWidget
:icon="mdiCounter"
:number="cumulativePlays"
label="Cumulative Plays"
suffix="plays"
icon-color="text-emerald-600"
/>
<CardBoxWidget
:icon="mdiGamepadOutline"
:number="userProfiles.length"
label="Games Played"
suffix="games"
icon-color="text-sky-300"
/>
<CardBoxWidget
:icon="mdiFire"
:number="longestStreak"
label="Longest Play Streak"
suffix="plays"
icon-color="text-red-600"
/>
</div>
@@ -161,6 +174,13 @@ function filterUserProfiles(userProfiles) {
type="plays"
/>
</div>
<SectionTitleLine :icon="mdiTrendingUp" title="Play Trends" main />
<CardBox class="mb-6">
<div v-if="userProfiles">
<LineChart :data="generateChartData(userProfiles)" class="h-96" />
</div>
</CardBox>
</SectionMain>
</LayoutAuthenticated>
</template>