peer-at-code-web/src/routes/(app)/chapters/[chapterId=id]/+page.svelte
2024-04-16 02:24:10 +02:00

159 lines
4.5 KiB
Svelte

<script lang="ts">
import type { PageData, Snapshot } from './$types';
import BarChart from 'lucide-svelte/icons/bar-chart-2';
import Users from 'lucide-svelte/icons/users';
import Puzzle from '$lib/components/puzzle.svelte';
import Button from '$lib/components/ui/button/button.svelte';
import Input from '$lib/components/ui/input/input.svelte';
import * as Select from '$lib/components/ui/select';
export let data: PageData;
// TODO: Refactor, this is for event purpose only, this is a mess
type Filters = {
name: string;
themeList?: string;
difficultyList?: string;
};
let name = '';
let themeList: string[] = [];
let difficultyList: string[] = [];
const difficulties = data.chapter?.puzzles?.reduce((acc, puzzle) => {
const tag = puzzle.tags?.find((tag) =>
['easy', 'medium', 'hard'].includes(tag.name.toLowerCase())
);
if (tag) {
acc[tag.name] = tag.name.toLowerCase();
}
return acc;
}, {}) as Record<string, string>;
const themes = data.chapter?.puzzles?.reduce((acc, puzzle) => {
puzzle.tags?.forEach((tag) => {
if (!['easy', 'medium', 'hard'].includes(tag.name.toLowerCase())) {
acc[tag.name] = tag.name.toLowerCase();
}
});
return acc;
}, {}) as Record<string, string>;
$: filteredPuzzles =
data.chapter?.puzzles?.filter((puzzle) => {
const regex = new RegExp(name, 'i');
const nameMatch = regex.test(puzzle.name);
const themeMatch = themeList.length
? puzzle.tags?.some((tag) => themeList.includes(tag.name.toLowerCase()))
: true;
const difficultyMatch = difficultyList.length
? puzzle.tags?.some((tag) => difficultyList.includes(tag.name.toLowerCase()))
: true;
return nameMatch && themeMatch && difficultyMatch;
}) ?? [];
export const snapshot: Snapshot<Filters> = {
capture: () => ({
name,
themeList: themeList.length ? themeList.join(',') : '',
difficultyList: difficultyList.length ? difficultyList.join(',') : ''
}),
restore: (value) => {
name = value.name;
themeList = value.themeList ? value.themeList.split(',') : [];
difficultyList = value.difficultyList ? value.difficultyList.split(',') : [];
}
};
</script>
<section class="flex w-full flex-col gap-2">
<header class="flex flex-col justify-between gap-2 lg:flex-row lg:items-center">
<div class="flex flex-col">
<h2 class="text-xl font-semibold">{data.chapter.name}</h2>
{#if !data.chapter?.puzzles?.length}
<p class="text-muted-foreground">Le chapitre ne contient pour l'instant aucun puzzle</p>
{:else}
<p class="text-muted-foreground">
Ils vous restent {data.chapter.puzzles.filter((p) => !p.score).length} puzzles à résoudre sur
un total de {data.chapter.puzzles.length}
</p>
{/if}
</div>
<div class="flex gap-2">
{#if data.chapter.start && data.chapter.end}
<Button href="/chapters/{data.chapter.id}/groups">
<Users class="mr-2 h-4 w-4" />
Voir les groupes
</Button>
<Button href="/chapters/{data.chapter.id}/leaderboard">
<BarChart class="mr-2 h-4 w-4" />
Voir le classement
</Button>
{/if}
</div>
</header>
<div class="flex flex-col md:flex-row gap-2">
<Input bind:value={name} placeholder="Rechercher un puzzle" />
<Select.Root
multiple
selected={themeList.map((theme) => ({ label: theme, value: theme }))}
onSelectedChange={(v) => {
if (v) {
themeList = v.map((item) => item.value);
} else {
themeList = [];
}
}}
>
<Select.Trigger class="md:w-[180px]">
<Select.Value placeholder="Thème" />
</Select.Trigger>
<Select.Content>
{#each Object.entries(themes) as [key, value]}
<Select.Item {value}>{key}</Select.Item>
{/each}
</Select.Content>
</Select.Root>
<Select.Root
multiple
selected={difficultyList.map((difficulty) => ({ label: difficulty, value: difficulty }))}
onSelectedChange={(v) => {
if (v) {
difficultyList = v.map((item) => item.value);
} else {
difficultyList = [];
}
}}
>
<Select.Trigger class="md:w-[180px]">
<Select.Value placeholder="Difficulté" />
</Select.Trigger>
<Select.Content>
{#each Object.entries(difficulties) as [key, value]}
<Select.Item {value}>{key}</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</div>
<ul class="flex flex-col gap-2">
{#if !filteredPuzzles.length}
<li class="flex h-16 w-full items-center justify-center rounded border border-border bg-card">
<p class="text-muted-foreground">Aucun puzzle trouvé</p>
</li>
{:else}
{#each filteredPuzzles as puzzle (puzzle.id)}
<Puzzle {puzzle} />
{/each}
{/if}
</ul>
</section>