From 5614fb942282b6e8b8da6c6d624eee86da8906d7 Mon Sep 17 00:00:00 2001 From: nns <278048682+nurdotnet@users.noreply.github.com> Date: Sat, 25 Apr 2026 23:58:49 +0500 Subject: [PATCH] =?UTF-8?q?fix(product-edit):=20=D1=87=D0=B5=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=D0=B5=D1=87=D0=BD=D0=B0=D1=8F=20=D0=BE=D1=88=D0=B8=D0=B1?= =?UTF-8?q?=D0=BA=D0=B0=20400=20+=20=D0=B1=D0=BB=D0=BE=D0=BA=20Save=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=BD=D0=B5=D0=B7=D0=B0=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20IsRequired=20=D1=86?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Сервер 400-нул сохранение товара когда обязательная цена пуста, а UI показывал «Request failed with status code 400» без указания причины. - onError save mutation: достаём response.data.error из axios-ответа и кладём в setError; общий generic message остаётся как fallback. - canSave дополнен проверкой что у каждого PriceType с IsRequired=true есть строка в form.prices с amount > 0 (та же что делает бэкенд, чтобы кнопка не отправляла запрос обречённый на 400). - Под секцией цен — красная подсказка-список незаполненных обязательных типов цен («Заполни обязательные цены: «Розничная2» (значение должно быть больше 0).»). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/pages/ProductEditPage.tsx | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/food-market.web/src/pages/ProductEditPage.tsx b/src/food-market.web/src/pages/ProductEditPage.tsx index a00265b..a982e61 100644 --- a/src/food-market.web/src/pages/ProductEditPage.tsx +++ b/src/food-market.web/src/pages/ProductEditPage.tsx @@ -166,7 +166,10 @@ export function ProductEditPage() { qc.invalidateQueries({ queryKey: ['/api/catalog/products'] }) navigate(created ? `/catalog/products/${created.id}` : '/catalog/products') }, - onError: (e: Error) => setError(e.message), + onError: (e: Error) => { + const msg = (e as { response?: { data?: { error?: string } } }).response?.data?.error ?? e.message + setError(msg) + }, }) const remove = useMutation({ @@ -190,10 +193,21 @@ export function ProductEditPage() { const updateBarcode = (i: number, patch: Partial) => setForm({ ...form, barcodes: form.barcodes.map((b, ix) => ix === i ? { ...b, ...patch } : b) }) + // У каждого PriceType с IsRequired=true должна быть запись в form.prices + // с Amount > 0. Сервер делает ту же проверку (400 иначе), но дублируем + // на фронте чтобы кнопка Save не клатилась с непонятным «Request failed with status 400». + const missingRequiredPrices = (priceTypes.data ?? []) + .filter((pt) => pt.isRequired) + .filter((pt) => { + const row = form.prices.find((p) => p.priceTypeId === pt.id) + return !row || row.amount <= 0 + }) + const canSave = form.name.trim().length > 0 && !!form.unitOfMeasureId && !!form.productGroupId && form.barcodes.length > 0 + && missingRequiredPrices.length === 0 return (
@@ -447,6 +461,11 @@ export function ProductEditPage() { {priceTypes.data?.length === 0 && (
Нет ни одного типа цен. Создай в «Настройки → Типы цен».
)} + {missingRequiredPrices.length > 0 && ( +
+ Заполни обязательные цены: {missingRequiredPrices.map((pt) => `«${pt.name}»`).join(', ')} (значение должно быть больше 0). +
+ )}