Skip to content

Commit

Permalink
Merge pull request #12 from mgmman/module7-task3
Browse files Browse the repository at this point in the history
  • Loading branch information
keksobot authored Nov 26, 2024
2 parents fcf78e8 + 7d61865 commit eeeb36f
Show file tree
Hide file tree
Showing 19 changed files with 145 additions and 25 deletions.
6 changes: 2 additions & 4 deletions src/Pages/login-page/login-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ export function LoginPage(): React.JSX.Element {
name="email"
placeholder="Email"
onChange={(event) =>
setLoginInfo({ ...loginInfo, email: event.target.value })
}
setLoginInfo({ ...loginInfo, email: event.target.value })}
required
/>
</div>
Expand All @@ -61,8 +60,7 @@ export function LoginPage(): React.JSX.Element {
setLoginInfo({
...loginInfo,
password: event.target.value,
})
}
})}
required
/>
</div>
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 @@ -24,7 +24,7 @@ export function MainPage(): React.JSX.Element {
: `${pluralizeAndCombine('place', offers.length)} to stay in ${city.name}`;
return (
<div className="page page--gray page--main">
<Layout showFooter>
<Layout>
<main className="page__main page__main--index">
<Helmet>6 cities</Helmet>
<h1 className="visually-hidden">Cities</h1>
Expand Down
13 changes: 10 additions & 3 deletions src/Pages/offer-page/offer-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { OffersList } from '../../components/offer/offers-list.tsx';
import { Reviews } from '../../components/reviews/reviews.tsx';
import { Map } from '../../components/map/map.tsx';
import React, { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { Navigate, useParams } from 'react-router-dom';
import { OfferInsideItems } from '../../components/offer/offer-inside-items.tsx';
import { OfferHost } from '../../components/offer/offer-host.tsx';
import { capitalize, pluralizeAndCombine } from '../../utils/string-utils.ts';
Expand All @@ -15,22 +15,29 @@ import { store, useAppSelector } from '../../store/store.ts';
import {
fetchNearbyOffers,
fetchOffer,
fetchReviews,
setCurrentOffer,
} from '../../store/actions.ts';
import { Spinner } from '../../components/spinner/Spinner.tsx';
import { AppRoutes } from '../../dataTypes/enums/app-routes.ts';

export function OfferPage(): React.JSX.Element {
const offerId = useParams().id;
useEffect(() => {
store.dispatch(setCurrentOffer(null));
store.dispatch(fetchOffer(offerId!));
store.dispatch(fetchNearbyOffers(offerId!));
store.dispatch(fetchReviews(offerId!));
}, [offerId]);
const nearbyOffers = useAppSelector((state) => state.nearbyOffers).slice(
0,
3,
);
const currentOffer = useAppSelector((state) => state.currentOffer);
const currentReviews = useAppSelector((state) => state.currentReviews);
if (currentOffer === undefined) {
return <Navigate to={AppRoutes.NotFoundPage} />;
}
return (
<div className="page">
<Layout>
Expand Down Expand Up @@ -60,7 +67,7 @@ export function OfferPage(): React.JSX.Element {
<Rating
rating={currentOffer.rating}
usePlace="offer"
isInOffer
showRatingValue
/>
<ul className="offer__features">
<li className="offer__feature offer__feature--entire">
Expand Down Expand Up @@ -89,7 +96,7 @@ export function OfferPage(): React.JSX.Element {
</p>
</div>
</div>
<Reviews reviews={[]} />
<Reviews reviews={currentReviews} />
</div>
</div>
<Map
Expand Down
5 changes: 4 additions & 1 deletion src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { getToken } from '../utils/token-utils.ts';
import { store } from '../store/store.ts';
import { AuthorizationStatus } from '../dataTypes/enums/authorization-status.ts';
import { setAuthorizationStatus } from '../store/actions.ts';
import { setAuthorizationStatus, setCurrentOffer } from '../store/actions.ts';

const BACKEND_URL = 'https://14.design.htmlacademy.pro/six-cities';
const REQUEST_TIMEOUT = 5000;
Expand Down Expand Up @@ -40,6 +40,9 @@ export const createAPI = (): AxiosInstance => {
setAuthorizationStatus(AuthorizationStatus.Unauthorized),
);
}
if (error.response && error.response.status === 404) {
store.dispatch(setCurrentOffer(undefined));
}
throw error;
},
);
Expand Down
5 changes: 3 additions & 2 deletions src/components/offer/offer-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AppRoutes } from '../../dataTypes/enums/app-routes.ts';
import cn from 'classnames';
import { Rating } from '../rating.tsx';
import { BookmarkButton } from '../bookmark-button.tsx';
import { capitalize } from '../../utils/string-utils.ts';

interface PlaceCardProps {
id: string;
Expand Down Expand Up @@ -40,7 +41,7 @@ export function OfferCard({
onMouseLeave={handleMouseLeave}
className={cn(
'place-card',
{ cities__card: isOnMainPage },
{ 'cities__card': isOnMainPage },
{ 'near-places__card': !isOnMainPage },
)}
>
Expand Down Expand Up @@ -82,7 +83,7 @@ export function OfferCard({
<h2 className="place-card__name">
<Link to={`${AppRoutes.Offer}/${id}`}>{title}</Link>
</h2>
<p className="place-card__type">{type}</p>
<p className="place-card__type">{capitalize(type)}</p>
</div>
</article>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/offer/offer-gallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function OfferGallery({
return (
<div className="offer__gallery-container container">
<div className="offer__gallery">
{imageSources.map((src) => (
{imageSources.slice(0, 6).map((src) => (
<div key={`${src}`} className="offer__image-wrapper">
<img className="offer__image" src={src} alt="Photo studio" />
</div>
Expand Down
14 changes: 12 additions & 2 deletions src/components/offer/offer-host.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,25 @@ export function OfferHost({ host }: OfferHostProps): React.JSX.Element {
<>
<h2 className="offer__host-title">Meet the host</h2>
<div className="offer__host-user user">
<div className="offer__avatar-wrapper offer__avatar-wrapper--pro user__avatar-wrapper">
{host.isPro ? (
<div className="offer__avatar-wrapper offer__avatar-wrapper--pro user__avatar-wrapper">
<img
className="offer__avatar user__avatar"
src={host.avatarUrl}
width="74"
height="74"
alt="Host avatar"
/>
</div>
) : (
<img
className="offer__avatar user__avatar"
src={host.avatarUrl}
width="74"
height="74"
alt="Host avatar"
/>
</div>
)}
<span className="offer__user-name">{getFirstName(host.name)}</span>
{host.isPro && <span className="offer__user-status">Pro</span>}
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/components/rating.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ import React from 'react';
interface RatingProps {
rating: number;
usePlace: string;
isInOffer?: boolean;
showRatingValue?: boolean;
}

export function Rating({
rating,
isInOffer,
showRatingValue,
usePlace,
}: RatingProps): React.JSX.Element {
return (
<div className={`${usePlace}__rating rating`}>
<div className={`${usePlace}__stars rating__stars`}>
<span style={{ width: `${rating * 20}%` }}></span>
<span style={{ width: `${Math.round(rating) * 20}%` }}></span>
<span className="visually-hidden">Rating</span>
</div>
{isInOffer && (
{showRatingValue && (
<span className="offer__rating-value rating__value">{rating}</span>
)}
</div>
Expand Down
37 changes: 34 additions & 3 deletions src/components/reviews/review-form.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { useState } from 'react';
import { store, useAppSelector } from '../../store/store.ts';
import { postReview } from '../../store/actions.ts';
import {
MAX_COMMENT_LENGTH,
MIN_COMMENT_LENGTH,
} from '../../consts/reviews.ts';

type UserReview = {
comment?: string;
Expand All @@ -7,16 +13,32 @@ type UserReview = {

export function ReviewForm(): React.JSX.Element {
const [review, setReview] = useState<UserReview>();
const offerId = useAppSelector((state) => state.currentOffer)!.id;
const onRatingChange: React.ChangeEventHandler<HTMLInputElement> = (
event,
): void => setReview({ ...review, rating: +event.target.value });
const onCommentChange: React.ChangeEventHandler<HTMLTextAreaElement> = (
event,
): void => setReview({ ...review, comment: event.target.value });
const handleSubmit: React.MouseEventHandler<HTMLButtonElement> = (
event,
): void => {
event.preventDefault();
store.dispatch(
postReview({
offerId: offerId,
rating: review?.rating || 5,
comment: review?.comment || '',
}),
);
};
const isValid =
review?.comment && review?.comment?.length >= 50 && review?.rating;
review?.comment &&
review?.comment?.length >= MIN_COMMENT_LENGTH &&
review?.comment?.length <= MAX_COMMENT_LENGTH &&
review?.rating;
return (
<form className="reviews__form form" action="#" method="post">
<form className="reviews__form form">
<label className="reviews__label form__label" htmlFor="review">
Your review
</label>
Expand Down Expand Up @@ -123,11 +145,20 @@ export function ReviewForm(): React.JSX.Element {
<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>.
with at least{' '}
<b className="reviews__text-amount">
{MIN_COMMENT_LENGTH} characters
</b>{' '}
and no more than{' '}
<b className="reviews__text-amount">
{MAX_COMMENT_LENGTH} characters
</b>
.
</p>
<button
className="reviews__submit form__submit button"
type="submit"
onClick={handleSubmit}
disabled={!isValid}
>
Submit
Expand Down
12 changes: 10 additions & 2 deletions src/components/reviews/reviews.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { ReviewForm } from './review-form.tsx';
import { ReviewsList } from './reviews-list.tsx';
import { Review } from '../../dataTypes/review.ts';
import { useAppSelector } from '../../store/store.ts';
import { AuthorizationStatus } from '../../dataTypes/enums/authorization-status.ts';

interface ReviewsProps {
reviews: Review[];
}

export function Reviews({ reviews }: ReviewsProps): React.JSX.Element {
const sortedReviews = reviews
.toSorted((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
.slice(0, 10);
const isAuthorized =
useAppSelector((state) => state.authorizationStatus) ===
AuthorizationStatus.Authorized;
const reviewsAvailable = reviews && reviews.length !== 0;
return (
<section className="offer__reviews reviews">
{reviewsAvailable ? (
<ReviewsList reviews={reviews} />
<ReviewsList reviews={sortedReviews} />
) : (
<span
className="reviews__item"
Expand All @@ -24,7 +32,7 @@ export function Reviews({ reviews }: ReviewsProps): React.JSX.Element {
No reviews available
</span>
)}
<ReviewForm />
{isAuthorized && <ReviewForm />}
</section>
);
}
4 changes: 2 additions & 2 deletions src/components/spinner/Spinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

export function Spinner({ caption }: SpinnerProps) {
return (
<>
<div className="spinner_container">
<div className="spinner"></div>
{caption && <span>{caption}</span>}
</>
</div>
);
}
6 changes: 6 additions & 0 deletions src/components/spinner/spinner.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
animation: spin 1s linear infinite;
}

.spinner_container {
display: flex;
justify-content: center;
align-items: center;
}

@keyframes spin {
to {
transform: rotate(360deg);
Expand Down
3 changes: 3 additions & 0 deletions src/consts/reviews.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const MIN_COMMENT_LENGTH = 50;

export const MAX_COMMENT_LENGTH = 300;
1 change: 1 addition & 0 deletions src/dataTypes/enums/api-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
Offers = '/offers',
Login = '/login',
Logout = '/logout',
Comments = '/comments',
}
1 change: 1 addition & 0 deletions src/dataTypes/enums/app-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export enum AppRoutes {
Login = '/login',
Offer = '/offer',
Favorites = '/favorites',
NotFoundPage = '/not-found',
}
2 changes: 2 additions & 0 deletions src/dataTypes/enums/room-type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export enum RoomType {
Apartment = 'Apartment',
Room = 'Room',
Hotel = 'Hotel',
House = 'House',
}
6 changes: 6 additions & 0 deletions src/dataTypes/review.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ export type Review = {
comment: string;
rating: number;
};

export type ReviewShortInfo = {
offerId: string;
comment: string;
rating: number;
};
Loading

0 comments on commit eeeb36f

Please sign in to comment.