diff --git a/src/App.tsx b/src/App.tsx index 6d41378..0f86c8b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,24 +1,36 @@ +import { useEffect } from 'react'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import { HelmetProvider } from 'react-helmet-async'; import MainPage from './components/MainPage/MainPage'; import Favorite from './components/Favorites/Favorite'; import LoginPage from './components/Login/LoginPage'; +import NotFoundPage from './components/NotFoundPage/NotFoundPage'; import Offer from './components/Offer/Offer'; -import { useAppSelector } from './hooks'; +import { useAppSelector,useAppDispatch } from './hooks'; import { REVIEWERS } from './mock/reviewers'; +import { fetchOfferObjectAction } from './api-actions.ts'; //import LoadingScreen from './components/loading-screen/loading-screen'; export const App: React.FC = () => { + const dispatch = useAppDispatch(); + + //const isLoading = useAppSelector(getLoadingOfferPage); const currentCity = useAppSelector((state) => state.currentCity); const offers = useAppSelector((state) => state.offerPage); const cities = useAppSelector((state) => state.Cities); + useEffect(() => { + dispatch(fetchOfferObjectAction()); + }, [dispatch]); + // eslint-disable-next-line no-unused-expressions + + const offerdetails = useAppSelector((state) => state.offerIdDetails); return ( - } /> + } /> } /> - } /> + o.id === offerdetails.offer.id).length) > 0 ? : } /> } /> diff --git a/src/action.ts b/src/action.ts index 714ffb3..e484034 100644 --- a/src/action.ts +++ b/src/action.ts @@ -1,7 +1,9 @@ import { createAction } from '@reduxjs/toolkit'; -import { OfferObject } from './types/types'; +import { OfferObject, OfferIdDetails } from './types/types'; export const changeCity = createAction('ChangeCity'); export const AddOffer = createAction('AddOffer'); export const loadOffers = createAction('data/fetchOffers'); + +export const loadOfferDetails = createAction('data/loadOffer'); diff --git a/src/api-actions.ts b/src/api-actions.ts index 17c2a8d..df5c6d9 100644 --- a/src/api-actions.ts +++ b/src/api-actions.ts @@ -1,7 +1,13 @@ /* eslint-disable @typescript-eslint/no-shadow */ import { AxiosInstance } from 'axios'; -import { createAsyncThunk } from '@reduxjs/toolkit'; -import { AppDispatch, State } from './types/types'; +import { createAction, createAsyncThunk } from '@reduxjs/toolkit'; +import { + AppDispatch, + OfferIdDetails, + State, + UserReview, + UserReviewPost, +} from './types/types'; import { OfferObject } from './types/types'; //import {redirectToRoute} from './action'; //import { saveToken, dropToken } from './token'; @@ -12,6 +18,10 @@ import { createAPI } from './api'; import { dropToken, saveToken } from './token'; export const api = createAPI(); +export const loadOfferNearby = createAction( + 'data/loadOfferNearby' +); +export const loadComments = createAction('data/loadComments'); export const fetchOfferObjectAction = createAsyncThunk< OfferObject[], undefined, @@ -24,18 +34,18 @@ export const fetchOfferObjectAction = createAsyncThunk< const { data } = await api.get(APIRoute.Offers); return data; }); -/* -export const checkAuthAction = createAsyncThunk( - 'user/checkAuth', - async (_arg, {extra: api}) => { - await api.get(APIRoute.Login); - }, -); -*/ +export const fetchOffer = createAsyncThunk< + OfferIdDetails, + string, + { + // dispatch: AppDispatch; + // state: State; + extra: AxiosInstance; + } +>('data/fetchOfferId', async (id, { extra: api }) => { + const { data } = await api.get(`${APIRoute.Offers}/${id}`); + return data; +}); export const login = createAsyncThunk< UserAuth, LoginAuth, @@ -70,3 +80,47 @@ export const logout = createAsyncThunk< await api.delete(APIRoute.Logout); dropToken(); }); + +export const fetchOfferNeibourhood = createAsyncThunk< + void, + string, + { + dispatch: AppDispatch; + state: State; + extra: AxiosInstance; + } +>('data/fetchOfferNearby', async (id, { dispatch, extra: api }) => { + const { data } = await api.get( + `${APIRoute.Offers}/${id}/nearby` + ); + dispatch(loadOfferNearby(data)); +}); + +export const fetchComments = createAsyncThunk< + void, + string, + { + dispatch: AppDispatch; + state: State; + extra: AxiosInstance; + } +>('data/fetchComments', async (id, { dispatch, extra: api }) => { + const { data } = await api.get(`${APIRoute.Comments}/${id}`); + dispatch(loadComments(data)); +}); + +export const postComment = createAsyncThunk< + void, + UserReviewPost, + { + dispatch: AppDispatch; + state: State; + extra: AxiosInstance; + } +>('post/Comment', async ({ comment, rating, id }, { dispatch, extra: api }) => { + const { data } = await api.post(`${APIRoute.Comments}/${id}`, { + comment, + rating, + }); + dispatch(loadComments(data)); +}); diff --git a/src/components/MainPage/MainPage.tsx b/src/components/MainPage/MainPage.tsx index 4f2eab6..3c8ed1f 100644 --- a/src/components/MainPage/MainPage.tsx +++ b/src/components/MainPage/MainPage.tsx @@ -1,36 +1,36 @@ import {FC} from 'react'; -import { useState, useEffect } from 'react'; +import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import Spinner from '../spinner/spinner.tsx'; +//import Spinner from '../spinner/spinner.tsx'; import OfferList from '../Offer/OfferList'; -import { useAppDispatch,useAppSelector } from '../../hooks'; -import { AppRoute, City, CardCssNameList, SortName} from '../../types/types'; +import { useAppDispatch } from '../../hooks'; +import { AppRoute, City, CardCssNameList, SortName, OfferObject} from '../../types/types'; import { changeCity } from '../../action'; import { ListCities } from '../../components/CityList/CityList'; import { FilterOffer } from '../FilterOffers/FilterOffer'; -import { getLoadingOfferPage,getOffer } from '../../store/selector'; -import { fetchOfferObjectAction } from '../../api-actions.ts'; import Map from '../Map/Map'; type MainPageProps = { currentCity: City; cities: City[]; + offers: OfferObject[]; }; export const MainPage : FC = ({ currentCity, cities, + offers, }:MainPageProps) => { const navigate = useNavigate(); const dispatch = useAppDispatch(); - const isLoading = useAppSelector(getLoadingOfferPage); - const offers = useAppSelector(getOffer); - useEffect(() => { - dispatch(fetchOfferObjectAction()); - }, [dispatch]); + // const isLoading = useAppSelector(getLoadingOfferPage); + // const offers = useAppSelector(getOffer); + //useEffect(() => { + //dispatch(fetchOfferObjectAction()); + //}, [dispatch]); const handleUserSelectCity = (cityName: string) => { dispatch(changeCity(cityName)); // dispatch(fillOffers()); }; - const [activeOffer, setActiveOffer] = useState(null); + const [activeOffer, setActiveOffer] = useState(null); const [sortType, setSortType] = useState(SortName.popular); const sortedOffers = offers?.filter((a) =>a.city.name === currentCity.title).slice().sort((a, b) => { @@ -94,11 +94,8 @@ export const MainPage : FC = ({ {sortedOffers?.length} places to stay in {currentCity.title} - { isLoading ? - - : - <>
a.city.name === currentCity.title)} cardcssname={CardCssNameList.citiesList} setActiveOffer={setActiveOffer} />
- } + +
a.city.name === currentCity.title)} cardcssname={CardCssNameList.citiesList} setActiveOffer={setActiveOffer} />
diff --git a/src/components/Map/Map.tsx b/src/components/Map/Map.tsx index ba08b28..10f35dd 100644 --- a/src/components/Map/Map.tsx +++ b/src/components/Map/Map.tsx @@ -24,7 +24,7 @@ type MainPageProps = { offers: OfferObject[] | undefined; currentCity: City; selectedPoint: OfferObject | undefined; - activeOffer: number | null; + activeOffer: string | null; }; function Map(props: MainPageProps): JSX.Element { diff --git a/src/components/NotFoundPage/NotFoundPage.tsx b/src/components/NotFoundPage/NotFoundPage.tsx new file mode 100644 index 0000000..2b4312c --- /dev/null +++ b/src/components/NotFoundPage/NotFoundPage.tsx @@ -0,0 +1,81 @@ +import {Link} from 'react-router-dom'; + +function NotFoundPage():JSX.Element{ + return( +
+
+ +
+ +
+

Cities

+
+
+ +
+

Error 404. Page not found. Back to main page

+
+
+
+
+
+
+ ); +} +export default NotFoundPage; diff --git a/src/components/Offer/Offer.tsx b/src/components/Offer/Offer.tsx index cff7642..679efe9 100644 --- a/src/components/Offer/Offer.tsx +++ b/src/components/Offer/Offer.tsx @@ -2,11 +2,17 @@ import { Link } from 'react-router-dom'; import SendCommentForm from '../SendCommentForm/SendCommentForm'; import { ReviewList } from '../Reviews/ReviewList'; +import { useAppSelector } from '../../hooks/index'; //import { offers } from '../../mock/offers'; -import { OtherPlacesNearby } from '../OtherPlacesNearby/OtherPlacesNearby'; + import Map from '../Map/Map'; -import { AppRoute, UserReview, OfferObject, City } from '../../types/types'; +import { AppRoute, UserReview, OfferObject, City,OfferIdDetails, CardCssNameList } from '../../types/types'; +import { AuthorizationStatus } from '../../const.ts'; +import OfferList from './OfferList.tsx'; + + type OfferProps = { + offerdetails:OfferIdDetails; reviews: UserReview[]; offers: OfferObject[] | null; currentCity: City; @@ -14,199 +20,161 @@ type OfferProps = { export const Offer: React.FC = ({ // eslint-disable-next-line react/prop-types - reviews, + offerdetails, + offers, currentCity, -}) => ( -
-
- -
+}) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access -
-
-
-
-
- Photo studio -
-
- Photo studio -
-
- Photo studio -
-
- Photo studio -
-
- Photo studio -
-
- Photo studio + + const authStatus = useAppSelector((store) => store.user.authorizationStatus); + const nearbyOffers = useAppSelector((store) => store.offerIdDetails.nearbyOffers); + const comments:UserReview[] = useAppSelector((store) => store.offerIdDetails.comments); + + + return ( +
+
+ -
-
-
- Premium -
-
-

- Beautiful & luxurious studio at great location -

- +
+ +
+
+
+
+ {offerdetails.images.map((image) => ( +
+ Фото студии +
+ ))}
-
-
- - Rating +
+
+
+ {offerdetails.isPremium ? ( +
+ Premium +
+ ) : null} +
+

+ {offerdetails.title} +

+
- 4.8 -
-
    -
  • - Apartment -
  • -
  • - 3 Bedrooms -
  • -
  • - Max 4 adults -
  • -
-
- €120 -  night -
-
-

What's inside

-
    -
  • Wi-Fi
  • -
  • Washing machine
  • -
  • Towels
  • -
  • Heating
  • -
  • Coffee machine
  • -
  • Baby seat
  • -
  • Kitchen
  • -
  • Dishwasher
  • -
  • Cabel TV
  • -
  • Fridge
  • -
-
-
-

Meet the host

-
-
- Host avatar +
+
+ + Rating
- Angelina - Pro + {offerdetails.rating} +
+
    +
  • + {offerdetails.type} +
  • +
  • + {offerdetails.bedrooms} Bedrooms +
  • +
  • + Max {offerdetails.maxAdults} Adults +
  • +
+
+ €{offerdetails.price} +  night
-
-

- A quiet cozy and picturesque that hides behind a a river by - the unique lightness of Amsterdam. The building is green and - from 18th century. -

-

- An independent House, strategically located between Rembrand - Square and National Opera, but where the bustle of the city - comes to rest in this alley flowery and colorful. -

+
+

What's inside

+
    + {offerdetails.goods.map((ins) => ( +
  • + {ins} +
  • + ))} +
+
+

Meet the host

+
+
+ Host avatar +
+ + {offerdetails.host.name} + + + {offerdetails.host.isPro} + +
+
+

+ {offerdetails.description} +

+
+
+
-
-
-
- +
+ +
-
-
- -
-
-

Reviews

- {} -
-
-
-); +
+ +
+
+

Reviews

+ {authStatus === AuthorizationStatus.Auth ? : null} +
+
+
+ ); +}; export default Offer; diff --git a/src/components/Offer/OfferCard.tsx b/src/components/Offer/OfferCard.tsx index 52badfe..b1156a3 100644 --- a/src/components/Offer/OfferCard.tsx +++ b/src/components/Offer/OfferCard.tsx @@ -1,20 +1,30 @@ import React from 'react'; import { Link } from 'react-router-dom'; -import { OfferObject, AppRoute } from '../../types/types'; +import { OfferObject } from '../../types/types'; import { useState } from 'react'; - +import { fetchComments, fetchOffer, fetchOfferNeibourhood } from '../../api-actions'; +//import {MouseEvent} from 'react'; +import { store } from '../../store'; +//import { useAppDispatch } from '../../hooks'; type OfferCardProps = { offer: OfferObject; cardcssname: string; - setActiveOffer?: (id: number | null) => void; + setActiveOffer?: (id: string | null) => void; }; - +//import { store } from '../../store/index.ts'; export const OfferCard: React.FC = ({ offer, cardcssname, setActiveOffer, }) => { + //const dispatch = useAppDispatch(); const [ isActiveCard, setActiveCard ] = useState(false); + const handleOfferIdLoad = () => { + //event.preventDefault(); + store.dispatch(fetchOffer(offer.id)); + store.dispatch(fetchOfferNeibourhood(offer.id)); + store.dispatch(fetchComments(offer.id)); + }; return (
setActiveOffer && setActiveOffer(offer.id)} @@ -26,9 +36,9 @@ export const OfferCard: React.FC = ({ Premium
)} -
+
- {offer.title} + {offer.title}
@@ -44,8 +54,8 @@ export const OfferCard: React.FC = ({ Rating
-

- {offer.title} +

+ {offer.title}

{offer.type}

diff --git a/src/components/Offer/OfferList.tsx b/src/components/Offer/OfferList.tsx index e9c847b..5959071 100644 --- a/src/components/Offer/OfferList.tsx +++ b/src/components/Offer/OfferList.tsx @@ -6,11 +6,11 @@ import { OfferObject } from '../../types/types'; type OfferListProps = { offers: OfferObject[] | undefined; cardcssname: string; - setActiveOffer?: (id: number | null) => void; + setActiveOffer?: (id: string | null) => void; }; const OfferList = ({ offers, cardcssname,setActiveOffer}: OfferListProps) => { - const [activeOfferId, setActiveOfferId] = useState(null); + const [activeOfferId, setActiveOfferId] = useState(null); return (
diff --git a/src/components/OtherPlacesNearby/OtherPlacesNearby.tsx b/src/components/OtherPlacesNearby/OtherPlacesNearby.tsx index c6e9db5..78deb7b 100644 --- a/src/components/OtherPlacesNearby/OtherPlacesNearby.tsx +++ b/src/components/OtherPlacesNearby/OtherPlacesNearby.tsx @@ -1,10 +1,6 @@ -import OfferList from '../Offer/OfferList'; -import { offers } from '../../mock/offers'; -import {CardCssNameList } from '../../types/types'; + export const OtherPlacesNearby = () => (

Other places in the neighbourhood

- -
); diff --git a/src/components/Reviews/Review.tsx b/src/components/Reviews/Review.tsx index 16d639e..b59cf8a 100644 --- a/src/components/Reviews/Review.tsx +++ b/src/components/Reviews/Review.tsx @@ -2,17 +2,6 @@ import { Rating } from '../Rating/Rating'; import { UserReview} from '../../types/types'; -export const dateToYearMonthDay = (date: Date) => - new Intl.DateTimeFormat('en-CA', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - }).format(date); - -export const dateToMonthWordYear = (date: Date) => - new Intl.DateTimeFormat('en-CA', { year: 'numeric', month: 'long' }).format( - date, - ); export const Review: React.FC = ({ comment, date, @@ -40,8 +29,8 @@ export const Review: React.FC = ({ mode="compact" />

{comment}

-
diff --git a/src/components/SendCommentForm/SendCommentForm.tsx b/src/components/SendCommentForm/SendCommentForm.tsx index 5c4af3b..598ed27 100644 --- a/src/components/SendCommentForm/SendCommentForm.tsx +++ b/src/components/SendCommentForm/SendCommentForm.tsx @@ -1,11 +1,22 @@ import React, { useState } from 'react'; +import { postComment } from '../../api-actions'; +import { useAppDispatch, useAppSelector } from '../../hooks'; const SendCommentForm = () => { const [rating, setRating] = useState(0); const [comment, setComment] = useState(''); - + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return + const offerId = useAppSelector((state) => state.offerIdDetails.offer.id); + const dispatch = useAppDispatch(); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); + if (comment !== null && rating > 0) { + dispatch(postComment({ + rating: rating, + comment: comment, + id: offerId + })); + } }; const numberofstars = [5, 4, 3, 2, 1]; return ( @@ -47,9 +58,9 @@ const SendCommentForm = () => {

To submit review please make sure to set rating and describe your stay - with at least 10 characters. + with at least 50 characters.

-
diff --git a/src/const.ts b/src/const.ts index 24511a3..dda599b 100644 --- a/src/const.ts +++ b/src/const.ts @@ -8,6 +8,7 @@ export enum APIRoute { Offers = '/offers', Login = '/login', Logout = '/logout', + Comments = '/comments', } export enum AuthorizationStatus { Auth = 'AUTH', diff --git a/src/mock/offers.ts b/src/mock/offers.ts index 76728e1..176ece4 100644 --- a/src/mock/offers.ts +++ b/src/mock/offers.ts @@ -1,98 +1,34 @@ -export const offers = [ - { - id: 1, - title: 'Luxury Apartment in Downtown', - price: 250, - rating: 4.9, - type: 'Apartment', - isPremium: true, - previewImage: 'img/apartment-01.jpg', - NumberOfPlaces: 1, +import { OfferIdDetails } from '../types/types'; + +export const emptyOffer: OfferIdDetails = { + id: '0', + title: '', + price: 0, + type: '', + city: { + name: '', location: { - latitude: 52.3909553943508, - longitude: 4.85309666406198, + latitude: 0, + longitude: 0, zoom: 0, }, - city: { - name: 'Amsterdam', - location: { - latitude: 52.35514938496378, - longitude: 4.673877537499948, - zoom: 8, - }, - }, - isFavorite: false, }, - { - id: 2, - title: 'Cozy House in Suburbs', - price: 100, - rating: 4.7, - type: 'House', - isPremium: false, - previewImage: 'img/apartment-02.jpg', - NumberOfPlaces: 1, - location: { - latitude: 52.3609553943508, - longitude: 4.85309666406198, - zoom: 0, - }, - city: { - name: 'Paris', - location: { - latitude: 52.35514938496378, - longitude: 4.673877537499948, - zoom: 8, - }, - }, - isFavorite: false, - }, - { - id: 3, - title: 'Stylish Studio in City Center', - price: 150, - rating: 4.8, - type: 'Studio', - isPremium: false, - previewImage: 'img/studio-01.jpg', - NumberOfPlaces: 1, - location: { - latitude: 52.3909553943508, - longitude: 4.929309666406198, - zoom: 0, - }, - city: { - name: 'Amsterdam', - location: { - latitude: 52.35514938496378, - longitude: 4.673877537499948, - zoom: 8, - }, - }, - isFavorite: false, + location: { + latitude: 0, + longitude: 0, + zoom: 0, }, - { - id: 4, - title: 'Modern Loft with River View', - price: 200, - rating: 5.0, - type: 'Loft', - isPremium: true, - previewImage: 'img/studio-01.jpg', - NumberOfPlaces: 1, - location: { - latitude: 52.3809553943508, - longitude: 4.939309666406198, - zoom: 0, - }, - city: { - name: 'Amsterdam', - location: { - latitude: 52.35514938496378, - longitude: 4.673877537499948, - zoom: 8, - }, - }, - isFavorite: false, + isFavorite: false, + isPremium: false, + rating: 0, + description: '', + bedrooms: 0, + goods: [''], + host: { + name: '', + avatarUrl: '', + isPro: false, }, -]; + images: [''], + maxAdults: 0, +}; diff --git a/src/mock/reviewers.ts b/src/mock/reviewers.ts index 6ed57a4..24ae851 100644 --- a/src/mock/reviewers.ts +++ b/src/mock/reviewers.ts @@ -7,7 +7,7 @@ export const REVIEWERS = [ }, rating: 5, comment: 'All super', - date: new Date(), + date: '2024-12-2', }, { id: 2, @@ -17,6 +17,6 @@ export const REVIEWERS = [ }, rating: 1, comment: 'All bad', - date: new Date(), + date: '2024-12-2', }, ]; diff --git a/src/reducer.ts b/src/reducer.ts index 40c4f5d..c50e2b4 100644 --- a/src/reducer.ts +++ b/src/reducer.ts @@ -1,21 +1,29 @@ import { combineReducers, createReducer } from '@reduxjs/toolkit'; -import { City, OfferObject } from './types/types'; -import { changeCity, AddOffer, loadOffers } from './action'; +import { City, OfferObject, OfferIdDetails } from './types/types'; +import { changeCity, AddOffer, loadOffers, loadOfferDetails } from './action'; import { offerPage } from './store/offer-data'; +import { offerPageId } from './store/offer-detail'; + import { user } from './store/user'; import { CITYLIST } from './mock/cities'; -import { offers } from './mock/offers'; +import { emptyOffer } from './mock/offers'; +//import { offerPageId } from './store/offer-detail'; +//import { AuthorizationStatus } from './const'; type InitialState = { currentCity: City; cities: City[]; offers: OfferObject[]; + offerIdDetails: OfferIdDetails; + //authorizationStatus: AuthorizationStatus; }; const initialState: InitialState = { currentCity: CITYLIST[0], cities: CITYLIST, - offers: offers, + offers: [], + offerIdDetails: emptyOffer, + //authorizationStatus: AuthorizationStatus.Unknown, }; export const reducer = createReducer(initialState, (builder) => { @@ -30,6 +38,9 @@ export const reducer = createReducer(initialState, (builder) => { }) .addCase(loadOffers, (state, action) => { state.offers = action.payload; + }) + .addCase(loadOfferDetails, (state, action) => { + state.offerIdDetails = action.payload; }); }); @@ -37,5 +48,7 @@ export const rootReducer = combineReducers({ Cities: reducer, currentCity: reducer, offerPage: offerPage.reducer, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access + offerIdDetails: offerPageId.reducer, user: user.reducer, }); diff --git a/src/store/offer-data.ts b/src/store/offer-data.ts index 841ade1..ecdbc59 100644 --- a/src/store/offer-data.ts +++ b/src/store/offer-data.ts @@ -3,16 +3,15 @@ import { OfferData } from '../types/types'; import { fetchOfferObjectAction } from '../api-actions'; const initialState: OfferData = { - offer: null, + offer: [], offerPageStatus: false, }; - export const offerPage = createSlice({ name: 'offerPage', initialState, reducers: { unmountOffer: (state) => { - state.offer = null; + state.offer = []; }, }, extraReducers(builder) { @@ -29,4 +28,5 @@ export const offerPage = createSlice({ }); }, }); + export const { unmountOffer } = offerPage.actions; diff --git a/src/store/offer-detail.ts b/src/store/offer-detail.ts new file mode 100644 index 0000000..b66226e --- /dev/null +++ b/src/store/offer-detail.ts @@ -0,0 +1,41 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { OfferIdDetailsPage } from '../types/types'; +import { fetchOffer, loadComments, loadOfferNearby } from '../api-actions'; +import { emptyOffer } from '../mock/offers'; + +const initialState: OfferIdDetailsPage = { + offer: emptyOffer, + OfferIdDetailsPageStatus: false, + nearbyOffers: [], + comments: [], +}; + +export const offerPageId = createSlice({ + name: 'offerIdDetails', + initialState, + reducers: { + unmountOfferId: (state) => { + state.offer = emptyOffer; + }, + }, + extraReducers(builder) { + builder + .addCase(fetchOffer.pending, (state) => { + state.OfferIdDetailsPageStatus = true; + }) + .addCase(fetchOffer.fulfilled, (state, action) => { + state.offer = action.payload; + state.OfferIdDetailsPageStatus = false; + }) + .addCase(fetchOffer.rejected, (state) => { + state.OfferIdDetailsPageStatus = false; + }) + .addCase(loadOfferNearby, (state, action) => { + state.nearbyOffers = action.payload; + }) + .addCase(loadComments, (state, action) => { + state.comments = action.payload; + }); + }, +}); +export const { unmountOfferId } = offerPageId.actions; diff --git a/src/store/selector.ts b/src/store/selector.ts deleted file mode 100644 index fd00cb0..0000000 --- a/src/store/selector.ts +++ /dev/null @@ -1,12 +0,0 @@ -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/store/store.rar b/src/store/store.rar new file mode 100644 index 0000000..7428097 Binary files /dev/null and b/src/store/store.rar differ diff --git a/src/types/types.ts b/src/types/types.ts index d4245e7..ce81f35 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -6,10 +6,47 @@ export type City = { lng: number; }; export type OfferData = { - offer: OfferObject[] | null; + offer: OfferObject[]; offerPageStatus: boolean; }; - +export type OfferIdDetailsPage = { + offer: OfferIdDetails; + OfferIdDetailsPageStatus: boolean; + nearbyOffers: OfferObject[]; + comments: UserReview[]; +}; +export type OfferIdDetails = { + id: string; + title: string; + type: string; + price: number; + city: { + name: string; + location: { + latitude: number; + longitude: number; + zoom: number; + }; + }; + location: { + latitude: number; + longitude: number; + zoom: number; + }; + isFavorite: boolean; + isPremium: boolean; + rating: number; + description: string; + bedrooms: number; + goods: [string]; + host: { + name: string; + avatarUrl: string; + isPro: boolean; + }; + images: [string]; + maxAdults: number; +}; export type AuthorizationSlice = { authorizationStatus: AuthorizationStatus; userData: UserAuth | null; @@ -22,7 +59,7 @@ export type Point = { lng: number; }; export type OfferObject = { - id: number; + id: string; title: string; price: number; rating: number; @@ -66,7 +103,12 @@ export type UserReview = { }; rating: number; comment: string; - date: Date; + date: string; +}; +export type UserReviewPost = { + rating: number; + comment: string; + id: string; }; export type UserObject = { name: string;