Skip to content

Commit

Permalink
feat: add currency & country entities and profile page if not auth
Browse files Browse the repository at this point in the history
  • Loading branch information
TomatoVan committed Dec 10, 2023
1 parent 9686533 commit 3b46adc
Show file tree
Hide file tree
Showing 23 changed files with 338 additions and 43 deletions.
2 changes: 2 additions & 0 deletions extractedTranslations/en/translation.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"Cancel": "",
"Choose country": "",
"Choose currency": "",
"Edit": "",
"PROFILE PAGE": "",
"Profile": "",
Expand Down
8 changes: 1 addition & 7 deletions json-server/db.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,7 @@
}
],
"profile": {
"first": "Тимур123",
"lastname": "Ульби11223",
"age": 22312,
"currency": "RUB",
"country": "Russia",
"city": "Moscow321",
"username": "admi1",
"avatar": "https://pic.rutubelist.ru/user/3b/27/3b2758ad5492a76b578f7ee072e4e894.jpg"
"country": "Russia"
}
}
52 changes: 33 additions & 19 deletions src/app/providers/router/ui/AppRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
import React, { Suspense } from 'react';
import React, { memo, Suspense, useMemo } from 'react';
import { Route, Routes } from 'react-router-dom';
import { routeConfig } from 'shared/config/routeConfig/routeConfig';
import { PageLoader } from 'shared/ui/PageLoader/PageLoader';
import { useSelector } from 'react-redux';
import { getUserAuthData } from 'entities/User';

const AppRouter = () => (
<Routes>
{Object.values(routeConfig).map(({ element, path }) => (
<Route
key={path}
path={path}
element={(
<Suspense fallback={<PageLoader />}>
<div className="page-wrapper">
{element}
</div>
</Suspense>
)}
/>
))}
</Routes>
);
const AppRouter = () => {
const isAuth = useSelector(getUserAuthData);

export default AppRouter;
const routes = useMemo(() => Object.values(routeConfig).filter((route) => {
if (route.authOnly && !isAuth) {
return false;
}

return true;
}), [isAuth]);

return (
<Routes>
{routes.map(({ element, path }) => (
<Route
key={path}
path={path}
element={(
<Suspense fallback={<PageLoader />}>
<div className="page-wrapper">
{element}
</div>
</Suspense>
)}
/>
))}
</Routes>
);
};

export default memo(AppRouter);
2 changes: 2 additions & 0 deletions src/entities/Country/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Country } from './model/types/country';
export { CountrySelect } from './ui/CountrySelect/CountrySelect';
7 changes: 7 additions & 0 deletions src/entities/Country/model/types/country.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export enum Country {
Russia = 'Russia',
Belarus = 'Belarus',
Ukraine = 'Ukraine',
Kazakhstan = 'Kazahstan',
Armenia = 'Armenia',
}
15 changes: 15 additions & 0 deletions src/entities/Country/ui/CountrySelect/CountrySelect.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { CountrySelect } from './CountrySelect';

export default {
title: 'entities/CountrySelect',
component: CountrySelect,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof CountrySelect>;

const Template: ComponentStory<typeof CountrySelect> = (args) => <CountrySelect {...args} />;

export const Primary = Template.bind({});
Primary.args = {};
46 changes: 46 additions & 0 deletions src/entities/Country/ui/CountrySelect/CountrySelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { useTranslation } from 'react-i18next';
import { Select } from 'shared/ui/Select/Select';
import { memo, useCallback } from 'react';
import { Country } from '../../model/types/country';

interface CountrySelectProps {
className?: string;
value?: string;
onChange?: (value: Country) => void;
readonly?:boolean;
}

const options = [
{ value: Country.Armenia, content: Country.Armenia },
{ value: Country.Ukraine, content: Country.Ukraine },
{ value: Country.Belarus, content: Country.Belarus },
{ value: Country.Kazakhstan, content: Country.Kazakhstan },
{ value: Country.Russia, content: Country.Russia },
];

export const CountrySelect = memo((props: CountrySelectProps) => {
const { t } = useTranslation();

const {
className,
value,
onChange,
readonly,
} = props;

const onChangeHandler = useCallback((value: string) => {
onChange?.(value as Country);
}, [onChange]);

return (
<Select
className={classNames('', {}, [className])}
label={t('Choose country')}
options={options}
value={value}
onChange={onChangeHandler}
readonly={readonly}
/>
);
});
2 changes: 2 additions & 0 deletions src/entities/Currency/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Currency } from './model/types/currency';
export { CurrencySelect } from './ui/CurrencySelect/CurrencySelect';
5 changes: 5 additions & 0 deletions src/entities/Currency/model/types/currency.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum Currency {
"RUB" = "RUB",
"EUR" = "EUR",
"USD" = "USD",
}
15 changes: 15 additions & 0 deletions src/entities/Currency/ui/CurrencySelect/CurrencySelect.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { CurrencySelect } from './CurrencySelect';

export default {
title: 'entities/CurrencySelect',
component: CurrencySelect,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof CurrencySelect>;

const Template: ComponentStory<typeof CurrencySelect> = (args) => <CurrencySelect {...args} />;

export const Primary = Template.bind({});
Primary.args = {};
44 changes: 44 additions & 0 deletions src/entities/Currency/ui/CurrencySelect/CurrencySelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { useTranslation } from 'react-i18next';
import { Select } from 'shared/ui/Select/Select';
import { memo, useCallback } from 'react';
import { Currency } from '../../model/types/currency';

interface CurrencySelectProps {
className?: string;
value?: string;
onChange?: (value: Currency) => void;
readonly?:boolean;
}

const options = [
{ value: Currency.RUB, content: Currency.RUB },
{ value: Currency.USD, content: Currency.USD },
{ value: Currency.EUR, content: Currency.EUR },
];

export const CurrencySelect = memo((props: CurrencySelectProps) => {
const { t } = useTranslation();

const {
className,
value,
onChange,
readonly,
} = props;

const onChangeHandler = useCallback((value: string) => {
onChange?.(value as Currency);
}, [onChange]);

return (
<Select
className={classNames('', {}, [className])}
label={t('Choose currency')}
options={options}
value={value}
onChange={onChangeHandler}
readonly={readonly}
/>
);
});
1 change: 1 addition & 0 deletions src/entities/Profile/model/slice/profileSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const profileSlice = createSlice({
state.isLoading = false;
state.data = action.payload;
state.form = action.payload;
state.readonly = true;
},
)
.addCase(updateProfileData.rejected, (state, action) => {
Expand Down
3 changes: 2 additions & 1 deletion src/entities/Profile/model/types/profile.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Country, Currency } from 'shared/const/common';
import { Currency } from 'entities/Currency/model/types/currency';
import { Country } from 'entities/Country';

export interface Profile {
first?: string,
Expand Down
4 changes: 4 additions & 0 deletions src/entities/Profile/ui/ProfileCard/ProfileCard.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@
display: flex;
justify-content: center;
}

.editing {
border: 2px solid var(--inverted-primary-color);
}
27 changes: 25 additions & 2 deletions src/entities/Profile/ui/ProfileCard/ProfileCard.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { classNames, Mods } from 'shared/lib/classNames/classNames';
import { useTranslation } from 'react-i18next';
import { Text, TextAlign, TextTheme } from 'shared/ui/Text/Text';
import { Input } from 'shared/ui/Input/Input';
import { Loader } from 'shared/ui/Loader/Loader';
import { Avatar } from 'shared/ui/Avatar/Avatar';
import { Select } from 'shared/ui/Select/Select';
import { Country, CountrySelect } from 'entities/Country';
import { Currency, CurrencySelect } from '../../../Currency';
import { Profile } from '../../model/types/profile';
import cls from './ProfileCard.module.scss';

Expand All @@ -19,6 +22,8 @@ interface ProfileCardProps {
onChangeCity?: (value?: string) => void
onChangeAvatar?: (value?: string) => void
onChangeUsername?: (value?: string) => void
onChangeCurrency?: (currency: Currency) => void
onChangeCountry?: (country: Country) => void
}

export const ProfileCard = (props: ProfileCardProps) => {
Expand All @@ -34,6 +39,8 @@ export const ProfileCard = (props: ProfileCardProps) => {
onChangeCity,
onChangeAvatar,
onChangeUsername,
onChangeCurrency,
onChangeCountry,
} = props;

const { t } = useTranslation('profile');
Expand All @@ -59,8 +66,12 @@ export const ProfileCard = (props: ProfileCardProps) => {
);
}

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

return (
<div className={classNames(cls.ProfileCard, {}, [className])}>
<div className={classNames(cls.ProfileCard, mods, [className])}>
<div className={cls.header} />
<div className={cls.data}>
{data?.avatar && (
Expand Down Expand Up @@ -113,6 +124,18 @@ export const ProfileCard = (props: ProfileCardProps) => {
onChange={onChangeUsername}
readonly={readonly}
/>
<CurrencySelect
className={cls.input}
value={data?.currency}
onChange={onChangeCurrency}
readonly={readonly}
/>
<CountrySelect
className={cls.input}
value={data?.country}
onChange={onChangeCountry}
readonly={readonly}
/>
</div>
</div>
);
Expand Down
18 changes: 18 additions & 0 deletions src/pages/ProfilePage/ui/ProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
ReducersList,
} from 'shared/lib/components/DynamicModuleLoared/DynamicModuleLoared';
import { useSelector } from 'react-redux';
import { Currency } from 'entities/Currency';
import { Country } from 'entities/Country';
import { ProfilePageHeader } from './ProfilePageHeader/ProfilePageHeader';
import { getProfileIsLoading } from '../../../entities/Profile/model/selectors/getProfileIsLoading/getProfileIsLoading';
import { getProfileError } from '../../../entities/Profile/model/selectors/getProfileError/getProfileError';
Expand Down Expand Up @@ -80,6 +82,20 @@ const ProfilePage = memo(({ className }: ProfilePageProps) => {
[dispatch],
);

const onChangeCurrency = useCallback(
(currency?: Currency) => {
dispatch(profileActions.updateProfile({ currency }));
},
[dispatch],
);

const onChangeCountry = useCallback(
(country?: Country) => {
dispatch(profileActions.updateProfile({ country }));
},
[dispatch],
);

return (
<DynamicModuleLoader reducers={reducers} removeAfterUnmount>
<div className={classNames('', {}, [className])}>
Expand All @@ -94,6 +110,8 @@ const ProfilePage = memo(({ className }: ProfilePageProps) => {
onChangeCity={onChangeCity}
onChangeAvatar={onChangeAvatar}
onChangeUsername={onChangeUsername}
onChangeCurrency={onChangeCurrency}
onChangeCountry={onChangeCountry}
readonly={readonly}
/>
</div>
Expand Down
7 changes: 6 additions & 1 deletion src/shared/config/routeConfig/routeConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { AboutPage } from 'pages/AboutPage';
import { NotFoundPage } from 'pages/NotFoundPage';
import { ProfilePage } from 'pages/ProfilePage';

type AppRoutesProps = RouteProps & {
authOnly?: boolean;
}

export enum AppRoutes {
MAIN = 'main',
ABOUT = 'about',
Expand All @@ -18,7 +22,7 @@ export const RoutePath: Record<AppRoutes, string> = {
[AppRoutes.NOT_FOUND]: '*',
};

export const routeConfig: Record<AppRoutes, RouteProps> = {
export const routeConfig: Record<AppRoutes, AppRoutesProps> = {
[AppRoutes.MAIN]: {
path: RoutePath.main,
element: <MainPage />,
Expand All @@ -30,6 +34,7 @@ export const routeConfig: Record<AppRoutes, RouteProps> = {
[AppRoutes.PROFILE]: {
path: RoutePath.profile,
element: <ProfilePage />,
authOnly: true,
},
[AppRoutes.NOT_FOUND]: {
path: RoutePath.not_found,
Expand Down
13 changes: 0 additions & 13 deletions src/shared/const/common.ts

This file was deleted.

Loading

0 comments on commit 3b46adc

Please sign in to comment.