top winners + by puzzle
This commit is contained in:
@@ -6,6 +6,7 @@ import AdminPanel from "@/components/AdminPanel.vue";
|
||||
import Results from "@/components/Results.vue";
|
||||
import Winners from "@/components/Winners.vue";
|
||||
import PuzzleResults from "@/components/PuzzleResults.vue";
|
||||
import TopUsersLeaderboard from "@/components/TopUsersLeaderboard.vue";
|
||||
import { apiService, errorHelpers } from "@/services/apiService";
|
||||
import { usePuzzlesStore } from "@/stores/puzzles";
|
||||
import { useSubmissionsStore } from "@/stores/submissions";
|
||||
@@ -191,10 +192,11 @@ const goHome = () => {
|
||||
<!-- Main Content -->
|
||||
<div v-else class="space-y-8">
|
||||
|
||||
<!-- Winners Section (only when tournament is closed) -->
|
||||
<!-- Results Section (only when tournament is closed) -->
|
||||
<div v-if="isTournamentClosed" class="space-y-8">
|
||||
<Winners />
|
||||
<TopUsersLeaderboard />
|
||||
<PuzzleResults />
|
||||
<Winners />
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { apiService } from "@/services/apiService";
|
||||
import type { TournamentPuzzleResults } from "@/types";
|
||||
|
||||
const isLoading = ref(true);
|
||||
const resultsData = ref<TournamentPuzzleResults | null>(null);
|
||||
const error = ref<string>("");
|
||||
|
||||
const fetchResults = async () => {
|
||||
isLoading.value = true;
|
||||
error.value = "";
|
||||
try {
|
||||
const response = await apiService.getPuzzleResults(100);
|
||||
if (response.data) {
|
||||
resultsData.value = response.data;
|
||||
} else if (response.error) {
|
||||
error.value = response.error;
|
||||
console.error("Error fetching results:", response.error);
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : "Failed to fetch results";
|
||||
console.error("Error fetching results:", err);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const getTop10Users = () => {
|
||||
if (!resultsData.value) return [];
|
||||
|
||||
const userScores: Record<string, { username: string; user_id: number; total_points: number }> = {};
|
||||
|
||||
resultsData.value.results.forEach((puzzle) => {
|
||||
puzzle.submissions.forEach((submission) => {
|
||||
const key = submission.user_id;
|
||||
if (!userScores[key]) {
|
||||
userScores[key] = {
|
||||
username: submission.username,
|
||||
user_id: submission.user_id,
|
||||
total_points: 0
|
||||
};
|
||||
}
|
||||
userScores[key].total_points += submission.rank_points || 0;
|
||||
});
|
||||
});
|
||||
|
||||
return Object.values(userScores)
|
||||
.sort((a, b) => b.total_points - a.total_points)
|
||||
.slice(0, 10);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchResults();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card bg-base-100 shadow-lg">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title text-2xl flex items-center gap-2">
|
||||
<i class="mdi mdi-podium text-yellow-500 text-3xl"></i>
|
||||
Top 10 Users
|
||||
</h2>
|
||||
|
||||
<div v-if="isLoading" class="flex justify-center py-12">
|
||||
<span class="loading loading-spinner loading-lg"></span>
|
||||
</div>
|
||||
|
||||
<div v-else-if="error" class="alert alert-error">
|
||||
<i class="mdi mdi-alert-circle text-xl"></i>
|
||||
<div>{{ error }}</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="getTop10Users().length === 0" class="text-center py-8">
|
||||
<p class="text-base-content/70">No results available yet.</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-3">
|
||||
<template v-for="(user, index) in getTop10Users()" :key="user.user_id">
|
||||
<div class="bg-base-200 p-4 rounded-lg text-center" :class="{
|
||||
'ring-2 ring-yellow-500': index === 0,
|
||||
'ring-2 ring-gray-400': index === 1,
|
||||
'ring-2 ring-orange-400': index === 2,
|
||||
}">
|
||||
<div class="text-3xl font-bold mb-2">
|
||||
{{ index === 0 ? '🥇' : index === 1 ? '🥈' : index === 2 ? '🥉' : `#${index + 1}` }}
|
||||
</div>
|
||||
<div class="font-semibold text-sm mb-2 truncate">{{ user.username }}</div>
|
||||
<div class="text-2xl font-bold text-primary">{{ user.total_points }} pts</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -65,7 +65,7 @@ const flattenedRows = computed(() => {
|
||||
});
|
||||
});
|
||||
|
||||
return rows;
|
||||
return rows.sort((a, b) => (b.total || 0) - (a.total || 0));
|
||||
});
|
||||
|
||||
const openImageModal = (fileUrl: string, fileName: string) => {
|
||||
|
||||
Reference in New Issue
Block a user