Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
231ca2f199
commit
9966722820
@ -41,8 +41,9 @@ export const useIssue = (key: string) =>
|
||||
useQuery({ queryKey: qk.issue(key), queryFn: () => api.get<IssueDetail>(`/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 }) =>
|
||||
|
||||
@ -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<Response>();
|
||||
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(<IssuesPage project="AWPCA" />, { 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(<IssuesPage project="AWPCA" />, { 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(<IssuesPage project="AWPCA" />, { route: "/issues?fact_status=В работе", path: "/issues" });
|
||||
|
||||
@ -64,19 +64,20 @@ export function IssuesPage({ project }: { project: string }) {
|
||||
const closed = useClosedStatuses();
|
||||
const query = useMemo<Filters>(() => (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 (
|
||||
<div data-testid="page-issues" className="issues">
|
||||
<PageHeader title="Задачи" subtitle={list.isPending ? undefined : `показано ${rows.length}${list.hasNextPage ? " · есть ещё" : ""}`} actions={<RefreshButton />} />
|
||||
<PageHeader title="Задачи" subtitle={loading ? undefined : `показано ${rows.length}${list.hasNextPage ? " · есть ещё" : ""}`} actions={<RefreshButton />} />
|
||||
<IssueFilters filters={filters} set={set} onReset={reset} q={q} setQ={setQ} showClosed={showClosed} opts={opts} hasBoard={!!board.data} />
|
||||
{list.isError && (
|
||||
<Notice tone="error" action={<Button size="md" onClick={() => void list.refetch()}>Повторить</Button>}>{(list.error as Error).message}</Notice>
|
||||
)}
|
||||
{rows.length > 0 && <IssueTable rows={rows} epicNames={names} closed={closed} onOpen={open} />}
|
||||
{list.isPending && <p className="issues__hint">Загрузка…</p>}
|
||||
{!list.isPending && !rows.length && !list.isError && <Notice tone="info">По этим фильтрам задач нет</Notice>}
|
||||
{loading && <p className="issues__hint">Загрузка…</p>}
|
||||
{!loading && !rows.length && !list.isError && <Notice tone="info">По этим фильтрам задач нет</Notice>}
|
||||
{list.hasNextPage && (
|
||||
<div className="issues__more">
|
||||
<Button disabled={list.isFetchingNextPage} onClick={() => void list.fetchNextPage()}>Показать ещё</Button>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user