Skip to content

Commit 454ee82

Browse files
authored
Add delete modal; add empty list msg (#93)
Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>
1 parent 7043c11 commit 454ee82

File tree

5 files changed

+161
-37
lines changed

5 files changed

+161
-37
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import {
8+
EuiButton,
9+
EuiModal,
10+
EuiModalBody,
11+
EuiModalFooter,
12+
EuiModalHeader,
13+
EuiModalHeaderTitle,
14+
EuiText,
15+
} from '@elastic/eui';
16+
import { Workflow } from '../../common';
17+
18+
interface DeleteWorkflowModalProps {
19+
workflow: Workflow;
20+
onClose: () => void;
21+
onConfirm: () => void;
22+
}
23+
24+
/**
25+
* A general delete workflow modal.
26+
*/
27+
export function DeleteWorkflowModal(props: DeleteWorkflowModalProps) {
28+
return (
29+
<EuiModal onClose={props.onClose}>
30+
<EuiModalHeader>
31+
<EuiModalHeaderTitle>
32+
<p>{`Delete ${props.workflow.name}?`}</p>
33+
</EuiModalHeaderTitle>
34+
</EuiModalHeader>
35+
<EuiModalBody>
36+
<EuiText>The workflow will be permanently deleted.</EuiText>
37+
</EuiModalBody>
38+
<EuiModalFooter>
39+
<EuiButton onClick={props.onConfirm} fill={true} color="danger">
40+
Confirm
41+
</EuiButton>
42+
</EuiModalFooter>
43+
</EuiModal>
44+
);
45+
}

public/general_components/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
*/
55

66
export { MultiSelectFilter } from './multi_select_filter';
7+
export { DeleteWorkflowModal } from './delete_workflow_modal';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import {
8+
EuiButton,
9+
EuiFlexGroup,
10+
EuiFlexItem,
11+
EuiSpacer,
12+
EuiText,
13+
EuiTitle,
14+
} from '@elastic/eui';
15+
16+
interface EmptyListMessageProps {
17+
onClickNewWorkflow: () => void;
18+
}
19+
20+
export function EmptyListMessage(props: EmptyListMessageProps) {
21+
return (
22+
<EuiFlexGroup direction="column" alignItems="center" gutterSize="m">
23+
<EuiFlexItem>
24+
<EuiSpacer size="m" />
25+
</EuiFlexItem>
26+
<EuiFlexItem>
27+
<EuiTitle size="s">
28+
<h3>No workflows found</h3>
29+
</EuiTitle>
30+
</EuiFlexItem>
31+
<EuiFlexItem>
32+
<EuiText size="s">
33+
Create a workflow to start building and testing your application.
34+
</EuiText>
35+
</EuiFlexItem>
36+
<EuiFlexItem>
37+
<EuiButton fill={false} onClick={props.onClickNewWorkflow}>
38+
New workflow
39+
</EuiButton>
40+
</EuiFlexItem>
41+
</EuiFlexGroup>
42+
);
43+
}

public/pages/workflows/workflow_list/workflow_list.tsx

+58-36
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ import {
1818
import { AppState, deleteWorkflow } from '../../../store';
1919
import { Workflow } from '../../../../common';
2020
import { columns } from './columns';
21-
import { MultiSelectFilter } from '../../../general_components';
21+
import {
22+
DeleteWorkflowModal,
23+
MultiSelectFilter,
24+
} from '../../../general_components';
2225
import { getStateOptions } from '../../../utils';
2326

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

45+
// delete workflow state
46+
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState<boolean>(false);
47+
const [workflowToDelete, setWorkflowToDelete] = useState<
48+
Workflow | undefined
49+
>(undefined);
50+
function clearDeleteState() {
51+
setWorkflowToDelete(undefined);
52+
setIsDeleteModalOpen(false);
53+
}
54+
4255
// search bar state
4356
const [searchQuery, setSearchQuery] = useState<string>('');
4457
const debounceSearchQuery = debounce((query: string) => {
@@ -70,48 +83,57 @@ export function WorkflowList(props: WorkflowListProps) {
7083
icon: 'trash',
7184
color: 'danger',
7285
onClick: (item: Workflow) => {
73-
dispatch(deleteWorkflow(item.id));
86+
setWorkflowToDelete(item);
87+
setIsDeleteModalOpen(true);
7488
},
7589
},
7690
];
7791

7892
return (
79-
<EuiFlexGroup direction="column">
80-
<EuiFlexItem>
81-
<EuiFlexGroup direction="row" gutterSize="m">
82-
<EuiFlexItem grow={true}>
83-
<EuiFieldSearch
84-
fullWidth={true}
85-
placeholder="Search workflows..."
86-
onChange={(e) => debounceSearchQuery(e.target.value)}
93+
<>
94+
{isDeleteModalOpen && workflowToDelete !== undefined && (
95+
<DeleteWorkflowModal
96+
workflow={workflowToDelete}
97+
onClose={() => {
98+
clearDeleteState();
99+
}}
100+
onConfirm={() => {
101+
dispatch(deleteWorkflow(workflowToDelete.id));
102+
clearDeleteState();
103+
}}
104+
/>
105+
)}
106+
<EuiFlexGroup direction="column">
107+
<EuiFlexItem>
108+
<EuiFlexGroup direction="row" gutterSize="m">
109+
<EuiFlexItem grow={true}>
110+
<EuiFieldSearch
111+
fullWidth={true}
112+
placeholder="Search workflows..."
113+
onChange={(e) => debounceSearchQuery(e.target.value)}
114+
/>
115+
</EuiFlexItem>
116+
<MultiSelectFilter
117+
filters={getStateOptions()}
118+
title="Status"
119+
setSelectedFilters={setSelectedStates}
87120
/>
88-
</EuiFlexItem>
89-
<MultiSelectFilter
90-
filters={getStateOptions()}
91-
title="Status"
92-
setSelectedFilters={setSelectedStates}
121+
</EuiFlexGroup>
122+
</EuiFlexItem>
123+
<EuiFlexItem>
124+
<EuiInMemoryTable<Workflow>
125+
items={filteredWorkflows}
126+
rowHeader="name"
127+
// @ts-ignore
128+
columns={columns(tableActions)}
129+
sorting={sorting}
130+
pagination={true}
131+
message={loading === true ? <EuiLoadingSpinner size="xl" /> : null}
132+
hasActions={true}
93133
/>
94-
</EuiFlexGroup>
95-
</EuiFlexItem>
96-
<EuiFlexItem>
97-
<EuiInMemoryTable<Workflow>
98-
items={filteredWorkflows}
99-
rowHeader="name"
100-
// @ts-ignore
101-
columns={columns(tableActions)}
102-
sorting={sorting}
103-
pagination={true}
104-
message={
105-
loading === true ? (
106-
<EuiLoadingSpinner size="xl" />
107-
) : (
108-
'No existing workflows found'
109-
)
110-
}
111-
hasActions={true}
112-
/>
113-
</EuiFlexItem>
114-
</EuiFlexGroup>
134+
</EuiFlexItem>
135+
</EuiFlexGroup>
136+
</>
115137
);
116138
}
117139

public/pages/workflows/workflows.tsx

+14-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { getCore } from '../../services';
2020
import { WorkflowList } from './workflow_list';
2121
import { NewWorkflow } from './new_workflow';
2222
import { AppState, searchWorkflows } from '../../store';
23+
import { EmptyListMessage } from './empty_list_message';
2324

2425
export interface WorkflowsRouterProps {}
2526

@@ -48,7 +49,9 @@ function replaceActiveTab(activeTab: string, props: WorkflowsProps) {
4849
*/
4950
export function Workflows(props: WorkflowsProps) {
5051
const dispatch = useDispatch();
51-
const { workflows } = useSelector((state: AppState) => state.workflows);
52+
const { workflows, loading } = useSelector(
53+
(state: AppState) => state.workflows
54+
);
5255

5356
const tabFromUrl = queryString.parse(useLocation().search)[
5457
ACTIVE_TAB_PARAM
@@ -130,6 +133,16 @@ export function Workflows(props: WorkflowsProps) {
130133
<EuiSpacer size="m" />
131134
{selectedTabId === WORKFLOWS_TAB.MANAGE && <WorkflowList />}
132135
{selectedTabId === WORKFLOWS_TAB.CREATE && <NewWorkflow />}
136+
{selectedTabId === WORKFLOWS_TAB.MANAGE &&
137+
Object.values(workflows).length === 0 &&
138+
!loading && (
139+
<EmptyListMessage
140+
onClickNewWorkflow={() => {
141+
setSelectedTabId(WORKFLOWS_TAB.CREATE);
142+
replaceActiveTab(WORKFLOWS_TAB.CREATE, props);
143+
}}
144+
/>
145+
)}
133146
</EuiPageContent>
134147
</EuiPageBody>
135148
</EuiPage>

0 commit comments

Comments
 (0)