Skip to content
Merged
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
5,367 changes: 3,923 additions & 1,444 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"react-dom": "19.0.0-rc.1",
"react-helmet-async": "1.3.0",
"react-redux": "8.1.3",
"react-router-dom": "6.16.0"
"react-router-dom": "6.16.0",
"react-toastify": "^10.0.5"
},
"devDependencies": {
"@jedmao/redux-mock-store": "3.0.5",
Expand Down
5 changes: 5 additions & 0 deletions src/browser-history.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createBrowserHistory } from 'history';

const browserHistory = createBrowserHistory();

export default browserHistory;
12 changes: 7 additions & 5 deletions src/components/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { Route, Routes } from 'react-router-dom';
import Layout from '../../layout/Layout';
import { useAppSelector } from '../../hooks';
import { AppRoute, AuthorizationStatus } from '../../const/const.ts';

import PrivateRoute from '../../pages/private-route/private-route';
import LoadingScreen from '../../pages/loading-screen/loading-screen';
Expand All @@ -10,11 +11,12 @@ import Favorites from '../../pages/favorites/favorites';
import Login from '../../pages/login/login';
import PageNotFound from '../../pages/page-not-found/page-not-found';

import HistoryRouter from '../history-route/history-route';
import browserHistory from '../../browser-history';

import { OffersType } from '../../types/offers';
import { ReviewsType } from '../../types/reviews';

import { reviewsMock } from '../../mocks/reviews.ts';
import { AppRoute, AuthorizationStatus } from '../../const/const';

function App() {
const offers: OffersType = useAppSelector((state) => state.main.currentOffers);
Expand All @@ -27,7 +29,7 @@ function App() {
}

return (
<BrowserRouter>
<HistoryRouter history={browserHistory}>
<Routes>
<Route path={AppRoute.Root} element={<Layout />}>
<Route index element={<Main offers={offers} />} />
Expand All @@ -47,7 +49,7 @@ function App() {
<Route path="*" element={<PageNotFound />} />
</Route>
</Routes>
</BrowserRouter>
</HistoryRouter>
);
}
export default App;
15 changes: 15 additions & 0 deletions src/components/cities-empty/cities-empty.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function CitiesEmpty() {
return (
<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>
);
}

export default CitiesEmpty;
31 changes: 0 additions & 31 deletions src/components/error-message/error-message.css

This file was deleted.

10 changes: 0 additions & 10 deletions src/components/error-message/error-message.tsx

This file was deleted.

44 changes: 38 additions & 6 deletions src/components/header/header.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import { Link } from 'react-router-dom';
import Logo from '../logo/logo';
import { AuthorizationStatus } from '../../const/const';
import { AppRoute } from '../../const/const';
import { useAppSelector, useAppDispatch } from '../../hooks';
import { logoutAction } from '../../store/api-action';
import { getEmail } from '../../services/token';

function Header() {
const dispatch = useAppDispatch(); // 👈 Добавьте эту строку
const authorizationStatus = useAppSelector((state) => state.main.authorizationStatus);
const userLogin = getEmail();

return (
<header className="header">
<div className="container">
Expand All @@ -14,12 +22,36 @@ function Header() {
</div>
<nav className="header__nav">
<ul className="header__nav-list">
<li className="header__nav-item user">
<Link to={AppRoute.Login} className="header__nav-link header__nav-link--profile">
<div className="header__avatar-wrapper user__avatar-wrapper"></div>
<span className="header__login">Sign in</span>
</Link>
</li>
{authorizationStatus === AuthorizationStatus.Auth ? (
<>
<li className="header__nav-item user">
<a className="header__nav-link header__nav-link--profile" href="#">
<div className="header__avatar-wrapper user__avatar-wrapper"></div>
<span className="header__user-name user__name">{userLogin}</span>
<span className="header__favorite-count">3</span>
</a>
</li>
<li className="header__nav-item">
<Link
className="header__nav-link"
to="/"
onClick={(evt) => {
evt.preventDefault();
dispatch(logoutAction());
}}
>
<span className="header__signout">Sign out</span>
</Link>
</li>
</>
) : (
<li className="header__nav-item user">
<Link to={AppRoute.Login} className="header__nav-link header__nav-link--profile">
<div className="header__avatar-wrapper user__avatar-wrapper"></div>
<span className="header__login">Sign in</span>
</Link>
</li>
)}
</ul>
</nav>
</div>
Expand Down
26 changes: 26 additions & 0 deletions src/components/history-route/history-route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useState, useLayoutEffect } from 'react';
import { Router } from 'react-router-dom';
import type { BrowserHistory } from 'history';

export interface HistoryRouterProps {
history: BrowserHistory;
basename?: string;
children?: React.ReactNode;
}

function HistoryRouter({ basename, children, history }: HistoryRouterProps) {
const [state, setState] = useState({
action: history.action,
location: history.location,
});

useLayoutEffect(() => history.listen(setState), [history]);

return (
<Router basename={basename} location={state.location} navigationType={state.action} navigator={history}>
{children}
</Router>
);
}

export default HistoryRouter;
4 changes: 2 additions & 2 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { ToastContainer } from 'react-toastify';
import App from './components/app/app';
import ErrorMessage from './components/error-message/error-message';
import { store } from './store/index.ts';
import { fetchOfferAction, checkAuthAction } from './store/api-action.ts';

Expand All @@ -14,7 +14,7 @@ const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
<React.StrictMode>
<Provider store={store}>
<ErrorMessage />
<ToastContainer />
<App />
</Provider>
</React.StrictMode>
Expand Down
45 changes: 29 additions & 16 deletions src/pages/login/login.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
import { useRef, FormEvent } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAppDispatch } from '../../hooks';
import { loginAction } from '../../store/api-action';
import { AppRoute } from '../../const/const';

function Login() {
const loginRef = useRef<HTMLInputElement | null>(null);
const passwordRef = useRef<HTMLInputElement | null>(null);

const dispatch = useAppDispatch();
const navigate = useNavigate();

const handleSubmit = (evt: FormEvent<HTMLFormElement>) => {
evt.preventDefault();

if (loginRef.current !== null && passwordRef.current !== null) {
dispatch(
loginAction({
login: loginRef.current.value,
password: passwordRef.current.value,
})
);
}
};

return (
<main className="page__main page__main--login">
<div className="page__login-container container">
<section className="login">
<h1 className="login__title">Sign in</h1>
<form className="login__form form" action="#" method="post">
<form className="login__form form" action="#" method="post" onSubmit={handleSubmit}>
<div className="login__input-wrapper form__input-wrapper">
<label className="visually-hidden">E-mail</label>
<input
className="login__input form__input"
type="email"
name="email"
placeholder="Email"
required
/>
<input ref={loginRef} className="login__input form__input" type="email" name="email" placeholder="Email" required />
</div>
<div className="login__input-wrapper form__input-wrapper">
<label className="visually-hidden">Password</label>
<input
className="login__input form__input"
type="password"
name="password"
placeholder="Password"
required
/>
<input ref={passwordRef} className="login__input form__input" type="password" name="password" placeholder="Password" required />
</div>
<button className="login__submit form__submit button" type="submit">
Sign in
Expand All @@ -32,7 +45,7 @@ function Login() {
</section>
<section className="locations locations--login locations--current">
<div className="locations__item">
<a className="locations__item-link" href="#">
<a className="locations__item-link" href="#" onClick={() => navigate(AppRoute.Favorites)}>
<span>Amsterdam</span>
</a>
</div>
Expand Down
15 changes: 10 additions & 5 deletions src/pages/main/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import LocationsList from '../locations-list/locations-list';
import Map from '../../components/map/map';
import { OffersType, OfferType, City } from '../../types/offers';
import { INITIAL_CITY } from '../../const/const';
import CitiesEmpty from '../../components/cities-empty/cities-empty';

type MainProps = {
offers: OffersType;
Expand All @@ -28,12 +29,16 @@ function Main({ offers }: MainProps) {
<LocationsList currentCity={currentCity} offers={offers} onDataCitySend={handleCityClick} />
</div>
<div className="cities">
<div className="cities__places-container container">
<CitiesPlaces city={currentCity} offers={offers} onCardHover={handleCardHover} />
<div className="cities__right-section">
<Map city={currentCity} points={offers} selectedOfferId={activeOffer?.id} />
{offers.length < 1 ? (
<CitiesEmpty />
) : (
<div className="cities__places-container container">
<CitiesPlaces city={currentCity} offers={offers} onCardHover={handleCardHover} />
<div className="cities__right-section">
<Map city={currentCity} points={offers} selectedOfferId={activeOffer?.id} />
</div>
</div>
</div>
)}
</div>
</main>
);
Expand Down
12 changes: 4 additions & 8 deletions src/pages/private-route/private-route.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { Navigate } from 'react-router-dom';
import { AppRoute, AuthorizationStatus } from '../../const/const';
import { useAppSelector } from '../../hooks';

type PrivateRouteProps = {
authorizationStatus: AuthorizationStatus;
children: JSX.Element;
};

function PrivateRoute(props: PrivateRouteProps) {
const { authorizationStatus, children } = props;

return authorizationStatus === AuthorizationStatus.Auth ? (
children
) : (
<Navigate to={AppRoute.Login} />
);
const { children } = props;
const authorizationStatus = useAppSelector((state) => state.main.authorizationStatus);
return authorizationStatus === AuthorizationStatus.Auth ? children : <Navigate to={AppRoute.Login} />;
}
export default PrivateRoute;
6 changes: 4 additions & 2 deletions src/services/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios, { AxiosInstance, InternalAxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
import { StatusCodes } from 'http-status-codes';
import { getToken } from './token';
import { processErrorHandle } from './process-error-handle';
import { toast } from 'react-toastify';

const BACKEND_URL = 'https://15.design.htmlacademy.pro/six-cities/';

Expand Down Expand Up @@ -42,7 +42,9 @@ export const createAPI = (): AxiosInstance => {
if (error.response && shouldDisplayError(error.response)) {
const detailMessage = error.response.data;

processErrorHandle(detailMessage.message);
if (detailMessage?.message) {
toast.error(detailMessage.message);
}
}

throw error;
Expand Down
19 changes: 11 additions & 8 deletions src/services/token.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
const AUTH_TOKEN_KEY_NAME = 'guess-melody-token';
const AUTH_TOKEN_KEY_NAME = 'six-cities-token';
const USER_EMAIL_KEY_NAME = 'six-cities-email';

export type Token = string;

export const getToken = (): Token => {
const token = localStorage.getItem(AUTH_TOKEN_KEY_NAME);
return token ?? '';
export const saveToken = (token: string): void => {
localStorage.setItem(AUTH_TOKEN_KEY_NAME, token);
};

export const saveToken = (token: Token): void => {
localStorage.setItem(AUTH_TOKEN_KEY_NAME, token);
export const getToken = (): string | null => localStorage.getItem(AUTH_TOKEN_KEY_NAME);

export const saveEmail = (email: string): void => {
localStorage.setItem(USER_EMAIL_KEY_NAME, email);
};

export const getEmail = (): string | null => localStorage.getItem(USER_EMAIL_KEY_NAME);

export const dropToken = (): void => {
localStorage.removeItem(AUTH_TOKEN_KEY_NAME);
localStorage.removeItem(USER_EMAIL_KEY_NAME);
};
Loading
Loading