From 48074f760387d979686c769856529b64401874e0 Mon Sep 17 00:00:00 2001 From: Jonas Kappa Date: Fri, 2 Jan 2026 18:00:57 +0100 Subject: [PATCH] Added Editing of Questions --- ...udioMultipleChoiceQuestionComponent.svelte | 43 ++- src/lib/AudioQuestionComponent.svelte | 26 +- src/lib/Button.svelte | 4 +- ...mageMultipleChoiceQuestionComponent.svelte | 47 ++- src/lib/ImageQuestionComponent.svelte | 31 +- .../MultipleChoiceQuestionComponent.svelte | 12 +- src/lib/RessourceManager.svelte | 1 - src/lib/Textfield.svelte | 22 +- src/lib/Types.ts | 5 +- src/lib/Wall.svelte | 11 +- src/lib/games/games.ts | 14 +- src/routes/editor/EditorState.ts | 12 + src/routes/editor/[gameid]/+page.svelte | 56 ++-- .../[categoryid]/[questionid]/+page.svelte | 291 +++++++++++++++++ .../[questionid]/EditorAudio.svelte | 14 + .../EditorAudioMultipleChoice.svelte | 14 + .../[questionid]/EditorImage.svelte | 14 + .../EditorImageMultipleChoice.svelte | 14 + .../[questionid]/EditorMediaField.svelte | 67 ++++ .../[questionid]/EditorMultipleChoice.svelte | 44 +++ .../[questionid]/EditorSimple.svelte | 13 + .../[questionid]/questionconverters.ts | 303 ++++++++++++++++++ src/routes/editor/fetchers.ts | 108 +++++++ 23 files changed, 1095 insertions(+), 71 deletions(-) create mode 100644 src/routes/editor/EditorState.ts create mode 100644 src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/+page.svelte create mode 100644 src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorAudio.svelte create mode 100644 src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorAudioMultipleChoice.svelte create mode 100644 src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorImage.svelte create mode 100644 src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorImageMultipleChoice.svelte create mode 100644 src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorMediaField.svelte create mode 100644 src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorMultipleChoice.svelte create mode 100644 src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorSimple.svelte create mode 100644 src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/questionconverters.ts create mode 100644 src/routes/editor/fetchers.ts diff --git a/src/lib/AudioMultipleChoiceQuestionComponent.svelte b/src/lib/AudioMultipleChoiceQuestionComponent.svelte index cb63f32..405ad49 100644 --- a/src/lib/AudioMultipleChoiceQuestionComponent.svelte +++ b/src/lib/AudioMultipleChoiceQuestionComponent.svelte @@ -1,6 +1,7 @@
@@ -42,7 +66,16 @@ {/if} {#if showPlayer}
- + {#if audioPath} + + {:else} +
+
+ +
+
Kein Audio ausgewählt
+
+ {/if}
{/if} {#if showQuestion} diff --git a/src/lib/AudioQuestionComponent.svelte b/src/lib/AudioQuestionComponent.svelte index eb170ca..1c4e237 100644 --- a/src/lib/AudioQuestionComponent.svelte +++ b/src/lib/AudioQuestionComponent.svelte @@ -1,6 +1,7 @@
@@ -23,7 +36,16 @@ {/if} {#if showPlayer}
- + {#if audioPath} + + {:else} +
+
+ +
+
Kein Audio ausgewählt
+
+ {/if}
{/if} {#if showAnswer} diff --git a/src/lib/Button.svelte b/src/lib/Button.svelte index 0ce4a62..f0aa9a3 100644 --- a/src/lib/Button.svelte +++ b/src/lib/Button.svelte @@ -9,13 +9,15 @@ ) => void; children: Snippet; class?: string; + disabled?: boolean; } - let { onclick, children, class: classes }: Props = $props(); + let { onclick, children, class: classes, disabled = false }: Props = $props(); +
+ {#if question} +
+ +
+
+ + +
+ +
+ +
+
+ {#if isSimpleQuestion(question)} + + {:else if isMultipleChoiceQuestion(question)} + + {:else if isImageQuestion(question)} + + {:else if isImageMultipleChoiceQuestion(question)} + + {:else if isAudioQuestion(question)} + + {:else if isAudioMultipleChoiceQuestion(question)} + {/if} +
+
+ +
+ {#if isSimpleQuestion(question)} + + {:else if isMultipleChoiceQuestion(question)} + + {:else if isImageQuestion(question)} + + {:else if isImageMultipleChoiceQuestion(question)} + + {:else if isAudioQuestion(question)} + + {:else if isAudioMultipleChoiceQuestion(question)} + + {/if} +
+
+ {:else} + No question isFileLoadingAllowed, please try again + {/if} +
diff --git a/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorAudio.svelte b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorAudio.svelte new file mode 100644 index 0000000..726aa7e --- /dev/null +++ b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorAudio.svelte @@ -0,0 +1,14 @@ + + + + diff --git a/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorAudioMultipleChoice.svelte b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorAudioMultipleChoice.svelte new file mode 100644 index 0000000..9cf9ae6 --- /dev/null +++ b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorAudioMultipleChoice.svelte @@ -0,0 +1,14 @@ + + + + diff --git a/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorImage.svelte b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorImage.svelte new file mode 100644 index 0000000..a43d513 --- /dev/null +++ b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorImage.svelte @@ -0,0 +1,14 @@ + + + + diff --git a/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorImageMultipleChoice.svelte b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorImageMultipleChoice.svelte new file mode 100644 index 0000000..bf1ae6c --- /dev/null +++ b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorImageMultipleChoice.svelte @@ -0,0 +1,14 @@ + + + + diff --git a/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorMediaField.svelte b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorMediaField.svelte new file mode 100644 index 0000000..152d69c --- /dev/null +++ b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorMediaField.svelte @@ -0,0 +1,67 @@ + + +
+ {#if isAudioQuestion(question) || isAudioMultipleChoiceQuestion(question)} +
Audio:
+ {:else} +
Bild:
+ {/if} +
+ {#if isAudioQuestion(question) || isAudioMultipleChoiceQuestion(question)} + + {:else} + + {/if} + +
+
+ + { + if (isAudioQuestion(question) || isAudioMultipleChoiceQuestion(question)) { + question.data.audio = res; + } else { + question.data.image = res; + } + }} +> diff --git a/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorMultipleChoice.svelte b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorMultipleChoice.svelte new file mode 100644 index 0000000..88b46be --- /dev/null +++ b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorMultipleChoice.svelte @@ -0,0 +1,44 @@ + + + +
+
Antworten
+ +
+
+{#each question.data.choices as _, answerIndex} +
+ + +
+{/each} diff --git a/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorSimple.svelte b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorSimple.svelte new file mode 100644 index 0000000..021ccd9 --- /dev/null +++ b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/EditorSimple.svelte @@ -0,0 +1,13 @@ + + + + diff --git a/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/questionconverters.ts b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/questionconverters.ts new file mode 100644 index 0000000..19a6339 --- /dev/null +++ b/src/routes/editor/[gameid]/[wallid]/[categoryid]/[questionid]/questionconverters.ts @@ -0,0 +1,303 @@ +import type { + AudioMultipleChoiceQuestion, + AudioQuestion, + ImageMultipleChoiceQuestion, + ImageQuestion, + MultipleChoiceQuestion, + Question, + QuestionType, + SimpleQuestion +} from "$lib/games/games"; +import type { GeneralQuestion } from "$lib/Types"; + +function defaultConversion(question: Question, type: QuestionType): Question { + return { + points: question.points, + _id: question._id, + owner: question.owner, + type + }; +} + +export function convert(q: GeneralQuestion, t: QuestionType) { + if (q.type === "SIMPLE") { + switch (t) { + case "SIMPLE": + return q; + case "MULTIPLE_CHOICE": + return simpleToMultipleChoice(q); + case "IMAGE": + return simpleToImage(q); + case "IMAGE_MULTIPLE_CHOICE": + return simpleToImageMultipleChoice(q); + case "AUDIO": + return simpleToAudio(q); + case "AUDIO_MULTIPLE_CHOICE": + return simpleToAudioMultipleChoice(q); + } + } else if (q.type === "MULTIPLE_CHOICE") { + switch (t) { + case "SIMPLE": + return multipleChoiceToSimple(q); + case "MULTIPLE_CHOICE": + return q; + case "IMAGE": + return multipleChoiceToImage(q); + case "IMAGE_MULTIPLE_CHOICE": + return multipleChoiceToImageMultipleChoice(q); + case "AUDIO": + return multipleChoiceToAudio(q); + case "AUDIO_MULTIPLE_CHOICE": + return multipleChoiceToAudioMultipleChoice(q); + } + } else if (q.type === "IMAGE") { + switch (t) { + case "SIMPLE": + return mediaToSimple(q); + case "MULTIPLE_CHOICE": + return simpleToMultipleChoice(q); + case "IMAGE": + return q; + case "IMAGE_MULTIPLE_CHOICE": + return imageToImageMultipleChoice(q); + case "AUDIO": + return simpleToAudio(q); + case "AUDIO_MULTIPLE_CHOICE": + return simpleToMultipleChoice(q); + } + } else if (q.type === "IMAGE_MULTIPLE_CHOICE") { + switch (t) { + case "SIMPLE": + return multipleChoiceToSimple(q); + case "MULTIPLE_CHOICE": + return mediaMultipleChoiceToMultipleChoice(q); + case "IMAGE": + return imageMultipleChoiceToImage(q); + case "IMAGE_MULTIPLE_CHOICE": + return q; + case "AUDIO": + return multipleChoiceToAudio(q); + case "AUDIO_MULTIPLE_CHOICE": + return multipleChoiceToAudioMultipleChoice(q); + } + } else if (q.type === "AUDIO") { + switch (t) { + case "SIMPLE": + return mediaToSimple(q); + case "MULTIPLE_CHOICE": + return simpleToMultipleChoice(q); + case "IMAGE": + return simpleToImage(q); + case "IMAGE_MULTIPLE_CHOICE": + return simpleToImageMultipleChoice(q); + case "AUDIO": + return q; + case "AUDIO_MULTIPLE_CHOICE": + return audioToAudioMultipleChoice(q); + } + } else if (q.type === "AUDIO_MULTIPLE_CHOICE") { + switch (t) { + case "SIMPLE": + return multipleChoiceToSimple(q); + case "MULTIPLE_CHOICE": + return mediaMultipleChoiceToMultipleChoice(q); + case "IMAGE": + return multipleChoiceToImage(q); + case "IMAGE_MULTIPLE_CHOICE": + return multipleChoiceToImageMultipleChoice(q); + case "AUDIO": + return audioMultipleChoiceToAudio(q); + case "AUDIO_MULTIPLE_CHOICE": + return q; + } + } + return q; +} + +export function simpleToMultipleChoice( + question: SimpleQuestion | ImageQuestion | AudioQuestion +): MultipleChoiceQuestion { + return { + ...(defaultConversion(question, "MULTIPLE_CHOICE") as MultipleChoiceQuestion), + data: { + question: question.data.question, + choices: [question.data.answer] + } + }; +} + +export function simpleToImage(question: SimpleQuestion | AudioQuestion): ImageQuestion { + return { + ...(defaultConversion(question, "IMAGE") as ImageQuestion), + data: { + question: question.data.question, + answer: question.data.answer, + image: null + } + }; +} + +export function simpleToImageMultipleChoice( + question: SimpleQuestion | AudioQuestion +): ImageMultipleChoiceQuestion { + return { + ...(defaultConversion(question, "IMAGE_MULTIPLE_CHOICE") as ImageMultipleChoiceQuestion), + data: { + question: question.data.question, + choices: [question.data.answer], + image: null + } + }; +} + +export function simpleToAudio(question: SimpleQuestion | ImageQuestion): AudioQuestion { + return { + ...(defaultConversion(question, "AUDIO") as AudioQuestion), + data: { + question: question.data.question, + answer: question.data.answer, + audio: null + } + }; +} + +export function simpleToAudioMultipleChoice(question: SimpleQuestion): AudioMultipleChoiceQuestion { + return { + ...(defaultConversion(question, "AUDIO_MULTIPLE_CHOICE") as AudioMultipleChoiceQuestion), + data: { + question: question.data.question, + choices: [question.data.answer], + audio: null + } + }; +} + +export function mediaToSimple(question: ImageQuestion | AudioQuestion): SimpleQuestion { + return { + ...(defaultConversion(question, "SIMPLE") as SimpleQuestion), + data: { + question: question.data.question, + answer: question.data.answer + } + }; +} + +export function mediaMultipleChoiceToMultipleChoice( + question: ImageMultipleChoiceQuestion | AudioMultipleChoiceQuestion +): MultipleChoiceQuestion { + return { + ...(defaultConversion(question, "MULTIPLE_CHOICE") as MultipleChoiceQuestion), + data: { + question: question.data.question, + choices: [...question.data.choices] + } + }; +} + +export function multipleChoiceToSimple( + question: MultipleChoiceQuestion | ImageMultipleChoiceQuestion | AudioMultipleChoiceQuestion +): SimpleQuestion { + return { + ...(defaultConversion(question, "SIMPLE") as SimpleQuestion), + data: { + question: question.data.question, + answer: question.data.choices[0] ? question.data.choices[0] : "Antwort" + } + }; +} + +export function multipleChoiceToImage( + question: MultipleChoiceQuestion | AudioMultipleChoiceQuestion +): ImageQuestion { + return { + ...(defaultConversion(question, "IMAGE") as ImageQuestion), + data: { + question: question.data.question, + answer: question.data.choices[0] ? question.data.choices[0] : "Antwort", + image: null + } + }; +} + +export function multipleChoiceToImageMultipleChoice( + question: MultipleChoiceQuestion | AudioMultipleChoiceQuestion +): ImageMultipleChoiceQuestion { + return { + ...(defaultConversion(question, "IMAGE_MULTIPLE_CHOICE") as ImageMultipleChoiceQuestion), + data: { + question: question.data.question, + choices: [...question.data.choices], + image: null + } + }; +} + +export function multipleChoiceToAudio( + question: MultipleChoiceQuestion | ImageMultipleChoiceQuestion +): AudioQuestion { + return { + ...(defaultConversion(question, "AUDIO") as AudioQuestion), + data: { + question: question.data.question, + answer: question.data.choices[0] ? question.data.choices[0] : "Antwort", + audio: null + } + }; +} + +export function multipleChoiceToAudioMultipleChoice( + question: MultipleChoiceQuestion | ImageMultipleChoiceQuestion +): AudioMultipleChoiceQuestion { + return { + ...(defaultConversion(question, "AUDIO_MULTIPLE_CHOICE") as AudioMultipleChoiceQuestion), + data: { + question: question.data.question, + choices: [...question.data.choices], + audio: null + } + }; +} + +export function imageToImageMultipleChoice(question: ImageQuestion): ImageMultipleChoiceQuestion { + return { + ...(defaultConversion(question, "IMAGE_MULTIPLE_CHOICE") as ImageMultipleChoiceQuestion), + data: { + question: question.data.question, + choices: [question.data.answer], + image: question.data.image + } + }; +} + +export function imageMultipleChoiceToImage(question: ImageMultipleChoiceQuestion): ImageQuestion { + return { + ...(defaultConversion(question, "IMAGE") as ImageQuestion), + data: { + question: question.data.question, + answer: question.data.choices[0] ? question.data.choices[0] : "Antwort", + image: question.data.image + } + }; +} + +export function audioToAudioMultipleChoice(question: AudioQuestion): AudioMultipleChoiceQuestion { + return { + ...(defaultConversion(question, "AUDIO_MULTIPLE_CHOICE") as AudioMultipleChoiceQuestion), + data: { + question: question.data.question, + choices: [question.data.answer], + audio: question.data.audio + } + }; +} + +export function audioMultipleChoiceToAudio(question: AudioMultipleChoiceQuestion): AudioQuestion { + return { + ...(defaultConversion(question, "AUDIO") as AudioQuestion), + data: { + question: question.data.question, + answer: question.data.choices[0] ? question.data.choices[0] : "Antwort", + audio: question.data.audio + } + }; +} diff --git a/src/routes/editor/fetchers.ts b/src/routes/editor/fetchers.ts new file mode 100644 index 0000000..f4593e9 --- /dev/null +++ b/src/routes/editor/fetchers.ts @@ -0,0 +1,108 @@ +import type { + AudioMultipleChoiceQuestion, + AudioQuestion, + ImageMultipleChoiceQuestion, + ImageQuestion, + MultipleChoiceQuestion, + Question, + SimpleQuestion +} from "$lib/games/games"; +import type { Category, Game, Wall } from "$lib/Types"; +import { url } from "$lib/util"; +import axios from "axios"; + +export function fetchGame(id: string) { + return axios.get(url(`/game?id=${id}`), { withCredentials: true }).then((response) => { + if (response.status === 200) { + return response.data as Game; + } else { + throw `Failed to fetch game: ${response.status}`; + } + }); +} + +export function fetchWalls(id: string) { + return axios.get(url(`/walls/${id}`), { withCredentials: true }).then((response) => { + if (response.status === 200) { + return response.data as Wall[]; + } else { + throw `Failed to fetch walls: ${response.status}`; + } + }); +} + +export function fetchWall(id: string) { + return axios.get(url(`/wall?id=${id}`), { withCredentials: true }).then((response) => { + if (response.status === 200) { + return response.data as Wall; + } else { + throw `Failed to fetch wall: ${response.status}`; + } + }); +} + +export function fetchCategory(id: string) { + return axios.get(url(`/category?id=${id}`), { withCredentials: true }).then((response) => { + if (response.status === 200) { + return response.data as Category; + } else { + throw `Failed to fetch category: ${response.status}`; + } + }); +} + +export function fetchQuestion(id: string) { + return axios + .get(url(`/question?id=${id}`), { withCredentials: true }) + .then(async (response) => { + if (response.status === 200) { + const q = response.data; + console.log(q); + if ( + (q as Question).type === "IMAGE" || + (q as Question).type === "IMAGE_MULTIPLE_CHOICE" + ) { + // request ressource + return axios + .get(url(`/ressource?id=${(q as ImageQuestion).data.image}`), { + withCredentials: true + }) + .then((ressourceResponse) => { + if (ressourceResponse.status === 200) { + (q as ImageQuestion | ImageMultipleChoiceQuestion).data.image = + ressourceResponse.data; + } else { + (q as ImageQuestion | ImageMultipleChoiceQuestion).data.image = + null; + } + return q as ImageQuestion | ImageMultipleChoiceQuestion; + }) + .catch(() => { + return q as ImageQuestion | ImageMultipleChoiceQuestion; + }); + } else if ( + (q as Question).type === "AUDIO" || + (q as Question).type === "AUDIO_MULTIPLE_CHOICE" + ) { + // request ressource + return axios + .get(url(`/ressource?id=${(q as AudioQuestion).data.audio}`), { + withCredentials: true + }) + .then((ressourceResponse) => { + if (ressourceResponse.status === 200) { + (q as AudioQuestion | AudioMultipleChoiceQuestion).data.audio = + ressourceResponse.data; + } else { + (q as AudioQuestion | AudioMultipleChoiceQuestion).data.audio = + null; + } + return q as AudioQuestion | AudioMultipleChoiceQuestion; + }) + .catch(() => { + return q as AudioQuestion | AudioMultipleChoiceQuestion; + }); + } else return q as SimpleQuestion | MultipleChoiceQuestion; + } else throw `Failed to fetch question: ${response.status}`; + }); +}