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] [Workflow list] Add delete modal; add empty list msg #94

Merged
merged 1 commit into from
Mar 1, 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
45 changes: 45 additions & 0 deletions public/general_components/delete_workflow_modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import {
EuiButton,
EuiModal,
EuiModalBody,
EuiModalFooter,
EuiModalHeader,
EuiModalHeaderTitle,
EuiText,
} from '@elastic/eui';
import { Workflow } from '../../common';

interface DeleteWorkflowModalProps {
workflow: Workflow;
onClose: () => void;
onConfirm: () => void;
}

/**
* A general delete workflow modal.
*/
export function DeleteWorkflowModal(props: DeleteWorkflowModalProps) {
return (
<EuiModal onClose={props.onClose}>
<EuiModalHeader>
<EuiModalHeaderTitle>
<p>{`Delete ${props.workflow.name}?`}</p>
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<EuiText>The workflow will be permanently deleted.</EuiText>
</EuiModalBody>
<EuiModalFooter>
<EuiButton onClick={props.onConfirm} fill={true} color="danger">
Confirm
</EuiButton>
</EuiModalFooter>
</EuiModal>
);
}
1 change: 1 addition & 0 deletions public/general_components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
*/

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

import React from 'react';
import {
EuiButton,
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
EuiText,
EuiTitle,
} from '@elastic/eui';

interface EmptyListMessageProps {
onClickNewWorkflow: () => void;
}

export function EmptyListMessage(props: EmptyListMessageProps) {
return (
<EuiFlexGroup direction="column" alignItems="center" gutterSize="m">
<EuiFlexItem>
<EuiSpacer size="m" />
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="s">
<h3>No workflows found</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="s">
Create a workflow to start building and testing your application.
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiButton fill={false} onClick={props.onClickNewWorkflow}>
New workflow
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
);
}
94 changes: 58 additions & 36 deletions public/pages/workflows/workflow_list/workflow_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import {
import { AppState, deleteWorkflow } from '../../../store';
import { Workflow } from '../../../../common';
import { columns } from './columns';
import { MultiSelectFilter } from '../../../general_components';
import {
DeleteWorkflowModal,
MultiSelectFilter,
} from '../../../general_components';
import { getStateOptions } from '../../../utils';

interface WorkflowListProps {}
Expand All @@ -39,6 +42,16 @@ export function WorkflowList(props: WorkflowListProps) {
(state: AppState) => state.workflows
);

// delete workflow state
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState<boolean>(false);
const [workflowToDelete, setWorkflowToDelete] = useState<
Workflow | undefined
>(undefined);
function clearDeleteState() {
setWorkflowToDelete(undefined);
setIsDeleteModalOpen(false);
}

// search bar state
const [searchQuery, setSearchQuery] = useState<string>('');
const debounceSearchQuery = debounce((query: string) => {
Expand Down Expand Up @@ -70,48 +83,57 @@ export function WorkflowList(props: WorkflowListProps) {
icon: 'trash',
color: 'danger',
onClick: (item: Workflow) => {
dispatch(deleteWorkflow(item.id));
setWorkflowToDelete(item);
setIsDeleteModalOpen(true);
},
},
];

return (
<EuiFlexGroup direction="column">
<EuiFlexItem>
<EuiFlexGroup direction="row" gutterSize="m">
<EuiFlexItem grow={true}>
<EuiFieldSearch
fullWidth={true}
placeholder="Search workflows..."
onChange={(e) => debounceSearchQuery(e.target.value)}
<>
{isDeleteModalOpen && workflowToDelete !== undefined && (
<DeleteWorkflowModal
workflow={workflowToDelete}
onClose={() => {
clearDeleteState();
}}
onConfirm={() => {
dispatch(deleteWorkflow(workflowToDelete.id));
clearDeleteState();
}}
/>
)}
<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}
/>
</EuiFlexItem>
<MultiSelectFilter
filters={getStateOptions()}
title="Status"
setSelectedFilters={setSelectedStates}
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiInMemoryTable<Workflow>
items={filteredWorkflows}
rowHeader="name"
// @ts-ignore
columns={columns(tableActions)}
sorting={sorting}
pagination={true}
message={loading === true ? <EuiLoadingSpinner size="xl" /> : null}
hasActions={true}
/>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiInMemoryTable<Workflow>
items={filteredWorkflows}
rowHeader="name"
// @ts-ignore
columns={columns(tableActions)}
sorting={sorting}
pagination={true}
message={
loading === true ? (
<EuiLoadingSpinner size="xl" />
) : (
'No existing workflows found'
)
}
hasActions={true}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</>
);
}

Expand Down
15 changes: 14 additions & 1 deletion public/pages/workflows/workflows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { getCore } from '../../services';
import { WorkflowList } from './workflow_list';
import { NewWorkflow } from './new_workflow';
import { AppState, searchWorkflows } from '../../store';
import { EmptyListMessage } from './empty_list_message';

export interface WorkflowsRouterProps {}

Expand Down Expand Up @@ -48,7 +49,9 @@ function replaceActiveTab(activeTab: string, props: WorkflowsProps) {
*/
export function Workflows(props: WorkflowsProps) {
const dispatch = useDispatch();
const { workflows } = useSelector((state: AppState) => state.workflows);
const { workflows, loading } = useSelector(
(state: AppState) => state.workflows
);

const tabFromUrl = queryString.parse(useLocation().search)[
ACTIVE_TAB_PARAM
Expand Down Expand Up @@ -130,6 +133,16 @@ export function Workflows(props: WorkflowsProps) {
<EuiSpacer size="m" />
{selectedTabId === WORKFLOWS_TAB.MANAGE && <WorkflowList />}
{selectedTabId === WORKFLOWS_TAB.CREATE && <NewWorkflow />}
{selectedTabId === WORKFLOWS_TAB.MANAGE &&
Object.values(workflows).length === 0 &&
!loading && (
<EmptyListMessage
onClickNewWorkflow={() => {
setSelectedTabId(WORKFLOWS_TAB.CREATE);
replaceActiveTab(WORKFLOWS_TAB.CREATE, props);
}}
/>
)}
</EuiPageContent>
</EuiPageBody>
</EuiPage>
Expand Down
Loading