Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add schedule option to approved change requests (#5252)
The button doesn't do anything at the moment, but it's there visually. Because this uses the same button as the dual-function button for approve/reject, I extracted that component into a reusable "multi-action" button. I could have copied the code wholesale, but it's a complex component, so I thought this would be a better solution. I'll add the dialog in a follow-up PR. This one already has a lot of changes. Visual: ![image](https://github.com/Unleash/unleash/assets/17786332/9a9bee77-4925-4054-9ef6-ef8ddbb61fae)
- Loading branch information
1 parent
95245c4
commit 9fbb61a
Showing
4 changed files
with
218 additions
and
129 deletions.
There are no files selected for viewing
31 changes: 31 additions & 0 deletions
31
frontend/src/component/changeRequest/ChangeRequestOverview/ApplyButton/ApplyButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
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'; | ||
|
||
export const ApplyButton: FC<{ | ||
disabled: boolean; | ||
onSchedule: () => void; | ||
onApply: () => void; | ||
}> = ({ disabled, onSchedule, onApply, children }) => ( | ||
<MultiActionButton | ||
permission={APPLY_CHANGE_REQUEST} | ||
disabled={disabled} | ||
actions={[ | ||
{ | ||
label: 'Apply changes', | ||
onSelect: onApply, | ||
icon: <CheckBox fontSize='small' />, | ||
}, | ||
{ | ||
label: 'Schedule changes', | ||
onSelect: onSchedule, | ||
icon: <Today fontSize='small' />, | ||
}, | ||
]} | ||
> | ||
{children} | ||
</MultiActionButton> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
...src/component/changeRequest/ChangeRequestOverview/MultiActionButton/MultiActionButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import React, { FC, useContext } from 'react'; | ||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; | ||
import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest'; | ||
|
||
import { | ||
ClickAwayListener, | ||
Grow, | ||
ListItemIcon, | ||
ListItemText, | ||
MenuItem, | ||
MenuList, | ||
Paper, | ||
Popper, | ||
} from '@mui/material'; | ||
|
||
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; | ||
onSelect: () => void; | ||
icon: JSX.Element; | ||
}; | ||
|
||
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); | ||
|
||
const [open, setOpen] = React.useState(false); | ||
const anchorRef = React.useRef<HTMLButtonElement>(null); | ||
|
||
const onToggle = () => { | ||
setOpen((prevOpen) => !prevOpen); | ||
}; | ||
|
||
const onClose = (event: Event) => { | ||
if (anchorRef.current?.contains(event.target as HTMLElement)) { | ||
return; | ||
} | ||
|
||
setOpen(false); | ||
}; | ||
const popperWidth = anchorRef.current | ||
? anchorRef.current.offsetWidth | ||
: null; | ||
|
||
return ( | ||
<React.Fragment> | ||
<PermissionButton | ||
variant='contained' | ||
disabled={ | ||
disabled || (data?.createdBy.id === user?.id && !isAdmin) | ||
} | ||
aria-controls={open ? 'review-options-menu' : undefined} | ||
aria-expanded={open ? 'true' : undefined} | ||
aria-label='review changes' | ||
aria-haspopup='menu' | ||
onClick={onToggle} | ||
ref={anchorRef} | ||
endIcon={<ArrowDropDownIcon />} | ||
permission={permission} | ||
projectId={projectId} | ||
environmentId={data?.environment} | ||
> | ||
{children} | ||
</PermissionButton> | ||
<Popper | ||
sx={{ | ||
zIndex: 1, | ||
width: popperWidth, | ||
}} | ||
open={open} | ||
anchorEl={anchorRef.current} | ||
role={undefined} | ||
transition | ||
disablePortal | ||
> | ||
{({ TransitionProps, placement }) => ( | ||
<Grow | ||
{...TransitionProps} | ||
style={{ | ||
transformOrigin: | ||
placement === 'bottom' | ||
? 'center top' | ||
: 'center bottom', | ||
}} | ||
> | ||
<Paper className='dropdown-outline'> | ||
<ClickAwayListener onClickAway={onClose}> | ||
<MenuList | ||
id='review-options-menu' | ||
autoFocusItem | ||
> | ||
{actions.map( | ||
({ label, onSelect, icon }) => ( | ||
<MenuItem onClick={onSelect}> | ||
<ListItemIcon> | ||
{icon} | ||
</ListItemIcon> | ||
<ListItemText> | ||
{label} | ||
</ListItemText> | ||
</MenuItem> | ||
), | ||
)} | ||
</MenuList> | ||
</ClickAwayListener> | ||
</Paper> | ||
</Grow> | ||
)} | ||
</Popper> | ||
</React.Fragment> | ||
); | ||
}; |
139 changes: 23 additions & 116 deletions
139
frontend/src/component/changeRequest/ChangeRequestOverview/ReviewButton/ReviewButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,124 +1,31 @@ | ||
import React, { FC, useContext } from 'react'; | ||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; | ||
import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest'; | ||
import { FC } from 'react'; | ||
|
||
import { | ||
ClickAwayListener, | ||
Grow, | ||
ListItemIcon, | ||
ListItemText, | ||
MenuItem, | ||
MenuList, | ||
Paper, | ||
Popper, | ||
} from '@mui/material'; | ||
|
||
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; | ||
import { APPROVE_CHANGE_REQUEST } from 'component/providers/AccessProvider/permissions'; | ||
import PermissionButton from 'component/common/PermissionButton/PermissionButton'; | ||
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser'; | ||
import AccessContext from 'contexts/AccessContext'; | ||
import CheckBox from '@mui/icons-material/Check'; | ||
import Clear from '@mui/icons-material/Clear'; | ||
import { MultiActionButton } from '../MultiActionButton/MultiActionButton'; | ||
import { APPROVE_CHANGE_REQUEST } from 'component/providers/AccessProvider/permissions'; | ||
|
||
export const ReviewButton: FC<{ | ||
disabled: boolean; | ||
onReject: () => void; | ||
onApprove: () => void; | ||
}> = ({ 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 [open, setOpen] = React.useState(false); | ||
const anchorRef = React.useRef<HTMLButtonElement>(null); | ||
|
||
const onToggle = () => { | ||
setOpen((prevOpen) => !prevOpen); | ||
}; | ||
|
||
const onClose = (event: Event) => { | ||
if (anchorRef.current?.contains(event.target as HTMLElement)) { | ||
return; | ||
} | ||
|
||
setOpen(false); | ||
}; | ||
const popperWidth = anchorRef.current | ||
? anchorRef.current.offsetWidth | ||
: null; | ||
|
||
return ( | ||
<React.Fragment> | ||
<PermissionButton | ||
variant='contained' | ||
disabled={ | ||
disabled || (data?.createdBy.id === user?.id && !isAdmin) | ||
} | ||
aria-controls={open ? 'review-options-menu' : undefined} | ||
aria-expanded={open ? 'true' : undefined} | ||
aria-label='review changes' | ||
aria-haspopup='menu' | ||
onClick={onToggle} | ||
ref={anchorRef} | ||
endIcon={<ArrowDropDownIcon />} | ||
permission={APPROVE_CHANGE_REQUEST} | ||
projectId={projectId} | ||
environmentId={data?.environment} | ||
> | ||
{children} | ||
</PermissionButton> | ||
<Popper | ||
sx={{ | ||
zIndex: 1, | ||
width: popperWidth, | ||
}} | ||
open={open} | ||
anchorEl={anchorRef.current} | ||
role={undefined} | ||
transition | ||
disablePortal | ||
> | ||
{({ TransitionProps, placement }) => ( | ||
<Grow | ||
{...TransitionProps} | ||
style={{ | ||
transformOrigin: | ||
placement === 'bottom' | ||
? 'center top' | ||
: 'center bottom', | ||
}} | ||
> | ||
<Paper className='dropdown-outline'> | ||
<ClickAwayListener onClickAway={onClose}> | ||
<MenuList | ||
id='review-options-menu' | ||
autoFocusItem | ||
> | ||
<MenuItem onClick={onApprove}> | ||
<ListItemIcon> | ||
<CheckBox fontSize='small' /> | ||
</ListItemIcon> | ||
<ListItemText> | ||
Approve changes | ||
</ListItemText> | ||
</MenuItem> | ||
<MenuItem onClick={onReject}> | ||
<ListItemIcon> | ||
<Clear fontSize='small' /> | ||
</ListItemIcon> | ||
<ListItemText> | ||
Reject changes | ||
</ListItemText> | ||
</MenuItem> | ||
</MenuList> | ||
</ClickAwayListener> | ||
</Paper> | ||
</Grow> | ||
)} | ||
</Popper> | ||
</React.Fragment> | ||
); | ||
}; | ||
}> = ({ disabled, onReject, onApprove, children }) => ( | ||
<MultiActionButton | ||
permission={APPROVE_CHANGE_REQUEST} | ||
disabled={disabled} | ||
actions={[ | ||
{ | ||
label: 'Approve', | ||
onSelect: onApprove, | ||
icon: <CheckBox fontSize='small' />, | ||
}, | ||
{ | ||
label: 'Reject', | ||
onSelect: onReject, | ||
icon: <Clear fontSize='small' />, | ||
}, | ||
]} | ||
> | ||
{children} | ||
</MultiActionButton> | ||
); |
9fbb61a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
unleash-monorepo-frontend – ./frontend
unleash-monorepo-frontend-git-main-unleash-team.vercel.app
unleash-monorepo-frontend-unleash-team.vercel.app
unleash-monorepo-frontend.vercel.app