From e9db5fd4e25108cbac052cc2967e92c073828be9 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen Date: Thu, 21 Sep 2023 14:42:41 -0700 Subject: [PATCH] Remove workflow builder; add workflow details Signed-off-by: Tyler Ohlsen --- common/index.ts | 2 + common/interfaces.ts | 11 +++ public/app.tsx | 25 +++---- public/pages/index.ts | 2 +- .../pages/use_cases/components/use_case.tsx | 2 +- .../workflow_builder/components/index.ts | 8 --- public/pages/workflow_builder/index.ts | 7 -- .../workflow_builder/workflow_builder.tsx | 68 ------------------ .../workflow_detail/components/header.tsx | 31 +++++++++ .../pages/workflow_detail/components/index.ts | 6 ++ public/pages/workflow_detail/index.ts | 6 ++ .../pages/workflow_detail/workflow_detail.tsx | 47 +++++++++++++ .../pages/workflow_detail/workspace/index.ts | 6 ++ .../workflow_detail/workspace/workspace.tsx | 26 +++++++ .../workspace_component/index.ts | 6 ++ .../workspace_component}/input_field_list.tsx | 0 .../input_fields/index.ts | 0 .../input_fields/json_field.tsx | 0 .../input_fields/select_field.tsx | 0 .../input_fields/text_field.tsx | 0 .../new_or_existing_tabs.tsx | 0 .../workspace_component.tsx} | 9 +-- public/pages/workflows/components/columns.tsx | 29 ++++++++ .../workflows/components/workflow_list.tsx | 69 ++++--------------- public/store/reducers/index.ts | 1 + public/store/reducers/workflows_reducer.ts | 40 +++++++++++ public/store/reducers/workspace_reducer.ts | 6 +- public/store/store.ts | 3 +- public/utils/constants.ts | 8 +-- 29 files changed, 252 insertions(+), 166 deletions(-) create mode 100644 common/interfaces.ts delete mode 100644 public/pages/workflow_builder/components/index.ts delete mode 100644 public/pages/workflow_builder/index.ts delete mode 100644 public/pages/workflow_builder/workflow_builder.tsx create mode 100644 public/pages/workflow_detail/components/header.tsx create mode 100644 public/pages/workflow_detail/components/index.ts create mode 100644 public/pages/workflow_detail/index.ts create mode 100644 public/pages/workflow_detail/workflow_detail.tsx create mode 100644 public/pages/workflow_detail/workspace/index.ts create mode 100644 public/pages/workflow_detail/workspace/workspace.tsx create mode 100644 public/pages/workflow_detail/workspace_component/index.ts rename public/pages/{workflow_builder/components => workflow_detail/workspace_component}/input_field_list.tsx (100%) rename public/pages/{workflow_builder/components => workflow_detail/workspace_component}/input_fields/index.ts (100%) rename public/pages/{workflow_builder/components => workflow_detail/workspace_component}/input_fields/json_field.tsx (100%) rename public/pages/{workflow_builder/components => workflow_detail/workspace_component}/input_fields/select_field.tsx (100%) rename public/pages/{workflow_builder/components => workflow_detail/workspace_component}/input_fields/text_field.tsx (100%) rename public/pages/{workflow_builder/components => workflow_detail/workspace_component}/new_or_existing_tabs.tsx (100%) rename public/pages/{workflow_builder/workflow_component.tsx => workflow_detail/workspace_component/workspace_component.tsx} (88%) create mode 100644 public/pages/workflows/components/columns.tsx create mode 100644 public/store/reducers/workflows_reducer.ts diff --git a/common/index.ts b/common/index.ts index 2e209c79..47f29dc4 100644 --- a/common/index.ts +++ b/common/index.ts @@ -4,3 +4,5 @@ */ export * from './constants'; +export * from './interfaces'; +export { IComponent } from '../public/component_types'; diff --git a/common/interfaces.ts b/common/interfaces.ts new file mode 100644 index 00000000..e0bd5f1a --- /dev/null +++ b/common/interfaces.ts @@ -0,0 +1,11 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +// TODO: this will grow as more fields are defined and what frontend reqts there will be +export interface Workflow { + name: string; + id: string; + description: string; +} diff --git a/public/app.tsx b/public/app.tsx index 786baadd..0130d4e8 100644 --- a/public/app.tsx +++ b/public/app.tsx @@ -7,7 +7,13 @@ import React from 'react'; import { Route, RouteComponentProps, Switch } from 'react-router-dom'; import { EuiPageSideBar, EuiSideNav, EuiPageTemplate } from '@elastic/eui'; import { Navigation, APP_PATH } from './utils'; -import { Overview, UseCases, Workflows, WorkflowBuilder } from './pages'; +import { + Overview, + UseCases, + Workflows, + WorkflowDetail, + WorkflowDetailRouterProps, +} from './pages'; // eslint-disable-next-line @typescript-eslint/no-empty-interface interface Props extends RouteComponentProps {} @@ -34,13 +40,6 @@ export const AiFlowDashboardsApp = (props: Props) => { href: `#${APP_PATH.WORKFLOWS}`, isSelected: props.location.pathname === APP_PATH.WORKFLOWS, }, - { - name: Navigation.WorkflowBuilder, - id: 3, - href: `#${APP_PATH.WORKFLOW_BUILDER}`, - isSelected: - props.location.pathname === APP_PATH.WORKFLOW_BUILDER, - }, ], }, ]} @@ -61,12 +60,14 @@ export const AiFlowDashboardsApp = (props: Props) => { render={(routeProps: RouteComponentProps) => } /> } + path={APP_PATH.WORKFLOW_DETAIL} + render={( + routeProps: RouteComponentProps + ) => } /> } + path={APP_PATH.WORKFLOWS} + render={(routeProps: RouteComponentProps) => } /> {/* Defaulting to Overview page */} { - // TODO: possibly link to the workflow builder with a pre-configured flow + // TODO: possibly link to the workflow details with a pre-configured flow }} > Go diff --git a/public/pages/workflow_builder/components/index.ts b/public/pages/workflow_builder/components/index.ts deleted file mode 100644 index adc5d0e4..00000000 --- a/public/pages/workflow_builder/components/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -export * from './input_fields'; -export { NewOrExistingTabs } from './new_or_existing_tabs'; -export { InputFieldList } from './input_field_list'; diff --git a/public/pages/workflow_builder/index.ts b/public/pages/workflow_builder/index.ts deleted file mode 100644 index 0e54fa8c..00000000 --- a/public/pages/workflow_builder/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -export { WorkflowBuilder } from './workflow_builder'; -export { WorkflowComponent } from './workflow_component'; diff --git a/public/pages/workflow_builder/workflow_builder.tsx b/public/pages/workflow_builder/workflow_builder.tsx deleted file mode 100644 index 4f0abfa1..00000000 --- a/public/pages/workflow_builder/workflow_builder.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React, { useEffect } from 'react'; -// import { useSelector, useDispatch } from 'react-redux'; -import { - EuiPageHeader, - EuiFlexGroup, - EuiFlexItem, - EuiTitle, - EuiSpacer, -} from '@elastic/eui'; -import { BREADCRUMBS } from '../../utils'; -import { getCore } from '../../services'; -// import { AppState, removeDirty, setComponents } from '../../store'; -import { - TextEmbeddingProcessor, - IComponent, - KnnIndex, -} from '../../component_types'; -import { WorkflowComponent } from './workflow_component'; - -export function WorkflowBuilder() { - // TODO: below commented out lines can be used for fetching & setting global redux state - // const dispatch = useDispatch(); - // const { isDirty, components } = useSelector( - // (state: AppState) => state.workspace - // ); - - useEffect(() => { - getCore().chrome.setBreadcrumbs([ - BREADCRUMBS.AI_APPLICATION_BUILDER, - BREADCRUMBS.WORKFLOW_BUILDER, - ]); - }); - - // TODO: Should be fetched from global state. Using some defaults for testing purposes - const curComponents = [ - new TextEmbeddingProcessor(), - new KnnIndex(), - ] as IComponent[]; - - return ( -
- - - - -

Workflow Builder

-
-
-
-
- - - {curComponents.map((component, idx) => { - return ( - - - - ); - })} - -
- ); -} diff --git a/public/pages/workflow_detail/components/header.tsx b/public/pages/workflow_detail/components/header.tsx new file mode 100644 index 00000000..e311041d --- /dev/null +++ b/public/pages/workflow_detail/components/header.tsx @@ -0,0 +1,31 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { EuiPageHeader, EuiButton } from '@elastic/eui'; +import { Workflow } from '../../../../common'; + +interface WorkflowDetailHeaderProps { + workflow: Workflow | undefined; +} + +export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) { + return ( +
+ {}}> + Prototype + , + {}}> + Save + , + ]} + /> +
+ ); +} diff --git a/public/pages/workflow_detail/components/index.ts b/public/pages/workflow_detail/components/index.ts new file mode 100644 index 00000000..b67dfd96 --- /dev/null +++ b/public/pages/workflow_detail/components/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { WorkflowDetailHeader } from './header'; diff --git a/public/pages/workflow_detail/index.ts b/public/pages/workflow_detail/index.ts new file mode 100644 index 00000000..1d03e52f --- /dev/null +++ b/public/pages/workflow_detail/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './workflow_detail'; diff --git a/public/pages/workflow_detail/workflow_detail.tsx b/public/pages/workflow_detail/workflow_detail.tsx new file mode 100644 index 00000000..4d0fddda --- /dev/null +++ b/public/pages/workflow_detail/workflow_detail.tsx @@ -0,0 +1,47 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useEffect } from 'react'; +import { RouteComponentProps } from 'react-router-dom'; +import { useSelector } from 'react-redux'; +import { EuiSpacer } from '@elastic/eui'; +import { BREADCRUMBS } from '../../utils'; +import { getCore } from '../../services'; +import { WorkflowDetailHeader } from './components'; +import { Workspace } from './workspace'; +import { AppState } from '../../store'; + +export interface WorkflowDetailRouterProps { + workflowId: string; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface WorkflowDetailProps + extends RouteComponentProps {} + +export function WorkflowDetail(props: WorkflowDetailProps) { + const { workflows } = useSelector((state: AppState) => state.workflows); + + const workflow = workflows.find( + (wf) => wf.id === props.match?.params?.workflowId + ); + const workflowName = workflow ? workflow.name : ''; + + useEffect(() => { + getCore().chrome.setBreadcrumbs([ + BREADCRUMBS.AI_APPLICATION_BUILDER, + BREADCRUMBS.WORKFLOWS, + { text: workflowName }, + ]); + }); + + return ( +
+ + + +
+ ); +} diff --git a/public/pages/workflow_detail/workspace/index.ts b/public/pages/workflow_detail/workspace/index.ts new file mode 100644 index 00000000..47ade8d8 --- /dev/null +++ b/public/pages/workflow_detail/workspace/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { Workspace } from './workspace'; diff --git a/public/pages/workflow_detail/workspace/workspace.tsx b/public/pages/workflow_detail/workspace/workspace.tsx new file mode 100644 index 00000000..96cb990d --- /dev/null +++ b/public/pages/workflow_detail/workspace/workspace.tsx @@ -0,0 +1,26 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { useSelector } from 'react-redux'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { AppState } from '../../../store'; +import { WorkspaceComponent } from '../workspace_component'; + +export function Workspace() { + const { components } = useSelector((state: AppState) => state.workspace); + + return ( + + {components.map((component, idx) => { + return ( + + + + ); + })} + + ); +} diff --git a/public/pages/workflow_detail/workspace_component/index.ts b/public/pages/workflow_detail/workspace_component/index.ts new file mode 100644 index 00000000..6bd992e7 --- /dev/null +++ b/public/pages/workflow_detail/workspace_component/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { WorkspaceComponent } from './workspace_component'; diff --git a/public/pages/workflow_builder/components/input_field_list.tsx b/public/pages/workflow_detail/workspace_component/input_field_list.tsx similarity index 100% rename from public/pages/workflow_builder/components/input_field_list.tsx rename to public/pages/workflow_detail/workspace_component/input_field_list.tsx diff --git a/public/pages/workflow_builder/components/input_fields/index.ts b/public/pages/workflow_detail/workspace_component/input_fields/index.ts similarity index 100% rename from public/pages/workflow_builder/components/input_fields/index.ts rename to public/pages/workflow_detail/workspace_component/input_fields/index.ts diff --git a/public/pages/workflow_builder/components/input_fields/json_field.tsx b/public/pages/workflow_detail/workspace_component/input_fields/json_field.tsx similarity index 100% rename from public/pages/workflow_builder/components/input_fields/json_field.tsx rename to public/pages/workflow_detail/workspace_component/input_fields/json_field.tsx diff --git a/public/pages/workflow_builder/components/input_fields/select_field.tsx b/public/pages/workflow_detail/workspace_component/input_fields/select_field.tsx similarity index 100% rename from public/pages/workflow_builder/components/input_fields/select_field.tsx rename to public/pages/workflow_detail/workspace_component/input_fields/select_field.tsx diff --git a/public/pages/workflow_builder/components/input_fields/text_field.tsx b/public/pages/workflow_detail/workspace_component/input_fields/text_field.tsx similarity index 100% rename from public/pages/workflow_builder/components/input_fields/text_field.tsx rename to public/pages/workflow_detail/workspace_component/input_fields/text_field.tsx diff --git a/public/pages/workflow_builder/components/new_or_existing_tabs.tsx b/public/pages/workflow_detail/workspace_component/new_or_existing_tabs.tsx similarity index 100% rename from public/pages/workflow_builder/components/new_or_existing_tabs.tsx rename to public/pages/workflow_detail/workspace_component/new_or_existing_tabs.tsx diff --git a/public/pages/workflow_builder/workflow_component.tsx b/public/pages/workflow_detail/workspace_component/workspace_component.tsx similarity index 88% rename from public/pages/workflow_builder/workflow_component.tsx rename to public/pages/workflow_detail/workspace_component/workspace_component.tsx index e2c2979d..4088abdd 100644 --- a/public/pages/workflow_builder/workflow_component.tsx +++ b/public/pages/workflow_detail/workspace_component/workspace_component.tsx @@ -11,10 +11,11 @@ import { EuiSpacer, EuiCard, } from '@elastic/eui'; -import { IComponent } from '../../component_types'; -import { InputFieldList, NewOrExistingTabs } from './components'; +import { IComponent } from '../../../component_types'; +import { InputFieldList } from './input_field_list'; +import { NewOrExistingTabs } from './new_or_existing_tabs'; -interface WorkflowComponentProps { +interface WorkspaceComponentProps { component: IComponent; } @@ -24,7 +25,7 @@ interface WorkflowComponentProps { * Similar to Flowise's CanvasNode - see * https://github.com/FlowiseAI/Flowise/blob/main/packages/ui/src/views/canvas/CanvasNode.js */ -export function WorkflowComponent(props: WorkflowComponentProps) { +export function WorkspaceComponent(props: WorkspaceComponentProps) { const { component } = props; const [selectedTabId, setSelectedTabId] = useState('existing'); diff --git a/public/pages/workflows/components/columns.tsx b/public/pages/workflows/components/columns.tsx new file mode 100644 index 00000000..07300ca0 --- /dev/null +++ b/public/pages/workflows/components/columns.tsx @@ -0,0 +1,29 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { EuiLink } from '@elastic/eui'; +import { PLUGIN_ID, Workflow } from '../../../../common'; + +export const columns = [ + { + field: 'name', + name: 'Name', + sortable: true, + render: (name: string, workflow: Workflow) => ( + {name} + ), + }, + { + field: 'id', + name: 'ID', + sortable: true, + }, + { + field: 'description', + name: 'Description', + sortable: false, + }, +]; diff --git a/public/pages/workflows/components/workflow_list.tsx b/public/pages/workflows/components/workflow_list.tsx index 90d765ad..ff38a6ac 100644 --- a/public/pages/workflows/components/workflow_list.tsx +++ b/public/pages/workflows/components/workflow_list.tsx @@ -3,77 +3,34 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useState } from 'react'; -import { EuiBasicTable } from '@elastic/eui'; +import React from 'react'; +import { useSelector } from 'react-redux'; +import { EuiInMemoryTable, Direction } from '@elastic/eui'; +import { AppState } from '../../../store'; +import { Workflow } from '../../../../common'; +import { columns } from './columns'; // eslint-disable-next-line @typescript-eslint/no-empty-interface interface WorkflowListProps {} -interface WorkflowItem { - name: string; - description: string; -} - export function WorkflowList(props: WorkflowListProps) { - const [pageIndex, setPageIndex] = useState(0); - const [pageSize, setPageSize] = useState(5); - const [sortField, setSortField] = useState('name'); - const [sortDirection, setSortDirection] = useState('asc'); - - const columns = [ - { - field: 'name', - name: 'Name', - sortable: true, - }, - { - field: 'description', - name: 'Description', - sortable: false, - }, - ]; - - const items = [ - { - name: 'Workflow 1', - }, - ] as WorkflowItem[]; + const { workflows } = useSelector((state: AppState) => state.workflows); const sorting = { sort: { - field: sortField, - direction: sortDirection, + field: 'name', + direction: 'asc' as Direction, }, - enableAllColumns: false, - readOnly: false, - }; - - const pagination = { - pageIndex, - pageSize, - totalItemCount: items.length, - pageSizeOptions: [5, 10, 20], - }; - - const onTableChange = ({ page = {}, sort = {} }) => { - const { index, size } = page; - const { field, direction } = sort; - - setPageIndex(index); - setPageSize(size); - setSortField(field); - setSortDirection(direction); }; return ( - - items={items} + + items={workflows} rowHeader="name" columns={columns} sorting={sorting} - pagination={pagination} - onChange={onTableChange} - noItemsMessage={'No existing workflows found'} + pagination={true} + message={'No existing workflows found'} /> ); } diff --git a/public/store/reducers/index.ts b/public/store/reducers/index.ts index 2f5cadc0..e43ac3a4 100644 --- a/public/store/reducers/index.ts +++ b/public/store/reducers/index.ts @@ -4,3 +4,4 @@ */ export * from './workspace_reducer'; +export * from './workflows_reducer'; diff --git a/public/store/reducers/workflows_reducer.ts b/public/store/reducers/workflows_reducer.ts new file mode 100644 index 00000000..18aa322f --- /dev/null +++ b/public/store/reducers/workflows_reducer.ts @@ -0,0 +1,40 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { createSlice } from '@reduxjs/toolkit'; +import { Workflow } from '../../../common'; + +const initialState = { + // TODO: fetch from server-side later + workflows: [ + { + name: 'Workflow-1', + id: 'workflow-1-id', + description: 'description for workflow 1', + }, + { + name: 'Workflow-2', + id: 'workflow-2-id', + description: 'description for workflow 2', + }, + ] as Workflow[], + loading: false, +}; + +const workflowsSlice = createSlice({ + name: 'workflows', + initialState, + reducers: { + setWorkflows(state, action) { + state.workflows = action.payload; + }, + setLoading(state, action) { + state.loading = action.payload; + }, + }, +}); + +export const workflowsReducer = workflowsSlice.reducer; +export const { setWorkflows, setLoading } = workflowsSlice.actions; diff --git a/public/store/reducers/workspace_reducer.ts b/public/store/reducers/workspace_reducer.ts index 70bc2095..4ef99dc7 100644 --- a/public/store/reducers/workspace_reducer.ts +++ b/public/store/reducers/workspace_reducer.ts @@ -4,10 +4,14 @@ */ import { createSlice } from '@reduxjs/toolkit'; +import { IComponent } from '../../../common'; +import { KnnIndex, TextEmbeddingProcessor } from '../../component_types'; const initialState = { isDirty: false, - components: [], + // TODO: fetch from server-size if it is a created workflow, else have some default + // mapping somewhere (e.g., 'semantic search': text_embedding_processor, knn_index, etc.) + components: [new TextEmbeddingProcessor(), new KnnIndex()] as IComponent[], }; const workspaceSlice = createSlice({ diff --git a/public/store/store.ts b/public/store/store.ts index 9359d352..80949331 100644 --- a/public/store/store.ts +++ b/public/store/store.ts @@ -5,10 +5,11 @@ import { configureStore } from '@reduxjs/toolkit'; import { combineReducers } from 'redux'; -import { workspaceReducer } from './reducers'; +import { workflowsReducer, workspaceReducer } from './reducers'; const rootReducer = combineReducers({ workspace: workspaceReducer, + workflows: workflowsReducer, }); export const store = configureStore({ reducer: rootReducer, diff --git a/public/utils/constants.ts b/public/utils/constants.ts index b20b36d9..10b5eb82 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -7,7 +7,6 @@ export enum Navigation { AiApplicationBuilder = 'AI Application Builder', UseCases = 'Use Cases', Workflows = 'Workflows', - WorkflowBuilder = 'Workflow Builder', } export enum APP_PATH { @@ -15,18 +14,13 @@ export enum APP_PATH { USE_CASES = '/use-cases', WORKSPACE = '/workspace', WORKFLOWS = '/workflows', - WORKFLOW_DETAIL = '/workflows/:workflowId/', - WORKFLOW_BUILDER = '/workflow-builder', + WORKFLOW_DETAIL = '/workflows/:workflowId', } export const BREADCRUMBS = Object.freeze({ AI_APPLICATION_BUILDER: { text: 'AI application builder', href: '#/' }, USE_CASES: { text: 'Use cases', href: `#${APP_PATH.USE_CASES}` }, WORKFLOWS: { text: 'Workflows', href: `#${APP_PATH.WORKFLOWS}` }, - WORKFLOW_BUILDER: { - text: 'Workflow builder', - href: `#${APP_PATH.WORKFLOW_BUILDER}`, - }, }); export enum COMPONENT_CATEGORY {