Skip to content

Commit

Permalink
SC-4 some text doesnt matter
Browse files Browse the repository at this point in the history
  • Loading branch information
Egor Podolskiy committed Oct 19, 2024
1 parent f858451 commit 07946ed
Show file tree
Hide file tree
Showing 21 changed files with 463 additions and 278 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"build": "tsc && vite build",
"lint": "eslint ./src --ext ts,tsx",
"lint:fix": "eslint ./src --ext ts,tsx --fix",
"typecheck": "tsc",
"preview": "vite preview",
"test": "vitest --passWithNoTests"
},
Expand Down
19 changes: 11 additions & 8 deletions src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,28 @@ import { MainPage } from '../pages/main-page';
import { LoginPage } from '../pages/login-page';
import { FavoritesPage } from '../pages/favorites-page';
import { OfferPage } from '../pages/offer-page';
import { PrivateRoute } from '../shared/routes/private-route';
import { PrivateRoute } from '../components/private-route';
import { Page404 } from '../pages/errors';
import { AppRoutes } from './routes';
import { AppRoutes } from '../constants/routes.ts';
import { Offer } from '../types/offer.ts';

const OFFERS_AMOUNT = 555;
type Props = {
offers: Offer[];
};

export const App = () => (
export const App = ({ offers }: Props) => (
<BrowserRouter>
<Routes>
<Route path={AppRoutes.Default}>
<Route index element={<MainPage offersAmount={OFFERS_AMOUNT} />} />
<Route index element={<MainPage offers={offers} />} />
<Route path={AppRoutes.Login} element={<LoginPage />} />
<Route path={AppRoutes.Favorites} element={(
<PrivateRoute isAuthenticated={false}>
<FavoritesPage />
<PrivateRoute isAuthenticated>
<FavoritesPage offers={offers} />
</PrivateRoute>
)}
/>
<Route path={AppRoutes.Offer} element={<OfferPage />} />
<Route path={AppRoutes.OfferForRouter} element={<OfferPage />} />
</Route>
<Route path='*' element={<Page404 />} />
</Routes>
Expand Down
69 changes: 69 additions & 0 deletions src/components/comment-form/index.tsx
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>
);
};
4 changes: 4 additions & 0 deletions src/components/comment-form/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type CommentFormState = {
rating: string;
review: string;
};
16 changes: 16 additions & 0 deletions src/components/comment-form/utils/index.ts
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 '';
}
};
44 changes: 44 additions & 0 deletions src/components/favorite-card/index.tsx
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">&euro;{price}</b>
<span className="place-card__price-text">&#47;&nbsp;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>
);
};
3 changes: 3 additions & 0 deletions src/components/index.ts
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';
90 changes: 56 additions & 34 deletions src/components/offer-card/index.tsx
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">&euro;120</b>
<span className="place-card__price-text">&#47;&nbsp;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 &amp; 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">&euro;{price}</b>
<span className="place-card__price-text">&#47;&nbsp;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.
33 changes: 33 additions & 0 deletions src/components/offers-list/index.tsx
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>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ReactNode } from 'react';
import { Navigate } from 'react-router-dom';
import { AppRoutes } from '../../../app/routes';
import { AppRoutes } from '../../constants/routes.ts';

type Props = {
isAuthenticated: boolean;
Expand Down
3 changes: 2 additions & 1 deletion src/app/routes/index.ts → src/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export enum AppRoutes {
Default = '/',
Login = '/login',
Favorites = '/favorites',
Offer = 'offer/:id',
OfferForRouter = 'offer/:offerId',
Offer = 'offer/',
}
3 changes: 2 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { App } from './app';
import { offersMock } from './mocks/offers.ts';


const root = ReactDOM.createRoot(
Expand All @@ -9,7 +10,7 @@ const root = ReactDOM.createRoot(

root.render(
<React.StrictMode>
<App />
<App offers={offersMock} />
</React.StrictMode>
);

Loading

0 comments on commit 07946ed

Please sign in to comment.