fix: multi-events

This commit is contained in:
2026-09-16 00:21:08 +02:00
parent 68de07645a
commit 85e67599af
4 changed files with 78 additions and 70 deletions
+10 -4
View File
@@ -14,6 +14,12 @@ import {
type SortingState, type SortingState,
} from "@tanstack/vue-table"; } from "@tanstack/vue-table";
interface Props {
eventPath: string;
}
const props = defineProps<Props>();
const noitaStore = useNoitaStore(); const noitaStore = useNoitaStore();
const { userInfo, objectives, leaderboard, isLoadingLeaderboard, isUploading } = storeToRefs(noitaStore); const { userInfo, objectives, leaderboard, isLoadingLeaderboard, isUploading } = storeToRefs(noitaStore);
@@ -135,7 +141,7 @@ const submitRun = async () => {
if (uploadedFiles.value.length === 0) return; if (uploadedFiles.value.length === 0) return;
try { try {
await noitaStore.submitRun(uploadedFiles.value); await noitaStore.submitRun(props.eventPath, uploadedFiles.value);
uploadedFiles.value = []; uploadedFiles.value = [];
alert("Run submitted successfully!"); alert("Run submitted successfully!");
} catch (error) { } catch (error) {
@@ -145,12 +151,12 @@ const submitRun = async () => {
}; };
const goHome = () => { const goHome = () => {
window.location.href = "/"; window.location.href = `/${props.eventPath}/`;
}; };
const clearCache = async () => { const clearCache = async () => {
try { try {
await noitaStore.clearCache(); await noitaStore.clearCache(props.eventPath);
alert("Cache cleared successfully!"); alert("Cache cleared successfully!");
} catch (error) { } catch (error) {
console.error("Error clearing cache:", error); console.error("Error clearing cache:", error);
@@ -159,7 +165,7 @@ const clearCache = async () => {
}; };
onMounted(() => { onMounted(() => {
noitaStore.loadUserData(); noitaStore.loadUserData(props.eventPath);
}); });
</script> </script>
+25 -23
View File
@@ -1,6 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import RankBadge from "./RankBadge.vue"; import RankBadge from "./RankBadge.vue";
import { animationsApiResults, polylanSubmitterApiClearCache, polylanSubmitterApiGetUserInfo } from "@/api";
import { useEventPath } from "@/composables/useEventPath";
const eventPath = useEventPath();
interface User { interface User {
id: number; id: number;
@@ -65,10 +69,10 @@ const userInfo = ref({
const fetchResults = async () => { const fetchResults = async () => {
isLoading.value = true; isLoading.value = true;
try { try {
const response = await fetch("/api/results/results"); const response = await animationsApiResults({ path: { event_path: eventPath } });
if (!response.ok) throw new Error("Failed to fetch results"); if (response.error || !response.data) throw new Error("Failed to fetch results");
resultsData.value = await response.json(); resultsData.value = response.data as unknown as ResultsData;
} catch (error) { } catch (error) {
console.error("Error fetching results:", error); console.error("Error fetching results:", error);
} finally { } finally {
@@ -117,16 +121,16 @@ const togglePuzzleExpanded = (puzzleId: number) => {
const clearCache = async () => { const clearCache = async () => {
try { try {
const response = await fetch("/api/cache/clear", { const response = await polylanSubmitterApiClearCache({
method: "POST", query: { event_path: eventPath },
}); });
if (response.ok) { if (!response.error) {
alert("Cache cleared successfully!"); alert("Cache cleared successfully!");
await fetchResults(); await fetchResults();
} else { } else {
const error = await response.json(); const detail = (response.error as { detail?: string })?.detail;
alert(`Error clearing cache: ${error.detail || "Unknown error"}`); alert(`Error clearing cache: ${detail || "Unknown error"}`);
} }
} catch (error) { } catch (error) {
console.error("Error clearing cache:", error); console.error("Error clearing cache:", error);
@@ -136,24 +140,22 @@ const clearCache = async () => {
const loadUserData = async () => { const loadUserData = async () => {
try { try {
const response = await fetch("/api/user"); const response = await polylanSubmitterApiGetUserInfo();
if (response.ok) { const user = response.data;
const user = await response.json(); if (user?.is_authenticated) {
if (user.is_authenticated) { userInfo.value.username = user.username ?? "";
userInfo.value.username = user.username; userInfo.value.isStaff = user.is_staff || false;
userInfo.value.isStaff = user.is_staff || false;
await fetchResults(); await fetchResults();
// Calculate user's rank and stats // Calculate user's rank and stats
const ranking = getOverallRanking(); const ranking = getOverallRanking();
const userRankIndex = ranking.findIndex((u) => u.user.id === user.id); const userRankIndex = ranking.findIndex((u) => u.user.id === user.id);
if (userRankIndex !== -1) { if (userRankIndex !== -1) {
userInfo.value.rank = userRankIndex + 1; userInfo.value.rank = userRankIndex + 1;
userInfo.value.totalPoints = ranking[userRankIndex].totalPoints; userInfo.value.totalPoints = ranking[userRankIndex].totalPoints;
userInfo.value.puzzlesSolved = ranking[userRankIndex].puzzlesSolved; userInfo.value.puzzlesSolved = ranking[userRankIndex].puzzlesSolved;
}
} }
} }
} catch (error) { } catch (error) {
+39 -40
View File
@@ -1,5 +1,12 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue' import { ref } from 'vue'
import {
noitaApiGetResults,
noitaApiGetLeaderboard,
noitaApiSubmitLogFile,
polylanSubmitterApiGetUserInfo,
polylanSubmitterApiClearCache,
} from '@/api'
import type { ObjectivResultOut, LeaderboardEntryOut } from '@/api/types.gen' import type { ObjectivResultOut, LeaderboardEntryOut } from '@/api/types.gen'
export type Objective = ObjectivResultOut export type Objective = ObjectivResultOut
@@ -30,12 +37,12 @@ export const useNoitaStore = defineStore('noita', () => {
const error = ref<string>('') const error = ref<string>('')
// Actions // Actions
const fetchUserResults = async () => { const fetchUserResults = async (eventPath: string) => {
try { try {
const response = await fetch('/api/noita/results') const response = await noitaApiGetResults({ path: { event_path: eventPath } })
if (!response.ok) throw new Error('Failed to fetch results') if (response.error || !response.data) throw new Error('Failed to fetch results')
const results = await response.json() const results = response.data
userInfo.value.score = results.total_score userInfo.value.score = results.total_score
userInfo.value.deathsCount = results.deaths_count userInfo.value.deathsCount = results.deaths_count
userInfo.value.runsSubmitted = results.objectives.length userInfo.value.runsSubmitted = results.objectives.length
@@ -46,14 +53,13 @@ export const useNoitaStore = defineStore('noita', () => {
} }
} }
const fetchLeaderboard = async () => { const fetchLeaderboard = async (eventPath: string) => {
isLoadingLeaderboard.value = true isLoadingLeaderboard.value = true
try { try {
const response = await fetch('/api/noita/leaderboard') const response = await noitaApiGetLeaderboard({ path: { event_path: eventPath } })
if (!response.ok) throw new Error('Failed to fetch leaderboard') if (response.error || !response.data) throw new Error('Failed to fetch leaderboard')
const data = await response.json() leaderboard.value = response.data.leaderboard
leaderboard.value = data.leaderboard
// Find current user's rank // Find current user's rank
const userRank = leaderboard.value.find( const userRank = leaderboard.value.find(
@@ -73,48 +79,41 @@ export const useNoitaStore = defineStore('noita', () => {
} }
} }
const loadUserData = async () => { const loadUserData = async (eventPath: string) => {
try { try {
const response = await fetch('/api/user') const response = await polylanSubmitterApiGetUserInfo()
if (response.ok) { if (response.data?.is_authenticated) {
const user = await response.json() userInfo.value.username = response.data.username ?? ''
if (user.is_authenticated) { userInfo.value.isStaff = response.data.is_staff || false
userInfo.value.username = user.username
userInfo.value.isStaff = user.is_staff || false
}
} }
} catch (err) { } catch (err) {
console.error('Error fetching user info:', err) console.error('Error fetching user info:', err)
} }
await Promise.all([fetchUserResults(), fetchLeaderboard()]) await Promise.all([fetchUserResults(eventPath), fetchLeaderboard(eventPath)])
} }
const submitRun = async (files: File[]) => { const submitRun = async (eventPath: string, files: File[]) => {
if (files.length === 0) return if (files.length === 0) return
isUploading.value = true isUploading.value = true
try { try {
for (const file of files) { for (const file of files) {
const formData = new FormData() const response = await noitaApiSubmitLogFile({
formData.append('file', file) path: { event_path: eventPath },
body: { file },
const response = await fetch('/api/noita/submit', {
method: 'POST',
body: formData,
}) })
if (!response.ok) { if (response.error) {
const errorData = await response.json() const detail = (response.error as { detail?: string })?.detail
throw new Error(errorData.detail || 'Unknown error') throw new Error(detail || 'Unknown error')
} }
const result = await response.json() console.log('Submission successful:', response.data)
console.log('Submission successful:', result)
} }
// Refresh objectives, score, and rank after successful submission // Refresh objectives, score, and rank after successful submission
await Promise.all([fetchUserResults(), fetchLeaderboard()]) await Promise.all([fetchUserResults(eventPath), fetchLeaderboard(eventPath)])
} catch (err) { } catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error' const errorMessage = err instanceof Error ? err.message : 'Unknown error'
error.value = `Error submitting run: ${errorMessage}` error.value = `Error submitting run: ${errorMessage}`
@@ -124,18 +123,18 @@ export const useNoitaStore = defineStore('noita', () => {
} }
} }
const clearCache = async () => { const clearCache = async (eventPath: string) => {
try { try {
const response = await fetch('/api/cache/clear', { const response = await polylanSubmitterApiClearCache({
method: 'POST', query: { event_path: eventPath },
}) })
if (!response.ok) { if (response.error) {
const errorData = await response.json() const detail = (response.error as { detail?: string })?.detail
throw new Error(errorData.detail || 'Unknown error') throw new Error(detail || 'Unknown error')
} }
await Promise.all([fetchUserResults(), fetchLeaderboard()]) await Promise.all([fetchUserResults(eventPath), fetchLeaderboard(eventPath)])
} catch (err) { } catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error' const errorMessage = err instanceof Error ? err.message : 'Unknown error'
error.value = `Error clearing cache: ${errorMessage}` error.value = `Error clearing cache: ${errorMessage}`
@@ -143,8 +142,8 @@ export const useNoitaStore = defineStore('noita', () => {
} }
} }
const refreshData = async () => { const refreshData = async (eventPath: string) => {
await Promise.all([fetchUserResults(), fetchLeaderboard()]) await Promise.all([fetchUserResults(eventPath), fetchLeaderboard(eventPath)])
} }
return { return {
+4 -3
View File
@@ -1,5 +1,6 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue' import { ref } from 'vue'
import { polylanSubmitterApiGetUserInfo } from '@/api'
interface UserInfo { interface UserInfo {
username: string username: string
@@ -19,9 +20,9 @@ export const useUserStore = defineStore('user', () => {
const loadUserData = async () => { const loadUserData = async () => {
isLoading.value = true isLoading.value = true
try { try {
const response = await fetch('/api/user') const response = await polylanSubmitterApiGetUserInfo()
if (response.ok) { if (response.data) {
const user = await response.json() const user = response.data
userInfo.value.isAuthenticated = user.is_authenticated || false userInfo.value.isAuthenticated = user.is_authenticated || false
userInfo.value.username = user.username || '' userInfo.value.username = user.username || ''
userInfo.value.isStaff = user.is_staff || false userInfo.value.isStaff = user.is_staff || false