Role page various fixes (#12324)
Various fixes from fast follows - Sort roles by alphabetical order - Change some tooltips - During role creation, role should have all permissions enabled by default - Changed Permission icons design and refactored duplicating logic in a dedicated component - Changed "Revoked by" design - Display role icon in default role picker - Workspace member avatar was missing in role list and member picker - Set "seeded" member role as editable for new workspaces - Various css fixes
This commit is contained in:
@ -7,7 +7,7 @@ import { Select } from '@/ui/input/components/Select';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { H2Title, IconUserPin } from 'twenty-ui/display';
|
||||
import { H2Title, IconUserPin, useIcons } from 'twenty-ui/display';
|
||||
import { Card, Section } from 'twenty-ui/layout';
|
||||
import {
|
||||
Role,
|
||||
@ -51,10 +51,18 @@ export const SettingsRoleDefaultRole = ({
|
||||
});
|
||||
};
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
if (!currentWorkspace || !defaultRole) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const options = roles.map((role) => ({
|
||||
label: role.label,
|
||||
value: role.id,
|
||||
Icon: getIcon(role.icon),
|
||||
}));
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<H2Title
|
||||
@ -71,10 +79,7 @@ export const SettingsRoleDefaultRole = ({
|
||||
selectSizeVariant="small"
|
||||
withSearchInput
|
||||
dropdownId="default-role-select"
|
||||
options={roles.map((role) => ({
|
||||
label: role.label,
|
||||
value: role.id,
|
||||
}))}
|
||||
options={options}
|
||||
value={defaultRole?.id ?? ''}
|
||||
onChange={(value) =>
|
||||
updateDefaultRole(value as string, currentWorkspace)
|
||||
|
||||
@ -9,11 +9,12 @@ import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { H2Title, IconPlus } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { H2Title, IconPlus } from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { sortByAscString } from '~/utils/array/sortByAscString';
|
||||
|
||||
const StyledCreateRoleSection = styled(Section)`
|
||||
border-top: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
@ -40,6 +41,10 @@ export const SettingsRolesList = () => {
|
||||
|
||||
const settingsAllRoles = useRecoilValue(settingsAllRolesSelector);
|
||||
|
||||
const sortedSettingsAllRoles = [...settingsAllRoles].sort((a, b) =>
|
||||
sortByAscString(a.label, b.label),
|
||||
);
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<H2Title
|
||||
@ -49,10 +54,10 @@ export const SettingsRolesList = () => {
|
||||
<Table>
|
||||
<SettingsRolesTableHeader />
|
||||
<StyledTableRows>
|
||||
{settingsAllRoles.length === 0 ? (
|
||||
{sortedSettingsAllRoles.length === 0 ? (
|
||||
<StyledNoRoles>{t`No roles found`}</StyledNoRoles>
|
||||
) : (
|
||||
settingsAllRoles.map((role) => (
|
||||
sortedSettingsAllRoles.map((role) => (
|
||||
<SettingsRolesTableRow key={role.id} role={role} />
|
||||
))
|
||||
)}
|
||||
|
||||
@ -111,11 +111,8 @@ export const SettingsRolesTableRow = ({ role }: SettingsRolesTableRowProps) => {
|
||||
<TableCell align={'left'}>
|
||||
<StyledAssignedText>{role.workspaceMembers.length}</StyledAssignedText>
|
||||
</TableCell>
|
||||
<TableCell align={'right'}>
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.md}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
<TableCell align={'right'} color={theme.font.color.tertiary}>
|
||||
<IconChevronRight size={theme.icon.size.md} />
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
);
|
||||
|
||||
@ -243,6 +243,7 @@ export const SettingsRoleAssignment = ({
|
||||
<Dropdown
|
||||
dropdownId="role-member-select"
|
||||
dropdownHotkeyScope={{ scope: 'roleAssignment' }}
|
||||
dropdownOffset={{ x: 0, y: 4 }}
|
||||
clickableComponent={
|
||||
<>
|
||||
<div id="assign-member">
|
||||
@ -256,7 +257,7 @@ export const SettingsRoleAssignment = ({
|
||||
</div>
|
||||
<AppTooltip
|
||||
anchorSelect="#assign-member"
|
||||
content={t`No more members to assign`}
|
||||
content={t`The workspace needs at least one Admin`}
|
||||
delay={TooltipDelay.noDelay}
|
||||
hidden={!allWorkspaceMembersHaveThisRole}
|
||||
/>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import styled from '@emotion/styled';
|
||||
import { WorkspaceMember } from '~/generated-metadata/graphql';
|
||||
import { Avatar, OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
import { WorkspaceMember } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledIconWrapper = styled.div`
|
||||
align-items: center;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { SearchRecord } from '~/generated-metadata/graphql';
|
||||
import { MenuItem, MenuItemAvatar } from 'twenty-ui/navigation';
|
||||
import { SearchRecord } from '~/generated-metadata/graphql';
|
||||
|
||||
type SettingsRoleAssignmentWorkspaceMemberPickerDropdownContentProps = {
|
||||
loading: boolean;
|
||||
@ -34,6 +34,7 @@ export const SettingsRoleAssignmentWorkspaceMemberPickerDropdownContent = ({
|
||||
size: 'md',
|
||||
placeholder: workspaceMember.label ?? '',
|
||||
placeholderColorSeed: workspaceMember.recordId,
|
||||
avatarUrl: workspaceMember.imageUrl,
|
||||
}}
|
||||
text={workspaceMember.label}
|
||||
/>
|
||||
|
||||
@ -1,34 +1,12 @@
|
||||
import { PermissionIcon } from '@/settings/roles/role-permissions/objects-permissions/components/PermissionIcon';
|
||||
import { SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectLevelPermissionToRoleObjectPermissionMapping';
|
||||
import { SETTINGS_ROLE_OBJECT_PERMISSION_ICON_CONFIG } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig';
|
||||
import { SettingsRoleObjectPermissionKey } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig';
|
||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
const StyledIconWrapper = styled.div<{ isForbidden?: boolean }>`
|
||||
align-items: center;
|
||||
background: ${({ theme, isForbidden }) =>
|
||||
isForbidden ? theme.adaptiveColors.orange1 : theme.adaptiveColors.blue1};
|
||||
border: 1px solid
|
||||
${({ theme, isForbidden }) =>
|
||||
isForbidden ? theme.adaptiveColors.orange3 : theme.adaptiveColors.blue3};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
display: flex;
|
||||
height: ${({ theme }) => theme.spacing(4)};
|
||||
justify-content: center;
|
||||
width: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
const StyledIcon = styled.div<{ isForbidden?: boolean }>`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
color: ${({ theme, isForbidden }) =>
|
||||
isForbidden ? theme.color.orange : theme.color.blue};
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const StyledSettingsRolePermissionsObjectLevelOverrideCell = styled.div`
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
@ -43,8 +21,6 @@ export const SettingsRolePermissionsObjectLevelOverrideCell = ({
|
||||
objectPermission,
|
||||
roleId,
|
||||
}: SettingsRolePermissionsObjectLevelOverrideCellProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const settingsDraftRole = useRecoilValue(
|
||||
settingsDraftRoleFamilyState(roleId),
|
||||
);
|
||||
@ -52,44 +28,33 @@ export const SettingsRolePermissionsObjectLevelOverrideCell = ({
|
||||
const permissionMappings =
|
||||
SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING;
|
||||
|
||||
type ObjectPermissionKey = keyof typeof permissionMappings;
|
||||
|
||||
const isOverridden = (permission: ObjectPermissionKey) => {
|
||||
const isOverridden = (permission: SettingsRoleObjectPermissionKey) => {
|
||||
const rolePermission = permissionMappings[permission];
|
||||
return (
|
||||
isDefined(objectPermission[permission]) &&
|
||||
!!settingsDraftRole[rolePermission as keyof typeof settingsDraftRole] !==
|
||||
!!objectPermission[permission]
|
||||
!!settingsDraftRole[rolePermission] !== !!objectPermission[permission]
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledSettingsRolePermissionsObjectLevelOverrideCell>
|
||||
{(Object.keys(permissionMappings) as ObjectPermissionKey[]).map(
|
||||
(permission) => {
|
||||
const { Icon, IconForbidden: IconOverride } =
|
||||
SETTINGS_ROLE_OBJECT_PERMISSION_ICON_CONFIG[permission];
|
||||
const permissionValue = objectPermission[permission];
|
||||
{(
|
||||
Object.keys(permissionMappings) as SettingsRoleObjectPermissionKey[]
|
||||
).map((permission) => {
|
||||
const permissionValue = objectPermission[permission];
|
||||
|
||||
if (!isOverridden(permission)) {
|
||||
return null;
|
||||
}
|
||||
if (!isOverridden(permission)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledIconWrapper
|
||||
key={permission}
|
||||
isForbidden={permissionValue === false}
|
||||
>
|
||||
<StyledIcon isForbidden={permissionValue === false}>
|
||||
{permissionValue === false && (
|
||||
<IconOverride size={theme.icon.size.sm} />
|
||||
)}
|
||||
{permissionValue === true && <Icon size={theme.icon.size.sm} />}
|
||||
</StyledIcon>
|
||||
</StyledIconWrapper>
|
||||
);
|
||||
},
|
||||
)}
|
||||
return (
|
||||
<PermissionIcon
|
||||
key={permission}
|
||||
permission={permission}
|
||||
state={permissionValue === false ? 'revoked' : 'granted'}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</StyledSettingsRolePermissionsObjectLevelOverrideCell>
|
||||
);
|
||||
};
|
||||
|
||||
@ -130,6 +130,7 @@ export const SettingsRolePermissionsObjectLevelSection = ({
|
||||
disabled={!isEditable}
|
||||
/>
|
||||
}
|
||||
dropdownOffset={{ x: 0, y: 4 }}
|
||||
dropdownComponents={
|
||||
<SettingsRolePermissionsObjectLevelObjectPickerDropdownContent
|
||||
excludedObjectMetadataIds={
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { OverridableCheckbox } from '@/settings/roles/role-permissions/object-level-permissions/object-form/components/OverridableCheckbox';
|
||||
import { PermissionIcon } from '@/settings/roles/role-permissions/objects-permissions/components/PermissionIcon';
|
||||
import { SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectLevelPermissionToRoleObjectPermissionMapping';
|
||||
import { SETTINGS_ROLE_OBJECT_PERMISSION_ICON_CONFIG } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig';
|
||||
import { SettingsRoleObjectPermissionKey } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig';
|
||||
import { SettingsRolePermissionsObjectLevelPermission } from '@/settings/roles/role-permissions/objects-permissions/types/SettingsRolePermissionsObjectPermission';
|
||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
@ -13,25 +13,36 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { ObjectPermission } from '~/generated-metadata/graphql';
|
||||
import type { Role } from '~/generated/graphql';
|
||||
|
||||
const StyledLabel = styled.span`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
`;
|
||||
|
||||
const StyledOverrideInfo = styled.span`
|
||||
background: ${({ theme }) => theme.adaptiveColors.orange1};
|
||||
border-radius: ${({ theme }) => theme.spacing(1)};
|
||||
color: ${({ theme }) => theme.color.orange};
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const StyledPermissionCell = styled(TableCell)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledPermissionContent = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledPermissionLabel = styled.span`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
`;
|
||||
|
||||
const StyledOverrideInfo = styled.div`
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledCheckboxCell = styled(TableCell)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@ -39,11 +50,6 @@ const StyledCheckboxCell = styled(TableCell)`
|
||||
padding-right: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
type OverridableCheckboxType = 'no_cta' | 'default' | 'override';
|
||||
|
||||
type SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRowProps = {
|
||||
@ -60,17 +66,12 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow =
|
||||
settingsDraftRoleObjectPermissions,
|
||||
roleId,
|
||||
}: SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRowProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const settingsDraftRole = useRecoilValue(
|
||||
settingsDraftRoleFamilyState(roleId),
|
||||
);
|
||||
|
||||
const label = permission.label;
|
||||
|
||||
const { Icon } =
|
||||
SETTINGS_ROLE_OBJECT_PERMISSION_ICON_CONFIG[permission.key];
|
||||
|
||||
const permissionMappings =
|
||||
SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING;
|
||||
|
||||
@ -120,13 +121,21 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow =
|
||||
return (
|
||||
<StyledTableRow>
|
||||
<StyledPermissionCell>
|
||||
<Icon size={theme.icon.size.sm} />
|
||||
<StyledLabel>{label}</StyledLabel>
|
||||
{isRevoked ? (
|
||||
<StyledOverrideInfo>
|
||||
{t`Revoked for this object`}
|
||||
</StyledOverrideInfo>
|
||||
) : null}
|
||||
<StyledPermissionContent>
|
||||
<PermissionIcon
|
||||
permission={permission.key as SettingsRoleObjectPermissionKey}
|
||||
state={isRevoked ? 'revoked' : 'granted'}
|
||||
/>
|
||||
<StyledPermissionLabel>{label}</StyledPermissionLabel>
|
||||
</StyledPermissionContent>
|
||||
<StyledOverrideInfo>
|
||||
{isRevoked ? (
|
||||
<>
|
||||
{' · '}
|
||||
{t`Revoked for this object`}
|
||||
</>
|
||||
) : null}
|
||||
</StyledOverrideInfo>
|
||||
</StyledPermissionCell>
|
||||
<StyledCheckboxCell>
|
||||
<OverridableCheckbox
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
import {
|
||||
SETTINGS_ROLE_OBJECT_PERMISSION_ICON_CONFIG,
|
||||
SettingsRoleObjectPermissionKey,
|
||||
} from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
type PermissionIconProps = {
|
||||
permission: SettingsRoleObjectPermissionKey;
|
||||
state: 'granted' | 'revoked';
|
||||
};
|
||||
|
||||
const StyledIconWrapper = styled.div<{ isRevoked?: boolean }>`
|
||||
align-items: center;
|
||||
background: ${({ theme, isRevoked }) =>
|
||||
isRevoked ? theme.adaptiveColors.orange1 : theme.adaptiveColors.blue1};
|
||||
border: 1px solid
|
||||
${({ theme, isRevoked }) =>
|
||||
isRevoked ? theme.adaptiveColors.orange3 : theme.adaptiveColors.blue3};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
display: flex;
|
||||
height: ${({ theme }) => theme.spacing(4)};
|
||||
justify-content: center;
|
||||
width: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
const StyledIcon = styled.div<{ isRevoked?: boolean }>`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
color: ${({ theme, isRevoked }) =>
|
||||
isRevoked ? theme.color.orange : theme.color.blue};
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
export const PermissionIcon = ({ permission, state }: PermissionIconProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const { Icon, IconForbidden } =
|
||||
SETTINGS_ROLE_OBJECT_PERMISSION_ICON_CONFIG[permission];
|
||||
|
||||
const isRevoked = state === 'revoked';
|
||||
|
||||
return (
|
||||
<StyledIconWrapper isRevoked={isRevoked}>
|
||||
<StyledIcon isRevoked={isRevoked}>
|
||||
{isRevoked && <IconForbidden size={theme.icon.size.sm} />}
|
||||
{!isRevoked && <Icon size={theme.icon.size.sm} />}
|
||||
</StyledIcon>
|
||||
</StyledIconWrapper>
|
||||
);
|
||||
};
|
||||
@ -1,32 +1,37 @@
|
||||
import { SETTINGS_ROLE_OBJECT_PERMISSION_ICON_CONFIG } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig';
|
||||
import { PermissionIcon } from '@/settings/roles/role-permissions/objects-permissions/components/PermissionIcon';
|
||||
import { SettingsRoleObjectPermissionKey } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig';
|
||||
import { SettingsRolePermissionsObjectPermission } from '@/settings/roles/role-permissions/objects-permissions/types/SettingsRolePermissionsObjectPermission';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import pluralize from 'pluralize';
|
||||
import { Checkbox, CheckboxAccent } from 'twenty-ui/input';
|
||||
|
||||
const StyledLabel = styled.span`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
`;
|
||||
|
||||
const StyledOverrideInfo = styled.span`
|
||||
background: ${({ theme }) => theme.adaptiveColors.orange1};
|
||||
border-radius: ${({ theme }) => theme.spacing(1)};
|
||||
color: ${({ theme }) => theme.color.orange};
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledPermissionCell = styled(TableCell)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledPermissionContent = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledPermissionLabel = styled.span`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
`;
|
||||
|
||||
const StyledOverrideInfo = styled.div`
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
const StyledCheckboxCell = styled(TableCell)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@ -48,25 +53,30 @@ export const SettingsRolePermissionsObjectsTableRow = ({
|
||||
permission,
|
||||
isEditable,
|
||||
}: SettingsRolePermissionsObjectsTableRowProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const revokedBy = permission.revokedBy;
|
||||
const isRevoked = revokedBy && revokedBy > 0;
|
||||
const isRevoked =
|
||||
revokedBy !== undefined && revokedBy !== null && revokedBy > 0;
|
||||
const label = permission.label;
|
||||
const pluralizedObject = pluralize('object', revokedBy);
|
||||
|
||||
const { Icon } = SETTINGS_ROLE_OBJECT_PERMISSION_ICON_CONFIG[permission.key];
|
||||
|
||||
return (
|
||||
<StyledTableRow>
|
||||
<StyledPermissionCell>
|
||||
<Icon size={theme.icon.size.sm} />
|
||||
<StyledLabel>{label}</StyledLabel>
|
||||
{isRevoked ? (
|
||||
<StyledOverrideInfo>
|
||||
{t`Revoked on ${revokedBy} ${pluralizedObject}`}
|
||||
</StyledOverrideInfo>
|
||||
) : null}
|
||||
<StyledPermissionContent>
|
||||
<PermissionIcon
|
||||
permission={permission.key as SettingsRoleObjectPermissionKey}
|
||||
state={isRevoked ? 'revoked' : 'granted'}
|
||||
/>
|
||||
<StyledPermissionLabel>{label}</StyledPermissionLabel>
|
||||
</StyledPermissionContent>
|
||||
<StyledOverrideInfo>
|
||||
{isRevoked ? (
|
||||
<>
|
||||
{' · '}
|
||||
{t`Revoked on ${revokedBy} ${pluralizedObject}`}
|
||||
</>
|
||||
) : null}
|
||||
</StyledOverrideInfo>
|
||||
</StyledPermissionCell>
|
||||
<StyledCheckboxCell>
|
||||
<Checkbox
|
||||
|
||||
@ -14,8 +14,14 @@ type SettingsRoleObjectPermissionIconConfig = {
|
||||
IconForbidden: IconComponent;
|
||||
};
|
||||
|
||||
export type SettingsRoleObjectPermissionKey =
|
||||
| 'canReadObjectRecords'
|
||||
| 'canUpdateObjectRecords'
|
||||
| 'canSoftDeleteObjectRecords'
|
||||
| 'canDestroyObjectRecords';
|
||||
|
||||
export const SETTINGS_ROLE_OBJECT_PERMISSION_ICON_CONFIG: Record<
|
||||
string,
|
||||
SettingsRoleObjectPermissionKey,
|
||||
SettingsRoleObjectPermissionIconConfig
|
||||
> = {
|
||||
canReadObjectRecords: {
|
||||
|
||||
@ -34,11 +34,11 @@ export const SettingsRoleCreateEffect = ({
|
||||
label: '',
|
||||
description: '',
|
||||
icon: 'IconUser',
|
||||
canUpdateAllSettings: false,
|
||||
canReadAllObjectRecords: false,
|
||||
canUpdateAllObjectRecords: false,
|
||||
canSoftDeleteAllObjectRecords: false,
|
||||
canDestroyAllObjectRecords: false,
|
||||
canUpdateAllSettings: true,
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: true,
|
||||
canSoftDeleteAllObjectRecords: true,
|
||||
canDestroyAllObjectRecords: true,
|
||||
isEditable: true,
|
||||
workspaceMembers: [],
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
@ -26,6 +27,7 @@ import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/wor
|
||||
ObjectPermissionModule,
|
||||
SettingPermissionModule,
|
||||
WorkspacePermissionsCacheModule,
|
||||
FileModule,
|
||||
],
|
||||
providers: [RoleService, RoleResolver],
|
||||
exports: [RoleService],
|
||||
|
||||
@ -8,8 +8,12 @@ import {
|
||||
Resolver,
|
||||
} from '@nestjs/graphql';
|
||||
|
||||
import { buildSignedPath } from 'twenty-shared/utils';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||
import { extractFilenameFromPath } from 'src/engine/core-modules/file/utils/extract-file-id-from-path.utils';
|
||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||
import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
@ -47,6 +51,7 @@ export class RoleResolver {
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly objectPermissionService: ObjectPermissionService,
|
||||
private readonly settingPermissionService: SettingPermissionService,
|
||||
private readonly fileService: FileService,
|
||||
) {}
|
||||
|
||||
@Query(() => [RoleDTO])
|
||||
@ -180,10 +185,29 @@ export class RoleResolver {
|
||||
@Parent() role: RoleDTO,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<WorkspaceMemberWorkspaceEntity[]> {
|
||||
return this.userRoleService.getWorkspaceMembersAssignedToRole(
|
||||
role.id,
|
||||
workspace.id,
|
||||
const workspaceMembers =
|
||||
await this.userRoleService.getWorkspaceMembersAssignedToRole(
|
||||
role.id,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
workspaceMembers.map(async (workspaceMember) => {
|
||||
if (workspaceMember && workspaceMember.avatarUrl) {
|
||||
const avatarUrlToken = this.fileService.encodeFileToken({
|
||||
filename: extractFilenameFromPath(workspaceMember.avatarUrl),
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
workspaceMember.avatarUrl = buildSignedPath({
|
||||
path: workspaceMember.avatarUrl,
|
||||
token: avatarUrlToken,
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return workspaceMembers;
|
||||
}
|
||||
|
||||
private async validatePermissionsV2EnabledOrThrow(workspace: Workspace) {
|
||||
|
||||
@ -218,7 +218,7 @@ export class RoleService {
|
||||
canUpdateAllObjectRecords: true,
|
||||
canSoftDeleteAllObjectRecords: true,
|
||||
canDestroyAllObjectRecords: true,
|
||||
isEditable: false,
|
||||
isEditable: true,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user