diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index cee9cc1fa..16102da20 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -2960,7 +2960,7 @@ export type OnDbEventSubscriptionVariables = Exact<{ export type OnDbEventSubscription = { __typename?: 'Subscription', onDbEvent: { __typename?: 'OnDbEventDTO', eventDate: string, action: DatabaseEventAction, objectNameSingular: string, updatedFields?: Array | null, record: any } }; -export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: any, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array | null, objectRecordsPermissions?: Array | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItem', id: any, hasReachedCurrentPeriodCap: boolean, billingProduct?: { __typename?: 'BillingProduct', name: string, description: string, metadata: { __typename?: 'BillingProductMetadata', planKey: BillingPlanKey, priceUsageBased: BillingUsageType, productKey: BillingProductKey } } | null }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> }; +export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: any, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array | null, objectRecordsPermissions?: Array | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItem', id: any, hasReachedCurrentPeriodCap: boolean, billingProduct?: { __typename?: 'BillingProduct', name: string, description: string, metadata: { __typename?: 'BillingProductMetadata', planKey: BillingPlanKey, priceUsageBased: BillingUsageType, productKey: BillingProductKey } } | null }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> }; export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>; @@ -2977,7 +2977,7 @@ export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProf export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>; -export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: any, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array | null, objectRecordsPermissions?: Array | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItem', id: any, hasReachedCurrentPeriodCap: boolean, billingProduct?: { __typename?: 'BillingProduct', name: string, description: string, metadata: { __typename?: 'BillingProductMetadata', planKey: BillingPlanKey, priceUsageBased: BillingUsageType, productKey: BillingProductKey } } | null }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> } }; +export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: any, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array | null, objectRecordsPermissions?: Array | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItem', id: any, hasReachedCurrentPeriodCap: boolean, billingProduct?: { __typename?: 'BillingProduct', name: string, description: string, metadata: { __typename?: 'BillingProductMetadata', planKey: BillingPlanKey, priceUsageBased: BillingUsageType, productKey: BillingProductKey } } | null }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> } }; export type ActivateWorkflowVersionMutationVariables = Exact<{ workflowVersionId: Scalars['String']; @@ -3216,15 +3216,6 @@ export const AvailableSsoIdentityProvidersFragmentFragmentDoc = gql` } } `; -export const ObjectPermissionFragmentFragmentDoc = gql` - fragment ObjectPermissionFragment on ObjectPermission { - objectMetadataId - canReadObjectRecords - canUpdateObjectRecords - canSoftDeleteObjectRecords - canDestroyObjectRecords -} - `; export const SettingPermissionFragmentFragmentDoc = gql` fragment SettingPermissionFragment on SettingPermission { id @@ -3259,6 +3250,15 @@ export const DeletedWorkspaceMemberQueryFragmentFragmentDoc = gql` userEmail } `; +export const ObjectPermissionFragmentFragmentDoc = gql` + fragment ObjectPermissionFragment on ObjectPermission { + objectMetadataId + canReadObjectRecords + canUpdateObjectRecords + canSoftDeleteObjectRecords + canDestroyObjectRecords +} + `; export const RoleFragmentFragmentDoc = gql` fragment RoleFragment on Role { id @@ -3295,6 +3295,9 @@ export const UserQueryFragmentFragmentDoc = gql` currentUserWorkspace { settingsPermissions objectRecordsPermissions + objectPermissions { + ...ObjectPermissionFragment + } } currentWorkspace { id @@ -3364,6 +3367,7 @@ export const UserQueryFragmentFragmentDoc = gql` } ${WorkspaceMemberQueryFragmentFragmentDoc} ${DeletedWorkspaceMemberQueryFragmentFragmentDoc} +${ObjectPermissionFragmentFragmentDoc} ${RoleFragmentFragmentDoc}`; export const GetTimelineCalendarEventsFromCompanyIdDocument = gql` query GetTimelineCalendarEventsFromCompanyId($companyId: UUID!, $page: Int!, $pageSize: Int!) { diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx index f899779d0..95f137ed0 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx @@ -66,8 +66,8 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record< position: 0, isPinned: true, Icon: IconPlus, - shouldBeRegistered: ({ hasObjectReadOnlyPermission }) => - !hasObjectReadOnlyPermission, + shouldBeRegistered: ({ objectPermissions }) => + objectPermissions.canUpdateObjectRecords, availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION], component: , }, @@ -194,10 +194,15 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record< Icon: IconTrash, accent: 'default', isPinned: true, - shouldBeRegistered: ({ selectedRecord, isSoftDeleteFilterActive }) => + shouldBeRegistered: ({ + selectedRecord, + isSoftDeleteFilterActive, + objectPermissions, + }) => isDefined(selectedRecord) && !selectedRecord.isRemote && - !isSoftDeleteFilterActive, + !isSoftDeleteFilterActive && + objectPermissions.canSoftDeleteObjectRecords, availableOn: [ ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION, ActionViewType.SHOW_PAGE, @@ -215,12 +220,12 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record< accent: 'default', isPinned: true, shouldBeRegistered: ({ - hasObjectReadOnlyPermission, + objectPermissions, isRemote, isSoftDeleteFilterActive, numberOfSelectedRecords, }) => - !hasObjectReadOnlyPermission && + objectPermissions.canSoftDeleteObjectRecords && !isRemote && !isSoftDeleteFilterActive && isDefined(numberOfSelectedRecords) && @@ -268,12 +273,8 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record< Icon: IconTrashX, accent: 'danger', isPinned: true, - shouldBeRegistered: ({ - selectedRecord, - hasObjectReadOnlyPermission, - isRemote, - }) => - !hasObjectReadOnlyPermission && + shouldBeRegistered: ({ selectedRecord, objectPermissions, isRemote }) => + objectPermissions.canDestroyObjectRecords && !isRemote && isDefined(selectedRecord?.deletedAt), availableOn: [ @@ -317,12 +318,12 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record< accent: 'danger', isPinned: true, shouldBeRegistered: ({ - hasObjectReadOnlyPermission, + objectPermissions, isRemote, isSoftDeleteFilterActive, numberOfSelectedRecords, }) => - !hasObjectReadOnlyPermission && + objectPermissions.canDestroyObjectRecords && !isRemote && isDefined(isSoftDeleteFilterActive) && isSoftDeleteFilterActive && @@ -343,14 +344,14 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record< isPinned: true, shouldBeRegistered: ({ selectedRecord, - hasObjectReadOnlyPermission, + objectPermissions, isRemote, isShowPage, isSoftDeleteFilterActive, }) => !isRemote && isDefined(selectedRecord?.deletedAt) && - !hasObjectReadOnlyPermission && + objectPermissions.canSoftDeleteObjectRecords && ((isDefined(isShowPage) && isShowPage) || (isDefined(isSoftDeleteFilterActive) && isSoftDeleteFilterActive)), availableOn: [ @@ -370,12 +371,12 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record< accent: 'default', isPinned: true, shouldBeRegistered: ({ - hasObjectReadOnlyPermission, + objectPermissions, isRemote, isSoftDeleteFilterActive, numberOfSelectedRecords, }) => - !hasObjectReadOnlyPermission && + objectPermissions.canSoftDeleteObjectRecords && !isRemote && isDefined(isSoftDeleteFilterActive) && isSoftDeleteFilterActive && diff --git a/packages/twenty-front/src/modules/action-menu/actions/types/ShouldBeRegisteredFunctionParams.ts b/packages/twenty-front/src/modules/action-menu/actions/types/ShouldBeRegisteredFunctionParams.ts index 49cb8ed2b..74e31ed84 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/types/ShouldBeRegisteredFunctionParams.ts +++ b/packages/twenty-front/src/modules/action-menu/actions/types/ShouldBeRegisteredFunctionParams.ts @@ -1,12 +1,13 @@ import { ActionViewType } from '@/action-menu/actions/types/ActionViewType'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { ObjectPermissions } from '@/object-record/cache/types/ObjectPermissions'; import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; export type ShouldBeRegisteredFunctionParams = { objectMetadataItem?: ObjectMetadataItem; - hasObjectReadOnlyPermission?: boolean; + objectPermissions: ObjectPermissions; isWorkflowEnabled: boolean; recordFilters?: RecordFilter[]; isShowPage?: boolean; diff --git a/packages/twenty-front/src/modules/action-menu/hooks/useShouldActionBeRegisteredParams.ts b/packages/twenty-front/src/modules/action-menu/hooks/useShouldActionBeRegisteredParams.ts index 633fef2e1..9e9647907 100644 --- a/packages/twenty-front/src/modules/action-menu/hooks/useShouldActionBeRegisteredParams.ts +++ b/packages/twenty-front/src/modules/action-menu/hooks/useShouldActionBeRegisteredParams.ts @@ -8,9 +8,9 @@ import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType import { useFavorites } from '@/favorites/hooks/useFavorites'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { isSoftDeleteFilterActiveComponentState } from '@/object-record/record-table/states/isSoftDeleteFilterActiveComponentState'; -import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObjectReadOnlyPermission'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useContext } from 'react'; @@ -42,7 +42,9 @@ export const useShouldActionBeRegisteredParams = ({ const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId ?? '')) || undefined; - const hasObjectReadOnlyPermission = useHasObjectReadOnlyPermission(); + const objectPermissions = useObjectPermissionsForObject( + objectMetadataItem?.id, + ); const isNoteOrTask = objectMetadataItem?.nameSingular === CoreObjectNameSingular.Note || @@ -78,7 +80,7 @@ export const useShouldActionBeRegisteredParams = ({ return { objectMetadataItem, isFavorite, - hasObjectReadOnlyPermission, + objectPermissions, isNoteOrTask, isInRightDrawer, isSoftDeleteFilterActive, diff --git a/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx b/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx index d0d28348e..f88ce9444 100644 --- a/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx +++ b/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx @@ -67,7 +67,10 @@ export const ActivityRichTextEditor = ({ objectNameSingular: activityObjectNameSingular, }); - const isRecordReadOnly = useIsRecordReadOnly({ recordId: activityId }); + const isRecordReadOnly = useIsRecordReadOnly({ + recordId: activityId, + objectMetadataId: objectMetadataItemActivity.id, + }); const isReadOnly = isFieldValueReadOnly({ objectNameSingular: activityObjectNameSingular, diff --git a/packages/twenty-front/src/modules/activities/files/components/Attachments.tsx b/packages/twenty-front/src/modules/activities/files/components/Attachments.tsx index 41dc27f40..f6377d39c 100644 --- a/packages/twenty-front/src/modules/activities/files/components/Attachments.tsx +++ b/packages/twenty-front/src/modules/activities/files/components/Attachments.tsx @@ -7,8 +7,11 @@ import { DropZone } from '@/activities/files/components/DropZone'; import { useAttachments } from '@/activities/files/hooks/useAttachments'; import { useUploadAttachmentFile } from '@/activities/files/hooks/useUploadAttachmentFile'; import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; -import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObjectReadOnlyPermission'; +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject'; import { isDefined } from 'twenty-shared/utils'; +import { IconPlus } from 'twenty-ui/display'; +import { Button } from 'twenty-ui/input'; import { AnimatedPlaceholder, AnimatedPlaceholderEmptyContainer, @@ -17,8 +20,6 @@ import { AnimatedPlaceholderEmptyTitle, EMPTY_PLACEHOLDER_TRANSITION_PROPS, } from 'twenty-ui/layout'; -import { Button } from 'twenty-ui/input'; -import { IconPlus } from 'twenty-ui/display'; const StyledAttachmentsContainer = styled.div` display: flex; @@ -47,8 +48,6 @@ export const Attachments = ({ const [isDraggingFile, setIsDraggingFile] = useState(false); - const hasObjectReadOnlyPermission = useHasObjectReadOnlyPermission(); - const handleFileChange = (e: ChangeEvent) => { if (isDefined(e.target.files)) onUploadFile?.(e.target.files[0]); }; @@ -63,6 +62,16 @@ export const Attachments = ({ const isAttachmentsEmpty = !attachments || attachments.length === 0; + const { objectMetadataItem } = useObjectMetadataItem({ + objectNameSingular: targetableObject.targetObjectNameSingular, + }); + + const objectPermissions = useObjectPermissionsForObject( + objectMetadataItem.id, + ); + + const hasObjectUpdatePermissions = objectPermissions.canUpdateObjectRecords; + if (loading && isAttachmentsEmpty) { return ; } @@ -94,7 +103,7 @@ export const Attachments = ({ onChange={handleFileChange} type="file" /> - {!hasObjectReadOnlyPermission && ( + {!hasObjectUpdatePermissions && (