Add personal scores to song overview

This commit is contained in:
Trenton Zimmer 2026-05-31 16:06:04 -04:00
parent 4bf8050cc6
commit 86f3f52dd9
3 changed files with 140 additions and 12 deletions

View File

@ -11,8 +11,11 @@ export function hydrateScoreData(thisGame, data) {
for (const chartId in songData.charts) {
var chart = songData.charts[chartId];
const maxScore = (chart.data.notecount ?? 5730) * 2;
chart.records = chart?.records.map((v) => ({ ...v, maxScore: maxScore }));
for (const recordIndex in chart.records) {
chart.records = chart?.records?.map((v) => ({
...v,
maxScore: maxScore,
}));
for (const recordIndex in chart?.records) {
var record = chart.records[recordIndex];
record = formatIIDXScore(record.maxScore, record);
chart.records[recordIndex] = record;

View File

@ -1,4 +1,5 @@
import { useMainStore } from "@/stores/main";
const mainStore = useMainStore();
export async function APIGetMusicData(
@ -8,13 +9,25 @@ export async function APIGetMusicData(
oneChart = false,
) {
try {
const params = new URLSearchParams({
game,
version,
});
if (oneChart) {
params.append("oneChart", "true");
}
if (songIds?.length) {
params.append("songIds", songIds.join(","));
}
const data = await mainStore.callApi(
`/music?game=${game}&version=${version}` +
(oneChart ? "&oneChart=true" : ""),
`/music?${params.toString()}`,
"GET",
null,
{ songIds: songIds.toString() },
);
return data.data;
} catch (error) {
console.log("Error fetching music data:", error);
@ -24,11 +37,20 @@ export async function APIGetMusicData(
export async function getAttemptData(game, userId = null) {
try {
const params = new URLSearchParams();
if (userId) {
params.append("userId", userId);
}
const query = params.toString();
const data = await mainStore.callApi(
`/attempts/${game}` + (userId ? `?userId=${userId}` : ""),
`/attempts/${game}${query ? `?${query}` : ""}`,
"GET",
null,
);
return data.data;
} catch (error) {
console.log("Error fetching attempt data:", error);
@ -36,13 +58,26 @@ export async function getAttemptData(game, userId = null) {
}
}
export async function APIGetRecordData(game, userId = null) {
export async function APIGetRecordData(game, userId = null, songId = null) {
try {
const params = new URLSearchParams();
if (userId) {
params.append("userId", userId);
}
if (songId) {
params.append("songId", songId);
}
const query = params.toString();
const data = await mainStore.callApi(
`/records/${game}` + (userId ? `?userId=${userId}` : ""),
`/records/${game}${query ? `?${query}` : ""}`,
"GET",
null,
);
return data.data;
} catch (error) {
console.log("Error fetching record data:", error);

View File

@ -1,7 +1,12 @@
<script setup>
import { reactive, ref, onMounted, computed } from "vue";
import { useRoute, useRouter } from "vue-router";
import { PhMusicNote, PhRanking, PhMedal } from "@phosphor-icons/vue";
import {
PhMusicNote,
PhRanking,
PhMedal,
PhListStar,
} from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
@ -12,17 +17,23 @@ import FormControl from "@/components/FormControl.vue";
import PillTag from "@/components/PillTag.vue";
import GameHeader from "@/components/Cards/GameHeader.vue";
import { APIGetTopScore } from "@/stores/api/music";
import { getGameInfo } from "@/constants";
import { useMainStore } from "@/stores/main.js";
import { APIGetTopScore, APIGetRecordData } from "@/stores/api/music";
import { getGameInfo, GameConstants } from "@/constants";
import { formatDifficulty } from "@/constants/scoreDataFilters";
import { getNestedValue } from "@/constants/values";
import { hydrateScoreData } from "@/helpers/score";
import { formatIIDXScore } from "@/helpers/score/iidx";
import { topScoreHeaders, formatScoreTable } from "@/constants/table/scores";
const mainStore = useMainStore();
const $route = useRoute();
const $router = useRouter();
var gameId = $route.params.game;
var songId = $route.params.songId;
const thisGame = getGameInfo(gameId);
var songData = ref({});
var anyRecords = ref(false);
var personalRecords = ref({});
if (!thisGame) {
$router.push({
@ -37,11 +48,44 @@ onMounted(async () => {
try {
const data = await APIGetTopScore(gameId, songId);
songData.value = hydrateScoreData(thisGame, data);
const personalData = await APIGetRecordData(
gameId,
mainStore.userId,
songData.value?.id,
);
personalRecords.value = formatPersonalRecords(personalData[0]);
} catch (error) {
console.error("Failed to fetch score data:", error);
}
});
function formatPersonalRecords(data) {
let filtered = [];
for (var chart of data.charts) {
if (!chart.record) {
filtered.push(null);
} else {
if (thisGame?.id == GameConstants.IIDX) {
for (const chartId in filtered) {
const maxScore = (chart.data?.notecount ?? 5730) * 2;
var record = chart.record;
record = formatIIDXScore(maxScore, record);
chart.record = record;
songData[chartId] = chart;
}
}
filtered.push(formatScoreTable(thisGame, [chart.record])[0]);
anyRecords.value = true;
}
}
return filtered;
}
const chartSelector = reactive({
currentChart: 0,
});
@ -112,6 +156,49 @@ const navigateToProfile = (item) => {
</div>
</CardBox>
<SectionTitleLine
v-if="anyRecords"
:icon="PhListStar"
title="Personal Records"
main
/>
<div class="grid grid-cols-2 lg:grid-cols-3 gap-4 mb-6">
<template v-for="chart of songData.charts" :key="chart.db_id">
<CardBoxWidget
v-if="
chart.data?.difficulty != 0 &&
thisGame.chartTable[chart.chart] &&
personalRecords[chart.chart]
"
:label="`${thisGame.chartTable[chart.chart]} - ${formatDifficulty(
chart.data?.difficulty,
thisGame.difficultyDenom,
)}`"
small-content
>
<div v-if="record = personalRecords[chart.chart]">
<div class="space-x-2">
<span class="text-2xl font-bold">
{{ record?.points.toLocaleString() }}
</span>
<span>Points</span>
</div>
<div
v-for="header of thisGame?.scoreHeaders"
:key="header"
class="space-x-2"
>
<span class="text-2xl font-bold">
{{ getNestedValue(record, header.value) ?? "0" }}
</span>
<span>{{ header.text }}</span>
</div>
</div>
</CardBoxWidget>
</template>
</div>
<SectionTitleLine :icon="PhRanking" title="Top Records" main />
<div class="grid grid-cols-2 lg:grid-cols-5 gap-4 mb-6">
<template v-for="chart of songData.charts" :key="chart.db_id">
@ -119,7 +206,10 @@ const navigateToProfile = (item) => {
v-if="
chart.data?.difficulty != 0 && thisGame.chartTable[chart.chart]
"
:label="`${thisGame.chartTable[chart.chart]}`"
:label="`${thisGame.chartTable[chart.chart]} - ${formatDifficulty(
chart.data?.difficulty,
thisGame.difficultyDenom,
)}`"
small-content
>{{
chart.records[0]