Skip to content

Commit

Permalink
rewrited profile edit tab using redux
Browse files Browse the repository at this point in the history
  • Loading branch information
YaroslavChuiko committed Jul 16, 2024
1 parent 89da9a2 commit 4970e79
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 124 deletions.
104 changes: 48 additions & 56 deletions src/containers/edit-profile/profile-tab/ProfileTab.tsx
Original file line number Diff line number Diff line change
@@ -1,77 +1,80 @@
import Box from '@mui/material/Box'
import { FC, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import Box from '@mui/material/Box'
import { useBlocker } from 'react-router-dom'

import useUpdateUser from '~/hooks/use-update-user'
import useConfirm from '~/hooks/use-confirm'
import useForm from '~/hooks/use-form'

import TitleWithDescription from '~/components/title-with-description/TitleWithDescription'
import AppButton from '~/components/app-button/AppButton'
import ProfileTabForm from '~/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm'
import {
getProfileInitialValues,
getUserUpdatedData
} from '~/utils/get-profile-values'
import { validations } from '~/components/user-steps-wrapper/constants'
import ProfileTabForm from '~/containers/edit-profile/profile-tab/profile-tab-form/ProfileTabForm'
import { styles } from '~/containers/edit-profile/profile-tab/ProfileTab.styles'
import { useDebounce } from '~/hooks/use-debounce'
import { useAppDispatch, useAppSelector } from '~/hooks/use-redux'
import {
ButtonVariantEnum,
EditProfileForm,
SizeEnum,
EditProfileTabUserProps
} from '~/types'
updateProfileData,
updateValidityStatus
} from '~/redux/features/editProfileSlice'
import { EditProfileForm, MainUserRole } from '~/types'

const ProfileTab: FC<EditProfileTabUserProps> = ({ user }) => {
const ProfileTab: FC = () => {
const { t } = useTranslation()
const { setNeedConfirmation, checkConfirmation } = useConfirm()
const { handleSubmit, loading } = useUpdateUser(user._id, true)
const dispatch = useAppDispatch()
const { userRole } = useAppSelector((state) => state.appMain)
const {
city,
country,
firstName,
lastName,
nativeLanguage,
photo,
professionalSummary,
videoLink
} = useAppSelector((state) => state.editProfile)

const initialValues = getProfileInitialValues(user)
const initialValues: EditProfileForm = {
city,
country,
firstName,
lastName,
nativeLanguage,
photo: photo || null,
professionalSummary: professionalSummary || '',
videoLink:
typeof videoLink === 'string'
? videoLink
: videoLink[userRole as MainUserRole] ?? ''
}

const {
isDirty,
isValid,
handleInputChange,
handleBlur,
handleNonInputValueChange,
trigger,
data,
errors
} = useForm<EditProfileForm>({
initialValues,
validations
})

const blocker = useBlocker(isDirty)
const debouncedUpdateProfileData = useDebounce(() => {
void dispatch(updateProfileData(data))
}, 300)

useEffect(() => {
void (async () => {
if (blocker.state === 'blocked') {
const confirmed = await checkConfirmation({
message: 'questions.goBackToProfile',
title: 'titles.discardChanges',
confirmButton: t('common.discard'),
cancelButton: t('common.cancel')
})
if (confirmed) {
blocker.proceed()
} else {
blocker.reset()
}
}
})()
}, [blocker, checkConfirmation, t])
debouncedUpdateProfileData()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data])

useEffect(() => {
setNeedConfirmation(isDirty)
}, [setNeedConfirmation, isDirty])

const handleUpdateData = () => {
const updatedData = getUserUpdatedData(data)
handleSubmit(updatedData)
}
trigger()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

const hasError = Object.values(errors).some((error) => error)
useEffect(() => {
void dispatch(updateValidityStatus({ tab: 'profileTab', value: isValid }))
}, [isValid, dispatch])

return (
<Box sx={styles.profileInnerContainer}>
Expand All @@ -89,17 +92,6 @@ const ProfileTab: FC<EditProfileTabUserProps> = ({ user }) => {
handleNonInputValueChange={handleNonInputValueChange}
/>
</Box>

<AppButton
disabled={hasError}
loading={loading}
onClick={handleUpdateData}
size={SizeEnum.ExtraLarge}
sx={styles.updateProfileBtn}
variant={ButtonVariantEnum.Contained}
>
{t('editProfilePage.profile.updateProfileBtn')}
</AppButton>
</Box>
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/edit-profile/EditProfile.constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const tabsData: UserProfileProps = {
[UserProfileTabsEnum.Profile]: {
icon: <AccountCircleIcon />,
title: 'editProfilePage.profile.generalTab.tabTitle',
content: (response) => <ProfileTab user={response} />
content: () => <ProfileTab />
},
[UserProfileTabsEnum.ProfessionalInfo]: {
icon: <SchoolIcon />,
Expand Down
20 changes: 16 additions & 4 deletions src/pages/edit-profile/EditProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback } from 'react'
import { useCallback, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { Link, useSearchParams } from 'react-router-dom'

Expand All @@ -9,7 +9,7 @@ import Divider from '@mui/material/Divider'
import useConfirm from '~/hooks/use-confirm'

import { authRoutes } from '~/router/constants/authRoutes'
import { useAppSelector } from '~/hooks/use-redux'
import { useAppDispatch, useAppSelector } from '~/hooks/use-redux'
import { userService } from '~/services/user-service'
import useAxios from '~/hooks/use-axios'
import Loader from '~/components/loader/Loader'
Expand All @@ -27,9 +27,13 @@ import {
import { tabsData } from '~/pages/edit-profile/EditProfile.constants'

import { styles } from '~/pages/edit-profile/EditProfile.styles'
import { fetchUserById } from '~/redux/features/editProfileSlice'
import { LoadingStatusEnum } from '~/redux/redux.constants'

const EditProfile = () => {
const { t } = useTranslation()
const dispatch = useAppDispatch()
const { loading } = useAppSelector((state) => state.editProfile)

const [searchParams, setSearchParams] = useSearchParams({
tab: UserProfileTabsEnum.Profile
Expand Down Expand Up @@ -57,14 +61,22 @@ const EditProfile = () => {
() => userService.getUserById(userId, userRole as UserRole, true),
[userId, userRole]
)

useEffect(() => {
void dispatch(
fetchUserById({ userId, role: userRole as UserRole, isEdit: true })
)
}, [dispatch, userId, userRole])

const { checkConfirmation } = useConfirm()

const { loading, response } = useAxios<UserResponse>({
//! delete when all tabs are ready
const { loading: userLoading, response } = useAxios<UserResponse>({
service: getUserData,
fetchOnMount: true
})

if (loading) {
if (loading === LoadingStatusEnum.Pending || userLoading) {
return <Loader pageLoad size={70} />
}

Expand Down
55 changes: 50 additions & 5 deletions src/redux/features/editProfileSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import {
} from '~/redux/redux.constants'
import {
DataByRole,
EditProfileForm,
ErrorResponse,
MainUserRole,
SubjectNameInterface,
UpdatedPhoto,
UpdateUserParams,
UserMainSubject,
UserMainSubjectFieldValues,
Expand All @@ -22,12 +24,12 @@ import { userService } from '~/services/user-service'
interface EditProfileState {
firstName: string
lastName: string
country: string
city: string
country: string | null
city: string | null
professionalSummary?: string
nativeLanguage: string | null
videoLink: DataByRole<string>
photo?: string | null
videoLink: DataByRole<string> | string
photo?: string | UpdatedPhoto | null
categories: DataByRole<UserMainSubject[]>
education?: string
workExperience?: string
Expand All @@ -39,6 +41,11 @@ interface EditProfileState {
isEmailNotification: boolean
loading: LoadingStatus
error: string | null
tabValidityStatus: {
profileTab: boolean
professionalInfoTab: boolean
notificationTab: boolean
}
}

const initialState: EditProfileState = {
Expand All @@ -60,7 +67,12 @@ const initialState: EditProfileState = {
isSimilarOffersNotification: false,
isEmailNotification: false,
loading: LoadingStatusEnum.Idle,
error: null
error: null,
tabValidityStatus: {
profileTab: true,
professionalInfoTab: true,
notificationTab: true
}
}

const updateStateFromPayload = (
Expand Down Expand Up @@ -140,6 +152,37 @@ const editProfileSlice = createSlice({
const { field, value } = action.payload
state[field] = value
},
updateValidityStatus: (
state,
action: PayloadAction<{
tab: keyof EditProfileState['tabValidityStatus']
value: boolean
}>
) => {
const { tab, value } = action.payload
state.tabValidityStatus[tab] = value
},
updateProfileData: (state, action: PayloadAction<EditProfileForm>) => {
const {
city,
country,
firstName,
lastName,
nativeLanguage,
photo,
professionalSummary,
videoLink
} = action.payload

state.city = city
state.country = country
state.firstName = firstName
state.lastName = lastName
state.nativeLanguage = nativeLanguage
state.photo = photo
state.professionalSummary = professionalSummary
state.videoLink = videoLink
},
addCategory: (
state,
action: PayloadAction<{
Expand Down Expand Up @@ -264,6 +307,8 @@ const { actions, reducer } = editProfileSlice

export const {
setField,
updateValidityStatus,
updateProfileData,
addCategory,
editCategory,
addSubjectToCategory,
Expand Down
4 changes: 0 additions & 4 deletions src/types/edit-profile/interfaces/editProfile.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,3 @@ export interface UserMainSubject extends ProfessionalCategory {
isDeletionBlocked: boolean
_id: string
}

export interface EditProfileTabUserProps {
user: UserResponse
}
4 changes: 3 additions & 1 deletion tests/test-utils.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { theme } from '~/styles/app-theme/custom-mui.styles'
import PopupsProvider from '~/PopupsProvider'
import cooperationsReducer from '~/redux/features/cooperationsSlice'
import snackbarReducer from '~/redux/features/snackbarSlice'
import editProfileReducer from '~/redux/features/editProfileSlice'
import AppSnackbar from '~/containers/layout/app-snackbar/AppSnackbar'

import MockAdapter from 'axios-mock-adapter'
Expand All @@ -22,7 +23,8 @@ export const renderWithProviders = (
reducer: {
appMain: reducer,
cooperations: cooperationsReducer,
snackbar: snackbarReducer
snackbar: snackbarReducer,
editProfile: editProfileReducer
},
preloadedState
}),
Expand Down
Loading

0 comments on commit 4970e79

Please sign in to comment.