diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ApplyButton/ApplyButton.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ApplyButton/ApplyButton.tsx index d3262017bdce..66493b496f13 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ApplyButton/ApplyButton.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ApplyButton/ApplyButton.tsx @@ -9,7 +9,8 @@ export const ApplyButton: FC<{ disabled: boolean; onSchedule: () => void; onApply: () => void; -}> = ({ disabled, onSchedule, onApply, children }) => ( + variant?: 'create' | 'update'; +}> = ({ disabled, onSchedule, onApply, variant = 'create', children }) => ( , }, { - label: 'Schedule changes', + label: + variant === 'create' + ? 'Schedule changes' + : 'Update schedule', onSelect: onSchedule, icon: , }, diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx index dbe21e4ad6e2..7a0ef34dfbe6 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx @@ -26,7 +26,6 @@ import { ChangeRequestReviewers } from './ChangeRequestReviewers/ChangeRequestRe import { ChangeRequestRejectDialogue } from './ChangeRequestRejectDialog/ChangeRequestRejectDialog'; import { ApplyButton } from './ApplyButton/ApplyButton'; import { useUiFlag } from 'hooks/useUiFlag'; -import { scheduler } from 'timers/promises'; const StyledAsideBox = styled(Box)(({ theme }) => ({ width: '30%', @@ -85,7 +84,6 @@ export const ChangeRequestOverview: FC = () => { const { setToastData, setToastApiError } = useToast(); const { isChangeRequestConfiguredForReview } = useChangeRequestsEnabled(projectId); - const scheduleChangeRequests = useUiFlag('scheduledConfigurationChanges'); if (!changeRequest) { @@ -191,7 +189,7 @@ export const ChangeRequestOverview: FC = () => { changeRequest.state === 'In review' && !isAdmin; - const hasApprovedAlready = changeRequest.approvals.some( + const hasApprovedAlready = changeRequest.approvals?.some( (approval) => approval.createdBy.id === user?.id, ); @@ -312,6 +310,35 @@ export const ChangeRequestOverview: FC = () => { /> } /> + { + console.log( + 'I would show the apply now dialog', + ); + }} + disabled={ + !allowChangeRequestActions || + loading + } + onSchedule={() => { + console.log( + 'I would schedule changes now', + ); + }} + variant={'update'} + > + Apply or schedule changes + + } + /> + { variant='outlined' onClick={onCancel} > - Cancel changes + {changeRequest.schedule + ? 'Reject' + : 'Cancel'}{' '} + changes } /> diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.styles.ts b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.styles.ts index 535396453d3f..ff08ed468e71 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.styles.ts +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.styles.ts @@ -1,5 +1,5 @@ import { styled } from '@mui/material'; -import { Cancel, CheckCircle } from '@mui/icons-material'; +import { Cancel, CheckCircle, Schedule, Edit } from '@mui/icons-material'; import { Box, Typography, Divider } from '@mui/material'; const styledComponentPropCheck = () => (prop: string) => @@ -36,6 +36,18 @@ export const StyledSuccessIcon = styled(CheckCircle)(({ theme }) => ({ marginRight: theme.spacing(1), })); +export const StyledScheduledIcon = styled(Schedule)(({ theme }) => ({ + color: theme.palette.warning.main, + height: '35px', + width: '35px', + marginRight: theme.spacing(1), +})); +export const StyledEditIcon = styled(Edit)(({ theme }) => ({ + color: theme.palette.text.secondary, + height: '24px', + width: '24px', +})); + export const StyledOuterContainer = styled(Box)(({ theme }) => ({ display: 'flex', marginTop: theme.spacing(2), @@ -77,3 +89,10 @@ export const StyledReviewTitle = styled(Typography, { fontWeight: 'bold', color, })); + +export const StyledScheduledBox = styled(Box)({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + width: '100%', +}); diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.tsx index d27611fd894f..96864362f35a 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.tsx @@ -1,5 +1,5 @@ import React, { FC } from 'react'; -import { Box, Theme, Typography, useTheme } from '@mui/material'; +import { Box, IconButton, Theme, Typography, useTheme } from '@mui/material'; import { ReactComponent as ChangesAppliedIcon } from 'assets/icons/merge.svg'; import { StyledOuterContainer, @@ -11,6 +11,9 @@ import { StyledWarningIcon, StyledReviewTitle, StyledDivider, + StyledScheduledIcon, + StyledEditIcon, + StyledScheduledBox, } from './ChangeRequestReviewStatus.styles'; import { ChangeRequestState, @@ -21,7 +24,7 @@ interface ISuggestChangeReviewsStatusProps { changeRequest: IChangeRequest; } const resolveBorder = (state: ChangeRequestState, theme: Theme) => { - if (state === 'Approved') { + if (state === 'Approved' || state === 'Scheduled') { return `2px solid ${theme.palette.success.main}`; } @@ -109,6 +112,12 @@ const ResolveComponent = ({ changeRequest }: IResolveComponentProps) => { return ; } + if (state === 'Scheduled') { + return ( + + ); + } + return ; }; @@ -194,6 +203,69 @@ const Applied = () => { ); }; +interface IScheduledProps { + scheduledDate?: string; +} +const Scheduled = ({ scheduledDate }: IScheduledProps) => { + const theme = useTheme(); + + if (!scheduledDate) { + return null; + } + + const getBrowserTimezone = (): string => { + const offset = -new Date().getTimezoneOffset(); + const hours = Math.floor(Math.abs(offset) / 60); + const minutes = Math.abs(offset) % 60; + let sign = '+'; + if (offset < 0) { + sign = '-'; + } + + // Ensure that hours and minutes are two digits + const zeroPaddedHours = hours.toString().padStart(2, '0'); + const zeroPaddedMinutes = minutes.toString().padStart(2, '0'); + + return `UTC${sign}${zeroPaddedHours}:${zeroPaddedMinutes}`; + }; + + const timezone = getBrowserTimezone(); + + return ( + <> + + + + + Changes approved + + + One approving review from requested approvers + + + + + + + + + + + + Changes are scheduled to be applied on:{' '} + {new Date(scheduledDate).toLocaleString()} + + Your timezone is {timezone} + + + + + + + + ); +}; + const Cancelled = () => { const theme = useTheme(); diff --git a/frontend/src/component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge.tsx b/frontend/src/component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge.tsx index 35d4999ca939..67e6bca33294 100644 --- a/frontend/src/component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge.tsx @@ -1,7 +1,7 @@ import { VFC } from 'react'; import { ChangeRequestState } from '../changeRequest.types'; import { Badge } from 'component/common/Badge/Badge'; -import { Check, CircleOutlined, Close } from '@mui/icons-material'; +import { AccessTime, Check, CircleOutlined, Close } from '@mui/icons-material'; interface IChangeRequestStatusBadgeProps { state: ChangeRequestState; @@ -47,6 +47,12 @@ export const ChangeRequestStatusBadge: VFC = ({ Rejected ); + case 'Scheduled': + return ( + }> + Scheduled + + ); default: return ; } diff --git a/frontend/src/component/changeRequest/changeRequest.types.ts b/frontend/src/component/changeRequest/changeRequest.types.ts index e882d6d6a356..e79a42fddb0c 100644 --- a/frontend/src/component/changeRequest/changeRequest.types.ts +++ b/frontend/src/component/changeRequest/changeRequest.types.ts @@ -18,6 +18,12 @@ export interface IChangeRequest { rejections: IChangeRequestApproval[]; comments: IChangeRequestComment[]; conflict?: string; + schedule?: IChangeRequestSchedule; +} + +export interface IChangeRequestSchedule { + scheduledAt: string; + status: 'pending' | 'failed'; } export interface IChangeRequestEnvironmentConfig { @@ -67,6 +73,7 @@ export type ChangeRequestState = | 'Approved' | 'In review' | 'Applied' + | 'Scheduled' | 'Cancelled' | 'Rejected';