working on....
47
db.json
@@ -10,5 +10,50 @@
|
|||||||
"name": " товарчик",
|
"name": " товарчик",
|
||||||
"price": 111
|
"price": 111
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"basket": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "Очки",
|
||||||
|
"price": 349,
|
||||||
|
"image": "images/glasses.jpg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"favorites": [],
|
||||||
|
"orders": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "Ложка",
|
||||||
|
"image": "images/spoon.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Вилка",
|
||||||
|
"image": "images/fork.jpg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "in-process"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "Утюг",
|
||||||
|
"image": "images/iron.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Бананы",
|
||||||
|
"image": "images/bananas.jpg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "completed"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"profile": {
|
||||||
|
"id": 1,
|
||||||
|
"firstName": "Иван",
|
||||||
|
"lastName": "Иванов",
|
||||||
|
"image": "images/бананы.jpg"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BIN
images/bananas.jpg
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
images/chery.jpg
Normal file
|
After Width: | Height: | Size: 280 KiB |
BIN
images/child.jpg
Normal file
|
After Width: | Height: | Size: 337 KiB |
BIN
images/fork.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
images/glasses.jpg
Normal file
|
After Width: | Height: | Size: 193 KiB |
BIN
images/gmail.png
Normal file
|
After Width: | Height: | Size: 781 B |
BIN
images/iron.jpg
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
images/knife.jpg
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
images/logo.jpg
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
images/masha.jpg
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
images/phone.png
Normal file
|
After Width: | Height: | Size: 595 B |
BIN
images/screwdriver.jpg
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
images/skateboard.jpg
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
images/spoon.jpg
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
images/telegram.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
images/vanadiy.jpg
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
images/vk.png
Normal file
|
After Width: | Height: | Size: 715 B |
BIN
images/бананы.jpg
Normal file
|
After Width: | Height: | Size: 128 KiB |
@@ -24,9 +24,6 @@ export default function Footer() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center mt-4">
|
|
||||||
<img src="images/бананы.jpg" alt="Бананы" className="img-fluid" />
|
|
||||||
</div>
|
|
||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
36
src/hooks/useBasket.jsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
export default function useBasket() {
|
||||||
|
const [basket, setBasket] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('http://localhost:5000/basket')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(setBasket);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const addToBasket = async (item) => {
|
||||||
|
const res = await fetch('http://localhost:5000/basket', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(item)
|
||||||
|
});
|
||||||
|
const newItem = await res.json();
|
||||||
|
setBasket([...basket, newItem]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeFromBasket = async (id) => {
|
||||||
|
await fetch(`http://localhost:5000/basket/${id}`, { method: 'DELETE' });
|
||||||
|
setBasket(basket.filter(item => item.id !== id));
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearBasket = async () => {
|
||||||
|
// Очищаем корзину полностью
|
||||||
|
for (let item of basket) {
|
||||||
|
await fetch(`http://localhost:5000/basket/${item.id}`, { method: 'DELETE' });
|
||||||
|
}
|
||||||
|
setBasket([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { basket, addToBasket, removeFromBasket, clearBasket };
|
||||||
|
}
|
||||||
28
src/hooks/useFavorites.jsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
export default function useFavorites() {
|
||||||
|
const [favorites, setFavorites] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('http://localhost:5000/favorites')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(setFavorites);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const addToFavorites = async (item) => {
|
||||||
|
const res = await fetch('http://localhost:5000/favorites', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(item)
|
||||||
|
});
|
||||||
|
const newItem = await res.json();
|
||||||
|
setFavorites([...favorites, newItem]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeFromFavorites = async (id) => {
|
||||||
|
await fetch(`http://localhost:5000/favorites/${id}`, { method: 'DELETE' });
|
||||||
|
setFavorites(favorites.filter(item => item.id !== id));
|
||||||
|
};
|
||||||
|
|
||||||
|
return { favorites, addToFavorites, removeFromFavorites };
|
||||||
|
}
|
||||||
27
src/hooks/useOrders.jsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
export default function useOrders() {
|
||||||
|
const [orders, setOrders] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('http://localhost:5000/orders')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(setOrders);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const addOrder = async (order) => {
|
||||||
|
const res = await fetch('http://localhost:5000/orders', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(order)
|
||||||
|
});
|
||||||
|
const newOrder = await res.json();
|
||||||
|
setOrders([...orders, newOrder]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Можно реализовать фильтрацию прямо в хуке
|
||||||
|
const inProcess = orders.filter(o => o.status === "in-process");
|
||||||
|
const completed = orders.filter(o => o.status === "completed");
|
||||||
|
|
||||||
|
return { orders, addOrder, inProcess, completed };
|
||||||
|
}
|
||||||
24
src/hooks/useProfile.jsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
export default function useProfile() {
|
||||||
|
const [profile, setProfile] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('http://localhost:5000/profile')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(setProfile);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const updateProfile = async (newProfile) => {
|
||||||
|
// PATCH или PUT — по ситуации
|
||||||
|
const res = await fetch('http://localhost:5000/profile', {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(newProfile)
|
||||||
|
});
|
||||||
|
const updated = await res.json();
|
||||||
|
setProfile(updated);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { profile, updateProfile };
|
||||||
|
}
|
||||||
@@ -1,17 +1,13 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
// import useProfile from "../hooks/useProfile";
|
import useProfile from "../hooks/useProfile";
|
||||||
|
|
||||||
export default function AccountPage() {
|
export default function AccountPage() {
|
||||||
// const { profile, updateProfile } = useProfile();
|
const { profile } = useProfile();
|
||||||
// Пример локального состояния
|
|
||||||
const [profile, setProfile] = useState({
|
|
||||||
firstName: "Иван",
|
|
||||||
lastName: "Иванов",
|
|
||||||
image: "images/бананы.jpg"
|
|
||||||
});
|
|
||||||
|
|
||||||
// Для модального окна и формы редактирования можно реализовать отдельный компонент
|
|
||||||
|
|
||||||
|
if (!profile) {
|
||||||
|
return <div className="text-center">Загрузка...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mt-5">
|
<div className="container mt-5">
|
||||||
<div className="card text-center mx-auto" style={{ maxWidth: 400, maxHeight: 400 }}>
|
<div className="card text-center mx-auto" style={{ maxWidth: 400, maxHeight: 400 }}>
|
||||||
|
|||||||
@@ -1,36 +1,38 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
// импортируй кастомный хук для корзины, который ты сам реализуешь
|
// импортируй кастомный хук для корзины, который ты сам реализуешь
|
||||||
// import useBasket from "../hooks/useBasket";
|
import useBasket from "../hooks/useBasket";
|
||||||
|
|
||||||
export default function BasketPage() {
|
export default function BasketPage() {
|
||||||
// const { basket, removeFromBasket, checkout } = useBasket();
|
|
||||||
// Ниже пример статики, замени на динамику после реализации useBasket
|
|
||||||
|
|
||||||
const basket = [
|
const { basket, addToBasket, removeFromBasket, clearBasket } = useBasket();
|
||||||
{ id: 1, name: "Очки", price: 349, image: "images/glasses.jpg" },
|
// const { basket, removeFromBasket, checkout } = useBasket();
|
||||||
{ id: 2, name: "Chery Tiggo 7 Pro Max", price: 5, image: "images/chery.jpg" },
|
// Ниже пример статики, замени на динамику после реализации useBasket
|
||||||
{ id: 3, name: "Ванадий", price: 2099, image: "images/vanadiy.jpg" },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
// const basket = [
|
||||||
<main className="container d-flex justify-content-center align-items-center" style={{ minHeight: "60vh" }}>
|
// { id: 1, name: "Очки", price: 349, image: "images/glasses.jpg" },
|
||||||
<div className="card p-4 shadow" style={{ minWidth: "60vw" }}>
|
// { id: 2, name: "Chery Tiggo 7 Pro Max", price: 5, image: "images/chery.jpg" },
|
||||||
<h2 className="text-center">Корзина</h2>
|
// { id: 3, name: "Ванадий", price: 2099, image: "images/vanadiy.jpg" },
|
||||||
<ul className="list-group list-group-flush">
|
// ];
|
||||||
{basket.map(item => (
|
|
||||||
<li className="list-group-item d-flex align-items-center" key={item.id}>
|
return (
|
||||||
<img src={item.image} alt={item.name} style={{ width: 100, height: 100 }} className="me-3" />
|
<main className="container d-flex justify-content-center align-items-center" style={{ minHeight: "60vh" }}>
|
||||||
{item.name} <span className="ms-auto">{item.price} руб.</span>
|
<div className="card p-4 shadow" style={{ minWidth: "60vw" }}>
|
||||||
{/* <button onClick={() => removeFromBasket(item.id)} className="btn btn-danger ms-2">Удалить</button> */}
|
<h2 className="text-center">Корзина</h2>
|
||||||
</li>
|
<ul className="list-group list-group-flush">
|
||||||
))}
|
{basket.map(item => (
|
||||||
</ul>
|
<li className="list-group-item d-flex align-items-center" key={item.id}>
|
||||||
<div className="text-center mt-3">
|
<img src={item.image} alt={item.name} style={{ width: 100, height: 100 }} className="me-3" />
|
||||||
<button className="btn btn-success w-100">
|
{item.name} <span className="ms-auto">{item.price} руб.</span>
|
||||||
Оплатить
|
{/* <button onClick={() => removeFromBasket(item.id)} className="btn btn-danger ms-2">Удалить</button> */}
|
||||||
</button>
|
</li>
|
||||||
</div>
|
))}
|
||||||
</div>
|
</ul>
|
||||||
</main>
|
<div className="text-center mt-3">
|
||||||
);
|
<button className="btn btn-success w-100">
|
||||||
|
Оплатить
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
// import useFavorites from "../hooks/useFavorites";
|
import useFavorites from "../hooks/useFavorites";
|
||||||
|
|
||||||
export default function FavoritesPage() {
|
export default function FavoritesPage() {
|
||||||
// const { favorites, removeFromFavorites } = useFavorites();
|
const { favorites, addToFavorites, removeFromFavorites } = useFavorites();
|
||||||
// Ниже пример статических карточек
|
// Ниже пример статических карточек
|
||||||
|
|
||||||
const favorites = [
|
// const favorites = [
|
||||||
{ id: 1, name: "Женщина", price: "бесценна", image: "images/masha.jpg" },
|
// { id: 1, name: "Женщина", price: "бесценна", image: "images/masha.jpg" },
|
||||||
{ id: 2, name: "Отвертка", price: "219 руб", image: "images/screwdriver.jpg" },
|
// { id: 2, name: "Отвертка", price: "219 руб", image: "images/screwdriver.jpg" },
|
||||||
];
|
// ];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mt-4">
|
<div className="container mt-4">
|
||||||
@@ -21,7 +21,7 @@ export default function FavoritesPage() {
|
|||||||
<div className="card-body text-center">
|
<div className="card-body text-center">
|
||||||
<h5 className="card-title">{item.name}</h5>
|
<h5 className="card-title">{item.name}</h5>
|
||||||
<p className="card-text">{item.price}</p>
|
<p className="card-text">{item.price}</p>
|
||||||
{/* <button onClick={() => removeFromFavorites(item.id)} className="btn btn-danger">Удалить</button> */}
|
<button onClick={() => removeFromFavorites(item.id)} className="btn btn-danger">Удалить</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,57 +1,58 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
// import useOrders from "../hooks/useOrders";
|
import useOrders from "../hooks/useOrders";
|
||||||
|
|
||||||
export default function OrderPage() {
|
export default function OrderPage() {
|
||||||
// const { inProcess, completed } = useOrders();
|
|
||||||
// Пример статики
|
|
||||||
|
|
||||||
const inProcess = [
|
const { orders, addOrder, inProcess, completed } = useOrders();
|
||||||
{ id: 1, name: "Ложка", image: "images/spoon.jpg" },
|
// Пример статики
|
||||||
{ id: 2, name: "Вилка", image: "images/fork.jpg" },
|
|
||||||
{ id: 3, name: "Нож", image: "images/knife.jpg" },
|
|
||||||
];
|
|
||||||
const completed = [
|
|
||||||
{ id: 4, name: "Утюг", image: "images/iron.jpg" },
|
|
||||||
{ id: 5, name: "Бананы", image: "images/bananas.jpg" },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
// const inProcess = [
|
||||||
<div className="container mt-4">
|
// { id: 1, name: "Ложка", image: "images/spoon.jpg" },
|
||||||
<h1 className="text-center">Заказы</h1>
|
// { id: 2, name: "Вилка", image: "images/fork.jpg" },
|
||||||
<div className="row">
|
// { id: 3, name: "Нож", image: "images/knife.jpg" },
|
||||||
<div className="col-md-6">
|
// ];
|
||||||
<div className="card shadow-sm">
|
// const completed = [
|
||||||
<div className="card-header bg-warning text-dark">
|
// { id: 4, name: "Утюг", image: "images/iron.jpg" },
|
||||||
<h2 className="h5 m-0">В процессе</h2>
|
// { id: 5, name: "Бананы", image: "images/bananas.jpg" },
|
||||||
|
// ];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mt-4">
|
||||||
|
<h1 className="text-center">Заказы</h1>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-6">
|
||||||
|
<div className="card shadow-sm">
|
||||||
|
<div className="card-header bg-warning text-dark">
|
||||||
|
<h2 className="h5 m-0">В процессе</h2>
|
||||||
|
</div>
|
||||||
|
<div className="card-body">
|
||||||
|
<ul className="list-group list-group-flush">
|
||||||
|
{inProcess.map(item => (
|
||||||
|
<li className="list-group-item d-flex align-items-center mb-2" key={item.id}>
|
||||||
|
<img src={item.image} className="me-2" style={{ width: 200, height: 200 }} alt={item.name} /> {item.name}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-6">
|
||||||
|
<div className="card shadow-sm">
|
||||||
|
<div className="card-header bg-success text-white">
|
||||||
|
<h2 className="h5 m-0">Завершённые</h2>
|
||||||
|
</div>
|
||||||
|
<div className="card-body">
|
||||||
|
<ul className="list-group list-group-flush">
|
||||||
|
{completed.map(item => (
|
||||||
|
<li className="list-group-item d-flex align-items-center mb-2" key={item.id}>
|
||||||
|
<img src={item.image} className="me-2" style={{ width: 200, height: 200 }} alt={item.name} /> {item.name}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card-body">
|
|
||||||
<ul className="list-group list-group-flush">
|
|
||||||
{inProcess.map(item => (
|
|
||||||
<li className="list-group-item d-flex align-items-center mb-2" key={item.id}>
|
|
||||||
<img src={item.image} className="me-2" style={{ minWidth: 90 }} alt={item.name} /> {item.name}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-6">
|
);
|
||||||
<div className="card shadow-sm">
|
|
||||||
<div className="card-header bg-success text-white">
|
|
||||||
<h2 className="h5 m-0">Завершённые</h2>
|
|
||||||
</div>
|
|
||||||
<div className="card-body">
|
|
||||||
<ul className="list-group list-group-flush">
|
|
||||||
{completed.map(item => (
|
|
||||||
<li className="list-group-item d-flex align-items-center mb-2" key={item.id}>
|
|
||||||
<img src={item.image} className="me-2" style={{ minWidth: 90 }} alt={item.name} /> {item.name}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||