New workflow table (#661)

This commit is contained in:
Kerem Yilmaz 2024-07-30 11:17:29 -07:00 committed by GitHub
parent 3c81e4269a
commit dadb3724b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 257 additions and 14 deletions

View file

@ -0,0 +1,7 @@
import { Skeleton } from "./ui/skeleton";
function BadgeLoading() {
return <Skeleton className="h-7 w-24" />;
}
export { BadgeLoading };

View file

@ -69,6 +69,8 @@
--border: 215.3 25% 26.7%;
--input: 215.3 25% 26.7%;
--ring: 212.7 26.8% 83.9%;
--slate-elevation-2: 228 37% 11%;
}
}

View file

@ -1,17 +1,5 @@
import { getClient } from "@/api/AxiosClient";
import { WorkflowApiResponse, WorkflowRunApiResponse } from "@/api/types";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
import { useQuery } from "@tanstack/react-query";
import { useNavigate, useSearchParams } from "react-router-dom";
import { WorkflowsBetaAlertCard } from "./WorkflowsBetaAlertCard";
import { StatusBadge } from "@/components/StatusBadge";
import {
Pagination,
@ -21,9 +9,21 @@ import {
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";
import { cn } from "@/util/utils";
import { WorkflowTitle } from "./WorkflowTitle";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
import { basicTimeFormat } from "@/util/timeFormat";
import { cn } from "@/util/utils";
import { useQuery } from "@tanstack/react-query";
import { useNavigate, useSearchParams } from "react-router-dom";
import { WorkflowsBetaAlertCard } from "./WorkflowsBetaAlertCard";
import { WorkflowTitle } from "./WorkflowTitle";
function Workflows() {
const credentialGetter = useCredentialGetter();

View file

@ -0,0 +1,27 @@
import { Skeleton } from "@/components/ui/skeleton";
import { useWorkflowLastRunQuery } from "../hooks/useWorkflowLastRunQuery";
import { basicTimeFormat } from "@/util/timeFormat";
type Props = {
workflowId: string;
};
function LastRunAtTime({ workflowId }: Props) {
const { data, isLoading } = useWorkflowLastRunQuery({ workflowId });
if (isLoading) {
return <Skeleton className="h-full w-full" />;
}
if (!data) {
return null;
}
if (data.status === "N/A") {
return <span>N/A</span>;
}
return <span>{basicTimeFormat(data.time)}</span>;
}
export { LastRunAtTime };

View file

@ -0,0 +1,27 @@
import { BadgeLoading } from "@/components/BadgeLoading";
import { StatusBadge } from "@/components/StatusBadge";
import { useWorkflowLastRunQuery } from "../hooks/useWorkflowLastRunQuery";
type Props = {
workflowId: string;
};
function LastRunStatus({ workflowId }: Props) {
const { data, isLoading } = useWorkflowLastRunQuery({ workflowId });
if (isLoading) {
return <BadgeLoading />;
}
if (!data) {
return null;
}
if (data.status === "N/A") {
return <span>N/A</span>;
}
return <StatusBadge status={data.status} />;
}
export { LastRunStatus };

View file

@ -0,0 +1,140 @@
import { getClient } from "@/api/AxiosClient";
import { WorkflowApiResponse } from "@/api/types";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
import { useQuery } from "@tanstack/react-query";
import { LastRunStatus } from "./LastRunStatus";
import { LastRunAtTime } from "./LastRunAtTime";
import { Skeleton } from "@/components/ui/skeleton";
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";
import { cn } from "@/util/utils";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
function WorkflowsTable() {
const [page, setPage] = useState(1);
const credentialGetter = useCredentialGetter();
const navigate = useNavigate();
const { data: workflows, isLoading } = useQuery<Array<WorkflowApiResponse>>({
queryKey: ["workflows", page],
queryFn: async () => {
const client = await getClient(credentialGetter);
const params = new URLSearchParams();
params.append("page", String(page));
return client
.get("/workflows", {
params,
})
.then((response) => response.data);
},
});
const skeleton = Array.from({ length: 5 }).map((_, index) => (
<TableRow key={index}>
<TableCell>
<Skeleton className="h-6 w-full" />
</TableCell>
<TableCell>
<Skeleton className="h-6 w-full" />
</TableCell>
<TableCell>
<Skeleton className="h-6 w-full" />
</TableCell>
</TableRow>
));
return (
<div className="space-y-4">
<Table>
<TableHeader className="bg-slate-elevation2 text-slate-400 [&_tr]:border-b-0">
<TableRow className="rounded-lg px-6 [&_th:first-child]:pl-6 [&_th]:py-4">
<TableHead className="text-sm text-slate-400">Title</TableHead>
<TableHead className="text-sm text-slate-400">
Last Run Status
</TableHead>
<TableHead className="text-sm text-slate-400">
Last Run Time
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="h-5"></TableCell>
</TableRow>
</TableBody>
<TableBody>
{isLoading && skeleton}
{workflows?.map((workflow) => {
return (
<TableRow
key={workflow.workflow_permanent_id}
className="cursor-pointer [&_td:first-child]:pl-6 [&_td:last-child]:pr-6 [&_td]:py-4"
onClick={(event) => {
if (event.ctrlKey || event.metaKey) {
window.open(
window.location.origin +
`/workflows/${workflow.workflow_permanent_id}`,
"_blank",
"noopener,noreferrer",
);
return;
}
navigate(`${workflow.workflow_permanent_id}`);
}}
>
<TableCell>
<span className="text-sm leading-5">{workflow.title}</span>
</TableCell>
<TableCell>
<LastRunStatus workflowId={workflow.workflow_permanent_id} />
</TableCell>
<TableCell>
<LastRunAtTime workflowId={workflow.workflow_permanent_id} />
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious
className={cn({ "cursor-not-allowed": page === 1 })}
onClick={() => {
setPage((prev) => Math.max(1, prev - 1));
}}
/>
</PaginationItem>
<PaginationItem>
<PaginationLink>{page}</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext
onClick={() => {
setPage((prev) => prev + 1);
}}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
);
}
export { WorkflowsTable };

View file

@ -0,0 +1,40 @@
import { getClient } from "@/api/AxiosClient";
import { Status, WorkflowRunApiResponse } from "@/api/types";
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
import { useQuery } from "@tanstack/react-query";
type Props = {
workflowId: string;
};
type LastRunInfo = {
status: Status | "N/A";
time: string | "N/A";
};
function useWorkflowLastRunQuery({ workflowId }: Props) {
const credentialGetter = useCredentialGetter();
const queryResult = useQuery<LastRunInfo | null>({
queryKey: ["lastRunInfo", workflowId],
queryFn: async () => {
const client = await getClient(credentialGetter);
const data = (await client
.get(`/workflows/${workflowId}/runs?page_size=1`)
.then((response) => response.data)) as Array<WorkflowRunApiResponse>;
if (data.length === 0) {
return {
status: "N/A",
time: "N/A",
};
}
return {
status: data[0]!.status,
time: data[0]!.created_at,
};
},
});
return queryResult;
}
export { useWorkflowLastRunQuery };