Skip to content

fix: delete the default page layout tabs and widgets when deleting the default relation fields#19994

Open
abusarah-tech wants to merge 5 commits intotwentyhq:mainfrom
abusarah-tech:fix/remove-stale-default-page-layout-tabs-and-widgets
Open

fix: delete the default page layout tabs and widgets when deleting the default relation fields#19994
abusarah-tech wants to merge 5 commits intotwentyhq:mainfrom
abusarah-tech:fix/remove-stale-default-page-layout-tabs-and-widgets

Conversation

@abusarah-tech
Copy link
Copy Markdown
Contributor

This PR should solve the issue #19990.

A summary of the code changes:

  • At deleteOneField in field-metadata.service.ts, we fetch the existing page layout tabs and widgets
  • compute if the current delete fields are RELATION and are matching the current default layout tabs
  • If so, then delete the page layout tabs and widgets thats related to that field

@twenty-ci-bot-public
Copy link
Copy Markdown

twenty-ci-bot-public Bot commented Apr 23, 2026

📊 API Changes Report

GraphQL Schema Changes

GraphQL Schema Changes

[error] Error: Unable to read JSON file: /home/runner/work/twenty/twenty/main-schema-introspection.json: Not valid JSON content
at JsonFileLoader.handleFileContent (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:147:19)
at /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:95:43
at async Promise.all (index 0)
at async JsonFileLoader.load (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:88:9)
at async /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/load-file.js:15:39
at async Promise.all (index 4)
at async loadFile (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/load-file.js:13:9)
at async /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/collect-sources.js:200:25
Error: Unable to read JSON file: /home/runner/work/twenty/twenty/main-schema-introspection.json: Not valid JSON content
at JsonFileLoader.handleFileContent (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:147:19)
at /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:95:43
at async Promise.all (index 0)
at async JsonFileLoader.load (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:88:9)
at async /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/load-file.js:15:39
at async Promise.all (index 4)
at async loadFile (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/load-file.js:13:9)
at async /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/collect-sources.js:200:25
⚠️ Breaking changes or errors detected in GraphQL schema

[error] Error: Unable to read JSON file: /home/runner/work/twenty/twenty/main-schema-introspection.json: Not valid JSON content
    at JsonFileLoader.handleFileContent (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:147:19)
    at /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:95:43
    at async Promise.all (index 0)
    at async JsonFileLoader.load (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:88:9)
    at async /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/load-file.js:15:39
    at async Promise.all (index 4)
    at async loadFile (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/load-file.js:13:9)
    at async /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/collect-sources.js:200:25
Error generating diff

GraphQL Metadata Schema Changes

GraphQL Metadata Schema Changes

[error] Error: Unable to read JSON file: /home/runner/work/twenty/twenty/main-metadata-schema-introspection.json: Not valid JSON content
at JsonFileLoader.handleFileContent (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:147:19)
at /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:95:43
at async Promise.all (index 0)
at async JsonFileLoader.load (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:88:9)
at async /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/load-file.js:15:39
at async Promise.all (index 4)
at async loadFile (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/load-file.js:13:9)
at async /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/collect-sources.js:200:25
Error: Unable to read JSON file: /home/runner/work/twenty/twenty/main-metadata-schema-introspection.json: Not valid JSON content
at JsonFileLoader.handleFileContent (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:147:19)
at /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:95:43
at async Promise.all (index 0)
at async JsonFileLoader.load (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:88:9)
at async /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/load-file.js:15:39
at async Promise.all (index 4)
at async loadFile (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/load-file.js:13:9)
at async /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/collect-sources.js:200:25
⚠️ Breaking changes or errors detected in GraphQL metadata schema

[error] Error: Unable to read JSON file: /home/runner/work/twenty/twenty/main-metadata-schema-introspection.json: Not valid JSON content
    at JsonFileLoader.handleFileContent (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:147:19)
    at /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:95:43
    at async Promise.all (index 0)
    at async JsonFileLoader.load (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/json-file-loader/cjs/index.js:88:9)
    at async /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/load-file.js:15:39
    at async Promise.all (index 4)
    at async loadFile (/opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/load-file.js:13:9)
    at async /opt/hostedtoolcache/node/24.14.1/x64/lib/node_modules/@graphql-inspector/cli/node_modules/@graphql-tools/load/cjs/load-typedefs/collect-sources.js:200:25
Error generating diff

REST API Analysis Error

⚠️ Error occurred while analyzing REST API changes

Error Output

REST Metadata API Analysis Error

⚠️ Error occurred while analyzing REST Metadata API changes

Error Output

⚠️ Please review these API changes carefully before merging.

@abusarah-tech abusarah-tech marked this pull request as ready for review April 23, 2026 00:54
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/twenty-server/src/modules/timeline/repositories/timeline-activity.repository.ts">

<violation number="1" location="packages/twenty-server/src/modules/timeline/repositories/timeline-activity.repository.ts:225">
P2: Morph-field guard is unsafe because it matches by field name globally, not by the timeline object/active field, so unrelated fields can cause false positives and bypass the intended protection.</violation>
</file>

<file name="packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts">

<violation number="1" location="packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts:201">
P1: Relation-tab cleanup is over-broad: tabs are deleted solely because they contain a matching relation widget, without ensuring the tab is default/system-only or free of unrelated widgets.</violation>

<violation number="2" location="packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts:234">
P1: Both the existing `flatPageLayoutWidgetsToDelete` spread and the new `relationPageLayoutWidgetsToDelete` spread produce the same `pageLayoutWidget` key. When both conditions are true, JavaScript object spread causes the second to overwrite the first, silently dropping `flatPageLayoutWidgetsToDelete`. Merge both arrays into a single `pageLayoutWidget.flatEntityToDelete` entry to ensure all widgets are deleted.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@abusarah-tech abusarah-tech changed the title fix: delete the default page layout tabs and widgets from the default relation fields fix: delete the default page layout tabs and widgets when deleting the default relation fields Apr 23, 2026
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/twenty-server/src/modules/timeline/repositories/timeline-activity.repository.ts">

<violation number="1" location="packages/twenty-server/src/modules/timeline/repositories/timeline-activity.repository.ts:226">
P1: The new morph-field existence check compares `object.id` against `field.objectMetadataUniversalIdentifier`, mixing ID domains and likely returning false for valid fields.</violation>
</file>

<file name="packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts">

<violation number="1" location="packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts:211">
P1: Tab deletion checks only relation-widget subset, so tabs containing other active widgets can be incorrectly deleted.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

relationPageLayoutWidgetsToDelete
.filter(
(widget) =>
widgetsCountByPageLayoutTabIds[widget.pageLayoutTabId] === 1,
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Tab deletion checks only relation-widget subset, so tabs containing other active widgets can be incorrectly deleted.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts, line 211:

<comment>Tab deletion checks only relation-widget subset, so tabs containing other active widgets can be incorrectly deleted.</comment>

<file context>
@@ -192,8 +192,24 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
         relationPageLayoutWidgetsToDelete
+          .filter(
+            (widget) =>
+              widgetsCountByPageLayoutTabIds[widget.pageLayoutTabId] === 1,
+          )
           .map((widget) => widget.pageLayoutTabId)
</file context>
Fix with Cubic

Copy link
Copy Markdown
Contributor Author

@abusarah-tech abusarah-tech Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Qodo-Free-For-OSS
Copy link
Copy Markdown

Hi, FieldMetadataService.deleteOneField deletes all page-layout widgets for the object whose widget configurationType matches the deleted relation field name (e.g. TIMELINE/TASKS), which can remove unrelated user-created/custom widgets.

Severity: action required | Category: correctness

How to fix: Restrict deletions to defaults

Agent prompt to fix - you can give this to your LLM of choice:

Issue description

deleteOneField currently deletes all widgets for an object that match a computed WidgetConfigurationType (TIMELINE/TASKS/NOTES/FILES/EMAILS/CALENDAR). This is too broad and can delete user-created widgets that happen to share the same configurationType.

Issue Context

Standard record-page widgets use configuration.configurationType as their main identifier. Custom widgets can also use these same configuration types.

Fix Focus Areas

  • packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts[178-225]

Fix approach

Constrain relationPageLayoutWidgetsToDelete to only those widgets that are known to be the default ones you want to remove. Options include:

  • Filter by applicationUniversalIdentifier === TWENTY_STANDARD_APPLICATION.universalIdentifier (if only standard defaults should be removed)
  • Additionally restrict to default record page layouts/tabs (e.g., only tabs belonging to standard default layouts)
  • Or match by known standard widget universalIdentifiers for the object/tab/widget definitions (more precise)

Found by Qodo code review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants