📝 Added documentation about development-setup
This commit is contained in:
@@ -6,16 +6,25 @@ You can create quizzes and play them remotely with other people.
|
|||||||
It is mainly made for teachers, who create a quiz, so students can compete with their knowledge against each other.
|
It is mainly made for teachers, who create a quiz, so students can compete with their knowledge against each other.
|
||||||
|
|
||||||
## Try it
|
## Try it
|
||||||
|
|
||||||
There is a hosted version at [ClassQuiz.Mawoka.eu](https://classquiz.mawoka.eu?utm_medium=Github&utm_source=Readme).
|
There is a hosted version at [ClassQuiz.Mawoka.eu](https://classquiz.mawoka.eu?utm_medium=Github&utm_source=Readme).
|
||||||
It is not intended for real-life usage, although it should work.
|
It is not intended for real-life usage, although it should work.
|
||||||
The server is located in Karlsruhe, Germany and hosted by [netcup](https://mawoka.eu/redir?token=2).
|
The server is located in Karlsruhe, Germany and hosted by [netcup](https://mawoka.eu/redir?token=2).
|
||||||
If you want to use it in real-life,
|
If you want to use it in real-life,
|
||||||
please contact me on [my website](https://mawoka.eu/contact?utm_medium=Github&utm_source=Readme).
|
please contact me on [my website](https://mawoka.eu/contact?utm_medium=Github&utm_source=Readme).
|
||||||
|
|
||||||
## Self-Host
|
## Self-Host
|
||||||
|
|
||||||
Please see https://classquiz.mawoka.eu/docs/self-host
|
Please see https://classquiz.mawoka.eu/docs/self-host
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
See https://classquiz.mawoka.eu/docs/develop
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
The docs are https://classquiz.mawoka.eu/docs on the website
|
||||||
### Things to know about the structure
|
### Things to know about the structure
|
||||||
|
|
||||||
1. Everything depends on Redis (API and frontend).
|
1. Everything depends on Redis (API and frontend).
|
||||||
2. The proxy-container is optional (Caddy), but makes your life easier
|
2. The proxy-container is optional (Caddy), but makes your life easier
|
||||||
3. You need to set your reverse proxy up, so that it allows Socket.io-connections.
|
3. You need to set your reverse proxy up, so that it allows Socket.io-connections.
|
||||||
|
|||||||
Generated
+3301
-2426
File diff suppressed because it is too large
Load Diff
+26
-22
@@ -1,25 +1,29 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang='en'>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset='utf-8' />
|
<meta charset="utf-8" />
|
||||||
<meta name='description' content='' />
|
<meta name="description" content="" />
|
||||||
<!-- <link rel="icon" href="%svelte.assets%/favicon.png" />-->
|
<!-- <link rel="icon" href="%svelte.assets%/favicon.png" />-->
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<script async='' defer='' data-domain='classquiz.mawoka.eu'
|
<script
|
||||||
src='https://sugar.mawoka.eu.org/js/plausible.hash.outbound-links.js'></script>
|
async=""
|
||||||
|
defer=""
|
||||||
|
data-domain="classquiz.mawoka.eu"
|
||||||
|
src="https://sugar.mawoka.eu.org/js/plausible.hash.outbound-links.js"
|
||||||
|
></script>
|
||||||
|
|
||||||
<link rel='apple-touch-icon' sizes='180x180' href='%svelte.assets%/apple-touch-icon.png'>
|
<link rel="apple-touch-icon" sizes="180x180" href="%svelte.assets%/apple-touch-icon.png" />
|
||||||
<link rel='icon' type='image/png' sizes='32x32' href='%svelte.assets%/favicon-32x32.png'>
|
<link rel="icon" type="image/png" sizes="32x32" href="%svelte.assets%/favicon-32x32.png" />
|
||||||
<link rel='icon' type='image/png' sizes='16x16' href='%svelte.assets%/favicon-16x16.png'>
|
<link rel="icon" type="image/png" sizes="16x16" href="%svelte.assets%/favicon-16x16.png" />
|
||||||
<link rel='manifest' href='%svelte.assets%/site.webmanifest'>
|
<link rel="manifest" href="%svelte.assets%/site.webmanifest" />
|
||||||
<link rel='mask-icon' href='%svelte.assets%/safari-pinned-tab.svg' color='#5bbad5'>
|
<link rel="mask-icon" href="%svelte.assets%/safari-pinned-tab.svg" color="#5bbad5" />
|
||||||
<meta name='apple-mobile-web-app-title' content='ClassQuiz'>
|
<meta name="apple-mobile-web-app-title" content="ClassQuiz" />
|
||||||
<meta name='application-name' content='ClassQuiz'>
|
<meta name="application-name" content="ClassQuiz" />
|
||||||
<meta name='msapplication-TileColor' content='#00a300'>
|
<meta name="msapplication-TileColor" content="#00a300" />
|
||||||
<meta name='theme-color' content='#ffffff'>
|
<meta name="theme-color" content="#ffffff" />
|
||||||
%svelte.head%
|
%svelte.head%
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div>%svelte.body%</div>
|
<div>%svelte.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -23,7 +23,13 @@
|
|||||||
|
|
||||||
<!-- Our open/close buttons -->
|
<!-- Our open/close buttons -->
|
||||||
<!-- Open menu -->
|
<!-- Open menu -->
|
||||||
<button class="lg:hidden" id="open-menu" on:click={toggleMenu} class:hidden={!openMenu} aria-label='Open navbar'>
|
<button
|
||||||
|
class="lg:hidden"
|
||||||
|
id="open-menu"
|
||||||
|
on:click={toggleMenu}
|
||||||
|
class:hidden={!openMenu}
|
||||||
|
aria-label="Open navbar"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="24"
|
width="24"
|
||||||
@@ -42,7 +48,13 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Close menu -->
|
<!-- Close menu -->
|
||||||
<button class="hidden" id="close-menu" class:hidden={!closeMenu} on:click={toggleMenu} aria-label='Close navbar'>
|
<button
|
||||||
|
class="hidden"
|
||||||
|
id="close-menu"
|
||||||
|
class:hidden={!closeMenu}
|
||||||
|
on:click={toggleMenu}
|
||||||
|
aria-label="Close navbar"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="14"
|
width="14"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script lang='ts'>
|
<script lang="ts">
|
||||||
import { socket } from '$lib/socket';
|
import { socket } from '$lib/socket';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import { browser } from '$app/env';
|
import { browser } from '$app/env';
|
||||||
import * as Sentry from "@sentry/browser";
|
import * as Sentry from '@sentry/browser';
|
||||||
|
|
||||||
export let game_pin: string;
|
export let game_pin: string;
|
||||||
|
|
||||||
@@ -11,8 +11,7 @@
|
|||||||
|
|
||||||
let hcaptcha = {
|
let hcaptcha = {
|
||||||
execute: async (_a, _b) => ({ response: '' }),
|
execute: async (_a, _b) => ({ response: '' }),
|
||||||
render: (_a, _b) => {
|
render: (_a, _b) => {}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let hcaptchaWidgetID;
|
let hcaptchaWidgetID;
|
||||||
|
|
||||||
@@ -33,8 +32,7 @@
|
|||||||
if (browser) {
|
if (browser) {
|
||||||
hcaptcha = {
|
hcaptcha = {
|
||||||
execute: async () => ({ response: '' }),
|
execute: async () => ({ response: '' }),
|
||||||
render: () => {
|
render: () => {}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -50,7 +48,7 @@
|
|||||||
});
|
});
|
||||||
captcha_resp = response;
|
captcha_resp = response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (import.meta.env.VITE_SENTRY !== null){
|
if (import.meta.env.VITE_SENTRY !== null) {
|
||||||
Sentry.captureException(e);
|
Sentry.captureException(e);
|
||||||
}
|
}
|
||||||
alert('Captcha failed, reloading the page probably helps!');
|
alert('Captcha failed, reloading the page probably helps!');
|
||||||
@@ -69,27 +67,27 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<script src='https://js.hcaptcha.com/1/api.js?render=explicit' async defer></script>
|
<script src="https://js.hcaptcha.com/1/api.js?render=explicit" async defer></script>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
{#if game_pin === '' || game_pin.length <= 7}
|
{#if game_pin === '' || game_pin.length <= 7}
|
||||||
<div class='flex flex-col justify-center align-center w-screen h-screen'>
|
<div class="flex flex-col justify-center align-center w-screen h-screen">
|
||||||
<form
|
<form
|
||||||
on:submit|preventDefault={() => {}}
|
on:submit|preventDefault={() => {}}
|
||||||
class='flex-col flex justify-center align-center mx-auto'
|
class="flex-col flex justify-center align-center mx-auto"
|
||||||
>
|
>
|
||||||
<h1 class='text-lg text-center'>Game Pin</h1>
|
<h1 class="text-lg text-center">Game Pin</h1>
|
||||||
<input
|
<input
|
||||||
class='border border-amber-800 self-center text-center text-black'
|
class="border border-amber-800 self-center text-center text-black"
|
||||||
bind:value={game_pin}
|
bind:value={game_pin}
|
||||||
maxlength='8'
|
maxlength="8"
|
||||||
/>
|
/>
|
||||||
<!-- use:tippy={{content: "Please enter the game pin", sticky: true, placement: 'top'}}-->
|
<!-- use:tippy={{content: "Please enter the game pin", sticky: true, placement: 'top'}}-->
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<button
|
<button
|
||||||
class='bg-amber-800 hover:bg-amber-700 text-white font-bold py-2 px-4 rounded disabled:opacity-50 disabled:cursor-not-allowed mt-2'
|
class="bg-amber-800 hover:bg-amber-700 text-white font-bold py-2 px-4 rounded disabled:opacity-50 disabled:cursor-not-allowed mt-2"
|
||||||
type='submit'
|
type="submit"
|
||||||
disabled={game_pin.length <= 7}
|
disabled={game_pin.length <= 7}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
@@ -97,16 +95,16 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class='flex flex-col justify-center align-center w-screen h-screen'>
|
<div class="flex flex-col justify-center align-center w-screen h-screen">
|
||||||
<form
|
<form
|
||||||
on:submit|preventDefault={setUsername}
|
on:submit|preventDefault={setUsername}
|
||||||
class='flex-col flex justify-center align-center mx-auto'
|
class="flex-col flex justify-center align-center mx-auto"
|
||||||
>
|
>
|
||||||
<h1 class='text-lg text-center'>Username</h1>
|
<h1 class="text-lg text-center">Username</h1>
|
||||||
<input class='border border-amber-800 text-black text-center' bind:value={username} />
|
<input class="border border-amber-800 text-black text-center" bind:value={username} />
|
||||||
<button
|
<button
|
||||||
class='bg-amber-800 hover:bg-amber-700 text-white font-bold py-2 px-4 rounded disabled:cursor-not-allowed disabled:opacity-50 mt-2'
|
class="bg-amber-800 hover:bg-amber-700 text-white font-bold py-2 px-4 rounded disabled:cursor-not-allowed disabled:opacity-50 mt-2"
|
||||||
type='submit'
|
type="submit"
|
||||||
disabled={username.length <= 3}
|
disabled={username.length <= 3}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
@@ -115,9 +113,9 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div
|
<div
|
||||||
id='hcaptcha'
|
id="hcaptcha"
|
||||||
class='h-captcha'
|
class="h-captcha"
|
||||||
data-sitekey={hcaptchaSitekey}
|
data-sitekey={hcaptchaSitekey}
|
||||||
data-size='invisible'
|
data-size="invisible"
|
||||||
data-theme='dark'
|
data-theme="dark"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -16,42 +16,41 @@
|
|||||||
tracesSampleRate: 0.5
|
tracesSampleRate: 0.5
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $navbarVisible}
|
{#if $navbarVisible}
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<div class='pt-16 h-screen'>
|
<div class="pt-16 h-screen">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<slot />
|
<slot />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style lang='scss'>
|
<style lang="scss">
|
||||||
:global(body) {
|
:global(body) {
|
||||||
// height: 100%;
|
// height: 100%;
|
||||||
// width: 100%;
|
// width: 100%;
|
||||||
background: linear-gradient(to right, #009444, #39b54a, #8dc63f) repeat-y;
|
background: linear-gradient(to right, #009444, #39b54a, #8dc63f) repeat-y;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
/*background: linear-gradient(-225deg, #231557 0%, #44107A 29%, #FF1361 67%, #FFF800 100%); */
|
/*background: linear-gradient(-225deg, #231557 0%, #44107A 29%, #FF1361 67%, #FFF800 100%); */
|
||||||
/*background: linear-gradient(-225deg, #22E1FF 0%, #1D8FE1 48%, #625EB1 100%); */
|
/*background: linear-gradient(-225deg, #22E1FF 0%, #1D8FE1 48%, #625EB1 100%); */
|
||||||
color: beige;
|
color: beige;
|
||||||
|
|
||||||
// background-size: 400% 400%;
|
// background-size: 400% 400%;
|
||||||
|
|
||||||
//animation: background_animation 5s ease infinite;
|
//animation: background_animation 5s ease infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes background_animation {
|
@keyframes background_animation {
|
||||||
0% {
|
0% {
|
||||||
background-position: 0% 50%;
|
background-position: 0% 50%;
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
background-position: 100% 50%;
|
background-position: 100% 50%;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
background-position: 0% 50%;
|
background-position: 0% 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
isSubmitting = false;
|
isSubmitting = false;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>ClassQuiz - Login</title>
|
<title>ClassQuiz - Login</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|||||||
@@ -68,6 +68,7 @@
|
|||||||
data: ''
|
data: ''
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>ClassQuiz - Register</title>
|
<title>ClassQuiz - Register</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|||||||
@@ -21,9 +21,7 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<svelte:head>
|
|
||||||
<title>ClassQuiz - Host</title>
|
|
||||||
</svelte:head>
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { QuizData } from '../app';
|
import type { QuizData } from '../app';
|
||||||
|
|
||||||
@@ -106,6 +104,10 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>ClassQuiz - Host</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
{#if !success}
|
{#if !success}
|
||||||
<input placeholder="game id" bind:value={game_token} />
|
<input placeholder="game id" bind:value={game_token} />
|
||||||
<input placeholder="game pin" bind:value={game_pin} />
|
<input placeholder="game pin" bind:value={game_pin} />
|
||||||
|
|||||||
@@ -21,9 +21,7 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<svelte:head>
|
|
||||||
<title>ClassQuiz - Create</title>
|
|
||||||
</svelte:head>
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import Editor from '$lib/editor.svelte';
|
import Editor from '$lib/editor.svelte';
|
||||||
@@ -82,6 +80,10 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>ClassQuiz - Create</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
{#if data !== undefined}
|
{#if data !== undefined}
|
||||||
<form on:submit|preventDefault={submit} class="grid grid-cols-1 gap-2">
|
<form on:submit|preventDefault={submit} class="grid grid-cols-1 gap-2">
|
||||||
<Editor bind:data />
|
<Editor bind:data />
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
<svelte:head>
|
||||||
|
<title>ClassQuiz/docs - Development-setup</title>
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="How to import quizzes from KAHOOT! into ClassQuiz, the open-source quiz-application, easily"
|
||||||
|
/>
|
||||||
|
</svelte:head>
|
||||||
|
<article
|
||||||
|
class="prose prose-sm sm:prose lg:prose-lg xl:prose-xl mx-auto mt-10 prose-pink text-yellow-50 px-4"
|
||||||
|
>
|
||||||
|
<h1>Development-setup</h1>
|
||||||
|
|
||||||
|
<h2>Requirements</h2>
|
||||||
|
<h4>Backend</h4>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://caddyserver.com/">Caddy</a></li>
|
||||||
|
<li><a href="https://pipenv.pypa.io/en/latest/">Pipenv</a></li>
|
||||||
|
<li><a href="https://redis.io/">Redis</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>Frontend</h4>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://pnpm.io/">pNpM</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Setup</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Clone the repo:
|
||||||
|
<pre><code>git clone https://github.com/mawoka-myblock/classquiz</code></pre>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Install the dependencies
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Python:
|
||||||
|
<pre><code>pipenv install</code></pre>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
JS:
|
||||||
|
<pre><code>cd frontend && pnpm i</code></pre>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Start the other services
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Caddy:
|
||||||
|
<pre><code>caddy run</code></pre>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Redis:
|
||||||
|
<pre><code>redis-server</code></pre>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
hcaptcha_key: str mail_address: str mail_password: str mail_username: str mail_server: str mail_port:
|
||||||
|
int secret_key: str
|
||||||
|
<li>
|
||||||
|
Set your config up in your .env-file. What you have to set up can you see in the
|
||||||
|
<code>classquiz/config.py</code>-file. The things you have to set are the following:
|
||||||
|
<ul>
|
||||||
|
<li><code>hcaptcha_key</code></li>
|
||||||
|
<li><code>mail_address</code></li>
|
||||||
|
<li><code>mail_password</code></li>
|
||||||
|
<li><code>mail_username</code></li>
|
||||||
|
<li><code>mail_server</code></li>
|
||||||
|
<li><code>mail_port</code></li>
|
||||||
|
<li><code>secret_key</code></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Start the server
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Backend:
|
||||||
|
<pre><code>pipenv run uvicorn classquiz:app --reload</code></pre>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Frontend:
|
||||||
|
<pre><code>cd frontend && pnpm dev</code></pre>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>BEFORE you submit a Pull-Request</h2>
|
||||||
|
<h4>Frontend</h4>
|
||||||
|
<p>Please format and lint your code with</p>
|
||||||
|
<pre><code>pnpm run format && pnpm run lint</code></pre>
|
||||||
|
|
||||||
|
<h4>Backend</h4>
|
||||||
|
<p>IDK, just check that it works!</p>
|
||||||
|
</article>
|
||||||
@@ -1,17 +1,25 @@
|
|||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>ClassQuiz/docs - Import from Kahoot</title>
|
<title>ClassQuiz/docs - Import from Kahoot</title>
|
||||||
<meta name=description content='How to import quizzes from KAHOOT! into ClassQuiz, the open-source quiz-application, easily'>
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="How to import quizzes from KAHOOT! into ClassQuiz, the open-source quiz-application, easily"
|
||||||
|
/>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
<article
|
<article
|
||||||
class='prose prose-sm sm:prose lg:prose-lg xl:prose-xl mx-auto mt-10 prose-pink text-yellow-50 px-4'
|
class="prose prose-sm sm:prose lg:prose-lg xl:prose-xl mx-auto mt-10 prose-pink text-yellow-50 px-4"
|
||||||
>
|
>
|
||||||
<h1>Import Quizzes from Kahoot into ClassQuiz</h1>
|
<h1>Import Quizzes from Kahoot into ClassQuiz</h1>
|
||||||
|
|
||||||
<p>All in all, this procedure is pretty simple, so just go to <a href='https://create.kahoot.it/discover'
|
<p>
|
||||||
rel='nofollow' target='_blank'>create.kahoot.it/discover</a>,
|
All in all, this procedure is pretty simple, so just go to <a
|
||||||
find the quiz you want to import, so click on it and copy the URL into your clipboard. Then go to <a
|
href="https://create.kahoot.it/discover"
|
||||||
href='https://classquiz.mawoka.eu/import' rel='nofollow' target='_blank'>classquiz.mawoka.eu/import</a> and paste
|
rel="nofollow"
|
||||||
the url into the text field.
|
target="_blank">create.kahoot.it/discover</a
|
||||||
|
>, find the quiz you want to import, so click on it and copy the URL into your clipboard.
|
||||||
|
Then go to
|
||||||
|
<a href="https://classquiz.mawoka.eu/import" rel="nofollow" target="_blank"
|
||||||
|
>classquiz.mawoka.eu/import</a
|
||||||
|
> and paste the url into the text field.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Limitations</h2>
|
<h2>Limitations</h2>
|
||||||
@@ -21,4 +29,4 @@
|
|||||||
<li>Metadata doesn't get imported</li>
|
<li>Metadata doesn't get imported</li>
|
||||||
<li>Point-multipliers don't get imported</li>
|
<li>Point-multipliers don't get imported</li>
|
||||||
</ul>
|
</ul>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -1,22 +1,27 @@
|
|||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>ClassQuiz/docs - Index</title>
|
<title>ClassQuiz/docs - Index</title>
|
||||||
<meta name=description content='The overview about the docs for ClassQuiz, the open-source quiz-application'>
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="The overview about the docs for ClassQuiz, the open-source quiz-application"
|
||||||
|
/>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
<article
|
<article
|
||||||
class='prose prose-sm sm:prose lg:prose-lg xl:prose-xl mx-auto mt-10 prose-pink text-yellow-50 px-4'
|
class="prose prose-sm sm:prose lg:prose-lg xl:prose-xl mx-auto mt-10 prose-pink text-yellow-50 px-4"
|
||||||
>
|
>
|
||||||
<h1>Welcome to the ClassQuiz docs!</h1>
|
<h1>Welcome to the ClassQuiz docs!</h1>
|
||||||
<h2>Index</h2>
|
<h2>Index</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href='/docs/roadmap'>Roadmap</a>
|
<a href="/docs/roadmap">Roadmap</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href='/docs/import-from-kahoot'>Import from Kahoot</a>
|
<a href="/docs/import-from-kahoot">Import from Kahoot</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href='/docs/self-host'>Self-hosting</a>
|
<a href="/docs/self-host">Self-hosting</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/docs/develop">Development-Setup</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</article>
|
||||||
</article>
|
|
||||||
|
|||||||
@@ -1,34 +1,26 @@
|
|||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>ClassQuiz/docs - Roadmap</title>
|
<title>ClassQuiz/docs - Roadmap</title>
|
||||||
<meta name=description content='The roadmap for ClassQuiz, the open-source quiz-application'>
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="The roadmap for ClassQuiz, the open-source quiz-application"
|
||||||
|
/>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
<article
|
<article
|
||||||
class='prose prose-sm sm:prose lg:prose-lg xl:prose-xl mx-auto mt-10 prose-pink text-yellow-50 px-4'
|
class="prose prose-sm sm:prose lg:prose-lg xl:prose-xl mx-auto mt-10 prose-pink text-yellow-50 px-4"
|
||||||
>
|
>
|
||||||
<h1>Roadmap</h1>
|
<h1>Roadmap</h1>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>User-management
|
<li>
|
||||||
|
User-management
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>Password-reset</li>
|
||||||
Password-reset
|
<li>Session-management</li>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Session-management
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>Images</li>
|
||||||
Images
|
<li>(Better) Admin-screen styling</li>
|
||||||
</li>
|
<li>(Better) Edit/Create-screen styling</li>
|
||||||
<li>
|
<li>Public quiz-discovery</li>
|
||||||
(Better) Admin-screen styling
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
(Better) Edit/Create-screen styling
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Public quiz-discovery
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<script lang='ts'>
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import '$lib/hljs.css';
|
import '$lib/hljs.css';
|
||||||
|
|
||||||
@@ -10,30 +10,36 @@
|
|||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>ClassQuiz/docs - Self-Host</title>
|
<title>ClassQuiz/docs - Self-Host</title>
|
||||||
<meta name=description content='How to self-host ClassQuiz, the open-source quiz-application'>
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="How to self-host ClassQuiz, the open-source quiz-application"
|
||||||
|
/>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
<article
|
<article
|
||||||
class='prose prose-sm sm:prose lg:prose-lg xl:prose-xl mx-auto mt-10 prose-pink text-yellow-50 px-4'
|
class="prose prose-sm sm:prose lg:prose-lg xl:prose-xl mx-auto mt-10 prose-pink text-yellow-50 px-4"
|
||||||
>
|
>
|
||||||
<h1>Self-Hosting</h1>
|
<h1>Self-Hosting</h1>
|
||||||
<p>Since ClassQuiz is open-source, it can also be self-hosted.</p>
|
<p>Since ClassQuiz is open-source, it can also be self-hosted.</p>
|
||||||
|
|
||||||
<h2>Requirements</h2>
|
<h2>Requirements</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href='https://docker.com'>Docker</a></li>
|
<li><a href="https://docker.com">Docker</a></li>
|
||||||
<li><a href='https://git-scm.com/'>Git</a></li>
|
<li><a href="https://git-scm.com/">Git</a></li>
|
||||||
<li>A <a href='https://redis.com'>Redis</a>-Server (I recommend <a href='https://upstash.com'>Upstash</a>)</li>
|
<li>
|
||||||
|
A <a href="https://redis.com">Redis</a>-Server (I recommend
|
||||||
|
<a href="https://upstash.com">Upstash</a>)
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>Installation</h2>
|
<h2>Installation</h2>
|
||||||
<p>
|
<p>At first, clone the repo:</p>
|
||||||
At first, clone the repo:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<pre><code class='language-bash'>git clone https://github.com/mawoka-myblock/classquiz && cd ClassQuiz</code></pre>
|
<pre><code class="language-bash"
|
||||||
|
>git clone https://github.com/mawoka-myblock/classquiz && cd ClassQuiz</code
|
||||||
|
></pre>
|
||||||
<p>
|
<p>
|
||||||
Now, set a <b>VALID</b> Redis-URI in <code>frontend/Dockerfile</code> and, if you Sentry want, a valid
|
Now, set a <b>VALID</b> Redis-URI in <code>frontend/Dockerfile</code> and, if you Sentry want,
|
||||||
Sentry-DSN.
|
a valid Sentry-DSN.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
You must set a valid hCaptcha-Sitekey in the <code>frontend/Dockerfile</code>.
|
You must set a valid hCaptcha-Sitekey in the <code>frontend/Dockerfile</code>.
|
||||||
@@ -44,7 +50,8 @@
|
|||||||
Before you can start your stack, you have to set some environment-variables in your
|
Before you can start your stack, you have to set some environment-variables in your
|
||||||
<code>docker-compose.yml</code>.
|
<code>docker-compose.yml</code>.
|
||||||
</p>
|
</p>
|
||||||
<pre><code class='language-yaml'>version: "3"
|
<pre><code class="language-yaml"
|
||||||
|
>version: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
frontend:
|
frontend:
|
||||||
@@ -112,6 +119,5 @@ volumes:
|
|||||||
data:
|
data:
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>Now build and deploy:</p>
|
<p>Now build and deploy:</p>
|
||||||
<pre><code class='language-bash'>docker compose build && docker compose up -d</code></pre>
|
<pre><code class="language-bash">docker compose build && docker compose up -d</code></pre>
|
||||||
|
</article>
|
||||||
</article>
|
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>ClassQuiz - Edit</title>
|
<title>ClassQuiz - Edit</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<script context='module' lang='ts'>
|
<script context="module" lang="ts">
|
||||||
export async function load({ session }) {
|
export async function load({ session }) {
|
||||||
if (!session.authenticated) {
|
if (!session.authenticated) {
|
||||||
return {
|
return {
|
||||||
@@ -13,13 +13,9 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<svelte:head>
|
|
||||||
<title>ClassQuiz - Import</title>
|
|
||||||
</svelte:head>
|
|
||||||
<script lang='ts'>
|
|
||||||
let url_input = '';
|
|
||||||
|
|
||||||
import tippy from 'sveltejs-tippy';
|
<script lang="ts">
|
||||||
|
let url_input = '';
|
||||||
|
|
||||||
let url_valid = false;
|
let url_valid = false;
|
||||||
let kahoot_regex = /^https:\/\/create\.kahoot\.it\/details\/([a-zA-Z-\d]{36})\/?$/;
|
let kahoot_regex = /^https:\/\/create\.kahoot\.it\/details\/([a-zA-Z-\d]{36})\/?$/;
|
||||||
@@ -51,6 +47,10 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>ClassQuiz - Import</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
<!--{#if is_loading}
|
<!--{#if is_loading}
|
||||||
<svg class='h-8 w-8 animate-spin mx-auto my-20' viewBox='3 3 18 18'>
|
<svg class='h-8 w-8 animate-spin mx-auto my-20' viewBox='3 3 18 18'>
|
||||||
<path
|
<path
|
||||||
@@ -68,70 +68,69 @@
|
|||||||
<input type='text' class="text-black w-2/5" bind:value={url_input} />
|
<input type='text' class="text-black w-2/5" bind:value={url_input} />
|
||||||
<button type='submit' disabled={!url_valid}>Submit</button>
|
<button type='submit' disabled={!url_valid}>Submit</button>
|
||||||
</form>-->
|
</form>-->
|
||||||
<div class='flex items-center justify-center h-full px-4'>
|
<div class="flex items-center justify-center h-full px-4">
|
||||||
<div>
|
<div>
|
||||||
<span class='p-4' />
|
<span class="p-4" />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class='lg:w-[64rem] lg:max-w-[64rem] w-screen max-w-screen mx-auto overflow-hidden bg-white rounded-lg shadow-md dark:bg-gray-800'
|
class="lg:w-[64rem] lg:max-w-[64rem] w-screen max-w-screen mx-auto overflow-hidden bg-white rounded-lg shadow-md dark:bg-gray-800"
|
||||||
|
|
||||||
>
|
>
|
||||||
<div class='px-6 py-4'>
|
<div class="px-6 py-4">
|
||||||
<h2 class='text-3xl font-bold text-center text-gray-700 dark:text-white'>
|
<h2 class="text-3xl font-bold text-center text-gray-700 dark:text-white">
|
||||||
ClassQuiz
|
ClassQuiz
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<h3 class='mt-1 text-xl font-medium text-center text-gray-600 dark:text-gray-200'>
|
<h3 class="mt-1 text-xl font-medium text-center text-gray-600 dark:text-gray-200">
|
||||||
Welcome Back
|
Welcome Back
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p class='mt-1 text-center text-gray-500 dark:text-gray-400'>
|
<p class="mt-1 text-center text-gray-500 dark:text-gray-400">
|
||||||
Login or create account
|
Login or create account
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<form on:submit|preventDefault={submit}>
|
<form on:submit|preventDefault={submit}>
|
||||||
<div class='w-full mt-4'>
|
<div class="w-full mt-4">
|
||||||
<div class='dark:bg-gray-800 bg-white p-4 rounded-lg'>
|
<div class="dark:bg-gray-800 bg-white p-4 rounded-lg">
|
||||||
<div class='relative bg-inherit w-full'>
|
<div class="relative bg-inherit w-full">
|
||||||
<input
|
<input
|
||||||
id='url'
|
id="url"
|
||||||
bind:value={url_input}
|
bind:value={url_input}
|
||||||
name='email'
|
name="email"
|
||||||
type='url'
|
type="url"
|
||||||
class='w-full peer bg-transparent h-10 rounded-lg text-gray-700 dark:text-white placeholder-transparent ring-2 px-2 ring-gray-500 focus:ring-sky-600 focus:outline-none focus:border-rose-600'
|
class="w-full peer bg-transparent h-10 rounded-lg text-gray-700 dark:text-white placeholder-transparent ring-2 px-2 ring-gray-500 focus:ring-sky-600 focus:outline-none focus:border-rose-600"
|
||||||
placeholder='https://create.kahoot.it/details/something'
|
placeholder="https://create.kahoot.it/details/something"
|
||||||
class:ring-red-700={!url_valid}
|
class:ring-red-700={!url_valid}
|
||||||
class:ring-green-600={url_valid}
|
class:ring-green-600={url_valid}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
for='url'
|
for="url"
|
||||||
class='absolute cursor-text left-0 -top-3 text-sm text-gray-700 dark:text-white bg-inherit mx-1 px-1 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-500 peer-placeholder-shown:top-2 peer-focus:-top-3 peer-focus:text-sky-600 peer-focus:text-sm transition-all'
|
class="absolute cursor-text left-0 -top-3 text-sm text-gray-700 dark:text-white bg-inherit mx-1 px-1 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-500 peer-placeholder-shown:top-2 peer-focus:-top-3 peer-focus:text-sky-600 peer-focus:text-sm transition-all"
|
||||||
>
|
>
|
||||||
URL
|
URL
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='flex items-center justify-between mt-4'>
|
<div class="flex items-center justify-between mt-4">
|
||||||
<span></span>
|
<span />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class='px-4 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded hover:bg-gray-600 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50'
|
class="px-4 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded hover:bg-gray-600 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
disabled={!url_valid}
|
disabled={!url_valid}
|
||||||
type='submit'
|
type="submit"
|
||||||
>
|
>
|
||||||
{#if is_loading}
|
{#if is_loading}
|
||||||
<svg
|
<svg
|
||||||
class='h-4 w-4 animate-spin mx-auto my-20'
|
class="h-4 w-4 animate-spin mx-auto my-20"
|
||||||
viewBox='3 3 18 18'
|
viewBox="3 3 18 18"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
class='fill-black'
|
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'
|
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
|
<path
|
||||||
class='fill-blue-100'
|
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'
|
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>
|
</svg>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -143,19 +142,17 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class='flex items-center justify-center py-4 text-center bg-gray-50 dark:bg-gray-700'
|
class="flex items-center justify-center py-4 text-center bg-gray-50 dark:bg-gray-700"
|
||||||
>
|
>
|
||||||
<span class='text-sm text-gray-600 dark:text-gray-200'
|
<span class="text-sm text-gray-600 dark:text-gray-200">Need help? </span>
|
||||||
>Need help?
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href='/docs/import-from-kahoot'
|
href="/docs/import-from-kahoot"
|
||||||
class='mx-2 text-sm font-bold text-blue-500 dark:text-blue-400 hover:underline'
|
class="mx-2 text-sm font-bold text-blue-500 dark:text-blue-400 hover:underline"
|
||||||
>Visit the docs</a
|
>Visit the docs</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--{/if}-->
|
<!--{/if}-->
|
||||||
|
|||||||
@@ -5,7 +5,10 @@
|
|||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>ClassQuiz - Home</title>
|
<title>ClassQuiz - Home</title>
|
||||||
<meta name="description" content="ClassQuiz is a quiz app like KAHOOT! for students, which is open source and free to use.">
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="ClassQuiz is a quiz app like KAHOOT! for students, which is open source and free to use."
|
||||||
|
/>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
|||||||
+124
-125
@@ -1,4 +1,4 @@
|
|||||||
<script context='module' lang='ts'>
|
<script context="module" lang="ts">
|
||||||
export async function load({ session }) {
|
export async function load({ session }) {
|
||||||
if (!session.authenticated) {
|
if (!session.authenticated) {
|
||||||
return {
|
return {
|
||||||
@@ -14,11 +14,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<script lang="ts">
|
||||||
<title>ClassQuiz - Overview</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<script lang='ts'>
|
|
||||||
import type { QuizData } from '../app';
|
import type { QuizData } from '../app';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
@@ -48,157 +44,160 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deleteQuiz = async (to_delete: string) => {
|
const deleteQuiz = async (to_delete: string) => {
|
||||||
const res = await fetch(`/api/v1/quiz/delete/${to_delete}`, {
|
await fetch(`/api/v1/quiz/delete/${to_delete}`, {
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
});
|
});
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>ClassQuiz - Overview</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
{#await getData()}
|
{#await getData()}
|
||||||
<svg class='h-8 w-8 animate-spin mx-auto my-20' viewBox='3 3 18 18'>
|
<svg class="h-8 w-8 animate-spin mx-auto my-20" viewBox="3 3 18 18">
|
||||||
<path
|
<path
|
||||||
class='fill-black'
|
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'
|
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
|
<path
|
||||||
class='fill-blue-100'
|
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'
|
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>
|
</svg>
|
||||||
{:then quizzes}
|
{:then quizzes}
|
||||||
<div class='flex flex-col w-fit mx-auto'>
|
<div class="flex flex-col w-fit mx-auto">
|
||||||
<!-- <button
|
<!-- <button
|
||||||
class='px-4 py-2 font-medium tracking-wide text-gray-500 whitespace-nowrap dark:text-gray-400 capitalize transition-colors dark:bg-gray-700 duration-200 transform bg-gray-50 rounded-md hover:bg-green-600 focus:outline-none focus:ring focus:ring-blue-300 focus:ring-opacity-80'>
|
class='px-4 py-2 font-medium tracking-wide text-gray-500 whitespace-nowrap dark:text-gray-400 capitalize transition-colors dark:bg-gray-700 duration-200 transform bg-gray-50 rounded-md hover:bg-green-600 focus:outline-none focus:ring focus:ring-blue-300 focus:ring-opacity-80'>
|
||||||
Primary
|
Primary
|
||||||
</button>-->
|
</button>-->
|
||||||
<div class='w-full grid grid-cols-3 gap-2'>
|
<div class="w-full grid grid-cols-3 gap-2">
|
||||||
<a
|
<a
|
||||||
href='/create'
|
href="/create"
|
||||||
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'
|
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"
|
||||||
>Create
|
>Create
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='/import'
|
href="/import"
|
||||||
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'
|
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"
|
||||||
>Import
|
>Import
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href='/api/v1/users/logout'
|
href="/api/v1/users/logout"
|
||||||
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'
|
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"
|
||||||
>Logout
|
>Logout
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class='overflow-x-auto sm:-mx-8 lg:-mx-8'>
|
<div class="overflow-x-auto sm:-mx-8 lg:-mx-8">
|
||||||
<div class='inline-block py-2 min-w-full sm:px-6 lg:px-8'>
|
<div class="inline-block py-2 min-w-full sm:px-6 lg:px-8">
|
||||||
<div class='overflow-hidden shadow-md sm:rounded-lg'>
|
<div class="overflow-hidden shadow-md sm:rounded-lg">
|
||||||
<table class='min-w-full'>
|
<table class="min-w-full">
|
||||||
<thead class='bg-gray-50 dark:bg-gray-700'>
|
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th
|
||||||
scope='col'
|
scope="col"
|
||||||
class='py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400'
|
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
||||||
>
|
>
|
||||||
Title
|
Title
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope='col'
|
scope="col"
|
||||||
class='py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400'
|
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
||||||
>
|
>
|
||||||
Created at
|
Created at
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope='col'
|
scope="col"
|
||||||
class='py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400'
|
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
||||||
>
|
>
|
||||||
Question count
|
Question count
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope='col'
|
scope="col"
|
||||||
class='py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400'
|
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
||||||
>
|
>
|
||||||
Play
|
Play
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope='col'
|
scope="col"
|
||||||
class='py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400'
|
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope='col'
|
scope="col"
|
||||||
class='py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400'
|
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope='col'
|
scope="col"
|
||||||
class='py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400'
|
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
||||||
>
|
>
|
||||||
Public
|
Public
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<!-- Product 1 -->
|
<!-- Product 1 -->
|
||||||
{#each quizzes as quiz}
|
{#each quizzes as quiz}
|
||||||
<tr class='bg-white border-b dark:bg-gray-800 dark:border-gray-700'>
|
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
||||||
<td
|
<td
|
||||||
class='py-4 px-6 text-sm font-medium text-gray-900 whitespace-nowrap dark:text-white'
|
class="py-4 px-6 text-sm font-medium text-gray-900 whitespace-nowrap dark:text-white"
|
||||||
>
|
>
|
||||||
{quiz.title}
|
{quiz.title}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class='py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400'
|
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
|
||||||
>
|
>
|
||||||
{formatDate(quiz.created_at)}
|
{formatDate(quiz.created_at)}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class='py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400'
|
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
|
||||||
>
|
>
|
||||||
{quiz.questions.length}
|
{quiz.questions.length}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class='py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400'
|
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
startGame(quiz.id);
|
startGame(quiz.id);
|
||||||
}}
|
}}
|
||||||
class='border border-green-600'
|
class="border border-green-600"
|
||||||
|
>
|
||||||
|
Start
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
|
||||||
>
|
>
|
||||||
Start
|
<a href="/edit?quiz_id={quiz.id}">Edit</a>
|
||||||
</button>
|
</td>
|
||||||
</td>
|
<td
|
||||||
<td
|
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
|
||||||
class='py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400'
|
>
|
||||||
>
|
<button
|
||||||
<a href='/edit?quiz_id={quiz.id}'>Edit</a>
|
on:click={() => {
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class='py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400'
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
on:click={() => {
|
|
||||||
deleteQuiz(quiz.id);
|
deleteQuiz(quiz.id);
|
||||||
}}
|
}}
|
||||||
class='border border-green-600'
|
class="border border-green-600"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
|
||||||
>
|
>
|
||||||
Delete
|
{#if quiz.public}
|
||||||
</button>
|
✅
|
||||||
</td>
|
{:else}
|
||||||
<td
|
❌
|
||||||
class='py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400'
|
{/if}
|
||||||
>
|
</td>
|
||||||
{#if quiz.public}
|
</tr>
|
||||||
✅
|
{/each}
|
||||||
{:else}
|
|
||||||
❌
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -68,6 +68,7 @@
|
|||||||
});
|
});
|
||||||
// The rest
|
// The rest
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>ClassQuiz - Play</title>
|
<title>ClassQuiz - Play</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "ClassQuiz",
|
"name": "ClassQuiz",
|
||||||
"short_name": "ClassQuiz",
|
"short_name": "ClassQuiz",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-192x192.png",
|
"src": "/android-chrome-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-512x512.png",
|
"src": "/android-chrome-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"theme_color": "#ffffff",
|
"theme_color": "#ffffff",
|
||||||
"background_color": "#ffffff",
|
"background_color": "#ffffff",
|
||||||
"display": "standalone"
|
"display": "standalone"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user