diff --git a/.prettierrc b/.prettierrc index 639c6972..8be254a5 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,5 +5,5 @@ "singleQuote": true, "trailingComma": "none", "jsxBracketSameLine": false, - "endOfLine": "lf" + "endOfLine": "auto" } diff --git a/package-lock.json b/package-lock.json index 6891fbef..4bbf17e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "react-modal": "^3.16.1", "react-router-dom": "^6.8.0", "react-scripts": "5.0.1", + "validator": "^13.12.0", "web-vitals": "^3.1.1" }, "devDependencies": { @@ -17116,6 +17117,15 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 249b6b05..6045acce 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "react-modal": "^3.16.1", "react-router-dom": "^6.8.0", "react-scripts": "5.0.1", + "validator": "^13.12.0", "web-vitals": "^3.1.1" }, "scripts": { diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js index 39da3cae..c4cdf58e 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -1,8 +1,19 @@ import { useState } from 'react'; -const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => { +const TextInput = ({ + value, + onChange, + errorResponse = '', + name, + label, + icon, + require = false, + type = 'text' +}) => { const [input, setInput] = useState(''); + // const [isValidInput, setIsValidInput] = useState(true); const [showpassword, setShowpassword] = useState(false); + if (type === 'password') { return (
@@ -11,6 +22,7 @@ const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => { type={type} name={name} value={value} + required={require} onChange={(e) => { onChange(e); setInput(e.target.value); @@ -26,6 +38,7 @@ const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => { > + {errorResponse && {errorResponse}}
); } else { @@ -36,10 +49,12 @@ const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => { type={type} name={name} value={value} + required={require} onChange={onChange} className={icon && 'input-has-icon'} /> {icon && {icon}} + {errorResponse && {errorResponse}} ); } diff --git a/src/context/auth.js b/src/context/auth.js index 47cd66c9..5f0bbdc9 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -29,13 +29,15 @@ const AuthProvider = ({ children }) => { const res = await login(email, password); if (!res.data.token) { - return navigate('/login'); + navigate('/login'); + return res; } localStorage.setItem('token', res.data.token); setToken(res.token); navigate(location.state?.from?.pathname || '/'); + return res; }; const handleLogout = () => { @@ -45,9 +47,13 @@ const AuthProvider = ({ children }) => { const handleRegister = async (email, password) => { const res = await register(email, password); - setToken(res.data.token); + if (res.status === 'success') { + const loginRes = await login(email, password); + setToken(loginRes.data.token); + navigate('/verification'); + } - navigate('/verification'); + return res; }; const handleCreateProfile = async (firstName, lastName, githubUrl, bio) => { diff --git a/src/pages/login/index.js b/src/pages/login/index.js index 08df7d5a..3bf36bc0 100644 --- a/src/pages/login/index.js +++ b/src/pages/login/index.js @@ -8,12 +8,22 @@ import './login.css'; const Login = () => { const { onLogin } = useAuth(); const [formData, setFormData] = useState({ email: '', password: '' }); + const [response, setResponse] = useState(''); const onChange = (e) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); }; + const submit = async (e) => { + e.preventDefault(); + const data = await onLogin(formData.email, formData.password); + if (data.status === 'fail') { + setResponse(data.data.email); + console.log(data.data.email); + } + }; + return (
{ altButtonText="Sign up" >
-
+ { name="password" label={'Password *'} type={'password'} + errorResponse={response} /> + +
diff --git a/src/pages/register/index.js b/src/pages/register/index.js index 5cc70e32..cc3f0284 100644 --- a/src/pages/register/index.js +++ b/src/pages/register/index.js @@ -4,10 +4,35 @@ import TextInput from '../../components/form/textInput'; import useAuth from '../../hooks/useAuth'; import CredentialsCard from '../../components/credentials'; import './register.css'; +import { validEmail, validPassword } from '../../service/inputValidationService'; const Register = () => { const { onRegister } = useAuth(); const [formData, setFormData] = useState({ email: '', password: '' }); + const [emailResponse, setEmailResponse] = useState(''); + const [passwordReponse, setPasswordResponse] = useState(''); + const submit = async (e) => { + e.preventDefault(); + setEmailResponse(''); + setPasswordResponse(''); + const emailValid = validEmail(formData.email); + const passwordValid = validPassword(formData.password); + if (!passwordValid.isValid) { + setPasswordResponse(passwordValid.message); // Set password error response + } + if (!emailValid.isValid) { + setEmailResponse(emailValid.message); // Set email error response + } + // Only request if email and password is valid + if (passwordValid.isValid && emailValid.isValid) { + const data = await onRegister(formData.email, formData.password); + console.log(data); + if (data.status === 'fail' && data.email !== null) { + console.log('email'); + setEmailResponse(data.data.email); + } + } + }; const onChange = (e) => { const { name, value } = e.target; @@ -24,27 +49,32 @@ const Register = () => { altButtonText="Log in" >
-
+ onChange(e)} + type="text" name="email" label={'Email *'} + errorResponse={emailResponse} /> + + Password must contain atleast 8 characters, including atleast one capital letter, one + number and one special character + + *Required +
diff --git a/src/service/apiClient.js b/src/service/apiClient.js index 5f3cdbcf..575a7e96 100644 --- a/src/service/apiClient.js +++ b/src/service/apiClient.js @@ -5,8 +5,8 @@ async function login(email, password) { } async function register(email, password) { - await post('users', { email, password }, false); - return await login(email, password); + return post('users', { email, password }, false); + // return await login(email, password); } async function createProfile(userId, firstName, lastName, githubUrl, bio) { diff --git a/src/service/inputValidationService.js b/src/service/inputValidationService.js new file mode 100644 index 00000000..dd93e6f0 --- /dev/null +++ b/src/service/inputValidationService.js @@ -0,0 +1,53 @@ +import validator from 'validator'; + +export const validEmail = (email) => { + if (validator.isEmail(email)) { + return { + isValid: true, + message: '' + }; + } else { + return { + isValid: false, + message: 'Invalid email format' + }; + } +}; + +export const validPassword = (password) => { + if (password.length < 8) { + return { + isValid: false, + message: 'Password must be at least 8 characters long' + }; + } + if (!/[A-Za-z]/.test(password)) { + return { + isValid: false, + message: 'Password must contain at least one letter' + }; + } + if (!/[A-Z]/.test(password)) { + return { + isValid: false, + message: 'Password must contain at least one uppercase letter' + }; + } + if (!/\d/.test(password)) { + return { + isValid: false, + message: 'Password must contain at least one number' + }; + } + if (!/[@$!%*?&]/.test(password)) { + return { + isValid: false, + message: 'Password must contain at least one special character' + }; + } + + return { + isValid: true, + message: 'Success' + }; +}; diff --git a/src/styles/_form.css b/src/styles/_form.css index 419cfb63..837ceb7c 100644 --- a/src/styles/_form.css +++ b/src/styles/_form.css @@ -31,6 +31,14 @@ form label { padding-right: 10px; } +.input-description{ + color: var(--color-blue2); +} + +.input-invalid { + color: var(--color-red) +} + .showpasswordbutton { position: absolute; bottom: 28px; diff --git a/src/styles/_globals.css b/src/styles/_globals.css index eb9772c6..6d4d1b3b 100644 --- a/src/styles/_globals.css +++ b/src/styles/_globals.css @@ -8,6 +8,7 @@ --color-green: #64dc78; --color-offwhite: #f0f5fa; --color-lightgrey: #f5f5f5; + --color-red: #F00000; } /* global settings here, resetting a lot of defaults */