diff --git a/web/src/hooks/useApi.ts b/web/src/hooks/useApi.ts index 0f28d0c..ed7271d 100644 --- a/web/src/hooks/useApi.ts +++ b/web/src/hooks/useApi.ts @@ -130,12 +130,12 @@ export function useCheckDomain(id: string, enabled = true) { enabled: !!project && !!id && enabled, }) } -export function useZoneRecords(id: string) { +export function useZoneRecords(id: string, enabled = true) { const { project } = useAuth() return useQuery({ queryKey: ["zoneRecords", project?.id, id], queryFn: () => api.zoneRecords(project!.id, id), - enabled: !!project && !!id, + enabled: !!project && !!id && enabled, }) } export function useCreateTemplateFromZone() { diff --git a/web/src/pages/DomainDiffPage.test.tsx b/web/src/pages/DomainDiffPage.test.tsx index 4eec0f8..f6eb08e 100644 --- a/web/src/pages/DomainDiffPage.test.tsx +++ b/web/src/pages/DomainDiffPage.test.tsx @@ -49,6 +49,7 @@ test("apply sends applyPrunes=false by default, true only after opting in", asyn readOnly: [], inSyncCount: 0, }) const applySpy = vi.spyOn(api, "applyDomain").mockResolvedValue({ updates: [], prunes: [], readOnly: [], inSyncCount: 0 }) + const zoneRecordsSpy = vi.spyOn(api, "zoneRecords") const user = userEvent.setup() renderPage() @@ -63,6 +64,32 @@ test("apply sends applyPrunes=false by default, true only after opting in", asyn await user.click(screen.getByRole("button", { name: /apply/i })) await waitFor(() => expect(applySpy).toHaveBeenCalledTimes(2)) expect(applySpy.mock.calls[1]).toEqual([PROJECT_ID, "d1", { applyUpdates: true, applyPrunes: true }]) + + // домен с шаблоном: записи зоны не нужны для диффа — запрос не должен уходить к провайдеру + expect(zoneRecordsSpy).not.toHaveBeenCalled() +}) + +test("пока список доменов грузится — показан общий лоадер, а не баннер об отсутствии шаблона", async () => { + let resolveListDomains: (domains: Domain[]) => void + vi.spyOn(api, "listDomains").mockReturnValue( + new Promise((resolve) => { + resolveListDomains = resolve + }), + ) + const checkSpy = vi.spyOn(api, "checkDomain").mockResolvedValue({ + updates: [], prunes: [], readOnly: [], inSyncCount: 0, + }) + const zoneRecordsSpy = vi.spyOn(api, "zoneRecords") + renderPage() + + expect(await screen.findByText(/загрузка/i)).toBeInTheDocument() + expect(screen.queryByText(/шаблон не привязан/i)).not.toBeInTheDocument() + expect(checkSpy).not.toHaveBeenCalled() + expect(zoneRecordsSpy).not.toHaveBeenCalled() + + resolveListDomains!([domainWithTemplate]) + + expect(await screen.findByRole("button", { name: /apply/i })).toBeInTheDocument() }) test("домен без шаблона показывает записи зоны и не вызывает check", async () => { diff --git a/web/src/pages/DomainDiffPage.tsx b/web/src/pages/DomainDiffPage.tsx index 1d0a7a6..6da5ad4 100644 --- a/web/src/pages/DomainDiffPage.tsx +++ b/web/src/pages/DomainDiffPage.tsx @@ -31,7 +31,10 @@ export function DomainDiffPage() { const check = useCheckDomain(id, hasTemplate) const apply = useApplyDomain(id) - const zoneRecords = useZoneRecords(id) + // Пока список доменов не загружен, hasTemplate недостоверно (false по + // умолчанию из-за domain === undefined) — не дёргаем provider-запрос + // записей зоны, пока не будет точно известно, что шаблона нет. + const zoneRecords = useZoneRecords(id, !domains.isPending && !hasTemplate) const createTemplateFromZone = useCreateTemplateFromZone() const [applyPrunes, setApplyPrunes] = useState(false) const pruneCheckboxId = useId() @@ -50,6 +53,17 @@ export function DomainDiffPage() { createTemplateFromZone.mutate(id) } + if (domains.isPending) { + return ( +
+
+ + Загрузка… +
+
+ ) + } + return (