From 34d425fb000b1422e0c305e91abf1e583e255f5e Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen Date: Thu, 5 Oct 2023 16:44:25 -0700 Subject: [PATCH] Refactor UseCases into nested tab; standardize page layouts (#54) Signed-off-by: Tyler Ohlsen --- public/app.tsx | 16 ++- public/pages/index.ts | 1 - public/pages/overview/overview.tsx | 25 +++- public/pages/use_cases/index.ts | 6 - public/pages/use_cases/use_cases.tsx | 61 ---------- .../workflow_detail/components/header.tsx | 26 ++-- .../pages/workflow_detail/workflow_detail.tsx | 13 +- .../workflow_detail/workspace/workspace.tsx | 78 ++++++------ public/pages/workflows/index.ts | 2 +- .../new_workflow}/index.ts | 2 +- .../workflows/new_workflow/new_workflow.tsx | 36 ++++++ .../new_workflow}/use_case.tsx | 2 +- .../{components => workflow_list}/columns.tsx | 0 .../{components => workflow_list}/index.ts | 0 .../workflow_list.tsx | 0 public/pages/workflows/workflows.tsx | 114 ++++++++++++++---- public/utils/constants.ts | 7 +- 17 files changed, 218 insertions(+), 171 deletions(-) delete mode 100644 public/pages/use_cases/index.ts delete mode 100644 public/pages/use_cases/use_cases.tsx rename public/pages/{use_cases/components => workflows/new_workflow}/index.ts (64%) create mode 100644 public/pages/workflows/new_workflow/new_workflow.tsx rename public/pages/{use_cases/components => workflows/new_workflow}/use_case.tsx (96%) rename public/pages/workflows/{components => workflow_list}/columns.tsx (100%) rename public/pages/workflows/{components => workflow_list}/index.ts (100%) rename public/pages/workflows/{components => workflow_list}/workflow_list.tsx (100%) diff --git a/public/app.tsx b/public/app.tsx index 1eb7d547..393a0145 100644 --- a/public/app.tsx +++ b/public/app.tsx @@ -9,10 +9,10 @@ import { EuiPageSideBar, EuiSideNav, EuiPageTemplate } from '@elastic/eui'; import { Navigation, APP_PATH } from './utils'; import { Overview, - UseCases, Workflows, WorkflowDetail, WorkflowDetailRouterProps, + WorkflowsRouterProps, } from './pages'; interface Props extends RouteComponentProps {} @@ -28,10 +28,10 @@ export const AiFlowDashboardsApp = (props: Props) => { id: 0, items: [ { - name: Navigation.UseCases, + name: Navigation.Overview, id: 1, - href: `#${APP_PATH.USE_CASES}`, - isSelected: props.location.pathname === APP_PATH.USE_CASES, + href: `#${APP_PATH.OVERVIEW}`, + isSelected: props.location.pathname === APP_PATH.OVERVIEW, }, { name: Navigation.Workflows, @@ -54,10 +54,6 @@ export const AiFlowDashboardsApp = (props: Props) => { pageSideBar={sidebar} > - } - /> { /> } + render={(routeProps: RouteComponentProps) => ( + + )} /> {/* Defaulting to Overview page */} { - getCore().chrome.setBreadcrumbs([BREADCRUMBS.AI_APPLICATION_BUILDER]); + getCore().chrome.setBreadcrumbs([ + BREADCRUMBS.AI_APPLICATION_BUILDER, + BREADCRUMBS.OVERVIEW, + ]); }); return ( - - Welcome to the AI Application Builder! - + + + + + + TODO: Put overview details here... + + + ); } diff --git a/public/pages/use_cases/index.ts b/public/pages/use_cases/index.ts deleted file mode 100644 index d569570e..00000000 --- a/public/pages/use_cases/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -export { UseCases } from './use_cases'; diff --git a/public/pages/use_cases/use_cases.tsx b/public/pages/use_cases/use_cases.tsx deleted file mode 100644 index 479e0024..00000000 --- a/public/pages/use_cases/use_cases.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React, { useEffect } from 'react'; -import { - EuiPageHeader, - EuiFlexGroup, - EuiFlexItem, - EuiTitle, - EuiFlexGrid, - EuiSpacer, -} from '@elastic/eui'; -import { BREADCRUMBS } from '../../utils'; -import { UseCase } from './components'; -import { getCore } from '../../services'; - -export function UseCases() { - useEffect(() => { - getCore().chrome.setBreadcrumbs([ - BREADCRUMBS.AI_APPLICATION_BUILDER, - BREADCRUMBS.USE_CASES, - ]); - }); - - return ( -
- - - - -

Use Cases

-
-
-
-
- - - - - - - - - - - - -
- ); -} diff --git a/public/pages/workflow_detail/components/header.tsx b/public/pages/workflow_detail/components/header.tsx index 4c70ef20..fdcccf66 100644 --- a/public/pages/workflow_detail/components/header.tsx +++ b/public/pages/workflow_detail/components/header.tsx @@ -13,19 +13,17 @@ interface WorkflowDetailHeaderProps { export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) { return ( -
- {}}> - Prototype - , - {}}> - Save - , - ]} - /> -
+ {}}> + Prototype + , + {}}> + Save + , + ]} + /> ); } diff --git a/public/pages/workflow_detail/workflow_detail.tsx b/public/pages/workflow_detail/workflow_detail.tsx index e110acaf..faa3af56 100644 --- a/public/pages/workflow_detail/workflow_detail.tsx +++ b/public/pages/workflow_detail/workflow_detail.tsx @@ -6,7 +6,7 @@ import React, { useEffect } from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { useSelector } from 'react-redux'; -import { EuiSpacer } from '@elastic/eui'; +import { EuiPage, EuiPageBody } from '@elastic/eui'; import { BREADCRUMBS } from '../../utils'; import { getCore } from '../../services'; import { WorkflowDetailHeader } from './components'; @@ -37,10 +37,11 @@ export function WorkflowDetail(props: WorkflowDetailProps) { }); return ( -
- - - -
+ + + + + + ); } diff --git a/public/pages/workflow_detail/workspace/workspace.tsx b/public/pages/workflow_detail/workspace/workspace.tsx index 561cb2f6..5ca1ed73 100644 --- a/public/pages/workflow_detail/workspace/workspace.tsx +++ b/public/pages/workflow_detail/workspace/workspace.tsx @@ -111,47 +111,45 @@ export function Workspace(props: WorkspaceProps) { }, [props.workflow]); return ( - - + - - {/** - * We have these wrapper divs & reactFlowWrapper ref to control and calculate the - * ReactFlow bounds when calculating node positioning. - */} -
-
- - - - -
+ {/** + * We have these wrapper divs & reactFlowWrapper ref to control and calculate the + * ReactFlow bounds when calculating node positioning. + */} +
+
+ + + +
- - - +
+ + ); } diff --git a/public/pages/workflows/index.ts b/public/pages/workflows/index.ts index 047a1868..32172f02 100644 --- a/public/pages/workflows/index.ts +++ b/public/pages/workflows/index.ts @@ -3,4 +3,4 @@ * SPDX-License-Identifier: Apache-2.0 */ -export { Workflows } from './workflows'; +export * from './workflows'; diff --git a/public/pages/use_cases/components/index.ts b/public/pages/workflows/new_workflow/index.ts similarity index 64% rename from public/pages/use_cases/components/index.ts rename to public/pages/workflows/new_workflow/index.ts index 2d64e1ab..a1918c7a 100644 --- a/public/pages/use_cases/components/index.ts +++ b/public/pages/workflows/new_workflow/index.ts @@ -3,4 +3,4 @@ * SPDX-License-Identifier: Apache-2.0 */ -export { UseCase } from './use_case'; +export { NewWorkflow } from './new_workflow'; diff --git a/public/pages/workflows/new_workflow/new_workflow.tsx b/public/pages/workflows/new_workflow/new_workflow.tsx new file mode 100644 index 00000000..580c7a6d --- /dev/null +++ b/public/pages/workflows/new_workflow/new_workflow.tsx @@ -0,0 +1,36 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { EuiFlexItem, EuiFlexGrid } from '@elastic/eui'; + +import { UseCase } from './use_case'; + +interface NewWorkflowProps {} + +export function NewWorkflow(props: NewWorkflowProps) { + return ( + + + + + + + + + + + + ); +} diff --git a/public/pages/use_cases/components/use_case.tsx b/public/pages/workflows/new_workflow/use_case.tsx similarity index 96% rename from public/pages/use_cases/components/use_case.tsx rename to public/pages/workflows/new_workflow/use_case.tsx index e4e4fe5f..c7094c9a 100644 --- a/public/pages/use_cases/components/use_case.tsx +++ b/public/pages/workflows/new_workflow/use_case.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useEffect } from 'react'; +import React from 'react'; import { EuiText, EuiFlexGroup, diff --git a/public/pages/workflows/components/columns.tsx b/public/pages/workflows/workflow_list/columns.tsx similarity index 100% rename from public/pages/workflows/components/columns.tsx rename to public/pages/workflows/workflow_list/columns.tsx diff --git a/public/pages/workflows/components/index.ts b/public/pages/workflows/workflow_list/index.ts similarity index 100% rename from public/pages/workflows/components/index.ts rename to public/pages/workflows/workflow_list/index.ts diff --git a/public/pages/workflows/components/workflow_list.tsx b/public/pages/workflows/workflow_list/workflow_list.tsx similarity index 100% rename from public/pages/workflows/components/workflow_list.tsx rename to public/pages/workflows/workflow_list/workflow_list.tsx diff --git a/public/pages/workflows/workflows.tsx b/public/pages/workflows/workflows.tsx index 2cb38b8f..c79a8736 100644 --- a/public/pages/workflows/workflows.tsx +++ b/public/pages/workflows/workflows.tsx @@ -3,19 +3,66 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; +import { RouteComponentProps, useLocation } from 'react-router-dom'; import { EuiPageHeader, EuiTitle, - EuiFlexGroup, - EuiFlexItem, + EuiPage, + EuiPageBody, + EuiPageContent, EuiSpacer, } from '@elastic/eui'; +import queryString from 'query-string'; +import { useSelector } from 'react-redux'; import { BREADCRUMBS } from '../../utils'; import { getCore } from '../../services'; -import { WorkflowList } from './components'; +import { WorkflowList } from './workflow_list'; +import { NewWorkflow } from './new_workflow'; +import { AppState } from '../../store'; + +export interface WorkflowsRouterProps {} + +interface WorkflowsProps extends RouteComponentProps {} + +enum WORKFLOWS_TAB { + MANAGE = 'manage', + CREATE = 'create', +} + +const ACTIVE_TAB_PARAM = 'active_tab'; + +function replaceActiveTab(activeTab: string, props: WorkflowsProps) { + props.history.replace({ + ...history, + search: queryString.stringify({ + [ACTIVE_TAB_PARAM]: activeTab, + }), + }); +} + +export function Workflows(props: WorkflowsProps) { + const { workflows } = useSelector((state: AppState) => state.workflows); + + const tabFromUrl = queryString.parse(useLocation().search)[ + ACTIVE_TAB_PARAM + ] as WORKFLOWS_TAB; + const [selectedTabId, setSelectedTabId] = useState(tabFromUrl); + + // If there is no selected tab, default to a tab depending on if user + // has existing created workflows or not. + useEffect(() => { + if (!selectedTabId) { + if (workflows?.length > 0) { + setSelectedTabId(WORKFLOWS_TAB.MANAGE); + replaceActiveTab(WORKFLOWS_TAB.MANAGE, props); + } else { + setSelectedTabId(WORKFLOWS_TAB.CREATE); + replaceActiveTab(WORKFLOWS_TAB.CREATE, props); + } + } + }, [selectedTabId, workflows]); -export function Workflows() { useEffect(() => { getCore().chrome.setBreadcrumbs([ BREADCRUMBS.AI_APPLICATION_BUILDER, @@ -24,22 +71,45 @@ export function Workflows() { }); return ( -
- - - - -

Workflows

-
-
-
-
- - - - - - -
+ + + { + setSelectedTabId(WORKFLOWS_TAB.MANAGE); + replaceActiveTab(WORKFLOWS_TAB.MANAGE, props); + }, + }, + { + id: WORKFLOWS_TAB.CREATE, + label: 'New workflow', + isSelected: selectedTabId === WORKFLOWS_TAB.CREATE, + onClick: () => { + setSelectedTabId(WORKFLOWS_TAB.CREATE); + replaceActiveTab(WORKFLOWS_TAB.CREATE, props); + }, + }, + ]} + /> + + + +

+ {selectedTabId === WORKFLOWS_TAB.MANAGE && 'Workflows'} + {selectedTabId === WORKFLOWS_TAB.CREATE && + 'Create a new workflow'} +

+
+ + {selectedTabId === WORKFLOWS_TAB.MANAGE && } + {selectedTabId === WORKFLOWS_TAB.CREATE && } +
+
+
); } diff --git a/public/utils/constants.ts b/public/utils/constants.ts index 35ccc224..63592fbc 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -5,21 +5,20 @@ export enum Navigation { AiApplicationBuilder = 'AI Application Builder', - UseCases = 'Use Cases', + Overview = 'Overview', Workflows = 'Workflows', } export enum APP_PATH { HOME = '/', - USE_CASES = '/use-cases', - WORKSPACE = '/workspace', + OVERVIEW = '/overview', WORKFLOWS = '/workflows', 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}` }, + OVERVIEW: { text: 'Overview', href: `#${APP_PATH.OVERVIEW}` }, WORKFLOWS: { text: 'Workflows', href: `#${APP_PATH.WORKFLOWS}` }, });