chore: market app build

This commit is contained in:
Loïc Gremaud 2026-05-24 09:24:56 +02:00
parent e557fe2cda
commit 9fd0122a67
Signed by: Legrems
GPG Key ID: D4620E6DF3E0121D
16 changed files with 168 additions and 88 deletions

View File

@ -1,36 +1,23 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { polylanSubmitterApiGetUserInfo, marketApiListMarkets } from "./api";
import type { UserInfoOut } from "./api/types.gen";
import type { Market } from "./types";
import { onMounted } from "vue";
import { storeToRefs } from "pinia";
import { useMarketStore } from "./stores/market";
import MarketCard from "./components/MarketCard.vue";
import UserBets from "./components/UserBets.vue";
const markets = ref<Market[]>([]);
const loading = ref(true);
const userInfo = ref<UserInfoOut | undefined>();
const marketStore = useMarketStore();
const { markets, userInfo, isLoading } = storeToRefs(marketStore);
const goHome = () => {
window.location.href = "/";
};
const reloadPage = () => {
window.location.reload();
const reloadPage = async () => {
await marketStore.refreshPage();
};
onMounted(async () => {
// Fetch user info
const userResponse = await polylanSubmitterApiGetUserInfo();
if (userResponse.data) {
userInfo.value = userResponse.data;
}
// Fetch markets
const response = await marketApiListMarkets();
if (response.data) {
markets.value = response.data as unknown as Market[];
}
loading.value = false;
onMounted(() => {
marketStore.initializeMarketPage();
});
</script>
@ -68,7 +55,7 @@ onMounted(async () => {
</div>
<!-- Loading -->
<div v-if="loading" class="flex justify-center py-20">
<div v-if="isLoading" class="flex justify-center py-20">
<span class="loading loading-spinner loading-lg"></span>
</div>

View File

@ -1,8 +1,10 @@
<script setup lang="ts">
import { ref, computed, onMounted } from "vue";
import { polylanSubmitterApiGetUserInfo, marketApiCreateBet, marketApiListUserBets, marketApiCloseMarket, marketApiResolveMarket } from "../api";
import { storeToRefs } from "pinia";
import { marketApiCreateBet, marketApiCloseMarket, marketApiResolveMarket } from "../api";
import { useMarketStore } from "../stores/market";
import type { Market, MarketOption } from "../types";
import type { UserInfoOut, UserBetSchema } from "../api/types.gen";
import type { UserBetSchema } from "../api/types.gen";
const props = defineProps<{
market: Market;
@ -12,12 +14,13 @@ const emit = defineEmits<{
refresh: [];
}>();
const marketStore = useMarketStore();
const { userInfo, userBets } = storeToRefs(marketStore);
const selectedOption = ref<string | null>(null);
const betAmount = ref<number>(0);
const loading = ref(false);
const error = ref<string>("");
const userInfo = ref<UserInfoOut | undefined>();
const userBets = ref<UserBetSchema[]>([]);
const existingBet = ref<UserBetSchema | null>(null);
const showResolveModal = ref(false);
const selectedWinningOption = ref<string | null>(null);
@ -139,7 +142,8 @@ const placeBet = async () => {
if (!response.error) {
// Reload user bets to reflect the new bet
await loadUserBets();
await marketStore.loadUserBets();
updateExistingBet();
betAmount.value = 0;
} else {
const err = response.error as any;
@ -158,32 +162,20 @@ const placeBet = async () => {
}
};
const initializeUserInfo = async () => {
const response = await polylanSubmitterApiGetUserInfo();
if (response.data) {
userInfo.value = response.data;
}
};
onMounted(() => {
// Update existing bet when component mounts or userBets changes
updateExistingBet();
});
const loadUserBets = async () => {
const response = await marketApiListUserBets();
if (response.data) {
userBets.value = response.data;
// Find if user has a bet on this market
existingBet.value = response.data.find(bet => bet.market?.uuid === props.market.uuid) || null;
const updateExistingBet = () => {
if (userBets.value) {
existingBet.value = userBets.value.find(bet => bet.market?.uuid === props.market.uuid) || null;
if (existingBet.value) {
selectedOption.value = existingBet.value.option.uuid;
betAmount.value = existingBet.value.amount;
}
}
};
onMounted(async () => {
await initializeUserInfo();
if (userInfo.value?.is_authenticated) {
await loadUserBets();
}
});
</script>
<template>

View File

@ -1,14 +1,15 @@
<script setup lang="ts">
import { ref, onMounted, computed } from "vue";
import { marketApiListUserBets } from "../api";
import type { UserBetSchema } from "../api/types.gen";
import { computed } from "vue";
import { storeToRefs } from "pinia";
import { useMarketStore } from "../stores/market";
defineEmits<{
refresh: [];
}>();
const userBets = ref<UserBetSchema[]>([]);
const loading = ref(true);
const marketStore = useMarketStore();
const { userBets } = storeToRefs(marketStore);
const loading = computed(() => marketStore.isLoading);
const totalBetAmount = computed(() => {
return userBets.value.reduce((sum, bet) => sum + bet.amount, 0);
@ -35,14 +36,6 @@ const openBets = computed(() => {
const totalWinnings = computed(() => {
return winningBets.value.reduce((sum, bet) => sum + bet.amount, 0);
});
onMounted(async () => {
const response = await marketApiListUserBets();
if (response.data) {
userBets.value = response.data;
}
loading.value = false;
});
</script>
<template>

View File

@ -1,8 +1,10 @@
import { createApp } from 'vue'
import Market from '@/Market.vue'
import { pinia } from '@/stores'
import '@/style.css'
const selector = "#app"
const mountData = document.querySelector<HTMLElement>(selector)
const app = createApp(Market, { ...mountData?.dataset })
app.use(pinia)
app.mount(selector)

View File

@ -0,0 +1,94 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { polylanSubmitterApiGetUserInfo, marketApiListMarkets, marketApiListUserBets } from '@/api'
import type { Market } from '@/types'
import type { UserInfoOut, UserBetSchema } from '@/api/types.gen'
export const useMarketStore = defineStore('market', () => {
// State
const markets = ref<Market[]>([])
const userInfo = ref<UserInfoOut | undefined>()
const userBets = ref<UserBetSchema[]>([])
const isLoading = ref(true)
const error = ref<string>('')
// Actions
const loadUserInfo = async () => {
try {
const response = await polylanSubmitterApiGetUserInfo()
if (response.data) {
userInfo.value = response.data
}
} catch (err) {
error.value = 'Failed to load user info'
console.error('Error loading user info:', err)
}
}
const loadMarkets = async () => {
try {
const response = await marketApiListMarkets()
if (response.data) {
markets.value = response.data as unknown as Market[]
}
} catch (err) {
error.value = 'Failed to load markets'
console.error('Error loading markets:', err)
}
}
const loadUserBets = async () => {
try {
const response = await marketApiListUserBets()
if (response.data) {
userBets.value = response.data
}
} catch (err) {
error.value = 'Failed to load user bets'
console.error('Error loading user bets:', err)
}
}
const initializeMarketPage = async () => {
isLoading.value = true
error.value = ''
try {
await Promise.all([
loadUserInfo(),
loadMarkets(),
])
// Load user bets if authenticated
if (userInfo.value?.is_authenticated) {
await loadUserBets()
}
} finally {
isLoading.value = false
}
}
const refreshPage = async () => {
await Promise.all([
loadUserInfo(),
loadMarkets(),
userInfo.value?.is_authenticated ? loadUserBets() : Promise.resolve(),
])
}
return {
// State
markets,
userInfo,
userBets,
isLoading,
error,
// Actions
loadUserInfo,
loadMarkets,
loadUserBets,
initializeMarketPage,
refreshPage,
}
})

View File

@ -1 +1 @@
import{k as t,l as a,p as n,v as s}from"./style-C9QoPxDN.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-CSeMeQaG.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 _};

View File

@ -1 +0,0 @@
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-C9QoPxDN.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);

View File

@ -0,0 +1 @@
import{k as v,r as l,I as g,l as o,p as r,s as e,B as x,F as f,y as h,v as y,x as _,O as w}from"./style-CSeMeQaG.js";import{g as k}from"./sdk.gen-CMTwTM_A.js";const j={class:"min-h-screen bg-base-300 flex items-center justify-center px-4"},S={class:"w-full max-w-6xl"},C={key:0,class:"flex justify-center py-20"},E={key:1,class:"grid grid-cols-1 md:grid-cols-2 gap-8"},B=["onClick"],N={class:"relative h-60 bg-base-300 overflow-hidden"},$=["src","alt","onError"],A={key:1,class:"w-full h-full bg-gradient-to-br from-blue-600 to-blue-400 flex items-center justify-center text-white"},I={class:"card-body"},L={class:"card-title text-2xl"},P=v({__name:"Home",setup(F){const i=l(),n=l(!0),d=l(new Set),u=s=>`https://cdn.akamai.steamstatic.com/steam/apps/${s}/header.jpg`,b=s=>{d.value.add(s)},c=s=>{window.location.href=s};return g(async()=>{const s=await k();s.data&&(i.value=s.data),n.value=!1}),(s,t)=>(r(),o("div",j,[e("div",S,[t[6]||(t[6]=e("div",{class:"text-center mb-12"},[e("h1",{class:"text-5xl font-bold mb-4"},"PolyLAN Submitter"),e("p",{class:"text-xl text-base-content/70"}," Choose a game and submit your best solutions ")],-1)),n.value?(r(),o("div",C,[...t[1]||(t[1]=[e("span",{class:"loading loading-spinner loading-lg"},null,-1)])])):(r(),o("div",E,[e("div",{onClick:t[0]||(t[0]=a=>c("/market")),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[2]||(t[2]=[x('<figure class="relative h-60 bg-gradient-to-br from-purple-600 to-blue-600 flex items-center justify-center"><i class="mdi mdi-chart-box text-6xl text-white opacity-80"></i><div class="absolute inset-0 bg-black/30 group-hover:bg-black/20 transition-colors"></div></figure><div class="card-body"><h2 class="card-title text-2xl">Market</h2><p class="text-base-content/70">Place your bets and compete</p><div class="card-actions justify-end mt-4"><button class="btn btn-primary"><i class="mdi mdi-arrow-right mr-2"></i> Place bets </button></div></div>',2)])]),(r(!0),o(f,null,h(i.value,a=>(r(),o("div",{key:a.steam_app_id,onClick:p=>c(a.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"},[e("figure",N,[d.value.has(a.steam_app_id)?(r(),o("div",A,[...t[3]||(t[3]=[e("i",{class:"mdi mdi-gamepad-variant text-5xl"},null,-1)])])):(r(),o("img",{key:0,src:u(a.steam_app_id),alt:a.name,onError:p=>b(a.steam_app_id),class:"w-full h-full object-cover"},null,40,$)),t[4]||(t[4]=e("div",{class:"absolute inset-0 bg-black/30 group-hover:bg-black/20 transition-colors"},null,-1))]),e("div",I,[e("h2",L,y(a.name),1),t[5]||(t[5]=e("div",{class:"card-actions justify-end mt-4"},[e("button",{class:"btn btn-primary"},[e("i",{class:"mdi mdi-arrow-right mr-2"}),_(" Submit results ")])],-1))])],8,B))),128))])),t[7]||(t[7]=e("div",{class:"text-center mt-12 text-base-content/50"},[e("p",null,"Select a game above to begin submitting")],-1))])]))}}),m="#app",V=document.querySelector(m),D=w(P,{...V?.dataset});D.mount(m);

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

View File

@ -1,16 +1,24 @@
{
"_RankBadge.vue_vue_type_script_setup_true_lang-CiUtgtLU.js": {
"file": "assets/RankBadge.vue_vue_type_script_setup_true_lang-CiUtgtLU.js",
"_RankBadge.vue_vue_type_script_setup_true_lang-CfKZiK2-.js": {
"file": "assets/RankBadge.vue_vue_type_script_setup_true_lang-CfKZiK2-.js",
"name": "RankBadge.vue_vue_type_script_setup_true_lang",
"imports": [
"_style-C9QoPxDN.js"
"_style-CSeMeQaG.js"
]
},
"_style-C9QoPxDN.js": {
"file": "assets/style-C9QoPxDN.js",
"_sdk.gen-CMTwTM_A.js": {
"file": "assets/sdk.gen-CMTwTM_A.js",
"name": "sdk.gen"
},
"_style-B7hBs3CR.css": {
"file": "assets/style-B7hBs3CR.css",
"src": "_style-B7hBs3CR.css"
},
"_style-CSeMeQaG.js": {
"file": "assets/style-CSeMeQaG.js",
"name": "style",
"css": [
"assets/style-Cs9btLod.css"
"assets/style-B7hBs3CR.css"
],
"assets": [
"assets/materialdesignicons-webfont-CSr8KVlo.eot",
@ -19,10 +27,6 @@
"assets/materialdesignicons-webfont-B7mPwVP_.ttf"
]
},
"_style-Cs9btLod.css": {
"file": "assets/style-Cs9btLod.css",
"src": "_style-Cs9btLod.css"
},
"node_modules/.pnpm/@mdi+font@7.4.47/node_modules/@mdi/font/fonts/materialdesignicons-webfont.eot": {
"file": "assets/materialdesignicons-webfont-CSr8KVlo.eot",
"src": "node_modules/.pnpm/@mdi+font@7.4.47/node_modules/@mdi/font/fonts/materialdesignicons-webfont.eot"
@ -40,32 +44,34 @@
"src": "node_modules/.pnpm/@mdi+font@7.4.47/node_modules/@mdi/font/fonts/materialdesignicons-webfont.woff2"
},
"src/home.ts": {
"file": "assets/home-Cpe9mjX7.js",
"file": "assets/home-SJcM6NSq.js",
"name": "home",
"src": "src/home.ts",
"isEntry": true,
"imports": [
"_style-C9QoPxDN.js"
"_style-CSeMeQaG.js",
"_sdk.gen-CMTwTM_A.js"
]
},
"src/noita.ts": {
"file": "assets/noita-C5wjrj1v.js",
"file": "assets/noita-D-VLRi4K.js",
"name": "noita",
"src": "src/noita.ts",
"isEntry": true,
"imports": [
"_style-C9QoPxDN.js",
"_RankBadge.vue_vue_type_script_setup_true_lang-CiUtgtLU.js"
"_style-CSeMeQaG.js",
"_RankBadge.vue_vue_type_script_setup_true_lang-CfKZiK2-.js"
]
},
"src/opus-magnum.ts": {
"file": "assets/opus_magnum-Ce7rjJAF.js",
"file": "assets/opus_magnum-CBjPWRy9.js",
"name": "opus_magnum",
"src": "src/opus-magnum.ts",
"isEntry": true,
"imports": [
"_style-C9QoPxDN.js",
"_RankBadge.vue_vue_type_script_setup_true_lang-CiUtgtLU.js"
"_style-CSeMeQaG.js",
"_sdk.gen-CMTwTM_A.js",
"_RankBadge.vue_vue_type_script_setup_true_lang-CfKZiK2-.js"
]
}
}

View File

@ -1 +1 @@
{"root":["./src/home.ts","./src/noita.ts","./src/opus-magnum.ts","./src/services/apiService.ts","./src/services/ocrService.ts","./src/stores/index.ts","./src/stores/puzzles.ts","./src/stores/submissions.ts","./src/stores/uploads.ts","./src/types/index.ts","./src/Home.vue","./src/Noita.vue","./src/OpusMagnum.vue","./src/components/AdminPanel.vue","./src/components/FileUpload.vue","./src/components/PuzzleCard.vue","./src/components/PuzzleResults.vue","./src/components/RankBadge.vue","./src/components/Results.vue","./src/components/SubmissionForm.vue","./src/components/TopUsersLeaderboard.vue","./src/components/Winners.vue"],"version":"5.9.3"}
{"root":["./src/home.ts","./src/market.ts","./src/noita.ts","./src/opus-magnum.ts","./src/api/client.gen.ts","./src/api/index.ts","./src/api/sdk.gen.ts","./src/api/types.gen.ts","./src/api/client/client.gen.ts","./src/api/client/index.ts","./src/api/client/types.gen.ts","./src/api/client/utils.gen.ts","./src/api/core/auth.gen.ts","./src/api/core/bodySerializer.gen.ts","./src/api/core/params.gen.ts","./src/api/core/pathSerializer.gen.ts","./src/api/core/queryKeySerializer.gen.ts","./src/api/core/serverSentEvents.gen.ts","./src/api/core/types.gen.ts","./src/api/core/utils.gen.ts","./src/services/apiService.ts","./src/services/ocrService.ts","./src/stores/index.ts","./src/stores/market.ts","./src/stores/puzzles.ts","./src/stores/submissions.ts","./src/stores/uploads.ts","./src/types/index.ts","./src/Home.vue","./src/Market.vue","./src/Noita.vue","./src/OpusMagnum.vue","./src/components/AdminPanel.vue","./src/components/FileUpload.vue","./src/components/MarketCard.vue","./src/components/PuzzleCard.vue","./src/components/PuzzleResults.vue","./src/components/RankBadge.vue","./src/components/Results.vue","./src/components/SubmissionForm.vue","./src/components/TopUsersLeaderboard.vue","./src/components/UserBets.vue","./src/components/Winners.vue"],"version":"5.9.3"}