From 1076368b55eab31b30c5a01c166fa66905e60e6c Mon Sep 17 00:00:00 2001 From: "Leon.kov" Date: Wed, 29 Nov 2023 17:24:32 +0300 Subject: [PATCH] feat: add profile page card / get profile data thunk --- extractedTranslations/en/profile.json | 7 +++ public/locales/en/profile.json | 0 public/locales/ru/profile.json | 0 src/entities/Profile/index.ts | 2 + .../getProfileData/getProfileData.ts | 3 ++ .../getProfileError/getProfileError.ts | 3 ++ .../getProfileIsLoading.ts | 3 ++ .../fetchProfileData/fetchProfileData.ts | 19 ++++++++ .../Profile/model/slice/profileSlice.ts | 23 +++++++++- .../ui/ProfileCard/ProfileCard.module.scss | 21 +++++++++ .../Profile/ui/ProfileCard/ProfileCard.tsx | 46 +++++++++++++++++++ src/pages/ProfilePage/ui/ProfilePage.tsx | 13 ++++-- src/shared/lib/classNames/classNames.ts | 6 ++- tsconfig.json | 1 + 14 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 extractedTranslations/en/profile.json create mode 100644 public/locales/en/profile.json create mode 100644 public/locales/ru/profile.json create mode 100644 src/entities/Profile/model/selectors/getProfileData/getProfileData.ts create mode 100644 src/entities/Profile/model/selectors/getProfileError/getProfileError.ts create mode 100644 src/entities/Profile/model/selectors/getProfileIsLoading/getProfileIsLoading.ts create mode 100644 src/entities/Profile/model/services/fetchProfileData/fetchProfileData.ts create mode 100644 src/entities/Profile/ui/ProfileCard/ProfileCard.module.scss create mode 100644 src/entities/Profile/ui/ProfileCard/ProfileCard.tsx diff --git a/extractedTranslations/en/profile.json b/extractedTranslations/en/profile.json new file mode 100644 index 0000000..e03d2f7 --- /dev/null +++ b/extractedTranslations/en/profile.json @@ -0,0 +1,7 @@ +{ + "Edit": "", + "Profile": "", + "Your lastname": "", + "Your name": "", + "Your surname": "" +} diff --git a/public/locales/en/profile.json b/public/locales/en/profile.json new file mode 100644 index 0000000..e69de29 diff --git a/public/locales/ru/profile.json b/public/locales/ru/profile.json new file mode 100644 index 0000000..e69de29 diff --git a/src/entities/Profile/index.ts b/src/entities/Profile/index.ts index 6a3845e..427a81e 100644 --- a/src/entities/Profile/index.ts +++ b/src/entities/Profile/index.ts @@ -1,3 +1,5 @@ export { Profile, ProfileSchema } from './model/types/profile'; export { profileActions, profileReducer } from './model/slice/profileSlice'; +export { fetchProfileData } from './model/services/fetchProfileData/fetchProfileData'; +export { ProfileCard } from '../Profile/ui/ProfileCard/ProfileCard'; diff --git a/src/entities/Profile/model/selectors/getProfileData/getProfileData.ts b/src/entities/Profile/model/selectors/getProfileData/getProfileData.ts new file mode 100644 index 0000000..98222f8 --- /dev/null +++ b/src/entities/Profile/model/selectors/getProfileData/getProfileData.ts @@ -0,0 +1,3 @@ +import { StateSchema } from 'app/providers/StoreProvider'; + +export const getProfileData = (state: StateSchema) => state.profile?.data; diff --git a/src/entities/Profile/model/selectors/getProfileError/getProfileError.ts b/src/entities/Profile/model/selectors/getProfileError/getProfileError.ts new file mode 100644 index 0000000..1dc0717 --- /dev/null +++ b/src/entities/Profile/model/selectors/getProfileError/getProfileError.ts @@ -0,0 +1,3 @@ +import { StateSchema } from 'app/providers/StoreProvider'; + +export const getProfileError = (state: StateSchema) => state.profile?.error; diff --git a/src/entities/Profile/model/selectors/getProfileIsLoading/getProfileIsLoading.ts b/src/entities/Profile/model/selectors/getProfileIsLoading/getProfileIsLoading.ts new file mode 100644 index 0000000..0689aa6 --- /dev/null +++ b/src/entities/Profile/model/selectors/getProfileIsLoading/getProfileIsLoading.ts @@ -0,0 +1,3 @@ +import { StateSchema } from 'app/providers/StoreProvider'; + +export const getProfileIsLoading = (state: StateSchema) => state.profile?.isLoading; diff --git a/src/entities/Profile/model/services/fetchProfileData/fetchProfileData.ts b/src/entities/Profile/model/services/fetchProfileData/fetchProfileData.ts new file mode 100644 index 0000000..5a39c50 --- /dev/null +++ b/src/entities/Profile/model/services/fetchProfileData/fetchProfileData.ts @@ -0,0 +1,19 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { ThunkConfig } from 'app/providers/StoreProvider'; +import { Profile } from '../../types/profile'; + +export const fetchProfileData = createAsyncThunk< +Profile, +void, +ThunkConfig +>('profile/fetchProfileData', async (_, thunkAPI) => { + const { extra, rejectWithValue } = thunkAPI; + + try { + const response = await extra.api.get('/profile'); + + return response.data; + } catch (error) { + return rejectWithValue('error'); + } +}); diff --git a/src/entities/Profile/model/slice/profileSlice.ts b/src/entities/Profile/model/slice/profileSlice.ts index 0251d14..2d95fb2 100644 --- a/src/entities/Profile/model/slice/profileSlice.ts +++ b/src/entities/Profile/model/slice/profileSlice.ts @@ -1,5 +1,6 @@ -import { createSlice } from '@reduxjs/toolkit'; -import { ProfileSchema } from '../types/profile'; +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { fetchProfileData } from '../services/fetchProfileData/fetchProfileData'; +import { Profile, ProfileSchema } from '../types/profile'; const initialState: ProfileSchema = { readonly: true, @@ -12,6 +13,24 @@ export const profileSlice = createSlice({ name: 'profile', initialState, reducers: {}, + extraReducers: (builder) => { + builder + .addCase(fetchProfileData.pending, (state) => { + state.error = undefined; + state.isLoading = true; + }) + .addCase( + fetchProfileData.fulfilled, + (state, action: PayloadAction) => { + state.isLoading = false; + state.data = action.payload; + }, + ) + .addCase(fetchProfileData.rejected, (state, action) => { + state.isLoading = false; + state.error = action.payload; + }); + }, }); export const { actions: profileActions } = profileSlice; diff --git a/src/entities/Profile/ui/ProfileCard/ProfileCard.module.scss b/src/entities/Profile/ui/ProfileCard/ProfileCard.module.scss new file mode 100644 index 0000000..a445e9a --- /dev/null +++ b/src/entities/Profile/ui/ProfileCard/ProfileCard.module.scss @@ -0,0 +1,21 @@ +.ProfileCard { + padding: 20px; + border: 2px solid var(--inverted-bg-color); +} + +.header { + display: flex; +} + +.editBtn { + margin-left: auto; +} + +.data { + margin-top: 30px; +} + +.input { + margin-top: 10px; +} + diff --git a/src/entities/Profile/ui/ProfileCard/ProfileCard.tsx b/src/entities/Profile/ui/ProfileCard/ProfileCard.tsx new file mode 100644 index 0000000..22b0718 --- /dev/null +++ b/src/entities/Profile/ui/ProfileCard/ProfileCard.tsx @@ -0,0 +1,46 @@ +import { classNames } from 'shared/lib/classNames/classNames'; +import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; +import { Text } from 'shared/ui/Text/Text'; +import { Button, ButtonTheme } from 'shared/ui/Button/Button'; +import { Input } from 'shared/ui/Input/Input'; +import { getProfileIsLoading } from '../../model/selectors/getProfileIsLoading/getProfileIsLoading'; +import { getProfileError } from '../../model/selectors/getProfileError/getProfileError'; +import { getProfileData } from '../../model/selectors/getProfileData/getProfileData'; +import cls from './ProfileCard.module.scss'; + +interface ProfileCardProps { + className?: string; +} + +export const ProfileCard = ({ className }: ProfileCardProps) => { + const { t } = useTranslation('profile'); + const data = useSelector(getProfileData); + const isLoading = useSelector(getProfileIsLoading); + const error = useSelector(getProfileError); + return ( +
+
+ + +
+
+ + +
+
+ ); +}; diff --git a/src/pages/ProfilePage/ui/ProfilePage.tsx b/src/pages/ProfilePage/ui/ProfilePage.tsx index dfda269..dc1098d 100644 --- a/src/pages/ProfilePage/ui/ProfilePage.tsx +++ b/src/pages/ProfilePage/ui/ProfilePage.tsx @@ -1,8 +1,9 @@ import { classNames } from 'shared/lib/classNames/classNames'; import { useTranslation } from 'react-i18next'; -import { memo } from 'react'; +import { memo, useEffect } from 'react'; import { DynamicModuleLoader, ReducersList } from 'shared/lib/components/DynamicModuleLoared/DynamicModuleLoared'; -import { profileReducer } from '../../../entities/Profile'; +import { fetchProfileData, ProfileCard, profileReducer } from '../../../entities/Profile'; +import { useAppDispatch } from '../../../shared/lib/hook/useAppDispatch/useAppDispatch'; const reducers: ReducersList = { profile: profileReducer, @@ -14,10 +15,16 @@ interface ProfilePageProps { const ProfilePage = memo(({ className }: ProfilePageProps) => { const { t } = useTranslation(); + const dispatch = useAppDispatch(); + + useEffect(() => { + dispatch(fetchProfileData()); + }, [dispatch]); + return (
- {t('PROFILE PAGE')} +
diff --git a/src/shared/lib/classNames/classNames.ts b/src/shared/lib/classNames/classNames.ts index 8bef969..78ad399 100644 --- a/src/shared/lib/classNames/classNames.ts +++ b/src/shared/lib/classNames/classNames.ts @@ -1,6 +1,10 @@ type Mods = Record -export function classNames(cls: string, mods: Mods = {}, additional: string[] = []): string { +export function classNames( + cls: string, + mods: Mods = {}, + additional: Array = [], +): string { return [ cls, ...additional.filter(Boolean), diff --git a/tsconfig.json b/tsconfig.json index 7ca9445..1c815a4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,6 +14,7 @@ "moduleResolution": "node", // Для абсолютного импорта "baseUrl": ".", + "strict": true, "paths": { "*": ["./src/*"] },