feat:
This commit is contained in:
@@ -48,7 +48,7 @@ export const createOrder = (data) => axios.post(`${API_URL}/orders/`, data);
|
|||||||
export const getUserOrders = () => axios.get(`${API_URL}/orders`);
|
export const getUserOrders = () => axios.get(`${API_URL}/orders`);
|
||||||
export const getOrder = (id) => axios.get(`${API_URL}/orders/${id}`);
|
export const getOrder = (id) => axios.get(`${API_URL}/orders/${id}`);
|
||||||
|
|
||||||
export const updateProfile = (data) => axios.patch(`${API_URL}/profile`);
|
export const updateProfile = (data) => axios.patch(`${API_URL}/profile`, data);
|
||||||
export const getUserInfo = () => axios.get(`${API_URL}/profile`);
|
export const getUserInfo = () => axios.get(`${API_URL}/profile`);
|
||||||
|
|
||||||
export const uploadItemAvatar = (id, file) => {
|
export const uploadItemAvatar = (id, file) => {
|
||||||
@@ -58,3 +58,9 @@ export const uploadItemAvatar = (id, file) => {
|
|||||||
headers: { "Content-Type": "multipart/form-data" },
|
headers: { "Content-Type": "multipart/form-data" },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getAllOrders = () => axios.get(`${API_URL}/admin/orders`);
|
||||||
|
export const updateOrderStatus = (id, data) =>
|
||||||
|
axios.patch(`${API_URL}/admin/orders/${id}`, data);
|
||||||
|
export const registerAdmin = (data) =>
|
||||||
|
axios.post(`${API_URL}/admin/register`, data);
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { getAllOrders, updateOrderStatus } from "../api/index";
|
||||||
|
|
||||||
|
const STATUSES = [
|
||||||
|
{ value: "new", label: "Новый" },
|
||||||
|
{ value: "packed", label: "Собран" },
|
||||||
|
{ value: "delivered", label: "Доставлен" },
|
||||||
|
{ value: "cancel", label: "Отменён" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function AdminOrders() {
|
||||||
|
const [orders, setOrders] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
const loadOrders = () => {
|
||||||
|
getAllOrders()
|
||||||
|
.then((res) => setOrders(res.data))
|
||||||
|
.catch(() => { })
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadOrders();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleStatusChange = async (id, newStatus) => {
|
||||||
|
try {
|
||||||
|
await updateOrderStatus(id, { status: newStatus });
|
||||||
|
setOrders((prev) =>
|
||||||
|
prev.map((o) => (o.id === id ? { ...o, status: newStatus } : o)),
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
alert("Не удалось изменить статус");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) return <p className="text-white/60">Загрузка...</p>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
{orders.length === 0 ? (
|
||||||
|
<p className="text-white/60">Заказов нет</p>
|
||||||
|
) : (
|
||||||
|
orders.map((order) => (
|
||||||
|
<div
|
||||||
|
key={order.id}
|
||||||
|
className="bg-white/5 rounded-lg p-4 border border-white/10"
|
||||||
|
>
|
||||||
|
{/* шапка: номер + селект статуса */}
|
||||||
|
<div className="flex justify-between items-center mb-3">
|
||||||
|
<span className="font-medium text-white">Заказ #{order.id}</span>
|
||||||
|
<select
|
||||||
|
value={order.status}
|
||||||
|
onChange={(e) => handleStatusChange(order.id, e.target.value)}
|
||||||
|
className="bg-slate-700 text-white text-sm rounded px-3 py-1 border border-white/10"
|
||||||
|
>
|
||||||
|
{STATUSES.map((s) => (
|
||||||
|
<option key={s.value} value={s.value}>
|
||||||
|
{s.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* товары заказа */}
|
||||||
|
<div className="flex flex-col gap-2 mb-3 text-sm text-white/70">
|
||||||
|
{order.items?.map((oi) => (
|
||||||
|
<div key={oi.id} className="flex items-center gap-3">
|
||||||
|
<img
|
||||||
|
src={`/static/items/${oi.item?.avatar_url}`}
|
||||||
|
alt=""
|
||||||
|
className="w-20 h-20 object-cover rounded shrink-0"
|
||||||
|
/>
|
||||||
|
<span className="grow">{oi.item?.name}</span>
|
||||||
|
<span className="whitespace-nowrap">
|
||||||
|
{oi.quantity} {oi.item?.unit} × {oi.price} ₽
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* подвал: адрес + итог */}
|
||||||
|
<div className="flex justify-between pt-2 border-t border-white/10 text-sm">
|
||||||
|
<span className="text-white/50">{order.delivery_address}</span>
|
||||||
|
<span className="font-medium text-white">
|
||||||
|
Итого: {order.total_price} ₽
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import PageLayout from "../components/PageLayout";
|
import PageLayout from "../components/PageLayout";
|
||||||
import AdminItems from "../components/AdminItems";
|
import AdminItems from "../components/AdminItems";
|
||||||
// import AdminOrders from "../components/AdminOrders"; // добавим позже
|
import AdminOrders from "../components/AdminOrders"; // добавим позже
|
||||||
// import AdminStaff from "../components/AdminStaff"; // добавим позже
|
// import AdminStaff from "../components/AdminStaff"; // добавим позже
|
||||||
|
|
||||||
export default function AdminPage() {
|
export default function AdminPage() {
|
||||||
@@ -38,9 +38,7 @@ export default function AdminPage() {
|
|||||||
|
|
||||||
{/* Содержимое активной вкладки */}
|
{/* Содержимое активной вкладки */}
|
||||||
{tab === "items" && <AdminItems />}
|
{tab === "items" && <AdminItems />}
|
||||||
{tab === "orders" && (
|
{tab === "orders" && <AdminOrders />}
|
||||||
<p className="text-white/50">Раздел заказов — в разработке</p>
|
|
||||||
)}
|
|
||||||
{tab === "staff" && (
|
{tab === "staff" && (
|
||||||
<p className="text-white/50">Раздел сотрудников — в разработке</p>
|
<p className="text-white/50">Раздел сотрудников — в разработке</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user