Skip to content

Commit

Permalink
final version
Browse files Browse the repository at this point in the history
  • Loading branch information
mashkin.m committed Jan 13, 2025
1 parent 878f2b0 commit cdeb6aa
Show file tree
Hide file tree
Showing 65 changed files with 1,938 additions and 1,283 deletions.
23 changes: 22 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"react-dom": "18.2.0",
"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": "^11.0.2"
},
"devDependencies": {
"@jedmao/redux-mock-store": "3.0.5",
Expand Down
5 changes: 5 additions & 0 deletions src/browser-history.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createBrowserHistory } from 'history';

const browserHistory = createBrowserHistory();

export default browserHistory;
41 changes: 23 additions & 18 deletions src/components/app.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
import MainScreen from '../pages/main.tsx';
import {BrowserRouter, Route, Routes} from 'react-router-dom';
import {AppRoute, AuthorizationStatus} from '../const';
import Main from '../pages/main.tsx';
import NotFound from '../pages/not-found.tsx';
import { Route, Routes } from 'react-router-dom';
import { AppRoutes } from '../consts/consts.ts';
import Login from '../pages/login.tsx';
import Favourites from '../pages/favourites.tsx';
import Offer from '../pages/offer.tsx';
import NotFound from '../pages/not_found.tsx';
import PrivateRoute from './private-route.tsx';
import {OfferCardType} from '../types/offer_card_type.ts';
import { useAppSelector } from '../store/hooks.ts';
import { LoadingScreen } from './loading-screen.tsx';
import HistoryRouter from './history-router.tsx';
import browserHistory from '../browser-history.ts';
import { isOffersDataStillLoading } from '../store/data-process/data-process.selectors.ts';

type AppProps = {
offerCards: OfferCardType[];
}
function App(): JSX.Element {
const isDataStillLoading = useAppSelector(isOffersDataStillLoading);
if (isDataStillLoading) {
return <LoadingScreen />;
}

function App({offerCards}: AppProps): JSX.Element {
return (
<BrowserRouter>
<HistoryRouter history={browserHistory}>
<Routes>
<Route path={AppRoute.Main} element={<MainScreen offerCards={offerCards}/>}/>
<Route path={AppRoute.Login} element={<Login/>}/>
<Route path={AppRoutes.Main} element={<Main />} />
<Route path={AppRoutes.Login} element={<Login />} />
<Route
path={AppRoute.Favourites}
path={AppRoutes.Favorites}
element={
<PrivateRoute authorizationStatus={AuthorizationStatus.Auth}>
<Favourites favourites={offerCards}/>
<PrivateRoute>
<Favourites />
</PrivateRoute>
}
/>
<Route path={AppRoute.Offer} element={<Offer/>}/>
<Route path="*" element={<NotFound/>}/>
<Route path={AppRoutes.Offer} element={<Offer />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
</HistoryRouter>
);
}

Expand Down
40 changes: 40 additions & 0 deletions src/components/cities-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { memo, useCallback } from 'react';
import { CityInfoList, CityData } from '../consts/consts.ts';
import { getCurrentCityName } from '../store/data-process/data-process.selectors.ts';
import { changeCityAction } from '../store/data-process/data-process.slice.ts';
import { useAppDispatch, useAppSelector } from '../store/hooks.ts';
import { City } from '../types/city.ts';

function CitiesList(): JSX.Element {
const dispatch = useAppDispatch();
const currentCityName = useAppSelector(getCurrentCityName);
const handleCityClick = useCallback(
(city: City) => dispatch(changeCityAction(city)),
[dispatch]
);
return (
<div className="tabs">
<section className="locations container">
<ul className="locations__list tabs__list">
{CityInfoList.map((city: City) => (
<li key={city.name} className="locations__item">
<a
className={`locations__item-link tabs__item${
currentCityName === city.name ? ' tabs__item--active' : ''
}`}
onClick={() => {
handleCityClick(CityData[city.name]);
}}
>
<span>{city.name}</span>
</a>
</li>
))}
</ul>
</section>
</div>
);
}

const MemorizedCitiesList = memo(CitiesList);
export default MemorizedCitiesList;
52 changes: 0 additions & 52 deletions src/components/favourite.tsx

This file was deleted.

18 changes: 0 additions & 18 deletions src/components/favourite_list.tsx

This file was deleted.

78 changes: 78 additions & 0 deletions src/components/header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Link } from 'react-router-dom';
import { AppRoutes } from '../consts/consts.ts';
import { useAppDispatch, useAppSelector } from '../store/hooks.ts';
import { logoutAction } from '../store/api-actions.ts';
import {
getAuthCheckedStatus,
getFavoriteOffersCount,
getUserData,
} from '../store/user-process/user-process.selectors.ts';
import { memo } from 'react';

function Header(): JSX.Element {
const favoritesCount = useAppSelector(getFavoriteOffersCount);
const isAuthorized = useAppSelector(getAuthCheckedStatus);
const userData = useAppSelector(getUserData);
const dispatch = useAppDispatch();

return (
<header className="header">
<div className="container">
<div className="header__wrapper">
<div className="header__left">
<Link className="header__logo-link" to="/">
<img
className="header__logo"
src="img/logo.svg"
alt="6 cities logo"
width="81"
height="41"
/>
</Link>
</div>
<nav className="header__nav">
<ul className="header__nav-list">
{isAuthorized && (
<li className="header__nav-item user">
<Link
className="header__nav-link header__nav-link--profile"
to={AppRoutes.Favorites}
>
<div className="header__avatar-wrapper user__avatar-wrapper"></div>
<span className="header__user-name user__name">
{userData?.name}
</span>
<span className="header__favorite-count">
{favoritesCount}
</span>
</Link>
</li>
)}
<li className="header__nav-item">
{isAuthorized ? (
<Link
className="header__nav-link"
onClick={(evt) => {
evt.preventDefault();
dispatch(logoutAction());
}}
to={AppRoutes.Main}
>
<span className="header__signout">Sign out</span>
</Link>
) : (
<Link className="header__nav-link" to={AppRoutes.Login}>
<span className="header__signout">Sign in</span>
</Link>
)}
</li>
</ul>
</nav>
</div>
</div>
</header>
);
}

const MemorizedHeader = memo(Header);
export default MemorizedHeader;
31 changes: 31 additions & 0 deletions src/components/history-router.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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;
3 changes: 3 additions & 0 deletions src/components/loading-screen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function LoadingScreen(): JSX.Element {
return <p>Loading ...</p>;
}
28 changes: 28 additions & 0 deletions src/components/location-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Link } from 'react-router-dom';
import { AppRoutes, CityInfoList } from '../consts/consts.ts';
import { changeCityAction } from '../store/data-process/data-process.slice.ts';
import { useAppDispatch } from '../store/hooks.ts';
import { redirectToRoute } from '../store/actions.ts';

export function LocationItem(): JSX.Element {
const randomCityIndex = Math.floor(Math.random() * CityInfoList.length);
const randomCity = CityInfoList[randomCityIndex];
const dispatch = useAppDispatch();
const handleCityClick = () => {
dispatch(changeCityAction(randomCity));
dispatch(redirectToRoute(AppRoutes.Main));
};
return (
<section className="locations locations--login locations--current">
<div className="locations__item">
<Link
className="locations__item-link"
to={AppRoutes.Main}
onClick={handleCityClick}
>
<span>{randomCity.name}</span>
</Link>
</div>
</section>
);
}
Loading

0 comments on commit cdeb6aa

Please sign in to comment.