mizuki-express/frontend/src/views/RegisterView.vue
2025-06-08 17:59:26 +02:00

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>