Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/components/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ function App(): JSX.Element {
}, [authorizationStatus]);
return (
<HelmetProvider>
{/* <BrowserRouter> */}
<Routes>
<Route
index
Expand All @@ -52,7 +51,7 @@ function App(): JSX.Element {
element={<OfferPage />}
/>
<Route
path='*'
path={APP_ROUTE.NotFound}
element={<NotFoundPage />}
/>
</Routes>
Expand Down
3 changes: 2 additions & 1 deletion src/components/card/card.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function renderCard({
};

const { withStoreComponent } = withStore(
<Card data={offerData} mode={CARD_MODE.VERTICAL} />,
<Card data={offerData} mode={CARD_MODE.VERTICAL} isNear={false}/>,
initialState
);

Expand Down Expand Up @@ -113,6 +113,7 @@ describe('Component: Card', () => {
mode={CARD_MODE.VERTICAL}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
isNear={false}
/>,
initialState
);
Expand Down
5 changes: 3 additions & 2 deletions src/components/card/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -35,7 +36,7 @@ function Card({ data, mode, onMouseEnter, onMouseLeave }: OfferCardProps): JSX.E
const authorizationStatus = useAppSelector(getAuthorizationStatus);
const isAuth = authorizationStatus === AuthorizationStatus.Auth;
return (
<article className={`${`${cardMode}__card`} place-card`}
<article className={isNear ? 'near-places__card place-card' : `${`${cardMode}__card`} place-card`}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
data-testid="card-container"
Expand Down
2 changes: 1 addition & 1 deletion src/components/cities/cards-map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function CardsMap(): JSX.Element {
<div className="cities__places-container container">
<section className="cities__places places">
<h2 className="visually-hidden">Places</h2>
<b className="places__found">{offersCount} places to stay in {currentCity.name}</b>
<b className="places__found">{offersCount} {offersCount <= 1 ? 'place' : 'places'} to stay in {currentCity.name}</b>
<Sort
currentSort={currentSort}
onSortChange={handleSortChange}
Expand Down
8 changes: 7 additions & 1 deletion src/components/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useAppDispatch, useAppSelector } from '../../store';
import { APP_ROUTE, AuthorizationStatus } from '../../const';
import { logoutAction } from '../../store/api-actions';
import { getAuthorizationStatus, selectFavoriteOffers, getUserEmail } from '../../store/selectors';
import { useEffect } from 'react';

function Header(): JSX.Element {
const dispatch = useAppDispatch();
Expand All @@ -19,9 +20,14 @@ function Header(): JSX.Element {
const handleLogout = (evt: React.MouseEvent<HTMLAnchorElement>) => {
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 (
<header className="header">
Expand Down
5 changes: 1 addition & 4 deletions src/components/map/map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
const mapRef = useRef<HTMLDivElement | null>(null);
const mapInstanceRef = useRef<L.Map | null>(null);


useEffect(() => {
if (mapRef.current !== null && mapInstanceRef.current === null) {
const map = leaflet.map(mapRef.current).setView(
Expand All @@ -45,9 +44,8 @@

mapInstanceRef.current = map;
}
}, []);

Check warning on line 47 in src/components/map/map.tsx

View workflow job for this annotation

GitHub Actions / Check

React Hook useEffect has missing dependencies: 'city.location.latitude', 'city.location.longitude', and 'city.location.zoom'. Either include them or remove the dependency array

Check warning on line 47 in src/components/map/map.tsx

View workflow job for this annotation

GitHub Actions / Check

React Hook useEffect has missing dependencies: 'city.location.latitude', 'city.location.longitude', and 'city.location.zoom'. Either include them or remove the dependency array


useEffect(() => {
const leafletMap = mapInstanceRef.current;

Expand All @@ -57,9 +55,8 @@

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;
}
Expand Down
1 change: 1 addition & 0 deletions src/components/offer-list/offer-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ function OfferList({ cardView, offers, onActiveCardToggle }: OfferListProps): JS
mode={CARD_MODE.VERTICAL}
onMouseEnter={() => onActiveCardToggle(card.id)}
onMouseLeave={() => onActiveCardToggle(null)}
isNear={false}
/>
))
}
Expand Down
4 changes: 3 additions & 1 deletion src/components/offer-other/offer-other.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ type OfferOtherProps = {

function OfferOther({ cardOtherView, offers }: OfferOtherProps): JSX.Element {
const cards = offers.slice(0, cardOtherView);

return (
<div className="container">
<section className="near-places places">
<h2 className="near-places__title">Other places in the neighbourhood</h2>
<div className="near-places__list places__list">
{cards.map((card) => (
< Card
<Card
key={card.id}
data={card}
mode={CARD_MODE.VERTICAL}
isNear
/>
))}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/offer-reviews/offer-reviews-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/components/offer-wrapper/offer-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ function OfferWrapper({ offerData }: OfferProps): JSX.Element {
<div className="offer__host">
<h2 className="offer__host-title">Meet the host</h2>
<div className="offer__host-user user">
<div className="offer__avatar-wrapper offer__avatar-wrapper--pro user__avatar-wrapper">
<div className={host.isPro ? 'offer__avatar-wrapper offer__avatar-wrapper--pro user__avatar-wrapper' : 'offer__avatar-wrapper user__avatar-wrapper'}>
<img className="offer__avatar user__avatar" src={host.avatarUrl} width="74" height="74" alt="Host avatar" />
</div>
<span className="offer__user-name">
Expand Down
1 change: 1 addition & 0 deletions src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export enum APP_ROUTE {
Favorites = '/favorites',
Offer = '/offer/:id',
Root = '/',
NotFound= '*',
}

export enum AuthorizationStatus {
Expand Down
1 change: 1 addition & 0 deletions src/pages/favorites-page/favorites-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function FavoritesPage(): JSX.Element {
mode={CARD_MODE.HORIZONTAL}
key={offer.id}
data={offer}
isNear={false}
/>
))}
</div>
Expand Down
20 changes: 15 additions & 5 deletions src/pages/login-page/login-page.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLInputElement | null>(null);
const passwordRef = useRef<HTMLInputElement | null>(null);
const [passwordError, setPasswordError] = useState('');
const dispatch = useAppDispatch();
const authorizationStatus = useAppSelector(getAuthorizationStatus);

const isAuth = authorizationStatus === AuthorizationStatus.Auth;
Expand Down Expand Up @@ -112,8 +122,8 @@ function LoginPage(): JSX.Element {
</section>
<section className="locations locations--login locations--current">
<div className="locations__item">
<a className="locations__item-link" href="#">
<span>Amsterdam</span>
<a className="locations__item-link" onClick={() => handleCityBind(randomCity)}>
<span>{randomCity.name}</span>
</a>
</div>
</section>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/offer-page/offer-page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
7 changes: 4 additions & 3 deletions src/pages/offer-page/offer-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ function OfferPage(): JSX.Element {
}

if (!currentOffer) {
return <Navigate to={APP_ROUTE.Root} />;
return <Navigate to={APP_ROUTE.NotFound} />;
}

return (
<div className="page">
<Helmet>
Expand All @@ -52,10 +53,10 @@ function OfferPage(): JSX.Element {
/>
<Map
city={currentOffer.city}
offers={[currentOffer]}
offers={[currentOffer].concat(nearbyOffers.slice(0, cardOtherView))}
type={MAP_TYPE.OFFERPAGE}
activeOffer={currentOffer.id}
allowHover={false}
allowHover
/>
</section>
<OfferOther
Expand Down
6 changes: 0 additions & 6 deletions src/store/api-actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,6 @@ describe('Async actions', () => {
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 () => {
Expand Down Expand Up @@ -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 () => {
Expand Down
4 changes: 2 additions & 2 deletions src/store/api-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const fetchReviewsAction = createAsyncThunk<Review[], string, {
'data/fetchReviews',
async (offerId, { extra: api }) => {
const { data } = await api.get<Review[]>(`${APIRoute.Comments}/${offerId}`);
return data;
return data.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
},
);

Expand All @@ -99,7 +99,7 @@ export const postReviewAction = createAsyncThunk<Review[], { offerId: string; re
const url = `${APIRoute.Comments}/${offerId}`;
await api.post<Review>(url, review);
const { data } = await api.get<Review[]>(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) {
Expand Down
1 change: 0 additions & 1 deletion src/store/slice/app-data.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}%`;
}
Loading