diff --git a/src/components/cards-sorting-options/cards-sorting-options.tsx b/src/components/cards-sorting-options/cards-sorting-options.tsx new file mode 100644 index 0000000..732c88d --- /dev/null +++ b/src/components/cards-sorting-options/cards-sorting-options.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { useAppDispatch, useAppSelector } from '../../hooks'; +import { filters } from '../../utils'; +import { changeSortOptions } from '../../store/action'; + +function CardsSortingOptions(): JSX.Element { + const chosenSortType = useAppSelector((state) => state.sortType); + const dispatch = useAppDispatch(); + const [isSortOpened, setIsSortOpened] = React.useState(false); + return ( +
+ Sort by + setIsSortOpened(!isSortOpened)}> + {chosenSortType} + + + + + +
+ ); +} +export default CardsSortingOptions; diff --git a/src/components/map/map.tsx b/src/components/map/map.tsx index 12606b5..4d4b501 100644 --- a/src/components/map/map.tsx +++ b/src/components/map/map.tsx @@ -1,19 +1,32 @@ import React, { useEffect } from 'react'; -import { Marker, layerGroup } from 'leaflet'; +import { Icon, Marker, layerGroup } from 'leaflet'; import useMap from '../../hooks/use-map'; -import { City } from '../../types/city'; import { Point } from '../../types/point'; import 'leaflet/dist/leaflet.css'; +import { useAppSelector } from '../../hooks'; +import { URL_MARKER_CURRENT, URL_MARKER_STANDART } from '../../const'; type MapProps = { - city: City; points: Point[]; - selectedPoint?: Point; } +const currentIcon = new Icon({ + iconUrl: URL_MARKER_CURRENT, + iconSize: [32, 32], + iconAnchor: [20, 40] +}); + +const standartIcon = new Icon({ + iconUrl: URL_MARKER_STANDART, + iconSize: [32, 32], + iconAnchor: [20, 40] +}); + function Map(props: MapProps): JSX.Element { - const {city, points, selectedPoint} = props; + const city = useAppSelector((state) => state.city); + const highlightedMarker = useAppSelector((state) => state.highlightedMarker); + const {points} = props; const mapRef = React.useRef(null); const map = useMap(mapRef, city); @@ -25,15 +38,22 @@ function Map(props: MapProps): JSX.Element { lat: point.lat, lng: point.lng }); + let icon; + if (point === highlightedMarker) { + icon = currentIcon; + } else { + icon = standartIcon; + } marker + .setIcon(icon) .addTo(markerLayer); }); - + map.setView([city.lat, city.lng], 11); return () => { map.removeLayer(markerLayer); }; } - }, [map, points, selectedPoint]); + }, [map, points, highlightedMarker, city]); return
; } diff --git a/src/components/offer-card/offer-card.tsx b/src/components/offer-card/offer-card.tsx index eb4aec7..5e69159 100644 --- a/src/components/offer-card/offer-card.tsx +++ b/src/components/offer-card/offer-card.tsx @@ -1,3 +1,5 @@ +import { useAppDispatch } from '../../hooks'; +import { changeHighlightedMarker } from '../../store/action'; import { Offer } from '../../types/offer'; import { Link } from 'react-router-dom'; @@ -7,8 +9,12 @@ type OfferProps = { } function OfferCard({offer, cardType}: OfferProps): JSX.Element { + const dispatch = useAppDispatch(); return ( -
+
dispatch(changeHighlightedMarker(offer.point))} + onMouseLeave={() => dispatch(changeHighlightedMarker(undefined))} + > {offer.isPremium ? (
Premium diff --git a/src/components/offer-list/offer-list.tsx b/src/components/offer-list/offer-list.tsx index 296b403..954629a 100644 --- a/src/components/offer-list/offer-list.tsx +++ b/src/components/offer-list/offer-list.tsx @@ -1,4 +1,5 @@ -import { listToCard, typeOfCardList } from '../../const'; +import { getSortedOffers, listToCard, typeOfCardList } from '../../utils'; +import { useAppSelector } from '../../hooks'; import { Offer } from '../../types/offer'; import OfferCard from '../offer-card/offer-card'; @@ -8,10 +9,11 @@ type OfferListProps = { }; function OfferList({offers, listType}: OfferListProps): JSX.Element { + const chosenSortType = useAppSelector((state) => state.sortType); const type = listToCard.get(listType); return (
- {offers.map((offer) => ( + {getSortedOffers(offers, chosenSortType)?.map((offer) => ( ))}
diff --git a/src/const.ts b/src/const.ts index 194543a..0687db9 100644 --- a/src/const.ts +++ b/src/const.ts @@ -1,7 +1,3 @@ -export const Settings = { - placesCount: 55, -} as const; - export enum AppRoute { Main = '/', Login = '/login', @@ -15,16 +11,8 @@ export enum AuthorizationStatus { Unknown = 'UNKNOWN', } -export enum typeOfCardList { - favourites = 'favorites__places', - nearest = 'near-places__list places__list', - standart = 'cities__places-list places__list tabs__content', -} +export const URL_MARKER_CURRENT = + '/img/pin-active.svg'; -export const listToCard = new Map( - [ - [typeOfCardList.favourites, 'favorites__card place-card'], - [typeOfCardList.nearest, 'near-places__card place-card'], - [typeOfCardList.standart, 'cities__card place-card'] - ] -); +export const URL_MARKER_STANDART = + '/img/pin.svg'; diff --git a/src/mocks/cities.ts b/src/mocks/cities.ts index 3fd1a29..10f3ffe 100644 --- a/src/mocks/cities.ts +++ b/src/mocks/cities.ts @@ -3,18 +3,18 @@ import { City } from '../types/city'; export const CITIES: City[] = [ { title: 'Paris', - lat: 52.3740300, - lng: 4.8896900 + lat: 48.864716, + lng: 2.349014 }, { title: 'Brussels', - lat: 52.3740300, - lng: 4.8896900 + lat: 50.85034, + lng: 4.35171 }, { title: 'Cologne', - lat: 52.3740300, - lng: 4.8896900 + lat: 50.935173, + lng: 6.953101 }, { title: 'Amsterdam', @@ -23,12 +23,12 @@ export const CITIES: City[] = [ }, { title: 'Hamburg', - lat: 52.3740300, - lng: 4.8896900 + lat: 53.551086, + lng: 9.993682 }, { title: 'Dusseldorf', - lat: 52.3740300, - lng: 4.8896900 + lat: 51.233334, + lng: 6.783333 }, ]; diff --git a/src/mocks/offers.ts b/src/mocks/offers.ts index 17b7543..81a94a3 100644 --- a/src/mocks/offers.ts +++ b/src/mocks/offers.ts @@ -27,7 +27,7 @@ export const OFFERS: Offer[] = [ isFavorite: true, rating: 4, reviews: [REVIEWS[1]], - city: CITIES[1], + city: CITIES[0], point: POINTS[1] }, { diff --git a/src/mocks/points.ts b/src/mocks/points.ts index 685d8d7..9993ea8 100644 --- a/src/mocks/points.ts +++ b/src/mocks/points.ts @@ -2,16 +2,16 @@ import { Point } from '../types/point'; export const POINTS: Point[] = [ { - lat: 52.3909553943508, - lng: 4.85309666406198 + lat: 48.856716, + lng: 2.369014 }, { - lat: 52.3609553943508, - lng: 4.85309666406198 + lat: 48.876716, + lng: 2.349014 }, { - lat: 52.3909553943508, - lng: 4.929309666406198 + lat: 48.826716, + lng: 2.319014 }, { lat: 52.3809553943508, diff --git a/src/pages/favorites-screen/favorites-screen.tsx b/src/pages/favorites-screen/favorites-screen.tsx index c52a6ed..2b222ab 100644 --- a/src/pages/favorites-screen/favorites-screen.tsx +++ b/src/pages/favorites-screen/favorites-screen.tsx @@ -1,6 +1,6 @@ import { Offer } from '../../types/offer'; import { Link } from 'react-router-dom'; -import { typeOfCardList } from '../../const'; +import { typeOfCardList } from '../../utils'; import OfferList from '../../components/offer-list/offer-list'; type FavoritesScreenProps = { diff --git a/src/pages/main-screen/main-screen.tsx b/src/pages/main-screen/main-screen.tsx index 4b066f2..96993a8 100644 --- a/src/pages/main-screen/main-screen.tsx +++ b/src/pages/main-screen/main-screen.tsx @@ -1,9 +1,10 @@ import OfferList from '../../components/offer-list/offer-list'; import { Link } from 'react-router-dom'; import Map from '../../components/map/map'; -import { typeOfCardList } from '../../const'; +import { typeOfCardList } from '../../utils'; import { useAppSelector } from '../../hooks'; import CityList from '../../components/city-list/city-list'; +import CardsSortingOptions from '../../components/cards-sorting-options/cards-sorting-options'; function MainScreen(): JSX.Element { const [city, offers] = useAppSelector((state) => [state.city, state.offers]); @@ -55,26 +56,12 @@ function MainScreen(): JSX.Element {

Places

{chosenOffers.length} places to stay in {city.title} -
- Sort by - - Popular - - - - -
    -
  • Popular
  • -
  • Price: low to high
  • -
  • Price: high to low
  • -
  • Top rated first
  • -
-
+
- +
diff --git a/src/pages/offer-screen/offer-screen.tsx b/src/pages/offer-screen/offer-screen.tsx index 06af9b0..2f8a840 100644 --- a/src/pages/offer-screen/offer-screen.tsx +++ b/src/pages/offer-screen/offer-screen.tsx @@ -2,11 +2,10 @@ import { Link } from 'react-router-dom'; import ReviewsList from '../../components/reviews-list/reviews-list'; import { OFFERS } from '../../mocks/offers'; import { Offer } from '../../types/offer'; -import { CITIES } from '../../mocks/cities'; import { POINTS } from '../../mocks/points'; import Map from '../../components/map/map'; import OfferList from '../../components/offer-list/offer-list'; -import { typeOfCardList } from '../../const'; +import { typeOfCardList } from '../../utils'; type OfferScreenProps = { offer: Offer; @@ -168,7 +167,7 @@ function OfferScreen({offer}: OfferScreenProps): JSX.Element {
- +
diff --git a/src/store/action.ts b/src/store/action.ts index fb1f884..30000ad 100644 --- a/src/store/action.ts +++ b/src/store/action.ts @@ -1,8 +1,17 @@ import { createAction } from '@reduxjs/toolkit'; import { City } from '../types/city'; +import { Point } from '../types/point'; export const getOffers = createAction('OFFERS_GET'); export const changeCity = createAction('CITY_CHANGE', (value: City) => ({ payload: value })); + +export const changeSortOptions = createAction('CHANGE_SORT_OPTIONS', (value: string) => ({ + payload: value +})); + +export const changeHighlightedMarker = createAction('CHANGE_HIGHLIGHTED_MARKER', (value: Point | undefined) => ({ + payload: value +})); diff --git a/src/store/reducer.ts b/src/store/reducer.ts index 16f7a07..f131dd1 100644 --- a/src/store/reducer.ts +++ b/src/store/reducer.ts @@ -3,16 +3,23 @@ import { CITIES } from '../mocks/cities'; import { OFFERS } from '../mocks/offers'; import { City } from '../types/city'; import { Offer } from '../types/offer'; -import { changeCity, getOffers } from './action'; +import { changeCity, changeHighlightedMarker, changeSortOptions, getOffers } from './action'; +import { filters } from '../utils'; +import { Point } from '../types/point'; + type StateType = { city: City; offers: Offer[]; + sortType: string; + highlightedMarker?: Point; } const initialState: StateType = { city: CITIES[0], - offers: OFFERS + offers: OFFERS, + sortType: filters.POPULAR, + highlightedMarker: undefined }; const reducer = createReducer(initialState, (builder) => { @@ -22,6 +29,12 @@ const reducer = createReducer(initialState, (builder) => { }) .addCase(changeCity, (state, action) => { state.city = action.payload; + }) + .addCase(changeSortOptions, (state, action) => { + state.sortType = action.payload; + }) + .addCase(changeHighlightedMarker, (state, action) => { + state.highlightedMarker = action.payload; }); }); diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..cd9dea5 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,39 @@ +import { Offer } from './types/offer'; + +export enum typeOfCardList { + favourites = 'favorites__places', + nearest = 'near-places__list places__list', + standart = 'cities__places-list places__list tabs__content', + } + +export const listToCard = new Map( + [ + [typeOfCardList.favourites, 'favorites__card place-card'], + [typeOfCardList.nearest, 'near-places__card place-card'], + [typeOfCardList.standart, 'cities__card place-card'] + ] +); + +export const filters = { + POPULAR: 'Popular', + LOW_TO_HIGH: 'Price: low to high', + HIGH_TO_LOW: 'Price: high to low', + TOP_RATED: 'Top rated first' +}; + +const sortFunctions = { + [filters.LOW_TO_HIGH]: (offers: Offer[]) => offers.sort((offerA, offerB) => offerA.price - offerB.price), + [filters.HIGH_TO_LOW]: (offers: Offer[]) => offers.sort((offerA, offerB) => offerB.price - offerA.price), + [filters.TOP_RATED]: (offers: Offer[]) => offers.sort((offerA, offerB) => offerB.rating - offerA.rating) +}; + +export const getSortedOffers = ( + offers: Offer[], + sortType: string +) => { + if (sortFunctions[sortType]) { + return sortFunctions[sortType](offers); + } else if (sortType === filters.POPULAR) { + return offers; + } +};