111 lines
2.6 KiB
Vue
111 lines
2.6 KiB
Vue
<template>
|
|
<UAuthForm
|
|
:fields="fields"
|
|
:schema="schema"
|
|
:providers="providers"
|
|
:title="$t('login.title')"
|
|
icon="i-lucide-lock"
|
|
@submit="onSubmit($event)"
|
|
>
|
|
<template #footer>
|
|
{{ $t('login.agree') }}
|
|
<ULink
|
|
to="/"
|
|
class="text-primary font-medium"
|
|
>
|
|
{{ $t('login.terms') }}
|
|
</ULink>.
|
|
</template>
|
|
</UAuthForm>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import * as z from 'zod'
|
|
import type { FormSubmitEvent } from '@nuxt/ui'
|
|
import type { AuthFormField } from '@nuxt/ui/runtime/components/AuthForm.vue.js'
|
|
|
|
definePageMeta({
|
|
layout: 'empty'
|
|
})
|
|
|
|
const { t } = useI18n()
|
|
const toast = useToast()
|
|
|
|
// The otpId is used to track the OTP request and verify the user
|
|
// null if no request was sent yet
|
|
const otpId = ref<string | null>(null)
|
|
|
|
const fields = computed(() => {
|
|
const fields: AuthFormField[] = [{
|
|
name: 'email',
|
|
type: 'text',
|
|
label: t('login.email'),
|
|
placeholder: t('login.emailPlaceholder'),
|
|
required: true
|
|
}]
|
|
if (otpId.value !== null) {
|
|
fields.push({
|
|
name: 'otp',
|
|
type: 'otp',
|
|
label: t('login.otp'),
|
|
length: 6,
|
|
size: 'xl'
|
|
})
|
|
}
|
|
return fields
|
|
})
|
|
|
|
const { signInWithEmail, signInWithOAuth, signInWithOtp } = useUser()
|
|
|
|
const oAuthAndRedirect = async (provider: 'apple' | 'google') => {
|
|
try {
|
|
await signInWithOAuth(provider)
|
|
navigateTo('/confirm')
|
|
} catch (error) {
|
|
toast.add({ color: 'error', description: (error as Error).message })
|
|
}
|
|
}
|
|
|
|
const providers = [
|
|
{
|
|
label: 'Google',
|
|
icon: 'i-simple-icons-google',
|
|
onClick: () => oAuthAndRedirect('google')
|
|
},
|
|
{
|
|
label: 'Apple',
|
|
icon: 'i-simple-icons-apple',
|
|
onClick: () => oAuthAndRedirect('apple')
|
|
}
|
|
]
|
|
|
|
const schema = z.object({
|
|
email: z.email(t('login.invalidEmail')),
|
|
otp: z.array(z.string()).optional().transform(val => val?.join(''))
|
|
})
|
|
|
|
type Schema = z.output<typeof schema>
|
|
|
|
const { locale } = useI18n()
|
|
async function onSubmit(payload: FormSubmitEvent<Schema>) {
|
|
try {
|
|
if (otpId.value !== null && payload.data.otp?.length) {
|
|
// If OTP is provided, sign in with OTP
|
|
await signInWithOtp(otpId.value, payload.data.otp)
|
|
navigateTo('/confirm')
|
|
} else {
|
|
// If OTP is not provided, sign in with email (send OTP)
|
|
otpId.value = await signInWithEmail(payload.data.email, locale.value)
|
|
toast.add({
|
|
title: t('login.emailSent'),
|
|
description: t('login.emailSentDescription'),
|
|
icon: 'i-lucide-check',
|
|
color: 'success'
|
|
})
|
|
}
|
|
} catch (error) {
|
|
toast.add({ color: 'error', description: (error as Error).message })
|
|
}
|
|
}
|
|
</script>
|