Fix pagination: disable Next correctly for workflows and run history (#3293)
Co-authored-by: Fratiman <bogdanfrman@yahoo.com>
This commit is contained in:
@@ -21,20 +21,53 @@ import {
|
||||
import { useRunsQuery } from "@/hooks/useRunsQuery";
|
||||
import { basicLocalTimeFormat, basicTimeFormat } from "@/util/timeFormat";
|
||||
import { cn } from "@/util/utils";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { getClient } from "@/api/AxiosClient";
|
||||
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
||||
|
||||
function isTask(run: Task | WorkflowRunApiResponse): run is Task {
|
||||
return "task_id" in run;
|
||||
}
|
||||
|
||||
function RunHistory() {
|
||||
const credentialGetter = useCredentialGetter();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const page = searchParams.get("page") ? Number(searchParams.get("page")) : 1;
|
||||
const itemsPerPage = searchParams.get("page_size")
|
||||
? Number(searchParams.get("page_size"))
|
||||
: 5;
|
||||
const [statusFilters, setStatusFilters] = useState<Array<Status>>([]);
|
||||
const { data: runs, isFetching } = useRunsQuery({ page, statusFilters });
|
||||
const { data: runs, isFetching } = useRunsQuery({
|
||||
page,
|
||||
pageSize: itemsPerPage,
|
||||
statusFilters,
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { data: nextPageRuns } = useQuery<Array<Task | WorkflowRunApiResponse>>(
|
||||
{
|
||||
queryKey: ["runs", { statusFilters }, page + 1, itemsPerPage],
|
||||
queryFn: async () => {
|
||||
const client = await getClient(credentialGetter);
|
||||
const params = new URLSearchParams();
|
||||
params.append("page", String(page + 1));
|
||||
params.append("page_size", String(itemsPerPage));
|
||||
if (statusFilters) {
|
||||
statusFilters.forEach((status) => {
|
||||
params.append("status", status);
|
||||
});
|
||||
}
|
||||
return client.get("/runs", { params }).then((res) => res.data);
|
||||
},
|
||||
enabled: runs && runs.length === itemsPerPage,
|
||||
},
|
||||
);
|
||||
|
||||
const isNextDisabled =
|
||||
isFetching || !nextPageRuns || nextPageRuns.length === 0;
|
||||
|
||||
function handleNavigate(event: React.MouseEvent, path: string) {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
window.open(
|
||||
@@ -46,6 +79,22 @@ function RunHistory() {
|
||||
navigate(path);
|
||||
}
|
||||
}
|
||||
|
||||
function setParamPatch(patch: Record<string, string>) {
|
||||
const params = new URLSearchParams(searchParams);
|
||||
Object.entries(patch).forEach(([k, v]) => params.set(k, v));
|
||||
setSearchParams(params, { replace: true });
|
||||
}
|
||||
|
||||
function handlePreviousPage() {
|
||||
if (page === 1) return;
|
||||
setParamPatch({ page: String(page - 1) });
|
||||
}
|
||||
|
||||
function handleNextPage() {
|
||||
if (isNextDisabled) return;
|
||||
setParamPatch({ page: String(page + 1) });
|
||||
}
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between">
|
||||
@@ -151,35 +200,50 @@ function RunHistory() {
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<Pagination className="pt-2">
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
className={cn({ "cursor-not-allowed": page === 1 })}
|
||||
onClick={() => {
|
||||
if (page === 1) {
|
||||
return;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
params.set("page", String(Math.max(1, page - 1)));
|
||||
setSearchParams(params, { replace: true });
|
||||
}}
|
||||
/>
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationLink>{page}</PaginationLink>
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
onClick={() => {
|
||||
const params = new URLSearchParams();
|
||||
params.set("page", String(page + 1));
|
||||
setSearchParams(params, { replace: true });
|
||||
}}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
<div className="relative px-3 py-3">
|
||||
<div className="absolute left-3 top-1/2 flex -translate-y-1/2 items-center gap-2 text-sm">
|
||||
<span className="text-slate-400">Items per page</span>
|
||||
<select
|
||||
className="h-9 rounded-md border border-slate-300 bg-background px-3"
|
||||
value={itemsPerPage}
|
||||
onChange={(e) => {
|
||||
const next = Number(e.target.value);
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set("page_size", String(next));
|
||||
params.set("page", "1");
|
||||
setSearchParams(params, { replace: true });
|
||||
}}
|
||||
>
|
||||
<option value={5}>5</option>
|
||||
<option value={10}>10</option>
|
||||
<option value={20}>20</option>
|
||||
<option value={50}>50</option>
|
||||
</select>
|
||||
</div>
|
||||
<Pagination className="pt-0">
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
className={cn({
|
||||
"cursor-not-allowed opacity-50": page === 1,
|
||||
})}
|
||||
onClick={handlePreviousPage}
|
||||
/>
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationLink>{page}</PaginationLink>
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
className={cn({
|
||||
"cursor-not-allowed opacity-50": isNextDisabled,
|
||||
})}
|
||||
onClick={handleNextPage}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user