diff --git a/awx/ui/src/screens/WorkflowApproval/WorkflowApproval.js b/awx/ui/src/screens/WorkflowApproval/WorkflowApproval.js index 07de41eb43..ebd0b3ef04 100644 --- a/awx/ui/src/screens/WorkflowApproval/WorkflowApproval.js +++ b/awx/ui/src/screens/WorkflowApproval/WorkflowApproval.js @@ -91,7 +91,10 @@ function WorkflowApproval({ setBreadcrumb }) { /> {workflowApproval && ( - + )} diff --git a/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalDetail/WorkflowApprovalDetail.js b/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalDetail/WorkflowApprovalDetail.js index 7a68fd9986..75dd963b3a 100644 --- a/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalDetail/WorkflowApprovalDetail.js +++ b/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalDetail/WorkflowApprovalDetail.js @@ -1,5 +1,4 @@ import React, { useCallback, useEffect } from 'react'; - import { t } from '@lingui/macro'; import { Link, useHistory, useParams } from 'react-router-dom'; import styled from 'styled-components'; @@ -27,6 +26,7 @@ import useRequest, { useDismissableError } from 'hooks/useRequest'; import { WorkflowApproval } from 'types'; import StatusLabel from 'components/StatusLabel'; import JobCancelButton from 'components/JobCancelButton'; +import useToast, { AlertVariant } from 'hooks/useToast'; import WorkflowApprovalButton from '../shared/WorkflowApprovalButton'; import WorkflowDenyButton from '../shared/WorkflowDenyButton'; import { @@ -48,9 +48,11 @@ const WFDetailList = styled(DetailList)` padding: 0px var(--pf-global--spacer--lg); `; -function WorkflowApprovalDetail({ workflowApproval }) { +function WorkflowApprovalDetail({ workflowApproval, fetchWorkflowApproval }) { const { id: workflowApprovalId } = useParams(); const history = useHistory(); + const { addToast, Toast, toastProps } = useToast(); + const { request: deleteWorkflowApproval, isLoading: isDeleteLoading, @@ -102,6 +104,18 @@ function WorkflowApprovalDetail({ workflowApproval }) { fetchWorkflowJob(); }, [fetchWorkflowJob]); + const handleToast = useCallback( + (id, title) => { + addToast({ + id, + title, + variant: AlertVariant.success, + hasTimeout: true, + }); + fetchWorkflowApproval(); + }, + [addToast, fetchWorkflowApproval] + ); const sourceWorkflowJob = workflowApproval?.summary_fields?.source_workflow_job; @@ -116,7 +130,9 @@ function WorkflowApprovalDetail({ workflowApproval }) { if (fetchWorkflowJobError) { return ; } - + const showDeleteButton = + workflowApproval.status !== 'pending' && + workflowApproval.summary_fields?.user_capabilities?.delete; return ( @@ -289,31 +305,35 @@ function WorkflowApprovalDetail({ workflowApproval }) { )} - {workflowApproval.status !== 'pending' && - workflowApproval.summary_fields?.user_capabilities?.delete && ( - - {t`Delete`} - - )} + {showDeleteButton && ( + + {t`Delete`} + + )} {deleteError && ( )} + ); } diff --git a/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalDetail/WorkflowApprovalDetail.test.js b/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalDetail/WorkflowApprovalDetail.test.js index 6b46536a38..49aebfe5ec 100644 --- a/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalDetail/WorkflowApprovalDetail.test.js +++ b/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalDetail/WorkflowApprovalDetail.test.js @@ -515,7 +515,10 @@ describe('', () => { let wrapper; await act(async () => { wrapper = mountWithContexts( - + ); }); waitForElement(wrapper, 'WorkflowApprovalDetail', (el) => el.length > 0); @@ -548,7 +551,10 @@ describe('', () => { let wrapper; await act(async () => { wrapper = mountWithContexts( - + ); }); waitForElement(wrapper, 'WorkflowApprovalDetail', (el) => el.length > 0); diff --git a/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalList/WorkflowApprovalListItem.js b/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalList/WorkflowApprovalListItem.js index b72f5fb446..c369747c6a 100644 --- a/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalList/WorkflowApprovalListItem.js +++ b/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalList/WorkflowApprovalListItem.js @@ -100,7 +100,10 @@ function WorkflowApprovalListItem({ 0 || workflowApproval.status === 'canceled'; const { error: approveApprovalError, request: approveWorkflowApprovals } = useRequest( useCallback(async () => WorkflowApprovalsAPI.approve(id), [id]), @@ -20,6 +26,7 @@ function WorkflowApprovalButton({ isDetailView, workflowApproval }) { const handleApprove = async () => { await approveWorkflowApprovals(); + onHandleToast(workflowApproval.id, t`Successfully Approved`); }; const { error: approveError, dismissError: dismissApproveError } = diff --git a/awx/ui/src/screens/WorkflowApproval/shared/WorkflowApprovalButton.test.js b/awx/ui/src/screens/WorkflowApproval/shared/WorkflowApprovalButton.test.js index f1f7989ffc..3cc2564c0d 100644 --- a/awx/ui/src/screens/WorkflowApproval/shared/WorkflowApprovalButton.test.js +++ b/awx/ui/src/screens/WorkflowApproval/shared/WorkflowApprovalButton.test.js @@ -18,7 +18,10 @@ describe(' shallow mount', () => { test('initially render successfully', () => { wrapper = shallowWithContexts( - + ); expect(wrapper.find('WorkflowApprovalButton')).toHaveLength(1); @@ -39,7 +42,10 @@ describe(', full mount', () => { test('should be disabled', () => { wrapper = mountWithContexts( - + ); expect(wrapper.find(approveButton)).toHaveLength(1); expect(wrapper.find(approveButton).prop('isDisabled')).toBe(true); @@ -47,7 +53,10 @@ describe(', full mount', () => { test('should handle approve', async () => { act(() => { wrapper = mountWithContexts( - + ); }); await act(() => wrapper.find(approveButton).prop('onClick')()); diff --git a/awx/ui/src/screens/WorkflowApproval/shared/WorkflowDenyButton.js b/awx/ui/src/screens/WorkflowApproval/shared/WorkflowDenyButton.js index b0546f6726..3399ec624d 100644 --- a/awx/ui/src/screens/WorkflowApproval/shared/WorkflowDenyButton.js +++ b/awx/ui/src/screens/WorkflowApproval/shared/WorkflowDenyButton.js @@ -9,10 +9,12 @@ import AlertModal from 'components/AlertModal'; import ErrorDetail from 'components/ErrorDetail'; import { getStatus } from './WorkflowApprovalUtils'; -function WorkflowDenyButton({ isDetailView, workflowApproval }) { - const hasBeenActedOn = workflowApproval.status === 'failed'; - const { id } = workflowApproval; +function WorkflowDenyButton({ isDetailView, workflowApproval, onHandleToast }) { + const hasBeenActedOn = + Object.keys(workflowApproval.summary_fields.approved_or_denied_by || {}) + .length > 0 || workflowApproval.status === 'canceled'; + const { id } = workflowApproval; const { error: denyApprovalError, request: denyWorkflowApprovals } = useRequest( useCallback(async () => WorkflowApprovalsAPI.deny(id), [id]), @@ -21,6 +23,7 @@ function WorkflowDenyButton({ isDetailView, workflowApproval }) { const handleDeny = async () => { await denyWorkflowApprovals(); + onHandleToast(workflowApproval.id, t`Successfully Denied`); }; const { error: denyError, dismissError: dismissDenyError } = diff --git a/awx/ui/src/screens/WorkflowApproval/shared/WorkflowDenyButton.test.js b/awx/ui/src/screens/WorkflowApproval/shared/WorkflowDenyButton.test.js index ebfef860c4..af00c5403a 100644 --- a/awx/ui/src/screens/WorkflowApproval/shared/WorkflowDenyButton.test.js +++ b/awx/ui/src/screens/WorkflowApproval/shared/WorkflowDenyButton.test.js @@ -39,7 +39,10 @@ describe(', full mount', () => { test('should be disabled', () => { wrapper = mountWithContexts( - + ); expect(wrapper.find(denyButton)).toHaveLength(1); expect(wrapper.find(denyButton).prop('isDisabled')).toBe(true); @@ -48,7 +51,10 @@ describe(', full mount', () => { test('should handle deny', async () => { act(() => { wrapper = mountWithContexts( - + ); }); await act(() => wrapper.find(denyButton).prop('onClick')()); @@ -70,7 +76,10 @@ describe(', full mount', () => { ); act(() => { wrapper = mountWithContexts( - + ); }); await act(() => wrapper.find(denyButton).prop('onClick')());