@rippling/rippling-sdk 0.2.0-alpha.36 → 0.2.0-alpha.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client.d.mts +7 -1
- package/client.d.mts.map +1 -1
- package/client.d.ts +7 -1
- package/client.d.ts.map +1 -1
- package/client.js +7 -1
- package/client.js.map +1 -1
- package/client.mjs +7 -1
- package/client.mjs.map +1 -1
- package/examples/manifest/dental-practice-management/dental-practice-management.ts +21 -34
- package/examples/manifest/gym-membership-management/gym-membership-management.ts +27 -38
- package/examples/manifest/gym-membership-management/manifest.json +1 -1
- package/examples/sdui/sdui-crud-todo/sdui-crud-todo.ts +3 -3
- package/examples/sdui/sdui-showcase/sdui-showcase.ts +6 -11
- package/examples/sdui/sdui-tab-multi-view/sdui-tab-multi-view.ts +14 -24
- package/examples/sdui/vote/vote.ts +7 -11
- package/internal/utils/log.d.mts.map +1 -1
- package/internal/utils/log.d.ts.map +1 -1
- package/internal/utils/log.js +2 -0
- package/internal/utils/log.js.map +1 -1
- package/internal/utils/log.mjs +2 -0
- package/internal/utils/log.mjs.map +1 -1
- package/lib/manifest/category.d.mts +4 -2
- package/lib/manifest/category.d.mts.map +1 -1
- package/lib/manifest/category.d.ts +4 -2
- package/lib/manifest/category.d.ts.map +1 -1
- package/lib/manifest/category.js +2 -2
- package/lib/manifest/category.js.map +1 -1
- package/lib/manifest/category.mjs +2 -2
- package/lib/manifest/category.mjs.map +1 -1
- package/lib/manifest/custom-object-field-section.d.mts +4 -0
- package/lib/manifest/custom-object-field-section.d.mts.map +1 -1
- package/lib/manifest/custom-object-field-section.d.ts +4 -0
- package/lib/manifest/custom-object-field-section.d.ts.map +1 -1
- package/lib/manifest/custom-object-field-section.js +6 -0
- package/lib/manifest/custom-object-field-section.js.map +1 -1
- package/lib/manifest/custom-object-field-section.mjs +6 -0
- package/lib/manifest/custom-object-field-section.mjs.map +1 -1
- package/lib/manifest/custom-object-page-layout.d.mts +23 -13
- package/lib/manifest/custom-object-page-layout.d.mts.map +1 -1
- package/lib/manifest/custom-object-page-layout.d.ts +23 -13
- package/lib/manifest/custom-object-page-layout.d.ts.map +1 -1
- package/lib/manifest/custom-object-page-layout.js +122 -9
- package/lib/manifest/custom-object-page-layout.js.map +1 -1
- package/lib/manifest/custom-object-page-layout.mjs +122 -9
- package/lib/manifest/custom-object-page-layout.mjs.map +1 -1
- package/lib/manifest/custom-object.d.mts +30 -1
- package/lib/manifest/custom-object.d.mts.map +1 -1
- package/lib/manifest/custom-object.d.ts +30 -1
- package/lib/manifest/custom-object.d.ts.map +1 -1
- package/lib/manifest/custom-object.js +15 -1
- package/lib/manifest/custom-object.js.map +1 -1
- package/lib/manifest/custom-object.mjs +15 -1
- package/lib/manifest/custom-object.mjs.map +1 -1
- package/lib/manifest/field.d.mts +25 -3
- package/lib/manifest/field.d.mts.map +1 -1
- package/lib/manifest/field.d.ts +25 -3
- package/lib/manifest/field.d.ts.map +1 -1
- package/lib/manifest/field.js +28 -2
- package/lib/manifest/field.js.map +1 -1
- package/lib/manifest/field.mjs +28 -2
- package/lib/manifest/field.mjs.map +1 -1
- package/lib/manifest/index.d.mts +1 -1
- package/lib/manifest/index.d.mts.map +1 -1
- package/lib/manifest/index.d.ts +1 -1
- package/lib/manifest/index.d.ts.map +1 -1
- package/lib/manifest/index.js.map +1 -1
- package/lib/manifest/index.mjs.map +1 -1
- package/lib/manifest/manifest-builder.d.mts +1 -1
- package/lib/manifest/manifest-builder.d.ts +1 -1
- package/lib/manifest/manifest-builder.js +1 -1
- package/lib/manifest/manifest-builder.mjs +1 -1
- package/lib/manifest/manifest-function.d.mts +8 -2
- package/lib/manifest/manifest-function.d.mts.map +1 -1
- package/lib/manifest/manifest-function.d.ts +8 -2
- package/lib/manifest/manifest-function.d.ts.map +1 -1
- package/lib/manifest/manifest-function.js +8 -2
- package/lib/manifest/manifest-function.js.map +1 -1
- package/lib/manifest/manifest-function.mjs +8 -2
- package/lib/manifest/manifest-function.mjs.map +1 -1
- package/lib/sdui/actions.d.mts.map +1 -1
- package/lib/sdui/actions.d.ts.map +1 -1
- package/lib/sdui/actions.js +23 -13
- package/lib/sdui/actions.js.map +1 -1
- package/lib/sdui/actions.mjs +23 -13
- package/lib/sdui/actions.mjs.map +1 -1
- package/lib/sdui/component.d.mts +28 -11
- package/lib/sdui/component.d.mts.map +1 -1
- package/lib/sdui/component.d.ts +28 -11
- package/lib/sdui/component.d.ts.map +1 -1
- package/lib/sdui/component.js +69 -36
- package/lib/sdui/component.js.map +1 -1
- package/lib/sdui/component.mjs +69 -36
- package/lib/sdui/component.mjs.map +1 -1
- package/lib/sdui/components.d.mts +1141 -132
- package/lib/sdui/components.d.mts.map +1 -1
- package/lib/sdui/components.d.ts +1141 -132
- package/lib/sdui/components.d.ts.map +1 -1
- package/lib/sdui/components.js +834 -206
- package/lib/sdui/components.js.map +1 -1
- package/lib/sdui/components.mjs +830 -203
- package/lib/sdui/components.mjs.map +1 -1
- package/lib/sdui/directives.d.mts.map +1 -1
- package/lib/sdui/directives.d.ts.map +1 -1
- package/lib/sdui/directives.js +4 -1
- package/lib/sdui/directives.js.map +1 -1
- package/lib/sdui/directives.mjs +4 -1
- package/lib/sdui/directives.mjs.map +1 -1
- package/lib/sdui/index.d.mts +314 -261
- package/lib/sdui/index.d.mts.map +1 -1
- package/lib/sdui/index.d.ts +314 -261
- package/lib/sdui/index.d.ts.map +1 -1
- package/lib/sdui/index.js +255 -204
- package/lib/sdui/index.js.map +1 -1
- package/lib/sdui/index.mjs +128 -103
- package/lib/sdui/index.mjs.map +1 -1
- package/lib/sdui/propTypes.d.mts +6330 -0
- package/lib/sdui/propTypes.d.mts.map +1 -0
- package/lib/sdui/propTypes.d.ts +6330 -0
- package/lib/sdui/propTypes.d.ts.map +1 -0
- package/lib/sdui/propTypes.js +14 -0
- package/lib/sdui/propTypes.js.map +1 -0
- package/lib/sdui/propTypes.mjs +13 -0
- package/lib/sdui/propTypes.mjs.map +1 -0
- package/lib/sdui/render.d.mts +1 -1
- package/lib/sdui/render.d.mts.map +1 -1
- package/lib/sdui/render.d.ts +1 -1
- package/lib/sdui/render.d.ts.map +1 -1
- package/lib/sdui/render.js +11 -3
- package/lib/sdui/render.js.map +1 -1
- package/lib/sdui/render.mjs +11 -3
- package/lib/sdui/render.mjs.map +1 -1
- package/lib/sdui/state.d.mts.map +1 -1
- package/lib/sdui/state.d.ts.map +1 -1
- package/lib/sdui/state.js +3 -1
- package/lib/sdui/state.js.map +1 -1
- package/lib/sdui/state.mjs +3 -1
- package/lib/sdui/state.mjs.map +1 -1
- package/package.json +1 -1
- package/resources/company-legal-entities.d.mts +180 -0
- package/resources/company-legal-entities.d.mts.map +1 -0
- package/resources/company-legal-entities.d.ts +180 -0
- package/resources/company-legal-entities.d.ts.map +1 -0
- package/resources/company-legal-entities.js +30 -0
- package/resources/company-legal-entities.js.map +1 -0
- package/resources/company-legal-entities.mjs +26 -0
- package/resources/company-legal-entities.mjs.map +1 -0
- package/resources/earning-types.d.mts +1 -6
- package/resources/earning-types.d.mts.map +1 -1
- package/resources/earning-types.d.ts +1 -6
- package/resources/earning-types.d.ts.map +1 -1
- package/resources/earning-types.js +1 -1
- package/resources/earning-types.mjs +1 -1
- package/resources/earnings-inputs.d.mts +1 -1
- package/resources/earnings-inputs.d.ts +1 -1
- package/resources/earnings-inputs.js +1 -1
- package/resources/earnings-inputs.mjs +1 -1
- package/resources/index.d.mts +2 -1
- package/resources/index.d.mts.map +1 -1
- package/resources/index.d.ts +2 -1
- package/resources/index.d.ts.map +1 -1
- package/resources/index.js +5 -3
- package/resources/index.js.map +1 -1
- package/resources/index.mjs +2 -1
- package/resources/index.mjs.map +1 -1
- package/resources/legal-entities.d.mts +1 -1
- package/resources/legal-entities.d.mts.map +1 -1
- package/resources/legal-entities.d.ts +1 -1
- package/resources/legal-entities.d.ts.map +1 -1
- package/resources/payroll-runs/index.d.mts +3 -0
- package/resources/payroll-runs/index.d.mts.map +1 -0
- package/resources/payroll-runs/index.d.ts +3 -0
- package/resources/payroll-runs/index.d.ts.map +1 -0
- package/resources/payroll-runs/index.js +9 -0
- package/resources/payroll-runs/index.js.map +1 -0
- package/resources/payroll-runs/index.mjs +4 -0
- package/resources/payroll-runs/index.mjs.map +1 -0
- package/resources/payroll-runs/payroll-runs.d.mts +199 -0
- package/resources/payroll-runs/payroll-runs.d.mts.map +1 -0
- package/resources/payroll-runs/payroll-runs.d.ts +199 -0
- package/resources/payroll-runs/payroll-runs.d.ts.map +1 -0
- package/resources/payroll-runs/payroll-runs.js +40 -0
- package/resources/payroll-runs/payroll-runs.js.map +1 -0
- package/resources/payroll-runs/payroll-runs.mjs +35 -0
- package/resources/payroll-runs/payroll-runs.mjs.map +1 -0
- package/resources/payroll-runs/worker-payroll-records.d.mts +200 -0
- package/resources/payroll-runs/worker-payroll-records.d.mts.map +1 -0
- package/resources/payroll-runs/worker-payroll-records.d.ts +200 -0
- package/resources/payroll-runs/worker-payroll-records.d.ts.map +1 -0
- package/resources/payroll-runs/worker-payroll-records.js +23 -0
- package/resources/payroll-runs/worker-payroll-records.js.map +1 -0
- package/resources/payroll-runs/worker-payroll-records.mjs +19 -0
- package/resources/payroll-runs/worker-payroll-records.mjs.map +1 -0
- package/resources/payroll-runs.d.mts +1 -194
- package/resources/payroll-runs.d.mts.map +1 -1
- package/resources/payroll-runs.d.ts +1 -194
- package/resources/payroll-runs.d.ts.map +1 -1
- package/resources/payroll-runs.js +2 -28
- package/resources/payroll-runs.js.map +1 -1
- package/resources/payroll-runs.mjs +1 -26
- package/resources/payroll-runs.mjs.map +1 -1
- package/src/client.ts +27 -7
- package/src/internal/utils/log.ts +2 -0
- package/src/lib/manifest/category.ts +7 -5
- package/src/lib/manifest/custom-object-field-section.ts +7 -0
- package/src/lib/manifest/custom-object-page-layout.ts +225 -18
- package/src/lib/manifest/custom-object.ts +30 -1
- package/src/lib/manifest/field.ts +29 -3
- package/src/lib/manifest/index.ts +2 -0
- package/src/lib/manifest/manifest-builder.ts +1 -1
- package/src/lib/manifest/manifest-function.ts +8 -2
- package/src/lib/sdui/actions.ts +23 -10
- package/src/lib/sdui/component.ts +84 -44
- package/src/lib/sdui/components.ts +1627 -212
- package/src/lib/sdui/directives.ts +4 -1
- package/src/lib/sdui/index.ts +565 -200
- package/src/lib/sdui/propTypes.ts +8470 -0
- package/src/lib/sdui/render.ts +13 -5
- package/src/lib/sdui/state.ts +8 -2
- package/src/resources/company-legal-entities.ts +237 -0
- package/src/resources/earning-types.ts +1 -7
- package/src/resources/earnings-inputs.ts +1 -1
- package/src/resources/index.ts +8 -1
- package/src/resources/legal-entities.ts +1 -1
- package/src/resources/payroll-runs/index.ts +15 -0
- package/src/resources/payroll-runs/payroll-runs.ts +270 -0
- package/src/resources/payroll-runs/worker-payroll-records.ts +255 -0
- package/src/resources/payroll-runs.ts +1 -249
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { generateId } from './_helpers';
|
|
2
|
+
import { CustomObjectFieldSection } from './custom-object-field-section';
|
|
2
3
|
import type { CustomObject } from './custom-object';
|
|
3
4
|
import type { Field } from './field';
|
|
4
5
|
|
|
@@ -65,10 +66,8 @@ export interface HeaderEdits {
|
|
|
65
66
|
// tab_edits — TabEditsDTO + section/tab DTOs
|
|
66
67
|
// ---------------------------------------------------------------------------
|
|
67
68
|
|
|
68
|
-
/**
|
|
69
|
-
export interface
|
|
70
|
-
/** RQL name of the field to display. Required. */
|
|
71
|
-
fieldRqlName: string;
|
|
69
|
+
/** Options for one field entry inside a `fields_section`. */
|
|
70
|
+
export interface SectionFieldOptions {
|
|
72
71
|
/** Hide this field from view. Optional. */
|
|
73
72
|
hidden?: boolean;
|
|
74
73
|
/** Allow inline editing of this field. Optional. */
|
|
@@ -77,15 +76,28 @@ export interface SectionField {
|
|
|
77
76
|
movementOption?: 'tab' | 'free';
|
|
78
77
|
}
|
|
79
78
|
|
|
79
|
+
/** A typed field entry inside a `fields_section`. */
|
|
80
|
+
export interface SectionFieldRef extends SectionFieldOptions {
|
|
81
|
+
/** Field to display. Required. */
|
|
82
|
+
field: Field;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** One field entry inside a `fields_section`. */
|
|
86
|
+
export type SectionField = Field | SectionFieldRef;
|
|
87
|
+
|
|
80
88
|
/** A standard field-grid section inside a tab. */
|
|
81
89
|
export interface FieldsSection {
|
|
82
90
|
type: 'fields_section';
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
|
|
91
|
+
/** Field section metadata for the fields displayed in this layout section. Required. */
|
|
92
|
+
section: CustomObjectFieldSection;
|
|
93
|
+
/** Stable layout key for this section. Optional. Defaults to the field section id. */
|
|
94
|
+
key?: string;
|
|
95
|
+
/** Display name of the layout section. Optional. Defaults to the field section name. */
|
|
96
|
+
name?: string;
|
|
87
97
|
/** Fields to display in this section. Required. */
|
|
88
98
|
fields: SectionField[];
|
|
99
|
+
/** Column layout for the fields. Optional. @default `'responsive'` (backend default). */
|
|
100
|
+
layout?: SectionFieldsLayout;
|
|
89
101
|
}
|
|
90
102
|
|
|
91
103
|
/**
|
|
@@ -255,6 +267,197 @@ export interface CustomObjectPageLayoutProps {
|
|
|
255
267
|
blueprintKey?: string;
|
|
256
268
|
}
|
|
257
269
|
|
|
270
|
+
interface SerializedSectionField extends SectionFieldOptions {
|
|
271
|
+
fieldRqlName: string;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
interface SerializedFieldsSection {
|
|
275
|
+
type: 'fields_section';
|
|
276
|
+
key: string;
|
|
277
|
+
name: string;
|
|
278
|
+
fields: SerializedSectionField[];
|
|
279
|
+
layout?: SectionFieldsLayout;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
interface SerializedTabWithSections {
|
|
283
|
+
type: 'tab_with_sections';
|
|
284
|
+
key: string;
|
|
285
|
+
name: string;
|
|
286
|
+
sections: SerializedFieldsSection[];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
type SerializedPageLayoutTab = SerializedTabWithSections | CustomTab;
|
|
290
|
+
|
|
291
|
+
interface SerializedSystemSectionEdit extends Omit<SystemSectionEdit, 'newFields'> {
|
|
292
|
+
newFields?: SerializedSectionField[];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
interface SerializedSystemTabEdit extends Omit<SystemTabEdit, 'newSections' | 'systemSectionEdits'> {
|
|
296
|
+
newSections?: SerializedFieldsSection[];
|
|
297
|
+
systemSectionEdits?: Record<string, SerializedSystemSectionEdit>;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
interface SerializedTabEdits extends Omit<TabEdits, 'newTabs' | 'systemTabEdits'> {
|
|
301
|
+
newTabs?: SerializedPageLayoutTab[];
|
|
302
|
+
systemTabEdits?: Record<string, SerializedSystemTabEdit>;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function isField(value: unknown): value is Field {
|
|
306
|
+
const candidate = value as {
|
|
307
|
+
getApiName?: unknown;
|
|
308
|
+
getCustomObjectApiName?: unknown;
|
|
309
|
+
getSectionId?: unknown;
|
|
310
|
+
_assignSection?: unknown;
|
|
311
|
+
} | null;
|
|
312
|
+
return (
|
|
313
|
+
candidate != null &&
|
|
314
|
+
typeof candidate.getApiName === 'function' &&
|
|
315
|
+
typeof candidate.getCustomObjectApiName === 'function' &&
|
|
316
|
+
typeof candidate.getSectionId === 'function' &&
|
|
317
|
+
typeof candidate._assignSection === 'function'
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function isFieldRef(value: SectionField): value is SectionFieldRef {
|
|
322
|
+
const candidate = value as { field?: unknown } | null;
|
|
323
|
+
return candidate != null && typeof candidate === 'object' && isField(candidate.field);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function assertSameObject(customObject: CustomObject, field: Field, sectionKey: string): void {
|
|
327
|
+
if (field.getCustomObjectApiName() !== customObject.getApiName()) {
|
|
328
|
+
throw new Error(
|
|
329
|
+
`CustomObjectPageLayout fields_section "${sectionKey}" references field "${field.getApiName()}" ` +
|
|
330
|
+
`from "${field.getCustomObjectApiName()}", but the layout belongs to "${customObject.getApiName()}".`,
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function assertSectionBelongsTo(customObject: CustomObject, section: CustomObjectFieldSection): void {
|
|
336
|
+
if (section.getModelApiName() !== customObject.getApiName()) {
|
|
337
|
+
throw new Error(
|
|
338
|
+
`CustomObjectPageLayout section "${section.getSectionId()}" belongs to "${section.getModelApiName()}", ` +
|
|
339
|
+
`but the layout belongs to "${customObject.getApiName()}".`,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function toSerializedSectionField(
|
|
345
|
+
fieldRqlName: string,
|
|
346
|
+
options: SectionFieldOptions = {},
|
|
347
|
+
): SerializedSectionField {
|
|
348
|
+
const normalized: SerializedSectionField = { fieldRqlName };
|
|
349
|
+
if (options.hidden != null) normalized.hidden = options.hidden;
|
|
350
|
+
if (options.editable != null) normalized.editable = options.editable;
|
|
351
|
+
if (options.movementOption != null) normalized.movementOption = options.movementOption;
|
|
352
|
+
return normalized;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function normalizeSectionField(
|
|
356
|
+
customObject: CustomObject,
|
|
357
|
+
section: CustomObjectFieldSection | string,
|
|
358
|
+
sectionKey: string,
|
|
359
|
+
entry: SectionField,
|
|
360
|
+
): SerializedSectionField {
|
|
361
|
+
if (isField(entry)) {
|
|
362
|
+
assertSameObject(customObject, entry, sectionKey);
|
|
363
|
+
entry._assignSection(section);
|
|
364
|
+
return toSerializedSectionField(entry.getApiName());
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (isFieldRef(entry)) {
|
|
368
|
+
assertSameObject(customObject, entry.field, sectionKey);
|
|
369
|
+
entry.field._assignSection(section);
|
|
370
|
+
return toSerializedSectionField(entry.field.getApiName(), entry);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
throw new Error(
|
|
374
|
+
`CustomObjectPageLayout fields_section "${sectionKey}" has an invalid field entry; ` +
|
|
375
|
+
'pass a Field instance or { field }.',
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function normalizeFieldsSection(
|
|
380
|
+
customObject: CustomObject,
|
|
381
|
+
input: PageLayoutSection,
|
|
382
|
+
): SerializedFieldsSection {
|
|
383
|
+
assertSectionBelongsTo(customObject, input.section);
|
|
384
|
+
const key = input.key ?? input.section.getSectionId();
|
|
385
|
+
const name = input.name ?? input.section.getName();
|
|
386
|
+
const normalized: SerializedFieldsSection = {
|
|
387
|
+
key,
|
|
388
|
+
name,
|
|
389
|
+
type: 'fields_section',
|
|
390
|
+
fields: input.fields.map((field) => normalizeSectionField(customObject, input.section, key, field)),
|
|
391
|
+
};
|
|
392
|
+
if (input.layout != null) normalized.layout = input.layout;
|
|
393
|
+
return normalized;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function normalizeTab(customObject: CustomObject, tab: PageLayoutTab): SerializedPageLayoutTab {
|
|
397
|
+
if (tab.type === 'custom_tab') return tab;
|
|
398
|
+
return {
|
|
399
|
+
...tab,
|
|
400
|
+
sections: tab.sections.map((section) => normalizeFieldsSection(customObject, section)),
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function normalizeSystemSectionEdit(
|
|
405
|
+
customObject: CustomObject,
|
|
406
|
+
tabKey: string,
|
|
407
|
+
sectionKey: string,
|
|
408
|
+
edit: SystemSectionEdit,
|
|
409
|
+
): SerializedSystemSectionEdit {
|
|
410
|
+
const { newFields: _newFields, ...rest } = edit;
|
|
411
|
+
const normalized: SerializedSystemSectionEdit = { ...rest };
|
|
412
|
+
if (edit.newFields != null) {
|
|
413
|
+
normalized.newFields = edit.newFields.map((field) =>
|
|
414
|
+
normalizeSectionField(customObject, sectionKey, `${tabKey}.${sectionKey}`, field),
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
return normalized;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function normalizeSystemTabEdit(
|
|
421
|
+
customObject: CustomObject,
|
|
422
|
+
tabKey: string,
|
|
423
|
+
edit: SystemTabEdit,
|
|
424
|
+
): SerializedSystemTabEdit {
|
|
425
|
+
const { newSections: _newSections, systemSectionEdits: _systemSectionEdits, ...rest } = edit;
|
|
426
|
+
const normalized: SerializedSystemTabEdit = { ...rest };
|
|
427
|
+
if (edit.newSections != null) {
|
|
428
|
+
normalized.newSections = edit.newSections.map((section) => normalizeFieldsSection(customObject, section));
|
|
429
|
+
}
|
|
430
|
+
if (edit.systemSectionEdits != null) {
|
|
431
|
+
const systemSectionEdits: Record<string, SerializedSystemSectionEdit> = {};
|
|
432
|
+
for (const [sectionKey, sectionEdit] of Object.entries(edit.systemSectionEdits)) {
|
|
433
|
+
systemSectionEdits[sectionKey] = normalizeSystemSectionEdit(
|
|
434
|
+
customObject,
|
|
435
|
+
tabKey,
|
|
436
|
+
sectionKey,
|
|
437
|
+
sectionEdit,
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
normalized.systemSectionEdits = systemSectionEdits;
|
|
441
|
+
}
|
|
442
|
+
return normalized;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function normalizeTabEdits(customObject: CustomObject, tabEdits: TabEdits): SerializedTabEdits {
|
|
446
|
+
const { newTabs: _newTabs, systemTabEdits: _systemTabEdits, ...rest } = tabEdits;
|
|
447
|
+
const normalized: SerializedTabEdits = { ...rest };
|
|
448
|
+
if (tabEdits.newTabs != null) {
|
|
449
|
+
normalized.newTabs = tabEdits.newTabs.map((tab) => normalizeTab(customObject, tab));
|
|
450
|
+
}
|
|
451
|
+
if (tabEdits.systemTabEdits != null) {
|
|
452
|
+
const systemTabEdits: Record<string, SerializedSystemTabEdit> = {};
|
|
453
|
+
for (const [tabKey, tabEdit] of Object.entries(tabEdits.systemTabEdits)) {
|
|
454
|
+
systemTabEdits[tabKey] = normalizeSystemTabEdit(customObject, tabKey, tabEdit);
|
|
455
|
+
}
|
|
456
|
+
normalized.systemTabEdits = systemTabEdits;
|
|
457
|
+
}
|
|
458
|
+
return normalized;
|
|
459
|
+
}
|
|
460
|
+
|
|
258
461
|
// ---------------------------------------------------------------------------
|
|
259
462
|
// Class
|
|
260
463
|
// ---------------------------------------------------------------------------
|
|
@@ -283,11 +486,11 @@ export interface CustomObjectPageLayoutProps {
|
|
|
283
486
|
* newTabs: [{
|
|
284
487
|
* key: 'member', name: 'Member', type: 'tab_with_sections',
|
|
285
488
|
* sections: [{
|
|
286
|
-
* key: 'profile',
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
* ],
|
|
489
|
+
* key: 'profile',
|
|
490
|
+
* name: 'Profile',
|
|
491
|
+
* type: 'fields_section',
|
|
492
|
+
* section: profileSection,
|
|
493
|
+
* fields: [memberFirstName, memberEmail],
|
|
291
494
|
* }],
|
|
292
495
|
* }],
|
|
293
496
|
* systemTabEdits: {},
|
|
@@ -304,7 +507,7 @@ export class CustomObjectPageLayout {
|
|
|
304
507
|
private readonly _apiName: string;
|
|
305
508
|
private readonly _customObjectApiName: string;
|
|
306
509
|
private readonly _name: string;
|
|
307
|
-
private readonly _tabEdits:
|
|
510
|
+
private readonly _tabEdits: SerializedTabEdits;
|
|
308
511
|
private readonly _headerEdits: HeaderEdits;
|
|
309
512
|
private readonly _visibilityConditions: VisibilityConditions;
|
|
310
513
|
private readonly _blueprintKey: string | undefined;
|
|
@@ -321,7 +524,7 @@ export class CustomObjectPageLayout {
|
|
|
321
524
|
this._apiName = props.apiName;
|
|
322
525
|
this._customObjectApiName = customObject.getApiName();
|
|
323
526
|
this._name = props.name;
|
|
324
|
-
this._tabEdits = props.tabEdits ?? {};
|
|
527
|
+
this._tabEdits = normalizeTabEdits(customObject, props.tabEdits ?? {});
|
|
325
528
|
this._headerEdits = props.headerEdits ?? {};
|
|
326
529
|
this._visibilityConditions = props.visibilityConditions ?? {};
|
|
327
530
|
this._blueprintKey = props.blueprintKey;
|
|
@@ -396,6 +599,11 @@ export class CustomObjectPageLayout {
|
|
|
396
599
|
): CustomObjectPageLayout {
|
|
397
600
|
const coApiName = customObject.getApiName();
|
|
398
601
|
const coName = customObject.getName();
|
|
602
|
+
const sectionPrefix = coApiName.replace(/__c$/, '').replace(/[^a-zA-Z0-9]+/g, '_');
|
|
603
|
+
const generalSection = new CustomObjectFieldSection(customObject, {
|
|
604
|
+
sectionId: `sec_${sectionPrefix}_general`,
|
|
605
|
+
name: 'General',
|
|
606
|
+
});
|
|
399
607
|
return new CustomObjectPageLayout(customObject, {
|
|
400
608
|
apiName: 'default',
|
|
401
609
|
name: `${coName} Layout`,
|
|
@@ -410,9 +618,8 @@ export class CustomObjectPageLayout {
|
|
|
410
618
|
key: 'general',
|
|
411
619
|
name: 'General',
|
|
412
620
|
type: 'fields_section',
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
})),
|
|
621
|
+
section: generalSection,
|
|
622
|
+
fields: props.fields ?? [],
|
|
416
623
|
},
|
|
417
624
|
],
|
|
418
625
|
},
|
|
@@ -215,6 +215,21 @@ export interface CustomObjectProps {
|
|
|
215
215
|
* Overrides for the auto-number name field — controls the format string and starting number.
|
|
216
216
|
* Leave `null` to use the default name field behavior.
|
|
217
217
|
*
|
|
218
|
+
* Example:
|
|
219
|
+
* ```ts
|
|
220
|
+
* nameFieldDetails: {
|
|
221
|
+
* is_autonumber_field: true,
|
|
222
|
+
* starting_number: 1,
|
|
223
|
+
* autonumber_format: 'EXP-{0000}',
|
|
224
|
+
* }
|
|
225
|
+
* ```
|
|
226
|
+
*
|
|
227
|
+
* @remarks
|
|
228
|
+
* The backend auto-appends `{0000}` to `autonumber_format` if the format string
|
|
229
|
+
* doesn't already contain a sequence token — so `'EXP-'` becomes `'EXP-{0000}'`
|
|
230
|
+
* on the wire. Include the token explicitly when you want a different width
|
|
231
|
+
* (e.g. `'EXP-{00000}'`).
|
|
232
|
+
*
|
|
218
233
|
* Optional. @default null
|
|
219
234
|
*/
|
|
220
235
|
nameFieldDetails?: Record<string, any> | null;
|
|
@@ -241,7 +256,7 @@ export interface CustomObjectProps {
|
|
|
241
256
|
* name: 'Gym Membership Management',
|
|
242
257
|
* });
|
|
243
258
|
*
|
|
244
|
-
* const gymCategory = new Category(manifest, { apiName: 'gym__c', name: 'Gym' });
|
|
259
|
+
* const gymCategory = new Category(manifest, { apiName: 'gym__c', name: 'Gym', description: 'Gym domain' });
|
|
245
260
|
*
|
|
246
261
|
* const memberObj = new CustomObject(manifest, {
|
|
247
262
|
* apiName: 'gym_member__c',
|
|
@@ -266,6 +281,20 @@ export interface CustomObjectProps {
|
|
|
266
281
|
* with `DEPENDENCY_ERROR: Page layout config for CO (api_name=...) not found in package
|
|
267
282
|
* configs. CO requires at least one page layout in the package.`
|
|
268
283
|
* Use {@link CustomObjectPageLayout.basic} for a minimal single-tab layout.
|
|
284
|
+
*
|
|
285
|
+
* **Consider defining at least one {@link ListViewDef}.** Without a list view,
|
|
286
|
+
* the records page has nothing to display by default — users land on an empty grid.
|
|
287
|
+
* Consider shipping a "default" view with the columns most users want to see
|
|
288
|
+
* (typically `name`, the status / category field, owner, and `created_at`), sorted
|
|
289
|
+
* however the records are most commonly browsed.
|
|
290
|
+
*
|
|
291
|
+
* **Consider system fields before authoring new ones.** Every CO ships with
|
|
292
|
+
* `owner_role`, `created_by`, and `last_modified_by` (all `Employee` references),
|
|
293
|
+
* plus `name`, `created_at`, `updated_at`, `external_id`, and `id`. For any
|
|
294
|
+
* "the employee responsible for this record" semantics, consider using
|
|
295
|
+
* `owner_role` rather than adding a new {@link LookupField} targeting `'role'`.
|
|
296
|
+
* `owner_role` drives Rippling's record-level permissions, so a parallel field
|
|
297
|
+
* can fragment ownership and complicate the permission story.
|
|
269
298
|
*/
|
|
270
299
|
export class CustomObject implements ManifestScope {
|
|
271
300
|
private readonly _scope: ManifestScope;
|
|
@@ -130,7 +130,7 @@ export class Field {
|
|
|
130
130
|
private readonly _rqlFormula: string | null;
|
|
131
131
|
private readonly _formulaAttrMetas: Record<string, any> | null;
|
|
132
132
|
private readonly _derivedFieldFormula: string | null;
|
|
133
|
-
private
|
|
133
|
+
private _section: string | null;
|
|
134
134
|
private readonly _summarySpec: SummarySpec | null;
|
|
135
135
|
private readonly _derivedAggregatedFieldStatic: Record<string, any> | null;
|
|
136
136
|
|
|
@@ -202,6 +202,14 @@ export class Field {
|
|
|
202
202
|
isSummary(): boolean {
|
|
203
203
|
return this._summarySpec != null;
|
|
204
204
|
}
|
|
205
|
+
/** Returns the field section id assigned to this field, if any. */
|
|
206
|
+
getSectionId(): string | null {
|
|
207
|
+
return this._section;
|
|
208
|
+
}
|
|
209
|
+
/** @internal Assigns this field to a field section during page-layout normalization. */
|
|
210
|
+
_assignSection(section: CustomObjectFieldSection | string): void {
|
|
211
|
+
this._section = typeof section === 'string' ? section : section.getSectionId();
|
|
212
|
+
}
|
|
205
213
|
|
|
206
214
|
// ── Serialization ──────────────────────────────────────────────────────
|
|
207
215
|
|
|
@@ -438,7 +446,12 @@ export interface SelectFieldProps extends CommonFieldProps {
|
|
|
438
446
|
/** Whether a value is required on save. Optional. */
|
|
439
447
|
required?: boolean;
|
|
440
448
|
}
|
|
441
|
-
/**
|
|
449
|
+
/**
|
|
450
|
+
* Multi-select dropdown field. Renders as a dropdown that allows multiple
|
|
451
|
+
* options to be selected at once.
|
|
452
|
+
*
|
|
453
|
+
* Use {@link RadioField} instead when only one option should be selectable.
|
|
454
|
+
*/
|
|
442
455
|
export class SelectField extends Field {
|
|
443
456
|
constructor(customObject: CustomObject, props: SelectFieldProps) {
|
|
444
457
|
super(customObject, { ...props, fieldType: 'SELECT' });
|
|
@@ -456,7 +469,11 @@ export interface RadioFieldProps extends CommonFieldProps {
|
|
|
456
469
|
/** Whether a value is required on save. Optional. */
|
|
457
470
|
required?: boolean;
|
|
458
471
|
}
|
|
459
|
-
/**
|
|
472
|
+
/**
|
|
473
|
+
* Single-select radio button group field. Exactly one option can be selected.
|
|
474
|
+
*
|
|
475
|
+
* Use {@link SelectField} instead when multiple options should be selectable.
|
|
476
|
+
*/
|
|
460
477
|
export class RadioField extends Field {
|
|
461
478
|
constructor(customObject: CustomObject, props: RadioFieldProps) {
|
|
462
479
|
super(customObject, { ...props, fieldType: 'RADIO' });
|
|
@@ -530,6 +547,15 @@ export interface LookupFieldProps extends CommonFieldProps {
|
|
|
530
547
|
*
|
|
531
548
|
* Use {@link ParentChildField} instead when the relationship should create a
|
|
532
549
|
* parent-child hierarchy with rollup support.
|
|
550
|
+
*
|
|
551
|
+
* @remarks
|
|
552
|
+
* **For "the employee responsible for this record" semantics, consider the built-in
|
|
553
|
+
* `owner_role` standard field instead of a new `LookupField` to `'role'`.** Every
|
|
554
|
+
* `CustomObject` ships with `owner_role` (an Employee reference) plus `created_by`
|
|
555
|
+
* and `last_modified_by`. `owner_role` drives Rippling's record-level permissions,
|
|
556
|
+
* so a parallel custom employee field can fragment ownership and complicate the
|
|
557
|
+
* permission story. Consider a new `LookupField` to `'role'` when you need a
|
|
558
|
+
* *second*, non-owner employee reference (e.g. an "approver", a "manager on record").
|
|
533
559
|
*/
|
|
534
560
|
export class LookupField extends Field {
|
|
535
561
|
constructor(customObject: CustomObject, props: LookupFieldProps) {
|
|
@@ -63,7 +63,7 @@ export interface ManifestBuilderProps {
|
|
|
63
63
|
* description: 'Members, trainers, schedules, and check-in',
|
|
64
64
|
* });
|
|
65
65
|
*
|
|
66
|
-
* const category = new Category(manifest, { apiName: 'gym__c', name: 'Gym' });
|
|
66
|
+
* const category = new Category(manifest, { apiName: 'gym__c', name: 'Gym', description: 'Gym domain' });
|
|
67
67
|
* const memberObj = new CustomObject(manifest, { apiName: 'gym_member__c', name: 'Member', category });
|
|
68
68
|
* // ... define fields, rules, list views, etc. ...
|
|
69
69
|
*
|
|
@@ -68,8 +68,14 @@ export interface ManifestFunctionProps {
|
|
|
68
68
|
* Defines a serverless function and registers it with the manifest.
|
|
69
69
|
*
|
|
70
70
|
* A `ManifestFunction` packages TypeScript handler code that runs on Rippling's
|
|
71
|
-
* serverless infrastructure.
|
|
72
|
-
*
|
|
71
|
+
* serverless infrastructure. The handler must export `onRipplingEvent`.
|
|
72
|
+
*
|
|
73
|
+
* Functions have two primary uses:
|
|
74
|
+
* 1. **Authoring SDUI for Custom Apps** — the function returns an SDUI spec
|
|
75
|
+
* (built with `@rippling/sdui-builder`) that renders as a page tab. Wrap the
|
|
76
|
+
* function in an {@link SduiPage} and reference it from an {@link App}.
|
|
77
|
+
* 2. **Workflow / event handlers** — functions invoked from rules, automations,
|
|
78
|
+
* or callbacks that do not render UI.
|
|
73
79
|
*
|
|
74
80
|
* This class is named `ManifestFunction` to avoid collision with the JavaScript
|
|
75
81
|
* built-in `Function`. It is also re-exported as `Function` from the barrel for
|
package/src/lib/sdui/actions.ts
CHANGED
|
@@ -12,15 +12,19 @@ export function set(ref: unknown, value: unknown): ActionBinding {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function push(ref: unknown, value: unknown, opts?: { clear?: unknown }): ActionBinding {
|
|
15
|
-
const params: Record<string, unknown> = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
const params: Record<string, unknown> = {
|
|
16
|
+
statePath: extractPath(ref),
|
|
17
|
+
value,
|
|
18
|
+
...(opts?.clear ? { clearStatePath: extractPath(opts.clear) } : {}),
|
|
19
|
+
};
|
|
19
20
|
return { action: 'pushState', params };
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export function remove(ref: unknown, index: unknown): ActionBinding {
|
|
23
|
-
return {
|
|
24
|
+
return {
|
|
25
|
+
action: 'removeState',
|
|
26
|
+
params: { statePath: extractPath(ref), index },
|
|
27
|
+
};
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export function callFunction(
|
|
@@ -29,11 +33,20 @@ export function callFunction(
|
|
|
29
33
|
opts?: Record<string, unknown>,
|
|
30
34
|
): ActionBinding {
|
|
31
35
|
const { responsePath, onSuccess, onError, ...rest } = opts || {};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
// Spread rest FIRST so explicit functionId/action always win if opts
|
|
37
|
+
// happens to carry colliding keys.
|
|
38
|
+
const params: Record<string, unknown> = {
|
|
39
|
+
...rest,
|
|
40
|
+
functionId,
|
|
41
|
+
action,
|
|
42
|
+
...(responsePath !== undefined ? { responsePath } : {}),
|
|
43
|
+
};
|
|
44
|
+
const binding: ActionBinding = {
|
|
45
|
+
action: 'triggerFunction',
|
|
46
|
+
params,
|
|
47
|
+
...(onSuccess ? { onSuccess: onSuccess as ActionBinding } : {}),
|
|
48
|
+
...(onError ? { onError: onError as ActionBinding } : {}),
|
|
49
|
+
};
|
|
37
50
|
return binding;
|
|
38
51
|
}
|
|
39
52
|
|