Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c3a78e506a | |||
| 6cfec52fb8 | |||
| 4f3380a73c | |||
| 3daa7a52e8 | |||
| b64d0af124 | |||
| 117109a1eb | |||
| 169ce23bed | |||
| 1a03116e40 | |||
| 195fbaf556 | |||
| 371bccf190 | |||
| 7bab2db70a | |||
| ad0b6bc86a | |||
| c44b4a283c | |||
| c5760f27b4 | |||
| ca519f47b1 | |||
| 2812645834 | |||
| b8e88a4866 | |||
| 102da1ad75 | |||
| a84b1972e7 | |||
| 2ba1a63832 |
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"products": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Breed Dry Dog Food",
|
||||
"price": 100,
|
||||
"imageUrl": "./src/assets/images/products/dog-food.svg"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "CANON EOS DSLR Camera",
|
||||
"price": 360,
|
||||
"imageUrl": "./src/assets/images/products/camera.svg"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "ASUS FHD Gaming Laptop",
|
||||
"price": 700,
|
||||
"imageUrl": "./src/assets/images/products/laptop.svg"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Curology Product Set ",
|
||||
"price": 500,
|
||||
"imageUrl": "./src/assets/images/products/curoset.svg"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Kids Electric Car",
|
||||
"price": 960,
|
||||
"imageUrl": "./src/assets/images/products/car.svg"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Jr. Zoom Soccer Cleats",
|
||||
"price": 1160,
|
||||
"imageUrl": "./src/assets/images/products/soccer.svg"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "GP11 Shooter USB Gamepad",
|
||||
"price": 660,
|
||||
"imageUrl": "./src/assets/images/products/gamepad.svg"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "Quilted Satin Jacket",
|
||||
"price": 660,
|
||||
"imageUrl": "./src/assets/images/products/jacket.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3,6 +3,12 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React</title>
|
||||
<style>
|
||||
@@ -11,11 +17,7 @@
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #ecfeff;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
{
|
||||
"name": "store-client",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.25.0",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"eslint": "^9.25.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"globals": "^16.0.0",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
"name": "store-client",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"server": "json-server --watch db.json --port 8000",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-router-dom": "^7.15.1",
|
||||
"sass-embedded": "^1.89.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.25.0",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"eslint": "^9.25.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"globals": "^16.0.0",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import styles from './App.module.css'
|
||||
import SneakersImage from './assets/images/sneakers-image.jpg'
|
||||
|
||||
function Product() {
|
||||
return (
|
||||
<div className={styles.product}>
|
||||
<img src={SneakersImage} alt='' />
|
||||
<div className={styles.content}>
|
||||
<div className={styles.title}>
|
||||
<h4>Sneakers Red & White 2025</h4>
|
||||
<button>+</button>
|
||||
</div>
|
||||
<p className={styles.description}>NIKE</p>
|
||||
<p className={styles.price}>$38.00</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.container}>
|
||||
<ul className={styles.list}>
|
||||
<li>
|
||||
<Product />
|
||||
</li>
|
||||
<li>
|
||||
<Product />
|
||||
</li>
|
||||
<li>
|
||||
<Product />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -1,63 +0,0 @@
|
||||
.container {
|
||||
max-width: 1220px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.list {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-radius: 10px;
|
||||
margin-top: 100px; /* временно */
|
||||
padding: 20px;
|
||||
background-color: white;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.product {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 350px;
|
||||
gap: 20px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
h4 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: black;
|
||||
color: white;
|
||||
border: 0;
|
||||
border-radius: 100%;
|
||||
outline: 0;
|
||||
|
||||
font-size: 20px;
|
||||
padding: 5px 10px;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.price {
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 27C11.5523 27 12 26.5523 12 26C12 25.4477 11.5523 25 11 25C10.4477 25 10 25.4477 10 26C10 26.5523 10.4477 27 11 27Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M25 27C25.5523 27 26 26.5523 26 26C26 25.4477 25.5523 25 25 25C24.4477 25 24 25.4477 24 26C24 26.5523 24.4477 27 25 27Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3 5H7L10 22H26" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10 16.6667H25.59C25.7056 16.6667 25.8177 16.6267 25.9072 16.5535C25.9966 16.4802 26.0579 16.3782 26.0806 16.2648L27.8806 7.26479C27.8951 7.19222 27.8934 7.11733 27.8755 7.04552C27.8575 6.97371 27.8239 6.90678 27.7769 6.84956C27.73 6.79234 27.6709 6.74625 27.604 6.71462C27.5371 6.68299 27.464 6.66661 27.39 6.66666H8" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 20L16.2223 16.2156M18.3158 11.1579C18.3158 13.0563 17.5617 14.8769 16.2193 16.2193C14.8769 17.5617 13.0563 18.3158 11.1579 18.3158C9.2595 18.3158 7.43886 17.5617 6.0965 16.2193C4.75413 14.8769 4 13.0563 4 11.1579C4 9.2595 4.75413 7.43886 6.0965 6.0965C7.43886 4.75413 9.2595 4 11.1579 4C13.0563 4 14.8769 4.75413 16.2193 6.0965C17.5617 7.43886 18.3158 9.2595 18.3158 11.1579V11.1579Z" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 560 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="18" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17 3.57143H2.33333L3.66667 19H14.3333L15.6667 3.57143H1M9 7.42857V15.1429M12.3333 7.42857L11.6667 15.1429M5.66667 7.42857L6.33333 15.1429M6.33333 3.57143L7 1H11L11.6667 3.57143" stroke="black" stroke-width="1.56" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 375 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M24 27V24.3333C24 22.9188 23.5224 21.5623 22.6722 20.5621C21.8221 19.5619 20.669 19 19.4667 19H11.5333C10.331 19 9.17795 19.5619 8.32778 20.5621C7.47762 21.5623 7 22.9188 7 24.3333V27" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.5 14C18.9853 14 21 11.9853 21 9.5C21 7.01472 18.9853 5 16.5 5C14.0147 5 12 7.01472 12 9.5C12 11.9853 14.0147 14 16.5 14Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 598 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="18" height="16" viewBox="0 0 18 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 1C2.7912 1 1 2.73964 1 4.88594C1 6.61852 1.7 10.7305 8.5904 14.8873C8.71383 14.961 8.85552 15 9 15C9.14448 15 9.28617 14.961 9.4096 14.8873C16.3 10.7305 17 6.61852 17 4.88594C17 2.73964 15.2088 1 13 1C10.7912 1 9 3.35511 9 3.35511C9 3.35511 7.2088 1 5 1Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 454 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 7C8.239 7 6 9.23669 6 11.9962C6 14.2238 6.875 19.5107 15.488 24.8551C15.6423 24.9499 15.8194 25 16 25C16.1806 25 16.3577 24.9499 16.512 24.8551C25.125 19.5107 26 14.2238 26 11.9962C26 9.23669 23.761 7 21 7C18.239 7 16 10.028 16 10.028C16 10.028 13.761 7 11 7Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 460 B |
|
After Width: | Height: | Size: 246 KiB |
|
After Width: | Height: | Size: 628 KiB |
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 394 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 406 KiB |
|
Before Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,4 @@
|
||||
$white-color: white;
|
||||
$black-color: black;
|
||||
|
||||
$primary-color: #db4444;
|
||||
@@ -0,0 +1,30 @@
|
||||
import styles from './index.module.scss'
|
||||
|
||||
import WishIcon from '../../assets/icons/wish.svg'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
const ProductCard = ({ id, name, price, imageUrl }) => {
|
||||
return (
|
||||
|
||||
<div className={styles.product}>
|
||||
<div className={styles.image}>
|
||||
<img src={imageUrl} alt='' />
|
||||
<div className={styles.buttons}>
|
||||
<button>
|
||||
<img src={WishIcon} alt='' />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<Link to={{ pathname: `/catalog/${id}` }} state={ name, price}>
|
||||
<h4>{name}</h4>
|
||||
</Link>
|
||||
<div className={styles.stats}>
|
||||
<span>${price}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductCard
|
||||
@@ -0,0 +1,48 @@
|
||||
@use '../../assets/styles/variables' as *;
|
||||
|
||||
.product {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
width: 270px;
|
||||
|
||||
.image {
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 35px;
|
||||
height: 250px;
|
||||
position: relative;
|
||||
|
||||
.buttons {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
padding: 10px;
|
||||
background-color: white;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.stats {
|
||||
span {
|
||||
font-size: 16px;
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import styles from './index.module.scss'
|
||||
|
||||
import CartIcon from '../../assets/icons/cart.svg'
|
||||
import UserIcon from '../../assets/icons/user.svg'
|
||||
import WishlistIcon from '../../assets/icons/wishlist.svg'
|
||||
import LoopIcon from '../../assets/icons/loop.svg'
|
||||
import { Link, NavLink } from 'react-router-dom'
|
||||
|
||||
const Header = () => {
|
||||
return (
|
||||
<header className={styles.header}>
|
||||
<h2>Exclusive</h2>
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<NavLink to={{ pathname: '/' }}
|
||||
className={ ({ isActive }) => (isActive ? styles.active : '')}>
|
||||
Главная
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={{ pathname: '/contact' }}>Контакты</Link>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink to={{ pathname: '/about' }}
|
||||
className={ ({ isActive }) => (isActive ? styles.active : '')}>
|
||||
О нас
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={{ pathname: '/login' }}>Sign Up</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div className={styles.search}>
|
||||
<input type='text' placeholder='What are you looking for?' />
|
||||
<img src={LoopIcon} alt='' />
|
||||
</div>
|
||||
<div className={styles.buttons}>
|
||||
<button>
|
||||
<Link to={{ pathname: '/wishlist' }}>
|
||||
<img src={WishlistIcon} alt='' />
|
||||
</Link>
|
||||
</button>
|
||||
<button>
|
||||
<NavLink to={{ pathname: '/cart', search: '?category=electronic', state: {id: 44} }}
|
||||
className={ ({ isActive }) => (isActive ? styles.active : '')}>
|
||||
<img src={CartIcon} alt='' />
|
||||
</NavLink>
|
||||
</button>
|
||||
<button>
|
||||
<NavLink to={{ pathname: '/profile/111' }}
|
||||
className={ ({ isActive }) => (isActive ? styles.active : '')}>
|
||||
<img src={UserIcon} alt='' />
|
||||
</NavLink>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
||||
@@ -0,0 +1,58 @@
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid gainsboro;
|
||||
padding: 30px 0;
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
gap: 48px;
|
||||
|
||||
a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #f5f5f5;
|
||||
padding: 7px 12px;
|
||||
gap: 35px;
|
||||
|
||||
input {
|
||||
outline: 0;
|
||||
border: 0;
|
||||
background-color: #f5f5f5;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
img {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
button {
|
||||
background: none;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.active {
|
||||
border-bottom: 1px solid red;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import React from 'react'
|
||||
import styles from './index.module.scss'
|
||||
|
||||
import ProductCard from '../card'
|
||||
|
||||
const ProductList = () => {
|
||||
const [products, setProducts] = React.useState([])
|
||||
const [loading, setLoading] = React.useState(true)
|
||||
const [error, setError] = React.useState(null)
|
||||
console.log("rendred");
|
||||
|
||||
React.useEffect(() => {
|
||||
let cancelled = false
|
||||
console.log("useEffect");
|
||||
const getProducts = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
|
||||
const response = await fetch('http://localhost:8000/products')
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
if (!cancelled) {
|
||||
setProducts(data)
|
||||
}
|
||||
} catch (err) {
|
||||
if (!cancelled) {
|
||||
setError(err.message)
|
||||
setProducts([])
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getProducts()
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
},[])
|
||||
|
||||
if (loading) {
|
||||
return <div className={styles.loading}>Загрузка продуктов...</div>
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className={styles.error}>
|
||||
<p>Ошибка при загрузке продуктов: {error}</p>
|
||||
<button onClick={() => window.location.reload()}>
|
||||
Попробовать снова
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className={styles.list}>
|
||||
{products.map(product => (
|
||||
<li key={product.id}>
|
||||
<ProductCard {...product} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductList
|
||||
@@ -0,0 +1,7 @@
|
||||
.list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
list-style: none;
|
||||
padding-top: 60px;
|
||||
gap: 30px;
|
||||
}
|
||||
@@ -1,9 +1,29 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Routes,
|
||||
Route,
|
||||
Navigate, // 1. Импортируем компонент для редиректа
|
||||
} from 'react-router-dom'
|
||||
|
||||
import Catalog from './pages/catalog'
|
||||
import Cart from './pages/cart'
|
||||
import About from './pages/about'
|
||||
import Login from './pages/login'
|
||||
import UserProfile from './pages/profile'
|
||||
import Item from './pages/item'
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>
|
||||
<Router>
|
||||
<Routes>
|
||||
{/* 2. Добавляем редирект с корневого пути */}
|
||||
<Route path='/' element={<Navigate to='/catalog' replace />} />
|
||||
<Route path='/catalog' element={<Catalog />} />
|
||||
<Route path='/catalog/:id' element={<Item />} />
|
||||
<Route path='/cart' element={<Cart />} />
|
||||
<Route path='/about' element={<About />} />
|
||||
<Route path='/login' element={<Login />} />
|
||||
<Route path='/profile/:id' element={<UserProfile name='Alex' />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import styles from './index.module.scss'
|
||||
|
||||
import Header from '../../components/header/index'
|
||||
|
||||
const About = () => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Header />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default About
|
||||
@@ -0,0 +1,4 @@
|
||||
.container {
|
||||
max-width: 1170px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import styles from './index.module.scss'
|
||||
|
||||
import Header from '../../components/header/index'
|
||||
|
||||
const Cart = () => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Header />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Cart
|
||||
@@ -0,0 +1,4 @@
|
||||
.container {
|
||||
max-width: 1170px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import styles from './index.module.scss'
|
||||
|
||||
import Header from '../../components/header/index'
|
||||
import ProductList from '../../components/list/index'
|
||||
|
||||
const Catalog = () => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Header />
|
||||
<ProductList />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Catalog
|
||||
@@ -0,0 +1,4 @@
|
||||
.container {
|
||||
max-width: 1170px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import styles from './index.module.scss'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
const Product = ({ name, price }) => {
|
||||
const { id } = useParams();
|
||||
return (
|
||||
<>
|
||||
<h2>Товар {id}. {name} по цене {price }</h2>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Product
|
||||
@@ -0,0 +1,22 @@
|
||||
import styles from './index.module.scss'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
const Login = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = formData => {
|
||||
//Какая то логика проверки авторизации
|
||||
navigate('/catalog')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Страница авторизации</h2>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<button type="submit">Войти</button>
|
||||
</form>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Login
|
||||
@@ -0,0 +1,15 @@
|
||||
import styles from './index.module.scss'
|
||||
import Header from '../../components/header/index'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
|
||||
const UserProfile = ({ name }) => {
|
||||
const { id } = useParams();
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Header />
|
||||
<h2>Профиль пользователя {id} {name}</h2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default UserProfile
|
||||
@@ -0,0 +1,4 @@
|
||||
.container {
|
||||
max-width: 1170px;
|
||||
margin: 0 auto;
|
||||
}
|
||||