refactor(server): unify the two metadata override mechanisms into one (#22417)
## Unify the two metadata override mechanisms into one Twenty had **two** override mechanisms: - **`standardOverrides`** — a bespoke JSONB column on `objectMetadata`/`fieldMetadata` with typed DTOs and a per-locale `translations` map, resolved by two i18n-aware resolvers. - **`OverridableEntity.overrides`** — a flat, registry-driven JSONB blob on view / view-field / view-field-group / command-menu-item / page-layout-tab / page-layout-widget, resolved by a plain spread. This PR collapses them into **one** concept: a single `overrides` blob, one registry-driven overridable set, one i18n-aware read path, and one write path (`computeMetadataOverridesBlob`, extracted in #22404). Object/field **stay on `SyncableEntity`** (not reparented to `OverridableEntity`) so their `isActive` default stays **FALSE** — this sidesteps the `isActive` default conflict entirely. ### GraphQL breaking change (accepted) The `standardOverrides` field is **removed** with no deprecation alias — `overrides` (a `JSON` scalar) is exposed instead on `Object` and `Field`. Product confirmed negligible external usage; the front-end has no hand-written consumer (only generated types), which are regenerated here. ### Commit structure (reviewable commit-by-commit) 1. **Unified resolver + parity harness** — `resolveEffectiveEntityProperty` is a strict superset of the three legacy resolvers; a corpus parity spec compares it against a *frozen reference* of the old logic across every locale, `isStandardApp` branch and override shape. 2. **Registry-driven** — object/field presentation props tagged `isOverridable` + `translatable`; the overridable/translatable sets are derived from the registry (a test asserts they equal the legacy hardcoded lists). 3. **Rename + swap + delete** — `standardOverrides` → `overrides` across entities, DTOs, flat/universal types, producers, the ~12 resolve/write/create/sync call sites, mocks and specs; the reconciler's two compare entries collapse to one; the three legacy resolvers, both DTOs and the hardcoded constants/types are deleted. 4. **Migration (zero-downtime, two-phase)** — split across two releases so a rolling deploy never drops a column a previous-release pod still `SELECT`s: - **2.19 fast** — add the `overrides` column (schema only). - **2.19 slow** — backfill `overrides` from `standardOverrides` in `runDataMigration` (kept out of the schema transaction so the bulk write doesn't hold the ACCESS EXCLUSIVE lock; skipped on fresh installs, which have no data to copy). - **2.20 fast** — drop the legacy `standardOverrides` column (gated by `TWENTY_NEXT_VERSIONS`, so it stays dormant until the instance reaches 2.20). 5. **Front/client-SDK regen** — regenerated metadata GraphQL types. 6. **Integration specs + i18n** — updated the standard object/field update integration specs + snapshots, and the reworded validator message catalog entry. ### Rolling-deploy safety `standardOverrides` is retained through 2.19 and only dropped in 2.20, mirroring the codebase's deferred-drop convention (`isUIReadOnly`/`isCustom`). During the 2.19 rollout both columns exist, so old and new pods coexist without "column does not exist" errors. The backfill lives in a slow `runDataMigration` (per the `no-data-mutation-in-fast-instance-command` rule) so it doesn't stall reads. ### `isActive` guard The migration never reads or writes `isActive`; the backfill asserts the active-row count is unchanged and aborts otherwise. Verified on a real DB: apply + revert preserves the blob **and** the nested `translations` map, with `isActive` counts identical before/after. ### Verification (local) - `nx typecheck twenty-server` + `nx typecheck twenty-front` — green - `nx lint:diff-with-main twenty-server` (oxlint `--type-aware` + oxfmt) — green - `nx test twenty-server` — green (unit + parity + registry + migration tests) - `nx run twenty-server:test:integration:with-db-reset` — green - `database:reset` applies the 2.19 phases and leaves **both** columns present (2.20 drop stays dormant); backfill + revert round-trip verified on a real DB - Metadata integration suites (standard object/field update, application sync) pass end-to-end against the two-column schema - Metadata GraphQL types regenerated against a booted server; zero `standardOverrides` references remain in application code (only the migration commands + the legacy schema baseline) --------- Co-authored-by: prastoin <paul@twenty.com>
This commit is contained in:
@@ -464,13 +464,6 @@ enum LogicFunctionExecutionMode {
|
||||
PREBUILT
|
||||
}
|
||||
|
||||
type StandardOverrides {
|
||||
label: String
|
||||
description: String
|
||||
icon: String
|
||||
translations: JSON
|
||||
}
|
||||
|
||||
type Field {
|
||||
id: UUID!
|
||||
universalIdentifier: String!
|
||||
@@ -479,7 +472,6 @@ type Field {
|
||||
label: String!
|
||||
description: String
|
||||
icon: String
|
||||
standardOverrides: StandardOverrides
|
||||
isActive: Boolean
|
||||
isSystem: Boolean
|
||||
isUIEditable: Boolean
|
||||
@@ -606,15 +598,6 @@ input BooleanFieldComparison {
|
||||
isNot: Boolean
|
||||
}
|
||||
|
||||
type ObjectStandardOverrides {
|
||||
labelSingular: String
|
||||
labelPlural: String
|
||||
description: String
|
||||
icon: String
|
||||
color: String
|
||||
translations: JSON
|
||||
}
|
||||
|
||||
type Object {
|
||||
id: UUID!
|
||||
universalIdentifier: String!
|
||||
@@ -624,7 +607,6 @@ type Object {
|
||||
labelPlural: String!
|
||||
description: String
|
||||
icon: String
|
||||
standardOverrides: ObjectStandardOverrides
|
||||
shortcut: String
|
||||
color: String
|
||||
isRemote: Boolean!
|
||||
|
||||
@@ -344,14 +344,6 @@ export interface LogicFunction {
|
||||
|
||||
export type LogicFunctionExecutionMode = 'LIVE' | 'PREBUILT'
|
||||
|
||||
export interface StandardOverrides {
|
||||
label?: Scalars['String']
|
||||
description?: Scalars['String']
|
||||
icon?: Scalars['String']
|
||||
translations?: Scalars['JSON']
|
||||
__typename: 'StandardOverrides'
|
||||
}
|
||||
|
||||
export interface Field {
|
||||
id: Scalars['UUID']
|
||||
universalIdentifier: Scalars['String']
|
||||
@@ -360,7 +352,6 @@ export interface Field {
|
||||
label: Scalars['String']
|
||||
description?: Scalars['String']
|
||||
icon?: Scalars['String']
|
||||
standardOverrides?: StandardOverrides
|
||||
isActive?: Scalars['Boolean']
|
||||
isSystem?: Scalars['Boolean']
|
||||
isUIEditable?: Scalars['Boolean']
|
||||
@@ -405,16 +396,6 @@ export interface Index {
|
||||
/** Type of the index */
|
||||
export type IndexType = 'BTREE' | 'GIN'
|
||||
|
||||
export interface ObjectStandardOverrides {
|
||||
labelSingular?: Scalars['String']
|
||||
labelPlural?: Scalars['String']
|
||||
description?: Scalars['String']
|
||||
icon?: Scalars['String']
|
||||
color?: Scalars['String']
|
||||
translations?: Scalars['JSON']
|
||||
__typename: 'ObjectStandardOverrides'
|
||||
}
|
||||
|
||||
export interface Object {
|
||||
id: Scalars['UUID']
|
||||
universalIdentifier: Scalars['String']
|
||||
@@ -424,7 +405,6 @@ export interface Object {
|
||||
labelPlural: Scalars['String']
|
||||
description?: Scalars['String']
|
||||
icon?: Scalars['String']
|
||||
standardOverrides?: ObjectStandardOverrides
|
||||
shortcut?: Scalars['String']
|
||||
color?: Scalars['String']
|
||||
isRemote: Scalars['Boolean']
|
||||
@@ -3350,15 +3330,6 @@ export interface LogicFunctionGenqlSelection{
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface StandardOverridesGenqlSelection{
|
||||
label?: boolean | number
|
||||
description?: boolean | number
|
||||
icon?: boolean | number
|
||||
translations?: boolean | number
|
||||
__typename?: boolean | number
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface FieldGenqlSelection{
|
||||
id?: boolean | number
|
||||
universalIdentifier?: boolean | number
|
||||
@@ -3367,7 +3338,6 @@ export interface FieldGenqlSelection{
|
||||
label?: boolean | number
|
||||
description?: boolean | number
|
||||
icon?: boolean | number
|
||||
standardOverrides?: StandardOverridesGenqlSelection
|
||||
isActive?: boolean | number
|
||||
isSystem?: boolean | number
|
||||
isUIEditable?: boolean | number
|
||||
@@ -3426,17 +3396,6 @@ export interface UUIDFilterComparison {is?: (Scalars['Boolean'] | null),isNot?:
|
||||
|
||||
export interface BooleanFieldComparison {is?: (Scalars['Boolean'] | null),isNot?: (Scalars['Boolean'] | null)}
|
||||
|
||||
export interface ObjectStandardOverridesGenqlSelection{
|
||||
labelSingular?: boolean | number
|
||||
labelPlural?: boolean | number
|
||||
description?: boolean | number
|
||||
icon?: boolean | number
|
||||
color?: boolean | number
|
||||
translations?: boolean | number
|
||||
__typename?: boolean | number
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface ObjectGenqlSelection{
|
||||
id?: boolean | number
|
||||
universalIdentifier?: boolean | number
|
||||
@@ -3446,7 +3405,6 @@ export interface ObjectGenqlSelection{
|
||||
labelPlural?: boolean | number
|
||||
description?: boolean | number
|
||||
icon?: boolean | number
|
||||
standardOverrides?: ObjectStandardOverridesGenqlSelection
|
||||
shortcut?: boolean | number
|
||||
color?: boolean | number
|
||||
isRemote?: boolean | number
|
||||
@@ -6790,14 +6748,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
|
||||
|
||||
|
||||
|
||||
const StandardOverrides_possibleTypes: string[] = ['StandardOverrides']
|
||||
export const isStandardOverrides = (obj?: { __typename?: any } | null): obj is StandardOverrides => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isStandardOverrides"')
|
||||
return StandardOverrides_possibleTypes.includes(obj.__typename)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const Field_possibleTypes: string[] = ['Field']
|
||||
export const isField = (obj?: { __typename?: any } | null): obj is Field => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isField"')
|
||||
@@ -6814,14 +6764,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
|
||||
|
||||
|
||||
|
||||
const ObjectStandardOverrides_possibleTypes: string[] = ['ObjectStandardOverrides']
|
||||
export const isObjectStandardOverrides = (obj?: { __typename?: any } | null): obj is ObjectStandardOverrides => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isObjectStandardOverrides"')
|
||||
return ObjectStandardOverrides_possibleTypes.includes(obj.__typename)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const Object_possibleTypes: string[] = ['Object']
|
||||
export const isObject = (obj?: { __typename?: any } | null): obj is Object => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isObject"')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1767,7 +1767,6 @@ export type Field = {
|
||||
options?: Maybe<Scalars['JSON']['output']>;
|
||||
relation?: Maybe<Relation>;
|
||||
settings?: Maybe<Scalars['JSON']['output']>;
|
||||
standardOverrides?: Maybe<StandardOverrides>;
|
||||
type: FieldMetadataType;
|
||||
universalIdentifier: Scalars['String']['output'];
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
@@ -3849,7 +3848,6 @@ export type Object = {
|
||||
nameSingular: Scalars['String']['output'];
|
||||
searchFieldMetadataList: Array<SearchField>;
|
||||
shortcut?: Maybe<Scalars['String']['output']>;
|
||||
standardOverrides?: Maybe<ObjectStandardOverrides>;
|
||||
universalIdentifier: Scalars['String']['output'];
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
};
|
||||
@@ -3979,16 +3977,6 @@ export enum ObjectRecordGroupByDateGranularity {
|
||||
YEAR = 'YEAR'
|
||||
}
|
||||
|
||||
export type ObjectStandardOverrides = {
|
||||
__typename?: 'ObjectStandardOverrides';
|
||||
color?: Maybe<Scalars['String']['output']>;
|
||||
description?: Maybe<Scalars['String']['output']>;
|
||||
icon?: Maybe<Scalars['String']['output']>;
|
||||
labelPlural?: Maybe<Scalars['String']['output']>;
|
||||
labelSingular?: Maybe<Scalars['String']['output']>;
|
||||
translations?: Maybe<Scalars['JSON']['output']>;
|
||||
};
|
||||
|
||||
/** Onboarding status */
|
||||
export enum OnboardingStatus {
|
||||
APPS_INSTALLATION = 'APPS_INSTALLATION',
|
||||
@@ -5100,14 +5088,6 @@ export type StandaloneRichTextConfiguration = {
|
||||
configurationType: WidgetConfigurationType;
|
||||
};
|
||||
|
||||
export type StandardOverrides = {
|
||||
__typename?: 'StandardOverrides';
|
||||
description?: Maybe<Scalars['String']['output']>;
|
||||
icon?: Maybe<Scalars['String']['output']>;
|
||||
label?: Maybe<Scalars['String']['output']>;
|
||||
translations?: Maybe<Scalars['JSON']['output']>;
|
||||
};
|
||||
|
||||
export type SubdomainAvailabilityDto = {
|
||||
__typename?: 'SubdomainAvailabilityDTO';
|
||||
available: Scalars['Boolean']['output'];
|
||||
|
||||
+1
-1
@@ -127,7 +127,7 @@ const buildLegacyCalendarEventRecordingPreferenceFieldMetadata = ({
|
||||
isUnique: false,
|
||||
isUIEditable: true,
|
||||
isLabelSyncedWithName: false,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
defaultValue: "'AUTO'",
|
||||
settings: null,
|
||||
options: [
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
const TABLES = ['objectMetadata', 'fieldMetadata'] as const;
|
||||
|
||||
@RegisteredInstanceCommand('2.19.0', 1820000100000)
|
||||
export class AddMetadataOverridesColumnFastInstanceCommand
|
||||
implements FastInstanceCommand
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
for (const table of TABLES) {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."${table}" ADD COLUMN IF NOT EXISTS "overrides" jsonb`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
for (const table of TABLES) {
|
||||
// Unconditional copy: a WHERE "overrides" IS NOT NULL guard would leave a
|
||||
// stale value in standardOverrides and resurrect a cleared override.
|
||||
await queryRunner.query(
|
||||
`UPDATE "core"."${table}" SET "standardOverrides" = "overrides"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."${table}" DROP COLUMN IF EXISTS "overrides"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { DataSource, QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { SlowInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/slow-instance-command.interface';
|
||||
|
||||
const TABLES = ['objectMetadata', 'fieldMetadata'] as const;
|
||||
|
||||
@RegisteredInstanceCommand('2.19.0', 1820000110000, { type: 'slow' })
|
||||
export class BackfillMetadataOverridesSlowInstanceCommand
|
||||
implements SlowInstanceCommand
|
||||
{
|
||||
async runDataMigration(dataSource: DataSource): Promise<void> {
|
||||
for (const table of TABLES) {
|
||||
const activeCountBefore = await this.getActiveCount(dataSource, table);
|
||||
|
||||
await dataSource.query(
|
||||
`UPDATE "core"."${table}" SET "overrides" = "standardOverrides" WHERE "standardOverrides" IS NOT NULL AND "overrides" IS NULL`,
|
||||
);
|
||||
|
||||
const activeCountAfter = await this.getActiveCount(dataSource, table);
|
||||
|
||||
if (activeCountBefore !== activeCountAfter) {
|
||||
throw new Error(
|
||||
`BackfillMetadataOverrides: "isActive" changed on "core"."${table}" (${activeCountBefore} -> ${activeCountAfter}), aborting.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async up(_queryRunner: QueryRunner): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
public async down(_queryRunner: QueryRunner): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
private async getActiveCount(
|
||||
dataSource: DataSource,
|
||||
table: (typeof TABLES)[number],
|
||||
): Promise<number> {
|
||||
const [{ count }] = await dataSource.query(
|
||||
`SELECT count(*)::int AS count FROM "core"."${table}" WHERE "isActive" = true`,
|
||||
);
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import { type QueryRunner } from 'typeorm';
|
||||
|
||||
import { AddMetadataOverridesColumnFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-19/2-19-instance-command-fast-1820000100000-add-metadata-overrides-column';
|
||||
|
||||
describe('AddMetadataOverridesColumnFastInstanceCommand', () => {
|
||||
let command: AddMetadataOverridesColumnFastInstanceCommand;
|
||||
|
||||
beforeEach(() => {
|
||||
command = new AddMetadataOverridesColumnFastInstanceCommand();
|
||||
});
|
||||
|
||||
describe('up', () => {
|
||||
it('adds the overrides column to both tables without mutating data or dropping standardOverrides', async () => {
|
||||
const query = jest.fn().mockResolvedValue(undefined);
|
||||
const queryRunner = { query } as unknown as QueryRunner;
|
||||
|
||||
await command.up(queryRunner);
|
||||
|
||||
const statements = query.mock.calls.map((call) => call[0] as string);
|
||||
|
||||
expect(statements).toEqual([
|
||||
'ALTER TABLE "core"."objectMetadata" ADD COLUMN IF NOT EXISTS "overrides" jsonb',
|
||||
'ALTER TABLE "core"."fieldMetadata" ADD COLUMN IF NOT EXISTS "overrides" jsonb',
|
||||
]);
|
||||
expect(
|
||||
statements.some(
|
||||
(statement) =>
|
||||
statement.includes('UPDATE') || statement.includes('DROP COLUMN'),
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('down', () => {
|
||||
it('copies overrides back into standardOverrides before dropping the column', async () => {
|
||||
const query = jest.fn().mockResolvedValue(undefined);
|
||||
const queryRunner = { query } as unknown as QueryRunner;
|
||||
|
||||
await command.down(queryRunner);
|
||||
|
||||
const statements = query.mock.calls.map((call) => call[0] as string);
|
||||
|
||||
expect(statements).toEqual([
|
||||
'UPDATE "core"."objectMetadata" SET "standardOverrides" = "overrides"',
|
||||
'ALTER TABLE "core"."objectMetadata" DROP COLUMN IF EXISTS "overrides"',
|
||||
'UPDATE "core"."fieldMetadata" SET "standardOverrides" = "overrides"',
|
||||
'ALTER TABLE "core"."fieldMetadata" DROP COLUMN IF EXISTS "overrides"',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import { type DataSource } from 'typeorm';
|
||||
|
||||
import { BackfillMetadataOverridesSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-19/2-19-instance-command-slow-1820000110000-backfill-metadata-overrides';
|
||||
|
||||
describe('BackfillMetadataOverridesSlowInstanceCommand', () => {
|
||||
let command: BackfillMetadataOverridesSlowInstanceCommand;
|
||||
|
||||
beforeEach(() => {
|
||||
command = new BackfillMetadataOverridesSlowInstanceCommand();
|
||||
});
|
||||
|
||||
describe('runDataMigration', () => {
|
||||
it('copies standardOverrides into overrides for both tables', async () => {
|
||||
const query = jest.fn().mockResolvedValue([{ count: 3 }]);
|
||||
const dataSource = { query } as unknown as DataSource;
|
||||
|
||||
await command.runDataMigration(dataSource);
|
||||
|
||||
const statements = query.mock.calls.map((call) => call[0] as string);
|
||||
|
||||
for (const table of ['objectMetadata', 'fieldMetadata']) {
|
||||
expect(statements).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.stringContaining(
|
||||
`UPDATE "core"."${table}" SET "overrides" = "standardOverrides"`,
|
||||
),
|
||||
]),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('aborts when the isActive row count changes', async () => {
|
||||
const query = jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce([{ count: 5 }]) // objectMetadata before
|
||||
.mockResolvedValueOnce(undefined) // UPDATE backfill
|
||||
.mockResolvedValueOnce([{ count: 4 }]); // objectMetadata after
|
||||
const dataSource = { query } as unknown as DataSource;
|
||||
|
||||
await expect(command.runDataMigration(dataSource)).rejects.toThrow(
|
||||
/"isActive" changed on "core"\."objectMetadata"/,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// Referenced by @WasIntroducedInUpgrade on the "overrides" column so pre-2.19
|
||||
// upgrade steps don't SELECT it before this command adds it.
|
||||
export const ADD_METADATA_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME =
|
||||
'2.19.0_AddMetadataOverridesColumnFastInstanceCommand_1820000100000';
|
||||
@@ -0,0 +1,134 @@
|
||||
# 2.20 — pending metadata-override cleanup
|
||||
|
||||
The `standardOverrides` column on `objectMetadata` and `fieldMetadata` is a
|
||||
**deferred drop**. As of 2.19 the column is:
|
||||
|
||||
- superseded by `overrides` (backfilled by
|
||||
`../2-19/2-19-instance-command-slow-1820000110000-backfill-metadata-overrides.ts`),
|
||||
- excluded from the flat-entity / registry via the `WasRemovedInUpgrade<T>` type
|
||||
wrapper on both entities,
|
||||
- still physically present in the database, so a 2.18 → 2.19 rolling deploy never
|
||||
breaks the previous release's pods, which still `SELECT "standardOverrides"`.
|
||||
|
||||
The physical `DROP COLUMN` is intentionally deferred to the next release (2.20):
|
||||
by the time it runs, every pod is on a release that reads/writes `overrides`
|
||||
only. This folder holds the ready-to-wire drop command so whoever opens the 2.20
|
||||
upgrade work can drop it in.
|
||||
|
||||
## Why it isn't wired up yet
|
||||
|
||||
`2.20.0` is not in `TWENTY_CROSS_UPGRADE_SUPPORTED_VERSIONS` (currently
|
||||
`[...TWENTY_PREVIOUS_VERSIONS, TWENTY_CURRENT_VERSION]`, and current is 2.19.0).
|
||||
Registering a 2.20 instance command — or pointing the entity's
|
||||
`@WasRemovedInUpgrade` decorator at a 2.20 command name — would fail boot
|
||||
validation (`unknown-step-name`) until 2.20 is the current version. So the
|
||||
command lives here as documentation, not as compiled code.
|
||||
|
||||
## Activation checklist (when 2.20 becomes the current version)
|
||||
|
||||
1. Create `2-20-instance-command-fast-<timestamp>-drop-metadata-standard-overrides-column.ts`
|
||||
in this folder with the command below. Regenerate `<timestamp>` (the value
|
||||
here is a placeholder) so ordering against other 2.20 commands is correct.
|
||||
2. Register it in
|
||||
`../instance-commands.constant.ts` (import + add to the array).
|
||||
3. Add the drop decorator to `standardOverrides` on **both**
|
||||
`object-metadata.entity.ts` and `field-metadata.entity.ts`:
|
||||
`@WasRemovedInUpgrade({ upgradeCommandName: DROP_METADATA_STANDARD_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME })`,
|
||||
and add the matching constant
|
||||
(`'2.20.0_DropMetadataStandardOverridesColumnFastInstanceCommand_<timestamp>'`),
|
||||
mirroring how `isCustom` pairs its decorator with a command name.
|
||||
4. Add the test below under `2-20/__tests__/`.
|
||||
5. Run `database:migrate:generate --name pending-migration-check` and confirm no
|
||||
drift, then run the integration suite.
|
||||
|
||||
## Command
|
||||
|
||||
```ts
|
||||
import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
// Phase 2 of unifying the metadata override mechanisms. Drops the legacy
|
||||
// "standardOverrides" column now that every pod runs a release which reads and
|
||||
// writes "overrides" (backfilled in 2.19). Runs only once the instance reaches
|
||||
// 2.20, so the 2.19 rolling deploy keeps the column available for older pods.
|
||||
// down() restores the column and copies the blob back from "overrides".
|
||||
const TABLES = ['objectMetadata', 'fieldMetadata'] as const;
|
||||
|
||||
@RegisteredInstanceCommand('2.20.0', 1825000000000)
|
||||
export class DropMetadataStandardOverridesColumnFastInstanceCommand
|
||||
implements FastInstanceCommand
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
for (const table of TABLES) {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."${table}" DROP COLUMN IF EXISTS "standardOverrides"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
for (const table of TABLES) {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."${table}" ADD COLUMN IF NOT EXISTS "standardOverrides" jsonb`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "core"."${table}" SET "standardOverrides" = "overrides"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```ts
|
||||
import { type QueryRunner } from 'typeorm';
|
||||
|
||||
import { DropMetadataStandardOverridesColumnFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1825000000000-drop-metadata-standard-overrides-column';
|
||||
|
||||
describe('DropMetadataStandardOverridesColumnFastInstanceCommand', () => {
|
||||
let command: DropMetadataStandardOverridesColumnFastInstanceCommand;
|
||||
|
||||
beforeEach(() => {
|
||||
command = new DropMetadataStandardOverridesColumnFastInstanceCommand();
|
||||
});
|
||||
|
||||
describe('up', () => {
|
||||
it('drops standardOverrides from both tables', async () => {
|
||||
const query = jest.fn().mockResolvedValue(undefined);
|
||||
const queryRunner = { query } as unknown as QueryRunner;
|
||||
|
||||
await command.up(queryRunner);
|
||||
|
||||
const statements = query.mock.calls.map((call) => call[0] as string);
|
||||
|
||||
expect(statements).toEqual([
|
||||
'ALTER TABLE "core"."objectMetadata" DROP COLUMN IF EXISTS "standardOverrides"',
|
||||
'ALTER TABLE "core"."fieldMetadata" DROP COLUMN IF EXISTS "standardOverrides"',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('down', () => {
|
||||
it('recreates and backfills standardOverrides from overrides', async () => {
|
||||
const query = jest.fn().mockResolvedValue(undefined);
|
||||
const queryRunner = { query } as unknown as QueryRunner;
|
||||
|
||||
await command.down(queryRunner);
|
||||
|
||||
const statements = query.mock.calls.map((call) => call[0] as string);
|
||||
|
||||
for (const table of ['objectMetadata', 'fieldMetadata']) {
|
||||
expect(statements).toEqual(
|
||||
expect.arrayContaining([
|
||||
`ALTER TABLE "core"."${table}" ADD COLUMN IF NOT EXISTS "standardOverrides" jsonb`,
|
||||
`UPDATE "core"."${table}" SET "standardOverrides" = "overrides"`,
|
||||
]),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
+4
@@ -41,6 +41,8 @@ import { CreateDpaAgreementCoreTableFastInstanceCommand } from 'src/database/com
|
||||
import { CreateApplicationTranslationCoreTableFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-17/2-17-instance-command-fast-1801000100000-create-application-translation-core-table';
|
||||
import { AddTsVectorFieldMetadataIdToSearchFieldMetadataFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-18/2-18-instance-command-fast-1810000001000-add-ts-vector-field-metadata-id-to-search-field-metadata';
|
||||
import { BackfillTsVectorFieldMetadataIdOnSearchFieldMetadataSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-18/2-18-instance-command-slow-1810000003000-backfill-ts-vector-field-metadata-id-on-search-field-metadata';
|
||||
import { AddMetadataOverridesColumnFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-19/2-19-instance-command-fast-1820000100000-add-metadata-overrides-column';
|
||||
import { BackfillMetadataOverridesSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-19/2-19-instance-command-slow-1820000110000-backfill-metadata-overrides';
|
||||
import { AddCacheTokensToAgentChatThreadFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777455269302-add-cache-tokens-to-agent-chat-thread';
|
||||
import { AddLogoToApplicationFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777539664664-add-logo-to-application';
|
||||
import { AddSubFieldNameToViewSortEarlyFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-fast-1747234200000-add-sub-field-name-to-view-sort';
|
||||
@@ -170,4 +172,6 @@ export const INSTANCE_COMMANDS = [
|
||||
CreateApplicationTranslationCoreTableFastInstanceCommand,
|
||||
AddTsVectorFieldMetadataIdToSearchFieldMetadataFastInstanceCommand,
|
||||
BackfillTsVectorFieldMetadataIdOnSearchFieldMetadataSlowInstanceCommand,
|
||||
AddMetadataOverridesColumnFastInstanceCommand,
|
||||
BackfillMetadataOverridesSlowInstanceCommand,
|
||||
];
|
||||
|
||||
+1
-1
@@ -163,7 +163,7 @@ export const mockPersonFlatObjectMetadata = (
|
||||
updatedAt: new Date().toISOString(),
|
||||
shortcut: null,
|
||||
description: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
isUIEditable: true,
|
||||
isUICreatable: true,
|
||||
applicationUniversalIdentifier: 'test-application-id',
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ export const fromFieldManifestToUniversalFlatFieldMetadata = ({
|
||||
label: fieldManifest.label,
|
||||
description: fieldManifest.description ?? null,
|
||||
icon: fieldManifest.icon ?? null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: fieldManifest.options ?? null,
|
||||
defaultValue,
|
||||
universalSettings: fieldManifest.universalSettings ?? null,
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ export const fromObjectManifestToUniversalFlatObjectMetadata = ({
|
||||
color: null,
|
||||
description: objectManifest.description ?? null,
|
||||
icon: objectManifest.icon ?? null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ const mockObjectMetadata: FlatObjectMetadata = {
|
||||
updatedAt: new Date().toISOString(),
|
||||
shortcut: null,
|
||||
description: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
isUIEditable: true,
|
||||
isUICreatable: true,
|
||||
labelIdentifierFieldMetadataId: null,
|
||||
|
||||
@@ -714,7 +714,7 @@ export class SearchService {
|
||||
recordId: record.id,
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
objectLabelSingular:
|
||||
objectMetadataItem.standardOverrides?.labelSingular ??
|
||||
objectMetadataItem.overrides?.labelSingular ??
|
||||
objectMetadataItem.labelSingular,
|
||||
label: this.getLabelIdentifierValue(
|
||||
record,
|
||||
|
||||
@@ -13,11 +13,11 @@ import { type FlatApplicationCacheMaps } from 'src/engine/core-modules/applicati
|
||||
import { I18nService } from 'src/engine/core-modules/i18n/i18n.service';
|
||||
import { type IDataloaders } from 'src/engine/dataloaders/dataloader.interface';
|
||||
import { filterMorphRelationDuplicateFields } from 'src/engine/dataloaders/utils/filter-morph-relation-duplicate-fields.util';
|
||||
import { FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/field-metadata/constants/field-metadata-standard-overrides-properties.constant';
|
||||
import { type FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||
import { RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/relation.dto';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { resolveFieldMetadataStandardOverride } from 'src/engine/metadata-modules/field-metadata/utils/resolve-field-metadata-standard-override.util';
|
||||
import { ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-overridable-properties-by-metadata-name.constant';
|
||||
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
@@ -419,23 +419,24 @@ export class DataloaderService {
|
||||
)
|
||||
: undefined;
|
||||
|
||||
return FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES.reduce(
|
||||
const overrides = flatFieldMetadata.overrides ?? undefined;
|
||||
const i18nContext = {
|
||||
locale,
|
||||
i18nInstance,
|
||||
isStandardApp: belongsToTwentyStandardApp(flatFieldMetadata),
|
||||
applicationCatalog,
|
||||
};
|
||||
|
||||
return ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.fieldMetadata.reduce(
|
||||
(acc, property) => ({
|
||||
...acc,
|
||||
[property]: resolveFieldMetadataStandardOverride(
|
||||
{
|
||||
label: flatFieldMetadata.label,
|
||||
description: flatFieldMetadata.description ?? undefined,
|
||||
icon: flatFieldMetadata.icon ?? undefined,
|
||||
standardOverrides:
|
||||
flatFieldMetadata.standardOverrides ?? undefined,
|
||||
},
|
||||
[property]: resolveEffectiveEntityProperty({
|
||||
metadataName: 'fieldMetadata',
|
||||
baseValue: flatFieldMetadata[property],
|
||||
overrides,
|
||||
property,
|
||||
locale,
|
||||
i18nInstance,
|
||||
belongsToTwentyStandardApp(flatFieldMetadata),
|
||||
applicationCatalog,
|
||||
),
|
||||
i18nContext,
|
||||
}),
|
||||
}),
|
||||
flatFieldMetadata,
|
||||
);
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ const mockObjectMetadata = {
|
||||
labelSingular: 'Person',
|
||||
description: 'A person',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: undefined,
|
||||
overrides: undefined,
|
||||
} as unknown as ObjectMetadataDTO;
|
||||
|
||||
const baseCommandMenuItem = {
|
||||
|
||||
+23
-25
@@ -1,15 +1,15 @@
|
||||
import { type I18n } from '@lingui/core';
|
||||
import { type APP_LOCALES } from 'twenty-shared/translations';
|
||||
|
||||
import { type ObjectStandardOverridesDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-standard-overrides.dto';
|
||||
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
|
||||
import { type ObjectMetadataOverrides } from 'src/engine/metadata-modules/object-metadata/types/object-metadata-overrides.type';
|
||||
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
|
||||
|
||||
export type NavigationInterpolationObjectMetadata = {
|
||||
labelPlural: string;
|
||||
labelSingular: string;
|
||||
description?: string | null;
|
||||
icon?: string | null;
|
||||
standardOverrides?: ObjectStandardOverridesDTO | null;
|
||||
overrides?: ObjectMetadataOverrides | null;
|
||||
};
|
||||
|
||||
export const buildNavigationInterpolationContext = ({
|
||||
@@ -25,31 +25,29 @@ export const buildNavigationInterpolationContext = ({
|
||||
i18nInstance: I18n;
|
||||
applicationCatalog?: Record<string, string>;
|
||||
}): Record<string, unknown> => {
|
||||
const overrideInput = {
|
||||
labelPlural: objectMetadata.labelPlural,
|
||||
labelSingular: objectMetadata.labelSingular,
|
||||
description: objectMetadata.description ?? undefined,
|
||||
icon: objectMetadata.icon ?? undefined,
|
||||
standardOverrides: objectMetadata.standardOverrides ?? undefined,
|
||||
const overrides = objectMetadata.overrides ?? undefined;
|
||||
const i18nContext = {
|
||||
locale,
|
||||
i18nInstance,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
};
|
||||
|
||||
const resolvedLabelPlural = resolveObjectMetadataStandardOverride(
|
||||
overrideInput,
|
||||
'labelPlural',
|
||||
locale,
|
||||
i18nInstance,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
);
|
||||
const resolvedLabelPlural = resolveEffectiveEntityProperty({
|
||||
metadataName: 'objectMetadata',
|
||||
baseValue: objectMetadata.labelPlural,
|
||||
overrides,
|
||||
property: 'labelPlural',
|
||||
i18nContext,
|
||||
});
|
||||
|
||||
const resolvedIcon = resolveObjectMetadataStandardOverride(
|
||||
overrideInput,
|
||||
'icon',
|
||||
locale,
|
||||
i18nInstance,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
);
|
||||
const resolvedIcon = resolveEffectiveEntityProperty({
|
||||
metadataName: 'objectMetadata',
|
||||
baseValue: objectMetadata.icon,
|
||||
overrides,
|
||||
property: 'icon',
|
||||
i18nContext,
|
||||
});
|
||||
|
||||
return {
|
||||
navigateToObjectMetadataItem: {
|
||||
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
import { type MetadataUniversalFlatEntityPropertiesToCompare } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-compare.type';
|
||||
|
||||
export const FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES = [
|
||||
'label',
|
||||
'description',
|
||||
'icon',
|
||||
] as const satisfies MetadataUniversalFlatEntityPropertiesToCompare<'fieldMetadata'>[];
|
||||
+1
-1
@@ -15,7 +15,7 @@ export class CreateFieldInput extends OmitType(
|
||||
'id',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'standardOverrides',
|
||||
'overrides',
|
||||
'applicationId',
|
||||
'morphId',
|
||||
'universalIdentifier',
|
||||
|
||||
+3
-4
@@ -32,7 +32,7 @@ import {
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { IsValidMetadataName } from 'src/engine/decorators/metadata/is-valid-metadata-name.decorator';
|
||||
import { FieldStandardOverridesDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-standard-overrides.dto';
|
||||
import { type FieldMetadataOverrides } from 'src/engine/metadata-modules/field-metadata/types/field-metadata-overrides.type';
|
||||
import { type FieldMetadataDefaultOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||
import { ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
import { transformEnumValue } from 'src/engine/utils/transform-enum-value';
|
||||
@@ -94,9 +94,8 @@ export class FieldMetadataDTO<T extends FieldMetadataType = FieldMetadataType> {
|
||||
@Field({ nullable: true })
|
||||
icon?: string;
|
||||
|
||||
@IsOptional()
|
||||
@Field(() => FieldStandardOverridesDTO, { nullable: true })
|
||||
standardOverrides?: FieldStandardOverridesDTO;
|
||||
@HideField()
|
||||
overrides?: FieldMetadataOverrides | null;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { IsJSON, IsOptional, IsString } from 'class-validator';
|
||||
import { GraphQLJSON } from 'graphql-type-json';
|
||||
import { type APP_LOCALES } from 'twenty-shared/translations';
|
||||
|
||||
import { FieldMetadataStandardOverridesProperties } from 'src/engine/metadata-modules/field-metadata/types/field-metadata-standard-overrides-properties.type';
|
||||
|
||||
@ObjectType('StandardOverrides')
|
||||
export class FieldStandardOverridesDTO implements Partial<
|
||||
Record<FieldMetadataStandardOverridesProperties, unknown>
|
||||
> {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field(() => String, { nullable: true })
|
||||
label?: string | null;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field(() => String, { nullable: true })
|
||||
description?: string | null;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field(() => String, { nullable: true })
|
||||
icon?: string | null;
|
||||
|
||||
@IsJSON()
|
||||
@IsOptional()
|
||||
@Field(() => GraphQLJSON, {
|
||||
nullable: true,
|
||||
})
|
||||
translations?: Partial<
|
||||
Record<
|
||||
keyof typeof APP_LOCALES,
|
||||
{
|
||||
label?: string | null;
|
||||
description?: string | null;
|
||||
}
|
||||
>
|
||||
> | null;
|
||||
}
|
||||
+1
-1
@@ -27,7 +27,7 @@ export class UpdateFieldInput extends OmitType(
|
||||
'type',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'standardOverrides',
|
||||
'overrides',
|
||||
'applicationId',
|
||||
'morphId',
|
||||
] as const,
|
||||
|
||||
+13
-2
@@ -23,8 +23,9 @@ import {
|
||||
import { WasIntroducedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-introduced-in-upgrade.decorator';
|
||||
import { WasRemovedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-removed-in-upgrade.decorator';
|
||||
import { ADD_IS_SYSTEM_SIDE_EFFECT_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-15/is-system-side-effect-upgrade-command-name.constant';
|
||||
import { ADD_METADATA_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-19/add-metadata-overrides-column-upgrade-command-name.constant';
|
||||
import { RENAME_IS_UI_READ_ONLY_TO_IS_UI_EDITABLE_UPGRADE_COMMAND_NAME } from 'src/engine/metadata-modules/object-metadata/constants/rename-is-ui-read-only-to-is-ui-editable-upgrade-command-name.constant';
|
||||
import { type FieldStandardOverridesDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-standard-overrides.dto';
|
||||
import { type FieldMetadataOverrides } from 'src/engine/metadata-modules/field-metadata/types/field-metadata-overrides.type';
|
||||
import { AssignIfIsGivenFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/assign-if-is-given-field-metadata-type.type';
|
||||
import { AssignTypeIfIsMorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/assign-type-if-is-morph-or-relation-field-metadata-type.type';
|
||||
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||
@@ -102,8 +103,18 @@ export class FieldMetadataEntity<
|
||||
@Column({ nullable: true, type: 'varchar' })
|
||||
icon: string | null;
|
||||
|
||||
@WasIntroducedInUpgrade({
|
||||
upgradeCommandName: ADD_METADATA_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME,
|
||||
})
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
standardOverrides: JsonbProperty<FieldStandardOverridesDTO> | null;
|
||||
overrides: JsonbProperty<FieldMetadataOverrides> | null;
|
||||
|
||||
/**
|
||||
* @deprecated Superseded by `overrides`; kept readable for pods on the
|
||||
* previous release during a rolling deploy. Drop deferred to 2-20/README.md.
|
||||
*/
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
standardOverrides: WasRemovedInUpgrade<JsonbProperty<FieldMetadataOverrides> | null>;
|
||||
|
||||
@Column('jsonb', { nullable: true })
|
||||
options: JsonbProperty<FieldMetadataOptions<TFieldMetadataType>>;
|
||||
|
||||
+17
-13
@@ -22,16 +22,16 @@ import { RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/rel
|
||||
import { UpdateOneFieldMetadataInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
|
||||
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service';
|
||||
import { fieldMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/field-metadata/utils/field-metadata-graphql-api-exception-handler.util';
|
||||
import { resolveFieldMetadataStandardOverride } from 'src/engine/metadata-modules/field-metadata/utils/resolve-field-metadata-standard-override.util';
|
||||
import { fromFlatFieldMetadataToFieldMetadataDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-flat-field-metadata-to-field-metadata-dto.util';
|
||||
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
|
||||
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
||||
|
||||
// Keep @Parent() structurally typed so ResolverValidationPipe does not validate
|
||||
// FieldMetadataDTO date decorators on already-loaded parent records.
|
||||
type FieldMetadataStandardOverrideParent = Parameters<
|
||||
typeof resolveFieldMetadataStandardOverride
|
||||
>[0] &
|
||||
Pick<FieldMetadataDTO, 'applicationId'>;
|
||||
type FieldMetadataStandardOverrideParent = Pick<
|
||||
FieldMetadataDTO,
|
||||
'label' | 'description' | 'icon' | 'overrides' | 'applicationId'
|
||||
>;
|
||||
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@UsePipes(ResolverValidationPipe)
|
||||
@@ -76,14 +76,18 @@ export class FieldMetadataResolver {
|
||||
locale: context.req.locale,
|
||||
});
|
||||
|
||||
return resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
labelKey,
|
||||
context.req.locale,
|
||||
i18n,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
);
|
||||
return resolveEffectiveEntityProperty({
|
||||
metadataName: 'fieldMetadata',
|
||||
baseValue: fieldMetadata[labelKey],
|
||||
overrides: fieldMetadata.overrides,
|
||||
property: labelKey,
|
||||
i18nContext: {
|
||||
locale: context.req.locale,
|
||||
i18nInstance: i18n,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ResolveField(() => String, { nullable: true })
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { type APP_LOCALES } from 'twenty-shared/translations';
|
||||
|
||||
export type FieldMetadataOverrides = {
|
||||
label?: string | null;
|
||||
description?: string | null;
|
||||
icon?: string | null;
|
||||
translations?: Partial<
|
||||
Record<
|
||||
keyof typeof APP_LOCALES,
|
||||
{
|
||||
label?: string | null;
|
||||
description?: string | null;
|
||||
}
|
||||
>
|
||||
> | null;
|
||||
};
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
import { type FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/field-metadata/constants/field-metadata-standard-overrides-properties.constant';
|
||||
|
||||
export type FieldMetadataStandardOverridesProperties =
|
||||
(typeof FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES)[number];
|
||||
-511
@@ -1,511 +0,0 @@
|
||||
import { type I18n } from '@lingui/core';
|
||||
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
|
||||
import { generateMessageId } from 'src/engine/core-modules/i18n/utils/generateMessageId';
|
||||
import { resolveFieldMetadataStandardOverride } from 'src/engine/metadata-modules/field-metadata/utils/resolve-field-metadata-standard-override.util';
|
||||
|
||||
jest.mock('src/engine/core-modules/i18n/utils/generateMessageId');
|
||||
|
||||
const mockGenerateMessageId = generateMessageId as jest.MockedFunction<
|
||||
typeof generateMessageId
|
||||
>;
|
||||
|
||||
describe('resolveFieldMetadataStandardOverride', () => {
|
||||
let mockI18n: jest.Mocked<I18n>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockI18n = {
|
||||
_: jest.fn(),
|
||||
} as unknown as jest.Mocked<I18n>;
|
||||
});
|
||||
|
||||
describe('Custom fields', () => {
|
||||
it('should return the field value for custom label field', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Custom Label',
|
||||
description: 'Custom Description',
|
||||
icon: 'custom-icon',
|
||||
isCustom: true,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Custom Label');
|
||||
});
|
||||
|
||||
it('should never translate a custom label even when it matches a standard catalog entry', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Status',
|
||||
description: 'Custom Description',
|
||||
icon: 'custom-icon',
|
||||
isCustom: true,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('status.message.id');
|
||||
mockI18n._.mockReturnValue('Statut');
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Status');
|
||||
expect(mockGenerateMessageId).not.toHaveBeenCalled();
|
||||
expect(mockI18n._).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return the field value for custom description field', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Custom Label',
|
||||
description: 'Custom Description',
|
||||
icon: 'custom-icon',
|
||||
isCustom: true,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'description',
|
||||
undefined,
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Custom Description');
|
||||
});
|
||||
|
||||
it('should return the field value for custom icon field', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Custom Label',
|
||||
description: 'Custom Description',
|
||||
icon: 'custom-icon',
|
||||
isCustom: true,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'icon',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('custom-icon');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Standard fields - Icon overrides', () => {
|
||||
it('should return override icon when available for standard field', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
icon: 'override-icon',
|
||||
},
|
||||
};
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'icon',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('override-icon');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Standard fields - Translation overrides', () => {
|
||||
it('should return translation override when available for non-icon fields', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
translations: {
|
||||
'fr-FR': {
|
||||
label: 'Libellé traduit',
|
||||
description: 'Description traduite',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
),
|
||||
).toBe('Libellé traduit');
|
||||
expect(
|
||||
resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'description',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
),
|
||||
).toBe('Description traduite');
|
||||
});
|
||||
|
||||
it('should fallback when translation override is not available for the locale', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
translations: {
|
||||
'es-ES': {
|
||||
label: 'Etiqueta en español',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('generated-message-id');
|
||||
mockI18n._.mockReturnValue('generated-message-id');
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Standard Label');
|
||||
});
|
||||
|
||||
it('should fallback when translation override is not available for the labelKey', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
translations: {
|
||||
'fr-FR': {
|
||||
label: 'Libellé traduit',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('generated-message-id');
|
||||
mockI18n._.mockReturnValue('generated-message-id');
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'description',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Standard Description');
|
||||
});
|
||||
|
||||
it('should not use translation overrides when locale is undefined', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
translations: {
|
||||
'fr-FR': {
|
||||
label: 'Libellé traduit',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('generated-message-id');
|
||||
mockI18n._.mockReturnValue('generated-message-id');
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
undefined,
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Standard Label');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Standard fields - SOURCE_LOCALE overrides', () => {
|
||||
it('should return direct override for SOURCE_LOCALE when available', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
label: 'Overridden Label',
|
||||
description: 'Overridden Description',
|
||||
icon: 'overridden-icon',
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
),
|
||||
).toBe('Overridden Label');
|
||||
expect(
|
||||
resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'description',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
),
|
||||
).toBe('Overridden Description');
|
||||
expect(
|
||||
resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'icon',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
),
|
||||
).toBe('overridden-icon');
|
||||
});
|
||||
|
||||
it('should use direct override for non-SOURCE_LOCALE when translation override is missing', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
label: 'Overridden Label',
|
||||
},
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('generated-message-id');
|
||||
mockI18n._.mockReturnValue('generated-message-id');
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Overridden Label');
|
||||
expect(mockGenerateMessageId).not.toHaveBeenCalled();
|
||||
expect(mockI18n._).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not use empty string override for SOURCE_LOCALE', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
label: '',
|
||||
},
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('generated-message-id');
|
||||
mockI18n._.mockReturnValue('generated-message-id');
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Standard Label');
|
||||
});
|
||||
|
||||
it('should not use undefined override for SOURCE_LOCALE', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
label: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('generated-message-id');
|
||||
mockI18n._.mockReturnValue('generated-message-id');
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Standard Label');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Standard fields - Auto translation fallback', () => {
|
||||
it('should return translated message when translation is available', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('standard.label.message.id');
|
||||
mockI18n._.mockReturnValue('Libellé traduit automatiquement');
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(mockGenerateMessageId).toHaveBeenCalledWith('Standard Label');
|
||||
expect(mockI18n._).toHaveBeenCalledWith('standard.label.message.id');
|
||||
expect(result).toBe('Libellé traduit automatiquement');
|
||||
});
|
||||
|
||||
it('should return original field value when no translation is found', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
const messageId = 'standard.label.message.id';
|
||||
|
||||
mockGenerateMessageId.mockReturnValue(messageId);
|
||||
mockI18n._.mockReturnValue(messageId);
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Standard Label');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Priority order - Standard fields', () => {
|
||||
it('should prioritize translation override over SOURCE_LOCALE override for non-SOURCE_LOCALE', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
label: 'Source Override',
|
||||
translations: {
|
||||
'fr-FR': {
|
||||
label: 'Translation Override',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Translation Override');
|
||||
expect(mockGenerateMessageId).not.toHaveBeenCalled();
|
||||
expect(mockI18n._).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should prioritize SOURCE_LOCALE override over auto translation for SOURCE_LOCALE', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
label: 'Source Override',
|
||||
},
|
||||
};
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Source Override');
|
||||
expect(mockGenerateMessageId).not.toHaveBeenCalled();
|
||||
expect(mockI18n._).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should use auto translation when no overrides are available', () => {
|
||||
const fieldMetadata = {
|
||||
label: 'Standard Label',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {},
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('auto.translation.id');
|
||||
mockI18n._.mockReturnValue('Auto Translated Label');
|
||||
|
||||
const result = resolveFieldMetadataStandardOverride(
|
||||
fieldMetadata,
|
||||
'label',
|
||||
'de-DE',
|
||||
mockI18n,
|
||||
!fieldMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Auto Translated Label');
|
||||
expect(mockGenerateMessageId).toHaveBeenCalledWith('Standard Label');
|
||||
expect(mockI18n._).toHaveBeenCalledWith('auto.translation.id');
|
||||
});
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -17,7 +17,7 @@ export const fromFieldMetadataEntityToFieldMetadataDto = (
|
||||
label: entity.label,
|
||||
description: entity.description ?? undefined,
|
||||
icon: entity.icon ?? undefined,
|
||||
standardOverrides: entity.standardOverrides ?? undefined,
|
||||
overrides: entity.overrides ?? undefined,
|
||||
isActive: entity.isActive,
|
||||
isSystem: entity.isSystem,
|
||||
isUIEditable: entity.isUIEditable,
|
||||
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
import { type I18n } from '@lingui/core';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { type APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { translateStandardLabel } from 'src/engine/core-modules/i18n/utils/translate-standard-label.util';
|
||||
import { type FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||
|
||||
export const resolveFieldMetadataStandardOverride = (
|
||||
fieldMetadata: Pick<
|
||||
FieldMetadataDTO,
|
||||
'label' | 'description' | 'icon' | 'standardOverrides'
|
||||
>,
|
||||
labelKey: 'label' | 'description' | 'icon',
|
||||
locale: keyof typeof APP_LOCALES | undefined,
|
||||
i18nInstance: I18n,
|
||||
isStandardApp: boolean,
|
||||
applicationCatalog?: Record<string, string>,
|
||||
): string => {
|
||||
const safeLocale = locale ?? SOURCE_LOCALE;
|
||||
|
||||
if (!isStandardApp && !isDefined(applicationCatalog)) {
|
||||
return fieldMetadata[labelKey] ?? '';
|
||||
}
|
||||
|
||||
if (labelKey === 'icon' && isDefined(fieldMetadata.standardOverrides?.icon)) {
|
||||
return fieldMetadata.standardOverrides.icon;
|
||||
}
|
||||
|
||||
if (
|
||||
isDefined(fieldMetadata.standardOverrides?.translations) &&
|
||||
labelKey !== 'icon'
|
||||
) {
|
||||
const translationValue =
|
||||
fieldMetadata.standardOverrides.translations[safeLocale]?.[labelKey];
|
||||
|
||||
if (isDefined(translationValue)) {
|
||||
return translationValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNonEmptyString(fieldMetadata.standardOverrides?.[labelKey])) {
|
||||
return fieldMetadata.standardOverrides[labelKey] ?? '';
|
||||
}
|
||||
|
||||
return translateStandardLabel({
|
||||
sourceValue: fieldMetadata[labelKey] ?? '',
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
i18nInstance,
|
||||
});
|
||||
};
|
||||
+4
-4
@@ -72,7 +72,7 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
|
||||
"label",
|
||||
"name",
|
||||
"options",
|
||||
"standardOverrides",
|
||||
"overrides",
|
||||
"universalSettings",
|
||||
"isUIEditable",
|
||||
"isNullable",
|
||||
@@ -80,7 +80,7 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
|
||||
"propertiesToStringify": [
|
||||
"defaultValue",
|
||||
"options",
|
||||
"standardOverrides",
|
||||
"overrides",
|
||||
"universalSettings",
|
||||
],
|
||||
},
|
||||
@@ -171,14 +171,14 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
|
||||
"namePlural",
|
||||
"nameSingular",
|
||||
"labelIdentifierFieldMetadataUniversalIdentifier",
|
||||
"standardOverrides",
|
||||
"overrides",
|
||||
"isUIEditable",
|
||||
"isUICreatable",
|
||||
"isSearchable",
|
||||
"imageIdentifierFieldMetadataUniversalIdentifier",
|
||||
],
|
||||
"propertiesToStringify": [
|
||||
"standardOverrides",
|
||||
"overrides",
|
||||
],
|
||||
},
|
||||
"objectPermission": {
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`registry-derived override property maps derives the overridable properties for every metadata entity 1`] = `
|
||||
{
|
||||
"agent": [],
|
||||
"applicationVariable": [],
|
||||
"commandMenuItem": [
|
||||
"label",
|
||||
"icon",
|
||||
"shortLabel",
|
||||
"position",
|
||||
"isPinned",
|
||||
"availabilityType",
|
||||
"availabilityObjectMetadataId",
|
||||
"engineComponentKey",
|
||||
"hotKeys",
|
||||
"pageLayoutId",
|
||||
],
|
||||
"connectionProvider": [],
|
||||
"fieldMetadata": [
|
||||
"description",
|
||||
"icon",
|
||||
"label",
|
||||
],
|
||||
"fieldPermission": [],
|
||||
"frontComponent": [],
|
||||
"index": [],
|
||||
"logicFunction": [],
|
||||
"navigationMenuItem": [],
|
||||
"objectMetadata": [
|
||||
"color",
|
||||
"description",
|
||||
"icon",
|
||||
"labelPlural",
|
||||
"labelSingular",
|
||||
],
|
||||
"objectPermission": [],
|
||||
"pageLayout": [],
|
||||
"pageLayoutTab": [
|
||||
"title",
|
||||
"position",
|
||||
"icon",
|
||||
],
|
||||
"pageLayoutWidget": [
|
||||
"title",
|
||||
"position",
|
||||
"pageLayoutTabId",
|
||||
"conditionalDisplay",
|
||||
"conditionalAvailabilityExpression",
|
||||
],
|
||||
"permissionFlag": [],
|
||||
"role": [],
|
||||
"rolePermissionFlag": [],
|
||||
"roleTarget": [],
|
||||
"rowLevelPermissionPredicate": [],
|
||||
"rowLevelPermissionPredicateGroup": [],
|
||||
"searchFieldMetadata": [],
|
||||
"skill": [],
|
||||
"view": [
|
||||
"name",
|
||||
"type",
|
||||
"icon",
|
||||
"position",
|
||||
"isCompact",
|
||||
"openRecordIn",
|
||||
"kanbanAggregateOperation",
|
||||
"kanbanAggregateOperationFieldMetadataId",
|
||||
"anyFieldFilterValue",
|
||||
"calendarLayout",
|
||||
"calendarFieldMetadataId",
|
||||
"visibility",
|
||||
"mainGroupByFieldMetadataId",
|
||||
"shouldHideEmptyGroups",
|
||||
"kanbanColumnWidth",
|
||||
],
|
||||
"viewField": [
|
||||
"isVisible",
|
||||
"size",
|
||||
"position",
|
||||
"aggregateOperation",
|
||||
"viewFieldGroupId",
|
||||
],
|
||||
"viewFieldGroup": [
|
||||
"name",
|
||||
"position",
|
||||
"isVisible",
|
||||
],
|
||||
"viewFilter": [],
|
||||
"viewFilterGroup": [],
|
||||
"viewGroup": [],
|
||||
"viewSort": [],
|
||||
"webhook": [],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`registry-derived override property maps derives the translatable properties for every metadata entity 1`] = `
|
||||
{
|
||||
"agent": [],
|
||||
"applicationVariable": [],
|
||||
"commandMenuItem": [],
|
||||
"connectionProvider": [],
|
||||
"fieldMetadata": [
|
||||
"description",
|
||||
"label",
|
||||
],
|
||||
"fieldPermission": [],
|
||||
"frontComponent": [],
|
||||
"index": [],
|
||||
"logicFunction": [],
|
||||
"navigationMenuItem": [],
|
||||
"objectMetadata": [
|
||||
"description",
|
||||
"labelPlural",
|
||||
"labelSingular",
|
||||
],
|
||||
"objectPermission": [],
|
||||
"pageLayout": [],
|
||||
"pageLayoutTab": [],
|
||||
"pageLayoutWidget": [],
|
||||
"permissionFlag": [],
|
||||
"role": [],
|
||||
"rolePermissionFlag": [],
|
||||
"roleTarget": [],
|
||||
"rowLevelPermissionPredicate": [],
|
||||
"rowLevelPermissionPredicateGroup": [],
|
||||
"searchFieldMetadata": [],
|
||||
"skill": [],
|
||||
"view": [],
|
||||
"viewField": [],
|
||||
"viewFieldGroup": [],
|
||||
"viewFilter": [],
|
||||
"viewFilterGroup": [],
|
||||
"viewGroup": [],
|
||||
"viewSort": [],
|
||||
"webhook": [],
|
||||
}
|
||||
`;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-overridable-properties-by-metadata-name.constant';
|
||||
import { ALL_TRANSLATABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-translatable-properties-by-metadata-name.constant';
|
||||
|
||||
describe('registry-derived override property maps', () => {
|
||||
it('derives the overridable properties for every metadata entity', () => {
|
||||
expect(ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('derives the translatable properties for every metadata entity', () => {
|
||||
expect(ALL_TRANSLATABLE_PROPERTIES_BY_METADATA_NAME).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
+35
-4
@@ -40,6 +40,7 @@ type MetadataEntityPropertyConfiguration<
|
||||
: boolean;
|
||||
toCompare: boolean;
|
||||
isOverridable?: boolean;
|
||||
translatable?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -59,8 +60,15 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
translatable: true,
|
||||
},
|
||||
icon: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
icon: { toCompare: true, toStringify: false, universalProperty: undefined },
|
||||
isActive: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
@@ -86,6 +94,8 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
translatable: true,
|
||||
},
|
||||
name: { toCompare: true, toStringify: false, universalProperty: undefined },
|
||||
options: {
|
||||
@@ -93,7 +103,7 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
toStringify: true,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
standardOverrides: {
|
||||
overrides: {
|
||||
toCompare: true,
|
||||
toStringify: true,
|
||||
universalProperty: undefined,
|
||||
@@ -164,13 +174,21 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
description: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
translatable: true,
|
||||
},
|
||||
icon: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
icon: { toCompare: true, toStringify: false, universalProperty: undefined },
|
||||
isActive: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
@@ -185,11 +203,15 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
translatable: true,
|
||||
},
|
||||
labelSingular: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
translatable: true,
|
||||
},
|
||||
namePlural: {
|
||||
toCompare: true,
|
||||
@@ -207,7 +229,7 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
// @ts-expect-error remove once https://github.com/twentyhq/core-team-issues/issues/2172 has been resolved
|
||||
universalProperty: 'labelIdentifierFieldMetadataUniversalIdentifier',
|
||||
},
|
||||
standardOverrides: {
|
||||
overrides: {
|
||||
toCompare: true,
|
||||
toStringify: true,
|
||||
universalProperty: undefined,
|
||||
@@ -1830,3 +1852,12 @@ export type MetadataEntityOverridablePropertyName<T extends AllMetadataName> =
|
||||
FilterOverridableKeys<
|
||||
(typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T]
|
||||
>;
|
||||
|
||||
type FilterTranslatableKeys<TConfig> = {
|
||||
[P in keyof TConfig]: TConfig[P] extends { translatable: true } ? P : never;
|
||||
}[keyof TConfig];
|
||||
|
||||
export type MetadataEntityTranslatablePropertyName<T extends AllMetadataName> =
|
||||
FilterTranslatableKeys<
|
||||
(typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T]
|
||||
>;
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
ALL_METADATA_NAME,
|
||||
type AllMetadataName,
|
||||
} from 'twenty-shared/metadata';
|
||||
|
||||
import {
|
||||
ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME,
|
||||
type MetadataEntityTranslatablePropertyName,
|
||||
} from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
|
||||
|
||||
const computeTranslatableProperties = <T extends AllMetadataName>(
|
||||
metadataName: T,
|
||||
): MetadataEntityTranslatablePropertyName<T>[] => {
|
||||
const config =
|
||||
ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME[metadataName];
|
||||
|
||||
return (Object.entries(config) as [string, { translatable?: boolean }][])
|
||||
.filter(([_, conf]) => conf.translatable === true)
|
||||
.map(([property]) => property as MetadataEntityTranslatablePropertyName<T>);
|
||||
};
|
||||
|
||||
export const ALL_TRANSLATABLE_PROPERTIES_BY_METADATA_NAME = Object.values(
|
||||
ALL_METADATA_NAME,
|
||||
).reduce(
|
||||
(acc, metadataName) => ({
|
||||
...acc,
|
||||
[metadataName]: computeTranslatableProperties(metadataName),
|
||||
}),
|
||||
{} as {
|
||||
[P in AllMetadataName]: MetadataEntityTranslatablePropertyName<P>[];
|
||||
},
|
||||
);
|
||||
+2
-2
@@ -17,7 +17,7 @@ type Assertions = [
|
||||
| 'description'
|
||||
| 'isActive'
|
||||
| 'defaultValue'
|
||||
| 'standardOverrides'
|
||||
| 'overrides'
|
||||
| 'options'
|
||||
| 'settings'
|
||||
| 'isUnique'
|
||||
@@ -43,7 +43,7 @@ type Assertions = [
|
||||
| 'color'
|
||||
| 'description'
|
||||
| 'isActive'
|
||||
| 'standardOverrides'
|
||||
| 'overrides'
|
||||
| 'isLabelSyncedWithName'
|
||||
| 'nameSingular'
|
||||
| 'namePlural'
|
||||
|
||||
+16
-16
@@ -19,7 +19,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: 'Attachment name',
|
||||
icon: 'IconFileUpload',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -41,7 +41,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: 'Attachment full path',
|
||||
icon: 'IconLink',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -63,7 +63,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: 'Attachment type',
|
||||
icon: 'IconList',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -85,7 +85,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -107,7 +107,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -129,7 +129,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -151,7 +151,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Date when the record was deleted',
|
||||
icon: 'IconCalendarMinus',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -173,7 +173,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachment author',
|
||||
icon: 'IconCircleUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -199,7 +199,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachment task',
|
||||
icon: 'IconNotes',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -225,7 +225,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachment note',
|
||||
icon: 'IconNotes',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -251,7 +251,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachment person',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -277,7 +277,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachment company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -303,7 +303,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachment opportunity',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -329,7 +329,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachments Rocket',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -355,7 +355,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachments Pet',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -381,7 +381,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachments Survey result',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
|
||||
+23
-23
@@ -19,7 +19,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: 'The company name',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -46,7 +46,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
description:
|
||||
'The company website URL. We use this url to fetch the company icon',
|
||||
icon: 'IconLink',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -72,7 +72,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: 'The company Linkedin account',
|
||||
icon: 'IconBrandLinkedin',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -94,7 +94,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: { amountMicros: null, currencyCode: "''" },
|
||||
description: "The company's total annual revenue",
|
||||
icon: 'IconMoneybag',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -125,7 +125,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: 'Address of the company',
|
||||
icon: 'IconMap',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -147,7 +147,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 0,
|
||||
description: 'Company record position',
|
||||
icon: 'IconHierarchy2',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -173,7 +173,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: 'The creator of the record',
|
||||
icon: 'IconCreativeCommonsSa',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -195,7 +195,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Field used for full-text search',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -217,7 +217,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -239,7 +239,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -261,7 +261,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -283,7 +283,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Date when the record was deleted',
|
||||
icon: 'IconCalendarMinus',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -305,7 +305,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'People linked to the company.',
|
||||
icon: 'IconUsers',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -328,7 +328,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
description:
|
||||
'Your team member responsible for managing the company account',
|
||||
icon: 'IconUserCircle',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -354,7 +354,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Tasks tied to the company',
|
||||
icon: 'IconCheckbox',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -376,7 +376,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Notes tied to the company',
|
||||
icon: 'IconNotes',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -398,7 +398,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Opportunities linked to the company.',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -420,7 +420,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachments linked to the company',
|
||||
icon: 'IconFileImport',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -442,7 +442,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Timeline Activities linked to the company',
|
||||
icon: 'IconTimelineEvent',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -464,7 +464,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: null,
|
||||
icon: 'IconAdCircle',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -490,7 +490,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: null,
|
||||
icon: 'IconVideo',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -512,7 +512,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: null,
|
||||
icon: 'IconHome',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: [
|
||||
{
|
||||
id: '325240d6-8c67-4fb2-bb02-51e9de4cd6be',
|
||||
@@ -556,7 +556,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: false,
|
||||
description: null,
|
||||
icon: 'IconBrandVisa',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
|
||||
+2
-2
@@ -40,7 +40,7 @@ export const getFlatFieldMetadataMock = <T extends FieldMetadataType>(
|
||||
isUIEditable: true,
|
||||
isLabelSyncedWithName: false,
|
||||
isSystem: false,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
workspaceId: faker.string.uuid(),
|
||||
applicationId: faker.string.uuid(),
|
||||
relationTargetFieldMetadataId: null,
|
||||
@@ -68,7 +68,7 @@ export const getStandardFlatFieldMetadataMock = (
|
||||
overrides: Omit<FlatFieldMetadataOverrides, 'isCustom' | 'isSystem'>,
|
||||
) => {
|
||||
return getFlatFieldMetadataMock({
|
||||
standardOverrides: {},
|
||||
overrides: {},
|
||||
isSystem: true,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ export const getRelationTargetFlatFieldMetadataMock = ({
|
||||
isUIEditable: true,
|
||||
isLabelSyncedWithName: false,
|
||||
isSystem: false,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
workspaceId: faker.string.uuid(),
|
||||
objectMetadataId,
|
||||
type,
|
||||
|
||||
+12
-12
@@ -15,7 +15,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 0,
|
||||
description: 'Note record position',
|
||||
icon: 'IconHierarchy2',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -37,7 +37,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: 'Note title',
|
||||
icon: 'IconNotes',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -59,7 +59,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Note body',
|
||||
icon: 'IconFilePencil',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -85,7 +85,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: 'The creator of the record',
|
||||
icon: 'IconCreativeCommonsSa',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -107,7 +107,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Field used for full-text search',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -129,7 +129,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -151,7 +151,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -173,7 +173,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -195,7 +195,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Date when the record was deleted',
|
||||
icon: 'IconCalendarMinus',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -217,7 +217,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Note targets',
|
||||
icon: 'IconArrowUpRight',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -239,7 +239,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Note attachments',
|
||||
icon: 'IconFileImport',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -261,7 +261,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Timeline Activities linked to the note.',
|
||||
icon: 'IconTimelineEvent',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
|
||||
+11
-11
@@ -19,7 +19,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -41,7 +41,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -63,7 +63,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -85,7 +85,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Date when the record was deleted',
|
||||
icon: 'IconCalendarMinus',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -107,7 +107,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'NoteTarget note',
|
||||
icon: 'IconNotes',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -133,7 +133,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'NoteTarget person',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -159,7 +159,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'NoteTarget company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -185,7 +185,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'NoteTarget opportunity',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -211,7 +211,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'NoteTargets Rocket',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -237,7 +237,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'NoteTargets Pet',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -263,7 +263,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'NoteTargets Survey result',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
|
||||
+17
-17
@@ -19,7 +19,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: 'The opportunity name',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -41,7 +41,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: { amountMicros: null, currencyCode: "''" },
|
||||
description: 'Opportunity amount',
|
||||
icon: 'IconCurrencyDollar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -63,7 +63,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Opportunity close date',
|
||||
icon: 'IconCalendarEvent',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -85,7 +85,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "'NEW'",
|
||||
description: 'Opportunity stage',
|
||||
icon: 'IconProgressCheck',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: [
|
||||
{
|
||||
id: 'fdf5cede-2b0e-44d0-adbe-3bc7e613efc9',
|
||||
@@ -143,7 +143,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 0,
|
||||
description: 'Opportunity record position',
|
||||
icon: 'IconHierarchy2',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -169,7 +169,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: 'The creator of the record',
|
||||
icon: 'IconCreativeCommonsSa',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -191,7 +191,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Field used for full-text search',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -213,7 +213,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -235,7 +235,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -257,7 +257,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -279,7 +279,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Date when the record was deleted',
|
||||
icon: 'IconCalendarMinus',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -301,7 +301,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Opportunity point of contact',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -327,7 +327,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Opportunity company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -353,7 +353,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Tasks tied to the opportunity',
|
||||
icon: 'IconCheckbox',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -375,7 +375,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Notes tied to the opportunity',
|
||||
icon: 'IconNotes',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -397,7 +397,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachments linked to the opportunity',
|
||||
icon: 'IconFileImport',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -419,7 +419,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Timeline Activities linked to the opportunity.',
|
||||
icon: 'IconTimelineEvent',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
|
||||
+25
-25
@@ -19,7 +19,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: { lastName: "''", firstName: "''" },
|
||||
description: 'Contact’s name',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -41,7 +41,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: { primaryEmail: "''", additionalEmails: null },
|
||||
description: 'Contact’s Emails',
|
||||
icon: 'IconMail',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -67,7 +67,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: 'Contact’s Linkedin account',
|
||||
icon: 'IconBrandLinkedin',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -89,7 +89,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: 'Contact’s job title',
|
||||
icon: 'IconBriefcase',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -116,7 +116,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: 'Contact’s phone numbers',
|
||||
icon: 'IconPhone',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -138,7 +138,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: 'Contact’s avatar',
|
||||
icon: 'IconFileUpload',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -160,7 +160,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 0,
|
||||
description: 'Person record Position',
|
||||
icon: 'IconHierarchy2',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -186,7 +186,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: 'The creator of the record',
|
||||
icon: 'IconCreativeCommonsSa',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -208,7 +208,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Field used for full-text search',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -230,7 +230,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -252,7 +252,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -274,7 +274,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -296,7 +296,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Date when the record was deleted',
|
||||
icon: 'IconCalendarMinus',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -318,7 +318,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Contact’s company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -345,7 +345,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
description:
|
||||
'List of opportunities for which that person is the point of contact',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -367,7 +367,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Tasks tied to the contact',
|
||||
icon: 'IconCheckbox',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -389,7 +389,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Notes tied to the contact',
|
||||
icon: 'IconNotes',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -411,7 +411,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachments linked to the contact.',
|
||||
icon: 'IconFileImport',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -433,7 +433,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Message Participants',
|
||||
icon: 'IconUserCircle',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -455,7 +455,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Calendar Event Participants',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -477,7 +477,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Events linked to the person',
|
||||
icon: 'IconTimelineEvent',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -499,7 +499,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: null,
|
||||
icon: 'IconNote',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -526,7 +526,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: null,
|
||||
icon: 'IconBrandWhatsapp',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -548,7 +548,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: null,
|
||||
icon: 'IconHome',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: [
|
||||
{
|
||||
id: 'c33d02fd-0bd9-4769-bd32-036d9ff645a5',
|
||||
@@ -592,7 +592,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: null,
|
||||
icon: 'IconStars',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: [
|
||||
{
|
||||
id: 'e6b4b1bb-c707-41cb-8f30-418a81ae3299',
|
||||
|
||||
+28
-28
@@ -15,7 +15,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'NoteTargets tied to the Pet',
|
||||
icon: 'IconCheckbox',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -37,7 +37,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -59,7 +59,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "'Untitled'",
|
||||
description: 'Name',
|
||||
icon: 'IconAbc',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -81,7 +81,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -103,7 +103,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -125,7 +125,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Deletion date',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -147,7 +147,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: { name: "''", source: "'MANUAL'" },
|
||||
description: 'The creator of the record',
|
||||
icon: 'IconCreativeCommonsSa',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -169,7 +169,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 0,
|
||||
description: 'Position',
|
||||
icon: 'IconHierarchy2',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -191,7 +191,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TimelineActivities tied to the Pet',
|
||||
icon: 'IconTimelineEvent',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -213,7 +213,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachments tied to the Pet',
|
||||
icon: 'IconFileImport',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -235,7 +235,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TaskTargets tied to the Pet',
|
||||
icon: 'IconCheckbox',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -257,7 +257,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Field used for full-text search',
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: false,
|
||||
@@ -279,7 +279,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: [
|
||||
{
|
||||
id: '23a75463-ca50-4b33-bfd8-701e2944c76c',
|
||||
@@ -344,7 +344,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: [
|
||||
{
|
||||
id: '5bb37088-b364-44c5-a984-54b01aa241ce',
|
||||
@@ -409,7 +409,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -431,7 +431,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -462,7 +462,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -489,7 +489,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -511,7 +511,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: { primaryEmail: "''", additionalEmails: null },
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -533,7 +533,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -555,7 +555,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -581,7 +581,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -603,7 +603,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: { amountMicros: null, currencyCode: "''" },
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -625,7 +625,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: { lastName: "''", firstName: "''" },
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -647,7 +647,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: [
|
||||
{
|
||||
id: '4a3ed94d-30db-4231-8c2e-5893d55df860',
|
||||
@@ -700,7 +700,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -722,7 +722,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -744,7 +744,7 @@ export const PET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: null,
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
|
||||
+12
-12
@@ -15,7 +15,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -37,7 +37,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "'Untitled'",
|
||||
description: 'Name',
|
||||
icon: 'IconAbc',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -59,7 +59,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -81,7 +81,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -103,7 +103,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Deletion date',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -125,7 +125,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: { name: "''", source: "'MANUAL'" },
|
||||
description: 'The creator of the record',
|
||||
icon: 'IconCreativeCommonsSa',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -147,7 +147,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 0,
|
||||
description: 'Position',
|
||||
icon: 'IconHierarchy2',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -169,7 +169,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TimelineActivities tied to the Rocket',
|
||||
icon: 'IconTimelineEvent',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -191,7 +191,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Attachments tied to the Rocket',
|
||||
icon: 'IconFileImport',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -213,7 +213,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'NoteTargets tied to the Rocket',
|
||||
icon: 'IconCheckbox',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -235,7 +235,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TaskTargets tied to the Rocket',
|
||||
icon: 'IconCheckbox',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -257,7 +257,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Field used for full-text search',
|
||||
icon: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: false,
|
||||
|
||||
+15
-15
@@ -19,7 +19,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 0,
|
||||
description: 'Task record position',
|
||||
icon: 'IconHierarchy2',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -41,7 +41,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: 'Task title',
|
||||
icon: 'IconNotes',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -63,7 +63,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Task body',
|
||||
icon: 'IconFilePencil',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -85,7 +85,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Task due date',
|
||||
icon: 'IconCalendarEvent',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -107,7 +107,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "'TODO'",
|
||||
description: 'Task status',
|
||||
icon: 'IconCheck',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: [
|
||||
{
|
||||
id: '178a8a6f-6411-4731-a417-36e85e5526a3',
|
||||
@@ -155,7 +155,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
},
|
||||
description: 'The creator of the record',
|
||||
icon: 'IconCreativeCommonsSa',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -177,7 +177,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Field used for full-text search',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -199,7 +199,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -221,7 +221,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -243,7 +243,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -265,7 +265,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Date when the record was deleted',
|
||||
icon: 'IconCalendarMinus',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -287,7 +287,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Task targets',
|
||||
icon: 'IconArrowUpRight',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -309,7 +309,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Task attachments',
|
||||
icon: 'IconFileImport',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
@@ -331,7 +331,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Task assignee',
|
||||
icon: 'IconUserCircle',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -357,7 +357,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Timeline Activities linked to the task.',
|
||||
icon: 'IconTimelineEvent',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isActive: true,
|
||||
|
||||
+11
-11
@@ -19,7 +19,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -41,7 +41,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -63,7 +63,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -85,7 +85,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Date when the record was deleted',
|
||||
icon: 'IconCalendarMinus',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -107,7 +107,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TaskTarget task',
|
||||
icon: 'IconCheckbox',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -133,7 +133,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TaskTarget person',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -159,7 +159,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TaskTarget company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -185,7 +185,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TaskTarget opportunity',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -211,7 +211,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TaskTargets Rocket',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -237,7 +237,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TaskTargets Pet',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -263,7 +263,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TaskTargets Survey result',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
|
||||
+22
-22
@@ -19,7 +19,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -41,7 +41,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: 'Event name',
|
||||
icon: 'IconAbc',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -63,7 +63,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Json value for event details',
|
||||
icon: 'IconListDetails',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -85,7 +85,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: "''",
|
||||
description: 'Cached record name',
|
||||
icon: 'IconAbc',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -107,7 +107,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Linked Record id',
|
||||
icon: 'IconAbc',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -129,7 +129,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Linked Object Metadata Id',
|
||||
icon: 'IconAbc',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -151,7 +151,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isActive: true,
|
||||
@@ -173,7 +173,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -195,7 +195,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -217,7 +217,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Date when the record was deleted',
|
||||
icon: 'IconCalendarMinus',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isActive: true,
|
||||
@@ -239,7 +239,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Event workspace member',
|
||||
icon: 'IconCircleUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -265,7 +265,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Event person',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -291,7 +291,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Event company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -317,7 +317,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Event opportunity',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -343,7 +343,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Event note',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -369,7 +369,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Event task',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
@@ -395,7 +395,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Event workflow',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -421,7 +421,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Event workflow version',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -447,7 +447,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'Event workflow run',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -473,7 +473,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TimelineActivities Rocket',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -499,7 +499,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TimelineActivities Pet',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
@@ -525,7 +525,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
|
||||
defaultValue: null,
|
||||
description: 'TimelineActivities Survey result',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
|
||||
+4
-4
@@ -137,10 +137,10 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"name": "newFieldPets",
|
||||
"objectMetadataUniversalIdentifier": Any<String>,
|
||||
"options": null,
|
||||
"overrides": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
|
||||
"searchFieldMetadataUniversalIdentifiers": [],
|
||||
"standardOverrides": null,
|
||||
"type": "MORPH_RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
"universalSettings": {
|
||||
@@ -173,10 +173,10 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"name": "pet",
|
||||
"objectMetadataUniversalIdentifier": Any<String>,
|
||||
"options": null,
|
||||
"overrides": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
|
||||
"searchFieldMetadataUniversalIdentifiers": [],
|
||||
"standardOverrides": null,
|
||||
"type": "RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
"universalSettings": {
|
||||
@@ -211,10 +211,10 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"name": "newFieldCompanies",
|
||||
"objectMetadataUniversalIdentifier": Any<String>,
|
||||
"options": null,
|
||||
"overrides": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
|
||||
"searchFieldMetadataUniversalIdentifiers": [],
|
||||
"standardOverrides": null,
|
||||
"type": "MORPH_RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
"universalSettings": {
|
||||
@@ -247,10 +247,10 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"name": "company",
|
||||
"objectMetadataUniversalIdentifier": Any<String>,
|
||||
"options": null,
|
||||
"overrides": null,
|
||||
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
|
||||
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
|
||||
"searchFieldMetadataUniversalIdentifiers": [],
|
||||
"standardOverrides": null,
|
||||
"type": "RELATION",
|
||||
"universalIdentifier": Any<String>,
|
||||
"universalSettings": {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ export const FLAT_FIELD_METADATA_RELATION_PROPERTIES_TO_COMPARE = [
|
||||
'label',
|
||||
'description',
|
||||
'isActive',
|
||||
'standardOverrides',
|
||||
'overrides',
|
||||
'icon',
|
||||
'name',
|
||||
'universalSettings',
|
||||
|
||||
+3
-3
@@ -44,7 +44,7 @@ export const computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate = ({
|
||||
flatObjectMetadata,
|
||||
isSystemBuild,
|
||||
}: ComputeFlatFieldToUpdateAndRelatedFlatFieldToUpdateArgs): ComputeFlatFieldToUpdateAndRelatedFlatFieldToUpdateReturnType => {
|
||||
const { standardOverrides, updatedEditableFieldProperties } =
|
||||
const { overrides, updatedEditableFieldProperties } =
|
||||
sanitizeRawUpdateFieldInput({
|
||||
existingFlatFieldMetadata: fromFlatFieldMetadata,
|
||||
rawUpdateFieldInput,
|
||||
@@ -62,7 +62,7 @@ export const computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate = ({
|
||||
],
|
||||
update: updatedEditableFieldProperties,
|
||||
}),
|
||||
standardOverrides,
|
||||
overrides,
|
||||
};
|
||||
|
||||
if (updatedEditableFieldProperties.settings !== undefined) {
|
||||
@@ -161,7 +161,7 @@ export const computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate = ({
|
||||
properties: relatedMorphPropertiesToUpdateTo,
|
||||
update: updatedEditableFieldProperties,
|
||||
}),
|
||||
standardOverrides,
|
||||
overrides,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ export const fromFlatFieldMetadataToFieldMetadataDto = (
|
||||
updatedAt,
|
||||
description,
|
||||
icon,
|
||||
standardOverrides,
|
||||
overrides,
|
||||
isNullable,
|
||||
isUnique,
|
||||
settings,
|
||||
@@ -49,7 +49,7 @@ export const fromFlatFieldMetadataToFieldMetadataDto = (
|
||||
updatedAt: new Date(updatedAt),
|
||||
description: description ?? undefined,
|
||||
icon: icon ?? undefined,
|
||||
standardOverrides: standardOverrides ?? undefined,
|
||||
overrides: overrides ?? undefined,
|
||||
isNullable: isNullable ?? false,
|
||||
isUnique: isUnique ?? false,
|
||||
settings: settings ?? undefined,
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ export const getDefaultFlatFieldMetadata = ({
|
||||
isUnique: createFieldInput.isUnique ?? false,
|
||||
label: createFieldInput.label,
|
||||
name: createFieldInput.name,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
type: createFieldInput.type,
|
||||
universalIdentifier: createFieldInput.universalIdentifier ?? v4(),
|
||||
options: createFieldInput.options ?? null,
|
||||
|
||||
+10
-10
@@ -4,12 +4,12 @@ import {
|
||||
} from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/field-metadata/constants/field-metadata-standard-overrides-properties.constant';
|
||||
import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
|
||||
import {
|
||||
FieldMetadataException,
|
||||
FieldMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-overridable-properties-by-metadata-name.constant';
|
||||
import { FLAT_FIELD_METADATA_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-editable-properties.constant';
|
||||
import { type FlatFieldMetadataEditableProperties } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-editable-properties.constant';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
@@ -62,7 +62,7 @@ export const sanitizeRawUpdateFieldInput = ({
|
||||
if (!isStandardField || isSystemBuild) {
|
||||
return {
|
||||
updatedEditableFieldProperties,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -82,16 +82,16 @@ export const sanitizeRawUpdateFieldInput = ({
|
||||
);
|
||||
}
|
||||
|
||||
const { overrides: standardOverrides, remainingProperties } =
|
||||
computeMetadataOverridesBlob({
|
||||
overridableProperties: FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES,
|
||||
updatedProperties: updatedEditableFieldProperties,
|
||||
existingEntity: existingFlatFieldMetadata,
|
||||
existingOverrides: existingFlatFieldMetadata.standardOverrides,
|
||||
});
|
||||
const { overrides, remainingProperties } = computeMetadataOverridesBlob({
|
||||
overridableProperties:
|
||||
ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.fieldMetadata,
|
||||
updatedProperties: updatedEditableFieldProperties,
|
||||
existingEntity: existingFlatFieldMetadata,
|
||||
existingOverrides: existingFlatFieldMetadata.overrides,
|
||||
});
|
||||
|
||||
return {
|
||||
standardOverrides,
|
||||
overrides,
|
||||
updatedEditableFieldProperties: remainingProperties,
|
||||
};
|
||||
};
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export const ATTACHMENT_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
labelPlural: 'Attachments',
|
||||
description: 'An attachment',
|
||||
icon: 'IconFileImport',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export const COMPANY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
labelPlural: 'Companies',
|
||||
description: 'A company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
|
||||
+2
-2
@@ -44,7 +44,7 @@ export const getFlatObjectMetadataMock = (
|
||||
nameSingular: 'defaultflatObjectMetadataNameSingular',
|
||||
shortcut: 'shortcut',
|
||||
applicationId,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: '',
|
||||
workspaceId: faker.string.uuid(),
|
||||
createdAt,
|
||||
@@ -69,7 +69,7 @@ export const getStandardFlatObjectMetadataMock = (
|
||||
overrides: Omit<FlatObjectMetadataOverrides, 'isCustom' | 'isSystem'>,
|
||||
) => {
|
||||
return getFlatObjectMetadataMock({
|
||||
standardOverrides: {},
|
||||
overrides: {},
|
||||
isSystem: true,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export const NOTE_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
labelPlural: 'Notes',
|
||||
description: 'A note',
|
||||
icon: 'IconNotes',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export const NOTE_TARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
labelPlural: 'Note Targets',
|
||||
description: 'A note target',
|
||||
icon: 'IconCheckbox',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export const OPPORTUNITY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
labelPlural: 'Opportunities',
|
||||
description: 'An opportunity',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export const PERSON_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
labelPlural: 'People',
|
||||
description: 'A person',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ export const PET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
labelPlural: 'Pets',
|
||||
description: null,
|
||||
icon: 'IconCat',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ export const ROCKET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
labelPlural: 'Rockets',
|
||||
description: 'A rocket',
|
||||
icon: 'IconRocket',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export const TASK_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
labelPlural: 'Tasks',
|
||||
description: 'A task',
|
||||
icon: 'IconCheckbox',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export const TASK_TARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
labelPlural: 'Task Targets',
|
||||
description: 'A task target',
|
||||
icon: 'IconCheckbox',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export const TIMELINE_ACTIVITY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
labelPlural: 'Timeline Activities',
|
||||
description: 'Aggregated / filtered event to be displayed on the timeline',
|
||||
icon: 'IconTimelineEvent',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
|
||||
namePlural: createObjectInput.namePlural,
|
||||
nameSingular: createObjectInput.nameSingular,
|
||||
shortcut: createObjectInput.shortcut ?? null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
applicationUniversalIdentifier: flatApplication.universalIdentifier,
|
||||
fieldUniversalIdentifiers: [],
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ export const fromFlatObjectMetadataToObjectMetadataDto = (
|
||||
color,
|
||||
description,
|
||||
icon,
|
||||
standardOverrides,
|
||||
overrides,
|
||||
shortcut,
|
||||
duplicateCriteria,
|
||||
id,
|
||||
@@ -55,7 +55,7 @@ export const fromFlatObjectMetadataToObjectMetadataDto = (
|
||||
color: color ?? undefined,
|
||||
description: description ?? undefined,
|
||||
icon: icon ?? undefined,
|
||||
standardOverrides: standardOverrides ?? undefined,
|
||||
overrides: overrides ?? undefined,
|
||||
shortcut: shortcut ?? undefined,
|
||||
duplicateCriteria: duplicateCriteria ?? undefined,
|
||||
applicationId,
|
||||
|
||||
+2
-2
@@ -66,7 +66,7 @@ export const fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities =
|
||||
const isStandardObject = belongsToTwentyStandardApp(
|
||||
existingFlatObjectMetadata,
|
||||
);
|
||||
const { standardOverrides, updatedEditableObjectProperties } =
|
||||
const { overrides, updatedEditableObjectProperties } =
|
||||
sanitizeRawUpdateObjectInput({
|
||||
existingFlatObjectMetadata,
|
||||
rawUpdateObjectInput,
|
||||
@@ -81,7 +81,7 @@ export const fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities =
|
||||
],
|
||||
update: updatedEditableObjectProperties,
|
||||
}),
|
||||
standardOverrides,
|
||||
overrides,
|
||||
};
|
||||
|
||||
if (
|
||||
|
||||
+11
-12
@@ -1,14 +1,13 @@
|
||||
import { extractAndSanitizeObjectStringFields } from 'twenty-shared/utils';
|
||||
|
||||
import { ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-overridable-properties-by-metadata-name.constant';
|
||||
import { FLAT_OBJECT_METADATA_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-object-metadata/constants/flat-object-metadata-editable-properties.constant';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/object-metadata/constants/object-metadata-standard-overrides-properties.constant';
|
||||
import { type UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
|
||||
import {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import { type ObjectMetadataStandardOverridesProperties } from 'src/engine/metadata-modules/object-metadata/types/object-metadata-standard-overrides-properties.types';
|
||||
import { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.util';
|
||||
import { computeMetadataOverridesBlob } from 'src/engine/metadata-modules/utils/compute-metadata-overrides-blob.util';
|
||||
|
||||
@@ -37,7 +36,7 @@ export const sanitizeRawUpdateObjectInput = ({
|
||||
if (!isStandardObject) {
|
||||
return {
|
||||
updatedEditableObjectProperties,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +45,7 @@ export const sanitizeRawUpdateObjectInput = ({
|
||||
).filter(
|
||||
(property) =>
|
||||
!FLAT_OBJECT_METADATA_EDITABLE_PROPERTIES.standard.includes(
|
||||
property as ObjectMetadataStandardOverridesProperties,
|
||||
property as (typeof FLAT_OBJECT_METADATA_EDITABLE_PROPERTIES.standard)[number],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -57,16 +56,16 @@ export const sanitizeRawUpdateObjectInput = ({
|
||||
);
|
||||
}
|
||||
|
||||
const { overrides: standardOverrides, remainingProperties } =
|
||||
computeMetadataOverridesBlob({
|
||||
overridableProperties: OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES,
|
||||
updatedProperties: updatedEditableObjectProperties,
|
||||
existingEntity: existingFlatObjectMetadata,
|
||||
existingOverrides: existingFlatObjectMetadata.standardOverrides,
|
||||
});
|
||||
const { overrides, remainingProperties } = computeMetadataOverridesBlob({
|
||||
overridableProperties:
|
||||
ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.objectMetadata,
|
||||
updatedProperties: updatedEditableObjectProperties,
|
||||
existingEntity: existingFlatObjectMetadata,
|
||||
existingOverrides: existingFlatObjectMetadata.overrides,
|
||||
});
|
||||
|
||||
return {
|
||||
standardOverrides,
|
||||
overrides,
|
||||
updatedEditableObjectProperties: remainingProperties,
|
||||
};
|
||||
};
|
||||
|
||||
+20
-22
@@ -15,8 +15,8 @@ import { type CollectionHashDTO } from 'src/engine/metadata-modules/minimal-meta
|
||||
import { MinimalMetadataDTO } from 'src/engine/metadata-modules/minimal-metadata/dtos/minimal-metadata.dto';
|
||||
import { MinimalObjectMetadataDTO } from 'src/engine/metadata-modules/minimal-metadata/dtos/minimal-object-metadata.dto';
|
||||
import { MinimalViewDTO } from 'src/engine/metadata-modules/minimal-metadata/dtos/minimal-view.dto';
|
||||
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
|
||||
import { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.util';
|
||||
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { type WorkspaceCacheKeyName } from 'src/engine/workspace-cache/types/workspace-cache-key.type';
|
||||
|
||||
@@ -80,33 +80,31 @@ export class MinimalMetadataService {
|
||||
.map((flatObjectMetadata) => {
|
||||
const isStandardApp = belongsToTwentyStandardApp(flatObjectMetadata);
|
||||
|
||||
const objectMetadataForOverride = {
|
||||
labelPlural: flatObjectMetadata.labelPlural,
|
||||
labelSingular: flatObjectMetadata.labelSingular,
|
||||
description: flatObjectMetadata.description ?? undefined,
|
||||
icon: flatObjectMetadata.icon ?? undefined,
|
||||
color: flatObjectMetadata.color ?? undefined,
|
||||
standardOverrides: flatObjectMetadata.standardOverrides ?? undefined,
|
||||
const overrides = flatObjectMetadata.overrides ?? undefined;
|
||||
const i18nContext = {
|
||||
locale: safeLocale,
|
||||
i18nInstance,
|
||||
isStandardApp,
|
||||
};
|
||||
|
||||
return {
|
||||
id: flatObjectMetadata.id,
|
||||
nameSingular: flatObjectMetadata.nameSingular,
|
||||
namePlural: flatObjectMetadata.namePlural,
|
||||
labelSingular: resolveObjectMetadataStandardOverride(
|
||||
objectMetadataForOverride,
|
||||
'labelSingular',
|
||||
safeLocale,
|
||||
i18nInstance,
|
||||
isStandardApp,
|
||||
),
|
||||
labelPlural: resolveObjectMetadataStandardOverride(
|
||||
objectMetadataForOverride,
|
||||
'labelPlural',
|
||||
safeLocale,
|
||||
i18nInstance,
|
||||
isStandardApp,
|
||||
),
|
||||
labelSingular: resolveEffectiveEntityProperty({
|
||||
metadataName: 'objectMetadata',
|
||||
baseValue: flatObjectMetadata.labelSingular,
|
||||
overrides,
|
||||
property: 'labelSingular',
|
||||
i18nContext,
|
||||
}),
|
||||
labelPlural: resolveEffectiveEntityProperty({
|
||||
metadataName: 'objectMetadata',
|
||||
baseValue: flatObjectMetadata.labelPlural,
|
||||
overrides,
|
||||
property: 'labelPlural',
|
||||
i18nContext,
|
||||
}),
|
||||
icon: flatObjectMetadata.icon ?? undefined,
|
||||
isActive: flatObjectMetadata.isActive,
|
||||
isSystem: flatObjectMetadata.isSystem,
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import { type MetadataUniversalFlatEntityPropertiesToCompare } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-compare.type';
|
||||
|
||||
export const OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES = [
|
||||
'color',
|
||||
'labelSingular',
|
||||
'labelPlural',
|
||||
'description',
|
||||
'icon',
|
||||
] as const satisfies MetadataUniversalFlatEntityPropertiesToCompare<'objectMetadata'>[];
|
||||
+8
-8
@@ -28,7 +28,7 @@ const PARTIAL_ID_FIELD = {
|
||||
isLabelSyncedWithName: false,
|
||||
defaultValue: 'uuid',
|
||||
options: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
morphId: null,
|
||||
universalSettings: null,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
@@ -58,7 +58,7 @@ const PARTIAL_CREATED_AT_FIELD = {
|
||||
isLabelSyncedWithName: false,
|
||||
defaultValue: 'now',
|
||||
options: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
morphId: null,
|
||||
universalSettings: null,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
@@ -88,7 +88,7 @@ const PARTIAL_UPDATED_AT_FIELD = {
|
||||
isLabelSyncedWithName: false,
|
||||
defaultValue: 'now',
|
||||
options: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
morphId: null,
|
||||
universalSettings: null,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
@@ -118,7 +118,7 @@ const PARTIAL_DELETED_AT_FIELD = {
|
||||
isLabelSyncedWithName: false,
|
||||
defaultValue: null,
|
||||
options: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
morphId: null,
|
||||
universalSettings: null,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
@@ -148,7 +148,7 @@ const PARTIAL_CREATED_BY_FIELD = {
|
||||
isLabelSyncedWithName: false,
|
||||
defaultValue: { name: "''", source: "'MANUAL'" },
|
||||
options: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
morphId: null,
|
||||
universalSettings: null,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
@@ -178,7 +178,7 @@ const PARTIAL_UPDATED_BY_FIELD = {
|
||||
isLabelSyncedWithName: false,
|
||||
defaultValue: { name: "''", source: "'MANUAL'" },
|
||||
options: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
morphId: null,
|
||||
universalSettings: null,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
@@ -208,7 +208,7 @@ const PARTIAL_POSITION_FIELD = {
|
||||
isLabelSyncedWithName: false,
|
||||
defaultValue: 0,
|
||||
options: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
morphId: null,
|
||||
universalSettings: null,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
@@ -238,7 +238,7 @@ const PARTIAL_SEARCH_VECTOR_FIELD = {
|
||||
isLabelSyncedWithName: false,
|
||||
defaultValue: null,
|
||||
options: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
morphId: null,
|
||||
// universalSettings for searchVector is computed at runtime
|
||||
// based on the name field (getTsVectorColumnExpressionFromFields)
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@ import { type WorkspaceEntityDuplicateCriteria } from 'src/engine/api/graphql/wo
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||
import { IndexMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-metadata.dto';
|
||||
import { ObjectStandardOverridesDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-standard-overrides.dto';
|
||||
import { type ObjectMetadataOverrides } from 'src/engine/metadata-modules/object-metadata/types/object-metadata-overrides.type';
|
||||
|
||||
@ObjectType('Object')
|
||||
@Authorize({
|
||||
@@ -53,8 +53,8 @@ export class ObjectMetadataDTO {
|
||||
@Field({ nullable: true })
|
||||
icon?: string;
|
||||
|
||||
@Field(() => ObjectStandardOverridesDTO, { nullable: true })
|
||||
standardOverrides?: ObjectStandardOverridesDTO;
|
||||
@HideField()
|
||||
overrides?: ObjectMetadataOverrides | null;
|
||||
|
||||
@Field({ nullable: true })
|
||||
shortcut?: string;
|
||||
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { IsJSON, IsOptional, IsString } from 'class-validator';
|
||||
import { GraphQLJSON } from 'graphql-type-json';
|
||||
import { type APP_LOCALES } from 'twenty-shared/translations';
|
||||
|
||||
@ObjectType('ObjectStandardOverrides')
|
||||
export class ObjectStandardOverridesDTO {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field(() => String, { nullable: true })
|
||||
labelSingular?: string | null;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field(() => String, { nullable: true })
|
||||
labelPlural?: string | null;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field(() => String, { nullable: true })
|
||||
description?: string | null;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field(() => String, { nullable: true })
|
||||
icon?: string | null;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field(() => String, { nullable: true })
|
||||
color?: string | null;
|
||||
|
||||
@IsJSON()
|
||||
@IsOptional()
|
||||
@Field(() => GraphQLJSON, {
|
||||
nullable: true,
|
||||
})
|
||||
translations?: Partial<
|
||||
Record<
|
||||
keyof typeof APP_LOCALES,
|
||||
{
|
||||
labelSingular?: string | null;
|
||||
labelPlural?: string | null;
|
||||
description?: string | null;
|
||||
}
|
||||
>
|
||||
> | null;
|
||||
}
|
||||
+13
-2
@@ -13,12 +13,13 @@ import { type WorkspaceEntityDuplicateCriteria } from 'src/engine/api/graphql/wo
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { SearchFieldMetadataEntity } from 'src/engine/metadata-modules/search-field-metadata/search-field-metadata.entity';
|
||||
import { type ObjectStandardOverridesDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-standard-overrides.dto';
|
||||
import { type ObjectMetadataOverrides } from 'src/engine/metadata-modules/object-metadata/types/object-metadata-overrides.type';
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
|
||||
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
import { WasIntroducedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-introduced-in-upgrade.decorator';
|
||||
import { WasRemovedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-removed-in-upgrade.decorator';
|
||||
import { ADD_METADATA_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-19/add-metadata-overrides-column-upgrade-command-name.constant';
|
||||
import { RENAME_IS_UI_READ_ONLY_TO_IS_UI_EDITABLE_UPGRADE_COMMAND_NAME } from 'src/engine/metadata-modules/object-metadata/constants/rename-is-ui-read-only-to-is-ui-editable-upgrade-command-name.constant';
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
|
||||
import { type JsonbProperty } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/jsonb-property.type';
|
||||
@@ -64,8 +65,18 @@ export class ObjectMetadataEntity
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
color: string | null;
|
||||
|
||||
@WasIntroducedInUpgrade({
|
||||
upgradeCommandName: ADD_METADATA_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME,
|
||||
})
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
standardOverrides: JsonbProperty<ObjectStandardOverridesDTO> | null;
|
||||
overrides: JsonbProperty<ObjectMetadataOverrides> | null;
|
||||
|
||||
/**
|
||||
* @deprecated Superseded by `overrides`; kept readable for pods on the
|
||||
* previous release during a rolling deploy. Drop deferred to 2-20/README.md.
|
||||
*/
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
standardOverrides: WasRemovedInUpgrade<JsonbProperty<ObjectMetadataOverrides> | null>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
|
||||
+13
-9
@@ -32,7 +32,7 @@ import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metada
|
||||
import { ObjectRecordCountService } from 'src/engine/metadata-modules/object-metadata/object-record-count.service';
|
||||
import { SearchFieldMetadataDTO } from 'src/engine/metadata-modules/search-field-metadata/dtos/search-field-metadata.dto';
|
||||
import { objectMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/object-metadata/utils/object-metadata-graphql-api-exception-handler.util';
|
||||
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
|
||||
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
|
||||
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
||||
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@@ -92,14 +92,18 @@ export class ObjectMetadataResolver {
|
||||
locale: context.req.locale,
|
||||
});
|
||||
|
||||
return resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
labelKey,
|
||||
context.req.locale,
|
||||
i18n,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
);
|
||||
return resolveEffectiveEntityProperty({
|
||||
metadataName: 'objectMetadata',
|
||||
baseValue: objectMetadata[labelKey],
|
||||
overrides: objectMetadata.overrides,
|
||||
property: labelKey,
|
||||
i18nContext: {
|
||||
locale: context.req.locale,
|
||||
i18nInstance: i18n,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ResolveField(() => String, { nullable: true })
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metada
|
||||
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
|
||||
|
||||
const OBJECT_STRIP_WHEN_NULLISH = [
|
||||
'standardOverrides',
|
||||
'overrides',
|
||||
'color',
|
||||
'duplicateCriteria',
|
||||
'shortcut',
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { type APP_LOCALES } from 'twenty-shared/translations';
|
||||
|
||||
export type ObjectMetadataOverrides = {
|
||||
labelSingular?: string | null;
|
||||
labelPlural?: string | null;
|
||||
description?: string | null;
|
||||
icon?: string | null;
|
||||
color?: string | null;
|
||||
translations?: Partial<
|
||||
Record<
|
||||
keyof typeof APP_LOCALES,
|
||||
{
|
||||
labelSingular?: string | null;
|
||||
labelPlural?: string | null;
|
||||
description?: string | null;
|
||||
}
|
||||
>
|
||||
> | null;
|
||||
};
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
import { type OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/object-metadata/constants/object-metadata-standard-overrides-properties.constant';
|
||||
|
||||
export type ObjectMetadataStandardOverridesProperties =
|
||||
(typeof OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES)[number];
|
||||
+1
-1
@@ -31,7 +31,7 @@ const makeFieldMetadata = (
|
||||
icon: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
relationTargetObjectMetadataUniversalIdentifier: null,
|
||||
createdAt: '2026-01-01T00:00:00.000Z',
|
||||
updatedAt: '2026-01-01T00:00:00.000Z',
|
||||
|
||||
-737
@@ -1,737 +0,0 @@
|
||||
import { type I18n } from '@lingui/core';
|
||||
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
|
||||
import { generateMessageId } from 'src/engine/core-modules/i18n/utils/generateMessageId';
|
||||
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
|
||||
|
||||
jest.mock('src/engine/core-modules/i18n/utils/generateMessageId');
|
||||
|
||||
const mockGenerateMessageId = generateMessageId as jest.MockedFunction<
|
||||
typeof generateMessageId
|
||||
>;
|
||||
|
||||
describe('resolveObjectMetadataStandardOverride', () => {
|
||||
let mockI18n: jest.Mocked<I18n>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockI18n = {
|
||||
_: jest.fn(),
|
||||
} as unknown as jest.Mocked<I18n>;
|
||||
});
|
||||
|
||||
describe('Custom objects', () => {
|
||||
it('should return the object value for custom labelSingular object', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'My Custom',
|
||||
labelPlural: 'My Customs',
|
||||
description: 'Custom Description',
|
||||
icon: 'custom-icon',
|
||||
color: 'blue',
|
||||
isCustom: true,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('My Custom');
|
||||
});
|
||||
|
||||
it('should never translate a custom label even when it matches a standard catalog entry', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Company',
|
||||
labelPlural: 'Companies',
|
||||
description: 'Custom Description',
|
||||
icon: 'custom-icon',
|
||||
color: 'blue',
|
||||
isCustom: true,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('company.message.id');
|
||||
mockI18n._.mockReturnValue('Entreprise');
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Company');
|
||||
expect(mockGenerateMessageId).not.toHaveBeenCalled();
|
||||
expect(mockI18n._).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return the object value for custom description object', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'My Custom',
|
||||
labelPlural: 'My Customs',
|
||||
description: 'Custom Description',
|
||||
icon: 'custom-icon',
|
||||
color: 'blue',
|
||||
isCustom: true,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'description',
|
||||
undefined,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Custom Description');
|
||||
});
|
||||
|
||||
it('should return the object value for custom icon object', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'My Custom',
|
||||
labelPlural: 'My Customs',
|
||||
description: 'Custom Description',
|
||||
icon: 'custom-icon',
|
||||
color: 'blue',
|
||||
isCustom: true,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'icon',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('custom-icon');
|
||||
});
|
||||
|
||||
it('should return the object value for custom color object', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'My Custom',
|
||||
labelPlural: 'My Customs',
|
||||
description: 'Custom Description',
|
||||
icon: 'custom-icon',
|
||||
color: 'green',
|
||||
isCustom: true,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'color',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('green');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Standard objects - Icon overrides', () => {
|
||||
it('should return override icon when available for standard object', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'My Custom',
|
||||
labelPlural: 'My Customs',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
icon: 'override-icon',
|
||||
},
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'icon',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('override-icon');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Standard objects - Color overrides', () => {
|
||||
it('should return override color when available for standard object', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Company',
|
||||
labelPlural: 'Companies',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
color: 'blue',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
color: 'red',
|
||||
},
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'color',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('red');
|
||||
});
|
||||
|
||||
it('should return base color when no override exists for standard object', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Company',
|
||||
labelPlural: 'Companies',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
color: 'blue',
|
||||
isCustom: false,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('generated-message-id');
|
||||
mockI18n._.mockReturnValue('generated-message-id');
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'color',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('blue');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Standard objects - Translation overrides', () => {
|
||||
it('should return translation override when available for non-icon objects', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
translations: {
|
||||
'fr-FR': {
|
||||
labelSingular: 'Libellé traduit',
|
||||
labelPlural: 'Libellés traduits',
|
||||
description: 'Description traduite',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
),
|
||||
).toBe('Libellé traduit');
|
||||
expect(
|
||||
resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelPlural',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
),
|
||||
).toBe('Libellés traduits');
|
||||
expect(
|
||||
resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'description',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
),
|
||||
).toBe('Description traduite');
|
||||
});
|
||||
|
||||
it('should fallback when translation override is not available for the locale', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
translations: {
|
||||
'es-ES': {
|
||||
labelSingular: 'Etiqueta en español',
|
||||
labelPlural: 'Etiquetas en español',
|
||||
description: 'Descripción en español',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('generated-message-id');
|
||||
mockI18n._.mockReturnValue('generated-message-id');
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Standard Label');
|
||||
});
|
||||
|
||||
it('should fallback when translation override is not available for the labelKey', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
translations: {
|
||||
'fr-FR': {
|
||||
labelPlural: 'Libellés traduits',
|
||||
labelSingular: 'Libellé traduit',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('generated-message-id');
|
||||
mockI18n._.mockReturnValue('generated-message-id');
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'description',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Standard Description');
|
||||
});
|
||||
|
||||
it('should not use translation overrides when locale is undefined', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
translations: {
|
||||
'fr-FR': {
|
||||
labelSingular: 'Libellé traduit',
|
||||
labelPlural: 'Libellés traduits',
|
||||
description: 'Description traduite',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('generated-message-id');
|
||||
mockI18n._.mockReturnValue('generated-message-id');
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
undefined,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Standard Label');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Standard objects - SOURCE_LOCALE overrides', () => {
|
||||
it('should return direct override for SOURCE_LOCALE when available', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
labelSingular: 'Overridden Label',
|
||||
labelPlural: 'Overridden Labels',
|
||||
description: 'Overridden Description',
|
||||
icon: 'overridden-icon',
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
),
|
||||
).toBe('Overridden Label');
|
||||
expect(
|
||||
resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelPlural',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
),
|
||||
).toBe('Overridden Labels');
|
||||
expect(
|
||||
resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'description',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
),
|
||||
).toBe('Overridden Description');
|
||||
expect(
|
||||
resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'icon',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
),
|
||||
).toBe('overridden-icon');
|
||||
});
|
||||
|
||||
it('should use direct override for non-SOURCE_LOCALE', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
labelSingular: 'Overridden Label',
|
||||
labelPlural: 'Overridden Labels',
|
||||
},
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Overridden Label');
|
||||
});
|
||||
|
||||
it('should not use undefined override for SOURCE_LOCALE', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
labelSingular: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('generated-message-id');
|
||||
mockI18n._.mockReturnValue('generated-message-id');
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Standard Label');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Standard objects - Auto translation fallback', () => {
|
||||
it('should return translated message when translation is available', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('standard.label.message.id');
|
||||
mockI18n._.mockReturnValue('Libellé traduit automatiquement');
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(mockGenerateMessageId).toHaveBeenCalledWith('Standard Label');
|
||||
expect(mockI18n._).toHaveBeenCalledWith('standard.label.message.id');
|
||||
expect(result).toBe('Libellé traduit automatiquement');
|
||||
});
|
||||
|
||||
it('should return original object value when no translation is found', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
const messageId = 'standard.label.message.id';
|
||||
|
||||
mockGenerateMessageId.mockReturnValue(messageId);
|
||||
mockI18n._.mockReturnValue(messageId);
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Standard Label');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Priority order - Standard objects', () => {
|
||||
it('should prioritize translation override over SOURCE_LOCALE override for non-SOURCE_LOCALE', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
labelSingular: 'Source Override',
|
||||
labelPlural: 'Source Overrides',
|
||||
translations: {
|
||||
'fr-FR': {
|
||||
labelSingular: 'Translation Override',
|
||||
labelPlural: 'Translation Overrides',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Translation Override');
|
||||
expect(mockGenerateMessageId).not.toHaveBeenCalled();
|
||||
expect(mockI18n._).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should prioritize SOURCE_LOCALE override over auto translation for SOURCE_LOCALE', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
labelSingular: 'Source Override',
|
||||
labelPlural: 'Source Overrides',
|
||||
},
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
SOURCE_LOCALE,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Source Override');
|
||||
expect(mockGenerateMessageId).not.toHaveBeenCalled();
|
||||
expect(mockI18n._).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should use auto translation when no overrides are available', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {},
|
||||
};
|
||||
|
||||
mockGenerateMessageId.mockReturnValue('auto.translation.id');
|
||||
mockI18n._.mockReturnValue('Auto Translated Label');
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
'de-DE',
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Auto Translated Label');
|
||||
expect(mockGenerateMessageId).toHaveBeenCalledWith('Standard Label');
|
||||
expect(mockI18n._).toHaveBeenCalledWith('auto.translation.id');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Undefined locale handling', () => {
|
||||
it('should use SOURCE_LOCALE fallback when locale is undefined for standard object', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
labelSingular: 'Source Override',
|
||||
},
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
undefined,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Source Override');
|
||||
expect(mockGenerateMessageId).not.toHaveBeenCalled();
|
||||
expect(mockI18n._).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fall back to auto translation when locale is undefined and no SOURCE_LOCALE override exists', () => {
|
||||
mockI18n._.mockReturnValue('Auto Translated Label');
|
||||
mockGenerateMessageId.mockReturnValue('auto.translation.id');
|
||||
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Standard Label',
|
||||
labelPlural: 'Standard Labels',
|
||||
description: 'Standard Description',
|
||||
icon: 'default-icon',
|
||||
isCustom: false,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
undefined,
|
||||
mockI18n,
|
||||
!objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
expect(result).toBe('Auto Translated Label');
|
||||
expect(mockGenerateMessageId).toHaveBeenCalledWith('Standard Label');
|
||||
expect(mockI18n._).toHaveBeenCalledWith('auto.translation.id');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Application objects - catalog translations', () => {
|
||||
it('should translate an application object label from its catalog', () => {
|
||||
mockGenerateMessageId.mockReturnValue('app.label.id');
|
||||
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Property',
|
||||
labelPlural: 'Properties',
|
||||
description: 'A property',
|
||||
icon: 'IconBuilding',
|
||||
isCustom: false,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
false,
|
||||
{ 'app.label.id': 'Bien' },
|
||||
);
|
||||
|
||||
expect(result).toBe('Bien');
|
||||
expect(mockI18n._).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fall back to the source value when the catalog has no entry', () => {
|
||||
mockGenerateMessageId.mockReturnValue('missing.id');
|
||||
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Property',
|
||||
labelPlural: 'Properties',
|
||||
description: 'A property',
|
||||
icon: 'IconBuilding',
|
||||
isCustom: false,
|
||||
standardOverrides: undefined,
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
false,
|
||||
{},
|
||||
);
|
||||
|
||||
expect(result).toBe('Property');
|
||||
});
|
||||
|
||||
it('should prioritize a workspace translation override over the catalog', () => {
|
||||
const objectMetadata = {
|
||||
labelSingular: 'Property',
|
||||
labelPlural: 'Properties',
|
||||
description: 'A property',
|
||||
icon: 'IconBuilding',
|
||||
isCustom: false,
|
||||
standardOverrides: {
|
||||
translations: {
|
||||
'fr-FR': {
|
||||
labelSingular: 'Bien immobilier',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = resolveObjectMetadataStandardOverride(
|
||||
objectMetadata,
|
||||
'labelSingular',
|
||||
'fr-FR',
|
||||
mockI18n,
|
||||
false,
|
||||
{ 'app.label.id': 'Bien' },
|
||||
);
|
||||
|
||||
expect(result).toBe('Bien immobilier');
|
||||
expect(mockGenerateMessageId).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -139,7 +139,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
options: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
morphId: null,
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataUniversalIdentifier,
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ export const fromObjectMetadataEntityToObjectMetadataDto = (
|
||||
icon: entity.icon ?? undefined,
|
||||
color: entity.color ?? undefined,
|
||||
shortcut: entity.shortcut ?? undefined,
|
||||
standardOverrides: entity.standardOverrides ?? undefined,
|
||||
overrides: entity.overrides ?? undefined,
|
||||
isRemote: entity.isRemote,
|
||||
isActive: entity.isActive,
|
||||
isSystem: entity.isSystem,
|
||||
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
import { type I18n } from '@lingui/core';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { type APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { translateStandardLabel } from 'src/engine/core-modules/i18n/utils/translate-standard-label.util';
|
||||
import { type ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
|
||||
export const resolveObjectMetadataStandardOverride = (
|
||||
objectMetadata: Pick<
|
||||
ObjectMetadataDTO,
|
||||
| 'color'
|
||||
| 'labelPlural'
|
||||
| 'labelSingular'
|
||||
| 'description'
|
||||
| 'icon'
|
||||
| 'standardOverrides'
|
||||
>,
|
||||
labelKey: 'color' | 'labelPlural' | 'labelSingular' | 'description' | 'icon',
|
||||
locale: keyof typeof APP_LOCALES | undefined,
|
||||
i18nInstance: I18n,
|
||||
isStandardApp: boolean,
|
||||
applicationCatalog?: Record<string, string>,
|
||||
): string => {
|
||||
const safeLocale = locale ?? SOURCE_LOCALE;
|
||||
|
||||
if (!isStandardApp && !isDefined(applicationCatalog)) {
|
||||
return objectMetadata[labelKey] ?? '';
|
||||
}
|
||||
|
||||
if (
|
||||
(labelKey === 'icon' || labelKey === 'color') &&
|
||||
isDefined(objectMetadata.standardOverrides?.[labelKey])
|
||||
) {
|
||||
return objectMetadata.standardOverrides[labelKey];
|
||||
}
|
||||
|
||||
if (
|
||||
isDefined(objectMetadata.standardOverrides?.translations) &&
|
||||
labelKey !== 'icon' &&
|
||||
labelKey !== 'color'
|
||||
) {
|
||||
const translationValue =
|
||||
objectMetadata.standardOverrides.translations[safeLocale]?.[labelKey];
|
||||
|
||||
if (isDefined(translationValue)) {
|
||||
return translationValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNonEmptyString(objectMetadata.standardOverrides?.[labelKey])) {
|
||||
return objectMetadata.standardOverrides[labelKey] ?? '';
|
||||
}
|
||||
|
||||
return translateStandardLabel({
|
||||
sourceValue: objectMetadata[labelKey] ?? '',
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
i18nInstance,
|
||||
});
|
||||
};
|
||||
+3
-7
@@ -36,7 +36,7 @@ import {
|
||||
} from 'src/engine/metadata-modules/page-layout/exceptions/page-layout.exception';
|
||||
import { fromFlatPageLayoutWithTabsAndWidgetsToPageLayoutDto } from 'src/engine/metadata-modules/page-layout/utils/from-flat-page-layout-with-tabs-and-widgets-to-page-layout-dto.util';
|
||||
import { isCallerOverridingEntity } from 'src/engine/metadata-modules/utils/is-caller-overriding-entity.util';
|
||||
import { resolveFlatEntityOverridableProperties } from 'src/engine/metadata-modules/utils/resolve-flat-entity-overridable-properties.util';
|
||||
import { resolveEffectiveEntity } from 'src/engine/metadata-modules/utils/resolve-effective-entity.util';
|
||||
import { sanitizeOverridableEntityInput } from 'src/engine/metadata-modules/utils/sanitize-overridable-entity-input.util';
|
||||
import { ViewService } from 'src/engine/metadata-modules/view/services/view.service';
|
||||
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
|
||||
@@ -283,9 +283,7 @@ export class PageLayoutUpdateService {
|
||||
.filter(isDefined)
|
||||
.filter((tab) => tab.pageLayoutId === existingPageLayout.id);
|
||||
|
||||
const resolvedExistingTabs = existingTabs.map(
|
||||
resolveFlatEntityOverridableProperties,
|
||||
);
|
||||
const resolvedExistingTabs = existingTabs.map(resolveEffectiveEntity);
|
||||
|
||||
const {
|
||||
toCreate: entitiesToCreate,
|
||||
@@ -561,9 +559,7 @@ export class PageLayoutUpdateService {
|
||||
flatPageLayoutWidgetMaps,
|
||||
});
|
||||
|
||||
const resolvedExistingWidgets = existingWidgets.map(
|
||||
resolveFlatEntityOverridableProperties,
|
||||
);
|
||||
const resolvedExistingWidgets = existingWidgets.map(resolveEffectiveEntity);
|
||||
|
||||
const {
|
||||
toCreate: entitiesToCreate,
|
||||
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
import { type I18n } from '@lingui/core';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { type APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { translateStandardLabel } from 'src/engine/core-modules/i18n/utils/translate-standard-label.util';
|
||||
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
|
||||
import { resolveEffectiveEntity } from 'src/engine/metadata-modules/utils/resolve-effective-entity.util';
|
||||
|
||||
// Frozen reference implementations of the three resolvers that existed before
|
||||
// unification (resolve-object-metadata-standard-override,
|
||||
// resolve-field-metadata-standard-override,
|
||||
// resolve-flat-entity-overridable-properties), with standardOverrides renamed
|
||||
// to overrides. The unified resolver must reproduce these byte-for-byte across
|
||||
// the corpus below — this is the safety net for the whole override unification.
|
||||
|
||||
type Locale = keyof typeof APP_LOCALES | undefined;
|
||||
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
type AnyOverrides = any;
|
||||
|
||||
const frozenResolveObjectOverride = (
|
||||
objectMetadata: AnyOverrides,
|
||||
labelKey: 'color' | 'labelPlural' | 'labelSingular' | 'description' | 'icon',
|
||||
locale: Locale,
|
||||
i18nInstance: I18n,
|
||||
isStandardApp: boolean,
|
||||
applicationCatalog?: Record<string, string>,
|
||||
): string => {
|
||||
const safeLocale = locale ?? SOURCE_LOCALE;
|
||||
|
||||
if (!isStandardApp && !isDefined(applicationCatalog)) {
|
||||
return objectMetadata[labelKey] ?? '';
|
||||
}
|
||||
|
||||
if (
|
||||
(labelKey === 'icon' || labelKey === 'color') &&
|
||||
isDefined(objectMetadata.overrides?.[labelKey])
|
||||
) {
|
||||
return objectMetadata.overrides[labelKey];
|
||||
}
|
||||
|
||||
if (
|
||||
isDefined(objectMetadata.overrides?.translations) &&
|
||||
labelKey !== 'icon' &&
|
||||
labelKey !== 'color'
|
||||
) {
|
||||
const translationValue =
|
||||
objectMetadata.overrides.translations[safeLocale]?.[labelKey];
|
||||
|
||||
if (isDefined(translationValue)) {
|
||||
return translationValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNonEmptyString(objectMetadata.overrides?.[labelKey])) {
|
||||
return objectMetadata.overrides[labelKey] ?? '';
|
||||
}
|
||||
|
||||
return translateStandardLabel({
|
||||
sourceValue: objectMetadata[labelKey] ?? '',
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
i18nInstance,
|
||||
});
|
||||
};
|
||||
|
||||
const frozenResolveFieldOverride = (
|
||||
fieldMetadata: AnyOverrides,
|
||||
labelKey: 'label' | 'description' | 'icon',
|
||||
locale: Locale,
|
||||
i18nInstance: I18n,
|
||||
isStandardApp: boolean,
|
||||
applicationCatalog?: Record<string, string>,
|
||||
): string => {
|
||||
const safeLocale = locale ?? SOURCE_LOCALE;
|
||||
|
||||
if (!isStandardApp && !isDefined(applicationCatalog)) {
|
||||
return fieldMetadata[labelKey] ?? '';
|
||||
}
|
||||
|
||||
if (labelKey === 'icon' && isDefined(fieldMetadata.overrides?.icon)) {
|
||||
return fieldMetadata.overrides.icon;
|
||||
}
|
||||
|
||||
if (isDefined(fieldMetadata.overrides?.translations) && labelKey !== 'icon') {
|
||||
const translationValue =
|
||||
fieldMetadata.overrides.translations[safeLocale]?.[labelKey];
|
||||
|
||||
if (isDefined(translationValue)) {
|
||||
return translationValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNonEmptyString(fieldMetadata.overrides?.[labelKey])) {
|
||||
return fieldMetadata.overrides[labelKey] ?? '';
|
||||
}
|
||||
|
||||
return translateStandardLabel({
|
||||
sourceValue: fieldMetadata[labelKey] ?? '',
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
i18nInstance,
|
||||
});
|
||||
};
|
||||
|
||||
const frozenResolveFlat = (flatEntity: AnyOverrides): AnyOverrides => {
|
||||
if (!isDefined(flatEntity.overrides)) {
|
||||
return flatEntity;
|
||||
}
|
||||
|
||||
return {
|
||||
...flatEntity,
|
||||
...flatEntity.overrides,
|
||||
};
|
||||
};
|
||||
|
||||
const mockI18n = {
|
||||
_: (id: string) => `translated:${id}`,
|
||||
} as unknown as I18n;
|
||||
|
||||
const LOCALES: Locale[] = [undefined, SOURCE_LOCALE, 'fr-FR'];
|
||||
const BOOLS = [true, false];
|
||||
|
||||
const OBJECT_KEYS = [
|
||||
'labelSingular',
|
||||
'labelPlural',
|
||||
'description',
|
||||
'icon',
|
||||
'color',
|
||||
] as const;
|
||||
|
||||
const FIELD_KEYS = ['label', 'description', 'icon'] as const;
|
||||
|
||||
const buildObjectBase = () => ({
|
||||
labelSingular: 'Company',
|
||||
labelPlural: 'Companies',
|
||||
description: 'A company',
|
||||
icon: 'IconBuilding',
|
||||
color: 'blue',
|
||||
});
|
||||
|
||||
const buildFieldBase = () => ({
|
||||
label: 'Name',
|
||||
description: 'The name',
|
||||
icon: 'IconAbc',
|
||||
});
|
||||
|
||||
const OBJECT_OVERRIDES_CORPUS: AnyOverrides[] = [
|
||||
undefined,
|
||||
null,
|
||||
{},
|
||||
{ labelSingular: 'Org' },
|
||||
{ labelSingular: '' },
|
||||
{ labelPlural: 'Orgs', description: 'custom' },
|
||||
{ icon: 'IconStar' },
|
||||
{ color: 'red' },
|
||||
{ icon: 'IconStar', color: 'red', labelSingular: 'Org' },
|
||||
{ translations: { 'fr-FR': { labelSingular: 'Société' } } },
|
||||
{ translations: { en: { labelPlural: 'Companies EN' } } },
|
||||
{
|
||||
labelSingular: 'Org',
|
||||
translations: {
|
||||
'fr-FR': { labelSingular: 'Société', description: 'desc fr' },
|
||||
},
|
||||
},
|
||||
{ translations: {} },
|
||||
{ translations: { 'fr-FR': {} } },
|
||||
];
|
||||
|
||||
const FIELD_OVERRIDES_CORPUS: AnyOverrides[] = [
|
||||
undefined,
|
||||
null,
|
||||
{},
|
||||
{ label: 'Full name' },
|
||||
{ label: '' },
|
||||
{ description: 'custom desc' },
|
||||
{ icon: 'IconStar' },
|
||||
{ icon: 'IconStar', label: 'Full name' },
|
||||
{ translations: { 'fr-FR': { label: 'Nom' } } },
|
||||
{
|
||||
label: 'Full name',
|
||||
translations: { 'fr-FR': { label: 'Nom', description: 'desc fr' } },
|
||||
},
|
||||
{ translations: {} },
|
||||
];
|
||||
|
||||
const CATALOGS: (Record<string, string> | undefined)[] = [
|
||||
undefined,
|
||||
{ 'some.id': 'From catalog' },
|
||||
];
|
||||
|
||||
describe('resolveEffectiveEntityProperty (parity with legacy resolvers)', () => {
|
||||
it('matches the frozen object resolver across the corpus', () => {
|
||||
for (const overrides of OBJECT_OVERRIDES_CORPUS) {
|
||||
for (const key of OBJECT_KEYS) {
|
||||
for (const locale of LOCALES) {
|
||||
for (const isStandardApp of BOOLS) {
|
||||
for (const applicationCatalog of CATALOGS) {
|
||||
const entity = { ...buildObjectBase(), overrides };
|
||||
|
||||
const expected = frozenResolveObjectOverride(
|
||||
entity,
|
||||
key,
|
||||
locale,
|
||||
mockI18n,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
);
|
||||
|
||||
const actual = resolveEffectiveEntityProperty({
|
||||
metadataName: 'objectMetadata',
|
||||
baseValue: entity[key as keyof typeof entity] as string,
|
||||
overrides: entity.overrides,
|
||||
property: key,
|
||||
i18nContext: {
|
||||
locale,
|
||||
i18nInstance: mockI18n,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual).toBe(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('matches the frozen field resolver across the corpus', () => {
|
||||
for (const overrides of FIELD_OVERRIDES_CORPUS) {
|
||||
for (const key of FIELD_KEYS) {
|
||||
for (const locale of LOCALES) {
|
||||
for (const isStandardApp of BOOLS) {
|
||||
for (const applicationCatalog of CATALOGS) {
|
||||
const entity = { ...buildFieldBase(), overrides };
|
||||
|
||||
const expected = frozenResolveFieldOverride(
|
||||
entity,
|
||||
key,
|
||||
locale,
|
||||
mockI18n,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
);
|
||||
|
||||
const actual = resolveEffectiveEntityProperty({
|
||||
metadataName: 'fieldMetadata',
|
||||
baseValue: entity[key as keyof typeof entity] as string,
|
||||
overrides: entity.overrides,
|
||||
property: key,
|
||||
i18nContext: {
|
||||
locale,
|
||||
i18nInstance: mockI18n,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual).toBe(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveEffectiveEntity (parity with legacy flat spread)', () => {
|
||||
it('matches the frozen flat spread across the corpus', () => {
|
||||
const flatCorpus: AnyOverrides[] = [
|
||||
{ name: 'View', position: 1, overrides: undefined },
|
||||
{ name: 'View', position: 1, overrides: null },
|
||||
{ name: 'View', position: 1, overrides: {} },
|
||||
{ name: 'View', position: 1, overrides: { name: 'Overridden' } },
|
||||
{
|
||||
name: 'View',
|
||||
position: 1,
|
||||
icon: 'IconList',
|
||||
overrides: { name: 'Overridden', position: 2 },
|
||||
},
|
||||
];
|
||||
|
||||
for (const flatEntity of flatCorpus) {
|
||||
expect(resolveEffectiveEntity(flatEntity)).toEqual(
|
||||
frozenResolveFlat(flatEntity),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { type I18n } from '@lingui/core';
|
||||
import { type APP_LOCALES } from 'twenty-shared/translations';
|
||||
|
||||
export type EffectiveEntityI18nContext = {
|
||||
locale: keyof typeof APP_LOCALES | undefined;
|
||||
i18nInstance: I18n;
|
||||
isStandardApp: boolean;
|
||||
applicationCatalog?: Record<string, string>;
|
||||
};
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
import { type APP_LOCALES } from 'twenty-shared/translations';
|
||||
|
||||
import {
|
||||
type MetadataEntityOverridablePropertyName,
|
||||
type MetadataEntityTranslatablePropertyName,
|
||||
} from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
|
||||
|
||||
type OverridesTranslationEntry<T extends AllMetadataName> = {
|
||||
[P in MetadataEntityTranslatablePropertyName<T>]?: string | null;
|
||||
};
|
||||
|
||||
export type MetadataPresentationOverrides<T extends AllMetadataName> = {
|
||||
[P in MetadataEntityOverridablePropertyName<T>]?: string | null;
|
||||
} & {
|
||||
translations?: Partial<
|
||||
Record<keyof typeof APP_LOCALES, OverridesTranslationEntry<T>>
|
||||
> | null;
|
||||
};
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { translateStandardLabel } from 'src/engine/core-modules/i18n/utils/translate-standard-label.util';
|
||||
import { type MetadataEntityOverridablePropertyName } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
|
||||
import { ALL_TRANSLATABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-translatable-properties-by-metadata-name.constant';
|
||||
import { type EffectiveEntityI18nContext } from 'src/engine/metadata-modules/utils/effective-entity-i18n-context.type';
|
||||
import { type MetadataPresentationOverrides } from 'src/engine/metadata-modules/utils/metadata-presentation-overrides.type';
|
||||
|
||||
export const resolveEffectiveEntityProperty = <T extends AllMetadataName>({
|
||||
metadataName,
|
||||
baseValue,
|
||||
overrides,
|
||||
property,
|
||||
i18nContext,
|
||||
}: {
|
||||
metadataName: T;
|
||||
baseValue: string | null | undefined;
|
||||
overrides: MetadataPresentationOverrides<T> | null | undefined;
|
||||
property: MetadataEntityOverridablePropertyName<T> & string;
|
||||
i18nContext: EffectiveEntityI18nContext;
|
||||
}): string => {
|
||||
const isTranslatable = (
|
||||
ALL_TRANSLATABLE_PROPERTIES_BY_METADATA_NAME[metadataName] as string[]
|
||||
).includes(property);
|
||||
|
||||
const overrideValue = (
|
||||
overrides as Record<string, unknown> | null | undefined
|
||||
)?.[property];
|
||||
|
||||
const { locale, i18nInstance, isStandardApp, applicationCatalog } =
|
||||
i18nContext;
|
||||
const safeLocale = locale ?? SOURCE_LOCALE;
|
||||
const safeBaseValue = baseValue ?? '';
|
||||
|
||||
// Custom (non-standard) entities without a catalog have no standard label to
|
||||
// resolve or translate, and never carry overrides.
|
||||
if (!isStandardApp && !isDefined(applicationCatalog)) {
|
||||
return safeBaseValue;
|
||||
}
|
||||
|
||||
if (!isTranslatable && isDefined(overrideValue)) {
|
||||
return overrideValue as string;
|
||||
}
|
||||
|
||||
if (isTranslatable && isDefined(overrides?.translations)) {
|
||||
const translationValue = (
|
||||
overrides.translations[safeLocale] as
|
||||
| Record<string, string | null | undefined>
|
||||
| undefined
|
||||
)?.[property];
|
||||
|
||||
if (isDefined(translationValue)) {
|
||||
return translationValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNonEmptyString(overrideValue)) {
|
||||
return overrideValue;
|
||||
}
|
||||
|
||||
return translateStandardLabel({
|
||||
sourceValue: safeBaseValue,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
i18nInstance,
|
||||
});
|
||||
};
|
||||
+1
-3
@@ -5,9 +5,7 @@ type FlatEntityWithOverrides = {
|
||||
overrides: Record<string, unknown> | null;
|
||||
};
|
||||
|
||||
export const resolveFlatEntityOverridableProperties = <
|
||||
T extends FlatEntityWithOverrides,
|
||||
>(
|
||||
export const resolveEffectiveEntity = <T extends FlatEntityWithOverrides>(
|
||||
flatEntity: T,
|
||||
): T => {
|
||||
if (!isDefined(flatEntity.overrides)) {
|
||||
+11
-13
@@ -24,7 +24,7 @@ import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
|
||||
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
|
||||
import { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.util';
|
||||
import { CreateViewPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/create-view-permission.guard';
|
||||
import { DeleteViewPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/delete-view-permission.guard';
|
||||
@@ -211,19 +211,17 @@ export class ViewController {
|
||||
|
||||
if (objectMetadata) {
|
||||
const i18n = this.i18nService.getI18nInstance(locale ?? 'en');
|
||||
const translatedObjectLabel = resolveObjectMetadataStandardOverride(
|
||||
{
|
||||
labelPlural: objectMetadata.labelPlural,
|
||||
labelSingular: objectMetadata.labelSingular,
|
||||
description: objectMetadata.description ?? undefined,
|
||||
icon: objectMetadata.icon ?? undefined,
|
||||
standardOverrides: objectMetadata.standardOverrides ?? undefined,
|
||||
const translatedObjectLabel = resolveEffectiveEntityProperty({
|
||||
metadataName: 'objectMetadata',
|
||||
baseValue: objectMetadata.labelPlural,
|
||||
overrides: objectMetadata.overrides ?? undefined,
|
||||
property: 'labelPlural',
|
||||
i18nContext: {
|
||||
locale,
|
||||
i18nInstance: i18n,
|
||||
isStandardApp: belongsToTwentyStandardApp(objectMetadata),
|
||||
},
|
||||
'labelPlural',
|
||||
locale,
|
||||
i18n,
|
||||
belongsToTwentyStandardApp(objectMetadata),
|
||||
);
|
||||
});
|
||||
|
||||
processedName = this.viewService.processViewNameWithTemplate(
|
||||
view.name,
|
||||
|
||||
+12
-14
@@ -24,7 +24,7 @@ import { CustomPermissionGuard } from 'src/engine/guards/custom-permission.guard
|
||||
import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
|
||||
import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
|
||||
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
|
||||
import { ViewFieldGroupDTO } from 'src/engine/metadata-modules/view-field-group/dtos/view-field-group.dto';
|
||||
import { ViewFieldDTO } from 'src/engine/metadata-modules/view-field/dtos/view-field.dto';
|
||||
import { ViewFilterGroupDTO } from 'src/engine/metadata-modules/view-filter-group/dtos/view-filter-group.dto';
|
||||
@@ -80,20 +80,18 @@ export class ViewResolver {
|
||||
workspaceId: workspace.id,
|
||||
locale: context.req.locale,
|
||||
});
|
||||
const translatedObjectLabel = resolveObjectMetadataStandardOverride(
|
||||
{
|
||||
labelPlural: objectMetadata.labelPlural,
|
||||
labelSingular: objectMetadata.labelSingular,
|
||||
description: objectMetadata.description ?? undefined,
|
||||
icon: objectMetadata.icon ?? undefined,
|
||||
standardOverrides: objectMetadata.standardOverrides ?? undefined,
|
||||
const translatedObjectLabel = resolveEffectiveEntityProperty({
|
||||
metadataName: 'objectMetadata',
|
||||
baseValue: objectMetadata.labelPlural,
|
||||
overrides: objectMetadata.overrides ?? undefined,
|
||||
property: 'labelPlural',
|
||||
i18nContext: {
|
||||
locale: context.req.locale,
|
||||
i18nInstance: i18n,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
},
|
||||
'labelPlural',
|
||||
context.req.locale,
|
||||
i18n,
|
||||
isStandardApp,
|
||||
applicationCatalog,
|
||||
);
|
||||
});
|
||||
|
||||
return this.viewService.processViewNameWithTemplate(
|
||||
view.name,
|
||||
|
||||
+18
-19
@@ -6,8 +6,7 @@ import { isDefined, isNonEmptyArray } from 'twenty-shared/utils';
|
||||
import { I18nService } from 'src/engine/core-modules/i18n/i18n.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { NavigationMenuItemRecordIdentifierService } from 'src/engine/metadata-modules/navigation-menu-item/services/navigation-menu-item-record-identifier.service';
|
||||
import { FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/field-metadata/constants/field-metadata-standard-overrides-properties.constant';
|
||||
import { OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/object-metadata/constants/object-metadata-standard-overrides-properties.constant';
|
||||
import { ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-overridable-properties-by-metadata-name.constant';
|
||||
import { type MetadataEventBatch } from 'src/engine/subscriptions/metadata-event/types/metadata-event-batch.type';
|
||||
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
|
||||
import { enrichCommandMenuItemEventWithResolvedNavigation } from 'src/engine/subscriptions/metadata-event/utils/enrich-command-menu-item-event-with-resolved-navigation.util';
|
||||
@@ -61,7 +60,7 @@ export class MetadataEventPublisher {
|
||||
metadataEventBatch as MetadataEventBatch<'commandMenuItem'>,
|
||||
);
|
||||
case 'objectMetadata':
|
||||
return this.resolveObjectMetadataStandardOverrides(
|
||||
return this.resolveObjectMetadataOverrides(
|
||||
metadataEventBatch as MetadataEventBatch<'objectMetadata'>,
|
||||
);
|
||||
default:
|
||||
@@ -87,9 +86,9 @@ export class MetadataEventPublisher {
|
||||
'before' in enrichedProperties &&
|
||||
isDefined(enrichedProperties.before)
|
||||
) {
|
||||
enrichedProperties.before = this.applyStandardOverridesToMetadataRecord(
|
||||
enrichedProperties.before = this.applyOverridesToMetadataRecord(
|
||||
enrichedProperties.before as Record<string, unknown>,
|
||||
FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES,
|
||||
ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.fieldMetadata,
|
||||
) as typeof enrichedProperties.before;
|
||||
}
|
||||
|
||||
@@ -103,9 +102,9 @@ export class MetadataEventPublisher {
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
enrichedProperties.after = this.applyStandardOverridesToMetadataRecord(
|
||||
enrichedProperties.after = this.applyOverridesToMetadataRecord(
|
||||
enrichedAfter,
|
||||
FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES,
|
||||
ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.fieldMetadata,
|
||||
) as typeof enrichedProperties.after;
|
||||
}
|
||||
|
||||
@@ -207,7 +206,7 @@ export class MetadataEventPublisher {
|
||||
return { ...metadataEventBatch, events: enrichedEvents };
|
||||
}
|
||||
|
||||
private resolveObjectMetadataStandardOverrides(
|
||||
private resolveObjectMetadataOverrides(
|
||||
metadataEventBatch: MetadataEventBatch<'objectMetadata'>,
|
||||
): MetadataEventBatch<'objectMetadata'> {
|
||||
const enrichedEvents = metadataEventBatch.events.map((event) => {
|
||||
@@ -217,9 +216,9 @@ export class MetadataEventPublisher {
|
||||
'before' in enrichedProperties &&
|
||||
isDefined(enrichedProperties.before)
|
||||
) {
|
||||
enrichedProperties.before = this.applyStandardOverridesToMetadataRecord(
|
||||
enrichedProperties.before = this.applyOverridesToMetadataRecord(
|
||||
enrichedProperties.before as Record<string, unknown>,
|
||||
OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES,
|
||||
ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.objectMetadata,
|
||||
) as typeof enrichedProperties.before;
|
||||
}
|
||||
|
||||
@@ -227,9 +226,9 @@ export class MetadataEventPublisher {
|
||||
'after' in enrichedProperties &&
|
||||
isDefined(enrichedProperties.after)
|
||||
) {
|
||||
enrichedProperties.after = this.applyStandardOverridesToMetadataRecord(
|
||||
enrichedProperties.after = this.applyOverridesToMetadataRecord(
|
||||
enrichedProperties.after as Record<string, unknown>,
|
||||
OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES,
|
||||
ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.objectMetadata,
|
||||
) as typeof enrichedProperties.after;
|
||||
}
|
||||
|
||||
@@ -239,24 +238,24 @@ export class MetadataEventPublisher {
|
||||
return { ...metadataEventBatch, events: enrichedEvents };
|
||||
}
|
||||
|
||||
private applyStandardOverridesToMetadataRecord(
|
||||
private applyOverridesToMetadataRecord(
|
||||
record: Record<string, unknown>,
|
||||
standardOverrideProperties: readonly string[],
|
||||
overridableProperties: readonly string[],
|
||||
): Record<string, unknown> {
|
||||
const standardOverrides = record.standardOverrides as
|
||||
const overrides = record.overrides as
|
||||
| Record<string, unknown>
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
if (!isDefined(standardOverrides)) {
|
||||
if (!isDefined(overrides)) {
|
||||
return record;
|
||||
}
|
||||
|
||||
const resolved = { ...record };
|
||||
|
||||
for (const key of standardOverrideProperties) {
|
||||
if (isDefined(standardOverrides[key])) {
|
||||
resolved[key] = standardOverrides[key];
|
||||
for (const key of overridableProperties) {
|
||||
if (isDefined(overrides[key])) {
|
||||
resolved[key] = overrides[key];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -32,7 +32,7 @@ const makeFlatObjectMetadata = (
|
||||
labelPlural: 'People',
|
||||
labelSingular: 'Person',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
...overrides,
|
||||
}) as unknown as FlatObjectMetadata;
|
||||
|
||||
@@ -148,10 +148,10 @@ describe('enrichCommandMenuItemEventWithResolvedNavigation', () => {
|
||||
const flatObjectMetadata = makeFlatObjectMetadata({
|
||||
labelPlural: 'People',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: {
|
||||
overrides: {
|
||||
labelPlural: 'Contacts',
|
||||
icon: 'IconContacts',
|
||||
} as unknown as FlatObjectMetadata['standardOverrides'],
|
||||
} as unknown as FlatObjectMetadata['overrides'],
|
||||
});
|
||||
const flatObjectMetadataMaps =
|
||||
makeFlatObjectMetadataMaps(flatObjectMetadata);
|
||||
@@ -174,7 +174,7 @@ describe('enrichCommandMenuItemEventWithResolvedNavigation', () => {
|
||||
const flatObjectMetadata = makeFlatObjectMetadata({
|
||||
labelPlural: 'Companies',
|
||||
icon: 'IconBuilding',
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
});
|
||||
const flatObjectMetadataMaps =
|
||||
makeFlatObjectMetadataMaps(flatObjectMetadata);
|
||||
|
||||
+2
-2
@@ -118,7 +118,7 @@ describe('WorkspaceEntityManager', () => {
|
||||
imageIdentifierFieldMetadataId: null,
|
||||
labelIdentifierFieldMetadataId: null,
|
||||
shortcut: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
applicationId: 'test-application-id',
|
||||
isLabelSyncedWithName: false,
|
||||
isUIEditable: true,
|
||||
@@ -162,7 +162,7 @@ describe('WorkspaceEntityManager', () => {
|
||||
isUnique: false,
|
||||
options: null,
|
||||
settings: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
workspaceId: 'test-workspace-id',
|
||||
viewFieldIds: [],
|
||||
viewFilterIds: [],
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ describe('WorkspaceRepository', () => {
|
||||
options: null,
|
||||
settings: null,
|
||||
morphId: null,
|
||||
standardOverrides: null,
|
||||
overrides: null,
|
||||
applicationId: 'application-id',
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user