From d56b47d9d7837fb5690e32db619667388c41b61a Mon Sep 17 00:00:00 2001 From: "hani.mohammad" Date: Sun, 22 Dec 2024 04:12:12 +0300 Subject: [PATCH 1/4] testcheck4 --- src/components/Offer/Offer.tsx | 45 ++++++++++--------- src/components/routes/private-route/index.tsx | 2 +- src/components/tests/app-router.test.tsx | 8 ++++ src/shared/mocks/index.ts | 39 +++++++++++++--- src/shared/providers/with-store.tsx | 12 ++--- src/store/userselector.ts | 3 +- 6 files changed, 74 insertions(+), 35 deletions(-) diff --git a/src/components/Offer/Offer.tsx b/src/components/Offer/Offer.tsx index 7120154..c66bdda 100644 --- a/src/components/Offer/Offer.tsx +++ b/src/components/Offer/Offer.tsx @@ -3,8 +3,6 @@ import { Link, useNavigate, useParams } from 'react-router-dom'; import SendCommentForm from '../SendCommentForm/SendCommentForm'; import { ReviewList } from '../Reviews/ReviewList'; import { useAppDispatch, useAppSelector } from '../../hooks/index'; -//import { offers } from '../../mock/offers'; - import Map from '../Map/Map'; import { AppRoute, UserReview, CardCssNameList } from '../../types/types'; import { AuthorizationStatus } from '../../const.ts'; @@ -13,22 +11,25 @@ import { getAuthStatus,getUserEmail} from '../../store/userselector.ts'; import { fetchComments, fetchOffer, fetchOfferNeibourhood, logout, setIsOfferFavorite } from '../../api-actions.ts'; import classNames from 'classnames'; import { useEffect, useState } from 'react'; -export const Offer: React.FC = () => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - const { id: idOffer } = useParams(); +import { store } from '../../store/index.ts'; + + +export default function Offer () { + const { id: OfferId } = useParams(); const dispatch = useAppDispatch(); + const userEmail = useAppSelector(getUserEmail); + const authStatus = useAppSelector(getAuthStatus); const currentCity = useAppSelector((state) => state.currentCity); useEffect(()=>{ - dispatch(fetchOffer(idOffer ?? '')); - dispatch(fetchOfferNeibourhood(idOffer ?? '')); - dispatch(fetchComments(idOffer ?? '')); - },[idOffer,dispatch]); - const nearbyOffers = useAppSelector((store) => store.offerIdDetails.nearbyOffers); - const offerdetails = useAppSelector((store) => store.offerIdDetails.offer); + store.dispatch(fetchOffer(OfferId ?? '')); + store.dispatch(fetchOfferNeibourhood(OfferId ?? '')); + store.dispatch(fetchComments(OfferId ?? '')); + },[OfferId,dispatch]); + const nearbyOffers = useAppSelector((state) => state.offerIdDetails.nearbyOffers); + const offerdetails = useAppSelector((state) => state.offerIdDetails.offer); const offers = useAppSelector((state) => state.offerPage); - const comments:UserReview[] = useAppSelector((store) => store.offerIdDetails.comments); - const userEmail = useAppSelector(getUserEmail); - const authStatus = useAppSelector(getAuthStatus); + const comments:UserReview[] = useAppSelector((state) => state.offerIdDetails.comments); + const [ isFavorite, setisFavorite ] = useState(false); const navigate = useNavigate(); useEffect(() => { @@ -198,12 +199,13 @@ export const Offer: React.FC = () => {
- + {offers !== null && offers.offer !== null && offers.offer.length > 0 ? + : }
@@ -216,5 +218,4 @@ export const Offer: React.FC = () => {
); -}; -export default Offer; +} diff --git a/src/components/routes/private-route/index.tsx b/src/components/routes/private-route/index.tsx index 1897c21..9a91894 100644 --- a/src/components/routes/private-route/index.tsx +++ b/src/components/routes/private-route/index.tsx @@ -7,7 +7,7 @@ interface IPrivateRoute extends PropsWithChildren{ authState: AuthorizationStatus; } function PrivateRoute({children, authState}: IPrivateRoute) { - return authState === AuthorizationStatus.Auth ? children : ; + return authState === AuthorizationStatus.Auth || authState === AuthorizationStatus.Unknown ? children : ; } export default PrivateRoute; diff --git a/src/components/tests/app-router.test.tsx b/src/components/tests/app-router.test.tsx index 5daa302..44a7c31 100644 --- a/src/components/tests/app-router.test.tsx +++ b/src/components/tests/app-router.test.tsx @@ -10,6 +10,7 @@ import { Cities } from '../../shared/api'; import MainPage from '../MainPage/MainPage'; import Favorite from '../Favorites/Favorite'; import NotFoundPage from '../NotFoundPage/NotFoundPage'; +import Offer from '../Offer/Offer'; describe('Application Routing', () => { let mockHistory: MemoryHistory; @@ -98,4 +99,11 @@ describe('Application Routing', () => { expect(screen.getByText(/404 Not Found/i)).toBeInTheDocument(); }); + it('should render OfferPage when user navigate to "/offer/a01640c0-fed5-4d3f-99b3-deb2391269fc"', () => { + const withHistoryComponent = withHistory(, mockHistory); + const { withStoreComponent } = withStore(withHistoryComponent, makeFakeStore()); + mockHistory.push('/offer/a01640c0-fed5-4d3f-99b3-deb2391269fc'); + render(withStoreComponent); + expect(screen.getByText(/Wood and stone place/i)).toBeInTheDocument(); + }); }); diff --git a/src/shared/mocks/index.ts b/src/shared/mocks/index.ts index c0a3772..2957715 100644 --- a/src/shared/mocks/index.ts +++ b/src/shared/mocks/index.ts @@ -1,11 +1,9 @@ import { AuthorizationStatus } from '../../const'; import { CITYLIST } from '../../mock/cities'; import { emptyOffer } from '../../mock/offers'; -import { RootState } from '../lib/types'; +import { State } from '../../types/types'; -export function makeFakeStore( - initialState: Partial = {} -): RootState { +export function makeFakeStore(initialState: Partial = {}): State { return { Cities: { currentCity: CITYLIST[0], @@ -18,7 +16,38 @@ export function makeFakeStore( offerPageStatus: false, }, offerIdDetails: { - offer: emptyOffer, + offer: { + id: 'a20a52b2-efc2-4b0f-9396-4bdfbe5e9543', + title: 'Wood and stone place', + type: 'apartment', + price: 576, + images: ['https://14.design.htmlacademy.pro/static/hotel/14.jpg'], + city: { + name: 'Amsterdam', + location: { + latitude: 48.85661, + longitude: 2.351499, + zoom: 13, + }, + }, + location: { + latitude: 48.868610000000004, + longitude: 2.342499, + zoom: 16, + }, + isFavorite: false, + isPremium: false, + rating: 2.1, + description: 'dsf', + bedrooms: 1, + goods: [''], + maxAdults: 1, + host: { + name: 'Oliver Conner', + avatarUrl: 'https://url-to-image/image.png', + isPro: false, + }, + }, OfferIdDetailsPageStatus: false, nearbyOffers: [], comments: [], diff --git a/src/shared/providers/with-store.tsx b/src/shared/providers/with-store.tsx index 3153352..859e009 100644 --- a/src/shared/providers/with-store.tsx +++ b/src/shared/providers/with-store.tsx @@ -1,17 +1,17 @@ import MockAdapter from 'axios-mock-adapter'; -import { RootState } from '../lib/types'; -import { $api } from '../api'; +import { api } from '../../api-actions'; import thunk from 'redux-thunk'; import { configureMockStore } from '@jedmao/redux-mock-store'; import { Action } from '@reduxjs/toolkit'; import { AppThunkDispatch } from '../lib'; import { Provider } from 'react-redux'; import { withStoreProviderType } from './types'; +import { State } from '../../types/types'; -export function withStore(component: JSX.Element, initialState: Partial = {}): withStoreProviderType { - const mockAxiosAdapter = new MockAdapter($api); - const middleware = [thunk.withExtraArgument($api)]; - const mockStoreCreator = configureMockStore, AppThunkDispatch>(middleware); +export function withStore(component: JSX.Element, initialState: Partial = {}): withStoreProviderType { + const mockAxiosAdapter = new MockAdapter(api); + const middleware = [thunk.withExtraArgument(api)]; + const mockStoreCreator = configureMockStore, AppThunkDispatch>(middleware); const store = mockStoreCreator(initialState); return { diff --git a/src/store/userselector.ts b/src/store/userselector.ts index c312bd4..1d4cc69 100644 --- a/src/store/userselector.ts +++ b/src/store/userselector.ts @@ -1,6 +1,7 @@ import { createSelector } from '@reduxjs/toolkit'; import { AuthorizationSlice, State } from '../types/types'; -const selectAuthStatus = (state: Pick) => +export type RootState = ReturnType; +const selectAuthStatus = (state: Pick) => state.user.authorizationStatus; export const getAuthStatus = createSelector( From 40827f24a166b51b21724bc8620465d23ae7b8ae Mon Sep 17 00:00:00 2001 From: "hani.mohammad" Date: Wed, 25 Dec 2024 02:39:50 +0300 Subject: [PATCH 2/4] module9-task2 --- src/components/Favorites/Favorite.tsx | 2 +- src/components/Main-Empty/Main-Empty.tsx | 18 +++++++++++++ src/components/MainPage/MainPage.tsx | 32 ++++++++++++++---------- src/types/types.ts | 12 +++++++++ 4 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 src/components/Main-Empty/Main-Empty.tsx diff --git a/src/components/Favorites/Favorite.tsx b/src/components/Favorites/Favorite.tsx index 41c7859..32c1517 100644 --- a/src/components/Favorites/Favorite.tsx +++ b/src/components/Favorites/Favorite.tsx @@ -12,7 +12,7 @@ const Favorite = ({ offers }: FavoriteProps) => (

Saved listings

- {offers?.map((offer) => ( + {offers?.filter((a) =>a.isFavorite === true)?.map((offer) => ( ))}
diff --git a/src/components/Main-Empty/Main-Empty.tsx b/src/components/Main-Empty/Main-Empty.tsx new file mode 100644 index 0000000..5b78e01 --- /dev/null +++ b/src/components/Main-Empty/Main-Empty.tsx @@ -0,0 +1,18 @@ +import { memo } from 'react'; +import { EmptyCityProps } from '../../types/types'; + +function MemoEmptyCityBlock({city}:EmptyCityProps) { + return ( +
+
+
+ No places to stay available +

We could not find any property available at the moment in {city}

+
+
+
+
+ ); +} + +export const EmptyCityBlock = memo(MemoEmptyCityBlock); diff --git a/src/components/MainPage/MainPage.tsx b/src/components/MainPage/MainPage.tsx index 331c667..a5a02f9 100644 --- a/src/components/MainPage/MainPage.tsx +++ b/src/components/MainPage/MainPage.tsx @@ -4,7 +4,7 @@ import { Link, useNavigate } from 'react-router-dom'; //import Spinner from '../spinner/spinner.tsx'; import OfferList from '../Offer/OfferList'; import { useAppDispatch, useAppSelector } from '../../hooks'; -import { AppRoute, City, CardCssNameList, SortName, OfferObject} from '../../types/types'; +import { AppRoute, City, CardCssNameList, SortName, OfferObject, Cities} from '../../types/types'; import { changeCity } from '../../action'; import { ListCities } from '../../components/CityList/CityList'; import { FilterOffer } from '../FilterOffers/FilterOffer'; @@ -12,6 +12,7 @@ import { getAuthStatus,getUserEmail} from '../../store/userselector.ts'; import Map from '../Map/Map'; import { AuthorizationStatus } from '../../const.ts'; import { logout } from '../../api-actions.ts'; +import { EmptyCityBlock } from '../Main-Empty/Main-Empty.tsx'; //import { getToken } from '../../token.ts'; type MainPageProps = { currentCity: City; @@ -115,20 +116,25 @@ export const MainPage : FC = ({
-
-
-

Places

- {offerListMemo?.filter((a) =>a.city.name === currentCity.title).length} places to stay in {currentCity.title} + { + !offerListMemo.length ? + + : +
+
+

Places

+ {offerListMemo?.filter((a) =>a.city.name === currentCity.title).length} places to stay in {currentCity.title} -
a.city.name === currentCity.title)} cardcssname={CardCssNameList.citiesList} setActiveOffer={setActiveOffer} />
-
-
-
- a.city.name === currentCity.title)} selectedPoint={sortedOffers?.[0]} activeOffer={activeOffer} currentCity={currentCity} /> -
-
-
+
a.city.name === currentCity.title)} cardcssname={CardCssNameList.citiesList} setActiveOffer={setActiveOffer} />
+
+
+
+ a.city.name === currentCity.title)} selectedPoint={sortedOffers?.[0]} activeOffer={activeOffer} currentCity={currentCity} /> +
+
+
+ }
diff --git a/src/types/types.ts b/src/types/types.ts index e88e23e..1ca9a84 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -16,6 +16,18 @@ export type OfferIdDetailsPage = { nearbyOffers: OfferObject[]; comments: UserReview[]; }; +export enum Cities { + Paris = 'Paris', + Cologne = 'Cologne', + Brussels = 'Brussels', + Amsterdam = 'Amsterdam', + Hamburg = 'Hamburg', + Dusseldorf = 'Dusseldorf', +} + +export interface EmptyCityProps { + city: Cities; +} export type OfferIdDetails = { id: string; title: string; From 3b19cae7887def83c0430cd4e48c6a77c1848afe Mon Sep 17 00:00:00 2001 From: "hani.mohammad" Date: Wed, 25 Dec 2024 02:42:35 +0300 Subject: [PATCH 3/4] module9-task2 --- src/types/types.ts | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/types/types.ts b/src/types/types.ts index 1ca9a84..d8ef9d6 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,6 +1,5 @@ import { store } from '../store'; import { AuthorizationStatus } from '../const'; - export type City = { title: string; lat: number; @@ -16,18 +15,6 @@ export type OfferIdDetailsPage = { nearbyOffers: OfferObject[]; comments: UserReview[]; }; -export enum Cities { - Paris = 'Paris', - Cologne = 'Cologne', - Brussels = 'Brussels', - Amsterdam = 'Amsterdam', - Hamburg = 'Hamburg', - Dusseldorf = 'Dusseldorf', -} - -export interface EmptyCityProps { - city: Cities; -} export type OfferIdDetails = { id: string; title: string; @@ -60,6 +47,19 @@ export type OfferIdDetails = { images: [string]; maxAdults: number; }; +export enum Cities { + Paris = 'Paris', + Cologne = 'Cologne', + Brussels = 'Brussels', + Amsterdam = 'Amsterdam', + Hamburg = 'Hamburg', + Dusseldorf = 'Dusseldorf', +} + +export interface EmptyCityProps { + city: Cities; +} + export type AuthorizationSlice = { authorizationStatus: AuthorizationStatus; userData: UserAuth | null; @@ -145,12 +145,6 @@ export type LoginAuth = { email: string; password: string; }; - -export type ReducerType = { - offer: OfferIdDetailsPage; - user: UserAuth; - review: UserReview; -}; export type Points = Point[]; export type State = ReturnType; From ea27d828b433ddf1cd4ee458eae4b2ff570aaa56 Mon Sep 17 00:00:00 2001 From: "hani.mohammad" Date: Wed, 25 Dec 2024 02:46:24 +0300 Subject: [PATCH 4/4] module9-task2 --- src/types/types.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/types/types.ts b/src/types/types.ts index d8ef9d6..994922e 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -145,6 +145,11 @@ export type LoginAuth = { email: string; password: string; }; +export type ReducerType = { + offer: OfferIdDetailsPage; + user: UserAuth; + review: UserReview; +}; export type Points = Point[]; export type State = ReturnType;