From 99667228207ee0cfa6df7b0219e32f11cca711d4 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Mon, 26 Oct 2026 01:06:48 +0400 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=BF=D0=B8=D1=81=D0=BE=D0=BA=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B4=D0=B0=D1=87:=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D1=81=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=BD=D0=B0=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=B5=D0=BA,=20=D0=B1=D0=B5=D0=B7=20=D0=BC?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BA=D0=B0=D0=BD=D0=B8=D1=8F=20=C2=AB=D0=B7?= =?UTF-8?q?=D0=B0=D0=B4=D0=B0=D1=87=20=D0=BD=D0=B5=D1=82=C2=BB=20(#124,=20?= =?UTF-8?q?#60=20M-3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- web/src/modules/jira/api/queries.ts | 3 +- .../modules/jira/issues/IssuesPage.test.tsx | 31 +++++++++++++++++++ web/src/modules/jira/issues/IssuesPage.tsx | 9 +++--- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/web/src/modules/jira/api/queries.ts b/web/src/modules/jira/api/queries.ts index 31a20b7..87eb87e 100644 --- a/web/src/modules/jira/api/queries.ts +++ b/web/src/modules/jira/api/queries.ts @@ -41,8 +41,9 @@ export const useIssue = (key: string) => useQuery({ queryKey: qk.issue(key), queryFn: () => api.get(`/issues/${encodeURIComponent(key)}`) }); // список порциями по ISSUE_PAGE: следующая порция есть, пока последняя полная -export const useIssues = (filters: IssueFilters) => +export const useIssues = (filters: IssueFilters, opts?: { enabled?: boolean }) => useInfiniteQuery({ + enabled: opts?.enabled ?? true, queryKey: qk.issues(filters), initialPageParam: 0, queryFn: ({ pageParam }) => diff --git a/web/src/modules/jira/issues/IssuesPage.test.tsx b/web/src/modules/jira/issues/IssuesPage.test.tsx index a1795eb..b4388ef 100644 --- a/web/src/modules/jira/issues/IssuesPage.test.tsx +++ b/web/src/modules/jira/issues/IssuesPage.test.tsx @@ -246,6 +246,37 @@ describe("список задач", () => { await waitFor(() => expect(calls().at(-1)!.searchParams.has("hide_closed")).toBe(false)); }); + it("закрытый статус в адресе до настроек: запроса нет, «Загрузка…», после настроек — один запрос без hide_closed (#60 M-3)", async () => { + const st = deferred(); + const fetch = mockFetch((_m, url) => { + if (url.pathname === "/settings") return st.promise; + if (url.pathname === "/board") return json(200, SNAP); + return json(200, { issues: [row(1)] }); + }); + renderWithProviders(, { route: "/issues?status=Готово", path: "/issues" }); + await screen.findByText("Загрузка…"); + expect(issuesCalls(fetch)).toHaveLength(0); + expect(screen.queryByText("По этим фильтрам задач нет")).toBeNull(); + st.resolve(json(200, { ...settings, closed_statuses: ["Готово"] })); + await screen.findByRole("link", { name: "AURORA-9001" }); + expect(issuesCalls(fetch)).toHaveLength(1); + const q = issuesCalls(fetch)[0].searchParams; + expect(q.get("status")).toBe("Готово"); + expect(q.has("hide_closed")).toBe(false); + }); + + it("R-11: /settings отвечает 500 — список всё равно запрашивается с hide_closed=1", async () => { + const fetch = mockFetch((_m, url) => { + if (url.pathname === "/settings") return json(500, { error: "boom" }); + if (url.pathname === "/board") return json(200, SNAP); + return json(200, { issues: [row(1)] }); + }); + renderWithProviders(, { route: "/issues", path: "/issues" }); + await screen.findByRole("link", { name: "AURORA-9001" }); + expect(issuesCalls(fetch)).toHaveLength(1); + expect(issuesCalls(fetch)[0].searchParams.get("hide_closed")).toBe("1"); + }); + it("статуса по факту нет: ни фильтра, ни колонки; fact_status из адреса не уходит в запрос", async () => { const fetch = hub(() => [row(1)]); renderWithProviders(, { route: "/issues?fact_status=В работе", path: "/issues" }); diff --git a/web/src/modules/jira/issues/IssuesPage.tsx b/web/src/modules/jira/issues/IssuesPage.tsx index c612f0a..c356be3 100644 --- a/web/src/modules/jira/issues/IssuesPage.tsx +++ b/web/src/modules/jira/issues/IssuesPage.tsx @@ -64,19 +64,20 @@ export function IssuesPage({ project }: { project: string }) { const closed = useClosedStatuses(); const query = useMemo(() => (showClosed || isClosed(filters.status, closed) ? { ...filters, project } : { ...filters, project, hide_closed: "1" }), [filters, project, showClosed, closed]); - const list = useIssues(query); + const list = useIssues(query, { enabled: !settings.isPending }); // закрытые статусы — из настроек: без них запрос ушёл бы с неверным hide_closed (#60 M-3); при ошибке настроек isPending=false (R-11) const rows = list.data?.pages.flat() ?? []; + const loading = list.isPending || settings.isPending; return (
- } /> + } /> {list.isError && ( void list.refetch()}>Повторить}>{(list.error as Error).message} )} {rows.length > 0 && } - {list.isPending &&

Загрузка…

} - {!list.isPending && !rows.length && !list.isError && По этим фильтрам задач нет} + {loading &&

Загрузка…

} + {!loading && !rows.length && !list.isError && По этим фильтрам задач нет} {list.hasNextPage && (