diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx
index c1bd1a266d..43aac2d721 100644
--- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx
+++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx
@@ -30,6 +30,7 @@ import {
ChangeRequestApplyScheduledDialogue,
ChangeRequestRejectScheduledDialogue,
} from './ChangeRequestScheduledDialogs/changeRequestScheduledDialogs';
+import { ScheduleChangeRequestDialog } from './ChangeRequestScheduledDialogs/ScheduleChangeRequestDialog';
const StyledAsideBox = styled(Box)(({ theme }) => ({
width: '30%',
@@ -77,6 +78,8 @@ export const ChangeRequestOverview: FC = () => {
const projectId = useRequiredPathParam('projectId');
const [showCancelDialog, setShowCancelDialog] = useState(false);
const [showRejectDialog, setShowRejectDialog] = useState(false);
+ const [showScheduleChangesDialog, setShowScheduleChangeDialog] =
+ useState(false);
const [showApplyScheduledDialog, setShowApplyScheduledDialog] =
useState(false);
const [showRejectScheduledDialog, setShowRejectScheduledDialog] =
@@ -111,6 +114,7 @@ export const ChangeRequestOverview: FC = () => {
await changeState(projectId, Number(id), {
state: 'Applied',
});
+ setShowApplyScheduledDialog(false);
refetchChangeRequest();
refetchChangeRequestOpen();
setToastData({
@@ -123,6 +127,25 @@ export const ChangeRequestOverview: FC = () => {
}
};
+ const onScheduleChangeRequest = async (scheduledDate: Date) => {
+ try {
+ await changeState(projectId, Number(id), {
+ state: 'Scheduled',
+ scheduledAt: scheduledDate.toISOString(),
+ });
+ setShowScheduleChangeDialog(false);
+ refetchChangeRequest();
+ refetchChangeRequestOpen();
+ setToastData({
+ type: 'success',
+ title: 'Success',
+ text: 'Changes scheduled',
+ });
+ } catch (error: unknown) {
+ setToastApiError(formatUnknownError(error));
+ }
+ };
+
const onAddComment = async () => {
try {
await addComment(projectId, id, commentText);
@@ -196,6 +219,7 @@ export const ChangeRequestOverview: FC = () => {
const onCancelAbort = () => setShowCancelDialog(false);
const onCancelReject = () => setShowRejectDialog(false);
const onApplyScheduledAbort = () => setShowApplyScheduledDialog(false);
+ const onScheduleChangeAbort = () => setShowApplyScheduledDialog(false);
const onRejectScheduledAbort = () => setShowRejectScheduledDialog(false);
const isSelfReview =
@@ -293,11 +317,11 @@ export const ChangeRequestOverview: FC = () => {
!allowChangeRequestActions ||
loading
}
- onSchedule={() => {
- console.log(
- 'I would schedule changes now',
- );
- }}
+ onSchedule={() =>
+ setShowScheduleChangeDialog(
+ true,
+ )
+ }
>
Apply or schedule changes
@@ -339,11 +363,9 @@ export const ChangeRequestOverview: FC = () => {
!allowChangeRequestActions ||
loading
}
- onSchedule={() => {
- console.log(
- 'I would schedule changes now',
- );
- }}
+ onSchedule={() =>
+ setShowScheduleChangeDialog(true)
+ }
variant={'update'}
>
Apply or schedule changes
@@ -421,6 +443,28 @@ export const ChangeRequestOverview: FC = () => {
condition={scheduleChangeRequests}
show={
<>
+
{
/>
({
@@ -23,8 +21,8 @@ const StyledAlert = styled(Alert)(({ theme }) => ({
borderColor: `${theme.palette.neutral.light}!important`,
}));
-export const ChangeRequestScheduledDialogue: FC<
- ChangeRequestScheduleDialogueProps
+export const ChangeRequestScheduledDialog: FC<
+ ChangeRequestScheduledDialogProps
> = ({
open,
onConfirm,
@@ -33,6 +31,7 @@ export const ChangeRequestScheduledDialogue: FC<
primaryButtonText,
message,
scheduledTime,
+ permissionButton,
}) => {
if (!scheduledTime) return null;
@@ -44,6 +43,7 @@ export const ChangeRequestScheduledDialogue: FC<
open={open}
onClose={onClose}
onClick={() => onConfirm()}
+ permissionButton={permissionButton}
fullWidth
>
diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestScheduledDialogs/ScheduleChangeRequestDialog.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestScheduledDialogs/ScheduleChangeRequestDialog.tsx
new file mode 100644
index 0000000000..039acb9467
--- /dev/null
+++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestScheduledDialogs/ScheduleChangeRequestDialog.tsx
@@ -0,0 +1,107 @@
+import { FC, useState } from 'react';
+import { Alert, Box, styled, Typography } from '@mui/material';
+import { Dialogue } from 'component/common/Dialogue/Dialogue';
+import { APPLY_CHANGE_REQUEST } from 'component/providers/AccessProvider/permissions';
+import PermissionButton from 'component/common/PermissionButton/PermissionButton';
+import { DateTimePicker } from 'component/common/DateTimePicker/DateTimePicker';
+import { getBrowserTimezoneInHumanReadableUTCOffset } from '../ChangeRequestReviewStatus/utils';
+
+export interface ScheduleChangeRequestDialogProps {
+ title: string;
+ primaryButtonText: string;
+ open: boolean;
+ onConfirm: (selectedDate: Date) => void;
+ onClose: () => void;
+ projectId: string;
+ environment: string;
+ disabled?: boolean;
+ scheduledAt?: string;
+}
+
+const StyledContainer = styled(Box)(({ theme }) => ({
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: theme.spacing(2),
+}));
+
+export const ScheduleChangeRequestDialog: FC =
+ ({
+ open,
+ onConfirm,
+ onClose,
+ title,
+ primaryButtonText,
+ projectId,
+ environment,
+ disabled,
+ scheduledAt,
+ }) => {
+ const [selectedDate, setSelectedDate] = useState(
+ scheduledAt ? new Date(scheduledAt) : new Date(),
+ );
+ const [error, setError] = useState(undefined);
+
+ const timezone = getBrowserTimezoneInHumanReadableUTCOffset();
+
+ return (
+ onConfirm(selectedDate)}
+ permissionButton={
+ onConfirm(selectedDate)}
+ projectId={projectId}
+ permission={APPLY_CHANGE_REQUEST}
+ environmentId={environment}
+ disabled={disabled}
+ >
+ {primaryButtonText}
+
+ }
+ fullWidth
+ >
+ theme.spacing(2) }}
+ >
+ The time shown below is based on your browser's time zone.
+
+ theme.spacing(4) }}
+ >
+ Select the date and time when these changes should be
+ applied. If you change your mind later, you can reschedule
+ the changes or apply the immediately.
+
+
+ {
+ setError(undefined);
+ if (date < new Date()) {
+ setError(
+ `The time you provided (${date.toLocaleString()}) is not valid because it's in the past. Please select a time in the future.`,
+ );
+ }
+ setSelectedDate(date);
+ }}
+ min={new Date()}
+ error={Boolean(error)}
+ errorText={error}
+ required
+ />
+
+ Your browser's time zone is {timezone}
+
+
+
+ );
+ };
diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestScheduledDialogs/changeRequestScheduledDialogs.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestScheduledDialogs/changeRequestScheduledDialogs.tsx
index 3cfd98bb73..466fe507cf 100644
--- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestScheduledDialogs/changeRequestScheduledDialogs.tsx
+++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestScheduledDialogs/changeRequestScheduledDialogs.tsx
@@ -1,19 +1,16 @@
-import { FC, useState } from 'react';
-import { TextField, Box, Alert, styled, Typography } from '@mui/material';
-import { Dialogue } from '../../../common/Dialogue/Dialogue';
-import { ConditionallyRender } from '../../../common/ConditionallyRender/ConditionallyRender';
+import { FC } from 'react';
import { APPLY_CHANGE_REQUEST } from '../../../providers/AccessProvider/permissions';
import PermissionButton from '../../../common/PermissionButton/PermissionButton';
import {
- ChangeRequestScheduledDialogue,
- ChangeRequestScheduleDialogueProps,
-} from './ChangeRequestScheduledDialogue';
+ ChangeRequestScheduledDialog,
+ ChangeRequestScheduledDialogProps,
+} from './ChangeRequestScheduledDialog';
export const ChangeRequestApplyScheduledDialogue: FC<
Omit<
- ChangeRequestScheduleDialogueProps,
+ ChangeRequestScheduledDialogProps,
'message' | 'title' | 'primaryButtonText' | 'permissionButton'
- >
+ > & { projectId: string; environment: string }
> = ({ projectId, environment, disabled, onConfirm, ...rest }) => {
const message =
'Applying the changes now means the scheduled time will be ignored';
@@ -21,7 +18,7 @@ export const ChangeRequestApplyScheduledDialogue: FC<
const primaryButtonText = 'Apply changes now';
return (
-
> = ({ ...rest }) => {
@@ -55,7 +52,7 @@ export const ChangeRequestRejectScheduledDialogue: FC<
const primaryButtonText = 'Reject changes';
return (
- {
state:
| 'Approved'
| 'Applied'
+ | 'Scheduled'
| 'Cancelled'
| 'In review'
| 'Rejected';
comment?: string;
+ scheduledAt?: string;
},
) => {
trackEvent('change_request', {