made the filestructure better and migrated the game creation from the old type of db to the newer relational db type!

This commit is contained in:
RezHackXYZ 2025-05-15 06:27:40 +05:30
parent 9ac813c699
commit 736059646d
No known key found for this signature in database
28 changed files with 343 additions and 736 deletions

10
.vscode/settings.json vendored
View file

@ -4,5 +4,13 @@
"editor.quickSuggestions": { "editor.quickSuggestions": {
"strings": true "strings": true
}, },
"cSpell.words": ["SUPABASE"] "cSpell.words": [
"correctanswer",
"creationdate",
"gameid",
"kahoot",
"questionid",
"questionstext",
"SUPABASE"
]
} }

View file

@ -1,38 +1,45 @@
# sv # DB Diagrams
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). ## Current DB Diagram
## Creating a project ```mermaid
erDiagram
If you're seeing this, you've probably already done this step. Congrats! GAMES {
int GamePIN PK
```bash string gameStatus
# create a new project in the current directory json questions
npx sv create jsonb players
}
# create a new project in my-app
npx sv create my-app
``` ```
## Developing ## Goal MVP DB Diagram
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: ```mermaid
erDiagram
GAMES {
int ID PK
string creator
date creationDate
string status
}
QUESTIONS {
int ID PK
int GameID FK
string QuestionsText
string CorrectAnswer
}
ANSWERS {
int ID PK
int QuestionID FK
string content
}
PLAYERS {
int ID PK
int GameID FK
int Score
}
```bash GAMES ||--o{ QUESTIONS : contains
npm run dev QUESTIONS ||--o{ ANSWERS : has
GAMES ||--o{ PLAYERS : participated_by
# or start the server and open the app in a new browser tab
npm run dev -- --open
``` ```
## Building
To create a production version of your app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.

View file

@ -2,13 +2,7 @@
"extends": "./.svelte-kit/tsconfig.json", "extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"allowJs": true, "allowJs": true,
"checkJs": false, "checkJs": true,
"moduleResolution": "bundler" "moduleResolution": "bundler"
} }
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
} }

10
package-lock.json generated
View file

@ -12,6 +12,7 @@
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-auto": "^4.0.0", "@sveltejs/adapter-auto": "^4.0.0",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.16.0", "@sveltejs/kit": "^2.16.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tailwindcss/postcss": "^4.1.6", "@tailwindcss/postcss": "^4.1.6",
@ -1091,6 +1092,15 @@
"@sveltejs/kit": "^2.0.0" "@sveltejs/kit": "^2.0.0"
} }
}, },
"node_modules/@sveltejs/adapter-static": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz",
"integrity": "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==",
"dev": true,
"peerDependencies": {
"@sveltejs/kit": "^2.0.0"
}
},
"node_modules/@sveltejs/kit": { "node_modules/@sveltejs/kit": {
"version": "2.20.8", "version": "2.20.8",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.8.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.8.tgz",

View file

@ -13,6 +13,7 @@
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-auto": "^4.0.0", "@sveltejs/adapter-auto": "^4.0.0",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.16.0", "@sveltejs/kit": "^2.16.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tailwindcss/postcss": "^4.1.6", "@tailwindcss/postcss": "^4.1.6",

View file

@ -1 +0,0 @@
@import 'tailwindcss';

View file

@ -1,11 +1,12 @@
<script> <script>
import '../app.css';
let { children } = $props(); let { children } = $props();
</script> </script>
<div class="h-full text-white">{@render children()}</div> <div class="h-full text-white">{@render children()}</div>
<style> <style>
@import 'tailwindcss';
:root { :root {
background-color: black; background-color: black;
} }

View file

@ -1,19 +1,18 @@
<script>
import { goto } from '$app/navigation';
</script>
<div class="bg-grey-900 flex h-full items-center justify-center"> <div class="bg-grey-900 flex h-full items-center justify-center">
<div class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"> <div class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg">
<h1 class="m-[0] text-6xl">DaKahootClone</h1> <h1 class="m-[0] text-6xl">DaKahootClone</h1>
<p class="m-[0] mb-2 text-lg text-gray-400">The best eveer kahoot clone.</p> <p class="m-[0] mb-2 text-lg text-gray-400">The best ever kahoot clone.</p>
<button <a href="./join">
on:click={() => goto('/join')} <button
class="cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10" class="cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
>Join a game</button >Join a game</button
> ></a
<button
class="cursor-pointer rounded-full bg-blue-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
on:click={() => goto('/create')}>Create and Host a game</button
> >
<a href="./create">
<button
class="cursor-pointer rounded-full bg-blue-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
>Create and Host a game</button
>
</a>
</div> </div>
</div> </div>

View file

@ -1,170 +1,22 @@
<script> <script>
import { supabase } from '$lib/supabase'; import UseDemoQuestions from './components/buttons/UseDemoQuestions.svelte';
import { goto } from '$app/navigation'; import NewQuestion from './components/buttons/NewQuestion.svelte';
let questions = $state([ import StartGame from './components/buttons/StartGame.svelte';
{ import Question from './components/Questions/question.svelte';
name: '', import { questions } from './logic/GameCreateData.svelte.js';
answers: ['', '', '', ''],
correctAnswer: undefined,
playersCompelted: 0
}
]);
async function startGame() { $inspect(questions);
if (questions.some((q) => q.name === '')) return alert('Please fill all questions');
if (questions.some((q) => q.answers.some((a) => a === ''))) return alert('Fill all options');
if (questions.some((q) => q.correctAnswer === undefined))
return alert('Select correct answers');
const gamePin = Math.floor(Math.random() * 1000000)
.toString()
.padStart(6, '0');
const { error } = await supabase.from('games').insert({
gamePIN: gamePin,
gameStatus: 'lobby',
questions: $state.snapshot(questions),
players: []
});
if (error) {
alert('Failed to create game: ' + error.message + '\n\nPlease try again.');
return;
}
goto('/host/' + gamePin);
}
</script> </script>
<div class="bg-grey-900 flex justify-center p-5"> <div class="bg-grey-900 flex justify-center p-5">
<div class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"> <div class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg">
<button <UseDemoQuestions />
onclick={() => { {#each questions.v as question, index}
questions = [ <Question {index} />
{
name: 'Is hack club awesome?',
answers: ['its ok', 'its the BEST place in the world', 'nahh its bad', 'Gu Gu Ga Ga'],
correctAnswer: 1,
playersCompelted: 0
},
{
name: 'Who is the best programer in the world?',
answers: ['Sundar Pichai', 'Bill Gates', 'RezHackXYZ', 'Elon musk'],
correctAnswer: 2,
playersCompelted: 0
},
{
name: 'What was the 5/11 incident?',
answers: [
'mass pings of @/birds',
'twin towers getting blasted by planes',
'the opening ceremony of the store of 7/11 on the 5/11 date',
'the opening ceremony of the competitor store of 7/11'
],
correctAnswer: 0,
playersCompelted: 0
}
];
}}
class="-mt-5 mb-3 flex h-fit cursor-pointer items-center justify-center rounded-xl bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" /></svg
>Use demo questions</button
>
{#each questions as question, i}
<div class="flex items-center gap-3">
<div
class="mb-3 flex flex-col items-center justify-center gap-1 rounded-2xl bg-gray-600 p-2"
>
<div class="flex h-fit items-center gap-3">
<h1 class="mt-2 mb-3 text-2xl">Q{i + 1}.</h1>
<input
type="text"
bind:value={question.name}
placeholder="Question {i + 1}"
class="h-fit w-[500px] rounded-xl bg-gray-800 p-1 text-center text-2xl text-white"
/><button
onclick={() => {
if (questions.length > 1) {
if (
confirm('Are you sure you want to delete this question? You cant undo this.')
) {
questions.splice(i, 1);
}
} else {
alert('You need at least one question.');
}
}}
class="flex h-fit cursor-pointer items-center justify-center rounded-xl bg-red-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"
/></svg
>Delete question</button
>
</div>
<div class="flex flex-col gap-2">
{#each question.answers as answer, i2}
<div class="flex items-center gap-2">
<input
type="radio"
value={i2}
name={i}
bind:group={question.correctAnswer}
class="input"
/><input
placeholder="Option {i2 + 1}"
bind:value={question.answers[i2]}
class="w-[500px] rounded-lg bg-gray-800 p-1 text-center text-white"
/>
</div>
{/each}
</div>
</div>
</div>
{/each} {/each}
<div class="flex gap-3"> <div class="flex gap-3">
<button <NewQuestion />
onclick={() => { <StartGame />
questions.push({
name: '',
answers: ['', '', '', ''],
correctAnswer: undefined,
playersCompelted: 0
});
}}
class="flex h-fit cursor-pointer items-center justify-center rounded-xl bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" /></svg
>New question</button
>
<button
onclick={startGame}
class="flex h-fit cursor-pointer items-center justify-center gap-1 rounded-xl bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="M560-360q17 0 29.5-12.5T602-402q0-17-12.5-29.5T560-444q-17 0-29.5 12.5T518-402q0 17 12.5 29.5T560-360Zm-30-128h60q0-29 6-42.5t28-35.5q30-30 40-48.5t10-43.5q0-45-31.5-73.5T560-760q-41 0-71.5 23T446-676l54 22q9-25 24.5-37.5T560-704q24 0 39 13.5t15 36.5q0 14-8 26.5T578-596q-33 29-40.5 45.5T530-488ZM320-240q-33 0-56.5-23.5T240-320v-480q0-33 23.5-56.5T320-880h480q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H320Zm0-80h480v-480H320v480ZM160-80q-33 0-56.5-23.5T80-160v-560h80v560h560v80H160Zm160-720v480-480Z"
/></svg
>Start Quiz</button
>
</div> </div>
</div> </div>
</div> </div>

View file

@ -0,0 +1,23 @@
<script>
import { questions } from '../../logic/GameCreateData.svelte.js';
let props = $props();
let questionsIndex = props.questionsIndex;
let index = props.answersIndex;
</script>
<div class="flex items-center gap-2">
<input
type="radio"
value={index}
name={index.toString()}
bind:group={questions.v[questionsIndex].correctAnswer}
class="input"
/>
<input
placeholder="Option {index + 1}"
bind:value={questions.v[questionsIndex].answers[index]}
class="w-[500px] rounded-lg bg-gray-800 p-1 text-center text-white"
/>
</div>

View file

@ -0,0 +1,30 @@
<script>
import DeleteQuestion from '../buttons/DeleteQuestion.svelte';
import Answers from './answers.svelte';
import { questions } from '../../logic/GameCreateData.svelte.js';
let props = $props();
let index = props.index;
</script>
<div class="flex items-center gap-3">
<div class="mb-3 flex flex-col items-center justify-center gap-1 rounded-2xl bg-gray-600 p-2">
<div class="flex h-fit items-center gap-3">
<h1 class="mt-2 mb-3 text-2xl">Q{index + 1}.</h1>
<input
type="text"
bind:value={questions.v[index].name}
placeholder="Question {index + 1}"
class="h-fit w-[500px] rounded-xl bg-gray-800 p-1 text-center text-2xl text-white"
/>
<DeleteQuestion {index} />
</div>
<div class="flex flex-col gap-2">
{#each questions.v[index].answers as answer, answersIndex}
<Answers questionsIndex={index} {answersIndex} />
{/each}
</div>
</div>
</div>

View file

@ -0,0 +1,20 @@
<script>
import { DeleteQuestion } from '../../logic/GameCreateData.svelte.js';
let props = $props();
</script>
<button
onclick={() => DeleteQuestion(props.index)}
class="flex h-fit cursor-pointer items-center justify-center rounded-xl bg-red-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"
/></svg
>Delete question</button
>

View file

@ -0,0 +1,15 @@
<script>
import { AddQuestion } from '../../logic/GameCreateData.svelte.js';
</script>
<button
onclick={() => AddQuestion()}
class="flex h-fit cursor-pointer items-center justify-center rounded-xl bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" /></svg
>New question</button
>

View file

@ -0,0 +1,18 @@
<script>
import { startGame } from '../../logic/StartGame.js';
</script>
<button
onclick={startGame}
class="flex h-fit cursor-pointer items-center justify-center gap-1 rounded-xl bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="M560-360q17 0 29.5-12.5T602-402q0-17-12.5-29.5T560-444q-17 0-29.5 12.5T518-402q0 17 12.5 29.5T560-360Zm-30-128h60q0-29 6-42.5t28-35.5q30-30 40-48.5t10-43.5q0-45-31.5-73.5T560-760q-41 0-71.5 23T446-676l54 22q9-25 24.5-37.5T560-704q24 0 39 13.5t15 36.5q0 14-8 26.5T578-596q-33 29-40.5 45.5T530-488ZM320-240q-33 0-56.5-23.5T240-320v-480q0-33 23.5-56.5T320-880h480q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H320Zm0-80h480v-480H320v480ZM160-80q-33 0-56.5-23.5T80-160v-560h80v560h560v80H160Zm160-720v480-480Z"
/></svg
>Start Quiz</button
>

View file

@ -0,0 +1,15 @@
<script>
import { SetQuestionsToDemoQuestions } from '../../logic/GameCreateData.svelte.js';
</script>
<button
onclick={() => SetQuestionsToDemoQuestions()}
class="-mt-5 mb-3 flex h-fit cursor-pointer items-center justify-center rounded-xl bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" /></svg
>Use demo questions</button
>

View file

@ -0,0 +1,54 @@
export let questions = $state({
v: [
{
name: '',
answers: ['', '', '', ''],
correctAnswer: undefined
}
]
});
let DemoQuestions = [
{
name: 'Is hack club awesome?',
answers: ['its ok', 'its the BEST place in the world', 'its bad', 'Gu Gu Ga Ga'],
correctAnswer: 1
},
{
name: 'Who is the best programer in the world?',
answers: ['Some person', 'Bill Gates', 'RezHackXYZ', 'another person'],
correctAnswer: 2
},
{
name: 'What was the 5/11 incident?',
answers: [
'mass pings of @/birds',
'twin towers getting blasted by planes',
'the opening ceremony of the store of 7/11 on the 5/11 date',
'the opening ceremony of the competitor store of 7/11'
],
correctAnswer: 0
}
];
export function SetQuestionsToDemoQuestions() {
questions.v = DemoQuestions;
}
export function AddQuestion() {
questions.v.push({
name: '',
answers: ['', '', '', ''],
correctAnswer: undefined
});
}
export function DeleteQuestion(index) {
if (questions.v.length > 1) {
if (confirm('Are you sure you want to delete this question? You cant undo this.')) {
questions.v.splice(index, 1);
}
} else {
alert('You need at least one question.');
}
}

View file

@ -0,0 +1,58 @@
import { supabase } from '$lib/supabase';
export async function createGame(questions, gamePin) {
// Insert the game into the GAMES table
const { data: gameData, error: gameError } = await supabase
.from('games')
.insert({
creator: 'anonymous', // Replace with actual creator if available
creationdate: new Date().toISOString(),
status: 'lobby',
GamePIN: gamePin
})
.select('id')
.single();
if (gameError) {
alert('Failed to create game: ' + gameError.message + '\n\nPlease try again.');
return;
}
const gameid = gameData.id;
// Prepare questions and answers for batch insertion
const questionsData = questions.map((q, index) => ({
gameid: gameid,
questionstext: q.name,
correctanswer: q.correctAnswer
}));
const { data: questionsResult, error: questionsError } = await supabase
.from('questions')
.insert(questionsData)
.select('id');
if (questionsError) {
alert('Failed to insert questions: ' + questionsError.message + '\n\nPlease try again.');
return;
}
const answersData = [];
questionsResult.forEach((question, index) => {
questions[index].answers.forEach((answer, answerIndex) => {
answersData.push({
questionid: question.id,
content: answer
});
});
});
const { error: answersError } = await supabase.from('answers').insert(answersData);
if (answersError) {
alert('Failed to insert answers: ' + answersError.message + '\n\nPlease try again.');
return;
}
alert('Game created successfully!');
}

View file

@ -0,0 +1,17 @@
import { goto } from '$app/navigation';
import { createGame } from './InsertGameInDB.js';
import { questions } from './GameCreateData.svelte.js';
export async function startGame() {
if (questions.v.some((q) => q.name === '')) return alert('Please fill all questions');
if (questions.v.some((q) => q.answers.some((a) => a === ''))) return alert('Fill all options');
if (questions.v.some((q) => q.correctAnswer === undefined)) return alert('Select correct answers');
const gamePin = Math.floor(Math.random() * 1000000)
.toString()
.padStart(6, '0');
createGame($state.snapshot(questions), gamePin);
goto('/host/' + gamePin);
}

View file

@ -1,6 +0,0 @@
// Example of what your +page.js or +page.server.js might need
export function load({ params }) {
return {
gamePin: params.gamePin
};
}

View file

@ -1,81 +0,0 @@
<script>
import { onMount } from 'svelte';
import { page } from '$app/stores';
import { supabase } from '$lib/supabase';
import { goto } from '$app/navigation';
export let data;
const gamePin = data.gamePin;
let players = [];
// Subscribe to realtime changes in players
function subscribeToPlayers() {
const channel = supabase
.channel(`game-${gamePin}`)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'games',
filter: `gamePIN=eq.${gamePin}`
},
(payload) => {
players = payload.new.players || [];
}
)
.subscribe();
}
onMount(async () => {
const { data: gameData } = await supabase
.from('games')
.select('players')
.eq('gamePIN', Number(gamePin))
.maybeSingle();
if (gameData?.players) {
players = gameData.players;
}
subscribeToPlayers();
});
</script>
<div class="bg-grey-900 flex h-full items-center justify-center">
<div
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
>
<h1 class="m-[0] text-9xl">HOSTING</h1>
<h1 class="m-[0] text-7xl">Game Pin:</h1>
<h1 class="m-[0] rounded-2xl bg-gray-700 pt-1.5 pr-2 pb-0 pl-2 font-mono text-5xl">
{gamePin}
</h1>
<h1 class="m-[0] mt-10 text-6xl">Players Joined:</h1>
<h1 class="m-[0] text-4xl text-gray-400">(Total Players: {players.length})</h1>
<div class="mt-2 flex flex-wrap justify-center gap-2">
{#each players as player}
<span class="m-[0] rounded-xl bg-gray-700 pt-1 pr-2 pb-0 pl-2 font-mono text-3xl"
>{player.name}</span
>
{/each}
</div>
<button
class="mt-5 cursor-pointer rounded-2xl bg-green-700 p-2 text-4xl transition-all hover:scale-110 hover:-rotate-10"
on:click={async () => {
if (players.length > 0) {
if (confirm('Are you sure you want to start the game?')) {
await supabase
.from('games')
.update({ gameStatus: 'started' })
.eq('gamePIN', Number(data.gamePin));
goto(`/hostgame/${gamePin}`);
}
} else {
alert('You need at least one player to start the game.');
}
}}>Start the game</button
>
</div>
</div>

View file

@ -1,6 +0,0 @@
// Example of what your +page.js or +page.server.js might need
export function load({ params }) {
return {
gamePin: params.gamePin
};
}

View file

@ -1,100 +0,0 @@
<script>
import { onMount } from 'svelte';
import { supabase } from '$lib/supabase';
export let data;
const gamePin = data.gamePin;
let totalQuetions = 3;
let currentQuestion = 0;
let PeopleAwnseredQ = 2;
let Totalplayers = 3;
let questions = [];
async function NewUpdate(question) {
if (question[currentQuestion].playersCompelted == Totalplayers) {
currentQuestion++;
await supabase
.from('games')
.update({ gameStatus: `${currentQuestion}` })
.eq('gamePIN', Number(data.gamePin));
PeopleAwnseredQ = 0;
} else {
PeopleAwnseredQ = question[currentQuestion].playersCompelted;
}
}
onMount(async () => {
const channel = supabase
.channel(`game-${gamePin}`)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'games',
filter: `gamePIN=eq.${gamePin}`
},
(payload) => {
if (payload.new.questions != questions) {
questions = payload.new.questions;
currentQuestion = Number(payload.new.gameStatus);
NewUpdate(questions);
}
}
)
.subscribe();
const { data, error } = await supabase
.from('games')
.select('players')
.eq('gamePIN', Number(gamePin))
.single();
Totalplayers = data.players.length;
const { data: data2, error: error2 } = await supabase
.from('games')
.select('questions')
.eq('gamePIN', Number(gamePin))
.single();
questions = data2.questions;
totalQuetions = questions.length;
currentQuestion = 0;
await supabase
.from('games')
.update({ gameStatus: currentQuestion.toString() })
.eq('gamePIN', Number(gamePin));
});
</script>
<div class="bg-grey-900 flex h-full items-center justify-center">
<div
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
>
<h1 class="m-[0] text-7xl">HOSTING</h1>
<div class="mt-1 mb-3 flex w-full flex-col rounded-2xl border-2 border-green-400 p-2">
<h3>Question {currentQuestion + 1} of {totalQuetions} is beeing awnsered</h3>
<div class="flex-1 rounded-full border-2 border-gray-600">
<div
class="h-4 rounded-full bg-green-600 transition-all duration-700"
style="width: {(currentQuestion / totalQuetions) * 100}%;"
></div>
</div>
</div>
<div class="mt-1 mb-3 flex w-full flex-col rounded-2xl border-2 border-green-400 p-2">
<h3>{PeopleAwnseredQ} out of {Totalplayers} have awnsered the quetion</h3>
<div class="flex-1 rounded-full border-2 border-gray-600">
<div
class="h-4 rounded-full bg-green-600 transition-all duration-700"
style="width: {(PeopleAwnseredQ / Totalplayers) * 100}%;"
></div>
</div>
</div>
</div>
</div>

View file

@ -1,72 +0,0 @@
<script>
import { goto } from '$app/navigation';
import { supabase } from '$lib/supabase';
let pin;
let name;
let Checking = false;
async function validateGamePin() {
const { data, error } = await supabase
.from('games')
.select('gamePIN')
.eq('gamePIN', Number(pin))
.maybeSingle();
// Return true if valid
return data !== null && !error;
}
async function joinGame() {
if (!pin || !name) {
alert('Please fill in the game pin and your name.');
return;
}
Checking = true;
const isValid = await validateGamePin();
if (!isValid) {
alert('Invalid game pin. Please try again.');
Checking = false;
return;
}
goto('/play-lobby/' + pin, {
state: {
name
}
});
}
</script>
<div class="bg-grey-900 flex h-full items-center justify-center">
<div class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg">
<h1 class="m-[0] mb-3 text-5xl">Join a game here</h1>
<input
placeholder="Enter game pin"
class="rounded-lg bg-gray-800 p-2 text-center text-white"
bind:value={pin}
/>
<input
placeholder="Enter your name"
bind:value={name}
class="rounded-lg bg-gray-800 p-2 text-center text-white"
/>
{#if !Checking}
<button
class="mt-4 cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
on:click={joinGame}
>
Join game
</button>
{:else}<button
class="mt-4 cursor-pointer rounded-full bg-gray-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
>
Checking if pin is valid...
</button>{/if}
</div>
</div>

View file

@ -1,6 +0,0 @@
// Example of what your +page.js or +page.server.js might need
export function load({ params }) {
return {
gamePin: params.gamePin
};
}

View file

@ -1,112 +0,0 @@
<script>
import { onMount } from 'svelte';
import { page } from '$app/stores';
import { supabase } from '$lib/supabase';
import { goto } from '$app/navigation';
export let data;
const gamePin = data.gamePin;
const name = $page.state?.name;
let players = [];
async function addPlayer() {
const { data: gameData, error } = await supabase
.from('games')
.select('players')
.eq('gamePIN', Number(gamePin))
.maybeSingle();
let updatedPlayers = gameData.players || [];
const alreadyExists = updatedPlayers.some((p) => p.name === name);
if (!alreadyExists) {
updatedPlayers.push({ name: name, score: 0 });
const { error: updateError } = await supabase
.from('games')
.update({ players: updatedPlayers })
.eq('gamePIN', Number(gamePin));
}
}
// Subscribe to realtime changes in players
function subscribeToPlayers() {
const channel = supabase
.channel(`game-${gamePin}`)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'games',
filter: `gamePIN=eq.${gamePin}`
},
(payload) => {
players = payload.new.players || [];
}
)
.subscribe();
}
function subscribeToGameStatus() {
supabase
.channel(`status-${gamePin}`)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'games',
filter: `gamePIN=eq.${gamePin}`
},
(payload) => {
if (payload.new.gameStatus === 'started') {
goto(`/play/${gamePin}`, {
state: {
name
}
});
}
}
)
.subscribe();
}
onMount(async () => {
await addPlayer();
const { data: gameData } = await supabase
.from('games')
.select('players')
.eq('gamePIN', Number(gamePin))
.maybeSingle();
if (gameData?.players) {
players = gameData.players;
}
subscribeToPlayers();
subscribeToGameStatus();
});
</script>
<div class="bg-grey-900 flex h-full items-center justify-center">
<div
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
>
<h1 class="m-[0] text-9xl">PLAYING</h1>
<h1 class="m-[0] text-7xl">Game Pin:</h1>
<h1 class="m-[0] rounded-2xl bg-gray-700 pt-1.5 pr-2 pb-0 pl-2 font-mono text-5xl">
{gamePin}
</h1>
<h1 class="m-[0] mt-10 text-6xl">Players Joined:</h1>
<h1 class="m-[0] text-4xl text-gray-400">(Total Players: {players.length})</h1>
<div class="mt-2 flex flex-wrap justify-center gap-2">
{#each players as player}
<span class="m-[0] rounded-xl bg-gray-700 pt-1 pr-2 pb-0 pl-2 font-mono text-3xl"
>{player.name}</span
>
{/each}
</div>
</div>
</div>

View file

@ -1,5 +0,0 @@
export function load({ params }) {
return {
gamePin: params.gamePin
};
}

View file

@ -1,134 +0,0 @@
<script>
import { supabase } from '$lib/supabase';
import { onMount } from 'svelte';
export let data;
const gamePin = data.gamePin;
import { page } from '$app/stores';
const name = $page.state?.name;
let question = [];
let Selected = null;
let currentQuestion = 0;
let isWait = true;
let gameStatus = '';
let players = [];
onMount(async () => {
const channel = supabase
.channel(`game-${gamePin}`)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'games',
filter: `gamePIN=eq.${gamePin}`
},
(payload) => {
if (payload.new.gameStatus != gameStatus) {
gameStatus = payload.new.gameStatus;
isWait = false;
Selected = null;
currentQuestion = Number(gameStatus);
}
}
)
.subscribe();
const { data, error } = await supabase
.from('games')
.select('questions')
.eq('gamePIN', Number(gamePin))
.single();
if (!error && data) {
question = data.questions || [];
}
});
async function SubmitAnswer() {
isWait = true;
const { data, error } = await supabase
.from('games')
.select('players')
.eq('gamePIN', Number(gamePin))
.single();
players = data.players;
if (question[currentQuestion].correctAnswer == Selected) {
players.forEach((player) => {
if (player.name == name) {
player.score += 1;
}
});
await supabase.from('games').update({ players: players }).eq('gamePIN', Number(gamePin));
}
const { data: data2 } = await supabase
.from('games')
.select('questions')
.eq('gamePIN', Number(gamePin))
.single();
question = data2.questions;
question[currentQuestion].playersCompelted++;
console.log('Players Completed:', question[currentQuestion].playersCompelted);
await supabase.from('games').update({ questions: question }).eq('gamePIN', Number(gamePin));
}
</script>
<div class="bg-grey-900 flex h-full items-center justify-center">
<div
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
>
<div class="mb-5 flex w-full items-center justify-center gap-3">
<h3>Question {currentQuestion + 1} of {question.length}</h3>
<div class="flex-1 rounded-full border-2 border-gray-600">
<div
class="h-4 rounded-full bg-green-600 transition-all duration-700"
style="width: {(currentQuestion / question.length) * 100}%;"
></div>
</div>
</div>
{#if isWait != true}
<h1 class="m-[0] text-center text-5xl">
Q{currentQuestion + 1}. {question[currentQuestion].name}
</h1>
<div class="mt-5 grid grid-cols-2 gap-5 gap-x-3">
{#each question[currentQuestion].answers as answer, index}
<div class="flex">
<input
type="radio"
id="O{index}"
name="question"
class="peer sr-only"
value={index}
bind:group={Selected}
/>
<label
class="w-full cursor-pointer rounded-lg border-3 border-gray-600 bg-gray-600 pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all peer-checked:border-blue-600 peer-checked:bg-blue-600 hover:border-blue-600"
for="O{index}">{answer}</label
>
</div>
{/each}
</div>
{#if Selected != null}
<button
class="mt-4 cursor-pointer gap-0 rounded-lg bg-green-700 p-2 text-3xl transition-all hover:scale-110 hover:-rotate-10"
onclick={SubmitAnswer}
>submit answer
</button>
{:else}
<button
class="mt-4 cursor-pointer gap-0 rounded-lg bg-gray-700 p-2 text-3xl transition-all hover:scale-110"
>select an answer to submit!
</button>
{/if}
{:else}<h1 class="m-[0] text-center text-5xl">
Please wait for everyone else to answer the question.
</h1>{/if}
</div>
</div>

View file

@ -6,7 +6,15 @@ const config = {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter. // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters. // See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter() adapter: adapter({
// default options are shown. On some platforms
// these options are set automatically — see below
pages: 'build',
assets: 'build',
fallback: undefined,
precompress: false,
strict: true
})
} }
}; };