1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-01 13:47:27 +02:00

feat: move apply button to a separate section (#10324)

This commit is contained in:
David Leek 2025-07-08 09:23:48 +02:00 committed by GitHub
parent 59990fb503
commit 068ef585be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,7 +1,15 @@
import { Alert, Box, Button, styled, Typography } from '@mui/material'; import {
Alert,
Box,
Button,
styled,
Typography,
useTheme,
} from '@mui/material';
import { type FC, useContext, useState } from 'react'; import { type FC, useContext, useState } from 'react';
import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest'; import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest';
import { ChangeRequestHeader } from './ChangeRequestHeader/ChangeRequestHeader.tsx'; import { ChangeRequestHeader } from './ChangeRequestHeader/ChangeRequestHeader.tsx';
import { ReactComponent as ApprovedIcon } from 'assets/icons/merge.svg';
import { import {
ChangeRequestTimeline, ChangeRequestTimeline,
type ISuggestChangeTimelineProps, type ISuggestChangeTimelineProps,
@ -49,9 +57,19 @@ const StyledAsideBox = styled(Box)(({ theme }) => ({
})); }));
const StyledPaper = styled(Paper)(({ theme }) => ({ const StyledPaper = styled(Paper)(({ theme }) => ({
padding: theme.spacing(1, 2),
borderRadius: theme.shape.borderRadiusLarge,
}));
const StyledApplyPaper = styled(Paper)(({ theme }) => ({
padding: theme.spacing(1, 2),
borderRadius: theme.shape.borderRadiusLarge,
marginTop: theme.spacing(2),
}));
const StyledDiv = styled('div')(({ theme }) => ({
marginTop: theme.spacing(2), marginTop: theme.spacing(2),
width: '70%', width: '70%',
padding: theme.spacing(1, 2),
borderRadius: theme.shape.borderRadiusLarge, borderRadius: theme.shape.borderRadiusLarge,
[theme.breakpoints.down(breakpoint)]: { [theme.breakpoints.down(breakpoint)]: {
width: '100%', width: '100%',
@ -68,6 +86,15 @@ const StyledInnerContainer = styled(Box)(({ theme }) => ({
padding: theme.spacing(2), padding: theme.spacing(2),
})); }));
const StyledApplyInnerContainer = styled(Box)(({ theme }) => ({
paddingBottom: theme.spacing(1.5),
}));
const StyledOuterContainer = styled(Box)(({ theme }) => ({
display: 'flex',
marginTop: theme.spacing(2),
}));
const StyledButton = styled(Button)(({ theme }) => ({ const StyledButton = styled(Button)(({ theme }) => ({
marginLeft: theme.spacing(2), marginLeft: theme.spacing(2),
})); }));
@ -80,6 +107,36 @@ const ChangeRequestBody = styled(Box)(({ theme }) => ({
}, },
})); }));
const StyledApplyContainer = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-end',
width: '100%',
}));
const StyledBox = styled(Box)(({ theme }) => ({
width: '100%',
}));
const StyledTypography = styled(Typography)(({ theme }) => ({
fontWeight: 'bold',
}));
const StyledButtonContainer = styled(Box)(({ theme }) => ({
borderRadius: `${theme.shape.borderRadiusMedium}px`,
backgroundColor: theme.palette.primary.main!,
padding: theme.spacing(1, 2),
marginRight: theme.spacing(2),
height: '45px',
width: '45px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
svg: {
color: theme.palette.background.paper,
},
}));
export const ChangeRequestOverview: FC = () => { export const ChangeRequestOverview: FC = () => {
const projectId = useRequiredPathParam('projectId'); const projectId = useRequiredPathParam('projectId');
const [showCancelDialog, setShowCancelDialog] = useState(false); const [showCancelDialog, setShowCancelDialog] = useState(false);
@ -110,6 +167,7 @@ export const ChangeRequestOverview: FC = () => {
const [disabled, setDisabled] = useState(false); const [disabled, setDisabled] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
const approversEnabled = useUiFlag('changeRequestApproverEmails'); const approversEnabled = useUiFlag('changeRequestApproverEmails');
const theme = useTheme();
if (!changeRequest) { if (!changeRequest) {
return null; return null;
@ -306,175 +364,237 @@ export const ChangeRequestOverview: FC = () => {
} }
/> />
</StyledAsideBox> </StyledAsideBox>
<StyledPaper elevation={0}> <StyledDiv>
<StyledInnerContainer> <StyledPaper elevation={0}>
Requested Changes ({countOfChanges}) <StyledInnerContainer>
<ChangeRequest Requested Changes ({countOfChanges})
changeRequest={changeRequest} <ChangeRequest
onRefetch={refetchChangeRequest} changeRequest={changeRequest}
/> onRefetch={refetchChangeRequest}
{changeRequest.comments?.map((comment) => (
<ChangeRequestComment
key={comment.id}
comment={comment}
/> />
))} {changeRequest.comments?.map((comment) => (
<AddCommentField <ChangeRequestComment
user={user} key={comment.id}
commentText={commentText} comment={comment}
onTypeComment={setCommentText} />
> ))}
<Button <AddCommentField
variant='outlined' user={user}
onClick={onAddComment} commentText={commentText}
disabled={ onTypeComment={setCommentText}
!allowChangeRequestActions ||
commentText.trim().length === 0 ||
commentText.trim().length > 1000 ||
disabled
}
> >
Comment <Button
</Button> variant='outlined'
</AddCommentField> onClick={onAddComment}
<ConditionallyRender disabled={
condition={isSelfReview} !allowChangeRequestActions ||
show={ commentText.trim().length === 0 ||
<Alert commentText.trim().length > 1000 ||
sx={(theme) => ({ disabled
marginTop: theme.spacing(1.5), }
})}
severity='info'
> >
You can not approve your own change request Comment
</Alert> </Button>
} </AddCommentField>
/>
<ChangeRequestReviewStatus
changeRequest={changeRequest}
onEditClick={() =>
setShowScheduleChangeDialog(true)
}
/>
<StyledButtonBox>
<ConditionallyRender <ConditionallyRender
condition={ condition={isSelfReview}
changeRequest.state === 'In review' &&
!hasApprovedAlready
}
show={ show={
<ReviewButton <Alert
onReject={() => sx={(theme) => ({
setShowRejectDialog(true) marginTop: theme.spacing(1.5),
} })}
onApprove={onApprove} severity='info'
disabled={
!allowChangeRequestActions ||
disabled
}
> >
Review changes ({countOfChanges}) You can not approve your own change
</ReviewButton> request
</Alert>
} }
/> />
<ChangeRequestReviewStatus
changeRequest={changeRequest}
onEditClick={() =>
setShowScheduleChangeDialog(true)
}
/>
<StyledButtonBox>
<ConditionallyRender
condition={
changeRequest.state === 'In review' &&
!hasApprovedAlready
}
show={
<ReviewButton
onReject={() =>
setShowRejectDialog(true)
}
onApprove={onApprove}
disabled={
!allowChangeRequestActions ||
disabled
}
>
Review changes ({countOfChanges})
</ReviewButton>
}
/>
<ConditionallyRender <ConditionallyRender
condition={changeRequest.state === 'Approved'} condition={
show={ changeRequest.state === 'Approved' &&
<ApplyButton !approversEnabled
onApply={onApplyChanges} }
disabled={ show={
!allowChangeRequestActions || <ApplyButton
disabled onApply={onApplyChanges}
} disabled={
onSchedule={() => !allowChangeRequestActions ||
setShowScheduleChangeDialog(true) disabled
} }
> onSchedule={() =>
Apply or schedule changes setShowScheduleChangeDialog(
</ApplyButton> true,
} )
/> }
<ConditionallyRender >
condition={changeRequest.state === 'Scheduled'} Apply or schedule changes
show={ </ApplyButton>
<ApplyButton }
onApply={() => />
setShowApplyScheduledDialog(true) <ConditionallyRender
} condition={
disabled={ changeRequest.state === 'Scheduled'
!allowChangeRequestActions || }
disabled show={
} <ApplyButton
onSchedule={() => onApply={() =>
setShowScheduleChangeDialog(true) setShowApplyScheduledDialog(
} true,
variant={'update'} )
> }
Apply or schedule changes disabled={
</ApplyButton> !allowChangeRequestActions ||
} disabled
/> }
onSchedule={() =>
setShowScheduleChangeDialog(
true,
)
}
variant={'update'}
>
Apply or schedule changes
</ApplyButton>
}
/>
<ConditionallyRender <ConditionallyRender
condition={ condition={
changeRequest.state === 'In review' || changeRequest.state === 'In review' ||
changeRequest.state === 'Approved' || changeRequest.state === 'Approved' ||
changeRequest.state === 'Scheduled' changeRequest.state === 'Scheduled'
} }
show={ show={
<StyledButton <StyledButton
variant='outlined' variant='outlined'
onClick={() => { onClick={() => {
navigate( navigate(
`/playground?changeRequest=${changeRequest.id}&projects=${projectId}&environments=${changeRequest.environment}`, `/playground?changeRequest=${changeRequest.id}&projects=${projectId}&environments=${changeRequest.environment}`,
); );
}} }}
> >
Preview changes Preview changes
</StyledButton> </StyledButton>
} }
/> />
<ConditionallyRender <ConditionallyRender
condition={ condition={
changeRequest.state !== 'Applied' && changeRequest.state !== 'Applied' &&
changeRequest.state !== 'Rejected' && changeRequest.state !== 'Rejected' &&
changeRequest.state !== 'Cancelled' && changeRequest.state !== 'Cancelled' &&
(changeRequest.createdBy.id === user?.id || (changeRequest.createdBy.id ===
isAdmin) user?.id ||
} isAdmin)
show={ }
<ConditionallyRender show={
condition={Boolean(scheduledAt)} <ConditionallyRender
show={ condition={Boolean(scheduledAt)}
<StyledButton show={
variant='outlined' <StyledButton
onClick={() => variant='outlined'
setShowRejectScheduledDialog( onClick={() =>
setShowRejectScheduledDialog(
true,
)
}
disabled={disabled}
>
Reject changes
</StyledButton>
}
elseShow={
<StyledButton
variant='outlined'
onClick={onCancel}
disabled={disabled}
>
Cancel changes
</StyledButton>
}
/>
}
/>
</StyledButtonBox>
</StyledInnerContainer>
</StyledPaper>
<ConditionallyRender
condition={
changeRequest.state === 'Approved' &&
approversEnabled
}
show={
<StyledApplyPaper elevation={0}>
<StyledApplyInnerContainer>
<StyledOuterContainer>
<StyledButtonContainer>
<ApprovedIcon
style={{
transform: `scale(1.5)`,
}}
/>
</StyledButtonContainer>
<StyledBox>
<StyledTypography>
Apply changes
</StyledTypography>
<Typography>
The change request has been
reviewed and approved
</Typography>
</StyledBox>
<StyledApplyContainer>
<ApplyButton
onApply={onApplyChanges}
disabled={
!allowChangeRequestActions ||
disabled
}
onSchedule={() =>
setShowScheduleChangeDialog(
true, true,
) )
} }
disabled={disabled}
> >
Reject changes Apply or schedule changes
</StyledButton> </ApplyButton>
} </StyledApplyContainer>
elseShow={ </StyledOuterContainer>
<StyledButton </StyledApplyInnerContainer>
variant='outlined' </StyledApplyPaper>
onClick={onCancel} }
disabled={disabled} />
> </StyledDiv>
Cancel changes
</StyledButton>
}
/>
}
/>
</StyledButtonBox>
</StyledInnerContainer>
</StyledPaper>
<Dialogue <Dialogue
open={showCancelDialog} open={showCancelDialog}
onClick={onCancelChanges} onClick={onCancelChanges}