setActiveOfferId(offer.id)}
diff --git a/src/components/loading-screen/loading-screen.tsx b/src/components/loading-screen/loading-screen.tsx
new file mode 100644
index 0000000..8c16a23
--- /dev/null
+++ b/src/components/loading-screen/loading-screen.tsx
@@ -0,0 +1,8 @@
+
+function LoadingScreen(): JSX.Element {
+ return (
+
Loading ...
+ );
+}
+
+export default LoadingScreen;
diff --git a/src/components/spinner/spinner.modules.css b/src/components/spinner/spinner.modules.css
new file mode 100644
index 0000000..dbdb43b
--- /dev/null
+++ b/src/components/spinner/spinner.modules.css
@@ -0,0 +1,35 @@
+.spinner-container {
+ display: flex;
+}
+.loader {
+ width: 48px;
+ height: 48px;
+ margin-left: auto;
+ margin-right: auto;
+ border-radius: 50%;
+ display: inline-block;
+ border-top: 4px solid #000;
+ border-right: 4px solid transparent;
+ box-sizing: border-box;
+ animation: rotation 1s linear infinite;
+}
+.loader::after {
+ content: '';
+ box-sizing: border-box;
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 48px;
+ height: 48px;
+ border-radius: 50%;
+ border-bottom: 4px solid #4481c3;
+ border-left: 4px solid transparent;
+}
+@keyframes rotation {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/components/spinner/spinner.tsx b/src/components/spinner/spinner.tsx
new file mode 100644
index 0000000..831038b
--- /dev/null
+++ b/src/components/spinner/spinner.tsx
@@ -0,0 +1,5 @@
+import './spinner.modules.css';
+
+export default function Spinner(): JSX.Element {
+ return
;
+}
diff --git a/src/const.ts b/src/const.ts
index 1b7e76b..485f151 100644
--- a/src/const.ts
+++ b/src/const.ts
@@ -3,3 +3,7 @@ export const URL_MARKER_DEFAULT =
export const URL_MARKER_CURRENT =
'https://assets.htmlacademy.ru/content/intensive/javascript-1/demo/interactive-map/main-pin.svg';
+
+export enum APIRoute {
+ Offers = '/offers',
+}
diff --git a/src/index.tsx b/src/index.tsx
index a850ac6..15f3da6 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -4,6 +4,10 @@ import {App} from './App';
import { Provider } from 'react-redux';
import { store } from './store';
+import {fetchOfferObjectAction} from './api-actions';
+
+store.dispatch(fetchOfferObjectAction);
+
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
diff --git a/src/reducer.ts b/src/reducer.ts
index 71c99c5..f11e7bd 100644
--- a/src/reducer.ts
+++ b/src/reducer.ts
@@ -1,8 +1,10 @@
-import { createReducer } from '@reduxjs/toolkit';
+import { combineReducers, createReducer } from '@reduxjs/toolkit';
import { City, OfferObject } from './types/types';
-import { changeCity, AddOffer } from './action';
+import { changeCity, AddOffer, loadOffers } from './action';
+import { offerPage } from './store/offer-data';
import { CITYLIST } from './mock/cities';
import { offers } from './mock/offers';
+
type InitialState = {
currentCity: City;
cities: City[];
@@ -24,5 +26,14 @@ export const reducer = createReducer(initialState, (builder) => {
})
.addCase(AddOffer, (state, action) => {
state.offers = action.payload;
+ })
+ .addCase(loadOffers, (state, action) => {
+ state.offers = action.payload;
});
});
+
+export const rootReducer = combineReducers({
+ Cities: reducer,
+ currentCity: reducer,
+ offerPage: offerPage.reducer,
+});
diff --git a/src/store/index.ts b/src/store/index.ts
index 320da04..0532eda 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -1,4 +1,15 @@
import { configureStore } from '@reduxjs/toolkit';
-import { reducer } from '../reducer';
+import { rootReducer } from '../reducer';
+import { createAPI } from '../api';
-export const store = configureStore({ reducer });
+export const api = createAPI();
+
+export const store = configureStore({
+ reducer: rootReducer,
+ middleware: (getDefaultMiddleware) =>
+ getDefaultMiddleware({
+ thunk: {
+ extraArgument: api,
+ },
+ }),
+});
diff --git a/src/store/offer-data.ts b/src/store/offer-data.ts
new file mode 100644
index 0000000..841ade1
--- /dev/null
+++ b/src/store/offer-data.ts
@@ -0,0 +1,32 @@
+import { createSlice } from '@reduxjs/toolkit';
+import { OfferData } from '../types/types';
+import { fetchOfferObjectAction } from '../api-actions';
+
+const initialState: OfferData = {
+ offer: null,
+ offerPageStatus: false,
+};
+
+export const offerPage = createSlice({
+ name: 'offerPage',
+ initialState,
+ reducers: {
+ unmountOffer: (state) => {
+ state.offer = null;
+ },
+ },
+ extraReducers(builder) {
+ builder
+ .addCase(fetchOfferObjectAction.pending, (state) => {
+ state.offerPageStatus = true;
+ })
+ .addCase(fetchOfferObjectAction.fulfilled, (state, action) => {
+ state.offer = action.payload;
+ state.offerPageStatus = false;
+ })
+ .addCase(fetchOfferObjectAction.rejected, (state) => {
+ state.offerPageStatus = false;
+ });
+ },
+});
+export const { unmountOffer } = offerPage.actions;
diff --git a/src/store/selector.ts b/src/store/selector.ts
new file mode 100644
index 0000000..fd00cb0
--- /dev/null
+++ b/src/store/selector.ts
@@ -0,0 +1,12 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { OfferData, State } from '../types/types';
+
+export const getOffer = createSelector(
+ (state: State) => state['offerPage'],
+ (state: OfferData) => state.offer
+);
+
+export const getLoadingOfferPage = createSelector(
+ (state: State) => state['offerPage'],
+ (state: OfferData) => state.offerPageStatus
+);
diff --git a/src/token.ts b/src/token.ts
new file mode 100644
index 0000000..ee97828
--- /dev/null
+++ b/src/token.ts
@@ -0,0 +1,16 @@
+const AUTH_TOKEN_KEY_NAME = 'guess-melody-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/types/types.ts b/src/types/types.ts
index d15929e..2b03a01 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -4,7 +4,10 @@ export type City = {
lat: number;
lng: number;
};
-
+export type OfferData = {
+ offer: OfferObject[] | null;
+ offerPageStatus: boolean;
+};
export type Point = {
title: string;
lat: number;