From 8d367f1ec301159ce1312bb0fb30e73c96e3ec28 Mon Sep 17 00:00:00 2001 From: Marie <51697796+ijreilly@users.noreply.github.com> Date: Mon, 4 Aug 2025 09:17:55 +0200 Subject: [PATCH] [permissions] Remove non-readable field from aggregate options (kanban) (#13559) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Non-readable fields should not be an option for aggregate, as they currently were still in kanban headers. Capture d’écran 2025-08-01 à 18 16
16 --- ...rdColumnHeaderAggregateDropdownContent.tsx | 51 +++++++++++++------ ...sIdsForAggregationFromObjectFields.test.ts | 32 ++++++------ ...FieldsIdsForAggregationFromObjectFields.ts | 11 ++-- 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContent.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContent.tsx index dcdb0ec8c5..54c23a4608 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContent.tsx @@ -1,4 +1,6 @@ import { useDropdownContextStateManagement } from '@/dropdown-context-state-management/hooks/useDropdownContextStateManagement'; +import { getReadRestrictedFieldMetadataIdsFromObjectPermissions } from '@/object-metadata/utils/getReadRestrictedFieldMetadataIdsFromObjectPermissions'; +import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { RecordBoardColumnHeaderAggregateDropdownContext } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext'; import { RecordBoardColumnHeaderAggregateDropdownFieldsContent } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownFieldsContent'; import { RecordBoardColumnHeaderAggregateDropdownMenuContent } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownMenuContent'; @@ -17,13 +19,27 @@ export const AggregateDropdownContent = () => { context: RecordBoardColumnHeaderAggregateDropdownContext, }); + const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); + + const restrictedFieldMetadataIds = + getReadRestrictedFieldMetadataIdsFromObjectPermissions({ + objectPermissions: [ + objectPermissionsByObjectMetadataId[objectMetadataItem.id], + ], + objectMetadataId: objectMetadataItem.id, + }); + + const readableFields = objectMetadataItem.fields.filter( + (field) => !restrictedFieldMetadataIds.includes(field.id), + ); + switch (currentContentId) { case 'countAggregateOperationsOptions': { const availableAggregations: AvailableFieldsForAggregateOperation = - getAvailableFieldsIdsForAggregationFromObjectFields( - objectMetadataItem.fields, - COUNT_AGGREGATE_OPERATION_OPTIONS, - ); + getAvailableFieldsIdsForAggregationFromObjectFields({ + fields: readableFields, + targetAggregateOperations: COUNT_AGGREGATE_OPERATION_OPTIONS, + }); return ( { } case 'percentAggregateOperationsOptions': { const availableAggregations: AvailableFieldsForAggregateOperation = - getAvailableFieldsIdsForAggregationFromObjectFields( - objectMetadataItem.fields, - PERCENT_AGGREGATE_OPERATION_OPTIONS, - ); + getAvailableFieldsIdsForAggregationFromObjectFields({ + fields: readableFields, + targetAggregateOperations: PERCENT_AGGREGATE_OPERATION_OPTIONS, + }); return ( { } case 'datesAggregateOperationOptions': { const datesAvailableAggregations: AvailableFieldsForAggregateOperation = - getAvailableFieldsIdsForAggregationFromObjectFields( - objectMetadataItem.fields, - [DateAggregateOperations.EARLIEST, DateAggregateOperations.LATEST], - ); + getAvailableFieldsIdsForAggregationFromObjectFields({ + fields: readableFields, + targetAggregateOperations: [ + DateAggregateOperations.EARLIEST, + DateAggregateOperations.LATEST, + ], + }); return ( { } case 'moreAggregateOperationOptions': { const availableAggregationsWithoutDates: AvailableFieldsForAggregateOperation = - getAvailableFieldsIdsForAggregationFromObjectFields( - objectMetadataItem.fields, - NON_STANDARD_AGGREGATE_OPERATION_OPTIONS, - ); + getAvailableFieldsIdsForAggregationFromObjectFields({ + fields: readableFields, + targetAggregateOperations: NON_STANDARD_AGGREGATE_OPERATION_OPTIONS, + }); return ( { it('should handle empty fields array', () => { - const result = getAvailableFieldsIdsForAggregationFromObjectFields( - [], - COUNT_AGGREGATE_OPERATION_OPTIONS, - ); + const result = getAvailableFieldsIdsForAggregationFromObjectFields({ + fields: [], + targetAggregateOperations: COUNT_AGGREGATE_OPERATION_OPTIONS, + }); COUNT_AGGREGATE_OPERATION_OPTIONS.forEach((operation) => { expect(result[operation]).toEqual([]); @@ -79,10 +79,10 @@ describe('getAvailableFieldsIdsForAggregationFromObjectFields', () => { describe('with count aggregate operations', () => { it('should include all fields', () => { - const result = getAvailableFieldsIdsForAggregationFromObjectFields( - FIELDS_MOCKS as FieldMetadataItem[], - COUNT_AGGREGATE_OPERATION_OPTIONS, - ); + const result = getAvailableFieldsIdsForAggregationFromObjectFields({ + fields: FIELDS_MOCKS as FieldMetadataItem[], + targetAggregateOperations: COUNT_AGGREGATE_OPERATION_OPTIONS, + }); expect(result.COUNT).toEqual( expect.arrayContaining([ @@ -107,10 +107,10 @@ describe('getAvailableFieldsIdsForAggregationFromObjectFields', () => { describe('with percentage aggregate operations', () => { it('should include all fields', () => { - const result = getAvailableFieldsIdsForAggregationFromObjectFields( - FIELDS_MOCKS as FieldMetadataItem[], - PERCENT_AGGREGATE_OPERATION_OPTIONS, - ); + const result = getAvailableFieldsIdsForAggregationFromObjectFields({ + fields: FIELDS_MOCKS as FieldMetadataItem[], + targetAggregateOperations: PERCENT_AGGREGATE_OPERATION_OPTIONS, + }); PERCENT_AGGREGATE_OPERATION_OPTIONS.forEach((operation) => { expect(result[operation]).toEqual([ @@ -132,10 +132,10 @@ describe('getAvailableFieldsIdsForAggregationFromObjectFields', () => { describe('with non standard aggregate operations', () => { it('should exclude non-numeric fields', () => { - const result = getAvailableFieldsIdsForAggregationFromObjectFields( - FIELDS_MOCKS as FieldMetadataItem[], - NON_STANDARD_AGGREGATE_OPERATION_OPTIONS, - ); + const result = getAvailableFieldsIdsForAggregationFromObjectFields({ + fields: FIELDS_MOCKS as FieldMetadataItem[], + targetAggregateOperations: NON_STANDARD_AGGREGATE_OPERATION_OPTIONS, + }); COUNT_AGGREGATE_OPERATION_OPTIONS.forEach((operation) => { expect(result[operation]).toBeUndefined(); diff --git a/packages/twenty-front/src/modules/object-record/utils/getAvailableFieldsIdsForAggregationFromObjectFields.ts b/packages/twenty-front/src/modules/object-record/utils/getAvailableFieldsIdsForAggregationFromObjectFields.ts index 5481bfa7af..daa4453cc6 100644 --- a/packages/twenty-front/src/modules/object-record/utils/getAvailableFieldsIdsForAggregationFromObjectFields.ts +++ b/packages/twenty-front/src/modules/object-record/utils/getAvailableFieldsIdsForAggregationFromObjectFields.ts @@ -5,10 +5,13 @@ import { getAvailableAggregationsFromObjectFields } from '@/object-record/utils/ import { initializeAvailableFieldsForAggregateOperationMap } from '@/object-record/utils/initializeAvailableFieldsForAggregateOperationMap'; import { isDefined } from 'twenty-shared/utils'; -export const getAvailableFieldsIdsForAggregationFromObjectFields = ( - fields: FieldMetadataItem[], - targetAggregateOperations: ExtendedAggregateOperations[], -): AvailableFieldsForAggregateOperation => { +export const getAvailableFieldsIdsForAggregationFromObjectFields = ({ + fields, + targetAggregateOperations, +}: { + fields: FieldMetadataItem[]; + targetAggregateOperations: ExtendedAggregateOperations[]; +}): AvailableFieldsForAggregateOperation => { const aggregationMap = initializeAvailableFieldsForAggregateOperationMap( targetAggregateOperations, );