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

[Backport 2.x] Add multi-filtering to tables; refactor details pages into tabs #83

Merged
merged 1 commit into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Expand Up @@ -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;
}

/**
* 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
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
Expand Up @@ -11,6 +11,7 @@ import { saveWorkflow } from '../utils';
import { rfContext, AppState, removeDirty } from '../../../store';

interface WorkflowDetailHeaderProps {
tabs: any[];
workflow?: Workflow;
}

Expand All @@ -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
Expand All @@ -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';
Loading
Loading