From 27da9d60ebebcfd61b1590746f9836af75b7bbd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Thu, 5 Feb 2026 14:35:46 +0100 Subject: [PATCH] fix: set isActive=true in morph migration & add system fields toggle (#17736) ## Summary Three fixes in this PR: ### 1. Migration commands now set `isActive = true` and use generic 'Target' label When converting relation fields to `MORPH_RELATION` type, the migration commands now: - Set `isActive = true` to prevent inactive fields from being selected as the representative morph field - Update all field labels to generic **'Target'** instead of keeping individual labels like 'Company', 'Person' This ensures the UI shows a coherent label ('Target') alongside 'X Objects' for the type. **Files changed:** - `1-17-migrate-note-target-to-morph-relations.command.ts` - `1-17-migrate-task-target-to-morph-relations.command.ts` ### 2. Added system fields/relations toggles in Settings The fields and relations tables in Settings > Data Model were filtering out system fields with no way to view them. Added a "System fields" / "System relations" toggle (visible in advanced mode) to allow viewing these fields. **Files changed:** - `SettingsObjectFieldTable.tsx` - `SettingsObjectRelationsTable.tsx` This matches the existing behavior on the Objects table which already has a "System objects" toggle. --- .../SettingsObjectRelationsTable.tsx | 27 ++++++++++++-- .../data-model/SettingsObjectFieldTable.tsx | 37 +++++++++++++++---- ...-note-target-to-morph-relations.command.ts | 10 +++-- ...-task-target-to-morph-relations.command.ts | 10 +++-- 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectRelationsTable.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectRelationsTable.tsx index 71eea032e0..a2ed2f915c 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectRelationsTable.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectRelationsTable.tsx @@ -9,12 +9,19 @@ import { Table } from '@/ui/layout/table/components/Table'; import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray'; import { type TableMetadata } from '@/ui/layout/table/types/TableMetadata'; +import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState'; import styled from '@emotion/styled'; import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react/macro'; import { useMemo, useState } from 'react'; +import { useRecoilValue } from 'recoil'; import { FieldMetadataType } from 'twenty-shared/types'; -import { IconArchive, IconFilter, IconSearch } from 'twenty-ui/display'; +import { + IconArchive, + IconFilter, + IconSearch, + IconSettings, +} from 'twenty-ui/display'; import { Button } from 'twenty-ui/input'; import { MenuItemToggle } from 'twenty-ui/navigation'; import { normalizeSearchText } from '~/utils/normalizeSearchText'; @@ -73,17 +80,20 @@ export const SettingsObjectRelationsTable = ({ const { t } = useLingui(); const [searchTerm, setSearchTerm] = useState(''); const [showInactive, setShowInactive] = useState(true); + const [showSystemRelations, setShowSystemRelations] = useState(false); + + const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState); const tableMetadata = SETTINGS_OBJECT_RELATION_TABLE_METADATA; const relationFields = useMemo(() => { return objectMetadataItem.fields.filter( (field) => - !field.isSystem && + (showSystemRelations || !field.isSystem) && (field.type === FieldMetadataType.RELATION || field.type === FieldMetadataType.MORPH_RELATION), ); - }, [objectMetadataItem.fields]); + }, [objectMetadataItem.fields, showSystemRelations]); const sortedRelationFields = useSortedArray(relationFields, tableMetadata); @@ -136,6 +146,17 @@ export const SettingsObjectRelationsTable = ({ text={t`Inactive`} toggleSize="small" /> + {isAdvancedModeEnabled && ( + + setShowSystemRelations(!showSystemRelations) + } + toggled={showSystemRelations} + text={t`System relations`} + toggleSize="small" + /> + )} } diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldTable.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldTable.tsx index 77afc69c9d..b30ef05ffd 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldTable.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldTable.tsx @@ -13,13 +13,19 @@ import { Table } from '@/ui/layout/table/components/Table'; import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray'; import { type TableMetadata } from '@/ui/layout/table/types/TableMetadata'; +import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState'; import styled from '@emotion/styled'; import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react/macro'; import { useEffect, useMemo, useState } from 'react'; -import { useRecoilState } from 'recoil'; +import { useRecoilState, useRecoilValue } from 'recoil'; import { FieldMetadataType } from 'twenty-shared/types'; -import { IconArchive, IconFilter, IconSearch } from 'twenty-ui/display'; +import { + IconArchive, + IconFilter, + IconSearch, + IconSettings, +} from 'twenty-ui/display'; import { Button } from 'twenty-ui/input'; import { MenuItemToggle } from 'twenty-ui/navigation'; import { useMapFieldMetadataItemToSettingsObjectDetailTableItem } from '~/pages/settings/data-model/hooks/useMapFieldMetadataItemToSettingsObjectDetailTableItem'; @@ -81,6 +87,9 @@ export const SettingsObjectFieldTable = ({ const { t } = useLingui(); const [searchTerm, setSearchTerm] = useState(''); const [showInactive, setShowInactive] = useState(true); + const [showSystemFields, setShowSystemFields] = useState(false); + + const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState); const tableMetadata = SETTINGS_OBJECT_FIELD_TABLE_METADATA; @@ -98,17 +107,19 @@ export const SettingsObjectFieldTable = ({ }, [objectMetadataItem, setSettingsObjectFields]); const allObjectSettingsDetailItems = useMemo(() => { - const nonSystemFields = settingsObjectFields?.filter( - (fieldMetadataItem) => !fieldMetadataItem.isSystem, - ); + const filteredBySystem = showSystemFields + ? settingsObjectFields + : settingsObjectFields?.filter( + (fieldMetadataItem) => !fieldMetadataItem.isSystem, + ); const fieldsToDisplay = excludeRelations - ? nonSystemFields?.filter( + ? filteredBySystem?.filter( (fieldMetadataItem) => fieldMetadataItem.type !== FieldMetadataType.RELATION && fieldMetadataItem.type !== FieldMetadataType.MORPH_RELATION, ) - : nonSystemFields; + : filteredBySystem; return ( fieldsToDisplay?.map( @@ -119,6 +130,7 @@ export const SettingsObjectFieldTable = ({ settingsObjectFields, mapFieldMetadataItemToSettingsObjectDetailTableItem, excludeRelations, + showSystemFields, ]); const sortedAllObjectSettingsDetailItems = useSortedArray( @@ -174,6 +186,17 @@ export const SettingsObjectFieldTable = ({ text={t`Inactive`} toggleSize="small" /> + {isAdvancedModeEnabled && ( + + setShowSystemFields(!showSystemFields) + } + toggled={showSystemFields} + text={t`System fields`} + toggleSize="small" + /> + )} } diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-migrate-note-target-to-morph-relations.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-migrate-note-target-to-morph-relations.command.ts index 59d39ce330..62461a6532 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-migrate-note-target-to-morph-relations.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-migrate-note-target-to-morph-relations.command.ts @@ -18,8 +18,8 @@ import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/service import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util'; -import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; +import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util'; import { getMetadataRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names.util'; import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util'; @@ -152,6 +152,7 @@ export class MigrateNoteTargetToMorphRelationsCommand extends ActiveOrSuspendedW const fieldMigrations = noteTargetRelationFields.map((field) => { const newFieldName = `target${capitalize(field.name)}`; + const newFieldLabel = 'Target'; const relationSettings: RelationFieldMetadataSettings = field.settings; const oldJoinColumnName = relationSettings?.joinColumnName ?? @@ -163,6 +164,7 @@ export class MigrateNoteTargetToMorphRelationsCommand extends ActiveOrSuspendedW return { field, newFieldName, + newFieldLabel, oldJoinColumnName, newJoinColumnName, }; @@ -204,6 +206,7 @@ export class MigrateNoteTargetToMorphRelationsCommand extends ActiveOrSuspendedW for (const { field: fieldToMigrate, newFieldName, + newFieldLabel, newJoinColumnName, } of fieldMigrations) { const settings = { @@ -214,7 +217,7 @@ export class MigrateNoteTargetToMorphRelationsCommand extends ActiveOrSuspendedW try { const result = await queryRunner.query( `UPDATE core."fieldMetadata" - SET name = $1, type = $5, "morphId" = $3, settings = $4 + SET name = $1, type = $5, "morphId" = $3, settings = $4, "isActive" = true, label = $6 WHERE id = $2`, [ newFieldName, @@ -222,6 +225,7 @@ export class MigrateNoteTargetToMorphRelationsCommand extends ActiveOrSuspendedW morphId, settings, FieldMetadataType.MORPH_RELATION, + newFieldLabel, ], ); @@ -229,7 +233,7 @@ export class MigrateNoteTargetToMorphRelationsCommand extends ActiveOrSuspendedW if (rowsUpdated > 0) { this.logger.log( - `Updated fieldMetadata: ${fieldToMigrate.name} → ${newFieldName} (type: MORPH_RELATION)`, + `Updated fieldMetadata: ${fieldToMigrate.name} → ${newFieldName} (label: ${newFieldLabel}, type: MORPH_RELATION)`, ); } } catch (error) { diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-migrate-task-target-to-morph-relations.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-migrate-task-target-to-morph-relations.command.ts index 3723c490ba..54f19d98aa 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-migrate-task-target-to-morph-relations.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-migrate-task-target-to-morph-relations.command.ts @@ -18,8 +18,8 @@ import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/service import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util'; -import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; +import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util'; import { getMetadataRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names.util'; import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util'; @@ -152,6 +152,7 @@ export class MigrateTaskTargetToMorphRelationsCommand extends ActiveOrSuspendedW const fieldMigrations = taskTargetRelationFields.map((field) => { const newFieldName = `target${capitalize(field.name)}`; + const newFieldLabel = 'Target'; const relationSettings: RelationFieldMetadataSettings = field.settings; const oldJoinColumnName = relationSettings?.joinColumnName ?? @@ -163,6 +164,7 @@ export class MigrateTaskTargetToMorphRelationsCommand extends ActiveOrSuspendedW return { field, newFieldName, + newFieldLabel, oldJoinColumnName, newJoinColumnName, }; @@ -204,6 +206,7 @@ export class MigrateTaskTargetToMorphRelationsCommand extends ActiveOrSuspendedW for (const { field: fieldToMigrate, newFieldName, + newFieldLabel, newJoinColumnName, } of fieldMigrations) { const settings = { @@ -214,7 +217,7 @@ export class MigrateTaskTargetToMorphRelationsCommand extends ActiveOrSuspendedW try { const result = await queryRunner.query( `UPDATE core."fieldMetadata" - SET name = $1, type = $5, "morphId" = $3, settings = $4 + SET name = $1, type = $5, "morphId" = $3, settings = $4, "isActive" = true, label = $6 WHERE id = $2`, [ newFieldName, @@ -222,6 +225,7 @@ export class MigrateTaskTargetToMorphRelationsCommand extends ActiveOrSuspendedW morphId, settings, FieldMetadataType.MORPH_RELATION, + newFieldLabel, ], ); @@ -229,7 +233,7 @@ export class MigrateTaskTargetToMorphRelationsCommand extends ActiveOrSuspendedW if (rowsUpdated > 0) { this.logger.log( - `Updated fieldMetadata: ${fieldToMigrate.name} → ${newFieldName} (type: MORPH_RELATION)`, + `Updated fieldMetadata: ${fieldToMigrate.name} → ${newFieldName} (label: ${newFieldLabel}, type: MORPH_RELATION)`, ); } } catch (error) {