From fdb5d2fbcd04478b7b701d677bd14f11bccada8c Mon Sep 17 00:00:00 2001 From: Tan Le Date: Fri, 13 Feb 2026 04:16:36 -0500 Subject: [PATCH] Feat 17408 : Add remove option for object permissions rule (#17601) This PR aims to fix: #17408 Main modification includes adding a new column for dropdown in the object permission rule table (containing options for editing and removal). Removal logic is implemented using existing pattern with hook ```useResetObjectPermission``` (relies on existing hooks ```useUpsertFieldPermissionInDraftRole``` and ```useUpsertObjectPermissionInDraftRole```). Feel free to suggest any necessary changes. Functionality (unrestricted access is allowed when permission removal is applied) is already tested. Demo video: https://drive.google.com/file/d/1M4RYHw-JEhDdJksKkL3MY_VyXAV3aS9I/view?usp=sharing --------- Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com> --- ...sRolePermissionsObjectLevelTableHeader.tsx | 1 + ...ingsRolePermissionsObjectLevelTableRow.tsx | 29 +++---- ...ionsObjectLevelTableRowOptionsDropdown.tsx | 72 +++++++++++++++++ ...jectLevelPermissionTableGridAutoColumns.ts | 2 +- .../hooks/useResetObjectPermission.ts | 78 +++++++++++++++++++ 5 files changed, 168 insertions(+), 14 deletions(-) create mode 100644 packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRowOptionsDropdown.tsx create mode 100644 packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/hooks/useResetObjectPermission.ts diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx index 21a309f204..15afad8384 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx @@ -16,5 +16,6 @@ export const SettingsRolePermissionsObjectLevelTableHeader = ({ {showPermissionsLabel ? t`See Fields` : ''} {showPermissionsLabel ? t`Edit Fields` : ''} + ); diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx index f3f0235e69..cb75025350 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx @@ -1,6 +1,7 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { SettingsRolePermissionsObjectLevelOverrideCellContainer } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCellContainer'; import { SettingsRolePermissionsObjectLevelSeeFieldsValueForObject } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSeeFieldsValueForObject'; +import { SettingsRolePermissionsObjectLevelTableRowOptionsDropdown } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRowOptionsDropdown'; import { SettingsRolePermissionsObjectLevelUpdateFieldsValueForObject } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelUpdateFieldsValueForObject'; import { OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS } from '@/settings/roles/role-permissions/object-level-permissions/constants/ObjectLevelPermissionTableGridAutoColumns'; import { TableCell } from '@/ui/layout/table/components/TableCell'; @@ -9,11 +10,7 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { SettingsPath } from 'twenty-shared/types'; import { getSettingsPath } from 'twenty-shared/utils'; -import { - IconChevronRight, - OverflowingTextWithTooltip, - useIcons, -} from 'twenty-ui/display'; +import { OverflowingTextWithTooltip, useIcons } from 'twenty-ui/display'; const StyledNameTableCell = styled(TableCell)` color: ${({ theme }) => theme.font.color.primary}; @@ -26,6 +23,11 @@ const StyledNameLabel = styled.div` overflow: hidden; `; +const StyledOptionsTableCell = styled(TableCell)` + justify-content: flex-end; + padding-right: ${({ theme }) => theme.spacing(1)}; +`; + type SettingsRolePermissionsObjectLevelTableRowProps = { objectMetadataItem: ObjectMetadataItem; roleId: string; @@ -90,14 +92,15 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({ objectMetadataItem={objectMetadataItem} /> - {isEditable && ( - - - - )} + + + + ); }; diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRowOptionsDropdown.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRowOptionsDropdown.tsx new file mode 100644 index 0000000000..87f031e373 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRowOptionsDropdown.tsx @@ -0,0 +1,72 @@ +import { useResetObjectPermission } from '@/settings/roles/role-permissions/object-level-permissions/hooks/useResetObjectPermission'; +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; +import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import { t } from '@lingui/core/macro'; +import { IconDotsVertical, IconPencil, IconTrash } from 'twenty-ui/display'; +import { IconButton } from 'twenty-ui/input'; +import { MenuItem, UndecoratedLink } from 'twenty-ui/navigation'; + +type SettingsRolePermissionsObjectLevelTableRowOptionsDropdownProps = { + roleId: string; + objectMetadataId: string; + objectPermissionDetailUrl: string; + isEditable: boolean; +}; + +export const SettingsRolePermissionsObjectLevelTableRowOptionsDropdown = ({ + roleId, + objectMetadataId, + objectPermissionDetailUrl, + isEditable, +}: SettingsRolePermissionsObjectLevelTableRowOptionsDropdownProps) => { + const dropdownId = `settings-role-object-level-options-${objectMetadataId}`; + + const { closeDropdown } = useCloseDropdown(); + + const { resetObjectPermission } = useResetObjectPermission(roleId); + + const handleRemove = () => { + closeDropdown(dropdownId); + resetObjectPermission(objectMetadataId); + }; + + return ( + + } + dropdownComponents={ + + {isEditable && ( + + closeDropdown(dropdownId)} + > + + + + )} + + + + + } + dropdownPlacement="bottom-end" + /> + ); +}; diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/constants/ObjectLevelPermissionTableGridAutoColumns.ts b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/constants/ObjectLevelPermissionTableGridAutoColumns.ts index c6b03a7790..1351cf4497 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/constants/ObjectLevelPermissionTableGridAutoColumns.ts +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/constants/ObjectLevelPermissionTableGridAutoColumns.ts @@ -4,7 +4,7 @@ const { } = { OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS_WITHOUT_FIELD_PERMISSIONS: '180px 1fr 1fr', - OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS: '30% 20% 20% 20% 10%', + OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS: '28% 18% 18% 18% 8% 10%', }; export { diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/hooks/useResetObjectPermission.ts b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/hooks/useResetObjectPermission.ts new file mode 100644 index 0000000000..f41f0e004b --- /dev/null +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/hooks/useResetObjectPermission.ts @@ -0,0 +1,78 @@ +import { useUpsertFieldPermissionInDraftRole } from '@/settings/roles/role-permissions/object-level-permissions/field-permissions/hooks/useUpsertFieldPermissionInDraftRole'; +import { useUpsertObjectPermissionInDraftRole } from '@/settings/roles/role-permissions/object-level-permissions/hooks/useUpsertObjectPermissionInDraftRole'; +import { type SettingsRoleObjectPermissionKey } from '@/settings/roles/role-permissions/objects-permissions/constants/SettingsRoleObjectPermissionIconConfig'; +import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState'; +import { useRecoilValue } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; +import { + type FieldPermission, + type ObjectPermission, +} from '~/generated-metadata/graphql'; + +const OBJECT_PERMISSION_KEYS: SettingsRoleObjectPermissionKey[] = [ + 'canReadObjectRecords', + 'canUpdateObjectRecords', + 'canSoftDeleteObjectRecords', + 'canDestroyObjectRecords', +]; + +export const useResetObjectPermission = (roleId: string) => { + const settingsDraftRole = useRecoilValue( + settingsDraftRoleFamilyState(roleId), + ); + + const { upsertObjectPermissionInDraftRole } = + useUpsertObjectPermissionInDraftRole(roleId); + + const { upsertFieldPermissionInDraftRole } = + useUpsertFieldPermissionInDraftRole(roleId); + + const resetObjectPermission = (objectMetadataItemId: string) => { + const fieldPermissionsForCurrentObject = + settingsDraftRole.fieldPermissions?.filter( + (permission) => permission.objectMetadataId === objectMetadataItemId, + ) ?? []; + const existingObjectPermission = settingsDraftRole.objectPermissions?.find( + (objectPermissionToFind) => + objectPermissionToFind.objectMetadataId === objectMetadataItemId, + ); + + const resetPermissions = OBJECT_PERMISSION_KEYS.reduce( + (acc, permissionKey) => { + acc[permissionKey] = null; + return acc; + }, + {} as Record, + ); + + if (!isDefined(existingObjectPermission)) { + const newObjectPermission = { + objectMetadataId: objectMetadataItemId, + ...resetPermissions, + } satisfies ObjectPermission; + + upsertObjectPermissionInDraftRole(newObjectPermission); + } else { + const updatedObjectPermission = { + ...existingObjectPermission, + ...resetPermissions, + }; + + upsertObjectPermissionInDraftRole(updatedObjectPermission); + } + + fieldPermissionsForCurrentObject.forEach( + (fieldPermission: FieldPermission) => { + upsertFieldPermissionInDraftRole({ + ...fieldPermission, + canUpdateFieldValue: null, + canReadFieldValue: null, + }); + }, + ); + }; + + return { + resetObjectPermission, + }; +};