Skip to content

Commit

Permalink
fix: refactor the workflow execution component to reduce repaint/rend…
Browse files Browse the repository at this point in the history
…er the entire page when filters were applied and some minor style changes
  • Loading branch information
rajesh-jonnalagadda committed Oct 15, 2024
1 parent 2b2f9e7 commit 9fdcd95
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 189 deletions.
274 changes: 174 additions & 100 deletions keep-ui/app/workflows/[workflow_id]/executions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ import { JSON_SCHEMA, load } from "js-yaml";
import { ExecutionTable } from "./workflow-execution-table";
import SideNavBar from "./side-nav-bar";
import { useWorkflowRun } from "utils/hooks/useWorkflowRun";
import BuilderWorkflowTestRunModalContent from "../builder/builder-workflow-testrun-modal";
import Modal from "react-modal";
import { TableFilters } from "./table-filters";
import AlertTriggerModal from "../workflow-run-with-alert-modal";
import useSWR from "swr";
import { getApiURL } from "@/utils/apiUrl";
import { fetcher } from "@/utils/fetcher";
import BuilderModalContent from "../builder/builder-modal";
import PageClient from "../builder/page.client";

const tabs = [
{ name: "All Time", value: "alltime" },
Expand Down Expand Up @@ -85,14 +88,7 @@ interface Pagination {
offset: number;
}

export default function WorkflowDetailPage({
params,
}: {
params: { workflow_id: string };
}) {
const router = useRouter();
const { data: session, status, update } = useSession();

const OverViewContent = ({ workflow_id }: { workflow_id: string }) => {
const [executionPagination, setExecutionPagination] = useState<Pagination>({
limit: 25,
offset: 0,
Expand All @@ -108,7 +104,7 @@ export default function WorkflowDetailPage({
}, [tab, searchParams]);

const { data, isLoading, error } = useWorkflowExecutionsV2(
params.workflow_id,
workflow_id,
tab,
executionPagination.limit,
executionPagination.offset
Expand All @@ -122,7 +118,7 @@ export default function WorkflowDetailPage({
message,
} = useWorkflowRun(data?.workflow!);

if (isLoading || !data) return <Loading />;
if (isLoading) return <Loading />;

if (error) {
return (
Expand All @@ -136,8 +132,6 @@ export default function WorkflowDetailPage({
</Callout>
);
}
if (status === "loading" || isLoading || !data) return <Loading />;
if (status === "unauthenticated") router.push("/signin");

const parsedWorkflowFile = load(data?.workflow?.workflow_raw ?? "", {
schema: JSON_SCHEMA,
Expand All @@ -153,98 +147,178 @@ export default function WorkflowDetailPage({
}
};

const workflow = { last_executions: data.items } as Partial<Workflow>;
const workflow = { last_executions: data?.items } as Partial<Workflow>;

return (
<>
<Card className="relative flex p-4 w-full gap-3">
<SideNavBar workflow={data.workflow} />
<div className="relative overflow-auto p-0.5 flex-1 flex-shrink-1">
<div className="sticky top-0 flex justify-between items-end">
<div className="flex-1">
{/*TO DO update searchParams for these filters*/}
<FilterTabs tabs={tabs} setTab={setTab} tab={tab} />
</div>
{!!data.workflow && (
<Button
disabled={isRunning || isRunButtonDisabled}
className="p-2 px-4"
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
e.preventDefault();
handleRunClick?.();
}}
tooltip={message}
>
Run now
</Button>
)}
</div>
{data?.items && (
<div className="mt-2 flex flex-col gap-2">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4 p-0.5">
<StatsCard data={`${data.count ?? 0}`}>
<Title>Total Executions</Title>
<div>
<h1 className="text-2xl font-bold">
{formatNumber(data.count ?? 0)}
</h1>
</div>
</StatsCard>
<StatsCard data={`${data.passCount}/${data.failCount}`}>
<Title>Pass / Fail ratio</Title>
<div>
<h1 className="text-2xl font-bold">
{formatNumber(data.passCount)}
{"/"}
{formatNumber(data.failCount)}
</h1>
</div>
</StatsCard>
<StatsCard>
<Title>Success %</Title>
<div>
<h1 className="text-2xl font-bold">
{(data.count
? (data.passCount / data.count) * 100
: 0
).toFixed(2)}
{"%"}
</h1>
</div>
</StatsCard>
<StatsCard>
<Title>Avg. duration</Title>
<div>
<h1 className="text-2xl font-bold">
{(data.avgDuration ?? 0).toFixed(2)}
</h1>
</div>
</StatsCard>
<StatsCard>
<Title>Involved Services</Title>
<WorkflowSteps workflow={parsedWorkflowFile} />
</StatsCard>
<div className="sticky top-0 flex justify-between items-end">
<div className="flex-1">
{/*TO DO update searchParams for these filters*/}
<FilterTabs tabs={tabs} setTab={setTab} tab={tab} />
</div>
{!!data?.workflow && (
<Button
disabled={isRunning || isRunButtonDisabled}
className="p-2 px-4"
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
e.preventDefault();
handleRunClick?.();
}}
tooltip={message}
>
Run now
</Button>
)}
</div>
{data?.items && (
<div className="mt-2 flex flex-col gap-2">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4 p-0.5">
<StatsCard data={`${data.count ?? 0}`}>
<Title>Total Executions</Title>
<div>
<h1 className="text-2xl font-bold">
{formatNumber(data.count ?? 0)}
</h1>
</div>
<WorkflowGraph
showLastExecutionStatus={false}
workflow={workflow}
limit={executionPagination.limit}
showAll={true}
size="sm"
/>
<h1 className="text-xl font-bold mt-4">Execution History</h1>
<TableFilters workflowId={data.workflow.id} />
<ExecutionTable
executions={data}
setPagination={setExecutionPagination}
/>
</div>
)}
</StatsCard>
<StatsCard data={`${data.passCount}/${data.failCount}`}>
<Title>Pass / Fail ratio</Title>
<div>
<h1 className="text-2xl font-bold">
{formatNumber(data.passCount)}
{"/"}
{formatNumber(data.failCount)}
</h1>
</div>
</StatsCard>
<StatsCard>
<Title>Success %</Title>
<div>
<h1 className="text-2xl font-bold">
{(data.count
? (data.passCount / data.count) * 100
: 0
).toFixed(2)}
{"%"}
</h1>
</div>
</StatsCard>
<StatsCard>
<Title>Avg. duration</Title>
<div>
<h1 className="text-2xl font-bold">
{(data.avgDuration ?? 0).toFixed(2)}
</h1>
</div>
</StatsCard>
<StatsCard>
<Title>Involved Services</Title>
<WorkflowSteps workflow={parsedWorkflowFile} />
</StatsCard>
</div>
<WorkflowGraph
showLastExecutionStatus={false}
workflow={workflow}
limit={executionPagination.limit}
showAll={true}
size="sm"
/>
<h1 className="text-xl font-bold mt-4">Execution History</h1>
<TableFilters workflowId={data.workflow.id} />
<ExecutionTable
executions={data}
setPagination={setExecutionPagination}
/>
</div>
</Card>
{!!data.workflow && !!getTriggerModalProps && (
)}
{!!data?.workflow && !!getTriggerModalProps && (
<AlertTriggerModal {...getTriggerModalProps()} />
)}
</>
);
};

export default function WorkflowDetailPage({
params,
}: {
params: { workflow_id: string };
}) {
const router = useRouter();
const { data: session, status } = useSession();
const [navlink, setNavLink] = useState("overview");

const apiUrl = getApiURL();

const {
data: workflow,
isLoading,
error,
} = useSWR<Partial<Workflow>>(
() => (session ? `${apiUrl}/workflows/${params.workflow_id}/data` : null),
(url: string) => fetcher(url, session?.accessToken)
);

// Render loading state if session is loading
if (status === "loading") return <Loading />;

// Redirect if user is not authenticated
if (status === "unauthenticated") {
useEffect(() => {
router.push("/signin");
}, [router]);
return null;
}

// Handle error state for fetching workflow data
if (isLoading) return <Loading />;
if (error) {
return (
<Callout
className="mt-4"
title="Error"
icon={ExclamationCircleIcon}
color="rose"
>
Failed to load workflow
</Callout>
);
}

if (!workflow) {
return null;
}

return (
<Card className="relative grid p-4 w-full gap-3 grid-cols-[1fr_4fr] h-full">
<SideNavBar
workflow={workflow}
handleLink={setNavLink}
navLink={navlink}
/>
<div className="relative overflow-auto p-0.5 flex-1 flex-shrink-1">
{navlink === "overview" && (
<OverViewContent workflow_id={params.workflow_id} />
)}
{navlink === "builder" && (
<div className="h-[95%]">
<PageClient
workflow={workflow.workflow_raw}
workflowId={workflow.id}
/>
</div>
)}
<div className="h-fit">
{navlink === "view_yaml" && (
<BuilderModalContent
closeModal={() => {}}
compiledAlert={workflow.workflow_raw!}
id={workflow.id}
hideClose={true}
/>
)}
</div>
</div>
</Card>
);
}
6 changes: 2 additions & 4 deletions keep-ui/app/workflows/[workflow_id]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,24 @@
import { ArrowLeftIcon } from "@radix-ui/react-icons";
import Link from "next/link";


export default function Layout({
children,
params,
}: {
children: any;
params: { workflow_id: string };
}) {

return (
<>
<div className="flex items-center mb-4 max-h-full">
<div className="flex flex-col mb-4 h-full gap-6">
<Link
href="/workflows"
className="flex items-center text-gray-500 hover:text-gray-700"
>
<ArrowLeftIcon className="h-5 w-5 mr-1" /> Back to Workflows
</Link>
<div className="flex-1 overflow-auto h-full">{children}</div>
</div>
<div className="overflow-auto">{children}</div>
</>
);
}
Loading

0 comments on commit 9fdcd95

Please sign in to comment.