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 { FC } from 'react';
import { Alert, styled } from '@mui/material';
import { FC, useContext } from 'react';
import { Box } from '@mui/material';
import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest';
import { ChangeRequestHeader } from './ChangeRequestHeader/ChangeRequestHeader';
@ -12,10 +12,13 @@ import { ChangeRequestReviewStatus } from './ChangeRequestReviewStatus/ChangeReq
import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import Button from '@mui/material/Button';
import Paper from '@mui/material/Paper';
import { ReviewButton } from './ReviewButton/ReviewButton';
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 }) => ({
width: '30%',
@ -43,6 +46,9 @@ const StyledInnerContainer = styled(Box)(({ theme }) => ({
export const ChangeRequestOverview: FC = () => {
const projectId = useRequiredPathParam('projectId');
const { user } = useAuthUser();
const { isAdmin } = useContext(AccessContext);
const id = useRequiredPathParam('id');
const { data: changeRequest, refetchChangeRequest } = useChangeRequest(
projectId,
@ -71,6 +77,11 @@ export const ChangeRequestOverview: FC = () => {
}
};
const isSelfReview =
changeRequest?.createdBy.id === user?.id &&
changeRequest.state === 'In review' &&
!isAdmin;
return (
<>
<ChangeRequestHeader changeRequest={changeRequest} />
@ -98,6 +109,19 @@ export const ChangeRequestOverview: FC = () => {
<StyledInnerContainer>
Changes
<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
state={changeRequest.state}
/>
@ -109,12 +133,17 @@ export const ChangeRequestOverview: FC = () => {
<ConditionallyRender
condition={changeRequest.state === 'Approved'}
show={
<Button
<PermissionButton
variant="contained"
onClick={onApplyChanges}
projectId={projectId}
permission={APPLY_CHANGE_REQUEST}
environmentId={
changeRequest.environment
}
>
Apply changes
</Button>
</PermissionButton>
}
/>
</StyledButtonBox>

View File

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

View File

@ -19,75 +19,86 @@ export interface IPermissionButtonProps extends Omit<ButtonProps, 'title'> {
tooltipProps?: Omit<ITooltipResolverProps, 'children'>;
}
const PermissionButton: React.FC<IPermissionButtonProps> = ({
permission,
variant = 'contained',
color = 'primary',
onClick,
children,
disabled,
projectId,
environmentId,
tooltipProps,
...rest
}) => {
const { hasAccess } = useContext(AccessContext);
const id = useId();
let access;
const handleAccess = () => {
const PermissionButton: React.FC<IPermissionButtonProps> = React.forwardRef(
(
{
permission,
variant = 'contained',
color = 'primary',
onClick,
children,
disabled,
projectId,
environmentId,
tooltipProps,
...rest
},
ref
) => {
const { hasAccess } = useContext(AccessContext);
const id = useId();
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;
};
access = handleAccess();
return (
<TooltipResolver
{...tooltipProps}
title={formatAccessText(access, tooltipProps?.title)}
arrow
>
<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" />}
/>
const handleAccess = () => {
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);
}
>
{children}
</Button>
</span>
</TooltipResolver>
);
};
});
} else {
if (projectId && environmentId) {
access = hasAccess(permission, projectId, environmentId);
} 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;