25fe66565c
## Before <img width="1452" height="709" alt="image" src="https://github.com/user-attachments/assets/cd384ffa-cbe6-49d5-a807-ca8d580f55a9" /> <img width="1074" height="452" alt="image" src="https://github.com/user-attachments/assets/720d38db-3495-4032-8831-17d24ec6a7e7" /> ## After <img width="1421" height="865" alt="image" src="https://github.com/user-attachments/assets/2275c996-c895-4800-8324-2aa2ddfddd43" /> <img width="1348" height="870" alt="image" src="https://github.com/user-attachments/assets/3e1a891d-6db0-4cbd-870a-2a5bbde4929d" /> ## Summary Adds typed application variables with optional select **options**. This is the other half of #22059, split out from the custom-settings-tab removal. ## Changes - **Shared types**: `ApplicationVariable` / `ServerVariables` gain an optional `type` (a `FieldMetadataType` subset — `TEXT`, `BOOLEAN`, `NUMBER`, `DATE`, `SELECT`, `MULTI_SELECT`, `RAW_JSON`, `RICH_TEXT`, `ARRAY`, …) and select `options`. New `serializeApplicationVariableValue` / `deserializeApplicationVariableValue` helpers convert typed values to/from the encrypted string storage. - **Server**: `type`/`options` columns on `applicationVariable` and `applicationRegistrationVariable` (entities + DTOs), a fast `2-17` instance command, manifest processing via the serialization helpers, and a `QueryDeepPartialEntity` cast where the manifest JSON column is persisted. - **Frontend**: a polymorphic `SettingsApplicationVariableInput` that renders the native `Form*` field component for each type (boolean, number, date/date-time, select, multi-select, array, raw JSON, rich text, text); fragment/query updates to fetch `type`/`options`. - **SDK**: `defineApplication` validates that `SELECT`/`MULTI_SELECT` variables declare non-empty `options` at build time (since `options` is kept structurally optional for TypeORM/SDK compatibility). Variables default to `TEXT` when no type is given, so existing manifests are unaffected. ## Notes The generated GraphQL artifacts (`type`/`options` on the variable types) are regenerated by codegen; that change accompanies this PR. https://claude.ai/code/session_013Z7UB35V2mvUozh55QHG23 --- _Generated by [Claude Code](https://claude.ai/code/session_013Z7UB35V2mvUozh55QHG23)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22157?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
176 lines
5.6 KiB
TypeScript
176 lines
5.6 KiB
TypeScript
import { defineApplication, FieldType } from 'twenty-sdk/define';
|
|
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from './src/roles/default-function.role';
|
|
|
|
export default defineApplication({
|
|
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
|
|
displayName: 'Rich App',
|
|
description: 'A simple rich app',
|
|
applicationVariables: {
|
|
DEFAULT_RECIPIENT_NAME: {
|
|
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
|
|
description: 'Default recipient name for postcards',
|
|
value: 'Alex Karp',
|
|
isSecret: false,
|
|
},
|
|
|
|
GREETING_TEXT: {
|
|
universalIdentifier: 'ad19edc5-4cc5-4003-a996-aef53a5c8de0',
|
|
description: 'Free text shown on the postcard',
|
|
type: FieldType.TEXT,
|
|
value: 'Hello from Rich App',
|
|
},
|
|
ENABLE_TRACKING: {
|
|
universalIdentifier: 'b9c58bb2-58c3-4c6c-9877-498ea6c03fde',
|
|
description: 'Toggle delivery tracking',
|
|
type: FieldType.BOOLEAN,
|
|
value: true,
|
|
},
|
|
MAX_POSTCARDS: {
|
|
universalIdentifier: '5f4497e4-9030-4085-85eb-2c48b8d53713',
|
|
description: 'Maximum postcards per batch',
|
|
type: FieldType.NUMBER,
|
|
value: 10,
|
|
},
|
|
DISCOUNT_RATE: {
|
|
universalIdentifier: 'd32f810f-06bb-4d29-b0ee-1dc4228f7cb8',
|
|
description: 'Bulk discount rate applied at checkout',
|
|
type: FieldType.NUMERIC,
|
|
value: 2.5,
|
|
},
|
|
CAMPAIGN_START_DATE: {
|
|
universalIdentifier: '5aa4fcec-e8a3-4bc1-9c7f-762e1f9dfb40',
|
|
description: 'Date the campaign starts',
|
|
type: FieldType.DATE,
|
|
value: '2026-01-01',
|
|
},
|
|
CAMPAIGN_START_AT: {
|
|
universalIdentifier: '273d0cfd-d6ab-4148-8816-c4a5d4b1d600',
|
|
description: 'Exact moment the campaign starts',
|
|
type: FieldType.DATE_TIME,
|
|
value: '2026-01-01T09:00:00.000Z',
|
|
},
|
|
DEFAULT_REGION: {
|
|
universalIdentifier: '76c5c321-b6b6-46eb-b4fc-f9f04bb04227',
|
|
description: 'Default shipping region',
|
|
type: FieldType.SELECT,
|
|
options: [
|
|
{ label: 'Europe', value: 'eu' },
|
|
{ label: 'United States', value: 'us' },
|
|
{ label: 'Asia-Pacific', value: 'apac' },
|
|
],
|
|
value: 'eu',
|
|
},
|
|
ENABLED_CHANNELS: {
|
|
universalIdentifier: '706a2b08-8284-4715-8bc0-922a99cb26af',
|
|
description: 'Channels the app is allowed to use',
|
|
type: FieldType.MULTI_SELECT,
|
|
options: [
|
|
{ label: 'Email', value: 'email' },
|
|
{ label: 'SMS', value: 'sms' },
|
|
{ label: 'Postcard', value: 'postcard' },
|
|
],
|
|
value: ['email', 'postcard'],
|
|
},
|
|
ALLOWED_TAGS: {
|
|
universalIdentifier: 'c1c8a4c9-9130-4ab8-8dce-18d2b50879ed',
|
|
description: 'Free-form tags applied to recipients',
|
|
type: FieldType.ARRAY,
|
|
value: ['vip', 'returning'],
|
|
},
|
|
PROVIDER_CONFIG: {
|
|
universalIdentifier: '183d5285-c70c-4f29-96e0-c68659fbe5ae',
|
|
description: 'Raw JSON configuration for the printing provider',
|
|
type: FieldType.RAW_JSON,
|
|
value: { retries: 3, timeoutMs: 5000 },
|
|
},
|
|
WELCOME_MESSAGE: {
|
|
universalIdentifier: '25a66ff5-8458-498e-9e7e-33ea458a6f3c',
|
|
description: 'Rich text welcome message',
|
|
type: FieldType.RICH_TEXT,
|
|
value: { blocknote: null, markdown: 'Welcome to **Rich App**!' },
|
|
},
|
|
},
|
|
serverVariables: {
|
|
POSTCARD_API_KEY: {
|
|
description: 'API key for the postcard printing service',
|
|
isSecret: true,
|
|
isRequired: true,
|
|
},
|
|
POSTCARD_SENDER_NAME: {
|
|
description: 'Default sender name on postcards',
|
|
isSecret: false,
|
|
isRequired: false,
|
|
},
|
|
POSTCARD_DELIVERY_SPEED: {
|
|
description: 'Delivery speed requested from the provider',
|
|
type: FieldType.SELECT,
|
|
options: [
|
|
{ label: 'Standard', value: 'standard' },
|
|
{ label: 'Express', value: 'express' },
|
|
],
|
|
isSecret: false,
|
|
isRequired: true,
|
|
},
|
|
POSTCARD_DAILY_LIMIT: {
|
|
description: 'Maximum postcards the provider will accept per day',
|
|
type: FieldType.NUMBER,
|
|
isSecret: false,
|
|
isRequired: false,
|
|
},
|
|
POSTCARD_UNIT_PRICE: {
|
|
description: 'Price charged by the provider per postcard',
|
|
type: FieldType.NUMERIC,
|
|
isSecret: false,
|
|
isRequired: false,
|
|
},
|
|
POSTCARD_SANDBOX_MODE: {
|
|
description: 'Send postcards through the provider sandbox instead of production',
|
|
type: FieldType.BOOLEAN,
|
|
isSecret: false,
|
|
isRequired: false,
|
|
},
|
|
POSTCARD_CONTRACT_START_DATE: {
|
|
description: 'Date the provider contract starts',
|
|
type: FieldType.DATE,
|
|
isSecret: false,
|
|
isRequired: false,
|
|
},
|
|
POSTCARD_CONTRACT_RENEWAL_AT: {
|
|
description: 'Exact moment the provider contract renews',
|
|
type: FieldType.DATE_TIME,
|
|
isSecret: false,
|
|
isRequired: false,
|
|
},
|
|
POSTCARD_ENABLED_REGIONS: {
|
|
description: 'Regions the provider is allowed to ship to',
|
|
type: FieldType.MULTI_SELECT,
|
|
options: [
|
|
{ label: 'Europe', value: 'eu' },
|
|
{ label: 'United States', value: 'us' },
|
|
{ label: 'Asia-Pacific', value: 'apac' },
|
|
],
|
|
isSecret: false,
|
|
isRequired: false,
|
|
},
|
|
POSTCARD_WEBHOOK_EVENTS: {
|
|
description: 'Provider webhook events the app subscribes to',
|
|
type: FieldType.ARRAY,
|
|
isSecret: false,
|
|
isRequired: false,
|
|
},
|
|
POSTCARD_PROVIDER_CONFIG: {
|
|
description: 'Raw JSON configuration for the printing provider',
|
|
type: FieldType.RAW_JSON,
|
|
isSecret: false,
|
|
isRequired: false,
|
|
},
|
|
POSTCARD_INVOICE_NOTE: {
|
|
description: 'Rich text note appended to provider invoices',
|
|
type: FieldType.RICH_TEXT,
|
|
isSecret: false,
|
|
isRequired: false,
|
|
},
|
|
},
|
|
defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
|
});
|