core/app/components/RegisterForm.vue

216 lines
8.1 KiB
Vue

<script setup lang="ts">
import { toast } from 'vue-sonner'
import * as z from 'zod'
useSeoMeta({
title: 'Daftar Akun',
ogTitle: 'Daftar Akun',
description: 'Ayo daftar dan nikmati layanan kami!',
ogDescription: 'Ayo daftar dan nikmati layanan kami!',
})
const config = useRuntimeConfig()
const router = useRouter()
const loading = ref(false)
const { signInWithGoogle, loading: googleLoading } = useGoogleAuth()
const fullName = ref('')
const email = ref('')
const username = ref('')
const password = ref('')
const passwordConfirmation = ref('')
const errors = ref<Record<string, string>>({})
const schema = z.object({
full_name: z.string().min(1, 'Nama lengkap wajib diisi').max(200, 'Nama lengkap maksimal 200 karakter'),
email: z.string().min(1, 'Email wajib diisi').email('Format email tidak valid').max(100, 'Email maksimal 100 karakter'),
username: z.string().min(1, 'Username wajib diisi').max(20, 'Username maksimal 20 karakter'),
password: z.string().min(8, 'Kata sandi minimal 8 karakter'),
password_confirmation: z.string().min(1, 'Konfirmasi kata sandi wajib diisi'),
}).refine(data => data.password === data.password_confirmation, {
message: 'Kata sandi tidak cocok',
path: ['password_confirmation'],
})
async function onSubmit() {
errors.value = {}
const result = schema.safeParse({
full_name: fullName.value,
email: email.value,
username: username.value,
password: password.value,
password_confirmation: passwordConfirmation.value,
})
if (!result.success) {
for (const issue of result.error.issues) {
const field = issue.path[0] as string
errors.value[field] = issue.message
}
return
}
loading.value = true
try {
await $fetch(`${config.public.apiBase}/auth/register`, {
method: 'POST',
body: {
full_name: fullName.value,
email: email.value,
username: username.value,
password: password.value,
password_confirmation: passwordConfirmation.value,
}
})
toast.success('Akun berhasil dibuat!', {
description: `Selamat datang, ${fullName.value}!`,
})
router.push('/')
} catch (error: any) {
const detail = error?.data?.detail
if (detail && Array.isArray(detail)) {
const fieldErrors = detail
.filter((e: any) => e.loc?.length >= 2)
.map((e: any) => ({
path: e.loc[e.loc.length - 1],
message: e.msg
}))
for (const fe of fieldErrors) {
errors.value[fe.path] = fe.message
}
} else {
toast.error('Gagal mendaftar', {
description: error?.data?.message || 'Ups, terjadi kesalahan. Silakan coba lagi beberapa saat.',
})
}
} finally {
loading.value = false
}
}
async function handleGoogleRegister() {
await signInWithGoogle(
(result) => {
toast.success('Berhasil mendaftar!', {
description: `Selamat datang, ${result.user.email}!`,
})
router.push('/')
},
(errorMsg) => {
toast.error('Gagal mendaftar', {
description: errorMsg,
})
}
)
}
</script>
<template>
<div class="flex flex-col gap-6">
<Card>
<CardHeader class="text-center">
<CardTitle class="text-xl">
Buat akun baru
</CardTitle>
<CardDescription>
Kelola bisnis Anda dengan mudah dan efisien bersama platform kami.
</CardDescription>
</CardHeader>
<CardContent>
<form @submit.prevent="onSubmit">
<FieldGroup>
<Field :class="{ 'text-destructive': errors.full_name }">
<FieldLabel for="full_name">
Nama Lengkap <span class="text-destructive">*</span>
</FieldLabel>
<Input id="full_name" v-model="fullName" type="text" placeholder="Masukkan nama lengkap"
:class="{ 'border-destructive': errors.full_name }" required />
<p v-if="errors.full_name" class="text-sm text-destructive mt-1">
{{ errors.full_name }}
</p>
</Field>
<Field :class="{ 'text-destructive': errors.email }">
<FieldLabel for="email">
Email <span class="text-destructive">*</span>
</FieldLabel>
<Input id="email" v-model="email" type="email" placeholder="Masukkan email"
:class="{ 'border-destructive': errors.email }" required />
<p v-if="errors.email" class="text-sm text-destructive mt-1">
{{ errors.email }}
</p>
</Field>
<Field :class="{ 'text-destructive': errors.username }">
<FieldLabel for="username">
Username <span class="text-destructive">*</span>
</FieldLabel>
<Input id="username" v-model="username" type="text" placeholder="Masukkan username"
:class="{ 'border-destructive': errors.username }" required />
<p v-if="errors.username" class="text-sm text-destructive mt-1">
{{ errors.username }}
</p>
</Field>
<Field :class="{ 'text-destructive': errors.password }">
<FieldLabel for="password">
Kata Sandi <span class="text-destructive">*</span>
</FieldLabel>
<Input id="password" v-model="password" type="password" placeholder="Buat kata sandi"
:class="{ 'border-destructive': errors.password }" required />
<p v-if="errors.password" class="text-sm text-destructive mt-1">
{{ errors.password }}
</p>
</Field>
<Field :class="{ 'text-destructive': errors.password_confirmation }">
<FieldLabel for="password_confirmation">
Konfirmasi Kata Sandi <span class="text-destructive">*</span>
</FieldLabel>
<Input id="password_confirmation" v-model="passwordConfirmation" type="password"
placeholder="Konfirmasi kata sandi"
:class="{ 'border-destructive': errors.password_confirmation }" required />
<p v-if="errors.password_confirmation" class="text-sm text-destructive mt-1">
{{ errors.password_confirmation }}
</p>
</Field>
<Field>
<Button type="submit" :disabled="loading">
<span v-if="loading" class="i-lucide-loader-2 h-4 w-4 animate-spin mr-2" />
Daftar
</Button>
<FieldDescription class="text-center">
Sudah punya akun?&nbsp;
<NuxtLink to="/auth/login" class="text-primary hover:underline">
Masuk
</NuxtLink>
</FieldDescription>
</Field>
<FieldSeparator class="*:data-[slot=field-separator-content]:bg-card">
Atau daftar dengan
</FieldSeparator>
<Field>
<Button variant="outline" type="button" :disabled="googleLoading" @click="handleGoogleRegister">
<span v-if="googleLoading" class="i-lucide-loader-2 h-4 w-4 animate-spin mr-2" />
<svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="h-4 w-4 mr-2">
<path
d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
fill="currentColor" />
</svg>
Daftar dengan Google
</Button>
</Field>
</FieldGroup>
</form>
</CardContent>
</Card>
<FieldDescription class="px-6 text-center">
Dengan mendaftar, Anda menyetujui
<NuxtLink to="/terms" class="text-primary hover:underline">
Ketentuan Layanan
</NuxtLink>
dan
<NuxtLink to="/privacy" class="text-primary hover:underline">
Kebijakan Privasi
</NuxtLink>.
</FieldDescription>
</div>
</template>