Félix MalfaitandClaude Opus 4.7 8461ddbadd feat(views): persist one-hop relation filters via relationTargetFieldMetadataId
Introduces a dedicated, stable identifier for the relation-traversal filter
target on `ViewFilterEntity` so saved views survive renames of the field
they reference, and removes the shortcut that previously cast a target
field's NAME into the closed `CompositeFieldSubFieldName` slot.

Schema
 - New column `core.viewFilter.relationTargetFieldMetadataId` (uuid,
   nullable, FK to `core.fieldMetadata.id` with ON DELETE SET NULL so
   filters degrade gracefully if their target field is deleted, rather
   than vanishing).
 - Partial index on the column where not-null.
 - Instance-fast migration (2.5.0, ts 1798000005000) adds the column,
   the FK, and the index; registered in `INSTANCE_COMMANDS`.
 - Field metadata configuration wired through:
   `FLAT_VIEW_FILTER_EDITABLE_PROPERTIES`,
   `all-entity-properties-configuration-by-metadata-name`,
   `all-many-to-one-metadata-foreign-key`,
   `all-many-to-one-metadata-relations` (universal foreign key
   `relationTargetFieldMetadataUniversalIdentifier`).

API
 - `ViewFilterDTO`, `CreateViewFilterInput`, `UpdateViewFilterInputUpdates`
   and `UpsertViewWidgetViewFilterInput` all carry the new optional UUID.
 - View → flat converters (`fromViewFilterEntityToFlatViewFilter`,
   `fromCreateViewFilterInputToFlatViewFilterToCreate`,
   `fromViewFilterManifestToUniversalFlatViewFilter`,
   `createStandardViewFilterFlatMetadata`,
   widget upsert, create-action handler) thread the new column
   through both the foreign-key and universal-identifier sides.

Shared
 - `RecordFilter.relationTargetFieldMetadataId` added on both the
   frontend and twenty-shared types.
 - `turnRecordFilterIntoRecordGqlOperationFilter` now branches on
   `relationTargetFieldMetadataId` BEFORE the emptiness shortcut and
   synthesizes the inner filter against the target field, wrapping it
   under the relation field's name (`{ company: { name: { ... } } }`).
   Falls through to the existing relation-by-record path if the target
   field isn't present in the provided `fieldMetadataItems`.

Frontend
 - New dedicated state
   `relationTargetFieldMetadataIdUsedInDropdownComponentState` (the
   existing `subFieldNameUsedInDropdownComponentState` stays narrowly
   typed for composite sub-fields).
 - `useSelectFieldUsedInAdvancedFilterDropdown` accepts an optional
   `relationTargetFieldMetadataItem`. When present it stores the target
   field's id on `RecordFilter.relationTargetFieldMetadataId` and uses
   the TARGET field's type (so operand picker / value input behave as if
   filtering it directly) and a "Relation → Target" label.
 - `ObjectFilterDropdownInnerSelectOperandDropdown` resolves the
   effective field via `useGetFieldMetadataItemByIdOrThrow` when the
   relation-target state is set, so operand options match the target's
   filterable type.
 - `AdvancedFilterFieldSelectMenu` routes MANY_TO_ONE clicks into the
   same sub-menu flow as composite fields, with a `'RELATION'` sentinel
   on `objectFilterDropdownSubMenuFieldType`.
 - `AdvancedFilterSubFieldSelectMenu` renders the target object's
   filterable fields via `useFilterableFieldMetadataItems`; the ghost
   `-1` selectable id that the composite branch used for the "Any X
   field" item is no longer included in the relation list (fixes the
   keyboard-navigation issue cubic-dev-ai flagged).
 - `ObjectFilterDropdownFilterSelectMenuItem` shows the sub-menu
   chevron for MANY_TO_ONE relations via a new
   `isManyToOneRelationField` util.
 - `mapRecordFilterToViewFilter` / `mapViewFiltersToFilters`,
   `areViewFiltersEqual`, the save mutation inputs, and the GraphQL
   fragment all thread the new field through the round-trip so saved
   views actually persist relation traversal.
 - Role-permissions sub-field menu narrows the `'RELATION'` sentinel
   back out (it never deals with relations).

Out of scope
 - Normal filter dropdown (`ViewBarFilterDropdownFieldSelectMenu`) —
   composite drill-down doesn't exist there today, so adding relation
   drill-down would mean introducing the entire sub-menu pattern on the
   normal surface. Tracking as follow-up; advanced filter is the only
   surface that currently builds nested filter shapes.
 - REST DSL, ONE_TO_MANY reverse traversal, aggregates — unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 15:23:34 +02:00
2025-08-07 17:02:12 +02:00
2026-05-13 19:32:03 +02:00

Twenty logo

The #1 Open-Source CRM

Website · Documentation · Roadmap · Discord · Figma

Twenty banner


Why Twenty

Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves. Twenty is the CRM you build, ship, and version like the rest of your stack.

Learn more about why we built Twenty


Installation

Cloud

The fastest way to get started. Sign up at twenty.com and spin up a workspace in under a minute, with no infrastructure to manage and always up to date.

Build an app

Scaffold a new app with the Twenty CLI:

npx create-twenty-app my-app

Define objects, fields, and views as code:

import { defineObject, FieldType } from 'twenty-sdk/define';

export default defineObject({
  nameSingular: 'deal',
  namePlural: 'deals',
  labelSingular: 'Deal',
  labelPlural: 'Deals',
  fields: [
    { name: 'name', label: 'Name', type: FieldType.TEXT },
    { name: 'amount', label: 'Amount', type: FieldType.CURRENCY },
    { name: 'closeDate', label: 'Close Date', type: FieldType.DATE_TIME },
  ],
});

Then ship it to your workspace:

npx twenty deploy

See the app development guide for objects, views, agents, and logic functions.

Self-hosting

Run Twenty on your own infrastructure with Docker Compose, or contribute locally via the local setup guide.



Everything you need

Twenty gives you the building blocks of a modern CRM (objects, views, workflows, and agents) and lets you extend them as code. Here's a tour of what's in the box.

Want to go deeper? Read the User Guide for product walkthroughs, or the Documentation for developer reference.

Create your apps

Learn more about apps in doc

Stay on top with version control

Learn more about version control in doc

All the tools you need to build anything

Learn more about primitives in doc

Customize your layouts

Learn more about layouts in doc

AI agents and chats

Learn more about AI in doc

Plus all the tools of a good CRM

Learn more about CRM features in doc


Stack

Thanks

Chromatic      Greptile      Sentry      Crowdin

Thanks to these amazing services that we use and recommend for UI testing (Chromatic), code review (Greptile), catching bugs (Sentry) and translating (Crowdin).

Join the Community

Star the repo · Discord · Feature requests · Releases · X · LinkedIn · Crowdin · Contribute

Languages
TypeScript 78%
MDX 18.5%
JavaScript 3.1%
Python 0.2%