diff --git a/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesContainer.tsx b/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesContainer.tsx index 0f4b551f6..d054fb327 100644 --- a/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesContainer.tsx +++ b/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesContainer.tsx @@ -19,7 +19,7 @@ export const SettingsRolesContainer = () => { const settingsAllRoles = useRecoilValue(settingsAllRolesSelector); const settingsRolesIsLoading = useRecoilValue(settingsRolesIsLoadingState); - if (settingsRolesIsLoading) { + if (settingsRolesIsLoading && !settingsAllRoles) { return null; } diff --git a/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesDefaultRole.tsx b/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesDefaultRole.tsx index 0ab4afc86..0414689ba 100644 --- a/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesDefaultRole.tsx +++ b/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesDefaultRole.tsx @@ -60,9 +60,13 @@ export const SettingsRoleDefaultRole = ({ const options = roles.map((role) => ({ label: role.label, value: role.id, - Icon: getIcon(role.icon), + Icon: getIcon(role.icon) ?? IconUserPin, })); + if (options.length === 0) { + return null; + } + return (
{
diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelObjectPicker.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelObjectPicker.tsx index f2cbfd495..0c0676fa5 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelObjectPicker.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelObjectPicker.tsx @@ -113,7 +113,7 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({ [objectMetadataItems, searchFilter, excludedObjectMetadataIds], ); - const basicObjects = filteredObjectMetadataItems.filter( + const standardObjects = filteredObjectMetadataItems.filter( (item) => !item.isCustom, ); const customObjects = filteredObjectMetadataItems.filter( @@ -135,11 +135,14 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({ - {basicObjects.length > 0 && ( + {standardObjects.length > 0 && (
- + - {basicObjects.map((objectMetadataItem) => { + {standardObjects.map((objectMetadataItem) => { const Icon = getIcon(objectMetadataItem.icon); return ( } @@ -181,7 +184,7 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({ key={objectMetadataItem.id} Icon={ } diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCell.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCell.tsx index e1934ac09..5e4168bfe 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCell.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCell.tsx @@ -1,60 +1,83 @@ +import { objectPermissionKeyToHumanReadable } from '@/settings/roles/role-permissions/object-level-permissions/utils/objectPermissionKeyToHumanReadableText'; 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 { SettingsRoleObjectPermissionKey } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig'; import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState'; import styled from '@emotion/styled'; +import { t } from '@lingui/core/macro'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; +import { AppTooltip, TooltipDelay } from 'twenty-ui/display'; import { ObjectPermission } from '~/generated/graphql'; -const StyledSettingsRolePermissionsObjectLevelOverrideCell = styled.div` +const StyledContainer = styled.div` display: flex; - gap: ${({ theme }) => theme.spacing(1)}; `; type SettingsRolePermissionsObjectLevelOverrideCellProps = { - objectPermission: ObjectPermission; + objectPermissions: ObjectPermission; + objectPermissionKey: SettingsRoleObjectPermissionKey; roleId: string; + objectLabel: string; }; export const SettingsRolePermissionsObjectLevelOverrideCell = ({ - objectPermission, + objectPermissions, + objectPermissionKey, roleId, + objectLabel, }: SettingsRolePermissionsObjectLevelOverrideCellProps) => { const settingsDraftRole = useRecoilValue( settingsDraftRoleFamilyState(roleId), ); + const roleLabel = settingsDraftRole.label; + const permissionMappings = SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING; - const isOverridden = (permission: SettingsRoleObjectPermissionKey) => { - const rolePermission = permissionMappings[permission]; + const permissionValue = objectPermissions[objectPermissionKey]; + + const isOverridden = ( + objectPermissionKey: SettingsRoleObjectPermissionKey, + ) => { + const rolePermission = permissionMappings[objectPermissionKey]; + return ( - isDefined(objectPermission[permission]) && - !!settingsDraftRole[rolePermission] !== !!objectPermission[permission] + isDefined(permissionValue) && + !!settingsDraftRole[rolePermission] !== !!permissionValue ); }; + if (!isOverridden(objectPermissionKey)) { + return null; + } + + const humanReadableAction = + objectPermissionKeyToHumanReadable(objectPermissionKey); + + const containerId = `object-level-permission-override-${roleId}-${objectPermissionKey}`; + return ( - - {( - Object.keys(permissionMappings) as SettingsRoleObjectPermissionKey[] - ).map((permission) => { - const permissionValue = objectPermission[permission]; - - if (!isOverridden(permission)) { - return null; + <> + + + + - ); - })} - + delay={TooltipDelay.shortDelay} + noArrow + place="bottom" + positionStrategy="fixed" + /> + ); }; diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCellContainer.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCellContainer.tsx new file mode 100644 index 000000000..4c5177ebc --- /dev/null +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCellContainer.tsx @@ -0,0 +1,43 @@ +import { SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectLevelPermissionToRoleObjectPermissionMapping'; +import { SettingsRoleObjectPermissionKey } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig'; +import styled from '@emotion/styled'; +import { ObjectPermission } from '~/generated/graphql'; +import { SettingsRolePermissionsObjectLevelOverrideCell } from './SettingsRolePermissionsObjectLevelOverrideCell'; + +const StyledSettingsRolePermissionsObjectLevelOverrideCell = styled.div` + display: flex; + gap: ${({ theme }) => theme.spacing(1)}; +`; + +type SettingsRolePermissionsObjectLevelOverrideCellContainerProps = { + objectPermissions: ObjectPermission; + roleId: string; + objectLabel: string; +}; + +export const SettingsRolePermissionsObjectLevelOverrideCellContainer = ({ + objectPermissions, + roleId, + objectLabel, +}: SettingsRolePermissionsObjectLevelOverrideCellContainerProps) => { + const permissionMappings = + SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING; + + return ( + + {( + Object.keys(permissionMappings) as SettingsRoleObjectPermissionKey[] + ).map((permissionKey) => { + return ( + + ); + })} + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSection.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSection.tsx index 877a541e6..3089399dc 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSection.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSection.tsx @@ -84,8 +84,8 @@ export const SettingsRolePermissionsObjectLevelSection = ({ return (
@@ -103,7 +103,7 @@ export const SettingsRolePermissionsObjectLevelSection = ({ /> )) ) : ( - {t`No overrides found`} + {t`No permissions found`} )}
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 bcc3c383e..2b6582642 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,5 +1,5 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { SettingsRolePermissionsObjectLevelOverrideCell } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCell'; +import { SettingsRolePermissionsObjectLevelOverrideCellContainer } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCellContainer'; import { SettingsPath } from '@/types/SettingsPath'; import { TableCell } from '@/ui/layout/table/components/TableCell'; import { TableRow } from '@/ui/layout/table/components/TableRow'; @@ -44,6 +44,8 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({ const Icon = getIcon(objectMetadataItem.icon); + const objectLabel = objectMetadataItem.labelPlural; + return ( )} - - + + - diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevel.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevel.tsx index 3620787b1..28c9a2234 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevel.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevel.tsx @@ -120,8 +120,8 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevel = ({ return (
diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow.tsx index 564f8317c..d3cb66c38 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow.tsx @@ -1,4 +1,5 @@ import { OverridableCheckbox } from '@/settings/roles/role-permissions/object-level-permissions/object-form/components/OverridableCheckbox'; +import { objectPermissionKeyToHumanReadable } from '@/settings/roles/role-permissions/object-level-permissions/utils/objectPermissionKeyToHumanReadableText'; 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 { SettingsRoleObjectPermissionKey } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig'; @@ -13,9 +14,10 @@ import { isDefined } from 'twenty-shared/utils'; import { ObjectPermission } from '~/generated-metadata/graphql'; import type { Role } from '~/generated/graphql'; -const StyledTableRow = styled(TableRow)` +const StyledTableRow = styled(TableRow)<{ isDisabled: boolean }>` align-items: center; display: flex; + cursor: ${({ isDisabled }) => (isDisabled ? 'default' : 'pointer')}; `; const StyledPermissionCell = styled(TableCell)` @@ -93,6 +95,15 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow = settingsDraftRoleGlobalPermissionValue === true && isChecked === false; + const isGranted = + isDefined(settingsDraftRoleObjectPermissionValue) && + settingsDraftRoleGlobalPermissionValue === false && + isChecked === true; + + const isGrantedAndInherited = + settingsDraftRoleObjectPermissionValue !== false && + settingsDraftRoleGlobalPermissionValue === true; + let checkboxType: OverridableCheckboxType; if ( @@ -118,8 +129,12 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow = } }; + const humanReadableAction = objectPermissionKeyToHumanReadable( + permission.key as SettingsRoleObjectPermissionKey, + ); + return ( - + + ) : isGranted ? ( + <> + {' · '} + {t`Granted for this object`} + + ) : isGrantedAndInherited ? ( + <> + {' · '} + {t`This role can ${humanReadableAction} all records`} + ) : null} - + e.stopPropagation()}> { + const permissionAction: Record = { + canReadObjectRecords: t`see`, + canUpdateObjectRecords: t`update`, + canSoftDeleteObjectRecords: t`delete`, + canDestroyObjectRecords: t`destroy`, + }; + + return permissionAction[objectPermissionKey]; +}; diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/objects-permissions/components/SettingsRolePermissionsObjectsSection.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/objects-permissions/components/SettingsRolePermissionsObjectsSection.tsx index 13b1d5495..6d683bfb5 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/objects-permissions/components/SettingsRolePermissionsObjectsSection.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/objects-permissions/components/SettingsRolePermissionsObjectsSection.tsx @@ -36,6 +36,12 @@ export const SettingsRolePermissionsObjectsSection = ({ { key: 'canReadObjectRecords', label: t`See Records on All Objects`, + grantedBy: + objectPermissions?.filter( + (permission) => + permission.canReadObjectRecords === true && + settingsDraftRole.canReadAllObjectRecords === false, + )?.length ?? 0, revokedBy: objectPermissions?.filter( (permission) => @@ -60,6 +66,12 @@ export const SettingsRolePermissionsObjectsSection = ({ { key: 'canUpdateObjectRecords', label: t`Edit Records on All Objects`, + grantedBy: + objectPermissions?.filter( + (permission) => + permission.canUpdateObjectRecords === true && + settingsDraftRole.canUpdateAllObjectRecords === false, + )?.length ?? 0, revokedBy: objectPermissions?.filter( (permission) => @@ -82,6 +94,12 @@ export const SettingsRolePermissionsObjectsSection = ({ { key: 'canSoftDeleteObjectRecords', label: t`Delete Records on All Objects`, + grantedBy: + objectPermissions?.filter( + (permission) => + permission.canSoftDeleteObjectRecords === true && + settingsDraftRole.canSoftDeleteAllObjectRecords === false, + )?.length ?? 0, revokedBy: objectPermissions?.filter( (permission) => @@ -104,6 +122,12 @@ export const SettingsRolePermissionsObjectsSection = ({ { key: 'canDestroyObjectRecords', label: t`Destroy Records on All Objects`, + grantedBy: + objectPermissions?.filter( + (permission) => + permission.canDestroyObjectRecords === true && + settingsDraftRole.canDestroyAllObjectRecords === false, + )?.length ?? 0, revokedBy: objectPermissions?.filter( (permission) => @@ -128,8 +152,8 @@ export const SettingsRolePermissionsObjectsSection = ({ return (
theme.spacing(4)}; + padding-right: ${({ theme }) => theme.spacing(1)}; `; type SettingsRolePermissionsObjectsTableHeaderProps = { diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/objects-permissions/components/SettingsRolePermissionsObjectsTableRow.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/objects-permissions/components/SettingsRolePermissionsObjectsTableRow.tsx index a82ec8afc..a3842356a 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/objects-permissions/components/SettingsRolePermissionsObjectsTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/objects-permissions/components/SettingsRolePermissionsObjectsTableRow.tsx @@ -36,12 +36,13 @@ const StyledCheckboxCell = styled(TableCell)` align-items: center; display: flex; justify-content: flex-end; - padding-right: ${({ theme }) => theme.spacing(4)}; + padding-right: ${({ theme }) => theme.spacing(1)}; `; -const StyledTableRow = styled(TableRow)` +const StyledTableRow = styled(TableRow)<{ isDisabled: boolean }>` align-items: center; display: flex; + cursor: ${({ isDisabled }) => (isDisabled ? 'default' : 'pointer')}; `; type SettingsRolePermissionsObjectsTableRowProps = { @@ -54,13 +55,21 @@ export const SettingsRolePermissionsObjectsTableRow = ({ isEditable, }: SettingsRolePermissionsObjectsTableRowProps) => { const revokedBy = permission.revokedBy; + const grantedBy = permission.grantedBy; const isRevoked = revokedBy !== undefined && revokedBy !== null && revokedBy > 0; const label = permission.label; - const pluralizedObject = pluralize('object', revokedBy); + const pluralizedRevokedObject = pluralize('object', revokedBy); + const pluralizedGrantedObject = pluralize('object', grantedBy); + const isDisabled = !isEditable; + + const handleRowClick = () => { + if (isDisabled) return; + permission.setValue(!permission.value); + }; return ( - + {label} - {isRevoked ? ( + {isRevoked && revokedBy > 0 ? ( <> {' · '} - {t`Revoked on ${revokedBy} ${pluralizedObject}`} + {t`Revoked for ${revokedBy} ${pluralizedRevokedObject}`} + + ) : grantedBy && grantedBy > 0 ? ( + <> + {' · '} + {t`Granted for ${grantedBy} ${pluralizedGrantedObject}`} ) : null} - + e.stopPropagation()}> permission.setValue(!permission.value)} - disabled={!isEditable} + disabled={isDisabled} accent={isRevoked ? CheckboxAccent.Orange : CheckboxAccent.Blue} /> diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/objects-permissions/types/SettingsRolePermissionsObjectPermission.ts b/packages/twenty-front/src/modules/settings/roles/role-permissions/objects-permissions/types/SettingsRolePermissionsObjectPermission.ts index c5312ae7e..de2ca7672 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/objects-permissions/types/SettingsRolePermissionsObjectPermission.ts +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/objects-permissions/types/SettingsRolePermissionsObjectPermission.ts @@ -4,6 +4,7 @@ export type SettingsRolePermissionsObjectPermission = { label: string | ReactNode; value?: boolean; setValue: (value: boolean) => void; + grantedBy?: number; revokedBy?: number; }; diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsSection.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsSection.tsx index 3a96ccd98..6fcce38ed 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsSection.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsSection.tsx @@ -130,7 +130,11 @@ export const SettingsRolePermissionsSettingsSection = ({ containAnimation={false} > - + {settingsPermissionsConfig.map((permission) => ( ( - - {t`Name`} - {t`Description`} - - -); +import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/settings-permissions/types/SettingsRolePermissionsSettingPermission'; +import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState'; +import { useRecoilState } from 'recoil'; +import { v4 } from 'uuid'; + +type SettingsRolePermissionsSettingsTableHeaderProps = { + roleId: string; + settingsPermissionsConfig: SettingsRolePermissionsSettingPermission[]; + isEditable: boolean; +}; + +const StyledActionsHeader = styled(TableHeader)` + align-items: center; + display: flex; + justify-content: flex-end; + padding-right: ${({ theme }) => theme.spacing(1)}; +`; + +export const SettingsRolePermissionsSettingsTableHeader = ({ + roleId, + settingsPermissionsConfig, + isEditable, +}: SettingsRolePermissionsSettingsTableHeaderProps) => { + const [settingsDraftRole, setSettingsDraftRole] = useRecoilState( + settingsDraftRoleFamilyState(roleId), + ); + const allSettingsPermissionsEnabled = settingsPermissionsConfig.every( + (permission) => + settingsDraftRole.settingPermissions?.some( + (settingPermission) => settingPermission.setting === permission.key, + ), + ); + + const someSettingsPermissionsEnabled = settingsPermissionsConfig.some( + (permission) => + settingsDraftRole.settingPermissions?.some( + (settingPermission) => settingPermission.setting === permission.key, + ), + ); + + return ( + + {t`Name`} + {t`Description`} + + { + const newValue = !allSettingsPermissionsEnabled; + + setSettingsDraftRole({ + ...settingsDraftRole, + settingPermissions: newValue + ? settingsPermissionsConfig.map((permission) => ({ + id: v4(), + setting: permission.key, + roleId, + })) + : [], + }); + }} + /> + + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsTableRow.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsTableRow.tsx index 96d50a559..ddf3dbd64 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsTableRow.tsx @@ -10,6 +10,10 @@ import { Checkbox } from 'twenty-ui/input'; import { v4 } from 'uuid'; import { FeatureFlagKey } from '~/generated-metadata/graphql'; +const StyledTableRow = styled(TableRow)<{ isDisabled: boolean }>` + cursor: ${({ isDisabled }) => (isDisabled ? 'default' : 'pointer')}; +`; + const StyledName = styled.span` color: ${({ theme }) => theme.font.color.primary}; `; @@ -29,7 +33,7 @@ const StyledCheckboxCell = styled(TableCell)` align-items: center; display: flex; justify-content: flex-end; - padding-right: ${({ theme }) => theme.spacing(4)}; + padding-right: ${({ theme }) => theme.spacing(1)}; `; const StyledIconContainer = styled.div` @@ -63,6 +67,10 @@ export const SettingsRolePermissionsSettingsTableRow = ({ (settingPermission) => settingPermission.setting === permission.key, ) ?? false; + const isChecked = isSettingPermissionEnabled || canUpdateAllSettings; + const isDisabled = + !isEditable || canUpdateAllSettings || !isPermissionsV2Enabled; + const handleChange = (value: boolean) => { const currentPermissions = settingsDraftRole.settingPermissions ?? []; @@ -88,8 +96,18 @@ export const SettingsRolePermissionsSettingsTableRow = ({ } }; + const handleRowClick = () => { + if (isDisabled) return; + handleChange(!isChecked); + }; + return ( - + {permission.description} - + e.stopPropagation()}> handleChange(event.target.checked)} /> - + ); }; diff --git a/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRole.tsx b/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRole.tsx index a510dd1fd..20f756b3c 100644 --- a/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRole.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRole.tsx @@ -57,7 +57,7 @@ const ROLE_BASIC_KEYS: Array = [ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => { const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, - SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID, + SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID + '-' + roleId, ); const isPermissionsV2Enabled = useIsFeatureEnabled( @@ -94,16 +94,16 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => { const isRoleEditable = isPermissionsV2Enabled && settingsDraftRole.isEditable; const tabs = [ - { - id: SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.ASSIGNMENT, - title: t`Assignment`, - Icon: IconUserPlus, - }, { id: SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.PERMISSIONS, title: t`Permissions`, Icon: IconLockOpen, }, + { + id: SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.ASSIGNMENT, + title: t`Assignment`, + Icon: IconUserPlus, + }, { id: SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.SETTINGS, title: t`Settings`, @@ -135,7 +135,7 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => { if (isCreateMode) { const roleId = v4(); - createRole({ + const { data } = await createRole({ variables: { createRoleInput: { id: roleId, @@ -152,60 +152,63 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => { settingsDraftRole.canDestroyAllObjectRecords, }, }, - onCompleted: async (data) => { - if (isDefined(dirtyFields.workspaceMembers)) { - await addWorkspaceMembersToRole({ + refetchQueries: [getOperationName(GET_ROLES) ?? ''], + }); + + if (!data) { + return; + } + + if (isDefined(dirtyFields.workspaceMembers)) { + await addWorkspaceMembersToRole({ + roleId: data.createOneRole.id, + workspaceMemberIds: settingsDraftRole.workspaceMembers.map( + (member) => member.id, + ), + }); + } + + if (isDefined(dirtyFields.settingPermissions)) { + await upsertSettingPermissions({ + variables: { + upsertSettingPermissionsInput: { roleId: data.createOneRole.id, - workspaceMemberIds: settingsDraftRole.workspaceMembers.map( - (member) => member.id, - ), - }); - } + settingPermissionKeys: + settingsDraftRole.settingPermissions?.map( + (settingPermission) => settingPermission.setting, + ) ?? [], + }, + }, + refetchQueries: [getOperationName(GET_ROLES) ?? ''], + }); + } - if (isDefined(dirtyFields.settingPermissions)) { - await upsertSettingPermissions({ - variables: { - upsertSettingPermissionsInput: { - roleId: data.createOneRole.id, - settingPermissionKeys: - settingsDraftRole.settingPermissions?.map( - (settingPermission) => settingPermission.setting, - ) ?? [], - }, - }, - refetchQueries: [getOperationName(GET_ROLES) ?? ''], - }); - } + if (isDefined(dirtyFields.objectPermissions)) { + await upsertObjectPermissions({ + variables: { + upsertObjectPermissionsInput: { + roleId: data.createOneRole.id, + objectPermissions: + settingsDraftRole.objectPermissions?.map( + (objectPermission) => ({ + objectMetadataId: objectPermission.objectMetadataId, + canReadObjectRecords: objectPermission.canReadObjectRecords, + canUpdateObjectRecords: + objectPermission.canUpdateObjectRecords, + canSoftDeleteObjectRecords: + objectPermission.canSoftDeleteObjectRecords, + canDestroyObjectRecords: + objectPermission.canDestroyObjectRecords, + }), + ) ?? [], + }, + }, + refetchQueries: [getOperationName(GET_ROLES) ?? ''], + }); + } - if (isDefined(dirtyFields.objectPermissions)) { - await upsertObjectPermissions({ - variables: { - upsertObjectPermissionsInput: { - roleId: data.createOneRole.id, - objectPermissions: - settingsDraftRole.objectPermissions?.map( - (objectPermission) => ({ - objectMetadataId: objectPermission.objectMetadataId, - canReadObjectRecords: - objectPermission.canReadObjectRecords, - canUpdateObjectRecords: - objectPermission.canUpdateObjectRecords, - canSoftDeleteObjectRecords: - objectPermission.canSoftDeleteObjectRecords, - canDestroyObjectRecords: - objectPermission.canDestroyObjectRecords, - }), - ) ?? [], - }, - }, - refetchQueries: [getOperationName(GET_ROLES) ?? ''], - }); - } - - navigateSettings(SettingsPath.RoleDetail, { - roleId: data.createOneRole.id, - }); - }, + navigateSettings(SettingsPath.RoleDetail, { + roleId: data.createOneRole.id, }); } else { if (isDefined(dirtyFields.settingPermissions)) { @@ -244,6 +247,7 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => { }, }, }, + refetchQueries: [getOperationName(GET_ROLES) ?? ''], }); } @@ -302,7 +306,9 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => { {activeTabId === SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.ASSIGNMENT && ( diff --git a/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleCreateEffect.tsx b/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleCreateEffect.tsx index 99d046b61..7b52e62b6 100644 --- a/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleCreateEffect.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleCreateEffect.tsx @@ -18,7 +18,7 @@ export const SettingsRoleCreateEffect = ({ ); const setActiveTabId = useSetRecoilComponentStateV2( activeTabIdComponentState, - SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID, + SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID + '-' + roleId, ); const [isInitialized, setIsInitialized] = useState(false); diff --git a/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleEditEffect.tsx b/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleEditEffect.tsx index 8878d420e..04898aad8 100644 --- a/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleEditEffect.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleEditEffect.tsx @@ -22,7 +22,7 @@ export const SettingsRoleEditEffect = ({ const role = useRecoilValue(settingsPersistedRoleFamilyState(roleId)); const setActiveTabId = useSetRecoilComponentStateV2( activeTabIdComponentState, - SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID, + SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID + '-' + roleId, ); const updateDraftRoleIfNeeded = useRecoilCallback( @@ -45,7 +45,7 @@ export const SettingsRoleEditEffect = ({ return; } - setActiveTabId(SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.ASSIGNMENT); + setActiveTabId(SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.PERMISSIONS); updateDraftRoleIfNeeded(role); setIsInitialized(true); }, [isInitialized, role, setActiveTabId, updateDraftRoleIfNeeded]); diff --git a/packages/twenty-front/src/modules/settings/roles/role/constants/SettingsRoleDetailTabs.ts b/packages/twenty-front/src/modules/settings/roles/role/constants/SettingsRoleDetailTabs.ts index a6732902e..98e8823d2 100644 --- a/packages/twenty-front/src/modules/settings/roles/role/constants/SettingsRoleDetailTabs.ts +++ b/packages/twenty-front/src/modules/settings/roles/role/constants/SettingsRoleDetailTabs.ts @@ -1,8 +1,8 @@ export const SETTINGS_ROLE_DETAIL_TABS = { COMPONENT_INSTANCE_ID: 'settings-role-detail-tabs', TABS_IDS: { - ASSIGNMENT: 'assignment', PERMISSIONS: 'permissions', + ASSIGNMENT: 'assignment', SETTINGS: 'settings', }, } as const;