diff --git a/src/components/FilterOffers/FilterOffer.tsx b/src/components/FilterOffers/FilterOffer.tsx new file mode 100644 index 0000000..c1c6c0f --- /dev/null +++ b/src/components/FilterOffers/FilterOffer.tsx @@ -0,0 +1,51 @@ +import React, { useState } from 'react'; +import { SortName } from '../../types/types'; + +type FilterOfferProps = { + currentSort: SortName; + onSortChange: (sortType: SortName) => void; +} + +const SORT_TYPES = [ + { label: 'Popular', value: SortName.popular }, + { label: 'Price: low to high', value: SortName.lowToHigh }, + { label: 'Price: high to low', value: SortName.highToLow }, + { label: 'Top rated first', value: SortName.topRated }, +]; + +const SORT_ACTIVE = 'places__option--active'; + +export const FilterOffer: React.FC = ({ + currentSort, + onSortChange, +}) => { + const [isActive, setActive] = useState(false); + + return ( +
+ Sort by + setActive((prevstate) => !prevstate)} + > + {SORT_TYPES.find((sort) => sort.value === currentSort)?.label || ''} + + + + + {isActive && + } +
+ ); +}; diff --git a/src/components/MainPage/MainPage.tsx b/src/components/MainPage/MainPage.tsx index 33cbf7c..772925e 100644 --- a/src/components/MainPage/MainPage.tsx +++ b/src/components/MainPage/MainPage.tsx @@ -1,10 +1,12 @@ import {FC} from 'react'; +import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import OfferList from '../Offer/OfferList'; import { useAppDispatch } from '../../hooks'; -import { OfferObject,AppRoute, City, CardCssNameList } from '../../types/types'; +import { OfferObject,AppRoute, City, CardCssNameList, SortName} from '../../types/types'; import { changeCity } from '../../action'; import { ListCities } from '../../components/CityList/CityList'; +import { FilterOffer } from '../FilterOffers/FilterOffer'; import Map from '../Map/Map'; type MainPageProps = { offers: OfferObject[]; @@ -23,7 +25,21 @@ export const MainPage : FC = ({ dispatch(changeCity(cityName)); // dispatch(fillOffers()); }; + const [activeOffer, setActiveOffer] = useState(null); + const [sortType, setSortType] = useState(SortName.popular); + const sortedOffers = offers.filter((a) =>a.city.name === currentCity.title).slice().sort((a, b) => { + switch (sortType) { + case SortName.lowToHigh: + return a.price - b.price; + case SortName.highToLow: + return b.price - a.price; + case SortName.topRated: + return b.rating - a.rating; + default: + return 0; + } + }); return (
@@ -70,28 +86,17 @@ export const MainPage : FC = ({

Places

{offers.filter((a) =>a.city.name === currentCity.title).length} places to stay in {currentCity.title} -
- Sort by - - Popular - - - - -
    -
  • Popular
  • -
  • Price: low to high
  • -
  • Price: high to low
  • -
  • Top rated first
  • -
-
+ + {sortedOffers.length} places to stay in {currentCity.title} + +
- a.city.name === currentCity.title)} cardcssname={CardCssNameList.citiesList}/> + a.city.name === currentCity.title)} cardcssname={CardCssNameList.citiesList} setActiveOffer={setActiveOffer}/>
- a.city.name === currentCity.title)} selectedPoint={offers[3]} currentCity={currentCity} /> + a.city.name === currentCity.title)} selectedPoint={sortedOffers[0]} activeOffer={activeOffer} currentCity={currentCity} />
diff --git a/src/components/Map/Map.tsx b/src/components/Map/Map.tsx index 0438578..5c64a51 100644 --- a/src/components/Map/Map.tsx +++ b/src/components/Map/Map.tsx @@ -1,5 +1,5 @@ import {useRef, useEffect} from 'react'; -import {Icon, Marker, layerGroup} from 'leaflet'; +import leaflet, {Icon} from 'leaflet'; import useMap from '../../hooks/use-map'; import { OfferObject, City } from '../../types/types'; import 'leaflet/dist/leaflet.css'; @@ -14,42 +14,48 @@ const currentCustomIcon = new Icon({ iconSize: [40, 40], iconAnchor: [20, 40] }); + +const activeCustomIcon = new Icon({ + iconUrl: '../../markup/img/pin-active.svg', + iconSize: [40, 40], + iconAnchor: [20, 40] +}); type MainPageProps = { offers: OfferObject[]; currentCity: City; selectedPoint: OfferObject; + activeOffer: number | null; }; function Map(props: MainPageProps): JSX.Element { - const {offers, currentCity,selectedPoint} = props; + const {offers, currentCity,activeOffer,selectedPoint} = props; const mapRef = useRef(null); const map = useMap(mapRef, currentCity.title); useEffect(() => { if (map) { - const markerLayer = layerGroup().addTo(map); - offers.forEach((offer) => { - const marker = new Marker({ - lat: offer.location.latitude, - lng: offer.location.longitude - }); - marker - .setIcon( - selectedPoint !== undefined && offer.title === selectedPoint.title - ? currentCustomIcon - : defaultCustomIcon, - ) - .addTo(markerLayer); + map.eachLayer((layer) => { + if (layer instanceof leaflet.Marker) { + layer.remove(); + } }); - return () => { - map.removeLayer(markerLayer); - }; + offers.forEach((offer) => { + leaflet + .marker({ + lat: offer.location.latitude, + lng: offer.location.longitude, + }, { + // eslint-disable-next-line no-nested-ternary + icon: activeOffer === offer.id ? activeCustomIcon : selectedPoint !== undefined && offer.title === selectedPoint.title ? currentCustomIcon : defaultCustomIcon, + }) + .addTo(map); + }); } - }, [map, offers,selectedPoint]); + }, [map, offers,activeOffer,selectedPoint]); - return
; + return (
); } export default Map; diff --git a/src/components/Offer/Offer.tsx b/src/components/Offer/Offer.tsx index 7bcf49a..efc208a 100644 --- a/src/components/Offer/Offer.tsx +++ b/src/components/Offer/Offer.tsx @@ -5,12 +5,7 @@ import { ReviewList } from '../Reviews/ReviewList'; //import { offers } from '../../mock/offers'; import { OtherPlacesNearby } from '../OtherPlacesNearby/OtherPlacesNearby'; import Map from '../Map/Map'; -import { - AppRoute, - UserReview, - OfferObject, - City, -} from '../../types/types'; +import { AppRoute, UserReview, OfferObject, City } from '../../types/types'; type OfferProps = { reviews: UserReview[]; offers: OfferObject[]; @@ -192,10 +187,7 @@ export const Offer: React.FC = ({

- +
@@ -203,6 +195,7 @@ export const Offer: React.FC = ({ offers={[...offers]} selectedPoint={offers[1]} currentCity={currentCity} + activeOffer={offers[1].id} />
diff --git a/src/components/Offer/OfferCard.tsx b/src/components/Offer/OfferCard.tsx index a0653df..52badfe 100644 --- a/src/components/Offer/OfferCard.tsx +++ b/src/components/Offer/OfferCard.tsx @@ -6,16 +6,20 @@ import { useState } from 'react'; type OfferCardProps = { offer: OfferObject; cardcssname: string; + setActiveOffer?: (id: number | null) => void; }; export const OfferCard: React.FC = ({ offer, - cardcssname + cardcssname, + setActiveOffer, }) => { const [ isActiveCard, setActiveCard ] = useState(false); return ( -
- setActiveCard(!isActiveCard)} +
setActiveOffer && setActiveOffer(offer.id)} + onMouseLeave={() => setActiveOffer && setActiveOffer(null)} + onMouseOver={() => setActiveCard(!isActiveCard)} > {offer.isPremium && (
diff --git a/src/components/Offer/OfferList.tsx b/src/components/Offer/OfferList.tsx index 1ac6f8d..e328d81 100644 --- a/src/components/Offer/OfferList.tsx +++ b/src/components/Offer/OfferList.tsx @@ -6,9 +6,10 @@ import { OfferObject } from '../../types/types'; type OfferListProps = { offers: OfferObject[]; cardcssname: string; + setActiveOffer?: (id: number | null) => void; }; -const OfferList = ({ offers, cardcssname}: OfferListProps) => { +const OfferList = ({ offers, cardcssname,setActiveOffer}: OfferListProps) => { const [activeOfferId, setActiveOfferId] = useState(null); return ( @@ -19,7 +20,7 @@ const OfferList = ({ offers, cardcssname}: OfferListProps) => { onMouseEnter={() => setActiveOfferId(offer.id)} onMouseLeave={() => setActiveOfferId(null)} > - +
))}
{activeOfferId &&

Active Offer ID: {activeOfferId}

}
diff --git a/src/types/types.ts b/src/types/types.ts index d24d2c2..d15929e 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -57,7 +57,12 @@ export type UserReview = { comment: string; date: Date; }; - +export const enum SortName { + popular = 'popular', + lowToHigh = 'lowToHigh', + highToLow = 'highToLow', + topRated = 'topRated', +} export type Points = Point[]; export type State = ReturnType;