Skip to content

Commit

Permalink
Merge pull request #13 from ktvtk/module8-task2
Browse files Browse the repository at this point in the history
  • Loading branch information
keksobot authored Dec 2, 2024
2 parents cbb5a7f + cb33d30 commit 3d41dea
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 24 deletions.
5 changes: 5 additions & 0 deletions src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,9 @@ export enum FavoriteStatus {
Add
}

export enum LoadingStatus {
Loading = 'loading' ,
Succeed = 'succeed',
Failed = 'failed'
}

66 changes: 66 additions & 0 deletions src/pages/main-empty-screen/main-empty-screen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {JSX} from 'react';
import MemoizedHeader from '../../components/header/header.tsx';
import {Helmet} from 'react-helmet-async';

export function MainEmptyScreen() : JSX.Element {
return (
<div className="page page--gray page--main">
<Helmet>
<title>6 cities</title>
</Helmet>
<MemoizedHeader />
<main className="page__main page__main--index page__main--index-empty">
<h1 className="visually-hidden">Cities</h1>
<div className="tabs">
<section className="locations container">
<ul className="locations__list tabs__list">
<li className="locations__item">
<a className="locations__item-link tabs__item" href="#">
<span>Paris</span>
</a>
</li>
<li className="locations__item">
<a className="locations__item-link tabs__item" href="#">
<span>Cologne</span>
</a>
</li>
<li className="locations__item">
<a className="locations__item-link tabs__item" href="#">
<span>Brussels</span>
</a>
</li>
<li className="locations__item">
<a className="locations__item-link tabs__item">
<span>Amsterdam</span>
</a>
</li>
<li className="locations__item">
<a className="locations__item-link tabs__item" href="#">
<span>Hamburg</span>
</a>
</li>
<li className="locations__item">
<a className="locations__item-link tabs__item tabs__item--active" href="#">
<span>Dusseldorf</span>
</a>
</li>
</ul>
</section>
</div>
<div className="cities">
<div className="cities__places-container cities__places-container--empty container">
<section className="cities__no-places">
<div className="cities__status-wrapper tabs__content">
<b className="cities__status">No places to stay available</b>
<p className="cities__status-description">We could not find any property available at the moment in
Dusseldorf
</p>
</div>
</section>
<div className="cities__right-section"></div>
</div>
</div>
</main>
</div>
);
}
14 changes: 13 additions & 1 deletion src/pages/main-screen/main-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import {useAppSelector} from '../../hooks';
import {Sorting} from '../../components/sorting/sorting.tsx';
import {SortOption} from '../../types/sort-option.ts';
import MemoizedHeader from '../../components/header/header.tsx';
import {getActiveCity} from '../../store/app-data/selectors.ts';
import {getActiveCity, getLoadingStatus} from '../../store/app-data/selectors.ts';
import {getOffers} from '../../store/offers-data/selectors.ts';
import {LoadingStatus} from '../../const.ts';
import {MainEmptyScreen} from '../main-empty-screen/main-empty-screen.tsx';
import {Loading} from '../../components/loading/loading.tsx';


export function MainScreen(): JSX.Element {
Expand Down Expand Up @@ -37,6 +40,15 @@ export function MainScreen(): JSX.Element {
const handleSortChange = (option: SortOption) => {
setSortingOption(option);
};
const loadingStatus = useAppSelector(getLoadingStatus);

if (loadingStatus !== LoadingStatus.Loading && offers.length === 0){
return <MainEmptyScreen />;
}

if (loadingStatus === LoadingStatus.Loading) {
return <Loading />;
}

return (
<div className="page page--gray page--main">
Expand Down
16 changes: 11 additions & 5 deletions src/pages/offer-screen/offer-screen.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {JSX, useEffect, useMemo} from 'react';
import {Helmet} from 'react-helmet-async';
import {Navigate, useParams} from 'react-router-dom';
import {AuthorizationStatus} from '../../const.ts';
import {AuthorizationStatus, LoadingStatus} from '../../const.ts';
import {Map} from '../../components/map/map.tsx';
import MemoizedReviewList from '../../components/review-list/review-list.tsx';
import {OffersList} from '../../components/offers-list/offers-list.tsx';
Expand All @@ -13,6 +13,7 @@ import {setDetailOffer} from '../../store/detail-offer-data/detail-offer-data.ts
import {getDetailOffer, getNearOffers, getReviews} from '../../store/detail-offer-data/selectors.ts';
import {getAuthoriztionStatus} from '../../store/user-data/selectors.ts';
import MemoizedReviewForm from '../../components/review-form/review-form.tsx';
import {getLoadingStatus} from '../../store/app-data/selectors.ts';

export function OfferScreen() : JSX.Element {
const {id} = useParams();
Expand All @@ -29,15 +30,20 @@ export function OfferScreen() : JSX.Element {
const memoizedNearOffers = useMemo(() => nearOffers.slice(0, 3), [nearOffers]);
const reviews = useAppSelector(getReviews);
const isAuth = useAppSelector(getAuthoriztionStatus) === AuthorizationStatus.Authorized;
const loadingStatus = useAppSelector(getLoadingStatus);

if (offer === null){
return (<Loading />);
if (loadingStatus === LoadingStatus.Loading){
return <Loading />;
}

if (offer === undefined) {
if (loadingStatus === LoadingStatus.Failed && !offer) {
return <Navigate to='*' />;
}

if (!offer) {
return <> </>;
}

return (
<div className="page">
<Helmet>
Expand Down Expand Up @@ -137,7 +143,7 @@ export function OfferScreen() : JSX.Element {
</div>
<Map
city={offer.city}
offers={nearOffers}
offers={memoizedNearOffers}
selectedOffer={undefined}
/>
</section>
Expand Down
4 changes: 0 additions & 4 deletions src/servises/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {getToken} from './token.ts';
import {store} from '../store';
import {AuthorizationStatus} from '../const.ts';
import {setAuthorizationStatus} from '../store/user-data/user-data.ts';
import {setDetailOffer} from '../store/detail-offer-data/detail-offer-data.ts';

const baseURL = 'https://14.design.htmlacademy.pro/six-cities';
const requestTimeout = 5000;
Expand Down Expand Up @@ -39,9 +38,6 @@ export const createAPI = () : AxiosInstance => {
setAuthorizationStatus(AuthorizationStatus.Unauthorized),
);
}
if (error.response && error.response.status === 404) {
store.dispatch(setDetailOffer(undefined));
}
throw error;
},
);
Expand Down
27 changes: 21 additions & 6 deletions src/store/api-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import {createAsyncThunk} from '@reduxjs/toolkit';
import {AppDispatch, State} from '../types/state.ts';
import {AxiosInstance} from 'axios';
import {Offer} from '../types/offer.ts';
import {ApiRoute, AuthorizationStatus} from '../const.ts';
import {ApiRoute, AuthorizationStatus, LoadingStatus} from '../const.ts';
import {DetailOffer} from '../types/detail-offer.ts';
import {Review, ReviewInfo} from '../types/review.ts';
import {saveToken, dropToken, getToken} from '../servises/token.ts';
import {dropToken, getToken, saveToken} from '../servises/token.ts';
import {AuthInfo, LoginInfo} from '../types/user.ts';
import {store} from './index.ts';
import {FavoriteInfo} from '../types/favorite-info.ts';
import {setFavoritesCount, setFavoritesOffers, setOffers, updateOffers} from './offers-data/offers-data.ts';
import {setDetailOffer, setNearOffers, setReviews} from './detail-offer-data/detail-offer-data.ts';
import {saveUserEmail, setAuthorizationStatus} from './user-data/user-data.ts';
import {setLoadingStatus} from './app-data/app-data.ts';

export const fetchOffers = createAsyncThunk<void, undefined, {
dispatch: AppDispatch;
Expand All @@ -20,8 +21,15 @@ export const fetchOffers = createAsyncThunk<void, undefined, {
}>(
'data/fetchOffers',
async (_arg, {dispatch, extra: api}) => {
const {data} = await api.get<Offer[]>(ApiRoute.offers);
dispatch(setOffers(data));
dispatch(setLoadingStatus(LoadingStatus.Loading));
try {
const { data } = await api.get<Offer[]>(ApiRoute.offers);
dispatch(setOffers(data)); // обновляем данные
dispatch(setLoadingStatus(LoadingStatus.Succeed));
} catch (error) {
dispatch(setLoadingStatus(LoadingStatus.Failed));
throw error;
}
}
);

Expand Down Expand Up @@ -59,8 +67,15 @@ export const fetchDetailOffer = createAsyncThunk<void, Offer['id'], {
}>(
'data/fetchDetailOffer',
async (offerId, {dispatch, extra: api}) => {
const {data} = await api.get<DetailOffer>(`${ApiRoute.offers}/${offerId}`);
dispatch(setDetailOffer(data));
dispatch(setLoadingStatus(LoadingStatus.Loading));
try {
const {data} = await api.get<DetailOffer>(`${ApiRoute.offers}/${offerId}`);
dispatch(setDetailOffer(data));
dispatch(setLoadingStatus(LoadingStatus.Succeed));
} catch (error) {
dispatch(setLoadingStatus(LoadingStatus.Failed));
throw error;
}
}
);

Expand Down
10 changes: 7 additions & 3 deletions src/store/app-data/app-data.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {AppData} from '../../types/state.ts';
import {Namespace, Paris} from '../../const.ts';
import {LoadingStatus, Namespace, Paris} from '../../const.ts';
import {createSlice, PayloadAction} from '@reduxjs/toolkit';
import {City} from '../../types/city.ts';

const initialState: AppData = {
city: Paris
city: Paris,
loadingStatus: null
};

export const appData = createSlice({
Expand All @@ -13,8 +14,11 @@ export const appData = createSlice({
reducers: {
changeActiveCity: (state, action: PayloadAction<City>) => {
state.city = action.payload;
},
setLoadingStatus: (state, action: PayloadAction<LoadingStatus>) => {
state.loadingStatus = action.payload;
}
}
});

export const {changeActiveCity} = appData.actions;
export const {changeActiveCity, setLoadingStatus} = appData.actions;
4 changes: 3 additions & 1 deletion src/store/app-data/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {State} from '../../types/state.ts';
import {Namespace} from '../../const.ts';
import {LoadingStatus, Namespace} from '../../const.ts';
import {City} from '../../types/city.ts';

export const getActiveCity = (state: State): City => state[Namespace.App].city;

export const getLoadingStatus = (state: State): LoadingStatus | null => state[Namespace.App].loadingStatus;
2 changes: 1 addition & 1 deletion src/store/detail-offer-data/detail-offer-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const detailOfferData = createSlice({
name: Namespace.DetailOffer,
initialState,
reducers:{
setDetailOffer: (state, action: PayloadAction<DetailOffer | null | undefined>) => {
setDetailOffer: (state, action: PayloadAction<DetailOffer | null>) => {
state.detailOffer = action.payload;
},
setNearOffers: (state, action: PayloadAction<Offer[]>) => {
Expand Down
2 changes: 1 addition & 1 deletion src/store/detail-offer-data/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import {Namespace} from '../../const.ts';
import {Offer} from '../../types/offer.ts';
import {Review} from '../../types/review.ts';

export const getDetailOffer = (state: State): DetailOffer | null | undefined=> state[Namespace.DetailOffer].detailOffer;
export const getDetailOffer = (state: State): DetailOffer | null => state[Namespace.DetailOffer].detailOffer;
export const getNearOffers = (state: State): Offer[] => state[Namespace.DetailOffer].nearOffers;
export const getReviews = (state: State): Review[] => state[Namespace.DetailOffer].reviews;
5 changes: 3 additions & 2 deletions src/types/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ import {City} from './city.ts';
import {DetailOffer} from './detail-offer.ts';
import {Offer} from './offer.ts';
import {Review} from './review.ts';
import {AuthorizationStatus} from '../const.ts';
import {AuthorizationStatus, LoadingStatus} from '../const.ts';

export type State = ReturnType<typeof store.getState>;

export type AppDispatch = typeof store.dispatch;

export type AppData = {
city: City;
loadingStatus: LoadingStatus | null;
};

export type DetailOfferData = {
detailOffer: DetailOffer | null | undefined;
detailOffer: DetailOffer | null;
nearOffers: Offer[];
reviews: Review[];
};
Expand Down

0 comments on commit 3d41dea

Please sign in to comment.