From 063564547f9066f3752ba102eb528f155d8c174b Mon Sep 17 00:00:00 2001 From: Erlend Skutlaberg Date: Wed, 26 Feb 2025 09:01:36 +0100 Subject: [PATCH 1/6] Set the endOfLine parameter for prettier to auto --- .prettierrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" } From 8e64d189b44cde3dee4b9e2c42870c044d7741c7 Mon Sep 17 00:00:00 2001 From: Sander Rasmussen Date: Wed, 26 Feb 2025 10:52:41 +0100 Subject: [PATCH 2/6] added password input validation in register from --- src/components/form/textInput/index.js | 3 ++- src/pages/register/index.js | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js index 39da3cae..db15aad0 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -1,6 +1,6 @@ import { useState } from 'react'; -const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => { +const TextInput = ({ value, onChange, pattern, name, label, icon, type = 'text' }) => { const [input, setInput] = useState(''); const [showpassword, setShowpassword] = useState(false); if (type === 'password') { @@ -11,6 +11,7 @@ const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => { type={type} name={name} value={value} + pattern={pattern.value} onChange={(e) => { onChange(e); setInput(e.target.value); diff --git a/src/pages/register/index.js b/src/pages/register/index.js index 5cc70e32..998cf0c0 100644 --- a/src/pages/register/index.js +++ b/src/pages/register/index.js @@ -9,6 +9,9 @@ const Register = () => { const { onRegister } = useAuth(); const [formData, setFormData] = useState({ email: '', password: '' }); + const Submit = () => { + console.log('submit'); + }; const onChange = (e) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); @@ -24,7 +27,7 @@ const Register = () => { altButtonText="Log in" >
-
+ { name="password" label={'Password *'} type={'password'} + pattern={{ + value: '^(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*])[A-Za-z\\d!@#$%^&*]{8,}$', + message: 'test' + }} + /> +
From aab5f5a64291e6ea8a9d621eb23861b18b042e60 Mon Sep 17 00:00:00 2001 From: Sander Rasmussen Date: Wed, 26 Feb 2025 12:25:59 +0100 Subject: [PATCH 3/6] added standard pattern to form --- src/components/form/textInput/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js index db15aad0..c6bbd1a9 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -1,7 +1,8 @@ import { useState } from 'react'; -const TextInput = ({ value, onChange, pattern, name, label, icon, type = 'text' }) => { +const TextInput = ({ value, onChange, pattern = '', name, label, icon, type = 'text' }) => { const [input, setInput] = useState(''); + // const [isValidInput, setIsValidInput] = useState(true); const [showpassword, setShowpassword] = useState(false); if (type === 'password') { return ( From 4c6b63d719e1592685e5a10e8f61fe144580eaad Mon Sep 17 00:00:00 2001 From: Erlend Skutlaberg Date: Wed, 26 Feb 2025 12:29:14 +0100 Subject: [PATCH 4/6] Testing some features --- src/components/form/textInput/index.js | 7 +++++- src/pages/register/index.js | 33 ++++++++++++++++++-------- src/styles/_form.css | 8 +++++++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js index 39da3cae..e6b769ba 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -1,14 +1,17 @@ import { useState } from 'react'; -const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => { +const TextInput = ({ value, onChange, name, label, icon, pattern, title, type = 'text' }) => { const [input, setInput] = useState(''); const [showpassword, setShowpassword] = useState(false); + if (type === 'password') { return (
{ @@ -34,12 +37,14 @@ const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => { {label && } {icon && {icon}} + {pattern.message}
); } diff --git a/src/pages/register/index.js b/src/pages/register/index.js index 5cc70e32..9c517326 100644 --- a/src/pages/register/index.js +++ b/src/pages/register/index.js @@ -1,14 +1,20 @@ import { useState } from 'react'; import Button from '../../components/button'; import TextInput from '../../components/form/textInput'; -import useAuth from '../../hooks/useAuth'; +// import useAuth from '../../hooks/useAuth'; import CredentialsCard from '../../components/credentials'; import './register.css'; const Register = () => { - const { onRegister } = useAuth(); + // const { onRegister } = useAuth(); const [formData, setFormData] = useState({ email: '', password: '' }); - + const onRegister = (email, password) => { + console.log('register'); + console.log(email); + }; + const submit = (e) => { + console.log('Submit'); + }; const onChange = (e) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); @@ -24,27 +30,34 @@ const Register = () => { altButtonText="Log in" >
-
+ onChange(e)} type="email" name="email" label={'Email *'} + pattern={{ + value: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$', + message: 'Email in wrong format' + }} /> +
diff --git a/src/styles/_form.css b/src/styles/_form.css index 419cfb63..ef564e52 100644 --- a/src/styles/_form.css +++ b/src/styles/_form.css @@ -31,6 +31,14 @@ form label { padding-right: 10px; } +input:invalid ~ .input-error{ + display: block; +} + +.input-error{ + display: none; +} + .showpasswordbutton { position: absolute; bottom: 28px; From 794aa750e9af65fc4e3dd4dd3161dfc5f0ca8d4a Mon Sep 17 00:00:00 2001 From: Erlend Skutlaberg Date: Wed, 26 Feb 2025 16:15:36 +0100 Subject: [PATCH 5/6] Changed from regex to the same validation as the backend. The error messages are prop drilled to the textinput components. No requests are made in the registration unless we are certain that both the email and password contains the correct format. Also contains provides a message if the email is already in use. gg --- package-lock.json | 10 +++++ package.json | 1 + src/components/form/textInput/index.js | 20 ++++++--- src/context/auth.js | 12 ++++-- src/pages/login/index.js | 20 ++++++--- src/pages/register/index.js | 60 ++++++++++++++++---------- src/service/apiClient.js | 4 +- src/service/inputValidationService.js | 53 +++++++++++++++++++++++ src/styles/_form.css | 8 ++-- src/styles/_globals.css | 1 + 10 files changed, 145 insertions(+), 44 deletions(-) create mode 100644 src/service/inputValidationService.js 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 76ab3aa1..c4cdf58e 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -1,6 +1,15 @@ import { useState } from 'react'; -const TextInput = ({ value, onChange, pattern = '', 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); @@ -10,12 +19,10 @@ const TextInput = ({ value, onChange, pattern = '', name, label, icon, type = 't
{ onChange(e); setInput(e.target.value); @@ -31,6 +38,7 @@ const TextInput = ({ value, onChange, pattern = '', name, label, icon, type = 't > + {errorResponse && {errorResponse}}
); } else { @@ -39,14 +47,14 @@ const TextInput = ({ value, onChange, pattern = '', name, label, icon, type = 't {label && } {icon && {icon}} - {pattern.message} + {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 ca8102af..0ecb70e7 100644 --- a/src/pages/register/index.js +++ b/src/pages/register/index.js @@ -1,20 +1,39 @@ import { useState } from 'react'; import Button from '../../components/button'; import TextInput from '../../components/form/textInput'; -// import useAuth from '../../hooks/useAuth'; +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 { onRegister } = useAuth(); const [formData, setFormData] = useState({ email: '', password: '' }); - const onRegister = (email, password) => { - console.log('register'); - console.log(email); - }; - const submit = (e) => { - console.log('Submit'); + 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; setFormData({ ...formData, [name]: value }); @@ -32,34 +51,29 @@ const Register = () => {
onChange(e)} type="email" name="email" label={'Email *'} - pattern={{ - value: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$', - message: 'Email in wrong format' - }} + errorResponse={emailResponse} /> -
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 ef564e52..837ceb7c 100644 --- a/src/styles/_form.css +++ b/src/styles/_form.css @@ -31,12 +31,12 @@ form label { padding-right: 10px; } -input:invalid ~ .input-error{ - display: block; +.input-description{ + color: var(--color-blue2); } -.input-error{ - display: none; +.input-invalid { + color: var(--color-red) } .showpasswordbutton { 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 */ From eccb001097ca96de1b4b5b6c738a798dd898f782 Mon Sep 17 00:00:00 2001 From: Erlend Skutlaberg Date: Thu, 27 Feb 2025 08:51:03 +0100 Subject: [PATCH 6/6] changed the email field in the register page to a text field, so that we only get one format of erorrmessage --- src/pages/register/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/register/index.js b/src/pages/register/index.js index 0ecb70e7..cc3f0284 100644 --- a/src/pages/register/index.js +++ b/src/pages/register/index.js @@ -54,7 +54,7 @@ const Register = () => { require={true} value={formData.email} onChange={(e) => onChange(e)} - type="email" + type="text" name="email" label={'Email *'} errorResponse={emailResponse}