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 @@
|
|||||||
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 @@
|
|||||||
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);
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user