Refactor read only object and fields (#13936)
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+1
@@ -53,6 +53,7 @@ const mockObjectMetadataItem: ObjectMetadataItem = {
|
||||
isLabelSyncedWithName: true,
|
||||
isRemote: false,
|
||||
isSystem: false,
|
||||
isUIReadOnly: false,
|
||||
};
|
||||
|
||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
|
||||
+1
@@ -31,6 +31,7 @@ const objectMetadataItemWithPositionField: ObjectMetadataItem = {
|
||||
icon: 'icon',
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isUIReadOnly: false,
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isSearchable: false,
|
||||
|
||||
+3
-7
@@ -1,8 +1,8 @@
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useIsRecordReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordReadOnly';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/utils/isRecordFieldReadOnly';
|
||||
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly';
|
||||
|
||||
export type UseFieldIsReadOnlyParams = {
|
||||
fieldMetadataId: string;
|
||||
@@ -42,10 +42,6 @@ export const useIsRecordFieldReadOnly = ({
|
||||
return isRecordFieldReadOnly({
|
||||
isRecordReadOnly,
|
||||
objectPermissions,
|
||||
fieldMetadataId,
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
fieldName: fieldMetadataItem.name,
|
||||
fieldType: fieldMetadataItem.type,
|
||||
isCustom: fieldMetadataItem.isCustom ?? false,
|
||||
fieldMetadataItem,
|
||||
});
|
||||
};
|
||||
+7
-1
@@ -1,6 +1,7 @@
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { isRecordReadOnly } from '@/object-record/record-field/ui/hooks/read-only/utils/isRecordReadOnly';
|
||||
import { isRecordReadOnly } from '@/object-record/read-only/utils/isRecordReadOnly';
|
||||
import { useIsRecordDeleted } from '@/object-record/record-field/ui/hooks/useIsRecordDeleted';
|
||||
|
||||
type UseIsRecordReadOnlyParams = {
|
||||
@@ -12,6 +13,10 @@ export const useIsRecordReadOnly = ({
|
||||
recordId,
|
||||
objectMetadataId,
|
||||
}: UseIsRecordReadOnlyParams) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItemById({
|
||||
objectId: objectMetadataId,
|
||||
});
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const objectPermissions = getObjectPermissionsForObject(
|
||||
@@ -24,5 +29,6 @@ export const useIsRecordReadOnly = ({
|
||||
return isRecordReadOnly({
|
||||
objectPermissions,
|
||||
isRecordDeleted,
|
||||
objectMetadataItem,
|
||||
});
|
||||
};
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { isObjectMetadataReadOnly } from '@/object-record/read-only/utils/isObjectMetadataReadOnly';
|
||||
|
||||
describe('isObjectMetadataReadOnly', () => {
|
||||
it('should return false if object can be updated and is not UI read only and is not remote', () => {
|
||||
const result = isObjectMetadataReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: true,
|
||||
objectMetadataId: '123',
|
||||
restrictedFields: {},
|
||||
},
|
||||
objectMetadataItem: {
|
||||
isUIReadOnly: false,
|
||||
isRemote: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if object cannot be updated and is not UI read only and is not remote', () => {
|
||||
const result = isObjectMetadataReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: false,
|
||||
objectMetadataId: '123',
|
||||
restrictedFields: {},
|
||||
},
|
||||
objectMetadataItem: {
|
||||
isUIReadOnly: false,
|
||||
isRemote: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if object metadata is UI read only', () => {
|
||||
const result = isObjectMetadataReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: true,
|
||||
objectMetadataId: '123',
|
||||
restrictedFields: {},
|
||||
},
|
||||
objectMetadataItem: {
|
||||
isUIReadOnly: true,
|
||||
isRemote: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if object metadata is remote', () => {
|
||||
const result = isObjectMetadataReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: true,
|
||||
objectMetadataId: '123',
|
||||
restrictedFields: {},
|
||||
},
|
||||
objectMetadataItem: {
|
||||
isUIReadOnly: false,
|
||||
isRemote: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
+24
-28
@@ -1,4 +1,4 @@
|
||||
import { isRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/utils/isRecordFieldReadOnly';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
describe('isRecordFieldReadOnly', () => {
|
||||
@@ -12,16 +12,18 @@ describe('isRecordFieldReadOnly', () => {
|
||||
isRecordReadOnly: false,
|
||||
objectPermissions: mockObjectPermissions,
|
||||
fieldMetadataId: 'field-123',
|
||||
objectNameSingular: 'person',
|
||||
fieldName: 'firstName',
|
||||
fieldType: FieldMetadataType.TEXT,
|
||||
isCustom: false,
|
||||
fieldMetadataType: FieldMetadataType.TEXT,
|
||||
isUIReadOnly: false,
|
||||
};
|
||||
|
||||
it('should return true when record is read-only', () => {
|
||||
const result = isRecordFieldReadOnly({
|
||||
...mockParams,
|
||||
isRecordReadOnly: true,
|
||||
fieldMetadataItem: {
|
||||
id: 'field-123',
|
||||
isUIReadOnly: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
@@ -34,6 +36,10 @@ describe('isRecordFieldReadOnly', () => {
|
||||
...mockObjectPermissions,
|
||||
canUpdateObjectRecords: false,
|
||||
},
|
||||
fieldMetadataItem: {
|
||||
id: 'field-123',
|
||||
isUIReadOnly: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
@@ -48,36 +54,22 @@ describe('isRecordFieldReadOnly', () => {
|
||||
'field-123': { canUpdate: false },
|
||||
},
|
||||
},
|
||||
fieldMetadataItem: {
|
||||
id: 'field-123',
|
||||
isUIReadOnly: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for system read-only fields like createdAt', () => {
|
||||
it('should return true when field is marked as UI read-only', () => {
|
||||
const result = isRecordFieldReadOnly({
|
||||
...mockParams,
|
||||
fieldName: 'createdAt',
|
||||
fieldType: FieldMetadataType.DATE_TIME,
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for calendar event objects (system read-only)', () => {
|
||||
const result = isRecordFieldReadOnly({
|
||||
...mockParams,
|
||||
objectNameSingular: 'calendarEvent',
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for workflow non-name fields (system read-only)', () => {
|
||||
const result = isRecordFieldReadOnly({
|
||||
...mockParams,
|
||||
objectNameSingular: 'workflow',
|
||||
fieldName: 'status',
|
||||
isCustom: false,
|
||||
fieldMetadataItem: {
|
||||
id: 'field-123',
|
||||
isUIReadOnly: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
@@ -86,6 +78,10 @@ describe('isRecordFieldReadOnly', () => {
|
||||
it('should return false when all conditions allow editing', () => {
|
||||
const result = isRecordFieldReadOnly({
|
||||
...mockParams,
|
||||
fieldMetadataItem: {
|
||||
id: 'field-123',
|
||||
isUIReadOnly: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(false);
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import { isRecordReadOnly } from '@/object-record/read-only/utils/isRecordReadOnly';
|
||||
|
||||
describe('isRecordReadOnly', () => {
|
||||
it('should return false if record is not deleted, has update permissions and object metadata is not read only', () => {
|
||||
const result = isRecordReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: true,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
isRecordDeleted: false,
|
||||
objectMetadataItem: {
|
||||
isUIReadOnly: false,
|
||||
isRemote: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if record is not deleted but lacks update permissions and object metadata is not read only', () => {
|
||||
const result = isRecordReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: false,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
isRecordDeleted: false,
|
||||
objectMetadataItem: {
|
||||
isUIReadOnly: false,
|
||||
isRemote: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if record is deleted even with update permissions and object metadata is not read only', () => {
|
||||
const result = isRecordReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: true,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
isRecordDeleted: true,
|
||||
objectMetadataItem: {
|
||||
isUIReadOnly: false,
|
||||
isRemote: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if record is not deleted and has update permissions but object metadata is UI read only', () => {
|
||||
const result = isRecordReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: false,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
isRecordDeleted: true,
|
||||
objectMetadataItem: {
|
||||
isUIReadOnly: true,
|
||||
isRemote: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if record is not deleted and has update permissions but object metadata is remote', () => {
|
||||
const result = isRecordReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: true,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
isRecordDeleted: false,
|
||||
objectMetadataItem: {
|
||||
isUIReadOnly: false,
|
||||
isRemote: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { type ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
type IsFieldMetadataReadOnlyByPermissionParams = {
|
||||
objectPermissions: ObjectPermission;
|
||||
fieldMetadataId: string;
|
||||
};
|
||||
|
||||
export const isFieldMetadataReadOnlyByPermissions = ({
|
||||
objectPermissions,
|
||||
fieldMetadataId,
|
||||
}: IsFieldMetadataReadOnlyByPermissionParams) => {
|
||||
if (objectPermissions.canUpdateObjectRecords === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const fieldMetadataIsRestrictedForUpdate =
|
||||
objectPermissions.restrictedFields?.[fieldMetadataId]?.canUpdate === false;
|
||||
|
||||
return fieldMetadataIsRestrictedForUpdate;
|
||||
};
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { isFieldMetadataReadOnlyByPermissions } from '@/object-record/read-only/utils/internal/isFieldMetadataReadOnlyByPermissions';
|
||||
import { isObjectMetadataReadOnly } from '@/object-record/read-only/utils/isObjectMetadataReadOnly';
|
||||
import { type ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
type IsFieldMetadataReadOnlyParams = {
|
||||
objectPermissions: ObjectPermission;
|
||||
objectMetadataItem: Pick<ObjectMetadataItem, 'isUIReadOnly' | 'isRemote'>;
|
||||
fieldMetadataItem: Pick<FieldMetadataItem, 'id' | 'isUIReadOnly'>;
|
||||
};
|
||||
|
||||
export const isFieldMetadataReadOnly = ({
|
||||
objectPermissions,
|
||||
objectMetadataItem,
|
||||
fieldMetadataItem,
|
||||
}: IsFieldMetadataReadOnlyParams) => {
|
||||
const objectMetadataReadOnly = isObjectMetadataReadOnly({
|
||||
objectPermissions,
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const fieldReadOnlyByPermissions = isFieldMetadataReadOnlyByPermissions({
|
||||
objectPermissions,
|
||||
fieldMetadataId: fieldMetadataItem.id,
|
||||
});
|
||||
|
||||
return (
|
||||
objectMetadataReadOnly ||
|
||||
fieldMetadataItem.isUIReadOnly ||
|
||||
fieldReadOnlyByPermissions
|
||||
);
|
||||
};
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { type ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
type IsObjectMetadataReadOnlyParams = {
|
||||
objectPermissions: ObjectPermission;
|
||||
objectMetadataItem: Pick<ObjectMetadataItem, 'isUIReadOnly' | 'isRemote'>;
|
||||
};
|
||||
|
||||
export const isObjectMetadataReadOnly = ({
|
||||
objectPermissions,
|
||||
objectMetadataItem,
|
||||
}: IsObjectMetadataReadOnlyParams) => {
|
||||
return (
|
||||
!objectPermissions.canUpdateObjectRecords ||
|
||||
objectMetadataItem.isUIReadOnly ||
|
||||
objectMetadataItem.isRemote
|
||||
);
|
||||
};
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { isFieldMetadataReadOnlyByPermissions } from '@/object-record/read-only/utils/internal/isFieldMetadataReadOnlyByPermissions';
|
||||
import { type ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
type IsRecordFieldReadOnlyParams = {
|
||||
isRecordReadOnly: boolean;
|
||||
fieldMetadataItem: Pick<FieldMetadataItem, 'id' | 'isUIReadOnly'>;
|
||||
objectPermissions: ObjectPermission;
|
||||
};
|
||||
|
||||
export const isRecordFieldReadOnly = ({
|
||||
objectPermissions,
|
||||
isRecordReadOnly,
|
||||
fieldMetadataItem,
|
||||
}: IsRecordFieldReadOnlyParams) => {
|
||||
const fieldReadOnlyByPermissions = isFieldMetadataReadOnlyByPermissions({
|
||||
objectPermissions,
|
||||
fieldMetadataId: fieldMetadataItem.id,
|
||||
});
|
||||
|
||||
return (
|
||||
isRecordReadOnly ||
|
||||
fieldMetadataItem.isUIReadOnly ||
|
||||
fieldReadOnlyByPermissions
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { isObjectMetadataReadOnly } from '@/object-record/read-only/utils/isObjectMetadataReadOnly';
|
||||
import { type ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
export type IsObjectReadOnlyParams = {
|
||||
objectPermissions: ObjectPermission;
|
||||
objectMetadataItem: Pick<ObjectMetadataItem, 'isUIReadOnly' | 'isRemote'>;
|
||||
isRecordDeleted: boolean;
|
||||
};
|
||||
|
||||
export const isRecordReadOnly = ({
|
||||
objectPermissions,
|
||||
isRecordDeleted,
|
||||
objectMetadataItem,
|
||||
}: IsObjectReadOnlyParams) => {
|
||||
return (
|
||||
isRecordDeleted ||
|
||||
isObjectMetadataReadOnly({
|
||||
objectPermissions,
|
||||
objectMetadataItem,
|
||||
})
|
||||
);
|
||||
};
|
||||
+5
-7
@@ -1,3 +1,4 @@
|
||||
import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly';
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { RecordBoardCardBodyContainer } from '@/object-record/record-board/record-board-card/components/RecordBoardCardBodyContainer';
|
||||
import { StopPropagationContainer } from '@/object-record/record-board/record-board-card/components/StopPropagationContainer';
|
||||
@@ -10,7 +11,6 @@ import {
|
||||
type RecordUpdateHook,
|
||||
type RecordUpdateHookParams,
|
||||
} from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/utils/isRecordFieldReadOnly';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { getFieldButtonIcon } from '@/object-record/record-field/ui/utils/getFieldButtonIcon';
|
||||
@@ -45,12 +45,10 @@ export const RecordBoardCardBody = ({
|
||||
isRecordFieldReadOnly: isRecordFieldReadOnly({
|
||||
isRecordReadOnly,
|
||||
objectPermissions,
|
||||
fieldMetadataId: fieldDefinition.fieldMetadataId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
fieldType: fieldDefinition.type,
|
||||
isCustom: fieldDefinition.metadata.isCustom,
|
||||
objectNameSingular:
|
||||
fieldDefinition.metadata.objectMetadataNameSingular ?? '',
|
||||
fieldMetadataItem: {
|
||||
id: fieldDefinition.fieldMetadataId,
|
||||
isUIReadOnly: fieldDefinition.metadata.isUIReadOnly ?? false,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
+1
-1
@@ -2,6 +2,7 @@ import styled from '@emotion/styled';
|
||||
import { Draggable } from '@hello-pangea/dnd';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { RecordBoardCard } from '@/object-record/record-board/record-board-card/components/RecordBoardCard';
|
||||
import { RecordBoardCardHotkeysEffect } from '@/object-record/record-board/record-board-card/components/RecordBoardCardHotkeysEffect';
|
||||
@@ -9,7 +10,6 @@ import { RecordBoardCardMultiDragPreview } from '@/object-record/record-board/re
|
||||
import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
|
||||
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
||||
import { isRecordBoardCardFocusedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardFocusedComponentFamilyState';
|
||||
import { useIsRecordReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordReadOnly';
|
||||
import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue';
|
||||
|
||||
const StyledDraggableContainer = styled.div`
|
||||
|
||||
+1
@@ -35,6 +35,7 @@ describe('buildRecordGqlFieldsAggregateForView', () => {
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isUIReadOnly: false,
|
||||
isRemote: false,
|
||||
isSearchable: false,
|
||||
labelIdentifierFieldMetadataId: '06b33746-5293-4d07-9f7f-ebf5ad396064',
|
||||
|
||||
+14
-17
@@ -3,6 +3,8 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
|
||||
import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly';
|
||||
import { RecordFieldListCellEditModePortal } from '@/object-record/record-field-list/anchored-portal/components/RecordFieldListCellEditModePortal';
|
||||
import { RecordFieldListCellHoveredPortal } from '@/object-record/record-field-list/anchored-portal/components/RecordFieldListCellHoveredPortal';
|
||||
import { useFieldListFieldMetadataItems } from '@/object-record/record-field-list/hooks/useFieldListFieldMetadataItems';
|
||||
@@ -11,8 +13,6 @@ import { RecordDetailRelationSection } from '@/object-record/record-field-list/r
|
||||
import { RecordFieldListComponentInstanceContext } from '@/object-record/record-field-list/states/contexts/RecordFieldListComponentInstanceContext';
|
||||
import { recordFieldListHoverPositionComponentState } from '@/object-record/record-field-list/states/recordFieldListHoverPositionComponentState';
|
||||
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { useIsRecordReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordReadOnly';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/utils/isRecordFieldReadOnly';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
|
||||
import { PropertyBox } from '@/object-record/record-inline-cell/property-box/components/PropertyBox';
|
||||
@@ -123,11 +123,10 @@ export const RecordFieldList = ({
|
||||
objectPermissionsByObjectMetadataId,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
}),
|
||||
fieldMetadataId: fieldMetadataItem.id,
|
||||
objectNameSingular,
|
||||
fieldName: fieldMetadataItem.name,
|
||||
fieldType: fieldMetadataItem.type,
|
||||
isCustom: fieldMetadataItem.isCustom ?? false,
|
||||
fieldMetadataItem: {
|
||||
id: fieldMetadataItem.id,
|
||||
isUIReadOnly: fieldMetadataItem.isUIReadOnly ?? false,
|
||||
},
|
||||
}),
|
||||
}}
|
||||
>
|
||||
@@ -172,11 +171,10 @@ export const RecordFieldList = ({
|
||||
objectPermissionsByObjectMetadataId,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
}),
|
||||
fieldMetadataId: fieldMetadataItem.id,
|
||||
objectNameSingular,
|
||||
fieldName: fieldMetadataItem.name,
|
||||
fieldType: fieldMetadataItem.type,
|
||||
isCustom: fieldMetadataItem.isCustom ?? false,
|
||||
fieldMetadataItem: {
|
||||
id: fieldMetadataItem.id,
|
||||
isUIReadOnly: fieldMetadataItem.isUIReadOnly ?? false,
|
||||
},
|
||||
}),
|
||||
onMouseEnter: () =>
|
||||
handleMouseEnter(
|
||||
@@ -233,11 +231,10 @@ export const RecordFieldList = ({
|
||||
objectPermissionsByObjectMetadataId,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
}),
|
||||
fieldMetadataId: fieldMetadataItem.id,
|
||||
objectNameSingular,
|
||||
fieldName: fieldMetadataItem.name,
|
||||
fieldType: fieldMetadataItem.type,
|
||||
isCustom: fieldMetadataItem.isCustom ?? false,
|
||||
fieldMetadataItem: {
|
||||
id: fieldMetadataItem.id,
|
||||
isUIReadOnly: fieldMetadataItem.isUIReadOnly ?? false,
|
||||
},
|
||||
}),
|
||||
}}
|
||||
>
|
||||
|
||||
+1
-1
@@ -1,10 +1,10 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
|
||||
import { RecordDetailRelationSectionDropdownToMany } from '@/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToMany';
|
||||
import { RecordDetailRelationSectionDropdownToOne } from '@/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToOne';
|
||||
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { useIsRecordReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordReadOnly';
|
||||
import { type FieldRelationMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { RelationType } from '~/generated-metadata/graphql';
|
||||
|
||||
|
||||
+1
-1
@@ -1,12 +1,12 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { useIsRecordFieldReadOnly } from '@/object-record/read-only/hooks/useIsRecordFieldReadOnly';
|
||||
import {
|
||||
FieldContext,
|
||||
type RecordUpdateHook,
|
||||
type RecordUpdateHookParams,
|
||||
} from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { useIsRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordFieldReadOnly';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
export const FieldContextProvider = ({
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
import { isObjectReadOnly } from '@/object-record/record-field/ui/hooks/read-only/utils/isObjectReadOnly';
|
||||
|
||||
describe('isObjectReadOnly', () => {
|
||||
it('should return true if object is not read only', () => {
|
||||
const result = isObjectReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: true,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false if object is read only', () => {
|
||||
const result = isObjectReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: false,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
import { isRecordReadOnly } from '@/object-record/record-field/ui/hooks/read-only/utils/isRecordReadOnly';
|
||||
|
||||
describe('isRecordReadOnly', () => {
|
||||
it('should return false if record is not deleted and has update permissions', () => {
|
||||
const result = isRecordReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: true,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
isRecordDeleted: false,
|
||||
});
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if record is not deleted but lacks update permissions', () => {
|
||||
const result = isRecordReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: false,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
isRecordDeleted: false,
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if record is deleted even with update permissions', () => {
|
||||
const result = isRecordReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: true,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
isRecordDeleted: true,
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if record is deleted and lacks update permissions', () => {
|
||||
const result = isRecordReadOnly({
|
||||
objectPermissions: {
|
||||
canUpdateObjectRecords: false,
|
||||
objectMetadataId: '123',
|
||||
},
|
||||
isRecordDeleted: true,
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
import { isObjectReadOnly } from '@/object-record/record-field/ui/hooks/read-only/utils/isObjectReadOnly';
|
||||
import { type ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
export type IsFieldReadOnlyByPermissionParams = {
|
||||
objectPermissions: ObjectPermission;
|
||||
fieldMetadataId: string;
|
||||
};
|
||||
|
||||
export const isFieldReadOnlyByPermissions = ({
|
||||
objectPermissions,
|
||||
fieldMetadataId,
|
||||
}: IsFieldReadOnlyByPermissionParams) => {
|
||||
if (isObjectReadOnly({ objectPermissions }) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const fieldMetadataIsRestrictedForUpdate =
|
||||
objectPermissions.restrictedFields[fieldMetadataId]?.canUpdate === false;
|
||||
|
||||
return fieldMetadataIsRestrictedForUpdate;
|
||||
};
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { isWorkflowSubObjectMetadata } from '@/object-metadata/utils/isWorkflowSubObjectMetadata';
|
||||
import { isWorkflowRunJsonField } from '@/object-record/record-field/ui/meta-types/utils/isWorkflowRunJsonField';
|
||||
import { isFieldActor } from '@/object-record/record-field/ui/types/guards/isFieldActor';
|
||||
import { isFieldRichText } from '@/object-record/record-field/ui/types/guards/isFieldRichText';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
export type IsFieldReadOnlyBySystemParams = {
|
||||
objectNameSingular: string;
|
||||
fieldName?: string;
|
||||
fieldType?: FieldMetadataType;
|
||||
isCustom?: boolean;
|
||||
};
|
||||
|
||||
export const isFieldReadOnlyBySystem = ({
|
||||
objectNameSingular,
|
||||
fieldName,
|
||||
fieldType,
|
||||
isCustom,
|
||||
}: IsFieldReadOnlyBySystemParams) => {
|
||||
if (
|
||||
isWorkflowRunJsonField({
|
||||
objectMetadataNameSingular: objectNameSingular,
|
||||
fieldName,
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isWorkflowSubObjectMetadata(objectNameSingular) && !isCustom) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (objectNameSingular === CoreObjectNameSingular.CalendarEvent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
objectNameSingular === CoreObjectNameSingular.Workflow &&
|
||||
fieldName !== 'name' &&
|
||||
!isCustom
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
objectNameSingular !== CoreObjectNameSingular.Note &&
|
||||
fieldName === 'noteTargets'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
objectNameSingular !== CoreObjectNameSingular.Task &&
|
||||
fieldName === 'taskTargets'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const isFieldDateOrDateTime =
|
||||
fieldType === FieldMetadataType.DATE ||
|
||||
fieldType === FieldMetadataType.DATE_TIME;
|
||||
const isFieldCreatedAtOrUpdatedAt =
|
||||
fieldName === 'createdAt' || fieldName === 'updatedAt';
|
||||
|
||||
if (isFieldDateOrDateTime && isFieldCreatedAtOrUpdatedAt) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
isDefined(fieldType) &&
|
||||
(isFieldActor({ type: fieldType }) || isFieldRichText({ type: fieldType }))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
import { type ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
type IsObjectReadOnlyParams = {
|
||||
objectPermissions: ObjectPermission;
|
||||
};
|
||||
|
||||
export const isObjectReadOnly = ({
|
||||
objectPermissions,
|
||||
}: IsObjectReadOnlyParams) => {
|
||||
return !objectPermissions.canUpdateObjectRecords;
|
||||
};
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
import {
|
||||
isFieldReadOnlyByPermissions,
|
||||
type IsFieldReadOnlyByPermissionParams,
|
||||
} from '@/object-record/record-field/ui/hooks/read-only/utils/internal/isFieldReadOnlyByPermissions';
|
||||
import {
|
||||
isFieldReadOnlyBySystem,
|
||||
type IsFieldReadOnlyBySystemParams,
|
||||
} from '@/object-record/record-field/ui/hooks/read-only/utils/internal/isFieldReadOnlyBySystem';
|
||||
|
||||
type IsRecordFieldReadOnlyParams = {
|
||||
isRecordReadOnly: boolean;
|
||||
} & IsFieldReadOnlyByPermissionParams &
|
||||
IsFieldReadOnlyBySystemParams;
|
||||
|
||||
export const isRecordFieldReadOnly = ({
|
||||
isRecordReadOnly,
|
||||
objectPermissions,
|
||||
fieldMetadataId,
|
||||
objectNameSingular,
|
||||
fieldName,
|
||||
fieldType,
|
||||
isCustom,
|
||||
}: IsRecordFieldReadOnlyParams) => {
|
||||
const fieldReadOnlyByPermissions = isFieldReadOnlyByPermissions({
|
||||
objectPermissions,
|
||||
fieldMetadataId,
|
||||
});
|
||||
|
||||
const fieldReadOnlyBySystem = isFieldReadOnlyBySystem({
|
||||
objectNameSingular,
|
||||
fieldName,
|
||||
fieldType,
|
||||
isCustom,
|
||||
});
|
||||
|
||||
return (
|
||||
isRecordReadOnly || fieldReadOnlyByPermissions || fieldReadOnlyBySystem
|
||||
);
|
||||
};
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
import { type ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
type IsObjectReadOnlyParams = {
|
||||
objectPermissions: ObjectPermission;
|
||||
isRecordDeleted: boolean;
|
||||
};
|
||||
|
||||
export const isRecordReadOnly = ({
|
||||
objectPermissions,
|
||||
isRecordDeleted,
|
||||
}: IsObjectReadOnlyParams) => {
|
||||
return isRecordDeleted || !objectPermissions.canUpdateObjectRecords;
|
||||
};
|
||||
+3
-7
@@ -29,7 +29,6 @@ import { recordStoreFamilySelector } from '@/object-record/record-store/states/s
|
||||
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { isWorkflowRunJsonField } from '@/object-record/record-field/ui/meta-types/utils/isWorkflowRunJsonField';
|
||||
import { isFieldArray } from '@/object-record/record-field/ui/types/guards/isFieldArray';
|
||||
import { isFieldArrayValue } from '@/object-record/record-field/ui/types/guards/isFieldArrayValue';
|
||||
import { isFieldRichText } from '@/object-record/record-field/ui/types/guards/isFieldRichText';
|
||||
@@ -143,13 +142,10 @@ export const usePersistField = ({
|
||||
const fieldIsArray =
|
||||
isFieldArray(fieldDefinition) && isFieldArrayValue(valueToPersist);
|
||||
|
||||
const isUnpersistableRawJsonField = isWorkflowRunJsonField({
|
||||
objectMetadataNameSingular:
|
||||
fieldDefinition.metadata.objectMetadataNameSingular,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
});
|
||||
const fieldIsUIReadOnly =
|
||||
fieldDefinition.metadata.isUIReadOnly ?? false;
|
||||
|
||||
if (fieldIsRawJson && isUnpersistableRawJsonField) {
|
||||
if (fieldIsRawJson && fieldIsUIReadOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+1
-6
@@ -1,7 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
|
||||
import { isWorkflowRunJsonField } from '@/object-record/record-field/ui/meta-types/utils/isWorkflowRunJsonField';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
|
||||
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
@@ -142,11 +141,7 @@ export const RawJsonFieldInput = () => {
|
||||
dependencies: [handleShiftTab, draftValue],
|
||||
});
|
||||
|
||||
const showEditingButton = !isWorkflowRunJsonField({
|
||||
objectMetadataNameSingular:
|
||||
fieldDefinition.metadata.objectMetadataNameSingular,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
});
|
||||
const showEditingButton = !fieldDefinition.metadata.isUIReadOnly;
|
||||
|
||||
const handleStartEditing = () => {
|
||||
setIsEditing(true);
|
||||
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
|
||||
// FIXME: This is temporary. We'll soon introduce a new display mode for all fields and we'll have to remove this code.
|
||||
export const isWorkflowRunJsonField = ({
|
||||
objectMetadataNameSingular,
|
||||
fieldName,
|
||||
}: {
|
||||
fieldName: string | undefined;
|
||||
objectMetadataNameSingular: string | undefined;
|
||||
}) => {
|
||||
return (
|
||||
objectMetadataNameSingular === CoreObjectNameSingular.WorkflowRun &&
|
||||
fieldName === 'state'
|
||||
);
|
||||
};
|
||||
+1
@@ -15,4 +15,5 @@ export type FieldDefinition<T extends FieldMetadata> = {
|
||||
infoTooltipContent?: string;
|
||||
defaultValue?: any;
|
||||
editButtonIcon?: IconComponent;
|
||||
isUIReadOnly?: boolean;
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ type BaseFieldMetadata = {
|
||||
fieldName: string;
|
||||
objectMetadataNameSingular?: string;
|
||||
isCustom?: boolean;
|
||||
isUIReadOnly?: boolean;
|
||||
};
|
||||
|
||||
export type FieldUuidMetadata = BaseFieldMetadata & {
|
||||
|
||||
+1
@@ -132,6 +132,7 @@ const mockObjectMetadataItem: ObjectMetadataItem = {
|
||||
isRemote: false,
|
||||
isSearchable: true,
|
||||
isSystem: false,
|
||||
isUIReadOnly: false,
|
||||
labelIdentifierFieldMetadataId: 'mock-id',
|
||||
labelPlural: 'Tests',
|
||||
labelSingular: 'Test',
|
||||
|
||||
+1
@@ -224,6 +224,7 @@ describe('useRecordData', () => {
|
||||
fieldName: 'updatedAt',
|
||||
isCustom: false,
|
||||
isNullable: false,
|
||||
isUIReadOnly: false,
|
||||
objectMetadataNameSingular: 'person',
|
||||
options: null,
|
||||
placeHolder: 'Last update',
|
||||
|
||||
+1
-1
@@ -2,13 +2,13 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataIte
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { useIsRecordFieldReadOnly } from '@/object-record/read-only/hooks/useIsRecordFieldReadOnly';
|
||||
import {
|
||||
FieldContext,
|
||||
type RecordUpdateHook,
|
||||
type RecordUpdateHookParams,
|
||||
} from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { FieldFocusContextProvider } from '@/object-record/record-field/ui/contexts/FieldFocusContextProvider';
|
||||
import { useIsRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordFieldReadOnly';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RecordInlineCellAnchoredPortalContext } from '@/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortalContext';
|
||||
import { RecordInlineCellCloseOnCommandMenuOpeningEffect } from '@/object-record/record-inline-cell/components/RecordInlineCellCloseOnCommandMenuOpeningEffect';
|
||||
|
||||
+1
-1
@@ -1,8 +1,8 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { useIsRecordFieldReadOnly } from '@/object-record/read-only/hooks/useIsRecordFieldReadOnly';
|
||||
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { useIsRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordFieldReadOnly';
|
||||
import { useRecordShowContainerActions } from '@/object-record/record-show/hooks/useRecordShowContainerActions';
|
||||
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
|
||||
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
|
||||
|
||||
+1
-1
@@ -2,8 +2,8 @@ import { useGetStandardObjectIcon } from '@/object-metadata/hooks/useGetStandard
|
||||
import { useLabelIdentifierFieldMetadataItem } from '@/object-metadata/hooks/useLabelIdentifierFieldMetadataItem';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useIsRecordFieldReadOnly } from '@/object-record/read-only/hooks/useIsRecordFieldReadOnly';
|
||||
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { useIsRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordFieldReadOnly';
|
||||
import { useRecordShowContainerActions } from '@/object-record/record-show/hooks/useRecordShowContainerActions';
|
||||
import { useRecordShowContainerData } from '@/object-record/record-show/hooks/useRecordShowContainerData';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
|
||||
+9
-2
@@ -1,4 +1,5 @@
|
||||
import { isObjectMetadataReadOnly } from '@/object-metadata/utils/isObjectMetadataReadOnly';
|
||||
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
|
||||
import { isObjectMetadataReadOnly } from '@/object-record/read-only/utils/isObjectMetadataReadOnly';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { type IconComponent } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
@@ -35,7 +36,13 @@ export const RecordTableEmptyStateDisplay = (
|
||||
props: RecordTableEmptyStateDisplayProps,
|
||||
) => {
|
||||
const { objectMetadataItem } = useRecordTableContextOrThrow();
|
||||
const isReadOnly = isObjectMetadataReadOnly(objectMetadataItem);
|
||||
const objectPermissions = useObjectPermissionsForObject(
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
const isReadOnly = isObjectMetadataReadOnly({
|
||||
objectPermissions,
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
return (
|
||||
<AnimatedPlaceholderEmptyContainer>
|
||||
|
||||
+5
-6
@@ -1,7 +1,7 @@
|
||||
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
|
||||
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly';
|
||||
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/utils/isRecordFieldReadOnly';
|
||||
import { isFieldRelationFromManyObjects } from '@/object-record/record-field/ui/types/guards/isFieldRelationFromManyObjects';
|
||||
import { isFieldRelationToOneObject } from '@/object-record/record-field/ui/types/guards/isFieldRelationToOneObject';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
@@ -63,11 +63,10 @@ export const RecordTableCellFieldContextGeneric = ({
|
||||
isRecordFieldReadOnly: isRecordFieldReadOnly({
|
||||
isRecordReadOnly: isRecordReadOnly ?? false,
|
||||
objectPermissions,
|
||||
fieldMetadataId: columnDefinition.fieldMetadataId,
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
fieldType: columnDefinition.type,
|
||||
isCustom: objectMetadataItem.isCustom,
|
||||
fieldMetadataItem: {
|
||||
id: columnDefinition.fieldMetadataId,
|
||||
isUIReadOnly: columnDefinition.metadata.isUIReadOnly ?? false,
|
||||
},
|
||||
}),
|
||||
isForbidden: !hasObjectReadPermissions,
|
||||
}}
|
||||
|
||||
+6
-7
@@ -1,6 +1,6 @@
|
||||
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly';
|
||||
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/utils/isRecordFieldReadOnly';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/useOpenRecordFromIndexView';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
@@ -70,13 +70,12 @@ export const RecordTableCellFieldContextLabelIdentifier = ({
|
||||
isLabelIdentifierCompact,
|
||||
displayedMaxRows: 1,
|
||||
isRecordFieldReadOnly: isRecordFieldReadOnly({
|
||||
objectPermissions,
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
fieldType: columnDefinition.type,
|
||||
isCustom: objectMetadataItem.isCustom,
|
||||
fieldMetadataId: columnDefinition.fieldMetadataId,
|
||||
isRecordReadOnly: isRecordReadOnly ?? false,
|
||||
objectPermissions,
|
||||
fieldMetadataItem: {
|
||||
id: columnDefinition.fieldMetadataId,
|
||||
isUIReadOnly: columnDefinition.metadata.isUIReadOnly ?? false,
|
||||
},
|
||||
}),
|
||||
maxWidth: columnDefinition.size,
|
||||
onRecordChipClick: () => {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage';
|
||||
import { useIsRecordReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordReadOnly';
|
||||
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
|
||||
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
|
||||
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
|
||||
import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||
|
||||
+3
-2
@@ -2,8 +2,8 @@ import styled from '@emotion/styled';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { isObjectMetadataReadOnly } from '@/object-record/read-only/utils/isObjectMetadataReadOnly';
|
||||
import { useUpdateRecordField } from '@/object-record/record-field/hooks/useUpdateRecordField';
|
||||
import { isObjectReadOnly } from '@/object-record/record-field/ui/hooks/read-only/utils/isObjectReadOnly';
|
||||
import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useCreateNewIndexRecord';
|
||||
@@ -241,8 +241,9 @@ export const RecordTableHeaderCell = ({
|
||||
createNewIndexRecord();
|
||||
};
|
||||
|
||||
const isReadOnly = isObjectReadOnly({
|
||||
const isReadOnly = isObjectMetadataReadOnly({
|
||||
objectPermissions,
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const hasObjectUpdatePermissions = objectPermissions.canUpdateObjectRecords;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage';
|
||||
import { useIsRecordReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordReadOnly';
|
||||
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
|
||||
|
||||
+2
@@ -22,6 +22,7 @@ describe('generateAggregateQuery', () => {
|
||||
isLabelSyncedWithName: true,
|
||||
isRemote: false,
|
||||
isSystem: false,
|
||||
isUIReadOnly: false,
|
||||
};
|
||||
|
||||
const mockRecordGqlFields = {
|
||||
@@ -63,6 +64,7 @@ describe('generateAggregateQuery', () => {
|
||||
isLabelSyncedWithName: true,
|
||||
isRemote: false,
|
||||
isSystem: false,
|
||||
isUIReadOnly: false,
|
||||
};
|
||||
|
||||
const mockRecordGqlFields = {
|
||||
|
||||
Reference in New Issue
Block a user