it mostly works and and it will generate a json (you can see the json in the console) and that json can be pussed to the supabase table with some extra code witch i will now write!

This commit is contained in:
RezHackXYZ 2025-06-02 12:04:34 +05:30
parent 52aab530e3
commit b3ee5ebc78
6 changed files with 243 additions and 179 deletions

View file

@ -59,20 +59,28 @@ export let AnswersSymbolAndColorScheme = [
export let DefaultQuestions = [ export let DefaultQuestions = [
{ {
name: "What should you do when you're free?", questionText: "What should you do when you're free?",
answers: ["Do something in real life!", "Play video games", "Code!", "Touch grass!"], timeLimit: 15,
correctAnswer: 2, type: "SingleAnswer",
timeLimit: 30, options: ["Do something in real life!", "Play video games", "Code!", "Touch grass!"],
CorrectOption: 2,
hasMedia: false,
mediaURL: null,
}, },
{ {
name: "Is RezHackXYZ the best programmer in the world?", questionText: "Is RezHackXYZ the best programmer in the world?",
answers: ["Yes :)", "No :("],
correctAnswer: 0,
timeLimit: 5, timeLimit: 5,
type: "SingleAnswer",
options: ["Yes :)", "No :("],
CorrectOption: 0,
hasMedia: true,
mediaURL: "https://github.com/RezHackXYZ.png",
}, },
{ {
name: "Best place in the world?", questionText: "Best place in the world?",
answers: [ timeLimit: 5,
type: "SingleAnswer",
options: [
"Google", "Google",
"Microsoft", "Microsoft",
"Apple", "Apple",
@ -82,55 +90,8 @@ export let DefaultQuestions = [
"Facebook", "Facebook",
"Twitter", "Twitter",
], ],
correctAnswer: 4, CorrectOption: 4,
timeLimit: 120, hasMedia: false,
mediaURL: null,
}, },
]; ];
export let AiPrompts = {
GenerateQuestionsUsingAI: `
You are the AI of a quiz game.
Generate a list of quiz questions with possible answers and the correct answer index.
Each question must have:
- A "name" (question text)
- An "answers" array (minimum 2, maximum 8 options)
- A "correctAnswer" (index starting from 0)
Ensure the questions are diverse.
Example format:
{
"name": "What is the capital of France?",
"answers": [
"Paris",
"London",
"Berlin",
"Madrid"
],
"correctAnswer": 0
}
JUST PROVIDE THE JSON AND NOTHING ELSE.
The user's topic of interest is:
[topic]`,
GenerateOptionsUsingAI: `
You are the AI of a quiz game.
Generate a list of answers relevant to the Question the correct answer index.
generate 2 things for the question:
- An "answers" array (minimum 2, maximum 8 options)
- A "correctAnswer" (index starting from 0)
Ensure the questions are diverse.
Example format if the question is "What is the capital of France?":
{
"answers": [
"Paris",
"London",
"Berlin",
"Madrid"
],
"correctAnswer": 0
}
JUST PROVIDE THE JSON AND NOTHING ELSE.
The user's Question that they want to generate options for is:
[question]
`,
};

View file

@ -1,5 +1,10 @@
<script>
import { DefaultQuestions } from "$lib/config.js";
import { QuestionsData } from "./create.svelte";
</script>
<div class="flex w-full justify-center gap-2 overflow-y-auto rounded border-2 p-3 pr-5"> <div class="flex w-full justify-center gap-2 overflow-y-auto rounded border-2 p-3 pr-5">
<button class="btn flex items-center gap-1"> <button class="btn flex items-center gap-1" onclick={() => (QuestionsData.v = DefaultQuestions)}>
<i class="nf nf-md-flash"></i> Use Demo Questions <i class="nf nf-md-flash"></i> Use Demo Questions
</button> </button>
<button class="btn flex items-center gap-1"> <button class="btn flex items-center gap-1">

View file

@ -1,96 +1,56 @@
<script> <script>
import { AnswersSymbolAndColorScheme } from "$lib/config.js"; import { AnswersSymbolAndColorScheme } from "$lib/config.js";
import { QuestionsData, selectedQuestionIndex } from "./create.svelte.js";
</script> </script>
<div class="flex h-full w-full flex-col gap-2 overflow-y-auto rounded border-2 p-3 pr-5"> <div class="flex h-full w-full flex-col gap-2 overflow-y-auto rounded border-2 p-3 pr-5">
<h1 class="text-4xl">How it will look</h1> <h1 class="text-4xl">How Question number {selectedQuestionIndex.v + 1} will look</h1>
<div class="grid h-full place-items-center"> <div class="grid h-full place-items-center">
<div class="card w-4/5"> <div class="card w-4/5">
<h1 class="flex flex-col text-3xl"> <h1 class="flex flex-col text-3xl">
<span class="text-5xl">1.</span>What is the capital of France? <span class="text-5xl">{selectedQuestionIndex.v + 1}.</span>
{QuestionsData.v[selectedQuestionIndex.v].questionText}
</h1> </h1>
{#if QuestionsData.v[selectedQuestionIndex.v].mediaURL && QuestionsData.v[selectedQuestionIndex.v].hasMedia}
<div class="flex justify-center">
<div class="mt-1 w-fit rounded border-2">
{#if QuestionsData.v[selectedQuestionIndex.v].mediaURL.match(/\.(mp4|webm|ogg|mov|avi|mkv)$/i)}
<!-- svelte-ignore a11y_media_has_caption -->
<video
src={QuestionsData.v[selectedQuestionIndex.v].mediaURL}
controls
class="h-75 rounded"
></video>
{:else}
<img
src={QuestionsData.v[selectedQuestionIndex.v].mediaURL}
alt="Question media"
class="h-75 rounded"
/>{/if}
</div>
</div>
{/if}
<div class="mt-5 grid grid-cols-2 gap-5 gap-x-3"> <div class="mt-5 grid grid-cols-2 gap-5 gap-x-3">
<div class="flex"> {#each QuestionsData.v[selectedQuestionIndex.v].options as question, questionIndex}
<input type="radio" id="O{0}" name="question" class="peer sr-only" value={0} /> <div class="flex">
<span
<label style="
for="O{1}" --border-color: {AnswersSymbolAndColorScheme[questionIndex].Color};
style=" --bg-color: {AnswersSymbolAndColorScheme[questionIndex].Color};
--border-color: {AnswersSymbolAndColorScheme[0].Color}; --border-color-checked: {AnswersSymbolAndColorScheme[questionIndex].SelectedColor};
--bg-color: {AnswersSymbolAndColorScheme[0].Color}; --bg-color-checked: {AnswersSymbolAndColorScheme[questionIndex].SelectedColor};
--border-color-checked: {AnswersSymbolAndColorScheme[0].SelectedColor}; --border-color-hover: {AnswersSymbolAndColorScheme[questionIndex].HoverBorderColor};
--bg-color-checked: {AnswersSymbolAndColorScheme[0].SelectedColor}; --border-color-checked: {AnswersSymbolAndColorScheme[questionIndex].SelectedBorderColor};
--border-color-hover: {AnswersSymbolAndColorScheme[0].HoverBorderColor}; --border-color-hover: {AnswersSymbolAndColorScheme[questionIndex].HoverBorderColor};
--border-color-checked: {AnswersSymbolAndColorScheme[0].SelectedBorderColor};
--border-color-hover: {AnswersSymbolAndColorScheme[0].HoverBorderColor};
" "
class="w-full cursor-pointer rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all peer-checked:border-[var(--border-color-checked)] peer-checked:bg-[var(--bg-color-checked)] hover:border-[var(--border-color-hover)]" class="w-full cursor-pointer rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all peer-checked:border-[var(--border-color-checked)] peer-checked:bg-[var(--bg-color-checked)] hover:border-[var(--border-color-hover)]"
> >
<i class="nf {AnswersSymbolAndColorScheme[0].Symbol}"></i> <i class="nf {AnswersSymbolAndColorScheme[questionIndex].Symbol}"></i>
answer 1 {question}
</label> </span>
</div> </div>
<div class="flex"> {/each}
<input type="radio" id="O{1}" name="question" class="peer sr-only" value={1} />
<label
for="O{1}"
style="
--border-color: {AnswersSymbolAndColorScheme[1].Color};
--bg-color: {AnswersSymbolAndColorScheme[1].Color};
--border-color-checked: {AnswersSymbolAndColorScheme[1].SelectedColor};
--bg-color-checked: {AnswersSymbolAndColorScheme[1].SelectedColor};
--border-color-hover: {AnswersSymbolAndColorScheme[1].HoverBorderColor};
--border-color-checked: {AnswersSymbolAndColorScheme[1].SelectedBorderColor};
--border-color-hover: {AnswersSymbolAndColorScheme[1].HoverBorderColor};
"
class="w-full cursor-pointer rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all peer-checked:border-[var(--border-color-checked)] peer-checked:bg-[var(--bg-color-checked)] hover:border-[var(--border-color-hover)]"
>
<i class="nf {AnswersSymbolAndColorScheme[1].Symbol}"></i>
answer 2
</label>
</div>
<div class="flex">
<input type="radio" id="O{2}" name="question" class="peer sr-only" value={2} />
<label
for="O{1}"
style="
--border-color: {AnswersSymbolAndColorScheme[2].Color};
--bg-color: {AnswersSymbolAndColorScheme[2].Color};
--border-color-checked: {AnswersSymbolAndColorScheme[2].SelectedColor};
--bg-color-checked: {AnswersSymbolAndColorScheme[2].SelectedColor};
--border-color-hover: {AnswersSymbolAndColorScheme[2].HoverBorderColor};
--border-color-checked: {AnswersSymbolAndColorScheme[2].SelectedBorderColor};
--border-color-hover: {AnswersSymbolAndColorScheme[2].HoverBorderColor};
"
class="w-full cursor-pointer rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all peer-checked:border-[var(--border-color-checked)] peer-checked:bg-[var(--bg-color-checked)] hover:border-[var(--border-color-hover)]"
>
<i class="nf {AnswersSymbolAndColorScheme[2].Symbol}"></i>
answer 3
</label>
</div>
<div class="flex">
<input type="radio" id="O{3}" name="question" class="peer sr-only" value={3} />
<label
for="O{1}"
style="
--border-color: {AnswersSymbolAndColorScheme[3].Color};
--bg-color: {AnswersSymbolAndColorScheme[3].Color};
--border-color-checked: {AnswersSymbolAndColorScheme[3].SelectedColor};
--bg-color-checked: {AnswersSymbolAndColorScheme[3].SelectedColor};
--border-color-hover: {AnswersSymbolAndColorScheme[3].HoverBorderColor};
--border-color-checked: {AnswersSymbolAndColorScheme[3].SelectedBorderColor};
--border-color-hover: {AnswersSymbolAndColorScheme[3].HoverBorderColor};
"
class="w-full cursor-pointer rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all peer-checked:border-[var(--border-color-checked)] peer-checked:bg-[var(--bg-color-checked)] hover:border-[var(--border-color-hover)]"
>
<i class="nf {AnswersSymbolAndColorScheme[3].Symbol}"></i>
answer 4
</label>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,3 +1,43 @@
<script>
import toast from "svelte-5-french-toast";
import { supabase } from "$lib/supabase.js";
import { QuestionsData, selectedQuestionIndex } from "./create.svelte.js";
let files;
export async function UpLoadFiles(file) {
if (!file) {
toast.error("Please select a file to upload first.");
return;
}
const fileExt = file.name.split(".").pop();
const fileName = `${Date.now()}.${fileExt}`;
const filePath = `${fileName}`;
const uploadPromise = supabase.storage.from("useruploadedcontent").upload(filePath, file);
const result = await toast.promise(uploadPromise, {
loading: "Uploading...",
success: "Upload successful!",
error: (error) => `Upload failed. ${error.message} Please try again.`,
});
if (result.error) {
toast.error("Upload error:" + result.error.message);
return;
}
// Retrieve public URL
const { data: publicData } = supabase.storage
.from("useruploadedcontent")
.getPublicUrl(filePath);
return publicData.publicUrl;
}
</script>
<div <div
class="flex h-full w-[30%] flex-col justify-between gap-2 overflow-y-auto rounded-l border-2 p-3 pr-5" class="flex h-full w-[30%] flex-col justify-between gap-2 overflow-y-auto rounded-l border-2 p-3 pr-5"
> >
@ -10,17 +50,23 @@
type="text" type="text"
id="QuetionText" id="QuetionText"
placeholder="Whats the capital of france?" placeholder="Whats the capital of france?"
bind:value={QuestionsData.v[selectedQuestionIndex.v].questionText}
class="input mt-1" class="input mt-1"
/> />
</div> </div>
<div class="mt-3"> <div class="mt-3">
<label for="TimeLimit" class="text-lg leading-0.5 text-gray-500">Time limit?</label> <label for="TimeLimit" class="text-lg leading-0.5 text-gray-500">Time limit?</label>
<select name="TimeLimit" id="TimeLimit" class="input mt-1"> <select
name="TimeLimit"
id="TimeLimit"
class="input mt-1"
bind:value={QuestionsData.v[selectedQuestionIndex.v].timeLimit}
>
<option value={null}>infinite</option> <option value={null}>infinite</option>
<option value={5}>5 sec</option> <option value={5}>5 sec</option>
<option value={10}>10 sec</option> <option value={10}>10 sec</option>
<option selected value={15}>15 sec</option> <option value={15}>15 sec</option>
<option value={30}>30 sec</option> <option value={30}>30 sec</option>
<option value={60}>1 min</option> <option value={60}>1 min</option>
<option value={120}>2 min</option> <option value={120}>2 min</option>
@ -30,43 +76,89 @@
<div class="mt-3"> <div class="mt-3">
<label for="QuestionType" class="text-lg leading-0.5 text-gray-500">Question type?</label> <label for="QuestionType" class="text-lg leading-0.5 text-gray-500">Question type?</label>
<select name="QuestionType" id="QuestionType" class="input mt-1"> <select
<option selected value="SingleAnswer">Single Answer</option> name="QuestionType"
id="QuestionType"
class="input mt-1"
bind:value={QuestionsData.v[selectedQuestionIndex.v].type}
>
<option value="SingleAnswer">Single Answer</option>
</select> </select>
</div> </div>
<div class="mt-3">
<label for="AddSomeMedia" class="text-lg leading-0.5 text-gray-500">Add Some Media?</label>
<input type="checkbox" bind:checked={QuestionsData.v[selectedQuestionIndex.v].hasMedia} />
{#if QuestionsData.v[selectedQuestionIndex.v].hasMedia}
<input
type="file"
class="sr-only"
bind:files
onchange={async () => {
QuestionsData.v[selectedQuestionIndex.v].mediaURL = await UpLoadFiles(files[0]);
}}
accept="image/*,video/*"
/>
<label for="media-upload" class="btn dull mt-2 cursor-pointer">
{QuestionsData.v[selectedQuestionIndex.v].mediaURL
? "choose different file"
: "upload files"}
</label>
<input
id="media-upload"
type="file"
class="hidden"
bind:files
onchange={async () => {
QuestionsData.v[selectedQuestionIndex.v].mediaURL = await UpLoadFiles(files[0]);
}}
accept="image/*,video/*"
/>
{/if}
</div>
<div class="mt-3"> <div class="mt-3">
{#if "SingleAnswer" === "SingleAnswer"} {#if "SingleAnswer" === "SingleAnswer"}
<span class="text-lg leading-0.5 text-gray-500">Options</span> <span class="text-lg leading-0.5 text-gray-500">Options</span>
<div class="mt-1 grid grid-cols-1 gap-2"> {#each QuestionsData.v[selectedQuestionIndex.v].options as Option, index}
<div class="flex items-center gap-2"> <div class="mt-1 grid grid-cols-1 gap-2">
<input type="radio" name="options" class="ratio" /> <div class="flex items-center gap-2">
<input type="text" placeholder="Option 1" class="input w-1/3" /> <input
<button class="btn slim dull" aria-label="Delete Option"> type="radio"
<i class="nf nf-md-delete"></i> name="options"
</button> class="ratio"
value={index}
bind:group={QuestionsData.v[selectedQuestionIndex.v].CorrectOption}
/>
<input type="text" placeholder="Option 1" bind:value={Option} class="input w-1/3" />
<button
class="btn slim dull"
aria-label="Delete Option"
onclick={() => {
if (QuestionsData.v[selectedQuestionIndex.v].options.length <= 2) {
toast.error("You need to have a minimum of 2 options");
} else {
QuestionsData.v[selectedQuestionIndex.v].options.splice(index, 1);
}
}}
>
<i class="nf nf-md-delete"></i>
</button>
</div>
</div> </div>
</div> {/each}
<div class="mt-1 grid grid-cols-1 gap-2">
<div class="flex items-center gap-2">
<input type="radio" name="options" class="ratio" />
<input type="text" placeholder="Option 2" class="input w-1/3" />
<button class="btn slim dull" aria-label="Delete Option">
<i class="nf nf-md-delete"></i>
</button>
</div>
</div>
<div class="mt-1 grid grid-cols-1 gap-2">
<div class="flex items-center gap-2">
<input type="radio" name="options" class="ratio" />
<input type="text" placeholder="Option 3" class="input w-1/3" />
<button class="btn slim dull" aria-label="Delete Option">
<i class="nf nf-md-delete"></i>
</button>
</div>
</div>
<div class="flex justify-center"> <div class="flex justify-center">
<button class="btn dull mt-2 w-fit">Add Option</button> <button
class="btn dull mt-2 w-fit"
onclick={() => {
if (QuestionsData.v[selectedQuestionIndex.v].options.length >= 8) {
toast.error("You can only have a max of 8 options");
} else {
QuestionsData.v[selectedQuestionIndex.v].options.push("");
}
}}>Add Option</button
>
</div> </div>
{/if} {/if}
</div> </div>
@ -74,7 +166,18 @@
<div> <div>
<div class="mt-4"> <div class="mt-4">
<label for="QuetionText" class="text-lg leading-0.5 text-gray-500">Danger</label> <label for="QuetionText" class="text-lg leading-0.5 text-gray-500">Danger</label>
<button class="btn mt-1 dull w-full" >delete this question</button> <button
class="btn dull mt-1 w-full"
onclick={() => {
if (QuestionsData.v.length <= 1) {
toast.error("You need to have at least 1 question");
} else if (confirm("Are you sure you want to delete this question?")) {
QuestionsData.v.splice(selectedQuestionIndex.v, 1);
selectedQuestionIndex.v = Math.max(0, selectedQuestionIndex.v - 1);
toast.success("Question deleted successfully");
}
}}>delete this question</button
>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,13 +1,31 @@
<script> <script>
let questions = ["Question 1", "Question 2", "Question 3"]; import { QuestionsData, selectedQuestionIndex } from "./create.svelte.js";
</script> </script>
<div class="flex h-full w-[15%] flex-col gap-2 overflow-y-auto rounded-r border-2 p-3 pr-5"> <div class="flex h-full w-[15%] flex-col gap-2 overflow-y-auto rounded-r border-2 p-3 pr-5">
<h1 class="text-4xl">Questions</h1> <h1 class="text-4xl">Questions</h1>
{#each questions as questions, questionIndex} {#each QuestionsData.v as question, questionIndex}
<div class="card flex flex-col"> <button
<span class="text-3xl">{questionIndex + 1}.</span><span>{questions}</span> class="card flex flex-col text-left"
</div> onclick={() => (selectedQuestionIndex.v = questionIndex)}
>
<span class="text-3xl">{questionIndex + 1}.</span><span>{question.questionText}</span>
</button>
{/each} {/each}
<div class="flex justify-center mt-2"><button class="btn"><i class="nf nf-oct-diff_added"></i> New Question</button></div> <div class="mt-2 flex justify-center">
<button
class="btn"
onclick={() => {
QuestionsData.v.push({
questionText: "",
timeLimit: 15,
type: "SingleAnswer",
options: ["", "", "", ""],
CorrectOption: null,
hasMedia: false,
mediaURL: null,
});
}}><i class="nf nf-oct-diff_added"></i> New Question</button
>
</div>
</div> </div>

View file

@ -0,0 +1,17 @@
export let selectedQuestionIndex = $state({ v: 0 });
export let QuestionsData = $state({
v: [
{
questionText: "",
timeLimit: 15,
type: "SingleAnswer",
options: ["", "", "", ""],
CorrectOption: null,
hasMedia: false,
mediaURL: null
},
],
});
$inspect(QuestionsData.v);