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.
---
...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,
);