From d57d1679cfc90187bf4e57b299d5b74a9d69979e Mon Sep 17 00:00:00 2001 From: Chetan Jain Date: Sun, 9 Apr 2023 21:52:44 -0400 Subject: [PATCH 1/4] update: improve signup page components --- package.json | 1 + src/components/CustomSelect.js | 28 +++++++++++++++++ src/components/CustomTextField.js | 3 +- src/components/Signup.js | 45 ++++++++++++++++++++++----- src/helper/index.js | 51 +++++++++++++++++++++++++++++-- 5 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 src/components/CustomSelect.js diff --git a/package.json b/package.json index 00bb49e..2da81e0 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "dependencies": { "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", + "@mui/icons-material": "^5.11.16", "@mui/material": "^5.11.16", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", diff --git a/src/components/CustomSelect.js b/src/components/CustomSelect.js new file mode 100644 index 0000000..0aaf182 --- /dev/null +++ b/src/components/CustomSelect.js @@ -0,0 +1,28 @@ +import React from 'react' +import MenuItem from '@mui/material/MenuItem'; +import Select from '@mui/material/Select'; +import h from '../helper/index' + +export default function CustomSelect(props) { + const onChange = (e) => { + props.onChange(props.name, e.target.value) + } + + return +} \ No newline at end of file diff --git a/src/components/CustomTextField.js b/src/components/CustomTextField.js index 244ea69..1b41b46 100644 --- a/src/components/CustomTextField.js +++ b/src/components/CustomTextField.js @@ -15,12 +15,13 @@ export default function CustomTextField(props) { id={props.name} name={props.name} onChange={onChange} - defaultValue={props.defaultValue || ''} value={props.value} label={h.capitalizeFirst(props.name)} variant="outlined" error={props.error} helperText={props.helperText} + type={props.type} + InputProps={props.inputProps} /> ); } \ No newline at end of file diff --git a/src/components/Signup.js b/src/components/Signup.js index 6a577d9..37383bd 100644 --- a/src/components/Signup.js +++ b/src/components/Signup.js @@ -4,6 +4,13 @@ import "./Signup.css"; import CustomTextField from "./CustomTextField"; import h from '../helper/index'; +import IconButton from '@mui/material/IconButton'; +import InputAdornment from '@mui/material/InputAdornment'; +import Visibility from '@mui/icons-material/Visibility'; +import VisibilityOff from '@mui/icons-material/VisibilityOff'; +import CustomSelect from "./CustomSelect"; + + const defaultUser = { username: "", firstname: "", @@ -20,14 +27,15 @@ const defaultUser = { const SignUp = (props) => { const [user, setUser] = React.useState(defaultUser); const [errors, setErrors] = React.useState(defaultUser); + const [showPassword, setShowPassword] = React.useState(false); + + const handleClickShowPassword = () => setShowPassword((show) => !show); - const onChangeOfValue = (key, value) => { - setUser({ ...user, [key]: value }); - }; + const handleMouseDownPassword = (e) => e.preventDefault(); - const onBlur = () => { - setErrors(h.validator(user)) - } + const onChangeOfValue = (key, value) => setUser({ ...user, [key]: value }); + + const onBlur = () => setErrors(h.validator(user)) return (
@@ -71,16 +79,39 @@ const SignUp = (props) => { error={errors.password.helperText} helperText={errors.password.helperText} name="password" + type={showPassword ? "text" : "password"} value={user.password} onChange={onChangeOfValue} + inputProps={{ + endAdornment: + + {showPassword ? : } + + + }} /> - { return /^(?=.{6,15}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(? { + return /^[a-zA-Z ]+$/.test(firstname) +} + +const validLastname = (lastname) => { + return validFirstname(lastname) +} + +const validEmail = (email) => { + return /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(email) +} + +const validPassword = (password) => { + return /^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{6,16}$/.test(password) +} + const isUsernameValid = (username) => { if(!username) return true; return validUsername(username) } +const isFirstnameValid = (firstname) => { + if(!firstname) return true; + return validFirstname(firstname) +} + +const isLastnameValid = (lastname) => { + if(!lastname) return true; + return validLastname(lastname) +} + +const isValidEmail = (email) => { + if(!email) return true; + return validEmail(email) +} + +const isValidPassword = (password) => { + if(!password) return true; + return validPassword(password) +} + const capitalizeFirst = (str) => { if(!str) return ''; return str[0].toUpperCase() + str.slice(1, str.length) @@ -23,15 +59,26 @@ const validator = (user) => { errorObj[key].helperText = undefined }) - if (!isUsernameValid(user.username)) { errorObj.username.helperText = `username is invalid` } - if (!isUsernameValid(user.firstname)) { + if (!isFirstnameValid(user.firstname)) { errorObj.firstname.helperText = `firstname is invalid` } + if (!isLastnameValid(user.lastname)) { + errorObj.lastname.helperText = `lastname is invalid` + } + + if (!isValidEmail(user.email)) { + errorObj.email.helperText = `email is invalid` + } + + if (!isValidPassword(user.password)) { + errorObj.password.helperText = 'password is invalid' + } + return errorObj } From 7a2d7fb51fabfa2f1a19ba0e0bff3c3f24bb2e67 Mon Sep 17 00:00:00 2001 From: Chetan Jain Date: Sun, 9 Apr 2023 22:11:10 -0400 Subject: [PATCH 2/4] add select component --- src/components/CustomSelect.js | 37 ++++++++++++++++++---------------- src/components/Signup.js | 9 ++++++++- src/helper/index.js | 35 +++++++++++++++++++++++--------- 3 files changed, 53 insertions(+), 28 deletions(-) diff --git a/src/components/CustomSelect.js b/src/components/CustomSelect.js index 0aaf182..972cf4f 100644 --- a/src/components/CustomSelect.js +++ b/src/components/CustomSelect.js @@ -2,27 +2,30 @@ import React from 'react' import MenuItem from '@mui/material/MenuItem'; import Select from '@mui/material/Select'; import h from '../helper/index' +import { InputLabel } from '@mui/material'; export default function CustomSelect(props) { const onChange = (e) => { props.onChange(props.name, e.target.value) } - return + return <> + {h.capitalizeFirst(props.name)} + + } \ No newline at end of file diff --git a/src/components/Signup.js b/src/components/Signup.js index 37383bd..5caf195 100644 --- a/src/components/Signup.js +++ b/src/components/Signup.js @@ -121,13 +121,20 @@ const SignUp = (props) => { value={user.age} onChange={onChangeOfValue} /> - { return /^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{6,16}$/.test(password) } +const validAge = (age) => { + const n = Number(age) + if(Number.isNaN(n)) { + return false + } else if(n <=0 || n > 120) { + return false + } + return true; +} + const isUsernameValid = (username) => { if(!username) return true; return validUsername(username) @@ -43,40 +53,45 @@ const isValidPassword = (password) => { return validPassword(password) } +const isValidAge = (age) => { + if(!age) return true; + return validAge(age) +} + const capitalizeFirst = (str) => { if(!str) return ''; return str[0].toUpperCase() + str.slice(1, str.length) } const validator = (user) => { - const errorObj = { - username: { - helperText: undefined - } - } + const errorObj = {} Object.keys(user).forEach((key) => { errorObj[key] = {} errorObj[key].helperText = undefined }) if (!isUsernameValid(user.username)) { - errorObj.username.helperText = `username is invalid` + errorObj.username.helperText = `Username is invalid` } if (!isFirstnameValid(user.firstname)) { - errorObj.firstname.helperText = `firstname is invalid` + errorObj.firstname.helperText = `Firstname is invalid` } if (!isLastnameValid(user.lastname)) { - errorObj.lastname.helperText = `lastname is invalid` + errorObj.lastname.helperText = `Lastname is invalid` } if (!isValidEmail(user.email)) { - errorObj.email.helperText = `email is invalid` + errorObj.email.helperText = `Email is invalid` } if (!isValidPassword(user.password)) { - errorObj.password.helperText = 'password is invalid' + errorObj.password.helperText = 'Password is invalid' + } + + if(!isValidAge(user.age)) { + errorObj.age.helperText = 'Age is invalid' } return errorObj From 13f2a5d42f09fe81b671992d9f2d83d9bb873668 Mon Sep 17 00:00:00 2001 From: Chetan Jain Date: Sun, 9 Apr 2023 22:52:47 -0400 Subject: [PATCH 3/4] add create account button and loading state --- src/components/CustomCheckbox.js | 22 ++++++++++++++++++++++ src/components/Signup.css | 4 ++++ src/components/Signup.js | 24 ++++++++++++++++++++++-- src/helper/index.js | 18 ++++++++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 src/components/CustomCheckbox.js diff --git a/src/components/CustomCheckbox.js b/src/components/CustomCheckbox.js new file mode 100644 index 0000000..7ef7e7b --- /dev/null +++ b/src/components/CustomCheckbox.js @@ -0,0 +1,22 @@ +import React from 'react'; +import Checkbox from '@mui/material/Checkbox'; + +const label = { inputProps: { 'aria-label': 'Checkbox label' } }; + +export default function CustomCheckbox(props) { + const onChange = (e) => { + props.onChange(props.name, e.target.checked) + } + + return ( +
+ + {props.text} +
+ ) +} \ No newline at end of file diff --git a/src/components/Signup.css b/src/components/Signup.css index c006280..86861d5 100644 --- a/src/components/Signup.css +++ b/src/components/Signup.css @@ -19,4 +19,8 @@ font-weight: bold; margin-bottom: 15px; text-align: center; +} + +.anonymous-input { + margin-left: -10px !important; } \ No newline at end of file diff --git a/src/components/Signup.js b/src/components/Signup.js index 5caf195..216e95f 100644 --- a/src/components/Signup.js +++ b/src/components/Signup.js @@ -9,7 +9,9 @@ import InputAdornment from '@mui/material/InputAdornment'; import Visibility from '@mui/icons-material/Visibility'; import VisibilityOff from '@mui/icons-material/VisibilityOff'; import CustomSelect from "./CustomSelect"; - +import CustomCheckbox from './CustomCheckbox'; +import Button from '@mui/material/Button'; +import CircularProgress from '@mui/material/CircularProgress'; const defaultUser = { username: "", @@ -28,6 +30,7 @@ const SignUp = (props) => { const [user, setUser] = React.useState(defaultUser); const [errors, setErrors] = React.useState(defaultUser); const [showPassword, setShowPassword] = React.useState(false); + const [saving, setSaving] = React.useState(null) const handleClickShowPassword = () => setShowPassword((show) => !show); @@ -35,8 +38,12 @@ const SignUp = (props) => { const onChangeOfValue = (key, value) => setUser({ ...user, [key]: value }); - const onBlur = () => setErrors(h.validator(user)) + const createUser = () => { + setSaving(true) + } + const onBlur = () => setErrors(h.validator(user)) + console.log('user', user) return (
@@ -152,6 +159,19 @@ const SignUp = (props) => { value={user.state} onChange={onChangeOfValue} /> + +
); }; diff --git a/src/helper/index.js b/src/helper/index.js index 4889c5b..c4db9b1 100644 --- a/src/helper/index.js +++ b/src/helper/index.js @@ -28,6 +28,10 @@ const validAge = (age) => { return true; } +const validCity = (city) => { + return /^[a-zA-Z ]+$/.test(city) +} + const isUsernameValid = (username) => { if(!username) return true; return validUsername(username) @@ -58,6 +62,12 @@ const isValidAge = (age) => { return validAge(age) } +const isValidCity = (city) => { + if(!city) return true; + if(city.length > 50) return false; + return validCity(city) +} + const capitalizeFirst = (str) => { if(!str) return ''; return str[0].toUpperCase() + str.slice(1, str.length) @@ -94,6 +104,14 @@ const validator = (user) => { errorObj.age.helperText = 'Age is invalid' } + if(!isValidCity(user.city)) { + errorObj.city.helperText = 'City is invalid' + } + + if(!isValidCity(user.state)) { + errorObj.state.helperText = 'State is invalid' + } + return errorObj } From 57bd4de2e9edf804a85c69ee7c812716ab57baa5 Mon Sep 17 00:00:00 2001 From: Chetan Jain Date: Sun, 9 Apr 2023 23:08:23 -0400 Subject: [PATCH 4/4] update the signup component --- src/api/index.js | 67 +++++++++++------------ src/components/Signup.css | 32 +++++------ src/components/Signup.js | 111 +++++++++++++++++++++----------------- 3 files changed, 111 insertions(+), 99 deletions(-) diff --git a/src/api/index.js b/src/api/index.js index 1abbc2d..afe7b2c 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,38 +1,35 @@ -const signInUser = async (email, password) => { - try { - const response = await fetch('https://lmel2.wiremockapi.cloud/json/1', { - method: 'POST', - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ email, password }) - }); - const responseJson = await response.json() - return responseJson - } catch(e) { - console.log('error occurred', e) - return false - } -} +const createUserAccount = async ({ email, password }) => { + try { + const response = await fetch("https://lmel2.wiremockapi.cloud/json/1", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, password }), + }); + const responseJson = await response.json(); + return responseJson; + } catch (e) { + console.log("error occurred", e); + return false; + } +}; const signUpUser = async ({ email, password }) => { - try { - const response = await fetch('https://lmel2.wiremockapi.cloud/json/1', { - method: 'POST', - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ email, password }) - }); - const responseJson = await response.json() - return responseJson - } catch(e) { - console.log('error occurred', e) - return false - } -} + try { + const response = await fetch("https://lmel2.wiremockapi.cloud/json/1", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, password }), + }); + const responseJson = await response.json(); + return responseJson; + } catch (e) { + console.log("error occurred", e); + return false; + } +}; -export { - signInUser, - signUpUser -} \ No newline at end of file +export { createUserAccount, signUpUser }; diff --git a/src/components/Signup.css b/src/components/Signup.css index 86861d5..16234fc 100644 --- a/src/components/Signup.css +++ b/src/components/Signup.css @@ -1,26 +1,26 @@ .signup-input { - margin: 10px 0px !important; + margin: 10px 0px !important; } .dialog { - display: flex; - flex-direction: column; - background-color: white; - padding: 20px 35px; - border-radius: 10px; - width: 240px; - height: fit-content; - box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); + display: flex; + flex-direction: column; + background-color: white; + padding: 20px 35px; + border-radius: 10px; + width: 240px; + height: fit-content; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); } .dialog-header { - color: #2d842d; - font-size: 20px; - font-weight: bold; - margin-bottom: 15px; - text-align: center; + color: #2d842d; + font-size: 20px; + font-weight: bold; + margin-bottom: 15px; + text-align: center; } .anonymous-input { - margin-left: -10px !important; -} \ No newline at end of file + margin-left: -10px !important; +} diff --git a/src/components/Signup.js b/src/components/Signup.js index 216e95f..82ce706 100644 --- a/src/components/Signup.js +++ b/src/components/Signup.js @@ -1,17 +1,17 @@ import * as React from "react"; -import { signUpUser } from "../api/index"; +import { createUserAccount } from "../api/index"; import "./Signup.css"; import CustomTextField from "./CustomTextField"; -import h from '../helper/index'; +import h from "../helper/index"; -import IconButton from '@mui/material/IconButton'; -import InputAdornment from '@mui/material/InputAdornment'; -import Visibility from '@mui/icons-material/Visibility'; -import VisibilityOff from '@mui/icons-material/VisibilityOff'; +import IconButton from "@mui/material/IconButton"; +import InputAdornment from "@mui/material/InputAdornment"; +import Visibility from "@mui/icons-material/Visibility"; +import VisibilityOff from "@mui/icons-material/VisibilityOff"; import CustomSelect from "./CustomSelect"; -import CustomCheckbox from './CustomCheckbox'; -import Button from '@mui/material/Button'; -import CircularProgress from '@mui/material/CircularProgress'; +import CustomCheckbox from "./CustomCheckbox"; +import Button from "@mui/material/Button"; +import CircularProgress from "@mui/material/CircularProgress"; const defaultUser = { username: "", @@ -30,7 +30,7 @@ const SignUp = (props) => { const [user, setUser] = React.useState(defaultUser); const [errors, setErrors] = React.useState(defaultUser); const [showPassword, setShowPassword] = React.useState(false); - const [saving, setSaving] = React.useState(null) + const [saving, setSaving] = React.useState(null); const handleClickShowPassword = () => setShowPassword((show) => !show); @@ -38,17 +38,18 @@ const SignUp = (props) => { const onChangeOfValue = (key, value) => setUser({ ...user, [key]: value }); - const createUser = () => { - setSaving(true) - } + const createUser = async () => { + setSaving(true); + const response = await createUserAccount(user); + console.log("----", response); + setSaving(false); + }; + + const onBlur = () => setErrors(h.validator(user)); - const onBlur = () => setErrors(h.validator(user)) - console.log('user', user) return (
-
- Sign up here! -
+
Sign up here!
{ value={user.password} onChange={onChangeOfValue} inputProps={{ - endAdornment: + endAdornment: ( + { {showPassword ? : } + ), }} /> - - - + { value={user.state} onChange={onChangeOfValue} /> -
);