mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-04 13:48:56 +02: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 useAuthSettings from 'hooks/api/getters/useAuthSettings/useAuthSettings';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
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 { useUsers } from 'hooks/api/getters/useUsers/useUsers';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { RoleSelect } from 'component/common/RoleSelect/RoleSelect';
|
import { RoleSelect } from 'component/common/RoleSelect/RoleSelect';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { IPermission, ICheckedPermissions } from 'interfaces/permissions';
|
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 { useRoles } from 'hooks/api/getters/useRoles/useRoles';
|
||||||
import { permissionsToCheckedPermissions } from 'utils/permissions';
|
import { permissionsToCheckedPermissions } from 'utils/permissions';
|
||||||
import { ROOT_ROLE_TYPE } from '@server/util/constants';
|
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 { Add } from '@mui/icons-material';
|
||||||
import { UPDATE_ROLE } from '@server/types/permissions';
|
import { UPDATE_ROLE } from '@server/types/permissions';
|
||||||
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
|
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
|
||||||
import IRole from 'interfaces/role';
|
import { IRole } from 'interfaces/role';
|
||||||
|
|
||||||
const StyledPageContent = styled(PageContent)(({ theme }) => ({
|
const StyledPageContent = styled(PageContent)(({ theme }) => ({
|
||||||
'& .page-header': {
|
'& .page-header': {
|
||||||
|
@ -3,7 +3,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
|||||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||||
import { useServiceAccounts } from 'hooks/api/getters/useServiceAccounts/useServiceAccounts';
|
import { useServiceAccounts } from 'hooks/api/getters/useServiceAccounts/useServiceAccounts';
|
||||||
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
|
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
|
||||||
import IRole from 'interfaces/role';
|
import { IRole } from 'interfaces/role';
|
||||||
import { RoleDeleteDialogUsers } from './RoleDeleteDialogUsers/RoleDeleteDialogUsers';
|
import { RoleDeleteDialogUsers } from './RoleDeleteDialogUsers/RoleDeleteDialogUsers';
|
||||||
import { RoleDeleteDialogServiceAccounts } from './RoleDeleteDialogServiceAccounts/RoleDeleteDialogServiceAccounts';
|
import { RoleDeleteDialogServiceAccounts } from './RoleDeleteDialogServiceAccounts/RoleDeleteDialogServiceAccounts';
|
||||||
import { useGroups } from 'hooks/api/getters/useGroups/useGroups';
|
import { useGroups } from 'hooks/api/getters/useGroups/useGroups';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { VFC } from 'react';
|
import { VFC } from 'react';
|
||||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||||
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
|
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 { useRole } from 'hooks/api/getters/useRole/useRole';
|
||||||
import { RoleDescription } from 'component/common/RoleDescription/RoleDescription';
|
import { RoleDescription } from 'component/common/RoleDescription/RoleDescription';
|
||||||
import { PREDEFINED_ROLE_TYPES } from '@server/util/constants';
|
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 { PREDEFINED_ROLE_TYPES } from '@server/util/constants';
|
||||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||||
import IRole from 'interfaces/role';
|
import { IRole } from 'interfaces/role';
|
||||||
import { VFC } from 'react';
|
import { VFC } from 'react';
|
||||||
|
|
||||||
const StyledBox = styled(Box)(() => ({
|
const StyledBox = styled(Box)(() => ({
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { Badge } from 'component/common/Badge/Badge';
|
import { Badge } from 'component/common/Badge/Badge';
|
||||||
import { styled } from '@mui/material';
|
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 { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
|
||||||
import { PREDEFINED_ROLE_TYPES } from '@server/util/constants';
|
import { PREDEFINED_ROLE_TYPES } from '@server/util/constants';
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
|
import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
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 useToast from 'hooks/useToast';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||||
|
@ -33,7 +33,7 @@ import { INewPersonalAPIToken } from 'interfaces/personalAPIToken';
|
|||||||
import { ServiceAccountTokens } from './ServiceAccountTokens/ServiceAccountTokens';
|
import { ServiceAccountTokens } from './ServiceAccountTokens/ServiceAccountTokens';
|
||||||
import { IServiceAccount } from 'interfaces/service-account';
|
import { IServiceAccount } from 'interfaces/service-account';
|
||||||
import { RoleSelect } from 'component/common/RoleSelect/RoleSelect';
|
import { RoleSelect } from 'component/common/RoleSelect/RoleSelect';
|
||||||
import IRole from 'interfaces/role';
|
import { IRole } from 'interfaces/role';
|
||||||
|
|
||||||
const StyledForm = styled('form')(() => ({
|
const StyledForm = styled('form')(() => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
|
import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import IRole from 'interfaces/role';
|
import { IRole } from 'interfaces/role';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||||
|
@ -5,7 +5,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
|||||||
import { EDIT } from 'constants/misc';
|
import { EDIT } from 'constants/misc';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { RoleSelect } from 'component/common/RoleSelect/RoleSelect';
|
import { RoleSelect } from 'component/common/RoleSelect/RoleSelect';
|
||||||
import IRole from 'interfaces/role';
|
import { IRole } from 'interfaces/role';
|
||||||
|
|
||||||
const StyledForm = styled('form')(() => ({
|
const StyledForm = styled('form')(() => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
@ -7,7 +7,7 @@ import ConfirmUserAdded from '../ConfirmUserAdded/ConfirmUserAdded';
|
|||||||
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
|
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
|
||||||
import useAdminUsersApi from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi';
|
import useAdminUsersApi from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi';
|
||||||
import { IUser } from 'interfaces/user';
|
import { IUser } from 'interfaces/user';
|
||||||
import IRole from 'interfaces/role';
|
import { IRole } from 'interfaces/role';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import { useUsersPlan } from 'hooks/useUsersPlan';
|
import { useUsersPlan } from 'hooks/useUsersPlan';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
|
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
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';
|
import { useRoles } from 'hooks/api/getters/useRoles/useRoles';
|
||||||
|
|
||||||
const useCreateUserForm = (
|
const useCreateUserForm = (
|
||||||
|
@ -5,9 +5,10 @@ import {
|
|||||||
styled,
|
styled,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useRoles } from 'hooks/api/getters/useRoles/useRoles';
|
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 { RoleDescription } from '../RoleDescription/RoleDescription';
|
||||||
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
|
||||||
|
import { ROOT_ROLE_TYPE } from '@server/util/constants';
|
||||||
|
|
||||||
const StyledRoleOption = styled('div')(({ theme }) => ({
|
const StyledRoleOption = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -20,18 +21,22 @@ const StyledRoleOption = styled('div')(({ theme }) => ({
|
|||||||
|
|
||||||
interface IRoleSelectProps
|
interface IRoleSelectProps
|
||||||
extends Partial<AutocompleteProps<IRole, false, false, false>> {
|
extends Partial<AutocompleteProps<IRole, false, false, false>> {
|
||||||
|
type?: PredefinedRoleType;
|
||||||
value: IRole | null;
|
value: IRole | null;
|
||||||
setValue: (role: IRole | null) => void;
|
setValue: (role: IRole | null) => void;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RoleSelect = ({
|
export const RoleSelect = ({
|
||||||
|
type = ROOT_ROLE_TYPE,
|
||||||
value,
|
value,
|
||||||
setValue,
|
setValue,
|
||||||
required,
|
required,
|
||||||
...rest
|
...rest
|
||||||
}: IRoleSelectProps) => {
|
}: IRoleSelectProps) => {
|
||||||
const { roles } = useRoles();
|
const { roles: rootRoles, projectRoles } = useRoles();
|
||||||
|
|
||||||
|
const roles = type === ROOT_ROLE_TYPE ? rootRoles : projectRoles;
|
||||||
|
|
||||||
const renderRoleOption = (
|
const renderRoleOption = (
|
||||||
props: React.HTMLAttributes<HTMLLIElement>,
|
props: React.HTMLAttributes<HTMLLIElement>,
|
||||||
|
@ -16,7 +16,7 @@ import useProjectAccess, {
|
|||||||
ENTITY_TYPE,
|
ENTITY_TYPE,
|
||||||
IProjectAccess,
|
IProjectAccess,
|
||||||
} from 'hooks/api/getters/useProjectAccess/useProjectAccess';
|
} from 'hooks/api/getters/useProjectAccess/useProjectAccess';
|
||||||
import { IProjectRole } from 'interfaces/role';
|
import { IRole } from 'interfaces/role';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
@ -25,7 +25,6 @@ import { formatUnknownError } from 'utils/formatUnknownError';
|
|||||||
import { IUser } from 'interfaces/user';
|
import { IUser } from 'interfaces/user';
|
||||||
import { IGroup } from 'interfaces/group';
|
import { IGroup } from 'interfaces/group';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { ProjectRoleDescription } from './ProjectRoleDescription/ProjectRoleDescription';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { GO_BACK } from 'constants/navigate';
|
import { GO_BACK } from 'constants/navigate';
|
||||||
import {
|
import {
|
||||||
@ -36,6 +35,8 @@ import {
|
|||||||
} from 'utils/testIds';
|
} from 'utils/testIds';
|
||||||
import { caseInsensitiveSearch } from 'utils/search';
|
import { caseInsensitiveSearch } from 'utils/search';
|
||||||
import { IServiceAccount } from 'interfaces/service-account';
|
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')(() => ({
|
const StyledForm = styled('form')(() => ({
|
||||||
display: 'flex',
|
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 {
|
interface IAccessOption {
|
||||||
id: number;
|
id: number;
|
||||||
entity: IUser | IGroup;
|
entity: IUser | IGroup;
|
||||||
@ -103,7 +95,7 @@ interface IProjectAccessAssignProps {
|
|||||||
users: IUser[];
|
users: IUser[];
|
||||||
serviceAccounts: IServiceAccount[];
|
serviceAccounts: IServiceAccount[];
|
||||||
groups: IGroup[];
|
groups: IGroup[];
|
||||||
roles: IProjectRole[];
|
roles: IRole[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProjectAccessAssign = ({
|
export const ProjectAccessAssign = ({
|
||||||
@ -190,7 +182,7 @@ export const ProjectAccessAssign = ({
|
|||||||
id === selected?.entity.id && type === selected?.type
|
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
|
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;
|
const isValid = selectedOptions.length > 0 && role;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -451,29 +431,13 @@ export const ProjectAccessAssign = ({
|
|||||||
Select the role to assign for this project
|
Select the role to assign for this project
|
||||||
</StyledInputDescription>
|
</StyledInputDescription>
|
||||||
<StyledAutocompleteWrapper>
|
<StyledAutocompleteWrapper>
|
||||||
<Autocomplete
|
<RoleSelect
|
||||||
data-testid={PA_ROLE_ID}
|
data-testid={PA_ROLE_ID}
|
||||||
size="small"
|
type={PROJECT_ROLE_TYPE}
|
||||||
openOnFocus
|
|
||||||
value={role}
|
value={role}
|
||||||
onChange={(_, newValue) => setRole(newValue)}
|
setValue={role => setRole(role || null)}
|
||||||
options={roles}
|
|
||||||
renderOption={renderRoleOption}
|
|
||||||
getOptionLabel={option => option.name}
|
|
||||||
renderInput={params => (
|
|
||||||
<TextField {...params} label="Role" />
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</StyledAutocompleteWrapper>
|
</StyledAutocompleteWrapper>
|
||||||
<ConditionallyRender
|
|
||||||
condition={Boolean(role?.id)}
|
|
||||||
show={
|
|
||||||
<ProjectRoleDescription
|
|
||||||
roleId={role?.id!}
|
|
||||||
projectId={projectId}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<StyledButtonContainer>
|
<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 { ProjectAccessCreate } from 'component/project/ProjectAccess/ProjectAccessCreate/ProjectAccessCreate';
|
||||||
import { ProjectAccessEditUser } from 'component/project/ProjectAccess/ProjectAccessEditUser/ProjectAccessEditUser';
|
import { ProjectAccessEditUser } from 'component/project/ProjectAccess/ProjectAccessEditUser/ProjectAccessEditUser';
|
||||||
import { ProjectAccessEditGroup } from 'component/project/ProjectAccess/ProjectAccessEditGroup/ProjectAccessEditGroup';
|
import { ProjectAccessEditGroup } from 'component/project/ProjectAccess/ProjectAccessEditGroup/ProjectAccessEditGroup';
|
||||||
import { ProjectAccessRoleCell } from './ProjectAccessRoleCell/ProjectAccessRoleCell';
|
import { RoleCell } from 'component/common/Table/cells/RoleCell/RoleCell';
|
||||||
import {
|
import {
|
||||||
PA_ASSIGN_BUTTON_ID,
|
PA_ASSIGN_BUTTON_ID,
|
||||||
PA_EDIT_BUTTON_ID,
|
PA_EDIT_BUTTON_ID,
|
||||||
@ -168,11 +168,7 @@ export const ProjectAccessTable: VFC = () => {
|
|||||||
access?.roles.find(({ id }) => id === row.entity.roleId)
|
access?.roles.find(({ id }) => id === row.entity.roleId)
|
||||||
?.name,
|
?.name,
|
||||||
Cell: ({ value, row: { original: row } }: any) => (
|
Cell: ({ value, row: { original: row } }: any) => (
|
||||||
<ProjectAccessRoleCell
|
<RoleCell roleId={row.entity.roleId} value={value} />
|
||||||
roleId={row.entity.roleId}
|
|
||||||
projectId={projectId}
|
|
||||||
value={value}
|
|
||||||
/>
|
|
||||||
),
|
),
|
||||||
maxWidth: 125,
|
maxWidth: 125,
|
||||||
filterName: 'role',
|
filterName: 'role',
|
||||||
|
@ -2,7 +2,7 @@ import useSWR, { mutate, SWRConfiguration } from 'swr';
|
|||||||
import { useState, useEffect, useMemo } from 'react';
|
import { useState, useEffect, useMemo } from 'react';
|
||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import { IProjectRole } from 'interfaces/role';
|
import { IRole } from 'interfaces/role';
|
||||||
import { IGroup } from 'interfaces/group';
|
import { IGroup } from 'interfaces/group';
|
||||||
import { IUser } from 'interfaces/user';
|
import { IUser } from 'interfaces/user';
|
||||||
import { mapGroupUsers } from '../useGroup/useGroup';
|
import { mapGroupUsers } from '../useGroup/useGroup';
|
||||||
@ -30,7 +30,7 @@ export interface IProjectAccessGroup extends IGroup {
|
|||||||
export interface IProjectAccessOutput {
|
export interface IProjectAccessOutput {
|
||||||
users: IProjectAccessUser[];
|
users: IProjectAccessUser[];
|
||||||
groups: IProjectAccessGroup[];
|
groups: IProjectAccessGroup[];
|
||||||
roles: IProjectRole[];
|
roles: IRole[];
|
||||||
rows: IProjectAccess[];
|
rows: IProjectAccess[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import { SWRConfiguration } from 'swr';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import IRole, { IRoleWithPermissions } from 'interfaces/role';
|
import { IRole, IRoleWithPermissions } from 'interfaces/role';
|
||||||
import useUiConfig from '../useUiConfig/useUiConfig';
|
import useUiConfig from '../useUiConfig/useUiConfig';
|
||||||
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
|
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 { useMemo } from 'react';
|
||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
@ -11,7 +11,15 @@ import {
|
|||||||
PREDEFINED_ROLE_TYPES,
|
PREDEFINED_ROLE_TYPES,
|
||||||
} from '@server/util/constants';
|
} 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 { isEnterprise, uiConfig } = useUiConfig();
|
||||||
|
|
||||||
const { data, error, mutate } = useConditionalSWR(
|
const { data, error, mutate } = useConditionalSWR(
|
||||||
@ -56,7 +64,7 @@ export const useRoles = () => {
|
|||||||
.filter(({ type }: IRole) =>
|
.filter(({ type }: IRole) =>
|
||||||
PROJECT_ROLE_TYPES.includes(type)
|
PROJECT_ROLE_TYPES.includes(type)
|
||||||
)
|
)
|
||||||
.sort(sortRoles) ?? []) as IProjectRole[],
|
.sort(sortRoles) ?? []) as IRole[],
|
||||||
loading: !error && !data,
|
loading: !error && !data,
|
||||||
refetch: () => mutate(),
|
refetch: () => mutate(),
|
||||||
error,
|
error,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import IRole from 'interfaces/role';
|
import { IRole } from 'interfaces/role';
|
||||||
import { IServiceAccount } from 'interfaces/service-account';
|
import { IServiceAccount } from 'interfaces/service-account';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
|
@ -3,7 +3,7 @@ import { useMemo } from 'react';
|
|||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import { IUser } from 'interfaces/user';
|
import { IUser } from 'interfaces/user';
|
||||||
import IRole from 'interfaces/role';
|
import { IRole } from 'interfaces/role';
|
||||||
|
|
||||||
interface IUseUsersOutput {
|
interface IUseUsersOutput {
|
||||||
users: IUser[];
|
users: IUser[];
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import IRole from './role';
|
import { IRole } from './role';
|
||||||
|
|
||||||
export interface IProfile {
|
export interface IProfile {
|
||||||
rootRole: IRole;
|
rootRole: IRole;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import IRole from './role';
|
import { IRole } from './role';
|
||||||
import { IUser } from './user';
|
import { IUser } from './user';
|
||||||
|
|
||||||
export interface ICreateInvitedUser {
|
export interface ICreateInvitedUser {
|
||||||
|
@ -5,7 +5,7 @@ export type PredefinedRoleType =
|
|||||||
| typeof ROOT_ROLE_TYPE
|
| typeof ROOT_ROLE_TYPE
|
||||||
| typeof PROJECT_ROLE_TYPE;
|
| typeof PROJECT_ROLE_TYPE;
|
||||||
|
|
||||||
interface IRole {
|
export interface IRole {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
project: string | null;
|
project: string | null;
|
||||||
@ -16,12 +16,3 @@ interface IRole {
|
|||||||
export interface IRoleWithPermissions extends IRole {
|
export interface IRoleWithPermissions extends IRole {
|
||||||
permissions: IPermission[];
|
permissions: IPermission[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IProjectRole {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default IRole;
|
|
||||||
|
Loading…
Reference in New Issue
Block a user