feat : working UI for words
This commit is contained in:
@@ -1,11 +1,20 @@
|
|||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import 'reflect-metadata';
|
import { useUserStore } from '~/store/user.store';
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
|
||||||
|
const req = await userStore.whoami()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<NuxtRouteAnnouncer />
|
<NuxtRouteAnnouncer />
|
||||||
<NuxtWelcome />
|
|
||||||
<NuxtLayout>
|
<NuxtLayout>
|
||||||
|
<NuxtLoadingIndicator> Loading... </NuxtLoadingIndicator>
|
||||||
|
<Header />
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import { useUserStore } from '~/store/user.store';
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<header>
|
||||||
|
<div id="self">
|
||||||
|
<template v-if="userStore.has_contacted">
|
||||||
|
<ClientOnly>
|
||||||
|
<template v-if="userStore.self && userStore.self.id">
|
||||||
|
<User :id="userStore.self.id"></User>
|
||||||
|
<button>Logout</button>
|
||||||
|
</template>
|
||||||
|
<button v-else>Login</button>
|
||||||
|
</ClientOnly>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
Contacting server...
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="links">
|
||||||
|
<NuxtLink to="/">Home</NuxtLink>
|
||||||
|
<NuxtLink to="/words">Words</NuxtLink>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
header,
|
||||||
|
header * {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#self {
|
||||||
|
height: 32px
|
||||||
|
}
|
||||||
|
|
||||||
|
div#links {
|
||||||
|
width: 100%;
|
||||||
|
gap: 1%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { fetchUser } from '~/service/user.service';
|
||||||
|
import { useUserStore } from '~/store/user.store';
|
||||||
|
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const user = await userStore.fetchUser(props.id)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="user">
|
||||||
|
<img class="user-avatar" :src="`https://cdn.discordapp.com/avatars/${user?.id}/${user?.avatar}.webp?size=32`">
|
||||||
|
{{ user?.username }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css">
|
||||||
|
div.user {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.user-avatar {
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { WordResponseDTO } from '~/netcode/events/dto/wordresponse.dto';
|
||||||
|
import { useWordStore } from '~/store/word.store';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
word: WordResponseDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const wordStore = useWordStore();
|
||||||
|
|
||||||
|
async function changeWordState() {
|
||||||
|
if (loading.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loading.value = true
|
||||||
|
let data
|
||||||
|
if (props.word.enabled) {
|
||||||
|
data = await wordStore.disableWord(props.word.id)
|
||||||
|
} else {
|
||||||
|
data = await wordStore.enableWord(props.word.id)
|
||||||
|
}
|
||||||
|
loading.value = false
|
||||||
|
props.word.enabled = data.enabled
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="word">
|
||||||
|
<span>{{ word.value }}</span>
|
||||||
|
<input type="checkbox" :class="{ loading }" :checked="word.enabled" :disabled="loading"
|
||||||
|
@click.prevent="changeWordState" />
|
||||||
|
<User :id="word.ownerId"></User>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
div.word {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.loading {
|
||||||
|
cursor: wait
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { Expose, Type } from 'class-transformer';
|
|
||||||
import { ConceptResponseDTO } from './conceptresponse.dto';
|
|
||||||
|
|
||||||
export class ConceptInTurnResponseDTO {
|
|
||||||
constructor(partial: Partial<ConceptInTurnResponseDTO>) {
|
|
||||||
Object.assign(this, partial);
|
|
||||||
}
|
|
||||||
@Type(() => ConceptResponseDTO)
|
|
||||||
@Expose()
|
|
||||||
concept: ConceptResponseDTO;
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
order: number;
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
subconcept: number;
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
markers: number;
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { Expose } from 'class-transformer';
|
|
||||||
|
|
||||||
export class ConceptResponseDTO {
|
|
||||||
constructor(partial: Partial<ConceptResponseDTO>) {
|
|
||||||
Object.assign(this, partial);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { Expose, Type } from 'class-transformer';
|
|
||||||
import { UserResponseDTO } from './userresponse.dto';
|
|
||||||
import { PlayerResponseDTO } from './playerresponse.dto';
|
|
||||||
import { ConceptInTurnResponseDTO } from './conceptinturnresponse.dto';
|
|
||||||
|
|
||||||
export class GameResponseDTO {
|
|
||||||
constructor(partial: Partial<GameResponseDTO>) {
|
|
||||||
Object.assign(this, partial);
|
|
||||||
this.owner = partial.owner;
|
|
||||||
this.players = partial.players;
|
|
||||||
this.currentPlayer = partial.currentPlayer;
|
|
||||||
this.currentConcepts = partial.currentConcepts;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@Type(() => UserResponseDTO)
|
|
||||||
@Expose()
|
|
||||||
owner: UserResponseDTO;
|
|
||||||
|
|
||||||
@Type(() => PlayerResponseDTO)
|
|
||||||
@Expose()
|
|
||||||
players: PlayerResponseDTO[];
|
|
||||||
|
|
||||||
@Type(() => PlayerResponseDTO)
|
|
||||||
@Expose()
|
|
||||||
currentPlayer: PlayerResponseDTO;
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
started: boolean;
|
|
||||||
|
|
||||||
@Type(() => ConceptInTurnResponseDTO)
|
|
||||||
@Expose()
|
|
||||||
currentConcepts: ConceptInTurnResponseDTO[];
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { Expose, Type } from 'class-transformer';
|
|
||||||
import { UserResponseDTO } from './userresponse.dto';
|
|
||||||
|
|
||||||
export class PlayerResponseDTO {
|
|
||||||
constructor(partial: Partial<PlayerResponseDTO>) {
|
|
||||||
Object.assign(this, partial);
|
|
||||||
this.user = partial.user;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Type(() => UserResponseDTO)
|
|
||||||
@Expose()
|
|
||||||
user: UserResponseDTO;
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
score: number;
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { Expose } from 'class-transformer';
|
|
||||||
|
|
||||||
export class UserResponseDTO {
|
|
||||||
constructor(partial: Partial<UserResponseDTO>) {
|
|
||||||
Object.assign(this, partial);
|
|
||||||
this.id = partial.id;
|
|
||||||
}
|
|
||||||
@Expose()
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
username: string;
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
avatar: string;
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { Expose } from 'class-transformer';
|
|
||||||
|
|
||||||
export class WordResponseDTO {
|
|
||||||
constructor(partial: Partial<WordResponseDTO>) {
|
|
||||||
Object.assign(this, partial);
|
|
||||||
}
|
|
||||||
@Expose()
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
value: string;
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
ownerId: string;
|
|
||||||
|
|
||||||
@Expose()
|
|
||||||
enabled: boolean;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import typescriptEslintEslintPlugin from '@typescript-eslint/eslint-plugin';
|
||||||
|
import globals from 'globals';
|
||||||
|
import tsParser from '@typescript-eslint/parser';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: __dirname,
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
allConfig: js.configs.all,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/.eslintrc.js'],
|
||||||
|
},
|
||||||
|
...compat.extends(
|
||||||
|
'plugin:@typescript-eslint/recommended-type-checked',
|
||||||
|
'plugin:prettier/recommended',
|
||||||
|
),
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
'@typescript-eslint': typescriptEslintEslintPlugin,
|
||||||
|
},
|
||||||
|
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
...globals.jest,
|
||||||
|
},
|
||||||
|
|
||||||
|
parser: tsParser,
|
||||||
|
ecmaVersion: 5,
|
||||||
|
sourceType: 'module',
|
||||||
|
|
||||||
|
parserOptions: {
|
||||||
|
project: 'tsconfig.json',
|
||||||
|
tsconfigRootDir: '/home/legonzaur/Documents/code/concept/back',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/interface-name-prefix': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'require-await': 'off',
|
||||||
|
'@typescript-eslint/require-await': 'error',
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_',
|
||||||
|
caughtErrorsIgnorePattern: '^_',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
const validPaths = ["/", "/rules", "/test"];
|
const validPaths = ["/", "/rules", "/test", "/words"];
|
||||||
|
|
||||||
export default defineNuxtRouteMiddleware(async (to, from) => {
|
export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export function subscribe(endpoint: string) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
const evtSource = new EventSource(config.public.endpoint + endpoint, {
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return evtSource;
|
||||||
|
}
|
||||||
+7
-2
@@ -1,8 +1,10 @@
|
|||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
|
import 'reflect-metadata';
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
compatibilityDate: '2024-11-01',
|
compatibilityDate: '2024-11-01',
|
||||||
ssr: true,
|
|
||||||
devtools: { enabled: true },
|
devtools: { enabled: true },
|
||||||
|
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
public: {
|
public: {
|
||||||
endpoint: "https://playtest.legonzaur.fr/api", // can be overridden by NUXT_PUBLIC_ENDPOINT environment variable
|
endpoint: "https://playtest.legonzaur.fr/api", // can be overridden by NUXT_PUBLIC_ENDPOINT environment variable
|
||||||
@@ -10,10 +12,13 @@ export default defineNuxtConfig({
|
|||||||
devMode: false,
|
devMode: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
nitro: {
|
nitro: {
|
||||||
prerender: {
|
prerender: {
|
||||||
routes: ["/login", "/"],
|
routes: ["/login", "/"],
|
||||||
crawlLinks: true,
|
crawlLinks: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
|
||||||
|
modules: ["@pinia/nuxt"],
|
||||||
|
})
|
||||||
Generated
+65
@@ -7,8 +7,10 @@
|
|||||||
"name": "nuxt-app",
|
"name": "nuxt-app",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@pinia/nuxt": "^0.9.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"nuxt": "^3.15.0",
|
"nuxt": "^3.15.0",
|
||||||
|
"pinia": "^2.3.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
"vue-router": "latest"
|
"vue-router": "latest"
|
||||||
@@ -1827,6 +1829,21 @@
|
|||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@pinia/nuxt": {
|
||||||
|
"version": "0.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pinia/nuxt/-/nuxt-0.9.0.tgz",
|
||||||
|
"integrity": "sha512-2yeRo7LeyCF68AbNeL3xu2h6uw0617RkcsYxmA8DJM0R0PMdz5wQHnc44KeENQxR/Mrq8T910XVT6buosqsjBQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@nuxt/kit": "^3.9.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/posva"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"pinia": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pkgjs/parseargs": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
@@ -6474,6 +6491,28 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pinia": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-ohZj3jla0LL0OH5PlLTDMzqKiVw2XARmC1XYLdLWIPBMdhDW/123ZWr4zVAhtJm+aoSkFa13pYXskAvAscIkhQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.6.3",
|
||||||
|
"vue-demi": "^0.14.10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/posva"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=4.4.4",
|
||||||
|
"vue": "^2.7.0 || ^3.5.11"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pkg-types": {
|
"node_modules/pkg-types": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.0.tgz",
|
||||||
@@ -9431,6 +9470,32 @@
|
|||||||
"ufo": "^1.5.4"
|
"ufo": "^1.5.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-demi": {
|
||||||
|
"version": "0.14.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
||||||
|
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||||
|
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.0.0-rc.1",
|
||||||
|
"vue": "^3.0.0-0 || ^2.6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue-devtools-stub": {
|
"node_modules/vue-devtools-stub": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/vue-devtools-stub/-/vue-devtools-stub-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/vue-devtools-stub/-/vue-devtools-stub-0.1.0.tgz",
|
||||||
|
|||||||
@@ -10,8 +10,10 @@
|
|||||||
"postinstall": "nuxt prepare"
|
"postinstall": "nuxt prepare"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@pinia/nuxt": "^0.9.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"nuxt": "^3.15.0",
|
"nuxt": "^3.15.0",
|
||||||
|
"pinia": "^2.3.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
"vue-router": "latest"
|
"vue-router": "latest"
|
||||||
|
|||||||
+5
-1
@@ -1,3 +1,7 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
Hello
|
Hello
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
<template>
|
<template>
|
||||||
Hello
|
Login
|
||||||
</template>
|
</template>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import { useWordStore } from '~/store/word.store';
|
||||||
|
|
||||||
|
const wordStore = useWordStore();
|
||||||
|
|
||||||
|
await wordStore.fetchWordList()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="words">
|
||||||
|
<div class="header">
|
||||||
|
<span>Word</span>
|
||||||
|
<span>Status</span>
|
||||||
|
<span>Submitter</span>
|
||||||
|
</div>
|
||||||
|
<Word v-for="word in wordStore.words" :word="word" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
div#words {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.header {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { UserResponseDTO } from "~/netcode/events/dto/userresponse.dto";
|
||||||
|
|
||||||
|
export async function fetchUser(id: string) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
const data = await useFetch<UserResponseDTO>(config.public.endpoint + `/users/` + id, {
|
||||||
|
credentials: "include",
|
||||||
|
}).then(_d => {
|
||||||
|
if (_d.data.value) {
|
||||||
|
_d.data.value = new UserResponseDTO(_d.data.value)
|
||||||
|
}
|
||||||
|
return _d
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { UserResponseDTO } from "~/netcode/events/dto/userresponse.dto";
|
||||||
|
|
||||||
|
export const useUserStore = defineStore("user", {
|
||||||
|
state: () => ({
|
||||||
|
users: {} as Record<string, UserResponseDTO>,
|
||||||
|
self: null as UserResponseDTO | null,
|
||||||
|
has_contacted: false
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
async fetchUser(id: string) {
|
||||||
|
if (id in this.users) {
|
||||||
|
return this.users[id]
|
||||||
|
}
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
const data = await useFetch<UserResponseDTO>(config.public.endpoint + `/users/` + id, {
|
||||||
|
credentials: "include",
|
||||||
|
immediate: true
|
||||||
|
})
|
||||||
|
if (data.data.value) {
|
||||||
|
this.users[data.data.value.id] = data.data.value
|
||||||
|
}
|
||||||
|
return data.data.value
|
||||||
|
},
|
||||||
|
async whoami() {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
const req = await useFetch<UserResponseDTO>(config.public.endpoint + "/whoami", {
|
||||||
|
credentials: "include",
|
||||||
|
server: false,
|
||||||
|
immediate: true,
|
||||||
|
onResponse: (data) => {
|
||||||
|
this.self = data.response._data
|
||||||
|
this.has_contacted = true
|
||||||
|
},
|
||||||
|
lazy: true
|
||||||
|
})
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { WordResponseDTO } from "~/netcode/events/dto/wordresponse.dto";
|
||||||
|
|
||||||
|
export const useWordStore = defineStore("word", {
|
||||||
|
state: () => ({
|
||||||
|
words: {} as Record<string, WordResponseDTO>,
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
async fetchWordList() {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
const data = await useFetch<WordResponseDTO[]>(config.public.endpoint + `/words/all`, {
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
if (data.data.value && !data.error.value) {
|
||||||
|
|
||||||
|
for (const word of data.data.value) {
|
||||||
|
this.words[word.id] = word
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
async enableWord(id: number) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
const data = await $fetch<WordResponseDTO>(config.public.endpoint + `/words/` + id + "/enable", {
|
||||||
|
credentials: "include",
|
||||||
|
method: "PUT"
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
async disableWord(id: number) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
const data = await $fetch<WordResponseDTO>(config.public.endpoint + `/words/` + id + "/disable", {
|
||||||
|
credentials: "include",
|
||||||
|
method: "PUT"
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
+7
-2
@@ -1,4 +1,9 @@
|
|||||||
{
|
{
|
||||||
// https://nuxt.com/docs/guide/concepts/typescript
|
// https://nuxt.com/docs/guide/concepts/typescript
|
||||||
"extends": "./.nuxt/tsconfig.json"
|
"extends": "./.nuxt/tsconfig.json",
|
||||||
}
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"strictPropertyInitialization": false,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user