🐛 fixing profile data and some QA issues

This commit is contained in:
ChatonDeAru 2024-09-26 16:14:57 +02:00
parent 622841eed3
commit 4bc3a58b3e
No known key found for this signature in database
GPG Key ID: 1B89BB058F9E56AB
18 changed files with 121 additions and 61 deletions

View File

@ -1,5 +1,6 @@
[![Netlify Status](https://api.netlify.com/api/v1/badges/bcf66326-b808-4ffd-9018-fb24315fce5f/deploy-status)](https://app.netlify.com/sites/force-orange/deploys) [![Netlify Status](https://api.netlify.com/api/v1/badges/bcf66326-b808-4ffd-9018-fb24315fce5f/deploy-status)](https://app.netlify.com/sites/force-orange/deploys)
![Supabase](https://img.shields.io/badge/Supabase-3ECF8E?style=for-the-badge&logo=supabase&logoColor=white)
# ForceOrange # ForceOrange
Le site des bénévoles de Paris est Ludique ! Le site des bénévoles de Paris est Ludique !

View File

@ -11,4 +11,8 @@ auth.onAuthStateChange(async (event) => {
<NuxtLayout> <NuxtLayout>
<NuxtPage /> <NuxtPage />
</NuxtLayout> </NuxtLayout>
</template> </template>
<style>
@import '~/assets/main.css';
</style>

View File

@ -0,0 +1,3 @@
.position-unset {
position: unset;
}

View File

@ -14,7 +14,7 @@ const links = computed(() => {
if (!user.value) { if (!user.value) {
tmp.push({ tmp.push({
label: 'Rejoindre FO', label: 'S\'inscrire sur FO',
to: '/join' to: '/join'
}) })
} else { } else {
@ -31,6 +31,8 @@ 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('/') router.push('/')
// TOFIX
} }
</script> </script>

View File

@ -2,6 +2,7 @@
import { object, string, type InferType } from 'yup' import { object, string, 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 } from '#ui/types'
import { lowerString } from '~/utils/commons';
definePageMeta({ definePageMeta({
name: 'EmailUpdateForm', name: 'EmailUpdateForm',
@ -15,7 +16,7 @@ const { inputStyle, formGroupStyle } = useFoStyle()
const loading = ref(false) const loading = ref(false)
const schema = object({ const schema = object({
email: string().lowercase().trim().email('Il semble que l\'email ne soit pas bon').required('Champ obligatoire'), email: string().lowercase().trim().email('Il semble que l\'email ne soit pas bon').max(500, 'hmmmm c\'est un peu beaucoup là non ?').required('Champ obligatoire'),
}) })
type Schema = InferType<typeof schema> type Schema = InferType<typeof schema>
@ -29,7 +30,7 @@ async function onSave(event: FormSubmitEvent<Schema>) {
const formSubmit = event.data const formSubmit = event.data
const { data, error } = await auth.updateUser({ const { data, error } = await auth.updateUser({
email: formSubmit.email, email: lowerString(formSubmit.email),
}) })
if (error) { if (error) {

View File

@ -17,7 +17,7 @@ const { inputStyle, formGroupStyle } = useFoStyle()
const loading = ref(false) const loading = ref(false)
const schema = object({ const schema = object({
password: string().min(6, 'Il faudrait un minimum de 6 charactères').test({ password: string().min(6, 'Il faudrait un minimum de 6 charactères').max(500, 'hmmmm c\'est un peu beaucoup là non ?').test({
name: 'password', name: 'password',
message: 'Le mot de passe n\'est pas assez fort ~~', message: 'Le mot de passe n\'est pas assez fort ~~',
test: (value) => { test: (value) => {

View File

@ -1,13 +0,0 @@
export const useAuth = () => {
const user = useSupabaseUser()
const { auth } = useSupabaseClient()
auth.onAuthStateChange(async (event) => {
if (event === 'SIGNED_OUT')
navigateTo('/')
})
return {
user
}
}

View File

@ -2,7 +2,7 @@ export const useFoStyle = () => {
return { return {
inputStyle: { inputStyle: {
ui: { ui: {
placeholder: 'placeholder-transparent', placeholder: 'placeholder-transparent dark:placeholder-transparent',
}, },
attrs: { attrs: {
variant: 'none' variant: 'none'
@ -10,10 +10,11 @@ export const useFoStyle = () => {
}, },
formGroupStyle: { formGroupStyle: {
ui: { 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', wrapper: 'flex flex-col relative md:flex-row rounded-lg py-1 px-4 border-b-2 border-orange-500 has-[:focus]:bg-orange-100 dark:border-orange-500 dark:has-[:focus]:bg-orange-800 has-[:focus]:border-t-2 has-[:focus]:border-x-2 has-[:focus]:border-b-0 mb-6',
inner: 'flex-2 content-center', inner: 'flex-2 content-center',
container: 'flex-1 mt-auto relative', container: 'flex-1 mt-auto position-unset',
error: 'text-red-500 text-sm absolute top[-100%] left-0 mt-2 pointer-events-none', error: 'text-red-500 text-sm absolute top[-100%] left-0 mt-2 pointer-events-none',
help: 'absolute top[-100%] left-0 mt-2 pointer-events-none',
}, },
}, },
} }

View File

@ -7,7 +7,9 @@ export const useProfile = () => {
const profile = ref<{ const profile = ref<{
firstname: string firstname: string
lastname: string lastname: string
displayname?: string displayname: string | null
email: string
is_validated: boolean
} | null>(null) } | null>(null)
const displayName = computed(() => { const displayName = computed(() => {
@ -37,8 +39,13 @@ export const useProfile = () => {
return return
} }
const { data, error } = await client.from('profiles').select('id,firstname,lastname,displayname,mail').eq('id', user.value.id).single() const { data, error } = await client.from('profiles').select('id,firstname,lastname,displayname,email,is_validated').eq('id', user.value.id).single()
return data
if (!error) {
return data
}
return null
}) })
if (error.value) { if (error.value) {
@ -53,6 +60,8 @@ export const useProfile = () => {
watch(user, (user) => { watch(user, (user) => {
if (user && !loading.value) { if (user && !loading.value) {
getProfile() getProfile()
} else {
profile.value = null
} }
}, { immediate: true }) }, { immediate: true })

View File

@ -2,6 +2,7 @@
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, FormErrorEvent } from '#ui/types' import type { FormSubmitEvent, FormErrorEvent } from '#ui/types'
import { lowerString } from '~/utils/commons';
definePageMeta({ definePageMeta({
name: 'Join', name: 'Join',
@ -16,8 +17,8 @@ const toast = useToast()
const { auth } = useSupabaseClient<Database>() const { auth } = useSupabaseClient<Database>()
const schema = object({ const schema = object({
email: string().lowercase().trim().email('Il semble que l\'email ne soit pas bon').required('Champ obligatoire'), email: string().lowercase().trim().max(500, 'hmmmm c\'est un peu beaucoup là non ?').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({ password: string().min(6, 'Il faudrait un minimum de 6 charactères').max(500, 'hmmmm c\'est un peu beaucoup là non ?').test({
name: 'password', name: 'password',
message: 'Le mot de passe n\'est pas assez fort ~~', message: 'Le mot de passe n\'est pas assez fort ~~',
test: (value) => { test: (value) => {
@ -25,9 +26,10 @@ const schema = object({
return testPassword(value).score >= 3 return testPassword(value).score >= 3
}, },
}).required('Champ obligatoire'), }).required('Champ obligatoire'),
firstname: string().lowercase().trim().required('Champ obligatoire'), firstname: string().trim().max(500, 'hmmmm c\'est un peu beaucoup là non ?').required('Champ obligatoire'),
lastname: string().lowercase().trim().required('Champ obligatoire'), lastname: string().trim().max(500, 'hmmmm c\'est un peu beaucoup là non ?').required('Champ obligatoire'),
isAdult: boolean().oneOf([true], 'Pour pouvoir t\'inscrire tu dois être majeur (ou bientôt)'), isAdult: boolean().oneOf([true], 'Pour pouvoir t\'inscrire tu dois être majeur (ou bientôt)'),
lastEdition: boolean(),
}) })
type Schema = InferType<typeof schema> type Schema = InferType<typeof schema>
@ -40,6 +42,7 @@ const state = reactive({
firstname: undefined, firstname: undefined,
lastname: undefined, lastname: undefined,
isAdult: false, isAdult: false,
lastEdition: false,
}) })
async function onSubmit(event: FormSubmitEvent<Schema>) { async function onSubmit(event: FormSubmitEvent<Schema>) {
@ -48,13 +51,14 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
// Do something with event.data // Do something with event.data
const formSubmit = event.data const formSubmit = event.data
const { data, error } = await auth.signUp({ const { data, error } = await auth.signUp({
email: formSubmit.email, email: lowerString(formSubmit.email),
password: formSubmit.password, password: formSubmit.password,
options: { options: {
data: { data: {
firstname: formSubmit.firstname, firstname: lowerString(formSubmit.firstname),
lastname: formSubmit.lastname, lastname: lowerString(formSubmit.lastname),
is_adult: formSubmit.isAdult, is_adult: formSubmit.isAdult,
last_edition: formSubmit.lastEdition,
}, },
emailRedirectTo: `${config.public.baseUrl}/join/valid`, emailRedirectTo: `${config.public.baseUrl}/join/valid`,
}, },
@ -96,7 +100,7 @@ async function onError(event: FormErrorEvent) {
formulaire ci-dessous: formulaire ci-dessous:
</p> </p>
<UForm ref="form" :schema="schema" :state="state" @submit="onSubmit" @error="onError"> <UForm ref="form" :schema="schema" :state="state" @submit="onSubmit" @error="onError" class="my-8">
<UFormGroup :ui="formGroupStyle.ui" label="Adresse courriel" name="email"> <UFormGroup :ui="formGroupStyle.ui" label="Adresse courriel" name="email">
<template #default="{ error }"> <template #default="{ error }">
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" placeholder="Adresse courriel" v-model="state.email" <UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" placeholder="Adresse courriel" v-model="state.email"
@ -121,11 +125,14 @@ async function onError(event: FormErrorEvent) {
<div class="flex md:flex-row flex-col gap-4 mb-4"> <div class="flex md:flex-row flex-col gap-4 mb-4">
<div class="flex-1"> <div class="flex-1">
<UFormGroup :ui="formGroupStyle.ui" label="Prénom" name="firstname"> <UFormGroup :ui="formGroupStyle.ui" label="Prénom (le vrai !)" name="firstname">
<template #default="{ error }"> <template #default="{ error }">
<UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" placeholder="Roger" v-model="state.firstname" <UInput :ui="inputStyle.ui" v-bind="inputStyle.attrs" placeholder="Roger" v-model="state.firstname"
:trailing-icon="error ? 'i-heroicons-exclamation-triangle-20-solid' : undefined" /> :trailing-icon="error ? 'i-heroicons-exclamation-triangle-20-solid' : undefined" />
</template> </template>
<template #help>
Attention pas de pseudos/surnom ici ! (sinon nous ne pouvons pas valider ton profil)
</template>
</UFormGroup> </UFormGroup>
</div> </div>
@ -139,11 +146,19 @@ async function onError(event: FormErrorEvent) {
</div> </div>
</div> </div>
<UFormGroup name="lastEdition" class="my-8">
<UCheckbox label="J'ai participer à la précédente édition ?" v-model="state.lastEdition" />
<template #help>
Dis nous si tu étais déjà bénévole à la dernière édition.<br>
Surtout ne mens pas, on vous connait ! (et on a les photos)
</template>
</UFormGroup>
<UFormGroup name="isAdult" class="mb-4"> <UFormGroup name="isAdult" class="mb-4">
<UCheckbox label="Je serais majeur avant la prochaine édition." v-model="state.isAdult" /> <UCheckbox label="Je serais majeur avant la prochaine édition." v-model="state.isAdult" />
</UFormGroup> </UFormGroup>
<UButton type="submit" :loading="isJoining" :disabled="isJoining">S'inscrire</UButton> <UButton color="orange" type="submit" :loading="isJoining" :disabled="isJoining">S'inscrire</UButton>
</UForm> </UForm>
</UCard> </UCard>
</template> </template>

View File

@ -17,13 +17,19 @@ const isEmailModalOpen = ref(false)
<div class="grid gap-8 md:grid-cols-2"> <div class="grid gap-8 md:grid-cols-2">
<UCard class="md:col-span-2"> <UCard class="md:col-span-2">
<h1 class="text-2xl">{{ displayName }}</h1> <h1 class="text-2xl">{{ displayName }}</h1>
<p v-if="!profile?.is_validated">
Ton compte sera bientôt validé pour les organisateurs du festival Paris Est Ludique.<br>
</p>
<p v-else>Ton compte a été validé ! Prêt pour Paris Est Ludique 2025 ?</p>
</UCard> </UCard>
<UCard> <UCard>
<h1 class="text-2xl uppercase mb-4">Authentification</h1> <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>Gérer mes manières de me connecter au site de Force Orange.</p>
<p class="text-orange-500 mt-4">{{ profile.mail }}</p> <p class="text-orange-500 mt-4">{{ profile?.email }}</p>
<div class="my-4 flex flex-col gap-4"> <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 variant="soft" icon="i-mdi-email" @click="isEmailModalOpen = true">Modifier mon adresse de couriel

View File

@ -2,6 +2,7 @@
import { object, string, type InferType } from 'yup' import { object, string, 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 } from '#ui/types'
import { lowerString } from '~/utils/commons';
definePageMeta({ definePageMeta({
name: 'SigninForgot', name: 'SigninForgot',
@ -31,7 +32,7 @@ async function onSend(event: FormSubmitEvent<Schema>) {
loading.value = true loading.value = true
const formSubmit = event.data const formSubmit = event.data
const { data, error } = await auth.resetPasswordForEmail(formSubmit.email, { const { data, error } = await auth.resetPasswordForEmail(lowerString(formSubmit.email), {
redirectTo: `${config.public.baseUrl}/profile/auth/reset`, redirectTo: `${config.public.baseUrl}/profile/auth/reset`,
}) })
@ -59,7 +60,7 @@ async function onSend(event: FormSubmitEvent<Schema>) {
<template> <template>
<UCard v-if="sended" class="container mx-auto max-w-screen-md"> <UCard v-if="sended" class="container mx-auto max-w-screen-md">
<h1 class="text-2xl uppercase mb-8">Cest partit</h1> <h1 class="text-2xl uppercase mb-8">Cest parti</h1>
<p> <p>
Si nous avons connaissance de cette adresse, vous devriez recevoir un courriel contenant les instructions pour Si nous avons connaissance de cette adresse, vous devriez recevoir un courriel contenant les instructions pour
récupérer votre compte Force Orange. récupérer votre compte Force Orange.

View File

@ -17,7 +17,7 @@ const { inputStyle, formGroupStyle } = useFoStyle()
const schema = object({ const schema = object({
email: string().lowercase().trim().email('Invalid email').required('Required'), email: string().lowercase().trim().email('Invalid email').required('Required'),
password: string().lowercase().trim().required('Required'), password: string().trim().required('Required'),
}) })
type Schema = InferType<typeof schema> type Schema = InferType<typeof schema>

View File

@ -0,0 +1,10 @@
export const stringToSlug = (str: string) => {
return str
.toLowerCase()
.replace(/ /g, '-')
.replace(/[^\w-]+/g, '')
}
export const lowerString = (str: string) => {
return str.trim().toLowerCase()
}

View File

@ -1,22 +1,40 @@
const preset = {
border: ''
}
export default defineAppConfig({ export default defineAppConfig({
ui: { ui: {
primary: 'orange', primary: 'orange',
gray: 'slate',
button: { button: {
colors: {
orange: {
solid: 'border-x-2 border-b-2 border-orange-600 dark:border-orange-900 active:border-t-2 active:border-b-0',
}
},
variant: {
solid: 'border-x-2 border-b-2 border-{color}-600 dark:border-{color}-900 active:border-t-2 active:border-b-0',
},
default: { default: {
color: 'primary', size: 'md',
}, },
}, },
input: { input: {
base: 'focus:bg-orange-100', colors: {
orange: {
base: 'dark:focus:bg-orange-800 focus:bg-orange-100',
}
},
base: 'dark:focus:bg-{color}-800 focus:bg-{color}-100',
default: { default: {
size: 'md', size: 'md',
color: 'primary',
}, },
}, },
formGroup: { formGroup: {
label: { label: {
base: 'font-thin' base: 'font-thin'
}, },
help: 'text-gray-400 text-xs leading-tight',
default: { default: {
size: 'md', size: 'md',
}, },

View File

@ -5,8 +5,6 @@ const currentDir = dirname(fileURLToPath(import.meta.url))
// https://nuxt.com/docs/api/configuration/nuxt-config // https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({ export default defineNuxtConfig({
compatibilityDate: '2024-09-11',
devtools: { enabled: true }, devtools: { enabled: true },
extends: ['@nuxt/ui-pro'], extends: ['@nuxt/ui-pro'],
@ -44,6 +42,10 @@ export default defineNuxtConfig({
}, },
], ],
ui: {
safelistColors: ['orange', 'slate']
},
fonts: { fonts: {
families: [ families: [
{ {

View File

@ -9,25 +9,25 @@ export default <Partial<Config>>{
sans: ['Londrina Solid', 'DM Sans', ...defaultTheme.fontFamily.sans] sans: ['Londrina Solid', 'DM Sans', ...defaultTheme.fontFamily.sans]
}, },
colors: { colors: {
'orange': { orange: {
'50': '#fffbec', 50: '#fffbec',
'100': '#fff6d3', 100: '#fff6d3',
'200': '#ffe9a5', 200: '#ffe9a5',
'300': '#ffd76d', 300: '#ffd76d',
'400': '#ffba32', 400: '#ffba32',
'500': '#ffa20a', 500: '#ffa20a',
'600': '#ff8a00', 600: '#ff8a00',
'700': '#cc6502', 700: '#cc6502',
'800': '#a14e0b', 800: '#a14e0b',
'900': '#82410c', 900: '#82410c',
'950': '#461f04', 950: '#461f04',
}, },
'black': { black: {
'500': '#303030', 500: '#303030',
} }
}, },
fontSize: { fontSize: {
'hlogo': '45px', hlogo: '45px',
}, },
boxShadow: { boxShadow: {
'input-orange': 'inset 0 0 0 1px #ffa20a', 'input-orange': 'inset 0 0 0 1px #ffa20a',

View File

@ -27,7 +27,7 @@ export type Database = {
is_validated: boolean is_validated: boolean
last_validation_date: string | null last_validation_date: string | null
lastname: string | null lastname: string | null
mail: string email: string
pel_editions: number[] pel_editions: number[]
phone_number: number | null phone_number: number | null
picture_url: string | null picture_url: string | null
@ -49,7 +49,7 @@ export type Database = {
is_validated?: boolean is_validated?: boolean
last_validation_date?: string | null last_validation_date?: string | null
lastname?: string | null lastname?: string | null
mail: string email: string
pel_editions?: number[] pel_editions?: number[]
phone_number?: number | null phone_number?: number | null
picture_url?: string | null picture_url?: string | null
@ -71,7 +71,7 @@ export type Database = {
is_validated?: boolean is_validated?: boolean
last_validation_date?: string | null last_validation_date?: string | null
lastname?: string | null lastname?: string | null
mail?: string email?: string
pel_editions?: number[] pel_editions?: number[]
phone_number?: number | null phone_number?: number | null
picture_url?: string | null picture_url?: string | null