feat : concept creation

This commit is contained in:
2025-01-13 22:33:55 +01:00
parent 8223ca5166
commit 452d76ca4b
8 changed files with 1279 additions and 54 deletions
+1
View File
@@ -1,6 +1,7 @@
{ {
"files.exclude": { "files.exclude": {
"**/.nuxt": true, "**/.nuxt": true,
"**/.output": true,
"**/node_modules": true "**/node_modules": true
} }
} }
+6 -2
View File
@@ -1,11 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { useUserStore } from "~/store/user.store"; import { useUserStore } from "~/store/user.store";
import { useLobbyStore } from "./store/lobby.store"; import { useLobbyStore } from "./store/lobby.store";
import { useConceptStore } from "./store/concepts.store";
const userStore = useUserStore(); const userStore = useUserStore();
const lobbyStore = useLobbyStore(); const lobbyStore = useLobbyStore();
const conceptStore = useConceptStore()
void userStore.whoami();
void conceptStore.fetchConceptTypes();
void lobbyStore.fetchGames();
userStore.whoami();
lobbyStore.fetchGames();
onBeforeMount(() => { onBeforeMount(() => {
lobbyStore.subscribe(); lobbyStore.subscribe();
}); });
+28 -25
View File
@@ -1,10 +1,13 @@
import typescriptEslintEslintPlugin from '@typescript-eslint/eslint-plugin'; /* eslint-disable @typescript-eslint/no-unsafe-member-access */
import globals from 'globals'; /* eslint-disable @typescript-eslint/no-unsafe-call */
import tsParser from '@typescript-eslint/parser'; /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import path from 'node:path'; import typescriptEslintEslintPlugin from "@typescript-eslint/eslint-plugin";
import { fileURLToPath } from 'node:url'; import globals from "globals";
import js from '@eslint/js'; import tsParser from "@typescript-eslint/parser";
import { FlatCompat } from '@eslint/eslintrc'; 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 __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
@@ -16,15 +19,15 @@ const compat = new FlatCompat({
export default [ export default [
{ {
ignores: ['**/.eslintrc.js'], ignores: ["**/.eslintrc.js"],
}, },
...compat.extends( ...compat.extends(
'plugin:@typescript-eslint/recommended-type-checked', "plugin:@typescript-eslint/recommended-type-checked",
'plugin:prettier/recommended', "plugin:prettier/recommended"
), ),
{ {
plugins: { plugins: {
'@typescript-eslint': typescriptEslintEslintPlugin, "@typescript-eslint": typescriptEslintEslintPlugin,
}, },
languageOptions: { languageOptions: {
@@ -35,27 +38,27 @@ export default [
parser: tsParser, parser: tsParser,
ecmaVersion: 5, ecmaVersion: 5,
sourceType: 'module', sourceType: "module",
parserOptions: { parserOptions: {
project: 'tsconfig.json', project: "tsconfig.json",
tsconfigRootDir: '/home/legonzaur/Documents/code/concept/back', tsconfigRootDir: __dirname,
}, },
}, },
rules: { rules: {
'@typescript-eslint/interface-name-prefix': 'off', "@typescript-eslint/interface-name-prefix": "off",
'@typescript-eslint/explicit-function-return-type': 'off', "@typescript-eslint/explicit-function-return-type": "off",
'@typescript-eslint/explicit-module-boundary-types': 'off', "@typescript-eslint/explicit-module-boundary-types": "off",
'@typescript-eslint/no-explicit-any': 'off', "@typescript-eslint/no-explicit-any": "off",
'require-await': 'off', "require-await": "off",
'@typescript-eslint/require-await': 'error', "@typescript-eslint/require-await": "error",
'@typescript-eslint/no-unused-vars': [ "@typescript-eslint/no-unused-vars": [
'warn', "warn",
{ {
argsIgnorePattern: '^_', argsIgnorePattern: "^_",
varsIgnorePattern: '^_', varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: '^_', caughtErrorsIgnorePattern: "^_",
}, },
], ],
}, },
+1192
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -18,5 +18,14 @@
"vue": "latest", "vue": "latest",
"vue-multiselect": "^3.1.0", "vue-multiselect": "^3.1.0",
"vue-router": "latest" "vue-router": "latest"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.20.0",
"@typescript-eslint/parser": "^8.20.0",
"@typescript-eslint/typescript-estree": "^8.20.0",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.1",
"typescript-eslint": "^8.20.0"
} }
} }
+11 -14
View File
@@ -5,7 +5,10 @@ import { useConceptStore } from "~/store/concepts.store";
const newConcept = ref(""); const newConcept = ref("");
const conceptStore = useConceptStore(); const conceptStore = useConceptStore();
await conceptStore.fetchConceptList(); await Promise.all([
conceptStore.fetchConceptList()
])
const loading = ref(false); const loading = ref(false);
const conceptInput = ref<HTMLInputElement | null>(null); const conceptInput = ref<HTMLInputElement | null>(null);
@@ -49,25 +52,18 @@ onUnmounted(() => {
<span>Delete</span> <span>Delete</span>
<span>Type</span> <span>Type</span>
</div> </div>
<div <div class="concept" v-for="concept in conceptStore.concepts" :key="concept.id">
class="concept"
v-for="concept in conceptStore.concepts"
:key="concept.id"
>
<span>{{ concept.value }}</span> <span>{{ concept.value }}</span>
<input type="checkbox" checked="true" disabled="true" /> <input type="checkbox" checked="true" disabled="true" />
<input type="button" value="Delete" @click="deleteConcept(concept.id)" /> <input type="button" value="Delete" @click="deleteConcept(concept.id)" />
<Multiselect :options="[]"></Multiselect> <Multiselect :options="Object.values(conceptStore.conceptTypes)" track-by="id"
deselect-label="Can't remove this value" :allow-empty="false" label="value" v-model="concept.type">
</Multiselect>
</div> </div>
<div class="new-concept"> <div class="new-concept">
<span> <span>
<input <input ref="conceptInput" type="text" :disabled="loading" @keypress.enter="createConcept"
ref="conceptInput" v-model="newConcept" />
type="text"
:disabled="loading"
@keypress.enter="createConcept"
v-model="newConcept"
/>
<button :disabled="loading" @click="createConcept">Submit</button> <button :disabled="loading" @click="createConcept">Submit</button>
</span> </span>
</div> </div>
@@ -84,6 +80,7 @@ div.concept {
display: grid; display: grid;
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
} }
div.header { div.header {
display: grid; display: grid;
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
+31 -12
View File
@@ -1,34 +1,54 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { subscribe } from "~/netcode"; import { subscribe } from "~/netcode";
import type { ConceptResponseDTO } from "~/netcode/events/dto/conceptresponse.dto"; import type {
ConceptResponseDTO,
ConceptTypeResponseDTO,
} from "~/netcode/events/dto/conceptresponse.dto";
import type { ConceptEvent } from "~/netcode/events/events";
import { handlers } from "~/netcode/handlers/concept.handler"; import { handlers } from "~/netcode/handlers/concept.handler";
export const useConceptStore = defineStore("concept", { export const useConceptStore = defineStore("concept", {
state: () => ({ state: () => ({
concepts: {} as Record<string, ConceptResponseDTO>, concepts: {} as Record<string, ConceptResponseDTO>,
eventSource: null as null | EventSource, eventSource: null as null | EventSource,
conceptTypes: {} as Record<string, ConceptTypeResponseDTO>,
}), }),
actions: { actions: {
async subscribe() { subscribe() {
this.eventSource = subscribe(`/concepts/subscribe`); this.eventSource = subscribe(`/concepts/subscribe`);
this.eventSource.addEventListener("message", async (message) => { this.eventSource.addEventListener("message", (message) => {
const data = JSON.parse(message.data); const data = JSON.parse(message.data) as ConceptEvent;
const type = data.type as string; const type = data.type;
if (type in handlers) { if (type in handlers) {
handlers[type as keyof typeof handlers](data); handlers[type as keyof typeof handlers](data);
} }
}); });
}, },
async close() { close() {
this.eventSource?.close(); this.eventSource?.close();
}, },
async fetchConceptTypes() {
const config = useRuntimeConfig();
const data = await useFetch<ConceptTypeResponseDTO[]>(
config.public.endpoint + `/concepts/types`,
{
credentials: "include",
},
);
if (data.data.value && !data.error.value) {
for (const conceptType of data.data.value) {
this.conceptTypes[conceptType.id] = conceptType;
}
}
return this.conceptTypes;
},
async fetchConceptList() { async fetchConceptList() {
const config = useRuntimeConfig(); const config = useRuntimeConfig();
const data = await useFetch<ConceptResponseDTO[]>( const data = await useFetch<ConceptResponseDTO[]>(
config.public.endpoint + `/concepts/all`, config.public.endpoint + `/concepts/all`,
{ {
credentials: "include", credentials: "include",
} },
); );
if (data.data.value && !data.error.value) { if (data.data.value && !data.error.value) {
for (const concept of data.data.value) { for (const concept of data.data.value) {
@@ -44,7 +64,7 @@ export const useConceptStore = defineStore("concept", {
{ {
credentials: "include", credentials: "include",
method: "PUT", method: "PUT",
} },
); );
// word.enabled = true; // word.enabled = true;
// return data; // return data;
@@ -56,12 +76,11 @@ export const useConceptStore = defineStore("concept", {
{ {
credentials: "include", credentials: "include",
method: "PUT", method: "PUT",
} },
); );
// word.enabled = false; // word.enabled = false;
// return data; // return data;
}, },
async createConcept(value: string) { async createConcept(value: string) {
const config = useRuntimeConfig(); const config = useRuntimeConfig();
const data = await $fetch<ConceptResponseDTO>( const data = await $fetch<ConceptResponseDTO>(
@@ -70,7 +89,7 @@ export const useConceptStore = defineStore("concept", {
credentials: "include", credentials: "include",
body: { value }, body: { value },
method: "POST", method: "POST",
} },
); );
}, },
async deleteConcept(id: number) { async deleteConcept(id: number) {
@@ -80,7 +99,7 @@ export const useConceptStore = defineStore("concept", {
{ {
credentials: "include", credentials: "include",
method: "DELETE", method: "DELETE",
} },
); );
}, },
}, },