diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeViewModal.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeViewModal.jsx
index 6eb01a9a0e..96ff7809fa 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeViewModal.jsx
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeViewModal.jsx
@@ -41,7 +41,7 @@ function getNodeType(node) {
}
}
-function NodeViewModal({ i18n }) {
+function NodeViewModal({ i18n, readOnly }) {
const dispatch = useContext(WorkflowDispatchContext);
const { nodeToView } = useContext(WorkflowStateContext);
const { unifiedJobTemplate } = nodeToView;
@@ -136,15 +136,20 @@ function NodeViewModal({ i18n }) {
title={unifiedJobTemplate.name}
aria-label={i18n._(t`Workflow node view modal`)}
onClose={() => dispatch({ type: 'SET_NODE_TO_VIEW', value: null })}
- actions={[
- ,
- ]}
+ actions={
+ readOnly
+ ? []
+ : [
+ ,
+ ]
+ }
>
{Content}
diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeViewModal.test.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeViewModal.test.jsx
index 3d968974d5..024517bfe3 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeViewModal.test.jsx
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeViewModal.test.jsx
@@ -168,6 +168,40 @@ describe('NodeViewModal', () => {
wrapper.unmount();
jest.clearAllMocks();
});
+
+ test('edit button shoud be shown when readOnly prop is false', async () => {
+ let wrapper;
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+
+
+
+
+ );
+ });
+ waitForLoaded(wrapper);
+ expect(wrapper.find('Button#node-view-edit-button').length).toBe(1);
+ wrapper.unmount();
+ jest.clearAllMocks();
+ });
+
+ test('edit button shoud be hidden when readOnly prop is true', async () => {
+ let wrapper;
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+
+
+
+
+ );
+ });
+ waitForLoaded(wrapper);
+ expect(wrapper.find('Button#node-view-edit-button').length).toBe(0);
+ wrapper.unmount();
+ jest.clearAllMocks();
+ });
});
describe('Project node', () => {
diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Visualizer.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Visualizer.jsx
index f3be938dcb..311d95cec9 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Visualizer.jsx
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Visualizer.jsx
@@ -424,6 +424,8 @@ function Visualizer({ template, i18n }) {
);
}
+ const readOnly = !template?.summary_fields?.user_capabilities?.edit;
+
return (
@@ -433,13 +435,12 @@ function Visualizer({ template, i18n }) {
onSave={handleVisualizerSave}
hasUnsavedChanges={unsavedChanges}
template={template}
+ readOnly={readOnly}
/>
{links.length > 0 ? (
-
+
) : (
-
+
)}
{nodeToDelete && }
@@ -459,7 +460,7 @@ function Visualizer({ template, i18n }) {
/>
)}
{showDeleteAllNodesModal && }
- {nodeToView && }
+ {nodeToView && }
);
diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerGraph.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerGraph.jsx
index b886ddfe20..8f07f037b9 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerGraph.jsx
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerGraph.jsx
@@ -271,6 +271,7 @@ function VisualizerGraph({ i18n, readOnly }) {
key="start"
showActionTooltip={!readOnly}
onUpdateHelpText={setHelpText}
+ readOnly={readOnly}
/>,
links.map(link => {
if (
diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerStartScreen.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerStartScreen.jsx
index 90b30ddbce..33eb1daa27 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerStartScreen.jsx
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerStartScreen.jsx
@@ -30,23 +30,31 @@ const StartPanelWrapper = styled.div`
justify-content: center;
`;
-function VisualizerStartScreen({ i18n }) {
+function VisualizerStartScreen({ i18n, readOnly }) {
const dispatch = useContext(WorkflowDispatchContext);
return (
- {i18n._(t`Please click the Start button to begin.`)}
-
+ {readOnly ? (
+
+ {i18n._(t`This workflow does not have any nodes configured.`)}
+
+ ) : (
+ <>
+ {i18n._(t`Please click the Start button to begin.`)}
+
+ >
+ )}
diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerStartScreen.test.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerStartScreen.test.jsx
index 7ba99dd11a..4bb7b2ddc7 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerStartScreen.test.jsx
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerStartScreen.test.jsx
@@ -19,4 +19,12 @@ describe('VisualizerStartScreen', () => {
sourceNodeId: 1,
});
});
+ test('start button hidden in read-only mode', () => {
+ const wrapper = mountWithContexts(
+
+
+
+ );
+ expect(wrapper.find('Button').length).toBe(0);
+ });
});
diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerToolbar.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerToolbar.jsx
index 0857313036..26c80f338f 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerToolbar.jsx
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerToolbar.jsx
@@ -56,14 +56,13 @@ function VisualizerToolbar({
onSave,
template,
hasUnsavedChanges,
+ readOnly,
}) {
const dispatch = useContext(WorkflowDispatchContext);
const { nodes, showLegend, showTools } = useContext(WorkflowStateContext);
const totalNodes = nodes.reduce((n, node) => n + !node.isDeleted, 0) - 1;
- const canLaunch =
- template.summary_fields?.user_capabilities?.start && !hasUnsavedChanges;
return (