From 7ca8967bc3adb7a3caaf104c7da5176c274c4fc5 Mon Sep 17 00:00:00 2001 From: LeonG11 Date: Wed, 24 Jun 2026 13:13:09 +0300 Subject: [PATCH] feat: add page items --- frontend/src/App.jsx | 2 + frontend/src/components/CardItem.jsx | 7 +- frontend/src/pages/Cart.jsx | 6 +- frontend/src/pages/ItemPage.jsx | 150 +++++++++++++++++++++++++++ frontend/src/pages/Profile.jsx | 3 +- 5 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 frontend/src/pages/ItemPage.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 764e7dc..d39847f 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -8,6 +8,7 @@ import NotFoundPage from "./pages/NotFoundPage"; import Contact from "./pages/Contact"; import Cart from "./pages/Cart"; import Profile from "./pages/Profile"; +import ItemPage from "./pages/ItemPage"; const PrivateRoute = ({ children }) => { const token = localStorage.getItem("token"); @@ -37,6 +38,7 @@ export default function App() { } /> + } /> } /> diff --git a/frontend/src/components/CardItem.jsx b/frontend/src/components/CardItem.jsx index 2e9782d..c5b3478 100644 --- a/frontend/src/components/CardItem.jsx +++ b/frontend/src/components/CardItem.jsx @@ -1,7 +1,9 @@ import { useState } from "react"; import Arrow from "../components/Arrow"; +import { useNavigate } from "react-router-dom"; export default function CardItem({ item, onAddToCart }) { + const navigate = useNavigate(); const isPiece = item.unit_type === "piece"; const step = isPiece ? 1 : 0.1; const minValue = isPiece ? 1 : 0.1; @@ -20,7 +22,10 @@ export default function CardItem({ item, onAddToCart }) { return (
-
+
navigate(`/items/${item.id}`)} + > {item.name} navigate(`/items/${ci.item.id}`)} />

diff --git a/frontend/src/pages/ItemPage.jsx b/frontend/src/pages/ItemPage.jsx new file mode 100644 index 0000000..e3571e4 --- /dev/null +++ b/frontend/src/pages/ItemPage.jsx @@ -0,0 +1,150 @@ +import PageLayout from "../components/PageLayout"; +import { useState, useEffect } from "react"; +import { useParams } from "react-router-dom"; +import { getItem, addToCart } from "../api/index"; +import Arrow from "../components/Arrow"; + +export default function ItemPage() { + const { id } = useParams(); + const [item, setItem] = useState(null); + const [loading, setLoading] = useState(true); + const [quantity, setQuantity] = useState(1); + + useEffect(() => { + getItem(id) + .then((res) => setItem(res.data)) + .catch(() => { }) + .finally(() => setLoading(false)); + }, [id]); + + const isPiece = item?.unit_type === "piece"; + const step = isPiece ? 1 : 0.1; + const minValue = isPiece ? 1 : 0.1; + + useEffect(() => { + if (item) setQuantity(isPiece ? 1 : 0.1); + }, [item, isPiece]); + + const handleIncrement = () => { + setQuantity((prev) => parseFloat((prev + step).toFixed(3))); + }; + const handleDecrement = () => { + setQuantity((prev) => + prev > minValue ? parseFloat((prev - step).toFixed(3)) : minValue, + ); + }; + + const handleAddToCart = async () => { + try { + await addToCart({ item_id: item.id, quantity }); + alert(`${item.name} добавлен в корзину`); + } catch { + alert("Не удалось добавить товар в корзину"); + } + }; + + if (loading) { + return ( + +

Загрузка...

+ + ); + } + + if (!item) { + return ( + +

Товар не найден

+
+ ); + } + + return ( + +
+
+ {/* ЛЕВО — фото */} +
+ {item.name} +
+ + {/* ПРАВО — всё инфо в одной колонке */} +
+

+ {item.name} +

+ +

+ {item.price} ₽ + / {item.unit} +

+ +
+
+ Категория + {item.category} +
+
+ Город + {item.city} +
+
+ Тип + {isPiece ? "Поштучно" : "На вес"} +
+
+ В наличии + 0 ? "text-green-400" : "text-red-400"} + > + {item.stock > 0 ? `${item.stock} ${item.unit}` : "нет"} + +
+
+ +
+ Количество: + + + {quantity} {item.unit} + + +
+ +

+ Стоимость:{" "} + + {parseFloat((quantity * item.price).toFixed(2))} ₽ + +

+ + +
+
+
+
+ ); +} diff --git a/frontend/src/pages/Profile.jsx b/frontend/src/pages/Profile.jsx index 5a6b88e..50c0266 100644 --- a/frontend/src/pages/Profile.jsx +++ b/frontend/src/pages/Profile.jsx @@ -97,7 +97,8 @@ export default function Profile() { {oi.item?.name} navigate(`/items/${oi.item.id}`)} /> {oi.item?.name}