diff --git a/src/components/AddPipeline/AddPipeline.tsx b/src/components/AddPipeline/AddPipeline.tsx
index 46d72c7ba..cdce86290 100644
--- a/src/components/AddPipeline/AddPipeline.tsx
+++ b/src/components/AddPipeline/AddPipeline.tsx
@@ -24,10 +24,12 @@ const AddPipeline = () => {
(state) => state.plugin.nodeOperations,
);
- const alreadyAvailableInstances = useTypedSelector(
- (state) => state.instance.pluginInstances.data,
+ const { pluginInstances, selectedPlugin } = useTypedSelector(
+ (state) => state.instance,
);
+ const alreadyAvailableInstances = pluginInstances.data;
+
const handleToggle = () => {
if (childPipeline) {
dispatch({
@@ -38,14 +40,11 @@ const AddPipeline = () => {
reactDispatch(getNodeOperations("childPipeline"));
};
- const feed = useTypedSelector((state) => state.feed.currentFeed.data);
- const { selectedPlugin } = useTypedSelector((state) => state.instance);
-
const addPipeline = async () => {
const id = pipelineToAdd?.data.id;
const resources = selectedPipeline?.[id];
- if (selectedPlugin && feed && resources) {
+ if (selectedPlugin && resources) {
const { parameters } = resources;
const client = ChrisAPIClient.getClient();
@@ -73,6 +72,7 @@ const AddPipeline = () => {
nodes_info: JSON.stringify(nodes_info),
});
+ // Use the pagination helper here
const pluginInstances = await workflow.getPluginInstances({
limit: 1000,
});
@@ -100,6 +100,14 @@ const AddPipeline = () => {
mutationFn: () => addPipeline(),
});
+ React.useEffect(() => {
+ if (mutation.isSuccess) {
+ setTimeout(() => {
+ handleToggle();
+ }, 1000);
+ }
+ }, [mutation.isSuccess]);
+
React.useEffect(() => {
const el = document.querySelector("#indicators");
diff --git a/src/components/DownloadNode/index.tsx b/src/components/DownloadNode/index.tsx
new file mode 100644
index 000000000..d628e7b49
--- /dev/null
+++ b/src/components/DownloadNode/index.tsx
@@ -0,0 +1,145 @@
+import { Button } from "@patternfly/react-core";
+import FaDownloadIcon from "@patternfly/react-icons/dist/esm/icons/download-icon";
+import { useMutation } from "@tanstack/react-query";
+import { Alert } from "antd";
+import { useEffect } from "react";
+import { useDispatch } from "react-redux";
+import ChrisAPIClient from "../../api/chrisapiclient";
+import { fetchComputeInfo, fetchResources } from "../../api/common";
+import { useTypedSelector } from "../../store/hooks";
+import {
+ getPluginInstancesSuccess,
+ getSelectedPlugin,
+} from "../../store/pluginInstance/actions";
+import { getPluginInstanceStatusRequest } from "../../store/resources/actions";
+import { SpinContainer } from "../Common";
+import { PerPipelinePayload } from "../PipelinesCopy/context";
+
+function DownloadNode() {
+ const { pluginInstances, selectedPlugin } = useTypedSelector(
+ (state) => state.instance,
+ );
+ const reactDispatch = useDispatch();
+
+ const alreadyAvailableInstances = pluginInstances.data;
+
+ async function fetchPipelines() {
+ const client = ChrisAPIClient.getClient();
+
+ try {
+ const pipelineList = await client.getPipelines({
+ name: "zip v20240311",
+ });
+
+ const pipelines = pipelineList.getItems();
+
+ if (pipelines && pipelines.length > 0) {
+ const pipeline = pipelines[0];
+ const { id } = pipeline.data;
+
+ const data: PerPipelinePayload = await fetchResources(pipeline);
+ const { parameters, pluginPipings } = data;
+
+ const nodes_info = client.computeWorkflowNodesInfo(parameters.data);
+
+ // This is an assumption as I know the zip file will only have on pluginPiping
+ const piping = pluginPipings[0];
+ const computeEnvPayload = await fetchComputeInfo(
+ piping.data.plugin_id,
+ `${piping.data.id}`,
+ );
+
+ for (const node of nodes_info) {
+ const activeNode = computeEnvPayload?.[node.piping_id];
+
+ if (activeNode) {
+ const compute_node = activeNode.currentlySelected;
+ node.compute_resource_name = compute_node;
+ }
+ }
+
+ const workflow = await client.createWorkflow(id, {
+ previous_plugin_inst_id: selectedPlugin?.data.id, // Ensure selectedPlugin is defined
+ nodes_info: JSON.stringify(nodes_info),
+ });
+
+ const pluginInstances = await workflow.getPluginInstances({
+ limit: 1000,
+ });
+
+ const instanceItems = pluginInstances.getItems();
+
+ if (
+ instanceItems &&
+ instanceItems.length > 0 &&
+ alreadyAvailableInstances
+ ) {
+ const firstInstance = instanceItems[instanceItems.length - 1];
+ const completeList = [...alreadyAvailableInstances, ...instanceItems];
+
+ // Assuming reactDispatch, getSelectedPlugin, getPluginInstanceStatusSuccess, and getPluginInstanceStatusRequest are defined elsewhere
+ reactDispatch(getSelectedPlugin(firstInstance));
+
+ const pluginInstanceObj = {
+ selected: firstInstance,
+ pluginInstances: completeList,
+ };
+
+ reactDispatch(getPluginInstancesSuccess(pluginInstanceObj));
+ reactDispatch(getPluginInstanceStatusRequest(pluginInstanceObj));
+ }
+ } else {
+ throw new Error(
+ "The pipeline to zip is not registered. Please contact an admin",
+ );
+ }
+ return pipelines;
+ } catch (error) {
+ throw error;
+ }
+ }
+
+ const mutation = useMutation({
+ mutationFn: () => fetchPipelines(),
+ });
+
+ useEffect(() => {
+ if (mutation.isSuccess) {
+ setTimeout(() => {
+ mutation.reset();
+ }, 1000);
+ }
+ }, [mutation.isSuccess]);
+
+ return (
+ <>
+
+ {mutation.isError || mutation.isSuccess || mutation.isPending ? (
+
+ {mutation.isError && (
+
+ )}
+ {mutation.isSuccess && (
+
+ )}
+ {mutation.isPending && (
+
+ )}
+
+ ) : null}
+ >
+ );
+}
+
+export default DownloadNode;
diff --git a/src/components/FeedOutputBrowser/FileBrowser.tsx b/src/components/FeedOutputBrowser/FileBrowser.tsx
index e64fd55f7..9af2f2a17 100644
--- a/src/components/FeedOutputBrowser/FileBrowser.tsx
+++ b/src/components/FeedOutputBrowser/FileBrowser.tsx
@@ -133,7 +133,7 @@ const FileBrowser = (props: FileBrowserProps) => {
} else {
toggleAnimation();
dispatch(setSelectedFile(item));
- !drawerState["preview"].open && dispatch(setFilePreviewPanel());
+ !drawerState.preview.open && dispatch(setFilePreviewPanel());
}
};
@@ -239,10 +239,10 @@ const FileBrowser = (props: FileBrowserProps) => {
) : (
{items.map((item: string | FeedFile, index) => {
- let type;
- let icon;
- let fsize;
- let fileName;
+ let type: string;
+ let icon: React.ReactNode;
+ let fsize: string;
+ let fileName: string;
type = "UNKNOWN FORMAT";
const isPreviewing = selectedFile === item;
let currentStatus = 0;
diff --git a/src/components/IconContainer/index.tsx b/src/components/IconContainer/index.tsx
index 4e12a64d3..8c8cb9129 100644
--- a/src/components/IconContainer/index.tsx
+++ b/src/components/IconContainer/index.tsx
@@ -71,7 +71,7 @@ const IconContainer = () => {
let prefix = "";
if (action === "merge") {
prefix = "Merge of ";
- } else if (action === "download") {
+ } else if (action === "") {
prefix = "archive-";
} else {
prefix = "";
@@ -208,7 +208,7 @@ const IconContainer = () => {
let newFeedName = feedNames.toString().replace(/[, ]+/g, "_");
let createdFeed: Feed | null = null;
- if (operation === "download") {
+ if (operation === "archive") {
newFeedName = `archive-${newFeedName}`;
newFeedName = newFeedName.substring(0, 100);
newFeedName = feedName === "" ? newFeedName : feedName;
@@ -286,11 +286,11 @@ const IconContainer = () => {
currentAction === "share" &&
shareFeedMutation.mutate({ bulkSelect, sharePublically, feedName });
currentAction === "delete" && deleteFeedMutation.mutate(bulkSelect);
- currentAction === "download" &&
+ currentAction === "archive" &&
handleDownloadMutation.mutate({
feedList: bulkSelect,
feedName,
- operation: "download",
+ operation: "archive",
});
currentAction === "merge" &&
handleDownloadMutation.mutate({
@@ -304,7 +304,7 @@ const IconContainer = () => {
return (
- {["download", "merge", "duplicate", "share", "delete"].map((action) => {
+ {["archive", "merge", "duplicate", "share", "delete"].map((action) => {
return (
,
+ archive: ,
merge: ,
duplicate: ,
share: ,
diff --git a/src/components/NodeDetails/NodeDetails.tsx b/src/components/NodeDetails/NodeDetails.tsx
index 733ea5cfa..3f81e2e7c 100644
--- a/src/components/NodeDetails/NodeDetails.tsx
+++ b/src/components/NodeDetails/NodeDetails.tsx
@@ -10,7 +10,7 @@ import {
Grid,
GridItem,
} from "@patternfly/react-core";
-import { EyeIcon, CalendarAltIcon } from "@patternfly/react-icons";
+import { CalendarAltIcon, EyeIcon } from "@patternfly/react-icons";
import React, { Fragment, ReactNode } from "react";
import { ErrorBoundary } from "react-error-boundary";
import { useNavigate } from "react-router";
@@ -20,16 +20,17 @@ import { AddNodeProvider } from "../AddNode/context";
import AddPipeline from "../AddPipeline/AddPipeline";
import GraphNodeContainer from "../AddTsNode";
import { SpinContainer } from "../Common";
-import { PipelineProvider } from "../PipelinesCopy/context";
+import { isPlVisualDataset } from "../DatasetRedirect/getDatasets";
import DeleteNode from "../DeleteNode";
+import DownloadNode from "../DownloadNode";
import FeedNote from "../FeedDetails/FeedNote";
+import { PipelineProvider } from "../PipelinesCopy/context";
import "./NodeDetails.css";
import PluginLog from "./PluginLog";
import PluginTitle from "./PluginTitle";
import Status from "./Status";
import StatusTitle from "./StatusTitle";
import { getErrorCodeMessage } from "./utils";
-import { isPlVisualDataset } from "../DatasetRedirect/getDatasets";
interface INodeState {
plugin?: Plugin;
@@ -55,7 +56,7 @@ const NodeDetails: React.FC = () => {
const drawerState = useTypedSelector((state) => state.drawers);
const { plugin, instanceParameters, pluginParameters } = nodeState;
- const [isExpanded, setIsExpanded] = React.useState(true);
+ const [isExpanded, setIsExpanded] = React.useState(false);
const [isErrorExpanded, setisErrorExpanded] = React.useState(false);
React.useEffect(() => {
@@ -253,6 +254,14 @@ const NodeDetails: React.FC = () => {
)}
+
+
+
+
+
+
+
+
{
// Jennings: hastily adding an extra button here.
// IMO the Node Details pane should be cleaned up.
diff --git a/src/components/Pacs/pfdcmClient.tsx b/src/components/Pacs/pfdcmClient.tsx
index d678cde56..4ab919136 100644
--- a/src/components/Pacs/pfdcmClient.tsx
+++ b/src/components/Pacs/pfdcmClient.tsx
@@ -303,6 +303,7 @@ class PfdcmClient {
!imagestatus.push &&
images.pushed === 0 &&
requestedFiles &&
+ requestedFiles > 0 &&
images.packed === +requestedFiles;
newImageStatus[1].icon = showPushDetails && ;
@@ -327,6 +328,7 @@ class PfdcmClient {
const showRegisterDetails =
requestedFiles &&
+ requestedFiles > 0 &&
images.pushed === +requestedFiles &&
!imagestatus.register &&
images.registered === 0;
diff --git a/src/store/pluginInstance/reducer.ts b/src/store/pluginInstance/reducer.ts
index cd2b4ff46..efba5da93 100644
--- a/src/store/pluginInstance/reducer.ts
+++ b/src/store/pluginInstance/reducer.ts
@@ -96,7 +96,8 @@ const reducer: Reducer = (
},
selectedPlugin: action.payload,
};
- } else return { ...state };
+ }
+ return { ...state };
}
case PluginInstanceTypes.ADD_NODE_REQUEST: {
@@ -118,16 +119,16 @@ const reducer: Reducer = (
},
loadingAddNode: false,
};
- } else
- return {
- ...state,
- pluginInstances: {
- data: action.payload,
- error: "",
- loading: false,
- },
- loadingAddNode: false,
- };
+ }
+ return {
+ ...state,
+ pluginInstances: {
+ data: action.payload,
+ error: "",
+ loading: false,
+ },
+ loadingAddNode: false,
+ };
}
case PluginInstanceTypes.ADD_SPLIT_NODES_SUCCESS: {
@@ -145,7 +146,8 @@ const reducer: Reducer = (
loading: false,
},
};
- } else return state;
+ }
+ return state;
}
case PluginInstanceTypes.DELETE_NODE_SUCCESS: {