From 445c6fe9f6b956e43a2b92bfb922b627defadd75 Mon Sep 17 00:00:00 2001 From: Joseph Chiang Date: Tue, 2 Jun 2026 19:42:28 +1000 Subject: [PATCH] feat: expose CURRENCY field settings (format/decimals) in shared types (#21090) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What Add a `CURRENCY` entry to `FieldMetadataSettingsMapping` (a `FieldMetadataCurrencySettings` type of `{ format?: 'short' | 'full'; decimals?: number }`) so `FieldMetadataSettings` resolves to the real settings shape instead of `null`. ## Why The currency **format** (Short/Full) and **decimals** selectors already ship in the field settings UI and persist through the generic `settings` jsonb column — they render via [`CurrencyDisplay.tsx`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-front/src/modules/ui/field/display/components/CurrencyDisplay.tsx) reading `settings.format` / `settings.decimals` (added in #12542 and #16439). But `twenty-shared` never got a `CURRENCY` entry in the settings mapping, so `FieldMetadataSettings` is `null`. The SDK's `defineField` derives its types from this mapping, so an app author cannot set these from code — `universalSettings: { format: 'full', decimals: 2 }` on a CURRENCY field is a type error, even though the server stores and the frontend honours it. This aligns the type layer with the already-shipped runtime behaviour. ## Changes - `twenty-shared`: add `FieldMetadataCurrencySettings` + `FieldCurrencyFormat`, wire the `CURRENCY` mapping entry, export `FieldCurrencyFormat`. - `twenty-server`: move `CurrencyFieldMetadata` from the `NotDefinedSettings` assertions to a defined-settings assertion in the field-metadata entity type test. No runtime change — the server already accepts and stores these settings via the generic jsonb column; this only makes them visible to the type system and the SDK. ## Test plan - [ ] `npx nx typecheck twenty-shared` / `twenty-server` pass - [ ] In an app, `defineField({ type: FieldType.CURRENCY, universalSettings: { format: 'full', decimals: 2 }, ... })` type-checks and deploys - [ ] Field renders with 2 decimals in full format, matching the equivalent UI configuration > Follow-up (not in this PR): the frontend keeps its own local `fieldMetadataCurrencyFormat` / `FieldCurrencyFormat`; it could import the shared `FieldCurrencyFormat` to de-duplicate. Co-authored-by: Claude Opus 4.8 --- .../__tests__/field-metadata-entity.test-type.ts | 11 ++++++++++- .../twenty-shared/src/types/FieldMetadataSettings.ts | 8 ++++++++ packages/twenty-shared/src/types/index.ts | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/types/__tests__/field-metadata-entity.test-type.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/types/__tests__/field-metadata-entity.test-type.ts index 41aa24e022..4aac61d93a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/types/__tests__/field-metadata-entity.test-type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/types/__tests__/field-metadata-entity.test-type.ts @@ -114,7 +114,6 @@ type NotDefinedSettings = { // oxlint-disable-next-line unused-imports/no-unused-vars type SettingsAssertions = [ - Expect>, Expect>, Expect>, Expect>, @@ -145,6 +144,16 @@ type SettingsAssertions = [ } > >, + Expect< + HasAllProperties< + CurrencyFieldMetadata, + { + settings: JsonbProperty< + FieldMetadataSettingsMapping[FieldMetadataType.CURRENCY] + >; + } + > + >, Expect< HasAllProperties< DateFieldMetadata, diff --git a/packages/twenty-shared/src/types/FieldMetadataSettings.ts b/packages/twenty-shared/src/types/FieldMetadataSettings.ts index cee9334ef2..1e122a2f28 100644 --- a/packages/twenty-shared/src/types/FieldMetadataSettings.ts +++ b/packages/twenty-shared/src/types/FieldMetadataSettings.ts @@ -20,12 +20,19 @@ export enum DateDisplayFormat { export type FieldNumberVariant = 'number' | 'percentage'; +export type FieldCurrencyFormat = 'short' | 'full'; + type FieldMetadataNumberSettings = { dataType?: NumberDataType; decimals?: number; type?: FieldNumberVariant; }; +type FieldMetadataCurrencySettings = { + format?: FieldCurrencyFormat; + decimals?: number; +}; + type FieldMetadataTextSettings = { displayedMaxRows?: number; }; @@ -62,6 +69,7 @@ type FieldMetadataTsVectorSettings = { export type FieldMetadataSettingsMapping = { [FieldMetadataType.NUMBER]: FieldMetadataNumberSettings | null; + [FieldMetadataType.CURRENCY]: FieldMetadataCurrencySettings | null; [FieldMetadataType.DATE]: FieldMetadataDateSettings | null; [FieldMetadataType.DATE_TIME]: FieldMetadataDateTimeSettings | null; [FieldMetadataType.TEXT]: FieldMetadataTextSettings | null; diff --git a/packages/twenty-shared/src/types/index.ts b/packages/twenty-shared/src/types/index.ts index 647a90a57b..ade278d81b 100644 --- a/packages/twenty-shared/src/types/index.ts +++ b/packages/twenty-shared/src/types/index.ts @@ -106,6 +106,7 @@ export { } from './FieldMetadataOptions'; export type { FieldNumberVariant, + FieldCurrencyFormat, FieldMetadataSettingsMapping, AllFieldMetadataSettings, FieldMetadataSettings,