Files
fishfish/frontend/src/pages/AdminOrderPage.jsx
T

190 lines
6.2 KiB
React

import { useState, useEffect } from "react";
import { getAdminOrder } from "../api";
import { useParams, useNavigate } from "react-router-dom";
// Статусы заказа — ПОДСТАВЬ свои, если в БД они другие
const STATUSES = [
{ value: "new", label: "Новый" },
{ value: "prepared", label: "Собран" },
{ value: "shipped", label: "Отправлен" },
{ value: "delivered", label: "Доставлен" },
{ value: "canceled", label: "Отменён" },
];
export default function AdminOrderDetail({ orderId }) {
const [order, setOrder] = useState(null);
const [status, setStatus] = useState("");
const [items, setItems] = useState([]); // редактируемая копия позиций
const [loading, setLoading] = useState(false);
const [saving, setSaving] = useState(false);
const [error, setError] = useState("");
const { id } = useParams();
const navigate = useNavigate();
// Загружаем заказ при монтировании / смене orderId
useEffect(() => {
setError("");
setLoading(true);
getAdminOrder(id)
.then((res) => {
setOrder(res.data);
setStatus(res.data.status);
setItems((res.data.items ?? []).map((it) => ({ ...it })));
})
.catch((err) => {
setError(err.response?.data?.error ?? "Не удалось загрузить заказ");
})
.finally(() => { });
}, [orderId]);
function handleQuantityChange(itemId, value) {
setItems((prev) =>
prev.map((it) =>
it.item_id === itemId
? { ...it, quantity: value === "" ? "" : parseFloat(value) }
: it,
),
);
}
// Пересчёт итога на лету
const total = items.reduce((sum, it) => {
const q = typeof it.quantity === "number" ? it.quantity : 0;
return sum + it.price * q;
}, 0);
if (!loading) {
return <div className="p-6 text-slate-300">Загрузка заказа</div>;
}
if (!order) {
return (
<div className="p-6">
<p className="text-red-400">{error || "Заказ не найден"}</p>
<button
onClick={() => navigate("/admin")}
className="mt-4 text-slate-300 hover:text-white"
>
Назад к заказам
</button>
</div>
);
}
const created = order.created_at
? new Date(order.created_at).toLocaleString("ru-RU")
: "";
return (
<div className="max-w-2xl mx-auto p-4 sm:p-6">
{/* Шапка */}
<div className="flex items-center justify-between mb-6">
<button
onClick={() => navigate("/admin")}
className="text-slate-400 hover:text-white transition-colors"
>
Заказы
</button>
<h1 className="text-lg font-semibold text-white">
Заказ #{order.order_id}
</h1>
</div>
{error && (
<div className="mb-4 rounded-lg bg-red-500/10 border border-red-500/30 px-4 py-2 text-red-400 text-sm">
{error}
</div>
)}
{/* Мета заказа */}
<div className="rounded-xl bg-slate-800 p-4 mb-4 space-y-3">
{order.address && (
<div className="flex justify-between text-sm">
<span className="text-slate-400">Адрес</span>
<span className="text-slate-200">{order.address}</span>
</div>
)}
{created && (
<div className="flex justify-between text-sm">
<span className="text-slate-400">Создан</span>
<span className="text-slate-200">{created}</span>
</div>
)}
{/* Редактируемый статус */}
<div className="flex items-center justify-between">
<span className="text-slate-400 text-sm">Статус</span>
<select
value={status}
onChange={(e) => setStatus(e.target.value)}
className="bg-slate-700 text-white rounded-lg px-3 py-1.5 text-sm outline-none focus:ring-2 focus:ring-sky-500"
>
{STATUSES.map((s) => (
<option
key={s.value}
value={s.value}
className="bg-slate-800 text-white"
>
{s.label}
</option>
))}
</select>
</div>
</div>
{/* Позиции с редактируемым весом */}
<div className="rounded-xl bg-slate-800 divide-y divide-slate-700 mb-4">
{items.map((it) => (
<div key={it.item_id} className="flex items-center gap-3 p-4">
<div className="grow min-w-0">
<p className="text-white truncate">{it.name}</p>
<p className="text-slate-400 text-sm">
{it.price} / {it.unit}
</p>
</div>
<div className="shrink-0 flex items-center gap-2">
<input
type="number"
step="0.001"
min="0"
value={it.quantity}
onChange={(e) =>
handleQuantityChange(it.item_id, e.target.value)
}
className="w-20 bg-slate-700 text-white rounded-lg px-2 py-1.5 text-right outline-none focus:ring-2 focus:ring-sky-500"
/>
<span className="text-slate-400 text-sm w-8">{it.unit}</span>
</div>
<div className="shrink-0 w-24 text-right text-slate-200">
{(
it.price * (typeof it.quantity === "number" ? it.quantity : 0)
).toFixed(2)}{" "}
</div>
</div>
))}
</div>
{/* Итог */}
<div className="flex justify-between items-center px-4 mb-6">
<span className="text-slate-400">Итого</span>
<span className="text-xl font-semibold text-white">
{total.toFixed(2)}
</span>
</div>
{/* Сохранить */}
<button
disabled={saving}
className="w-full bg-sky-600 hover:bg-sky-500 disabled:opacity-50 text-white rounded-xl py-3 font-medium transition-colors"
>
{saving ? "Сохранение…" : "Сохранить изменения"}
</button>
</div>
);
}