Refactor project structure and update dependencies
This commit is contained in:
57
nuxt/app/components/auth/AuthForgotPasswordForm.vue
Normal file
57
nuxt/app/components/auth/AuthForgotPasswordForm.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<script setup lang="ts">
|
||||
import type { IForgotPasswordResponse } from '~/types/account'
|
||||
|
||||
const form = useTemplateRef('form')
|
||||
|
||||
const state = reactive({
|
||||
email: '',
|
||||
})
|
||||
|
||||
const { refresh: onSubmit, status: forgotStatus } = useFetch<IForgotPasswordResponse>('forgot-password', {
|
||||
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) {
|
||||
useToast().add({
|
||||
title: 'Success',
|
||||
description: response._data.message,
|
||||
color: 'primary',
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm
|
||||
ref="form"
|
||||
:state="state"
|
||||
class="space-y-8"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<UFormField
|
||||
label="Email"
|
||||
name="email"
|
||||
>
|
||||
<UInput
|
||||
v-model="state.email"
|
||||
class="w-full"
|
||||
/>
|
||||
</UFormField>
|
||||
|
||||
<UButton
|
||||
block
|
||||
size="md"
|
||||
type="submit"
|
||||
:loading="forgotStatus === 'pending'"
|
||||
icon="i-heroicons-envelope"
|
||||
>
|
||||
Reset Password
|
||||
</UButton>
|
||||
</UForm>
|
||||
</template>
|
||||
173
nuxt/app/components/auth/AuthLoginForm.vue
Normal file
173
nuxt/app/components/auth/AuthLoginForm.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<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>
|
||||
91
nuxt/app/components/auth/AuthResetPasswordForm.vue
Normal file
91
nuxt/app/components/auth/AuthResetPasswordForm.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<script setup lang="ts">
|
||||
import type { IAccountResetPasswordResponse } from '~/types/account'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const auth = useAuthStore()
|
||||
const form = useTemplateRef('form')
|
||||
|
||||
const state = reactive({
|
||||
email: route.query.email as string,
|
||||
token: route.params.token,
|
||||
password: '',
|
||||
password_confirmation: '',
|
||||
})
|
||||
|
||||
const { refresh: onSubmit, status: resetStatus } = useFetch<IAccountResetPasswordResponse>('reset-password', {
|
||||
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) {
|
||||
useToast().add({
|
||||
title: 'Success',
|
||||
description: response._data.message,
|
||||
color: 'primary',
|
||||
})
|
||||
|
||||
if (auth.isLoggedIn) {
|
||||
await auth.fetchUser()
|
||||
await router.push('/')
|
||||
}
|
||||
else {
|
||||
await router.push('/login')
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm
|
||||
ref="form"
|
||||
:state="state"
|
||||
class="space-y-4"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<UFormField
|
||||
label="Email"
|
||||
name="email"
|
||||
>
|
||||
<UInput
|
||||
v-model="state.email"
|
||||
disabled
|
||||
/>
|
||||
</UFormField>
|
||||
|
||||
<UFormField
|
||||
label="Password"
|
||||
name="password"
|
||||
>
|
||||
<UInput
|
||||
v-model="state.password"
|
||||
type="password"
|
||||
/>
|
||||
</UFormField>
|
||||
|
||||
<UFormField
|
||||
label="Confirm Password"
|
||||
name="password_confirmation"
|
||||
>
|
||||
<UInput
|
||||
v-model="state.password_confirmation"
|
||||
type="password"
|
||||
/>
|
||||
</UFormField>
|
||||
|
||||
<UButton
|
||||
block
|
||||
size="md"
|
||||
type="submit"
|
||||
:loading="resetStatus === 'pending'"
|
||||
icon="i-heroicon-lock-closed"
|
||||
>
|
||||
Change Password
|
||||
</UButton>
|
||||
</UForm>
|
||||
</template>
|
||||
Reference in New Issue
Block a user