diff --git a/package-lock.json b/package-lock.json
index 0ad8646..bda9a2c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,7 +18,8 @@
"react-dom": "18.2.0",
"react-helmet-async": "1.3.0",
"react-redux": "8.1.3",
- "react-router-dom": "6.16.0"
+ "react-router-dom": "6.16.0",
+ "react-toastify": "^10.0.6"
},
"devDependencies": {
"@jedmao/redux-mock-store": "3.0.5",
@@ -2305,6 +2306,14 @@
"node": ">=0.8.0"
}
},
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -2347,9 +2356,9 @@
"dev": true
},
"node_modules/cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"dependencies": {
"path-key": "^3.1.0",
@@ -5227,6 +5236,18 @@
"react-dom": ">=16.8"
}
},
+ "node_modules/react-toastify": {
+ "version": "10.0.6",
+ "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.6.tgz",
+ "integrity": "sha512-yYjp+omCDf9lhZcrZHKbSq7YMuK0zcYkDFTzfRFgTXkTFHZ1ToxwAonzA4JI5CxA91JpjFLmwEsZEgfYfOqI1A==",
+ "dependencies": {
+ "clsx": "^2.1.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
"node_modules/read-pkg": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -5476,9 +5497,9 @@
}
},
"node_modules/rollup": {
- "version": "3.29.4",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
- "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
+ "version": "3.29.5",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz",
+ "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
diff --git a/package.json b/package.json
index 5a67c5f..c9d4c81 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,8 @@
"react-dom": "18.2.0",
"react-helmet-async": "1.3.0",
"react-redux": "8.1.3",
- "react-router-dom": "6.16.0"
+ "react-router-dom": "6.16.0",
+ "react-toastify": "^10.0.6"
},
"devDependencies": {
"@jedmao/redux-mock-store": "3.0.5",
diff --git a/src/browser-history.ts b/src/browser-history.ts
new file mode 100644
index 0000000..55c728c
--- /dev/null
+++ b/src/browser-history.ts
@@ -0,0 +1,3 @@
+import {createBrowserHistory} from 'history';
+
+export const browserHistory = createBrowserHistory();
diff --git a/src/components/app/app.tsx b/src/components/app/app.tsx
index 3ea609d..6e968ef 100644
--- a/src/components/app/app.tsx
+++ b/src/components/app/app.tsx
@@ -1,57 +1,61 @@
import {MainPage} from '../../pages/main-page/main-page';
-import {BrowserRouter, Route, Routes} from 'react-router-dom';
+import {Route, Routes} from 'react-router-dom';
import {HelmetProvider} from 'react-helmet-async';
import {AppRoute} from '../../const.ts';
import {LoginPage} from '../../pages/login-page/login-page.tsx';
import {PrivateRoute} from '../private-route/private-route.tsx';
import {OfferPage} from '../../pages/offer-page/offer-page.tsx';
import {NotFoundPage} from '../../pages/404-not-found-page/not-found-page.tsx';
-import {AuthorizationStatus} from '../../const.ts';
import {FavoritesPage} from '../../pages/favorites-page/favorites-page.tsx';
import {useAppSelector} from '../../hooks';
import {Spinner} from '../spinner/spinner.tsx';
+import {HistoryRouter} from '../history-route/history-route.tsx';
+import {browserHistory} from '../../browser-history.ts';
+import {PrivateLoginRoute} from '../private-login-route/private-login-route.tsx';
-export function App() : JSX.Element {
- const isOffersDataLoading = useAppSelector((state) => state.isOffersDataLoading);
+export function App(): JSX.Element {
+ const isOffersDataLoading = useAppSelector((state) => state.isDataLoading);
if (isOffersDataLoading) {
return (
-
+
);
}
return (
-
+
}
+ element={}
/>
}
+ element={
+
+
+
+ }
/>
-
+
+
}
/>
}
+ element={}
/>
}
+ element={}
/>
-
+
);
}
diff --git a/src/components/custom-toast/custom-toast.tsx b/src/components/custom-toast/custom-toast.tsx
new file mode 100644
index 0000000..af0b0b5
--- /dev/null
+++ b/src/components/custom-toast/custom-toast.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import { ToastContainer, toast } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+
+const CustomToast: React.FC<{ message: string }> = ({ message }) => (
+
+ {message}
+
+);
+
+export const showCustomToast = (message: string) => {
+ toast();
+};
+
+export function CustomToastContainer() {
+ return (
+
+ );
+}
diff --git a/src/components/favorite-card/favorite-card.tsx b/src/components/favorite-card/favorite-card.tsx
index 5a8d42e..c812463 100644
--- a/src/components/favorite-card/favorite-card.tsx
+++ b/src/components/favorite-card/favorite-card.tsx
@@ -8,14 +8,14 @@ type FavoriteCardProps = {
export function FavoriteCard({offer}: FavoriteCardProps): JSX.Element {
return (
-
+
{offer.isPremium &&
Premium
}
-
+
diff --git a/src/components/favorites-list/favorites-list.tsx b/src/components/favorites-list/favorites-list.tsx
index 1251c2d..dc92f87 100644
--- a/src/components/favorites-list/favorites-list.tsx
+++ b/src/components/favorites-list/favorites-list.tsx
@@ -7,15 +7,14 @@ type FavoritesListProps = {
export function FavoritesList({offers} : FavoritesListProps) {
const groupedOffers = offers.reduce((acc, offer) => {
- if (offer.isFavorite) {
- const cityName = offer.city.name;
+ const cityName = offer.city.name;
- if (!acc[cityName]) {
- acc[cityName] = [];
- }
-
- acc[cityName].push(offer);
+ if (!acc[cityName]) {
+ acc[cityName] = [];
}
+
+ acc[cityName].push(offer);
+
return acc;
}, {} as Record
);
diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx
new file mode 100644
index 0000000..38d6ac5
--- /dev/null
+++ b/src/components/header/header.tsx
@@ -0,0 +1,62 @@
+import {useAppDispatch, useAppSelector} from '../../hooks';
+import {Link, useLocation} from 'react-router-dom';
+import {AppRoute} from '../../const.ts';
+import {logoutAction} from '../../store/api-actions.ts';
+
+export function Header() {
+ const dispatch = useAppDispatch();
+ const userData = useAppSelector((state) => state.userData);
+ const favoriteOffers = useAppSelector((state) => state.favoriteOffers);
+ const location = useLocation();
+
+ const isLoginPage = location.pathname === AppRoute.Login as string;
+
+ return (
+
+
+
+
+ {!isLoginPage && (
+
+ )}
+
+
+
+ );
+}
diff --git a/src/components/history-route/history-route.tsx b/src/components/history-route/history-route.tsx
new file mode 100644
index 0000000..58e52df
--- /dev/null
+++ b/src/components/history-route/history-route.tsx
@@ -0,0 +1,33 @@
+import React, {useState, useLayoutEffect} from 'react';
+import {Router} from 'react-router-dom';
+import type {BrowserHistory} from 'history';
+
+export interface HistoryRouterProps {
+ history: BrowserHistory;
+ basename?: string;
+ children?: React.ReactNode;
+}
+
+export function HistoryRouter({
+ basename,
+ children,
+ history,
+}: HistoryRouterProps) {
+ const [state, setState] = useState({
+ action: history.action,
+ location: history.location,
+ });
+
+ useLayoutEffect(() => history.listen(setState), [history]);
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/login-form/login-form.tsx b/src/components/login-form/login-form.tsx
new file mode 100644
index 0000000..cb68719
--- /dev/null
+++ b/src/components/login-form/login-form.tsx
@@ -0,0 +1,64 @@
+import {FormEvent, useRef} from 'react';
+import {useAppDispatch} from '../../hooks';
+import {loginAction} from '../../store/api-actions.ts';
+import {showCustomToast} from '../custom-toast/custom-toast.tsx';
+
+const validatePassword = (password: string): boolean => {
+ const hasLetter = /[a-zA-Z]/.test(password);
+ const hasNumber = /\d/.test(password);
+ const hasNoSpaces = !/\s/.test(password);
+
+ return hasLetter && hasNumber && hasNoSpaces;
+};
+
+export function LoginForm() {
+ const loginFormRef = useRef(null);
+
+ const dispatch = useAppDispatch();
+
+ const handleSubmit = (evt: FormEvent) => {
+ evt.preventDefault();
+
+ if (!loginFormRef.current) {
+ return;
+ }
+
+ const formData = new FormData(loginFormRef.current);
+ const email = formData.get('email') as string;
+ const password = formData.get('password') as string;
+
+ if (!validatePassword(password)) {
+ showCustomToast('Password must contain at least one letter, one number, and no spaces.');
+ return;
+ }
+
+ if (email && password) {
+ dispatch(loginAction({
+ email,
+ password
+ }));
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/src/components/private-login-route/private-login-route.tsx b/src/components/private-login-route/private-login-route.tsx
new file mode 100644
index 0000000..2b6b587
--- /dev/null
+++ b/src/components/private-login-route/private-login-route.tsx
@@ -0,0 +1,17 @@
+import {Navigate} from 'react-router-dom';
+import {AppRoute, AuthorizationStatus} from '../../const';
+import {useAppSelector} from '../../hooks';
+
+type PrivateLoginRouteProps = {
+ children: JSX.Element;
+}
+
+export function PrivateLoginRoute({children}: PrivateLoginRouteProps): JSX.Element {
+ const authorizationStatus = useAppSelector((state) => state.authorizationStatus);
+
+ return (
+ authorizationStatus !== AuthorizationStatus.Auth
+ ? children
+ :
+ );
+}
diff --git a/src/components/private-route/private-route.tsx b/src/components/private-route/private-route.tsx
index 9a56759..145ed40 100644
--- a/src/components/private-route/private-route.tsx
+++ b/src/components/private-route/private-route.tsx
@@ -1,12 +1,14 @@
import {Navigate} from 'react-router-dom';
import {AppRoute, AuthorizationStatus} from '../../const';
+import {useAppSelector} from '../../hooks';
type PrivateRouteProps = {
- authorizationStatus: AuthorizationStatus;
children: JSX.Element;
}
-export function PrivateRoute({authorizationStatus, children}: PrivateRouteProps): JSX.Element {
+export function PrivateRoute({children}: PrivateRouteProps): JSX.Element {
+ const authorizationStatus = useAppSelector((state) => state.authorizationStatus);
+
return (
authorizationStatus === AuthorizationStatus.Auth
? children
diff --git a/src/const.ts b/src/const.ts
index d51bb60..a4fd354 100644
--- a/src/const.ts
+++ b/src/const.ts
@@ -5,11 +5,14 @@ export enum AppRoute {
Login = '/login',
Favorites = '/favorites',
Offer = '/offer',
- OfferWithId = '/offer/:id'
+ OfferWithId = '/offer/:id',
}
export enum APIRoute {
- Offers = '/offers'
+ Offers = '/offers',
+ Login = '/login',
+ Logout = '/logout',
+ Favorite = '/favorite',
}
export enum AuthorizationStatus {
@@ -24,5 +27,5 @@ export const SORTING_OPTIONS : SortingOption[] = [
'Popular',
'Price: low to high',
'Price: high to low',
- 'Top rated first'
+ 'Top rated first',
];
diff --git a/src/index.tsx b/src/index.tsx
index 76b3513..2aff7f2 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -3,8 +3,10 @@ import ReactDOM from 'react-dom/client';
import {App} from './components/app/app';
import {Provider} from 'react-redux';
import {store} from './store';
-import {fetchOffersAction} from './store/api-actions.ts';
+import {checkAuthAction, fetchOffersAction} from './store/api-actions.ts';
+import {CustomToastContainer} from './components/custom-toast/custom-toast.tsx';
+store.dispatch(checkAuthAction());
store.dispatch(fetchOffersAction());
const root = ReactDOM.createRoot(
@@ -14,6 +16,7 @@ const root = ReactDOM.createRoot(
root.render(
+
diff --git a/src/pages/favorites-page/favorites-page.tsx b/src/pages/favorites-page/favorites-page.tsx
index a3f598c..e6f07d0 100644
--- a/src/pages/favorites-page/favorites-page.tsx
+++ b/src/pages/favorites-page/favorites-page.tsx
@@ -1,49 +1,23 @@
import {Helmet} from 'react-helmet-async';
import {FavoritesList} from '../../components/favorites-list/favorites-list.tsx';
import {useAppSelector} from '../../hooks';
+import {Header} from '../../components/header/header.tsx';
export function FavoritesPage() : JSX.Element {
- const offers = useAppSelector((state) => state.offers);
+ const favoriteOffers = useAppSelector((state) => state.favoriteOffers);
return (
Favorites - 6 cities
-
+
diff --git a/src/pages/login-page/login-page.tsx b/src/pages/login-page/login-page.tsx
index 22c7f25..193cfcd 100644
--- a/src/pages/login-page/login-page.tsx
+++ b/src/pages/login-page/login-page.tsx
@@ -1,4 +1,6 @@
import {Helmet} from 'react-helmet-async';
+import {LoginForm} from '../../components/login-form/login-form.tsx';
+import {Header} from '../../components/header/header.tsx';
export function LoginPage() : JSX.Element {
return (
@@ -6,34 +8,11 @@ export function LoginPage() : JSX.Element {
Log In - 6 cities
-
+
-
+
diff --git a/src/pages/main-page/main-page.tsx b/src/pages/main-page/main-page.tsx
index a930735..80ab7ba 100644
--- a/src/pages/main-page/main-page.tsx
+++ b/src/pages/main-page/main-page.tsx
@@ -9,6 +9,7 @@ import {setCity} from '../../store/action.ts';
import {useMemo, useState} from 'react';
import {SortingOption} from '../../types/sorting-option.ts';
import {SortingOptions} from '../../components/sorting-options/sorting-options.tsx';
+import {Header} from '../../components/header/header.tsx';
function getPlacesText(count: number): string {
if (count === 1) {
@@ -64,33 +65,7 @@ export function MainPage(): JSX.Element {
6 cities
-
+
Cities
diff --git a/src/services/api.ts b/src/services/api.ts
index e36fec6..b42a85c 100644
--- a/src/services/api.ts
+++ b/src/services/api.ts
@@ -1,9 +1,54 @@
-import axios, {AxiosInstance} from 'axios';
+import axios, {AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestConfig} from 'axios';
+import {getToken} from './token.ts';
+import {StatusCodes} from 'http-status-codes';
+import {showCustomToast} from '../components/custom-toast/custom-toast.tsx';
+
+type DetailMessageType = {
+ type: string;
+ message: string;
+}
+
+const StatusCodeMapping: Record = {
+ [StatusCodes.BAD_REQUEST]: true,
+ [StatusCodes.UNAUTHORIZED]: true,
+ [StatusCodes.NOT_FOUND]: true
+};
+
+const shouldDisplayError = (response: AxiosResponse) => StatusCodeMapping[response.status];
const BACKEND_URL = 'https://14.design.htmlacademy.pro/six-cities';
const REQUEST_TIMEOUT = 5000;
-export const createAPI = (): AxiosInstance => axios.create({
- baseURL: BACKEND_URL,
- timeout: REQUEST_TIMEOUT,
-});
+export const createAPI = (): AxiosInstance => {
+ const api = axios.create({
+ baseURL: BACKEND_URL,
+ timeout: REQUEST_TIMEOUT,
+ });
+
+ api.interceptors.request.use(
+ (config: InternalAxiosRequestConfig) => {
+ const token = getToken();
+
+ if (token && config.headers) {
+ config.headers['x-token'] = token;
+ }
+
+ return config;
+ },
+ );
+
+ api.interceptors.response.use(
+ (response) => response,
+ (error: AxiosError) => {
+ if (error.response && shouldDisplayError(error.response)) {
+ const detailMessage = (error.response.data);
+
+ showCustomToast(detailMessage.message);
+ }
+
+ throw error;
+ }
+ );
+
+ return api;
+};
diff --git a/src/services/token.ts b/src/services/token.ts
new file mode 100644
index 0000000..df439d4
--- /dev/null
+++ b/src/services/token.ts
@@ -0,0 +1,16 @@
+const AUTH_TOKEN_KEY_NAME = 'six-cities-token';
+
+export type Token = string;
+
+export const getToken = (): Token => {
+ const token = localStorage.getItem(AUTH_TOKEN_KEY_NAME);
+ return token ?? '';
+};
+
+export const saveToken = (token: Token): void => {
+ localStorage.setItem(AUTH_TOKEN_KEY_NAME, token);
+};
+
+export const dropToken = (): void => {
+ localStorage.removeItem(AUTH_TOKEN_KEY_NAME);
+};
diff --git a/src/store/action.ts b/src/store/action.ts
index 6176f11..19de4fe 100644
--- a/src/store/action.ts
+++ b/src/store/action.ts
@@ -1,8 +1,18 @@
import { createAction } from '@reduxjs/toolkit';
import {Offer} from '../types/offer.ts';
+import {AppRoute, AuthorizationStatus} from '../const.ts';
+import {UserData} from '../types/user.ts';
export const setCity = createAction('city/setCity');
export const setOffers = createAction('offers/setOffers');
-export const setOffersDataLoadingStatus = createAction('data/setOffersDataLoadingStatus');
+export const setDataLoadingStatus = createAction('data/setDataLoadingStatus');
+
+export const requireAuthorization = createAction('user/requireAuthorization');
+
+export const setUserData = createAction('user/setUserData');
+
+export const setFavoriteOffers = createAction('user/setFavoriteOffers');
+
+export const redirectToRoute = createAction('auth/redirectToRoute');
diff --git a/src/store/api-actions.ts b/src/store/api-actions.ts
index ccc0ad7..31a3e55 100644
--- a/src/store/api-actions.ts
+++ b/src/store/api-actions.ts
@@ -2,19 +2,90 @@ import {AxiosInstance} from 'axios';
import {AppDispatch, State} from '../types/state.ts';
import {createAsyncThunk} from '@reduxjs/toolkit';
import {Offer} from '../types/offer.ts';
-import {APIRoute} from '../const.ts';
-import {setOffers, setOffersDataLoadingStatus} from './action.ts';
+import {APIRoute, AppRoute, AuthorizationStatus} from '../const.ts';
+import {
+ redirectToRoute,
+ requireAuthorization,
+ setOffers,
+ setDataLoadingStatus,
+ setUserData,
+ setFavoriteOffers
+} from './action.ts';
+import {dropToken, saveToken} from '../services/token.ts';
+import {AuthData, UserData} from '../types/user.ts';
export const fetchOffersAction = createAsyncThunk(
- 'data/fetchQuestions',
+ 'data/fetchOffers',
async (_arg, {dispatch, extra: api}) => {
- dispatch(setOffersDataLoadingStatus(true));
+ dispatch(setDataLoadingStatus(true));
const {data} = await api.get(APIRoute.Offers);
- dispatch(setOffersDataLoadingStatus(false));
+ dispatch(setDataLoadingStatus(false));
dispatch(setOffers(data));
},
);
+
+export const fetchFavoriteOffersAction = createAsyncThunk(
+ 'data/fetchFavoriteOffers',
+ async (_arg, {dispatch, extra: api}) => {
+ const {data} = await api.get(APIRoute.Favorite);
+ dispatch(setFavoriteOffers(data));
+ },
+);
+
+export const checkAuthAction = createAsyncThunk(
+ 'user/checkAuth',
+ async (_arg, {dispatch, extra: api}) => {
+ try {
+ const response = await api.get(APIRoute.Login);
+ dispatch(requireAuthorization(AuthorizationStatus.Auth));
+ dispatch(setUserData(response.data));
+ dispatch(fetchFavoriteOffersAction());
+ } catch (error) {
+ dispatch(requireAuthorization(AuthorizationStatus.NoAuth));
+ }
+ },
+);
+
+export const loginAction = createAsyncThunk(
+ 'user/login',
+ async ({email, password}, {dispatch, extra: api}) => {
+ const response = await api.post(APIRoute.Login, {email, password});
+ saveToken(response.data.token);
+ dispatch(requireAuthorization(AuthorizationStatus.Auth));
+ dispatch(setUserData(response.data));
+ dispatch(fetchOffersAction());
+ dispatch(fetchFavoriteOffersAction());
+ dispatch(redirectToRoute(AppRoute.Main));
+ },
+);
+
+export const logoutAction = createAsyncThunk(
+ 'user/logout',
+ async (_arg, {dispatch, extra: api}) => {
+ await api.delete(APIRoute.Logout);
+ dropToken();
+ dispatch(requireAuthorization(AuthorizationStatus.NoAuth));
+ dispatch(setUserData(null));
+ dispatch(setFavoriteOffers([]));
+ },
+);
diff --git a/src/store/index.ts b/src/store/index.ts
index 7ef91d2..e75099b 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -1,6 +1,7 @@
import { configureStore } from '@reduxjs/toolkit';
import { reducer } from './reducer';
import {createAPI} from '../services/api';
+import {redirect} from './middlewares/redirect.ts';
export const api = createAPI();
@@ -11,5 +12,5 @@ export const store = configureStore({
thunk: {
extraArgument: api,
},
- }),
+ }).concat(redirect),
});
diff --git a/src/store/middlewares/redirect.ts b/src/store/middlewares/redirect.ts
new file mode 100644
index 0000000..902f42a
--- /dev/null
+++ b/src/store/middlewares/redirect.ts
@@ -0,0 +1,17 @@
+import {PayloadAction} from '@reduxjs/toolkit';
+import {browserHistory} from '../../browser-history';
+import {Middleware} from 'redux';
+import {reducer} from '../reducer';
+
+type Reducer = ReturnType;
+
+export const redirect: Middleware =
+ () =>
+ (next) =>
+ (action: PayloadAction) => {
+ if (action.type === 'auth/redirectToRoute') {
+ browserHistory.push(action.payload);
+ }
+
+ return next(action);
+ };
diff --git a/src/store/reducer.ts b/src/store/reducer.ts
index fd51a3d..25c8347 100644
--- a/src/store/reducer.ts
+++ b/src/store/reducer.ts
@@ -1,17 +1,25 @@
import { createReducer } from '@reduxjs/toolkit';
-import {setCity, setOffers, setOffersDataLoadingStatus} from './action';
+import {requireAuthorization, setCity, setOffers, setDataLoadingStatus, setUserData, setFavoriteOffers} from './action';
import {Offer} from '../types/offer.ts';
+import {AuthorizationStatus} from '../const.ts';
+import {UserData} from '../types/user.ts';
type InitialState = {
activeCity: string;
offers: Offer[];
- isOffersDataLoading: boolean;
+ favoriteOffers: Offer[];
+ isDataLoading: boolean;
+ authorizationStatus: AuthorizationStatus;
+ userData: UserData | null;
}
const initialState: InitialState = {
activeCity: 'Paris',
offers: [],
- isOffersDataLoading: false
+ favoriteOffers: [],
+ isDataLoading: false,
+ authorizationStatus: AuthorizationStatus.Unknown,
+ userData: null,
};
export const reducer = createReducer(initialState, (builder) => {
@@ -22,7 +30,16 @@ export const reducer = createReducer(initialState, (builder) => {
.addCase(setOffers, (state, action) => {
state.offers = action.payload;
})
- .addCase(setOffersDataLoadingStatus, (state, action) => {
- state.isOffersDataLoading = action.payload;
+ .addCase(setFavoriteOffers, (state, action) => {
+ state.favoriteOffers = action.payload;
+ })
+ .addCase(setDataLoadingStatus, (state, action) => {
+ state.isDataLoading = action.payload;
+ })
+ .addCase(requireAuthorization, (state, action) => {
+ state.authorizationStatus = action.payload;
+ })
+ .addCase(setUserData, (state, action) => {
+ state.userData = action.payload;
});
});
diff --git a/src/types/user.ts b/src/types/user.ts
index 9a1670e..2b5d4bf 100644
--- a/src/types/user.ts
+++ b/src/types/user.ts
@@ -3,3 +3,16 @@ export type User = {
avatarUrl: string;
isPro: boolean;
}
+
+export type UserData = {
+ name: string;
+ avatarUrl: string;
+ isPro: boolean;
+ email: string;
+ token: string;
+}
+
+export type AuthData = {
+ email: string;
+ password: string;
+}