Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multi-filtering to tables; refactor details pages into tabs #63

Merged
merged 4 commits into from
Feb 26, 2024
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions common/interfaces.ts
Original file line number Diff line number Diff line change
@@ -76,9 +76,29 @@ export type Workflow = {
workspaceFlowState?: WorkspaceFlowState;
template: UseCaseTemplate;
lastUpdated: number;
state: WORKFLOW_STATE;
};

export enum USE_CASE {
SEMANTIC_SEARCH = 'semantic_search',
CUSTOM = 'custom',
}

/**
********** MISC TYPES/INTERFACES ************
*/

// TODO: finalize how we have the launch data model
export type WorkflowLaunch = {
id: string;
state: WORKFLOW_STATE;
lastUpdated: number;
};

// TODO: finalize list of possible workflow states from backend
export enum WORKFLOW_STATE {
SUCCEEDED = 'Succeeded',
FAILED = 'Failed',
IN_PROGRESS = 'In progress',
NOT_STARTED = 'Not started',
}
5 changes: 5 additions & 0 deletions public/general_components/general-component-styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.multi-select-filter {
&--width {
width: 150px;
}
}
6 changes: 6 additions & 0 deletions public/general_components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { MultiSelectFilter } from './multi_select_filter';
98 changes: 98 additions & 0 deletions public/general_components/multi_select_filter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useState } from 'react';
import {
EuiFilterSelectItem,
EuiFilterGroup,
EuiPopover,
EuiFilterButton,
EuiFlexItem,
} from '@elastic/eui';

// styling
import './general-component-styles.scss';

interface MultiSelectFilterProps {
title: string;
filters: EuiFilterSelectItem[];
setSelectedFilters: (filters: EuiFilterSelectItem[]) => void;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have this setter as a prop since typically any filter changes will need to be propagated to the parent component(s).

}

/**
* A general multi-select filter.
*/
export function MultiSelectFilter(props: MultiSelectFilterProps) {
const [filters, setFilters] = useState(props.filters);
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);

function onButtonClick() {
setIsPopoverOpen(!isPopoverOpen);
}
function onPopoverClose() {
setIsPopoverOpen(false);
}

function updateFilter(index: number) {
if (!filters[index]) {
return;
}
const newFilters = [...filters];
// @ts-ignore
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TLDR of adding all of these ignores is due to IDE complaints that the type fields don't match. This is due to differences in EUI vs. OUI and their respective documentation. This follows the latest OUI documentation.

newFilters[index].checked =
// @ts-ignore
newFilters[index].checked === 'on' ? undefined : 'on';

setFilters(newFilters);
props.setSelectedFilters(
// @ts-ignore
newFilters.filter((filter) => filter.checked === 'on')
);
}

return (
<EuiFlexItem grow={false} className="multi-select-filter--width">
<EuiFilterGroup>
<EuiPopover
button={
<EuiFilterButton
iconType="arrowDown"
onClick={onButtonClick}
isSelected={isPopoverOpen}
numFilters={filters.length}
hasActiveFilters={
// @ts-ignore
!!filters.find((filter) => filter.checked === 'on')
}
numActiveFilters={
// @ts-ignore
filters.filter((filter) => filter.checked === 'on').length
}
>
{props.title}
</EuiFilterButton>
}
isOpen={isPopoverOpen}
closePopover={onPopoverClose}
panelPaddingSize="none"
>
<div className="euiFilterSelect__items multi-select-filter--width">
{filters.map((filter, index) => (
<EuiFilterSelectItem
// @ts-ignore
checked={filter.checked}
key={index}
onClick={() => updateFilter(index)}
>
{/* @ts-ignore */}
{filter.name}
</EuiFilterSelectItem>
))}
</div>
</EuiPopover>
</EuiFilterGroup>
</EuiFlexItem>
);
}
4 changes: 3 additions & 1 deletion public/pages/workflow_detail/components/header.tsx
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ import { saveWorkflow } from '../utils';
import { rfContext, AppState, removeDirty } from '../../../store';

interface WorkflowDetailHeaderProps {
tabs: any[];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tab isn't exposed by EUI/OUI library so we set as any.

workflow?: Workflow;
}

@@ -22,7 +23,6 @@ export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) {
return (
<EuiPageHeader
pageTitle={props.workflow ? props.workflow.name : ''}
description={props.workflow ? props.workflow.description : ''}
rightSideItems={[
<EuiButton fill={false} onClick={() => {}}>
Prototype
@@ -39,6 +39,8 @@ export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) {
Save
</EuiButton>,
]}
tabs={props.tabs}
bottomBorder={true}
/>
);
}
22 changes: 22 additions & 0 deletions public/pages/workflow_detail/launches/columns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export const columns = [
{
field: 'id',
name: 'Launch ID',
sortable: true,
},
{
field: 'state',
name: 'Status',
sortable: true,
},
{
field: 'lastUpdatedTime',
name: 'Last updated time',
sortable: true,
},
];
6 changes: 6 additions & 0 deletions public/pages/workflow_detail/launches/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { Launches } from './launches';
13 changes: 13 additions & 0 deletions public/pages/workflow_detail/launches/launch_details.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { EuiText } from '@elastic/eui';

interface LaunchDetailsProps {}

export function LaunchDetails(props: LaunchDetailsProps) {
return <EuiText>TODO: add selected launch details here</EuiText>;
}
119 changes: 119 additions & 0 deletions public/pages/workflow_detail/launches/launch_list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useState, useEffect } from 'react';
import { debounce } from 'lodash';
import {
EuiInMemoryTable,
Direction,
EuiFlexGroup,
EuiFlexItem,
EuiFieldSearch,
EuiFilterSelectItem,
} from '@elastic/eui';
import { WORKFLOW_STATE, WorkflowLaunch } from '../../../../common';
import { columns } from './columns';
import { MultiSelectFilter } from '../../../general_components';
import { getStateOptions } from '../../../utils';

interface LaunchListProps {}

/**
* The searchable list of launches for this particular workflow.
*/
export function LaunchList(props: LaunchListProps) {
// TODO: finalize how we persist launches for a particular workflow.
// We may just add UI metadata tags to group workflows under a single, overall "workflow"
// const { workflows } = useSelector((state: AppState) => state.workflows);
const workflowLaunches = [
{
id: 'Launch_1',
state: WORKFLOW_STATE.IN_PROGRESS,
lastUpdated: 12345678,
},
{
id: 'Launch_2',
state: WORKFLOW_STATE.FAILED,
lastUpdated: 12345677,
},
] as WorkflowLaunch[];

// search bar state
const [searchQuery, setSearchQuery] = useState<string>('');
const debounceSearchQuery = debounce((query: string) => {
setSearchQuery(query);
}, 100);

// filters state
const [selectedStates, setSelectedStates] = useState<EuiFilterSelectItem[]>(
getStateOptions()
);
const [filteredLaunches, setFilteredLaunches] = useState<WorkflowLaunch[]>(
workflowLaunches
);

// When a filter selection or search query changes, update the filtered launches
useEffect(() => {
setFilteredLaunches(
fetchFilteredLaunches(workflowLaunches, selectedStates, searchQuery)
);
}, [selectedStates, searchQuery]);

const sorting = {
sort: {
field: 'id',
direction: 'asc' as Direction,
},
};

return (
<EuiFlexGroup direction="column">
<EuiFlexItem>
<EuiFlexGroup direction="row" gutterSize="m">
<EuiFlexItem grow={true}>
<EuiFieldSearch
fullWidth={true}
placeholder="Search launches..."
onChange={(e) => debounceSearchQuery(e.target.value)}
/>
</EuiFlexItem>
<MultiSelectFilter
filters={getStateOptions()}
title="Status"
setSelectedFilters={setSelectedStates}
/>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiInMemoryTable<WorkflowLaunch>
items={filteredLaunches}
rowHeader="id"
columns={columns}
sorting={sorting}
pagination={true}
message={'No existing launches found'}
/>
</EuiFlexItem>
</EuiFlexGroup>
);
}

// Collect the final launch list after applying all filters
function fetchFilteredLaunches(
allLaunches: WorkflowLaunch[],
stateFilters: EuiFilterSelectItem[],
searchQuery: string
): WorkflowLaunch[] {
// @ts-ignore
const stateFilterStrings = stateFilters.map((filter) => filter.name);
const filteredLaunches = allLaunches.filter((launch) =>
stateFilterStrings.includes(launch.state)
);
return searchQuery.length === 0
? filteredLaunches
: filteredLaunches.filter((launch) =>
launch.id.toLowerCase().includes(searchQuery.toLowerCase())
);
}
42 changes: 42 additions & 0 deletions public/pages/workflow_detail/launches/launches.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiPageContent,
EuiSpacer,
EuiTitle,
} from '@elastic/eui';
import { Workflow } from '../../../../common';
import { LaunchList } from './launch_list';
import { LaunchDetails } from './launch_details';

interface LaunchesProps {
workflow?: Workflow;
}

/**
* The launches page to browse launch history and view individual launch details.
*/
export function Launches(props: LaunchesProps) {
return (
<EuiPageContent>
<EuiTitle>
<h2>Launches</h2>
</EuiTitle>
<EuiSpacer size="m" />
<EuiFlexGroup direction="row">
<EuiFlexItem>
<LaunchList />
</EuiFlexItem>
<EuiFlexItem>
<LaunchDetails />
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
);
}
6 changes: 6 additions & 0 deletions public/pages/workflow_detail/prototype/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { Prototype } from './prototype';
37 changes: 37 additions & 0 deletions public/pages/workflow_detail/prototype/prototype.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import {
EuiFlexItem,
EuiPageContent,
EuiSpacer,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { Workflow } from '../../../../common';

interface PrototypeProps {
workflow?: Workflow;
}

/**
* The prototype page. Dedicated for testing out a launched workflow.
* Will have default simple interfaces for common application types, such as
* conversational chatbots.
*/
export function Prototype(props: PrototypeProps) {
return (
<EuiPageContent>
<EuiTitle>
<h2>Prototype</h2>
</EuiTitle>
<EuiSpacer size="m" />
<EuiFlexItem>
<EuiText>TODO: add prototype page</EuiText>
</EuiFlexItem>
</EuiPageContent>
);
}
84 changes: 79 additions & 5 deletions public/pages/workflow_detail/workflow_detail.tsx
Original file line number Diff line number Diff line change
@@ -3,24 +3,47 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useEffect } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import React, { useEffect, useState } from 'react';
import { RouteComponentProps, useLocation } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { ReactFlowProvider } from 'reactflow';
import queryString from 'query-string';
import { EuiPage, EuiPageBody } from '@elastic/eui';
import { BREADCRUMBS } from '../../utils';
import { getCore } from '../../services';
import { WorkflowDetailHeader } from './components';
import { AppState } from '../../store';
import { ResizableWorkspace } from './workspace';
import { Launches } from './launches';
import { Prototype } from './prototype';

export interface WorkflowDetailRouterProps {
workflowId: string;
}

interface WorkflowDetailProps
export interface WorkflowDetailProps
extends RouteComponentProps<WorkflowDetailRouterProps> {}

export enum WORKFLOW_DETAILS_TAB {
EDITOR = 'editor',
LAUNCHES = 'launches',
PROTOTYPE = 'prototype',
}

export const ACTIVE_TAB_PARAM = 'tab';

export function replaceActiveTab(
activeTab: string,
props: WorkflowDetailProps
) {
props.history.replace({
...history,
search: queryString.stringify({
[ACTIVE_TAB_PARAM]: activeTab,
}),
});
}

/**
* The workflow details page. This is where users will configure, create, and
* test their created workflows. Additionally, can be used to load existing workflows
@@ -34,6 +57,21 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
);
const workflowName = workflow ? workflow.name : '';

const tabFromUrl = queryString.parse(useLocation().search)[
ACTIVE_TAB_PARAM
] as WORKFLOW_DETAILS_TAB;
const [selectedTabId, setSelectedTabId] = useState<WORKFLOW_DETAILS_TAB>(
tabFromUrl
);

// Default to editor tab if there is none specified via url.
useEffect(() => {
if (!selectedTabId) {
setSelectedTabId(WORKFLOW_DETAILS_TAB.EDITOR);
replaceActiveTab(WORKFLOW_DETAILS_TAB.EDITOR, props);
}
}, []);

useEffect(() => {
getCore().chrome.setBreadcrumbs([
BREADCRUMBS.AI_APPLICATION_BUILDER,
@@ -42,12 +80,48 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
]);
});

const tabs = [
{
id: WORKFLOW_DETAILS_TAB.EDITOR,
label: 'Editor',
isSelected: selectedTabId === WORKFLOW_DETAILS_TAB.EDITOR,
onClick: () => {
setSelectedTabId(WORKFLOW_DETAILS_TAB.EDITOR);
replaceActiveTab(WORKFLOW_DETAILS_TAB.EDITOR, props);
},
},
{
id: WORKFLOW_DETAILS_TAB.LAUNCHES,
label: 'Launches',
isSelected: selectedTabId === WORKFLOW_DETAILS_TAB.LAUNCHES,
onClick: () => {
setSelectedTabId(WORKFLOW_DETAILS_TAB.LAUNCHES);
replaceActiveTab(WORKFLOW_DETAILS_TAB.LAUNCHES, props);
},
},
{
id: WORKFLOW_DETAILS_TAB.PROTOTYPE,
label: 'Prototype',
isSelected: selectedTabId === WORKFLOW_DETAILS_TAB.PROTOTYPE,
onClick: () => {
setSelectedTabId(WORKFLOW_DETAILS_TAB.PROTOTYPE);
replaceActiveTab(WORKFLOW_DETAILS_TAB.PROTOTYPE, props);
},
},
];

return (
<ReactFlowProvider>
<EuiPage>
<EuiPageBody>
<WorkflowDetailHeader workflow={workflow} />
<ResizableWorkspace workflow={workflow} />
<WorkflowDetailHeader workflow={workflow} tabs={tabs} />
{selectedTabId === WORKFLOW_DETAILS_TAB.EDITOR && (
<ResizableWorkspace workflow={workflow} />
)}
{selectedTabId === WORKFLOW_DETAILS_TAB.LAUNCHES && <Launches />}
{selectedTabId === WORKFLOW_DETAILS_TAB.PROTOTYPE && (
<Prototype workflow={workflow} />
)}
</EuiPageBody>
</EuiPage>
</ReactFlowProvider>
10 changes: 8 additions & 2 deletions public/pages/workflow_detail/workspace/resizable_workspace.tsx
Original file line number Diff line number Diff line change
@@ -141,7 +141,7 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
<Form>
<EuiResizableContainer
direction="horizontal"
style={{ marginLeft: '-14px' }}
style={{ marginLeft: '-8px', marginTop: '-8px' }}
>
{(EuiResizablePanel, EuiResizableButton, { togglePanel }) => {
if (togglePanel) {
@@ -151,7 +151,12 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {

return (
<>
<EuiResizablePanel mode="main" initialSize={75} minSize="50%">
<EuiResizablePanel
mode="main"
initialSize={75}
minSize="50%"
paddingSize="s"
>
<Workspace
workflow={props.workflow}
onNodesChange={onNodesChange}
@@ -163,6 +168,7 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
mode="collapsible"
initialSize={25}
minSize="10%"
paddingSize="s"
onToggleCollapsedInternal={() => onToggleChange()}
>
<ComponentDetails selectedComponent={selectedComponent} />
5 changes: 5 additions & 0 deletions public/pages/workflows/workflow_list/columns.tsx
Original file line number Diff line number Diff line change
@@ -26,4 +26,9 @@ export const columns = [
name: 'Description',
sortable: false,
},
{
field: 'state',
name: 'Status',
sortable: true,
},
];
101 changes: 85 additions & 16 deletions public/pages/workflows/workflow_list/workflow_list.tsx
Original file line number Diff line number Diff line change
@@ -3,36 +3,105 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import React, { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { EuiInMemoryTable, Direction } from '@elastic/eui';
import { debounce } from 'lodash';
import {
EuiInMemoryTable,
Direction,
EuiFlexGroup,
EuiFlexItem,
EuiFilterSelectItem,
EuiFieldSearch,
} from '@elastic/eui';
import { AppState } from '../../../store';
import { Workflow } from '../../../../common';
import { columns } from './columns';
import { MultiSelectFilter } from '../../../general_components';
import { getStateOptions } from '../../../utils';

interface WorkflowListProps {}

const sorting = {
sort: {
field: 'name',
direction: 'asc' as Direction,
},
};

/**
* The searchable list of created workflows.
*/
export function WorkflowList(props: WorkflowListProps) {
const { workflows } = useSelector((state: AppState) => state.workflows);

const sorting = {
sort: {
field: 'name',
direction: 'asc' as Direction,
},
};
// search bar state
const [searchQuery, setSearchQuery] = useState<string>('');
const debounceSearchQuery = debounce((query: string) => {
setSearchQuery(query);
}, 100);

// filters state
const [selectedStates, setSelectedStates] = useState<EuiFilterSelectItem[]>(
getStateOptions()
);
const [filteredWorkflows, setFilteredWorkflows] = useState<Workflow[]>(
workflows || []
);

// When a filter selection or search query changes, update the list
useEffect(() => {
setFilteredWorkflows(
fetchFilteredWorkflows(workflows, selectedStates, searchQuery)
);
}, [selectedStates, searchQuery]);

return (
<EuiInMemoryTable<Workflow>
items={workflows}
rowHeader="name"
columns={columns}
sorting={sorting}
pagination={true}
message={'No existing workflows found'}
/>
<EuiFlexGroup direction="column">
<EuiFlexItem>
<EuiFlexGroup direction="row" gutterSize="m">
<EuiFlexItem grow={true}>
<EuiFieldSearch
fullWidth={true}
placeholder="Search workflows..."
onChange={(e) => debounceSearchQuery(e.target.value)}
/>
</EuiFlexItem>
<MultiSelectFilter
filters={getStateOptions()}
title="Status"
setSelectedFilters={setSelectedStates}
/>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiInMemoryTable<Workflow>
items={filteredWorkflows}
rowHeader="name"
columns={columns}
sorting={sorting}
pagination={true}
message={'No existing workflows found'}
/>
</EuiFlexItem>
</EuiFlexGroup>
);
}

// Collect the final workflow list after applying all filters
function fetchFilteredWorkflows(
allWorkflows: Workflow[],
stateFilters: EuiFilterSelectItem[],
searchQuery: string
): Workflow[] {
// @ts-ignore
const stateFilterStrings = stateFilters.map((filter) => filter.name);
const filteredWorkflows = allWorkflows.filter((workflow) =>
stateFilterStrings.includes(workflow.state)
);
return searchQuery.length === 0
? filteredWorkflows
: filteredWorkflows.filter((workflow) =>
workflow.name.toLowerCase().includes(searchQuery.toLowerCase())
);
}
3 changes: 2 additions & 1 deletion public/pages/workflows/workflows.tsx
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ enum WORKFLOWS_TAB {
CREATE = 'create',
}

const ACTIVE_TAB_PARAM = 'active_tab';
const ACTIVE_TAB_PARAM = 'tab';

function replaceActiveTab(activeTab: string, props: WorkflowsProps) {
props.history.replace({
@@ -100,6 +100,7 @@ export function Workflows(props: WorkflowsProps) {
},
},
]}
bottomBorder={true}
/>

<EuiPageContent>
3 changes: 3 additions & 0 deletions public/store/reducers/workflows_reducer.ts
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ import {
TextEmbeddingProcessor,
generateId,
initComponentData,
WORKFLOW_STATE,
} from '../../../common';

// TODO: remove after fetching from server-side
@@ -46,6 +47,7 @@ const initialState = {
name: 'Workflow-1',
id: 'workflow-1-id',
description: 'description for workflow 1',
state: WORKFLOW_STATE.SUCCEEDED,
workspaceFlowState: {
nodes: dummyNodes,
edges: [] as ReactFlowEdge[],
@@ -56,6 +58,7 @@ const initialState = {
name: 'Workflow-2',
id: 'workflow-2-id',
description: 'description for workflow 2',
state: WORKFLOW_STATE.FAILED,
workspaceFlowState: {
nodes: dummyNodes,
edges: [] as ReactFlowEdge[],
27 changes: 27 additions & 0 deletions public/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
*/

import { FormikErrors, FormikTouched, FormikValues } from 'formik';
import { EuiFilterSelectItem } from '@elastic/eui';
import { Schema, ObjectSchema } from 'yup';
import * as yup from 'yup';
import {
@@ -13,6 +14,7 @@ import {
IComponentData,
IComponentField,
WorkspaceFormValues,
WORKFLOW_STATE,
} from '../../common';

// Append 16 random characters
@@ -124,3 +126,28 @@ function getFieldSchema(field: IComponentField): Schema {
? baseSchema.optional()
: baseSchema.required('Required');
}

export function getStateOptions(): EuiFilterSelectItem[] {
return [
// @ts-ignore
{
name: WORKFLOW_STATE.SUCCEEDED,
checked: 'on',
} as EuiFilterSelectItem,
// @ts-ignore
{
name: WORKFLOW_STATE.NOT_STARTED,
checked: 'on',
} as EuiFilterSelectItem,
// @ts-ignore
{
name: WORKFLOW_STATE.IN_PROGRESS,
checked: 'on',
} as EuiFilterSelectItem,
// @ts-ignore
{
name: WORKFLOW_STATE.FAILED,
checked: 'on',
} as EuiFilterSelectItem,
];
}