Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Оптимизируй это (часть 2) #14

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
@@ -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",
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';
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';
@@ -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>
@@ -63,8 +62,7 @@ export function LoginPage(): React.JSX.Element {
setLoginInfo({
...loginInfo,
password: event.target.value,
})
}
})}
required
/>
</div>
8 changes: 4 additions & 4 deletions src/Pages/main-page/main-page.tsx
Original file line number Diff line number Diff line change
@@ -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';
@@ -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
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';
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';
@@ -13,7 +13,7 @@ interface BookmarkButtonProps {
offerId: Offer['id'];
}

function BookmarkButtonImpl({
export function BookmarkButton({
size,
offerId,
isFavorite,
@@ -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
@@ -38,7 +38,7 @@ export function OfferCardImpl({
onMouseLeave={handleMouseLeave}
className={cn(
'place-card',
{ cities__card: isOnMainPage },
{ 'cities__card': isOnMainPage },
{ 'near-places__card': !isOnMainPage },
)}
>
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;
@@ -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 });
@@ -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 &&
@@ -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"
@@ -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"
@@ -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"
@@ -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"
@@ -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"
@@ -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{' '}
@@ -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>
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>
);
5 changes: 5 additions & 0 deletions src/components/spinner/spinner.css
Original file line number Diff line number Diff line change
@@ -13,6 +13,11 @@
align-items: center;
}

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

@keyframes spin {
to {
transform: rotate(360deg);
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
@@ -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,
@@ -18,6 +19,7 @@ store.dispatch(checkAuthorization());

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