@@ -34,7 +13,7 @@ const Favorite = ({ offers }: FavoriteProps) => (
Saved listings
{offers.map((offer) => (
-
+
))}
diff --git a/src/components/MainPage/MainPage.tsx b/src/components/MainPage/MainPage.tsx
index 90845e8..33cbf7c 100644
--- a/src/components/MainPage/MainPage.tsx
+++ b/src/components/MainPage/MainPage.tsx
@@ -1,41 +1,39 @@
import {FC} 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 { changeCity } from '../../action';
+import { ListCities } from '../../components/CityList/CityList';
import Map from '../Map/Map';
-type Offer = {
- id: number;
- title: string;
- price: number;
- rating: number;
- type: string;
- isPremium: boolean;
- isFavorite: boolean;
- NumberOfPlaces: number;
- previewImage: string;
- city: {
- name: string;
- location: {
- latitude: number;
- longitude: number;
- zoom: number;
- };
- };
- location: {
- latitude: number;
- longitude: number;
- zoom: number;
- };
-};
type MainPageProps = {
- offers: Offer[];
+ offers: OfferObject[];
+ currentCity: City;
+ cities: City[];
};
-export const MainPage : FC
= ({ offers }) =>
- (
+export const MainPage : FC = ({
+ offers,
+ currentCity,
+ cities,
+}:MainPageProps) => {
+ const navigate = useNavigate();
+ const dispatch = useAppDispatch();
+
+ const handleUserSelectCity = (cityName: string) => {
+ dispatch(changeCity(cityName));
+ // dispatch(fillOffers());
+ };
+
+ return (
@@ -64,45 +62,14 @@ export const MainPage : FC
= ({ offers }) =>
Cities
Places
- {offers.length} places to stay in Amsterdam
+ {offers.filter((a) =>a.city.name === currentCity.title).length} places to stay in {currentCity.title}
-
+ a.city.name === currentCity.title)} cardcssname={CardCssNameList.citiesList}/>
-
+ a.city.name === currentCity.title)} selectedPoint={offers[3]} currentCity={currentCity} />
@@ -132,4 +99,5 @@ export const MainPage : FC
= ({ offers }) =>
);
+};
export default MainPage;
diff --git a/src/components/Map/Map.tsx b/src/components/Map/Map.tsx
index 3a6693a..0438578 100644
--- a/src/components/Map/Map.tsx
+++ b/src/components/Map/Map.tsx
@@ -1,31 +1,8 @@
import {useRef, useEffect} from 'react';
import {Icon, Marker, layerGroup} from 'leaflet';
import useMap from '../../hooks/use-map';
+import { OfferObject, City } from '../../types/types';
import 'leaflet/dist/leaflet.css';
-type Offer = {
- id: number;
- title: string;
- price: number;
- rating: number;
- type: string;
- isPremium: boolean;
- isFavorite: boolean;
- NumberOfPlaces: number;
- previewImage: string;
- city: {
- name: string;
- location: {
- latitude: number;
- longitude: number;
- zoom: number;
- };
- };
- location: {
- latitude: number;
- longitude: number;
- zoom: number;
- };
-};
const defaultCustomIcon = new Icon({
iconUrl: 'https://assets.htmlacademy.ru/content/intensive/javascript-1/demo/interactive-map/pin.svg',
iconSize: [40, 40],
@@ -38,15 +15,16 @@ const currentCustomIcon = new Icon({
iconAnchor: [20, 40]
});
type MainPageProps = {
- offers: Offer[];
- selectedPoint :Offer;
+ offers: OfferObject[];
+ currentCity: City;
+ selectedPoint: OfferObject;
};
function Map(props: MainPageProps): JSX.Element {
- const {offers, selectedPoint} = props;
+ const {offers, currentCity,selectedPoint} = props;
const mapRef = useRef(null);
- const map = useMap(mapRef, 'Амстердам');
+ const map = useMap(mapRef, currentCity.title);
useEffect(() => {
if (map) {
diff --git a/src/components/Offer/Offer.tsx b/src/components/Offer/Offer.tsx
index 957e487..7bcf49a 100644
--- a/src/components/Offer/Offer.tsx
+++ b/src/components/Offer/Offer.tsx
@@ -1,209 +1,219 @@
+/* eslint-disable react/prop-types */
+import { Link } from 'react-router-dom';
import SendCommentForm from '../SendCommentForm/SendCommentForm';
import { ReviewList } from '../Reviews/ReviewList';
-import { offers } from '../../mock/offers';
+//import { offers } from '../../mock/offers';
import { OtherPlacesNearby } from '../OtherPlacesNearby/OtherPlacesNearby';
import Map from '../Map/Map';
-export default function Offer () {
- return (
-
-
-
-
-
-
-
-
+import {
+ AppRoute,
+ UserReview,
+ OfferObject,
+ City,
+} from '../../types/types';
+type OfferProps = {
+ reviews: UserReview[];
+ offers: OfferObject[];
+ currentCity: City;
+};
+
+export const Offer: React.FC
= ({
+ // eslint-disable-next-line react/prop-types
+ reviews,
+ offers,
+ currentCity,
+}) => (
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
- Premium
-
-
-
- Beautiful & luxurious studio at great location
-
-
-
-
-
- To bookmarks
-
-
-
+
+
+
+ Premium
+
+
+
+ Beautiful & luxurious studio at great location
+
+
+
+
+
+ To bookmarks
+
+
+
+
+
+ Rating
-
-
- Apartment
-
-
- 3 Bedrooms
-
-
- Max 4 adults
-
+ 4.8
+
+
+
+ Apartment
+
+
+ 3 Bedrooms
+
+
+ Max 4 adults
+
+
+
+ €120
+ night
+
+
+
What's inside
+
+ Wi-Fi
+ Washing machine
+ Towels
+ Heating
+ Coffee machine
+ Baby seat
+ Kitchen
+ Dishwasher
+ Cabel TV
+ Fridge
-
- €120
- night
-
-
-
What's inside
-
- Wi-Fi
- Washing machine
- Towels
- Heating
- Coffee machine
- Baby seat
- Kitchen
- Dishwasher
- Cabel TV
- Fridge
-
-
-
-
Meet the host
-
-
-
-
-
Angelina
-
Pro
-
-
-
- A quiet cozy and picturesque that hides behind a a river by
- the unique lightness of Amsterdam. The building is green and
- from 18th century.
-
-
- An independent House, strategically located between Rembrand
- Square and National Opera, but where the bustle of the city
- comes to rest in this alley flowery and colorful.
-
+
+
+
Meet the host
+
+
+
+
Angelina
+
Pro
+
+
+
+ A quiet cozy and picturesque that hides behind a a river by
+ the unique lightness of Amsterdam. The building is green and
+ from 18th century.
+
+
+ An independent House, strategically located between Rembrand
+ Square and National Opera, but where the bustle of the city
+ comes to rest in this alley flowery and colorful.
+
-
+
-
-
-
-
-
-
Reviews
- {}
-
-
-
- );
-}
+
+
+
+
+
+
+
Reviews
+ {}
+
+
+
+);
+export default Offer;
diff --git a/src/components/Offer/OfferCard.tsx b/src/components/Offer/OfferCard.tsx
index f644358..a0653df 100644
--- a/src/components/Offer/OfferCard.tsx
+++ b/src/components/Offer/OfferCard.tsx
@@ -1,65 +1,51 @@
+import React from 'react';
import { Link } from 'react-router-dom';
-
-type Offer = {
- id: number;
- title: string;
- price: number;
- rating: number;
- type: string;
- isPremium: boolean;
- isFavorite: boolean;
- NumberOfPlaces: number;
- previewImage: string;
- city: {
- name: string;
- location: {
- latitude: number;
- longitude: number;
- zoom: number;
- };
- };
- location: {
- latitude: number;
- longitude: number;
- zoom: number;
- };
-};
+import { OfferObject, AppRoute } from '../../types/types';
+import { useState } from 'react';
type OfferCardProps = {
- offer: Offer;
+ offer: OfferObject;
+ cardcssname: string;
};
-const OfferCard = ({ offer }: OfferCardProps) => (
-
- {offer.isPremium && (
-
- Premium
-
- )}
-
-
-
-
-
-
-
-
-
€{offer.price}
-
/ night
+export const OfferCard: React.FC
= ({
+ offer,
+ cardcssname
+}) => {
+ const [ isActiveCard, setActiveCard ] = useState(false);
+ return (
+
+ setActiveCard(!isActiveCard)}
+ >
+ {offer.isPremium && (
+
+ Premium
+ )}
+
+
+
+
-
-
-
-
Rating
+
+
+
+ €{offer.price}
+ / night
+
+
+
+ {offer.title}
+
+
{offer.type}
-
- {offer.title}
-
-
{offer.type}
-
-
-);
-
+
+ );
+};
export default OfferCard;
diff --git a/src/components/Offer/OfferList.tsx b/src/components/Offer/OfferList.tsx
index 32825f3..1ac6f8d 100644
--- a/src/components/Offer/OfferList.tsx
+++ b/src/components/Offer/OfferList.tsx
@@ -1,36 +1,14 @@
import { useState } from 'react';
import OfferCard from './OfferCard';
+import { OfferObject } from '../../types/types';
-type Offer = {
- id: number;
- title: string;
- price: number;
- rating: number;
- type: string;
- isPremium: boolean;
- isFavorite: boolean;
- NumberOfPlaces: number;
- previewImage: string;
- city: {
- name: string;
- location: {
- latitude: number;
- longitude: number;
- zoom: number;
- };
- };
- location: {
- latitude: number;
- longitude: number;
- zoom: number;
- };
-};
type OfferListProps = {
- offers: Offer[];
+ offers: OfferObject[];
+ cardcssname: string;
};
-const OfferList = ({ offers }: OfferListProps) => {
+const OfferList = ({ offers, cardcssname}: OfferListProps) => {
const [activeOfferId, setActiveOfferId] = useState
(null);
return (
@@ -41,7 +19,7 @@ const OfferList = ({ offers }: OfferListProps) => {
onMouseEnter={() => setActiveOfferId(offer.id)}
onMouseLeave={() => setActiveOfferId(null)}
>
-
+
))}
{activeOfferId &&
Active Offer ID: {activeOfferId}
}
diff --git a/src/components/OtherPlacesNearby/OtherPlacesNearby.tsx b/src/components/OtherPlacesNearby/OtherPlacesNearby.tsx
index 2f64499..c6e9db5 100644
--- a/src/components/OtherPlacesNearby/OtherPlacesNearby.tsx
+++ b/src/components/OtherPlacesNearby/OtherPlacesNearby.tsx
@@ -1,10 +1,10 @@
import OfferList from '../Offer/OfferList';
import { offers } from '../../mock/offers';
-
+import {CardCssNameList } from '../../types/types';
export const OtherPlacesNearby = () => (
Other places in the neighbourhood
-
+
);
diff --git a/src/components/Reviews/Review.tsx b/src/components/Reviews/Review.tsx
index 515ec11..16d639e 100644
--- a/src/components/Reviews/Review.tsx
+++ b/src/components/Reviews/Review.tsx
@@ -1,16 +1,7 @@
/* eslint-disable react/prop-types */
import { Rating } from '../Rating/Rating';
-type ReviewObject = {
- id: string;
- date: Date;
- user: {
- name: string;
- avatarUrl: string;
- isPro: boolean;
- };
- comment: string;
- rating: number;
-};
+import { UserReview} from '../../types/types';
+
export const dateToYearMonthDay = (date: Date) =>
new Intl.DateTimeFormat('en-CA', {
year: 'numeric',
@@ -22,7 +13,7 @@ export const dateToMonthWordYear = (date: Date) =>
new Intl.DateTimeFormat('en-CA', { year: 'numeric', month: 'long' }).format(
date,
);
-export const Review: React.FC = ({
+export const Review: React.FC = ({
comment,
date,
rating,
@@ -33,7 +24,7 @@ export const Review: React.FC = ({
useDispatch
();
+export const useAppSelector: TypedUseSelectorHook = useSelector;
diff --git a/src/index.tsx b/src/index.tsx
index 36201f0..a850ac6 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,13 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
-import App from './App';
-import { offers } from './mock/offers';
+import {App} from './App';
+
+import { Provider } from 'react-redux';
+import { store } from './store';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
-
+
+
+
);
diff --git a/src/mock/cities.ts b/src/mock/cities.ts
new file mode 100644
index 0000000..2ec39fc
--- /dev/null
+++ b/src/mock/cities.ts
@@ -0,0 +1,34 @@
+import { City } from '../types/types';
+
+export const CITYLIST: City[] = [
+ {
+ title: 'Paris',
+ lng: 2.3522,
+ lat: 48.8566,
+ },
+ {
+ title: 'Amsterdam',
+ lng: 4.9041,
+ lat: 52.3676,
+ },
+ {
+ title: 'Cologne',
+ lng: 6.9603,
+ lat: 50.9375,
+ },
+ {
+ title: 'Brussels',
+ lng: 4.3517,
+ lat: 50.8503,
+ },
+ {
+ title: 'Hamburg',
+ lng: 9.9937,
+ lat: 53.5511,
+ },
+ {
+ title: 'Dusseldorf',
+ lng: 6.7735,
+ lat: 51.2277,
+ },
+];
diff --git a/src/mock/offers.ts b/src/mock/offers.ts
index b253cd2..76728e1 100644
--- a/src/mock/offers.ts
+++ b/src/mock/offers.ts
@@ -38,7 +38,7 @@ export const offers = [
zoom: 0,
},
city: {
- name: 'Amsterdam',
+ name: 'Paris',
location: {
latitude: 52.35514938496378,
longitude: 4.673877537499948,
diff --git a/src/mock/reviewers.ts b/src/mock/reviewers.ts
new file mode 100644
index 0000000..6ed57a4
--- /dev/null
+++ b/src/mock/reviewers.ts
@@ -0,0 +1,22 @@
+export const REVIEWERS = [
+ {
+ id: 1,
+ user: {
+ name: 'Max',
+ avatar: 'img/avatar-01.jpg',
+ },
+ rating: 5,
+ comment: 'All super',
+ date: new Date(),
+ },
+ {
+ id: 2,
+ user: {
+ name: 'Igor',
+ avatar: 'img/avatar-02.jpg',
+ },
+ rating: 1,
+ comment: 'All bad',
+ date: new Date(),
+ },
+];
diff --git a/src/reducer.ts b/src/reducer.ts
new file mode 100644
index 0000000..71c99c5
--- /dev/null
+++ b/src/reducer.ts
@@ -0,0 +1,28 @@
+import { createReducer } from '@reduxjs/toolkit';
+import { City, OfferObject } from './types/types';
+import { changeCity, AddOffer } from './action';
+import { CITYLIST } from './mock/cities';
+import { offers } from './mock/offers';
+type InitialState = {
+ currentCity: City;
+ cities: City[];
+ offers: OfferObject[];
+};
+
+const initialState: InitialState = {
+ currentCity: CITYLIST[0],
+ cities: CITYLIST,
+ offers: offers,
+};
+
+export const reducer = createReducer(initialState, (builder) => {
+ builder
+ .addCase(changeCity, (state, action) => {
+ state.currentCity = state.cities.find(
+ (city) => city.title === action.payload
+ )!;
+ })
+ .addCase(AddOffer, (state, action) => {
+ state.offers = action.payload;
+ });
+});
diff --git a/src/store/index.ts b/src/store/index.ts
new file mode 100644
index 0000000..320da04
--- /dev/null
+++ b/src/store/index.ts
@@ -0,0 +1,4 @@
+import { configureStore } from '@reduxjs/toolkit';
+import { reducer } from '../reducer';
+
+export const store = configureStore({ reducer });
diff --git a/src/types/types.ts b/src/types/types.ts
index b661606..d24d2c2 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -1,8 +1,8 @@
+import { store } from '../store';
export type City = {
title: string;
lat: number;
lng: number;
- zoom: number;
};
export type Point = {
@@ -10,5 +10,55 @@ export type Point = {
lat: number;
lng: number;
};
+export type OfferObject = {
+ id: number;
+ title: string;
+ price: number;
+ rating: number;
+ type: string;
+ isPremium: boolean;
+ isFavorite: boolean;
+ NumberOfPlaces: number;
+ previewImage: string;
+ city: {
+ name: string;
+ location: {
+ latitude: number;
+ longitude: number;
+ zoom: number;
+ };
+ };
+ location: {
+ latitude: number;
+ longitude: number;
+ zoom: number;
+ };
+};
+export enum AppRoute {
+ Login = '/login',
+ Favorites = '/favorites',
+ Main = '/',
+ Offer = '/offer/:id',
+}
+
+export const enum CardCssNameList {
+ citiesList = 'cities__card',
+ neardPlace = 'near-places__card',
+ favoritePlace = 'favorites__card',
+}
+
+export type UserReview = {
+ id: number;
+ user: {
+ name: string;
+ avatar: string;
+ };
+ rating: number;
+ comment: string;
+ date: Date;
+};
export type Points = Point[];
+export type State = ReturnType;
+
+export type AppDispatch = typeof store.dispatch;