Skip to content

Commit

Permalink
Merge pull request #14 from mgmman/module8-task2
Browse files Browse the repository at this point in the history
  • Loading branch information
keksobot authored Dec 3, 2024
2 parents 07e8ad9 + ebc593a commit a896a43
Show file tree
Hide file tree
Showing 20 changed files with 204 additions and 92 deletions.
25 changes: 24 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"react-dom": "18.2.0",
"react-helmet-async": "2.0.5",
"react-redux": "8.1.3",
"react-router-dom": "6.16.0"
"react-router-dom": "6.16.0",
"react-toastify": "^10.0.6"
},
"devDependencies": {
"@jedmao/redux-mock-store": "3.0.5",
Expand Down
2 changes: 1 addition & 1 deletion src/Pages/favorites-page/favorites-page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Helmet } from 'react-helmet-async';
import { Layout } from '../../components/Layout/layout.tsx';
import { Layout } from '../../components/layout/layout.tsx';
import { FavoritesSection } from '../../components/favorites-section.tsx';
import { useAppSelector } from '../../store/store.ts';
import { getFavoritesOffers } from '../../store/offers/offers.selectors.ts';
Expand Down
8 changes: 3 additions & 5 deletions src/Pages/login-page/login-page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Helmet } from 'react-helmet-async';
import { FormEvent, useState } from 'react';
import { Layout } from '../../components/Layout/layout.tsx';
import { Layout } from '../../components/layout/layout.tsx';
import { LoginInfo } from '../../dataTypes/user.ts';
import { useAppDispatch } from '../../store/store.ts';
import { login } from '../../store/async-actions.ts';
Expand Down Expand Up @@ -47,8 +47,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 @@ -63,8 +62,7 @@ export function LoginPage(): React.JSX.Element {
setLoginInfo({
...loginInfo,
password: event.target.value,
})
}
})}
required
/>
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/Pages/main-page/main-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import React, { useCallback, useState } from 'react';
import { Offer } from '../../dataTypes/offer.ts';
import { OffersList } from '../../components/offer/offers-list.tsx';
import { Layout } from '../../components/Layout/layout.tsx';
import { Layout } from '../../components/layout/layout.tsx';
import { Helmet } from 'react-helmet-async';
import { Nullable } from 'vitest';
import { Map } from '../../components/map/map.tsx';
Expand Down Expand Up @@ -55,9 +55,9 @@ export function MainPage(): React.JSX.Element {
selectedPoint={
activeOffer
? {
location: activeOffer?.location,
id: activeOffer?.id,
}
location: activeOffer?.location,
id: activeOffer?.id,
}
: undefined
}
isOnMainPage
Expand Down
2 changes: 1 addition & 1 deletion src/Pages/offer-page/offer-page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Helmet } from 'react-helmet-async';
import { Layout } from '../../components/Layout/layout.tsx';
import { Layout } from '../../components/layout/layout.tsx';
import { OffersList } from '../../components/offer/offers-list.tsx';
import { Reviews } from '../../components/reviews/reviews.tsx';
import { Map } from '../../components/map/map.tsx';
Expand Down
6 changes: 2 additions & 4 deletions src/components/bookmark-button.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { memo, useState } from 'react';
import React, { useState } from 'react';
import { store, useAppSelector } from '../store/store.ts';
import { bookmarkOffer } from '../store/async-actions.ts';
import { Offer } from '../dataTypes/offer.ts';
Expand All @@ -13,7 +13,7 @@ interface BookmarkButtonProps {
offerId: Offer['id'];
}

function BookmarkButtonImpl({
export function BookmarkButton({
size,
offerId,
isFavorite,
Expand Down Expand Up @@ -62,5 +62,3 @@ function BookmarkButtonImpl({
</button>
);
}

export const BookmarkButton = memo(BookmarkButtonImpl);
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/components/offer/offer-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function OfferCardImpl({
onMouseLeave={handleMouseLeave}
className={cn(
'place-card',
{ cities__card: isOnMainPage },
{ 'cities__card': isOnMainPage },
{ 'near-places__card': !isOnMainPage },
)}
>
Expand Down
40 changes: 32 additions & 8 deletions src/components/reviews/review-form.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { useState } from 'react';
import { store, useAppSelector } from '../../store/store.ts';
import { useEffect, useState } from 'react';
import { useAppDispatch, useAppSelector } from '../../store/store.ts';
import {
MAX_COMMENT_LENGTH,
MIN_COMMENT_LENGTH,
} from '../../consts/reviews.ts';
import { postReview } from '../../store/async-actions.ts';
import { getCurrentOffer } from '../../store/current-offer/current-offer.selectors.ts';
import {
getCurrentOffer,
getReviewPostingStatus,
} from '../../store/current-offer/current-offer.selectors.ts';
import { ReviewStatus } from '../../dataTypes/enums/review-status.ts';
import { setReviewPostingStatus } from '../../store/current-offer/current-offer.slice.ts';
import { Spinner } from '../spinner/Spinner.tsx';

type UserReview = {
comment?: string;
Expand All @@ -14,7 +20,14 @@ type UserReview = {

export function ReviewForm(): React.JSX.Element {
const [review, setReview] = useState<UserReview>();
const dispatch = useAppDispatch();
const offerId = useAppSelector(getCurrentOffer)!.id;
const reviewPostingStatus = useAppSelector(getReviewPostingStatus);
useEffect(() => {
if (reviewPostingStatus === ReviewStatus.Success) {
setReview({ comment: '', rating: undefined });
}
}, [reviewPostingStatus]);
const onRatingChange: React.ChangeEventHandler<HTMLInputElement> = (
event,
): void => setReview({ ...review, rating: +event.target.value });
Expand All @@ -25,14 +38,14 @@ export function ReviewForm(): React.JSX.Element {
event,
): void => {
event.preventDefault();
store.dispatch(
dispatch(
postReview({
offerId: offerId,
rating: review?.rating || 5,
comment: review?.comment || '',
}),
);
setReview({ comment: '', rating: undefined });
dispatch(setReviewPostingStatus(ReviewStatus.Pending));
};
const isValid =
review?.comment &&
Expand All @@ -53,6 +66,7 @@ export function ReviewForm(): React.JSX.Element {
type="radio"
checked={review?.rating === 5}
onChange={onRatingChange}
disabled={reviewPostingStatus === ReviewStatus.Pending}
/>
<label
htmlFor="5-stars"
Expand All @@ -72,6 +86,7 @@ export function ReviewForm(): React.JSX.Element {
type="radio"
checked={review?.rating === 4}
onChange={onRatingChange}
disabled={reviewPostingStatus === ReviewStatus.Pending}
/>
<label
htmlFor="4-stars"
Expand All @@ -91,6 +106,7 @@ export function ReviewForm(): React.JSX.Element {
type="radio"
checked={review?.rating === 3}
onChange={onRatingChange}
disabled={reviewPostingStatus === ReviewStatus.Pending}
/>
<label
htmlFor="3-stars"
Expand All @@ -110,6 +126,7 @@ export function ReviewForm(): React.JSX.Element {
type="radio"
checked={review?.rating === 2}
onChange={onRatingChange}
disabled={reviewPostingStatus === ReviewStatus.Pending}
/>
<label
htmlFor="2-stars"
Expand All @@ -129,6 +146,7 @@ export function ReviewForm(): React.JSX.Element {
type="radio"
checked={review?.rating === 1}
onChange={onRatingChange}
disabled={reviewPostingStatus === ReviewStatus.Pending}
/>
<label
htmlFor="1-star"
Expand All @@ -147,7 +165,9 @@ export function ReviewForm(): React.JSX.Element {
placeholder="Tell how was your stay, what you like and what can be improved"
value={review?.comment || ''}
onChange={onCommentChange}
></textarea>
disabled={reviewPostingStatus === ReviewStatus.Pending}
>
</textarea>
<div className="reviews__button-wrapper">
<p className="reviews__help">
To submit review please make sure to set{' '}
Expand All @@ -166,9 +186,13 @@ export function ReviewForm(): React.JSX.Element {
className="reviews__submit form__submit button"
type="submit"
onClick={handleSubmit}
disabled={!isValid}
disabled={!isValid || reviewPostingStatus === ReviewStatus.Pending}
>
Submit
{reviewPostingStatus === ReviewStatus.Pending ? (
<Spinner small />
) : (
'Submit'
)}
</button>
</div>
</form>
Expand Down
5 changes: 3 additions & 2 deletions src/components/spinner/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
interface SpinnerProps {
caption?: string;
small?: boolean;
}

export function Spinner({ caption }: SpinnerProps) {
export function Spinner({ caption, small }: SpinnerProps) {
return (
<div className="spinner_container">
<div className="spinner"></div>
<div className={`spinner ${small && 'spinner_small'}`}></div>
{caption && <span>{caption}</span>}
</div>
);
Expand Down
5 changes: 5 additions & 0 deletions src/components/spinner/spinner.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
align-items: center;
}

.spinner_small {
width: 20px;
height: 20px;
}

@keyframes spin {
to {
transform: rotate(360deg);
Expand Down
5 changes: 5 additions & 0 deletions src/dataTypes/enums/review-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum ReviewStatus {
Pending = 'pending',
Success = 'success',
Error = 'error',
}
2 changes: 2 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
fetchFavoriteOffers,
fetchOffers,
} from './store/async-actions.ts';
import { ToastContainer } from 'react-toastify';

const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement,
Expand All @@ -18,6 +19,7 @@ store.dispatch(checkAuthorization());

root.render(
<React.StrictMode>
<ToastContainer />
<App />
</React.StrictMode>,
);
Loading

0 comments on commit a896a43

Please sign in to comment.