Files
veridian/app/pages/auth/register.vue
T
2026-01-11 05:04:29 -06:00

132 lines
4.5 KiB
Vue

<script setup lang="ts">
definePageMeta({
layout: 'auth',
})
const { signUp, session, fetchSession, authClient } = useAuth();
if (session.value !== null) {
navigateTo("/");
}
if (import.meta.server) {
if (process.env.DISABLE_SIGNUP?.toLowerCase() === "true" || process.env.DISABLE_SIGNUP === "1") {
navigateTo("/auth/login")
}
}
const form = reactive({
name: "",
email: "",
password: "",
confirmPassword: "",
});
const loading = ref(false);
let nameInputEl = ref<HTMLInputElement | null>(null);
let emailInputEl = ref<HTMLInputElement | null>(null);
let passwordInputEl = ref<HTMLInputElement | null>(null);
let confirmPasswordInputEl = ref<HTMLInputElement | null>(null);
onMounted(() => {
nameInputEl.value!.addEventListener("input", () => {
nameInputEl.value!.setCustomValidity("");
});
emailInputEl.value!.addEventListener("input", () => {
emailInputEl.value!.setCustomValidity("");
});
passwordInputEl.value!.addEventListener("input", () => {
passwordInputEl.value!.setCustomValidity("");
});
confirmPasswordInputEl.value!.addEventListener("input", () => {
if (passwordInputEl.value!.value !== confirmPasswordInputEl.value!.value) {
confirmPasswordInputEl.value!.setCustomValidity("Passwords do not match");
confirmPasswordInputEl.value!.reportValidity();
} else {
confirmPasswordInputEl.value!.setCustomValidity("");
}
});
});
const submit = async () => {
const inputs = [nameInputEl, emailInputEl, passwordInputEl, confirmPasswordInputEl];
for (const input of inputs) {
if (input.value?.validity.valid === false) {
input.value!.reportValidity();
return;
}
}
if (form.password !== form.confirmPassword) {
alert("Passwords do not match")
return;
}
loading.value = true;
await signUp.email({
name: form.name,
email: form.email,
password: form.password,
}, {
onSuccess: async () => {
await fetchSession();
navigateTo("/")
},
onError: (ctx) => {
const error = ctx.error.code as keyof typeof authClient.$ERROR_CODES;
// TODO: i18n
// ref https://www.better-auth.com/docs/concepts/client#error-codes
switch (error) {
case "USER_ALREADY_EXISTS":
case "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL":
emailInputEl.value!.setCustomValidity("Account with this email already exists");
emailInputEl.value!.reportValidity();
break;
case "INVALID_EMAIL":
emailInputEl.value!.setCustomValidity(ctx.error.message);
emailInputEl.value!.reportValidity();
break;
case "INVALID_PASSWORD":
passwordInputEl.value!.setCustomValidity(ctx.error.message);
passwordInputEl.value!.reportValidity();
break;
default:
console.log(ctx.error);
alert("Something went wrong")
break;
}
}
});
loading.value = false;
}
</script>
<template>
<h1 class="font-bold text-center mb-4">Register</h1>
<form class="flex flex-col [&>input]:mb-2" @submit.prevent="submit">
<label for="name">Name</label>
<input required ref="nameInputEl" type="text" id="name" v-model="form.name" />
<label for="email">Email</label>
<input required pattern=".{1,}@.{1,}\..{2,3}" ref="emailInputEl" type="email" autocomplete="email" id="email"
v-model="form.email" />
<label for="password">Password</label>
<input required minlength="8" maxlength="128" ref="passwordInputEl" type="password" autocomplete="new-password"
id="password" v-model="form.password" />
<label for="confirmPassword">Confirm Password</label>
<input required minlength="8" maxlength="128" ref="confirmPasswordInputEl" type="password"
autocomplete="new-password" id="confirmPassword" v-model="form.confirmPassword" />
<button class="accent" type="submit">
<Icon v-if="loading" class="text-6" name="svg-spinners:90-ring-with-bg" />
<span v-else>Register</span>
</button>
</form>
<p class="text-center">Already have an account? <a href="/auth/login">Login</a></p>
</template>