Skip to content

Commit

Permalink
Merge pull request #13 from antoshkaxxr/module7-task3
Browse files Browse the repository at this point in the history
  • Loading branch information
keksobot authored Nov 26, 2024
2 parents 93ed670 + ecd7dc4 commit f01eae9
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 81 deletions.
41 changes: 22 additions & 19 deletions src/components/comment-form/comment-form.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import React, {ChangeEvent, FormEvent, useState} from 'react';
import {useAppDispatch} from '../../hooks';
import {postCommentAction} from '../../store/api-actions.ts';
import {showCustomToast} from '../custom-toast/custom-toast.tsx';

const getRatingTitle = (star: number) => {
switch (star) {
Expand All @@ -15,42 +18,44 @@ const getRatingTitle = (star: number) => {
}
};

export function CommentForm() {
type CommentFormProps = {
offerId: string;
}

export function CommentForm({offerId}: CommentFormProps) {
const [formData, setFormData] = useState({
rating: '',
review: ''
});

const [error, setError] = useState('');
const [isSubmitted, setIsSubmitted] = useState(false);
const dispatch = useAppDispatch();

const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
const {name, value} = e.target;
setFormData((prevState) => ({
...prevState,
[name]: value
}));
setError('');
};

const handleSubmit = (e: FormEvent) => {
e.preventDefault();
const { rating, review } = formData;
const {rating, review} = formData;

if (!rating) {
setError('Please select a rating.');
showCustomToast('Please select a rating.');
return;
}
if (review.length < 50) {
setError('The review must contain at least 50 characters.');

if (review.length < 50 || review.length > 300) {
showCustomToast('The review must contain from 50 to 300 characters.');
return;
}

// eslint-disable-next-line no-console
console.log('Отправка данных:', formData);

setFormData({ rating: '', review: '' });
setIsSubmitted(true);
dispatch(postCommentAction({offerId, comment: review, rating: Number(rating)}))
.then(() => {
setFormData({rating: '', review: ''});
});
};

return (
Expand All @@ -60,7 +65,7 @@ export function CommentForm() {
{[5, 4, 3, 2, 1].map((star) => (
<React.Fragment key={star}>
<input className="form__rating-input visually-hidden" name="rating" value={`${star}`} id={`${star}-stars`}
type="radio" onChange={handleChange}
type="radio" onChange={handleChange} checked={formData.rating === `${star}`}
/>
<label htmlFor={`${star}-stars`} className="reviews__rating-label form__rating-label"
title={getRatingTitle(star)}
Expand All @@ -78,15 +83,13 @@ export function CommentForm() {
onChange={handleChange}
>
</textarea>
{error && <p className="error-message">{error}</p>}
<div className="reviews__button-wrapper">
<p className="reviews__help">
To submit review please make sure to set <span className="reviews__star">rating</span> and describe your stay
with at least <b className="reviews__text-amount">50 characters</b>.
To submit review please make sure to set <span className="reviews__star">rating</span>&nbsp;
and describe your stay <b className="reviews__text-amount">from 50 to 300 characters</b>.
</p>
<button className="reviews__submit form__submit button" type="submit">Submit</button>
</div>
{isSubmitted && <p className="success-message">Ваш отзыв был успешно отправлен! 💌</p>}
</form>
);
}
4 changes: 2 additions & 2 deletions src/components/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export function Header() {
<div className="container">
<div className="header__wrapper">
<div className="header__left">
<a className="header__logo-link header__logo-link--active">
<Link to={AppRoute.Main} className="header__logo-link header__logo-link--active">
<img className="header__logo" src="/img/logo.svg" alt="6 cities logo" width="81" height="41"/>
</a>
</Link>
</div>
{!isLoginPage && (
<nav className="header__nav">
Expand Down
5 changes: 3 additions & 2 deletions src/components/map/map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import {Point} from '../../types/offer.ts';
type MapProps = {
points: Point[];
activePointId: string | null;
height: number;
}

export function Map({points, activePointId} : MapProps) {
export function Map({points, activePointId, height} : MapProps) {
const mapRef = useRef(null);
const map = useMap(mapRef, points[0].city);

Expand Down Expand Up @@ -50,7 +51,7 @@ export function Map({points, activePointId} : MapProps) {

return (
<div
style={{height: '700px'}}
style={{height: `${height}px`}}
ref={mapRef}
>
</div>
Expand Down
9 changes: 7 additions & 2 deletions src/components/reviews-list/reviews-list.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import {Review} from '../../types/review.ts';
import {ReviewItem} from '../review-item/review-item.tsx';
import {CommentForm} from '../comment-form/comment-form.tsx';
import {State} from '../../types/state.ts';
import {useAppSelector} from '../../hooks';

type ReviewsListProps = {
reviews: Review[];
offerId: string;
}

export function ReviewsList({reviews} : ReviewsListProps) {
export function ReviewsList({reviews, offerId} : ReviewsListProps) {
const userData = useAppSelector((state: State) => state.userData);

return (
<section className="offer__reviews reviews">
<h2 className="reviews__title">Reviews &middot; <span className="reviews__amount">{reviews.length}</span></h2>
Expand All @@ -18,7 +23,7 @@ export function ReviewsList({reviews} : ReviewsListProps) {
/>
))}
</ul>
<CommentForm />
{userData && <CommentForm offerId={offerId} />}
</section>
);
}
2 changes: 2 additions & 0 deletions src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ export enum AppRoute {
Favorites = '/favorites',
Offer = '/offer',
OfferWithId = '/offer/:id',
NotFound = '/not-found',
}

export enum APIRoute {
Offers = '/offers',
Login = '/login',
Logout = '/logout',
Favorite = '/favorite',
Comments = '/comments',
}

export enum AuthorizationStatus {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/404-not-found-page/not-found-page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Link } from 'react-router-dom';
import './not-found-page.css';
import {Helmet} from 'react-helmet-async';
import {AppRoute} from '../../const.ts';
import {Link} from 'react-router-dom';

export function NotFoundPage() {
return (
Expand Down
2 changes: 1 addition & 1 deletion src/pages/main-page/main-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function MainPage(): JSX.Element {
</section>
<div className="cities__right-section">
<section className="cities__map map">
<Map points={points} activePointId={activeOfferId} />
<Map points={points} activePointId={activeOfferId} height={700} />
</section>
</div>
</div>
Expand Down
94 changes: 45 additions & 49 deletions src/pages/offer-page/offer-page.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,59 @@
import {Helmet} from 'react-helmet-async';
// import {useParams} from 'react-router-dom';
import {useParams} from 'react-router-dom';
import {useEffect, useMemo} from 'react';
import {ReviewsList} from '../../components/reviews-list/reviews-list.tsx';
import {Reviews} from '../../mocks/reviews.ts';
import {NearbyOffers} from '../../mocks/nearby-offers.ts';
import {Map} from '../../components/map/map';
import {OffersList} from '../../components/offers-list/offers-list.tsx';
import {FullOffer} from '../../mocks/detailed-offer.ts';
import {Point} from '../../types/offer.ts';

import {fetchDetailedOfferAction} from '../../store/api-actions.ts';
import {State} from '../../types/state.ts';
import {useAppDispatch, useAppSelector} from '../../hooks';
import {Spinner} from '../../components/spinner/spinner.tsx';
import { Header } from '../../components/header/header.tsx';

export function OfferPage(): JSX.Element {
// const { id } = useParams();
const offer = FullOffer;
const points: Point[] = [
...NearbyOffers.map((nearbyOffer) => ({
id: nearbyOffer.id,
city: nearbyOffer.city,
location: nearbyOffer.location
})),
{
id: FullOffer.id,
city: FullOffer.city,
location: FullOffer.location
const { id } = useParams<{ id: string }>();
const dispatch = useAppDispatch();
const offer = useAppSelector((state: State) => state.offer);
const nearbyOffers = useAppSelector((state: State) => state.nearbyOffers);
const comments = useAppSelector((state: State) => state.comments);

useEffect(() => {
if (id) {
dispatch(fetchDetailedOfferAction(id));
}
}, [dispatch, id]);

const points: Point[] = useMemo(() => {
if (!offer) {
return [];
}
];
return [
...nearbyOffers.map((nearbyOffer) => ({
id: nearbyOffer.id,
city: nearbyOffer.city,
location: nearbyOffer.location
})),
{
id: offer.id,
city: offer.city,
location: offer.location
}
];
}, [nearbyOffers, offer]);

if (!offer) {
return (
<Spinner />
);
}

return (
<div className="page">
<Helmet>
<title>{offer.title} - 6 cities</title>
</Helmet>
<header className="header">
<div className="container">
<div className="header__wrapper">
<div className="header__left">
<a className="header__logo-link" href="main.html">
<img className="header__logo" src="img/logo.svg" alt="6 cities logo" width="81" height="41"/>
</a>
</div>
<nav className="header__nav">
<ul className="header__nav-list">
<li className="header__nav-item user">
<a className="header__nav-link header__nav-link--profile" href="#">
<div className="header__avatar-wrapper user__avatar-wrapper">
</div>
<span className="header__user-name user__name">[email protected]</span>
<span className="header__favorite-count">3</span>
</a>
</li>
<li className="header__nav-item">
<a className="header__nav-link" href="#">
<span className="header__signout">Sign out</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</header>
<Header />

<main className="page__main page__main--offer">
<section className="offer">
Expand Down Expand Up @@ -141,17 +137,17 @@ export function OfferPage(): JSX.Element {
</p>
</div>
</div>
<ReviewsList reviews={Reviews} />
<ReviewsList reviews={comments} offerId={offer.id} />
</div>
</div>
<section className={'offer__map map'}>
<Map points={points} activePointId={FullOffer.id} />
<Map points={points} activePointId={offer.id} height={600} />
</section>
</section>
<div className="container">
<section className="near-places places">
<h2 className="near-places__title">Other places in the neighbourhood</h2>
<OffersList offers={NearbyOffers} setActiveOfferId={() => {}} isNearby />
<OffersList offers={nearbyOffers} setActiveOfferId={() => {}} isNearby />
</section>
</div>
</main>
Expand Down
11 changes: 10 additions & 1 deletion src/store/action.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { createAction } from '@reduxjs/toolkit';
import {Offer} from '../types/offer.ts';
import {DetailedOffer, Offer} from '../types/offer.ts';
import {AppRoute, AuthorizationStatus} from '../const.ts';
import {UserData} from '../types/user.ts';
import {Review} from '../types/review.ts';

export const setCity = createAction<string>('city/setCity');

Expand All @@ -16,3 +17,11 @@ export const setUserData = createAction<UserData | null>('user/setUserData');
export const setFavoriteOffers = createAction<Offer[]>('user/setFavoriteOffers');

export const redirectToRoute = createAction<AppRoute>('auth/redirectToRoute');

export const setDetailedOffer = createAction<DetailedOffer>('offer/setDetailedOffer');

export const setNearbyOffers = createAction<Offer[]>('offers/setNearbyOffers');

export const setComments = createAction<Review[]>('comments/setComments');

export const addComment = createAction<Review>('comments/addComment');
Loading

0 comments on commit f01eae9

Please sign in to comment.