mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-04-27 20:19:39 -05:00
Merge pull request #131 from bluewave-labs/fix/error-checking
Simplified form control in Login and Register. Fixed registration bu…
This commit is contained in:
@@ -36,6 +36,7 @@ const levelConfig = {
|
||||
* @component
|
||||
* @param {Object} props
|
||||
* @param {'primary' | 'secondary' | 'tertiary' | 'error' | 'imagePrimary' | 'imageSecondary' | 'imageTertiary'} props.level - The level of the button
|
||||
* @param {string} props.type - The type of the button
|
||||
* @param {string} props.label - The label of the button
|
||||
* @param {React.ReactNode} props.img - Image for button
|
||||
* @param {boolean} [props.disabled] - Whether the button is disabled
|
||||
@@ -44,13 +45,14 @@ const levelConfig = {
|
||||
* @returns {JSX.Element}
|
||||
* @example
|
||||
* // Render an error button
|
||||
* <Button level="error" label="Error" disabled sx={{marginTop: "1rem"}}/>
|
||||
* <Button type="submit" level="error" label="Error" disabled sx={{marginTop: "1rem"}}/>
|
||||
*/
|
||||
|
||||
const Button = ({ level, label, disabled, img, onClick, sx }) => {
|
||||
const Button = ({ type, level, label, disabled, img, onClick, sx }) => {
|
||||
const { variant, color } = levelConfig[level];
|
||||
return (
|
||||
<MuiButton
|
||||
type={type}
|
||||
variant={variant}
|
||||
color={color}
|
||||
disabled={disabled}
|
||||
@@ -67,6 +69,7 @@ const Button = ({ level, label, disabled, img, onClick, sx }) => {
|
||||
};
|
||||
|
||||
Button.propTypes = {
|
||||
type: PropTypes.string,
|
||||
level: PropTypes.string.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
img: PropTypes.node,
|
||||
|
||||
@@ -32,6 +32,7 @@ const EmailTextField = ({
|
||||
label = "Email",
|
||||
variant,
|
||||
placeholder,
|
||||
autoComplete,
|
||||
icon,
|
||||
helperText,
|
||||
error,
|
||||
@@ -60,6 +61,7 @@ const EmailTextField = ({
|
||||
id={id}
|
||||
variant={variant}
|
||||
placeholder={placeholder}
|
||||
autoComplete={autoComplete}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
|
||||
@@ -22,6 +22,7 @@ const PasswordTextField = ({
|
||||
onChange,
|
||||
id,
|
||||
label = "Password",
|
||||
autoComplete,
|
||||
variant,
|
||||
placeholder,
|
||||
icon,
|
||||
@@ -48,6 +49,7 @@ const PasswordTextField = ({
|
||||
<TextField
|
||||
onChange={onChange}
|
||||
type="password"
|
||||
autoComplete={autoComplete}
|
||||
error={error}
|
||||
className="password-text-field-input"
|
||||
id={id}
|
||||
|
||||
@@ -19,6 +19,7 @@ import { useTheme } from "@mui/material";
|
||||
*/
|
||||
const StringTextField = ({
|
||||
onChange,
|
||||
autoComplete,
|
||||
id,
|
||||
label,
|
||||
variant,
|
||||
@@ -51,6 +52,7 @@ const StringTextField = ({
|
||||
id={id}
|
||||
variant={variant}
|
||||
placeholder={placeholder}
|
||||
autoComplete={autoComplete}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
|
||||
@@ -44,9 +44,10 @@ const Login = () => {
|
||||
} else {
|
||||
setErrors({});
|
||||
}
|
||||
}, []);
|
||||
}, [form]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await loginValidation.validateAsync(form, { abortEarly: false });
|
||||
const action = await dispatch(login(form));
|
||||
@@ -77,15 +78,6 @@ const Login = () => {
|
||||
};
|
||||
|
||||
const handleInput = (e) => {
|
||||
const fieldName = idMap[e.target.id];
|
||||
// Extract and validate individual fields as input changes
|
||||
const fieldSchema = loginValidation.extract(fieldName);
|
||||
const { error } = fieldSchema.validate(e.target.value);
|
||||
let errMsg = "";
|
||||
if (error) {
|
||||
errMsg = error.message;
|
||||
}
|
||||
setErrors({ ...errors, [fieldName]: errMsg });
|
||||
const newForm = { ...form, [idMap[e.target.id]]: e.target.value };
|
||||
setForm(newForm);
|
||||
};
|
||||
@@ -93,7 +85,7 @@ const Login = () => {
|
||||
return (
|
||||
<div className="login-page">
|
||||
<BackgroundPattern></BackgroundPattern>
|
||||
<div className="login-form">
|
||||
<form className="login-form" onSubmit={handleSubmit}>
|
||||
<div className="login-form-header">
|
||||
<img
|
||||
className="login-form-header-logo"
|
||||
@@ -110,6 +102,7 @@ const Login = () => {
|
||||
error={errors.email ? true : false}
|
||||
helperText={errors.email ? errors.email : ""}
|
||||
placeholder="Enter your email"
|
||||
autoComplete="email"
|
||||
id="login-email-input"
|
||||
/>
|
||||
<div className="login-form-v2-spacing" />
|
||||
@@ -118,6 +111,7 @@ const Login = () => {
|
||||
error={errors.password ? true : false}
|
||||
helperText={errors.password ? errors.password : ""}
|
||||
placeholder="Password"
|
||||
autoComplete="current-password"
|
||||
id="login-password-input"
|
||||
/>
|
||||
</div>
|
||||
@@ -129,10 +123,10 @@ const Login = () => {
|
||||
<div className="login-form-v3-spacing" />
|
||||
<div className="login-form-actions">
|
||||
<Button
|
||||
type="submit"
|
||||
level="primary"
|
||||
label="Sign in"
|
||||
sx={{ width: "100%" }}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
<div className="login-form-v-spacing" />
|
||||
<Button
|
||||
@@ -147,7 +141,7 @@ const Login = () => {
|
||||
Don’t have an account?
|
||||
<span className="new-account-option-span">Sign up</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -52,23 +52,15 @@ const Register = () => {
|
||||
} else {
|
||||
setErrors({});
|
||||
}
|
||||
}, []);
|
||||
}, [form]);
|
||||
|
||||
const handleInput = (e) => {
|
||||
const fieldName = idMap[e.target.id];
|
||||
// Extract and validate individual fields as input changes
|
||||
const fieldSchema = registerValidation.extract(fieldName);
|
||||
const { error } = fieldSchema.validate(e.target.value);
|
||||
let errMsg = "";
|
||||
if (error) {
|
||||
errMsg = error.message;
|
||||
}
|
||||
setErrors({ ...errors, [fieldName]: errMsg });
|
||||
const newForm = { ...form, [idMap[e.target.id]]: e.target.value };
|
||||
setForm(newForm);
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await registerValidation.validateAsync(form, { abortEarly: false });
|
||||
const action = await dispatch(register(form));
|
||||
@@ -103,82 +95,82 @@ const Register = () => {
|
||||
return (
|
||||
<div className="register-page">
|
||||
<BackgroundPattern></BackgroundPattern>
|
||||
<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" />
|
||||
<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>
|
||||
<form className="register-form" onSubmit={handleSubmit}>
|
||||
<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"
|
||||
autoComplete="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"
|
||||
autoComplete="current-password"
|
||||
id="register-password-input"
|
||||
/>
|
||||
</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
|
||||
type="submit"
|
||||
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 className="register-bottom-spacing"></div>
|
||||
</form>
|
||||
<div className="register-bottom-spacing"></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const express = require("express");
|
||||
const logger = require("../utils/logger");
|
||||
require("dotenv").config();
|
||||
const {
|
||||
@@ -11,7 +10,6 @@ const {
|
||||
editAlertBodyValidation,
|
||||
deleteAlertParamValidation,
|
||||
} = require("../validation/joi");
|
||||
const { get } = require("mongoose");
|
||||
|
||||
const SERVICE_NAME = "alerts";
|
||||
|
||||
|
||||
@@ -45,18 +45,6 @@ const registerController = async (req, res, next) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the user exists
|
||||
try {
|
||||
const isUser = await req.db.getUserByEmail(req, res);
|
||||
if (isUser) {
|
||||
throw new Error("User already exists");
|
||||
}
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
next(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new user
|
||||
try {
|
||||
const newUser = await req.db.insertUser(req, res);
|
||||
@@ -101,8 +89,8 @@ const loginController = async (req, res, next) => {
|
||||
|
||||
// Compare password
|
||||
const match = await user.comparePassword(req.body.password);
|
||||
if (!match) {
|
||||
throw new Error("Password does not match!");
|
||||
if (match !== true) {
|
||||
throw new Error("Incorrect password");
|
||||
}
|
||||
|
||||
// Remove password from user object. Should this be abstracted to DB layer?
|
||||
|
||||
@@ -5,6 +5,7 @@ const Check = require("../models/Check");
|
||||
const Alert = require("../models/Alert");
|
||||
const RecoveryToken = require("../models/RecoveryToken");
|
||||
const crypto = require("crypto");
|
||||
const DUPLICATE_KEY_CODE = 11000; // MongoDB error code for duplicate key
|
||||
|
||||
const connect = async () => {
|
||||
try {
|
||||
@@ -34,6 +35,9 @@ const insertUser = async (req, res) => {
|
||||
await newUser.save();
|
||||
return await UserModel.findOne({ _id: newUser._id }).select("-password"); // .select() doesn't work with create, need to save then find
|
||||
} catch (error) {
|
||||
if (error.code === DUPLICATE_KEY_CODE) {
|
||||
throw new Error("Email already exists");
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ const handleErrors = (error, req, res, next) => {
|
||||
const status = error.status || 500;
|
||||
const message = error.message || "Something went wrong";
|
||||
const service = error.service || "Unknown service";
|
||||
|
||||
logger.error(error.message, { service: service });
|
||||
res.status(status).json({ success: false, msg: message });
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user