Complete all user management with forms and DB

This commit is contained in:
ChatonDeAru
2024-09-15 01:48:41 +02:00
committed by ChatonDeAru (Romain)
parent 37b2238b84
commit 5fa1f24caf
22 changed files with 730 additions and 108 deletions

View File

@@ -5,7 +5,12 @@ definePageMeta({
</script>
<template>
<div>
<h1>Home</h1>
</div>
<WIPNotif />
<UCard class="container mx-auto max-w-screen-md">
<h1 class="text-2xl uppercase">Bienvenue sur Force Orange</h1>
<p class="font-light">
Tu retrouveras toutes les informations nécessaires pour t'accompagner dans la préparation du festival Paris Est
Ludique 2025.
</p>
</UCard>
</template>

View File

@@ -1,27 +1,41 @@
<script setup lang="ts">
import { object, string, boolean, type InferType } from 'yup'
import type { Database } from '@pel/supabase/types'
import type { FormSubmitEvent } from '#ui/types'
import type { FormSubmitEvent, FormErrorEvent } from '#ui/types'
definePageMeta({
name: 'Join',
})
const user = useSupabaseUser()
const { testPassword } = usePasswordStrength()
const { inputStyle, formGroupStyle } = useFoStyle()
const config = useRuntimeConfig()
const router = useRouter()
const toast = useToast()
const { auth } = useSupabaseClient<Database>()
const schema = object({
mail: string().email('Invalid email').required('Required'),
password: string().min(6).required('Required'),
firstname: string().required('Required'),
lastname: string().required('Required'),
isAdult: boolean().required('Required'),
email: string().lowercase().trim().email('Il semble que l\'email ne soit pas bon').required('Champ obligatoire'),
password: string().min(6, 'Il faudrait un minimum de 6 charactères').test({
name: 'password',
message: 'Le mot de passe n\'est pas assez fort ~~',
test: (value) => {
if (!value) return false
return testPassword(value).score >= 3
},
}).required('Champ obligatoire'),
firstname: string().lowercase().trim().required('Champ obligatoire'),
lastname: string().lowercase().trim().required('Champ obligatoire'),
isAdult: boolean().oneOf([true], 'Pour pouvoir t\'inscrire tu dois être majeur (ou bientôt)'),
})
type Schema = InferType<typeof schema>
const form = ref<HTMLFormElement | null>(null)
const isJoining = ref(false)
const state = reactive({
mail: undefined,
email: undefined,
password: undefined,
firstname: undefined,
lastname: undefined,
@@ -29,12 +43,12 @@ const state = reactive({
})
async function onSubmit(event: FormSubmitEvent<Schema>) {
isJoining.value = true
// Do something with event.data
const formSubmit = event.data
console.log(event.data)
const { data, error } = await auth.signUp({
email: formSubmit.mail,
email: formSubmit.email,
password: formSubmit.password,
options: {
data: {
@@ -42,46 +56,94 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
lastname: formSubmit.lastname,
is_adult: formSubmit.isAdult,
},
emailRedirectTo: `${config.public.baseUrl}/join/valid`,
},
})
// TODO
// [x] create member in database if user logged in
if (error) {
console.log(error)
toast.add({
title: 'Erreur',
description: 'Une erreur est survenue lors de l\'inscription',
color: 'red',
})
} else {
console.log(data)
// Redirect to the waiting page
toast.add({
title: 'Super !',
description: 'On a bien reçu ton inscription, un email de validation t\'a été envoyé',
color: 'green',
})
router.push({ name: 'JoinWaiting' })
}
if (error) console.log(error)
isJoining.value = false
}
async function onError(event: FormErrorEvent) {
console.log(event.errors)
const element = document.getElementById(event.errors[0].id)
element?.focus()
element?.scrollIntoView({ behavior: 'smooth', block: 'center' })
}
</script>
<template>
<UCard class="container mx-auto">
<h1 class="text-2xl uppercase">Rejoindre Paris est Ludique!</h1>
<UCard class="container mx-auto max-w-screen-lg">
<h1 class="text-2xl uppercase mb-4">Rejoindre Paris est Ludique!</h1>
<p class="font-light">Pour rejoindre la Force orange de Paris est Ludique!, il est nécessaire de compléter le
formulaire ci-dessous:
</p>
<UForm :schema="schema" :state="state" @submit="onSubmit">
<UFormGroup label="Adresse courriel" eager-validation>
<UInput placeholder="Adresse courriel" name="mail" type="email" v-model="state.mail" />
<UForm ref="form" :schema="schema" :state="state" @submit="onSubmit" @error="onError">
<UFormGroup :ui="formGroupStyle.ui" label="Adresse courriel" name="email">
<template #default="{ error }">
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" placeholder="Adresse courriel" v-model="state.email"
:trailing-icon="error ? 'i-heroicons-exclamation-triangle-20-solid' : undefined" />
</template>
</UFormGroup>
<UFormGroup label="Password" eager-validation>
<UInput placeholder="Password" type="password" v-model="state.password" />
</UFormGroup>
<PasswordStrength v-model="state.password" />
<div class="flex md:flex-row flex-col gap-4">
<UFormGroup label="Prénom" class="flex-1" eager-validation>
<UInput placeholder="Prénom" name="firstname" v-model="state.firstname" />
</UFormGroup>
<UFormGroup label="Nom de famille" class="flex-1" eager-validation>
<UInput placeholder="Nom de famille" name="lastname" v-model="state.lastname" />
</UFormGroup>
<div class="flex md:flex-row flex-col gap-4 my-4">
<div class="flex-1">
<UFormGroup :ui="formGroupStyle.ui" label="Mot de passe" name="password">
<template #default="{ error }">
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" placeholder="***" type="password"
v-model="state.password"
:trailing-icon="error ? 'i-heroicons-exclamation-triangle-20-solid' : undefined" />
</template>
</UFormGroup>
</div>
<div class="flex-1">
<PasswordStrength v-model="state.password" />
</div>
</div>
<UCheckbox name="isAdult" label="Je serais majeur avant la prochaine édition." v-model="state.isAdult" />
<div class="flex md:flex-row flex-col gap-4 mb-4">
<div class="flex-1">
<UFormGroup :ui="formGroupStyle.ui" label="Prénom" name="firstname">
<template #default="{ error }">
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" placeholder="Roger" v-model="state.firstname"
:trailing-icon="error ? 'i-heroicons-exclamation-triangle-20-solid' : undefined" />
</template>
</UFormGroup>
</div>
<UButton type="submit">S'inscrire</UButton>
<div class="flex-1">
<UFormGroup :ui="formGroupStyle.ui" label="Nom de famille" name="lastname">
<template #default="{ error }">
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" placeholder="Rabbit" v-model="state.lastname"
:trailing-icon="error ? 'i-heroicons-exclamation-triangle-20-solid' : undefined" />
</template>
</UFormGroup>
</div>
</div>
<UFormGroup name="isAdult" class="mb-4">
<UCheckbox label="Je serais majeur avant la prochaine édition." v-model="state.isAdult" />
</UFormGroup>
<UButton type="submit" :loading="isJoining" :disabled="isJoining">S'inscrire</UButton>
</UForm>
</UCard>
</template>

View File

@@ -0,0 +1,27 @@
<script setup lang="ts">
definePageMeta({
name: 'JoinValid',
})
const user = useSupabaseUser()
// TODO : Add the necessary code to handle the validation of the user's email address
// http://localhost:3000/join/valid?error=access_denied&error_code=403&error_description=Email+link+is+invalid+or+has+expired#error=access_denied&error_code=403&error_description=Email+link+is+invalid+or+has+expired
</script>
<template>
<UCard class="container mx-auto max-w-screen-md">
<h1 class="text-2xl uppercase mb-4">Bravo ! Tu as vaillament réussi à valider ton compte Force Orange</h1>
<p class="font-light">
Tu peux maintenant te connecter à ton compte et profiter de toutes les fonctionnalités de Force Orange.
</p>
<UButton v-if="user" to="/" color="primary" class="mt-4">
Retour à l'accueil
</UButton>
<UButton v-else to="/signin" color="primary" class="mt-4">
Se connecter
</UButton>
</UCard>
</template>

View File

@@ -0,0 +1,25 @@
<script setup lang="ts">
definePageMeta({
name: 'JoinWaiting',
})
const user = useSupabaseUser()
</script>
<template>
<UCard class="container mx-auto max-w-screen-md">
<h1 class="text-2xl uppercase mb-4">En attente de validation</h1>
<p class="font-light">
Un email de validation t'a été envoyé à ton adresse de courriel.<br>
Merci de cliquer sur le lien pour valider ton inscription sur Force Orange.<br>
</p>
<UButton v-if="user" to="/" color="primary" class="mt-4">
Retour à l'accueil
</UButton>
<UButton v-else to="/signin" color="primary" class="mt-4">
Se connecter
</UButton>
</UCard>
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
definePageMeta({
name: 'News',
})
</script>
<template>
<WIPNotif />
<UCard class="container mx-auto max-w-screen-md">
<h1 class="text-2xl uppercase">Les actus ça sera ici !</h1>
</UCard>
</template>

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
definePageMeta({
name: 'ProfileAuthResetPassword',
})
</script>
<template>
<UCard class="container mx-auto max-w-screen-md">
<PasswordUpdateForm />
</UCard>
</template>

View File

@@ -0,0 +1,49 @@
<script setup lang="ts">
import EmailUpdateForm from '~/components/profile/EmailUpdateForm.vue';
definePageMeta({
name: 'Profile',
})
const { displayName, profile } = useProfile()
const isPasswordModalOpen = ref(false)
const isEmailModalOpen = ref(false)
</script>
<template>
<WIPNotif />
<div class="grid gap-8 md:grid-cols-2">
<UCard class="md:col-span-2">
<h1 class="text-2xl">{{ displayName }}</h1>
</UCard>
<UCard>
<h1 class="text-2xl uppercase mb-4">Authentification</h1>
<p>Gérer mes manières de me connecter au site de Force Orange.</p>
<p class="text-orange-500 mt-4">{{ profile.mail }}</p>
<div class="my-4 flex flex-col gap-4">
<UButton variant="soft" icon="i-mdi-email" @click="isEmailModalOpen = true">Modifier mon adresse de couriel
</UButton>
<UButton variant="soft" icon="i-mdi-form-textbox-password" @click="isPasswordModalOpen = true">Modifier mon mot
de passe
</UButton>
</div>
</UCard>
<UModal v-model="isEmailModalOpen">
<div class="p-4">
<EmailUpdateForm @success="isEmailModalOpen = false" />
</div>
</UModal>
<UModal v-model="isPasswordModalOpen">
<div class="p-4">
<PasswordUpdateForm @success="isPasswordModalOpen = false" />
</div>
</UModal>
</div>
</template>

View File

@@ -1,6 +1,10 @@
<script setup lang="ts">
import type { Database } from '@pel/supabase/types'
definePageMeta({
name: 'SigninConfirm',
})
const user = useSupabaseUser()
const { auth } = useSupabaseClient<Database>()
@@ -19,9 +23,9 @@ onMounted(async () => {
</script>
<template>
<div>
<UCard class="container mx-auto max-w-screen-md">
<p class="u-text-black">
Redirecting...
</p>
</div>
</UCard>
</template>

View File

@@ -0,0 +1,88 @@
<script setup lang="ts">
import { object, string, type InferType } from 'yup'
import type { Database } from '@pel/supabase/types'
import type { FormSubmitEvent } from '#ui/types'
definePageMeta({
name: 'SigninForgot',
})
const config = useRuntimeConfig()
const router = useRouter()
const toast = useToast()
const loading = ref(false)
const sended = ref(false)
const { auth } = useSupabaseClient<Database>()
const { inputStyle, formGroupStyle } = useFoStyle()
const schema = object({
email: string().lowercase().trim().email('Il semble que l\'email ne soit pas bon').required('Champ obligatoire'),
})
type Schema = InferType<typeof schema>
const state = reactive({
email: undefined,
})
async function onSignin(event: FormSubmitEvent<Schema>) {
loading.value = true
const formSubmit = event.data
const { data, error } = await auth.resetPasswordForEmail(formSubmit.email, {
redirectTo: `${config.public.baseUrl}/profile/auth/reset`,
})
if (error) {
console.log(error)
toast.add({
title: 'Erreur',
description: 'Une erreur est survenue lors de l\'envoi du lien de récupération',
color: 'red',
})
} else {
console.log(data)
toast.add({
title: 'Va regarder ta boite de courriel !',
description: 'Nous avons envoyé un lien de récupération à votre adresse courriel',
color: 'orange',
})
}
loading.value = false
sended.value = true
}
</script>
<template>
<UCard class="container mx-auto max-w-screen-md">
<h1 class="text-2xl uppercase mb-4">Réinitialiser votre mot de passe</h1>
<p>Nous pouvons envoyer un lien de récupération à ladresse courriel associé à votre compte.</p>
<UForm :schema="schema" :state="state" @submit="onSignin" class="my-8">
<UFormGroup :ui="formGroupStyle.ui" label="Adresse courriel" name="email">
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" v-model="state.email" />
</UFormGroup>
<p class="mt-8 text-center">
<UButton type="submit" variant="soft" :disabled="loading" :loading="loading">Récupérer mon compte</UButton>
</p>
</UForm>
</UCard>
<UCard v-if="sended">
<h1 class="text-2xl uppercase mb-8">Cest partit</h1>
<p>
Si nous avons connaissance de cette adresse, vous devriez recevoir un courriel contenant les instructions pour
récupérer votre compte Force Orange.
</p>
<p class="my-8">
<UButton to="/" variant="ghost">
Fermer cette page
</UButton>
</p>
</UCard>
</template>

View File

@@ -7,26 +7,32 @@ definePageMeta({
name: 'Signin',
})
const user = useSupabaseUser()
const router = useRouter()
const toast = useToast()
const loading = ref(false)
const { auth } = useSupabaseClient<Database>()
const { inputStyle, formGroupStyle } = useFoStyle()
const schema = object({
mail: string().email('Invalid email').required('Required'),
password: string().required('Required'),
email: string().lowercase().trim().email('Invalid email').required('Required'),
password: string().lowercase().trim().required('Required'),
})
type Schema = InferType<typeof schema>
const state = reactive({
mail: undefined,
email: undefined,
password: undefined,
})
async function onSignin(event: FormSubmitEvent<Schema>) {
loading.value = true
const formSubmit = event.data
const { data, error } = await auth.signInWithPassword({
email: formSubmit.mail,
email: formSubmit.email,
password: formSubmit.password
})
@@ -36,41 +42,56 @@ async function onSignin(event: FormSubmitEvent<Schema>) {
// options: {
// // set this to false if you do not want the user to be automatically signed up
// shouldCreateUser: false,
// emailRedirectTo: 'https://example.com/welcome',
// emailRedirectTo: 'https://example.com/waiting',
// },
// })
// TODO confirm signin
if (error) {
console.log(error)
toast.add({
title: 'Erreur',
description: 'Une erreur est survenue lors de ton inscription',
color: 'red',
})
} else {
console.log(data)
if (error) console.log(error)
toast.add({
title: 'Connecté(e)',
description: 'Que la force Orange soit avec toi !',
color: 'orange',
})
router.push('/')
}
loading.value = false
}
</script>
<template>
<UCard class="container mx-auto max-w-screen-md">
<h1 class="text-2xl uppercase mb-4">Qui êtes-vous ?</h1>
<UCard>
<h1 class="text-2xl uppercase">Qui êtes-vous ?</h1>
<UForm :state="state" @submit="onSignin">
<UFormGroup label="Adresse courriel">
<UInput name="mail" v-model="state.mail" />
<UForm :schema="schema" :state="state" @submit="onSignin">
<UFormGroup :ui="formGroupStyle.ui" label="Adresse courriel" name="email">
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" v-model="state.email" />
</UFormGroup>
<UFormGroup label="Mot de passe">
<UInput type="password" name="password" v-model="state.password" />
<UFormGroup :ui="formGroupStyle.ui" label="Mot de passe" name="password">
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" type="password" v-model="state.password" />
</UFormGroup>
<UButton type="submit">Se Connecter</UButton>
<UButton to="/join">Mot de passe oublié ?</UButton>
<div class="mt-8 md:justify-center flex flex-col md:flex-row gap-8">
<UButton type="submit" :disabled="loading" :loading="loading">Se Connecter</UButton>
<UButton :to="{ name: 'SigninForgot' }" variant="ghost">Mot de passe oublié ?</UButton>
</div>
</UForm>
<!-- <p class="text-error">Nous navons pas reconnu vos identifiants.</p> -->
<p>ou bien</p>
<!-- <p>ou bien</p>
<div>
<UButton>Se connecter avec Google</UButton>
<UButton>Se connecter avec Discord</UButton>
</div>
</div> -->
</UCard>
</template>