Skip to content

Commit 7687f4f

Browse files
authored
[permissions][FE] followup design fixes 4 (#12737)
## Context - Whole row is now clickable - Fix padding on role tables - Fix tab being persistant between roles - Change various texts/descriptions - Add un/check all on settings permissions - Fix flash between role detail and roles - Add "Granted for X object(s)" - Swap permissions and assignment tabs position - add tooltip for object level permission actions - Add the inherited info on object-level permissions
1 parent 57abc24 commit 7687f4f

22 files changed

+385
-138
lines changed

packages/twenty-front/src/modules/settings/roles/components/SettingsRolesContainer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const SettingsRolesContainer = () => {
1919
const settingsAllRoles = useRecoilValue(settingsAllRolesSelector);
2020
const settingsRolesIsLoading = useRecoilValue(settingsRolesIsLoadingState);
2121

22-
if (settingsRolesIsLoading) {
22+
if (settingsRolesIsLoading && !settingsAllRoles) {
2323
return null;
2424
}
2525

packages/twenty-front/src/modules/settings/roles/components/SettingsRolesDefaultRole.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,13 @@ export const SettingsRoleDefaultRole = ({
6060
const options = roles.map((role) => ({
6161
label: role.label,
6262
value: role.id,
63-
Icon: getIcon(role.icon),
63+
Icon: getIcon(role.icon) ?? IconUserPin,
6464
}));
6565

66+
if (options.length === 0) {
67+
return null;
68+
}
69+
6670
return (
6771
<Section>
6872
<H2Title

packages/twenty-front/src/modules/settings/roles/components/SettingsRolesList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const SettingsRolesList = () => {
4949
<Section>
5050
<H2Title
5151
title={t`All roles`}
52-
description={t`Assign roles to specify each member's access permissions`}
52+
description={t`Assign roles to manage each members access and permissions`}
5353
/>
5454
<Table>
5555
<SettingsRolesTableHeader />

packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelObjectPicker.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({
113113
[objectMetadataItems, searchFilter, excludedObjectMetadataIds],
114114
);
115115

116-
const basicObjects = filteredObjectMetadataItems.filter(
116+
const standardObjects = filteredObjectMetadataItems.filter(
117117
(item) => !item.isCustom,
118118
);
119119
const customObjects = filteredObjectMetadataItems.filter(
@@ -135,11 +135,14 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({
135135
</StyledSearchContainer>
136136
</Section>
137137

138-
{basicObjects.length > 0 && (
138+
{standardObjects.length > 0 && (
139139
<Section>
140-
<H2Title title={t`Basics`} description={t`All the basic objects`} />
140+
<H2Title
141+
title={t`Standard`}
142+
description={t`All the standard objects`}
143+
/>
141144
<StyledContainer>
142-
{basicObjects.map((objectMetadataItem) => {
145+
{standardObjects.map((objectMetadataItem) => {
143146
const Icon = getIcon(objectMetadataItem.icon);
144147
return (
145148
<StyledCardContainer
@@ -151,7 +154,7 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({
151154
<SettingsCard
152155
Icon={
153156
<Icon
154-
size={theme.icon.size.xl}
157+
size={theme.icon.size.lg}
155158
stroke={theme.icon.stroke.sm}
156159
/>
157160
}
@@ -181,7 +184,7 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({
181184
key={objectMetadataItem.id}
182185
Icon={
183186
<Icon
184-
size={theme.icon.size.xl}
187+
size={theme.icon.size.lg}
185188
stroke={theme.icon.stroke.sm}
186189
/>
187190
}
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,83 @@
1+
import { objectPermissionKeyToHumanReadable } from '@/settings/roles/role-permissions/object-level-permissions/utils/objectPermissionKeyToHumanReadableText';
12
import { PermissionIcon } from '@/settings/roles/role-permissions/objects-permissions/components/PermissionIcon';
23
import { SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectLevelPermissionToRoleObjectPermissionMapping';
34
import { SettingsRoleObjectPermissionKey } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig';
45
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
56
import styled from '@emotion/styled';
7+
import { t } from '@lingui/core/macro';
68
import { useRecoilValue } from 'recoil';
79
import { isDefined } from 'twenty-shared/utils';
10+
import { AppTooltip, TooltipDelay } from 'twenty-ui/display';
811
import { ObjectPermission } from '~/generated/graphql';
912

10-
const StyledSettingsRolePermissionsObjectLevelOverrideCell = styled.div`
13+
const StyledContainer = styled.div`
1114
display: flex;
12-
gap: ${({ theme }) => theme.spacing(1)};
1315
`;
1416

1517
type SettingsRolePermissionsObjectLevelOverrideCellProps = {
16-
objectPermission: ObjectPermission;
18+
objectPermissions: ObjectPermission;
19+
objectPermissionKey: SettingsRoleObjectPermissionKey;
1720
roleId: string;
21+
objectLabel: string;
1822
};
1923

2024
export const SettingsRolePermissionsObjectLevelOverrideCell = ({
21-
objectPermission,
25+
objectPermissions,
26+
objectPermissionKey,
2227
roleId,
28+
objectLabel,
2329
}: SettingsRolePermissionsObjectLevelOverrideCellProps) => {
2430
const settingsDraftRole = useRecoilValue(
2531
settingsDraftRoleFamilyState(roleId),
2632
);
2733

34+
const roleLabel = settingsDraftRole.label;
35+
2836
const permissionMappings =
2937
SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING;
3038

31-
const isOverridden = (permission: SettingsRoleObjectPermissionKey) => {
32-
const rolePermission = permissionMappings[permission];
39+
const permissionValue = objectPermissions[objectPermissionKey];
40+
41+
const isOverridden = (
42+
objectPermissionKey: SettingsRoleObjectPermissionKey,
43+
) => {
44+
const rolePermission = permissionMappings[objectPermissionKey];
45+
3346
return (
34-
isDefined(objectPermission[permission]) &&
35-
!!settingsDraftRole[rolePermission] !== !!objectPermission[permission]
47+
isDefined(permissionValue) &&
48+
!!settingsDraftRole[rolePermission] !== !!permissionValue
3649
);
3750
};
3851

52+
if (!isOverridden(objectPermissionKey)) {
53+
return null;
54+
}
55+
56+
const humanReadableAction =
57+
objectPermissionKeyToHumanReadable(objectPermissionKey);
58+
59+
const containerId = `object-level-permission-override-${roleId}-${objectPermissionKey}`;
60+
3961
return (
40-
<StyledSettingsRolePermissionsObjectLevelOverrideCell>
41-
{(
42-
Object.keys(permissionMappings) as SettingsRoleObjectPermissionKey[]
43-
).map((permission) => {
44-
const permissionValue = objectPermission[permission];
45-
46-
if (!isOverridden(permission)) {
47-
return null;
62+
<>
63+
<StyledContainer id={containerId}>
64+
<PermissionIcon
65+
permission={objectPermissionKey}
66+
state={permissionValue === false ? 'revoked' : 'granted'}
67+
/>
68+
</StyledContainer>
69+
<AppTooltip
70+
anchorSelect={`#${containerId}`}
71+
content={
72+
permissionValue === false
73+
? t`${roleLabel} can't ${humanReadableAction} ${objectLabel} records`
74+
: t`${roleLabel} can ${humanReadableAction} ${objectLabel} records`
4875
}
49-
50-
return (
51-
<PermissionIcon
52-
key={permission}
53-
permission={permission}
54-
state={permissionValue === false ? 'revoked' : 'granted'}
55-
/>
56-
);
57-
})}
58-
</StyledSettingsRolePermissionsObjectLevelOverrideCell>
76+
delay={TooltipDelay.shortDelay}
77+
noArrow
78+
place="bottom"
79+
positionStrategy="fixed"
80+
/>
81+
</>
5982
);
6083
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectLevelPermissionToRoleObjectPermissionMapping';
2+
import { SettingsRoleObjectPermissionKey } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig';
3+
import styled from '@emotion/styled';
4+
import { ObjectPermission } from '~/generated/graphql';
5+
import { SettingsRolePermissionsObjectLevelOverrideCell } from './SettingsRolePermissionsObjectLevelOverrideCell';
6+
7+
const StyledSettingsRolePermissionsObjectLevelOverrideCell = styled.div`
8+
display: flex;
9+
gap: ${({ theme }) => theme.spacing(1)};
10+
`;
11+
12+
type SettingsRolePermissionsObjectLevelOverrideCellContainerProps = {
13+
objectPermissions: ObjectPermission;
14+
roleId: string;
15+
objectLabel: string;
16+
};
17+
18+
export const SettingsRolePermissionsObjectLevelOverrideCellContainer = ({
19+
objectPermissions,
20+
roleId,
21+
objectLabel,
22+
}: SettingsRolePermissionsObjectLevelOverrideCellContainerProps) => {
23+
const permissionMappings =
24+
SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING;
25+
26+
return (
27+
<StyledSettingsRolePermissionsObjectLevelOverrideCell>
28+
{(
29+
Object.keys(permissionMappings) as SettingsRoleObjectPermissionKey[]
30+
).map((permissionKey) => {
31+
return (
32+
<SettingsRolePermissionsObjectLevelOverrideCell
33+
key={permissionKey}
34+
objectPermissions={objectPermissions}
35+
objectPermissionKey={permissionKey}
36+
roleId={roleId}
37+
objectLabel={objectLabel}
38+
/>
39+
);
40+
})}
41+
</StyledSettingsRolePermissionsObjectLevelOverrideCell>
42+
);
43+
};

packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSection.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ export const SettingsRolePermissionsObjectLevelSection = ({
8484
return (
8585
<Section>
8686
<H2Title
87-
title={t`Object-Level Permissions`}
88-
description={t`Ability to interact with specific objects`}
87+
title={t`Object-Level`}
88+
description={t`Actions users can perform on specific objects`}
8989
/>
9090
<Table>
9191
<SettingsRolePermissionsObjectLevelTableHeader />
@@ -103,7 +103,7 @@ export const SettingsRolePermissionsObjectLevelSection = ({
103103
/>
104104
))
105105
) : (
106-
<StyledNoOverride>{t`No overrides found`}</StyledNoOverride>
106+
<StyledNoOverride>{t`No permissions found`}</StyledNoOverride>
107107
)}
108108
</StyledTableRows>
109109
</Table>

packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
2-
import { SettingsRolePermissionsObjectLevelOverrideCell } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCell';
2+
import { SettingsRolePermissionsObjectLevelOverrideCellContainer } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCellContainer';
33
import { SettingsPath } from '@/types/SettingsPath';
44
import { TableCell } from '@/ui/layout/table/components/TableCell';
55
import { TableRow } from '@/ui/layout/table/components/TableRow';
@@ -44,6 +44,8 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({
4444

4545
const Icon = getIcon(objectMetadataItem.icon);
4646

47+
const objectLabel = objectMetadataItem.labelPlural;
48+
4749
return (
4850
<TableRow
4951
to={getSettingsPath(SettingsPath.RoleObjectLevel, {
@@ -60,14 +62,15 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({
6062
stroke={theme.icon.stroke.sm}
6163
/>
6264
)}
63-
<StyledNameLabel title={objectMetadataItem.labelPlural}>
64-
<OverflowingTextWithTooltip text={objectMetadataItem.labelPlural} />
65+
<StyledNameLabel title={objectLabel}>
66+
<OverflowingTextWithTooltip text={objectLabel} />
6567
</StyledNameLabel>
6668
</StyledNameTableCell>
6769
<TableCell>
68-
<SettingsRolePermissionsObjectLevelOverrideCell
69-
objectPermission={objectPermission}
70+
<SettingsRolePermissionsObjectLevelOverrideCellContainer
71+
objectPermissions={objectPermission}
7072
roleId={roleId}
73+
objectLabel={objectLabel}
7174
/>
7275
</TableCell>
7376
<TableCell align={'right'}>

packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevel = ({
120120
return (
121121
<Section>
122122
<H2Title
123-
title={t`Object-Level Permissions`}
124-
description={t`Ability to interact with this specific object`}
123+
title={t`Object-Level`}
124+
description={t`Actions users can perform on this object`}
125125
/>
126126
<StyledTable>
127127
<SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableHeader />

packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow.tsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { OverridableCheckbox } from '@/settings/roles/role-permissions/object-level-permissions/object-form/components/OverridableCheckbox';
2+
import { objectPermissionKeyToHumanReadable } from '@/settings/roles/role-permissions/object-level-permissions/utils/objectPermissionKeyToHumanReadableText';
23
import { PermissionIcon } from '@/settings/roles/role-permissions/objects-permissions/components/PermissionIcon';
34
import { SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectLevelPermissionToRoleObjectPermissionMapping';
45
import { SettingsRoleObjectPermissionKey } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig';
@@ -13,9 +14,10 @@ import { isDefined } from 'twenty-shared/utils';
1314
import { ObjectPermission } from '~/generated-metadata/graphql';
1415
import type { Role } from '~/generated/graphql';
1516

16-
const StyledTableRow = styled(TableRow)`
17+
const StyledTableRow = styled(TableRow)<{ isDisabled: boolean }>`
1718
align-items: center;
1819
display: flex;
20+
cursor: ${({ isDisabled }) => (isDisabled ? 'default' : 'pointer')};
1921
`;
2022

2123
const StyledPermissionCell = styled(TableCell)`
@@ -93,6 +95,15 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow =
9395
settingsDraftRoleGlobalPermissionValue === true &&
9496
isChecked === false;
9597

98+
const isGranted =
99+
isDefined(settingsDraftRoleObjectPermissionValue) &&
100+
settingsDraftRoleGlobalPermissionValue === false &&
101+
isChecked === true;
102+
103+
const isGrantedAndInherited =
104+
settingsDraftRoleObjectPermissionValue !== false &&
105+
settingsDraftRoleGlobalPermissionValue === true;
106+
96107
let checkboxType: OverridableCheckboxType;
97108

98109
if (
@@ -118,8 +129,12 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow =
118129
}
119130
};
120131

132+
const humanReadableAction = objectPermissionKeyToHumanReadable(
133+
permission.key as SettingsRoleObjectPermissionKey,
134+
);
135+
121136
return (
122-
<StyledTableRow>
137+
<StyledTableRow onClick={handleCheckboxChange} isDisabled={!isEditable}>
123138
<StyledPermissionCell>
124139
<StyledPermissionContent>
125140
<PermissionIcon
@@ -134,10 +149,20 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow =
134149
{' · '}
135150
{t`Revoked for this object`}
136151
</>
152+
) : isGranted ? (
153+
<>
154+
{' · '}
155+
{t`Granted for this object`}
156+
</>
157+
) : isGrantedAndInherited ? (
158+
<>
159+
{' · '}
160+
{t`This role can ${humanReadableAction} all records`}
161+
</>
137162
) : null}
138163
</StyledOverrideInfo>
139164
</StyledPermissionCell>
140-
<StyledCheckboxCell>
165+
<StyledCheckboxCell onClick={(e) => e.stopPropagation()}>
141166
<OverridableCheckbox
142167
onChange={handleCheckboxChange}
143168
disabled={!isEditable}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { SettingsRoleObjectPermissionKey } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig';
2+
import { t } from '@lingui/core/macro';
3+
4+
export const objectPermissionKeyToHumanReadable = (
5+
objectPermissionKey: SettingsRoleObjectPermissionKey,
6+
) => {
7+
const permissionAction: Record<SettingsRoleObjectPermissionKey, string> = {
8+
canReadObjectRecords: t`see`,
9+
canUpdateObjectRecords: t`update`,
10+
canSoftDeleteObjectRecords: t`delete`,
11+
canDestroyObjectRecords: t`destroy`,
12+
};
13+
14+
return permissionAction[objectPermissionKey];
15+
};

0 commit comments

Comments
 (0)