From cb85501f7c9d726b8cf19ed8199d21565aa0314a Mon Sep 17 00:00:00 2001 From: evgeniywas Date: Wed, 12 Jul 2023 21:47:42 +0100 Subject: [PATCH 1/7] RC-8: update locale for profile page --- .../main/navigationPages/profile/i18n/en.js | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/app/main/navigationPages/profile/i18n/en.js b/src/app/main/navigationPages/profile/i18n/en.js index 2d905d1..a92eb53 100644 --- a/src/app/main/navigationPages/profile/i18n/en.js +++ b/src/app/main/navigationPages/profile/i18n/en.js @@ -1,3 +1,25 @@ -const locale = {}; +const locale = { + upload_picture_btn: 'Upload New Picture', + delete_picture: 'Delete', + first_picture_req: 'Lorem ipsum dolor st ut nec.', + second_picture_req: 'Lorem ipsum dolor sit amet consectetur. Etiam tristique feugiat ut nec.', + picture_size_error: 'The file is too large', + picture_extensions_error: 'We only support jpeg and png file extensions', + first_name: 'First Name', + last_name: 'Last Name', + display_name: 'Display Name', + display_name_error: + 'Lorem ipsum dolor sit amet consectetur. Eget pellentesque id consequat consectetur eu quis.', + email: 'Email', + email_error: + 'Lorem ipsum dolor sit amet consectetur. Eget pellentesque id consequat consectetur eu quis.', + mobile_number: 'Mobile Number', + mobile_number_error: 'The mobile number is not correct', + information: 'Biographical Information', + address: 'Address', + save_changes: 'save changes', + max_length_error: 'The maximum length is {{length}}', + min_length_error: 'The minimum length is {{length}}', +}; export default locale; From b0d0579ce71b2f051a50ff888f29426fba16dbad Mon Sep 17 00:00:00 2001 From: evgeniywas Date: Wed, 12 Jul 2023 21:48:28 +0100 Subject: [PATCH 2/7] RC-8: correct fuse layout and update theme config --- src/@fuse/core/FuseLayout/FuseLayout.js | 2 +- src/app/configs/themesConfig.js | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/@fuse/core/FuseLayout/FuseLayout.js b/src/@fuse/core/FuseLayout/FuseLayout.js index d5975df..ad418c7 100644 --- a/src/@fuse/core/FuseLayout/FuseLayout.js +++ b/src/@fuse/core/FuseLayout/FuseLayout.js @@ -53,7 +53,7 @@ const inputGlobalStyles = ( // }, // }, '[class*="MuiOutlinedInput-root"]': { - borderRadius: theme.size?.inputRadius, + borderRadius: `${theme.spacing('10px')}!important`, }, '[class^="border"]': { borderColor: theme.palette.divider, diff --git a/src/app/configs/themesConfig.js b/src/app/configs/themesConfig.js index c0c5010..b77c012 100644 --- a/src/app/configs/themesConfig.js +++ b/src/app/configs/themesConfig.js @@ -48,9 +48,6 @@ const themesConfig = { dark: '#b71c1c', }, }, - size: { - inputRadius: '10px', - }, }, defaultDark: { palette: { From e6dfcc8cf77ec63cf5b19a2879963ae01aeb5e43 Mon Sep 17 00:00:00 2001 From: evgeniywas Date: Wed, 12 Jul 2023 21:49:07 +0100 Subject: [PATCH 3/7] RC-8: update auth service and user slice --- src/app/main/authPages/sign-up/SignUpPage.js | 2 +- src/app/services/authService.js | 7 +++---- src/app/store/userSlice.js | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/app/main/authPages/sign-up/SignUpPage.js b/src/app/main/authPages/sign-up/SignUpPage.js index b40e5e7..0ee1f36 100644 --- a/src/app/main/authPages/sign-up/SignUpPage.js +++ b/src/app/main/authPages/sign-up/SignUpPage.js @@ -83,7 +83,7 @@ function SignUpPage({ t }) { className="mb-28" label={t('name')} autoFocus - type="name" + type="text" error={!!errors.name} helperText={errors?.name?.message} variant="outlined" diff --git a/src/app/services/authService.js b/src/app/services/authService.js index 387fa3a..50b618b 100644 --- a/src/app/services/authService.js +++ b/src/app/services/authService.js @@ -63,13 +63,12 @@ export default class AuthService extends FuseUtils.EventEmitter { } const userRef = firebaseDb.ref(this.#db, `users/${this.#auth.currentUser.uid}`); - const value = { data: { ...user } }; firebaseDb - .set(userRef, value) + .set(userRef, user) .then(() => { - if (user.email) { - return firebaseAuth.updateEmail(this.#auth, user.email); + if (user.data.email !== this.#auth.currentUser.email) { + return firebaseAuth.updateEmail(this.#auth, user.data.email); } return null; diff --git a/src/app/store/userSlice.js b/src/app/store/userSlice.js index d9d6da1..002b421 100644 --- a/src/app/store/userSlice.js +++ b/src/app/store/userSlice.js @@ -22,7 +22,7 @@ export const updateUserSettings = createAsyncThunk( 'user/updateSettings', async (settings, { dispatch, getState }) => { const { user } = getState(); - const newUser = _.merge({}, user, { data: { settings } }); + const newUser = _.merge({}, user, { data: { ...settings } }); dispatch(updateUserData(newUser)); @@ -67,7 +67,6 @@ const initialState = { role: [], // guest data: { displayName: 'John Doe', - photoURL: 'assets/images/avatars/brian-hughes.jpg', email: 'johndoe@withinpixels.com', }, }; From ae1dba3da9c7ac71eaba62fc319414434d50b072 Mon Sep 17 00:00:00 2001 From: evgeniywas Date: Wed, 12 Jul 2023 21:50:06 +0100 Subject: [PATCH 4/7] RC-8: create profile page --- .../main/navigationPages/profile/Profile.js | 394 +++++++++++++++++- .../navigationPages/profile/ProfileConfig.js | 29 +- 2 files changed, 375 insertions(+), 48 deletions(-) diff --git a/src/app/main/navigationPages/profile/Profile.js b/src/app/main/navigationPages/profile/Profile.js index da0be44..2ec9f32 100644 --- a/src/app/main/navigationPages/profile/Profile.js +++ b/src/app/main/navigationPages/profile/Profile.js @@ -1,36 +1,386 @@ -import { styled } from '@mui/material/styles'; -import { useTranslation } from 'react-i18next'; import FusePageSimple from '@fuse/core/FusePageSimple'; -import DemoContent from '@fuse/core/DemoContent'; +import _ from '@lodash'; +import FuseSvgIcon from '@fuse/core/FuseSvgIcon/FuseSvgIcon'; +import { yupResolver } from '@hookform/resolvers/yup'; +import Button from '@mui/material/Button'; +import Paper from '@mui/material/Paper'; +import TextField from '@mui/material/TextField'; +import { styled } from '@mui/material/styles'; +import { selectUser, updateUserSettings } from 'app/store/userSlice'; +import { Controller, useForm } from 'react-hook-form'; +import { withTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; +import * as yup from 'yup'; +import { toBase64 } from 'src/app/utils'; + +const MAX_PICTURE_SIZE = 5000000; +const AVAILABLE_MEDIA_TYPES = ['image/png', 'image/jpeg']; const Root = styled(FusePageSimple)(({ theme }) => ({ - '& .FusePageSimple-header': { - backgroundColor: theme.palette.background.paper, - borderBottomWidth: 1, - borderStyle: 'solid', - borderColor: theme.palette.divider, - }, + '& .FusePageSimple-header': {}, '& .FusePageSimple-toolbar': {}, - '& .FusePageSimple-content': {}, + '& .FusePageSimple-content': { + backgroundColor: theme.palette.background.default, + }, '& .FusePageSimple-sidebarHeader': {}, '& .FusePageSimple-sidebarContent': {}, })); -function ProfilePage(props) { - const { t } = useTranslation('profilePage'); +function ProfilePage({ t }) { + const dispatch = useDispatch(); + const user = useSelector(selectUser); + + const defaultValues = { + photoURL: '', + firstName: '', + lastName: '', + displayName: '', + email: '', + mobileNumber: '', + information: '', + address: '', + ...user.data, + }; + const schema = yup.object().shape({ + photoURL: yup.string().notRequired(), + firstName: yup + .string() + .max(150, t('max_length_error', { length: 150 })) + .trim() + .notRequired(), + lastName: yup + .string() + .max(150, t('max_length_error', { length: 150 })) + .trim() + .notRequired(), + displayName: yup + .string() + .required(t('display_name_error')) + .max(30, t('max_length_error', { length: 30 })) + .trim(), + email: yup + .string() + .matches(/^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/g, t('email_error')) + .required(t('email_error')) + .max(40, t('max_length_error', { length: 40 })), + mobileNumber: yup + .string() + .matches(/^(?=(.*\d){7,})\+?(\d[\d-. ]+)?(\([\d-. ]+\))?[\d-. ]+\d$/, { + message: t('mobile_number_error'), + excludeEmptyString: true, + }) + .max(20, t('max_length_error', { length: 20 })) + .notRequired(), + information: yup + .string() + .max(300, t('max_length_error', { length: 300 })) + .trim() + .notRequired(), + address: yup + .string() + .max(150, t('max_length_error', { length: 150 })) + .trim() + .notRequired(), + }); + + const { control, formState, watch, setValue, handleSubmit, setError, reset } = useForm({ + mode: 'onChange', + defaultValues, + resolver: yupResolver(schema), + }); + const { dirtyFields, errors } = formState; + + const uploadPicture = async (event) => { + const { target } = event; + if (target.files && target.files[0]) { + const file = target.files[0]; + if (file.size > MAX_PICTURE_SIZE) { + return setError('photoURL', { type: 'custom', message: t('picture_size_error') }); + } + + if (!AVAILABLE_MEDIA_TYPES.includes(file.type)) { + return setError('photoURL', { + type: 'custom', + message: t('picture_extensions_error'), + }); + } + + const base64 = await toBase64(file); + setValue('photoURL', base64, { shouldDirty: true }); + } else { + setError('photoURL', { type: 'custom', message: 'Choose a file please' }); + } + }; + + const deletePicture = () => setValue('photoURL', '', { shouldDirty: true }); + + const onSubmit = (data) => { + dispatch(updateUserSettings(data)).catch((error) => { + setError('root', { + type: 'manual', + message: error.message, + }); + }); + reset(data); + }; return ( - //

{t('TITLE')}

- // - // } content={ -
-

Content

-
- +
+
+
+ + {watch('photoURL') && ( + user + )} + +
+ ( + + )} + /> + +
    +
  • {t('first_picture_req')}
  • +
  • {t('second_picture_req')}
  • +
+
+
+ +
+ ( + theme.palette.background.paper, + }, + }} + /> + )} + /> + + ( + theme.palette.background.paper, + }, + }} + /> + )} + /> + + ( + theme.palette.background.paper, + }, + }} + /> + )} + /> + + ( + theme.palette.background.paper, + }, + }} + /> + )} + /> + + ( + theme.palette.background.paper, + }, + }} + /> + )} + /> + + ( + theme.palette.background.paper, + padding: 0, + }, + }} + // eslint-disable-next-line react/jsx-no-duplicate-props + inputProps={{ + sx: { + padding: '16.5px 14px', + }, + }} + /> + )} + /> + + ( + theme.palette.background.paper, + padding: 0, + }, + }} + // eslint-disable-next-line react/jsx-no-duplicate-props + inputProps={{ + sx: { + padding: '16.5px 14px', + }, + }} + /> + )} + /> +
+ +
+ + {errors.root?.message && ( +

{errors.root?.message}

+ )} +
+
} scroll="content" @@ -38,4 +388,4 @@ function ProfilePage(props) { ); } -export default ProfilePage; +export default withTranslation('profilePage')(ProfilePage); diff --git a/src/app/main/navigationPages/profile/ProfileConfig.js b/src/app/main/navigationPages/profile/ProfileConfig.js index 1603bd4..349dd6f 100644 --- a/src/app/main/navigationPages/profile/ProfileConfig.js +++ b/src/app/main/navigationPages/profile/ProfileConfig.js @@ -1,11 +1,13 @@ +import { lazy } from 'react'; import i18next from 'i18next'; import authRoles from '../../../configs/authRoles'; import en from './i18n/en'; -import Profile from './Profile'; i18next.addResourceBundle('en', 'profilePage', en); +const Profile = lazy(() => import('./Profile')); + const ProfileConfig = { settings: { layout: { @@ -22,28 +24,3 @@ const ProfileConfig = { }; export default ProfileConfig; - -/** - * Lazy load Example - */ -/* -import React from 'react'; - -const Example = lazy(() => import('./Example')); - -const ExampleConfig = { - settings: { - layout: { - config: {}, - }, - }, - routes: [ - { - path: 'example', - element: , - }, - ], -}; - -export default ExampleConfig; -*/ From 3dc66e8fa09793781be2581f5f21da7c974da08e Mon Sep 17 00:00:00 2001 From: evgeniywas Date: Wed, 12 Jul 2023 21:50:21 +0100 Subject: [PATCH 5/7] RC-8: create to base 64 util --- src/app/utils/index.js | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/app/utils/index.js diff --git a/src/app/utils/index.js b/src/app/utils/index.js new file mode 100644 index 0000000..fbc8822 --- /dev/null +++ b/src/app/utils/index.js @@ -0,0 +1,7 @@ +export const toBase64 = (value) => + new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(value); + reader.onload = () => resolve(reader.result); + reader.onerror = reject; + }); From 9469c76a23868acfdc15469cef0d0939c50a14c6 Mon Sep 17 00:00:00 2001 From: evgeniywas Date: Wed, 12 Jul 2023 21:50:35 +0100 Subject: [PATCH 6/7] RC-8: add global class --- src/styles/app-components.css | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/styles/app-components.css b/src/styles/app-components.css index db1004b..df34b27 100644 --- a/src/styles/app-components.css +++ b/src/styles/app-components.css @@ -10,3 +10,22 @@ @import 'prism.css'; @tailwind components; + +@layer components { + .bullet { + position: relative; + padding-left: 30px; + } + + .bullet::before { + content: ''; + position: absolute; + transform: translateY(50%); + left: 0; + display: inline-block; + width: 10px; + height: 10px; + background-color: #4d53ff; + border-radius: 50%; + } +} From 8c9c37cd8dac39a7654e3abd9afaef5d319551fe Mon Sep 17 00:00:00 2001 From: evgeniywas Date: Wed, 12 Jul 2023 21:51:04 +0100 Subject: [PATCH 7/7] RC-8: add database rules --- database.rules.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 database.rules.json diff --git a/database.rules.json b/database.rules.json new file mode 100644 index 0000000..e27a977 --- /dev/null +++ b/database.rules.json @@ -0,0 +1,21 @@ +{ + "rules": { + ".read": false, + ".write": false, + "users": { + "$userId": { + ".read": "auth.uid === $userId", + ".write": "auth.uid === $userId" + } + } + // // readable node + // "messages": { + // ".read": true + // }, + // // readable and writable node + // "messages": { + // ".read": true, + // ".write": true + // } + } +}