mirror of
https://github.com/Paris-est-Ludique/ForceOrange.git
synced 2025-06-08 01:04:20 +02:00
✨ Complete all user management with forms and DB
This commit is contained in:
parent
37b2238b84
commit
5fa1f24caf
@ -1,3 +1,12 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const { auth } = useSupabaseClient()
|
||||||
|
|
||||||
|
auth.onAuthStateChange(async (event) => {
|
||||||
|
if (event === 'SIGNED_OUT')
|
||||||
|
navigateTo('/')
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NuxtLayout>
|
<NuxtLayout>
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
|
@ -5,38 +5,57 @@ defineOptions({
|
|||||||
name: 'FOHeader',
|
name: 'FOHeader',
|
||||||
})
|
})
|
||||||
|
|
||||||
const links = [{
|
const router = useRouter()
|
||||||
label: 'Les actus',
|
const { user, displayName, waitingMailValidation, loading } = useProfile()
|
||||||
to: '/'
|
|
||||||
}, {
|
|
||||||
label: 'Rejoindre FO',
|
|
||||||
to: '/join'
|
|
||||||
}]
|
|
||||||
|
|
||||||
const user = useSupabaseUser()
|
|
||||||
const { auth } = useSupabaseClient<Database>()
|
const { auth } = useSupabaseClient<Database>()
|
||||||
|
|
||||||
|
const links = computed(() => {
|
||||||
|
const tmp = []
|
||||||
|
|
||||||
|
if (!user.value) {
|
||||||
|
tmp.push({
|
||||||
|
label: 'Rejoindre FO',
|
||||||
|
to: '/join'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
tmp.push({
|
||||||
|
label: 'Les actus',
|
||||||
|
to: '/news'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmp
|
||||||
|
})
|
||||||
|
|
||||||
const signOut = async () => {
|
const signOut = async () => {
|
||||||
const { error } = await auth.signOut()
|
const { error } = await auth.signOut()
|
||||||
if (error) console.log(error)
|
if (error) console.log(error)
|
||||||
|
router.push('/')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UHeader :links="links" class="bg-white dark:bg-gray-900 rounded-xl shadow-lg mx-4 mt-4">
|
<UHeader :links="links" class="bg-white dark:bg-gray-900 rounded-xl shadow-lg mx-4 mt-4">
|
||||||
<template #left>
|
<template #left>
|
||||||
<!-- <p class="font-logo text-orange-500 stroke-5 stroke-black-500 text-hlogo">Force Orange</p> -->
|
<NuxtLink to="/">
|
||||||
<NuxtImg src="/assets/img/logo-fo.svg" alt="Force Orange" />
|
<NuxtImg src="/assets/img/logo-fo.svg" alt="Force Orange" />
|
||||||
|
</NuxtLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #right>
|
<template #right>
|
||||||
<UColorModeButton />
|
<UColorModeButton />
|
||||||
{{ user?.user_metadata.firstname }}
|
|
||||||
<UButton v-if="user" @click="signOut" variant="soft">Se déconnecter</UButton>
|
<template v-if="user">
|
||||||
|
<UButton to="/profile" :loading="loading" icon="i-mdi-account-circle">
|
||||||
|
{{ displayName }}
|
||||||
|
</UButton>
|
||||||
|
<UButton @click="signOut" variant="soft">Se déconnecter</UButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
<UButton v-else to="/signin" variant="soft">Se connecter</UButton>
|
<UButton v-else to="/signin" variant="soft">Se connecter</UButton>
|
||||||
</template>
|
</template>
|
||||||
</UHeader>
|
</UHeader>
|
||||||
|
|
||||||
<UNotification v-if="user && !user?.email_confirmed_at" title="N'oubliez pas de confirmer votre adresse de courriel !"
|
<UNotification v-if="waitingMailValidation" title="N'oublies pas de confirmer ton adresse de courriel !" :id="1"
|
||||||
:id="1" :timeout="0" />
|
:timeout="0" />
|
||||||
</template>
|
</template>
|
@ -20,22 +20,40 @@ const color = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UMeter :value="strength.score" :max="4" :color="color">
|
<div>
|
||||||
<template #label>
|
<p class="text-sm">
|
||||||
<p class="text-sm font-thin">
|
force de ton mot de passe :
|
||||||
<template v-if="strength.score === 1">
|
</p>
|
||||||
Le mot de passe est faible
|
<div class="flex">
|
||||||
|
|
||||||
|
<UMeter :value="strength.score" :max="4" :color="color">
|
||||||
|
<template #label>
|
||||||
|
<p class="text-sm font-thin">
|
||||||
|
<template v-if="strength.score === 1">
|
||||||
|
Le mot de passe est faible
|
||||||
|
</template>
|
||||||
|
<template v-else-if="strength.score === 2">
|
||||||
|
Le mot de passe est moyen
|
||||||
|
</template>
|
||||||
|
<template v-else-if="strength.score === 3">
|
||||||
|
Le mot de passe est bon
|
||||||
|
</template>
|
||||||
|
<template v-else-if="strength.score === 4">
|
||||||
|
Le mot de passe est fort
|
||||||
|
</template>
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="strength.score === 2">
|
</UMeter>
|
||||||
Le mot de passe est moyen
|
<UPopover>
|
||||||
|
<UIcon name="i-mdi-info-circle" color="blue" class="w-5 h-5 m-2 hover:text-blue-300" />
|
||||||
|
<template #panel>
|
||||||
|
<p class="max-w-sm p-4 text-gray-400 font-thin">
|
||||||
|
Un mot bon mot de passe est souvent lié au nombres de charactères (une phrase est un bon mot de
|
||||||
|
passe) et à sa complexité à deviner (non, votre code postal et le nom de votre lapin n'est pas un bon motde
|
||||||
|
passe)
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="strength.score === 3">
|
</UPopover>
|
||||||
Le mot de passe est bon
|
</div>
|
||||||
</template>
|
</div>
|
||||||
<template v-else-if="strength.score === 4">
|
|
||||||
Le mot de passe est fort
|
|
||||||
</template>
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
</UMeter>
|
|
||||||
</template>
|
</template>
|
4
modules/app/components/WIPNotif.vue
Normal file
4
modules/app/components/WIPNotif.vue
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<template>
|
||||||
|
<UAlert color="amber" class="my-4" icon="i-mdi-dice" title="Ça arrive bientôt !!!"
|
||||||
|
description="Force Orange est encore en cours de développement mais sera fréquement mis à jour ! en attendant surveille bien ta boîte de courriel avec les Gazettes de PEL." />
|
||||||
|
</template>
|
77
modules/app/components/profile/EmailUpdateForm.vue
Normal file
77
modules/app/components/profile/EmailUpdateForm.vue
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<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: 'EmailUpdateForm',
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['success', 'error'])
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
const { auth } = useSupabaseClient<Database>()
|
||||||
|
const { inputStyle, formGroupStyle } = useFoStyle()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
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 onSave(event: FormSubmitEvent<Schema>) {
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
const formSubmit = event.data
|
||||||
|
const { data, error } = await auth.updateUser({
|
||||||
|
email: formSubmit.email,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.log(error)
|
||||||
|
toast.add({
|
||||||
|
title: 'Erreur',
|
||||||
|
description: 'Une erreur est survenue lors de l\'envoi du nouveau courriel',
|
||||||
|
color: 'red',
|
||||||
|
})
|
||||||
|
|
||||||
|
emit('error')
|
||||||
|
} else {
|
||||||
|
console.log(data)
|
||||||
|
|
||||||
|
toast.add({
|
||||||
|
title: 'Adresse de courriel changé',
|
||||||
|
description: 'Nouveau courriel sauvegardé, vérifie tes mails pour confirmer le changement',
|
||||||
|
color: 'orange',
|
||||||
|
})
|
||||||
|
|
||||||
|
emit('success')
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1 class="text-2xl uppercase">Modification de votre adresse de couriel</h1>
|
||||||
|
|
||||||
|
<UForm :schema="schema" :state="state" @submit="onSave">
|
||||||
|
<UFormGroup :ui="formGroupStyle.ui" label="Nouvelle adresse courriel" name="email">
|
||||||
|
<template #default="{ error }">
|
||||||
|
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" placeholder="Adresse courriel" type="email"
|
||||||
|
v-model="state.email" :trailing-icon="error ? 'i-heroicons-exclamation-triangle-20-solid' : undefined" />
|
||||||
|
</template>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<p class="mt-8 text-center">
|
||||||
|
<UButton type="submit" variant="soft" :disabled="loading" :loading="loading">
|
||||||
|
Sauvegarder la nouvelle adresse
|
||||||
|
</UButton>
|
||||||
|
</p>
|
||||||
|
</UForm>
|
||||||
|
</template>
|
88
modules/app/components/profile/PasswordUpdateForm.vue
Normal file
88
modules/app/components/profile/PasswordUpdateForm.vue
Normal 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: 'PasswordUpdateForm',
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['success', 'error'])
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
const { auth } = useSupabaseClient<Database>()
|
||||||
|
const { testPassword } = usePasswordStrength()
|
||||||
|
const { inputStyle, formGroupStyle } = useFoStyle()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
const schema = object({
|
||||||
|
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'),
|
||||||
|
})
|
||||||
|
|
||||||
|
type Schema = InferType<typeof schema>
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
password: undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
async function onSave(event: FormSubmitEvent<Schema>) {
|
||||||
|
loading.value = true
|
||||||
|
const formSubmit = event.data
|
||||||
|
|
||||||
|
const { data, error } = await auth.updateUser({
|
||||||
|
password: formSubmit.password,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.log(error)
|
||||||
|
toast.add({
|
||||||
|
title: 'Erreur',
|
||||||
|
description: 'Une erreur est survenue lors de l\'envoi du nouveau mot de passe',
|
||||||
|
color: 'red',
|
||||||
|
})
|
||||||
|
|
||||||
|
emit('error')
|
||||||
|
} else {
|
||||||
|
console.log(data)
|
||||||
|
|
||||||
|
toast.add({
|
||||||
|
title: 'Mot de passe changé',
|
||||||
|
description: 'Nouveau mot de passe sauvegardé, vérifie tes mails pour confirmer le changement',
|
||||||
|
color: 'orange',
|
||||||
|
})
|
||||||
|
|
||||||
|
emit('success')
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1 class="text-2xl uppercase">Réinitialiser votre mot de passe</h1>
|
||||||
|
<p>Nous pouvons envoyer un lien de récupération à l’adresse courriel associé à votre compte.</p>
|
||||||
|
|
||||||
|
<UForm :schema="schema" :state="state" @submit="onSave" class="my-8">
|
||||||
|
<UFormGroup :ui="formGroupStyle.ui" label="Nouveau 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>
|
||||||
|
|
||||||
|
<PasswordStrength v-model="state.password" />
|
||||||
|
|
||||||
|
<p class="mt-8 text-center">
|
||||||
|
<UButton type="submit" variant="soft" :disabled="loading" :loading="loading">Sauvegarder le nouveau mot de passe
|
||||||
|
</UButton>
|
||||||
|
</p>
|
||||||
|
</UForm>
|
||||||
|
</template>
|
13
modules/app/composables/auth.ts
Normal file
13
modules/app/composables/auth.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export const useAuth = () => {
|
||||||
|
const user = useSupabaseUser()
|
||||||
|
const { auth } = useSupabaseClient()
|
||||||
|
|
||||||
|
auth.onAuthStateChange(async (event) => {
|
||||||
|
if (event === 'SIGNED_OUT')
|
||||||
|
navigateTo('/')
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
user
|
||||||
|
}
|
||||||
|
}
|
20
modules/app/composables/foStyle.ts
Normal file
20
modules/app/composables/foStyle.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export const useFoStyle = () => {
|
||||||
|
return {
|
||||||
|
inputStyle: {
|
||||||
|
ui: {
|
||||||
|
placeholder: 'placeholder-transparent',
|
||||||
|
},
|
||||||
|
attrs: {
|
||||||
|
variant: 'none'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formGroupStyle: {
|
||||||
|
ui: {
|
||||||
|
wrapper: 'flex flex-col md:flex-row rounded-lg py-1 px-4 border-b-2 border-orange-500 has-[:focus]:bg-orange-100 has-[:focus]:border-t-2 has-[:focus]:border-x-2 has-[:focus]:border-b-0 mb-6',
|
||||||
|
inner: 'flex-2 content-center',
|
||||||
|
container: 'flex-1 mt-auto relative',
|
||||||
|
error: 'text-red-500 text-sm absolute top[-100%] left-0 mt-2 pointer-events-none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@ import { zxcvbn, zxcvbnOptions, type ZxcvbnResult } from '@zxcvbn-ts/core'
|
|||||||
import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common'
|
import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common'
|
||||||
import * as zxcvbnEnPackage from '@zxcvbn-ts/language-fr'
|
import * as zxcvbnEnPackage from '@zxcvbn-ts/language-fr'
|
||||||
|
|
||||||
export const usePasswordStrength = (password: Ref<string | undefined>) => {
|
export const usePasswordStrength = (password?: Ref<string | undefined>) => {
|
||||||
|
|
||||||
// 0 # too guessable: risky password. (guesses < 10 ^ 3)
|
// 0 # too guessable: risky password. (guesses < 10 ^ 3)
|
||||||
// 1 # very guessable: protection from throttled online attacks. (guesses < 10 ^ 6)
|
// 1 # very guessable: protection from throttled online attacks. (guesses < 10 ^ 6)
|
||||||
@ -25,18 +25,25 @@ export const usePasswordStrength = (password: Ref<string | undefined>) => {
|
|||||||
|
|
||||||
zxcvbnOptions.setOptions(options)
|
zxcvbnOptions.setOptions(options)
|
||||||
|
|
||||||
watchDebounced(password, (newPassword) => {
|
function testPassword(password: string) {
|
||||||
if (!newPassword) {
|
return zxcvbn(password.trim())
|
||||||
strength.value = {
|
}
|
||||||
score: 0,
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
strength.value = zxcvbn(newPassword.trim())
|
if (password) {
|
||||||
}, { immediate: true, debounce: 500 })
|
watchDebounced(password, (newPassword) => {
|
||||||
|
if (!newPassword) {
|
||||||
|
strength.value = {
|
||||||
|
score: 0,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
strength.value = zxcvbn(newPassword.trim())
|
||||||
|
}, { immediate: true, debounce: 500 })
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
strength,
|
strength,
|
||||||
|
testPassword,
|
||||||
}
|
}
|
||||||
}
|
}
|
66
modules/app/composables/profile.ts
Normal file
66
modules/app/composables/profile.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
export const useProfile = () => {
|
||||||
|
const user = useSupabaseUser()
|
||||||
|
const client = useSupabaseClient()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const profile = ref<{
|
||||||
|
firstname: string
|
||||||
|
lastname: string
|
||||||
|
displayname?: string
|
||||||
|
} | null>(null)
|
||||||
|
|
||||||
|
const displayName = computed(() => {
|
||||||
|
if (!profile.value) {
|
||||||
|
return '...'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { firstname, lastname, displayname } = profile.value || {}
|
||||||
|
|
||||||
|
if (!displayname) {
|
||||||
|
return `${firstname || ''} ${lastname || ''}`.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
return displayname
|
||||||
|
})
|
||||||
|
|
||||||
|
const waitingMailValidation = computed(() => {
|
||||||
|
return user.value && !user.value?.email_confirmed_at
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
async function getProfile() {
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
const { data, error } = await useAsyncData('profiles', async () => {
|
||||||
|
if (!user.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await client.from('profiles').select('id,firstname,lastname,displayname,mail').eq('id', user.value.id).single()
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
toast.add({ color: 'red', description: error.value?.message, title: 'Error' })
|
||||||
|
} else {
|
||||||
|
profile.value = data.value
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(user, (user) => {
|
||||||
|
if (user && !loading.value) {
|
||||||
|
getProfile()
|
||||||
|
}
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
return {
|
||||||
|
user,
|
||||||
|
loading,
|
||||||
|
profile,
|
||||||
|
displayName,
|
||||||
|
waitingMailValidation,
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ export default defineNuxtConfig({
|
|||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
public: {
|
public: {
|
||||||
env: '',
|
env: '',
|
||||||
|
baseUrl: process.env.NUXT_PUBLIC_BASE_URL || 'http://localhost:3000',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ export default defineNuxtConfig({
|
|||||||
redirectOptions: {
|
redirectOptions: {
|
||||||
login: '/signin',
|
login: '/signin',
|
||||||
callback: '/signin/confirm',
|
callback: '/signin/confirm',
|
||||||
exclude: ['/signin/*', '/join', '/public/*'],
|
exclude: ['/profile/auth/reset', '/signin/*', '/join', '/join/*', '/public/*'],
|
||||||
},
|
},
|
||||||
cookieName: 'fo-cookies',
|
cookieName: 'fo-cookies',
|
||||||
cookieOptions: {
|
cookieOptions: {
|
||||||
|
@ -5,7 +5,12 @@ definePageMeta({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<WIPNotif />
|
||||||
<h1>Home</h1>
|
<UCard class="container mx-auto max-w-screen-md">
|
||||||
</div>
|
<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>
|
</template>
|
@ -1,27 +1,41 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { object, string, boolean, type InferType } from 'yup'
|
import { object, string, boolean, type InferType } from 'yup'
|
||||||
import type { Database } from '@pel/supabase/types'
|
import type { Database } from '@pel/supabase/types'
|
||||||
import type { FormSubmitEvent } from '#ui/types'
|
import type { FormSubmitEvent, FormErrorEvent } from '#ui/types'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
name: 'Join',
|
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 { auth } = useSupabaseClient<Database>()
|
||||||
|
|
||||||
const schema = object({
|
const schema = object({
|
||||||
mail: string().email('Invalid email').required('Required'),
|
email: string().lowercase().trim().email('Il semble que l\'email ne soit pas bon').required('Champ obligatoire'),
|
||||||
password: string().min(6).required('Required'),
|
password: string().min(6, 'Il faudrait un minimum de 6 charactères').test({
|
||||||
firstname: string().required('Required'),
|
name: 'password',
|
||||||
lastname: string().required('Required'),
|
message: 'Le mot de passe n\'est pas assez fort ~~',
|
||||||
isAdult: boolean().required('Required'),
|
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>
|
type Schema = InferType<typeof schema>
|
||||||
|
|
||||||
|
const form = ref<HTMLFormElement | null>(null)
|
||||||
|
const isJoining = ref(false)
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
mail: undefined,
|
email: undefined,
|
||||||
password: undefined,
|
password: undefined,
|
||||||
firstname: undefined,
|
firstname: undefined,
|
||||||
lastname: undefined,
|
lastname: undefined,
|
||||||
@ -29,12 +43,12 @@ const state = reactive({
|
|||||||
})
|
})
|
||||||
|
|
||||||
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||||
|
isJoining.value = true
|
||||||
|
|
||||||
// Do something with event.data
|
// Do something with event.data
|
||||||
const formSubmit = event.data
|
const formSubmit = event.data
|
||||||
console.log(event.data)
|
|
||||||
|
|
||||||
const { data, error } = await auth.signUp({
|
const { data, error } = await auth.signUp({
|
||||||
email: formSubmit.mail,
|
email: formSubmit.email,
|
||||||
password: formSubmit.password,
|
password: formSubmit.password,
|
||||||
options: {
|
options: {
|
||||||
data: {
|
data: {
|
||||||
@ -42,46 +56,94 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
|
|||||||
lastname: formSubmit.lastname,
|
lastname: formSubmit.lastname,
|
||||||
is_adult: formSubmit.isAdult,
|
is_adult: formSubmit.isAdult,
|
||||||
},
|
},
|
||||||
|
emailRedirectTo: `${config.public.baseUrl}/join/valid`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO
|
if (error) {
|
||||||
// [x] create member in database if user logged in
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UCard class="container mx-auto">
|
<UCard class="container mx-auto max-w-screen-lg">
|
||||||
<h1 class="text-2xl uppercase">Rejoindre Paris est Ludique!</h1>
|
<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
|
<p class="font-light">Pour rejoindre la Force orange de Paris est Ludique!, il est nécessaire de compléter le
|
||||||
formulaire ci-dessous:
|
formulaire ci-dessous:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<UForm :schema="schema" :state="state" @submit="onSubmit">
|
<UForm ref="form" :schema="schema" :state="state" @submit="onSubmit" @error="onError">
|
||||||
<UFormGroup label="Adresse courriel" eager-validation>
|
<UFormGroup :ui="formGroupStyle.ui" label="Adresse courriel" name="email">
|
||||||
<UInput placeholder="Adresse courriel" name="mail" type="email" v-model="state.mail" />
|
<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>
|
||||||
|
|
||||||
<UFormGroup label="Password" eager-validation>
|
<div class="flex md:flex-row flex-col gap-4 my-4">
|
||||||
<UInput placeholder="Password" type="password" v-model="state.password" />
|
<div class="flex-1">
|
||||||
</UFormGroup>
|
<UFormGroup :ui="formGroupStyle.ui" label="Mot de passe" name="password">
|
||||||
<PasswordStrength v-model="state.password" />
|
<template #default="{ error }">
|
||||||
|
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" placeholder="***" type="password"
|
||||||
<div class="flex md:flex-row flex-col gap-4">
|
v-model="state.password"
|
||||||
<UFormGroup label="Prénom" class="flex-1" eager-validation>
|
:trailing-icon="error ? 'i-heroicons-exclamation-triangle-20-solid' : undefined" />
|
||||||
<UInput placeholder="Prénom" name="firstname" v-model="state.firstname" />
|
</template>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
</div>
|
||||||
<UFormGroup label="Nom de famille" class="flex-1" eager-validation>
|
<div class="flex-1">
|
||||||
<UInput placeholder="Nom de famille" name="lastname" v-model="state.lastname" />
|
<PasswordStrength v-model="state.password" />
|
||||||
</UFormGroup>
|
</div>
|
||||||
</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>
|
</UForm>
|
||||||
</UCard>
|
</UCard>
|
||||||
</template>
|
</template>
|
27
modules/app/pages/join/valid.vue
Normal file
27
modules/app/pages/join/valid.vue
Normal 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>
|
25
modules/app/pages/join/waiting.vue
Normal file
25
modules/app/pages/join/waiting.vue
Normal 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>
|
12
modules/app/pages/news.vue
Normal file
12
modules/app/pages/news.vue
Normal 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>
|
11
modules/app/pages/profile/auth/reset.vue
Normal file
11
modules/app/pages/profile/auth/reset.vue
Normal 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>
|
49
modules/app/pages/profile/index.vue
Normal file
49
modules/app/pages/profile/index.vue
Normal 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>
|
@ -1,6 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Database } from '@pel/supabase/types'
|
import type { Database } from '@pel/supabase/types'
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
name: 'SigninConfirm',
|
||||||
|
})
|
||||||
|
|
||||||
const user = useSupabaseUser()
|
const user = useSupabaseUser()
|
||||||
const { auth } = useSupabaseClient<Database>()
|
const { auth } = useSupabaseClient<Database>()
|
||||||
|
|
||||||
@ -19,9 +23,9 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<UCard class="container mx-auto max-w-screen-md">
|
||||||
<p class="u-text-black">
|
<p class="u-text-black">
|
||||||
Redirecting...
|
Redirecting...
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</UCard>
|
||||||
</template>
|
</template>
|
||||||
|
88
modules/app/pages/signin/forgot.vue
Normal file
88
modules/app/pages/signin/forgot.vue
Normal 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 à l’adresse 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">C’est 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>
|
@ -7,26 +7,32 @@ definePageMeta({
|
|||||||
name: 'Signin',
|
name: 'Signin',
|
||||||
})
|
})
|
||||||
|
|
||||||
const user = useSupabaseUser()
|
const router = useRouter()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
const { auth } = useSupabaseClient<Database>()
|
const { auth } = useSupabaseClient<Database>()
|
||||||
|
|
||||||
|
const { inputStyle, formGroupStyle } = useFoStyle()
|
||||||
|
|
||||||
const schema = object({
|
const schema = object({
|
||||||
mail: string().email('Invalid email').required('Required'),
|
email: string().lowercase().trim().email('Invalid email').required('Required'),
|
||||||
password: string().required('Required'),
|
password: string().lowercase().trim().required('Required'),
|
||||||
})
|
})
|
||||||
|
|
||||||
type Schema = InferType<typeof schema>
|
type Schema = InferType<typeof schema>
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
mail: undefined,
|
email: undefined,
|
||||||
password: undefined,
|
password: undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
async function onSignin(event: FormSubmitEvent<Schema>) {
|
async function onSignin(event: FormSubmitEvent<Schema>) {
|
||||||
|
loading.value = true
|
||||||
const formSubmit = event.data
|
const formSubmit = event.data
|
||||||
|
|
||||||
const { data, error } = await auth.signInWithPassword({
|
const { data, error } = await auth.signInWithPassword({
|
||||||
email: formSubmit.mail,
|
email: formSubmit.email,
|
||||||
password: formSubmit.password
|
password: formSubmit.password
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -36,41 +42,56 @@ async function onSignin(event: FormSubmitEvent<Schema>) {
|
|||||||
// options: {
|
// options: {
|
||||||
// // set this to false if you do not want the user to be automatically signed up
|
// // set this to false if you do not want the user to be automatically signed up
|
||||||
// shouldCreateUser: false,
|
// 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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<UCard class="container mx-auto max-w-screen-md">
|
||||||
|
<h1 class="text-2xl uppercase mb-4">Qui êtes-vous ?</h1>
|
||||||
|
|
||||||
<UCard>
|
<UForm :schema="schema" :state="state" @submit="onSignin">
|
||||||
<h1 class="text-2xl uppercase">Qui êtes-vous ?</h1>
|
<UFormGroup :ui="formGroupStyle.ui" label="Adresse courriel" name="email">
|
||||||
|
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" v-model="state.email" />
|
||||||
<UForm :state="state" @submit="onSignin">
|
|
||||||
<UFormGroup label="Adresse courriel">
|
|
||||||
<UInput name="mail" v-model="state.mail" />
|
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup label="Mot de passe">
|
<UFormGroup :ui="formGroupStyle.ui" label="Mot de passe" name="password">
|
||||||
<UInput type="password" name="password" v-model="state.password" />
|
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" type="password" v-model="state.password" />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UButton type="submit">Se Connecter</UButton>
|
<div class="mt-8 md:justify-center flex flex-col md:flex-row gap-8">
|
||||||
<UButton to="/join">Mot de passe oublié ?</UButton>
|
<UButton type="submit" :disabled="loading" :loading="loading">Se Connecter</UButton>
|
||||||
|
<UButton :to="{ name: 'SigninForgot' }" variant="ghost">Mot de passe oublié ?</UButton>
|
||||||
|
</div>
|
||||||
</UForm>
|
</UForm>
|
||||||
|
|
||||||
<!-- <p class="text-error">Nous n’avons pas reconnu vos identifiants.</p> -->
|
<!-- <p>ou bien</p>
|
||||||
|
|
||||||
<p>ou bien</p>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<UButton>Se connecter avec Google</UButton>
|
<UButton>Se connecter avec Google</UButton>
|
||||||
<UButton>Se connecter avec Discord</UButton>
|
<UButton>Se connecter avec Discord</UButton>
|
||||||
</div>
|
</div> -->
|
||||||
</UCard>
|
</UCard>
|
||||||
</template>
|
</template>
|
@ -7,17 +7,13 @@ export default defineAppConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
placeholder: 'placeholder-transparent',
|
base: 'focus:bg-orange-100',
|
||||||
default: {
|
default: {
|
||||||
size: 'md',
|
size: 'md',
|
||||||
color: 'primary',
|
color: 'primary',
|
||||||
variant: 'none'
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
formGroup: {
|
formGroup: {
|
||||||
wrapper: 'flex flex-col md:flex-row rounded-lg py-1 px-4 border-b-2 border-orange-500',
|
|
||||||
inner: 'flex-2 content-center',
|
|
||||||
container: 'flex-1 mt-auto',
|
|
||||||
label: {
|
label: {
|
||||||
base: 'font-thin'
|
base: 'font-thin'
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user