diff --git a/polylan_submitter/openapi-ts.config.ts b/polylan_submitter/openapi-ts.config.ts
index 895e7f9..3aa4875 100644
--- a/polylan_submitter/openapi-ts.config.ts
+++ b/polylan_submitter/openapi-ts.config.ts
@@ -3,4 +3,5 @@ import { defineConfig } from '@hey-api/openapi-ts';
export default defineConfig({
input: `http://localhost:7777/api/openapi.json`,
output: 'src/api/',
+ plugins: ['@hey-api/typescript', '@hey-api/sdk', '@tanstack/vue-query'],
});
diff --git a/polylan_submitter/package.json b/polylan_submitter/package.json
index a6a3bd6..8914717 100644
--- a/polylan_submitter/package.json
+++ b/polylan_submitter/package.json
@@ -12,6 +12,9 @@
},
"dependencies": {
"@tailwindcss/vite": "^4.1.16",
+ "@tanstack/query-persist-client-core": "^5.104.3",
+ "@tanstack/query-sync-storage-persister": "^5.104.3",
+ "@tanstack/vue-query": "^5.104.3",
"@tanstack/vue-table": "^8.21.3",
"@vueuse/core": "^14.0.0",
"dayjs": "^1.11.20",
diff --git a/polylan_submitter/pnpm-lock.yaml b/polylan_submitter/pnpm-lock.yaml
index 753f0fa..6de85d5 100644
--- a/polylan_submitter/pnpm-lock.yaml
+++ b/polylan_submitter/pnpm-lock.yaml
@@ -11,6 +11,15 @@ importers:
'@tailwindcss/vite':
specifier: ^4.1.16
version: 4.1.16(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.4))
+ '@tanstack/query-persist-client-core':
+ specifier: ^5.104.3
+ version: 5.104.3
+ '@tanstack/query-sync-storage-persister':
+ specifier: ^5.104.3
+ version: 5.104.3
+ '@tanstack/vue-query':
+ specifier: ^5.104.3
+ version: 5.104.3(vue@3.5.22(typescript@5.9.3))
'@tanstack/vue-table':
specifier: ^8.21.3
version: 8.21.3(vue@3.5.22(typescript@5.9.3))
@@ -515,10 +524,32 @@ packages:
peerDependencies:
vite: ^5.2.0 || ^6 || ^7
+ '@tanstack/match-sorter-utils@9.2.6':
+ resolution: {integrity: sha512-DIpUSOhPfQ7u/qfUi/Wgm0wBO56MyBYXGvnlI0NcauDRPqdBUlAZZjW2Dxm92m+k1UWVhKw8YJ4f47+75Aujyw==}
+ engines: {node: '>=20'}
+
+ '@tanstack/query-core@5.104.3':
+ resolution: {integrity: sha512-DZY9TuJAOgU3UyHUhXb4CcTYEHNZszlwkPg4VViIwTxOKzLprPUU+rbaPZQd3srqkGOtpaDdKpW972UV/LKlcg==}
+
+ '@tanstack/query-persist-client-core@5.104.3':
+ resolution: {integrity: sha512-6nhc/QbMpkKeuAM81SFSdcsJ9B8dm3fGHPODbau2cRSDyqcXiSl7sXQM7POVktch/YdgIGYPIQyZOQQaRarHMA==}
+
+ '@tanstack/query-sync-storage-persister@5.104.3':
+ resolution: {integrity: sha512-sNM9X7QZdTHBlzHuNxuHQjECn3/zncuMgw5pesOFXu8XQ01UXBGASdMKZSRaxfNOhigRjm5souHhfmByN97NEg==}
+
'@tanstack/table-core@8.21.3':
resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
engines: {node: '>=12'}
+ '@tanstack/vue-query@5.104.3':
+ resolution: {integrity: sha512-bdVlhyEFPllLfHioHx8kwtCAQ1YJQrvYczlsNzyiYFsp6n5yz9isNXiy6MIu+QGmG+RyghVdnp1UmiNDqLS22g==}
+ peerDependencies:
+ '@vue/composition-api': ^1.1.2
+ vue: ^2.6.0 || ^3.3.0
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
'@tanstack/vue-table@8.21.3':
resolution: {integrity: sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw==}
engines: {node: '>=12'}
@@ -961,6 +992,9 @@ packages:
regenerator-runtime@0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
+ remove-accents@0.5.0:
+ resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==}
+
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
@@ -1072,6 +1106,17 @@ packages:
vscode-uri@3.1.0:
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
+ vue-demi@0.14.10:
+ resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
+ engines: {node: '>=12'}
+ hasBin: true
+ peerDependencies:
+ '@vue/composition-api': ^1.0.0-rc.1
+ vue: ^3.0.0-0 || ^2.6.0
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
vue-i18n@11.4.12:
resolution: {integrity: sha512-yvArvvo3ErLKFNlQrZUGmRkxL/ctM5xFYtE9FxTKiE/PalWP1bnTopBgbSRGLluYEyYSNl1Pt8l2J7zVavbXww==}
engines: {node: '>= 22'}
@@ -1445,8 +1490,31 @@ snapshots:
tailwindcss: 4.1.16
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.4)
+ '@tanstack/match-sorter-utils@9.2.6':
+ dependencies:
+ remove-accents: 0.5.0
+
+ '@tanstack/query-core@5.104.3': {}
+
+ '@tanstack/query-persist-client-core@5.104.3':
+ dependencies:
+ '@tanstack/query-core': 5.104.3
+
+ '@tanstack/query-sync-storage-persister@5.104.3':
+ dependencies:
+ '@tanstack/query-core': 5.104.3
+ '@tanstack/query-persist-client-core': 5.104.3
+
'@tanstack/table-core@8.21.3': {}
+ '@tanstack/vue-query@5.104.3(vue@3.5.22(typescript@5.9.3))':
+ dependencies:
+ '@tanstack/match-sorter-utils': 9.2.6
+ '@tanstack/query-core': 5.104.3
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.22(typescript@5.9.3)
+ vue-demi: 0.14.10(vue@3.5.22(typescript@5.9.3))
+
'@tanstack/vue-table@8.21.3(vue@3.5.22(typescript@5.9.3))':
dependencies:
'@tanstack/table-core': 8.21.3
@@ -1863,6 +1931,8 @@ snapshots:
regenerator-runtime@0.13.11: {}
+ remove-accents@0.5.0: {}
+
resolve-pkg-maps@1.0.0: {}
rfdc@1.4.1: {}
@@ -1962,6 +2032,10 @@ snapshots:
vscode-uri@3.1.0: {}
+ vue-demi@0.14.10(vue@3.5.22(typescript@5.9.3)):
+ dependencies:
+ vue: 3.5.22(typescript@5.9.3)
+
vue-i18n@11.4.12(vue@3.5.22(typescript@5.9.3)):
dependencies:
'@intlify/core-base': 11.4.12
diff --git a/polylan_submitter/src/App.vue b/polylan_submitter/src/App.vue
index 45dd592..35685f9 100644
--- a/polylan_submitter/src/App.vue
+++ b/polylan_submitter/src/App.vue
@@ -1,20 +1,14 @@
diff --git a/polylan_submitter/src/Home.vue b/polylan_submitter/src/Home.vue
index d0146ea..55dff42 100644
--- a/polylan_submitter/src/Home.vue
+++ b/polylan_submitter/src/Home.vue
@@ -1,9 +1,10 @@
diff --git a/polylan_submitter/src/OpusMagnum.vue b/polylan_submitter/src/OpusMagnum.vue
index 4a8647d..7a0c67a 100644
--- a/polylan_submitter/src/OpusMagnum.vue
+++ b/polylan_submitter/src/OpusMagnum.vue
@@ -9,10 +9,11 @@ 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 { polylanSubmitterApiGetUserInfo, opusMagnumApiGetCollection } from "@/api";
+import { opusMagnumApiGetCollection } from "@/api";
import { apiService, errorHelpers } from "@/services/apiService";
import { usePuzzlesStore } from "@/stores/puzzles";
import { useSubmissionsStore } from "@/stores/submissions";
+import { queryClient, userQuery } from "@/queries";
import { provideEventPath } from "@/composables/useEventPath";
import type { PuzzleResponse, UserInfo, SteamCollection } from "@/types";
import { useCountdown } from "@vueuse/core";
@@ -74,13 +75,7 @@ async function initialize() {
// Load user info
console.log("Loading user info...");
- const userResponse = await polylanSubmitterApiGetUserInfo();
- if (userResponse.data) {
- userInfo.value = userResponse.data as UserInfo;
- console.log("User info loaded:", userResponse.data);
- } else if (userResponse.error) {
- console.warn("User info error:", userResponse.error);
- }
+ userInfo.value = (await queryClient.fetchQuery(userQuery()).catch(() => null)) as UserInfo | null;
// Load collection data
console.log("Loading collection...");
diff --git a/polylan_submitter/src/Submissions.vue b/polylan_submitter/src/Submissions.vue
index 8cd7a10..5e550d8 100644
--- a/polylan_submitter/src/Submissions.vue
+++ b/polylan_submitter/src/Submissions.vue
@@ -1,12 +1,13 @@