Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: schedule changes dialog #5285

Merged
merged 11 commits into from
Nov 7, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
ChangeRequestApplyScheduledDialogue,
ChangeRequestRejectScheduledDialogue,
} from './ChangeRequestScheduledDialogs/changeRequestScheduledDialogs';
import { ScheduleChangeRequestDialog } from './ChangeRequestScheduledDialogs/ScheduleChangeRequestDialog';

const StyledAsideBox = styled(Box)(({ theme }) => ({
width: '30%',
Expand Down Expand Up @@ -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] =
Expand Down Expand Up @@ -111,6 +114,7 @@ export const ChangeRequestOverview: FC = () => {
await changeState(projectId, Number(id), {
state: 'Applied',
});
setShowApplyScheduledDialog(false);
refetchChangeRequest();
refetchChangeRequestOpen();
setToastData({
Expand All @@ -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);
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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
</ApplyButton>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -421,6 +443,28 @@ export const ChangeRequestOverview: FC = () => {
condition={scheduleChangeRequests}
show={
<>
<ScheduleChangeRequestDialog
open={showScheduleChangesDialog}
onConfirm={onScheduleChangeRequest}
onClose={onScheduleChangeAbort}
disabled={!allowChangeRequestActions || loading}
projectId={projectId}
environment={changeRequest.environment}
primaryButtonText={
changeRequest?.schedule?.scheduledAt
? 'Update scheduled time'
: 'Schedule changes'
}
title={
changeRequest?.schedule?.scheduledAt
? 'Update schedule'
: 'Schedule changes'
}
scheduledAt={
changeRequest?.schedule?.scheduledAt ||
undefined
}
/>
<ChangeRequestApplyScheduledDialogue
open={showApplyScheduledDialog}
onConfirm={onApplyChanges}
Expand All @@ -434,7 +478,7 @@ export const ChangeRequestOverview: FC = () => {
/>
<ChangeRequestRejectScheduledDialogue
open={showRejectScheduledDialog}
onConfirm={onCancelChanges}
onConfirm={onReject}
onClose={onRejectScheduledAbort}
scheduledTime={
changeRequest?.schedule?.scheduledAt
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { FC, ReactElement } from 'react';
import { Alert, styled, Typography } from '@mui/material';
import { Dialogue } from '../../../common/Dialogue/Dialogue';
import { Dialogue } from 'component/common/Dialogue/Dialogue';

export interface ChangeRequestScheduleDialogueProps {
export interface ChangeRequestScheduledDialogProps {
title: string;
primaryButtonText: string;
open: boolean;
Expand All @@ -12,8 +12,6 @@ export interface ChangeRequestScheduleDialogueProps {
message: string;
permissionButton?: ReactElement;
disabled?: boolean;
projectId?: string;
environment?: string;
}

const StyledAlert = styled(Alert)(({ theme }) => ({
Expand All @@ -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,
Expand All @@ -33,6 +31,7 @@ export const ChangeRequestScheduledDialogue: FC<
primaryButtonText,
message,
scheduledTime,
permissionButton,
}) => {
if (!scheduledTime) return null;

Expand All @@ -44,6 +43,7 @@ export const ChangeRequestScheduledDialogue: FC<
open={open}
onClose={onClose}
onClick={() => onConfirm()}
permissionButton={permissionButton}
fullWidth
>
<StyledAlert icon={false}>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ScheduleChangeRequestDialogProps> =
({
open,
onConfirm,
onClose,
title,
primaryButtonText,
projectId,
environment,
disabled,
scheduledAt,
}) => {
const [selectedDate, setSelectedDate] = useState(
scheduledAt ? new Date(scheduledAt) : new Date(),
);
const [error, setError] = useState<string | undefined>(undefined);

const timezone = getBrowserTimezoneInHumanReadableUTCOffset();

return (
<Dialogue
title={title}
primaryButtonText={primaryButtonText}
secondaryButtonText='Cancel'
open={open}
onClose={onClose}
onClick={() => onConfirm(selectedDate)}
permissionButton={
<PermissionButton
variant='contained'
onClick={() => onConfirm(selectedDate)}
projectId={projectId}
permission={APPLY_CHANGE_REQUEST}
environmentId={environment}
disabled={disabled}
>
{primaryButtonText}
</PermissionButton>
}
fullWidth
>
<Alert
severity={'info'}
sx={{ mb: (theme) => theme.spacing(2) }}
>
The time shown below is based on your browser's time zone.
</Alert>
<Typography
variant={'body1'}
sx={{ mb: (theme) => 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.
</Typography>
<StyledContainer>
<DateTimePicker
label='Date'
value={selectedDate}
onChange={(date) => {
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
/>
<Typography variant={'body2'}>
Your browser's time zone is {timezone}
</Typography>
</StyledContainer>
</Dialogue>
);
};
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
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';
const title = 'Apply changes';
const primaryButtonText = 'Apply changes now';

return (
<ChangeRequestScheduledDialogue
<ChangeRequestScheduledDialog
message={message}
title={title}
primaryButtonText={primaryButtonText}
Expand All @@ -45,7 +42,7 @@ export const ChangeRequestApplyScheduledDialogue: FC<

export const ChangeRequestRejectScheduledDialogue: FC<
Omit<
ChangeRequestScheduleDialogueProps,
ChangeRequestScheduledDialogProps,
'message' | 'title' | 'primaryButtonText'
>
> = ({ ...rest }) => {
Expand All @@ -55,7 +52,7 @@ export const ChangeRequestRejectScheduledDialogue: FC<
const primaryButtonText = 'Reject changes';

return (
<ChangeRequestScheduledDialogue
<ChangeRequestScheduledDialog
message={message}
title={title}
primaryButtonText={primaryButtonText}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,12 @@ export const useChangeRequestApi = () => {
state:
| 'Approved'
| 'Applied'
| 'Scheduled'
| 'Cancelled'
| 'In review'
| 'Rejected';
comment?: string;
scheduledAt?: string;
},
) => {
trackEvent('change_request', {
Expand Down
Loading