1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: add permission buttons for change requests (#2392)

* Adds permission buttons for change requests
This commit is contained in:
Fredrik Strand Oseberg 2022-11-11 11:04:59 +01:00 committed by GitHub
parent 74c5189159
commit b9db7952fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 127 additions and 77 deletions

View File

@ -1,5 +1,5 @@
import { FormControlLabel, styled, Switch } from '@mui/material'; import { Alert, styled } from '@mui/material';
import { FC } from 'react'; import { FC, useContext } from 'react';
import { Box } from '@mui/material'; import { Box } from '@mui/material';
import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest'; import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest';
import { ChangeRequestHeader } from './ChangeRequestHeader/ChangeRequestHeader'; import { ChangeRequestHeader } from './ChangeRequestHeader/ChangeRequestHeader';
@ -12,10 +12,13 @@ import { ChangeRequestReviewStatus } from './ChangeRequestReviewStatus/ChangeReq
import useToast from 'hooks/useToast'; import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError'; import { formatUnknownError } from 'utils/formatUnknownError';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import Button from '@mui/material/Button';
import Paper from '@mui/material/Paper'; import Paper from '@mui/material/Paper';
import { ReviewButton } from './ReviewButton/ReviewButton'; import { ReviewButton } from './ReviewButton/ReviewButton';
import { ChangeRequestReviewer } from './ChangeRequestReviewers/ChangeRequestReviewer'; import { ChangeRequestReviewer } from './ChangeRequestReviewers/ChangeRequestReviewer';
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
import { APPLY_CHANGE_REQUEST } from 'component/providers/AccessProvider/permissions';
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
import AccessContext from 'contexts/AccessContext';
const StyledAsideBox = styled(Box)(({ theme }) => ({ const StyledAsideBox = styled(Box)(({ theme }) => ({
width: '30%', width: '30%',
@ -43,6 +46,9 @@ const StyledInnerContainer = styled(Box)(({ theme }) => ({
export const ChangeRequestOverview: FC = () => { export const ChangeRequestOverview: FC = () => {
const projectId = useRequiredPathParam('projectId'); const projectId = useRequiredPathParam('projectId');
const { user } = useAuthUser();
const { isAdmin } = useContext(AccessContext);
const id = useRequiredPathParam('id'); const id = useRequiredPathParam('id');
const { data: changeRequest, refetchChangeRequest } = useChangeRequest( const { data: changeRequest, refetchChangeRequest } = useChangeRequest(
projectId, projectId,
@ -71,6 +77,11 @@ export const ChangeRequestOverview: FC = () => {
} }
}; };
const isSelfReview =
changeRequest?.createdBy.id === user?.id &&
changeRequest.state === 'In review' &&
!isAdmin;
return ( return (
<> <>
<ChangeRequestHeader changeRequest={changeRequest} /> <ChangeRequestHeader changeRequest={changeRequest} />
@ -98,6 +109,19 @@ export const ChangeRequestOverview: FC = () => {
<StyledInnerContainer> <StyledInnerContainer>
Changes Changes
<ChangeRequest changeRequest={changeRequest} /> <ChangeRequest changeRequest={changeRequest} />
<ConditionallyRender
condition={isSelfReview}
show={
<Alert
sx={theme => ({
marginTop: theme.spacing(1.5),
})}
severity="info"
>
You can not approve your own change request
</Alert>
}
/>
<ChangeRequestReviewStatus <ChangeRequestReviewStatus
state={changeRequest.state} state={changeRequest.state}
/> />
@ -109,12 +133,17 @@ export const ChangeRequestOverview: FC = () => {
<ConditionallyRender <ConditionallyRender
condition={changeRequest.state === 'Approved'} condition={changeRequest.state === 'Approved'}
show={ show={
<Button <PermissionButton
variant="contained" variant="contained"
onClick={onApplyChanges} onClick={onApplyChanges}
projectId={projectId}
permission={APPLY_CHANGE_REQUEST}
environmentId={
changeRequest.environment
}
> >
Apply changes Apply changes
</Button> </PermissionButton>
} }
/> />
</StyledButtonBox> </StyledButtonBox>

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useContext } from 'react';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest'; import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest';
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi'; import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
@ -6,21 +6,27 @@ import { formatUnknownError } from 'utils/formatUnknownError';
import useToast from 'hooks/useToast'; import useToast from 'hooks/useToast';
import { import {
Button,
Grow, Grow,
Paper, Paper,
Popper, Popper,
MenuItem, MenuItem,
MenuList, MenuList,
ClickAwayListener, ClickAwayListener,
Alert,
} from '@mui/material'; } from '@mui/material';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; 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';
export const ReviewButton = () => { export const ReviewButton = () => {
const { isAdmin } = useContext(AccessContext);
const projectId = useRequiredPathParam('projectId'); const projectId = useRequiredPathParam('projectId');
const id = useRequiredPathParam('id'); const id = useRequiredPathParam('id');
const { refetchChangeRequest } = useChangeRequest(projectId, id); const { user } = useAuthUser();
const { refetchChangeRequest, data } = useChangeRequest(projectId, id);
const { setToastApiError, setToastData } = useToast(); const { setToastApiError, setToastData } = useToast();
const { changeState } = useChangeRequestApi(); const { changeState } = useChangeRequestApi();
@ -77,8 +83,9 @@ export const ReviewButton = () => {
return ( return (
<React.Fragment> <React.Fragment>
<Button <PermissionButton
variant="contained" variant="contained"
disabled={data?.createdBy.id === user?.id && !isAdmin}
aria-controls={open ? 'review-options-menu' : undefined} aria-controls={open ? 'review-options-menu' : undefined}
aria-expanded={open ? 'true' : undefined} aria-expanded={open ? 'true' : undefined}
aria-label="review changes" aria-label="review changes"
@ -86,9 +93,12 @@ export const ReviewButton = () => {
onClick={onToggle} onClick={onToggle}
ref={anchorRef} ref={anchorRef}
endIcon={<ArrowDropDownIcon />} endIcon={<ArrowDropDownIcon />}
permission={APPROVE_CHANGE_REQUEST}
projectId={projectId}
environmentId={data?.environment}
> >
Review changes Review changes
</Button> </PermissionButton>
<Popper <Popper
sx={{ sx={{
zIndex: 1, zIndex: 1,

View File

@ -19,75 +19,86 @@ export interface IPermissionButtonProps extends Omit<ButtonProps, 'title'> {
tooltipProps?: Omit<ITooltipResolverProps, 'children'>; tooltipProps?: Omit<ITooltipResolverProps, 'children'>;
} }
const PermissionButton: React.FC<IPermissionButtonProps> = ({ const PermissionButton: React.FC<IPermissionButtonProps> = React.forwardRef(
permission, (
variant = 'contained', {
color = 'primary', permission,
onClick, variant = 'contained',
children, color = 'primary',
disabled, onClick,
projectId, children,
environmentId, disabled,
tooltipProps, projectId,
...rest environmentId,
}) => { tooltipProps,
const { hasAccess } = useContext(AccessContext); ...rest
const id = useId(); },
let access; ref
) => {
const handleAccess = () => { const { hasAccess } = useContext(AccessContext);
const id = useId();
let access; let access;
if (Array.isArray(permission)) {
access = permission.some(permission => {
if (projectId && environmentId) {
return hasAccess(permission, projectId, environmentId);
} else if (projectId) {
return hasAccess(permission, projectId);
} else {
return hasAccess(permission);
}
});
} else {
if (projectId && environmentId) {
access = hasAccess(permission, projectId, environmentId);
} else if (projectId) {
access = hasAccess(permission, projectId);
} else {
access = hasAccess(permission);
}
}
return access; const handleAccess = () => {
}; let access;
if (Array.isArray(permission)) {
access = handleAccess(); access = permission.some(permission => {
if (projectId && environmentId) {
return ( return hasAccess(permission, projectId, environmentId);
<TooltipResolver } else if (projectId) {
{...tooltipProps} return hasAccess(permission, projectId);
title={formatAccessText(access, tooltipProps?.title)} } else {
arrow return hasAccess(permission);
>
<span id={id}>
<Button
onClick={onClick}
disabled={disabled || !access}
aria-labelledby={id}
variant={variant}
color={color}
{...rest}
endIcon={
<ConditionallyRender
condition={!access}
show={<Lock titleAccess="Locked" />}
/>
} }
> });
{children} } else {
</Button> if (projectId && environmentId) {
</span> access = hasAccess(permission, projectId, environmentId);
</TooltipResolver> } else if (projectId) {
); access = hasAccess(permission, projectId);
}; } else {
access = hasAccess(permission);
}
}
return access;
};
access = handleAccess();
return (
<TooltipResolver
{...tooltipProps}
title={formatAccessText(access, tooltipProps?.title)}
arrow
>
<span id={id}>
<Button
ref={ref}
onClick={onClick}
disabled={disabled || !access}
aria-labelledby={id}
variant={variant}
color={color}
{...rest}
endIcon={
<>
<ConditionallyRender
condition={!access}
show={<Lock titleAccess="Locked" />}
elseShow={
Boolean(rest.endIcon) && rest.endIcon
}
/>
</>
}
>
{children}
</Button>
</span>
</TooltipResolver>
);
}
);
export default PermissionButton; export default PermissionButton;