diff --git a/frontend/src/components/AdminItems.jsx b/frontend/src/components/AdminItems.jsx index 7af299e..01447fb 100644 --- a/frontend/src/components/AdminItems.jsx +++ b/frontend/src/components/AdminItems.jsx @@ -116,7 +116,6 @@ function ItemFormModal({ item, onClose, onSaved }) { placeholder="Остаток" value={form.stock} onChange={handleChange("stock")} - disabled /> {saving ? "Сохранение..." : isEdit ? "Сохранить" : "Создать"} diff --git a/frontend/src/components/AdminStock.jsx b/frontend/src/components/AdminStock.jsx index 780689a..57594d9 100644 --- a/frontend/src/components/AdminStock.jsx +++ b/frontend/src/components/AdminStock.jsx @@ -1,5 +1,10 @@ import { useState, useEffect } from "react"; -import { getStockMovements } from "../api/index"; +import { + getStockMovements, + stockIn, + stockOut, + getAllItems, +} from "../api/index"; const REASON_LABELS = { purchase: "Закупка", @@ -10,75 +15,245 @@ const REASON_LABELS = { export default function AdminStock() { const [movements, setMovements] = useState([]); + const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); - const loadMovements = () => { - getStockMovements() - .then((res) => { - setMovements(res.data); + // состояние форм + const [inForm, setInForm] = useState({ + item_id: "", + quantity: "", + comment: "", + }); + const [outForm, setOutForm] = useState({ + item_id: "", + quantity: "", + comment: "", + }); + const [busy, setBusy] = useState(false); + + const loadAll = () => { + Promise.all([getStockMovements(), getAllItems()]) + .then(([movRes, itemsRes]) => { + setMovements(movRes.data); + setItems(itemsRes.data); }) .catch(() => { }) .finally(() => setLoading(false)); }; useEffect(() => { - loadMovements(); + loadAll(); }, []); - if (loading) return
Загрузка...
; + // приход + const handleStockIn = async () => { + if (!inForm.item_id || !inForm.quantity || Number(inForm.quantity) <= 0) { + alert("Выберите товар и укажите количество"); + return; + } + setBusy(true); + try { + await stockIn({ + item_id: Number(inForm.item_id), + quantity: Number(inForm.quantity), + comment: inForm.comment, + }); + setInForm({ item_id: "", quantity: "", comment: "" }); + loadAll(); // обновить историю и остатки + } catch { + alert("Не удалось оформить приход"); + } finally { + setBusy(false); + } + }; + + // уход + const handleStockOut = async () => { + if ( + !outForm.item_id || + !outForm.quantity || + Number(outForm.quantity) <= 0 + ) { + alert("Выберите товар и укажите количество"); + return; + } + setBusy(true); + try { + await stockOut({ + item_id: Number(outForm.item_id), + quantity: Number(outForm.quantity), + comment: outForm.comment, + }); + setOutForm({ item_id: "", quantity: "", comment: "" }); + loadAll(); + } catch (err) { + // бэк возвращает "недостаточно товара" при нехватке + alert(err.response?.data?.error || "Не удалось оформить списание"); + } finally { + setBusy(false); + } + }; + + if (loading) returnЗагрузка...
; + + // общий класс инпутов + const inputClass = + "bg-white/5 border border-white/10 rounded px-3 py-2 text-white placeholder-white/40 focus:border-gold focus:outline-none"; return ( -Движений нет
- ) : ( -| Дата | -Товар | -Тип | -Количество | -Причина | -Комментарий | -
|---|---|---|---|---|---|
| - {new Date(m.created_at).toLocaleDateString("ru-RU")} - | -{m.item?.name} | -- - {m.type === "in" ? "Приход" : "Уход"} - - | -- {m.quantity} {m.item?.unit} - | -- {REASON_LABELS[m.reason] || m.reason} - | -- {m.comment || "-"} - | -
Движений нет
+ ) : ( +| Дата | +Товар | +Тип | +Количество | +Причина | +Комментарий | +
|---|---|---|---|---|---|
| + {new Date(m.created_at).toLocaleDateString("ru-RU")} + | +{m.item?.name} | ++ + {m.type === "in" ? "Приход" : "Уход"} + + | ++ {m.quantity} {m.item?.unit} + | ++ {REASON_LABELS[m.reason] || m.reason} + | ++ {m.comment || "—"} + | +