fix(price-types): IsRequired применяется сразу, без перезагрузки страницы
Some checks are pending
Some checks are pending
Баг: переключение «Обязательная» в /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) <noreply@anthropic.com>
This commit is contained in:
parent
bcc6976bd0
commit
defad7cbb4
|
|
@ -9,7 +9,14 @@ function useLookup<T>(key: string, url: string) {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: [`lookup:${key}`],
|
queryKey: [`lookup:${key}`],
|
||||||
queryFn: async () => (await api.get<PagedResult<T>>(`${url}?pageSize=500`)).data.items,
|
queryFn: async () => (await api.get<PagedResult<T>>(`${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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { Button } from '@/components/Button'
|
||||||
import { Modal } from '@/components/Modal'
|
import { Modal } from '@/components/Modal'
|
||||||
import { Field, TextInput, Checkbox } from '@/components/Field'
|
import { Field, TextInput, Checkbox } from '@/components/Field'
|
||||||
import { useCatalogList, useCatalogMutations } from '@/lib/useCatalog'
|
import { useCatalogList, useCatalogMutations } from '@/lib/useCatalog'
|
||||||
|
import { useQueryClient } from '@tanstack/react-query'
|
||||||
import type { PriceType } from '@/lib/types'
|
import type { PriceType } from '@/lib/types'
|
||||||
|
|
||||||
const URL = '/api/catalog/price-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<PriceType>(URL)
|
const { data, isLoading, page, setPage, search, setSearch, sortKey, sortOrder, setSort } = useCatalogList<PriceType>(URL)
|
||||||
const { create, update, remove } = useCatalogMutations(URL, URL)
|
const { create, update, remove } = useCatalogMutations(URL, URL)
|
||||||
const [form, setForm] = useState<Form | null>(null)
|
const [form, setForm] = useState<Form | null>(null)
|
||||||
|
const qc = useQueryClient()
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
if (!form) return
|
if (!form) return
|
||||||
|
|
@ -33,6 +35,11 @@ export function PriceTypesPage() {
|
||||||
void _omit
|
void _omit
|
||||||
if (id) await update.mutateAsync({ id, input: payload })
|
if (id) await update.mutateAsync({ id, input: payload })
|
||||||
else await create.mutateAsync(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)
|
setForm(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,6 +93,7 @@ export function PriceTypesPage() {
|
||||||
<Button variant="danger" size="sm" onClick={async () => {
|
<Button variant="danger" size="sm" onClick={async () => {
|
||||||
if (confirm('Удалить тип цены?')) {
|
if (confirm('Удалить тип цены?')) {
|
||||||
await remove.mutateAsync(form.id!)
|
await remove.mutateAsync(form.id!)
|
||||||
|
await qc.invalidateQueries({ queryKey: ['lookup:price-types'] })
|
||||||
setForm(null)
|
setForm(null)
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue