Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🔄 synced local 'skyvern-frontend/src/' with remote 'skyvern-frontend/…
Browse files Browse the repository at this point in the history
…src/'

<!-- ELLIPSIS_HIDDEN -->

| 🚀 | This description was created by [Ellipsis](https://www.ellipsis.dev) for commit 66851bd5d051b072fd6ec6577110f7f3557b20e4  |
|--------|--------|

### Summary:
Introduced a new workflow table with components for displaying workflow status and last run time, along with necessary CSS and Tailwind configuration updates.

**Key points**:
- Added `skyvern-frontend/src/routes/workflows/components/WorkflowsTable.tsx` to display workflows with pagination.
- Introduced `skyvern-frontend/src/routes/workflows/components/LastRunStatus.tsx` and `skyvern-frontend/src/routes/workflows/components/LastRunAtTime.tsx` for displaying the last run status and time of workflows.
- Created `skyvern-frontend/src/components/BadgeLoading.tsx` for loading skeletons.
- Added `skyvern-frontend/src/routes/workflows/hooks/useWorkflowLastRunQuery.ts` to fetch the last run information of workflows.
- Updated `skyvern-frontend/src/routes/workflows/Workflows.tsx` to integrate the new `WorkflowsTable` component.
- Modified `skyvern-frontend/src/index.css` and `skyvern-frontend/tailwind.config.js` to include new CSS variables and Tailwind configurations for the updated UI.

----
Generated with ❤️ by [ellipsis.dev](https://www.ellipsis.dev)

<!-- ELLIPSIS_HIDDEN -->
ykeremy committed Jul 30, 2024
1 parent 3c81e42 commit 5c8eb0b
Showing 7 changed files with 254 additions and 11 deletions.
7 changes: 7 additions & 0 deletions skyvern-frontend/src/components/BadgeLoading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Skeleton } from "./ui/skeleton";

function BadgeLoading() {
return <Skeleton className="h-7 w-24" />;
}

export { BadgeLoading };
2 changes: 2 additions & 0 deletions skyvern-frontend/src/index.css
Original file line number Diff line number Diff line change
@@ -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%;
}
}

22 changes: 11 additions & 11 deletions skyvern-frontend/src/routes/workflows/Workflows.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { getClient } from "@/api/AxiosClient";
import { WorkflowApiResponse, WorkflowRunApiResponse } from "@/api/types";
import { StatusBadge } from "@/components/StatusBadge";
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";
import {
Table,
TableBody,
@@ -9,21 +18,12 @@ import {
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 { StatusBadge } from "@/components/StatusBadge";
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";
import { cn } from "@/util/utils";
import { WorkflowTitle } from "./WorkflowTitle";
import { basicTimeFormat } from "@/util/timeFormat";

function Workflows() {
const credentialGetter = useCredentialGetter();
27 changes: 27 additions & 0 deletions skyvern-frontend/src/routes/workflows/components/LastRunAtTime.tsx
Original file line number Diff line number Diff line change
@@ -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 };
27 changes: 27 additions & 0 deletions skyvern-frontend/src/routes/workflows/components/LastRunStatus.tsx
Original file line number Diff line number Diff line change
@@ -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 };
140 changes: 140 additions & 0 deletions skyvern-frontend/src/routes/workflows/components/WorkflowsTable.tsx
Original file line number Diff line number Diff line change
@@ -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 };
Original file line number Diff line number Diff line change
@@ -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 };

0 comments on commit 5c8eb0b

Please sign in to comment.