From 67aabc8c8c5eca1b64d097d89f31849cad7df507 Mon Sep 17 00:00:00 2001 From: Vegard Stigen Date: Thu, 25 Sep 2025 12:59:15 +0200 Subject: [PATCH 1/4] fixed patch request on welcome and edit --- src/context/auth.js | 4 ++-- src/service/apiClient.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/context/auth.js b/src/context/auth.js index 2e03047..9e60379 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -4,7 +4,7 @@ import Header from '../components/header'; import Modal from '../components/modal'; import Navigation from '../components/navigation'; import useAuth from '../hooks/useAuth'; -import { patchProfile, login, register, getUserById } from '../service/apiClient'; +import { patchProfile, login, register, getUserById, patchUser } from '../service/apiClient'; import { normalizeClaims } from '../service/tokenDecode'; import Loader from '../components/loader/Loader'; @@ -153,7 +153,7 @@ const AuthProvider = ({ children }) => { const { id, ...body } = updatedUserData; try { - const res = await patchProfile(updatedUserData.id, body); + const res = await patchUser(updatedUserData.id, body); if (!res.ok) { console.error('Failed to created profile:', res.json()); return; diff --git a/src/service/apiClient.js b/src/service/apiClient.js index 0e4a154..3f0744d 100644 --- a/src/service/apiClient.js +++ b/src/service/apiClient.js @@ -28,8 +28,7 @@ async function register(email, password, longLife = false) { } async function patchProfile(userId, userData) { - const { password, ...dataToSend } = userData; - return await patch(`users/${userId}`, dataToSend, true, true); + return await patch(`users/${userId}`, userData, true, true); } // POST @@ -89,8 +88,9 @@ async function getUsersByName(name) { return await get(`users?name=${name}`, true, true); } -async function patchUser(id, photoUrl) { - return await patch(`users/${id}`, { photo: photoUrl }); +async function patchUser(userId, userData) { + const { password, ...dataToSend } = userData; + return await patch(`users/${userId}`, dataToSend, true, true); } // COHORTS From 3fea12d74a581bfd0173736e55aa2c5b805276ce Mon Sep 17 00:00:00 2001 From: Vegard Stigen Date: Thu, 25 Sep 2025 15:06:49 +0200 Subject: [PATCH 2/4] added hints for password edit, only get hints when clicking password input, not able to save unless password is valid or empty --- src/components/form/textInput/index.js | 2 + src/pages/profile/ProfilePage.jsx | 33 ++++++++++++++-- src/pages/profile/contactInfo/index.jsx | 50 ++++++++++++++++++++----- src/pages/profile/editButton/index.jsx | 4 +- 4 files changed, 75 insertions(+), 14 deletions(-) diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js index 2957205..0f9058a 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -13,6 +13,7 @@ const TextInput = ({ readOnly = false, type = 'text', placeholder = '', + onClick, onBlur: onBlurProp, disabled = false, maxLength = 280, @@ -41,6 +42,7 @@ const TextInput = ({ onChange={onChange} onKeyDown={onKeyDown} onBlur={handleBlur} + onClick={onClick} autoComplete={type === 'password' ? 'current-password' : undefined} className={className} disabled={disabled} diff --git a/src/pages/profile/ProfilePage.jsx b/src/pages/profile/ProfilePage.jsx index 79fcca6..a6df6da 100644 --- a/src/pages/profile/ProfilePage.jsx +++ b/src/pages/profile/ProfilePage.jsx @@ -12,11 +12,19 @@ import { ProfileEditButton } from './editButton'; import { getUserById } from '../../service/apiClient'; import Loader from '../../components/loader/Loader'; +import { + valEightChars, + valCapLetter, + valNumber, + valSpecialChar +} from '../register/registrationValidation'; + const ProfilePage = () => { const { id: pathParamId } = useParams(); - const { user, setUser, onPatchProfile } = useAuth(); + const { user, setUser, onPatchProfile, onCreateProfile } = useAuth(); const [isLoading, setIsLoading] = useState(null); const [isEditing, setIsEditing] = useState(false); + const [canSave, setCanSave] = useState(false); const [originalCurrentUser, setOriginalCurrentUser] = useState(user); // The original, before edit, state of the user we are looking at. const [tempCurrentUser, setTempCurrentUser] = useState(user); // The edited, under/after edit, state of the user we are looking at. @@ -66,11 +74,30 @@ const ProfilePage = () => { setTempCurrentUser((prev) => ({ ...prev, [field]: value })); }; + + useEffect(() => { + const password = tempCurrentUser?.password || ''; + + const isValid = + valEightChars(password) && + valCapLetter(password) && + valNumber(password) && + valSpecialChar(password); + + setCanSave(isValid || password === ''); + }, [tempCurrentUser?.password]) + + // When edit button gets toggled on/off const toggleEdit = () => { if (isEditing) { tempCurrentUser.id = pathParamId || user.id; - onPatchProfile(tempCurrentUser); + // if the password field is empty then patch without changing password, else patch with new password. + if (tempCurrentUser.password === '') { + onCreateProfile(tempCurrentUser); + } else { + onPatchProfile(tempCurrentUser); + } if (!pathParamId || String(pathParamId) === String(user.id)) { const { password, ...userWithoutPassword } = tempCurrentUser; @@ -142,7 +169,7 @@ const ProfilePage = () => { /> {/* Edit button */} - + diff --git a/src/pages/profile/contactInfo/index.jsx b/src/pages/profile/contactInfo/index.jsx index 7f52bfb..9b2e8c4 100644 --- a/src/pages/profile/contactInfo/index.jsx +++ b/src/pages/profile/contactInfo/index.jsx @@ -1,13 +1,23 @@ +import { useState } from 'react'; import { useParams } from 'react-router-dom'; import Form from '../../../components/form'; import TextInput from '../../../components/form/textInput'; import useAuth from '../../../hooks/useAuth'; import { getInputClass, canEditField } from '../helpers'; +import { + valEightChars, + valCapLetter, + valNumber, + valSpecialChar +} from '../../register/registrationValidation'; + const ProfileContactInfo = ({ email, mobile, password, onChange, isEditing }) => { const { id: pathParamId } = useParams(); const { user } = useAuth(); + const [passwordCondition, setPasswordCondition] = useState(false); + return (
@@ -32,15 +42,37 @@ const ProfileContactInfo = ({ email, mobile, password, onChange, isEditing }) => /> {String(pathParamId) === String(user.id) ? ( - onChange('password', e.target.value)} - className={getInputClass('password', isEditing, user.role)} - disabled={!canEditField('password', isEditing, user.role)} - /> +
+ onChange('password', e.target.value)} + onClick={() => { + setPasswordCondition(true); + }} + className={getInputClass('password', isEditing, user.role)} + disabled={!canEditField('password', isEditing, user.role)} + /> + {passwordCondition && ( +
+ Password must contain at least:
+
    +
  • + - eight characters +
  • +
  • + - one capital letter +
  • +
  • - one number
  • +
  • + - one special character +
  • +
+
+ )} +
) : null}
diff --git a/src/pages/profile/editButton/index.jsx b/src/pages/profile/editButton/index.jsx index 5483110..a1a66db 100644 --- a/src/pages/profile/editButton/index.jsx +++ b/src/pages/profile/editButton/index.jsx @@ -1,7 +1,7 @@ import { useParams } from 'react-router-dom'; import useAuth from '../../../hooks/useAuth'; -export const ProfileEditButton = ({ isEditing, toggleEdit }) => { +export const ProfileEditButton = ({ isEditing, toggleEdit, canSave }) => { const { id: pathParamId } = useParams(); const { user } = useAuth(); @@ -10,7 +10,7 @@ export const ProfileEditButton = ({ isEditing, toggleEdit }) => { if (user.role !== 1 && !isOwnProfile) return null; return ( - ); From 5daf9f2f172a43bd63cede6218e6836317ccde8b Mon Sep 17 00:00:00 2001 From: Vegard-S <70863160+Vegard-S@users.noreply.github.com> Date: Thu, 25 Sep 2025 13:55:44 +0000 Subject: [PATCH 3/4] chore: apply linter autofixes --- src/pages/profile/ProfilePage.jsx | 7 ++----- src/pages/profile/editButton/index.jsx | 7 ++++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/pages/profile/ProfilePage.jsx b/src/pages/profile/ProfilePage.jsx index 77f0b8e..336c5f5 100644 --- a/src/pages/profile/ProfilePage.jsx +++ b/src/pages/profile/ProfilePage.jsx @@ -74,7 +74,6 @@ const ProfilePage = () => { setTempCurrentUser((prev) => ({ ...prev, [field]: value })); }; - useEffect(() => { const password = tempCurrentUser?.password || ''; @@ -85,9 +84,8 @@ const ProfilePage = () => { valSpecialChar(password); setCanSave(isValid || password === ''); - }, [tempCurrentUser?.password]) + }, [tempCurrentUser?.password]); - // When edit button gets toggled on/off const toggleEdit = () => { if (isEditing) { @@ -95,14 +93,13 @@ const ProfilePage = () => { const { cohort, ...tempCurrentUserWithoutCohort } = tempCurrentUser; // if the password field is empty then patch without changing password, else patch with new password. - + if (tempCurrentUser.password === '') { onCreateProfile(tempCurrentUserWithoutCohort); } else { onPatchProfile(tempCurrentUserWithoutCohort); } - if (!pathParamId || String(pathParamId) === String(user.id)) { const { password, ...userWithoutPassword } = tempCurrentUser; // localStorage.setItem('user', JSON.stringify(userWithoutPassword)); diff --git a/src/pages/profile/editButton/index.jsx b/src/pages/profile/editButton/index.jsx index a1a66db..eb6526f 100644 --- a/src/pages/profile/editButton/index.jsx +++ b/src/pages/profile/editButton/index.jsx @@ -10,7 +10,12 @@ export const ProfileEditButton = ({ isEditing, toggleEdit, canSave }) => { if (user.role !== 1 && !isOwnProfile) return null; return ( - ); From 4352a56ff53a0913ed71b6599445fc99f2512cfc Mon Sep 17 00:00:00 2001 From: Jonnashell <35744008+Jonnashell@users.noreply.github.com> Date: Fri, 26 Sep 2025 08:22:36 +0200 Subject: [PATCH 4/4] Remove password from textinput after saving --- src/pages/profile/ProfilePage.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/profile/ProfilePage.jsx b/src/pages/profile/ProfilePage.jsx index 336c5f5..4ccf123 100644 --- a/src/pages/profile/ProfilePage.jsx +++ b/src/pages/profile/ProfilePage.jsx @@ -100,6 +100,8 @@ const ProfilePage = () => { onPatchProfile(tempCurrentUserWithoutCohort); } + tempCurrentUser.password = ''; + if (!pathParamId || String(pathParamId) === String(user.id)) { const { password, ...userWithoutPassword } = tempCurrentUser; // localStorage.setItem('user', JSON.stringify(userWithoutPassword));