diff --git a/src/lib/components/Chapter.svelte b/src/lib/components/Chapter.svelte new file mode 100644 index 0000000..5754117 --- /dev/null +++ b/src/lib/components/Chapter.svelte @@ -0,0 +1,39 @@ + + +
  • + +
    +

    + {chapter.name} +

    + +
    + + + +
    +
  • diff --git a/src/lib/components/Icons/Discord.svelte b/src/lib/components/Icons/Discord.svelte new file mode 100644 index 0000000..a592cde --- /dev/null +++ b/src/lib/components/Icons/Discord.svelte @@ -0,0 +1,19 @@ + + + \ No newline at end of file diff --git a/src/lib/components/Icons/Git.svelte b/src/lib/components/Icons/Git.svelte new file mode 100644 index 0000000..29b01e5 --- /dev/null +++ b/src/lib/components/Icons/Git.svelte @@ -0,0 +1,23 @@ + + + diff --git a/src/lib/components/Icons/Help.svelte b/src/lib/components/Icons/Help.svelte new file mode 100644 index 0000000..6c534aa --- /dev/null +++ b/src/lib/components/Icons/Help.svelte @@ -0,0 +1,23 @@ + + + diff --git a/src/lib/components/Icons/Leaderboard.svelte b/src/lib/components/Icons/Leaderboard.svelte index dcac8ca..90e8281 100644 --- a/src/lib/components/Icons/Leaderboard.svelte +++ b/src/lib/components/Icons/Leaderboard.svelte @@ -12,7 +12,6 @@ width="24" height="24" viewBox="0 0 24 24" - fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" diff --git a/src/lib/components/Icons/Mail.svelte b/src/lib/components/Icons/Mail.svelte new file mode 100644 index 0000000..137ac8e --- /dev/null +++ b/src/lib/components/Icons/Mail.svelte @@ -0,0 +1,25 @@ + + + diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index 6cf9952..3ed4594 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -7,7 +7,10 @@ export let isOpen: boolean; $: 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() { isOpen = !isOpen; @@ -27,11 +30,24 @@ {/if} - {#if !isOpen && segment} + {#if !isOpen && segments.length} +
    + {#each breadcrumb as segment} + {@const last = segment === breadcrumb[breadcrumb.length - 1]} + + {segment.name} + + {#if !last} + / + {/if} + {/each} +
    + {/if} +
    diff --git a/src/lib/components/Puzzle.svelte b/src/lib/components/Puzzle.svelte index aa2d00d..926c7a5 100644 --- a/src/lib/components/Puzzle.svelte +++ b/src/lib/components/Puzzle.svelte @@ -1,10 +1,13 @@ @@ -20,7 +23,10 @@ } )} > - +

    {puzzle.name} diff --git a/src/lib/components/Sidenav.svelte b/src/lib/components/Sidenav.svelte index c8cd1df..1da35a9 100644 --- a/src/lib/components/Sidenav.svelte +++ b/src/lib/components/Sidenav.svelte @@ -1,5 +1,6 @@ + +
    +
    +
    +

    Chaptires

    +

    + Les chapitres sont les différentes parties du jeu. Chaque chapitre est composé de plusieurs + puzzles. +

    +
    +
    + {#each chapters as chapter} + + {/each} +
    diff --git a/src/routes/dashboard/chapters/[chapterId]/+page.server.ts b/src/routes/dashboard/chapters/[chapterId]/+page.server.ts new file mode 100644 index 0000000..3476e9a --- /dev/null +++ b/src/routes/dashboard/chapters/[chapterId]/+page.server.ts @@ -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; diff --git a/src/routes/dashboard/chapters/[chapterId]/+page.svelte b/src/routes/dashboard/chapters/[chapterId]/+page.svelte new file mode 100644 index 0000000..9f3b048 --- /dev/null +++ b/src/routes/dashboard/chapters/[chapterId]/+page.svelte @@ -0,0 +1,97 @@ + + +
    +
    +
    +
    +

    {chapter.name}

    + +
    +
    + {#if chapter.startDate && chapter.endDate} +
    + + + {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' + })} + +
    + {:else} +
    +
    +
    + {/if} +
    + +
    +
    +
    +
      + {#each chapter.puzzles as puzzle} + + {/each} +
    + +
    +
    diff --git a/src/routes/dashboard/chapters/[chapterId]/puzzle/+page.server.ts b/src/routes/dashboard/chapters/[chapterId]/puzzle/+page.server.ts new file mode 100644 index 0000000..aa307ab --- /dev/null +++ b/src/routes/dashboard/chapters/[chapterId]/puzzle/+page.server.ts @@ -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; diff --git a/src/routes/dashboard/puzzles/[id]/+page.server.ts b/src/routes/dashboard/chapters/[chapterId]/puzzle/[puzzleId]/+page.server.ts similarity index 69% rename from src/routes/dashboard/puzzles/[id]/+page.server.ts rename to src/routes/dashboard/chapters/[chapterId]/puzzle/[puzzleId]/+page.server.ts index b2612c3..b89fa15 100644 --- a/src/routes/dashboard/puzzles/[id]/+page.server.ts +++ b/src/routes/dashboard/chapters/[chapterId]/puzzle/[puzzleId]/+page.server.ts @@ -1,18 +1,35 @@ -import type { PageServerLoad } from './$types'; import { API_URL } from '$env/static/private'; import { error, redirect, type Actions } from '@sveltejs/kit'; 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(); const session = cookies.get('session'); - if (isNaN(parseInt(id))) { - throw redirect(303, '/dashboard/puzzles'); + if (isNaN(parseInt(puzzleId))) { + 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: { Authorization: `Bearer ${session}` } diff --git a/src/routes/dashboard/puzzles/[id]/+page.svelte b/src/routes/dashboard/chapters/[chapterId]/puzzle/[puzzleId]/+page.svelte similarity index 100% rename from src/routes/dashboard/puzzles/[id]/+page.svelte rename to src/routes/dashboard/chapters/[chapterId]/puzzle/[puzzleId]/+page.svelte diff --git a/src/routes/dashboard/leaderboard/+page.svelte b/src/routes/dashboard/leaderboard/+page.svelte index c1f49bd..3f25b37 100644 --- a/src/routes/dashboard/leaderboard/+page.svelte +++ b/src/routes/dashboard/leaderboard/+page.svelte @@ -24,6 +24,8 @@ )} -->
      {#each groups as group} + {@const players = group.players.sort((a, b) => b.score - a.score)} + {@const last = players[players.length - 1]}
    • {group.name} - - {#if group.players.length > 1} - {#each group.players as player} - {player.pseudo || 'Anonyme'} {' '} + {#if players.length > 1} + {#each players as player} + {player.pseudo || 'Anonyme'}{#if player !== last}, {/if} + {' '} {/each} {:else} {group.players[0].pseudo} @@ -54,8 +51,12 @@
      - Essai{group.players.reduce((a, b) => a + b.tries, 0) || 0 ? 's' : ''} - {group.players.reduce((a, b) => a + b.tries, 0) || 0} + Essai{group.players.reduce((a, b) => a + b.tries, 0) || 0 ? 's' : ''} + {group.players.reduce((a, b) => a + b.tries, 0) || 0}
      Score diff --git a/src/routes/dashboard/settings/+page.svelte b/src/routes/dashboard/settings/+page.svelte index dc62bec..7beb80e 100644 --- a/src/routes/dashboard/settings/+page.svelte +++ b/src/routes/dashboard/settings/+page.svelte @@ -1,17 +1,19 @@
      @@ -35,6 +37,23 @@ value={user?.description} /> + + +
      + + plausible.set(!optedOut)} + checked={optedOut} + /> +
      +

      + Nous utilisons Plausible pour analyser l'utilisation de notre site web de manière anonyme. +

      +