Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
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