From db360ff302a9c524475db6abb221c37b20419b35 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen Date: Thu, 21 Sep 2023 18:54:51 -0700 Subject: [PATCH] Connect to redux store Signed-off-by: Tyler Ohlsen --- common/index.ts | 2 + common/interfaces.ts | 11 +++ .../pages/use_cases/components/use_case.tsx | 2 +- .../workflow_builder/workflow_builder.tsx | 68 --------------- .../workflow_detail/components/header.tsx | 7 +- .../pages/workflow_detail/workflow_detail.tsx | 30 +++---- .../workflow_detail/workspace/workspace.tsx | 11 ++- public/pages/workflows/components/columns.tsx | 29 +++++++ .../workflows/components/workflow_list.tsx | 85 +++---------------- 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 +- 13 files changed, 123 insertions(+), 172 deletions(-) create mode 100644 common/interfaces.ts delete mode 100644 public/pages/workflow_builder/workflow_builder.tsx 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/pages/use_cases/components/use_case.tsx b/public/pages/use_cases/components/use_case.tsx index d1ddb889..e4e4fe5f 100644 --- a/public/pages/use_cases/components/use_case.tsx +++ b/public/pages/use_cases/components/use_case.tsx @@ -40,7 +40,7 @@ export function UseCase(props: UseCaseProps) { disabled={false} isLoading={false} onClick={() => { - // 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/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 index 0f16c923..e311041d 100644 --- a/public/pages/workflow_detail/components/header.tsx +++ b/public/pages/workflow_detail/components/header.tsx @@ -5,17 +5,18 @@ import React from 'react'; import { EuiPageHeader, EuiButton } from '@elastic/eui'; +import { Workflow } from '../../../../common'; interface WorkflowDetailHeaderProps { - componentName: string; + workflow: Workflow | undefined; } export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) { return (
{}}> Prototype diff --git a/public/pages/workflow_detail/workflow_detail.tsx b/public/pages/workflow_detail/workflow_detail.tsx index c04aefa3..4d0fddda 100644 --- a/public/pages/workflow_detail/workflow_detail.tsx +++ b/public/pages/workflow_detail/workflow_detail.tsx @@ -5,18 +5,13 @@ import React, { useEffect } from 'react'; import { RouteComponentProps } from 'react-router-dom'; -// import { useSelector, useDispatch } from 'react-redux'; +import { useSelector } from 'react-redux'; import { EuiSpacer } from '@elastic/eui'; import { BREADCRUMBS } from '../../utils'; import { getCore } from '../../services'; import { WorkflowDetailHeader } from './components'; -import { - TextEmbeddingProcessor, - IComponent, - KnnIndex, -} from '../../component_types'; import { Workspace } from './workspace'; -// import { AppState, removeDirty, setComponents } from '../../store'; +import { AppState } from '../../store'; export interface WorkflowDetailRouterProps { workflowId: string; @@ -27,31 +22,26 @@ interface WorkflowDetailProps extends RouteComponentProps {} export function WorkflowDetail(props: WorkflowDetailProps) { - // 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 - // ); + const { workflows } = useSelector((state: AppState) => state.workflows); - // TODO: Should be fetched from global state. Using some defaults for testing purposes - const curComponents = [ - new TextEmbeddingProcessor(), - new KnnIndex(), - ] as IComponent[]; + 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: 'workflow-name-placeholder' }, + { text: workflowName }, ]); }); return (
- + - +
); } diff --git a/public/pages/workflow_detail/workspace/workspace.tsx b/public/pages/workflow_detail/workspace/workspace.tsx index 67934f49..96cb990d 100644 --- a/public/pages/workflow_detail/workspace/workspace.tsx +++ b/public/pages/workflow_detail/workspace/workspace.tsx @@ -4,18 +4,17 @@ */ import React from 'react'; +import { useSelector } from 'react-redux'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { AppState } from '../../../store'; import { WorkspaceComponent } from '../workspace_component'; -import { IComponent } from '../../../component_types'; -interface WorkspaceProps { - components: IComponent[]; -} +export function Workspace() { + const { components } = useSelector((state: AppState) => state.workspace); -export function Workspace(props: WorkspaceProps) { return ( - {props.components.map((component, idx) => { + {components.map((component, idx) => { return ( 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 0016e0f7..ff38a6ac 100644 --- a/public/pages/workflows/components/workflow_list.tsx +++ b/public/pages/workflows/components/workflow_list.tsx @@ -3,93 +3,34 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useState } from 'react'; -import { EuiBasicTable, EuiLink } from '@elastic/eui'; -import { PLUGIN_ID } from '../../../../common'; +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; - id: 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'); - - // TODO: move column details to separate file - const columns = [ - { - field: 'name', - name: 'Name', - sortable: true, - render: (name: string, workflow: WorkflowItem) => ( - - {name} - - ), - }, - { - field: 'id', - name: 'ID', - sortable: false, - }, - { - field: 'description', - name: 'Description', - sortable: false, - }, - ]; - - // TODO: remove items and fetch from store - const items = [ - { - name: 'Workflow-1', - id: 'workflow-1-id', - description: 'some test description', - }, - ] 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,