174 lines
4.0 KiB
Vue
174 lines
4.0 KiB
Vue
<script setup lang="ts">
|
|
import type { IAccountLoginResponse, IAccountProvider, IAccountProviderData } from '~/types/account'
|
|
import type { ButtonProps } from '#ui/components/Button.vue'
|
|
|
|
const config = useRuntimeConfig()
|
|
const router = useRouter()
|
|
const auth = useAuthStore()
|
|
const form = useTemplateRef('form')
|
|
|
|
const state = reactive({
|
|
email: '',
|
|
password: '',
|
|
remember: false,
|
|
})
|
|
|
|
const { refresh: onSubmit, status: loginStatus } = useFetch<IAccountLoginResponse>('login', {
|
|
method: 'POST',
|
|
body: state,
|
|
immediate: false,
|
|
watch: false,
|
|
async onResponse({ response }) {
|
|
if (response?.status === 422) {
|
|
form?.value?.setErrors(response._data?.errors)
|
|
}
|
|
else if (response._data?.ok) {
|
|
auth.token = response._data.token
|
|
|
|
await auth.fetchUser()
|
|
await router.push('/')
|
|
}
|
|
},
|
|
})
|
|
|
|
const providers = ref<{ [key: string]: IAccountProvider }>(
|
|
Object.fromEntries(
|
|
Object.entries(config.public.providers).map(([key, provider]) => [
|
|
key,
|
|
{
|
|
...provider,
|
|
color: provider.color as ButtonProps['color'],
|
|
},
|
|
]),
|
|
),
|
|
)
|
|
|
|
async function handleMessage(event: { data: IAccountProviderData }): Promise<void> {
|
|
const provider = event.data.provider as string
|
|
|
|
if (Object.keys(providers.value).includes(provider) && event.data.token) {
|
|
if (providers?.value[provider]?.loading) {
|
|
providers.value[provider].loading = false
|
|
}
|
|
auth.token = event.data.token
|
|
|
|
await auth.fetchUser()
|
|
await router.push('/')
|
|
}
|
|
else if (event.data.message) {
|
|
useToast().add({
|
|
icon: 'i-heroicons-exclamation-circle-solid',
|
|
color: 'error',
|
|
title: event.data.message,
|
|
})
|
|
}
|
|
}
|
|
|
|
function loginVia(provider: string): void {
|
|
providers.value[provider]!.loading = true
|
|
|
|
const width = 640
|
|
const height = 660
|
|
const left = window.screen.width / 2 - width / 2
|
|
const top = window.screen.height / 2 - height / 2
|
|
|
|
const popup = window.open(
|
|
`${config.public.apiBase}${config.public.apiPrefix}/login/${provider}/redirect`,
|
|
'Sign In',
|
|
`toolbar=no, location=no, directories=no, status=no, menubar=no, scollbars=no, resizable=no, copyhistory=no, width=${width},height=${height},top=${top},left=${left}`,
|
|
)
|
|
|
|
const interval = setInterval(() => {
|
|
if (!popup || popup.closed) {
|
|
clearInterval(interval)
|
|
providers.value[provider]!.loading = false
|
|
}
|
|
}, 500)
|
|
}
|
|
|
|
onMounted(() => window.addEventListener('message', handleMessage))
|
|
onBeforeUnmount(() => window.removeEventListener('message', handleMessage))
|
|
</script>
|
|
|
|
<template>
|
|
<UForm
|
|
ref="form"
|
|
:state="state"
|
|
class="space-y-4"
|
|
@submit="onSubmit"
|
|
>
|
|
<UFormField
|
|
label="Email"
|
|
name="email"
|
|
>
|
|
<UInput
|
|
v-model="state.email"
|
|
class="w-full"
|
|
/>
|
|
</UFormField>
|
|
|
|
<UFormField
|
|
label="Password"
|
|
name="password"
|
|
>
|
|
<UInput
|
|
v-model="state.password"
|
|
type="password"
|
|
class="w-full"
|
|
/>
|
|
</UFormField>
|
|
|
|
<div class="flex items-center justify-between">
|
|
<UCheckbox
|
|
id="remember-me"
|
|
v-model="state.remember"
|
|
label="Remember me"
|
|
name="remember-me"
|
|
/>
|
|
|
|
<div class="text-sm leading-6">
|
|
<NuxtLink
|
|
to="/forgot-password"
|
|
class="text-primary hover:text-primary-300 font-semibold"
|
|
>
|
|
Forgot
|
|
password?
|
|
</NuxtLink>
|
|
</div>
|
|
</div>
|
|
|
|
<UButton
|
|
block
|
|
size="md"
|
|
type="submit"
|
|
:loading="loginStatus === 'pending'"
|
|
icon="i-heroicons-arrow-right-on-rectangle"
|
|
>
|
|
Login
|
|
</UButton>
|
|
</UForm>
|
|
<USeparator
|
|
v-if="Object.keys(providers).length > 0"
|
|
color="neutral"
|
|
label="Login with"
|
|
class="my-4"
|
|
/>
|
|
<div class="flex gap-4">
|
|
<UButton
|
|
v-for="(provider, key) in providers"
|
|
:key="key"
|
|
:loading="provider.loading"
|
|
:icon="provider.icon"
|
|
:color="provider.color"
|
|
:label="provider.name"
|
|
size="lg"
|
|
class="w-full flex items-center justify-center"
|
|
@click="loginVia(key as string)"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
|
|
</style>
|