Pre fix, still searching why it doesn't work
This commit is contained in:
parent
4c78ee4a21
commit
18ad462424
20 changed files with 254 additions and 305 deletions
|
@ -3,13 +3,17 @@
|
|||
|
||||
import * as Breadcrumb from '$lib/components/ui/breadcrumb';
|
||||
|
||||
$: segments = $page.url.pathname.slice(1).split('/');
|
||||
$: breadcrumb = segments.map((segment, index) => {
|
||||
return {
|
||||
name: segment.charAt(0).toUpperCase() + segment.slice(1),
|
||||
href: '/' + segments.slice(0, index + 1).join('/')
|
||||
};
|
||||
}) as { name: string; href: string }[];
|
||||
export let breadcrumb: { name: string; href: string }[] = [];
|
||||
|
||||
$: page.subscribe(({ url: { pathname } }) => {
|
||||
breadcrumb = pathname
|
||||
.split('/')
|
||||
.slice(1)
|
||||
.map((segment, index, segments) => ({
|
||||
name: segment.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()),
|
||||
href: '/' + segments.slice(0, index + 1).join('/')
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
|
||||
<Breadcrumb.Root>
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
<Boring name={$page.data.user?.pseudo} variant="beam" />
|
||||
</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
<span class="sr-only">Menu utilisateur</span>
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content class="w-56">
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
|
||||
<nav class="w-full border-b border-muted p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<Breadcrumb />
|
||||
<div class="flex items-center gap-2">
|
||||
<MobileNav />
|
||||
<Breadcrumb />
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<NavbarUser />
|
||||
<MobileNav />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const loginSchema = z.object({
|
||||
pseudo: z.string({ required_error: "Nom d'utilisateur requis", })
|
||||
pseudo: z.string()
|
||||
.trim()
|
||||
.min(1, { message: "Nom d'utilisateur requis" }),
|
||||
passwd: z.string({ required_error: 'Mot de passe requis' })
|
||||
passwd: z.string()
|
||||
.trim()
|
||||
.min(1, { message: 'Mot de passe requis' }),
|
||||
});
|
||||
|
||||
export const registerSchema = z.object({
|
||||
email: z
|
||||
.string({
|
||||
required_error: 'Email requis'
|
||||
})
|
||||
.string()
|
||||
.trim()
|
||||
.max(64, {
|
||||
message: 'Email trop long (max 64 caractères)'
|
||||
|
@ -22,20 +20,15 @@ export const registerSchema = z.object({
|
|||
message: 'Email invalide'
|
||||
}),
|
||||
firstname: z.string()
|
||||
.trim(),
|
||||
.trim().min(1, { message: "Prénom requis" }),
|
||||
lastname: z.string()
|
||||
.trim(),
|
||||
pseudo: z.string({
|
||||
required_error: 'Nom d\'utilisateur requis'
|
||||
}).trim(),
|
||||
.trim().min(1, { message: "Nom requis" }),
|
||||
pseudo: z.string().trim().min(1, { message: "Nom d'utilisateur requis" }),
|
||||
});
|
||||
|
||||
|
||||
export const registerConfirmationSchema = z.object({
|
||||
email: z
|
||||
.string({
|
||||
required_error: 'Email requis'
|
||||
})
|
||||
.string()
|
||||
.trim()
|
||||
.max(64, {
|
||||
message: 'Email trop long (max 64 caractères)'
|
||||
|
@ -44,41 +37,61 @@ export const registerConfirmationSchema = z.object({
|
|||
message: 'Email invalide'
|
||||
}),
|
||||
firstname: z.string()
|
||||
.trim(),
|
||||
.trim()
|
||||
.min(1, { message: "Prénom requis" }),
|
||||
lastname: z.string()
|
||||
.trim(),
|
||||
pseudo: z.string({
|
||||
required_error: 'Nom d\'utilisateur requis'
|
||||
}).trim(),
|
||||
passwd: z.string({
|
||||
required_error: 'Mot de passe requis'
|
||||
})
|
||||
.trim()
|
||||
.min(1, { message: "Nom requis" }),
|
||||
pseudo: z.string().trim().min(1, { message: "Nom d'utilisateur requis" }),
|
||||
passwd: z.string()
|
||||
.trim()
|
||||
.min(1, { message: 'Mot de passe requis' }),
|
||||
code: z.string({
|
||||
required_error: 'Code manquant'
|
||||
})
|
||||
confirm: z.string()
|
||||
.trim()
|
||||
.min(1, { message: 'Confirmation du mot de passe requise' }),
|
||||
code: z.string()
|
||||
.trim()
|
||||
.regex(/^[0-9]{4}$/, { message: 'Code invalide, il doit contenir 4 chiffres' })
|
||||
.trim(),
|
||||
}).refine((data) => data.passwd == data.confirm, {
|
||||
message: 'Les mots de passe ne correspondent pas',
|
||||
path: ['confirm']
|
||||
});
|
||||
|
||||
export const requestPasswordResetSchema = z.object({
|
||||
email: z.string({ required_error: 'Email requis' })
|
||||
email: z.string()
|
||||
.trim()
|
||||
.email({ message: 'Email invalide' })
|
||||
.min(1, { message: 'Email requis' })
|
||||
.max(64, {
|
||||
message: 'Email trop long (max 64 caractères)'
|
||||
})
|
||||
.email({
|
||||
message: 'Email invalide'
|
||||
}),
|
||||
});
|
||||
|
||||
export const resetPasswordSchema = z.object({
|
||||
email: z.string({ required_error: 'Email requis' })
|
||||
email: z.string()
|
||||
.trim()
|
||||
.email({ message: 'Email invalide' })
|
||||
.min(1, { message: 'Email requis' }),
|
||||
password: z.string({ required_error: 'Mot de passe requis' })
|
||||
.max(64, {
|
||||
message: 'Email trop long (max 64 caractères)'
|
||||
})
|
||||
.email({
|
||||
message: 'Email invalide'
|
||||
}),
|
||||
password: z.string()
|
||||
.trim()
|
||||
.min(1, { message: 'Mot de passe requis' }),
|
||||
code: z.string({
|
||||
required_error: 'Code manquant'
|
||||
})
|
||||
.regex(/^[0-9]{4}$/, { message: 'Code invalide, il doit contenir 4 chiffres' }),
|
||||
confirm: z.string()
|
||||
.trim()
|
||||
.min(1, { message: 'Confirmation du mot de passe requise' }),
|
||||
code: z.string()
|
||||
.regex(/^[0-9]{4}$/, { message: 'Code invalide, il doit contenir 4 chiffres' }),
|
||||
}).refine((data) => data.password == data.confirm, {
|
||||
message: 'Les mots de passe ne correspondent pas',
|
||||
path: ['confirm']
|
||||
});
|
||||
|
||||
export const settingSchema = z.object({
|
||||
firstname: z.string().trim().min(1, { message: "Prénom requis" }),
|
||||
lastname: z.string().trim().min(1, { message: "Nom requis" }),
|
||||
pseudo: z.string().trim().min(1, { message: "Nom d'utilisateur requis" }),
|
||||
});
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const groupSchema = z.object({
|
||||
name: z.string({
|
||||
required_error: 'Un nom est requis',
|
||||
})
|
||||
.min(1, 'Un nom est requis',),
|
||||
name: z.string().trim().min(1, 'Un nom de groupe est requis'),
|
||||
});
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const puzzleSchema = z.object({
|
||||
answer: z.string({
|
||||
required_error: 'Une réponse est requise',
|
||||
})
|
||||
answer: z.string()
|
||||
.min(1, 'Une réponse est requise')
|
||||
});
|
||||
|
|
|
@ -34,15 +34,7 @@
|
|||
>
|
||||
<div class="flex w-full flex-col justify-between md:flex-row md:items-center md:gap-4">
|
||||
<span class="text-lg font-semibold">{data.event.name}</span>
|
||||
{#if data.event.start && data.event.end}
|
||||
<span class="text-muted-foreground">
|
||||
{new Date(data.event.start).toLocaleDateString()} - {new Date(
|
||||
data.event.end
|
||||
).toLocaleDateString()}
|
||||
</span>
|
||||
{:else}
|
||||
<span class="text-muted-foreground">Aucune date</span>
|
||||
{/if}
|
||||
<span class="text-muted-foreground"> Participer en équipe de 1 à 4 joueurs </span>
|
||||
</div>
|
||||
<Button href="/chapters/{data.event.id}">Participer</Button>
|
||||
</div>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
Voir les groupes
|
||||
</Button>
|
||||
{#if data.chapter.start && data.chapter.end}
|
||||
<Button href="/leaderboard/{data.chapter.id}">
|
||||
<Button href="/chapters/{data.chapter.id}/leaderboard">
|
||||
<BarChart2 class="mr-2 h-4 w-4" />
|
||||
Voir le classement
|
||||
</Button>
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import { API_URL } from '$env/static/private';
|
||||
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
import type { LeaderboardEvent } from '$lib/types';
|
||||
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
export const load = (async ({ locals: { user }, fetch, cookies, params: { chapterId } }) => {
|
||||
export const load: PageServerLoad = async ({ locals: { user }, fetch, cookies, params: { chapterId } }) => {
|
||||
|
||||
if (!user) redirect(302, '/login');
|
||||
|
||||
|
@ -20,14 +18,18 @@ export const load = (async ({ locals: { user }, fetch, cookies, params: { chapte
|
|||
}
|
||||
});
|
||||
|
||||
if (!res.ok) redirect(302, '/');
|
||||
if (!res.ok) return {
|
||||
leaderboard: []
|
||||
}
|
||||
|
||||
const leaderboard = (await res.json()) as LeaderboardEvent;
|
||||
|
||||
if (!leaderboard) redirect(302, '/');
|
||||
if (!leaderboard) return {
|
||||
leaderboard: []
|
||||
}
|
||||
|
||||
return {
|
||||
title: "Classement",
|
||||
leaderboard
|
||||
};
|
||||
}) satisfies PageServerLoad;
|
||||
};
|
|
@ -26,14 +26,15 @@
|
|||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
<main class="flex flex-col justify-between gap-4 pb-4">
|
||||
<main class="pb-4">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full min-w-max table-auto">
|
||||
<table class="w-full min-w-max table">
|
||||
<thead
|
||||
class="border-x border-b border-t border-border bg-card/50 text-sm text-muted-foreground"
|
||||
>
|
||||
<tr>
|
||||
<th scope="col" class="text-left">#</th>
|
||||
<th scope="col" class="text-left">Equipe</th>
|
||||
<th scope="col" class="text-left">Joueurs</th>
|
||||
<th scope="col" class="text-right">Score</th>
|
||||
<th scope="col" class="text-right">Essais</th>
|
||||
|
@ -42,14 +43,15 @@
|
|||
<tbody class="border-x border-b border-border bg-card align-middle">
|
||||
{#if !data.leaderboard.groups.length}
|
||||
<tr>
|
||||
<td colspan="4" class="text-center text-muted-foreground"
|
||||
>Aucun groupe n'a encore de score</td
|
||||
>
|
||||
<td colspan="5" class="text-center text-muted-foreground">
|
||||
Aucun groupe n'a encore de score
|
||||
</td>
|
||||
</tr>
|
||||
{:else}
|
||||
{#each data.leaderboard.groups.filter( (g) => g.players.reduce((a, b) => a + b.score, 0) ) as group (group)}
|
||||
<tr class={cn(SCORE_COLORS[group.rank - 1])}>
|
||||
<td>{group.rank}</td>
|
||||
<td>{group.name}</td>
|
||||
<td class="text-lg">
|
||||
{#if group.players?.length}
|
||||
<span>{group.players.map((player) => player?.pseudo).join(', ')} </span>
|
|
@ -19,7 +19,7 @@ export const load = (async ({ locals: { user }, fetch, cookies }) => {
|
|||
|
||||
if (!res.ok) {
|
||||
return {
|
||||
leaderboard: [] as Leaderboard[]
|
||||
leaderboard: []
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -15,10 +15,12 @@
|
|||
<p class="text-muted-foreground">Suivez la progression des élèves en direct</p>
|
||||
</div>
|
||||
</header>
|
||||
<main class="flex flex-col justify-between gap-4 pb-4">
|
||||
<main class="pb-4">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full min-w-max table-auto">
|
||||
<thead class="border-x border-b text-muted-foreground border-t border-border bg-card/50 text-sm">
|
||||
<table class="table w-full min-w-max">
|
||||
<thead
|
||||
class="border-x border-b border-t border-border bg-card/50 text-sm text-muted-foreground"
|
||||
>
|
||||
<tr>
|
||||
<th scope="col" class="text-left">#</th>
|
||||
<th scope="col" class="text-left">Nom</th>
|
||||
|
|
|
@ -2,17 +2,11 @@ import { API_URL } from '$env/static/private';
|
|||
import { fail, redirect, type Actions } from '@sveltejs/kit';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
import { settingSchema } from '$lib/validations/auth';
|
||||
import { zod } from 'sveltekit-superforms/adapters';
|
||||
import { superValidate } from 'sveltekit-superforms/server';
|
||||
import { z } from 'zod';
|
||||
import { setError, superValidate } from 'sveltekit-superforms/server';
|
||||
|
||||
const settingSchema = z.object({
|
||||
firstname: z.string().min(1, { message: "Prénom requis" }),
|
||||
lastname: z.string().min(1, { message: "Nom requis" }),
|
||||
pseudo: z.string().min(1, { message: "Nom d'utilisateur requi" }),
|
||||
});
|
||||
|
||||
export const load = (async ({ locals: { user } }) => {
|
||||
export const load: PageServerLoad = async ({ locals: { user } }) => {
|
||||
if (!user) redirect(302, '/login');
|
||||
|
||||
const form = await superValidate(user, zod(settingSchema));
|
||||
|
@ -21,10 +15,13 @@ export const load = (async ({ locals: { user } }) => {
|
|||
title: 'Paramètres',
|
||||
form
|
||||
};
|
||||
}) satisfies PageServerLoad;
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async ({ request, cookies, locals: { user } }) => {
|
||||
|
||||
if (!user) return fail(401);
|
||||
|
||||
export const actions = {
|
||||
default: async ({ request, cookies }) => {
|
||||
const session = cookies.get('session');
|
||||
|
||||
const form = await superValidate(request, zod(settingSchema));
|
||||
|
@ -38,21 +35,20 @@ export const actions = {
|
|||
headers: {
|
||||
Authorization: `Bearer ${session}`
|
||||
},
|
||||
body: JSON.stringify(form.data)
|
||||
body: JSON.stringify({
|
||||
...form.data
|
||||
})
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
return {
|
||||
success: true
|
||||
};
|
||||
if (!res.ok) {
|
||||
if (res.status === 400) {
|
||||
return setError(form, "pseudo", "Ce pseudo est déjà utilisé");
|
||||
}
|
||||
return setError(form, "pseudo", "Une erreur est survenue lors de la sauvegarde des paramètres");
|
||||
}
|
||||
|
||||
if (res.status === 400) {
|
||||
form.errors.pseudo = ['Le pseudo est déjà utilisé'];
|
||||
|
||||
return fail(400, { form });
|
||||
}
|
||||
|
||||
return fail(500, { form });
|
||||
return {
|
||||
form
|
||||
};
|
||||
}
|
||||
} satisfies Actions;
|
||||
};
|
||||
|
|
|
@ -1,106 +1,76 @@
|
|||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
import { Loader2 } from 'lucide-svelte';
|
||||
import Loader from 'lucide-svelte/icons/loader-circle';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { zodClient } from 'sveltekit-superforms/adapters';
|
||||
import { superForm } from 'sveltekit-superforms/client';
|
||||
|
||||
import Button from '$lib/components/ui/button/button.svelte';
|
||||
import * as Form from '$lib/components/ui/form';
|
||||
import Input from '$lib/components/ui/input/input.svelte';
|
||||
|
||||
import plausible from '$lib/stores/plausible';
|
||||
import Label from '$lib/components/ui/label/label.svelte';
|
||||
|
||||
import { settingSchema } from '$lib/validations/auth';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let submitting = false;
|
||||
|
||||
const { form, errors, enhance } = superForm(data.form, {
|
||||
onSubmit() {
|
||||
submitting = true;
|
||||
},
|
||||
const form = superForm(data.form, {
|
||||
validators: zodClient(settingSchema),
|
||||
delayMs: 500,
|
||||
multipleSubmits: 'prevent',
|
||||
onResult({ result }) {
|
||||
if (result.type === 'success') {
|
||||
toast.message('Succès', { description: 'Vos informations ont été mises à jour.' });
|
||||
}
|
||||
|
||||
submitting = false;
|
||||
}
|
||||
});
|
||||
|
||||
const { form: formData, enhance, delayed } = form;
|
||||
|
||||
// TODO: Handle overflow X on small screens
|
||||
</script>
|
||||
|
||||
<section>
|
||||
<form class="flex flex-col gap-4" method="POST" use:enhance>
|
||||
<form class="flex flex-col gap-2" method="POST" use:enhance>
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-xl font-bold">Paramètres généraux</h2>
|
||||
<Button type="submit" variant="default" disabled={submitting}>
|
||||
{#if submitting}
|
||||
<Loader2 class="mr-2 h-4 w-4 animate-spin" />
|
||||
<Form.Button disabled={$delayed}>
|
||||
{#if $delayed}
|
||||
<Loader class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
Modifier
|
||||
</Button>
|
||||
</Form.Button>
|
||||
</div>
|
||||
|
||||
<label for="email">Email</label>
|
||||
<Label>Email</Label>
|
||||
<Input
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="philipzcwbarlow@peerat.dev"
|
||||
value={data.user?.email}
|
||||
value={$page.data.user?.email}
|
||||
disabled
|
||||
placeholder="philiphzcwbarlow@peerat.dev"
|
||||
/>
|
||||
|
||||
<label for="firstname">Prénom</label>
|
||||
<Input
|
||||
name="firstname"
|
||||
type="text"
|
||||
placeholder="Philip"
|
||||
aria-invalid={$errors.firstname ? 'true' : undefined}
|
||||
bind:value={$form.firstname}
|
||||
/>
|
||||
{#if $errors.firstname}
|
||||
<span class="text-sm text-red-500">{$errors.firstname}</span>
|
||||
{/if}
|
||||
|
||||
<label for="lastname">Nom</label>
|
||||
<Input
|
||||
name="lastname"
|
||||
type="text"
|
||||
placeholder="Barlow"
|
||||
aria-invalid={$errors.lastname ? 'true' : undefined}
|
||||
bind:value={$form.lastname}
|
||||
/>
|
||||
{#if $errors.lastname}
|
||||
<span class="text-sm text-red-500">{$errors.lastname}</span>
|
||||
{/if}
|
||||
|
||||
<label for="pseudo"> Nom d'utilisateur </label>
|
||||
<Input
|
||||
name="pseudo"
|
||||
type="text"
|
||||
placeholder="Cypher Wolf"
|
||||
aria-invalid={$errors.pseudo ? 'true' : undefined}
|
||||
bind:value={$form.pseudo}
|
||||
/>
|
||||
{#if $errors.pseudo}
|
||||
<span class="text-sm text-red-500">{$errors.pseudo}</span>
|
||||
{/if}
|
||||
|
||||
<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"
|
||||
bind:value={$plausible}
|
||||
on:change={() => plausible.set(!$plausible)}
|
||||
checked={$plausible}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p class="text-highlight-secondary text-sm">
|
||||
Nous utilisons Plausible pour analyser l'utilisation de notre site web de manière anonyme.
|
||||
</p>
|
||||
<p class="text-sm text-muted-foreground">Votre email ne peut pas être modifié.</p>
|
||||
<Form.Field {form} name="firstname">
|
||||
<Form.Control let:attrs>
|
||||
<Form.Label>Prénom</Form.Label>
|
||||
<Input {...attrs} bind:value={$formData.firstname} placeholder="Philip" />
|
||||
</Form.Control>
|
||||
<Form.FieldErrors />
|
||||
</Form.Field>
|
||||
<Form.Field {form} name="lastname">
|
||||
<Form.Control let:attrs>
|
||||
<Form.Label>Nom de famille</Form.Label>
|
||||
<Input {...attrs} bind:value={$formData.lastname} placeholder="Barlow" />
|
||||
</Form.Control>
|
||||
<Form.FieldErrors />
|
||||
</Form.Field>
|
||||
<Form.Field {form} name="pseudo">
|
||||
<Form.Control let:attrs>
|
||||
<Form.Label>Nom d'utilisateur</Form.Label>
|
||||
<Input {...attrs} bind:value={$formData.pseudo} placeholder="Cypherwolf" />
|
||||
</Form.Control>
|
||||
<Form.Description>Ce nom sera visible par les autres utilisateurs.</Form.Description>
|
||||
<Form.FieldErrors />
|
||||
</Form.Field>
|
||||
</form>
|
||||
</section>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { fail, redirect, type Actions } from '@sveltejs/kit';
|
|||
import type { PageServerLoad } from './$types';
|
||||
|
||||
import { zod } from 'sveltekit-superforms/adapters';
|
||||
import { superValidate } from 'sveltekit-superforms/server';
|
||||
import { setError, superValidate } from 'sveltekit-superforms/server';
|
||||
|
||||
import { loginSchema } from '$lib/validations/auth';
|
||||
|
||||
|
@ -37,19 +37,13 @@ export const actions: Actions = {
|
|||
});
|
||||
|
||||
if (!res.ok) {
|
||||
|
||||
form.errors.passwd = ["Nom d'utilisateur ou mot de passe incorrect"];
|
||||
|
||||
return fail(400, { form });
|
||||
return setError(form, 'passwd', "Nom d'utilisateur ou mot de passe incorrect");
|
||||
}
|
||||
|
||||
const token = res.headers.get('Authorization')?.split(' ').pop();
|
||||
const token = res.headers.get('Authorization')?.split('Bearer ').pop();
|
||||
|
||||
if (!token) {
|
||||
|
||||
form.errors.passwd = ["Une erreur est survenue, veuillez réessayer plus tard"];
|
||||
|
||||
return fail(500, { form });
|
||||
return setError(form, 'passwd', "Une erreur est survenue, veuillez réessayer plus tard");
|
||||
}
|
||||
|
||||
cookies.set('session', token, {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
|
||||
import { Loader2 } from 'lucide-svelte';
|
||||
import Loader from 'lucide-svelte/icons/loader-circle';
|
||||
import { zodClient } from 'sveltekit-superforms/adapters';
|
||||
import { superForm } from 'sveltekit-superforms/client';
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
|||
<div class="container flex h-screen">
|
||||
<div class="flex w-full flex-col items-center justify-center">
|
||||
<div class="flex w-full max-w-xs flex-col gap-4">
|
||||
<h1 class="mx-auto text-xl font-bold">Connexion</h1>
|
||||
<h2 class="mx-auto text-xl font-bold">Connexion</h2>
|
||||
<form class="flex flex-col justify-center gap-2" method="POST" use:enhance>
|
||||
<Form.Field {form} name="pseudo">
|
||||
<Form.Control let:attrs>
|
||||
|
@ -54,7 +54,7 @@
|
|||
</Form.Field>
|
||||
<Form.Button disabled={$delayed}>
|
||||
{#if $delayed}
|
||||
<Loader2 class="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
Se connecter
|
||||
</Form.Button>
|
||||
|
|
|
@ -6,12 +6,11 @@ import { fail, redirect, type Actions } from '@sveltejs/kit';
|
|||
import type { PageServerLoad } from './$types';
|
||||
|
||||
import { zod } from 'sveltekit-superforms/adapters';
|
||||
import { superValidate } from 'sveltekit-superforms/server';
|
||||
import { setError, superValidate } from 'sveltekit-superforms/server';
|
||||
|
||||
import { registerConfirmationSchema, registerSchema } from '$lib/validations/auth';
|
||||
|
||||
|
||||
export const load = (async ({ locals: { user } }) => {
|
||||
export const load: PageServerLoad = async ({ locals: { user } }) => {
|
||||
if (user) redirect(302, '/');
|
||||
|
||||
const registerForm = await superValidate(zod(registerSchema));
|
||||
|
@ -22,10 +21,11 @@ export const load = (async ({ locals: { user } }) => {
|
|||
registerForm,
|
||||
registerConfirmationForm
|
||||
};
|
||||
}) satisfies PageServerLoad;
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
export const actions: Actions = {
|
||||
register: async ({ request }) => {
|
||||
|
||||
const form = await superValidate(request, zod(registerSchema));
|
||||
|
||||
if (!form.valid) {
|
||||
|
@ -39,26 +39,19 @@ export const actions = {
|
|||
})
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
return {
|
||||
success: true
|
||||
};
|
||||
if (!res.ok) {
|
||||
if (res.status === 400) {
|
||||
const { email_valid, username_valid } = await res.json();
|
||||
if (!email_valid) return setError(form, 'email', 'Un compte avec cette adresse email existe déjà');
|
||||
if (!username_valid) return setError(form, 'pseudo', 'Ce pseudo est déjà utilisé');
|
||||
}
|
||||
|
||||
return setError(form, 'email', "Une erreur est survenue lors de l'inscription");
|
||||
}
|
||||
|
||||
if (res.status === 400) {
|
||||
const { email_valid, username_valid } = await res.json();
|
||||
|
||||
if (!email_valid) form.errors.email = ['Un compte avec cette adresse email existe déjà'];
|
||||
if (!username_valid) form.errors.pseudo = ['Ce pseudo est déjà utilisé'];
|
||||
|
||||
return fail(400, { form });
|
||||
}
|
||||
|
||||
form.errors.pseudo = ["Une erreur s'est produite"];
|
||||
|
||||
return fail(400, {
|
||||
return {
|
||||
form
|
||||
});
|
||||
};
|
||||
},
|
||||
confirmation: async ({ request, cookies }) => {
|
||||
const form = await superValidate(request, zod(registerConfirmationSchema));
|
||||
|
@ -79,46 +72,27 @@ export const actions = {
|
|||
})
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
const token = res.headers.get('Authorization')?.split('Bearer ')[1];
|
||||
|
||||
if (!token) {
|
||||
|
||||
form.errors.code = [`Une erreur s'est produite lors de la confirmation de votre compte.`];
|
||||
|
||||
return fail(400, {
|
||||
form
|
||||
});
|
||||
}
|
||||
|
||||
cookies.set('session', token, {
|
||||
path: '/',
|
||||
secure: !dev,
|
||||
sameSite: 'strict',
|
||||
});
|
||||
|
||||
redirect(302, '/');
|
||||
}
|
||||
|
||||
if (res.status === 400) {
|
||||
try {
|
||||
if (!res.ok) {
|
||||
if (res.status === 400) {
|
||||
const { email_valid, username_valid } = await res.json();
|
||||
|
||||
if (email_valid) form.errors.email = ['Un compte avec cette adresse email existe déjà'];
|
||||
if (username_valid) form.errors.pseudo = ['Ce pseudo est déjà utilisé'];
|
||||
|
||||
return fail(400, { form });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
form.errors.code = ['Le code envoyé est invalide.'];
|
||||
return fail(400, { form });
|
||||
if (!email_valid) return setError(form, 'email', 'Un compte avec cette adresse email existe déjà');
|
||||
if (!username_valid) return setError(form, 'pseudo', "Ce nom d'utilisateur est déjà utilisé");
|
||||
}
|
||||
return setError(form, 'code', "Une erreur est survenue lors de la confirmation");
|
||||
}
|
||||
|
||||
form.errors.code = [`Le code envoyé est invalide.`];
|
||||
const token = res.headers.get('Authorization')?.split('Bearer ').pop();
|
||||
|
||||
return fail(400, {
|
||||
form
|
||||
if (!token) {
|
||||
return setError(form, 'code', "Une erreur est survenue, veuillez réessayer plus tard");
|
||||
}
|
||||
|
||||
cookies.set('session', token, {
|
||||
path: '/',
|
||||
secure: !dev,
|
||||
sameSite: 'strict',
|
||||
});
|
||||
}
|
||||
} satisfies Actions;
|
||||
|
||||
redirect(302, '/');
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
import type { PageData, Snapshot } from './$types';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
import Loader from 'lucide-svelte/icons/loader-circle';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { zod, zodClient } from 'sveltekit-superforms/adapters';
|
||||
import { zodClient } from 'sveltekit-superforms/adapters';
|
||||
import { superForm } from 'sveltekit-superforms/client';
|
||||
|
||||
import * as Form from '$lib/components/ui/form';
|
||||
import Input from '$lib/components/ui/input/input.svelte';
|
||||
|
||||
import { registerConfirmationSchema, registerSchema } from '$lib/validations/auth';
|
||||
import Button from '$lib/components/ui/button/button.svelte';
|
||||
import { enhance } from '$app/forms';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
|||
validators: zodClient(registerSchema),
|
||||
delayMs: 500,
|
||||
multipleSubmits: 'prevent',
|
||||
invalidateAll: false,
|
||||
onResult({ result }) {
|
||||
switch (result.type) {
|
||||
case 'success':
|
||||
|
@ -30,6 +31,7 @@
|
|||
registerConfirmationFormData.set({
|
||||
...$registerFormData,
|
||||
passwd: '',
|
||||
confirm: '',
|
||||
code: ''
|
||||
});
|
||||
confirmation = true;
|
||||
|
@ -38,9 +40,6 @@
|
|||
(field as HTMLInputElement)?.focus();
|
||||
}, 100);
|
||||
break;
|
||||
case 'error':
|
||||
confirmation = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -52,7 +51,7 @@
|
|||
} = registerForm;
|
||||
|
||||
const registerConfirmationForm = superForm(data.registerConfirmationForm, {
|
||||
validators: zod(registerConfirmationSchema),
|
||||
validators: zodClient(registerConfirmationSchema),
|
||||
delayMs: 500,
|
||||
multipleSubmits: 'prevent'
|
||||
});
|
||||
|
@ -62,18 +61,13 @@
|
|||
enhance: registerConfirmationEnhance,
|
||||
delayed: registerConfirmationDelayed
|
||||
} = registerConfirmationForm;
|
||||
|
||||
export const snapshot: Snapshot = {
|
||||
capture: () => confirmation,
|
||||
restore: (value) => (confirmation = value)
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="container flex h-screen">
|
||||
<div class="flex w-full flex-col items-center justify-center">
|
||||
<div class="flex w-full max-w-xs flex-col gap-4">
|
||||
{#if confirmation}
|
||||
<h1 class="mx-auto text-xl font-bold">Confirmation</h1>
|
||||
<h2 class="mx-auto text-xl font-bold">Confirmation</h2>
|
||||
<form
|
||||
class="flex flex-col justify-center gap-2"
|
||||
method="POST"
|
||||
|
@ -143,6 +137,18 @@
|
|||
</Form.Control>
|
||||
<Form.FieldErrors />
|
||||
</Form.Field>
|
||||
<Form.Field form={registerConfirmationForm} name="confirm">
|
||||
<Form.Control let:attrs>
|
||||
<Form.Label>Confirmer le mot de passe</Form.Label>
|
||||
<Input
|
||||
{...attrs}
|
||||
type="password"
|
||||
bind:value={$registerConfirmationFormData.confirm}
|
||||
placeholder="************"
|
||||
/>
|
||||
</Form.Control>
|
||||
<Form.FieldErrors />
|
||||
</Form.Field>
|
||||
<Form.Field form={registerConfirmationForm} name="code">
|
||||
<Form.Control let:attrs>
|
||||
<Form.Label>Code</Form.Label>
|
||||
|
@ -156,16 +162,12 @@
|
|||
</Form.Field>
|
||||
</div>
|
||||
|
||||
<Form.Button type="submit" disabled={$registerConfirmationDelayed}>
|
||||
<Form.Button disabled={$registerConfirmationDelayed}>
|
||||
{#if $registerConfirmationDelayed}
|
||||
<Loader class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
Continuer
|
||||
</Form.Button>
|
||||
|
||||
<!-- <Button variant="link" formaction="?/register" on:click={() => toast('Code renvoyé')}>
|
||||
Renvoyer le code
|
||||
</Button> -->
|
||||
</form>
|
||||
{:else}
|
||||
<h1 class="mx-auto text-xl font-bold">Inscription</h1>
|
||||
|
@ -208,14 +210,12 @@
|
|||
</Form.Control>
|
||||
<Form.FieldErrors />
|
||||
</Form.Field>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Form.Button type="submit" disabled={$registerDelayed}>
|
||||
{#if $registerDelayed}
|
||||
<Loader className="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
S'inscrire
|
||||
</Form.Button>
|
||||
</div>
|
||||
<Form.Button disabled={$registerDelayed}>
|
||||
{#if $registerDelayed}
|
||||
<Loader className="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
S'inscrire
|
||||
</Form.Button>
|
||||
</form>
|
||||
{/if}
|
||||
<ul class="flex justify-between">
|
||||
|
|
|
@ -6,7 +6,7 @@ import { fail, redirect, type Actions } from '@sveltejs/kit';
|
|||
import type { PageServerLoad } from './$types';
|
||||
|
||||
import { zod } from 'sveltekit-superforms/adapters';
|
||||
import { superValidate } from 'sveltekit-superforms/server';
|
||||
import { setError, superValidate } from 'sveltekit-superforms/server';
|
||||
|
||||
import { requestPasswordResetSchema, resetPasswordSchema } from '$lib/validations/auth';
|
||||
|
||||
|
@ -23,7 +23,7 @@ export const load = (async ({ locals: { user } }) => {
|
|||
};
|
||||
}) satisfies PageServerLoad;
|
||||
|
||||
export const actions = {
|
||||
export const actions: Actions = {
|
||||
request: async ({ request, fetch }) => {
|
||||
const form = await superValidate(request, zod(requestPasswordResetSchema));
|
||||
|
||||
|
@ -33,16 +33,17 @@ export const actions = {
|
|||
|
||||
const res = await fetch(`${API_URL}/user/fpw`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(form.data)
|
||||
body: JSON.stringify({
|
||||
...form.data
|
||||
})
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
form.errors.email = ["Une erreur s'est produite ou l'email n'existe pas"];
|
||||
return fail(400, { form });
|
||||
return setError(form, 'email', "Une erreur s'est produite ou l'email n'existe pas");
|
||||
}
|
||||
|
||||
return {
|
||||
success: true
|
||||
form
|
||||
}
|
||||
},
|
||||
confirmation: async ({ request, cookies, fetch }) => {
|
||||
|
@ -62,11 +63,10 @@ export const actions = {
|
|||
});
|
||||
|
||||
if (res.ok) {
|
||||
const token = res.headers.get('Authorization')?.split('Bearer ')[1];
|
||||
const token = res.headers.get('Authorization')?.split('Bearer ').pop();
|
||||
|
||||
if (!token) {
|
||||
form.errors.code = ["Une erreur s'est produite"];
|
||||
return fail(400, { form });
|
||||
return setError(form, 'code', "Une erreur est survenue, veuillez réessayer plus tard");
|
||||
}
|
||||
|
||||
cookies.set('session', token, {
|
||||
|
@ -79,13 +79,9 @@ export const actions = {
|
|||
}
|
||||
|
||||
if (res.status === 400) {
|
||||
form.errors.code = ['Code invalide'];
|
||||
} else {
|
||||
form.errors.code = [`Une erreur s'est produite`];
|
||||
return setError(form, 'code', "Le code de confirmation est incorrect");
|
||||
}
|
||||
|
||||
return fail(400, {
|
||||
form
|
||||
});
|
||||
return setError(form, 'code', "Une erreur est survenue, veuillez réessayer plus tard");
|
||||
}
|
||||
} satisfies Actions;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
import type { PageData, Snapshot } from './$types';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
import { Loader2 } from 'lucide-svelte';
|
||||
import Loader from 'lucide-svelte/icons/loader-circle';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { zodClient } from 'sveltekit-superforms/adapters';
|
||||
import { superForm } from 'sveltekit-superforms/client';
|
||||
|
||||
import * as Form from '$lib/components/ui/form';
|
||||
import Input from '$lib/components/ui/input/input.svelte';
|
||||
import { requestPasswordResetSchema } from '$lib/validations/auth';
|
||||
import { requestPasswordResetSchema, resetPasswordSchema } from '$lib/validations/auth';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
|||
validators: zodClient(requestPasswordResetSchema),
|
||||
delayMs: 500,
|
||||
multipleSubmits: 'prevent',
|
||||
invalidateAll: false,
|
||||
onResult({ result }) {
|
||||
switch (result.type) {
|
||||
case 'success':
|
||||
|
@ -28,6 +29,7 @@
|
|||
resetPasswordFormData.set({
|
||||
...$requestPasswordResetFormData,
|
||||
password: '',
|
||||
confirm: '',
|
||||
code: ''
|
||||
});
|
||||
confirmation = true;
|
||||
|
@ -50,6 +52,7 @@
|
|||
} = requestPasswordResetForm;
|
||||
|
||||
const resetPasswordForm = superForm(data.resetPasswordForm, {
|
||||
validators: zodClient(resetPasswordSchema),
|
||||
delayMs: 500,
|
||||
multipleSubmits: 'prevent'
|
||||
});
|
||||
|
@ -59,18 +62,13 @@
|
|||
enhance: resetPasswordEnhance,
|
||||
delayed: resetPasswordDelayed
|
||||
} = resetPasswordForm;
|
||||
|
||||
export const snapshot: Snapshot = {
|
||||
capture: () => confirmation,
|
||||
restore: (value) => (confirmation = value)
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="container flex h-screen">
|
||||
<div class="flex w-full flex-col items-center justify-center">
|
||||
<div class="flex w-full max-w-xs flex-col gap-4">
|
||||
{#if confirmation}
|
||||
<h1 class="mx-auto text-xl font-bold">Confirmation</h1>
|
||||
<h2 class="mx-auto text-xl font-bold">Confirmation</h2>
|
||||
<form
|
||||
class="flex flex-col justify-center gap-2"
|
||||
method="POST"
|
||||
|
@ -107,6 +105,18 @@
|
|||
</Form.Control>
|
||||
<Form.FieldErrors />
|
||||
</Form.Field>
|
||||
<Form.Field form={resetPasswordForm} name="confirm">
|
||||
<Form.Control let:attrs>
|
||||
<Form.Label>Confirmer le mot de passe</Form.Label>
|
||||
<Input
|
||||
{...attrs}
|
||||
type="password"
|
||||
bind:value={$resetPasswordFormData.confirm}
|
||||
placeholder="************"
|
||||
/>
|
||||
</Form.Control>
|
||||
<Form.FieldErrors />
|
||||
</Form.Field>
|
||||
<Form.Field form={resetPasswordForm} name="code">
|
||||
<Form.Control let:attrs>
|
||||
<Form.Label>Code</Form.Label>
|
||||
|
@ -118,7 +128,7 @@
|
|||
|
||||
<Form.Button disabled={$resetPasswordDelayed}>
|
||||
{#if $resetPasswordDelayed}
|
||||
<Loader2 class="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
Changer le mot de passe
|
||||
</Form.Button>
|
||||
|
@ -157,7 +167,7 @@
|
|||
</Form.Field>
|
||||
<Form.Button disabled={$requestPasswordResetDelayed}>
|
||||
{#if $requestPasswordResetDelayed}
|
||||
<Loader2 class="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
Continuer
|
||||
</Form.Button>
|
||||
|
|
Loading…
Add table
Reference in a new issue