Skip to content

Commit

Permalink
Add delete modal; add empty list msg (opensearch-project#93)
Browse files Browse the repository at this point in the history
Signed-off-by: Tyler Ohlsen <[email protected]>
  • Loading branch information
ohltyler authored Mar 1, 2024
1 parent 7043c11 commit 454ee82
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 37 deletions.
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

0 comments on commit 454ee82

Please sign in to comment.