mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: use new role components in project access (#4018)
https://linear.app/unleash/issue/2-1143/adapt-project-roles-to-use-the-new-role-selector-and-role-description This PR further unifies roles, by having a single `IRole` interface to cover both types, and re-using the same components for project roles.
This commit is contained in:
		
							parent
							
								
									a5ee50cfc9
								
							
						
					
					
						commit
						a9e9ae8c3e
					
				@ -10,7 +10,7 @@ import { ItemList } from 'component/common/ItemList/ItemList';
 | 
			
		||||
import useAuthSettings from 'hooks/api/getters/useAuthSettings/useAuthSettings';
 | 
			
		||||
import { Link } from 'react-router-dom';
 | 
			
		||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
 | 
			
		||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
 | 
			
		||||
import { RoleSelect } from 'component/common/RoleSelect/RoleSelect';
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { useEffect, useState } from 'react';
 | 
			
		||||
import { IPermission, ICheckedPermissions } from 'interfaces/permissions';
 | 
			
		||||
import IRole, { PredefinedRoleType } from 'interfaces/role';
 | 
			
		||||
import { IRole, PredefinedRoleType } from 'interfaces/role';
 | 
			
		||||
import { useRoles } from 'hooks/api/getters/useRoles/useRoles';
 | 
			
		||||
import { permissionsToCheckedPermissions } from 'utils/permissions';
 | 
			
		||||
import { ROOT_ROLE_TYPE } from '@server/util/constants';
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ import { PageHeader } from 'component/common/PageHeader/PageHeader';
 | 
			
		||||
import { Add } from '@mui/icons-material';
 | 
			
		||||
import { UPDATE_ROLE } from '@server/types/permissions';
 | 
			
		||||
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
 | 
			
		||||
const StyledPageContent = styled(PageContent)(({ theme }) => ({
 | 
			
		||||
    '& .page-header': {
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
 | 
			
		||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
 | 
			
		||||
import { useServiceAccounts } from 'hooks/api/getters/useServiceAccounts/useServiceAccounts';
 | 
			
		||||
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
import { RoleDeleteDialogUsers } from './RoleDeleteDialogUsers/RoleDeleteDialogUsers';
 | 
			
		||||
import { RoleDeleteDialogServiceAccounts } from './RoleDeleteDialogServiceAccounts/RoleDeleteDialogServiceAccounts';
 | 
			
		||||
import { useGroups } from 'hooks/api/getters/useGroups/useGroups';
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { VFC } from 'react';
 | 
			
		||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
 | 
			
		||||
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
import { useRole } from 'hooks/api/getters/useRole/useRole';
 | 
			
		||||
import { RoleDescription } from 'component/common/RoleDescription/RoleDescription';
 | 
			
		||||
import { PREDEFINED_ROLE_TYPES } from '@server/util/constants';
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import { Box, styled } from '@mui/material';
 | 
			
		||||
import { PREDEFINED_ROLE_TYPES } from '@server/util/constants';
 | 
			
		||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
 | 
			
		||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
import { VFC } from 'react';
 | 
			
		||||
 | 
			
		||||
const StyledBox = styled(Box)(() => ({
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
			
		||||
import { Badge } from 'component/common/Badge/Badge';
 | 
			
		||||
import { styled } from '@mui/material';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
 | 
			
		||||
import { PREDEFINED_ROLE_TYPES } from '@server/util/constants';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { useMemo, useState } from 'react';
 | 
			
		||||
import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
 | 
			
		||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
			
		||||
import IRole, { PredefinedRoleType } from 'interfaces/role';
 | 
			
		||||
import { IRole, PredefinedRoleType } from 'interfaces/role';
 | 
			
		||||
import useToast from 'hooks/useToast';
 | 
			
		||||
import { formatUnknownError } from 'utils/formatUnknownError';
 | 
			
		||||
import { PageContent } from 'component/common/PageContent/PageContent';
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ import { INewPersonalAPIToken } from 'interfaces/personalAPIToken';
 | 
			
		||||
import { ServiceAccountTokens } from './ServiceAccountTokens/ServiceAccountTokens';
 | 
			
		||||
import { IServiceAccount } from 'interfaces/service-account';
 | 
			
		||||
import { RoleSelect } from 'component/common/RoleSelect/RoleSelect';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
 | 
			
		||||
const StyledForm = styled('form')(() => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { useMemo, useState } from 'react';
 | 
			
		||||
import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
 | 
			
		||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
import useToast from 'hooks/useToast';
 | 
			
		||||
import { formatUnknownError } from 'utils/formatUnknownError';
 | 
			
		||||
import { PageContent } from 'component/common/PageContent/PageContent';
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
 | 
			
		||||
import { EDIT } from 'constants/misc';
 | 
			
		||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
 | 
			
		||||
import { RoleSelect } from 'component/common/RoleSelect/RoleSelect';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
 | 
			
		||||
const StyledForm = styled('form')(() => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ import ConfirmUserAdded from '../ConfirmUserAdded/ConfirmUserAdded';
 | 
			
		||||
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
 | 
			
		||||
import useAdminUsersApi from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi';
 | 
			
		||||
import { IUser } from 'interfaces/user';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
import useToast from 'hooks/useToast';
 | 
			
		||||
import { formatUnknownError } from 'utils/formatUnknownError';
 | 
			
		||||
import { useUsersPlan } from 'hooks/useUsersPlan';
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { useEffect, useState } from 'react';
 | 
			
		||||
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
 | 
			
		||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
import { useRoles } from 'hooks/api/getters/useRoles/useRoles';
 | 
			
		||||
 | 
			
		||||
const useCreateUserForm = (
 | 
			
		||||
 | 
			
		||||
@ -5,9 +5,10 @@ import {
 | 
			
		||||
    styled,
 | 
			
		||||
} from '@mui/material';
 | 
			
		||||
import { useRoles } from 'hooks/api/getters/useRoles/useRoles';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole, PredefinedRoleType } from 'interfaces/role';
 | 
			
		||||
import { RoleDescription } from '../RoleDescription/RoleDescription';
 | 
			
		||||
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
 | 
			
		||||
import { ROOT_ROLE_TYPE } from '@server/util/constants';
 | 
			
		||||
 | 
			
		||||
const StyledRoleOption = styled('div')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
@ -20,18 +21,22 @@ const StyledRoleOption = styled('div')(({ theme }) => ({
 | 
			
		||||
 | 
			
		||||
interface IRoleSelectProps
 | 
			
		||||
    extends Partial<AutocompleteProps<IRole, false, false, false>> {
 | 
			
		||||
    type?: PredefinedRoleType;
 | 
			
		||||
    value: IRole | null;
 | 
			
		||||
    setValue: (role: IRole | null) => void;
 | 
			
		||||
    required?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const RoleSelect = ({
 | 
			
		||||
    type = ROOT_ROLE_TYPE,
 | 
			
		||||
    value,
 | 
			
		||||
    setValue,
 | 
			
		||||
    required,
 | 
			
		||||
    ...rest
 | 
			
		||||
}: IRoleSelectProps) => {
 | 
			
		||||
    const { roles } = useRoles();
 | 
			
		||||
    const { roles: rootRoles, projectRoles } = useRoles();
 | 
			
		||||
 | 
			
		||||
    const roles = type === ROOT_ROLE_TYPE ? rootRoles : projectRoles;
 | 
			
		||||
 | 
			
		||||
    const renderRoleOption = (
 | 
			
		||||
        props: React.HTMLAttributes<HTMLLIElement>,
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ import useProjectAccess, {
 | 
			
		||||
    ENTITY_TYPE,
 | 
			
		||||
    IProjectAccess,
 | 
			
		||||
} from 'hooks/api/getters/useProjectAccess/useProjectAccess';
 | 
			
		||||
import { IProjectRole } from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
 | 
			
		||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
 | 
			
		||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
 | 
			
		||||
@ -25,7 +25,6 @@ import { formatUnknownError } from 'utils/formatUnknownError';
 | 
			
		||||
import { IUser } from 'interfaces/user';
 | 
			
		||||
import { IGroup } from 'interfaces/group';
 | 
			
		||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
			
		||||
import { ProjectRoleDescription } from './ProjectRoleDescription/ProjectRoleDescription';
 | 
			
		||||
import { useNavigate } from 'react-router-dom';
 | 
			
		||||
import { GO_BACK } from 'constants/navigate';
 | 
			
		||||
import {
 | 
			
		||||
@ -36,6 +35,8 @@ import {
 | 
			
		||||
} from 'utils/testIds';
 | 
			
		||||
import { caseInsensitiveSearch } from 'utils/search';
 | 
			
		||||
import { IServiceAccount } from 'interfaces/service-account';
 | 
			
		||||
import { RoleSelect } from 'component/common/RoleSelect/RoleSelect';
 | 
			
		||||
import { PROJECT_ROLE_TYPE } from '@server/util/constants';
 | 
			
		||||
 | 
			
		||||
const StyledForm = styled('form')(() => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
@ -82,15 +83,6 @@ const StyledUserOption = styled('div')(({ theme }) => ({
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledRoleOption = styled('div')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    flexDirection: 'column',
 | 
			
		||||
    '& > span:last-of-type': {
 | 
			
		||||
        fontSize: theme.fontSizes.smallerBody,
 | 
			
		||||
        color: theme.palette.text.secondary,
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
interface IAccessOption {
 | 
			
		||||
    id: number;
 | 
			
		||||
    entity: IUser | IGroup;
 | 
			
		||||
@ -103,7 +95,7 @@ interface IProjectAccessAssignProps {
 | 
			
		||||
    users: IUser[];
 | 
			
		||||
    serviceAccounts: IServiceAccount[];
 | 
			
		||||
    groups: IGroup[];
 | 
			
		||||
    roles: IProjectRole[];
 | 
			
		||||
    roles: IRole[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ProjectAccessAssign = ({
 | 
			
		||||
@ -190,7 +182,7 @@ export const ProjectAccessAssign = ({
 | 
			
		||||
                    id === selected?.entity.id && type === selected?.type
 | 
			
		||||
            )
 | 
			
		||||
    );
 | 
			
		||||
    const [role, setRole] = useState<IProjectRole | null>(
 | 
			
		||||
    const [role, setRole] = useState<IRole | null>(
 | 
			
		||||
        roles.find(({ id }) => id === selected?.entity.roleId) ?? null
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -317,18 +309,6 @@ export const ProjectAccessAssign = ({
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const renderRoleOption = (
 | 
			
		||||
        props: React.HTMLAttributes<HTMLLIElement>,
 | 
			
		||||
        option: IProjectRole
 | 
			
		||||
    ) => (
 | 
			
		||||
        <li {...props}>
 | 
			
		||||
            <StyledRoleOption>
 | 
			
		||||
                <span>{option.name}</span>
 | 
			
		||||
                <span>{option.description}</span>
 | 
			
		||||
            </StyledRoleOption>
 | 
			
		||||
        </li>
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const isValid = selectedOptions.length > 0 && role;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
@ -451,29 +431,13 @@ export const ProjectAccessAssign = ({
 | 
			
		||||
                            Select the role to assign for this project
 | 
			
		||||
                        </StyledInputDescription>
 | 
			
		||||
                        <StyledAutocompleteWrapper>
 | 
			
		||||
                            <Autocomplete
 | 
			
		||||
                            <RoleSelect
 | 
			
		||||
                                data-testid={PA_ROLE_ID}
 | 
			
		||||
                                size="small"
 | 
			
		||||
                                openOnFocus
 | 
			
		||||
                                type={PROJECT_ROLE_TYPE}
 | 
			
		||||
                                value={role}
 | 
			
		||||
                                onChange={(_, newValue) => setRole(newValue)}
 | 
			
		||||
                                options={roles}
 | 
			
		||||
                                renderOption={renderRoleOption}
 | 
			
		||||
                                getOptionLabel={option => option.name}
 | 
			
		||||
                                renderInput={params => (
 | 
			
		||||
                                    <TextField {...params} label="Role" />
 | 
			
		||||
                                )}
 | 
			
		||||
                                setValue={role => setRole(role || null)}
 | 
			
		||||
                            />
 | 
			
		||||
                        </StyledAutocompleteWrapper>
 | 
			
		||||
                        <ConditionallyRender
 | 
			
		||||
                            condition={Boolean(role?.id)}
 | 
			
		||||
                            show={
 | 
			
		||||
                                <ProjectRoleDescription
 | 
			
		||||
                                    roleId={role?.id!}
 | 
			
		||||
                                    projectId={projectId}
 | 
			
		||||
                                />
 | 
			
		||||
                            }
 | 
			
		||||
                        />
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
                    <StyledButtonContainer>
 | 
			
		||||
 | 
			
		||||
@ -1,163 +0,0 @@
 | 
			
		||||
import { styled, SxProps, Theme } from '@mui/material';
 | 
			
		||||
import { ForwardedRef, forwardRef, useMemo, VFC } from 'react';
 | 
			
		||||
import { useRole } from 'hooks/api/getters/useRole/useRole';
 | 
			
		||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
			
		||||
import useProjectAccess from 'hooks/api/getters/useProjectAccess/useProjectAccess';
 | 
			
		||||
import { ProjectRoleDescriptionProjectPermissions } from './ProjectRoleDescriptionProjectPermissions/ProjectRoleDescriptionProjectPermissions';
 | 
			
		||||
import { ProjectRoleDescriptionEnvironmentPermissions } from './ProjectRoleDescriptionEnvironmentPermissions/ProjectRoleDescriptionEnvironmentPermissions';
 | 
			
		||||
 | 
			
		||||
const StyledDescription = styled('div', {
 | 
			
		||||
    shouldForwardProp: prop =>
 | 
			
		||||
        prop !== 'roleId' && prop !== 'popover' && prop !== 'sx',
 | 
			
		||||
})<IProjectRoleDescriptionStyleProps>(({ theme, popover }) => ({
 | 
			
		||||
    width: '100%',
 | 
			
		||||
    maxWidth: theme.spacing(50),
 | 
			
		||||
    padding: theme.spacing(3),
 | 
			
		||||
    backgroundColor: popover
 | 
			
		||||
        ? theme.palette.background.paper
 | 
			
		||||
        : theme.palette.neutral.light,
 | 
			
		||||
    color: theme.palette.text.secondary,
 | 
			
		||||
    fontSize: theme.fontSizes.smallBody,
 | 
			
		||||
    borderRadius: theme.shape.borderRadiusMedium,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledDescriptionBlock = styled('div')(({ theme }) => ({
 | 
			
		||||
    '& p:last-child': {
 | 
			
		||||
        marginBottom: theme.spacing(2),
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledDescriptionHeader = styled('p')(({ theme }) => ({
 | 
			
		||||
    color: theme.palette.text.primary,
 | 
			
		||||
    fontSize: theme.fontSizes.smallBody,
 | 
			
		||||
    fontWeight: theme.fontWeight.bold,
 | 
			
		||||
    marginBottom: theme.spacing(2),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledDescriptionSubHeader = styled('p')(({ theme }) => ({
 | 
			
		||||
    color: theme.palette.text.primary,
 | 
			
		||||
    fontSize: theme.fontSizes.smallBody,
 | 
			
		||||
    marginBottom: theme.spacing(1),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
interface IProjectRoleDescriptionStyleProps {
 | 
			
		||||
    popover?: boolean;
 | 
			
		||||
    className?: string;
 | 
			
		||||
    sx?: SxProps<Theme>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IProjectRoleDescriptionProps
 | 
			
		||||
    extends IProjectRoleDescriptionStyleProps {
 | 
			
		||||
    roleId: number;
 | 
			
		||||
    projectId: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ProjectRoleDescription: VFC<IProjectRoleDescriptionProps> =
 | 
			
		||||
    forwardRef(
 | 
			
		||||
        (
 | 
			
		||||
            {
 | 
			
		||||
                roleId,
 | 
			
		||||
                projectId,
 | 
			
		||||
                className,
 | 
			
		||||
                sx,
 | 
			
		||||
                ...props
 | 
			
		||||
            }: IProjectRoleDescriptionProps,
 | 
			
		||||
            ref: ForwardedRef<HTMLDivElement>
 | 
			
		||||
        ) => {
 | 
			
		||||
            const { role } = useRole(roleId.toString());
 | 
			
		||||
            const { access } = useProjectAccess(projectId);
 | 
			
		||||
            const accessRole = access?.roles.find(role => role.id === roleId);
 | 
			
		||||
 | 
			
		||||
            const environments = useMemo(() => {
 | 
			
		||||
                const environments = new Set<string>();
 | 
			
		||||
                role?.permissions
 | 
			
		||||
                    ?.filter((permission: any) => permission.environment)
 | 
			
		||||
                    .forEach((permission: any) => {
 | 
			
		||||
                        environments.add(permission.environment);
 | 
			
		||||
                    });
 | 
			
		||||
                return [...environments].sort();
 | 
			
		||||
            }, [role]);
 | 
			
		||||
 | 
			
		||||
            const projectPermissions = useMemo(() => {
 | 
			
		||||
                return role?.permissions?.filter(
 | 
			
		||||
                    (permission: any) => !permission.environment
 | 
			
		||||
                );
 | 
			
		||||
            }, [role]);
 | 
			
		||||
 | 
			
		||||
            return (
 | 
			
		||||
                <StyledDescription
 | 
			
		||||
                    className={className}
 | 
			
		||||
                    sx={sx}
 | 
			
		||||
                    {...props}
 | 
			
		||||
                    ref={ref}
 | 
			
		||||
                >
 | 
			
		||||
                    <ConditionallyRender
 | 
			
		||||
                        condition={Boolean(
 | 
			
		||||
                            role?.permissions && role?.permissions?.length > 0
 | 
			
		||||
                        )}
 | 
			
		||||
                        show={
 | 
			
		||||
                            <>
 | 
			
		||||
                                <ConditionallyRender
 | 
			
		||||
                                    condition={Boolean(
 | 
			
		||||
                                        projectPermissions?.length
 | 
			
		||||
                                    )}
 | 
			
		||||
                                    show={
 | 
			
		||||
                                        <>
 | 
			
		||||
                                            <StyledDescriptionHeader>
 | 
			
		||||
                                                Project permissions
 | 
			
		||||
                                            </StyledDescriptionHeader>
 | 
			
		||||
                                            <StyledDescriptionBlock>
 | 
			
		||||
                                                <ProjectRoleDescriptionProjectPermissions
 | 
			
		||||
                                                    permissions={
 | 
			
		||||
                                                        role?.permissions || []
 | 
			
		||||
                                                    }
 | 
			
		||||
                                                />
 | 
			
		||||
                                            </StyledDescriptionBlock>
 | 
			
		||||
                                        </>
 | 
			
		||||
                                    }
 | 
			
		||||
                                />
 | 
			
		||||
                                <ConditionallyRender
 | 
			
		||||
                                    condition={Boolean(environments.length)}
 | 
			
		||||
                                    show={
 | 
			
		||||
                                        <>
 | 
			
		||||
                                            <StyledDescriptionHeader>
 | 
			
		||||
                                                Environment permissions
 | 
			
		||||
                                            </StyledDescriptionHeader>
 | 
			
		||||
                                            {environments.map(environment => (
 | 
			
		||||
                                                <div key={environment}>
 | 
			
		||||
                                                    <StyledDescriptionSubHeader>
 | 
			
		||||
                                                        {environment}
 | 
			
		||||
                                                    </StyledDescriptionSubHeader>
 | 
			
		||||
                                                    <StyledDescriptionBlock>
 | 
			
		||||
                                                        <ProjectRoleDescriptionEnvironmentPermissions
 | 
			
		||||
                                                            environment={
 | 
			
		||||
                                                                environment
 | 
			
		||||
                                                            }
 | 
			
		||||
                                                            permissions={
 | 
			
		||||
                                                                role?.permissions ||
 | 
			
		||||
                                                                []
 | 
			
		||||
                                                            }
 | 
			
		||||
                                                        />
 | 
			
		||||
                                                    </StyledDescriptionBlock>
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                            ))}
 | 
			
		||||
                                        </>
 | 
			
		||||
                                    }
 | 
			
		||||
                                />
 | 
			
		||||
                            </>
 | 
			
		||||
                        }
 | 
			
		||||
                        elseShow={
 | 
			
		||||
                            <>
 | 
			
		||||
                                <StyledDescriptionSubHeader>
 | 
			
		||||
                                    {accessRole?.name}
 | 
			
		||||
                                </StyledDescriptionSubHeader>
 | 
			
		||||
                                <StyledDescriptionBlock>
 | 
			
		||||
                                    {accessRole?.description}
 | 
			
		||||
                                </StyledDescriptionBlock>
 | 
			
		||||
                            </>
 | 
			
		||||
                        }
 | 
			
		||||
                    />
 | 
			
		||||
                </StyledDescription>
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
@ -1,26 +0,0 @@
 | 
			
		||||
interface IProjectRoleDescriptionEnvironmentPermissionsProps {
 | 
			
		||||
    environment: string;
 | 
			
		||||
    permissions: any[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ProjectRoleDescriptionEnvironmentPermissions = ({
 | 
			
		||||
    environment,
 | 
			
		||||
    permissions,
 | 
			
		||||
}: IProjectRoleDescriptionEnvironmentPermissionsProps) => (
 | 
			
		||||
    <>
 | 
			
		||||
        {[
 | 
			
		||||
            ...new Set(
 | 
			
		||||
                permissions
 | 
			
		||||
                    .filter(
 | 
			
		||||
                        (permission: any) =>
 | 
			
		||||
                            permission.environment === environment
 | 
			
		||||
                    )
 | 
			
		||||
                    .map((permission: any) => permission.displayName)
 | 
			
		||||
            ),
 | 
			
		||||
        ]
 | 
			
		||||
            .sort()
 | 
			
		||||
            .map((permission: any) => (
 | 
			
		||||
                <p key={`${environment}-${permission}`}>{permission}</p>
 | 
			
		||||
            ))}
 | 
			
		||||
    </>
 | 
			
		||||
);
 | 
			
		||||
@ -1,17 +0,0 @@
 | 
			
		||||
interface IProjectRoleDescriptionProjectPermissionsProps {
 | 
			
		||||
    permissions: any[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ProjectRoleDescriptionProjectPermissions = ({
 | 
			
		||||
    permissions,
 | 
			
		||||
}: IProjectRoleDescriptionProjectPermissionsProps) => (
 | 
			
		||||
    <>
 | 
			
		||||
        {permissions
 | 
			
		||||
            ?.filter((permission: any) => !permission.environment)
 | 
			
		||||
            .map((permission: any) => permission.displayName)
 | 
			
		||||
            .sort()
 | 
			
		||||
            .map((permission: any) => (
 | 
			
		||||
                <p key={permission}>{permission}</p>
 | 
			
		||||
            ))}
 | 
			
		||||
    </>
 | 
			
		||||
);
 | 
			
		||||
@ -1,40 +0,0 @@
 | 
			
		||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
 | 
			
		||||
import { VFC } from 'react';
 | 
			
		||||
import { ProjectRoleDescription } from 'component/project/ProjectAccess/ProjectAccessAssign/ProjectRoleDescription/ProjectRoleDescription';
 | 
			
		||||
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
 | 
			
		||||
 | 
			
		||||
interface IProjectAccessRoleCellProps {
 | 
			
		||||
    roleId: number;
 | 
			
		||||
    projectId: string;
 | 
			
		||||
    value?: string;
 | 
			
		||||
    emptyText?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ProjectAccessRoleCell: VFC<IProjectAccessRoleCellProps> = ({
 | 
			
		||||
    roleId,
 | 
			
		||||
    projectId,
 | 
			
		||||
    value,
 | 
			
		||||
    emptyText,
 | 
			
		||||
}) => {
 | 
			
		||||
    if (!value) return <TextCell>{emptyText}</TextCell>;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <TextCell>
 | 
			
		||||
            <TooltipLink
 | 
			
		||||
                tooltip={
 | 
			
		||||
                    <ProjectRoleDescription
 | 
			
		||||
                        roleId={roleId}
 | 
			
		||||
                        projectId={projectId}
 | 
			
		||||
                        popover
 | 
			
		||||
                    />
 | 
			
		||||
                }
 | 
			
		||||
                tooltipProps={{
 | 
			
		||||
                    maxWidth: 500,
 | 
			
		||||
                    maxHeight: 600,
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
                {value}
 | 
			
		||||
            </TooltipLink>
 | 
			
		||||
        </TextCell>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@ -43,7 +43,7 @@ import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton
 | 
			
		||||
import { ProjectAccessCreate } from 'component/project/ProjectAccess/ProjectAccessCreate/ProjectAccessCreate';
 | 
			
		||||
import { ProjectAccessEditUser } from 'component/project/ProjectAccess/ProjectAccessEditUser/ProjectAccessEditUser';
 | 
			
		||||
import { ProjectAccessEditGroup } from 'component/project/ProjectAccess/ProjectAccessEditGroup/ProjectAccessEditGroup';
 | 
			
		||||
import { ProjectAccessRoleCell } from './ProjectAccessRoleCell/ProjectAccessRoleCell';
 | 
			
		||||
import { RoleCell } from 'component/common/Table/cells/RoleCell/RoleCell';
 | 
			
		||||
import {
 | 
			
		||||
    PA_ASSIGN_BUTTON_ID,
 | 
			
		||||
    PA_EDIT_BUTTON_ID,
 | 
			
		||||
@ -168,11 +168,7 @@ export const ProjectAccessTable: VFC = () => {
 | 
			
		||||
                    access?.roles.find(({ id }) => id === row.entity.roleId)
 | 
			
		||||
                        ?.name,
 | 
			
		||||
                Cell: ({ value, row: { original: row } }: any) => (
 | 
			
		||||
                    <ProjectAccessRoleCell
 | 
			
		||||
                        roleId={row.entity.roleId}
 | 
			
		||||
                        projectId={projectId}
 | 
			
		||||
                        value={value}
 | 
			
		||||
                    />
 | 
			
		||||
                    <RoleCell roleId={row.entity.roleId} value={value} />
 | 
			
		||||
                ),
 | 
			
		||||
                maxWidth: 125,
 | 
			
		||||
                filterName: 'role',
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import useSWR, { mutate, SWRConfiguration } from 'swr';
 | 
			
		||||
import { useState, useEffect, useMemo } from 'react';
 | 
			
		||||
import { formatApiPath } from 'utils/formatPath';
 | 
			
		||||
import handleErrorResponses from '../httpErrorResponseHandler';
 | 
			
		||||
import { IProjectRole } from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
import { IGroup } from 'interfaces/group';
 | 
			
		||||
import { IUser } from 'interfaces/user';
 | 
			
		||||
import { mapGroupUsers } from '../useGroup/useGroup';
 | 
			
		||||
@ -30,7 +30,7 @@ export interface IProjectAccessGroup extends IGroup {
 | 
			
		||||
export interface IProjectAccessOutput {
 | 
			
		||||
    users: IProjectAccessUser[];
 | 
			
		||||
    groups: IProjectAccessGroup[];
 | 
			
		||||
    roles: IProjectRole[];
 | 
			
		||||
    roles: IRole[];
 | 
			
		||||
    rows: IProjectAccess[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import { SWRConfiguration } from 'swr';
 | 
			
		||||
import { useMemo } from 'react';
 | 
			
		||||
import { formatApiPath } from 'utils/formatPath';
 | 
			
		||||
import handleErrorResponses from '../httpErrorResponseHandler';
 | 
			
		||||
import IRole, { IRoleWithPermissions } from 'interfaces/role';
 | 
			
		||||
import { IRole, IRoleWithPermissions } from 'interfaces/role';
 | 
			
		||||
import useUiConfig from '../useUiConfig/useUiConfig';
 | 
			
		||||
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import IRole, { IProjectRole } from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
import { useMemo } from 'react';
 | 
			
		||||
import { formatApiPath } from 'utils/formatPath';
 | 
			
		||||
import handleErrorResponses from '../httpErrorResponseHandler';
 | 
			
		||||
@ -11,7 +11,15 @@ import {
 | 
			
		||||
    PREDEFINED_ROLE_TYPES,
 | 
			
		||||
} from '@server/util/constants';
 | 
			
		||||
 | 
			
		||||
export const useRoles = () => {
 | 
			
		||||
interface IUseRolesOutput {
 | 
			
		||||
    roles: IRole[];
 | 
			
		||||
    projectRoles: IRole[];
 | 
			
		||||
    loading: boolean;
 | 
			
		||||
    refetch: () => void;
 | 
			
		||||
    error?: Error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const useRoles = (): IUseRolesOutput => {
 | 
			
		||||
    const { isEnterprise, uiConfig } = useUiConfig();
 | 
			
		||||
 | 
			
		||||
    const { data, error, mutate } = useConditionalSWR(
 | 
			
		||||
@ -56,7 +64,7 @@ export const useRoles = () => {
 | 
			
		||||
                    .filter(({ type }: IRole) =>
 | 
			
		||||
                        PROJECT_ROLE_TYPES.includes(type)
 | 
			
		||||
                    )
 | 
			
		||||
                    .sort(sortRoles) ?? []) as IProjectRole[],
 | 
			
		||||
                    .sort(sortRoles) ?? []) as IRole[],
 | 
			
		||||
                loading: !error && !data,
 | 
			
		||||
                refetch: () => mutate(),
 | 
			
		||||
                error,
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
import { IServiceAccount } from 'interfaces/service-account';
 | 
			
		||||
import { useMemo } from 'react';
 | 
			
		||||
import { formatApiPath } from 'utils/formatPath';
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import { useMemo } from 'react';
 | 
			
		||||
import { formatApiPath } from 'utils/formatPath';
 | 
			
		||||
import handleErrorResponses from '../httpErrorResponseHandler';
 | 
			
		||||
import { IUser } from 'interfaces/user';
 | 
			
		||||
import IRole from 'interfaces/role';
 | 
			
		||||
import { IRole } from 'interfaces/role';
 | 
			
		||||
 | 
			
		||||
interface IUseUsersOutput {
 | 
			
		||||
    users: IUser[];
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import IRole from './role';
 | 
			
		||||
import { IRole } from './role';
 | 
			
		||||
 | 
			
		||||
export interface IProfile {
 | 
			
		||||
    rootRole: IRole;
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import IRole from './role';
 | 
			
		||||
import { IRole } from './role';
 | 
			
		||||
import { IUser } from './user';
 | 
			
		||||
 | 
			
		||||
export interface ICreateInvitedUser {
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ export type PredefinedRoleType =
 | 
			
		||||
    | typeof ROOT_ROLE_TYPE
 | 
			
		||||
    | typeof PROJECT_ROLE_TYPE;
 | 
			
		||||
 | 
			
		||||
interface IRole {
 | 
			
		||||
export interface IRole {
 | 
			
		||||
    id: number;
 | 
			
		||||
    name: string;
 | 
			
		||||
    project: string | null;
 | 
			
		||||
@ -16,12 +16,3 @@ interface IRole {
 | 
			
		||||
export interface IRoleWithPermissions extends IRole {
 | 
			
		||||
    permissions: IPermission[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IProjectRole {
 | 
			
		||||
    id: number;
 | 
			
		||||
    name: string;
 | 
			
		||||
    description: string;
 | 
			
		||||
    type: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default IRole;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user