Compare commits

...

4 commits

Author SHA1 Message Date
Théo
e6cd69f0ad feat: merge to chapters > id > puzzle > id 2023-09-04 13:22:30 +02:00
Théo
8702c8e790 feat: plausible 2023-09-04 13:22:14 +02:00
Théo
9a0c80f246 fix: clear cookie on incorrect session 2023-09-04 13:21:58 +02:00
Théo
6205d0c26d misc: renamed global.css to app.css 2023-09-04 13:21:45 +02:00
22 changed files with 529 additions and 47 deletions

View file

@ -19,6 +19,7 @@ export const handle = (async ({ event, resolve }) => {
event.locals.user = user; event.locals.user = user;
} else { } else {
event.locals.user = undefined; event.locals.user = undefined;
event.cookies.delete('session');
} }
} }

View file

@ -0,0 +1,39 @@
<script lang="ts">
import { cn } from '$lib/Utils';
import type { Chapter } from '$lib/types';
import ChevronRight from './Icons/ChevronRight.svelte';
export let chapter: Chapter;
</script>
<li
class={cn(
'font-code group relative flex h-full w-full rounded-md bg-primary-700 transition-colors duration-150 hover:bg-primary-600'
// 'cursor-not-allowed': !isStarted(chapter)
)}
>
<a class="flex h-full w-full items-center gap-4 p-4" href="/dashboard/chapters/{chapter.id}">
<div class="flex w-full flex-col justify-between gap-2 sm:flex-row">
<h2 class="text-base font-semibold">
{chapter.name}
</h2>
<!-- <div class="flex items-center gap-x-6">
{#if puzzle.tags?.length}
<div class="flex gap-x-2 text-sm">
{#each puzzle.tags as tag}
<span
class="inline-block rounded-md bg-primary-800 px-2 py-1 text-highlight-secondary"
>
{tag.name}
</span>
{/each}
</div>
{/if}
</div> -->
</div>
<span class="translate-x-0 transform-gpu duration-300 group-hover:translate-x-2">
<ChevronRight />
</span>
</a>
</li>

View file

@ -0,0 +1,19 @@
<script lang="ts">
import { cn } from '$lib';
let className: string | undefined | null = undefined;
export { className as class };
</script>
<svg
class={cn(className)}
xmlns="http://www.w3.org/2000/svg"
height="24"
width="24"
viewBox="0 0 640 512"
fill="currentColor"
><path
d="M524.531,69.836a1.5,1.5,0,0,0-.764-.7A485.065,485.065,0,0,0,404.081,32.03a1.816,1.816,0,0,0-1.923.91,337.461,337.461,0,0,0-14.9,30.6,447.848,447.848,0,0,0-134.426,0,309.541,309.541,0,0,0-15.135-30.6,1.89,1.89,0,0,0-1.924-.91A483.689,483.689,0,0,0,116.085,69.137a1.712,1.712,0,0,0-.788.676C39.068,183.651,18.186,294.69,28.43,404.354a2.016,2.016,0,0,0,.765,1.375A487.666,487.666,0,0,0,176.02,479.918a1.9,1.9,0,0,0,2.063-.676A348.2,348.2,0,0,0,208.12,430.4a1.86,1.86,0,0,0-1.019-2.588,321.173,321.173,0,0,1-45.868-21.853,1.885,1.885,0,0,1-.185-3.126c3.082-2.309,6.166-4.711,9.109-7.137a1.819,1.819,0,0,1,1.9-.256c96.229,43.917,200.41,43.917,295.5,0a1.812,1.812,0,0,1,1.924.233c2.944,2.426,6.027,4.851,9.132,7.16a1.884,1.884,0,0,1-.162,3.126,301.407,301.407,0,0,1-45.89,21.83,1.875,1.875,0,0,0-1,2.611,391.055,391.055,0,0,0,30.014,48.815,1.864,1.864,0,0,0,2.063.7A486.048,486.048,0,0,0,610.7,405.729a1.882,1.882,0,0,0,.765-1.352C623.729,277.594,590.933,167.465,524.531,69.836ZM222.491,337.58c-28.972,0-52.844-26.587-52.844-59.239S193.056,219.1,222.491,219.1c29.665,0,53.306,26.82,52.843,59.239C275.334,310.993,251.924,337.58,222.491,337.58Zm195.38,0c-28.971,0-52.843-26.587-52.843-59.239S388.437,219.1,417.871,219.1c29.667,0,53.307,26.82,52.844,59.239C470.715,310.993,447.538,337.58,417.871,337.58Z"
/></svg
>

View file

@ -0,0 +1,23 @@
<script lang="ts">
import { cn } from '$lib';
let className: string | undefined | null = undefined;
export { className as class };
</script>
<svg
class={cn(className)}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path
d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"
/><path d="M9 18c-4.51 2-5-2-7-2" /></svg
>

View file

@ -0,0 +1,23 @@
<script lang="ts">
import { cn } from '$lib';
let className: string | undefined | null = undefined;
export { className as class };
</script>
<svg
class={cn(className)}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><circle cx="12" cy="12" r="10" /><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" /><path
d="M12 17h.01"
/></svg
>

View file

@ -12,7 +12,6 @@
width="24" width="24"
height="24" height="24"
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="none"
stroke="currentColor" stroke="currentColor"
stroke-width="2" stroke-width="2"
stroke-linecap="round" stroke-linecap="round"

View file

@ -0,0 +1,25 @@
<script lang="ts">
import { cn } from '$lib';
let className: string | undefined | null = undefined;
export { className as class };
</script>
<svg
class={cn(className)}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path d="M22 10.5V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h12.5" /><path
d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"
/><path d="M18 15.28c.2-.4.5-.8.9-1a2.1 2.1 0 0 1 2.6.4c.3.4.5.8.5 1.3 0 1.3-2 2-2 2" /><path
d="M20 22v.01"
/></svg
>

View file

@ -7,7 +7,10 @@
export let isOpen: boolean; export let isOpen: boolean;
$: user = $page.data.user; $: user = $page.data.user;
$: segment = $page.url.pathname.slice(1).replaceAll('/', ' / '); $: segments = $page.url.pathname.slice(1).split('/');
$: breadcrumb = segments.map((segment, index) => {
return { name: segment, href: '/' + segments.slice(0, index + 1).join('/') };
}) as { name: string; href: string }[];
function handleToggle() { function handleToggle() {
isOpen = !isOpen; isOpen = !isOpen;
@ -27,11 +30,24 @@
{/if} {/if}
</button> </button>
</div> </div>
{#if !isOpen && segment} {#if !isOpen && segments.length}
<div class="flex items-center justify-center capitalize text-highlight-secondary">
{#each breadcrumb as segment}
{@const last = segment === breadcrumb[breadcrumb.length - 1]}
<a class="hover:underline hover:text-primary" href={segment.href}>
{segment.name}
</a>
{#if !last}
<span class="mx-1 text-highlight-secondary">/</span>
{/if}
{/each}
</div>
{/if}
<!-- {#if !isOpen && segment}
<div class="flex items-center justify-center capitalize text-highlight-secondary"> <div class="flex items-center justify-center capitalize text-highlight-secondary">
{segment} {segment}
</div> </div>
{/if} {/if} -->
</div> </div>
<div class="flex flex-row items-center gap-2"> <div class="flex flex-row items-center gap-2">
<Avatar /> <Avatar />

View file

@ -1,10 +1,13 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores';
import { cn } from '$lib/Utils'; import { cn } from '$lib/Utils';
import type { Puzzle } from '$lib/types'; import type { Puzzle } from '$lib/types';
import ChevronRight from './Icons/ChevronRight.svelte'; import ChevronRight from './Icons/ChevronRight.svelte';
export let puzzle: Puzzle; export let puzzle: Puzzle;
const chapterId = $page.params.chapterId;
$: tags = puzzle.tags?.filter((tag) => !['easy', 'medium', 'hard'].includes(tag.name)); $: tags = puzzle.tags?.filter((tag) => !['easy', 'medium', 'hard'].includes(tag.name));
</script> </script>
@ -20,7 +23,10 @@
} }
)} )}
> >
<a class="flex h-full w-full items-center gap-4 p-4" href="/dashboard/puzzles/{puzzle.id}"> <a
class="flex h-full w-full items-center gap-4 p-4"
href="/dashboard/chapters/{chapterId}/puzzle/{puzzle.id}"
>
<div class="flex w-full flex-col justify-between gap-2 sm:flex-row"> <div class="flex w-full flex-col justify-between gap-2 sm:flex-row">
<h2 class="text-base font-semibold"> <h2 class="text-base font-semibold">
{puzzle.name} {puzzle.name}

View file

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores'; import { page } from '$app/stores';
import { cn } from '$lib/Utils'; import { cn } from '$lib/Utils';
import Badge from '$lib/components/Icons/Badge.svelte'; import Badge from '$lib/components/Icons/Badge.svelte';
@ -7,6 +8,10 @@
import Dashboard from '$lib/components/Icons/Dashboard.svelte'; import Dashboard from '$lib/components/Icons/Dashboard.svelte';
import Leaderboard from '$lib/components/Icons/Leaderboard.svelte'; import Leaderboard from '$lib/components/Icons/Leaderboard.svelte';
import Settings from '$lib/components/Icons/Settings.svelte'; import Settings from '$lib/components/Icons/Settings.svelte';
import Discord from './Icons/Discord.svelte';
import Git from './Icons/Git.svelte';
import Help from './Icons/Help.svelte';
import Mail from './Icons/Mail.svelte';
$: path = $page.url.pathname; $: path = $page.url.pathname;
$: isActive = (slug: string) => path === slug; $: isActive = (slug: string) => path === slug;
@ -25,8 +30,8 @@
icon: Leaderboard icon: Leaderboard
}, },
{ {
name: 'Puzzles', name: 'Chapitres',
slug: '/dashboard/puzzles', slug: '/dashboard/chapters',
icon: Code icon: Code
}, },
{ {
@ -83,7 +88,13 @@
)} )}
> >
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<svelte:component this={item.icon} /> <svelte:component
this={item.icon}
class={cn({
'stroke-highlight-secondary transition-colors duration-150 group-hover:stroke-primary-0':
!isActive(item.slug)
})}
/>
<span <span
class={cn('hidden lg:block', { class={cn('hidden lg:block', {
'block sm:hidden': isOpen, 'block sm:hidden': isOpen,
@ -106,28 +117,108 @@
<div class="px-4 pt-4"> <div class="px-4 pt-4">
<ul class="flex flex-col gap-4"> <ul class="flex flex-col gap-4">
<li> <li>
<!-- <NavItem <a
item={{ on:click={() => {
name: 'Discord', isOpen = false;
slug: 'https://discord.gg/72vuHcwUkE', }}
icon: Icons.Discord, href="/dashboard/help"
disabled: false class="group flex justify-center rounded-md px-3 py-3 text-sm transition-colors duration-150 hover:bg-primary-700 lg:justify-start"
}} >
isOpen={isOpen} <div class="flex items-center gap-2">
onClick={toggle} <Help
/> --> class="stroke-highlight-secondary transition-colors duration-150 group-hover:stroke-primary-0"
/>
<span
class={cn(
'hidden text-highlight-secondary transition-colors duration-150 group-hover:text-primary lg:block',
{
'block sm:hidden': isOpen,
hidden: !isOpen
}
)}
>
Aide
</span>
</div>
</a>
</li> </li>
<li> <li>
<!-- <NavItem <a
item={{ on:click={() => {
name: 'Git', isOpen = false;
slug: 'https://git.peerat.dev/Peer-at-Code', }}
icon: Github, href="mailto:cyberbottle@peerat.dev"
disabled: false class="group flex justify-center rounded-md px-3 py-3 text-sm transition-colors duration-150 hover:bg-primary-700 lg:justify-start"
}} >
isOpen={isOpen} <div class="flex items-center gap-2">
onClick={toggle} <Mail
/> --> class="stroke-highlight-secondary transition-colors duration-150 group-hover:stroke-primary-0"
/>
<span
class={cn(
'hidden text-highlight-secondary transition-colors duration-150 group-hover:text-primary lg:block',
{
'block sm:hidden': isOpen,
hidden: !isOpen
}
)}
>
Mail
</span>
</div>
</a>
</li>
<li>
<a
on:click={() => {
isOpen = false;
}}
href="//discord.gg/72vuHcwUkE"
class="group flex justify-center rounded-md px-3 py-3 text-sm transition-colors duration-150 hover:bg-primary-700 lg:justify-start"
>
<div class="flex items-center gap-2">
<Discord
class="fill-highlight-secondary transition-colors duration-150 group-hover:fill-primary-0"
/>
<span
class={cn(
'hidden text-highlight-secondary transition-colors duration-150 group-hover:text-primary lg:block',
{
'block sm:hidden': isOpen,
hidden: !isOpen
}
)}
>
Discord
</span>
</div>
</a>
</li>
<li>
<a
on:click={() => {
isOpen = false;
}}
href="//git.peerat.dev"
class="group flex justify-center rounded-md px-3 py-3 text-sm transition-colors duration-150 hover:bg-primary-700 lg:justify-start"
>
<div class="flex items-center gap-2">
<Git
class="stroke-highlight-secondary transition-colors duration-150 group-hover:stroke-primary-0"
/>
<span
class={cn(
'hidden text-highlight-secondary transition-colors duration-150 group-hover:text-primary lg:block',
{
'block sm:hidden': isOpen,
hidden: !isOpen
}
)}
>
Git
</span>
</div>
</a>
</li> </li>
</ul> </ul>
</div> </div>

View file

@ -0,0 +1,17 @@
import { browser } from '$app/environment';
import { writable } from 'svelte/store';
const defaultValue = true;
const initialValue = browser
? window.localStorage.getItem('plausible_ignore') === 'true'
: defaultValue;
const plausible = writable<boolean>(initialValue);
plausible.subscribe((value) => {
if (browser) {
window.localStorage.setItem('plausible_ignore', value ? 'true' : 'false');
}
});
export default plausible;

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import '../global.css'; import '../app.css';
import { page } from '$app/stores'; import { page } from '$app/stores';
@ -33,6 +33,8 @@
property="twitter:description" property="twitter:description"
content="Apprendre la programmation et la cybersécurité en s'amusant." content="Apprendre la programmation et la cybersécurité en s'amusant."
/> />
<script defer data-domain="app.peerat.dev" src="https://plosibl.peerat.dev/js/script.js"></script>
</svelte:head> </svelte:head>
<slot /> <slot />

View file

@ -0,0 +1,29 @@
import { API_URL } from '$env/static/private';
import type { PageServerLoad } from './$types';
import type { Chapter } from '$lib/types';
export const load = (async ({ parent, fetch, cookies }) => {
await parent();
const session = cookies.get('session');
const res = await fetch(`${API_URL}/chapters`, {
headers: {
Authorization: `Bearer ${session}`
}
});
if (!res.ok) {
return {
chapters: []
};
}
const chapters = (await res.json()) as Chapter[];
return {
chapters
};
}) satisfies PageServerLoad;

View file

@ -0,0 +1,22 @@
<script lang="ts">
import Chapter from '$lib/components/Chapter.svelte';
export let data;
$: chapters = data.chapters;
</script>
<section class="flex w-full flex-col space-y-6">
<header class="sticky flex items-center justify-between">
<div class="flex flex-col">
<h1 class="text-xl font-semibold">Chaptires</h1>
<p class="text-muted">
Les chapitres sont les différentes parties du jeu. Chaque chapitre est composé de plusieurs
puzzles.
</p>
</div>
</header>
{#each chapters as chapter}
<Chapter {chapter} />
{/each}
</section>

View file

@ -0,0 +1,28 @@
import { API_URL } from '$env/static/private';
import type { PageServerLoad } from './$types';
import type { Chapter } from '$lib/types';
import { redirect } from '@sveltejs/kit';
export const load = (async ({ parent, fetch, cookies, params: { chapterId } }) => {
await parent();
const session = cookies.get('session');
const res = await fetch(`${API_URL}/chapter/${chapterId}`, {
headers: {
Authorization: `Bearer ${session}`
}
});
if (!res.ok) {
throw redirect(302, '/dashboard/chapters');
}
const chapter = (await res.json()) as Chapter;
return {
chapter
};
}) satisfies PageServerLoad;

View file

@ -0,0 +1,97 @@
<script lang="ts">
import Puzzle from '$lib/components/Puzzle.svelte';
export let data;
$: chapter = data.chapter;
</script>
<section class="flex w-full flex-col space-y-6">
<div class="flex flex-col">
<div class="flex flex-col justify-between md:flex-row md:items-center">
<div class="flex items-center gap-x-2">
<h1 class="text-xl font-semibold">{chapter.name}</h1>
<!-- {!isInEventGroup(chapter) && isBeforeStart(chapter) && (
<Dialog
key={chapter.id}
title={chapter.name}
open={isOpen[chapter.id]}
onOpenChange={() => handleClick(chapter.id)}
trigger={
<button class="flex items-center gap-x-2 text-sm font-semibold text-muted hover:text-brand">
{/* <Icon name="group-line" /> */}
Rejoindre un groupe
</button>
}
class="right-96 p-4"
>
<GroupForm chapter={chapter} token={token} />
</Dialog>
)} -->
</div>
<div class="flex flex-col">
{#if chapter.startDate && chapter.endDate}
<div class="flex items-center justify-start gap-x-2 md:justify-end">
<!-- {/* <Icon name="calendar-line" class="text-sm text-muted" /> */} -->
<span class="text-sm text-muted">
{new Date(chapter.startDate).toLocaleDateString('fr-FR', {
day: 'numeric',
month: 'long',
year: 'numeric',
hour: 'numeric',
minute: 'numeric'
})}{' '}
-{' '}
{new Date(chapter.endDate).toLocaleDateString('fr-FR', {
day: 'numeric',
month: 'long',
year: 'numeric',
hour: 'numeric',
minute: 'numeric'
})}
</span>
</div>
{:else}
<div class="h-1 w-1/4 rounded-lg bg-gray-200">
<div class="h-1 w-1/2 rounded-lg bg-gradient-to-tl from-brand to-brand-accent" />
</div>
{/if}
<div class="mt-1 flex justify-start gap-x-2">
<!-- {isInEventGroup(chapter) && (
<>
<FilterDifficulty
chapters={data}
chapter={chapter}
filter={filterDifficulty}
setFilter={setFilterDifficulty}
setFilterChapter={setFilterChapter}
/>
<FilterTags
chapters={data}
chapter={chapter}
filter={filterTags}
setFilter={setFilterTags}
setFilterChapter={setFilterChapter}
/>
</>
)} -->
</div>
</div>
</div>
<ul class="mt-4 flex flex-col space-y-4">
{#each chapter.puzzles as puzzle}
<Puzzle {puzzle} />
{/each}
</ul>
<!-- {isInEventGroup(chapter) && (
<ul class="mt-4 flex flex-col space-y-4">
{filteredData &&
filteredData
.sort((a, b) => a.scoreMax - b.scoreMax)
.map((puzzle) => (
<PuzzleProp key={puzzle.id} puzzle={puzzle} chapter={chapter} />
))}
</ul>
)} -->
</div>
</section>

View file

@ -0,0 +1,8 @@
import { API_URL } from '$env/static/private';
import { redirect, type Actions } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
export const load = (async ({ parent, fetch, cookies, params: { chapterId } }) => {
await parent();
throw redirect(303, chapterId ? `/dashboard/chapters/${chapterId}` : `/dashboard/chapters`);
}) satisfies PageServerLoad;

View file

@ -1,18 +1,35 @@
import type { PageServerLoad } from './$types';
import { API_URL } from '$env/static/private'; import { API_URL } from '$env/static/private';
import { error, redirect, type Actions } from '@sveltejs/kit'; import { error, redirect, type Actions } from '@sveltejs/kit';
import type Puzzle from '$lib/components/Puzzle.svelte'; import type Puzzle from '$lib/components/Puzzle.svelte';
import type { PageServerLoad } from './$types';
export const load = (async ({ parent, fetch, cookies, params: { id } }) => { export const load = (async ({ parent, fetch, cookies, params: { chapterId, puzzleId } }) => {
await parent(); await parent();
const session = cookies.get('session'); const session = cookies.get('session');
if (isNaN(parseInt(id))) { if (isNaN(parseInt(puzzleId))) {
throw redirect(303, '/dashboard/puzzles'); throw redirect(303, `/dashboard/chapters/${chapterId}`);
} }
const res = await fetch(`${API_URL}/puzzle/${id}`, { // check if puzzle is from the chapter
let res = await fetch(`${API_URL}/chapter/${chapterId}`, {
headers: {
Authorization: `Bearer ${session}`
}
});
if (!res.ok) {
throw redirect(303, `/dashboard/chapters`);
}
const { puzzles } = (await res.json()) as { puzzles: Puzzle[] };
if (!puzzles.some((puzzle) => puzzle.id === parseInt(puzzleId))) {
throw redirect(303, `/dashboard/chapters/${chapterId}`);
}
res = await fetch(`${API_URL}/puzzle/${puzzleId}`, {
headers: { headers: {
Authorization: `Bearer ${session}` Authorization: `Bearer ${session}`
} }

View file

@ -24,6 +24,8 @@
)} --> )} -->
<ul class="flex flex-col space-y-2"> <ul class="flex flex-col space-y-2">
{#each groups as group} {#each groups as group}
{@const players = group.players.sort((a, b) => b.score - a.score)}
{@const last = players[players.length - 1]}
<li class="flex justify-between space-x-2"> <li class="flex justify-between space-x-2">
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<span <span
@ -35,15 +37,10 @@
<div class="flex flex-col gap-x-2 sm:flex-row sm:items-center"> <div class="flex flex-col gap-x-2 sm:flex-row sm:items-center">
<span class="text-lg">{group.name}</span> <span class="text-lg">{group.name}</span>
<span class="text-sm text-highlight-secondary"> <span class="text-sm text-highlight-secondary">
<!-- {group.players && group.players.length > 1 {#if players.length > 1}
? group.players {#each players as player}
.map((player) => player.pseudo || 'Anonyme') {player.pseudo || 'Anonyme'}{#if player !== last}, {/if}
.sort((a, b) => a.localeCompare(b)) {' '}
.join(', ')
: group.players[0].pseudo} -->
{#if group.players.length > 1}
{#each group.players as player}
{player.pseudo || 'Anonyme'} {' '}
{/each} {/each}
{:else} {:else}
{group.players[0].pseudo} {group.players[0].pseudo}
@ -54,8 +51,12 @@
</div> </div>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<div class="flex flex-col"> <div class="flex flex-col">
<span class="text-sm font-semibold">Essai{group.players.reduce((a, b) => a + b.tries, 0) || 0 ? 's' : ''}</span> <span class="text-sm font-semibold"
<span class="text-lg text-highlight-secondary">{group.players.reduce((a, b) => a + b.tries, 0) || 0}</span> >Essai{group.players.reduce((a, b) => a + b.tries, 0) || 0 ? 's' : ''}</span
>
<span class="text-lg text-highlight-secondary"
>{group.players.reduce((a, b) => a + b.tries, 0) || 0}</span
>
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="text-sm font-semibold">Score</span> <span class="text-sm font-semibold">Score</span>

View file

@ -1,17 +1,19 @@
<script lang="ts"> <script lang="ts">
import { enhance } from '$app/forms'; import { enhance } from '$app/forms';
import { page } from '$app/stores'; import { page } from '$app/stores';
import type { ActionData } from './$types'; import type { ActionData } from './$types';
import Button from '$lib/components/ui/Button.svelte'; import Button from '$lib/components/ui/Button.svelte';
import Input from '$lib/components/ui/Input.svelte'; import Input from '$lib/components/ui/Input.svelte';
import plausible from '$lib/stores/Plausible';
$: user = $page.data.user; $: user = $page.data.user;
export let form: ActionData; export let form: ActionData;
$: console.log(form); $: optedOut = $plausible;
</script> </script>
<form class="flex flex-col gap-4" method="POST" use:enhance> <form class="flex flex-col gap-4" method="POST" use:enhance>
@ -35,6 +37,23 @@
value={user?.description} value={user?.description}
/> />
<!-- TODO -->
<div class="flex items-center justify-between">
<label for="optout"> Ne pas me tracer de manière anonyme </label>
<input
class="h-4 w-4"
name="optout"
type="checkbox"
value={optedOut}
on:change={() => plausible.set(!optedOut)}
checked={optedOut}
/>
</div>
<p class="text-sm text-highlight-secondary">
Nous utilisons Plausible pour analyser l'utilisation de notre site web de manière anonyme.
</p>
<Button variant="brand"> <Button variant="brand">
<!-- {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />} --> <!-- {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />} -->
Modifier Modifier