From 63a98b1a00caf2caf5b2de449cf3e9a333a0b28a Mon Sep 17 00:00:00 2001 From: Bohdan Date: Tue, 23 Jul 2024 17:28:21 +0300 Subject: [PATCH 01/37] change from "None" to "No category" --- src/components/app-button-menu/AppButtonMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/app-button-menu/AppButtonMenu.tsx b/src/components/app-button-menu/AppButtonMenu.tsx index d672cfd40..33dcea531 100644 --- a/src/components/app-button-menu/AppButtonMenu.tsx +++ b/src/components/app-button-menu/AppButtonMenu.tsx @@ -73,7 +73,7 @@ const AppButtonMenu = >({ const filteredItems = useMemo(() => { const noneItem = { _id: 'null', - [valueField as string]: 'None' + [valueField as string]: 'No category' } const filtered = response.filter((item) => From 1e47e6779e785ce679508cbdfd261a7c4ea9e6d0 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 24 Jul 2024 10:29:35 +0300 Subject: [PATCH 02/37] implement disabled state for none checked categories --- .../app-button-menu/AppButtonMenu.tsx | 21 ++++++++++++++++--- .../app-select-button/AppSelectButton.tsx | 8 ++++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/components/app-button-menu/AppButtonMenu.tsx b/src/components/app-button-menu/AppButtonMenu.tsx index 33dcea531..f0f197709 100644 --- a/src/components/app-button-menu/AppButtonMenu.tsx +++ b/src/components/app-button-menu/AppButtonMenu.tsx @@ -35,6 +35,7 @@ interface AppButtonMenuProps extends Omit { valueField?: keyof T showNoneProperty?: boolean customSx?: { root?: SxProps } + disabled?: boolean } const AppButtonMenu = >({ @@ -51,13 +52,24 @@ const AppButtonMenu = >({ const { t } = useTranslation() const [inputValue, setInputValue] = useState('') const [selectedNames, setSelectedNames] = useState([]) + const [, setIsNoneSelectedNames] = useState(false) const { anchorEl, openMenu, renderMenu } = useMenu() const onMenuItemClick = (item: string, id: string) => { - if (selectedNames.includes(item)) { - setSelectedItems(selectedItems.filter((selected) => selected !== id)) + if (item === 'No category') { + if (selectedNames.includes(item)) { + setIsNoneSelectedNames(false) + setSelectedItems([]) + } else { + setIsNoneSelectedNames(true) + setSelectedItems([id]) + } } else { - setSelectedItems([...selectedItems, id]) + if (selectedNames.includes(item)) { + setSelectedItems(selectedItems.filter((selected) => selected !== id)) + } else { + setSelectedItems([...selectedItems, id]) + } } } @@ -87,9 +99,12 @@ const AppButtonMenu = >({ const menuItems = filteredItems.map((item) => { const field = String(valueField ? (item as T)[valueField] : item) const id = item._id + const isDisabled = + selectedNames.includes('No category') && field !== 'No category' return ( onMenuItemClick(field, id)} > diff --git a/src/components/app-select-button/AppSelectButton.tsx b/src/components/app-select-button/AppSelectButton.tsx index 299ec8c3a..34821a0f8 100644 --- a/src/components/app-select-button/AppSelectButton.tsx +++ b/src/components/app-select-button/AppSelectButton.tsx @@ -6,17 +6,19 @@ import { styles } from '~/components/app-select-button/AppSelectButton.styles' interface AppSelectButtonProps { onMenuItemClick: () => void checked: boolean + disabled?: boolean children: ReactNode } const AppSelectButton: FC = ({ checked, children, - onMenuItemClick + onMenuItemClick, + disabled = false }) => { return ( - - + + {children} ) From 8135c2895230b3023adf8f1838d77eee974c5314 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 24 Jul 2024 12:09:03 +0300 Subject: [PATCH 03/37] remove unnessesary code --- .../app-button-menu/AppButtonMenu.tsx | 21 +++---------------- .../app-select-button/AppSelectButton.tsx | 8 +++---- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/src/components/app-button-menu/AppButtonMenu.tsx b/src/components/app-button-menu/AppButtonMenu.tsx index f0f197709..33dcea531 100644 --- a/src/components/app-button-menu/AppButtonMenu.tsx +++ b/src/components/app-button-menu/AppButtonMenu.tsx @@ -35,7 +35,6 @@ interface AppButtonMenuProps extends Omit { valueField?: keyof T showNoneProperty?: boolean customSx?: { root?: SxProps } - disabled?: boolean } const AppButtonMenu = >({ @@ -52,24 +51,13 @@ const AppButtonMenu = >({ const { t } = useTranslation() const [inputValue, setInputValue] = useState('') const [selectedNames, setSelectedNames] = useState([]) - const [, setIsNoneSelectedNames] = useState(false) const { anchorEl, openMenu, renderMenu } = useMenu() const onMenuItemClick = (item: string, id: string) => { - if (item === 'No category') { - if (selectedNames.includes(item)) { - setIsNoneSelectedNames(false) - setSelectedItems([]) - } else { - setIsNoneSelectedNames(true) - setSelectedItems([id]) - } + if (selectedNames.includes(item)) { + setSelectedItems(selectedItems.filter((selected) => selected !== id)) } else { - if (selectedNames.includes(item)) { - setSelectedItems(selectedItems.filter((selected) => selected !== id)) - } else { - setSelectedItems([...selectedItems, id]) - } + setSelectedItems([...selectedItems, id]) } } @@ -99,12 +87,9 @@ const AppButtonMenu = >({ const menuItems = filteredItems.map((item) => { const field = String(valueField ? (item as T)[valueField] : item) const id = item._id - const isDisabled = - selectedNames.includes('No category') && field !== 'No category' return ( onMenuItemClick(field, id)} > diff --git a/src/components/app-select-button/AppSelectButton.tsx b/src/components/app-select-button/AppSelectButton.tsx index 34821a0f8..299ec8c3a 100644 --- a/src/components/app-select-button/AppSelectButton.tsx +++ b/src/components/app-select-button/AppSelectButton.tsx @@ -6,19 +6,17 @@ import { styles } from '~/components/app-select-button/AppSelectButton.styles' interface AppSelectButtonProps { onMenuItemClick: () => void checked: boolean - disabled?: boolean children: ReactNode } const AppSelectButton: FC = ({ checked, children, - onMenuItemClick, - disabled = false + onMenuItemClick }) => { return ( - - + + {children} ) From d8901917b3ca6ee1c8b60daa71447681c9ddec26 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Sun, 28 Jul 2024 13:52:32 +0300 Subject: [PATCH 04/37] rewrite LoginForm.jsx to tsx --- .../{LoginForm.jsx => LoginForm.tsx} | 54 ++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) rename src/containers/guest-home-page/login-form/{LoginForm.jsx => LoginForm.tsx} (66%) diff --git a/src/containers/guest-home-page/login-form/LoginForm.jsx b/src/containers/guest-home-page/login-form/LoginForm.tsx similarity index 66% rename from src/containers/guest-home-page/login-form/LoginForm.jsx rename to src/containers/guest-home-page/login-form/LoginForm.tsx index d02aea5a8..164e84e4d 100644 --- a/src/containers/guest-home-page/login-form/LoginForm.jsx +++ b/src/containers/guest-home-page/login-form/LoginForm.tsx @@ -15,7 +15,30 @@ import AppButton from '~/components/app-button/AppButton' import { styles } from '~/containers/guest-home-page/login-form/LoginForm.styles' -const LoginForm = ({ +interface LoginFormProps { + handleSubmit: (e: React.FormEvent) => void + handleChange: ( + name: string + ) => (e: React.ChangeEvent) => void + handleBlur: (name: string) => (e: React.FocusEvent) => void + data: { + email: string + password: string + rememberMe: boolean + } + errors: { + email?: string + password?: string + } +} + +interface AppState { + appMain: { + authLoading: boolean + } +} + +const LoginForm: React.FC = ({ handleSubmit, handleChange, handleBlur, @@ -23,9 +46,9 @@ const LoginForm = ({ errors }) => { const { inputVisibility: passwordVisibility, showInputText: showPassword } = - useInputVisibility(errors.password) + useInputVisibility(errors.password ?? '') - const { authLoading } = useSelector((state) => state.appMain) + const { authLoading } = useSelector((state: AppState) => state.appMain) const { openModal } = useModalContext() @@ -35,18 +58,33 @@ const LoginForm = ({ openModal({ component: }) } + const handleCheckboxChange = ( + event: React.SyntheticEvent, + checked: boolean + ) => { + const target = event.target as HTMLInputElement + const changeEvent = { + ...event, + target: { + ...target, + value: checked.toString() + } + } as React.ChangeEvent + handleChange(target.name)(changeEvent) + } + return ( } + control={} label={t('login.rememberMe')} labelPlacement='end' - onChange={handleChange('rememberMe')} + onChange={handleCheckboxChange} sx={styles.checkboxLabel} value={data.rememberMe} /> From 423d653cf0cfce998f41419102bfc5010290d889 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Sun, 28 Jul 2024 16:28:20 +0300 Subject: [PATCH 05/37] replace UseSelector by UseAppSelector --- .../guest-home-page/login-form/LoginForm.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/containers/guest-home-page/login-form/LoginForm.tsx b/src/containers/guest-home-page/login-form/LoginForm.tsx index 164e84e4d..c949dfb55 100644 --- a/src/containers/guest-home-page/login-form/LoginForm.tsx +++ b/src/containers/guest-home-page/login-form/LoginForm.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next' import useInputVisibility from '~/hooks/use-input-visibility' -import { useSelector } from 'react-redux' +import { useAppSelector } from '~/hooks/use-redux' import Box from '@mui/material/Box' import ButtonBase from '@mui/material/ButtonBase' @@ -32,12 +32,6 @@ interface LoginFormProps { } } -interface AppState { - appMain: { - authLoading: boolean - } -} - const LoginForm: React.FC = ({ handleSubmit, handleChange, @@ -48,7 +42,7 @@ const LoginForm: React.FC = ({ const { inputVisibility: passwordVisibility, showInputText: showPassword } = useInputVisibility(errors.password ?? '') - const { authLoading } = useSelector((state: AppState) => state.appMain) + const { authLoading } = useAppSelector((state) => state.appMain) const { openModal } = useModalContext() From d36821dee1f9fe9827f1dab329029d58d8696520 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Mon, 29 Jul 2024 11:47:43 +0300 Subject: [PATCH 06/37] rewrite GoogleLogin to tsx format --- ...{GoogleLogin.styles.js => GoogleLogin.styles.ts} | 2 +- .../{GoogleLogin.jsx => GoogleLogin.tsx} | 13 ++++++++++++- tsconfig.json | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) rename src/containers/guest-home-page/google-login/{GoogleLogin.styles.js => GoogleLogin.styles.ts} (95%) rename src/containers/guest-home-page/google-login/{GoogleLogin.jsx => GoogleLogin.tsx} (90%) diff --git a/src/containers/guest-home-page/google-login/GoogleLogin.styles.js b/src/containers/guest-home-page/google-login/GoogleLogin.styles.ts similarity index 95% rename from src/containers/guest-home-page/google-login/GoogleLogin.styles.js rename to src/containers/guest-home-page/google-login/GoogleLogin.styles.ts index ba0270263..a62e0fa0b 100644 --- a/src/containers/guest-home-page/google-login/GoogleLogin.styles.js +++ b/src/containers/guest-home-page/google-login/GoogleLogin.styles.ts @@ -34,6 +34,6 @@ export const styles = { googleForm: { display: 'flex', flexDirection: 'column', - minWidth: { sm: '330px' } + minWidth: '330px' } } diff --git a/src/containers/guest-home-page/google-login/GoogleLogin.jsx b/src/containers/guest-home-page/google-login/GoogleLogin.tsx similarity index 90% rename from src/containers/guest-home-page/google-login/GoogleLogin.jsx rename to src/containers/guest-home-page/google-login/GoogleLogin.tsx index b20117f82..1bb46ba66 100644 --- a/src/containers/guest-home-page/google-login/GoogleLogin.jsx +++ b/src/containers/guest-home-page/google-login/GoogleLogin.tsx @@ -8,8 +8,19 @@ import LoginDialog from '~/containers/guest-home-page/login-dialog/LoginDialog' import GoogleButton from '~/containers/guest-home-page/google-button/GoogleButton' import { styles } from '~/containers/guest-home-page/google-login/GoogleLogin.styles' +import { UserRole } from '~/types' -const GoogleLogin = ({ type, buttonWidth, role }) => { +interface GoogleLoginProps { + type: 'signup' | 'login' + buttonWidth: string + role: UserRole +} + +const GoogleLogin: React.FC = ({ + type, + buttonWidth, + role +}) => { const { t, i18n } = useTranslation() const { whatCanYouDo } = guestRoutes.navBar const { openModal, closeModal } = useModalContext() diff --git a/tsconfig.json b/tsconfig.json index 694261e69..6da05dc05 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,3 +1,4 @@ + { "compilerOptions": { "module": "Esnext", From f199784ff49cbc8474c2b931a8a368b35acef864 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Mon, 29 Jul 2024 12:21:03 +0300 Subject: [PATCH 07/37] rewrite OfferDetails to tsx format --- .../{OfferDetails.jsx => OfferDetails.tsx} | 12 +++++++++++- tsconfig.json | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) rename src/components/offer-card/offer-details/{OfferDetails.jsx => OfferDetails.tsx} (76%) diff --git a/src/components/offer-card/offer-details/OfferDetails.jsx b/src/components/offer-card/offer-details/OfferDetails.tsx similarity index 76% rename from src/components/offer-card/offer-details/OfferDetails.jsx rename to src/components/offer-card/offer-details/OfferDetails.tsx index 6870baabe..48502290b 100644 --- a/src/components/offer-card/offer-details/OfferDetails.jsx +++ b/src/components/offer-card/offer-details/OfferDetails.tsx @@ -5,8 +5,18 @@ import LanguagesListWithIcon from '~/components/languages-list-with-icon/Languag import SubjectLevelChips from '~/components/subject-level-chips/SubjectLevelChips' import { styles } from '~/components/offer-card/offer-details/OfferDetails.styles' +import { LanguagesEnum, ProficiencyLevelEnum } from '~/types' -const OfferDetails = ({ +interface OfferDetailsProps { + subject: string + chipsColor: string + level: ProficiencyLevelEnum + title: string + description: string + languages: LanguagesEnum +} + +const OfferDetails: React.FC = ({ subject, chipsColor, level, diff --git a/tsconfig.json b/tsconfig.json index 694261e69..6da05dc05 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,3 +1,4 @@ + { "compilerOptions": { "module": "Esnext", From 7a5958ee762fb1ca43e76b8a53ba64f8a4a6958e Mon Sep 17 00:00:00 2001 From: Bohdan Date: Sun, 28 Jul 2024 13:52:32 +0300 Subject: [PATCH 08/37] rewrite LoginForm.jsx to tsx --- .../{LoginForm.jsx => LoginForm.tsx} | 54 ++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) rename src/containers/guest-home-page/login-form/{LoginForm.jsx => LoginForm.tsx} (66%) diff --git a/src/containers/guest-home-page/login-form/LoginForm.jsx b/src/containers/guest-home-page/login-form/LoginForm.tsx similarity index 66% rename from src/containers/guest-home-page/login-form/LoginForm.jsx rename to src/containers/guest-home-page/login-form/LoginForm.tsx index d02aea5a8..164e84e4d 100644 --- a/src/containers/guest-home-page/login-form/LoginForm.jsx +++ b/src/containers/guest-home-page/login-form/LoginForm.tsx @@ -15,7 +15,30 @@ import AppButton from '~/components/app-button/AppButton' import { styles } from '~/containers/guest-home-page/login-form/LoginForm.styles' -const LoginForm = ({ +interface LoginFormProps { + handleSubmit: (e: React.FormEvent) => void + handleChange: ( + name: string + ) => (e: React.ChangeEvent) => void + handleBlur: (name: string) => (e: React.FocusEvent) => void + data: { + email: string + password: string + rememberMe: boolean + } + errors: { + email?: string + password?: string + } +} + +interface AppState { + appMain: { + authLoading: boolean + } +} + +const LoginForm: React.FC = ({ handleSubmit, handleChange, handleBlur, @@ -23,9 +46,9 @@ const LoginForm = ({ errors }) => { const { inputVisibility: passwordVisibility, showInputText: showPassword } = - useInputVisibility(errors.password) + useInputVisibility(errors.password ?? '') - const { authLoading } = useSelector((state) => state.appMain) + const { authLoading } = useSelector((state: AppState) => state.appMain) const { openModal } = useModalContext() @@ -35,18 +58,33 @@ const LoginForm = ({ openModal({ component: }) } + const handleCheckboxChange = ( + event: React.SyntheticEvent, + checked: boolean + ) => { + const target = event.target as HTMLInputElement + const changeEvent = { + ...event, + target: { + ...target, + value: checked.toString() + } + } as React.ChangeEvent + handleChange(target.name)(changeEvent) + } + return ( } + control={} label={t('login.rememberMe')} labelPlacement='end' - onChange={handleChange('rememberMe')} + onChange={handleCheckboxChange} sx={styles.checkboxLabel} value={data.rememberMe} /> From f9a78a8353c07be84e1b87cae895f01c54bef3a5 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Sun, 28 Jul 2024 16:28:20 +0300 Subject: [PATCH 09/37] replace UseSelector by UseAppSelector --- .../guest-home-page/login-form/LoginForm.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/containers/guest-home-page/login-form/LoginForm.tsx b/src/containers/guest-home-page/login-form/LoginForm.tsx index 164e84e4d..c949dfb55 100644 --- a/src/containers/guest-home-page/login-form/LoginForm.tsx +++ b/src/containers/guest-home-page/login-form/LoginForm.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next' import useInputVisibility from '~/hooks/use-input-visibility' -import { useSelector } from 'react-redux' +import { useAppSelector } from '~/hooks/use-redux' import Box from '@mui/material/Box' import ButtonBase from '@mui/material/ButtonBase' @@ -32,12 +32,6 @@ interface LoginFormProps { } } -interface AppState { - appMain: { - authLoading: boolean - } -} - const LoginForm: React.FC = ({ handleSubmit, handleChange, @@ -48,7 +42,7 @@ const LoginForm: React.FC = ({ const { inputVisibility: passwordVisibility, showInputText: showPassword } = useInputVisibility(errors.password ?? '') - const { authLoading } = useSelector((state: AppState) => state.appMain) + const { authLoading } = useAppSelector((state) => state.appMain) const { openModal } = useModalContext() From 0400bb37b05c9bfcaee1f5ec9f49d795352500ef Mon Sep 17 00:00:00 2001 From: Bohdan Date: Mon, 29 Jul 2024 13:01:55 +0300 Subject: [PATCH 10/37] remove unnessesary handleCheckboxChange function --- .../guest-home-page/login-form/LoginForm.tsx | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/containers/guest-home-page/login-form/LoginForm.tsx b/src/containers/guest-home-page/login-form/LoginForm.tsx index c949dfb55..79fcb2d7d 100644 --- a/src/containers/guest-home-page/login-form/LoginForm.tsx +++ b/src/containers/guest-home-page/login-form/LoginForm.tsx @@ -19,7 +19,11 @@ interface LoginFormProps { handleSubmit: (e: React.FormEvent) => void handleChange: ( name: string - ) => (e: React.ChangeEvent) => void + ) => ( + e: + | React.ChangeEvent + | { target: { name: string; value: string | boolean } } + ) => void handleBlur: (name: string) => (e: React.FocusEvent) => void data: { email: string @@ -52,21 +56,6 @@ const LoginForm: React.FC = ({ openModal({ component: }) } - const handleCheckboxChange = ( - event: React.SyntheticEvent, - checked: boolean - ) => { - const target = event.target as HTMLInputElement - const changeEvent = { - ...event, - target: { - ...target, - value: checked.toString() - } - } as React.ChangeEvent - handleChange(target.name)(changeEvent) - } - return ( = ({ } + control={ + + handleChange('rememberMe')({ + ...e, + target: { ...e.target, value: e.target.checked } + }) + } + /> + } label={t('login.rememberMe')} labelPlacement='end' - onChange={handleCheckboxChange} sx={styles.checkboxLabel} - value={data.rememberMe} /> Date: Mon, 29 Jul 2024 13:06:36 +0300 Subject: [PATCH 11/37] remove unnessesary space --- tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 6da05dc05..694261e69 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,3 @@ - { "compilerOptions": { "module": "Esnext", From 4b09c96052bfa876c0f5c3e058b55b925391c4f2 Mon Sep 17 00:00:00 2001 From: Pasichnichenkoilya Date: Thu, 25 Jul 2024 10:44:50 +0300 Subject: [PATCH 12/37] Changed image url concatenations to use createUrlPath helper --- src/components/message/Message.tsx | 4 +++- src/components/user-profile-info/UserProfileInfo.tsx | 2 +- src/containers/about-chat-sidebar/AboutChatSidebar.tsx | 3 ++- src/containers/chat/chat-item/ChatItem.tsx | 7 +++++-- .../profile-tab/profile-tab-form/ProfileTabForm.tsx | 3 ++- .../my-cooperations-details/MyCooperationsDetails.tsx | 5 ++++- src/containers/navigation-icons/AccountIcon.tsx | 3 ++- .../user-profile/profile-info/ProfileContainerDesktop.jsx | 3 ++- .../user-profile/profile-info/ProfileContainerMobile.jsx | 6 +++++- 9 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/components/message/Message.tsx b/src/components/message/Message.tsx index 2b5570a31..b1bad0d9e 100644 --- a/src/components/message/Message.tsx +++ b/src/components/message/Message.tsx @@ -76,7 +76,9 @@ const Message: FC = ({ const avatar = !isMyMessage && isAvatarVisible && ( diff --git a/src/components/user-profile-info/UserProfileInfo.tsx b/src/components/user-profile-info/UserProfileInfo.tsx index 1b05b2d9f..d4777b3f7 100644 --- a/src/components/user-profile-info/UserProfileInfo.tsx +++ b/src/components/user-profile-info/UserProfileInfo.tsx @@ -67,7 +67,7 @@ const UserProfileInfo: FC = ({ const avatar = ( ) diff --git a/src/containers/about-chat-sidebar/AboutChatSidebar.tsx b/src/containers/about-chat-sidebar/AboutChatSidebar.tsx index 929a8c26e..decf4b0d2 100644 --- a/src/containers/about-chat-sidebar/AboutChatSidebar.tsx +++ b/src/containers/about-chat-sidebar/AboutChatSidebar.tsx @@ -91,7 +91,8 @@ const AboutChatSidebar: FC = ({ diff --git a/src/containers/chat/chat-item/ChatItem.tsx b/src/containers/chat/chat-item/ChatItem.tsx index bf26b6104..5a2976511 100644 --- a/src/containers/chat/chat-item/ChatItem.tsx +++ b/src/containers/chat/chat-item/ChatItem.tsx @@ -15,7 +15,7 @@ import { OverlapEnum, PositionEnum } from '~/types' -import { getFormattedDate } from '~/utils/helper-functions' +import { createUrlPath, getFormattedDate } from '~/utils/helper-functions' interface ItemOfChatProps { isActiveChat: boolean @@ -84,7 +84,10 @@ const ChatItem: FC = ({ overlap={OverlapEnum.Circular} > diff --git a/src/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.tsx b/src/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.tsx index 402950e54..d4b809fac 100644 --- a/src/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.tsx +++ b/src/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.tsx @@ -32,6 +32,7 @@ import { snackbarVariants } from '~/constants' import { imageResize } from '~/utils/image-resize' import { styles } from '~/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.styles' import { openAlert } from '~/redux/features/snackbarSlice' +import { createUrlPath } from '~/utils/helper-functions' export interface ProfileTabFormProps { data: EditProfileForm @@ -106,7 +107,7 @@ const ProfileTabForm: FC = ({ const { photo } = data const photoToDisplay = typeof photo === 'string' - ? photo && `${import.meta.env.VITE_APP_IMG_USER_URL}${photo}` + ? photo && createUrlPath(import.meta.env.VITE_APP_IMG_USER_URL, photo) : photo?.src return ( diff --git a/src/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.tsx b/src/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.tsx index 9c17c2857..0e2b2dc86 100644 --- a/src/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.tsx +++ b/src/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.tsx @@ -104,7 +104,10 @@ const MyCooperationsDetails = () => { diff --git a/src/containers/navigation-icons/AccountIcon.tsx b/src/containers/navigation-icons/AccountIcon.tsx index 178265c39..26a38a184 100644 --- a/src/containers/navigation-icons/AccountIcon.tsx +++ b/src/containers/navigation-icons/AccountIcon.tsx @@ -13,6 +13,7 @@ import { defaultResponses } from '~/constants' import { styles } from '~/containers/navigation-icons/NavigationIcons.styles' import { UserResponse, UserRole } from '~/types' +import { createUrlPath } from '~/utils/helper-functions' interface AccountIconProps { openMenu: (event: MouseEvent) => void @@ -45,7 +46,7 @@ const AccountIcon: FC = ({ openMenu }) => { {!loading && firstName && lastName && `${firstName[0]}${lastName[0]}`} diff --git a/src/containers/user-profile/profile-info/ProfileContainerDesktop.jsx b/src/containers/user-profile/profile-info/ProfileContainerDesktop.jsx index 4c336b4c0..3754f13d9 100644 --- a/src/containers/user-profile/profile-info/ProfileContainerDesktop.jsx +++ b/src/containers/user-profile/profile-info/ProfileContainerDesktop.jsx @@ -8,6 +8,7 @@ import AppChipList from '~/components/app-chips-list/AppChipList' import ProfileDoneItemsList from '~/components/icon-with-text-list/ProfileDoneItemsList' import { styles } from '~/containers/user-profile/profile-info/ProfileInfo.styles' +import { createUrlPath } from '~/utils/helper-functions' const ProfileContainerDesktop = ({ userData, @@ -24,7 +25,7 @@ const ProfileContainerDesktop = ({ diff --git a/src/containers/user-profile/profile-info/ProfileContainerMobile.jsx b/src/containers/user-profile/profile-info/ProfileContainerMobile.jsx index 7870fb68d..ae02173e9 100644 --- a/src/containers/user-profile/profile-info/ProfileContainerMobile.jsx +++ b/src/containers/user-profile/profile-info/ProfileContainerMobile.jsx @@ -8,6 +8,7 @@ import AppChipList from '~/components/app-chips-list/AppChipList' import ProfileDoneItemsList from '~/components/icon-with-text-list/ProfileDoneItemsList' import { styles } from '~/containers/user-profile/profile-info/ProfileInfo.styles' +import { createUrlPath } from '~/utils/helper-functions' const ProfileContainerMobile = ({ actionIcon, @@ -25,7 +26,10 @@ const ProfileContainerMobile = ({ From a49075bcd043fc9f8a9849361b00b22bff393ade Mon Sep 17 00:00:00 2001 From: Pasichnichenkoilya Date: Thu, 25 Jul 2024 12:34:42 +0300 Subject: [PATCH 13/37] Update AccountIcon.tsx --- src/containers/navigation-icons/AccountIcon.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/containers/navigation-icons/AccountIcon.tsx b/src/containers/navigation-icons/AccountIcon.tsx index 26a38a184..dba1aad21 100644 --- a/src/containers/navigation-icons/AccountIcon.tsx +++ b/src/containers/navigation-icons/AccountIcon.tsx @@ -46,7 +46,10 @@ const AccountIcon: FC = ({ openMenu }) => { {!loading && firstName && lastName && `${firstName[0]}${lastName[0]}`} From c28b02aaddd8243f5976bd9f0a007f3f5edafc87 Mon Sep 17 00:00:00 2001 From: Pasichnichenkoilya Date: Thu, 25 Jul 2024 12:46:31 +0300 Subject: [PATCH 14/37] Update createUrlPath in helper-functions.tsx --- src/utils/helper-functions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/helper-functions.tsx b/src/utils/helper-functions.tsx index cd4c6a238..5ceb78269 100644 --- a/src/utils/helper-functions.tsx +++ b/src/utils/helper-functions.tsx @@ -206,7 +206,7 @@ export const createUrlPath = ( query = {} ) => { let trimmedUrl = URL - while (trimmedUrl.endsWith('/')) { + while (trimmedUrl && trimmedUrl.endsWith('/')) { trimmedUrl = trimmedUrl.slice(0, -1) } From bd492846abdf7639c891361f73b554a93d2962ee Mon Sep 17 00:00:00 2001 From: Bohdan Date: Mon, 29 Jul 2024 16:03:33 +0300 Subject: [PATCH 15/37] code fix --- .../guest-home-page/login-form/LoginForm.tsx | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/src/containers/guest-home-page/login-form/LoginForm.tsx b/src/containers/guest-home-page/login-form/LoginForm.tsx index 79fcb2d7d..09f4f89c1 100644 --- a/src/containers/guest-home-page/login-form/LoginForm.tsx +++ b/src/containers/guest-home-page/login-form/LoginForm.tsx @@ -1,6 +1,5 @@ import { useTranslation } from 'react-i18next' import useInputVisibility from '~/hooks/use-input-visibility' -import { useAppSelector } from '~/hooks/use-redux' import Box from '@mui/material/Box' import ButtonBase from '@mui/material/ButtonBase' @@ -14,17 +13,16 @@ import AppTextField from '~/components/app-text-field/AppTextField' import AppButton from '~/components/app-button/AppButton' import { styles } from '~/containers/guest-home-page/login-form/LoginForm.styles' +import { useAppSelector } from '~/hooks/use-redux' interface LoginFormProps { - handleSubmit: (e: React.FormEvent) => void + handleSubmit: (event: React.FormEvent) => void handleChange: ( - name: string - ) => ( - e: - | React.ChangeEvent - | { target: { name: string; value: string | boolean } } - ) => void - handleBlur: (name: string) => (e: React.FocusEvent) => void + field: string + ) => (event: React.SyntheticEvent) => void + handleBlur: ( + field: string + ) => (event: React.FocusEvent) => void data: { email: string password: string @@ -87,21 +85,16 @@ const LoginForm: React.FC = ({ - handleChange('rememberMe')({ - ...e, - target: { ...e.target, value: e.target.checked } - }) - } - /> - } + control={} label={t('login.rememberMe')} labelPlacement='end' + onChange={(event) => + handleChange('rememberMe')( + event as React.ChangeEvent + ) + } sx={styles.checkboxLabel} + value={data.rememberMe} /> Date: Mon, 29 Jul 2024 16:09:16 +0300 Subject: [PATCH 16/37] remove unnessesary space --- tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 6da05dc05..694261e69 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,3 @@ - { "compilerOptions": { "module": "Esnext", From 757a5aa05ba1f8f9eaf61257cdf3f27d098c8da3 Mon Sep 17 00:00:00 2001 From: Pasichnichenkoilya Date: Mon, 29 Jul 2024 16:10:55 +0300 Subject: [PATCH 17/37] Fix sonar: refactor to use an optional chain expression --- src/utils/helper-functions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/helper-functions.tsx b/src/utils/helper-functions.tsx index 5ceb78269..90dd12253 100644 --- a/src/utils/helper-functions.tsx +++ b/src/utils/helper-functions.tsx @@ -206,7 +206,7 @@ export const createUrlPath = ( query = {} ) => { let trimmedUrl = URL - while (trimmedUrl && trimmedUrl.endsWith('/')) { + while (trimmedUrl?.endsWith('/')) { trimmedUrl = trimmedUrl.slice(0, -1) } From abcc8a14cf178eeab9b22013651c30452fc4f01c Mon Sep 17 00:00:00 2001 From: PavloDolia <123400767+PavloDolia@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:08:31 +0300 Subject: [PATCH 18/37] Rewrote ProfileItem to tsx format (#2226) --- ...ileItem.styles.js => ProfileItem.styles.ts} | 0 .../{ProfileItem.jsx => ProfileItem.tsx} | 18 +++++++++--------- 2 files changed, 9 insertions(+), 9 deletions(-) rename src/components/profile-item/{ProfileItem.styles.js => ProfileItem.styles.ts} (100%) rename src/components/profile-item/{ProfileItem.jsx => ProfileItem.tsx} (73%) diff --git a/src/components/profile-item/ProfileItem.styles.js b/src/components/profile-item/ProfileItem.styles.ts similarity index 100% rename from src/components/profile-item/ProfileItem.styles.js rename to src/components/profile-item/ProfileItem.styles.ts diff --git a/src/components/profile-item/ProfileItem.jsx b/src/components/profile-item/ProfileItem.tsx similarity index 73% rename from src/components/profile-item/ProfileItem.jsx rename to src/components/profile-item/ProfileItem.tsx index a05c5da3a..39e7cb15f 100644 --- a/src/components/profile-item/ProfileItem.jsx +++ b/src/components/profile-item/ProfileItem.tsx @@ -5,8 +5,14 @@ import { useTranslation } from 'react-i18next' import { styles } from '~/components/profile-item/ProfileItem.styles' import useBreakpoints from '~/hooks/use-breakpoints' +import { ProfileItemType } from '~/components/profile-item/complete-profile.constants' -const ProfileItem = ({ item, isFilled = false }) => { +interface ProfileItemProps { + item: ProfileItemType + isFilled?: boolean +} + +const ProfileItem = ({ item, isFilled = false }: ProfileItemProps) => { const { t } = useTranslation() const { isMobile } = useBreakpoints() const { id, icon } = item @@ -17,16 +23,10 @@ const ProfileItem = ({ item, isFilled = false }) => { {!isMobile && {icon}} - + {t(`completeProfile.${id}.title`)} - + {t(`completeProfile.${id}.subtitle`)} From de8efa582f4e2c17ed25eb9d1eb790f6fdde2e99 Mon Sep 17 00:00:00 2001 From: PavloDolia <123400767+PavloDolia@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:34:49 +0300 Subject: [PATCH 19/37] Created tests for count-active-filters.tsx (#2191) --- tests/unit/utils/count-active-filters.spec.js | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/unit/utils/count-active-filters.spec.js diff --git a/tests/unit/utils/count-active-filters.spec.js b/tests/unit/utils/count-active-filters.spec.js new file mode 100644 index 000000000..bd3dff071 --- /dev/null +++ b/tests/unit/utils/count-active-filters.spec.js @@ -0,0 +1,99 @@ +import { + countActiveOfferFilters, + countActiveCourseFilters +} from '~/utils/count-active-filters' + +const mockedDefaultFilters = { + category: '', + subject: '', + title: '', + page: 1, + search: 'search', + price: [1, 2] +} + +const activeOfferIgnoredFields = { + sort: '', + authorRole: '', + categoryId: '', + subjectId: '', + page: '' +} + +const activeCourseIgnoredFields = { + sort: '', + page: '' +} + +describe('countActiveOfferFilters', () => { + it("Should return 0 if there aren't search params", () => { + const mockedSearchParams = new URLSearchParams({}) + const count = countActiveCourseFilters( + mockedSearchParams, + mockedDefaultFilters + ) + expect(count).toBe(0) + }) + + it('Should not count ignored fields', () => { + const mockedSearchParams = new URLSearchParams({ + ...activeOfferIgnoredFields, + title: 'title counts' + }) + const count = countActiveOfferFilters( + mockedSearchParams, + mockedDefaultFilters + ) + expect(count).toBe(1) + }) + + it('Should not count default price', () => { + const mockedSearchParams = new URLSearchParams({ + price: [1, 2], + title: 'title counts' + }) + const count = countActiveOfferFilters( + mockedSearchParams, + mockedDefaultFilters + ) + expect(count).toBe(1) + }) + + it('Should count search params that are not equal to default filter params', () => { + const mockedSearchParams = new URLSearchParams({ + title: 'title counts', + page: 1, + search: 'search' + }) + const count = countActiveOfferFilters( + mockedSearchParams, + mockedDefaultFilters + ) + expect(count).toBe(1) + }) +}) + +describe('countActiveCourseFilters', () => { + it('Should not count ignored fields', () => { + const mockedSearchParams = new URLSearchParams(activeCourseIgnoredFields) + const count = countActiveOfferFilters( + mockedSearchParams, + mockedDefaultFilters + ) + expect(count).toBe(0) + }) + + it('Should return correct count of filters', () => { + const mockedSearchParams = new URLSearchParams({ + foo: 'bar', + title: 'title counts', + subject: 'subject counts' + }) + + const count = countActiveCourseFilters( + mockedSearchParams, + mockedDefaultFilters + ) + expect(count).toBe(2) + }) +}) From 90688db3a1fb581f139292fcfb86395c82d0a71c Mon Sep 17 00:00:00 2001 From: Anastasiia Matiushenko <102433497+docia@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:36:18 +0300 Subject: [PATCH 20/37] Rewrote DateFilter to tsx format and fixed sonar issues (#2240) * rewrote DateFilter to tsx * extended DesktopDatePickerProps --- .../date-filter/DateFilter.constants.ts | 35 +++++++++++++++++ ...eFilter.styles.js => DateFilter.styles.ts} | 0 .../{DateFilter.jsx => DateFilter.tsx} | 38 +++++++++++++++---- .../enhanced-table/date-filter/constants.js | 15 -------- src/types/mui/customTypes.index.ts | 1 + src/types/mui/customTypes/customTypes.d.ts | 7 ++++ .../date-filter/DateFilter.spec.jsx | 12 ++---- 7 files changed, 77 insertions(+), 31 deletions(-) create mode 100644 src/components/enhanced-table/date-filter/DateFilter.constants.ts rename src/components/enhanced-table/date-filter/{DateFilter.styles.js => DateFilter.styles.ts} (100%) rename src/components/enhanced-table/date-filter/{DateFilter.jsx => DateFilter.tsx} (74%) delete mode 100644 src/components/enhanced-table/date-filter/constants.js create mode 100644 src/types/mui/customTypes.index.ts create mode 100644 src/types/mui/customTypes/customTypes.d.ts diff --git a/src/components/enhanced-table/date-filter/DateFilter.constants.ts b/src/components/enhanced-table/date-filter/DateFilter.constants.ts new file mode 100644 index 000000000..6cc5574a0 --- /dev/null +++ b/src/components/enhanced-table/date-filter/DateFilter.constants.ts @@ -0,0 +1,35 @@ +export type Placement = + | 'auto-end' + | 'auto-start' + | 'auto' + | 'bottom-end' + | 'bottom-start' + | 'bottom' + | 'left-end' + | 'left-start' + | 'left' + | 'right-end' + | 'right-start' + | 'right' + | 'top-end' + | 'top-start' + | 'top' + +export const datePickersOptions: { + placement: Placement + direction: 'from' | 'to' +}[] = [ + { + placement: 'bottom-end', + direction: 'from' + }, + { + placement: 'bottom-start', + direction: 'to' + } +] + +export const initialState: { from: boolean; to: boolean } = { + from: false, + to: false +} diff --git a/src/components/enhanced-table/date-filter/DateFilter.styles.js b/src/components/enhanced-table/date-filter/DateFilter.styles.ts similarity index 100% rename from src/components/enhanced-table/date-filter/DateFilter.styles.js rename to src/components/enhanced-table/date-filter/DateFilter.styles.ts diff --git a/src/components/enhanced-table/date-filter/DateFilter.jsx b/src/components/enhanced-table/date-filter/DateFilter.tsx similarity index 74% rename from src/components/enhanced-table/date-filter/DateFilter.jsx rename to src/components/enhanced-table/date-filter/DateFilter.tsx index 5ef534455..913caf890 100644 --- a/src/components/enhanced-table/date-filter/DateFilter.jsx +++ b/src/components/enhanced-table/date-filter/DateFilter.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import React, { useState } from 'react' import { format } from 'date-fns' import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider' import { DesktopDatePicker } from '@mui/x-date-pickers/DesktopDatePicker' @@ -9,21 +9,43 @@ import IconButton from '@mui/material/IconButton' import ClearIcon from '@mui/icons-material/Clear' import CalendarMonthIcon from '@mui/icons-material/CalendarMonth' -import { initialState, datePickersOptions } from './constants' +import { initialState, datePickersOptions } from './DateFilter.constants' import { styles } from './DateFilter.styles' -const DateFilter = ({ filter, setFilter, clearFilter }) => { - const [open, setOpen] = useState(initialState) +interface Filter { + from: string | null + to: string | null +} + +interface OpenState { + from: boolean + to: boolean +} + +export interface DateFilterProps { + filter: Filter + setFilter: (filter: Filter) => void + clearFilter: () => void +} + +const DateFilter: React.FC = ({ + filter, + setFilter, + clearFilter +}) => { + const [open, setOpen] = useState(initialState) - const handleChange = (direction) => (date) => { - setFilter({ ...filter, [direction]: format(date, 'yyyy-MM-dd') }) + const handleChange = (direction: string) => (date: Date | null) => { + if (date) { + setFilter({ ...filter, [direction]: format(date, 'yyyy-MM-dd') }) + } } - const handleClose = (direction) => { + const handleClose = (direction: string) => { setOpen((prev) => ({ ...prev, [direction]: false })) } - const handleOpen = (direction) => { + const handleOpen = (direction: string) => { setOpen((prev) => ({ ...prev, [direction]: true })) } diff --git a/src/components/enhanced-table/date-filter/constants.js b/src/components/enhanced-table/date-filter/constants.js deleted file mode 100644 index 9109b6d79..000000000 --- a/src/components/enhanced-table/date-filter/constants.js +++ /dev/null @@ -1,15 +0,0 @@ -export const datePickersOptions = [ - { - placement: 'bottom-end', - direction: 'from' - }, - { - placement: 'bottom-start', - direction: 'to' - } -] - -export const initialState = { - from: false, - to: false -} diff --git a/src/types/mui/customTypes.index.ts b/src/types/mui/customTypes.index.ts new file mode 100644 index 000000000..99f4c6045 --- /dev/null +++ b/src/types/mui/customTypes.index.ts @@ -0,0 +1 @@ +export * from '~/types/mui/customTypes/customTypes' diff --git a/src/types/mui/customTypes/customTypes.d.ts b/src/types/mui/customTypes/customTypes.d.ts new file mode 100644 index 000000000..467358a3c --- /dev/null +++ b/src/types/mui/customTypes/customTypes.d.ts @@ -0,0 +1,7 @@ +import { TextFieldProps } from '@mui/material/TextField' + +declare module '@mui/x-date-pickers/DesktopDatePicker' { + interface DesktopDatePickerProps { + inputProps?: TextFieldProps['inputProps'] + } +} diff --git a/tests/unit/components/date-filter/DateFilter.spec.jsx b/tests/unit/components/date-filter/DateFilter.spec.jsx index 67eed0dc5..39621bfcd 100644 --- a/tests/unit/components/date-filter/DateFilter.spec.jsx +++ b/tests/unit/components/date-filter/DateFilter.spec.jsx @@ -6,21 +6,18 @@ import DateFilter from '~/components/enhanced-table/date-filter/DateFilter' const props = { filter: { from: '', to: '' }, setFilter: vi.fn(), - clearFilter: vi.fn(), + clearFilter: vi.fn() } const dateMock = new Date('2023-01-01') describe('DateFilter test', () => { beforeEach(() => { - render( - - - ) + render() }) it('Should open, and change value in calendar', () => { - const calendarIcon = screen.getByTestId('calendar-icon') + const calendarIcon = screen.getByTestId('calendar-icon') fireEvent.click(calendarIcon) @@ -38,12 +35,11 @@ describe('DateFilter test', () => { }) it('Should clear value in calendar', async () => { - const dateToInput = screen.getByLabelText('date-filter-to') fireEvent.change(dateToInput, { target: { value: dateMock } }) - const clearIcon = screen.getByTestId('clear-icon') + const clearIcon = screen.getByTestId('clear-icon') fireEvent.click(clearIcon) From 321a0653e852d080d48355efc3f06ef43d260874 Mon Sep 17 00:00:00 2001 From: PavloDolia <123400767+PavloDolia@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:37:53 +0300 Subject: [PATCH 21/37] Rewrote SubjectsStep to tsx format (#2233) * Rewrote SubjectsStep to tsx format * fix sonar issue * fix sonar issue 2 --- ...sStep.styles.js => SubjectsStep.styles.ts} | 0 .../{SubjectsStep.jsx => SubjectsStep.tsx} | 47 +++++++++++++------ .../{constants.js => constants.ts} | 0 .../common/interfaces/common.interfaces.ts | 2 +- 4 files changed, 33 insertions(+), 16 deletions(-) rename src/containers/tutor-home-page/subjects-step/{SubjectsStep.styles.js => SubjectsStep.styles.ts} (100%) rename src/containers/tutor-home-page/subjects-step/{SubjectsStep.jsx => SubjectsStep.tsx} (80%) rename src/containers/tutor-home-page/subjects-step/{constants.js => constants.ts} (100%) diff --git a/src/containers/tutor-home-page/subjects-step/SubjectsStep.styles.js b/src/containers/tutor-home-page/subjects-step/SubjectsStep.styles.ts similarity index 100% rename from src/containers/tutor-home-page/subjects-step/SubjectsStep.styles.js rename to src/containers/tutor-home-page/subjects-step/SubjectsStep.styles.ts diff --git a/src/containers/tutor-home-page/subjects-step/SubjectsStep.jsx b/src/containers/tutor-home-page/subjects-step/SubjectsStep.tsx similarity index 80% rename from src/containers/tutor-home-page/subjects-step/SubjectsStep.jsx rename to src/containers/tutor-home-page/subjects-step/SubjectsStep.tsx index 6c20cb24c..61dc0440c 100644 --- a/src/containers/tutor-home-page/subjects-step/SubjectsStep.jsx +++ b/src/containers/tutor-home-page/subjects-step/SubjectsStep.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback } from 'react' +import { useState, useCallback, SyntheticEvent } from 'react' import { useTranslation } from 'react-i18next' import Box from '@mui/material/Box' @@ -16,25 +16,35 @@ import img from '~/assets/img/tutor-home-page/become-tutor/study-category.svg' import { useStepContext } from '~/context/step-context' import { categoryService } from '~/services/category-service' import { subjectService } from '~/services/subject-service' +import { CategoryNameInterface, SubjectNameInterface } from '~/types' -const SubjectsStep = ({ btnsBox }) => { +interface SubjectsStepProps { + btnsBox: JSX.Element +} + +interface SubjectsType { + category: CategoryNameInterface | null + subject: SubjectNameInterface | null +} + +const SubjectsStep = ({ btnsBox }: SubjectsStepProps) => { const { t } = useTranslation() const { isLaptopAndAbove, isMobile } = useBreakpoints() const { stepData, handleSubjects } = useStepContext() const subjectData = stepData.subjects - const [subjects, setSubjects] = useState({ + const [subjects, setSubjects] = useState({ category: null, subject: null }) const [subjectError, setSubjectError] = useState('') - const [subjectFetched, setSubjectIsFetched] = useState(false) + const [subjectIsFetched, setSubjectIsFetched] = useState(false) const fetchSubjectHandler = () => setSubjectIsFetched(true) const getSubjectsNames = useCallback( - () => subjectService.getSubjectsNames(subjects.category._id), + () => subjectService.getSubjectsNames(subjects.category?._id ?? null), [subjects.category] ) @@ -44,18 +54,25 @@ const SubjectsStep = ({ btnsBox }) => { ) - const onChangeCategory = (_, value) => { - setSubjects( - (prev) => - prev.category?._id !== value?._id && { - category: value, - subject: null - } + const onChangeCategory = ( + _: SyntheticEvent, + value: CategoryNameInterface | null + ) => { + setSubjects((prev) => + prev.category?._id !== value?._id + ? { + category: value, + subject: null + } + : prev ) setSubjectIsFetched(false) } - const onChangeSubject = (_, value) => { + const onChangeSubject = ( + _: SyntheticEvent, + value: SubjectNameInterface | null + ) => { setSubjects((prev) => ({ category: prev.category, subject: value })) subjectError && setSubjectError('') } @@ -89,7 +106,7 @@ const SubjectsStep = ({ btnsBox }) => { }) } - const handleChipDelete = (item) => { + const handleChipDelete = (item: string) => { const newItems = subjectData.filter(({ name }) => name !== item) handleSubjects(newItems) } @@ -117,7 +134,7 @@ const SubjectsStep = ({ btnsBox }) => { } photo: string[] - subjects: SubjectInterface[] + subjects: Array language: UserResponse['nativeLanguage'] } From 80e18daab8a900b37b69a0f2addad74279930c15 Mon Sep 17 00:00:00 2001 From: PavloDolia <123400767+PavloDolia@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:40:46 +0300 Subject: [PATCH 22/37] Rewrote FindBlock to tsx format (#2234) * Rewrote FindBlock to tsx format * fixed tests --- .../find-block/{FindBlock.jsx => FindBlock.tsx} | 14 +++++++++----- .../{find-block.styles.js => find-block.styles.ts} | 0 ...dent-constants.js => find-student-constants.ts} | 0 ...-tutor-constants.js => find-tutor-constants.ts} | 0 .../unit/components/find-block/FindBlock.spec.jsx | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) rename src/components/find-block/{FindBlock.jsx => FindBlock.tsx} (84%) rename src/components/find-block/{find-block.styles.js => find-block.styles.ts} (100%) rename src/components/find-block/{find-student-constants.js => find-student-constants.ts} (100%) rename src/components/find-block/{find-tutor-constants.js => find-tutor-constants.ts} (100%) diff --git a/src/components/find-block/FindBlock.jsx b/src/components/find-block/FindBlock.tsx similarity index 84% rename from src/components/find-block/FindBlock.jsx rename to src/components/find-block/FindBlock.tsx index ec3151ca0..f419678a8 100644 --- a/src/components/find-block/FindBlock.jsx +++ b/src/components/find-block/FindBlock.tsx @@ -1,4 +1,4 @@ -import { useCallback, useState } from 'react' +import { ChangeEvent, KeyboardEvent, useCallback, useState } from 'react' import { useNavigate } from 'react-router' import { Link } from 'react-router-dom' import { useTranslation } from 'react-i18next' @@ -13,7 +13,11 @@ import bag from '~/assets/img/student-home/bag.png' import { authRoutes } from '~/router/constants/authRoutes' import { styles } from '~/components/find-block/find-block.styles' -const FindBlock = ({ translationKey }) => { +interface FindBlockProps { + translationKey: string +} + +const FindBlock = ({ translationKey }: FindBlockProps) => { const [inputValue, setInputValue] = useState('') const { t } = useTranslation() const navigate = useNavigate() @@ -21,12 +25,12 @@ const FindBlock = ({ translationKey }) => { const encodedInputValue = encodeURIComponent(inputValue) const findOffers = `${authRoutes.findOffers.path}?search=${encodedInputValue}` - const onChange = (e) => { + const onChange = (e: ChangeEvent) => { setInputValue(e.target.value) } const handleEnterPress = useCallback( - (e) => { + (e: KeyboardEvent) => { if (e.key === 'Enter' && inputValue) { navigate(findOffers) } @@ -45,7 +49,7 @@ const FindBlock = ({ translationKey }) => { fullWidth={isMobile} onChange={onChange} onClear={onClear} - onKeyPress={handleEnterPress} + onKeyDown={handleEnterPress} placeholder={t(`${translationKey}.label`)} startIcon={} sx={styles.input} diff --git a/src/components/find-block/find-block.styles.js b/src/components/find-block/find-block.styles.ts similarity index 100% rename from src/components/find-block/find-block.styles.js rename to src/components/find-block/find-block.styles.ts diff --git a/src/components/find-block/find-student-constants.js b/src/components/find-block/find-student-constants.ts similarity index 100% rename from src/components/find-block/find-student-constants.js rename to src/components/find-block/find-student-constants.ts diff --git a/src/components/find-block/find-tutor-constants.js b/src/components/find-block/find-tutor-constants.ts similarity index 100% rename from src/components/find-block/find-tutor-constants.js rename to src/components/find-block/find-tutor-constants.ts diff --git a/tests/unit/components/find-block/FindBlock.spec.jsx b/tests/unit/components/find-block/FindBlock.spec.jsx index 167c3645e..45cf7503b 100644 --- a/tests/unit/components/find-block/FindBlock.spec.jsx +++ b/tests/unit/components/find-block/FindBlock.spec.jsx @@ -42,7 +42,7 @@ describe('FindBlock test', () => { const input = screen.getByRole('textbox') fireEvent.change(input, { target: { value: 'test' } }) - fireEvent.keyPress(input, { key: 'Enter', code: 'Enter', charCode: 13 }) + fireEvent.keyDown(input, { key: 'Enter', code: 'Enter', charCode: 13 }) expect(mockNavigate).toHaveBeenCalled() }) From 0f90c1ce05a3f4bd735c91ace906da3d479a01b8 Mon Sep 17 00:00:00 2001 From: PavloDolia <123400767+PavloDolia@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:41:57 +0300 Subject: [PATCH 23/37] Rewrote ProfileDoneItemsList to tsx format (#2235) * Rewrote ProfileDoneItemsList to tsx format * changed imports --- ...style.js => ProfileDoneItemsList.style.ts} | 0 ...ItemsList.jsx => ProfileDoneItemsList.tsx} | 20 ++++++++++++++++--- src/types/components/components.index.ts | 1 + .../profileDoneItemsList.interfaces.ts | 4 ++++ 4 files changed, 22 insertions(+), 3 deletions(-) rename src/components/icon-with-text-list/{ProfileDoneItemsList.style.js => ProfileDoneItemsList.style.ts} (100%) rename src/components/icon-with-text-list/{ProfileDoneItemsList.jsx => ProfileDoneItemsList.tsx} (72%) create mode 100644 src/types/components/icon-with-text-list/profileDoneItemsList.interfaces.ts diff --git a/src/components/icon-with-text-list/ProfileDoneItemsList.style.js b/src/components/icon-with-text-list/ProfileDoneItemsList.style.ts similarity index 100% rename from src/components/icon-with-text-list/ProfileDoneItemsList.style.js rename to src/components/icon-with-text-list/ProfileDoneItemsList.style.ts diff --git a/src/components/icon-with-text-list/ProfileDoneItemsList.jsx b/src/components/icon-with-text-list/ProfileDoneItemsList.tsx similarity index 72% rename from src/components/icon-with-text-list/ProfileDoneItemsList.jsx rename to src/components/icon-with-text-list/ProfileDoneItemsList.tsx index 3281229ca..399fbd06f 100644 --- a/src/components/icon-with-text-list/ProfileDoneItemsList.jsx +++ b/src/components/icon-with-text-list/ProfileDoneItemsList.tsx @@ -1,14 +1,27 @@ +import { FC } from 'react' import { useTranslation } from 'react-i18next' import Box from '@mui/material/Box' import Typography from '@mui/material/Typography' +import { PopoverOrigin } from '@mui/material' import AppPopover from '../app-popover/AppPopover' import TitleWithDescription from '~/components/title-with-description/TitleWithDescription' import { styles } from '~/components/icon-with-text-list/ProfileDoneItemsList.style' +import { ProfileDoneItem } from '~/types' -const ProfileDoneItemsList = ({ items, icon, defaultQuantity }) => { +interface ProfileDoneItemsListProps { + items: ProfileDoneItem[] + icon: JSX.Element + defaultQuantity: number +} + +const ProfileDoneItemsList: FC = ({ + items, + icon, + defaultQuantity +}) => { const { t } = useTranslation() const shouldShowMore = items.length > defaultQuantity @@ -36,12 +49,13 @@ const ProfileDoneItemsList = ({ items, icon, defaultQuantity }) => { return ( {itemsList} diff --git a/src/types/components/components.index.ts b/src/types/components/components.index.ts index 5d3923d89..d874ea631 100644 --- a/src/types/components/components.index.ts +++ b/src/types/components/components.index.ts @@ -12,3 +12,4 @@ export * from '~/types/components/user-profile-info/userProfileInfo.interface' export * from '~/types/components/app-breadcrumbs/appBreadcrumbs.interfaces' export * from '~/types/components/app-breadcrumbs/appBreadcrumbs.types' export * from '~/types/components/subject-level-chips/subjectLevelChips.interface' +export * from '~/types/components/icon-with-text-list/profileDoneItemsList.interfaces' diff --git a/src/types/components/icon-with-text-list/profileDoneItemsList.interfaces.ts b/src/types/components/icon-with-text-list/profileDoneItemsList.interfaces.ts new file mode 100644 index 000000000..77182a373 --- /dev/null +++ b/src/types/components/icon-with-text-list/profileDoneItemsList.interfaces.ts @@ -0,0 +1,4 @@ +export interface ProfileDoneItem { + title: string + description: string +} From 1428ac8b0e35583cf1fa3ba536cb23ffb9a9fe70 Mon Sep 17 00:00:00 2001 From: Illia Pasichnichenko <54638946+Pasichnichenkoilya@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:44:37 +0300 Subject: [PATCH 24/37] Fixed privacy policy translation (#2153) * Fixed privacy policy translation * Fixed en version --- src/constants/translations/en/signup.json | 4 +++- src/constants/translations/uk/signup.json | 4 +++- src/containers/guest-home-page/signup-form/SignupForm.jsx | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/constants/translations/en/signup.json b/src/constants/translations/en/signup.json index 9178fb7d6..a38b2dbb0 100644 --- a/src/constants/translations/en/signup.json +++ b/src/constants/translations/en/signup.json @@ -11,5 +11,7 @@ "joinUs": "Login!", "confirmEmailTitle": "Your email address needs to be verified", "confirmEmailMessage": "We sent a confirmation email to: ", - "confirmEmailDesc": " Check your email and click on the confirmation button to continue." + "confirmEmailDesc": " Check your email and click on the confirmation button to continue.", + "terms": "Terms", + "privacyPolicy": "Privacy policy" } diff --git a/src/constants/translations/uk/signup.json b/src/constants/translations/uk/signup.json index ad5729ed0..50e81ce5d 100644 --- a/src/constants/translations/uk/signup.json +++ b/src/constants/translations/uk/signup.json @@ -11,5 +11,7 @@ "joinUs": "Увійти!", "confirmEmailTitle": "Вашу електронну адресу потрібно підтвердити", "confirmEmailMessage": "Ми відправили лист з підтвердженням на електронну пошту: ", - "confirmEmailDesc": " Перевірте свою електронну пошту та натисніть на кнопку підтвердження для продовження." + "confirmEmailDesc": " Перевірте свою електронну пошту та натисніть на кнопку підтвердження для продовження.", + "terms": "Умовами", + "privacyPolicy": "Політикою конфіденційності" } diff --git a/src/containers/guest-home-page/signup-form/SignupForm.jsx b/src/containers/guest-home-page/signup-form/SignupForm.jsx index 83a5118cd..9e03a0df8 100644 --- a/src/containers/guest-home-page/signup-form/SignupForm.jsx +++ b/src/containers/guest-home-page/signup-form/SignupForm.jsx @@ -54,7 +54,7 @@ const SignupForm = ({ to={termOfUse.path} variant='subtitle2' > - {t('common.labels.terms')} + {t('signup.terms')} {t('signup.and')} @@ -67,7 +67,7 @@ const SignupForm = ({ to={privacyPolicy.path} variant='subtitle2' > - {t('common.labels.privacyPolicy')} + {t('signup.privacyPolicy')} ) From 702daaa51fc360c5eec9ad696737762f9a3f844d Mon Sep 17 00:00:00 2001 From: Illia Pasichnichenko <54638946+Pasichnichenkoilya@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:45:20 +0300 Subject: [PATCH 25/37] Add an error tooltip for User Profile (#2164) * Add an error tooltip for User Profile * Add open on hover behaviour * Fix style: added gap and decreased error icon * Add an error tooltip to the personal info tab * Fix test * Fix linter errors * Update src/pages/edit-profile/EditProfile.tsx Co-authored-by: Olenka Hryk * Refactor enableTooltipError to use optional chain expression --------- Co-authored-by: Olenka Hryk --- .../sidebar-menu/SidebarMenu.styles.ts | 9 ++++++ src/components/sidebar-menu/SidebarMenu.tsx | 29 +++++++++++++++++-- .../translations/en/edit-profile.json | 3 +- .../translations/uk/edit-profile.json | 3 +- src/pages/edit-profile/EditProfile.tsx | 10 ++++++- 5 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/components/sidebar-menu/SidebarMenu.styles.ts b/src/components/sidebar-menu/SidebarMenu.styles.ts index d475eb1f3..93409b8a7 100644 --- a/src/components/sidebar-menu/SidebarMenu.styles.ts +++ b/src/components/sidebar-menu/SidebarMenu.styles.ts @@ -37,5 +37,14 @@ export const styles = { minWidth: 'unset' } } + }, + listItemContent: { + display: 'flex', + alignItems: 'center', + gap: 1.25 + }, + errorIcon: { + color: `${palette.error[800]} !important`, + fontSize: '1.25rem' } } diff --git a/src/components/sidebar-menu/SidebarMenu.tsx b/src/components/sidebar-menu/SidebarMenu.tsx index 84064ee56..84aa5afce 100644 --- a/src/components/sidebar-menu/SidebarMenu.tsx +++ b/src/components/sidebar-menu/SidebarMenu.tsx @@ -3,6 +3,8 @@ import ListItemButton from '@mui/material/ListItemButton' import ListItemText from '@mui/material/ListItemText' import List from '@mui/material/List' import ListItemIcon from '@mui/material/ListItemIcon' +import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline' +import { Box, Tooltip } from '@mui/material' import { FC } from 'react' import { useTranslation } from 'react-i18next' @@ -16,9 +18,15 @@ interface SidebarMenu { tabsData: UserProfileProps handleClick: (tab: UserProfileTabsEnum) => void activeTab: UserProfileTabsEnum + errorTooltipHolders: Partial> } -const SidebarMenu: FC = ({ handleClick, tabsData, activeTab }) => { +const SidebarMenu: FC = ({ + handleClick, + tabsData, + activeTab, + errorTooltipHolders +}) => { const { t } = useTranslation() const list = Object.keys(tabsData).map((key) => { @@ -27,11 +35,28 @@ const SidebarMenu: FC = ({ handleClick, tabsData, activeTab }) => { const isTabActive = tabKey === activeTab + const enableTooltipError = !isTabActive && errorTooltipHolders?.[tabKey] + + const toolTip = enableTooltipError && ( + + + + ) + return ( handleClick(tabKey)}> {item.icon} - + + + {t(item.title)} + {toolTip} + + ) diff --git a/src/constants/translations/en/edit-profile.json b/src/constants/translations/en/edit-profile.json index f1d42b1fa..c49262ed0 100644 --- a/src/constants/translations/en/edit-profile.json +++ b/src/constants/translations/en/edit-profile.json @@ -19,7 +19,8 @@ "professionalStatus": "Describe in short your professional summary. Maximum 200 characters.", "languagesTitle": "Language", "languagesDesc": "Select the language in which you would like to study and cooperate.", - "videoPresentationDesc": "Add the link to your presentational video. Your video needs to be public and uploaded to YouTube." + "videoPresentationDesc": "Add the link to your presentational video. Your video needs to be public and uploaded to YouTube.", + "errorTooltip": "There is a problem with your changes" }, "professionalTab": { "tabTitle": "Professional info", diff --git a/src/constants/translations/uk/edit-profile.json b/src/constants/translations/uk/edit-profile.json index 15775a79c..d2b67d7ac 100644 --- a/src/constants/translations/uk/edit-profile.json +++ b/src/constants/translations/uk/edit-profile.json @@ -19,7 +19,8 @@ "professionalStatus": "Коротко опишіть своє професійне резюме. Максимум 200 символів.", "languagesTitle": "Мова", "languagesDesc": "Виберіть мову, якою ви б хотіли навчатися та співпрацювати.", - "videoPresentationDesc": "Додайте посилання на вашу відеопрезентацію. Відео має бути публічним та розміщеним на YouTube." + "videoPresentationDesc": "Додайте посилання на вашу відеопрезентацію. Відео має бути публічним та розміщеним на YouTube.", + "errorTooltip": "Виникла проблема з вашими змінами" }, "professionalTab": { "tabTitle": "Професійні відомості", diff --git a/src/pages/edit-profile/EditProfile.tsx b/src/pages/edit-profile/EditProfile.tsx index 7972062f3..65b53ac9a 100644 --- a/src/pages/edit-profile/EditProfile.tsx +++ b/src/pages/edit-profile/EditProfile.tsx @@ -29,7 +29,9 @@ import { LoadingStatusEnum } from '~/redux/redux.constants' const EditProfile = () => { const { t } = useTranslation() const dispatch = useAppDispatch() - const { loading } = useAppSelector((state) => state.editProfile) + const { loading, tabValidityStatus } = useAppSelector( + (state) => state.editProfile + ) const [searchParams, setSearchParams] = useSearchParams({ tab: UserProfileTabsEnum.Profile @@ -66,6 +68,11 @@ const EditProfile = () => { } const cooperationContent = activeTab && tabsData[activeTab]?.content + const errorTooltipHolders = { + [UserProfileTabsEnum.Profile]: !tabValidityStatus.profileTab, + [UserProfileTabsEnum.ProfessionalInfo]: + !tabValidityStatus.professionalInfoTab + } return ( @@ -92,6 +99,7 @@ const EditProfile = () => { void handleClick(tab)} tabsData={tabsData} /> From 42c26cebbf9b8fd0c2edb4814f7bb9f199e7d1b6 Mon Sep 17 00:00:00 2001 From: Illia Pasichnichenko <54638946+Pasichnichenkoilya@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:45:56 +0300 Subject: [PATCH 26/37] Fix error caused when category is cleared (#2205) --- .../AddProfessionalCategoryModal.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/containers/edit-profile/professional-info-tab/add-professional-category-modal/AddProfessionalCategoryModal.tsx b/src/containers/edit-profile/professional-info-tab/add-professional-category-modal/AddProfessionalCategoryModal.tsx index 23e11ad66..5f5ffe7af 100644 --- a/src/containers/edit-profile/professional-info-tab/add-professional-category-modal/AddProfessionalCategoryModal.tsx +++ b/src/containers/edit-profile/professional-info-tab/add-professional-category-modal/AddProfessionalCategoryModal.tsx @@ -162,6 +162,13 @@ const AddProfessionalCategoryModal: FC = ({ _: SyntheticEvent, value: CategoryNameInterface | null ) => { + if (!value) { + handleDataChange({ + category: { _id: '', name: '' }, + subjects: [{ _id: '', name: '' }] + }) + return + } handleDataChange({ category: value, professionalSubjectTemplate }) } From ba869829fedd68666b66b514ab2cad2abf44a751 Mon Sep 17 00:00:00 2001 From: Illia Pasichnichenko <54638946+Pasichnichenkoilya@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:46:11 +0300 Subject: [PATCH 27/37] Rewrote ProfileContainerMobile to tsx (#2220) * Rewrote ProfileContainerMobile to tsx * Refactor doneItems type * Fix doneItems type --- ...rMobile.jsx => ProfileContainerMobile.tsx} | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) rename src/containers/user-profile/profile-info/{ProfileContainerMobile.jsx => ProfileContainerMobile.tsx} (75%) diff --git a/src/containers/user-profile/profile-info/ProfileContainerMobile.jsx b/src/containers/user-profile/profile-info/ProfileContainerMobile.tsx similarity index 75% rename from src/containers/user-profile/profile-info/ProfileContainerMobile.jsx rename to src/containers/user-profile/profile-info/ProfileContainerMobile.tsx index 7870fb68d..2868b5e8b 100644 --- a/src/containers/user-profile/profile-info/ProfileContainerMobile.jsx +++ b/src/containers/user-profile/profile-info/ProfileContainerMobile.tsx @@ -1,3 +1,5 @@ +import { ReactNode } from 'react' + import Box from '@mui/material/Box' import SchoolIcon from '@mui/icons-material/School' import DoneIcon from '@mui/icons-material/Done' @@ -8,6 +10,17 @@ import AppChipList from '~/components/app-chips-list/AppChipList' import ProfileDoneItemsList from '~/components/icon-with-text-list/ProfileDoneItemsList' import { styles } from '~/containers/user-profile/profile-info/ProfileInfo.styles' +import { UserResponse } from '~/types' + +interface ProfileContainerMobileProps { + actionIcon: ReactNode + accInfo: ReactNode + buttonGroup: ReactNode + defaultQuantity: number + doneItems: { title: string; description: string }[] + userData: UserResponse + chipItems: string[] +} const ProfileContainerMobile = ({ actionIcon, @@ -17,18 +30,15 @@ const ProfileContainerMobile = ({ doneItems, userData, chipItems -}) => { +}: ProfileContainerMobileProps) => { + const avatarSrc = userData.photo + ? `${import.meta.env.VITE_APP_IMG_USER_URL}${userData.photo}` + : '' return ( - + Date: Fri, 2 Aug 2024 13:46:20 +0300 Subject: [PATCH 28/37] Rewrote ProfileContainerDesktop to tsx (#2223) * Rewrote ProfileContainerDesktop to tsx * Refactor doneItems type * Fix doneItems type --- ...esktop.jsx => ProfileContainerDesktop.tsx} | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) rename src/containers/user-profile/profile-info/{ProfileContainerDesktop.jsx => ProfileContainerDesktop.tsx} (74%) diff --git a/src/containers/user-profile/profile-info/ProfileContainerDesktop.jsx b/src/containers/user-profile/profile-info/ProfileContainerDesktop.tsx similarity index 74% rename from src/containers/user-profile/profile-info/ProfileContainerDesktop.jsx rename to src/containers/user-profile/profile-info/ProfileContainerDesktop.tsx index 4c336b4c0..f87d73882 100644 --- a/src/containers/user-profile/profile-info/ProfileContainerDesktop.jsx +++ b/src/containers/user-profile/profile-info/ProfileContainerDesktop.tsx @@ -1,3 +1,5 @@ +import { ReactNode } from 'react' + import Box from '@mui/material/Box' import SchoolIcon from '@mui/icons-material/School' import DoneIcon from '@mui/icons-material/Done' @@ -8,6 +10,17 @@ import AppChipList from '~/components/app-chips-list/AppChipList' import ProfileDoneItemsList from '~/components/icon-with-text-list/ProfileDoneItemsList' import { styles } from '~/containers/user-profile/profile-info/ProfileInfo.styles' +import { UserResponse } from '~/types' + +interface ProfileContainerDesktopProps { + actionIcon: ReactNode + accInfo: ReactNode + buttonGroup: ReactNode + defaultQuantity: number + doneItems: { title: string; description: string }[] + userData: UserResponse + chipItems: string[] +} const ProfileContainerDesktop = ({ userData, @@ -17,17 +30,14 @@ const ProfileContainerDesktop = ({ defaultQuantity, doneItems, chipItems -}) => { +}: ProfileContainerDesktopProps) => { + const avatarSrc = userData.photo + ? `${import.meta.env.VITE_APP_IMG_USER_URL}${userData.photo}` + : '' return ( - + {actionIcon} From 56919a2ff90f12a1addab48353491351e149bd16 Mon Sep 17 00:00:00 2001 From: Illia Pasichnichenko <54638946+Pasichnichenkoilya@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:14:08 +0300 Subject: [PATCH 29/37] Rewrote SearchInput to tsx (#2210) * Rewrote SearchInput to tsx * Refactor setSearch to use React.Dispatch type --- .../search-input/{SearchInput.jsx => SearchInput.tsx} | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) rename src/components/search-input/{SearchInput.jsx => SearchInput.tsx} (86%) diff --git a/src/components/search-input/SearchInput.jsx b/src/components/search-input/SearchInput.tsx similarity index 86% rename from src/components/search-input/SearchInput.jsx rename to src/components/search-input/SearchInput.tsx index 337765099..3b989cca8 100644 --- a/src/components/search-input/SearchInput.jsx +++ b/src/components/search-input/SearchInput.tsx @@ -7,7 +7,12 @@ import SearchIcon from '@mui/icons-material/Search' import { styles } from './SearchInput.styles' -const SearchInput = ({ search, setSearch }) => { +interface SearchInputProps { + search: string + setSearch: React.Dispatch> +} + +const SearchInput = ({ search, setSearch }: SearchInputProps) => { const [searchInput, setSearchInput] = useState(search) return ( @@ -37,7 +42,7 @@ const SearchInput = ({ search, setSearch }) => { autoComplete: 'off' }} onChange={(e) => setSearchInput(e.target.value)} - onKeyPress={(e) => { + onKeyDown={(e) => { if (e.key === 'Enter') { setSearch(searchInput) } From c30c9185cf15ec296e00d94afd8faffecbc13e41 Mon Sep 17 00:00:00 2001 From: Illia Pasichnichenko <54638946+Pasichnichenkoilya@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:14:21 +0300 Subject: [PATCH 30/37] Rewrote VideoPresentation to tsx (#2212) --- .../{VideoPresentation.jsx => VideoPresentation.tsx} | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) rename src/containers/user-profile/video-presentation/{VideoPresentation.jsx => VideoPresentation.tsx} (77%) diff --git a/src/containers/user-profile/video-presentation/VideoPresentation.jsx b/src/containers/user-profile/video-presentation/VideoPresentation.tsx similarity index 77% rename from src/containers/user-profile/video-presentation/VideoPresentation.jsx rename to src/containers/user-profile/video-presentation/VideoPresentation.tsx index 890eda16d..ad8057c3d 100644 --- a/src/containers/user-profile/video-presentation/VideoPresentation.jsx +++ b/src/containers/user-profile/video-presentation/VideoPresentation.tsx @@ -4,7 +4,17 @@ import Typography from '@mui/material/Typography' import VideoBox from '~/components/video-box/VideoBox' import { ComponentEnum } from '~/types' -const VideoPresentation = ({ video, videoPreview, videoMock }) => { +interface VideoPresentationProps { + video: string + videoPreview: boolean + videoMock: string +} + +const VideoPresentation = ({ + video, + videoPreview, + videoMock +}: VideoPresentationProps) => { const { t } = useTranslation() return ( From 8abc624b15bcfcebb79468694a58729c29a195f4 Mon Sep 17 00:00:00 2001 From: Illia Pasichnichenko <54638946+Pasichnichenkoilya@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:15:25 +0300 Subject: [PATCH 31/37] Rewrote EmailConfirmModal to tsx (#2244) --- .../{EmailConfirmModal.jsx => EmailConfirmModal.tsx} | 12 ++++++++++-- src/context/modal-context.tsx | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) rename src/containers/email-confirm-modal/{EmailConfirmModal.jsx => EmailConfirmModal.tsx} (90%) diff --git a/src/containers/email-confirm-modal/EmailConfirmModal.jsx b/src/containers/email-confirm-modal/EmailConfirmModal.tsx similarity index 90% rename from src/containers/email-confirm-modal/EmailConfirmModal.jsx rename to src/containers/email-confirm-modal/EmailConfirmModal.tsx index bf2c8f947..4a86d0329 100644 --- a/src/containers/email-confirm-modal/EmailConfirmModal.jsx +++ b/src/containers/email-confirm-modal/EmailConfirmModal.tsx @@ -16,9 +16,17 @@ import imgReject from '~/assets/img/email-confirmation-modals/not-success-icon.s import { AuthService } from '~/services/auth-service' import { ButtonVariantEnum } from '~/types' import useAxios from '~/hooks/use-axios' -import { useModalContext } from '~/context/modal-context' +import { Component, useModalContext } from '~/context/modal-context' -const EmailConfirmModal = ({ confirmToken, openModal }) => { +interface EmailConfirmModalProps { + confirmToken: string + openModal: (component: Component, delayToClose?: number) => void +} + +const EmailConfirmModal = ({ + confirmToken, + openModal +}: EmailConfirmModalProps) => { const { t } = useTranslation() const { closeModal } = useModalContext() diff --git a/src/context/modal-context.tsx b/src/context/modal-context.tsx index 56472a704..6199e33ac 100644 --- a/src/context/modal-context.tsx +++ b/src/context/modal-context.tsx @@ -11,7 +11,7 @@ import { import PopupDialog from '~/components/popup-dialog/PopupDialog' import { PaperProps } from '@mui/material/Paper' -interface Component { +export interface Component { component: ReactElement paperProps?: PaperProps customCloseModal?: () => void From c6cd921cbb3747b82a0e2b76a7d9194010fc34ec Mon Sep 17 00:00:00 2001 From: Illia Pasichnichenko <54638946+Pasichnichenkoilya@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:15:49 +0300 Subject: [PATCH 32/37] Rewrote AdminNavBarItem to tsx (#2237) --- ...AdminNavBarItem.jsx => AdminNavBarItem.tsx} | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) rename src/containers/layout/admin-portal/admin-nav-bar-item/{AdminNavBarItem.jsx => AdminNavBarItem.tsx} (84%) diff --git a/src/containers/layout/admin-portal/admin-nav-bar-item/AdminNavBarItem.jsx b/src/containers/layout/admin-portal/admin-nav-bar-item/AdminNavBarItem.tsx similarity index 84% rename from src/containers/layout/admin-portal/admin-nav-bar-item/AdminNavBarItem.jsx rename to src/containers/layout/admin-portal/admin-nav-bar-item/AdminNavBarItem.tsx index 318527974..f29c4d87f 100644 --- a/src/containers/layout/admin-portal/admin-nav-bar-item/AdminNavBarItem.jsx +++ b/src/containers/layout/admin-portal/admin-nav-bar-item/AdminNavBarItem.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react' +import { ReactNode, useMemo, useState } from 'react' import { Link } from 'react-router-dom' import { useTranslation } from 'react-i18next' @@ -12,6 +12,18 @@ import ListItemText from '@mui/material/ListItemText' import Box from '@mui/material/Box' import { styles } from './AdminNavBarItem.styles' +interface AdminNavBarItemProps { + label: string + icon: ReactNode + expanded: boolean + children: { subLabel: string; path: string }[] + path: string + active: boolean + showSubItems: boolean + handleShowSubItems: (label: string) => void + handleActive: () => void +} + const AdminNavBarItem = ({ label, icon, @@ -22,8 +34,8 @@ const AdminNavBarItem = ({ showSubItems, handleShowSubItems, handleActive -}) => { - const [activeSubItem, setActiveSubItem] = useState(null) +}: AdminNavBarItemProps) => { + const [activeSubItem, setActiveSubItem] = useState(null) const { t } = useTranslation() From 8ad6d72e0ef0419006505f223329794fadc5654a Mon Sep 17 00:00:00 2001 From: Illia Pasichnichenko <54638946+Pasichnichenkoilya@users.noreply.github.com> Date: Fri, 2 Aug 2024 17:00:15 +0300 Subject: [PATCH 33/37] Rewrote SignupForm to tsx (#2238) --- .../{SignupForm.jsx => SignupForm.tsx} | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) rename src/containers/guest-home-page/signup-form/{SignupForm.jsx => SignupForm.tsx} (88%) diff --git a/src/containers/guest-home-page/signup-form/SignupForm.jsx b/src/containers/guest-home-page/signup-form/SignupForm.tsx similarity index 88% rename from src/containers/guest-home-page/signup-form/SignupForm.jsx rename to src/containers/guest-home-page/signup-form/SignupForm.tsx index 9e03a0df8..d0cacd0f6 100644 --- a/src/containers/guest-home-page/signup-form/SignupForm.jsx +++ b/src/containers/guest-home-page/signup-form/SignupForm.tsx @@ -1,7 +1,12 @@ -import { useState, useMemo } from 'react' +import { + useState, + useMemo, + FormEventHandler, + FocusEvent, + ChangeEvent +} from 'react' import { useTranslation } from 'react-i18next' import HashLink from '~/components/hash-link/HashLink' -import { useSelector } from 'react-redux' import Box from '@mui/material/Box' import FormControlLabel from '@mui/material/FormControlLabel' @@ -13,6 +18,16 @@ import { guestRoutes } from '~/router/constants/guestRoutes' import AppButton from '~/components/app-button/AppButton' import { styles } from '~/containers/guest-home-page/signup-form/SignupForm.styles' +import { SignupParams, UseFormErrors, UseFormEventHandler } from '~/types' +import { useAppSelector } from '~/hooks/use-redux' + +interface SignupFormProps { + handleSubmit: FormEventHandler + handleChange: UseFormEventHandler> + handleBlur: UseFormEventHandler> + data: SignupParams + errors: UseFormErrors +} const SignupForm = ({ handleSubmit, @@ -20,7 +35,7 @@ const SignupForm = ({ handleBlur, data, errors -}) => { +}: SignupFormProps) => { const { t } = useTranslation() const { privacyPolicy, termOfUse } = guestRoutes const [isAgreementChecked, setIsAgreementChecked] = useState(false) @@ -30,7 +45,7 @@ const SignupForm = ({ inputVisibility: confirmPasswordVisibility, showInputText: showConfirmPassword } = useInputVisibility(errors.confirmPassword) - const { authLoading } = useSelector((state) => state.appMain) + const { authLoading } = useAppSelector((state) => state.appMain) const handleOnAgreementChange = () => { setIsAgreementChecked((prev) => !prev) From 6d92eb698be6eccf7b14bae54e317e86ea0ff8ed Mon Sep 17 00:00:00 2001 From: Pasichnichenkoilya Date: Thu, 25 Jul 2024 10:44:50 +0300 Subject: [PATCH 34/37] Rebase develop into feature/1944/change-image-url-concatenation --- src/components/message/Message.tsx | 4 +++- .../user-profile-info/UserProfileInfo.tsx | 2 +- .../about-chat-sidebar/AboutChatSidebar.tsx | 3 ++- src/containers/chat/chat-item/ChatItem.tsx | 7 +++++-- .../profile-tab-form/ProfileTabForm.tsx | 3 ++- .../MyCooperationsDetails.tsx | 5 ++++- src/containers/navigation-icons/AccountIcon.tsx | 3 ++- .../profile-info/ProfileContainerDesktop.tsx | 12 ++++++++---- .../profile-info/ProfileContainerMobile.tsx | 15 +++++++++++---- 9 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/components/message/Message.tsx b/src/components/message/Message.tsx index 2b5570a31..b1bad0d9e 100644 --- a/src/components/message/Message.tsx +++ b/src/components/message/Message.tsx @@ -76,7 +76,9 @@ const Message: FC = ({ const avatar = !isMyMessage && isAvatarVisible && ( diff --git a/src/components/user-profile-info/UserProfileInfo.tsx b/src/components/user-profile-info/UserProfileInfo.tsx index 1b05b2d9f..d4777b3f7 100644 --- a/src/components/user-profile-info/UserProfileInfo.tsx +++ b/src/components/user-profile-info/UserProfileInfo.tsx @@ -67,7 +67,7 @@ const UserProfileInfo: FC = ({ const avatar = ( ) diff --git a/src/containers/about-chat-sidebar/AboutChatSidebar.tsx b/src/containers/about-chat-sidebar/AboutChatSidebar.tsx index 929a8c26e..decf4b0d2 100644 --- a/src/containers/about-chat-sidebar/AboutChatSidebar.tsx +++ b/src/containers/about-chat-sidebar/AboutChatSidebar.tsx @@ -91,7 +91,8 @@ const AboutChatSidebar: FC = ({ diff --git a/src/containers/chat/chat-item/ChatItem.tsx b/src/containers/chat/chat-item/ChatItem.tsx index bf26b6104..5a2976511 100644 --- a/src/containers/chat/chat-item/ChatItem.tsx +++ b/src/containers/chat/chat-item/ChatItem.tsx @@ -15,7 +15,7 @@ import { OverlapEnum, PositionEnum } from '~/types' -import { getFormattedDate } from '~/utils/helper-functions' +import { createUrlPath, getFormattedDate } from '~/utils/helper-functions' interface ItemOfChatProps { isActiveChat: boolean @@ -84,7 +84,10 @@ const ChatItem: FC = ({ overlap={OverlapEnum.Circular} > diff --git a/src/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.tsx b/src/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.tsx index 402950e54..d4b809fac 100644 --- a/src/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.tsx +++ b/src/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.tsx @@ -32,6 +32,7 @@ import { snackbarVariants } from '~/constants' import { imageResize } from '~/utils/image-resize' import { styles } from '~/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm.styles' import { openAlert } from '~/redux/features/snackbarSlice' +import { createUrlPath } from '~/utils/helper-functions' export interface ProfileTabFormProps { data: EditProfileForm @@ -106,7 +107,7 @@ const ProfileTabForm: FC = ({ const { photo } = data const photoToDisplay = typeof photo === 'string' - ? photo && `${import.meta.env.VITE_APP_IMG_USER_URL}${photo}` + ? photo && createUrlPath(import.meta.env.VITE_APP_IMG_USER_URL, photo) : photo?.src return ( diff --git a/src/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.tsx b/src/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.tsx index 9c17c2857..0e2b2dc86 100644 --- a/src/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.tsx +++ b/src/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.tsx @@ -104,7 +104,10 @@ const MyCooperationsDetails = () => { diff --git a/src/containers/navigation-icons/AccountIcon.tsx b/src/containers/navigation-icons/AccountIcon.tsx index 178265c39..26a38a184 100644 --- a/src/containers/navigation-icons/AccountIcon.tsx +++ b/src/containers/navigation-icons/AccountIcon.tsx @@ -13,6 +13,7 @@ import { defaultResponses } from '~/constants' import { styles } from '~/containers/navigation-icons/NavigationIcons.styles' import { UserResponse, UserRole } from '~/types' +import { createUrlPath } from '~/utils/helper-functions' interface AccountIconProps { openMenu: (event: MouseEvent) => void @@ -45,7 +46,7 @@ const AccountIcon: FC = ({ openMenu }) => { {!loading && firstName && lastName && `${firstName[0]}${lastName[0]}`} diff --git a/src/containers/user-profile/profile-info/ProfileContainerDesktop.tsx b/src/containers/user-profile/profile-info/ProfileContainerDesktop.tsx index f87d73882..79577978a 100644 --- a/src/containers/user-profile/profile-info/ProfileContainerDesktop.tsx +++ b/src/containers/user-profile/profile-info/ProfileContainerDesktop.tsx @@ -11,6 +11,7 @@ import ProfileDoneItemsList from '~/components/icon-with-text-list/ProfileDoneIt import { styles } from '~/containers/user-profile/profile-info/ProfileInfo.styles' import { UserResponse } from '~/types' +import { createUrlPath } from '~/utils/helper-functions' interface ProfileContainerDesktopProps { actionIcon: ReactNode @@ -31,13 +32,16 @@ const ProfileContainerDesktop = ({ doneItems, chipItems }: ProfileContainerDesktopProps) => { - const avatarSrc = userData.photo - ? `${import.meta.env.VITE_APP_IMG_USER_URL}${userData.photo}` - : '' return ( - + {actionIcon} diff --git a/src/containers/user-profile/profile-info/ProfileContainerMobile.tsx b/src/containers/user-profile/profile-info/ProfileContainerMobile.tsx index 2868b5e8b..82e21b279 100644 --- a/src/containers/user-profile/profile-info/ProfileContainerMobile.tsx +++ b/src/containers/user-profile/profile-info/ProfileContainerMobile.tsx @@ -11,6 +11,7 @@ import ProfileDoneItemsList from '~/components/icon-with-text-list/ProfileDoneIt import { styles } from '~/containers/user-profile/profile-info/ProfileInfo.styles' import { UserResponse } from '~/types' +import { createUrlPath } from '~/utils/helper-functions' interface ProfileContainerMobileProps { actionIcon: ReactNode @@ -31,14 +32,20 @@ const ProfileContainerMobile = ({ userData, chipItems }: ProfileContainerMobileProps) => { - const avatarSrc = userData.photo - ? `${import.meta.env.VITE_APP_IMG_USER_URL}${userData.photo}` - : '' return ( - + Date: Thu, 25 Jul 2024 12:34:42 +0300 Subject: [PATCH 35/37] Update AccountIcon.tsx --- src/containers/navigation-icons/AccountIcon.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/containers/navigation-icons/AccountIcon.tsx b/src/containers/navigation-icons/AccountIcon.tsx index 26a38a184..dba1aad21 100644 --- a/src/containers/navigation-icons/AccountIcon.tsx +++ b/src/containers/navigation-icons/AccountIcon.tsx @@ -46,7 +46,10 @@ const AccountIcon: FC = ({ openMenu }) => { {!loading && firstName && lastName && `${firstName[0]}${lastName[0]}`} From 0c650a37f06dbbc7d7a37f8a4e798241bea6fbe3 Mon Sep 17 00:00:00 2001 From: Pasichnichenkoilya Date: Thu, 25 Jul 2024 12:46:31 +0300 Subject: [PATCH 36/37] Update createUrlPath in helper-functions.tsx --- src/utils/helper-functions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/helper-functions.tsx b/src/utils/helper-functions.tsx index cd4c6a238..5ceb78269 100644 --- a/src/utils/helper-functions.tsx +++ b/src/utils/helper-functions.tsx @@ -206,7 +206,7 @@ export const createUrlPath = ( query = {} ) => { let trimmedUrl = URL - while (trimmedUrl.endsWith('/')) { + while (trimmedUrl && trimmedUrl.endsWith('/')) { trimmedUrl = trimmedUrl.slice(0, -1) } From 05aed7957e3395000aef608bacadff64a081a86d Mon Sep 17 00:00:00 2001 From: Pasichnichenkoilya Date: Mon, 29 Jul 2024 16:10:55 +0300 Subject: [PATCH 37/37] Fix sonar: refactor to use an optional chain expression --- src/utils/helper-functions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/helper-functions.tsx b/src/utils/helper-functions.tsx index 5ceb78269..90dd12253 100644 --- a/src/utils/helper-functions.tsx +++ b/src/utils/helper-functions.tsx @@ -206,7 +206,7 @@ export const createUrlPath = ( query = {} ) => { let trimmedUrl = URL - while (trimmedUrl && trimmedUrl.endsWith('/')) { + while (trimmedUrl?.endsWith('/')) { trimmedUrl = trimmedUrl.slice(0, -1) }