Skip to content

Commit

Permalink
feat: added basic new workflows ui
Browse files Browse the repository at this point in the history
  • Loading branch information
rajesh-jonnalagadda committed Jul 17, 2024
1 parent 103e7c0 commit f5c567b
Show file tree
Hide file tree
Showing 14 changed files with 802 additions and 170 deletions.
1 change: 1 addition & 0 deletions keep-ui/app/workflows/builder/page.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default function PageClient({
}: {
workflow?: string;
workflowId?: string;
isPreview?: boolean;
}) {
const [buttonsEnabled, setButtonsEnabled] = useState(false);
const [generateEnabled, setGenerateEnabled] = useState(false);
Expand Down
133 changes: 133 additions & 0 deletions keep-ui/app/workflows/mockworkflows.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import React, { useState } from "react";
import { MockWorkflow, Workflow } from './models';
import { getApiURL } from "../../utils/apiUrl";
import Loading from "../loading";
import { Button } from "@tremor/react";
import Modal from "@/components/ui/Modal";
import PageClient from "./builder/page.client";
import { useRouter } from "next/navigation";
import Image from "next/image";
import { TiArrowRight } from "react-icons/ti";



export function WorkflowSteps({ workflow }: { workflow: MockWorkflow}) {
const isActionPresent = !!workflow?.actions?.length;
return (
<div className="flex gap-2 items-center mb-4 flex-wrap">
{workflow?.actions?.map((step: any, index: number) => {
console.log("step in action", step, index);
const provider = step?.provider;
return (
<div key={`action-${index}`} className="flex items-center gap-2">
{index > 0 && <TiArrowRight style={{ width: 30, height: 30 }} className="text-gray-500 align-self: center" />}
<Image
src={`/icons/${provider?.type}-icon.png`}
width={30}
height={30}
alt={provider?.type}
className="mt-6"
/>
</div>
);
})}
{workflow?.steps?.map((step: any, index: number) => {
console.log("step in steps", step);
const provider = step?.provider;
return (
<div key={`step-${index}`} className="flex items-center gap-2">
{(index > 0 || isActionPresent) && (
<TiArrowRight style={{ width: 30, height: 30 }} className="text-gray-500 align-self: center" />
)}
<Image
src={`/icons/${provider?.type}-icon.png`}
width={30}
height={30}
alt={provider?.type}
className="mt-6"
/>
</div>
);
})}
</div>
);
}

export default function MockWorkflowCardSection({ mockWorkflows, mockError, mockLoading }:{
mockWorkflows: MockWorkflow[],
mockError: any,
mockLoading: boolean | null,
}) {
const router = useRouter();

const getNameFromId = (id: string) => {
if (!id) {
return '';
}

return id.split('-').join(' ');
}

console.log("mockWorkflows====>", mockWorkflows);

// if mockError is not null, handle the error case
if (mockError) {
return <p>Error: {mockError.message}</p>;
}

return (
<section className="pt-10 mt-10">
<h2 className="text-xl sm:text-2xl font-semibold mb-6">Discover existing workflow templates</h2>
<div className="flex flex-col sm:flex-row justify-between mb-6 flex-wrap">
<div className="flex gap-2 mb-4 sm:mb-0">
<input
type="text"
placeholder="Search through workflow examples..."
className="px-4 py-2 border rounded w-full sm:w-auto"
/>
<button className="px-4 py-2 bg-gray-200 border rounded">Integrations used</button>
</div>
<div className="flex gap-2 flex-wrap">
<button className="px-4 py-2 bg-gray-200 border rounded">All workflows</button>
<button className="px-4 py-2 bg-gray-200 border rounded">Notifications</button>
<button className="px-4 py-2 bg-gray-200 border rounded">Databases</button>
<button className="px-4 py-2 bg-gray-200 border rounded">CI/CD</button>
<button className="px-4 py-2 bg-gray-200 border rounded">Other</button>
</div>
</div>

{mockError && <p className="text-center text-red-100 m-auto">Error: {mockError.message || "Something went wrong!"}</p>}
{!mockLoading && !mockError && mockWorkflows.length === 0 && <p className="text-center m-auto">No workflows found</p>}

<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{mockError && <p className="text-center text-red-100">Error: {mockError.message || "Something went wrong!"}</p>}
{mockLoading && <Loading />}
{!mockLoading && mockWorkflows.length > 0 && mockWorkflows.map((template: any, index: number) => {
const workflow = template.workflow;
console.log("insise th emao workflwo", workflow);
return (
<div key={index} className="card p-4 border rounded bg-white flex flex-col shadow">
<div className="flex-grow">
<WorkflowSteps workflow={workflow} />
<h3 className="text-lg sm:text-xl font-semibold line-clamp-2">{workflow.name || getNameFromId(workflow.id)}</h3>
<p className="mt-2 text-sm sm:text-base line-clamp-3">{workflow.description}</p>
</div>
<div className="mt-auto">
<Button
className="inline-block mt-8 px-4 py-2 border-none bg-gray-200 hover:bg-gray-300 bold-medium transition text-black rounded"
onClick={(e) => {
e.preventDefault();
localStorage.setItem('preview_workflow', JSON.stringify(template));
router.push(`/workflows/preview/${workflow.id}`);
}}
>
Preview
</Button>
</div>
</div>
);
})}
</div>
</section>
);
};
54 changes: 54 additions & 0 deletions keep-ui/app/workflows/models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ export type Trigger = {
};


export type WorkflowExecution = {
id: string;
status: string;
started: string;
execution_time: number;
workflow: Workflow;
};

export type Workflow = {
id: string;
name: string;
Expand All @@ -31,4 +39,50 @@ export type Workflow = {
last_updated: string;
workflow_raw: string;
workflow_raw_id: string;
last_execution_started?: string;
last_executions?: Pick<WorkflowExecution, 'execution_time' | 'status' | 'started'>[]
}

export type MockProvider = {
type: string;
config: string;
with?: {
command?: string;
timeout?: number;
_from?: string;
to?: string;
subject?: string;
html?: string;
};
};

export type MockCondition = {
assert: string;
name: string;
type: string;
};

export type WorkflowAction = {
condition: MockCondition[];
name: string;
provider: MockProvider;
};

export type MockStep = {
name: string;
provider: MockProvider;
};

export type MockTrigger = {
type: string;
};

export type MockWorkflow = {
id: string;
description: string;
triggers: MockTrigger[];
owners: any[]; // Adjust the type if you have more specific information about the owners
services: any[]; // Adjust the type if you have more specific information about the services
steps: MockStep[];
actions: WorkflowAction[];
};
74 changes: 69 additions & 5 deletions keep-ui/app/workflows/noworfklows.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,76 @@
import React from "react";
import React, { useState, useEffect } from "react";
import { CircleStackIcon } from "@heroicons/react/24/outline";
import { Callout, Italic } from "@tremor/react";
import { Callout, Italic, Button } from "@tremor/react";
import Link from "next/link";
import { Workflow } from './models';
import { useRouter } from "next/navigation";
import { MdArrowForwardIos } from "react-icons/md";
import { IoMdCard } from "react-icons/io";


const links = [
{
href: '/learn-basics',
label: 'Learn more about Workflows',
},
{
href: '/create-notification',
label: 'How to create a basic notification flow',
},
{
href: '/get-support',
label: 'Get support on your Workflow',
},
];


const DetailsSection = () => {

const router = useRouter();
return (
<section className="flex flex-col items-center justify-center mb-10">
<h2 className="sm:text-2xl text-xl font-bold">Create your first workflow</h2>
<p className="mt-2 font-bold text-sm">You do not have any workflow added yet.</p>
<div className="text-sm mt-4 text-gray-500 max-w-md text-center">you can start by creating your very first Workflow from scratch, or browse thorugh some available Workflow templates below</div>
<Button
className="mt-4 px-6 py-2"
color="orange"
variant="primary"
onClick={()=>{router.push('/workflows/builder')}}
>
Create a new workflow
</Button>
<div className="mt-10 divide-y flex flex-col border border-gray-200 rounded bg-white shadow text-sm">
{links.map((link) => (
<div
key={link.href}
onClick={() => { router.push(link.href) }}
className="flex items-center p-2 bg-white hover:bg-gray-100 transition cursor-pointer gap-4"
>
<div className="flex flex-row items-center gap-2">
<IoMdCard className="w-4 h-4 text-gray-500"/>
<p className="truncate">{link.label}</p>
</div>
<span className="ml-auto flex items-center">
<MdArrowForwardIos className="w-4 h-4 text-gray-500 ml-2" />
</span>
</div>
))}
</div>
</section>
);
};





const WorkflowsEmptyState = () => {
const loadAlert = () => document.getElementById("uploadWorkflowButton")?.click();

return (
<div className="text-center mt-4">
<Callout
<div className="mt-4">
{/* <Callout
title="No Workflows"
icon={CircleStackIcon}
color="yellow"
Expand All @@ -23,9 +85,11 @@ const WorkflowsEmptyState = () => {
<Link href="/workflows/builder">Workflow Builder</Link>
</Italic>{" "}
to create a new workflow.
</Callout>
</Callout> */}
<DetailsSection />
</div>
);
};

export default WorkflowsEmptyState;

39 changes: 39 additions & 0 deletions keep-ui/app/workflows/preview/[workflowId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use client'
import { useEffect, useState } from 'react';
import PageClient from "../../builder/page.client";
import Loading from "../../../loading";
import Link from "next/link";

export default function PageWithId({ params }: { params: { workflowId: string } }) {
const [workflowPreviewData, setWorkflowPreviewData] = useState<any>(null);
const key = params?.workflowId;

useEffect(() => {
if (key) {
const data = localStorage.getItem('preview_workflow');
if (data) {
setWorkflowPreviewData(JSON.parse(data));
}
} else {
workflowPreviewData({})
}
}, [params.workflowId]);

return (
<>

{!workflowPreviewData && <Loading />}
{workflowPreviewData && workflowPreviewData.name === key && <PageClient workflow={workflowPreviewData.workflow_raw || ''} workflowId={key || ''} />}
{workflowPreviewData && workflowPreviewData.name !== key && <>
<Link className="p-2 bg-orange-500 text-white hover:bg-orange-600 rounded" href="/workflows">
Go Back
</Link>
<div className="flex items-center justify-center min-h-screen">
<div className="text-center text-red-500">Workflow not found!</div>
</div>
</>}


</>
);
}
44 changes: 44 additions & 0 deletions keep-ui/app/workflows/preview/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use client'
import { useEffect, useState, Suspense } from "react";
import Loading from "../../loading";
import PageClient from "../builder/page.client";
import Link from "next/link";

type PageProps = {
params: { workflowId: string };
searchParams: { [key: string]: string | undefined };
};

export default function Page({ params, searchParams }: PageProps) {
const [workflowPreviewData, setWorkflowPreviewData] = useState<any>(null);
const key = params.workflowId || searchParams.workflowId;

useEffect(() => {
if (key) {
const data = localStorage.getItem('preview_workflow');
if (data) {
setWorkflowPreviewData(JSON.parse(data) || {});
}
} else {
setWorkflowPreviewData({});
}
}, [params.workflowId, searchParams.workflowId]);

return (
<>
{!workflowPreviewData && <Loading />}
{workflowPreviewData && workflowPreviewData.name === key &&
<PageClient workflow={workflowPreviewData?.Workflow_raw} workflowId={params?.workflowId} isPreview={true}/>}
{workflowPreviewData && workflowPreviewData.name !== key && <>
<Link className="p-2 bg-orange-500 text-white hover:bg-orange-600 rounded" href="/workflows">
Go Back
</Link>
<div className="flex items-center justify-center min-h-screen">
<div className="text-center text-red-500">Workflow not found!</div>
</div>
</>}


</>
);
}
Loading

0 comments on commit f5c567b

Please sign in to comment.