From f45c54679c3bf3c9974dd859814cb1785788d562 Mon Sep 17 00:00:00 2001
From: Arun <184187559+carbonFibreCode@users.noreply.github.com>
Date: Sun, 14 Jun 2026 20:15:28 +0530
Subject: [PATCH] [Fix] : fix: Allow label identifier system fields in view
creation and fix resulting duplicate header columns (#19009)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
fixes #18994
After :
Before :
---------
Co-authored-by: Arun kumar
Co-authored-by: Charles Bochet
---
.../components/RecordTableHeader.tsx | 28 +++-----
.../RecordTableHeaderFirstScrollableCell.tsx | 12 +---
...erFieldsForRecordTableViewCreation.test.ts | 67 +++++++++++++++++++
.../buildRecordTableWidgetViewSnapshot.ts | 7 +-
.../filterFieldsForRecordTableViewCreation.ts | 7 +-
5 files changed, 88 insertions(+), 33 deletions(-)
create mode 100644 packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/__tests__/filterFieldsForRecordTableViewCreation.test.ts
diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx
index 01ba0b1181..9a551191f6 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx
@@ -1,4 +1,3 @@
-import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { TABLE_Z_INDEX } from '@/object-record/record-table/constants/TableZIndex';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableHeaderAddColumnButton } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderAddColumnButton';
@@ -15,7 +14,6 @@ import { isRecordTableColumnHeadersReadOnlyComponentState } from '@/object-recor
import { isRecordTableDragColumnHiddenComponentState } from '@/object-record/record-table/states/isRecordTableDragColumnHiddenComponentState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { styled } from '@linaria/react';
-import { filterOutByProperty } from 'twenty-shared/utils';
const StyledHeaderContainer = styled.div`
display: flex;
@@ -27,7 +25,6 @@ const StyledHeaderContainer = styled.div`
export const RecordTableHeader = () => {
const { visibleRecordFields } = useRecordTableContextOrThrow();
- const { labelIdentifierFieldMetadataItem } = useRecordIndexContextOrThrow();
const isRecordTableColumnHeadersReadOnly = useAtomComponentStateValue(
isRecordTableColumnHeadersReadOnlyComponentState,
@@ -41,14 +38,7 @@ export const RecordTableHeader = () => {
isRecordTableCheckboxColumnHiddenComponentState,
);
- const recordFieldsWithoutLabelIdentifierAndFirstOne = visibleRecordFields
- .filter(
- filterOutByProperty(
- 'fieldMetadataItemId',
- labelIdentifierFieldMetadataItem?.id,
- ),
- )
- .slice(1);
+ const recordFieldsWithoutFirstTwo = visibleRecordFields.slice(2);
useResizeTableHeader();
@@ -60,15 +50,13 @@ export const RecordTableHeader = () => {
)}
- {recordFieldsWithoutLabelIdentifierAndFirstOne.map(
- (recordField, index) => (
-
- ),
- )}
+ {recordFieldsWithoutFirstTwo.map((recordField, index) => (
+
+ ))}
{isRecordTableColumnHeadersReadOnly ? (
) : (
diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderFirstScrollableCell.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderFirstScrollableCell.tsx
index 320086b7aa..49a8923725 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderFirstScrollableCell.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderFirstScrollableCell.tsx
@@ -1,5 +1,4 @@
import { type RecordField } from '@/object-record/record-field/types/RecordField';
-import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { TABLE_Z_INDEX } from '@/object-record/record-table/constants/TableZIndex';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableColumnHead } from '@/object-record/record-table/record-table-header/components/RecordTableColumnHead';
@@ -21,7 +20,7 @@ import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hoo
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { cx } from '@linaria/core';
-import { filterOutByProperty, isDefined } from 'twenty-shared/utils';
+import { isDefined } from 'twenty-shared/utils';
export const RecordTableHeaderFirstScrollableCell = () => {
const { objectMetadataItem, visibleRecordFields } =
@@ -45,14 +44,7 @@ export const RecordTableHeaderFirstScrollableCell = () => {
0,
);
- const { labelIdentifierFieldMetadataItem } = useRecordIndexContextOrThrow();
-
- const recordField = visibleRecordFields.filter(
- filterOutByProperty(
- 'fieldMetadataItemId',
- labelIdentifierFieldMetadataItem?.id,
- ),
- )[0] as RecordField | undefined;
+ const recordField = visibleRecordFields[1] as RecordField | undefined;
const isRecordTableRowFocusActive = useAtomComponentStateValue(
isRecordTableRowFocusActiveComponentState,
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/__tests__/filterFieldsForRecordTableViewCreation.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/__tests__/filterFieldsForRecordTableViewCreation.test.ts
new file mode 100644
index 0000000000..566cce2328
--- /dev/null
+++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/__tests__/filterFieldsForRecordTableViewCreation.test.ts
@@ -0,0 +1,67 @@
+import { filterFieldsForRecordTableViewCreation } from '@/page-layout/widgets/record-table/utils/filterFieldsForRecordTableViewCreation';
+import { getMockFieldMetadataItemOrThrow } from '~/testing/utils/getMockFieldMetadataItemOrThrow';
+import { getMockObjectMetadataItemOrThrow } from '~/testing/utils/getMockObjectMetadataItemOrThrow';
+
+describe('filterFieldsForRecordTableViewCreation', () => {
+ const objectMetadataItem = getMockObjectMetadataItemOrThrow('company');
+ const labelIdentifierFieldMetadataId =
+ objectMetadataItem.labelIdentifierFieldMetadataId;
+
+ // domainName is an active, non-system field that is not the label identifier
+ const nonSystemField = getMockFieldMetadataItemOrThrow({
+ objectMetadataItem,
+ fieldName: 'domainName',
+ });
+ // id is a hidden system field
+ const systemField = getMockFieldMetadataItemOrThrow({
+ objectMetadataItem,
+ fieldName: 'id',
+ });
+ // position is a hidden system field
+ const hiddenSystemField = getMockFieldMetadataItemOrThrow({
+ objectMetadataItem,
+ fieldName: 'position',
+ });
+
+ it('should include active non-system fields', () => {
+ expect(
+ filterFieldsForRecordTableViewCreation(
+ nonSystemField,
+ labelIdentifierFieldMetadataId,
+ ),
+ ).toBe(true);
+ });
+
+ it('should exclude inactive fields', () => {
+ expect(
+ filterFieldsForRecordTableViewCreation(
+ { ...nonSystemField, isActive: false },
+ labelIdentifierFieldMetadataId,
+ ),
+ ).toBe(false);
+ });
+
+ it('should exclude system fields that are not the label identifier', () => {
+ expect(
+ filterFieldsForRecordTableViewCreation(
+ systemField,
+ labelIdentifierFieldMetadataId,
+ ),
+ ).toBe(false);
+ });
+
+ it('should include system fields when they are the label identifier', () => {
+ expect(
+ filterFieldsForRecordTableViewCreation(systemField, systemField.id),
+ ).toBe(true);
+ });
+
+ it('should include hidden system fields when they are the label identifier', () => {
+ expect(
+ filterFieldsForRecordTableViewCreation(
+ hiddenSystemField,
+ hiddenSystemField.id,
+ ),
+ ).toBe(true);
+ });
+});
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/buildRecordTableWidgetViewSnapshot.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/buildRecordTableWidgetViewSnapshot.ts
index db26d639dc..17401f59ee 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/buildRecordTableWidgetViewSnapshot.ts
+++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/buildRecordTableWidgetViewSnapshot.ts
@@ -33,8 +33,11 @@ export const buildRecordTableWidgetViewSnapshot = (
isActive: true,
};
- const eligibleFields = objectMetadataItem.fields.filter(
- filterFieldsForRecordTableViewCreation,
+ const eligibleFields = objectMetadataItem.fields.filter((field) =>
+ filterFieldsForRecordTableViewCreation(
+ field,
+ objectMetadataItem.labelIdentifierFieldMetadataId,
+ ),
);
const sortedFields = eligibleFields.toSorted(
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/filterFieldsForRecordTableViewCreation.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/filterFieldsForRecordTableViewCreation.ts
index e8499a26aa..1778fc806a 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/filterFieldsForRecordTableViewCreation.ts
+++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/filterFieldsForRecordTableViewCreation.ts
@@ -3,6 +3,11 @@ import { isHiddenSystemField } from '@/object-metadata/utils/isHiddenSystemField
export const filterFieldsForRecordTableViewCreation = (
field: FieldMetadataItem,
+ labelIdentifierFieldMetadataId: string,
) => {
- return field.isActive && !field.isSystem && !isHiddenSystemField(field);
+ const isLabelIdentifier = field.id === labelIdentifierFieldMetadataId;
+ return (
+ field.isActive &&
+ (isLabelIdentifier || (!field.isSystem && !isHiddenSystemField(field)))
+ );
};