mirror of
https://github.com/twentyhq/twenty.git
synced 2026-09-16 16:06:17 -04:00
A user reported that their select options had no visible color and could not be edited in field settings. Investigating the report revealed that missing or unsupported colors could reach saved option metadata, leaving the settings form unable to save otherwise valid edits. This also exposed inactive validation in `options.input.ts`: its classes were used only as TypeScript types, and options arrived as JSON without nested class validation. Their decorators therefore never checked incoming options. This PR validates colors in the flat-field validators and replaces the unused duplicate DTO classes with the existing option types from `twenty-shared`. - Default missing or null colors to `gray` when creating SELECT/MULTI_SELECT fields, updating their options, or converting app manifests. - Centralize color trimming and defaults in `sanitizeSelectOptionColors`. This replaces `color` in the creation helper's generic whitespace-trimming list and keeps creation and updates consistent. - Reject explicitly unsupported colors in backend validation and SDK definitions. Malformed SDK option entries produce validation errors. - Normalize existing invalid colors to `gray` when opening field settings so affected options remain editable. Unrelated field updates remain allowed when legacy options contain invalid colors. - Keep the supported palette and `TagColor` in `twenty-shared` for the frontend, SDK, and backend, and document the option color rules for app developers. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/25805?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. -->
52 lines
3.5 KiB
Plaintext
52 lines
3.5 KiB
Plaintext
---
|
|
title: Extending Objects
|
|
description: Add fields to standard Twenty objects (Person, Company, …) or to objects from other apps using defineField.
|
|
icon: "wand-magic-sparkles"
|
|
---
|
|
|
|
Use `defineField()` to add a field to an object you don't own — a standard Twenty object like Person or Company, or an object shipped by another installed app. Unlike inline fields declared inside [`defineObject`](/developers/extend/apps/data/objects), standalone fields require an `objectUniversalIdentifier` to specify which object they extend.
|
|
|
|
```ts src/fields/company-loyalty-tier.field.ts
|
|
import { defineField, FieldType } from 'twenty-sdk/define';
|
|
|
|
export default defineField({
|
|
universalIdentifier: 'f2a1b3c4-d5e6-7890-abcd-ef1234567890',
|
|
objectUniversalIdentifier: '701aecb9-eb1c-4d84-9d94-b954b231b64b', // Company object
|
|
name: 'loyaltyTier',
|
|
type: FieldType.SELECT,
|
|
label: 'Loyalty Tier',
|
|
icon: 'IconStar',
|
|
options: [
|
|
{ value: 'BRONZE', label: 'Bronze', position: 0, color: 'orange' },
|
|
{ value: 'SILVER', label: 'Silver', position: 1, color: 'gray' },
|
|
{ value: 'GOLD', label: 'Gold', position: 2, color: 'yellow' },
|
|
],
|
|
});
|
|
```
|
|
|
|
## Key points
|
|
|
|
- `objectUniversalIdentifier` identifies the target object. For standard Twenty objects, import the constant from `twenty-sdk`:
|
|
|
|
```ts
|
|
import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
|
|
|
|
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier
|
|
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier
|
|
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier
|
|
// …
|
|
```
|
|
|
|
- When defining fields **inline inside `defineObject()`**, you do **not** need `objectUniversalIdentifier` — it's inherited from the parent object.
|
|
- `defineField()` is the only way to add fields to objects you didn't create with `defineObject()`.
|
|
- `SELECT` and `MULTI_SELECT` follow the same [option validation and color rules](/developers/extend/apps/data/objects#select-options) as inline fields. An omitted or null color defaults to `gray`; unsupported colors are rejected.
|
|
- File location is up to you. The convention is `src/fields/<name>.field.ts`, but the SDK detects fields anywhere in `src/`.
|
|
- `writability` can also be set per field: `MetadataWritability.APPLICATION` restricts writes to your app's own logic functions, `MetadataWritability.SYSTEM` is reserved for platform-managed data. A field's writability can only be stricter than its object's level, and reads are unaffected. See [Objects](/developers/extend/apps/data/objects) for the full semantics.
|
|
- `isSearchable: true` can be set on `defineField()` with the same constraints as inline fields (searchable target object, text-compatible field type), so records of the target object become findable through your field's values in full-text search.
|
|
- `isAuditLogged: false` can be set on `defineField()` to keep the field's changes out of the target record's timeline, which matters most for fields your app rewrites on a schedule.
|
|
- To add a tab to a standard page layout (e.g. the Task or Company detail page), use [`definePageLayoutTab`](/developers/extend/apps/layout/page-layouts#definepagelayouttab) with `STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS` from `twenty-sdk/define`.
|
|
|
|
## Adding a relation to an existing object
|
|
|
|
To add a relation field (e.g. linking your custom object to a standard `Person`), use `defineField()` with `FieldType.RELATION`. The pattern is the same as for inline relations but with `objectUniversalIdentifier` set explicitly. See [Relations](/developers/extend/apps/data/relations) for the bidirectional pattern.
|