-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Egor Podolskiy
committed
Oct 19, 2024
1 parent
f858451
commit 07946ed
Showing
21 changed files
with
463 additions
and
278 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { useState, ChangeEvent, FormEvent, Fragment } from 'react'; | ||
import { getRatingTitle } from './utils'; | ||
import { CommentFormState } from './types'; | ||
|
||
const MIN_COMMENT_MESSAGE_LENGTH = 50; | ||
|
||
export const CommentForm = () => { | ||
const [formData, setFormData] = useState<CommentFormState>({ | ||
rating: '', | ||
review: '' | ||
}); | ||
|
||
const handleFieldChange = (evt: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | ||
const { name, value } = evt.target; | ||
|
||
setFormData((prevData) => ({ | ||
...prevData, | ||
[name]: value | ||
})); | ||
}; | ||
|
||
const handleSubmit = (evt: FormEvent<HTMLFormElement>) => { | ||
evt.preventDefault(); | ||
// eslint-disable-next-line no-console | ||
console.log('Submitted:', formData); | ||
}; | ||
|
||
const isSubmitDisabled = !formData.rating.length || formData.review.length < MIN_COMMENT_MESSAGE_LENGTH; | ||
|
||
return ( | ||
<form className="reviews__form form" action="#" method="post" onSubmit={handleSubmit}> | ||
<label className="reviews__label form__label" htmlFor="review">Your review</label> | ||
<div className="reviews__rating-form form__rating"> | ||
{[5, 4, 3, 2, 1].map((star) => ( | ||
<Fragment key={star}> | ||
<input | ||
className="form__rating-input visually-hidden" | ||
name="rating" | ||
value={star.toString()} | ||
id={`${star}-stars`} | ||
type="radio" | ||
onChange={handleFieldChange} | ||
/> | ||
<label htmlFor={`${star}-stars`} className="reviews__rating-label form__rating-label" title={getRatingTitle(star)}> | ||
<svg className="form__star-image" width="37" height="33"> | ||
<use xlinkHref="#icon-star"></use> | ||
</svg> | ||
</label> | ||
</Fragment> | ||
))} | ||
</div> | ||
<textarea | ||
className="reviews__textarea form__textarea" | ||
id="review" | ||
name="review" | ||
value={formData.review} | ||
onChange={handleFieldChange} | ||
placeholder="Tell how was your stay, what you like and what can be improved" | ||
/> | ||
<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">{MIN_COMMENT_MESSAGE_LENGTH}</b>. | ||
</p> | ||
<button className="reviews__submit form__submit button" type="submit" disabled={isSubmitDisabled}>Submit</button> | ||
</div> | ||
</form> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export type CommentFormState = { | ||
rating: string; | ||
review: string; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
export const getRatingTitle = (rating: number): string => { | ||
switch (rating) { | ||
case 5: | ||
return 'perfect'; | ||
case 4: | ||
return 'good'; | ||
case 3: | ||
return 'not bad'; | ||
case 2: | ||
return 'badly'; | ||
case 1: | ||
return 'terribly'; | ||
default: | ||
return ''; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { Link } from 'react-router-dom'; | ||
import { Offer } from '../../types/offer.ts'; | ||
import { AppRoutes } from '../../constants/routes.ts'; | ||
|
||
type Props = Offer | ||
|
||
export const FavoriteCard = ({ id, price, previewImage, title, type }: Props) => { | ||
return ( | ||
<article className="favorites__card place-card"> | ||
<div className="favorites__image-wrapper place-card__image-wrapper"> | ||
<Link to={`${AppRoutes.Offer}${id}`}> | ||
<img className="place-card__image" src={previewImage} width="150" height="110" alt="Place image"/> | ||
</Link> | ||
</div> | ||
<div className="favorites__card-info place-card__info"> | ||
<div className="place-card__price-wrapper"> | ||
<div className="place-card__price"> | ||
<b className="place-card__price-value">€{price}</b> | ||
<span className="place-card__price-text">/ night</span> | ||
</div> | ||
<button | ||
className="place-card__bookmark-button place-card__bookmark-button--active button" | ||
type="button" | ||
> | ||
<svg className="place-card__bookmark-icon" width="18" height="19"> | ||
<use xlinkHref="#icon-bookmark"></use> | ||
</svg> | ||
<span className="visually-hidden">In bookmarks</span> | ||
</button> | ||
</div> | ||
<div className="place-card__rating rating"> | ||
<div className="place-card__stars rating__stars"> | ||
<span style={{ width: '80%' }}></span> | ||
<span className="visually-hidden">Rating</span> | ||
</div> | ||
</div> | ||
<h2 className="place-card__name"> | ||
<Link to={`${AppRoutes.Offer}${id}`}>{title}</Link> | ||
</h2> | ||
<p className="place-card__type">{type}</p> | ||
</div> | ||
</article> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
export { OfferCard } from './offer-card'; | ||
export { OffersList } from './offers-list'; | ||
export { FavoriteCard } from './favorite-card'; | ||
export { CommentForm } from './comment-form'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,58 @@ | ||
export const OfferCard = () => ( | ||
<article className="cities__card place-card"> | ||
<div className="place-card__mark"> | ||
<span>Premium</span> | ||
</div> | ||
<div className="cities__image-wrapper place-card__image-wrapper"> | ||
<a href="#"> | ||
<img className="place-card__image" src="img/apartment-01.jpg" width="260" height="200" alt="Place image" /> | ||
</a> | ||
</div> | ||
<div className="place-card__info"> | ||
<div className="place-card__price-wrapper"> | ||
<div className="place-card__price"> | ||
<b className="place-card__price-value">€120</b> | ||
<span className="place-card__price-text">/ night</span> | ||
import { Link } from 'react-router-dom'; | ||
import { Offer } from '../../types/offer.ts'; | ||
import { AppRoutes } from '../../constants/routes.ts'; | ||
|
||
type Props = { | ||
onHover?: (id: Offer['id'] | undefined) => void; | ||
} & Offer; | ||
|
||
export const OfferCard = (props: Props) => { | ||
const { id, price, title, previewImage, onHover } = props; | ||
|
||
const handleMouseEnter = () => { | ||
onHover?.(id); | ||
}; | ||
|
||
const handleMouseLeave = () => { | ||
onHover?.(undefined); | ||
}; | ||
|
||
return ( | ||
( | ||
<article className="cities__card place-card" onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}> | ||
<div className="place-card__mark"> | ||
<span>Premium</span> | ||
</div> | ||
<button className="place-card__bookmark-button button" type="button"> | ||
<svg className="place-card__bookmark-icon" width="18" height="19"> | ||
<use xlinkHref="#icon-bookmark"></use> | ||
</svg> | ||
<span className="visually-hidden">To bookmarks</span> | ||
</button> | ||
</div> | ||
<div className="place-card__rating rating"> | ||
<div className="place-card__stars rating__stars"> | ||
<span style={{ width: '80%' }}></span> | ||
<span className="visually-hidden">Rating</span> | ||
<div className="cities__image-wrapper place-card__image-wrapper"> | ||
<Link to={`${AppRoutes.Offer}${id}`}> | ||
<img className="place-card__image" src={previewImage} width="260" height="200" alt={`${title} place image`} /> | ||
</Link> | ||
</div> | ||
</div> | ||
<h2 className="place-card__name"> | ||
<a href="#">Beautiful & luxurious apartment at great location</a> | ||
</h2> | ||
<p className="place-card__type">Apartment</p> | ||
</div> | ||
</article> | ||
); | ||
<div className="place-card__info"> | ||
<div className="place-card__price-wrapper"> | ||
<div className="place-card__price"> | ||
<b className="place-card__price-value">€{price}</b> | ||
<span className="place-card__price-text">/ night</span> | ||
</div> | ||
<button className="place-card__bookmark-button button" type="button"> | ||
<svg className="place-card__bookmark-icon" width="18" height="19"> | ||
<use xlinkHref="#icon-bookmark"></use> | ||
</svg> | ||
<span className="visually-hidden">To bookmarks</span> | ||
</button> | ||
</div> | ||
<div className="place-card__rating rating"> | ||
<div className="place-card__stars rating__stars"> | ||
<span style={{ width: '80%' }}></span> | ||
<span className="visually-hidden">Rating</span> | ||
</div> | ||
</div> | ||
<h2 className="place-card__name"> | ||
<Link to={`${AppRoutes.Offer}${id}`}>{title}</Link> | ||
</h2> | ||
<p className="place-card__type">Apartment</p> | ||
</div> | ||
</article> | ||
) | ||
); | ||
}; |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { useCallback, useState } from 'react'; | ||
import { Offer } from '../../types/offer.ts'; | ||
import { OfferCard } from '../offer-card'; | ||
import { FavoriteCard } from '../favorite-card'; | ||
|
||
type Props = { | ||
offers: Offer[]; | ||
favorites?: boolean; | ||
}; | ||
|
||
export const OffersList = ({ offers, favorites }: Props) => { | ||
const [activeCardId, setActiveCardId] = useState<Offer['id'] | undefined>(undefined); | ||
|
||
// eslint-disable-next-line no-console | ||
console.log('activeCardId: ', activeCardId); | ||
|
||
const handleCardSetActiveStatus = useCallback((id: Offer['id'] | undefined) => { | ||
setActiveCardId(id); | ||
}, []); | ||
|
||
return ( | ||
<div className={favorites ? 'favorites__places' : 'cities__places-list places__list tabs__content'}> | ||
{offers.length ? offers.map((offer) => { | ||
const { id } = offer; | ||
|
||
// TODO: Объединить карточки в один компонент | ||
return favorites ? | ||
<FavoriteCard key={id} {...offer} /> : | ||
<OfferCard key={id} {...offer} onHover={handleCardSetActiveStatus} />; | ||
}) : null} | ||
</div> | ||
); | ||
}; |
2 changes: 1 addition & 1 deletion
2
src/shared/routes/private-route/index.tsx → src/components/private-route/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.