Skip to content

Commit a6bb277

Browse files
committed
Merge branch 'release'
2 parents b460d80 + b13b8d8 commit a6bb277

File tree

15 files changed

+111
-45
lines changed

15 files changed

+111
-45
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44

55
## Содержание
66

7-
1. [Экраны](#экраны)
7+
1. [Внешний вид](#внешний-вид)
88
1.1 [Главная](#главная)
99
1.2 [Страница просмотра картины](#страница-просмотра-картины)
1010
1.3 [Страница c избранными картинами](#страница-с-избранными-картинами)
1111
2. [Как запустить проект локально](#как-запустить-проект-локально)
1212
3. [Техническое задание](#техническое-задание)
1313
3.1 [Основные требования](#основные-требования)
14-
3.2 [Дополнительные требования](#дополнительные-требования)
14+
3.2 [Дополнительные требования](#дополнительные-требования)
1515
3.3 [Дополнительно](#дополнительно)
1616
4. [Комментарии](#комментарии)
1717
5. [Используемые технологии](#используемые-технологии)
@@ -125,12 +125,13 @@ npm run coverage
125125
- [x] Навигация на предыдущую страницу со страницы `ArtworkPage` при нажатии на кнопку `Back`
126126
- [x] При отсутствии избранных картин или конкретной картины на странице `ArtworkPage` отображается фолбэк контент со ссылкой на `HomePage`
127127
- [x] При увеличение изображения на `ArtworkPage` отображается также альтернативный текст
128+
- [x] При переходе по неверному пути отображается `NotFoundPage`
128129

129130
## Комментарии
130131

131132
- [x] **Реализация сортировки картин на клиенте**: поскольку сортировка с сервера доступна только по полю даты добавления и требует дополнительных поисковых запросов для сортировки по другим критериям, была реализована сортировка на клиентской стороне. Пользователи могут сортировать картины по названию или имени художника (по возрастанию/убыванию) без необходимости дополнительных запросов к серверу.
132133
- [x] **Оганичение пагинации**: пагинация рассчитана на 20 страниц по 5 картин на каждой, поскольку API ограничивает максимальное количество картин в одном запросе до 100. Так как сортировка на клиенте, динамическая загрузка данных не самое хорошее решение (изменения коснутся всех работ сразу, а не только подгруженных => предыдущие загруженные картины окажутся на других страницах).
133-
- [x] **Незагруженные картины**: не все картины загружаются по ссылке, предусмотренной API для этого. Для каждой картины предусмотрен альтернативный текст, который отображается вместо изображения.
134+
- [x] **Незагруженные картины**: не все картины загружаются по ссылке, предусмотренной API для этого. Для таких случаев используется `placeholder картинка` с сообщением `No image`
134135

135136
## Используемые технологии
136137

public/_redirects

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* /index.html 200

src/components/App/App.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { BrowserRouter } from 'react-router-dom';
2-
import { Router } from '../../pages/router';
31
import { ArtworksContextProvider } from '../../store';
42
import { FavoritesContextProvider } from '../../store';
3+
import { routes } from '../../pages/pagesData';
4+
import { RouterProvider } from 'react-router-dom';
55

66
function App() {
77
return (
88
<ArtworksContextProvider>
99
<FavoritesContextProvider>
10-
<BrowserRouter>
11-
<Router />
12-
</BrowserRouter>
10+
<RouterProvider router={routes} />
1311
</FavoritesContextProvider>
1412
</ArtworksContextProvider>
1513
);

src/components/ArtworkCard/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useLocation, useNavigate } from 'react-router-dom';
22
import { FavoriteButton } from '../FavoriteButton';
33
import { Artwork } from '../../types';
4+
import { ROUTES } from '../../constants';
45

56
interface ArtworkCardProps {
67
artwork: Artwork;
@@ -27,7 +28,7 @@ export const ArtworkCard: React.FC<ArtworkCardProps> = ({
2728

2829
window.scrollTo(0, 0);
2930
navigate({
30-
pathname: `/artwork/${artworkId}`,
31+
pathname: `${ROUTES.artwork.replace(':id', String(artworkId))}`,
3132
search: updatedSearchParams.toString(),
3233
});
3334
};
@@ -39,6 +40,10 @@ export const ArtworkCard: React.FC<ArtworkCardProps> = ({
3940
<img
4041
src={`https://www.artic.edu/iiif/2/${artwork.image_id}/full/843,/0/default.jpg`}
4142
alt={artwork.thumbnail?.alt_text ?? artwork.title}
43+
onError={e => {
44+
e.currentTarget.src =
45+
'https://placehold.co/843/f7d5a2/383838?text=no-image&font=lato';
46+
}}
4247
className="artwork__image"
4348
data-testid="image"
4449
/>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Link } from 'react-router-dom';
2+
3+
interface FallbackContentProps {
4+
children: React.ReactNode;
5+
link?: {
6+
linkPath: string;
7+
linkName: string;
8+
};
9+
}
10+
11+
export const FallbackContent: React.FC<FallbackContentProps> = ({
12+
children,
13+
link,
14+
}) => {
15+
return (
16+
<div className="wrapper__fallback-content">
17+
{children}
18+
{link && <Link to={link?.linkPath}>{link?.linkName}</Link>}
19+
</div>
20+
);
21+
};

src/components/Header/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import museumLogoLight from '../../assets/logos/museum-logo-light.svg';
66
import homeIcon from '../../assets/icons/home-icon.svg';
77
import bookmarkIcon from '../../assets/icons/bookmark-light-orange-icon.svg';
88
import burgerIcon from '../../assets/icons/burger-icon.svg';
9+
import { ROUTES } from '../../constants';
910

1011
interface HeaderProps {
1112
isHomePage: boolean;
@@ -32,21 +33,21 @@ const HeaderComponent: React.FC<HeaderProps> = ({ isHomePage }) => {
3233
return (
3334
<header className="header">
3435
<div className="wrapper">
35-
<Link to="/" className="header__navlist__link">
36+
<Link to={ROUTES.home} className="header__navlist__link">
3637
<img src={museumLogoLight} alt="Museum icon with museum title" />
3738
</Link>
3839
<nav className="header__nav">
3940
<ul className="header__navlist">
4041
{!isHomePage && (
4142
<li>
42-
<Link to="/" className="header__navlist__link">
43+
<Link to={ROUTES.home} className="header__navlist__link">
4344
<img src={homeIcon} alt="Home icon" />
4445
<span>Home</span>
4546
</Link>
4647
</li>
4748
)}
4849
<li>
49-
<Link to="/favorites" className="header__navlist__link">
50+
<Link to={ROUTES.favorites} className="header__navlist__link">
5051
<img src={bookmarkIcon} alt="Bookmark icon" />
5152
<span>Your favorites</span>
5253
</Link>

src/components/Sidebar/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Link } from 'react-router-dom';
33
import homeIcon from '../../assets/icons/home-icon.svg';
44
import bookmarkIcon from '../../assets/icons/bookmark-light-orange-icon.svg';
55
import clearIcon from '../../assets/icons/clear-icon.svg';
6+
import { ROUTES } from '../../constants';
67

78
interface SidebarProps {
89
isHomePage: boolean;
@@ -26,14 +27,14 @@ export const Sidebar: React.FC<SidebarProps> = ({
2627
<ul className="sidebar__navlist">
2728
{!isHomePage && (
2829
<li>
29-
<Link to="/" className="sidebar__navlist__link">
30+
<Link to={ROUTES.home} className="sidebar__navlist__link">
3031
<img src={homeIcon} alt="Home icon" />
3132
<span>Home</span>
3233
</Link>
3334
</li>
3435
)}
3536
<li>
36-
<Link to="/favorites" className="sidebar__navlist__link">
37+
<Link to={ROUTES.favorites} className="sidebar__navlist__link">
3738
<img src={bookmarkIcon} alt="Bookmark icon" />
3839
<span>Your favorites</span>
3940
</Link>

src/constants/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ export const HAS_LETTERS_OR_NUMBERS_ERROR_VALUE = [
2525
'Input should include letters or numbers',
2626
];
2727

28+
export const ROUTES = {
29+
home: '/',
30+
favorites: '/favorites',
31+
artwork: '/artwork/:id',
32+
};
33+
34+
export const LINK_TO_HOME_PAGE = {
35+
linkName: 'Go to Home Page',
36+
linkPath: ROUTES.home,
37+
};
38+
2839
//Mock data for tests
2940
export const MOCK_URL = new URL('https://example.com/api/v1/data');
3041
export const MOCK_DATA = { data: 'mock data' };

src/pages/ArtworkPage/index.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import { useContext, useState } from 'react';
2-
import { useParams, Link, useNavigate } from 'react-router-dom';
2+
import { useParams, useSearchParams } from 'react-router-dom';
33
import { ArtworksContext, FavoritesContext } from '../../store';
44
import { PageLayout } from '../../components/PageLayout';
55
import { FavoriteButton } from '../../components/FavoriteButton';
66
import { Modal } from '../../components/Modal';
7+
import { FallbackContent } from '../../components/FallbackContent';
8+
import { LINK_TO_HOME_PAGE } from '../../constants';
9+
import { Link } from 'react-router-dom';
710

811
export const ArtworkPage: React.FC = () => {
912
const { id } = useParams();
13+
const [searchParams] = useSearchParams();
1014
const { artworks } = useContext(ArtworksContext);
1115
const { favoriteArtworks } = useContext(FavoritesContext);
1216

1317
const [isModalOpen, setIsModalOpen] = useState(false);
1418

15-
const navigate = useNavigate();
16-
1719
const currentArtwork =
1820
artworks.find(item => item.id === Number(id)) ||
1921
favoriteArtworks.find(item => item.id === Number(id));
@@ -37,18 +39,23 @@ export const ArtworkPage: React.FC = () => {
3739
)}
3840
{currentArtwork ? (
3941
<div className="artwork-container">
40-
<button
41-
onClick={() => navigate(-1)}
42+
<Link
43+
to={`../..?${searchParams}`}
44+
relative="path"
4245
className="button button-navigate"
4346
>
4447
Back
45-
</button>
48+
</Link>
4649
<article className="artwork-details">
4750
<div className="artwork-details__image-container">
4851
<img
4952
src={`https://www.artic.edu/iiif/2/${currentArtwork.image_id}/full/843,/0/default.jpg`}
5053
alt={currentArtwork.thumbnail?.alt_text ?? currentArtwork.title}
5154
onClick={handleOpenModal}
55+
onError={e => {
56+
e.currentTarget.src =
57+
'https://placehold.co/843/f7d5a2/383838?text=no-image&font=lato';
58+
}}
5259
className="artwork-details__image"
5360
/>
5461
<div className="artwork-details__button">
@@ -103,12 +110,11 @@ export const ArtworkPage: React.FC = () => {
103110
</article>
104111
</div>
105112
) : (
106-
<div className="wrapper__fallback-content">
107-
<p>Artwork not found!</p>
113+
<FallbackContent link={LINK_TO_HOME_PAGE}>
108114
<p>
109-
But don't worry, find another one on <Link to={`/`}>homepage</Link>
115+
Artwork not found! But don't worry, find another one on home page
110116
</p>
111-
</div>
117+
</FallbackContent>
112118
)}
113119
</PageLayout>
114120
);

src/pages/FavoritesPage/index.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { useContext } from 'react';
2-
import { Link } from 'react-router-dom';
32
import { FavoritesContext } from '../../store';
43
import { PageLayout } from '../../components/PageLayout';
54
import { SectionLayout } from '../../components/SectionLayout';
65
import { ArtworkCard } from '../../components/ArtworkCard';
76
import bookmarkIcon from '../../assets/icons/bookmark-icon.svg';
7+
import { FallbackContent } from '../../components/FallbackContent';
8+
import { LINK_TO_HOME_PAGE } from '../../constants';
89

910
export const FavoritesPage: React.FC = () => {
1011
const { favoriteArtworks } = useContext(FavoritesContext);
@@ -29,12 +30,11 @@ export const FavoritesPage: React.FC = () => {
2930
</div>
3031
</SectionLayout>
3132
) : (
32-
<div className="wrapper__fallback-content">
33-
<p>You haven't saved any artworks yet.</p>
33+
<FallbackContent link={LINK_TO_HOME_PAGE}>
3434
<p>
35-
Explore artworks at <Link to={`/`}>homepage</Link>
35+
You haven't saved any artworks yet. Explore artworks on home page
3636
</p>
37-
</div>
37+
</FallbackContent>
3838
)}
3939
</PageLayout>
4040
);

0 commit comments

Comments
 (0)