Skip to content

Commit

Permalink
feat: add readOnly for inputs, isLoading and error for profile page
Browse files Browse the repository at this point in the history
  • Loading branch information
TomatoVan committed Dec 5, 2023
1 parent 51d9cae commit 6d3a378
Show file tree
Hide file tree
Showing 15 changed files with 195 additions and 44 deletions.
5 changes: 5 additions & 0 deletions extractedTranslations/en/profile.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
{
"": "",
"Edit": "",
"Error": "",
"Error with getting profile data": "",
"Error with profile ": "",
"Profile": "",
"Try to reload page": "",
"Your lastname": "",
"Your name": "",
"Your surname": ""
Expand Down
3 changes: 3 additions & 0 deletions extractedTranslations/en/translation.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"Cancel": "",
"Edit": "",
"PROFILE PAGE": "",
"Profile": "",
"auth_form": "",
"auth_password": "",
"auth_username": "",
Expand Down
2 changes: 1 addition & 1 deletion src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function App() {
const dispatch = useDispatch();

useEffect(() => {
dispatch(userActions.initAuthData);
dispatch(userActions.initAuthData());
}, [dispatch]);

return (
Expand Down
5 changes: 5 additions & 0 deletions src/entities/Profile/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ 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';

export { getProfileData } from './model/selectors/getProfileData/getProfileData';
export { getProfileError } from './model/selectors/getProfileError/getProfileError';
export { getProfileIsLoading } from './model/selectors/getProfileIsLoading/getProfileIsLoading';
export { getProfileReadonly } from './model/selectors/getProfileReadonly/getProfileReadonly';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { StateSchema } from 'app/providers/StoreProvider';

export const getProfileReadonly = (state: StateSchema) => state.profile?.readonly;
6 changes: 5 additions & 1 deletion src/entities/Profile/model/slice/profileSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ const initialState: ProfileSchema = {
export const profileSlice = createSlice({
name: 'profile',
initialState,
reducers: {},
reducers: {
setReadonly: (state, action:PayloadAction<boolean>) => {
state.readonly = action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(fetchProfileData.pending, (state) => {
Expand Down
19 changes: 7 additions & 12 deletions src/entities/Profile/ui/ProfileCard/ProfileCard.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,14 @@
border: 2px solid var(--inverted-bg-color);
}

.header {
display: flex;
}

.editBtn {
margin-left: auto;
}

.data {
margin-top: 30px;
}

.input {
margin-top: 10px;
}

.loading,
.error {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
}
56 changes: 37 additions & 19 deletions src/entities/Profile/ui/ProfileCard/ProfileCard.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,52 @@
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 { Text, TextAlign, TextTheme } from 'shared/ui/Text/Text';
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 { Loader } from 'shared/ui/Loader/Loader';
import { Profile } from '../../model/types/profile';
import cls from './ProfileCard.module.scss';

interface ProfileCardProps {
className?: string;
data?: Profile;
isLoading?: boolean;
error?: string;
}

export const ProfileCard = ({ className }: ProfileCardProps) => {
export const ProfileCard = (props: ProfileCardProps) => {
const {
className,
data,
isLoading,
error,
} = props;

const { t } = useTranslation('profile');
const data = useSelector(getProfileData);
const isLoading = useSelector(getProfileIsLoading);
const error = useSelector(getProfileError);

if (isLoading) {
return (
<div className={classNames(cls.ProfileCard, {}, [className, cls.loading])}>
<Loader />
</div>
);
}

if (error) {
return (
<div className={classNames(cls.ProfileCard, {}, [className, cls.error])}>
<Text
theme={TextTheme.ERROR}
title={t('Error with getting profile data')}
text={t('Try to reload page')}
align={TextAlign.CENTER}
/>
</div>
);
}

return (
<div className={classNames(cls.ProfileCard, {}, [className])}>
<div className={cls.header}>
<Text title={t('Profile')} />
<Button
className={cls.editBtn}
theme={ButtonTheme.OUTLINE}
>
{t('Edit')}
</Button>
</div>
<div className={cls.header} />
<div className={cls.data}>
<Input
value={data?.first}
Expand Down
29 changes: 24 additions & 5 deletions src/pages/ProfilePage/ui/ProfilePage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { useTranslation } from 'react-i18next';
import { memo, useEffect } from 'react';
import { DynamicModuleLoader, ReducersList } from 'shared/lib/components/DynamicModuleLoared/DynamicModuleLoared';
import { fetchProfileData, ProfileCard, profileReducer } from '../../../entities/Profile';
import {
DynamicModuleLoader,
ReducersList,
} from 'shared/lib/components/DynamicModuleLoared/DynamicModuleLoared';
import { useSelector } from 'react-redux';
import { ProfilePageHeader } from './ProfilePageHeader/ProfilePageHeader';
import { getProfileIsLoading } from '../../../entities/Profile/model/selectors/getProfileIsLoading/getProfileIsLoading';
import { getProfileError } from '../../../entities/Profile/model/selectors/getProfileError/getProfileError';
import { getProfileData } from '../../../entities/Profile/model/selectors/getProfileData/getProfileData';
import { useAppDispatch } from '../../../shared/lib/hook/useAppDispatch/useAppDispatch';
import {
fetchProfileData,
ProfileCard,
profileReducer,
} from '../../../entities/Profile';

const reducers: ReducersList = {
profile: profileReducer,
Expand All @@ -14,17 +25,25 @@ interface ProfilePageProps {
}

const ProfilePage = memo(({ className }: ProfilePageProps) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();

const data = useSelector(getProfileData);
const isLoading = useSelector(getProfileIsLoading);
const error = useSelector(getProfileError);

useEffect(() => {
dispatch(fetchProfileData());
}, [dispatch]);

return (
<DynamicModuleLoader reducers={reducers} removeAfterUnmount>
<div className={classNames('', {}, [className])}>
<ProfileCard />
<ProfilePageHeader />
<ProfileCard
data={data}
isLoading={isLoading}
error={error}
/>
</div>
</DynamicModuleLoader>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.ProfilePageHeader {
display: flex;
margin-bottom: 20px;

.editBtn {
margin-left: auto;
}
}
53 changes: 53 additions & 0 deletions src/pages/ProfilePage/ui/ProfilePageHeader/ProfilePageHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { useTranslation } from 'react-i18next';
import { Text } from 'shared/ui/Text/Text';
import { Button, ButtonTheme } from 'shared/ui/Button/Button';
import { useSelector } from 'react-redux';
import { getProfileReadonly, profileActions } from 'entities/Profile';
import { useCallback } from 'react';
import { useAppDispatch } from 'shared/lib/hook/useAppDispatch/useAppDispatch';
import cls from './ProfilePageHeader.module.scss';

interface ProfilePageHeaderProps {
className?: string;
}

export const ProfilePageHeader = ({ className }: ProfilePageHeaderProps) => {
const { t } = useTranslation();

const readonly = useSelector(getProfileReadonly);
const dispatch = useAppDispatch();

const onEdit = useCallback(() => {
dispatch(profileActions.setReadonly(false));
}, [dispatch]);

const onCancelEdit = useCallback(() => {
dispatch(profileActions.setReadonly(true));
}, [dispatch]);

return (
<div className={classNames(cls.ProfilePageHeader, {}, [className])}>
<Text title={t('Profile')} />
{readonly
? (
<Button
className={cls.editBtn}
theme={ButtonTheme.OUTLINE}
onClick={onEdit}
>
{t('Edit')}
</Button>
) : (
<Button
className={cls.editBtn}
theme={ButtonTheme.OUTLINE}
onClick={onCancelEdit}
>
{t('Cancel')}
</Button>
)}

</div>
);
};
5 changes: 5 additions & 0 deletions src/shared/ui/Input/Input.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
animation: blink 0.7s forwards infinite;
}

.readonly {
opacity: 0.7;
}

@keyframes blink {
0% {
opacity: 0;
Expand All @@ -47,3 +51,4 @@
opacity: 1;
}
}

14 changes: 10 additions & 4 deletions src/shared/ui/Input/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { classNames, Mods } from 'shared/lib/classNames/classNames';
import {
ChangeEvent,
InputHTMLAttributes,
Expand All @@ -11,14 +11,15 @@ import cls from './Input.module.scss';

type HTMLInputProps = Omit<
InputHTMLAttributes<HTMLInputElement>,
'value' | 'onChange'
'value' | 'onChange' | 'readOnly'
>;

interface InputProps extends HTMLInputProps {
className?: string;
value?: string;
onChange?: (value: string) => void;
autofocus?: boolean;
readonly?: boolean;

}

Expand All @@ -30,7 +31,7 @@ export const Input = memo((props: InputProps) => {
type = 'text',
placeholder,
autofocus,

readonly,
...otherProps
} = props;
const ref = useRef<HTMLInputElement>(null);
Expand Down Expand Up @@ -60,8 +61,12 @@ export const Input = memo((props: InputProps) => {
}
}, [autofocus]);

const mods: Mods = {
[cls.readOnly]: readonly,
};

return (
<div className={classNames(cls.InputWrapper, {}, [className])}>
<div className={classNames(cls.InputWrapper, mods, [className])}>
{placeholder && (
<div className={cls.placeholder}>
{`${placeholder}>`}
Expand All @@ -78,6 +83,7 @@ export const Input = memo((props: InputProps) => {
onBlur={onBlur}
onSelect={onSelect}
onFocus={onFocus}
readOnly={readonly}
{...otherProps}
/>
{isFocused && (
Expand Down
12 changes: 12 additions & 0 deletions src/shared/ui/Text/Text.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,15 @@
color: var(--red-dark);
}
}

.left {
text-align: left;
}

.right {
text-align: right;
}

.center {
text-align: center;
}
Loading

0 comments on commit 6d3a378

Please sign in to comment.