Skip to content
Open
Show file tree
Hide file tree
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Use the data from `/public/api` and images from `/public/img` folders. You can r
4. The footer with the link to the GitHub repo and `Back to top` button.
- The content should be limited to the same width as the page content;
- `Back to top` button should scroll to the top smoothly;
5. Add `NotFoundPage` containing text `Page not found` for all the unknown URLs.
5. Add `NotFoundProduct` containing text `Page not found` for all the unknown URLs.
6. All changes the hover effects should be smooth.
7. Scale all image links by 10% on hover.
8. Implement all form elements and icons according to the UI Kit.
Expand Down Expand Up @@ -125,7 +125,7 @@ Create `Favorites` page with a `ProductsList` showing favorite products at `/fav

## Other tasks

1. Add `NotFoundPage` containing text `Page not found` for all the other URLs with the link to `HomePage`.
1. Add `NotFoundProduct` containing text `Page not found` for all the other URLs with the link to `HomePage`.
2. Implement the `Product was not found` state for the `ProductDetailsPage`.

## (*) Advanced tasks
Expand Down
3 changes: 2 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<link rel="icon" type="image/png" href="./src/images/logo_black.svg" />
<title>Phone Catalog</title>
</head>
<body>
<div id="root"></div>
Expand Down
8,104 changes: 4,378 additions & 3,726 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
"license": "GPL-3.0",
"dependencies": {
"@fortawesome/fontawesome-free": "^6.5.2",
"@types/swiper": "^5.4.3",
"bulma": "^1.0.1",
"classnames": "^2.5.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.25.1",
"react-transition-group": "^4.4.5"
"react-svg": "^17.2.4",
"react-transition-group": "^4.4.5",
"swiper": "12.1.2"
},
"devDependencies": {
"@cypress/react18": "^2.0.1",
"@mate-academy/scripts": "^1.8.5",
"@mate-academy/scripts": "^2.1.3",
"@mate-academy/students-ts-config": "*",
"@mate-academy/stylelint-config": "*",
"@types/node": "^20.14.10",
Expand All @@ -43,7 +46,7 @@
"sass": "^1.77.8",
"stylelint": "^16.7.0",
"typescript": "^5.2.2",
"vite": "^5.3.1"
"vite": "^7.3.3"
},
"scripts": {
"start": "mate-scripts start -l",
Expand Down
11 changes: 11 additions & 0 deletions src/App.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@import './styles/main';

.app{
display: flex;
flex-direction: column;
min-height: 100vh;
}

.main{
flex-grow: 1;
}
1 change: 0 additions & 1 deletion src/App.scss

This file was deleted.

23 changes: 19 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import './App.scss';
import styles from './App.module.scss';
import React from 'react';
import { Outlet } from 'react-router-dom';
import { Header } from './components/Header/Header';
import './styles/main.scss';
import { ThemeProvider } from './modules/shared/context/ThemeContext';
import { Footer } from './components/Footer/Footes';
import { CartAndFavProvider } from './modules/shared/context/CartAndFavContext';

export const App = () => (
<div className="App">
<h1>Product Catalog</h1>
</div>
<ThemeProvider>
<CartAndFavProvider>
<div className={styles.app}>
<Header />
<main className={styles.main}>
<Outlet />
</main>
<Footer />
</div>
</CartAndFavProvider>
</ThemeProvider>
);
32 changes: 32 additions & 0 deletions src/Root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { HashRouter as Router, Routes, Route } from 'react-router-dom';
import { HomePage } from './modules/HomePage';
import { NotFoundPage } from './modules/NotFoundPage/NotFoundPage';
import { App } from './App';
import { CartPage } from './modules/CartPage/CartPage';
import { CatalogPage } from './modules/CatalogPage';
import { FavoritesPage } from './modules/FavoritesPage/FavoritesPage';
import { ProductDetailsPage } from './modules/ProductDetailsPage';

export const Root = () => {
return (
<Router>
<Routes>
<Route path="/" element={<App />}>
<Route index element={<HomePage />} />
<Route path="favourites" element={<FavoritesPage />} />
<Route path="cart" element={<CartPage />} />

<Route path="not-found" element={<NotFoundPage />} />

<Route path=":category">
<Route index element={<CatalogPage />} />
<Route path=":itemId" element={<ProductDetailsPage />} />
</Route>

<Route path="*" element={<NotFoundPage />} />
</Route>
</Routes>
</Router>
);
};
23 changes: 23 additions & 0 deletions src/api/fetchClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Product } from '../types/ProductType';
import { AllCategoryType } from '../types/AllCategoryType';

const API_URL_PRODUCT = 'api/products.json';
const API_URL_PHONES = 'api/phones.json';
const API_URL_TABLETS = 'api/tablets.json';
const API_URL_ACCESSORIES = 'api/accessories.json';

export async function getProductData(): Promise<Product[]> {
return fetch(API_URL_PRODUCT).then(response => response.json());
}

export async function getPhonesData(): Promise<AllCategoryType[]> {
return fetch(API_URL_PHONES).then(response => response.json());
}

export async function getTabletsData(): Promise<AllCategoryType[]> {
return fetch(API_URL_TABLETS).then(response => response.json());
}

export async function getAccessoriesData(): Promise<AllCategoryType[]> {
return fetch(API_URL_ACCESSORIES).then(response => response.json());
}
200 changes: 200 additions & 0 deletions src/components/BurgerMenu/BurgerMenu.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
@import '../../styles/main';

$icon-height: 64px;

@keyframes slide-down-fade {
from {
transform: translateY(-20px);
opacity: 0;
}

to {
transform: translateY(0);
opacity: 1;
}
}

@keyframes fade-in-right {
from {
transform: translateX(-15px);
opacity: 0;
}

to {
transform: translateX(0);
opacity: 1;
}
}

.menu {
position: absolute;
z-index: 100;
top: 64px;
left: 0;

display: flex;
flex-direction: column;

width: 100%;
height: calc(100vh - 64px);

background-color: $white-color;

animation: slide-down-fade $transition-time + 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;

.container {
position: relative;

display: flex;
flex: 1;
flex-direction: column;
justify-content: space-between;

padding-top: 40px;

.list {
display: flex;
flex-direction: column;
gap: 32px;
align-items: center;

margin: 0;
padding: 0;

list-style: none;

li {
opacity: 0;
animation: fade-in-right $transition-time cubic-bezier(0.16, 1, 0.3, 1) forwards;

&:nth-child(1) { animation-delay: $transition-time + 0.1s * 1; }
&:nth-child(2) { animation-delay: $transition-time + 0.1s * 2; }
&:nth-child(3) { animation-delay: $transition-time + 0.1s * 3 }
&:nth-child(4) { animation-delay: $transition-time + 0.1s * 4 }
}
}

.icons {
display: flex;

margin-top: auto;
border-top: 1px solid $elements-color;

opacity: 0;
background-color: $white-color;

animation: slide-down-fade $transition-time cubic-bezier(0.16, 1, 0.3, 1) $transition-time forwards;
}
}
}

.link {
position: relative;

display: flex;
align-items: center;

width: fit-content;
padding: 8px 0;

font-weight: 700;
color: $secondary-color;
text-decoration: none;
text-transform: uppercase;
letter-spacing: 0.05em;

transition: color $transition-time ease, transform $transition-time ease;

@include font-dt-uppercase;

&::after {
content: '';

position: absolute;
bottom: 0;
left: 0;
transform-origin: center;
transform: scaleX(0);

width: 100%;
height: 2px;

background-color: $primary-color;

transition: transform $transition-time ease-in-out;
}

&:hover {
transform: translateY(-2px);
color: $primary-color;

&::after {
transform: scaleX(0.5);
}
}

&--active {
color: $primary-color;

&::after {
transform: scaleX(1) !important;
}
}
}

.icon {
position: relative;

display: flex;
flex: 1;
align-items: center;
justify-content: center;

box-sizing: border-box;
height: $icon-height;

box-shadow: -1.1px 0 0 0 $elements-color;

transition: background-color 0.2s ease;


.line {
position: absolute;
top: 0;
left: 0;
transform-origin: center;
transform: scaleX(0);

width: 100%;
height: 2px;

background-color: $primary-color;

transition: transform $transition-time ease-in-out;
}

&--active {
.line {
transform: scaleX(1) !important;
}
}

&:hover {
background-color: rgba($primary-color, 0.03);

.line {
transform: scaleX(0.4);
}
}

.theme {
width: 18px;
height: 18px;
border: 2px solid $primary-color;
border-radius: 50%;

background-color: $accent-color;

transition: all $transition-time ease;
}
}
Loading
Loading