Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/components/form/textInput/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const TextInput = ({
readOnly = false,
type = 'text',
placeholder = '',
onClick,
onBlur: onBlurProp,
disabled = false,
maxLength = 280,
Expand Down Expand Up @@ -41,6 +42,7 @@ const TextInput = ({
onChange={onChange}
onKeyDown={onKeyDown}
onBlur={handleBlur}
onClick={onClick}
autoComplete={type === 'password' ? 'current-password' : undefined}
className={className}
disabled={disabled}
Expand Down
4 changes: 2 additions & 2 deletions src/context/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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;
Expand Down
35 changes: 32 additions & 3 deletions src/pages/profile/ProfilePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -66,12 +74,33 @@ 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;

const { cohort, ...tempCurrentUserWithoutCohort } = tempCurrentUser;
onPatchProfile(tempCurrentUserWithoutCohort);
// if the password field is empty then patch without changing password, else patch with new password.

if (tempCurrentUser.password === '') {
onCreateProfile(tempCurrentUserWithoutCohort);
} else {
onPatchProfile(tempCurrentUserWithoutCohort);
}

tempCurrentUser.password = '';

if (!pathParamId || String(pathParamId) === String(user.id)) {
const { password, ...userWithoutPassword } = tempCurrentUser;
Expand Down Expand Up @@ -143,7 +172,7 @@ const ProfilePage = () => {
/>

{/* Edit button */}
<ProfileEditButton isEditing={isEditing} toggleEdit={toggleEdit} />
<ProfileEditButton isEditing={isEditing} toggleEdit={toggleEdit} canSave={canSave} />
</div>
</Card>
</main>
Expand Down
50 changes: 41 additions & 9 deletions src/pages/profile/contactInfo/index.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<Form>
<section>
Expand All @@ -32,15 +42,37 @@ const ProfileContactInfo = ({ email, mobile, password, onChange, isEditing }) =>
/>

{String(pathParamId) === String(user.id) ? (
<TextInput
label="Password"
type="password"
name="password"
value={password}
onChange={(e) => onChange('password', e.target.value)}
className={getInputClass('password', isEditing, user.role)}
disabled={!canEditField('password', isEditing, user.role)}
/>
<div>
<TextInput
label="Password"
type="password"
name="password"
value={password}
onChange={(e) => onChange('password', e.target.value)}
onClick={() => {
setPasswordCondition(true);
}}
className={getInputClass('password', isEditing, user.role)}
disabled={!canEditField('password', isEditing, user.role)}
/>
{passwordCondition && (
<div className="password-hint">
Password must contain at least: <br />
<ul className="password-hint-3">
<li className={valEightChars(password) ? 'valid' : 'invalid'}>
- eight characters
</li>
<li className={valCapLetter(password) ? 'valid' : 'invalid'}>
- one capital letter
</li>
<li className={valNumber(password) ? 'valid' : 'invalid'}>- one number</li>
<li className={valSpecialChar(password) ? 'valid' : 'invalid'}>
- one special character
</li>
</ul>
</div>
)}
</div>
) : null}
</div>
</section>
Expand Down
9 changes: 7 additions & 2 deletions src/pages/profile/editButton/index.jsx
Original file line number Diff line number Diff line change
@@ -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();

Expand All @@ -10,7 +10,12 @@ export const ProfileEditButton = ({ isEditing, toggleEdit }) => {
if (user.role !== 1 && !isOwnProfile) return null;

return (
<button className="edit-btn" onClick={toggleEdit} type="button">
<button
className="edit-btn"
onClick={toggleEdit}
type="button"
disabled={!canSave && isEditing}
>
{isEditing ? 'Save' : 'Edit'}
</button>
);
Expand Down
8 changes: 4 additions & 4 deletions src/service/apiClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading