Skip to content

Commit

Permalink
Merge pull request #1101 from PintoGideon/Update-Pipelines
Browse files Browse the repository at this point in the history
Clear alerts on both success and error states
  • Loading branch information
PintoGideon authored Mar 12, 2024
2 parents 0ef7581 + c7c4ced commit 6799aa0
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 208 deletions.
217 changes: 119 additions & 98 deletions src/components/DeleteNode/index.tsx
Original file line number Diff line number Diff line change
@@ -1,119 +1,140 @@
import React from "react";
import { Dispatch } from "redux";
import { useDispatch } from "react-redux";
import { ErrorBoundary } from "react-error-boundary";
import { PluginInstance } from "@fnndsc/chrisapi";
import { Button, Modal, ModalVariant } from "@patternfly/react-core";
import { connect } from "react-redux";
import { ApplicationState } from "../../store/root/applicationState";
import { Feed, PluginInstance } from "@fnndsc/chrisapi";
import {
clearDeleteState,
deleteNode,
deleteNodeError,
} from "../../store/pluginInstance/actions";
import TrashIcon from "@patternfly/react-icons/dist/esm/icons/trash-icon";

import { useMutation } from "@tanstack/react-query";
import { Alert } from "antd";
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { fetchResource } from "../../api/common";
import { useTypedSelector } from "../../store/hooks";
import { getNodeOperations } from "../../store/plugin/actions";
import {
getPluginInstancesSuccess,
getSelectedPlugin,
} from "../../store/pluginInstance/actions";
import { getPluginInstanceStatusRequest } from "../../store/resources/actions";
import { SpinContainer } from "../Common";

interface DeleteNodeProps {
selectedPlugin?: PluginInstance;
deleteNode: (instance: PluginInstance, feed: Feed) => void;
deleteNodeState: {
error: string;
success: boolean;
};
}

const DeleteNode: React.FC<DeleteNodeProps> = ({
selectedPlugin,
deleteNode,
deleteNodeState,
}: DeleteNodeProps) => {
const DeleteNode = () => {
const dispatch = useDispatch();
const feed = useTypedSelector((state) => state.feed.currentFeed);
const { deleteNode: isModalOpen } = useTypedSelector(
(state) => state.plugin.nodeOperations,
);
const { data } = feed;
const handleModalToggle = React.useCallback(() => {
dispatch(getNodeOperations("deleteNode"));
if (isModalOpen) {
dispatch(clearDeleteState());
}
}, [dispatch, isModalOpen]);
const { selectedPlugin } = useTypedSelector((state) => state.instance);
const currentFeed = useTypedSelector((state) => state.feed.currentFeed.data);

const handleDelete = async () => {
if (selectedPlugin && data) {
deleteNode(selectedPlugin, data);
handleModalToggle();
if (selectedPlugin) {
try {
const statuses = [
"finishedSuccessfully",
"finishedWithError",
"cancelled",
];
const status = selectedPlugin.data.status;

if (!statuses.includes(status)) {
// Always cancel an active node first to avoid side effects
await selectedPlugin.put({
status: "cancelled",
});
}

await selectedPlugin.delete();
//Fetch Resources again because I don't understand how delete works in cube. It's highly inconsistent
if (currentFeed) {
const params = { limit: 15, offset: 0 };

const fn = currentFeed.getPluginInstances;
const boundFn = fn.bind(currentFeed);
const { resource: pluginInstances } =
await fetchResource<PluginInstance>(params, boundFn);

const selected = pluginInstances[pluginInstances.length - 1];
const pluginInstanceObj = {
selected,
pluginInstances,
};

dispatch(getSelectedPlugin(selected));
dispatch(getPluginInstancesSuccess(pluginInstanceObj));
dispatch(getPluginInstanceStatusRequest(pluginInstanceObj));
}
} catch (e) {
throw e;
}
} else {
dispatch(
deleteNodeError({
error_message: "Please wait for the plugin to finish running",
}),
);
throw new Error("Please select a node to delete");
}
};

const mutation = useMutation({
mutationFn: () => handleDelete(),
});

const handleModalToggle = () => {
dispatch(getNodeOperations("deleteNode"));
};

useEffect(() => {
if (mutation.isSuccess) {
setTimeout(() => {
mutation.reset();
dispatch(getNodeOperations("deleteNode"));
}, 1000);
}
}, [mutation.isSuccess]);

return (
<React.Fragment>
<ErrorBoundary fallback={<FallbackComponent />}>
<Button
disabled={!selectedPlugin}
onClick={handleModalToggle}
icon={<TrashIcon />}
type="button"
>
Delete Node{" "}
<span style={{ padding: "2px", color: "#F5F5DC" }}>( D )</span>
</Button>
<Modal
variant={ModalVariant.small}
title="Delete Node Confirmation"
isOpen={isModalOpen}
onClose={handleModalToggle}
actions={[
<React.Fragment key="modal-action">
<Button key="confirm" variant="primary" onClick={handleDelete}>
Confirm
</Button>
<Button
key="cancel"
variant="primary"
onClick={handleModalToggle}
>
Cancel
</Button>
</React.Fragment>,
]}
>
<>
<Button
isDisabled={
selectedPlugin.data.plugin_type === "fs" &&
selectedPlugin.data.plugin_name === "pl-dircopy"
}
onClick={handleModalToggle}
icon={<TrashIcon />}
variant="primary"
type="button"
>
Delete Node{" "}
</Button>
<Modal
variant={ModalVariant.small}
title="Delete Selected Node"
isOpen={isModalOpen}
onClose={handleModalToggle}
actions={[
<>
<Button
key="confirm"
variant="primary"
onClick={() => mutation.mutate()}
>
Confirm
</Button>
<Button key="cancel" variant="primary" onClick={handleModalToggle}>
Cancel
</Button>
</>,
]}
>
<span>
{" "}
Deleting a node will delete all it&apos;s descendants as well. Please
confirm if you are sure
{deleteNodeState.error && <span>{deleteNodeState.error}</span>}
</Modal>
</ErrorBoundary>
</React.Fragment>
</span>

{mutation.isPending && <SpinContainer title="Deleting..." />}
{mutation.isError && (
<Alert type="error" description={mutation.error.message} />
)}
{mutation.isSuccess && (
<Alert type="success" description="Deleted Successfully" />
)}
</Modal>
</>
);
};

const mapStateToProps = (state: ApplicationState) => ({
selectedPlugin: state.instance.selectedPlugin,
deleteNodeState: state.instance.deleteNode,
});

const mapDispatchToProps = (dispatch: Dispatch) => ({
deleteNode: (instance: PluginInstance, feed: Feed) =>
dispatch(deleteNode(instance, feed)),
});

const DeleteNodeConnect = connect(
mapStateToProps,
mapDispatchToProps,
)(DeleteNode);

export default DeleteNodeConnect;

function FallbackComponent() {
return <div>Oops ! Delete was not successful. Please try again.</div>;
}
export default DeleteNode;
4 changes: 2 additions & 2 deletions src/components/DownloadNode/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,12 @@ function DownloadNode() {
});

useEffect(() => {
if (mutation.isSuccess) {
if (mutation.isSuccess || mutation.isError) {
setTimeout(() => {
mutation.reset();
}, 1000);
}
}, [mutation.isSuccess]);
}, [mutation.isSuccess, mutation.isError]);

return (
<>
Expand Down
8 changes: 3 additions & 5 deletions src/components/NodeDetails/NodeDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,9 @@ const NodeDetails: React.FC = () => {
<GraphNodeContainer />
</RenderButtonGridItem>

{selectedPlugin.data.previous_id !== undefined && (
<RenderButtonGridItem>
<DeleteNode />
</RenderButtonGridItem>
)}
<RenderButtonGridItem>
<DeleteNode />
</RenderButtonGridItem>
</Grid>

<Grid hasGutter={true}>
Expand Down
26 changes: 4 additions & 22 deletions src/store/pluginInstance/actions.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { Feed, PluginInstance } from "@fnndsc/chrisapi";
import { action } from "typesafe-actions";
import {
PluginInstanceTypes,
PluginInstanceObj,
AddNodePayload,
PluginInstanceObj,
PluginInstanceTypes,
SplitNodesPayload,
} from "./types";
import { PluginInstance, Feed } from "@fnndsc/chrisapi";

export const getSelectedPlugin = (item: PluginInstance) =>
action(PluginInstanceTypes.GET_SELECTED_PLUGIN, item);

export const getSelectedD3Node = (item: any) =>
action(PluginInstanceTypes.GET_SELECTED_D3_NODE, item);

export const getPluginInstancesRequest = (feed: Feed) =>
action(PluginInstanceTypes.GET_PLUGIN_INSTANCES_REQUEST, feed);
export const getPluginInstancesSuccess = (items: PluginInstanceObj) =>
Expand All @@ -22,28 +21,11 @@ export const addNodeRequest = (item: AddNodePayload) =>
action(PluginInstanceTypes.ADD_NODE_REQUEST, item);
export const addNodeSuccess = (pluginItem: PluginInstance) =>
action(PluginInstanceTypes.ADD_NODE_SUCCESS, pluginItem);

export const setPluginTitle = (pluginItem: PluginInstance) =>
action(PluginInstanceTypes.SET_PLUGIN_TITLE, pluginItem);

export const deleteNode = (instance: PluginInstance, feed: Feed) => {
return action(PluginInstanceTypes.DELETE_NODE, {
instance,
feed,
});
};
export const deleteNodeSuccess = () =>
action(PluginInstanceTypes.DELETE_NODE_SUCCESS);

export const deleteNodeError = (error: { error_message: string }) =>
action(PluginInstanceTypes.DELETE_NODE_ERROR, error);

export const addSplitNodes = (splitNodesPayload: SplitNodesPayload) =>
action(PluginInstanceTypes.ADD_SPLIT_NODES, splitNodesPayload);
export const addSplitNodesSuccess = (splitNodes: PluginInstance[]) =>
action(PluginInstanceTypes.ADD_SPLIT_NODES_SUCCESS, splitNodes);

export const resetPluginInstances = () =>
action(PluginInstanceTypes.RESET_PLUGIN_INSTANCES);

export const clearDeleteState = () => action(PluginInstanceTypes.CLEAR_DELETE);
35 changes: 0 additions & 35 deletions src/store/pluginInstance/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ export const initialState: IPluginInstanceState = {
error: "",
loading: false,
},
deleteNode: {
error: "",
success: false,
},
selectedD3Node: undefined,
};

Expand Down Expand Up @@ -61,16 +57,6 @@ const reducer: Reducer<IPluginInstanceState> = (
};
}

case PluginInstanceTypes.CLEAR_DELETE: {
return {
...state,
deleteNode: {
success: false,
error: "",
},
};
}

case PluginInstanceTypes.GET_SELECTED_PLUGIN: {
return {
...state,
Expand Down Expand Up @@ -150,27 +136,6 @@ const reducer: Reducer<IPluginInstanceState> = (
return state;
}

case PluginInstanceTypes.DELETE_NODE_SUCCESS: {
return {
...state,

deleteNode: {
...state.deleteNode,
success: true,
},
};
}

case PluginInstanceTypes.DELETE_NODE_ERROR: {
return {
...state,
deleteNode: {
success: false,
error: action.payload.error_message,
},
};
}

case PluginInstanceTypes.RESET_PLUGIN_INSTANCES: {
return {
...initialState,
Expand Down
Loading

0 comments on commit 6799aa0

Please sign in to comment.