Files
AceJobAgency/AceJobAgency.client/src/components/SignupView.tsx
2025-02-10 14:27:38 +08:00

181 lines
5.2 KiB
TypeScript

import { Input, Button, Link, Select, SelectItem } from "@heroui/react";
import { IconMail, IconLock } from "@tabler/icons-react";
import { useState } from "react";
import { toast } from "react-toastify";
import http, { login } from "../http";
export const validatePassword = (password: string): boolean => {
const passwordComplexityRegex =
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z\d])[A-Za-z\d@$!%*?&\\]{12,}$/;
return passwordComplexityRegex.test(password);
};
export default function SignupView({
onLogin,
email = "",
}: {
onLogin: () => void;
email: string;
}) {
const [emailValue, setEmailValue] = useState(email);
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [nric, setNric] = useState("");
const [gender, setGender] = useState("0");
const [dateOfBirth, setDateOfBirth] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [signupEnabled, setSignupEnabled] = useState(true);
const validateFields = () => {
if (
!firstName ||
!lastName ||
!emailValue ||
!nric ||
!dateOfBirth ||
!password ||
!confirmPassword
) {
toast.error("All fields are required.");
return false;
}
const nricRegex = /^[A-Za-z][0-9]{7}[A-Za-z]$/;
if (!nricRegex.test(nric)) {
toast.error("Please enter a valid NRIC.");
return false;
}
if (password !== confirmPassword) {
toast.error("Passwords do not match.");
return false;
}
if (!validatePassword(password)) {
toast.error(
"Password must be at least 12 characters long and include uppercase, lowercase, number, and special character."
);
return false;
}
return true;
};
const handleSubmit = async () => {
if (!validateFields()) return;
const user = {
id: "",
firstName,
lastName,
gender: Number(gender),
nationalRegistrationIdentityCardNumber: nric.toUpperCase(),
email: emailValue.toLowerCase(),
password,
dateOfBirth: new Date(dateOfBirth),
resumeName: "",
whoAmI: "",
};
try {
const response = await http.post("/User/register", user);
if (response.status !== 200) {
throw new Error("Failed to sign up");
}
setSignupEnabled(false);
toast.success("Welcome to Ace Job, " + firstName + "!", {
onClose: () => {
http
.post("/User/login", { email: emailValue, password: password })
.then((response) => {
if (response.status === 200) {
login(response.data.token);
} else {
toast.error("Failed to login");
}
});
},
});
} catch (error) {
toast.error((error as any).response?.data || "Error during signup");
}
};
return (
<div className="flex flex-col gap-6">
<div className="flex flex-col">
<p className="text-3xl">Welcome!</p>
<p className="text-md opacity-50">Become a membership today!</p>
</div>
<div className="flex flex-col gap-2">
<div className="flex flex-row gap-2">
<Input
label="First name"
value={firstName}
onValueChange={setFirstName}
/>
<Input
label="Last name"
value={lastName}
onValueChange={setLastName}
/>
</div>
<Input
endContent={<IconMail />}
label="Email"
type="email"
value={emailValue}
onValueChange={setEmailValue}
/>
<Input label="NRIC" value={nric} onValueChange={setNric} />
<div className="flex flex-row gap-2">
<Select
label="Gender"
selectedKeys={[gender]}
onChange={(e) => {
setGender(e.target.value);
}}
>
<SelectItem key={"0"}>Male</SelectItem>
<SelectItem key={"1"}>Female</SelectItem>
</Select>
<input
type="date"
className="rounded-xl px-4 transition-colors dark:bg-neutral-800 dark:hover:bg-neutral-700"
value={dateOfBirth}
onChange={(e) => setDateOfBirth(e.target.value)}
/>
</div>
<Input
endContent={<IconLock />}
label="Password"
type="password"
value={password}
onValueChange={setPassword}
/>
<Input
endContent={<IconLock />}
label="Confirm password"
type="password"
value={confirmPassword}
onValueChange={setConfirmPassword}
/>
</div>
<div className="flex flex-col gap-4 w-full">
<Button
color="primary"
className="w-full"
onPress={handleSubmit}
isDisabled={!signupEnabled}
>
Sign up
</Button>
<div className="flex flex-row gap-2 w-full justify-center *:my-auto">
<p className="text-sm">Already have an account?</p>
<Link color="primary" onPress={onLogin} className="text-sm">
Login
</Link>
</div>
</div>
</div>
);
}