130 lines
4.6 KiB
Vue
130 lines
4.6 KiB
Vue
<script setup lang="ts">
|
|
import { ref, onMounted } from "vue";
|
|
import { useRouter, useRoute } from "vue-router";
|
|
import Password from "primevue/password";
|
|
import { Form, type FormResolverOptions, type FormSubmitEvent } from "@primevue/forms";
|
|
import InputText from "primevue/inputtext";
|
|
import Button from "primevue/button";
|
|
import Message from "primevue/message";
|
|
import Divider from "primevue/divider";
|
|
import { useToast } from 'primevue/usetoast';
|
|
import axiosClient from "@/api/axiosClient";
|
|
|
|
const toast = useToast();
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
|
|
const username = ref("");
|
|
const password = ref("");
|
|
const repeatPassword = ref("");
|
|
|
|
onMounted(() => {
|
|
if ("error" in route.query) {
|
|
toast.add({
|
|
severity: "error",
|
|
detail: route.query.error
|
|
});
|
|
}
|
|
});
|
|
|
|
const onFormSubmit = async (ev: FormSubmitEvent) => {
|
|
if (!ev.valid) {
|
|
toast.add({ severity: "error", detail: "Invalid data supplied." });
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const resp = await axiosClient.post("/api/user/register", {
|
|
username: username.value,
|
|
password: password.value
|
|
});
|
|
|
|
localStorage.setItem("token", resp.data.token);
|
|
toast.add({ severity: "success", detail: "Registered successfully!" });
|
|
await router.push("/");
|
|
|
|
} catch (err: any) {
|
|
const msg = err.response?.data?.reason || "Registration failed";
|
|
toast.add({ severity: "error", detail: msg });
|
|
}
|
|
};
|
|
|
|
const formResolver = (ev: FormResolverOptions): Record<string, any> => {
|
|
const resp: Record<string, any> = {
|
|
errors: {
|
|
Username: [],
|
|
Password: [],
|
|
RepeatPassword: []
|
|
}
|
|
};
|
|
|
|
const usernameRegex = /^[a-zA-Z0-9_.]*$/;
|
|
if (!ev.values["Username"]?.match(usernameRegex)) {
|
|
resp.errors["Username"] = ["Username contains illegal characters or is empty."];
|
|
}
|
|
|
|
const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@#$%^&*!])[A-Za-z\d@#$%^&*!]{8,}$/;
|
|
if (!ev.values["Password"]?.match(regex)) {
|
|
resp.errors["Password"] = ["Password doesn't meet requirements."];
|
|
}
|
|
|
|
if (ev.values["RepeatPassword"] !== ev.values["Password"]) {
|
|
resp.errors["RepeatPassword"] = ["Passwords do not match."];
|
|
}
|
|
|
|
return resp;
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="min-h-screen bg-gray-100 flex items-center justify-center px-4">
|
|
<div class="bg-white shadow-lg rounded-2xl p-8 w-full max-w-md">
|
|
<h1 class="text-4xl font-bold text-center text-amber-500 mb-6">Create Account</h1>
|
|
|
|
<Form v-slot="$form" :resolver="formResolver" :validateOnValueUpdate="true" :validateOnBlur="true" @submit="onFormSubmit" class="space-y-5">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Username</label>
|
|
<InputText v-model="username" name="Username" placeholder="Enter username" class="w-full" required />
|
|
<Message v-if="$form.Username?.invalid" severity="error" size="small" class="mt-1">{{ $form.Username.error }}</Message>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Password</label>
|
|
<Password v-model="password" name="Password" placeholder="Enter password" toggleMask class="w-full" required>
|
|
<template #header>
|
|
<h6 class="text-sm font-medium">Pick a secure password</h6>
|
|
</template>
|
|
<template #footer>
|
|
<Divider class="my-2" />
|
|
<p class="text-sm mb-1">Requirements:</p>
|
|
<ul class="text-sm text-gray-600 list-disc list-inside space-y-1">
|
|
<li>At least one lowercase</li>
|
|
<li>At least one uppercase</li>
|
|
<li>At least one numeric</li>
|
|
<li>At least one of @#$%^&*!</li>
|
|
<li>Minimum 8 characters</li>
|
|
</ul>
|
|
</template>
|
|
</Password>
|
|
<Message v-if="$form.Password?.invalid" severity="error" size="small" class="mt-1">{{ $form.Password.error }}</Message>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Repeat Password</label>
|
|
<Password v-model="repeatPassword" name="RepeatPassword" placeholder="Repeat password" toggleMask class="w-full" required />
|
|
<Message v-if="$form.RepeatPassword?.invalid" severity="error" size="small" class="mt-1">{{ $form.RepeatPassword.error }}</Message>
|
|
</div>
|
|
|
|
<Button type="submit" label="Register" class="w-full" severity="secondary" />
|
|
|
|
<div class="text-center text-sm text-gray-600">
|
|
Already have an account?
|
|
<a href="/login" class="text-amber-600 hover:underline">Log in here!</a>
|
|
</div>
|
|
</Form>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
</style>
|