Fix meeting bot CalendarEvent field visibility and editability (#21883)
- Add the meeting bot preference field to the CalendarEvent record page fields view. - Use a Standard-app ownership gate for record field read-only logic. - Allow app-owned and workspace-custom fields on system objects to follow isUIEditable and permissions. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21883?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
+5
-4
@@ -1,4 +1,4 @@
|
||||
import { useGetIsMetadataItemCustom } from '@/object-metadata/hooks/useGetIsMetadataItemCustom';
|
||||
import { useGetIsMetadataItemFromStandardApplication } from '@/object-metadata/hooks/useGetIsMetadataItemFromStandardApplication';
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
|
||||
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
|
||||
@@ -26,8 +26,8 @@ export const useIsRecordFieldReadOnly = ({
|
||||
);
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const getIsMetadataItemCustom = useGetIsMetadataItemCustom();
|
||||
const getIsMetadataItemFromStandardApplication =
|
||||
useGetIsMetadataItemFromStandardApplication();
|
||||
|
||||
const objectPermissions = getObjectPermissionsForObject(
|
||||
objectPermissionsByObjectMetadataId,
|
||||
@@ -51,7 +51,8 @@ export const useIsRecordFieldReadOnly = ({
|
||||
return isRecordFieldReadOnly({
|
||||
isRecordReadOnly,
|
||||
isSystemObject: objectMetadataItem.isSystem,
|
||||
isFieldCustom: getIsMetadataItemCustom(fieldMetadataItem),
|
||||
isFieldFromStandardApplication:
|
||||
getIsMetadataItemFromStandardApplication(fieldMetadataItem),
|
||||
objectPermissions,
|
||||
fieldMetadataItem,
|
||||
fieldDefinition,
|
||||
|
||||
+29
-16
@@ -10,7 +10,6 @@ describe('isRecordFieldReadOnly', () => {
|
||||
|
||||
const mockParams = {
|
||||
isRecordReadOnly: false,
|
||||
isFieldCustom: false,
|
||||
objectPermissions: mockObjectPermissions,
|
||||
fieldMetadataId: 'field-123',
|
||||
fieldMetadataType: FieldMetadataType.TEXT,
|
||||
@@ -88,7 +87,35 @@ describe('isRecordFieldReadOnly', () => {
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when field is non-custom on a system object', () => {
|
||||
it('should return true when field is from the standard application on a system object', () => {
|
||||
const result = isRecordFieldReadOnly({
|
||||
...mockParams,
|
||||
isSystemObject: true,
|
||||
isFieldFromStandardApplication: true,
|
||||
fieldMetadataItem: {
|
||||
id: 'field-123',
|
||||
isUIEditable: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when field is not from the standard application on a system object', () => {
|
||||
const result = isRecordFieldReadOnly({
|
||||
...mockParams,
|
||||
isSystemObject: true,
|
||||
isFieldFromStandardApplication: false,
|
||||
fieldMetadataItem: {
|
||||
id: 'field-123',
|
||||
isUIEditable: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when field application is not resolved on a system object', () => {
|
||||
const result = isRecordFieldReadOnly({
|
||||
...mockParams,
|
||||
isSystemObject: true,
|
||||
@@ -101,20 +128,6 @@ describe('isRecordFieldReadOnly', () => {
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when field is custom on a system object', () => {
|
||||
const result = isRecordFieldReadOnly({
|
||||
...mockParams,
|
||||
isSystemObject: true,
|
||||
isFieldCustom: true,
|
||||
fieldMetadataItem: {
|
||||
id: 'field-123',
|
||||
isUIEditable: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when isSystemObject is not provided', () => {
|
||||
const result = isRecordFieldReadOnly({
|
||||
...mockParams,
|
||||
|
||||
+8
-3
@@ -15,7 +15,7 @@ type ObjectPermissionsByObjectMetadataId = Record<
|
||||
type IsRecordFieldReadOnlyParams = {
|
||||
isRecordReadOnly: boolean;
|
||||
isSystemObject?: boolean;
|
||||
isFieldCustom: boolean;
|
||||
isFieldFromStandardApplication?: boolean;
|
||||
fieldMetadataItem: Pick<FieldMetadataItem, 'id' | 'isUIEditable'>;
|
||||
objectPermissions: ObjectPermission;
|
||||
fieldDefinition?: FieldDefinition<FieldMetadata>;
|
||||
@@ -26,7 +26,7 @@ export const isRecordFieldReadOnly = ({
|
||||
objectPermissions,
|
||||
isRecordReadOnly,
|
||||
isSystemObject,
|
||||
isFieldCustom,
|
||||
isFieldFromStandardApplication,
|
||||
fieldMetadataItem,
|
||||
fieldDefinition,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
@@ -44,9 +44,14 @@ export const isRecordFieldReadOnly = ({
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
|
||||
// Keep system-object standard fields read-only. If the application origin
|
||||
// cannot be resolved yet, fail closed until metadata finishes loading.
|
||||
const isReadOnlyStandardFieldOnSystemObject =
|
||||
isSystemObject === true && isFieldFromStandardApplication !== false;
|
||||
|
||||
return (
|
||||
isRecordReadOnly ||
|
||||
(isSystemObject === true && !isFieldCustom) ||
|
||||
isReadOnlyStandardFieldOnSystemObject ||
|
||||
!(fieldMetadataItem.isUIEditable ?? true) ||
|
||||
fieldReadOnlyByPermissions ||
|
||||
oneToManyTargetReadOnly
|
||||
|
||||
+8
-7
@@ -1,4 +1,4 @@
|
||||
import { useGetIsMetadataItemCustom } from '@/object-metadata/hooks/useGetIsMetadataItemCustom';
|
||||
import { useGetIsMetadataItemFromStandardApplication } from '@/object-metadata/hooks/useGetIsMetadataItemFromStandardApplication';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly';
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { StopPropagationContainer } from '@/object-record/record-board/record-board-card/components/StopPropagationContainer';
|
||||
@@ -55,8 +55,8 @@ export const RecordBoardCardBody = () => {
|
||||
const setRecordBoardCardHoverPosition = useSetAtomComponentState(
|
||||
recordBoardCardHoverPositionComponentState,
|
||||
);
|
||||
|
||||
const getIsMetadataItemCustom = useGetIsMetadataItemCustom();
|
||||
const getIsMetadataItemFromStandardApplication =
|
||||
useGetIsMetadataItemFromStandardApplication();
|
||||
|
||||
const handleMouseEnter = (index: number) => {
|
||||
setRecordBoardCardHoverPosition(index);
|
||||
@@ -78,10 +78,11 @@ export const RecordBoardCardBody = () => {
|
||||
isRecordFieldReadOnly: isRecordFieldReadOnly({
|
||||
isRecordReadOnly,
|
||||
isSystemObject: objectMetadataItem.isSystem,
|
||||
isFieldCustom: getIsMetadataItemCustom({
|
||||
applicationId:
|
||||
correspondingFieldDefinition.metadata.applicationId,
|
||||
}),
|
||||
isFieldFromStandardApplication:
|
||||
getIsMetadataItemFromStandardApplication({
|
||||
applicationId:
|
||||
correspondingFieldDefinition.metadata.applicationId,
|
||||
}),
|
||||
objectPermissions,
|
||||
fieldMetadataItem: {
|
||||
id: recordField.fieldMetadataItemId,
|
||||
|
||||
+8
-7
@@ -1,4 +1,4 @@
|
||||
import { useGetIsMetadataItemCustom } from '@/object-metadata/hooks/useGetIsMetadataItemCustom';
|
||||
import { useGetIsMetadataItemFromStandardApplication } from '@/object-metadata/hooks/useGetIsMetadataItemFromStandardApplication';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly';
|
||||
import { StopPropagationContainer } from '@/object-record/record-board/record-board-card/components/StopPropagationContainer';
|
||||
@@ -64,8 +64,8 @@ export const RecordCalendarCardBody = ({
|
||||
const setRecordCalendarCardHoverPosition = useSetAtomComponentState(
|
||||
recordCalendarCardHoverPositionComponentState,
|
||||
);
|
||||
|
||||
const getIsMetadataItemCustom = useGetIsMetadataItemCustom();
|
||||
const getIsMetadataItemFromStandardApplication =
|
||||
useGetIsMetadataItemFromStandardApplication();
|
||||
|
||||
const handleMouseEnter = (index: number) => {
|
||||
setRecordCalendarCardHoverPosition(index);
|
||||
@@ -87,10 +87,11 @@ export const RecordCalendarCardBody = ({
|
||||
isRecordFieldReadOnly: isRecordFieldReadOnly({
|
||||
isRecordReadOnly,
|
||||
isSystemObject: objectMetadataItem.isSystem,
|
||||
isFieldCustom: getIsMetadataItemCustom({
|
||||
applicationId:
|
||||
correspondingFieldDefinition.metadata.applicationId,
|
||||
}),
|
||||
isFieldFromStandardApplication:
|
||||
getIsMetadataItemFromStandardApplication({
|
||||
applicationId:
|
||||
correspondingFieldDefinition.metadata.applicationId,
|
||||
}),
|
||||
objectPermissions,
|
||||
fieldMetadataItem: {
|
||||
id: recordField.fieldMetadataItemId,
|
||||
|
||||
+11
-6
@@ -1,5 +1,5 @@
|
||||
import { ActivityTargetsInlineCell } from '@/activities/inline-cell/components/ActivityTargetsInlineCell';
|
||||
import { useGetIsMetadataItemCustom } from '@/object-metadata/hooks/useGetIsMetadataItemCustom';
|
||||
import { useGetIsMetadataItemFromStandardApplication } from '@/object-metadata/hooks/useGetIsMetadataItemFromStandardApplication';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
|
||||
@@ -58,8 +58,8 @@ export const RecordFieldList = ({
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const getIsMetadataItemCustom = useGetIsMetadataItemCustom();
|
||||
const getIsMetadataItemFromStandardApplication =
|
||||
useGetIsMetadataItemFromStandardApplication();
|
||||
|
||||
const { useUpdateOneObjectRecordMutation } = useRecordShowContainerActions({
|
||||
objectNameSingular,
|
||||
@@ -131,7 +131,10 @@ export const RecordFieldList = ({
|
||||
objectPermissionsByObjectMetadataId,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
}),
|
||||
isFieldCustom: getIsMetadataItemCustom(fieldMetadataItem),
|
||||
isFieldFromStandardApplication:
|
||||
getIsMetadataItemFromStandardApplication(
|
||||
fieldMetadataItem,
|
||||
),
|
||||
fieldMetadataItem: {
|
||||
id: fieldMetadataItem.id,
|
||||
isUIEditable: fieldMetadataItem.isUIEditable ?? true,
|
||||
@@ -187,7 +190,8 @@ export const RecordFieldList = ({
|
||||
objectPermissionsByObjectMetadataId,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
}),
|
||||
isFieldCustom: getIsMetadataItemCustom(fieldMetadataItem),
|
||||
isFieldFromStandardApplication:
|
||||
getIsMetadataItemFromStandardApplication(fieldMetadataItem),
|
||||
fieldMetadataItem: {
|
||||
id: fieldMetadataItem.id,
|
||||
isUIEditable: fieldMetadataItem.isUIEditable ?? true,
|
||||
@@ -267,7 +271,8 @@ export const RecordFieldList = ({
|
||||
objectPermissionsByObjectMetadataId,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
}),
|
||||
isFieldCustom: getIsMetadataItemCustom(fieldMetadataItem),
|
||||
isFieldFromStandardApplication:
|
||||
getIsMetadataItemFromStandardApplication(fieldMetadataItem),
|
||||
fieldMetadataItem: {
|
||||
id: fieldMetadataItem.id,
|
||||
isUIEditable: fieldMetadataItem.isUIEditable ?? true,
|
||||
|
||||
+7
-6
@@ -1,4 +1,4 @@
|
||||
import { useGetIsMetadataItemCustom } from '@/object-metadata/hooks/useGetIsMetadataItemCustom';
|
||||
import { useGetIsMetadataItemFromStandardApplication } from '@/object-metadata/hooks/useGetIsMetadataItemFromStandardApplication';
|
||||
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
|
||||
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
||||
import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly';
|
||||
@@ -44,8 +44,8 @@ export const RecordTableCellFieldContextGeneric = ({
|
||||
fieldDefinitionByFieldMetadataItemId[recordField.fieldMetadataItemId];
|
||||
|
||||
const updateRecord = useContext(RecordTableUpdateContext);
|
||||
|
||||
const getIsMetadataItemCustom = useGetIsMetadataItemCustom();
|
||||
const getIsMetadataItemFromStandardApplication =
|
||||
useGetIsMetadataItemFromStandardApplication();
|
||||
|
||||
let hasObjectReadPermissions = objectPermissions.canReadObjectRecords;
|
||||
|
||||
@@ -113,9 +113,10 @@ export const RecordTableCellFieldContextGeneric = ({
|
||||
isRecordFieldReadOnly({
|
||||
isRecordReadOnly: isRecordReadOnly ?? false,
|
||||
isSystemObject: objectMetadataItem.isSystem,
|
||||
isFieldCustom: getIsMetadataItemCustom({
|
||||
applicationId: fieldDefinition.metadata.applicationId,
|
||||
}),
|
||||
isFieldFromStandardApplication:
|
||||
getIsMetadataItemFromStandardApplication({
|
||||
applicationId: fieldDefinition.metadata.applicationId,
|
||||
}),
|
||||
objectPermissions,
|
||||
fieldMetadataItem: {
|
||||
id: fieldDefinition.fieldMetadataId,
|
||||
|
||||
+7
-6
@@ -1,4 +1,4 @@
|
||||
import { useGetIsMetadataItemCustom } from '@/object-metadata/hooks/useGetIsMetadataItemCustom';
|
||||
import { useGetIsMetadataItemFromStandardApplication } from '@/object-metadata/hooks/useGetIsMetadataItemFromStandardApplication';
|
||||
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';
|
||||
@@ -46,8 +46,8 @@ export const RecordTableCellFieldContextLabelIdentifier = ({
|
||||
const hasObjectReadPermissions = objectPermissions.canReadObjectRecords;
|
||||
|
||||
const updateRecord = useContext(RecordTableUpdateContext);
|
||||
|
||||
const getIsMetadataItemCustom = useGetIsMetadataItemCustom();
|
||||
const getIsMetadataItemFromStandardApplication =
|
||||
useGetIsMetadataItemFromStandardApplication();
|
||||
|
||||
const fieldDefinition =
|
||||
fieldDefinitionByFieldMetadataItemId[recordField.fieldMetadataItemId];
|
||||
@@ -70,9 +70,10 @@ export const RecordTableCellFieldContextLabelIdentifier = ({
|
||||
isRecordFieldReadOnly({
|
||||
isRecordReadOnly: isRecordReadOnly ?? false,
|
||||
isSystemObject: objectMetadataItem.isSystem,
|
||||
isFieldCustom: getIsMetadataItemCustom({
|
||||
applicationId: fieldDefinition.metadata.applicationId,
|
||||
}),
|
||||
isFieldFromStandardApplication:
|
||||
getIsMetadataItemFromStandardApplication({
|
||||
applicationId: fieldDefinition.metadata.applicationId,
|
||||
}),
|
||||
objectPermissions,
|
||||
fieldMetadataItem: {
|
||||
id: recordField.fieldMetadataItemId,
|
||||
|
||||
Reference in New Issue
Block a user