Remove Backups and copies
This commit is contained in:
@@ -1,279 +0,0 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { tinykeys } from '$lib/tinykeys';
|
||||
import { fade } from 'svelte/transition';
|
||||
import MiniSearch from 'minisearch';
|
||||
|
||||
let open = false;
|
||||
let input = '';
|
||||
let bg_text = '';
|
||||
let title_ms: MiniSearch;
|
||||
let command_ms: MiniSearch;
|
||||
let selected: null | number = null;
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
type ActionFunction = (args: string[]) => void;
|
||||
const actions: {
|
||||
id: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
command?: string;
|
||||
args?: string[];
|
||||
action: ActionFunction;
|
||||
}[] = [
|
||||
{
|
||||
id: 0,
|
||||
title: 'Close CommandPalette',
|
||||
description: 'Closes CommandPalette',
|
||||
command: 'close',
|
||||
action: () => close_cp(undefined)
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: 'Create Quiz',
|
||||
description: 'Opens editor to create a new quiz',
|
||||
command: 'newquiz',
|
||||
args: ['title'],
|
||||
action: (args) => window.location.assign(`/create?title=${args.join(' ')}`)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Import a Quiz',
|
||||
description: 'Opens the import page',
|
||||
command: 'import',
|
||||
args: ['url'],
|
||||
action: (args) => window.location.assign(`/import?url=${args?.[0] ?? ''}`)
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Create Quiztivity',
|
||||
description: 'Opens the editor for quiztivities',
|
||||
command: 'newquiztivity',
|
||||
args: ['title'],
|
||||
action: (args) => window.location.assign(`/quiztivity/create?title=${args.join(' ')}`)
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'View Results',
|
||||
description: 'Opens the Results viewer',
|
||||
command: 'results',
|
||||
action: () => window.location.assign('/results')
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Explore Quizzes',
|
||||
description: 'Opens the Explore-page',
|
||||
command: 'explore',
|
||||
action: () => window.location.assign('/explore')
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: 'Dashboard',
|
||||
description: 'Go to Dashboard',
|
||||
command: 'dash',
|
||||
action: () => window.location.assign('/dashboard')
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: 'Docs',
|
||||
description: 'Go to documentation',
|
||||
command: 'docs',
|
||||
action: () => window.location.assign('/docs')
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: 'Settings',
|
||||
description: 'Opens the Settings page',
|
||||
command: 'settings',
|
||||
action: () => window.location.assign('/account/settings')
|
||||
}
|
||||
];
|
||||
let visible_items = actions;
|
||||
|
||||
const toggle_open = (e: KeyboardEvent | undefined) => {
|
||||
e.preventDefault();
|
||||
open = !open;
|
||||
console.log('TOGGLE!');
|
||||
};
|
||||
|
||||
const close_cp = (e: KeyboardEvent | undefined) => {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
open = false;
|
||||
};
|
||||
|
||||
const close_on_outside = (e: Event) => {
|
||||
if (e.target == e.currentTarget) {
|
||||
open = false;
|
||||
}
|
||||
};
|
||||
|
||||
const execute_action = () => {
|
||||
let args = [];
|
||||
const entry = visible_items[selected];
|
||||
if (input.startsWith('/')) {
|
||||
const tokens = input.split(' ');
|
||||
args = tokens.slice(1);
|
||||
}
|
||||
console.log(args);
|
||||
entry.action(args);
|
||||
};
|
||||
|
||||
const search = (term: string) => {
|
||||
if (!command_ms || !title_ms) {
|
||||
return;
|
||||
}
|
||||
if (term === '' || term === '/') {
|
||||
selected = 0;
|
||||
visible_items = actions;
|
||||
bg_text = '';
|
||||
return;
|
||||
}
|
||||
let suggestions;
|
||||
let res;
|
||||
if (term.startsWith('/')) {
|
||||
term = term.substring(1);
|
||||
suggestions = command_ms.autoSuggest(term, { boost: { command: 2 }, prefix: true });
|
||||
res = command_ms.search(term, { boost: { command: 2 }, prefix: true });
|
||||
bg_text = suggestions[0]?.suggestion;
|
||||
bg_text ??= '';
|
||||
bg_text = `/${bg_text}`;
|
||||
} else {
|
||||
suggestions = title_ms.autoSuggest(term, { boost: { command: 2 }, prefix: true });
|
||||
res = title_ms.search(term, { boost: { command: 2 }, prefix: true });
|
||||
bg_text = suggestions[0]?.suggestion;
|
||||
bg_text ??= '';
|
||||
}
|
||||
|
||||
visible_items = [];
|
||||
|
||||
console.log(res);
|
||||
for (const quiz_data of res) {
|
||||
visible_items.push(actions[quiz_data.id]);
|
||||
}
|
||||
visible_items = visible_items;
|
||||
if (visible_items.length === 1) {
|
||||
selected = 0;
|
||||
}
|
||||
if (visible_items.length === 0) {
|
||||
selected = null;
|
||||
}
|
||||
};
|
||||
|
||||
const autocomplete_on_tab = (e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
input = bg_text;
|
||||
};
|
||||
|
||||
const on_arrow_down = (e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
if (visible_items.length < 1) {
|
||||
return;
|
||||
}
|
||||
if (selected + 1 === visible_items.length) {
|
||||
return;
|
||||
}
|
||||
selected += 1;
|
||||
};
|
||||
const on_arrow_up = (e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
if (visible_items.length < 1) {
|
||||
return;
|
||||
}
|
||||
if (selected === 0) {
|
||||
return;
|
||||
}
|
||||
selected -= 1;
|
||||
};
|
||||
|
||||
const on_enter = (e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
if (selected === null) {
|
||||
return;
|
||||
}
|
||||
execute_action();
|
||||
input = '';
|
||||
};
|
||||
|
||||
$: search(input);
|
||||
// $: input = lower_input(input)
|
||||
$: input = input.toLowerCase();
|
||||
onMount(async () => {
|
||||
tinykeys(window, {
|
||||
'$mod+k': toggle_open,
|
||||
Escape: close_cp,
|
||||
Tab: autocomplete_on_tab,
|
||||
ArrowDown: on_arrow_down,
|
||||
ArrowUp: on_arrow_up,
|
||||
Enter: on_enter
|
||||
});
|
||||
title_ms = new MiniSearch<any>({
|
||||
fields: ['title'],
|
||||
storeFields: ['id']
|
||||
});
|
||||
title_ms.addAll(actions);
|
||||
command_ms = new MiniSearch<any>({
|
||||
fields: ['command'],
|
||||
storeFields: ['id']
|
||||
});
|
||||
command_ms.addAll(actions);
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if open}
|
||||
<div
|
||||
class="fixed top-0 left-0 w-screen h-screen flex bg-black bg-opacity-50 z-50"
|
||||
on:click={close_on_outside}
|
||||
transition:fade={{ duration: 60 }}
|
||||
>
|
||||
<div class="m-auto w-1/3 h-2/3 rounded bg-black flex flex-col">
|
||||
<div class="grid grid-cols-1 grid-rows-1 border-b border-b-white">
|
||||
<p
|
||||
class="col-start-1 row-start-1 w-full p-4 outline-none bg-gray-700 rounded-t text-gray-400"
|
||||
>
|
||||
{bg_text}
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
autofocus
|
||||
class="col-start-1 row-start-1 bg-transparent w-full p-4 outline-none bg-gray-700 rounded"
|
||||
bind:value={input}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col p-2 gap-2 overflow-scroll">
|
||||
{#each visible_items as vi, i}
|
||||
<div
|
||||
transition:fade|local={{ duration: 60 }}
|
||||
class="p-2 transition rounded"
|
||||
class:bg-[#B07156]={selected === i}
|
||||
class:bg-gray-700={selected !== i}
|
||||
on:mouseenter={() => (selected = i)}
|
||||
on:mousedown={execute_action}
|
||||
>
|
||||
<div class="flex">
|
||||
<h3 class="text-lg my-auto">{vi.title}</h3>
|
||||
<p
|
||||
class="font-mono my-auto ml-auto h-fit bg-black bg-opacity-50 rounded p-0.5"
|
||||
>
|
||||
/{vi.command}
|
||||
{#if vi.args}
|
||||
{#each vi.args as arg}
|
||||
{<span class="text-indigo-400">{arg}</span
|
||||
>}{/each}
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
<p class="text-sm">{vi.description}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -1,555 +0,0 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { navbarVisible } from '$lib/stores';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import Footer from '$lib/footer.svelte';
|
||||
import WebPOpenGraph from '$lib/assets/landing/opengraph-home.webp';
|
||||
import JpgOpenGraph from '$lib/assets/landing/opengraph-home.jpg';
|
||||
import Newsletter from '$lib/landing/newsletter.svelte';
|
||||
import { fly, fade } from 'svelte/transition';
|
||||
|
||||
/* import LandingPromo from '$lib/landing/landing-promo.svelte';*/
|
||||
|
||||
import FindScreenshot from '$lib/assets/landing_new/find.webp';
|
||||
import ImportScreenshot from '$lib/assets/landing_new/import.webp';
|
||||
import EditScreenshot from '$lib/assets/landing_new/edit.webp';
|
||||
import SelectScreenshot from '$lib/assets/landing_new/select.webp';
|
||||
import ResultScreenshot from '$lib/assets/landing_new/result.webp';
|
||||
import WinnersScreenshot from '$lib/assets/landing_new/winners.webp';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
const { t } = getLocalization();
|
||||
|
||||
navbarVisible.set(true);
|
||||
|
||||
/* interface StatsData {
|
||||
quiz_count: number;
|
||||
user_count: number;
|
||||
}*/
|
||||
|
||||
/* const getStats = async (): Promise<StatsData> => {
|
||||
const response = await fetch('/api/v1/stats/combined');
|
||||
return await response.json();
|
||||
};*/
|
||||
let newsletterModalOpen;
|
||||
onMount(() => {
|
||||
const ls = localStorage.getItem('newsletter');
|
||||
newsletterModalOpen = ls === null;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
enum SelectedCreateThing {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
Create,
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
Find,
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
Import
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
enum SelectedPlayThing {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
Select,
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
Results,
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
Winners
|
||||
}
|
||||
|
||||
let selected_create_thing = SelectedCreateThing.Create;
|
||||
let selected_play_thing = SelectedPlayThing.Select;
|
||||
|
||||
/* <li>No;
|
||||
Tracking < /li>
|
||||
< li > Self - hostable < /li>
|
||||
< li > German;
|
||||
Server < /li>
|
||||
< li > user - friendly < /li>
|
||||
< li > Completely;
|
||||
free < /li>
|
||||
< li > Quiz - results;
|
||||
are;
|
||||
downloadable < /li>;*/
|
||||
|
||||
const classquiz_reasons = [
|
||||
{
|
||||
headline: $t('index_page.no_player_limit'),
|
||||
content: $t('index_page.no_player_limit_content')
|
||||
},
|
||||
{
|
||||
headline: $t('index_page.no_tracking'),
|
||||
content: $t('index_page.no_tracking_content')
|
||||
},
|
||||
{
|
||||
headline: $t('index_page.self_hostable'),
|
||||
content: $t('index_page.self_hostable_content')
|
||||
},
|
||||
{
|
||||
headline: $t('index_page.german_server'),
|
||||
content: $t('index_page.german_server_content')
|
||||
},
|
||||
{
|
||||
headline: $t('index_page.user_friendly'),
|
||||
content: $t('index_page.user_friendly_content')
|
||||
},
|
||||
{
|
||||
headline: $t('index_page.completely_free'),
|
||||
content: $t('index_page.completely_free_content')
|
||||
},
|
||||
{
|
||||
headline: $t('index_page.quiz_results_downloadable'),
|
||||
content: $t('index_page.quiz_results_downloadable_content')
|
||||
},
|
||||
{
|
||||
headline: $t('index_page.multilingual'),
|
||||
content: $t('index_page.multilingual_content')
|
||||
},
|
||||
{
|
||||
headline: $t('index_page.dark_mode'),
|
||||
content: $t('index_page.dark_mode_content')
|
||||
},
|
||||
{
|
||||
headline: $t('index_page.download_quizzes'),
|
||||
content: $t('index_page.download_quizzes_content')
|
||||
},
|
||||
{
|
||||
headline: $t('index_page.community_driven'),
|
||||
content: $t('index_page.community_driven_content')
|
||||
}
|
||||
];
|
||||
let selected_classquiz_reason = 0;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>ClassQuiz - {$t('index_page.meta.title')}</title>
|
||||
<meta name="description" content={$t('index_page.meta.description')} />
|
||||
<title>ClassQuiz - Home</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="ClassQuiz is a quiz-application like KAHOOT!, but open-source. You can create quizzes and play them remotely with other people."
|
||||
/>
|
||||
|
||||
<meta property="og:url" content="https://classquiz.de/" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="ClassQuiz - {$t('index_page.meta.title')}" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="ClassQuiz is a quiz-application like KAHOOT!, but open-source. You can create quizzes and play them remotely with other people."
|
||||
/>
|
||||
<meta property="og:image" content={JpgOpenGraph} />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:domain" content="classquiz.de" />
|
||||
<meta property="twitter:url" content="https://classquiz.de/" />
|
||||
<meta name="twitter:title" content="ClassQuiz - {$t('index_page.meta.title')}" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="ClassQuiz is a quiz-application like KAHOOT!, but open-source. You can create quizzes and play them remotely with other people."
|
||||
/>
|
||||
<meta name="twitter:image" content={WebPOpenGraph} />
|
||||
</svelte:head>
|
||||
|
||||
<!--<div class="min-h-screen flex flex-col">
|
||||
<section class="pb-40">
|
||||
<div class="pt-12 text-center">
|
||||
<h1 class="sm:text-8xl text-6xl mt-6 marck-script">ClassQuiz</h1>
|
||||
<p class="text-xl mt-4">{$t('index_page.slogan')}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="features" class="mt-8">
|
||||
<div class="text-center snap-y">
|
||||
<h1 class="sm:text-6xl text-4xl">{$t('words.features')}</h1>
|
||||
<p class="text-xl pt-4">
|
||||
{$t('index_page.features_description.1')}
|
||||
<br />
|
||||
{$t('index_page.features_description.2')}
|
||||
<br />
|
||||
{$t('index_page.features_description.3')}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="py-8">
|
||||
<h1 class="sm:text-6xl text-4xl text-center break-words">
|
||||
{$t('words.screenshot', { count: 2 })}
|
||||
</h1>
|
||||
<div>
|
||||
<LandingPromo />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h1 class="sm:text-6xl text-4xl text-center">Testimonials</h1>
|
||||
{#await import('$lib/landing/testimonials.svelte') then testimonials}
|
||||
<svelte:component this={testimonials.default} />
|
||||
{/await}
|
||||
</section>
|
||||
|
||||
<section id="stats">
|
||||
<div class="text-center pb-20 pt-10 snap-y">
|
||||
<h1 class="sm:text-6xl text-4xl">{$t('words.stats')}</h1>
|
||||
<p class="text-xl pt-4">
|
||||
{#await getStats() then stats}
|
||||
{$t('index_page.stats', {
|
||||
user_count: stats.user_count,
|
||||
quiz_count: stats.quiz_count
|
||||
})}
|
||||
{/await}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>-->
|
||||
<div class="min-h-screen flex flex-col">
|
||||
<section class="pb-40">
|
||||
<div class="pt-12 text-center">
|
||||
<h1 class="sm:text-8xl text-6xl mt-6 marck-script">ClassQuiz</h1>
|
||||
<p class="text-xl mt-4">{$t('index_page.slogan')}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="text-center text-5xl mb-6">{$t('index_page.how_does_classquiz_work')}</h2>
|
||||
|
||||
<div class="flex justify-center w-full">
|
||||
<h3 class="text-center text-3xl rounded-t-lg bg-opacity-40 bg-white py-2 px-6">
|
||||
{$t('index_page.get_a_quiz')}
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="grid grid-rows-2 lg:grid-rows-1 lg:grid-cols-2 bg-opacity-40 bg-white shadow-lg mb-12 lg:mx-12 mx-4 rounded-lg"
|
||||
>
|
||||
<div>
|
||||
<div class="p-2 rounded-lg">
|
||||
{#if selected_create_thing === SelectedCreateThing.Create}
|
||||
<img
|
||||
class="rounded-lg relative"
|
||||
src={EditScreenshot}
|
||||
in:fade
|
||||
alt="Screenshot of the import-page showing an URL to Kahoot! entered"
|
||||
/>
|
||||
{:else if selected_create_thing === SelectedCreateThing.Find}
|
||||
<img
|
||||
class="rounded-lg relative"
|
||||
src={FindScreenshot}
|
||||
in:fade
|
||||
alt="Screenshot of the search-page showing one found quiz for the term 'Country'"
|
||||
/>
|
||||
{:else if selected_create_thing === SelectedCreateThing.Import}
|
||||
<img
|
||||
class="rounded-lg relative"
|
||||
src={ImportScreenshot}
|
||||
in:fade
|
||||
alt="Screenshot of the import-page showing an URL to Kahoot! entered"
|
||||
/>
|
||||
{:else}
|
||||
<p>Shouldn't happen!</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="lg:border-l lg:border-l-black lg:border-t-0 border-t border-t-black flex lg:flex-col flex-row stretch"
|
||||
>
|
||||
<div
|
||||
class="m-2 rounded-lg p-2 bg-opacity-40 bg-white transition-all cursor-pointer lg:h-full"
|
||||
on:click={() => {
|
||||
selected_create_thing = SelectedCreateThing.Create;
|
||||
}}
|
||||
class:shadow-2xl={selected_create_thing === SelectedCreateThing.Create}
|
||||
class:opacity-70={selected_create_thing !== SelectedCreateThing.Create}
|
||||
>
|
||||
<div
|
||||
class="rounded-lg w-fit p-1 bg-lime-500 hover:bg-lime-400 transition shadow-lg"
|
||||
>
|
||||
<svg
|
||||
aria-label="Pencil-Icon"
|
||||
class="w-8 h-8 text-black"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h5 class="text-xl w-fit dark:text-black">{$t('words.create')}</h5>
|
||||
<p class="dark:text-black">{$t('index_page.create_a_quiz_from_scratch')}</p>
|
||||
</div>
|
||||
<div
|
||||
class="m-2 rounded-lg p-2 bg-opacity-40 bg-white transition-all cursor-pointer lg:h-full"
|
||||
on:click={() => {
|
||||
selected_create_thing = SelectedCreateThing.Find;
|
||||
}}
|
||||
class:shadow-2xl={selected_create_thing === SelectedCreateThing.Find}
|
||||
class:opacity-70={selected_create_thing !== SelectedCreateThing.Find}
|
||||
>
|
||||
<div
|
||||
class="rounded-lg w-fit p-1 bg-lime-500 hover:bg-lime-400 transition shadow-lg"
|
||||
>
|
||||
<svg
|
||||
class="w-8 h-8 text-black"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="magnifying glass-Icon"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h5 class="text-xl dark:text-black">{$t('words.find')}</h5>
|
||||
<p class="dark:text-black">{$t('index_page.find_or_explore')}</p>
|
||||
</div>
|
||||
<!--<div
|
||||
class="m-2 rounded-lg p-2 bg-opacity-40 bg-white transition-all cursor-pointer lg:h-full"
|
||||
on:click={() => {
|
||||
selected_create_thing = SelectedCreateThing.Import;
|
||||
}}
|
||||
class:shadow-2xl={selected_create_thing === SelectedCreateThing.Import}
|
||||
class:opacity-70={selected_create_thing !== SelectedCreateThing.Import}
|
||||
>
|
||||
<div
|
||||
class="rounded-lg w-fit p-1 bg-lime-500 hover:bg-lime-400 transition shadow-lg"
|
||||
>
|
||||
<svg
|
||||
class="w-8 h-8 text-black"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="Cloud with arrow pointing down"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h5 class="text-xl dark:text-black">{$t('words.import')}</h5>
|
||||
<p class="dark:text-black">
|
||||
{$t('index_page.import_quiz_from_kahoot_and_edit')}
|
||||
</p>
|
||||
</div>-->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mt-24">
|
||||
<div class="flex justify-center w-full">
|
||||
<h2 class="text-center text-3xl rounded-t-lg bg-opacity-40 bg-white py-2 px-6">
|
||||
{$t('index_page.play_quiz')}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="grid grid-rows-2 lg:grid-rows-1 lg:grid-cols-2 bg-opacity-40 bg-white shadow-lg mb-12 lg:mx-12 mx-4 rounded-lg"
|
||||
>
|
||||
<div>
|
||||
<div class="p-2 rounded-lg">
|
||||
{#if selected_play_thing === SelectedPlayThing.Select}
|
||||
<img
|
||||
class="rounded-lg relative"
|
||||
src={SelectScreenshot}
|
||||
in:fade
|
||||
alt="Screenshot of the screen where an answer can be selected"
|
||||
/>
|
||||
{:else if selected_play_thing === SelectedPlayThing.Results}
|
||||
<img
|
||||
class="rounded-lg relative"
|
||||
src={ResultScreenshot}
|
||||
in:fade
|
||||
alt="Screenshot of the results with a table showing how many players chose which answer"
|
||||
/>
|
||||
{:else if selected_play_thing === SelectedPlayThing.Winners}
|
||||
<img
|
||||
class="rounded-lg relative"
|
||||
src={WinnersScreenshot}
|
||||
in:fade
|
||||
alt="Screenshot of the import-page showing an URL to Kahoot! entered"
|
||||
/>
|
||||
{:else}
|
||||
<p>Shouldn't happen!</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="lg:border-l lg:border-l-black lg:border-t-0 border-t border-t-black flex lg:flex-col flex-row stretch"
|
||||
>
|
||||
<div
|
||||
class="m-2 rounded-lg p-2 bg-opacity-40 bg-white transition-all cursor-pointer lg:h-full"
|
||||
on:click={() => {
|
||||
selected_play_thing = SelectedPlayThing.Select;
|
||||
}}
|
||||
class:shadow-2xl={selected_play_thing === SelectedPlayThing.Select}
|
||||
class:opacity-70={selected_play_thing !== SelectedPlayThing.Select}
|
||||
>
|
||||
<div
|
||||
class="rounded-lg bg-emerald-300 w-fit p-1 bg-lime-500 hover:bg-lime-400 transition shadow-lg"
|
||||
>
|
||||
<svg
|
||||
aria-label="Mouse-Click icon"
|
||||
class="w-8 h-8 text-black"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h5 class="text-xl w-fit dark:text-black">{$t('index_page.select_answer')}</h5>
|
||||
<p class="dark:text-black">{$t('index_page.choose_answer_wisely')}</p>
|
||||
</div>
|
||||
<div
|
||||
class="m-2 rounded-lg p-2 bg-opacity-40 bg-white transition-all cursor-pointer lg:h-full"
|
||||
on:click={() => {
|
||||
selected_play_thing = SelectedPlayThing.Results;
|
||||
}}
|
||||
class:shadow-2xl={selected_play_thing === SelectedPlayThing.Results}
|
||||
class:opacity-70={selected_play_thing !== SelectedPlayThing.Results}
|
||||
>
|
||||
<div
|
||||
class="rounded-lg bg-emerald-300 w-fit p-1 bg-lime-500 hover:bg-lime-400 transition shadow-lg"
|
||||
>
|
||||
<svg
|
||||
aria-label="context-menu icon"
|
||||
class="w-8 h-8 text-black"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M4 6h16M4 10h16M4 14h16M4 18h16"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h5 class="text-xl dark:text-black">{$t('index_page.view_results')}</h5>
|
||||
<p class="dark:text-black">{$t('index_page.check_if_chosen_wisely')}</p>
|
||||
</div>
|
||||
<div
|
||||
class="m-2 rounded-lg p-2 bg-opacity-40 bg-white transition-all cursor-pointer lg:h-full"
|
||||
on:click={() => {
|
||||
selected_play_thing = SelectedPlayThing.Winners;
|
||||
}}
|
||||
class:shadow-2xl={selected_play_thing === SelectedPlayThing.Winners}
|
||||
class:opacity-70={selected_play_thing !== SelectedPlayThing.Winners}
|
||||
>
|
||||
<div
|
||||
class="rounded-lg bg-emerald-300 w-fit p-1 bg-lime-500 hover:bg-lime-400 transition shadow-lg"
|
||||
>
|
||||
<svg
|
||||
aria-label="sparkling stars-icon"
|
||||
class="w-8 h-8 text-black"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h5 class="text-xl dark:text-black">{$t('index_page.list_winners')}</h5>
|
||||
<p class="dark:text-black">{$t('index_page.get_ranking_and_winners')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mt-24">
|
||||
<div class="flex justify-center w-full">
|
||||
<h2 class="text-center text-3xl rounded-t-lg bg-opacity-40 bg-white py-2 px-6">
|
||||
{$t('index_page.why_classquiz')}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="grid grid-rows-2 lg:grid-rows-1 lg:grid-cols-2 bg-opacity-40 bg-white shadow-lg mb-12 lg:mx-12 mx-4 rounded-lg"
|
||||
>
|
||||
<div>
|
||||
<div class="p-12 rounded-lg flex justify-center items-center h-full">
|
||||
<p class="dark:text-black">
|
||||
{classquiz_reasons[selected_classquiz_reason].content}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="lg:border-l lg:border-l-black lg:border-t-0 border-t border-t-black flex lg:flex-col flex-row stretch overflow-x-auto why-classquiz"
|
||||
>
|
||||
{#each classquiz_reasons as reason, index}
|
||||
<div
|
||||
class="m-2 rounded-lg p-2 bg-opacity-40 bg-white transition-all cursor-pointer lg:h-full"
|
||||
on:click={() => {
|
||||
selected_classquiz_reason = index;
|
||||
}}
|
||||
class:shadow-2xl={selected_classquiz_reason === index}
|
||||
class:opacity-70={selected_classquiz_reason !== index}
|
||||
>
|
||||
<h5 class="text-xl dark:text-black">{reason.headline}</h5>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{#if newsletterModalOpen}
|
||||
<div
|
||||
class="fixed bottom-8 right-5 bg-white rounded-lg h-fit w-11/12 ml-5 lg:w-2/12 z-50 p-2 bg-white dark:bg-gray-700"
|
||||
transition:fly
|
||||
>
|
||||
<Newsletter bind:open={newsletterModalOpen} />
|
||||
</div>
|
||||
{/if}
|
||||
<Footer />
|
||||
|
||||
<style>
|
||||
.why-classquiz::-webkit-scrollbar {
|
||||
height: 0.8rem;
|
||||
margin-bottom: 5rem;
|
||||
}
|
||||
|
||||
.why-classquiz::-webkit-scrollbar-track {
|
||||
box-shadow: inset 0 0 10px 10px transparent;
|
||||
border: solid 3px transparent;
|
||||
}
|
||||
|
||||
.why-classquiz::-webkit-scrollbar-thumb {
|
||||
box-shadow: inset 0 0 10px 10px #374151;
|
||||
border: solid 3px transparent;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.why-classquiz::-webkit-scrollbar-thumb:hover {
|
||||
box-shadow: inset 0 0 10px 10px #555;
|
||||
border: solid 3px transparent;
|
||||
}
|
||||
</style>
|
||||
@@ -1,141 +0,0 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
<script lang="ts">
|
||||
export let session_data = {};
|
||||
export let step;
|
||||
export let selected_method;
|
||||
|
||||
let available_methods;
|
||||
|
||||
const set_available_methods = (step_var: number) => {
|
||||
if (step_var === 1) {
|
||||
available_methods = session_data.step_1;
|
||||
} else if (step_var === 2) {
|
||||
available_methods = session_data.step_2;
|
||||
}
|
||||
};
|
||||
|
||||
$: set_available_methods(step);
|
||||
</script>
|
||||
|
||||
<div class="px-6 py-4">
|
||||
<h2 class="text-3xl font-bold text-center text-gray-700 dark:text-white">ClassQuiz</h2>
|
||||
|
||||
<div class="w-full mt-4">
|
||||
<div class="dark:bg-gray-800 bg-white p-4 rounded-lg">
|
||||
<ul class="flex flex-col gap-4">
|
||||
{#if available_methods.includes('PASSKEY')}
|
||||
<div
|
||||
class="flex flex-row bg-gray-100 dark:bg-gray-700 rounded-lg p-2 hover:cursor-pointer hover:bg-gray-200 transition"
|
||||
on:click={() => {
|
||||
selected_method = 'PASSKEY';
|
||||
}}
|
||||
on:keyup={() => {
|
||||
selected_method = 'PASSKEY';
|
||||
}}
|
||||
>
|
||||
<!-- heroicons/key -->
|
||||
<svg
|
||||
class="w-12 h-12"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"
|
||||
/>
|
||||
</svg>
|
||||
<div class="ml-2">
|
||||
<p>Key</p>
|
||||
<p class="text-sm">Authenticate using a security key</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if available_methods.includes('PASSWORD')}
|
||||
<div
|
||||
class="flex flex-row bg-gray-100 dark:bg-gray-700 rounded-lg p-2 hover:cursor-pointer hover:bg-gray-200 transition"
|
||||
on:click={() => {
|
||||
selected_method = 'PASSWORD';
|
||||
}}
|
||||
on:keyup={() => {
|
||||
selected_method = 'PASSWORD';
|
||||
}}
|
||||
>
|
||||
<!-- iconoir/password-cursor -->
|
||||
<svg
|
||||
class="w-12 h-12 dark:text-white"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M21 13V8a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h7"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.03"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M20.879 16.917c.494.304.463 1.043-.045 1.101l-2.567.291-1.151 2.312c-.228.459-.933.234-1.05-.334l-1.255-6.116c-.099-.48.333-.782.75-.525l5.318 3.271z"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.03"
|
||||
/>
|
||||
<path
|
||||
d="M12 11.01l.01-.011M16 11.01l.01-.011M8 11.01l.01-.011"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.03"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
<div class="ml-2">
|
||||
<p>Password</p>
|
||||
<p class="text-sm">Authenticate using a Password</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if available_methods.includes('TOTP')}
|
||||
<div
|
||||
class="flex flex-row bg-gray-100 rounded-lg p-2 hover:cursor-pointer hover:bg-gray-200 transition"
|
||||
on:click={() => {
|
||||
selected_method = 'TOTP';
|
||||
}}
|
||||
on:keyup={() => {
|
||||
selected_method = 'TOTP';
|
||||
}}
|
||||
>
|
||||
<!-- heroicons/clock -->
|
||||
<svg
|
||||
class="w-12 h-12"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<div class="ml-2">
|
||||
<p>Totp</p>
|
||||
<p class="text-sm">Authenticate using a one-time password</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,113 +0,0 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import OAuthBlock from './oauth_block.svelte';
|
||||
|
||||
export let session_data = {};
|
||||
export let step;
|
||||
|
||||
const { t } = getLocalization();
|
||||
let email = '';
|
||||
let emailEmpty = true;
|
||||
let isSubmitting = false;
|
||||
|
||||
$: emailEmpty = email === '';
|
||||
const start_login = async (): Promise<void> => {
|
||||
if (emailEmpty) {
|
||||
return;
|
||||
}
|
||||
isSubmitting = true;
|
||||
// alert("Alert message");
|
||||
const res = await fetch('/api/v1/login/start', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ email: email })
|
||||
});
|
||||
session_data = await res.json();
|
||||
step = 1;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="px-6 py-4">
|
||||
<h2 class="text-3xl font-bold text-center text-gray-700 dark:text-white">ClassQuiz</h2>
|
||||
|
||||
<h3 class="mt-1 text-xl font-medium text-center text-gray-600 dark:text-gray-200">
|
||||
{$t('login_page.welcome_back')}
|
||||
</h3>
|
||||
|
||||
<p class="mt-1 text-center text-gray-500 dark:text-gray-400">
|
||||
{$t('login_page.login_or_create_account')}
|
||||
</p>
|
||||
|
||||
<form on:submit|preventDefault={start_login}>
|
||||
<div class="w-full mt-4">
|
||||
<div class="dark:bg-gray-800 bg-white p-4 rounded-lg">
|
||||
<div class="relative bg-inherit w-full">
|
||||
<input
|
||||
id="email"
|
||||
bind:value={email}
|
||||
name="email"
|
||||
type="text"
|
||||
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={$t('login_page.email_or_username')}
|
||||
autocomplete="email"
|
||||
/>
|
||||
<label
|
||||
for="email"
|
||||
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"
|
||||
>
|
||||
{$t('login_page.email_or_username')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-4">
|
||||
<a
|
||||
href="/account/reset-password"
|
||||
class="text-sm text-gray-600 dark:text-gray-200 hover:text-gray-500"
|
||||
>{$t('register_page.forgot_password?')}</a
|
||||
>
|
||||
|
||||
<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"
|
||||
disabled={emailEmpty}
|
||||
type="submit"
|
||||
>
|
||||
{#if isSubmitting}
|
||||
<svg class="h-4 w-4 animate-spin mx-auto" 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>
|
||||
{:else}
|
||||
{$t('words.continue')}
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
<OAuthBlock />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div 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"
|
||||
>{$t('login_page.already_have_account')}
|
||||
</span>
|
||||
|
||||
<a
|
||||
href="/account/register"
|
||||
class="mx-2 text-sm font-bold text-blue-500 dark:text-blue-400 hover:underline"
|
||||
>{$t('words.register')}</a
|
||||
>
|
||||
</div>
|
||||
Reference in New Issue
Block a user