✨ Added /explore and /view
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
from pydantic import BaseModel, Json
|
from pydantic import BaseModel, Json
|
||||||
@@ -32,7 +33,7 @@ class UserSession(ormar.Model):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4())
|
id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4())
|
||||||
user: uuid.UUID = ormar.ForeignKey(User)
|
user: Optional[User] = ormar.ForeignKey(User)
|
||||||
session_key: str = ormar.String(unique=True, max_length=64)
|
session_key: str = ormar.String(unique=True, max_length=64)
|
||||||
created_at: datetime = ormar.DateTime(default=datetime.now())
|
created_at: datetime = ormar.DateTime(default=datetime.now())
|
||||||
ip_address: str = ormar.String(max_length=100, nullable=True)
|
ip_address: str = ormar.String(max_length=100, nullable=True)
|
||||||
@@ -71,7 +72,7 @@ class Quiz(ormar.Model):
|
|||||||
description: str = ormar.String(max_length=300, nullable=True)
|
description: str = ormar.String(max_length=300, nullable=True)
|
||||||
created_at: datetime = ormar.DateTime(default=datetime.now())
|
created_at: datetime = ormar.DateTime(default=datetime.now())
|
||||||
updated_at: datetime = ormar.DateTime(default=datetime.now())
|
updated_at: datetime = ormar.DateTime(default=datetime.now())
|
||||||
user_id: uuid.UUID = ormar.UUID(foreign_key=User.id)
|
user_id: uuid.UUID = ormar.ForeignKey(User)
|
||||||
questions: Json[list[QuizQuestion]] = ormar.JSON(nullable=False)
|
questions: Json[list[QuizQuestion]] = ormar.JSON(nullable=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from datetime import datetime
|
|||||||
|
|
||||||
from aiohttp import ClientSession
|
from aiohttp import ClientSession
|
||||||
|
|
||||||
from classquiz.config import settings, storage
|
from classquiz.config import settings, storage, meilisearch
|
||||||
from classquiz.db.models import Quiz, QuizAnswer, QuizQuestion, User
|
from classquiz.db.models import Quiz, QuizAnswer, QuizQuestion, User
|
||||||
from classquiz.kahoot_importer.get import get as get_quiz
|
from classquiz.kahoot_importer.get import get as get_quiz
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ async def import_quiz(quiz_id: str, user: User) -> Quiz | str:
|
|||||||
)
|
)
|
||||||
quiz_data = Quiz(
|
quiz_data = Quiz(
|
||||||
id=quiz_id,
|
id=quiz_id,
|
||||||
public=False,
|
public=True,
|
||||||
title=quiz.kahoot.title,
|
title=quiz.kahoot.title,
|
||||||
description=quiz.kahoot.description,
|
description=quiz.kahoot.description,
|
||||||
created_at=datetime.now(),
|
created_at=datetime.now(),
|
||||||
@@ -96,4 +96,10 @@ async def import_quiz(quiz_id: str, user: User) -> Quiz | str:
|
|||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
questions=json.dumps(quiz_questions),
|
questions=json.dumps(quiz_questions),
|
||||||
)
|
)
|
||||||
|
meilisearch.index(settings.meilisearch_index).add_documents([{
|
||||||
|
"id": str(quiz_data.id),
|
||||||
|
"title": quiz_data.title,
|
||||||
|
"description": quiz_data.description,
|
||||||
|
"user": (await User.objects.filter(id=quiz_data.user_id).first()).username,
|
||||||
|
}])
|
||||||
return await quiz_data.save()
|
return await quiz_data.save()
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ async def create_quiz_lol(quiz_input: QuizInput, user: User = Depends(get_curren
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/get/{quiz_id}")
|
@router.get("/get/{quiz_id}")
|
||||||
async def get_quiz_from_id(quiz_id: str, user: User | None = Depends(get_current_user_optional)):
|
async def get_quiz_from_id(quiz_id: str, user: User | None = Depends(get_current_user)):
|
||||||
try:
|
try:
|
||||||
quiz_id = uuid.UUID(quiz_id)
|
quiz_id = uuid.UUID(quiz_id)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@@ -59,6 +59,19 @@ async def get_quiz_from_id(quiz_id: str, user: User | None = Depends(get_current
|
|||||||
return quiz
|
return quiz
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/get/public/{quiz_id}", response_model=Quiz)
|
||||||
|
async def get_public_quiz(quiz_id: str):
|
||||||
|
try:
|
||||||
|
quiz_id = uuid.UUID(quiz_id)
|
||||||
|
except ValueError:
|
||||||
|
raise HTTPException(status_code=400, detail="badly formed quiz id")
|
||||||
|
quiz = await Quiz.objects.get_or_none(id=quiz_id, public=True)
|
||||||
|
if quiz is None:
|
||||||
|
return JSONResponse(status_code=404, content={"detail": "quiz not found"})
|
||||||
|
else:
|
||||||
|
return quiz
|
||||||
|
|
||||||
|
|
||||||
@router.post("/start/{quiz_id}")
|
@router.post("/start/{quiz_id}")
|
||||||
async def start_quiz(quiz_id: str, user: User = Depends(get_current_user)):
|
async def start_quiz(quiz_id: str, user: User = Depends(get_current_user)):
|
||||||
try:
|
try:
|
||||||
@@ -67,7 +80,9 @@ async def start_quiz(quiz_id: str, user: User = Depends(get_current_user)):
|
|||||||
raise HTTPException(status_code=400, detail="badly formed quiz id")
|
raise HTTPException(status_code=400, detail="badly formed quiz id")
|
||||||
quiz = await Quiz.objects.get_or_none(id=quiz_id, user_id=user.id)
|
quiz = await Quiz.objects.get_or_none(id=quiz_id, user_id=user.id)
|
||||||
if quiz is None:
|
if quiz is None:
|
||||||
return JSONResponse(status_code=404, content={"detail": "quiz not found"})
|
quiz = await Quiz.objects.get_or_none(id=quiz_id, public=True)
|
||||||
|
if quiz is None:
|
||||||
|
return JSONResponse(status_code=404, content={"detail": "quiz not found"})
|
||||||
else:
|
else:
|
||||||
game_pin = randint(10000000, 99999999)
|
game_pin = randint(10000000, 99999999)
|
||||||
game = PlayGame(
|
game = PlayGame(
|
||||||
|
|||||||
@@ -50,9 +50,10 @@
|
|||||||
"svelte": "^3.47.0",
|
"svelte": "^3.47.0",
|
||||||
"svelte-check": "^2.6.0",
|
"svelte-check": "^2.6.0",
|
||||||
"svelte-preprocess": "^4.10.5",
|
"svelte-preprocess": "^4.10.5",
|
||||||
"sveltejs-tippy": "^3.0.0",
|
"svelte-tippy": "^1.3.2",
|
||||||
"swiper": "^8.1.0",
|
"swiper": "^8.1.0",
|
||||||
"tailwindcss": "^3.0.24",
|
"tailwindcss": "^3.0.24",
|
||||||
|
"tippy.js": "^6.3.7",
|
||||||
"tslib": "^2.3.1",
|
"tslib": "^2.3.1",
|
||||||
"typescript": "~4.6",
|
"typescript": "~4.6",
|
||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
|
|||||||
Generated
+2821
-3796
File diff suppressed because it is too large
Load Diff
Vendored
-44
@@ -1,44 +0,0 @@
|
|||||||
/// <reference types="@sveltejs/kit" />
|
|
||||||
|
|
||||||
// See https://kit.svelte.dev/docs/types#the-app-namespace
|
|
||||||
// for information about these interfaces
|
|
||||||
declare namespace App {
|
|
||||||
// interface Locals {}
|
|
||||||
// interface Platform {}
|
|
||||||
interface Session {
|
|
||||||
authenticated: boolean;
|
|
||||||
token: string | null;
|
|
||||||
email: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// interface Stuff {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface QuizData {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
quiz_id: string;
|
|
||||||
questions: Question[];
|
|
||||||
game_id: string;
|
|
||||||
game_pin: string;
|
|
||||||
started: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Question {
|
|
||||||
time: string;
|
|
||||||
question: string;
|
|
||||||
image?: string;
|
|
||||||
answers: Answer[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Answer {
|
|
||||||
right: boolean;
|
|
||||||
answer: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Keep an eye on this shit
|
|
||||||
// export interface Answer {
|
|
||||||
// username: string;
|
|
||||||
// answer: string;
|
|
||||||
// right: boolean;
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<script>
|
||||||
|
export let headerText;
|
||||||
|
|
||||||
|
let expanded = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class='collapsible'>
|
||||||
|
<h3>
|
||||||
|
<button aria-expanded={expanded} on:click={() => expanded = !expanded}>{headerText}
|
||||||
|
<svg viewBox='0 0 20 20' fill='none'>
|
||||||
|
<path class='vert' d='M10 1V19' stroke='black' stroke-width='2' />
|
||||||
|
<path d='M1 10L19 10' stroke='black' stroke-width='2' />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class='contents' class:hidden={!expanded}>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.collapsible {
|
||||||
|
border-bottom: 1px solid var(--gray-light, #eee);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: var(--background, #fff);
|
||||||
|
color: var(--gray-darkest, #282828);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[aria-expanded="true"] {
|
||||||
|
border-bottom: 1px solid var(--gray-light, #eee);
|
||||||
|
}
|
||||||
|
|
||||||
|
button[aria-expanded="true"] .vert {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus svg {
|
||||||
|
outline: 2px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
button [aria-expanded="true"] rect {
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
height: 0.7em;
|
||||||
|
width: 0.7em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { QuizData } from '../../app';
|
import type { QuizData } from '$lib/quiz_types';
|
||||||
|
|
||||||
export let quiz_data: QuizData;
|
export let quiz_data: QuizData;
|
||||||
export let final_results: Array<null> | Array<Array<PlayerAnswer>>;
|
export let final_results: Array<null> | Array<Array<PlayerAnswer>>;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Question } from '../../app';
|
import type { Question } from '$lib/quiz_types';
|
||||||
import { socket } from '$lib/socket';
|
import { socket } from '$lib/socket';
|
||||||
|
|
||||||
export let question: Question;
|
export let question: Question;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Answer, QuizData } from '../../app';
|
import type { Answer, QuizData } from '$lib/quiz_types';
|
||||||
|
|
||||||
export let results: Array<Answer>;
|
export let results: Array<Answer>;
|
||||||
export let game_data: QuizData;
|
export let game_data: QuizData;
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/// <reference types="@sveltejs/kit" />
|
||||||
|
|
||||||
|
// See https://kit.svelte.dev/docs/types#the-app-namespace
|
||||||
|
// for information about these interfaces
|
||||||
|
declare namespace App {
|
||||||
|
// interface Locals {}
|
||||||
|
// interface Platform {}
|
||||||
|
interface Session {
|
||||||
|
authenticated: boolean;
|
||||||
|
token: string | null;
|
||||||
|
email: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// interface Stuff {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuizData {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
quiz_id: string;
|
||||||
|
questions: Question[];
|
||||||
|
game_id: string;
|
||||||
|
game_pin: string;
|
||||||
|
started: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Question {
|
||||||
|
time: string;
|
||||||
|
question: string;
|
||||||
|
image?: string;
|
||||||
|
answers: Answer[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Answer {
|
||||||
|
right: boolean;
|
||||||
|
answer: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Keep an eye on this shit
|
||||||
|
// export interface Answer {
|
||||||
|
// username: string;
|
||||||
|
// answer: string;
|
||||||
|
// right: boolean;
|
||||||
|
// }
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { QuizData } from '../app';
|
import type { QuizData } from '$lib/quiz_types';
|
||||||
|
|
||||||
import { socket } from '$lib/socket';
|
import { socket } from '$lib/socket';
|
||||||
import { getLocalization } from '$lib/i18n';
|
import { getLocalization } from '$lib/i18n';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { QuizData } from '../app';
|
import type { QuizData } from '$lib/quiz_types';
|
||||||
import Title from '$lib/play/title.svelte';
|
import Title from '$lib/play/title.svelte';
|
||||||
import Question from '$lib/play/question.svelte';
|
import Question from '$lib/play/question.svelte';
|
||||||
import { socket } from '$lib/socket';
|
import { socket } from '$lib/socket';
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<script lang='ts'>
|
||||||
|
const getData = async () => {
|
||||||
|
const response = await fetch('/api/v1/search/search?q=*');
|
||||||
|
return await response.json();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await getData()}
|
||||||
|
<svg class='h-8 w-8 animate-spin mx-auto my-20' viewBox='3 3 18 18'>
|
||||||
|
<path
|
||||||
|
class='fill-black'
|
||||||
|
d='M12 5C8.13401 5 5 8.13401 5 12C5 15.866 8.13401 19 12 19C15.866 19 19 15.866 19 12C19 8.13401 15.866 5 12 5ZM3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12Z'
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class='fill-blue-100'
|
||||||
|
d='M16.9497 7.05015C14.2161 4.31648 9.78392 4.31648 7.05025 7.05015C6.65973 7.44067 6.02656 7.44067 5.63604 7.05015C5.24551 6.65962 5.24551 6.02646 5.63604 5.63593C9.15076 2.12121 14.8492 2.12121 18.364 5.63593C18.7545 6.02646 18.7545 6.65962 18.364 7.05015C17.9734 7.44067 17.3403 7.44067 16.9497 7.05015Z'
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{:then quizzes}
|
||||||
|
<div class='grid grid-cols-3'>
|
||||||
|
{#each quizzes.hits as quiz}
|
||||||
|
<div class='flex justify-center'>
|
||||||
|
<a href='/view/{quiz.id}' class='h-max w-fit'>
|
||||||
|
<div class='max-w-md py-4 px-8 bg-white shadow-lg rounded-lg my-20'>
|
||||||
|
<!-- <div class='flex justify-center md:justify-end -mt-16'>
|
||||||
|
<img class='w-20 h-20 object-cover rounded-full border-2 border-indigo-500'
|
||||||
|
src='https://images.unsplash.com/photo-1499714608240-22fc6ad53fb2?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=334&q=80'>
|
||||||
|
</div>-->
|
||||||
|
<div>
|
||||||
|
<h2 class='text-gray-800 text-3xl font-semibold'>{quiz.title}</h2>
|
||||||
|
<p class='mt-2 text-gray-600'>{quiz.description}</p>
|
||||||
|
</div>
|
||||||
|
<div class='flex mt-4'>
|
||||||
|
<span>Made by {quiz.user}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/await}
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Question } from '../app';
|
import type { Question } from '$lib/quiz_types';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { getLocalization } from '$lib/i18n';
|
import { getLocalization } from '$lib/i18n';
|
||||||
import Footer from '$lib/footer.svelte';
|
import Footer from '$lib/footer.svelte';
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { socket } from '$lib/socket';
|
import { socket } from '$lib/socket';
|
||||||
import JoinGame from '$lib/play/join.svelte';
|
import JoinGame from '$lib/play/join.svelte';
|
||||||
import type { Answer, QuizData } from '../app';
|
import type { Answer, QuizData } from '$lib/quiz_types';
|
||||||
import ShowTitle from '$lib/play/title.svelte';
|
import ShowTitle from '$lib/play/title.svelte';
|
||||||
import Question from '$lib/play/question.svelte';
|
import Question from '$lib/play/question.svelte';
|
||||||
import ShowResults from '$lib/play/show_results.svelte';
|
import ShowResults from '$lib/play/show_results.svelte';
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
<script lang='ts' context='module'>
|
||||||
|
|
||||||
|
export async function load({ params, fetch, session }) {
|
||||||
|
const { quiz_id } = params;
|
||||||
|
const res = await fetch(`/api/v1/quiz/get/public/${quiz_id}`);
|
||||||
|
if (res.status === 404) {
|
||||||
|
return {
|
||||||
|
status: 404
|
||||||
|
};
|
||||||
|
} else if (res.status === 200) {
|
||||||
|
const quiz = await res.json();
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
quiz: quiz,
|
||||||
|
logged_in: session.authenticated
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
status: 500
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang='ts'>
|
||||||
|
import { getLocalization } from '$lib/i18n';
|
||||||
|
import CollapsSection from '$lib/collapsible.svelte';
|
||||||
|
import { createTippy } from 'svelte-tippy';
|
||||||
|
import 'tippy.js/animations/perspective-subtle.css';
|
||||||
|
import 'tippy.js/dist/tippy.css';
|
||||||
|
|
||||||
|
const tippy = createTippy({
|
||||||
|
arrow: true,
|
||||||
|
animation: 'perspective-subtle',
|
||||||
|
placement: 'right'
|
||||||
|
});
|
||||||
|
|
||||||
|
const { t } = getLocalization();
|
||||||
|
export let logged_in: boolean;
|
||||||
|
export let quiz: QuizData;
|
||||||
|
|
||||||
|
interface Question {
|
||||||
|
time: string;
|
||||||
|
question: string;
|
||||||
|
image?: string;
|
||||||
|
answers: Answer[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Answer {
|
||||||
|
right: boolean;
|
||||||
|
answer: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QuizData {
|
||||||
|
id: string;
|
||||||
|
public: boolean;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
user_id: string;
|
||||||
|
questions: Question[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const startGame = async (id: string): Promise<void> => {
|
||||||
|
console.log('start game', id);
|
||||||
|
const res = await fetch(`/api/v1/quiz/start/${id}`, {
|
||||||
|
method: 'POST'
|
||||||
|
// headers: {
|
||||||
|
// 'Content-Type': 'application/json'
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw new Error('Failed to start game');
|
||||||
|
}
|
||||||
|
const data = await res.json();
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
plausible('Started Game', { props: { quiz_id: id } });
|
||||||
|
window.location.replace(`/admin?token=${data.game_id}&pin=${data.game_pin}&connect=1`);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h1 class='text-4xl text-center'>{quiz.title}</h1>
|
||||||
|
<div class='text-center'>
|
||||||
|
<p>{quiz.description}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='flex justify-center m-8'>
|
||||||
|
{#if logged_in}
|
||||||
|
<button
|
||||||
|
class='px-4 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded text-center hover:bg-gray-600 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50'
|
||||||
|
on:click={() => {
|
||||||
|
startGame(quiz.id);
|
||||||
|
}}>
|
||||||
|
{$t('words.start')}
|
||||||
|
</button>
|
||||||
|
{:else }
|
||||||
|
<button
|
||||||
|
class='px-4 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded text-center hover:bg-gray-600 focus:outline-none cursor-not-allowed opacity-50'
|
||||||
|
use:tippy={{content: "You need to be logged in to start a game"}}>
|
||||||
|
|
||||||
|
{$t('words.start')}
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{#each quiz.questions as question, index_question}
|
||||||
|
<div class='px-4 py-1'>
|
||||||
|
<CollapsSection headerText={question.question}>
|
||||||
|
<div class='ml-8 grid grid-cols-1 gap-2 m-2 border border-black border-2'>
|
||||||
|
<h1 class='text-3xl m-1'>{$t('words.question')} {index_question + 1}</h1>
|
||||||
|
|
||||||
|
<!-- <label class='m-1 flex flex-row gap-2 w-3/5'>-->
|
||||||
|
<p
|
||||||
|
class='text-black w-full bg-inherit'
|
||||||
|
>{$t('words.question')}: {question.question}</p>
|
||||||
|
<!-- </label>-->
|
||||||
|
{#if question.image}
|
||||||
|
<label class='m-1 flex flex-row gap-2 w-3/5'>
|
||||||
|
{$t('words.image')}:
|
||||||
|
<img src='{question.image}' alt='Not provided'>
|
||||||
|
</label>
|
||||||
|
{/if}
|
||||||
|
<label class='m-1 flex flex-row gap-2 w-3/5 flex-nowrap whitespace-nowrap'>
|
||||||
|
{$t('editor.time_in_seconds')}:
|
||||||
|
<p>{question.time}</p>
|
||||||
|
</label>
|
||||||
|
{#each question.answers as answer, index_answer}
|
||||||
|
<div class='ml-8 grid grid-cols-1 gap-2 m-2 border border-black border-2 m-1'>
|
||||||
|
<h1 class='text-3xl m-1'>{$t('words.answer')} {index_answer + 1}</h1>
|
||||||
|
<p class='m-1'>
|
||||||
|
{$t('words.answer')}: {index_answer + 1}
|
||||||
|
{$t('words.question')}: {index_question + 1}
|
||||||
|
</p>
|
||||||
|
<p>{$t('words.answer')}
|
||||||
|
: {quiz.questions[index_question].answers[index_answer].answer}</p>
|
||||||
|
<label class='m-1 flex flex-row gap-2 w-2/6 flex-nowrap whitespace-nowrap'>
|
||||||
|
<input type='checkbox' bind:checked={answer.right} class='text-black w-fit'
|
||||||
|
disabled />
|
||||||
|
<span class='w-fit'>{$t('editor.right_or_true?')}</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</CollapsSection>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
Reference in New Issue
Block a user