mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-12 04:39:48 -06:00
Added client side validation to registration page
This commit is contained in:
609
Client/package-lock.json
generated
609
Client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@
|
||||
"@reduxjs/toolkit": "2.2.5",
|
||||
"axios": "1.7.2",
|
||||
"dayjs": "1.11.11",
|
||||
"joi": "17.13.1",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import axios from "axios";
|
||||
@@ -11,12 +11,14 @@ import StringTextField from "../../Components/TextFields/Text/TextField";
|
||||
import Check from "../../Components/Check/Check";
|
||||
import Button from "../../Components/Button";
|
||||
import Google from "../../assets/Images/Google.png";
|
||||
import { registerValidation } from "../../Validation/validation";
|
||||
|
||||
const BASE_URL = import.meta.env.VITE_APP_API_BASE_URL;
|
||||
|
||||
const Register = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
// TODO If possible, change the IDs of these fields to match the backend
|
||||
const idMap = {
|
||||
"register-firstname-input": "firstname",
|
||||
"register-lastname-input": "lastname",
|
||||
@@ -24,6 +26,8 @@ const Register = () => {
|
||||
"register-password-input": "password",
|
||||
};
|
||||
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
const [form, setForm] = useState({
|
||||
firstname: "",
|
||||
lastname: "",
|
||||
@@ -31,99 +35,128 @@ const Register = () => {
|
||||
password: "",
|
||||
});
|
||||
|
||||
const handleInput = (e) => {
|
||||
setForm({
|
||||
...form,
|
||||
[idMap[e.target.id]]: e.target.value,
|
||||
useEffect(() => {
|
||||
const { error } = registerValidation.validate(form, {
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
// Creates an error object in the format { field: message }
|
||||
const validationErrors = error.details.reduce((acc, err) => {
|
||||
return { ...acc, [err.path[0]]: err.message };
|
||||
}, {});
|
||||
setErrors(validationErrors);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleInput = (e) => {
|
||||
const newForm = { ...form, [idMap[e.target.id]]: e.target.value };
|
||||
setForm(newForm);
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
//TODO input validation
|
||||
console.log("submitting");
|
||||
try {
|
||||
await registerValidation.validateAsync(form, { abortEarly: false });
|
||||
|
||||
const result = await axios.post(`${BASE_URL}/auth/register`, form);
|
||||
const token = result.data.data;
|
||||
localStorage.setItem("token", token);
|
||||
navigate("/"); // Redirect to home page
|
||||
} catch (error) {
|
||||
// TODO error handlign
|
||||
alert(`Something went wrong: ${error.response.data.msg}`);
|
||||
// TODO User friendly error display
|
||||
if (error.name === "ValidationError") {
|
||||
console.log(error);
|
||||
alert("Invalid input");
|
||||
} else if (error.response) {
|
||||
console.log(error);
|
||||
alert(error.response.data.msg);
|
||||
} else {
|
||||
console.log(error);
|
||||
alert("Unknown error");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="register-page">
|
||||
<BackgroundPattern></BackgroundPattern>
|
||||
<div className="register-form">
|
||||
<div className="register-form-header">
|
||||
<img
|
||||
className="register-form-header-logo"
|
||||
src={Logomark}
|
||||
alt="Logomark"
|
||||
/>
|
||||
<div className="register-form-v-spacing-large" />
|
||||
<div className="register-form-heading">Create an account</div>
|
||||
<div className="register-form-v-spacing-large"></div>
|
||||
</div>
|
||||
<div className="register-form-v-spacing-40px" />
|
||||
<div className="register-form-inputs">
|
||||
<StringTextField
|
||||
onChange={handleInput}
|
||||
error={false}
|
||||
label="First name*"
|
||||
placeholder="Enter your first name"
|
||||
id="register-firstname-input"
|
||||
/>
|
||||
<form>
|
||||
<div className="register-form">
|
||||
<div className="register-form-header">
|
||||
<img
|
||||
className="register-form-header-logo"
|
||||
src={Logomark}
|
||||
alt="Logomark"
|
||||
/>
|
||||
<div className="register-form-v-spacing-large" />
|
||||
<div className="register-form-heading">Create an account</div>
|
||||
<div className="register-form-v-spacing-large"></div>
|
||||
</div>
|
||||
<div className="register-form-v-spacing-40px" />
|
||||
<div className="register-form-inputs">
|
||||
<StringTextField
|
||||
onChange={handleInput}
|
||||
error={errors.firstname ? true : false}
|
||||
helperText={errors.firstname ? errors.firstname : ""}
|
||||
label="First name*"
|
||||
placeholder="Enter your first name"
|
||||
id="register-firstname-input"
|
||||
/>
|
||||
<div className="login-form-v2-spacing" />
|
||||
<StringTextField
|
||||
onChange={handleInput}
|
||||
error={errors.lastname ? true : false}
|
||||
helperText={errors.lastname ? errors.lastname : ""}
|
||||
label="Last name*"
|
||||
placeholder="Enter your last name"
|
||||
id="register-lastname-input"
|
||||
/>
|
||||
<div className="login-form-v2-spacing" />
|
||||
<EmailTextField
|
||||
onChange={handleInput}
|
||||
label="Email*"
|
||||
error={errors.email ? true : false}
|
||||
helperText={errors.email ? errors.email : ""}
|
||||
placeholder="Enter your email"
|
||||
id="register-email-input"
|
||||
/>
|
||||
<div className="login-form-v2-spacing" />
|
||||
<PasswordTextField
|
||||
onChange={handleInput}
|
||||
label="Password*"
|
||||
error={errors.password ? true : false}
|
||||
helperText={errors.password ? errors.password : ""}
|
||||
placeholder="Create a password"
|
||||
id="register-password-input"
|
||||
/>
|
||||
</div>
|
||||
<div className="login-form-v2-spacing" />
|
||||
<StringTextField
|
||||
onChange={handleInput}
|
||||
error={false}
|
||||
label="Last name*"
|
||||
placeholder="Enter your last name"
|
||||
id="register-lastname-input"
|
||||
/>
|
||||
<div className="register-form-checks">
|
||||
<Check text="Must be at least 8 characters" />
|
||||
<div className="register-form-v-spacing-small"></div>
|
||||
<Check text="Must contain one special character" />
|
||||
</div>
|
||||
<div className="login-form-v2-spacing" />
|
||||
<EmailTextField
|
||||
onChange={handleInput}
|
||||
label="Email*"
|
||||
error={false}
|
||||
placeholder="Enter your email"
|
||||
id="register-email-input"
|
||||
/>
|
||||
<div className="login-form-v2-spacing" />
|
||||
<PasswordTextField
|
||||
onChange={handleInput}
|
||||
label="Password*"
|
||||
error={false}
|
||||
placeholder="Create a password"
|
||||
id="register-password-input"
|
||||
/>
|
||||
<div className="register-form-actions">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
level="primary"
|
||||
label="Get started"
|
||||
sx={{ width: "100%" }}
|
||||
/>
|
||||
<div className="login-form-v-spacing" />
|
||||
<Button
|
||||
disabled={true}
|
||||
level="secondary"
|
||||
label="Sign up with Google"
|
||||
sx={{ width: "100%", color: "#344054", fontWeight: "700" }}
|
||||
img={<img className="google-enter" src={Google} alt="Google" />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="login-form-v2-spacing" />
|
||||
<div className="register-form-checks">
|
||||
<Check text="Must be at least 8 characters" />
|
||||
<div className="register-form-v-spacing-small"></div>
|
||||
<Check text="Must contain one special character" />
|
||||
</div>
|
||||
<div className="login-form-v2-spacing" />
|
||||
<div className="register-form-actions">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
level="primary"
|
||||
label="Get started"
|
||||
sx={{ width: "100%" }}
|
||||
/>
|
||||
<div className="login-form-v-spacing" />
|
||||
<Button
|
||||
disabled={true}
|
||||
level="secondary"
|
||||
label="Sign up with Google"
|
||||
sx={{ width: "100%", color: "#344054", fontWeight: "700" }}
|
||||
img={<img className="google-enter" src={Google} alt="Google" />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="register-bottom-spacing"></div>
|
||||
<div className="register-bottom-spacing"></div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
27
Client/src/Validation/validation.js
Normal file
27
Client/src/Validation/validation.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import joi from "joi";
|
||||
|
||||
const registerValidation = joi.object({
|
||||
firstname: joi.string().required().messages({
|
||||
"string.empty": "First name is required",
|
||||
}),
|
||||
|
||||
lastname: joi.string().required().messages({
|
||||
"string.empty": "Last name is required",
|
||||
}),
|
||||
|
||||
email: joi
|
||||
.string()
|
||||
.email({ tlds: { allow: false } })
|
||||
.required()
|
||||
.messages({
|
||||
"string.email": "Email must be a valid email",
|
||||
"string.empty": "Email is required",
|
||||
}),
|
||||
|
||||
password: joi.string().min(8).required().messages({
|
||||
"string.min": "Password must be at least 8 characters",
|
||||
"string.empty": "Password is required",
|
||||
}),
|
||||
});
|
||||
|
||||
export { registerValidation };
|
||||
@@ -8,6 +8,7 @@ const loginValidation = joi.object({
|
||||
email: joi.string().email().required(),
|
||||
password: joi.string().min(8).required(),
|
||||
});
|
||||
|
||||
const registerValidation = joi.object({
|
||||
firstname: joi.string().required(),
|
||||
lastname: joi.string().required(),
|
||||
|
||||
Reference in New Issue
Block a user