From fea3498b8ba489e54b27984a2624cee0969c7eff Mon Sep 17 00:00:00 2001 From: nns <278048682+nurdotnet@users.noreply.github.com> Date: Sun, 26 Apr 2026 00:38:15 +0500 Subject: [PATCH] =?UTF-8?q?fix(price-types):=20IsRequired=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=BC=D0=B5=D0=BD=D1=8F=D0=B5=D1=82=D1=81=D1=8F=20=D1=81?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D1=83,=20=D0=B1=D0=B5=D0=B7=20=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Баг: переключение «Обязательная» в /catalog/price-types не приводило к валидации в карточке товара — Save шёл и проходил, не требуя цену. Корневая причина — два разных queryKey: - useCatalogMutations в PriceTypesPage инвалидирует ['/api/catalog/price-types'] (для самой List-страницы), - но usePriceTypes-хук, которым пользуется ProductEditPage и ProductsPage, живёт под ключом ['lookup:price-types']. В итоге свежий снапшот справочника не доходит до потребителей. Фикс: - useLookups: staleTime=0 + refetchOnMount: 'always' + refetchOnWindowFocus=true. Любой переход на ProductEditPage делает свежий GET и видит актуальный IsRequired/IsRetail. - PriceTypesPage save/delete: дополнительно вызывают qc.invalidateQueries({ queryKey: ['lookup:price-types'] }) — потребители тут же перерендериваются, без необходимости F5. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/food-market.web/src/lib/useLookups.ts | 9 ++++++++- src/food-market.web/src/pages/PriceTypesPage.tsx | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/food-market.web/src/lib/useLookups.ts b/src/food-market.web/src/lib/useLookups.ts index a1be6fe..055e442 100644 --- a/src/food-market.web/src/lib/useLookups.ts +++ b/src/food-market.web/src/lib/useLookups.ts @@ -9,7 +9,14 @@ function useLookup(key: string, url: string) { return useQuery({ queryKey: [`lookup:${key}`], queryFn: async () => (await api.get>(`${url}?pageSize=500`)).data.items, - staleTime: 5 * 60 * 1000, + // staleTime=0 + refetchOnMount: 'always' гарантируют что любая страница + // (например ProductEditPage), монтирующая usePriceTypes / useUnits / etc, + // сразу подтянет свежий снапшот справочника после правки в его UI — + // без необходимости явно прокидывать invalidate-pair (lookup-ключ ↔ + // URL-ключ useCatalogMutations). + staleTime: 0, + refetchOnMount: 'always', + refetchOnWindowFocus: true, }) } diff --git a/src/food-market.web/src/pages/PriceTypesPage.tsx b/src/food-market.web/src/pages/PriceTypesPage.tsx index df7fe04..e7e96e7 100644 --- a/src/food-market.web/src/pages/PriceTypesPage.tsx +++ b/src/food-market.web/src/pages/PriceTypesPage.tsx @@ -8,6 +8,7 @@ import { Button } from '@/components/Button' import { Modal } from '@/components/Modal' import { Field, TextInput, Checkbox } from '@/components/Field' import { useCatalogList, useCatalogMutations } from '@/lib/useCatalog' +import { useQueryClient } from '@tanstack/react-query' import type { PriceType } from '@/lib/types' const URL = '/api/catalog/price-types' @@ -26,6 +27,7 @@ export function PriceTypesPage() { const { data, isLoading, page, setPage, search, setSearch, sortKey, sortOrder, setSort } = useCatalogList(URL) const { create, update, remove } = useCatalogMutations(URL, URL) const [form, setForm] = useState
(null) + const qc = useQueryClient() const save = async () => { if (!form) return @@ -33,6 +35,11 @@ export function PriceTypesPage() { void _omit if (id) await update.mutateAsync({ id, input: payload }) else await create.mutateAsync(payload) + // useCatalogMutations инвалидирует ['/api/catalog/price-types'] (List на этой + // странице), но usePriceTypes-хук живёт под ключом 'lookup:price-types' — + // явно тригерим перефетч чтобы карточка товара сразу увидела новый + // IsRequired/IsRetail без перезагрузки страницы. + await qc.invalidateQueries({ queryKey: ['lookup:price-types'] }) setForm(null) } @@ -86,6 +93,7 @@ export function PriceTypesPage() {