Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
3 changes: 2 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="img/icons/Favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>Nice Gatgets</title>
</head>
<body>
<div id="root"></div>
Expand Down
9 changes: 5 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
"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 Down
Binary file added public/img/icons/ArrowLeft.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/ArrowLeft111.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/ArrowLeftHollow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/ArrowLeftHover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/ArrowRight.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/ArrowRightHollow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/ArrowRightHover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/ArrowUp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/Close.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/Favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/FavoriteFilled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/Favorites.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/img/icons/Favorites.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/Home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/Menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/icons/Shoppingbag.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions public/img/icons/Shoppingbag.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/logo/Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions public/img/logo/Logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 35 additions & 1 deletion src/App.scss
Original file line number Diff line number Diff line change
@@ -1 +1,35 @@
// not empty
@import './styles/utils';

a {
text-decoration: none;
color: inherit;
transition: color 0.3s;

&:hover {
text-decoration: none;
color: inherit;
}
}

.App {
min-height: 100vh;
display: flex;
flex-direction: column;
font-family: Mont, sans-serif;
background-color: $hover-bg;
color: $primary
}

body {
margin: 0 auto;
font-family: Mont, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;

background-color: #FAFBFC;
color: #313237;
}

.container {
@include container;
}
37 changes: 32 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
import { Navigate, Route, Routes } from 'react-router-dom';
import './App.scss';
import { HomePage } from './modules/HomePage/HomePage';
import { ProductsPage } from './modules/ProductsPage/ProductsPage';
import { CartPage } from './modules/CartPage/CartPage';
import { Header } from '../src/components/Header/Header';
// eslint-disable-next-line max-len
import { ProductDetailsPage } from './modules/ProductsDetailsPage/ProductDetailsPage';
import { FavoritesPage } from './modules/FavoritesPage/FavoritesPage';
import { Footer } from './components/Footer/Footer';

export const App = () => (
<div className="App">
<h1>Product Catalog</h1>
</div>
);
export const App = () => {
return (
<div className="App">
<Header />

<div className="section">
<div className="container">
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/home" element={<Navigate to="/" replace />} />
<Route path="/phones" element={<ProductsPage />} />
<Route path="product/:productId" element={<ProductDetailsPage />} />
<Route path="/tablets" element={<ProductsPage />} />
<Route path="/accessories" element={<ProductsPage />} />
<Route path="/cart" element={<CartPage />} />
<Route path="/favorites" element={<FavoritesPage />} />
</Routes>
</div>
</div>
<Footer />
</div>
);
};
54 changes: 54 additions & 0 deletions src/api/Products.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Product } from '../types/Product';
import { ProductDetails } from '../types/ProductDetails';

const BASE_URL = 'api/products.json';

export function getProducts(): Promise<Product[]> {
return fetch(BASE_URL).then(response => {
if (!response) {
throw new Error('Failed to fetch products');
}

return response.json();
});
}

const fetchFromCategory = (category: string, productId: string) => {
const url = `api/${category}.json`;

return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error();
}

return response.json();
})
.then((products: ProductDetails[]) => {
const found = products.find(p => p.id === productId);

if (!found) {
throw new Error();
}

return found;
});
};

export function getProductDetails(productId: string): Promise<ProductDetails> {
return fetchFromCategory('phones', productId)
.catch(() => {
return fetchFromCategory('tablets', productId);
})
.catch(() => {
return fetchFromCategory('accessories', productId);
});
}

export function getSuggestedProducts(): Promise<Product[]> {
return getProducts().then(products => {
const shuffled = [...products].sort(() => 0.5 - Math.random());

return shuffled.slice(0, 8);
});
}
50 changes: 50 additions & 0 deletions src/components/Breadcrumbs/Breadcrumbs.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
@import '../../styles/utils';

.breadcrumbs {
display: flex;
align-items: flex-start;
gap: 8px;
margin-bottom: 24px;
margin-top: 24px;
}

.homeLink {
display: flex;
align-items: center;
justify-content: center;

img {
width: 16px;
height: 16px;
}
}

.separator {
display: flex;
align-items: center;
justify-content: center;

img {
width: 16px;
height: 16px;
filter: grayscale(100%) opacity(0.5);
}
}

.link {
color: $secondary;
font-size: 12px;
font-weight: 600;
text-decoration: none;
transition: color 0.3s;

&:hover {
color: $primary;
}
}

.lastItem {
color: $secondary;
font-size: 12px;
font-weight: 600;
}
37 changes: 37 additions & 0 deletions src/components/Breadcrumbs/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import { Link } from 'react-router-dom';
import styles from './Breadcrumbs.module.scss';

type Props = {
category: string;
lastItem?: string;
};

export const Breadcrumbs: React.FC<Props> = ({ category, lastItem }) => {
const categoryName = category.charAt(0).toUpperCase() + category.slice(1);

return (
<div className={styles.breadcrumbs} data-cy="breadCrumbs">
<Link to="/" className={styles.homeLink}>
<img src="img/icons/Home.png" alt="Home" />
</Link>

<div className={styles.separator}>
<img src="img/icons/ArrowRight.png" alt=">" />
</div>

<Link to={`/${category}`} className={styles.link}>
{categoryName}
</Link>

{lastItem && (
<>
<div className={styles.separator}>
<img src="img/icons/ArrowRight.png" alt=">" />
</div>
<span className={styles.lastItem}>{lastItem}</span>
</>
)}
</div>
);
};
93 changes: 93 additions & 0 deletions src/components/Footer/Footer.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
@import '../../styles/utils';

.footer {
border-top: 1px solid #E2E6E9;
background-color: $white;
padding: 32px 0;
margin-top: auto;
}

.content {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;

@media (max-width: 640px) {
flex-direction: column;
gap: 24px;
}
}

.logo {
display: flex;
align-items: center;
}

.links {
display: flex;
gap: 106px;

a {
text-decoration: none;
color: $secondary;
font-weight: 700;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.04em;
transition: color 0.3s;

&:hover {
color: $primary;
}
}

@media (max-width: 640px) {
flex-direction: column;
align-items: center;
gap: 16px;
}
}

.arrowButton {
width: 32px;
height: 32px;
border: 1px solid #B4BDC3;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.3s;
background-color: transparent;
padding: 0;

img {
width: 14px;
height: 14px;
object-fit: contain;
}
}

.backToTop {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
cursor: pointer;
background: none;
border: none;

color: $secondary;
font-weight: 600;
font-size: 12px;
transition: color 0.3s;

&:hover {
color: $primary;

.arrowButton {
border-color: $primary;
color: $primary;
}
}
}
Loading
Loading