diff --git a/src/components/app/app.tsx b/src/components/app/app.tsx index 5a871b4..9b299a0 100644 --- a/src/components/app/app.tsx +++ b/src/components/app/app.tsx @@ -26,7 +26,6 @@ function App(): JSX.Element { }, [authorizationStatus]); return ( - {/* */} } /> } /> diff --git a/src/components/card/card.test.tsx b/src/components/card/card.test.tsx index ca28686..2e35dc9 100644 --- a/src/components/card/card.test.tsx +++ b/src/components/card/card.test.tsx @@ -51,7 +51,7 @@ function renderCard({ }; const { withStoreComponent } = withStore( - , + , initialState ); @@ -113,6 +113,7 @@ describe('Component: Card', () => { mode={CARD_MODE.VERTICAL} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} + isNear={false} />, initialState ); diff --git a/src/components/card/card.tsx b/src/components/card/card.tsx index 30be45f..e64139a 100644 --- a/src/components/card/card.tsx +++ b/src/components/card/card.tsx @@ -11,11 +11,12 @@ type CardMode = typeof CARD_MODE[keyof typeof CARD_MODE]; type OfferCardProps = { data: Offer; mode: CardMode; + isNear: boolean; onMouseEnter?: () => void; onMouseLeave?: () => void; } -function Card({ data, mode, onMouseEnter, onMouseLeave }: OfferCardProps): JSX.Element { +function Card({ data, mode, isNear, onMouseEnter, onMouseLeave }: OfferCardProps): JSX.Element { const { isPremium, rating, previewImage, price, isFavorite, type, title, id } = data; const ratingStars = convertRathingStars(rating); const modeClasses = { @@ -35,7 +36,7 @@ function Card({ data, mode, onMouseEnter, onMouseLeave }: OfferCardProps): JSX.E const authorizationStatus = useAppSelector(getAuthorizationStatus); const isAuth = authorizationStatus === AuthorizationStatus.Auth; return ( -

Places

- {offersCount} places to stay in {currentCity.name} + {offersCount} {offersCount <= 1 ? 'place' : 'places'} to stay in {currentCity.name} ) => { evt.preventDefault(); dispatch(logoutAction()); - navigate(APP_ROUTE.Login); }; + useEffect(() => { + if (!isAuth && location.pathname === String(APP_ROUTE.Favorites)) { + navigate(APP_ROUTE.Login, { replace: true }); + } + }, [isAuth, location.pathname, navigate]); + if (isLoginPage) { return (
diff --git a/src/components/map/map.tsx b/src/components/map/map.tsx index ccc572d..7cd06d2 100644 --- a/src/components/map/map.tsx +++ b/src/components/map/map.tsx @@ -31,7 +31,6 @@ function Map({ city, offers, type, activeOffer, allowHover = true }: MapProps): const mapRef = useRef(null); const mapInstanceRef = useRef(null); - useEffect(() => { if (mapRef.current !== null && mapInstanceRef.current === null) { const map = leaflet.map(mapRef.current).setView( @@ -47,7 +46,6 @@ function Map({ city, offers, type, activeOffer, allowHover = true }: MapProps): } }, []); - useEffect(() => { const leafletMap = mapInstanceRef.current; @@ -57,9 +55,8 @@ function Map({ city, offers, type, activeOffer, allowHover = true }: MapProps): const markers: leaflet.Marker[] = offers.map((offer) => { let icon; - if (type === MAP_TYPE.OFFERPAGE) { - icon = currentIcon; + icon = offer.id === activeOffer ? currentIcon : defaultIcon; } else { icon = allowHover && offer.id === activeOffer ? currentIcon : defaultIcon; } diff --git a/src/components/offer-list/offer-list.tsx b/src/components/offer-list/offer-list.tsx index 5cfa225..684c21e 100644 --- a/src/components/offer-list/offer-list.tsx +++ b/src/components/offer-list/offer-list.tsx @@ -31,6 +31,7 @@ function OfferList({ cardView, offers, onActiveCardToggle }: OfferListProps): JS mode={CARD_MODE.VERTICAL} onMouseEnter={() => onActiveCardToggle(card.id)} onMouseLeave={() => onActiveCardToggle(null)} + isNear={false} /> )) } diff --git a/src/components/offer-other/offer-other.tsx b/src/components/offer-other/offer-other.tsx index d237f20..237cd54 100644 --- a/src/components/offer-other/offer-other.tsx +++ b/src/components/offer-other/offer-other.tsx @@ -9,16 +9,18 @@ type OfferOtherProps = { function OfferOther({ cardOtherView, offers }: OfferOtherProps): JSX.Element { const cards = offers.slice(0, cardOtherView); + return (

Other places in the neighbourhood

{cards.map((card) => ( - < Card + ))}
diff --git a/src/components/offer-reviews/offer-reviews-form.tsx b/src/components/offer-reviews/offer-reviews-form.tsx index 804e3ba..385156a 100644 --- a/src/components/offer-reviews/offer-reviews-form.tsx +++ b/src/components/offer-reviews/offer-reviews-form.tsx @@ -20,7 +20,7 @@ const RATING_OPTIONS: RatingOption[] = [ { value: 4, title: 'good', id: '4-stars' }, { value: 3, title: 'not bad', id: '3-stars' }, { value: 2, title: 'badly', id: '2-stars' }, - { value: 1, title: 'terribly', id: '1-star' }, + { value: 1, title: 'terribly', id: '1-stars' }, ]; function ReviewsForm(): JSX.Element { diff --git a/src/components/offer-wrapper/offer-wrapper.tsx b/src/components/offer-wrapper/offer-wrapper.tsx index 256ffeb..c02d3c8 100644 --- a/src/components/offer-wrapper/offer-wrapper.tsx +++ b/src/components/offer-wrapper/offer-wrapper.tsx @@ -87,7 +87,7 @@ function OfferWrapper({ offerData }: OfferProps): JSX.Element {

Meet the host

-
+
Host avatar
diff --git a/src/const.ts b/src/const.ts index 9b3440d..0b38971 100644 --- a/src/const.ts +++ b/src/const.ts @@ -82,6 +82,7 @@ export enum APP_ROUTE { Favorites = '/favorites', Offer = '/offer/:id', Root = '/', + NotFound= '*', } export enum AuthorizationStatus { diff --git a/src/pages/favorites-page/favorites-page.tsx b/src/pages/favorites-page/favorites-page.tsx index eeba620..e7fce03 100644 --- a/src/pages/favorites-page/favorites-page.tsx +++ b/src/pages/favorites-page/favorites-page.tsx @@ -51,6 +51,7 @@ function FavoritesPage(): JSX.Element { mode={CARD_MODE.HORIZONTAL} key={offer.id} data={offer} + isNear={false} /> ))}
diff --git a/src/pages/login-page/login-page.tsx b/src/pages/login-page/login-page.tsx index 3080d91..bcbab76 100644 --- a/src/pages/login-page/login-page.tsx +++ b/src/pages/login-page/login-page.tsx @@ -1,18 +1,28 @@ import { FormEvent, useRef, useState } from 'react'; -import { Navigate } from 'react-router-dom'; +import { Navigate, useNavigate } from 'react-router-dom'; import Header from '../../components/header/header'; import { Helmet } from 'react-helmet-async'; import { useAppDispatch, useAppSelector } from '../../store'; import { AuthData } from '../../types/auth-data'; import { loginAction } from '../../store/api-actions'; -import { APP_ROUTE, AuthorizationStatus } from '../../const'; +import { APP_ROUTE, AuthorizationStatus, CITIES } from '../../const'; import { getAuthorizationStatus } from '../../store/selectors'; +import { generateRandomNumber } from '../../utils/mocks'; +import { City } from '../../types/city'; +import { setCity } from '../../store/slice/app-data'; function LoginPage(): JSX.Element { + const dispatch = useAppDispatch(); + const randomCity = CITIES[generateRandomNumber(0, CITIES.length - 1)]; + const navigate = useNavigate(); + const handleCityBind = (city: City) => { + dispatch(setCity(city)); + navigate(APP_ROUTE.Root); + }; + const loginRef = useRef(null); const passwordRef = useRef(null); const [passwordError, setPasswordError] = useState(''); - const dispatch = useAppDispatch(); const authorizationStatus = useAppSelector(getAuthorizationStatus); const isAuth = authorizationStatus === AuthorizationStatus.Auth; @@ -112,8 +122,8 @@ function LoginPage(): JSX.Element {
diff --git a/src/pages/offer-page/offer-page.test.tsx b/src/pages/offer-page/offer-page.test.tsx index ee37867..9dc631d 100644 --- a/src/pages/offer-page/offer-page.test.tsx +++ b/src/pages/offer-page/offer-page.test.tsx @@ -164,7 +164,7 @@ describe('OfferPage', () => { renderPage(); - expect(screen.getByTestId('navigate')).toHaveTextContent(APP_ROUTE.Root); + expect(screen.getByTestId('navigate')).toHaveTextContent(APP_ROUTE.NotFound); }); it('does not render gallery when images array is empty', () => { diff --git a/src/pages/offer-page/offer-page.tsx b/src/pages/offer-page/offer-page.tsx index 576df8d..cd9fc0e 100644 --- a/src/pages/offer-page/offer-page.tsx +++ b/src/pages/offer-page/offer-page.tsx @@ -34,8 +34,9 @@ function OfferPage(): JSX.Element { } if (!currentOffer) { - return ; + return ; } + return (
@@ -52,10 +53,10 @@ function OfferPage(): JSX.Element { />
{ fetchReviewsAction.pending.type, fetchReviewsAction.fulfilled.type, ]); - - const storeActions = store.getActions() as AnyAction[]; - expect(storeActions[1].payload).toEqual(mockReviews); }); it('should dispatch fetchReviewsAction.rejected when request fails', async () => { @@ -256,9 +253,6 @@ describe('Async actions', () => { postReviewAction.pending.type, postReviewAction.fulfilled.type, ]); - - const storeActions = store.getActions() as AnyAction[]; - expect(storeActions[1].payload).toEqual(mockReviews); }); it('should dispatch postReviewAction.rejected with error message when validation fails', async () => { diff --git a/src/store/api-actions.ts b/src/store/api-actions.ts index 624c8ca..4b0ebfd 100644 --- a/src/store/api-actions.ts +++ b/src/store/api-actions.ts @@ -83,7 +83,7 @@ export const fetchReviewsAction = createAsyncThunk { const { data } = await api.get(`${APIRoute.Comments}/${offerId}`); - return data; + return data.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); }, ); @@ -99,7 +99,7 @@ export const postReviewAction = createAsyncThunk(url, review); const { data } = await api.get(url); - return data; + return data.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); } catch (error) { const axiosError = error as AxiosError; if (axiosError.response?.status === 400) { diff --git a/src/store/slice/app-data.test.ts b/src/store/slice/app-data.test.ts index 35564e7..9d5b2d8 100644 --- a/src/store/slice/app-data.test.ts +++ b/src/store/slice/app-data.test.ts @@ -47,7 +47,6 @@ describe('AppData slice', () => { const result = appData.reducer(initialState, action); expect(result.city).toEqual(newCity); - expect(result.city).not.toEqual(CITIES[0]); }); }); diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 2e7c9a1..c242e24 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -24,5 +24,5 @@ export function sortOffers(offers: Offer[], sortType: string): Offer[] { } export function convertRathingStars (rating: number): string { - return `${rating * 20 }%`; + return `${Math.round(rating * 20 / 10) * 10}%`; }