object level override form (#11672)
This commit is contained in:
@ -517,6 +517,15 @@ export type CustomDomainValidRecords = {
|
|||||||
records: Array<CustomDomainRecord>;
|
records: Array<CustomDomainRecord>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Database Event Action */
|
||||||
|
export enum DatabaseEventAction {
|
||||||
|
CREATED = 'CREATED',
|
||||||
|
DELETED = 'DELETED',
|
||||||
|
DESTROYED = 'DESTROYED',
|
||||||
|
RESTORED = 'RESTORED',
|
||||||
|
UPDATED = 'UPDATED'
|
||||||
|
}
|
||||||
|
|
||||||
export type DateFilter = {
|
export type DateFilter = {
|
||||||
eq?: InputMaybe<Scalars['Date']['input']>;
|
eq?: InputMaybe<Scalars['Date']['input']>;
|
||||||
gt?: InputMaybe<Scalars['Date']['input']>;
|
gt?: InputMaybe<Scalars['Date']['input']>;
|
||||||
@ -1004,7 +1013,7 @@ export type Mutation = {
|
|||||||
uploadImage: Scalars['String']['output'];
|
uploadImage: Scalars['String']['output'];
|
||||||
uploadProfilePicture: Scalars['String']['output'];
|
uploadProfilePicture: Scalars['String']['output'];
|
||||||
uploadWorkspaceLogo: Scalars['String']['output'];
|
uploadWorkspaceLogo: Scalars['String']['output'];
|
||||||
upsertOneObjectPermission: ObjectPermission;
|
upsertObjectPermissions: Array<ObjectPermission>;
|
||||||
upsertSettingPermissions: Array<SettingPermission>;
|
upsertSettingPermissions: Array<SettingPermission>;
|
||||||
userLookupAdminPanel: UserLookup;
|
userLookupAdminPanel: UserLookup;
|
||||||
validateApprovedAccessDomain: ApprovedAccessDomain;
|
validateApprovedAccessDomain: ApprovedAccessDomain;
|
||||||
@ -1368,8 +1377,8 @@ export type MutationUploadWorkspaceLogoArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationUpsertOneObjectPermissionArgs = {
|
export type MutationUpsertObjectPermissionsArgs = {
|
||||||
upsertObjectPermissionInput: UpsertObjectPermissionInput;
|
upsertObjectPermissionsInput: UpsertObjectPermissionsInput;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1481,6 +1490,14 @@ export type ObjectPermission = {
|
|||||||
roleId: Scalars['String']['output'];
|
roleId: Scalars['String']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ObjectPermissionInput = {
|
||||||
|
canDestroyObjectRecords?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
|
canReadObjectRecords?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
|
canSoftDeleteObjectRecords?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
|
canUpdateObjectRecords?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
|
objectMetadataId: Scalars['String']['input'];
|
||||||
|
};
|
||||||
|
|
||||||
export type ObjectRecordFilterInput = {
|
export type ObjectRecordFilterInput = {
|
||||||
and?: InputMaybe<Array<ObjectRecordFilterInput>>;
|
and?: InputMaybe<Array<ObjectRecordFilterInput>>;
|
||||||
createdAt?: InputMaybe<DateFilter>;
|
createdAt?: InputMaybe<DateFilter>;
|
||||||
@ -1500,6 +1517,21 @@ export type ObjectStandardOverrides = {
|
|||||||
translations?: Maybe<Scalars['JSON']['output']>;
|
translations?: Maybe<Scalars['JSON']['output']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type OnDbEventDto = {
|
||||||
|
__typename?: 'OnDbEventDTO';
|
||||||
|
action: DatabaseEventAction;
|
||||||
|
eventDate: Scalars['DateTime']['output'];
|
||||||
|
objectNameSingular: Scalars['String']['output'];
|
||||||
|
record: Scalars['JSON']['output'];
|
||||||
|
updatedFields?: Maybe<Array<Scalars['String']['output']>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OnDbEventInput = {
|
||||||
|
action?: InputMaybe<DatabaseEventAction>;
|
||||||
|
objectNameSingular?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
recordId?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
};
|
||||||
|
|
||||||
/** Onboarding status */
|
/** Onboarding status */
|
||||||
export enum OnboardingStatus {
|
export enum OnboardingStatus {
|
||||||
COMPLETED = 'COMPLETED',
|
COMPLETED = 'COMPLETED',
|
||||||
@ -2090,6 +2122,16 @@ export type SubmitFormStepInput = {
|
|||||||
workflowRunId: Scalars['String']['input'];
|
workflowRunId: Scalars['String']['input'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Subscription = {
|
||||||
|
__typename?: 'Subscription';
|
||||||
|
onDbEvent: OnDbEventDto;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type SubscriptionOnDbEventArgs = {
|
||||||
|
input: OnDbEventInput;
|
||||||
|
};
|
||||||
|
|
||||||
export enum SubscriptionInterval {
|
export enum SubscriptionInterval {
|
||||||
Day = 'Day',
|
Day = 'Day',
|
||||||
Month = 'Month',
|
Month = 'Month',
|
||||||
@ -2321,12 +2363,8 @@ export type UpdateWorkspaceInput = {
|
|||||||
subdomain?: InputMaybe<Scalars['String']['input']>;
|
subdomain?: InputMaybe<Scalars['String']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpsertObjectPermissionInput = {
|
export type UpsertObjectPermissionsInput = {
|
||||||
canDestroyObjectRecords?: InputMaybe<Scalars['Boolean']['input']>;
|
objectPermissions: Array<ObjectPermissionInput>;
|
||||||
canReadObjectRecords?: InputMaybe<Scalars['Boolean']['input']>;
|
|
||||||
canSoftDeleteObjectRecords?: InputMaybe<Scalars['Boolean']['input']>;
|
|
||||||
canUpdateObjectRecords?: InputMaybe<Scalars['Boolean']['input']>;
|
|
||||||
objectMetadataId: Scalars['String']['input'];
|
|
||||||
roleId: Scalars['String']['input'];
|
roleId: Scalars['String']['input'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -929,7 +929,7 @@ export type Mutation = {
|
|||||||
uploadImage: Scalars['String'];
|
uploadImage: Scalars['String'];
|
||||||
uploadProfilePicture: Scalars['String'];
|
uploadProfilePicture: Scalars['String'];
|
||||||
uploadWorkspaceLogo: Scalars['String'];
|
uploadWorkspaceLogo: Scalars['String'];
|
||||||
upsertOneObjectPermission: ObjectPermission;
|
upsertObjectPermissions: Array<ObjectPermission>;
|
||||||
upsertSettingPermissions: Array<SettingPermission>;
|
upsertSettingPermissions: Array<SettingPermission>;
|
||||||
userLookupAdminPanel: UserLookup;
|
userLookupAdminPanel: UserLookup;
|
||||||
validateApprovedAccessDomain: ApprovedAccessDomain;
|
validateApprovedAccessDomain: ApprovedAccessDomain;
|
||||||
@ -1243,8 +1243,8 @@ export type MutationUploadWorkspaceLogoArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationUpsertOneObjectPermissionArgs = {
|
export type MutationUpsertObjectPermissionsArgs = {
|
||||||
upsertObjectPermissionInput: UpsertObjectPermissionInput;
|
upsertObjectPermissionsInput: UpsertObjectPermissionsInput;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1356,6 +1356,14 @@ export type ObjectPermission = {
|
|||||||
roleId: Scalars['String'];
|
roleId: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ObjectPermissionInput = {
|
||||||
|
canDestroyObjectRecords?: InputMaybe<Scalars['Boolean']>;
|
||||||
|
canReadObjectRecords?: InputMaybe<Scalars['Boolean']>;
|
||||||
|
canSoftDeleteObjectRecords?: InputMaybe<Scalars['Boolean']>;
|
||||||
|
canUpdateObjectRecords?: InputMaybe<Scalars['Boolean']>;
|
||||||
|
objectMetadataId: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
export type ObjectRecordFilterInput = {
|
export type ObjectRecordFilterInput = {
|
||||||
and?: InputMaybe<Array<ObjectRecordFilterInput>>;
|
and?: InputMaybe<Array<ObjectRecordFilterInput>>;
|
||||||
createdAt?: InputMaybe<DateFilter>;
|
createdAt?: InputMaybe<DateFilter>;
|
||||||
@ -2142,12 +2150,8 @@ export type UpdateWorkspaceInput = {
|
|||||||
subdomain?: InputMaybe<Scalars['String']>;
|
subdomain?: InputMaybe<Scalars['String']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpsertObjectPermissionInput = {
|
export type UpsertObjectPermissionsInput = {
|
||||||
canDestroyObjectRecords?: InputMaybe<Scalars['Boolean']>;
|
objectPermissions: Array<ObjectPermissionInput>;
|
||||||
canReadObjectRecords?: InputMaybe<Scalars['Boolean']>;
|
|
||||||
canSoftDeleteObjectRecords?: InputMaybe<Scalars['Boolean']>;
|
|
||||||
canUpdateObjectRecords?: InputMaybe<Scalars['Boolean']>;
|
|
||||||
objectMetadataId: Scalars['String'];
|
|
||||||
roleId: Scalars['String'];
|
roleId: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2759,6 +2763,13 @@ export type UpdateWorkspaceMemberRoleMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type UpdateWorkspaceMemberRoleMutation = { __typename?: 'Mutation', updateWorkspaceMemberRole: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, roles?: Array<{ __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, name: { __typename?: 'FullName', firstName: string, lastName: string } } };
|
export type UpdateWorkspaceMemberRoleMutation = { __typename?: 'Mutation', updateWorkspaceMemberRole: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, roles?: Array<{ __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, name: { __typename?: 'FullName', firstName: string, lastName: string } } };
|
||||||
|
|
||||||
|
export type UpsertObjectPermissionsMutationVariables = Exact<{
|
||||||
|
upsertObjectPermissionsInput: UpsertObjectPermissionsInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type UpsertObjectPermissionsMutation = { __typename?: 'Mutation', upsertObjectPermissions: Array<{ __typename?: 'ObjectPermission', id: string, objectMetadataId: string, roleId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> };
|
||||||
|
|
||||||
export type UpsertSettingPermissionsMutationVariables = Exact<{
|
export type UpsertSettingPermissionsMutationVariables = Exact<{
|
||||||
upsertSettingPermissionsInput: UpsertSettingPermissionsInput;
|
upsertSettingPermissionsInput: UpsertSettingPermissionsInput;
|
||||||
}>;
|
}>;
|
||||||
@ -5058,6 +5069,41 @@ export function useUpdateWorkspaceMemberRoleMutation(baseOptions?: Apollo.Mutati
|
|||||||
export type UpdateWorkspaceMemberRoleMutationHookResult = ReturnType<typeof useUpdateWorkspaceMemberRoleMutation>;
|
export type UpdateWorkspaceMemberRoleMutationHookResult = ReturnType<typeof useUpdateWorkspaceMemberRoleMutation>;
|
||||||
export type UpdateWorkspaceMemberRoleMutationResult = Apollo.MutationResult<UpdateWorkspaceMemberRoleMutation>;
|
export type UpdateWorkspaceMemberRoleMutationResult = Apollo.MutationResult<UpdateWorkspaceMemberRoleMutation>;
|
||||||
export type UpdateWorkspaceMemberRoleMutationOptions = Apollo.BaseMutationOptions<UpdateWorkspaceMemberRoleMutation, UpdateWorkspaceMemberRoleMutationVariables>;
|
export type UpdateWorkspaceMemberRoleMutationOptions = Apollo.BaseMutationOptions<UpdateWorkspaceMemberRoleMutation, UpdateWorkspaceMemberRoleMutationVariables>;
|
||||||
|
export const UpsertObjectPermissionsDocument = gql`
|
||||||
|
mutation UpsertObjectPermissions($upsertObjectPermissionsInput: UpsertObjectPermissionsInput!) {
|
||||||
|
upsertObjectPermissions(
|
||||||
|
upsertObjectPermissionsInput: $upsertObjectPermissionsInput
|
||||||
|
) {
|
||||||
|
...ObjectPermissionFragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${ObjectPermissionFragmentFragmentDoc}`;
|
||||||
|
export type UpsertObjectPermissionsMutationFn = Apollo.MutationFunction<UpsertObjectPermissionsMutation, UpsertObjectPermissionsMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useUpsertObjectPermissionsMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useUpsertObjectPermissionsMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useUpsertObjectPermissionsMutation` returns a tuple that includes:
|
||||||
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [upsertObjectPermissionsMutation, { data, loading, error }] = useUpsertObjectPermissionsMutation({
|
||||||
|
* variables: {
|
||||||
|
* upsertObjectPermissionsInput: // value for 'upsertObjectPermissionsInput'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useUpsertObjectPermissionsMutation(baseOptions?: Apollo.MutationHookOptions<UpsertObjectPermissionsMutation, UpsertObjectPermissionsMutationVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useMutation<UpsertObjectPermissionsMutation, UpsertObjectPermissionsMutationVariables>(UpsertObjectPermissionsDocument, options);
|
||||||
|
}
|
||||||
|
export type UpsertObjectPermissionsMutationHookResult = ReturnType<typeof useUpsertObjectPermissionsMutation>;
|
||||||
|
export type UpsertObjectPermissionsMutationResult = Apollo.MutationResult<UpsertObjectPermissionsMutation>;
|
||||||
|
export type UpsertObjectPermissionsMutationOptions = Apollo.BaseMutationOptions<UpsertObjectPermissionsMutation, UpsertObjectPermissionsMutationVariables>;
|
||||||
export const UpsertSettingPermissionsDocument = gql`
|
export const UpsertSettingPermissionsDocument = gql`
|
||||||
mutation UpsertSettingPermissions($upsertSettingPermissionsInput: UpsertSettingPermissionsInput!) {
|
mutation UpsertSettingPermissions($upsertSettingPermissionsInput: UpsertSettingPermissionsInput!) {
|
||||||
upsertSettingPermissions(
|
upsertSettingPermissions(
|
||||||
|
|||||||
@ -2,10 +2,12 @@ import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDr
|
|||||||
import { settingsPersistedRoleFamilyState } from '@/settings/roles/states/settingsPersistedRoleFamilyState';
|
import { settingsPersistedRoleFamilyState } from '@/settings/roles/states/settingsPersistedRoleFamilyState';
|
||||||
import { settingsRoleIdsState } from '@/settings/roles/states/settingsRoleIdsState';
|
import { settingsRoleIdsState } from '@/settings/roles/states/settingsRoleIdsState';
|
||||||
import { settingsRolesIsLoadingState } from '@/settings/roles/states/settingsRolesIsLoadingState';
|
import { settingsRolesIsLoadingState } from '@/settings/roles/states/settingsRolesIsLoadingState';
|
||||||
|
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { Role, useGetRolesQuery } from '~/generated/graphql';
|
import { Role, useGetRolesQuery } from '~/generated/graphql';
|
||||||
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
|
|
||||||
export const SettingsRolesQueryEffect = () => {
|
export const SettingsRolesQueryEffect = () => {
|
||||||
const { data, loading } = useGetRolesQuery({
|
const { data, loading } = useGetRolesQuery({
|
||||||
@ -17,11 +19,18 @@ export const SettingsRolesQueryEffect = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const populateRoles = useRecoilCallback(
|
const populateRoles = useRecoilCallback(
|
||||||
({ set }) =>
|
({ set, snapshot }) =>
|
||||||
(roles: Role[]) => {
|
(roles: Role[]) => {
|
||||||
const roleIds = roles.map((role) => role.id);
|
const roleIds = roles.map((role) => role.id);
|
||||||
set(settingsRoleIdsState, roleIds);
|
set(settingsRoleIdsState, roleIds);
|
||||||
roles.forEach((role) => {
|
roles.forEach((role) => {
|
||||||
|
const persistedRole = getSnapshotValue(
|
||||||
|
snapshot,
|
||||||
|
settingsPersistedRoleFamilyState(role.id),
|
||||||
|
);
|
||||||
|
if (isDeeplyEqual(role, persistedRole)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
set(settingsDraftRoleFamilyState(role.id), role);
|
set(settingsDraftRoleFamilyState(role.id), role);
|
||||||
set(settingsPersistedRoleFamilyState(role.id), role);
|
set(settingsPersistedRoleFamilyState(role.id), role);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
import { OBJECT_PERMISSION_FRAGMENT } from '@/settings/roles/graphql/fragments/objectPermissionFragment';
|
||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const UPSERT_OBJECT_PERMISSIONS = gql`
|
||||||
|
${OBJECT_PERMISSION_FRAGMENT}
|
||||||
|
mutation UpsertObjectPermissions(
|
||||||
|
$upsertObjectPermissionsInput: UpsertObjectPermissionsInput!
|
||||||
|
) {
|
||||||
|
upsertObjectPermissions(
|
||||||
|
upsertObjectPermissionsInput: $upsertObjectPermissionsInput
|
||||||
|
) {
|
||||||
|
...ObjectPermissionFragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@ -14,11 +14,13 @@ const StyledRolePermissionsContainer = styled.div`
|
|||||||
type SettingsRolePermissionsProps = {
|
type SettingsRolePermissionsProps = {
|
||||||
roleId: string;
|
roleId: string;
|
||||||
isEditable: boolean;
|
isEditable: boolean;
|
||||||
|
isCreateMode: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SettingsRolePermissions = ({
|
export const SettingsRolePermissions = ({
|
||||||
roleId,
|
roleId,
|
||||||
isEditable,
|
isEditable,
|
||||||
|
isCreateMode,
|
||||||
}: SettingsRolePermissionsProps) => {
|
}: SettingsRolePermissionsProps) => {
|
||||||
const isPermissionsV2Enabled = useIsFeatureEnabled(
|
const isPermissionsV2Enabled = useIsFeatureEnabled(
|
||||||
FeatureFlagKey.IsPermissionsV2Enabled,
|
FeatureFlagKey.IsPermissionsV2Enabled,
|
||||||
@ -30,7 +32,7 @@ export const SettingsRolePermissions = ({
|
|||||||
roleId={roleId}
|
roleId={roleId}
|
||||||
isEditable={isEditable}
|
isEditable={isEditable}
|
||||||
/>
|
/>
|
||||||
{isPermissionsV2Enabled && (
|
{isPermissionsV2Enabled && !isCreateMode && (
|
||||||
<SettingsRolePermissionsObjectLevelSection
|
<SettingsRolePermissionsObjectLevelSection
|
||||||
roleId={roleId}
|
roleId={roleId}
|
||||||
isEditable={isEditable}
|
isEditable={isEditable}
|
||||||
|
|||||||
@ -3,10 +3,10 @@ import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDr
|
|||||||
import { Meta, StoryObj } from '@storybook/react';
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||||
import { PENDING_ROLE_ID } from '~/pages/settings/roles/SettingsRoleCreate';
|
import { PENDING_ROLE_ID } from '~/pages/settings/roles/SettingsRoleCreate';
|
||||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||||
import { getRolesMock } from '~/testing/mock-data/roles';
|
import { getRolesMock } from '~/testing/mock-data/roles';
|
||||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
|
||||||
|
|
||||||
const SettingsRolePermissionsWrapper = (
|
const SettingsRolePermissionsWrapper = (
|
||||||
args: React.ComponentProps<typeof SettingsRolePermissions>,
|
args: React.ComponentProps<typeof SettingsRolePermissions>,
|
||||||
@ -25,6 +25,7 @@ const SettingsRolePermissionsWrapper = (
|
|||||||
<SettingsRolePermissions
|
<SettingsRolePermissions
|
||||||
roleId={args.roleId}
|
roleId={args.roleId}
|
||||||
isEditable={args.isEditable}
|
isEditable={args.isEditable}
|
||||||
|
isCreateMode={args.isCreateMode}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -42,6 +43,7 @@ export const Default: Story = {
|
|||||||
args: {
|
args: {
|
||||||
roleId: '1',
|
roleId: '1',
|
||||||
isEditable: true,
|
isEditable: true,
|
||||||
|
isCreateMode: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -49,11 +51,14 @@ export const ReadOnly: Story = {
|
|||||||
args: {
|
args: {
|
||||||
roleId: '1',
|
roleId: '1',
|
||||||
isEditable: false,
|
isEditable: false,
|
||||||
|
isCreateMode: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PendingRole: Story = {
|
export const PendingRole: Story = {
|
||||||
args: {
|
args: {
|
||||||
roleId: PENDING_ROLE_ID,
|
roleId: PENDING_ROLE_ID,
|
||||||
|
isEditable: true,
|
||||||
|
isCreateMode: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -19,7 +19,8 @@ export const SettingsRolePermissionsObjectLevelObjectPickerDropdownContent = ({
|
|||||||
}: SettingsRolePermissionsObjectLevelObjectPickerDropdownContentProps) => {
|
}: SettingsRolePermissionsObjectLevelObjectPickerDropdownContentProps) => {
|
||||||
const [searchFilter, setSearchFilter] = useState('');
|
const [searchFilter, setSearchFilter] = useState('');
|
||||||
|
|
||||||
const { objectMetadataItems } = useFilteredObjectMetadataItems();
|
const { alphaSortedActiveObjectMetadataItems: objectMetadataItems } =
|
||||||
|
useFilteredObjectMetadataItems();
|
||||||
|
|
||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
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 { SETTINGS_ROLE_OBJECT_PERMISSION_ICON_CONFIG } from '@/settings/roles/role-permissions/objects-permissions/constants/settingsRoleObjectPermissionIconConfig';
|
||||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
@ -46,12 +47,8 @@ export const SettingsRolePermissionsObjectLevelOverrideCell = ({
|
|||||||
settingsDraftRoleFamilyState(objectPermission.roleId),
|
settingsDraftRoleFamilyState(objectPermission.roleId),
|
||||||
);
|
);
|
||||||
|
|
||||||
const permissionMappings = {
|
const permissionMappings =
|
||||||
canReadObjectRecords: 'canReadAllObjectRecords',
|
SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING;
|
||||||
canUpdateObjectRecords: 'canUpdateAllObjectRecords',
|
|
||||||
canSoftDeleteObjectRecords: 'canSoftDeleteAllObjectRecords',
|
|
||||||
canDestroyObjectRecords: 'canDestroyAllObjectRecords',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
type ObjectPermissionKey = keyof typeof permissionMappings;
|
type ObjectPermissionKey = keyof typeof permissionMappings;
|
||||||
|
|
||||||
|
|||||||
@ -1,24 +1,30 @@
|
|||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import { SettingsRolePermissionsObjectLevelObjectPickerDropdownContent } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelObjectPickerDropdownContent';
|
||||||
import { SettingsRolePermissionsObjectLevelTableHeader } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader';
|
import { SettingsRolePermissionsObjectLevelTableHeader } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader';
|
||||||
import { SettingsRolePermissionsObjectLevelTableRow } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow';
|
import { SettingsRolePermissionsObjectLevelTableRow } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow';
|
||||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||||
|
import { SettingsPath } from '@/types/SettingsPath';
|
||||||
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { Table } from '@/ui/layout/table/components/Table';
|
import { Table } from '@/ui/layout/table/components/Table';
|
||||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { H2Title } from 'twenty-ui/display';
|
import { H2Title, IconPlus } from 'twenty-ui/display';
|
||||||
|
import { Button } from 'twenty-ui/input';
|
||||||
import { Section } from 'twenty-ui/layout';
|
import { Section } from 'twenty-ui/layout';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||||
|
|
||||||
// const StyledCreateObjectOverrideSection = styled(Section)`
|
const StyledCreateObjectOverrideSection = styled(Section)`
|
||||||
// border-top: 1px solid ${({ theme }) => theme.border.color.light};
|
border-top: 1px solid ${({ theme }) => theme.border.color.light};
|
||||||
// display: flex;
|
display: flex;
|
||||||
// justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
// padding-top: ${({ theme }) => theme.spacing(2)};
|
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||||
// padding-bottom: ${({ theme }) => theme.spacing(2)};
|
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||||
// `;
|
`;
|
||||||
|
|
||||||
const StyledTableRows = styled.div`
|
const StyledTableRows = styled.div`
|
||||||
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||||
@ -36,11 +42,14 @@ const StyledNoOverride = styled(TableCell)`
|
|||||||
|
|
||||||
export const SettingsRolePermissionsObjectLevelSection = ({
|
export const SettingsRolePermissionsObjectLevelSection = ({
|
||||||
roleId,
|
roleId,
|
||||||
|
isEditable,
|
||||||
}: SettingsRolePermissionsObjectLevelSectionProps) => {
|
}: SettingsRolePermissionsObjectLevelSectionProps) => {
|
||||||
const settingsDraftRole = useRecoilValue(
|
const [settingsDraftRole, setSettingsDraftRole] = useRecoilState(
|
||||||
settingsDraftRoleFamilyState(roleId),
|
settingsDraftRoleFamilyState(roleId),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const navigate = useNavigateSettings();
|
||||||
|
|
||||||
const objectMetadataItems = useObjectMetadataItems();
|
const objectMetadataItems = useObjectMetadataItems();
|
||||||
|
|
||||||
const objectMetadataMap = objectMetadataItems.objectMetadataItems.reduce(
|
const objectMetadataMap = objectMetadataItems.objectMetadataItems.reduce(
|
||||||
@ -51,29 +60,52 @@ export const SettingsRolePermissionsObjectLevelSection = ({
|
|||||||
{} as Record<string, ObjectMetadataItem>,
|
{} as Record<string, ObjectMetadataItem>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const objectPermissions = settingsDraftRole.objectPermissions;
|
const filteredObjectPermissions = settingsDraftRole.objectPermissions?.filter(
|
||||||
|
(objectPermission) =>
|
||||||
|
(isDefined(objectPermission.canReadObjectRecords) &&
|
||||||
|
objectPermission.canReadObjectRecords !==
|
||||||
|
settingsDraftRole.canReadAllObjectRecords) ||
|
||||||
|
(isDefined(objectPermission.canUpdateObjectRecords) &&
|
||||||
|
objectPermission.canUpdateObjectRecords !==
|
||||||
|
settingsDraftRole.canUpdateAllObjectRecords) ||
|
||||||
|
(isDefined(objectPermission.canSoftDeleteObjectRecords) &&
|
||||||
|
objectPermission.canSoftDeleteObjectRecords !==
|
||||||
|
settingsDraftRole.canSoftDeleteAllObjectRecords) ||
|
||||||
|
(isDefined(objectPermission.canDestroyObjectRecords) &&
|
||||||
|
objectPermission.canDestroyObjectRecords !==
|
||||||
|
settingsDraftRole.canDestroyAllObjectRecords),
|
||||||
|
);
|
||||||
|
|
||||||
// const handleSelectObjectMetadata = (objectMetadataId: string) => {
|
const handleSelectObjectMetadata = (objectMetadataId: string) => {
|
||||||
// setSettingsDraftRole((draftRole) => ({
|
setSettingsDraftRole((draftRole) => ({
|
||||||
// ...draftRole,
|
...draftRole,
|
||||||
// objectPermissions: [
|
objectPermissions: [
|
||||||
// ...(draftRole.objectPermissions ?? []),
|
...(draftRole.objectPermissions ?? []).filter(
|
||||||
// { objectMetadataId, roleId, id: v4() },
|
(permission) =>
|
||||||
// ],
|
permission.objectMetadataId !== objectMetadataId ||
|
||||||
// }));
|
permission.roleId !== roleId,
|
||||||
// };
|
),
|
||||||
|
{ objectMetadataId, roleId, id: v4() },
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
navigate(SettingsPath.RoleObjectLevel, {
|
||||||
|
roleId,
|
||||||
|
objectMetadataId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section>
|
<Section>
|
||||||
<H2Title
|
<H2Title
|
||||||
title={t`Object-Level Permissions`}
|
title={t`Object-Level Permissions`}
|
||||||
description={t`Set additional object-level permissions`}
|
description={t`Ability to interact with specific objects`}
|
||||||
/>
|
/>
|
||||||
<Table>
|
<Table>
|
||||||
<SettingsRolePermissionsObjectLevelTableHeader />
|
<SettingsRolePermissionsObjectLevelTableHeader />
|
||||||
<StyledTableRows>
|
<StyledTableRows>
|
||||||
{isDefined(objectPermissions) && objectPermissions?.length > 0 ? (
|
{isDefined(filteredObjectPermissions) &&
|
||||||
objectPermissions?.map((objectPermission) => (
|
filteredObjectPermissions?.length > 0 ? (
|
||||||
|
filteredObjectPermissions?.map((objectPermission) => (
|
||||||
<SettingsRolePermissionsObjectLevelTableRow
|
<SettingsRolePermissionsObjectLevelTableRow
|
||||||
key={objectPermission.id}
|
key={objectPermission.id}
|
||||||
objectPermission={objectPermission}
|
objectPermission={objectPermission}
|
||||||
@ -87,14 +119,14 @@ export const SettingsRolePermissionsObjectLevelSection = ({
|
|||||||
)}
|
)}
|
||||||
</StyledTableRows>
|
</StyledTableRows>
|
||||||
</Table>
|
</Table>
|
||||||
{/* <StyledCreateObjectOverrideSection>
|
<StyledCreateObjectOverrideSection>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId="role-object-select"
|
dropdownId="role-object-select"
|
||||||
dropdownHotkeyScope={{ scope: 'roleObject' }}
|
dropdownHotkeyScope={{ scope: 'roleObject' }}
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<Button
|
<Button
|
||||||
Icon={IconPlus}
|
Icon={IconPlus}
|
||||||
title={t`Add Object`}
|
title={t`Add rule`}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
disabled={!isEditable}
|
disabled={!isEditable}
|
||||||
@ -102,12 +134,16 @@ export const SettingsRolePermissionsObjectLevelSection = ({
|
|||||||
}
|
}
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<SettingsRolePermissionsObjectLevelObjectPickerDropdownContent
|
<SettingsRolePermissionsObjectLevelObjectPickerDropdownContent
|
||||||
excludedObjectMetadataIds={[]}
|
excludedObjectMetadataIds={
|
||||||
|
filteredObjectPermissions?.map(
|
||||||
|
(objectPermission) => objectPermission.objectMetadataId,
|
||||||
|
) ?? []
|
||||||
|
}
|
||||||
onSelect={handleSelectObjectMetadata}
|
onSelect={handleSelectObjectMetadata}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</StyledCreateObjectOverrideSection> */}
|
</StyledCreateObjectOverrideSection>
|
||||||
</Section>
|
</Section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,9 +3,9 @@ import { TableRow } from '@/ui/layout/table/components/TableRow';
|
|||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
|
|
||||||
export const SettingsRolePermissionsObjectLevelTableHeader = () => (
|
export const SettingsRolePermissionsObjectLevelTableHeader = () => (
|
||||||
<TableRow>
|
<TableRow gridAutoColumns="180px 1fr 1fr">
|
||||||
<TableHeader>{t`Object`}</TableHeader>
|
<TableHeader>{t`Object`}</TableHeader>
|
||||||
<TableHeader>{t`Permission overrides`}</TableHeader>
|
<TableHeader>{t`Permissions`}</TableHeader>
|
||||||
<TableHeader></TableHeader>
|
<TableHeader></TableHeader>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -5,7 +5,11 @@ import { TableCell } from '@/ui/layout/table/components/TableCell';
|
|||||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { IconChevronRight, useIcons } from 'twenty-ui/display';
|
import {
|
||||||
|
IconChevronRight,
|
||||||
|
OverflowingTextWithTooltip,
|
||||||
|
useIcons,
|
||||||
|
} from 'twenty-ui/display';
|
||||||
import { ObjectPermission } from '~/generated/graphql';
|
import { ObjectPermission } from '~/generated/graphql';
|
||||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||||
|
|
||||||
@ -44,6 +48,7 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({
|
|||||||
roleId: objectPermission.roleId,
|
roleId: objectPermission.roleId,
|
||||||
objectMetadataId: objectPermission.objectMetadataId,
|
objectMetadataId: objectPermission.objectMetadataId,
|
||||||
})}
|
})}
|
||||||
|
gridAutoColumns="180px 1fr 1fr"
|
||||||
>
|
>
|
||||||
<StyledNameTableCell>
|
<StyledNameTableCell>
|
||||||
{!!Icon && (
|
{!!Icon && (
|
||||||
@ -54,7 +59,7 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<StyledNameLabel title={objectMetadataItem.labelPlural}>
|
<StyledNameLabel title={objectMetadataItem.labelPlural}>
|
||||||
{objectMetadataItem.labelPlural}
|
<OverflowingTextWithTooltip text={objectMetadataItem.labelPlural} />
|
||||||
</StyledNameLabel>
|
</StyledNameLabel>
|
||||||
</StyledNameTableCell>
|
</StyledNameTableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
|
|||||||
@ -0,0 +1,95 @@
|
|||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { IconReload, IconX } from 'twenty-ui/display';
|
||||||
|
import { Checkbox } from 'twenty-ui/input';
|
||||||
|
|
||||||
|
export type OverridableCheckboxType = 'default' | 'override' | 'no_cta';
|
||||||
|
|
||||||
|
const StyledOverridableCheckboxContainer = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
width: 48px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledOverridableCheckboxContainerItem = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
height: 24px;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledIconWrapper = styled.div<{
|
||||||
|
isDisabled?: boolean;
|
||||||
|
}>`
|
||||||
|
align-items: center;
|
||||||
|
cursor: ${({ isDisabled }) => (isDisabled ? 'not-allowed' : 'pointer')};
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: ${({ isDisabled }) => (isDisabled ? 0.5 : 1)};
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export type OverridableCheckboxProps = {
|
||||||
|
type?: OverridableCheckboxType;
|
||||||
|
onChange: () => void;
|
||||||
|
checked: boolean;
|
||||||
|
disabled: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OverridableCheckbox = ({
|
||||||
|
type = 'default',
|
||||||
|
onChange,
|
||||||
|
checked,
|
||||||
|
disabled,
|
||||||
|
}: OverridableCheckboxProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledOverridableCheckboxContainer>
|
||||||
|
{type === 'default' && (
|
||||||
|
<>
|
||||||
|
<StyledOverridableCheckboxContainerItem>
|
||||||
|
<Checkbox checked={true} disabled={true} />
|
||||||
|
</StyledOverridableCheckboxContainerItem>
|
||||||
|
<StyledOverridableCheckboxContainerItem>
|
||||||
|
<StyledIconWrapper
|
||||||
|
onClick={disabled ? undefined : onChange}
|
||||||
|
isDisabled={disabled}
|
||||||
|
>
|
||||||
|
<IconX
|
||||||
|
size={theme.icon.size.md}
|
||||||
|
color={theme.font.color.secondary}
|
||||||
|
/>
|
||||||
|
</StyledIconWrapper>
|
||||||
|
</StyledOverridableCheckboxContainerItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{type === 'override' && (
|
||||||
|
<>
|
||||||
|
<StyledOverridableCheckboxContainerItem>
|
||||||
|
<Checkbox checked={false} disabled={true} />
|
||||||
|
</StyledOverridableCheckboxContainerItem>
|
||||||
|
<StyledOverridableCheckboxContainerItem>
|
||||||
|
<StyledIconWrapper
|
||||||
|
onClick={disabled ? undefined : onChange}
|
||||||
|
isDisabled={disabled}
|
||||||
|
>
|
||||||
|
<IconReload
|
||||||
|
size={theme.icon.size.md}
|
||||||
|
color={theme.adaptiveColors.orange4}
|
||||||
|
/>
|
||||||
|
</StyledIconWrapper>
|
||||||
|
</StyledOverridableCheckboxContainerItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{type === 'no_cta' && (
|
||||||
|
<StyledOverridableCheckboxContainerItem>
|
||||||
|
<Checkbox checked={checked} disabled={disabled} onChange={onChange} />
|
||||||
|
</StyledOverridableCheckboxContainerItem>
|
||||||
|
)}
|
||||||
|
</StyledOverridableCheckboxContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,13 +1,14 @@
|
|||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { SettingsRolePermissionsObjectLevelObjectFormObjectLevelHeader } from '@/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevelHeader';
|
import { SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableHeader } from '@/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableHeader';
|
||||||
import { SettingsRolePermissionsObjectsTableRow } from '@/settings/roles/role-permissions/objects-permissions/components/SettingsRolePermissionsObjectsTableRow';
|
import { SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow } from '@/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow';
|
||||||
import { SettingsRolePermissionsObjectPermission } from '@/settings/roles/role-permissions/objects-permissions/types/SettingsRolePermissionsObjectPermission';
|
import { SettingsRolePermissionsObjectLevelPermission } from '@/settings/roles/role-permissions/objects-permissions/types/SettingsRolePermissionsObjectPermission';
|
||||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
import { H2Title } from 'twenty-ui/display';
|
import { H2Title } from 'twenty-ui/display';
|
||||||
import { Section } from 'twenty-ui/layout';
|
import { Section } from 'twenty-ui/layout';
|
||||||
|
import { ObjectPermission } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
const StyledTable = styled.div`
|
const StyledTable = styled.div`
|
||||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||||
@ -27,7 +28,7 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevel = ({
|
|||||||
roleId,
|
roleId,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
}: SettingsRolePermissionsObjectLevelObjectFormObjectLevelProps) => {
|
}: SettingsRolePermissionsObjectLevelObjectFormObjectLevelProps) => {
|
||||||
const settingsDraftRole = useRecoilValue(
|
const [settingsDraftRole, setSettingsDraftRole] = useRecoilState(
|
||||||
settingsDraftRoleFamilyState(roleId),
|
settingsDraftRoleFamilyState(roleId),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -42,40 +43,56 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevel = ({
|
|||||||
|
|
||||||
const objectLabel = objectMetadataItem.labelPlural;
|
const objectLabel = objectMetadataItem.labelPlural;
|
||||||
|
|
||||||
const objectPermissionsConfig: SettingsRolePermissionsObjectPermission[] = [
|
const updateObjectPermission = (
|
||||||
{
|
permissionKey: keyof ObjectPermission,
|
||||||
key: 'canReadObjectRecords',
|
value: boolean | null,
|
||||||
label: t`See Records on ${objectLabel}`,
|
) => {
|
||||||
value: settingsDraftRoleObjectPermissions.canReadObjectRecords,
|
setSettingsDraftRole((currentRole) => {
|
||||||
setValue: (_value: boolean) => {
|
const updatedPermissions = currentRole.objectPermissions?.map((perm) => {
|
||||||
// TODO: Implement
|
if (perm.objectMetadataId === objectMetadataItem.id) {
|
||||||
|
return { ...perm, [permissionKey]: value };
|
||||||
|
}
|
||||||
|
return perm;
|
||||||
|
});
|
||||||
|
return { ...currentRole, objectPermissions: updatedPermissions };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const objectPermissionsConfig: SettingsRolePermissionsObjectLevelPermission[] =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
key: 'canReadObjectRecords',
|
||||||
|
label: t`See Records on ${objectLabel}`,
|
||||||
|
value: settingsDraftRoleObjectPermissions.canReadObjectRecords,
|
||||||
|
setValue: (value: boolean | null) => {
|
||||||
|
updateObjectPermission('canReadObjectRecords', value);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'canUpdateObjectRecords',
|
||||||
key: 'canUpdateObjectRecords',
|
label: t`Edit Records on ${objectLabel}`,
|
||||||
label: t`Edit Records on ${objectLabel}`,
|
value: settingsDraftRoleObjectPermissions.canUpdateObjectRecords,
|
||||||
value: settingsDraftRoleObjectPermissions.canUpdateObjectRecords,
|
setValue: (value: boolean | null) => {
|
||||||
setValue: (_value: boolean) => {
|
updateObjectPermission('canUpdateObjectRecords', value);
|
||||||
// TODO: Implement
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'canSoftDeleteObjectRecords',
|
||||||
key: 'canSoftDeleteObjectRecords',
|
label: t`Delete Records on ${objectLabel}`,
|
||||||
label: t`Delete Records on ${objectLabel}`,
|
value: settingsDraftRoleObjectPermissions.canSoftDeleteObjectRecords,
|
||||||
value: settingsDraftRoleObjectPermissions.canSoftDeleteObjectRecords,
|
setValue: (value: boolean | null) => {
|
||||||
setValue: (_value: boolean) => {
|
updateObjectPermission('canSoftDeleteObjectRecords', value);
|
||||||
// TODO: Implement
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'canDestroyObjectRecords',
|
||||||
key: 'canDestroyObjectRecords',
|
label: t`Destroy Records on ${objectLabel}`,
|
||||||
label: t`Destroy Records on ${objectLabel}`,
|
value: settingsDraftRoleObjectPermissions.canDestroyObjectRecords,
|
||||||
value: settingsDraftRoleObjectPermissions.canDestroyObjectRecords,
|
setValue: (value: boolean | null) => {
|
||||||
setValue: (_value: boolean) => {
|
updateObjectPermission('canDestroyObjectRecords', value);
|
||||||
// TODO: Implement
|
},
|
||||||
},
|
},
|
||||||
},
|
];
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section>
|
<Section>
|
||||||
@ -84,13 +101,17 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevel = ({
|
|||||||
description={t`Ability to interact with this specific object`}
|
description={t`Ability to interact with this specific object`}
|
||||||
/>
|
/>
|
||||||
<StyledTable>
|
<StyledTable>
|
||||||
<SettingsRolePermissionsObjectLevelObjectFormObjectLevelHeader />
|
<SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableHeader />
|
||||||
<StyledTableRows>
|
<StyledTableRows>
|
||||||
{objectPermissionsConfig.map((permission) => (
|
{objectPermissionsConfig.map((permission) => (
|
||||||
<SettingsRolePermissionsObjectsTableRow
|
<SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow
|
||||||
key={permission.key}
|
key={permission.key}
|
||||||
permission={permission}
|
permission={permission}
|
||||||
isEditable={settingsDraftRole.isEditable}
|
isEditable={settingsDraftRole.isEditable}
|
||||||
|
settingsDraftRoleObjectPermissions={
|
||||||
|
settingsDraftRoleObjectPermissions
|
||||||
|
}
|
||||||
|
roleId={roleId}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</StyledTableRows>
|
</StyledTableRows>
|
||||||
|
|||||||
@ -2,9 +2,9 @@ import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
|||||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
|
|
||||||
export const SettingsRolePermissionsObjectLevelObjectFormObjectLevelHeader =
|
export const SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableHeader =
|
||||||
() => (
|
() => (
|
||||||
<TableRow gridAutoColumns="1fr 24px">
|
<TableRow gridAutoColumns="1fr 48px">
|
||||||
<TableHeader>{t`Name`}</TableHeader>
|
<TableHeader>{t`Name`}</TableHeader>
|
||||||
<TableHeader aria-label={t`Actions`}></TableHeader>
|
<TableHeader aria-label={t`Actions`}></TableHeader>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@ -0,0 +1,141 @@
|
|||||||
|
import { OverridableCheckbox } from '@/settings/roles/role-permissions/object-level-permissions/object-form/components/OverridableCheckbox';
|
||||||
|
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 { 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';
|
||||||
|
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 StyledPermissionCell = styled(TableCell)`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledCheckboxCell = styled(TableCell)`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: ${({ theme }) => theme.spacing(4)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTableRow = styled(TableRow)`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type OverridableCheckboxType = 'no_cta' | 'default' | 'override';
|
||||||
|
|
||||||
|
type SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRowProps = {
|
||||||
|
permission: SettingsRolePermissionsObjectLevelPermission;
|
||||||
|
isEditable: boolean;
|
||||||
|
settingsDraftRoleObjectPermissions: ObjectPermission;
|
||||||
|
roleId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SettingsRolePermissionsObjectLevelObjectFormObjectLevelTableRow =
|
||||||
|
({
|
||||||
|
permission,
|
||||||
|
isEditable,
|
||||||
|
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;
|
||||||
|
|
||||||
|
const settingsDraftRoleObjectPermissionValue =
|
||||||
|
settingsDraftRoleObjectPermissions[
|
||||||
|
permission.key as keyof ObjectPermission
|
||||||
|
];
|
||||||
|
|
||||||
|
const rolePermission =
|
||||||
|
permissionMappings[permission.key as keyof typeof permissionMappings];
|
||||||
|
|
||||||
|
const settingsDraftRoleGlobalPermissionValue =
|
||||||
|
settingsDraftRole[rolePermission as keyof Role];
|
||||||
|
|
||||||
|
const isChecked = !!settingsDraftRoleObjectPermissionValue;
|
||||||
|
|
||||||
|
const isRevoked =
|
||||||
|
isDefined(settingsDraftRoleObjectPermissionValue) &&
|
||||||
|
settingsDraftRoleGlobalPermissionValue === true &&
|
||||||
|
isChecked === false;
|
||||||
|
|
||||||
|
let checkboxType: OverridableCheckboxType;
|
||||||
|
|
||||||
|
if (
|
||||||
|
settingsDraftRoleGlobalPermissionValue === true &&
|
||||||
|
settingsDraftRoleObjectPermissionValue === false
|
||||||
|
) {
|
||||||
|
checkboxType = 'override';
|
||||||
|
} else if (settingsDraftRoleGlobalPermissionValue === false) {
|
||||||
|
checkboxType = 'no_cta';
|
||||||
|
} else {
|
||||||
|
checkboxType = 'default';
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCheckboxChange = () => {
|
||||||
|
if (!isEditable) return;
|
||||||
|
|
||||||
|
if (checkboxType === 'default') {
|
||||||
|
permission.setValue(false);
|
||||||
|
} else if (checkboxType === 'override') {
|
||||||
|
permission.setValue(null);
|
||||||
|
} else if (checkboxType === 'no_cta') {
|
||||||
|
permission.setValue(!isChecked);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledTableRow>
|
||||||
|
<StyledPermissionCell>
|
||||||
|
<Icon size={theme.icon.size.sm} />
|
||||||
|
<StyledLabel>{label}</StyledLabel>
|
||||||
|
{isRevoked ? (
|
||||||
|
<StyledOverrideInfo>
|
||||||
|
{t`Revoked for this object`}
|
||||||
|
</StyledOverrideInfo>
|
||||||
|
) : null}
|
||||||
|
</StyledPermissionCell>
|
||||||
|
<StyledCheckboxCell>
|
||||||
|
<OverridableCheckbox
|
||||||
|
onChange={handleCheckboxChange}
|
||||||
|
disabled={!isEditable}
|
||||||
|
type={checkboxType}
|
||||||
|
checked={isChecked}
|
||||||
|
/>
|
||||||
|
</StyledCheckboxCell>
|
||||||
|
</StyledTableRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -5,7 +5,6 @@ import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDr
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
|
||||||
import { H2Title } from 'twenty-ui/display';
|
import { H2Title } from 'twenty-ui/display';
|
||||||
import { Section } from 'twenty-ui/layout';
|
import { Section } from 'twenty-ui/layout';
|
||||||
|
|
||||||
@ -37,12 +36,11 @@ export const SettingsRolePermissionsObjectsSection = ({
|
|||||||
{
|
{
|
||||||
key: 'canReadObjectRecords',
|
key: 'canReadObjectRecords',
|
||||||
label: t`See Records on All Objects`,
|
label: t`See Records on All Objects`,
|
||||||
overriddenBy:
|
revokedBy:
|
||||||
objectPermissions?.filter(
|
objectPermissions?.filter(
|
||||||
(permission) =>
|
(permission) =>
|
||||||
isDefined(permission.canReadObjectRecords) &&
|
permission.canReadObjectRecords === false &&
|
||||||
permission.canReadObjectRecords !==
|
settingsDraftRole.canReadAllObjectRecords === true,
|
||||||
settingsDraftRole.canReadAllObjectRecords,
|
|
||||||
)?.length ?? 0,
|
)?.length ?? 0,
|
||||||
value: settingsDraftRole.canReadAllObjectRecords,
|
value: settingsDraftRole.canReadAllObjectRecords,
|
||||||
setValue: (value: boolean) => {
|
setValue: (value: boolean) => {
|
||||||
@ -55,12 +53,11 @@ export const SettingsRolePermissionsObjectsSection = ({
|
|||||||
{
|
{
|
||||||
key: 'canUpdateObjectRecords',
|
key: 'canUpdateObjectRecords',
|
||||||
label: t`Edit Records on All Objects`,
|
label: t`Edit Records on All Objects`,
|
||||||
overriddenBy:
|
revokedBy:
|
||||||
objectPermissions?.filter(
|
objectPermissions?.filter(
|
||||||
(permission) =>
|
(permission) =>
|
||||||
isDefined(permission.canUpdateObjectRecords) &&
|
permission.canUpdateObjectRecords === false &&
|
||||||
permission.canUpdateObjectRecords !==
|
settingsDraftRole.canUpdateAllObjectRecords === true,
|
||||||
settingsDraftRole.canUpdateAllObjectRecords,
|
|
||||||
)?.length ?? 0,
|
)?.length ?? 0,
|
||||||
value: settingsDraftRole.canUpdateAllObjectRecords,
|
value: settingsDraftRole.canUpdateAllObjectRecords,
|
||||||
setValue: (value: boolean) => {
|
setValue: (value: boolean) => {
|
||||||
@ -73,12 +70,11 @@ export const SettingsRolePermissionsObjectsSection = ({
|
|||||||
{
|
{
|
||||||
key: 'canSoftDeleteObjectRecords',
|
key: 'canSoftDeleteObjectRecords',
|
||||||
label: t`Delete Records on All Objects`,
|
label: t`Delete Records on All Objects`,
|
||||||
overriddenBy:
|
revokedBy:
|
||||||
objectPermissions?.filter(
|
objectPermissions?.filter(
|
||||||
(permission) =>
|
(permission) =>
|
||||||
isDefined(permission.canSoftDeleteObjectRecords) &&
|
permission.canSoftDeleteObjectRecords === false &&
|
||||||
permission.canSoftDeleteObjectRecords !==
|
settingsDraftRole.canSoftDeleteAllObjectRecords === true,
|
||||||
settingsDraftRole.canSoftDeleteAllObjectRecords,
|
|
||||||
)?.length ?? 0,
|
)?.length ?? 0,
|
||||||
value: settingsDraftRole.canSoftDeleteAllObjectRecords,
|
value: settingsDraftRole.canSoftDeleteAllObjectRecords,
|
||||||
setValue: (value: boolean) => {
|
setValue: (value: boolean) => {
|
||||||
@ -91,12 +87,11 @@ export const SettingsRolePermissionsObjectsSection = ({
|
|||||||
{
|
{
|
||||||
key: 'canDestroyObjectRecords',
|
key: 'canDestroyObjectRecords',
|
||||||
label: t`Destroy Records on All Objects`,
|
label: t`Destroy Records on All Objects`,
|
||||||
overriddenBy:
|
revokedBy:
|
||||||
objectPermissions?.filter(
|
objectPermissions?.filter(
|
||||||
(permission) =>
|
(permission) =>
|
||||||
isDefined(permission.canDestroyObjectRecords) &&
|
permission.canDestroyObjectRecords === false &&
|
||||||
permission.canDestroyObjectRecords !==
|
settingsDraftRole.canDestroyAllObjectRecords === true,
|
||||||
settingsDraftRole.canDestroyAllObjectRecords,
|
|
||||||
)?.length ?? 0,
|
)?.length ?? 0,
|
||||||
value: settingsDraftRole.canDestroyAllObjectRecords,
|
value: settingsDraftRole.canDestroyAllObjectRecords,
|
||||||
setValue: (value: boolean) => {
|
setValue: (value: boolean) => {
|
||||||
@ -112,7 +107,7 @@ export const SettingsRolePermissionsObjectsSection = ({
|
|||||||
<Section>
|
<Section>
|
||||||
<H2Title
|
<H2Title
|
||||||
title={t`Objects`}
|
title={t`Objects`}
|
||||||
description={t`Ability to interact with each object`}
|
description={t`Actions you can perform on all objects`}
|
||||||
/>
|
/>
|
||||||
<StyledTable>
|
<StyledTable>
|
||||||
<SettingsRolePermissionsObjectsTableHeader
|
<SettingsRolePermissionsObjectsTableHeader
|
||||||
|
|||||||
@ -50,10 +50,10 @@ export const SettingsRolePermissionsObjectsTableRow = ({
|
|||||||
}: SettingsRolePermissionsObjectsTableRowProps) => {
|
}: SettingsRolePermissionsObjectsTableRowProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const isOverriddenBy = permission.overriddenBy;
|
const revokedBy = permission.revokedBy;
|
||||||
const isOverridden = isOverriddenBy && isOverriddenBy > 0;
|
const isRevoked = revokedBy && revokedBy > 0;
|
||||||
const label = permission.label;
|
const label = permission.label;
|
||||||
const pluralizedObject = pluralize('object', isOverriddenBy);
|
const pluralizedObject = pluralize('object', revokedBy);
|
||||||
|
|
||||||
const { Icon } = SETTINGS_ROLE_OBJECT_PERMISSION_ICON_CONFIG[permission.key];
|
const { Icon } = SETTINGS_ROLE_OBJECT_PERMISSION_ICON_CONFIG[permission.key];
|
||||||
|
|
||||||
@ -62,9 +62,9 @@ export const SettingsRolePermissionsObjectsTableRow = ({
|
|||||||
<StyledPermissionCell>
|
<StyledPermissionCell>
|
||||||
<Icon size={theme.icon.size.sm} />
|
<Icon size={theme.icon.size.sm} />
|
||||||
<StyledLabel>{label}</StyledLabel>
|
<StyledLabel>{label}</StyledLabel>
|
||||||
{isOverridden ? (
|
{isRevoked ? (
|
||||||
<StyledOverrideInfo>
|
<StyledOverrideInfo>
|
||||||
{t`Overridden on ${isOverriddenBy} ${pluralizedObject}`}
|
{t`Revoked on ${revokedBy} ${pluralizedObject}`}
|
||||||
</StyledOverrideInfo>
|
</StyledOverrideInfo>
|
||||||
) : null}
|
) : null}
|
||||||
</StyledPermissionCell>
|
</StyledPermissionCell>
|
||||||
@ -73,7 +73,7 @@ export const SettingsRolePermissionsObjectsTableRow = ({
|
|||||||
checked={permission.value ?? false}
|
checked={permission.value ?? false}
|
||||||
onChange={() => permission.setValue(!permission.value)}
|
onChange={() => permission.setValue(!permission.value)}
|
||||||
disabled={!isEditable}
|
disabled={!isEditable}
|
||||||
accent={isOverridden ? CheckboxAccent.Orange : CheckboxAccent.Blue}
|
accent={isRevoked ? CheckboxAccent.Orange : CheckboxAccent.Blue}
|
||||||
/>
|
/>
|
||||||
</StyledCheckboxCell>
|
</StyledCheckboxCell>
|
||||||
</StyledTableRow>
|
</StyledTableRow>
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
export const SETTINGS_ROLE_OBJECT_LEVEL_PERMISSION_TO_ROLE_OBJECT_PERMISSION_MAPPING =
|
||||||
|
{
|
||||||
|
canReadObjectRecords: 'canReadAllObjectRecords',
|
||||||
|
canUpdateObjectRecords: 'canUpdateAllObjectRecords',
|
||||||
|
canSoftDeleteObjectRecords: 'canSoftDeleteAllObjectRecords',
|
||||||
|
canDestroyObjectRecords: 'canDestroyAllObjectRecords',
|
||||||
|
} as const;
|
||||||
@ -2,7 +2,14 @@ import { ReactNode } from 'react';
|
|||||||
export type SettingsRolePermissionsObjectPermission = {
|
export type SettingsRolePermissionsObjectPermission = {
|
||||||
key: string;
|
key: string;
|
||||||
label: string | ReactNode;
|
label: string | ReactNode;
|
||||||
value?: boolean | null;
|
value?: boolean;
|
||||||
setValue: (value: boolean) => void;
|
setValue: (value: boolean) => void;
|
||||||
overriddenBy?: number;
|
revokedBy?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SettingsRolePermissionsObjectLevelPermission = {
|
||||||
|
key: string;
|
||||||
|
label: string | ReactNode;
|
||||||
|
value?: boolean | null;
|
||||||
|
setValue: (value: boolean | null) => void;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import {
|
|||||||
Role,
|
Role,
|
||||||
useCreateOneRoleMutation,
|
useCreateOneRoleMutation,
|
||||||
useUpdateOneRoleMutation,
|
useUpdateOneRoleMutation,
|
||||||
|
useUpsertObjectPermissionsMutation,
|
||||||
useUpsertSettingPermissionsMutation,
|
useUpsertSettingPermissionsMutation,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||||
@ -67,6 +68,7 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
|||||||
const [createRole] = useCreateOneRoleMutation();
|
const [createRole] = useCreateOneRoleMutation();
|
||||||
const [updateRole] = useUpdateOneRoleMutation();
|
const [updateRole] = useUpdateOneRoleMutation();
|
||||||
const [upsertSettingPermissions] = useUpsertSettingPermissionsMutation();
|
const [upsertSettingPermissions] = useUpsertSettingPermissionsMutation();
|
||||||
|
const [upsertObjectPermissions] = useUpsertObjectPermissionsMutation();
|
||||||
|
|
||||||
const { addWorkspaceMembersToRole } = useUpdateWorkspaceMemberRole(roleId);
|
const { addWorkspaceMembersToRole } = useUpdateWorkspaceMemberRole(roleId);
|
||||||
|
|
||||||
@ -142,25 +144,54 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
onCompleted: async (data) => {
|
onCompleted: async (data) => {
|
||||||
await addWorkspaceMembersToRole({
|
if (isDefined(dirtyFields.workspaceMembers)) {
|
||||||
roleId: data.createOneRole.id,
|
await addWorkspaceMembersToRole({
|
||||||
workspaceMemberIds: settingsDraftRole.workspaceMembers.map(
|
roleId: data.createOneRole.id,
|
||||||
(member) => member.id,
|
workspaceMemberIds: settingsDraftRole.workspaceMembers.map(
|
||||||
),
|
(member) => member.id,
|
||||||
});
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await upsertSettingPermissions({
|
if (isDefined(dirtyFields.settingPermissions)) {
|
||||||
variables: {
|
await upsertSettingPermissions({
|
||||||
upsertSettingPermissionsInput: {
|
variables: {
|
||||||
roleId: data.createOneRole.id,
|
upsertSettingPermissionsInput: {
|
||||||
settingPermissionKeys:
|
roleId: data.createOneRole.id,
|
||||||
settingsDraftRole.settingPermissions?.map(
|
settingPermissionKeys:
|
||||||
(settingPermission) => settingPermission.setting,
|
settingsDraftRole.settingPermissions?.map(
|
||||||
) ?? [],
|
(settingPermission) => settingPermission.setting,
|
||||||
|
) ?? [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
|
||||||
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, {
|
navigateSettings(SettingsPath.RoleDetail, {
|
||||||
roleId: data.createOneRole.id,
|
roleId: data.createOneRole.id,
|
||||||
@ -206,6 +237,30 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
|||||||
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
|
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isDefined(dirtyFields.objectPermissions)) {
|
||||||
|
await upsertObjectPermissions({
|
||||||
|
variables: {
|
||||||
|
upsertObjectPermissionsInput: {
|
||||||
|
roleId: roleId,
|
||||||
|
objectPermissions:
|
||||||
|
settingsDraftRole.objectPermissions?.map(
|
||||||
|
(objectPermission) => ({
|
||||||
|
objectMetadataId: objectPermission.objectMetadataId,
|
||||||
|
canReadObjectRecords: objectPermission.canReadObjectRecords,
|
||||||
|
canUpdateObjectRecords:
|
||||||
|
objectPermission.canUpdateObjectRecords,
|
||||||
|
canSoftDeleteObjectRecords:
|
||||||
|
objectPermission.canSoftDeleteObjectRecords,
|
||||||
|
canDestroyObjectRecords:
|
||||||
|
objectPermission.canDestroyObjectRecords,
|
||||||
|
}),
|
||||||
|
) ?? [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -251,6 +306,7 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
|||||||
<SettingsRolePermissions
|
<SettingsRolePermissions
|
||||||
roleId={roleId}
|
roleId={roleId}
|
||||||
isEditable={isRoleEditable}
|
isEditable={isRoleEditable}
|
||||||
|
isCreateMode={isCreateMode}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{activeTabId === SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.SETTINGS && (
|
{activeTabId === SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.SETTINGS && (
|
||||||
|
|||||||
@ -2,10 +2,13 @@ import { SETTINGS_ROLE_DETAIL_TABS } from '@/settings/roles/role/constants/Setti
|
|||||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||||
import { settingsPersistedRoleFamilyState } from '@/settings/roles/states/settingsPersistedRoleFamilyState';
|
import { settingsPersistedRoleFamilyState } from '@/settings/roles/states/settingsPersistedRoleFamilyState';
|
||||||
import { activeTabIdComponentState } from '@/ui/layout/tab/states/activeTabIdComponentState';
|
import { activeTabIdComponentState } from '@/ui/layout/tab/states/activeTabIdComponentState';
|
||||||
|
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { Role } from '~/generated/graphql';
|
||||||
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
|
|
||||||
type SettingsRoleEditEffectProps = {
|
type SettingsRoleEditEffectProps = {
|
||||||
roleId: string;
|
roleId: string;
|
||||||
@ -17,24 +20,35 @@ export const SettingsRoleEditEffect = ({
|
|||||||
const [isInitialized, setIsInitialized] = useState(false);
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
|
|
||||||
const role = useRecoilValue(settingsPersistedRoleFamilyState(roleId));
|
const role = useRecoilValue(settingsPersistedRoleFamilyState(roleId));
|
||||||
const setDraftRole = useSetRecoilState(settingsDraftRoleFamilyState(roleId));
|
|
||||||
const setActiveTabId = useSetRecoilComponentStateV2(
|
const setActiveTabId = useSetRecoilComponentStateV2(
|
||||||
activeTabIdComponentState,
|
activeTabIdComponentState,
|
||||||
SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID,
|
SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const updateDraftRoleIfNeeded = useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
(newRole: Role) => {
|
||||||
|
const currentPersistedRole = getSnapshotValue(
|
||||||
|
snapshot,
|
||||||
|
settingsPersistedRoleFamilyState(newRole.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isDeeplyEqual(newRole, currentPersistedRole)) {
|
||||||
|
set(settingsDraftRoleFamilyState(newRole.id), newRole);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isInitialized) {
|
if (isInitialized || !isDefined(role)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setActiveTabId(SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.ASSIGNMENT);
|
setActiveTabId(SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.ASSIGNMENT);
|
||||||
|
updateDraftRoleIfNeeded(role);
|
||||||
if (isDefined(role)) {
|
setIsInitialized(true);
|
||||||
setDraftRole(role);
|
}, [isInitialized, role, setActiveTabId, updateDraftRoleIfNeeded]);
|
||||||
setIsInitialized(true);
|
|
||||||
}
|
|
||||||
}, [isInitialized, role, setActiveTabId, setDraftRole]);
|
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,14 +1,30 @@
|
|||||||
import { Field, InputType } from '@nestjs/graphql';
|
import { Field, InputType } from '@nestjs/graphql';
|
||||||
|
|
||||||
import { IsBoolean, IsNotEmpty, IsOptional, IsUUID } from 'class-validator';
|
import {
|
||||||
|
ArrayMinSize,
|
||||||
|
IsArray,
|
||||||
|
IsBoolean,
|
||||||
|
IsNotEmpty,
|
||||||
|
IsOptional,
|
||||||
|
IsUUID,
|
||||||
|
} from 'class-validator';
|
||||||
|
|
||||||
@InputType()
|
@InputType()
|
||||||
export class UpsertObjectPermissionInput {
|
export class UpsertObjectPermissionsInput {
|
||||||
@IsUUID()
|
@IsUUID()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@Field()
|
@Field()
|
||||||
roleId: string;
|
roleId: string;
|
||||||
|
|
||||||
|
@IsArray()
|
||||||
|
@ArrayMinSize(1)
|
||||||
|
@IsNotEmpty()
|
||||||
|
@Field(() => [ObjectPermissionInput])
|
||||||
|
objectPermissions: ObjectPermissionInput[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputType()
|
||||||
|
export class ObjectPermissionInput {
|
||||||
@IsUUID()
|
@IsUUID()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@Field()
|
@Field()
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { Repository } from 'typeorm';
|
import { In, Repository } from 'typeorm';
|
||||||
|
|
||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
import { UpsertObjectPermissionInput } from 'src/engine/metadata-modules/object-permission/dtos/upsert-object-permission-input';
|
import { UpsertObjectPermissionsInput } from 'src/engine/metadata-modules/object-permission/dtos/upsert-object-permissions.input';
|
||||||
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
|
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
|
||||||
import {
|
import {
|
||||||
PermissionsException,
|
PermissionsException,
|
||||||
@ -25,24 +25,29 @@ export class ObjectPermissionService {
|
|||||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async upsertObjectPermission({
|
public async upsertObjectPermissions({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
input,
|
input,
|
||||||
}: {
|
}: {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
input: UpsertObjectPermissionInput;
|
input: UpsertObjectPermissionsInput;
|
||||||
}): Promise<ObjectPermissionEntity | null> {
|
}): Promise<ObjectPermissionEntity[]> {
|
||||||
try {
|
try {
|
||||||
await this.validateRoleIsEditableOrThrow({
|
await this.validateRoleIsEditableOrThrow({
|
||||||
roleId: input.roleId,
|
roleId: input.roleId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await this.objectPermissionRepository.upsert(
|
const objectPermissions = input.objectPermissions.map(
|
||||||
{
|
(objectPermission) => ({
|
||||||
|
...objectPermission,
|
||||||
|
roleId: input.roleId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
...input,
|
}),
|
||||||
},
|
);
|
||||||
|
|
||||||
|
const result = await this.objectPermissionRepository.upsert(
|
||||||
|
objectPermissions,
|
||||||
{
|
{
|
||||||
conflictPaths: ['objectMetadataId', 'roleId'],
|
conflictPaths: ['objectMetadataId', 'roleId'],
|
||||||
},
|
},
|
||||||
@ -61,9 +66,14 @@ export class ObjectPermissionService {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.objectPermissionRepository.findOne({
|
return this.objectPermissionRepository.find({
|
||||||
where: {
|
where: {
|
||||||
id: objectPermissionId,
|
roleId: input.roleId,
|
||||||
|
objectMetadataId: In(
|
||||||
|
input.objectPermissions.map(
|
||||||
|
(objectPermission) => objectPermission.objectMetadataId,
|
||||||
|
),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -71,7 +81,9 @@ export class ObjectPermissionService {
|
|||||||
error,
|
error,
|
||||||
roleId: input.roleId,
|
roleId: input.roleId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
objectMetadataId: input.objectMetadataId,
|
objectMetadataIds: input.objectPermissions.map(
|
||||||
|
(objectPermission) => objectPermission.objectMetadataId,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
@ -82,12 +94,12 @@ export class ObjectPermissionService {
|
|||||||
error,
|
error,
|
||||||
roleId,
|
roleId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
objectMetadataId,
|
objectMetadataIds,
|
||||||
}: {
|
}: {
|
||||||
error: Error;
|
error: Error;
|
||||||
roleId: string;
|
roleId: string;
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
objectMetadataId: string;
|
objectMetadataIds: string[];
|
||||||
}) {
|
}) {
|
||||||
if (error.message.includes('violates foreign key constraint')) {
|
if (error.message.includes('violates foreign key constraint')) {
|
||||||
const role = await this.roleRepository.findOne({
|
const role = await this.roleRepository.findOne({
|
||||||
@ -104,14 +116,14 @@ export class ObjectPermissionService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const objectMetadata = await this.objectMetadataRepository.findOne({
|
const objectMetadata = await this.objectMetadataRepository.find({
|
||||||
where: {
|
where: {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
id: objectMetadataId,
|
id: In(objectMetadataIds),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isDefined(objectMetadata)) {
|
if (objectMetadata.length !== objectMetadataIds.length) {
|
||||||
throw new PermissionsException(
|
throw new PermissionsException(
|
||||||
PermissionsExceptionMessage.OBJECT_METADATA_NOT_FOUND,
|
PermissionsExceptionMessage.OBJECT_METADATA_NOT_FOUND,
|
||||||
PermissionsExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
PermissionsExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import { AuthWorkspaceMemberId } from 'src/engine/decorators/auth/auth-workspace
|
|||||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||||
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
|
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
|
||||||
import { ObjectPermissionDTO } from 'src/engine/metadata-modules/object-permission/dtos/object-permission.dto';
|
import { ObjectPermissionDTO } from 'src/engine/metadata-modules/object-permission/dtos/object-permission.dto';
|
||||||
import { UpsertObjectPermissionInput } from 'src/engine/metadata-modules/object-permission/dtos/upsert-object-permission-input';
|
import { UpsertObjectPermissionsInput } from 'src/engine/metadata-modules/object-permission/dtos/upsert-object-permissions.input';
|
||||||
import { ObjectPermissionService } from 'src/engine/metadata-modules/object-permission/object-permission.service';
|
import { ObjectPermissionService } from 'src/engine/metadata-modules/object-permission/object-permission.service';
|
||||||
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
||||||
import {
|
import {
|
||||||
@ -147,21 +147,18 @@ export class RoleResolver {
|
|||||||
return deletedRoleId;
|
return deletedRoleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mutation(() => ObjectPermissionDTO)
|
@Mutation(() => [ObjectPermissionDTO])
|
||||||
async upsertOneObjectPermission(
|
async upsertObjectPermissions(
|
||||||
@AuthWorkspace() workspace: Workspace,
|
@AuthWorkspace() workspace: Workspace,
|
||||||
@Args('upsertObjectPermissionInput')
|
@Args('upsertObjectPermissionsInput')
|
||||||
upsertObjectPermissionInput: UpsertObjectPermissionInput,
|
upsertObjectPermissionsInput: UpsertObjectPermissionsInput,
|
||||||
) {
|
): Promise<ObjectPermissionDTO[]> {
|
||||||
await this.validatePermissionsV2EnabledOrThrow(workspace);
|
await this.validatePermissionsV2EnabledOrThrow(workspace);
|
||||||
|
|
||||||
const objectPermission =
|
return this.objectPermissionService.upsertObjectPermissions({
|
||||||
await this.objectPermissionService.upsertObjectPermission({
|
workspaceId: workspace.id,
|
||||||
workspaceId: workspace.id,
|
input: upsertObjectPermissionsInput,
|
||||||
input: upsertObjectPermissionInput,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return objectPermission;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mutation(() => [SettingPermissionDTO])
|
@Mutation(() => [SettingPermissionDTO])
|
||||||
@ -169,7 +166,7 @@ export class RoleResolver {
|
|||||||
@AuthWorkspace() workspace: Workspace,
|
@AuthWorkspace() workspace: Workspace,
|
||||||
@Args('upsertSettingPermissionsInput')
|
@Args('upsertSettingPermissionsInput')
|
||||||
upsertSettingPermissionsInput: UpsertSettingPermissionsInput,
|
upsertSettingPermissionsInput: UpsertSettingPermissionsInput,
|
||||||
) {
|
): Promise<SettingPermissionDTO[]> {
|
||||||
await this.validatePermissionsV2EnabledOrThrow(workspace);
|
await this.validatePermissionsV2EnabledOrThrow(workspace);
|
||||||
|
|
||||||
return this.settingPermissionService.upsertSettingPermissions({
|
return this.settingPermissionService.upsertSettingPermissions({
|
||||||
|
|||||||
@ -72,19 +72,12 @@ describe('roles permissions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
const disablePermissionsQuery = updateFeatureFlagFactory(
|
|
||||||
SEED_APPLE_WORKSPACE_ID,
|
|
||||||
'IsPermissionsEnabled',
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
const disablePermissionsV2Query = updateFeatureFlagFactory(
|
const disablePermissionsV2Query = updateFeatureFlagFactory(
|
||||||
SEED_APPLE_WORKSPACE_ID,
|
SEED_APPLE_WORKSPACE_ID,
|
||||||
'IsPermissionsV2Enabled',
|
'IsPermissionsV2Enabled',
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
await makeGraphqlAPIRequest(disablePermissionsQuery);
|
|
||||||
await makeGraphqlAPIRequest(disablePermissionsV2Query);
|
await makeGraphqlAPIRequest(disablePermissionsV2Query);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -480,9 +473,10 @@ describe('roles permissions', () => {
|
|||||||
roleId: string;
|
roleId: string;
|
||||||
}) => `
|
}) => `
|
||||||
mutation UpsertObjectPermissions {
|
mutation UpsertObjectPermissions {
|
||||||
upsertOneObjectPermission(upsertObjectPermissionInput: {objectMetadataId: "${objectMetadataId}", roleId: "${roleId}", canUpdateObjectRecords: true}) {
|
upsertObjectPermissions(upsertObjectPermissionsInput: { roleId: "${roleId}", objectPermissions: [{objectMetadataId: "${objectMetadataId}", canUpdateObjectRecords: true}]}) {
|
||||||
id
|
id
|
||||||
roleId
|
roleId
|
||||||
|
objectMetadataId
|
||||||
canUpdateObjectRecords
|
canUpdateObjectRecords
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -540,12 +534,15 @@ describe('roles permissions', () => {
|
|||||||
.expect((res) => {
|
.expect((res) => {
|
||||||
expect(res.body.data).toBeDefined();
|
expect(res.body.data).toBeDefined();
|
||||||
expect(res.body.errors).toBeUndefined();
|
expect(res.body.errors).toBeUndefined();
|
||||||
expect(res.body.data.upsertOneObjectPermission.roleId).toBe(
|
expect(res.body.data.upsertObjectPermissions).toEqual(
|
||||||
createdEditableRoleId,
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
roleId: createdEditableRoleId,
|
||||||
|
objectMetadataId: listingObjectId,
|
||||||
|
canUpdateObjectRecords: true,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
);
|
);
|
||||||
expect(
|
|
||||||
res.body.data.upsertOneObjectPermission.canUpdateObjectRecords,
|
|
||||||
).toBe(true);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user