Skip to content

Commit

Permalink
Merge pull request #8 from mgmman/module6-task2
Browse files Browse the repository at this point in the history
  • Loading branch information
keksobot authored Nov 20, 2024
2 parents 95cde29 + a97e3e6 commit aa4e18c
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Helmet } from 'react-helmet-async';
import { Layout } from '../components/layout.tsx';
import { OfferGroup } from '../components/offer/offer-group.tsx';
import { Offer } from '../dataTypes/offer.ts';
import { Layout } from '../../components/layout.tsx';
import { OfferGroup } from '../../components/offer/offer-group.tsx';
import { Offer } from '../../dataTypes/offer.ts';

interface FavoritesPageProps {
offers: Offer[];
Expand Down
File renamed without changes.
54 changes: 17 additions & 37 deletions src/Pages/main-page.tsx → src/Pages/main-page/main-page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useState } from 'react';
import { Offer } from '../dataTypes/offer.ts';
import { OffersList } from '../components/offer/offers-list.tsx';
import { Layout } from '../components/layout.tsx';
import { Offer } from '../../dataTypes/offer.ts';
import { OffersList } from '../../components/offer/offers-list.tsx';
import { Layout } from '../../components/layout.tsx';
import { Helmet } from 'react-helmet-async';
import { Nullable } from 'vitest';
import { Map } from '../components/map/map.tsx';
import { useAppSelector } from '../store/store.ts';
import { CitiesList } from '../components/cities-list.tsx';
import { pluralizeAndCombine } from '../utils/string-utils.ts';
import { Map } from '../../components/map/map.tsx';
import { useAppSelector } from '../../store/store.ts';
import { CitiesList } from '../../components/cities-list.tsx';
import { pluralizeAndCombine } from '../../utils/string-utils.ts';
import { OfferSortSelect } from '../../components/offer/offer-sort-select.tsx';

export function MainPage(): React.JSX.Element {
const [activeOffer, setActiveOffer] = useState<Nullable<Offer>>(null);
const city = useAppSelector((state) => state.city);
const offers = useAppSelector((state) => state.offers).filter(
const unsortedOffers = useAppSelector((state) => state.offers).filter(
(offer) => offer.city.name === city.name,
);

const sort = useAppSelector((state) => state.sorting);
const offers = sort(unsortedOffers);
const offersCountCaption = offers.length === 0
? 'No places to stay available'
: `${pluralizeAndCombine('place', offers.length)} to stay in ${city.name}`;
return (
<div className="page page--gray page--main">
<Layout showFooter>
Expand All @@ -28,35 +34,9 @@ export function MainPage(): React.JSX.Element {
<section className="cities__places places">
<h2 className="visually-hidden">Places</h2>
<b className="places__found">
{pluralizeAndCombine('place', offers.length)} to stay in{' '}
{city.name}
{offersCountCaption}
</b>
<form className="places__sorting" action="#" method="get">
<span className="places__sorting-caption">Sort by</span>
<span className="places__sorting-type" tabIndex={0}>
Popular
<svg className="places__sorting-arrow" width="7" height="4">
<use xlinkHref="#icon-arrow-select"></use>
</svg>
</span>
<ul className="places__options places__options--custom places__options--opened">
<li
className="places__option places__option--active"
tabIndex={0}
>
Popular
</li>
<li className="places__option" tabIndex={0}>
Price: low to high
</li>
<li className="places__option" tabIndex={0}>
Price: high to low
</li>
<li className="places__option" tabIndex={0}>
Top rated first
</li>
</ul>
</form>
<OfferSortSelect/>
<OffersList
offers={offers}
onActiveOfferChange={(offer: Nullable<Offer>) =>
Expand Down
26 changes: 13 additions & 13 deletions src/Pages/offer-page.tsx → src/Pages/offer-page/offer-page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { Helmet } from 'react-helmet-async';
import { Layout } from '../components/layout.tsx';
import { OffersList } from '../components/offer/offers-list.tsx';
import { offerMocks } from '../mocks/offers.ts';
import { Reviews } from '../components/reviews/reviews.tsx';
import { reviewMocks } from '../mocks/reviews.ts';
import { Map } from '../components/map/map.tsx';
import { Layout } from '../../components/layout.tsx';
import { OffersList } from '../../components/offer/offers-list.tsx';
import { offerMocks } from '../../mocks/offers.ts';
import { Reviews } from '../../components/reviews/reviews.tsx';
import { reviewMocks } from '../../mocks/reviews.ts';
import { Map } from '../../components/map/map.tsx';
import React from 'react';
import { useParams } from 'react-router-dom';
import { OfferInsideItems } from '../components/offer/offer-inside-items.tsx';
import { detailedOfferMocks } from '../mocks/detailed-offer.ts';
import { OfferHost } from '../components/offer/offer-host.tsx';
import { capitalize, pluralizeAndCombine } from '../utils/string-utils.ts';
import { Rating } from '../components/rating.tsx';
import { OfferGallery } from '../components/offer/offer-gallery.tsx';
import { BookmarkButton } from '../components/bookmark-button.tsx';
import { OfferInsideItems } from '../../components/offer/offer-inside-items.tsx';
import { detailedOfferMocks } from '../../mocks/detailed-offer.ts';
import { OfferHost } from '../../components/offer/offer-host.tsx';
import { capitalize, pluralizeAndCombine } from '../../utils/string-utils.ts';
import { Rating } from '../../components/rating.tsx';
import { OfferGallery } from '../../components/offer/offer-gallery.tsx';
import { BookmarkButton } from '../../components/bookmark-button.tsx';

export function OfferPage(): React.JSX.Element {
const offerId = useParams().id;
Expand Down
8 changes: 4 additions & 4 deletions src/components/app.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { MainPage } from '../pages/main-page.tsx';
import { MainPage } from '../pages/main-page/main-page.tsx';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { LoginPage } from '../pages/login-page.tsx';
import { FavoritesPage } from '../pages/favorites-page.tsx';
import { OfferPage } from '../pages/offer-page.tsx';
import { LoginPage } from '../pages/login-page/login-page.tsx';
import { FavoritesPage } from '../pages/favorites-page/favorites-page.tsx';
import { OfferPage } from '../pages/offer-page/offer-page.tsx';
import { NotFoundPage } from '../pages/not-found-page/not-found-page.tsx';
import { AuthorizationWrapper } from './authorization-wrapper.tsx';
import { AppRoutes } from '../dataTypes/enums/app-routes.ts';
Expand Down
61 changes: 61 additions & 0 deletions src/components/offer/offer-sort-select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, { useState } from 'react';
import { Offer } from '../../dataTypes/offer.ts';
import { SortOffers } from '../../dataTypes/sort-offers.ts';
import { useAppDispatch } from '../../store/store.ts';
import { setSorting } from '../../store/actions.ts';

const sortingOptions: [string, SortOffers][] = [
['Popular', (offers: Offer[]) => offers],
[
'Price: low to high',
(offers: Offer[]) => offers.toSorted((a, b) => a.price - b.price),
],
[
'Price: high to low',
(offers: Offer[]) => offers.toSorted((a, b) => b.price - a.price),
],
[
'Top rated first',
(offers: Offer[]) => offers.toSorted((a, b) => b.rating - a.rating),
],
];

export function OfferSortSelect(): React.JSX.Element {
const [isOpen, setIsOpen] = useState(false);
const [sortingOption, setSortingOption] = useState('Popular');
const dispatch = useAppDispatch();
const handleSortChange = (sort: SortOffers, sortingOptionName: string) => {
setSortingOption(sortingOptionName);
dispatch(setSorting(sort));
setIsOpen(false);
};
return (
<form className="places__sorting" action="#" method="get">
<span className="places__sorting-caption">Sort by</span>
<span
className="places__sorting-type"
tabIndex={0}
onClick={() => setIsOpen(!isOpen)}
>
{sortingOption}
<svg className="places__sorting-arrow" width="7" height="4">
<use xlinkHref="#icon-arrow-select"></use>
</svg>
</span>
{isOpen && (
<ul className="places__options places__options--custom places__options--opened">
{sortingOptions.map(([name, sort]) => (
<li
className={`places__option ${name === sortingOption ? 'places__option--active' : ''}`}
tabIndex={0}
key={name}
onClick={() => handleSortChange(sort, name)}
>
{name}
</li>
))}
</ul>
)}
</form>
);
}
3 changes: 3 additions & 0 deletions src/dataTypes/sort-offers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Offer } from './offer.ts';

export type SortOffers = (offers: Offer[]) => Offer[];
31 changes: 27 additions & 4 deletions src/mocks/offers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Offer} from '../dataTypes/offer.ts';
import {RoomType} from '../dataTypes/enums/room-type.ts';
import { Offer } from '../dataTypes/offer.ts';
import { RoomType } from '../dataTypes/enums/room-type.ts';

export const offerMocks: Offer[] = [
{
Expand Down Expand Up @@ -45,7 +45,7 @@ export const offerMocks: Offer[] = [
},
isFavorite: false,
isPremium: true,
rating: 4,
rating: 3,
previewImage: 'apartment-02.jpg',
},
{
Expand Down Expand Up @@ -91,7 +91,30 @@ export const offerMocks: Offer[] = [
},
isFavorite: true,
isPremium: true,
rating: 4,
rating: 5,
previewImage: 'apartment-02.jpg',
},
{
id: '6af6f711-c28d-4121-82cd-e0b462a27f44',
title: 'amogus',
type: RoomType.Apartment,
price: 88,
city: {
name: 'Paris',
location: {
latitude: 52.35514938496378,
longitude: 4.673877537499948,
zoom: 8,
},
},
location: {
latitude: 48.9,
longitude: 2.4,
zoom: 8,
},
isFavorite: true,
isPremium: true,
rating: 5,
previewImage: 'apartment-02.jpg',
},
];
7 changes: 5 additions & 2 deletions src/store/actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { createAction } from '@reduxjs/toolkit';
import { City } from '../dataTypes/city.ts';
import { Offer } from '../dataTypes/offer.ts';
import { SortOffers } from '../dataTypes/sort-offers.ts';

export const changeCity = createAction<City>('mainPage/changeCity');
export const changeCity = createAction<City>('offers/changeCity');

export const fillOffers = createAction<Offer[]>('mainPage/fillOffers');
export const setOffers = createAction<Offer[]>('offers/setOffers');

export const setSorting = createAction<SortOffers>('offers/setSorting');
9 changes: 7 additions & 2 deletions src/store/store.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import { configureStore, createReducer } from '@reduxjs/toolkit';
import { changeCity, fillOffers } from './actions.ts';
import {changeCity, setOffers, setSorting} from './actions.ts';
import { offerMocks } from '../mocks/offers.ts';
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import { AppDispatch, State } from '../dataTypes/store-types.ts';
import { PARIS } from '../consts/cities.ts';
import {Offer} from '../dataTypes/offer.ts';

const initialState = {
city: PARIS,
offers: offerMocks,
sorting: (offers: Offer[]) => offers,
};

const reducer = createReducer(initialState, (builder) => {
builder
.addCase(changeCity, (state, action) => {
state.city = action.payload;
})
.addCase(fillOffers, (state, action) => {
.addCase(setOffers, (state, action) => {
state.offers = action.payload;
})
.addCase(setSorting, (state, action) => {
state.sorting = action.payload;
});
});

Expand Down

0 comments on commit aa4e18c

Please sign in to comment.