diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ApplyButton/ApplyButton.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ApplyButton/ApplyButton.tsx
index 66493b496f..3678e1335a 100644
--- a/frontend/src/component/changeRequest/ChangeRequestOverview/ApplyButton/ApplyButton.tsx
+++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ApplyButton/ApplyButton.tsx
@@ -2,34 +2,45 @@ import { FC } from 'react';
import CheckBox from '@mui/icons-material/Check';
import Today from '@mui/icons-material/Today';
-import { MultiActionButton } from '../MultiActionButton/MultiActionButton';
import { APPLY_CHANGE_REQUEST } from 'component/providers/AccessProvider/permissions';
+import { MultiActionButton } from 'component/common/MultiActionButton/MultiActionButton';
+import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest';
+import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
export const ApplyButton: FC<{
disabled: boolean;
onSchedule: () => void;
onApply: () => void;
variant?: 'create' | 'update';
-}> = ({ disabled, onSchedule, onApply, variant = 'create', children }) => (
- ,
- },
- {
- label:
- variant === 'create'
- ? 'Schedule changes'
- : 'Update schedule',
- onSelect: onSchedule,
- icon: ,
- },
- ]}
- >
- {children}
-
-);
+}> = ({ disabled, onSchedule, onApply, variant = 'create', children }) => {
+ const projectId = useRequiredPathParam('projectId');
+ const id = useRequiredPathParam('id');
+ const { data } = useChangeRequest(projectId, id);
+
+ return (
+ ,
+ },
+ {
+ label:
+ variant === 'create'
+ ? 'Schedule changes'
+ : 'Update schedule',
+ onSelect: onSchedule,
+ icon: ,
+ },
+ ]}
+ environmentId={data?.environment}
+ projectId={projectId}
+ ariaLabel='apply or schedule changes'
+ >
+ {children}
+
+ );
+};
diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.test.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.test.tsx
index 7398f39a02..e7f0de932d 100644
--- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.test.tsx
+++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.test.tsx
@@ -126,11 +126,17 @@ const changeRequestConfig = () =>
'get',
);
-const setupChangeRequest = (featureName: string, state: ChangeRequestState) => {
- pendingChangeRequest(mockChangeRequest(featureName, state));
- changeRequest(mockChangeRequest(featureName, state));
+const setupChangeRequest = (
+ featureName: string,
+ state: ChangeRequestState,
+ overrides: Partial> = {},
+) => {
+ pendingChangeRequest({
+ ...mockChangeRequest(featureName, state),
+ ...overrides,
+ });
+ changeRequest({ ...mockChangeRequest(featureName, state), ...overrides });
};
-
const uiConfig = () => {
testServerRoute(server, '/api/admin/ui-config', {
versionInfo: {
@@ -249,6 +255,44 @@ test('should show a reschedule dialog when change request is scheduled and updat
await screen.findByRole('dialog', { name: 'Update schedule' });
});
+test('should be allowed to apply your own change request if it is approved', async () => {
+ setupChangeRequest(featureName, 'Approved', {
+ createdBy: {
+ id: 17,
+ imageUrl:
+ 'https://gravatar.com/avatar/21232f297a57a5a743894a0e4a801fc3?size=42&default=retro',
+ },
+ });
+
+ render(, {
+ route: '/projects/default/change-requests/1',
+ permissions: [
+ {
+ permission: APPLY_CHANGE_REQUEST,
+ project: 'default',
+ environment: 'production',
+ },
+ ],
+ });
+
+ const applyOrScheduleButton = await screen.findByText(
+ 'Apply or schedule changes',
+ );
+ await waitFor(() => expect(applyOrScheduleButton).toBeEnabled(), {
+ timeout: 3000,
+ });
+
+ fireEvent.click(applyOrScheduleButton);
+
+ const scheduleChangesButton = await screen.findByRole('menuitem', {
+ name: 'Schedule changes',
+ });
+
+ fireEvent.click(scheduleChangesButton);
+
+ await screen.findByRole('dialog', { name: 'Schedule changes' });
+});
+
test('should show an apply dialog when change request is scheduled and apply is selected', async () => {
setupChangeRequest(featureName, 'Scheduled');
diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ReviewButton/ReviewButton.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ReviewButton/ReviewButton.tsx
index 7091edcf1d..5bff480a47 100644
--- a/frontend/src/component/changeRequest/ChangeRequestOverview/ReviewButton/ReviewButton.tsx
+++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ReviewButton/ReviewButton.tsx
@@ -1,31 +1,49 @@
-import { FC } from 'react';
+import { FC, useContext } from 'react';
import CheckBox from '@mui/icons-material/Check';
import Clear from '@mui/icons-material/Clear';
-import { MultiActionButton } from '../MultiActionButton/MultiActionButton';
+import { MultiActionButton } from 'component/common/MultiActionButton/MultiActionButton';
import { APPROVE_CHANGE_REQUEST } from 'component/providers/AccessProvider/permissions';
+import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
+import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
+import AccessContext from 'contexts/AccessContext';
+import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest';
export const ReviewButton: FC<{
disabled: boolean;
onReject: () => void;
onApprove: () => void;
-}> = ({ disabled, onReject, onApprove, children }) => (
- ,
- },
- {
- label: 'Reject',
- onSelect: onReject,
- icon: ,
- },
- ]}
- >
- {children}
-
-);
+}> = ({ disabled, onReject, onApprove, children }) => {
+ const { isAdmin } = useContext(AccessContext);
+ const projectId = useRequiredPathParam('projectId');
+ const id = useRequiredPathParam('id');
+ const { user } = useAuthUser();
+ const { data } = useChangeRequest(projectId, id);
+
+ const approverIsCreator = data?.createdBy.id === user?.id;
+ const disableApprove = disabled || (approverIsCreator && !isAdmin);
+
+ return (
+ ,
+ },
+ {
+ label: 'Reject',
+ onSelect: onReject,
+ icon: ,
+ },
+ ]}
+ environmentId={data?.environment}
+ projectId={projectId}
+ ariaLabel='review or reject changes'
+ >
+ {children}
+
+ );
+};
diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/MultiActionButton/MultiActionButton.tsx b/frontend/src/component/common/MultiActionButton/MultiActionButton.tsx
similarity index 85%
rename from frontend/src/component/changeRequest/ChangeRequestOverview/MultiActionButton/MultiActionButton.tsx
rename to frontend/src/component/common/MultiActionButton/MultiActionButton.tsx
index cc0c663e6b..c212d68fef 100644
--- a/frontend/src/component/changeRequest/ChangeRequestOverview/MultiActionButton/MultiActionButton.tsx
+++ b/frontend/src/component/common/MultiActionButton/MultiActionButton.tsx
@@ -15,8 +15,6 @@ import {
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
-import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
-import AccessContext from 'contexts/AccessContext';
type Action = {
label: string;
@@ -28,13 +26,18 @@ export const MultiActionButton: FC<{
disabled: boolean;
actions: Action[];
permission: string;
-}> = ({ disabled, children, actions, permission }) => {
- const { isAdmin } = useContext(AccessContext);
- const projectId = useRequiredPathParam('projectId');
- const id = useRequiredPathParam('id');
- const { user } = useAuthUser();
- const { data } = useChangeRequest(projectId, id);
-
+ projectId?: string;
+ environmentId?: string;
+ ariaLabel?: string;
+}> = ({
+ disabled,
+ children,
+ actions,
+ permission,
+ projectId,
+ ariaLabel,
+ environmentId,
+}) => {
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);
@@ -57,19 +60,17 @@ export const MultiActionButton: FC<{
}
permission={permission}
projectId={projectId}
- environmentId={data?.environment}
+ environmentId={environmentId}
>
{children}