feat: gate record creation on isUICreatable only, decoupled from isSystem (#21527)
## Context The generic "create a record" UI affordance previously required `!isSystem`, conflating two distinct concerns: **"hidden from Data Model"** and **"not user-creatable"**. This blocked legitimately creatable system objects (e.g. marketing message lists kept `isSystem: true` only to stay out of the Data Model). This PR makes creatability depend on `isUICreatable` alone, so visibility (`isSystem`) and creatability (`isUICreatable`) become independent. ## Changes - **Front gate** (`canCreateRecordsForObjectMetadataItem.ts`): drop the `!isSystem` clause — creatability is now `isUICreatable && !readOnly`. Updated comment + unit test. - **Command menu** (`standard-command-menu-item.constant.ts`): drop the matching `not objectMetadataItem.isSystem` clause from the `createNewRecord` availability expression so the "Create new X" command mirrors the front gate. Existing workspaces get this via the already-present `SyncCreateRecordCommandAvailabilityExpressionCommand`, which re-syncs from the live definition. - **Standard object audit** (`create-standard-flat-object-metadata.util.ts`): the 15 sync/system-created standard objects that relied on `!isSystem` to stay non-creatable now set `isUICreatable: false` (attachment, blocklist, calendar*/message*/note/task targets, message, messageThread, messageParticipant, timelineActivity, callRecording, workflowAutomatedTrigger, …). `workflowRun`/`workflowVersion`/`workspaceMember` were already `false`. Non-system objects (company, person, note, opportunity, dashboard, task, workflow) are untouched. - **Backfill**: new fast instance command (`2-13-…-1781277480000-backfill-non-ui-creatable-standard-system-objects.ts`) runs `UPDATE core.objectMetadata SET isUICreatable = false` for those standard system objects (symmetric `down`), registered in `instance-commands.constant.ts`. `isSystem` and Data-Model visibility logic are unchanged. ## Verification - Front gate unit test (7 pass), standard-application suite incl. callRecording (10 pass) - `oxlint` clean on all changed files - `twenty-server` and `twenty-front` typecheck green 🤖 https://claude.ai/code/session_01TF4kjD56hHjP31wkHPxPv3 --- _Generated by [Claude Code](https://claude.ai/code/session_01TF4kjD56hHjP31wkHPxPv3)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21527?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. --> --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -44,7 +44,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
|
||||
shortLabel: 'New ${capitalize(objectMetadataItem.labelSingular)}',
|
||||
availabilityType: CommandMenuItemAvailabilityType.GLOBAL_OBJECT_CONTEXT,
|
||||
conditionalAvailabilityExpression:
|
||||
'pageType == "INDEX_PAGE" and objectPermissions.canUpdateObjectRecords and not hasAnySoftDeleteFilterOnView and objectMetadataItem.isUICreatable and objectMetadataItem.isUIEditable and not objectMetadataItem.isSystem and not objectMetadataItem.isRemote',
|
||||
'pageType == "INDEX_PAGE" and objectPermissions.canUpdateObjectRecords and not hasAnySoftDeleteFilterOnView and objectMetadataItem.isUICreatable and objectMetadataItem.isUIEditable and not objectMetadataItem.isRemote',
|
||||
availabilityObjectMetadataUniversalIdentifier: null,
|
||||
frontComponentUniversalIdentifier: null,
|
||||
engineComponentKey: EngineComponentKey.CREATE_NEW_RECORD,
|
||||
|
||||
+15
@@ -29,6 +29,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
description: i18nLabel(msg`An attachment`),
|
||||
icon: 'IconFileImport',
|
||||
isSystem: true,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'name',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -55,6 +56,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
description: i18nLabel(msg`Blocklist`),
|
||||
icon: 'IconForbid2',
|
||||
isSystem: true,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'handle',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -86,6 +88,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
icon: 'IconCalendar',
|
||||
isSystem: true,
|
||||
isAuditLogged: false,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'id',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -117,6 +120,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
icon: 'IconCalendar',
|
||||
isSystem: true,
|
||||
isAuditLogged: false,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'handle',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -147,6 +151,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
icon: 'IconCalendar',
|
||||
isSystem: true,
|
||||
isAuditLogged: false,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'title',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -177,6 +182,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
icon: 'IconVideo',
|
||||
isSystem: true,
|
||||
isAuditLogged: false,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'title',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -263,6 +269,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
icon: 'IconMessage',
|
||||
isSystem: true,
|
||||
isAuditLogged: false,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'id',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -301,6 +308,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
icon: 'IconFolder',
|
||||
isSystem: true,
|
||||
isAuditLogged: false,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'id',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -332,6 +340,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
icon: 'IconUserCircle',
|
||||
isSystem: true,
|
||||
isAuditLogged: false,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'handle',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -362,6 +371,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
icon: 'IconMessage',
|
||||
isSystem: true,
|
||||
isAuditLogged: false,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'subject',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -389,6 +399,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
icon: 'IconMessage',
|
||||
isSystem: true,
|
||||
isAuditLogged: false,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'subject',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -442,6 +453,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
description: i18nLabel(msg`A note target`),
|
||||
icon: 'IconCheckbox',
|
||||
isSystem: true,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'id',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -555,6 +567,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
description: i18nLabel(msg`A task target`),
|
||||
icon: 'IconCheckbox',
|
||||
isSystem: true,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'id',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -588,6 +601,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
icon: 'IconTimelineEvent',
|
||||
isSystem: true,
|
||||
isAuditLogged: false,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'name',
|
||||
},
|
||||
workspaceId,
|
||||
@@ -645,6 +659,7 @@ export const STANDARD_FLAT_OBJECT_METADATA_BUILDERS_BY_OBJECT_NAME = {
|
||||
description: i18nLabel(msg`A workflow automated trigger`),
|
||||
icon: 'IconSettingsAutomation',
|
||||
isSystem: true,
|
||||
isUICreatable: false,
|
||||
labelIdentifierFieldMetadataName: 'id',
|
||||
},
|
||||
workspaceId,
|
||||
|
||||
Reference in New Issue
Block a user