mirror of
https://github.com/PhaseII-eAmusement-Network/PhaseWeb3-Vue.git
synced 2026-08-24 10:04:18 -05:00
Add additional data to the dashboard, including today stats and score stats. Add score stats to chart.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
VITE_APP_VERSION="3.0.8-shrimplinks"
|
||||
VITE_APP_VERSION="3.0.9"
|
||||
VITE_API_URL="http://10.5.7.5:8000/"
|
||||
VITE_API_KEY="your_api_key_should_be_here"
|
||||
VITE_ASSET_PATH="/assets"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VITE_APP_VERSION="3.0.8-shrimplinks"
|
||||
VITE_APP_VERSION="3.0.9"
|
||||
VITE_API_URL="https://restfulsleep.phaseii.network"
|
||||
VITE_API_KEY="your_api_key_should_be_here"
|
||||
VITE_ASSET_PATH="https://cdn.phaseii.network/file/PhaseII/web-assets"
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
"3.0.6": ["- (Major) Replace non-standard game cards with a standardized and new header card. May break some layouts so PLEASE REPORT BUGS!", "- (Feature) Create song detail page, not yet completed or reactive.", "- (Bugfix) Fix issue where deleteSession fails if the session has already been deleted.", "- (Minor) Edit ring color on red buttons", "- (Admin) Add Arcades admin page", "- (Bugfix) Fix incorrect prop types for Notes Radar"],
|
||||
"3.0.7": ["- (Major) Song data page now loads data. This data is quite broken for a few games so EXPECT BUGS!", "- (Optimization) Add in table data for games, still missing some data but the basics are complete now."],
|
||||
"3.0.8": ["- (Minor) Added game event options for Classic IIDX versions"],
|
||||
"3.0.8-shrimplinks": ["- (Bugfix) Fix broken session."]
|
||||
"3.0.8-shrimplinks": ["- (Bugfix) Fix broken session."],
|
||||
"3.0.9": ["- (Minor) Add additional data to the dashboard, including today stats and score stats. Add score stats to chart."]
|
||||
}
|
||||
@@ -27,6 +27,48 @@ const datasetObject = (color, points) => {
|
||||
};
|
||||
};
|
||||
|
||||
const datasetRecordObject = (color, points) => {
|
||||
return {
|
||||
fill: false,
|
||||
borderColor: chartColors.default[color],
|
||||
borderWidth: 2,
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
pointBackgroundColor: chartColors.default[color],
|
||||
pointBorderColor: "rgba(255,255,255,0)",
|
||||
pointHoverBackgroundColor: chartColors.default[color],
|
||||
pointBorderWidth: 20,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBorderWidth: 15,
|
||||
pointRadius: 4,
|
||||
data: points,
|
||||
tension: 0.5,
|
||||
cubicInterpolationMode: "default",
|
||||
label: "Records",
|
||||
};
|
||||
};
|
||||
|
||||
const datasetAttemptObject = (color, points) => {
|
||||
return {
|
||||
fill: false,
|
||||
borderColor: chartColors.default[color],
|
||||
borderWidth: 2,
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
pointBackgroundColor: chartColors.default[color],
|
||||
pointBorderColor: "rgba(255,255,255,0)",
|
||||
pointHoverBackgroundColor: chartColors.default[color],
|
||||
pointBorderWidth: 20,
|
||||
pointHoverRadius: 4,
|
||||
pointHoverBorderWidth: 15,
|
||||
pointRadius: 4,
|
||||
data: points,
|
||||
tension: 0.5,
|
||||
cubicInterpolationMode: "default",
|
||||
label: "Attempts",
|
||||
};
|
||||
};
|
||||
|
||||
const parseArcadeHistory = (users) => {
|
||||
const groupByDay = (timestamps) => {
|
||||
const dayMap = {};
|
||||
@@ -67,7 +109,7 @@ const parseArcadeHistory = (users) => {
|
||||
return { labels: last14Days, data: playCounts };
|
||||
};
|
||||
|
||||
export const generateChartData = (users) => {
|
||||
export const generateChartData = (users, scoreStats = null) => {
|
||||
if (!Array.isArray(users) || users.length === 0) {
|
||||
return {
|
||||
labels: [],
|
||||
@@ -76,9 +118,39 @@ export const generateChartData = (users) => {
|
||||
}
|
||||
|
||||
const { labels, data } = parseArcadeHistory(users);
|
||||
const baseLabels = labels;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
const recordsData = [];
|
||||
const attemptsData = [];
|
||||
|
||||
if (scoreStats?.records && scoreStats?.attempts) {
|
||||
const recordTimestamps = scoreStats.records.map((r) => r.timestamp);
|
||||
const attemptTimestamps = scoreStats.attempts.map((a) => a.timestamp);
|
||||
|
||||
const recordCountsByDay = groupByDay(recordTimestamps);
|
||||
const attemptCountsByDay = groupByDay(attemptTimestamps);
|
||||
|
||||
baseLabels.forEach((day) => {
|
||||
recordsData.push(recordCountsByDay[day] || 0);
|
||||
attemptsData.push(attemptCountsByDay[day] || 0);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
labels,
|
||||
datasets: [datasetObject("info", data)],
|
||||
labels: baseLabels,
|
||||
datasets: [
|
||||
datasetObject("info", data),
|
||||
datasetRecordObject("primary", recordsData),
|
||||
datasetAttemptObject("danger", attemptsData),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ export const useMainStore = defineStore("main", {
|
||||
userArcades: [],
|
||||
profiles: {},
|
||||
userCustomize: {},
|
||||
userScoreStats: {},
|
||||
|
||||
/* Field focus with ctrl+k (to register only once) */
|
||||
isFieldFocusRegistered: false,
|
||||
@@ -76,6 +77,9 @@ export const useMainStore = defineStore("main", {
|
||||
if (payload.customize) {
|
||||
this.userCustomize = payload.customize;
|
||||
}
|
||||
if (payload.userScoreStats) {
|
||||
this.userScoreStats = payload.userScoreStats;
|
||||
}
|
||||
},
|
||||
|
||||
fetch(sampleDataKey) {
|
||||
@@ -268,6 +272,7 @@ export const useMainStore = defineStore("main", {
|
||||
profiles: user.profiles,
|
||||
arcades: user.arcades,
|
||||
customize: user.data?.customize,
|
||||
userScoreStats: user.scoreStats,
|
||||
});
|
||||
this.userLoaded = true;
|
||||
return true;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, ref, onMounted, watch } from "vue";
|
||||
import { computed, ref, onMounted } from "vue";
|
||||
import {
|
||||
mdiGamepad,
|
||||
mdiNewspaperVariant,
|
||||
@@ -41,13 +41,8 @@ function humanReadableTime(timestamp) {
|
||||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
const userProfiles = ref(mainStore.userProfiles);
|
||||
watch(
|
||||
() => mainStore.userProfiles,
|
||||
(newValue) => {
|
||||
userProfiles.value = newValue;
|
||||
}
|
||||
);
|
||||
const userProfiles = computed(() => mainStore.userProfiles);
|
||||
const userScoreStats = computed(() => mainStore.userScoreStats);
|
||||
|
||||
const cumulativePlays = computed(() => {
|
||||
return userProfiles.value.reduce(
|
||||
@@ -104,6 +99,60 @@ function filterUserProfiles(userProfiles) {
|
||||
|
||||
return filteredProfiles;
|
||||
}
|
||||
|
||||
const today = new Date().toISOString().split("T")[0];
|
||||
|
||||
const totalAttempts = computed(() => {
|
||||
return userScoreStats.value?.attempts?.length || 0;
|
||||
});
|
||||
|
||||
const totalRecords = computed(() => {
|
||||
return userScoreStats.value?.records?.length || 0;
|
||||
});
|
||||
|
||||
const todayAttempts = computed(() => {
|
||||
return (
|
||||
userScoreStats.value?.attempts?.filter((a) => {
|
||||
const attemptDate = new Date(a.timestamp * 1000)
|
||||
.toISOString()
|
||||
.split("T")[0];
|
||||
return attemptDate === today;
|
||||
}).length || 0
|
||||
);
|
||||
});
|
||||
|
||||
const todayRecords = computed(() => {
|
||||
return (
|
||||
userScoreStats.value?.records?.filter((r) => {
|
||||
const recordDate = new Date(r.timestamp * 1000)
|
||||
.toISOString()
|
||||
.split("T")[0];
|
||||
return recordDate === today;
|
||||
}).length || 0
|
||||
);
|
||||
});
|
||||
|
||||
const todayPlays = computed(() => {
|
||||
const todayStr = new Date().toISOString().split("T")[0];
|
||||
let total = 0;
|
||||
|
||||
userProfiles.value.forEach((profile) => {
|
||||
const arcadeHistory = profile.data?.arcade_history || {};
|
||||
|
||||
Object.values(arcadeHistory).forEach((machines) => {
|
||||
Object.values(machines).forEach((timestamps) => {
|
||||
timestamps.forEach((ts) => {
|
||||
const dateStr = new Date(ts * 1000).toISOString().split("T")[0];
|
||||
if (dateStr === todayStr) {
|
||||
total++;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return total;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -152,6 +201,14 @@ function filterUserProfiles(userProfiles) {
|
||||
suffix="games"
|
||||
icon-color="text-sky-300"
|
||||
/>
|
||||
<CardBoxWidget
|
||||
v-if="todayPlays"
|
||||
:icon="mdiGamepadOutline"
|
||||
:number="todayPlays"
|
||||
label="Plays Today"
|
||||
suffix="games"
|
||||
icon-color="text-sky-300"
|
||||
/>
|
||||
<CardBoxWidget
|
||||
:icon="mdiFire"
|
||||
:number="longestStreak"
|
||||
@@ -159,6 +216,36 @@ function filterUserProfiles(userProfiles) {
|
||||
suffix="plays"
|
||||
icon-color="text-red-600"
|
||||
/>
|
||||
<CardBoxWidget
|
||||
:icon="mdiGamepadOutline"
|
||||
:number="totalRecords"
|
||||
label="Total Records"
|
||||
suffix="records"
|
||||
icon-color="text-sky-300"
|
||||
/>
|
||||
<CardBoxWidget
|
||||
:icon="mdiGamepadOutline"
|
||||
:number="totalAttempts"
|
||||
label="Total Attempts"
|
||||
suffix="attempts"
|
||||
icon-color="text-sky-300"
|
||||
/>
|
||||
<CardBoxWidget
|
||||
v-if="todayRecords"
|
||||
:icon="mdiGamepadOutline"
|
||||
:number="todayRecords"
|
||||
label="Records Today"
|
||||
suffix="records"
|
||||
icon-color="text-sky-300"
|
||||
/>
|
||||
<CardBoxWidget
|
||||
v-if="todayAttempts"
|
||||
:icon="mdiGamepadOutline"
|
||||
:number="todayAttempts"
|
||||
label="Attempts Today"
|
||||
suffix="attempts"
|
||||
icon-color="text-sky-300"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SectionTitleLine :icon="mdiGamepad" title="Showcase" main />
|
||||
@@ -178,7 +265,10 @@ function filterUserProfiles(userProfiles) {
|
||||
<SectionTitleLine :icon="mdiTrendingUp" title="Play Trends" main />
|
||||
<CardBox class="mb-6">
|
||||
<div v-if="userProfiles">
|
||||
<LineChart :data="generateChartData(userProfiles)" class="h-96" />
|
||||
<LineChart
|
||||
:data="generateChartData(userProfiles, userScoreStats)"
|
||||
class="h-96"
|
||||
/>
|
||||
</div>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
|
||||
Reference in New Issue
Block a user