admin display

This commit is contained in:
Loïc Gremaud 2026-05-15 14:50:59 +02:00
parent 65cc2e555f
commit 35824de310
Signed by: Legrems
GPG Key ID: D4620E6DF3E0121D
5 changed files with 33 additions and 16 deletions

View File

@ -33,6 +33,7 @@ class PuzzleResponseRankingOut(ModelSchema):
class UserDisplayOut(Schema): class UserDisplayOut(Schema):
id: int id: int
username: str username: str
is_staff: bool
class RankingSchema(Schema): class RankingSchema(Schema):

View File

@ -195,7 +195,7 @@ def get_leaderboard(request: HttpRequest):
users_with_scores.append( users_with_scores.append(
{ {
"user_id": user_id, "user_id": user_id,
"username": user.username, "user": user,
"total_score": total_score, "total_score": total_score,
"objectives_count": objectives_count, "objectives_count": objectives_count,
"deaths_count": deaths_count, "deaths_count": deaths_count,
@ -207,7 +207,8 @@ def get_leaderboard(request: HttpRequest):
leaderboard = [ leaderboard = [
{ {
"rank": idx + 1, "rank": idx + 1,
"username": entry["username"], "username": entry["user"].username,
"is_staff": entry["user"].is_staff,
"total_score": entry["total_score"], "total_score": entry["total_score"],
"objectives_count": entry["objectives_count"], "objectives_count": entry["objectives_count"],
"deaths_count": entry["deaths_count"], "deaths_count": entry["deaths_count"],

View File

@ -30,6 +30,7 @@ class ResultsOut(Schema):
class LeaderboardEntryOut(Schema): class LeaderboardEntryOut(Schema):
rank: int rank: int
username: str username: str
is_staff: bool
total_score: int total_score: int
objectives_count: int objectives_count: int
deaths_count: int deaths_count: int

View File

@ -365,6 +365,9 @@ onMounted(() => {
<span v-if="entry.username === userInfo.username" class="badge badge-primary badge-sm ml-1"> <span v-if="entry.username === userInfo.username" class="badge badge-primary badge-sm ml-1">
You You
</span> </span>
<span v-if="entry.is_staff" class="badge badge-warning badge-sm ml-1">
admin
</span>
</td> </td>
<td class="text-right text-sm font-bold text-primary">{{ entry.total_score.toLocaleString() }} <td class="text-right text-sm font-bold text-primary">{{ entry.total_score.toLocaleString() }}
</td> </td>

View File

@ -5,6 +5,7 @@ import RankBadge from "./RankBadge.vue";
interface User { interface User {
id: number; id: number;
username: string; username: string;
is_staff: boolean,
first_name?: string; first_name?: string;
last_name?: string; last_name?: string;
} }
@ -84,7 +85,7 @@ const getOverallRanking = () => {
const count = responses.length; const count = responses.length;
return { return {
username: user.username, user: user,
totalPoints, totalPoints,
puzzlesSolved: count, puzzlesSolved: count,
}; };
@ -98,9 +99,9 @@ const getPuzzleRanking = (puzzleId: number) => {
const ranking = resultsData.value.ranking_by_puzzle[puzzleId] || []; const ranking = resultsData.value.ranking_by_puzzle[puzzleId] || [];
return ranking.map((response) => { return ranking.map((response) => {
const user = resultsData.value!.users.find((u) => u.id === response.user_id); const user = resultsData.value!.users.find((u) => u.id === response.user_id) as User;
return { return {
username: user?.username || "Unknown", user: user,
cost: response.final_cost, cost: response.final_cost,
cycles: response.final_cycles, cycles: response.final_cycles,
area: response.final_area, area: response.final_area,
@ -146,7 +147,7 @@ const loadUserData = async () => {
// Calculate user's rank and stats // Calculate user's rank and stats
const ranking = getOverallRanking(); const ranking = getOverallRanking();
const userRankIndex = ranking.findIndex((u) => u.username === user.username); 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;
@ -235,7 +236,7 @@ onMounted(() => {
<div v-else class="space-y-6"> <div v-else class="space-y-6">
<!-- Tabs --> <!-- Tabs -->
<div class="tabs tabs-boxed"> <div class="tabs tabs-border">
<button @click="selectedTab = 'overall'" :class="[ <button @click="selectedTab = 'overall'" :class="[
'tab', 'tab',
selectedTab === 'overall' ? 'tab-active' : '', selectedTab === 'overall' ? 'tab-active' : '',
@ -269,13 +270,18 @@ onMounted(() => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="(user, index) in getOverallRanking()" :key="user.username"> <tr v-for="(ranking, index) in getOverallRanking()" :key="ranking.user.username">
<td class="font-bold"> <td class="font-bold">
<RankBadge :rank="index + 1" /> <RankBadge :rank="index + 1" />
</td> </td>
<td class="font-medium">{{ user.username }}</td> <td class="font-medium">
<td class="text-right">{{ user.puzzlesSolved }}</td> {{ ranking.user.username }}
<td class="text-right font-bold">{{ user.totalPoints }}</td> <span v-if="ranking.user.is_staff" class="badge badge-warning">
admin
</span>
</td>
<td class="text-right">{{ ranking.puzzlesSolved }}</td>
<td class="text-right font-bold">{{ ranking.totalPoints }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -309,17 +315,17 @@ onMounted(() => {
<div class="grid grid-cols-3 gap-4"> <div class="grid grid-cols-3 gap-4">
<div class="text-center"> <div class="text-center">
<span class="text-2xl font-bold text-primary"><small>x</small>{{ puzzle.points_factor.cost <span class="text-2xl font-bold text-primary"><small>x</small>{{ puzzle.points_factor.cost
}}</span> }}</span>
<p class="text-xs text-base-content/70">Cost</p> <p class="text-xs text-base-content/70">Cost</p>
</div> </div>
<div class="text-center"> <div class="text-center">
<span class="text-2xl font-bold text-primary"><small>x</small>{{ puzzle.points_factor.cycles <span class="text-2xl font-bold text-primary"><small>x</small>{{ puzzle.points_factor.cycles
}}</span> }}</span>
<p class="text-xs text-base-content/70">Cycles</p> <p class="text-xs text-base-content/70">Cycles</p>
</div> </div>
<div class="text-center"> <div class="text-center">
<span class="text-2xl font-bold text-primary"><small>x</small>{{ puzzle.points_factor.area <span class="text-2xl font-bold text-primary"><small>x</small>{{ puzzle.points_factor.area
}}</span> }}</span>
<p class="text-xs text-base-content/70">Area</p> <p class="text-xs text-base-content/70">Area</p>
</div> </div>
</div> </div>
@ -333,7 +339,12 @@ onMounted(() => {
<div class="text-xs text-base-content/70 font-bold"> <div class="text-xs text-base-content/70 font-bold">
{{ index === 0 ? '🏆 1st Place' : index === 1 ? '🥈 2nd Place' : '🥉 3rd Place' }} {{ index === 0 ? '🏆 1st Place' : index === 1 ? '🥈 2nd Place' : '🥉 3rd Place' }}
</div> </div>
<h4 class="font-bold text-lg">{{ response.username }}</h4> <h4 class="font-bold text-lg">
{{ response.user.username }}
<span v-if="response.user.is_staff" class="badge badge-warning">
admin
</span>
</h4>
<div class="divider my-2"></div> <div class="divider my-2"></div>
<div class="space-y-2 text-sm"> <div class="space-y-2 text-sm">
<div class="flex justify-between"> <div class="flex justify-between">
@ -402,7 +413,7 @@ onMounted(() => {
<span v-else-if="index === 2" class="badge">🥉</span> <span v-else-if="index === 2" class="badge">🥉</span>
<span v-else>#{{ index + 1 }}</span> <span v-else>#{{ index + 1 }}</span>
</td> </td>
<td class="font-medium">{{ response.username }}</td> <td class="font-medium">{{ response.user.username }}</td>
<td class="text-center"> <td class="text-center">
<span v-if="response.cost" class="badge badge-sm">{{ response.cost }}</span> <span v-if="response.cost" class="badge badge-sm">{{ response.cost }}</span>
<span v-else class="text-base-content/40"></span> <span v-else class="text-base-content/40"></span>