From ef3edc48d16abfc262af3825dcc052c1a7624a08 Mon Sep 17 00:00:00 2001 From: Tin Pham <72054441+tinpham5614@users.noreply.github.com> Date: Thu, 14 Mar 2024 09:55:24 -0700 Subject: [PATCH] belinda-nextjs-04-262-update-signup-page-01 (#263) * update sign up page * add reroute the user to signin page afterward --- app/auth/sign-up/page.tsx | 328 +++++++++++++++--------- app/auth/sign-up/signup-page.module.css | 64 ----- components/ErrorAlert.tsx | 14 + components/SuccessAlert.tsx | 17 ++ 4 files changed, 237 insertions(+), 186 deletions(-) delete mode 100644 app/auth/sign-up/signup-page.module.css create mode 100644 components/ErrorAlert.tsx create mode 100644 components/SuccessAlert.tsx diff --git a/app/auth/sign-up/page.tsx b/app/auth/sign-up/page.tsx index 63f99ece..66ca7bb9 100644 --- a/app/auth/sign-up/page.tsx +++ b/app/auth/sign-up/page.tsx @@ -1,11 +1,17 @@ "use client"; -import InputField from "@/components/InputFields"; -import Link from "next/link"; + import { ChangeEventHandler, FormEventHandler, useState } from "react"; -import styles from './signup-page.module.css'; import Visibility from "@mui/icons-material/Visibility"; import VisibilityOff from "@mui/icons-material/VisibilityOff"; -import {IconButton, InputAdornment, TextField} from "@mui/material"; +import { IconButton, InputAdornment, TextField } from "@mui/material"; +import { Box, Button, Container, Paper, Typography } from "@mui/material"; +import Image from "next/image"; +import mascot from "../../nsc_mascot_green_cropped.png"; +import { Link as MuiLink } from "@mui/material"; +import ErrorAlert from "@/components/ErrorAlert"; +import SuccessAlert from "@/components/SuccessAlert"; +import { useRouter } from "next/navigation"; + const SignUp = () => { // handling user's incoming info const [userInfo, setUserInfo] = useState({ @@ -14,29 +20,35 @@ const SignUp = () => { email: "", password: "", confirmPassword: "", - role: "creator", + role: "user", }); - // Set initial state for errors - const [errors, setErrors] = useState({ - firstName: "", - lastName: "", - email: "", - password: "", - confirmPassword: "", - }); + // Set initial state for errors and success + const [error, setError] = useState(""); + const [success, setSuccess] = useState(""); + + // destructure user's info const { firstName, lastName, email, password, confirmPassword } = userInfo; // State to track whether the password is visible or not - const [showPassword, setShowPassword] = useState(false); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); - // Function to toggle password visibility - const togglePasswordVisibility = () => { - setShowPassword(!showPassword); - }; + // Function to toggle password visibility + const togglePasswordVisibility = () => { + setShowPassword(!showPassword); + }; + + // Toggle confirm password visibility + const toggleConfirmPasswordVisibility = () => { + setShowConfirmPassword(!showConfirmPassword); + } + + // route to sign in page + const router = useRouter(); const handleChange: ChangeEventHandler = ({ target }) => { const { name, value } = target; - // updating user's info + // updating user's info setUserInfo({ ...userInfo, [name]: value }); }; @@ -45,126 +57,198 @@ const SignUp = () => { // prevent the default behavior e.preventDefault(); - // Check if password and confirmPassword match - if (password !== confirmPassword) { - console.error("Passwords do not match"); - return; - } - + // Check if inputs are empty + if (!firstName || !lastName || !email || !password || !confirmPassword) { + setError("Please fill in all fields"); + return; + } + + // Check if password and confirmPassword match + if (password !== confirmPassword) { + setError("Passwords do not match"); + return; + } + + // Check if password is less than 6 characters + if (password.length < 8) { + setError("Password must be at least 8 characters"); + return; + } + + // Check if email is valid + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email)) { + setError("Invalid email"); + return; + } + // send request to backend api then log the response // hardwired link for local development, change port if needed - const res = await fetch("http://localhost:3000/api/auth/signup", { - method: "POST", - body: JSON.stringify(userInfo), - headers: { - "Content-type": "application/json" - } - }); try { - const data = await res.json(); - console.log(data); + const res = await fetch("http://localhost:3000/api/auth/signup", { + method: "POST", + body: JSON.stringify(userInfo), + headers: { + "Content-type": "application/json", + }, + }); + if (!res.ok) { + const { message } = await res.json(); + setError(message); + setSuccess(""); + return; + } + setError(""); + setSuccess("Sign up successful!"); + router.push("/auth/sign-in"); } catch (error) { - console.error("Error parson JSON response:", error); + console.error("Failed to sign up", error); } - - // temporary alert for testing - if (res.ok) alert('sign up was successful') - }; return ( -
-
-

Sign Up

- + logo + + + Sign Up + + + - + - + - {/* Password input with toggle button */} + required + fullWidth + id="email" + label="Email Address" + name="email" + autoComplete="email" + value={email} + onChange={handleChange} + type="email" + autoFocus={false} + /> - {/* Toggle button to control password visibility */} - - {showPassword ? : } - - - ), - }} + sx={{ mb: 2 }} + margin="normal" + required + fullWidth + id="password" + label="Password" + name="password" + type={showPassword ? "text" : "password"} + autoComplete="password" + value={password} + onChange={handleChange} + autoFocus={false} + InputProps={{ + endAdornment: ( + + + {showPassword ? : } + + + ), + }} /> - {/* Confirm Password input with toggle button */} - {/* Toggle button to control password visibility */} - - {showPassword ? : } - - - ), - }} + sx={{ mb: 2 }} + margin="normal" + required + fullWidth + id="confirmPassword" + label="Confirm Password" + name="confirmPassword" + type={showConfirmPassword ? "text" : "password"} + autoComplete="confirm-password" + value={confirmPassword} + onChange={handleChange} + autoFocus={false} + InputProps={{ + endAdornment: ( + + + {showConfirmPassword ? : } + + + ), + }} /> - {/**/} - {/* {showPassword ? "Hide Password" : "Show Password"}*/} - {/**/} - - -
+ {error && } + {success && } + + + + Already have an account?{" "} + + {"Sign In"} + + + + + + ); - }; -export default SignUp; \ No newline at end of file +export default SignUp; diff --git a/app/auth/sign-up/signup-page.module.css b/app/auth/sign-up/signup-page.module.css deleted file mode 100644 index 90642169..00000000 --- a/app/auth/sign-up/signup-page.module.css +++ /dev/null @@ -1,64 +0,0 @@ -/* Sign Up page CSS */ -.container { - background-color: #bec3c8; - display: flex; - justify-content: center; - align-items: center; - height: 100vh; - } - - .formContainer { - background-color: #dbdfe0; - padding: 2rem; - border-radius: 15px; - width: 400px; - max-width: 100%; - margin-bottom: 20vh; - } - - .title { - text-align: center; - color: #101c29; - font-weight: bold; - font-size: 1.8rem; - margin-bottom: 1rem; - } - - .submitButton { - width: 100%; - padding: 8px; - border: none; - border-radius: 4px; - color: #f0f0f0; - background-color: #101c29; - cursor: pointer; - font-weight: bold; - font-size: 1rem; - margin: 1.5rem 0; - transition: background-color 0.3s ease-in-out; - } - - .submitButton:hover { - background-color: #0c141c; - } - - .textCenter { - text-align: center; - color: #101c29; - font-size: 1rem; - margin-bottom: 1rem; - } - - .link { - color: #101c29; - text-decoration: none; - font-weight: bold; - display: block; - text-align: center; - margin-top: 1rem; - } - - .link:hover { - text-decoration: underline; - text-underline-offset: 2px; - } \ No newline at end of file diff --git a/components/ErrorAlert.tsx b/components/ErrorAlert.tsx new file mode 100644 index 00000000..f2b3dd88 --- /dev/null +++ b/components/ErrorAlert.tsx @@ -0,0 +1,14 @@ +import { Alert } from "@mui/material"; + +interface ErrorAlertProps { + message: string; +} +const CustomErrorText: React.FC = (props: ErrorAlertProps) => { + return ( + <> + {props.message} + + ); +}; + +export default CustomErrorText; diff --git a/components/SuccessAlert.tsx b/components/SuccessAlert.tsx new file mode 100644 index 00000000..00ea18d4 --- /dev/null +++ b/components/SuccessAlert.tsx @@ -0,0 +1,17 @@ +import { Alert } from "@mui/material"; + +interface SuccessAlertProps { + message: string; +} + +const SuccessAlert: React.FC = ( + props: SuccessAlertProps +) => { + return ( + <> + {props.message} + + ); +}; + +export default SuccessAlert; \ No newline at end of file