Skip to content
Open
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
1 change: 1 addition & 0 deletions client-side/public/images/notification.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions client-side/public/images/pencil.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions client-side/src/components/signUp/PasswordStrength.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import { Box, LinearProgress } from '@mui/material';
import './PasswordStrength.scss';
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reorder the imports, style files should be the last ones

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

קשור לאפרת הרטמן

import { PASSWORD_STRENGTH } from '../../constants';

const PasswordStrengthMeter = ({ password }) => {

const colors = {
passwordStrength: {
empty: '#f00',
weak: '#ffa500',
medium: '#008000',
}
};
const calculatePasswordStrength = (password) => {
if (!password) return PASSWORD_STRENGTH.EMPTY;
if (password.length < 4) return PASSWORD_STRENGTH.WEAK;
if (/[A-Za-z]/.test(password) && /[0-9]/.test(password) && password.length > 4) return PASSWORD_STRENGTH.MEDIUM;
return PASSWORD_STRENGTH.WEAK;
};
const strength = calculatePasswordStrength(password);

const { color, message } = strength;
const strengthColor = colors.passwordStrength[color];
const strengthClass = {
[PASSWORD_STRENGTH.EMPTY.key]: 'weak',
[PASSWORD_STRENGTH.WEAK.key]: 'medium',
[PASSWORD_STRENGTH.MEDIUM.key]: 'strong',
}[strength.key];
return (
<Box className="centered-container">
<LinearProgress
variant="determinate"
value={(strength.key / 3) * 100}
className={`password-strength-meter ${strengthClass}`}
/>
<Box className="password-strength-message" sx={{ color: strengthColor }}>
{message}
</Box>
</Box>
);
};
export default PasswordStrengthMeter;
35 changes: 35 additions & 0 deletions client-side/src/components/signUp/PasswordStrength.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.centered-container {
width: 50%;
margin-left: 2%;
}

.password-strength-meter {
height: 10px;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace with %

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

קשור לאפרת הרטמן

border-radius: 5px;
right: 3%;
background-color: #D3D3D3;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

colors should be saved in variable, fix all occurrences

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

קשור לאפרת הרטמן


&.weak {
& .MuiLinearProgress-bar {
background-color: red;
}
}

&.medium {
& .MuiLinearProgress-bar {
background-color: orange;
}
}

&.strong {
& .MuiLinearProgress-bar {
background-color: green;
}
}
}

.password-strength-message {
margin-top: 1px;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace with %

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

קשור לאפרת הרטמן

margin-right: 34%;
font-size: small;
}
166 changes: 166 additions & 0 deletions client-side/src/components/signUp/signUp.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { enqueueSnackbar, useSnackbar } from 'notistack';
// import ReCAPTCHA from 'react-google-recaptcha';
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all comments

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

בקוד של אפרת יש את זה כשזה עובד. זה אמור להיות באמת בקוד. למחוק? או להשאיר בהערה?

import { useFormik } from 'formik';
import * as Yup from 'yup';
import { useNavigate } from 'react-router-dom';
import PasswordStrengthMeter from '../signUp/PasswordStrength';
import GenericButton from '../../stories/Button/GenericButton';
import ToastMessage from '../../stories/Toast/ToastMessage.jsx';
import GenericInput from '../../stories/GenericInput/genericInput';
import { MessagesSignUp,TOAST_MESSAGES } from '../../constants';
import { createUser, updateUser } from '../../services/userService';
import { addUser, updateUserDetails } from '../../redux/user/user.slice';
import './signUp.scss';

const SignUpSchema = Yup.object().shape({
name: Yup.string()
.matches(/^[a-zA-Z\u0590-\u05FF\s]+$/, MessagesSignUp.name.matches)
.required(MessagesSignUp.name.required),
email: Yup.string()
.email(MessagesSignUp.email.invalid)
.required(MessagesSignUp.email.required),
password: Yup.string()
.required(MessagesSignUp.password.required)
.matches(/[A-Za-z]/, MessagesSignUp.password.matches.letters)
.matches(/\d/, MessagesSignUp.password.matches.digits)
.min(4, MessagesSignUp.password.min)
});
function SignUp({ isEditMode = false, onSave }) {
const [password, setPassword] = useState('');
// const [robotPass, setRobotPass] = useState(null)
const url = process.env.REACT_APP_SITEKEY;
const dispatch = useDispatch();
const navigate=useNavigate();
const user = useSelector(state => state.user.currentUser|| {});
const userId =user.id || null;
const formik = useFormik({
initialValues:{ name: user.name || '', email: user.email || '', password: '' },
validationSchema: SignUpSchema,
onSubmit: (values) => {
const user = {
name: values.name,
email: values.email,
password: values.password,
};
if (isEditMode) {
editUser(user);
} else {
userSignUp(user);
}
},
});
useEffect(() => {
if (isEditMode) {
formik.setValues({ name: user.name || '', email: user.email || '', password: '' });
}
}, [isEditMode]);
const resetFormValues = () => {
formik.resetForm({
values: {
name: user.name || '',
email: user.email || '',
password: ''
}
});
};

const userSignUp = async (user) => {
try {
await createUser(user);
dispatch(addUser(user));
navigate('/home');
} catch (error) {
console.error("The user is not included in the system");
}
};
const editUser = async (user) => {
try {
await updateUser(user,userId);
dispatch(updateUserDetails(user));
enqueueSnackbar(<ToastMessage message={TOAST_MESSAGES.USER_UPDATED_SUCCESS} type="success" />);
navigate('/home');
} catch (error) {
if (error.response && error.response.status === 401) {
enqueueSnackbar(<ToastMessage message={TOAST_MESSAGES.USER_UPDATED_ERROR_UNAUTHORIZED} type="error" />);
}else{
enqueueSnackbar(<ToastMessage message={TOAST_MESSAGES.USER_UPDATED_ERROR} type="error" />);
}
resetFormValues();
}
};
return (
<div className="signup-container">
<h2>{isEditMode ? 'Edit User' : 'Sign Up'}</h2>
<form onSubmit={formik.handleSubmit}>
<div className="form-group">
<GenericInput
label="user name"
type="text"
name="name"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.name}
width="100%"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all styles to scss file

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

קשור לאפרת הרטמן

size="medium"
/>
{formik.touched.name && formik.errors.name ? (
<div className="error">{formik.errors.name}</div>
) : null}
</div>
<div className="form-group">
<GenericInput
label="email"
type="text"
name="email"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.email}
width="100%"
size="medium"
/>
{formik.touched.email && formik.errors.email ? (
<div className="error">{formik.errors.email}</div>
) : null}
</div>
<div className="form-group" >
<GenericInput
label="password"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

labels should be taken from constants file

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

קשור לאפרת הרטמן

type="password"
name="password"

onBlur={formik.handleBlur}
value={formik.values.password}
onChange={(e) => {
formik.handleChange(e);
setPassword(e.target.value);
}}

width="100%"
size="medium"
/>
{formik.touched.password && formik.errors.password ? (
<div className="error">{formik.errors.password}</div>
) : null}
<PasswordStrengthMeter password={password} />
</div>
{/* <ReCAPTCHA
sitekey={url}

onChange={(val) => setRobotPass(val)}
/> */}
<GenericButton
className="secondary"
//
label={isEditMode ? 'Update' : 'Sign Up'}
onClick={formik.handleSubmit}
size="medium"
disabled={formik.isSubmitting }//|| !robotPass
/>
</form>
</div>
);
}
export default SignUp;

62 changes: 62 additions & 0 deletions client-side/src/components/signUp/signUp.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

$primary-color: #ccc;
$border-radius: 1%;
$direction: rtl;
.signup-container {
max-width: 25%;
margin: 0 auto;
padding: 1%;
border: 1px solid $primary-color;
border-radius: $border-radius;
box-shadow: 0 0 1% rgba(0, 0, 0, 0.1);
text-align: $direction;
}
h2 {
margin-bottom: 2%;
direction: ltr;
margin-right: calc(100% - 78%);
}
.form-group {
margin-bottom: 3%;
}
@mixin form-element {
display: block;
margin-bottom: 2%;

& + & {
margin-top: 1%;
}
}
label {
@include form-element;
}

input {
@include form-element;

width: 100%;
padding: 1%;
box-sizing: border-box;
border: 1px solid $primary-color;
border-radius: $border-radius;
direction: $direction;
margin-bottom: 16px;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace with %

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

קשור לאפרת הרטמן

}
button {
width: 100%;
padding: 2%;
color: white;
border: none;
border-radius: $border-radius;
cursor: pointer;
font-size: 5%;
}
.error {
color: red;
font-size: small;
margin-top: 1%;
margin-right: calc(100% - 54%);
}
.css-eglki6-MuiLinearProgress-root{
margin-top: 12px;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace with %

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

קשור לאפרת הרטמן

}
47 changes: 46 additions & 1 deletion client-side/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,49 @@ export const MESSAGES = {
EMAIL_LABEL: 'Email',
EMAIL_PLACEHOLDER: 'example@example.com',
PASSWORD_LABEL: 'Password',
};
};
export const MessagesSignUp = {
name: {
required: 'required field',
matches: 'The field must contain only letters',
},
email: {
required: 'required field',
invalid: 'invalid email',
},
password: {
required: 'required field',
matches: {
letters: 'field must contain english letter',
digits: 'field must contain one digit',
},
min: 'password must contain at least 4 characters',
max: 'password must be up to 20 characters',
},
};
export const PASSWORD_STRENGTH = {
EMPTY: {
key: 1,
message: 'Weak password',
color: 'red'
},
WEAK: {
key: 2,
message: 'Medium password',
color: 'orange'
},
MEDIUM: {
key: 3,
message: 'Strong password',
color: 'green'
}
};
export const HEADER = {
edit_user: 'edit user',
manage_notifications:'manage notifications'
};
export const TOAST_MESSAGES = {
USER_UPDATED_SUCCESS: 'User updated successfully!',
USER_UPDATED_ERROR: 'Error updating user!',
USER_UPDATED_ERROR_UNAUTHORIZED: 'Error updating user, Connect to the site and try again',
}
9 changes: 8 additions & 1 deletion client-side/src/redux/user/user.slice.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ const userSlice = createSlice({
* @param {UserStateType} state
* @param {PayloadAction<string>} action
*/
updateUserDetails: (state, action) => {
state.currentUser = { ...state.currentUser, ...action.payload };
},
/**
* @param {UserStateType} state
* @param {PayloadAction<string>} action
*/
deleteUser: (state, action) => {
const index = state.users.findIndex(user => user.id === action.payload);
if (index !== -1) {
Expand All @@ -50,5 +57,5 @@ const userSlice = createSlice({
}
});

export const { setUser, addUser, updateUser, deleteUser } = userSlice.actions;
export const { setUser, addUser, updateUser, deleteUser,updateUserDetails } = userSlice.actions;
export default userSlice.reducer;
Loading