Compare commits
2
Commits
65cc2e555f
...
2ee8cd6be3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ee8cd6be3
|
||
|
|
35824de310
|
@@ -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):
|
||||||
|
|||||||
@@ -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"],
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
import{k as t,l as a,p as n,v as s}from"./style-CgW_ewEM.js";const c={key:0,class:"flex justify-center"},k={key:0,class:"badge badge-warning badge-lg"},d={key:1,class:"badge badge-lg"},l={key:2,class:"badge badge-lg"},o={key:3,class:"badge badge-lg"},g={key:1,class:"text-2xl text-base-content/50"},y=t({__name:"RankBadge",props:{rank:{}},setup(e){return(i,r)=>e.rank!==null?(n(),a("div",c,[e.rank===1?(n(),a("span",k," 🏆 #"+s(e.rank),1)):e.rank===2?(n(),a("span",d," 🥈 #"+s(e.rank),1)):e.rank===3?(n(),a("span",l," 🥉 #"+s(e.rank),1)):(n(),a("span",o," #"+s(e.rank),1))])):(n(),a("div",g," No rank yet "))}});export{y as _};
|
import{k as t,l as a,p as n,v as s}from"./style-CrNkWMsg.js";const c={key:0,class:"flex justify-center"},k={key:0,class:"badge badge-warning badge-lg"},d={key:1,class:"badge badge-lg"},l={key:2,class:"badge badge-lg"},o={key:3,class:"badge badge-lg"},g={key:1,class:"text-2xl text-base-content/50"},y=t({__name:"RankBadge",props:{rank:{}},setup(e){return(i,r)=>e.rank!==null?(n(),a("div",c,[e.rank===1?(n(),a("span",k," 🏆 #"+s(e.rank),1)):e.rank===2?(n(),a("span",d," 🥈 #"+s(e.rank),1)):e.rank===3?(n(),a("span",l," 🥉 #"+s(e.rank),1)):(n(),a("span",o," #"+s(e.rank),1))])):(n(),a("div",g," No rank yet "))}});export{y as _};
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
import{k as b,c as v,r as g,l as a,p as n,s as t,F as h,y as x,v as i,x as f,O as _}from"./style-CgW_ewEM.js";const y={class:"min-h-screen bg-base-300 flex items-center justify-center px-4"},w={class:"w-full max-w-6xl"},k={class:"grid grid-cols-1 md:grid-cols-2 gap-8"},S=["onClick"],I={class:"relative h-60 bg-base-300 overflow-hidden"},j=["src","alt","onError"],E={key:1,class:"w-full h-full bg-gradient-to-br from-blue-600 to-blue-400 flex items-center justify-center text-white"},N={class:"card-body"},C={class:"card-title text-2xl"},B={class:"text-base-content/70"},O=b({__name:"Home",setup(A){const c=v(()=>[{id:"opus-magnum",title:"Opus Magnum",description:"Submit your best Opus Magnum puzzle solutions",appId:558990,path:"/opus-magnum"},{id:"noita",title:"Noita",description:"Submit your greatest Noita achievements",appId:881100,path:"/noita"}]),r=g(new Set),d=o=>`https://cdn.akamai.steamstatic.com/steam/apps/${o}/header.jpg`,u=o=>{r.value.add(o)},p=o=>{window.location.href=o};return(o,e)=>(n(),a("div",y,[t("div",w,[e[3]||(e[3]=t("div",{class:"text-center mb-12"},[t("h1",{class:"text-5xl font-bold mb-4"},"PolyLAN Submitter"),t("p",{class:"text-xl text-base-content/70"}," Choose a game and submit your best solutions ")],-1)),t("div",k,[(n(!0),a(h,null,x(c.value,s=>(n(),a("div",{key:s.id,onClick:m=>p(s.path),class:"card card-xl bg-base-200 shadow-xl hover:shadow-2xl transition-all cursor-pointer transform hover:-translate-y-2 hover:scale-[1.05] hover:bg-base-100 overflow-hidden"},[t("figure",I,[r.value.has(s.appId)?(n(),a("div",E,[...e[0]||(e[0]=[t("i",{class:"mdi mdi-gamepad-variant text-5xl"},null,-1)])])):(n(),a("img",{key:0,src:d(s.appId),alt:s.title,onError:m=>u(s.appId),class:"w-full h-full object-cover"},null,40,j)),e[1]||(e[1]=t("div",{class:"absolute inset-0 bg-black/30 group-hover:bg-black/20 transition-colors"},null,-1))]),t("div",N,[t("h2",C,i(s.title),1),t("p",B,i(s.description),1),e[2]||(e[2]=t("div",{class:"card-actions justify-end mt-4"},[t("button",{class:"btn btn-primary"},[t("i",{class:"mdi mdi-arrow-right mr-2"}),f(" Submit results ")])],-1))])],8,S))),128))]),e[4]||(e[4]=t("div",{class:"text-center mt-12 text-base-content/50"},[t("p",null,"Select a game above to begin submitting")],-1))])]))}}),l="#app",$=document.querySelector(l),z=_(O,{...$?.dataset});z.mount(l);
|
import{k as b,c as v,r as g,l as a,p as n,s as t,F as h,y as x,v as i,x as f,O as _}from"./style-CrNkWMsg.js";const y={class:"min-h-screen bg-base-300 flex items-center justify-center px-4"},w={class:"w-full max-w-6xl"},k={class:"grid grid-cols-1 md:grid-cols-2 gap-8"},S=["onClick"],I={class:"relative h-60 bg-base-300 overflow-hidden"},j=["src","alt","onError"],E={key:1,class:"w-full h-full bg-gradient-to-br from-blue-600 to-blue-400 flex items-center justify-center text-white"},N={class:"card-body"},C={class:"card-title text-2xl"},B={class:"text-base-content/70"},O=b({__name:"Home",setup(A){const c=v(()=>[{id:"opus-magnum",title:"Opus Magnum",description:"Submit your best Opus Magnum puzzle solutions",appId:558990,path:"/opus-magnum"},{id:"noita",title:"Noita",description:"Submit your greatest Noita achievements",appId:881100,path:"/noita"}]),r=g(new Set),d=o=>`https://cdn.akamai.steamstatic.com/steam/apps/${o}/header.jpg`,u=o=>{r.value.add(o)},p=o=>{window.location.href=o};return(o,e)=>(n(),a("div",y,[t("div",w,[e[3]||(e[3]=t("div",{class:"text-center mb-12"},[t("h1",{class:"text-5xl font-bold mb-4"},"PolyLAN Submitter"),t("p",{class:"text-xl text-base-content/70"}," Choose a game and submit your best solutions ")],-1)),t("div",k,[(n(!0),a(h,null,x(c.value,s=>(n(),a("div",{key:s.id,onClick:m=>p(s.path),class:"card card-xl bg-base-200 shadow-xl hover:shadow-2xl transition-all cursor-pointer transform hover:-translate-y-2 hover:scale-[1.05] hover:bg-base-100 overflow-hidden"},[t("figure",I,[r.value.has(s.appId)?(n(),a("div",E,[...e[0]||(e[0]=[t("i",{class:"mdi mdi-gamepad-variant text-5xl"},null,-1)])])):(n(),a("img",{key:0,src:d(s.appId),alt:s.title,onError:m=>u(s.appId),class:"w-full h-full object-cover"},null,40,j)),e[1]||(e[1]=t("div",{class:"absolute inset-0 bg-black/30 group-hover:bg-black/20 transition-colors"},null,-1))]),t("div",N,[t("h2",C,i(s.title),1),t("p",B,i(s.description),1),e[2]||(e[2]=t("div",{class:"card-actions justify-end mt-4"},[t("button",{class:"btn btn-primary"},[t("i",{class:"mdi mdi-arrow-right mr-2"}),f(" Submit results ")])],-1))])],8,S))),128))]),e[4]||(e[4]=t("div",{class:"text-center mt-12 text-base-content/50"},[t("p",null,"Select a game above to begin submitting")],-1))])]))}}),l="#app",$=document.querySelector(l),z=_(O,{...$?.dataset});z.mount(l);
|
||||||
+3
-3
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -1,20 +1,16 @@
|
|||||||
{
|
{
|
||||||
"_RankBadge.vue_vue_type_script_setup_true_lang-DXi0jahW.js": {
|
"_RankBadge.vue_vue_type_script_setup_true_lang-CzaIkt15.js": {
|
||||||
"file": "assets/RankBadge.vue_vue_type_script_setup_true_lang-DXi0jahW.js",
|
"file": "assets/RankBadge.vue_vue_type_script_setup_true_lang-CzaIkt15.js",
|
||||||
"name": "RankBadge.vue_vue_type_script_setup_true_lang",
|
"name": "RankBadge.vue_vue_type_script_setup_true_lang",
|
||||||
"imports": [
|
"imports": [
|
||||||
"_style-CgW_ewEM.js"
|
"_style-CrNkWMsg.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_style-CgCQPOku.css": {
|
"_style-CrNkWMsg.js": {
|
||||||
"file": "assets/style-CgCQPOku.css",
|
"file": "assets/style-CrNkWMsg.js",
|
||||||
"src": "_style-CgCQPOku.css"
|
|
||||||
},
|
|
||||||
"_style-CgW_ewEM.js": {
|
|
||||||
"file": "assets/style-CgW_ewEM.js",
|
|
||||||
"name": "style",
|
"name": "style",
|
||||||
"css": [
|
"css": [
|
||||||
"assets/style-CgCQPOku.css"
|
"assets/style-D95xr4by.css"
|
||||||
],
|
],
|
||||||
"assets": [
|
"assets": [
|
||||||
"assets/materialdesignicons-webfont-CSr8KVlo.eot",
|
"assets/materialdesignicons-webfont-CSr8KVlo.eot",
|
||||||
@@ -23,6 +19,10 @@
|
|||||||
"assets/materialdesignicons-webfont-B7mPwVP_.ttf"
|
"assets/materialdesignicons-webfont-B7mPwVP_.ttf"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"_style-D95xr4by.css": {
|
||||||
|
"file": "assets/style-D95xr4by.css",
|
||||||
|
"src": "_style-D95xr4by.css"
|
||||||
|
},
|
||||||
"node_modules/.pnpm/@mdi+font@7.4.47/node_modules/@mdi/font/fonts/materialdesignicons-webfont.eot": {
|
"node_modules/.pnpm/@mdi+font@7.4.47/node_modules/@mdi/font/fonts/materialdesignicons-webfont.eot": {
|
||||||
"file": "assets/materialdesignicons-webfont-CSr8KVlo.eot",
|
"file": "assets/materialdesignicons-webfont-CSr8KVlo.eot",
|
||||||
"src": "node_modules/.pnpm/@mdi+font@7.4.47/node_modules/@mdi/font/fonts/materialdesignicons-webfont.eot"
|
"src": "node_modules/.pnpm/@mdi+font@7.4.47/node_modules/@mdi/font/fonts/materialdesignicons-webfont.eot"
|
||||||
@@ -40,32 +40,32 @@
|
|||||||
"src": "node_modules/.pnpm/@mdi+font@7.4.47/node_modules/@mdi/font/fonts/materialdesignicons-webfont.woff2"
|
"src": "node_modules/.pnpm/@mdi+font@7.4.47/node_modules/@mdi/font/fonts/materialdesignicons-webfont.woff2"
|
||||||
},
|
},
|
||||||
"src/home.ts": {
|
"src/home.ts": {
|
||||||
"file": "assets/home-Cnotf5sq.js",
|
"file": "assets/home-CCO6cuKi.js",
|
||||||
"name": "home",
|
"name": "home",
|
||||||
"src": "src/home.ts",
|
"src": "src/home.ts",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_style-CgW_ewEM.js"
|
"_style-CrNkWMsg.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/noita.ts": {
|
"src/noita.ts": {
|
||||||
"file": "assets/noita-BxC854hz.js",
|
"file": "assets/noita-ByhLkfmW.js",
|
||||||
"name": "noita",
|
"name": "noita",
|
||||||
"src": "src/noita.ts",
|
"src": "src/noita.ts",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_style-CgW_ewEM.js",
|
"_style-CrNkWMsg.js",
|
||||||
"_RankBadge.vue_vue_type_script_setup_true_lang-DXi0jahW.js"
|
"_RankBadge.vue_vue_type_script_setup_true_lang-CzaIkt15.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/opus-magnum.ts": {
|
"src/opus-magnum.ts": {
|
||||||
"file": "assets/opus_magnum-CgTJgCB5.js",
|
"file": "assets/opus_magnum-CBb_5LJ1.js",
|
||||||
"name": "opus_magnum",
|
"name": "opus_magnum",
|
||||||
"src": "src/opus-magnum.ts",
|
"src": "src/opus-magnum.ts",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_style-CgW_ewEM.js",
|
"_style-CrNkWMsg.js",
|
||||||
"_RankBadge.vue_vue_type_script_setup_true_lang-DXi0jahW.js"
|
"_RankBadge.vue_vue_type_script_setup_true_lang-CzaIkt15.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user