@rippling/rippling-sdk 0.2.0-alpha.35 → 0.2.0-alpha.37

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.
Files changed (149) hide show
  1. package/client.d.mts +13 -1
  2. package/client.d.mts.map +1 -1
  3. package/client.d.ts +13 -1
  4. package/client.d.ts.map +1 -1
  5. package/client.js +13 -1
  6. package/client.js.map +1 -1
  7. package/client.mjs +13 -1
  8. package/client.mjs.map +1 -1
  9. package/examples/.keep +4 -0
  10. package/examples/manifest/dental-practice-management/dental-practice-management.ts +638 -0
  11. package/examples/manifest/dental-practice-management/manifest.json +1870 -0
  12. package/examples/manifest/gym-membership-management/gym-membership-management.ts +746 -0
  13. package/examples/manifest/gym-membership-management/manifest.json +1975 -0
  14. package/examples/sdui/sdui-crud-todo/rippling-function.ts +367 -0
  15. package/examples/sdui/sdui-crud-todo/sdui-crud-todo.ts +282 -0
  16. package/examples/sdui/sdui-showcase/rippling-function.ts +381 -0
  17. package/examples/sdui/sdui-showcase/sdui-showcase.ts +351 -0
  18. package/examples/sdui/sdui-tab-multi-view/rippling-function.ts +1061 -0
  19. package/examples/sdui/sdui-tab-multi-view/sdui-tab-multi-view.ts +924 -0
  20. package/examples/sdui/vote/rippling-function.ts +330 -0
  21. package/examples/sdui/vote/vote.ts +298 -0
  22. package/internal/utils/log.d.mts.map +1 -1
  23. package/internal/utils/log.d.ts.map +1 -1
  24. package/internal/utils/log.js +2 -0
  25. package/internal/utils/log.js.map +1 -1
  26. package/internal/utils/log.mjs +2 -0
  27. package/internal/utils/log.mjs.map +1 -1
  28. package/lib/manifest/custom-object-field-section.d.mts +4 -0
  29. package/lib/manifest/custom-object-field-section.d.mts.map +1 -1
  30. package/lib/manifest/custom-object-field-section.d.ts +4 -0
  31. package/lib/manifest/custom-object-field-section.d.ts.map +1 -1
  32. package/lib/manifest/custom-object-field-section.js +6 -0
  33. package/lib/manifest/custom-object-field-section.js.map +1 -1
  34. package/lib/manifest/custom-object-field-section.mjs +6 -0
  35. package/lib/manifest/custom-object-field-section.mjs.map +1 -1
  36. package/lib/manifest/custom-object-page-layout.d.mts +21 -13
  37. package/lib/manifest/custom-object-page-layout.d.mts.map +1 -1
  38. package/lib/manifest/custom-object-page-layout.d.ts +21 -13
  39. package/lib/manifest/custom-object-page-layout.d.ts.map +1 -1
  40. package/lib/manifest/custom-object-page-layout.js +119 -9
  41. package/lib/manifest/custom-object-page-layout.js.map +1 -1
  42. package/lib/manifest/custom-object-page-layout.mjs +119 -9
  43. package/lib/manifest/custom-object-page-layout.mjs.map +1 -1
  44. package/lib/manifest/field.d.mts +5 -1
  45. package/lib/manifest/field.d.mts.map +1 -1
  46. package/lib/manifest/field.d.ts +5 -1
  47. package/lib/manifest/field.d.ts.map +1 -1
  48. package/lib/manifest/field.js +8 -0
  49. package/lib/manifest/field.js.map +1 -1
  50. package/lib/manifest/field.mjs +8 -0
  51. package/lib/manifest/field.mjs.map +1 -1
  52. package/lib/manifest/index.d.mts +1 -1
  53. package/lib/manifest/index.d.mts.map +1 -1
  54. package/lib/manifest/index.d.ts +1 -1
  55. package/lib/manifest/index.d.ts.map +1 -1
  56. package/lib/manifest/index.js.map +1 -1
  57. package/lib/manifest/index.mjs.map +1 -1
  58. package/package.json +11 -1
  59. package/resources/company-legal-entities.d.mts +180 -0
  60. package/resources/company-legal-entities.d.mts.map +1 -0
  61. package/resources/company-legal-entities.d.ts +180 -0
  62. package/resources/company-legal-entities.d.ts.map +1 -0
  63. package/resources/company-legal-entities.js +30 -0
  64. package/resources/company-legal-entities.js.map +1 -0
  65. package/resources/company-legal-entities.mjs +26 -0
  66. package/resources/company-legal-entities.mjs.map +1 -0
  67. package/resources/company-legal-entity-workers.d.mts +79 -0
  68. package/resources/company-legal-entity-workers.d.mts.map +1 -0
  69. package/resources/company-legal-entity-workers.d.ts +79 -0
  70. package/resources/company-legal-entity-workers.d.ts.map +1 -0
  71. package/resources/company-legal-entity-workers.js +30 -0
  72. package/resources/company-legal-entity-workers.js.map +1 -0
  73. package/resources/company-legal-entity-workers.mjs +26 -0
  74. package/resources/company-legal-entity-workers.mjs.map +1 -0
  75. package/resources/earning-types.d.mts +1 -6
  76. package/resources/earning-types.d.mts.map +1 -1
  77. package/resources/earning-types.d.ts +1 -6
  78. package/resources/earning-types.d.ts.map +1 -1
  79. package/resources/earning-types.js +1 -1
  80. package/resources/earning-types.mjs +1 -1
  81. package/resources/earnings-inputs.d.mts +1 -1
  82. package/resources/earnings-inputs.d.ts +1 -1
  83. package/resources/earnings-inputs.js +1 -1
  84. package/resources/earnings-inputs.mjs +1 -1
  85. package/resources/index.d.mts +3 -1
  86. package/resources/index.d.mts.map +1 -1
  87. package/resources/index.d.ts +3 -1
  88. package/resources/index.d.ts.map +1 -1
  89. package/resources/index.js +7 -3
  90. package/resources/index.js.map +1 -1
  91. package/resources/index.mjs +3 -1
  92. package/resources/index.mjs.map +1 -1
  93. package/resources/legal-entities.d.mts +1 -1
  94. package/resources/legal-entities.d.mts.map +1 -1
  95. package/resources/legal-entities.d.ts +1 -1
  96. package/resources/legal-entities.d.ts.map +1 -1
  97. package/resources/payroll-runs/index.d.mts +3 -0
  98. package/resources/payroll-runs/index.d.mts.map +1 -0
  99. package/resources/payroll-runs/index.d.ts +3 -0
  100. package/resources/payroll-runs/index.d.ts.map +1 -0
  101. package/resources/payroll-runs/index.js +9 -0
  102. package/resources/payroll-runs/index.js.map +1 -0
  103. package/resources/payroll-runs/index.mjs +4 -0
  104. package/resources/payroll-runs/index.mjs.map +1 -0
  105. package/resources/payroll-runs/payroll-runs.d.mts +199 -0
  106. package/resources/payroll-runs/payroll-runs.d.mts.map +1 -0
  107. package/resources/payroll-runs/payroll-runs.d.ts +199 -0
  108. package/resources/payroll-runs/payroll-runs.d.ts.map +1 -0
  109. package/resources/payroll-runs/payroll-runs.js +40 -0
  110. package/resources/payroll-runs/payroll-runs.js.map +1 -0
  111. package/resources/payroll-runs/payroll-runs.mjs +35 -0
  112. package/resources/payroll-runs/payroll-runs.mjs.map +1 -0
  113. package/resources/payroll-runs/worker-payroll-records.d.mts +200 -0
  114. package/resources/payroll-runs/worker-payroll-records.d.mts.map +1 -0
  115. package/resources/payroll-runs/worker-payroll-records.d.ts +200 -0
  116. package/resources/payroll-runs/worker-payroll-records.d.ts.map +1 -0
  117. package/resources/payroll-runs/worker-payroll-records.js +23 -0
  118. package/resources/payroll-runs/worker-payroll-records.js.map +1 -0
  119. package/resources/payroll-runs/worker-payroll-records.mjs +19 -0
  120. package/resources/payroll-runs/worker-payroll-records.mjs.map +1 -0
  121. package/resources/payroll-runs.d.mts +1 -194
  122. package/resources/payroll-runs.d.mts.map +1 -1
  123. package/resources/payroll-runs.d.ts +1 -194
  124. package/resources/payroll-runs.d.ts.map +1 -1
  125. package/resources/payroll-runs.js +2 -28
  126. package/resources/payroll-runs.js.map +1 -1
  127. package/resources/payroll-runs.mjs +1 -26
  128. package/resources/payroll-runs.mjs.map +1 -1
  129. package/src/client.ts +47 -7
  130. package/src/internal/utils/log.ts +2 -0
  131. package/src/lib/manifest/custom-object-field-section.ts +7 -0
  132. package/src/lib/manifest/custom-object-page-layout.ts +220 -18
  133. package/src/lib/manifest/field.ts +9 -1
  134. package/src/lib/manifest/index.ts +2 -0
  135. package/src/resources/company-legal-entities.ts +237 -0
  136. package/src/resources/company-legal-entity-workers.ts +115 -0
  137. package/src/resources/earning-types.ts +1 -7
  138. package/src/resources/earnings-inputs.ts +1 -1
  139. package/src/resources/index.ts +15 -1
  140. package/src/resources/legal-entities.ts +1 -1
  141. package/src/resources/payroll-runs/index.ts +15 -0
  142. package/src/resources/payroll-runs/payroll-runs.ts +270 -0
  143. package/src/resources/payroll-runs/worker-payroll-records.ts +255 -0
  144. package/src/resources/payroll-runs.ts +1 -249
  145. package/src/version.ts +1 -1
  146. package/version.d.mts +1 -1
  147. package/version.d.ts +1 -1
  148. package/version.js +1 -1
  149. package/version.mjs +1 -1
@@ -0,0 +1,367 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * PASTE THIS ENTIRE FILE INTO THE RIPPLING FUNCTION EDITOR TO PREVIEW RENDERED
4
+ * SDUI (CUSTOM PAGE). DEPENDS ON @rippling/rippling-sdk AND @rippling/sdui-builder.
5
+ * MOCK DATA ONLY — NO CUSTOM OBJECT API NAMES; SAFE TO RENDER WITHOUT CO DEFINITIONS.
6
+ * ----------------------------------------------------------------------------
7
+ * Pair file sdui-crud-todo.ts uses import from '@rippling/rippling-sdk/lib/sdui' for local runs.
8
+ */
9
+
10
+ /*
11
+ import SDUI from '@rippling/sdui-builder';
12
+ import { FunctionContext, FunctionEvent, FunctionResponse } from '@rippling/rippling-sdk';
13
+
14
+ const INITIAL_TODOS = [
15
+ {
16
+ id: 't1',
17
+ _key: 't1',
18
+ title__c: 'Ship SDK patch',
19
+ status__c: 'In Progress',
20
+ priority__c: 'High',
21
+ notes__c: 'Regression pass on SDUI examples',
22
+ },
23
+ {
24
+ id: 't2',
25
+ _key: 't2',
26
+ title__c: 'Document Function paste flow',
27
+ status__c: 'To Do',
28
+ priority__c: 'Medium',
29
+ notes__c: 'Caps comment on rippling-function.ts',
30
+ },
31
+ {
32
+ id: 't3',
33
+ _key: 't3',
34
+ title__c: 'Weekly sync',
35
+ status__c: 'Done',
36
+ priority__c: 'Low',
37
+ notes__c: '',
38
+ },
39
+ ];
40
+
41
+ let mutableTodos = INITIAL_TODOS.map((r) => ({ ...r }));
42
+
43
+ function renderSpec(functionId: string) {
44
+ const s = SDUI.createState({
45
+ todos: mutableTodos,
46
+ selectedRow: null as unknown,
47
+ showCreateModal: false,
48
+ showEditModal: false,
49
+ form: { title: '', status: '', priority: '', notes: '' },
50
+ });
51
+
52
+ const mainColumn = SDUI.components.VStack({
53
+ gap: 'space500',
54
+ children: [
55
+ SDUI.components.VStack({
56
+ gap: 'space300',
57
+ children: [
58
+ SDUI.components.HStack({
59
+ gap: 'space200',
60
+ align: 'center',
61
+ wrap: true,
62
+ children: [
63
+ SDUI.components.Heading({
64
+ level: '2',
65
+ text: 'Team todos',
66
+ }),
67
+ SDUI.components.Badge({
68
+ text: 'Demo data',
69
+ appearance: 'NEUTRAL',
70
+ size: 'S',
71
+ }),
72
+ ],
73
+ }),
74
+ SDUI.components.Text({
75
+ text: 'Create, edit, and delete call this Function; the grid below uses sample rows until you connect Custom Objects or your own API.',
76
+ typestyle: 'bodyMd',
77
+ color: 'secondary',
78
+ maxWidth: '720px',
79
+ }),
80
+ ],
81
+ }),
82
+ SDUI.components.Layout({
83
+ width: '100%',
84
+ children: [SDUI.components.Divider({})],
85
+ }),
86
+ SDUI.components.Grid({
87
+ data: s.todos,
88
+ columns: [
89
+ { id: 'title__c', header: 'Title', data: 'title__c' },
90
+ { id: 'status__c', header: 'Status', data: 'status__c', type: 'text' },
91
+ { id: 'priority__c', header: 'Priority', data: 'priority__c', type: 'text' },
92
+ { id: 'notes__c', header: 'Notes', data: 'notes__c' },
93
+ ],
94
+ selectedRow: s.selectedRow.$bind,
95
+ headerActions: [{ id: 'new', label: 'New Todo', icon: 'ADD', appearance: 'PRIMARY' }],
96
+ onHeaderAction: [
97
+ SDUI.actions.set(s.form, { title: '', status: '', priority: '', notes: '' }),
98
+ SDUI.actions.set(s.showCreateModal, true),
99
+ ],
100
+ rowActions: [
101
+ { id: 'edit', label: 'Edit', icon: 'EDIT_OUTLINE', placement: 'menu' },
102
+ { id: 'delete', label: 'Delete', icon: 'TRASH_OUTLINE', placement: 'menu' },
103
+ ],
104
+ onRowAction: [
105
+ SDUI.actions.set(s.form, {
106
+ title: s.selectedRow['title__c'],
107
+ status: s.selectedRow['status__c'],
108
+ priority: s.selectedRow['priority__c'],
109
+ notes: s.selectedRow['notes__c'],
110
+ }),
111
+ SDUI.actions.set(s.showEditModal, true),
112
+ ],
113
+ onRowClick: [
114
+ SDUI.actions.set(s.form, {
115
+ title: s.selectedRow['title__c'],
116
+ status: s.selectedRow['status__c'],
117
+ priority: s.selectedRow['priority__c'],
118
+ notes: s.selectedRow['notes__c'],
119
+ }),
120
+ SDUI.actions.set(s.showEditModal, true),
121
+ ],
122
+ }),
123
+ ],
124
+ });
125
+
126
+ const root = SDUI.components.VStack({
127
+ align: 'center',
128
+ gap: 'space0',
129
+ children: [
130
+ SDUI.components.Layout({
131
+ maxWidth: '1080px',
132
+ width: '100%',
133
+ margin: { left: 'auto', right: 'auto' },
134
+ padding: { top: 32, bottom: 40, left: 24, right: 24 },
135
+ children: [mainColumn],
136
+ }),
137
+
138
+ SDUI.components.Modal({
139
+ title: 'New Todo',
140
+ isOpen: s.showCreateModal.$bind,
141
+ onClose: SDUI.actions.set(s.showCreateModal, false),
142
+ children: [
143
+ SDUI.components.VStack({
144
+ gap: 'space300',
145
+ children: [
146
+ SDUI.components.TextInput({
147
+ label: 'Title',
148
+ name: 'title',
149
+ value: s.form.title.$bind,
150
+ isRequired: true,
151
+ }),
152
+ SDUI.components.Select({
153
+ label: 'Status',
154
+ name: 'status',
155
+ value: s.form.status.$bind,
156
+ options: [
157
+ { label: 'To Do', value: 'To Do' },
158
+ { label: 'In Progress', value: 'In Progress' },
159
+ { label: 'Done', value: 'Done' },
160
+ ],
161
+ }),
162
+ SDUI.components.Select({
163
+ label: 'Priority',
164
+ name: 'priority',
165
+ value: s.form.priority.$bind,
166
+ options: [
167
+ { label: 'Low', value: 'Low' },
168
+ { label: 'Medium', value: 'Medium' },
169
+ { label: 'High', value: 'High' },
170
+ ],
171
+ }),
172
+ SDUI.components.Textarea({
173
+ label: 'Notes',
174
+ name: 'notes',
175
+ value: s.form.notes.$bind,
176
+ }),
177
+ ],
178
+ }),
179
+ SDUI.components.HStack({
180
+ gap: 'space200',
181
+ justify: 'flex-end',
182
+ children: [
183
+ SDUI.components.Button({
184
+ label: 'Cancel',
185
+ appearance: 'OUTLINE',
186
+ onPress: SDUI.actions.set(s.showCreateModal, false),
187
+ }),
188
+ SDUI.components.Button({
189
+ label: 'Create',
190
+ appearance: 'PRIMARY',
191
+ onPress: SDUI.actions.callFunction(functionId, 'create', {
192
+ title: s.form.title,
193
+ status: s.form.status,
194
+ priority: s.form.priority,
195
+ notes: s.form.notes,
196
+ responsePath: '/todos',
197
+ onSuccess: { set: { '/showCreateModal': false } },
198
+ }),
199
+ }),
200
+ ],
201
+ }),
202
+ ],
203
+ }),
204
+
205
+ SDUI.components.Modal({
206
+ title: 'Edit Todo',
207
+ isOpen: s.showEditModal.$bind,
208
+ onClose: SDUI.actions.set(s.showEditModal, false),
209
+ children: [
210
+ SDUI.components.VStack({
211
+ gap: 'space300',
212
+ children: [
213
+ SDUI.components.TextInput({
214
+ label: 'Title',
215
+ name: 'edit_title',
216
+ value: s.form.title.$bind,
217
+ isRequired: true,
218
+ }),
219
+ SDUI.components.Select({
220
+ label: 'Status',
221
+ name: 'edit_status',
222
+ value: s.form.status.$bind,
223
+ options: [
224
+ { label: 'To Do', value: 'To Do' },
225
+ { label: 'In Progress', value: 'In Progress' },
226
+ { label: 'Done', value: 'Done' },
227
+ ],
228
+ }),
229
+ SDUI.components.Select({
230
+ label: 'Priority',
231
+ name: 'edit_priority',
232
+ value: s.form.priority.$bind,
233
+ options: [
234
+ { label: 'Low', value: 'Low' },
235
+ { label: 'Medium', value: 'Medium' },
236
+ { label: 'High', value: 'High' },
237
+ ],
238
+ }),
239
+ SDUI.components.Textarea({
240
+ label: 'Notes',
241
+ name: 'edit_notes',
242
+ value: s.form.notes.$bind,
243
+ }),
244
+ ],
245
+ }),
246
+ SDUI.components.HStack({
247
+ gap: 'space200',
248
+ justify: 'flex-end',
249
+ children: [
250
+ SDUI.components.Button({
251
+ label: 'Delete',
252
+ appearance: 'DESTRUCTIVE',
253
+ onPress: SDUI.actions.callFunction(functionId, 'delete', {
254
+ id: s.selectedRow['id'],
255
+ responsePath: '/todos',
256
+ onSuccess: { set: { '/showEditModal': false } },
257
+ }),
258
+ }),
259
+ SDUI.components.Button({
260
+ label: 'Cancel',
261
+ appearance: 'OUTLINE',
262
+ onPress: SDUI.actions.set(s.showEditModal, false),
263
+ }),
264
+ SDUI.components.Button({
265
+ label: 'Save',
266
+ appearance: 'PRIMARY',
267
+ onPress: SDUI.actions.callFunction(functionId, 'update', {
268
+ id: s.selectedRow['id'],
269
+ title: s.form.title,
270
+ status: s.form.status,
271
+ priority: s.form.priority,
272
+ notes: s.form.notes,
273
+ responsePath: '/todos',
274
+ onSuccess: { set: { '/showEditModal': false } },
275
+ }),
276
+ }),
277
+ ],
278
+ }),
279
+ ],
280
+ }),
281
+ ],
282
+ });
283
+
284
+ return SDUI.render(s, root);
285
+ }
286
+
287
+ export async function onRipplingEvent(
288
+ event: FunctionEvent,
289
+ context: FunctionContext,
290
+ ): Promise<FunctionResponse> {
291
+ const params = event['parameters'] as Record<string, string | undefined> | undefined;
292
+ const action = params?.['action'];
293
+ const functionId = context.function?.function_id ?? 'demo-fn';
294
+
295
+ if (!action || action === 'init') {
296
+ mutableTodos = INITIAL_TODOS.map((r) => ({ ...r }));
297
+ const spec = renderSpec(functionId);
298
+ return new FunctionResponse({
299
+ body: { spec: JSON.stringify(spec) },
300
+ statusCode: 200,
301
+ headers: {},
302
+ message: '',
303
+ });
304
+ }
305
+
306
+ if (action === 'create') {
307
+ const p = params ?? {};
308
+ const id = `t_${mutableTodos.length + 1}`;
309
+ mutableTodos = [
310
+ ...mutableTodos,
311
+ {
312
+ id,
313
+ _key: id,
314
+ title__c: (p['title'] as string) || 'Untitled',
315
+ status__c: (p['status'] as string) || 'To Do',
316
+ priority__c: (p['priority'] as string) || 'Medium',
317
+ notes__c: (p['notes'] as string) || '',
318
+ },
319
+ ];
320
+ return new FunctionResponse({
321
+ body: mutableTodos as unknown as Record<string, unknown>,
322
+ statusCode: 200,
323
+ headers: {},
324
+ message: '',
325
+ });
326
+ }
327
+
328
+ if (action === 'update') {
329
+ const p = params ?? {};
330
+ mutableTodos = mutableTodos.map((r) =>
331
+ r.id === p['id']
332
+ ? {
333
+ ...r,
334
+ title__c: (p['title'] as string) || r.title__c,
335
+ status__c: (p['status'] as string) || r.status__c,
336
+ priority__c: (p['priority'] as string) || r.priority__c,
337
+ notes__c: (p['notes'] as string) ?? r.notes__c,
338
+ }
339
+ : r,
340
+ );
341
+ return new FunctionResponse({
342
+ body: mutableTodos as unknown as Record<string, unknown>,
343
+ statusCode: 200,
344
+ headers: {},
345
+ message: '',
346
+ });
347
+ }
348
+
349
+ if (action === 'delete') {
350
+ const id = params?.['id'];
351
+ mutableTodos = mutableTodos.filter((r) => r.id !== id);
352
+ return new FunctionResponse({
353
+ body: mutableTodos as unknown as Record<string, unknown>,
354
+ statusCode: 200,
355
+ headers: {},
356
+ message: '',
357
+ });
358
+ }
359
+
360
+ return new FunctionResponse({
361
+ body: {},
362
+ statusCode: 400,
363
+ headers: {},
364
+ message: 'Unknown action',
365
+ });
366
+ }
367
+ */
@@ -0,0 +1,282 @@
1
+ /**
2
+ * SDUI CRUD (Todo list) — mock data only; no Custom Objects / objectApiName.
3
+ * Uses in-repo `@rippling/rippling-sdk/lib/sdui` (same pattern as manifest examples).
4
+ *
5
+ * Run:
6
+ * npx ts-node --transpile-only -r tsconfig-paths/register examples/sdui/sdui-crud-todo/sdui-crud-todo.ts
7
+ */
8
+
9
+ import SDUI from '@rippling/rippling-sdk/lib/sdui';
10
+
11
+ const MOCK_TODOS = [
12
+ {
13
+ id: 't1',
14
+ _key: 't1',
15
+ title__c: 'Ship SDK patch',
16
+ status__c: 'In Progress',
17
+ priority__c: 'High',
18
+ notes__c: 'Regression pass on SDUI examples',
19
+ },
20
+ {
21
+ id: 't2',
22
+ _key: 't2',
23
+ title__c: 'Document Function paste flow',
24
+ status__c: 'To Do',
25
+ priority__c: 'Medium',
26
+ notes__c: 'Caps comment on rippling-function.ts',
27
+ },
28
+ {
29
+ id: 't3',
30
+ _key: 't3',
31
+ title__c: 'Weekly sync',
32
+ status__c: 'Done',
33
+ priority__c: 'Low',
34
+ notes__c: '',
35
+ },
36
+ ];
37
+
38
+ function renderSpec(functionId: string) {
39
+ const s = SDUI.createState({
40
+ todos: MOCK_TODOS,
41
+ selectedRow: null as unknown,
42
+ showCreateModal: false,
43
+ showEditModal: false,
44
+ form: { title: '', status: '', priority: '', notes: '' },
45
+ });
46
+
47
+ const mainColumn = SDUI.components.VStack({
48
+ gap: 'space500',
49
+ children: [
50
+ SDUI.components.VStack({
51
+ gap: 'space300',
52
+ children: [
53
+ SDUI.components.HStack({
54
+ gap: 'space200',
55
+ align: 'center',
56
+ wrap: true,
57
+ children: [
58
+ SDUI.components.Heading({
59
+ level: '2',
60
+ text: 'Team todos',
61
+ }),
62
+ SDUI.components.Badge({
63
+ text: 'Demo data',
64
+ appearance: 'NEUTRAL',
65
+ size: 'S',
66
+ }),
67
+ ],
68
+ }),
69
+ SDUI.components.Text({
70
+ text: 'Create, edit, and delete call this Function; the grid below uses sample rows until you connect Custom Objects or your own API.',
71
+ typestyle: 'bodyMd',
72
+ color: 'secondary',
73
+ maxWidth: '720px',
74
+ }),
75
+ ],
76
+ }),
77
+ SDUI.components.Layout({
78
+ width: '100%',
79
+ children: [SDUI.components.Divider({})],
80
+ }),
81
+ SDUI.components.Grid({
82
+ data: s.todos,
83
+ columns: [
84
+ { id: 'title__c', header: 'Title', data: 'title__c' },
85
+ { id: 'status__c', header: 'Status', data: 'status__c', type: 'text' },
86
+ { id: 'priority__c', header: 'Priority', data: 'priority__c', type: 'text' },
87
+ { id: 'notes__c', header: 'Notes', data: 'notes__c' },
88
+ ],
89
+ selectedRow: s.selectedRow.$bind,
90
+ headerActions: [{ id: 'new', label: 'New Todo', icon: 'ADD', appearance: 'PRIMARY' }],
91
+ onHeaderAction: [
92
+ SDUI.actions.set(s.form, { title: '', status: '', priority: '', notes: '' }),
93
+ SDUI.actions.set(s.showCreateModal, true),
94
+ ],
95
+ rowActions: [
96
+ { id: 'edit', label: 'Edit', icon: 'EDIT_OUTLINE', placement: 'menu' },
97
+ { id: 'delete', label: 'Delete', icon: 'TRASH_OUTLINE', placement: 'menu' },
98
+ ],
99
+ onRowAction: [
100
+ SDUI.actions.set(s.form, {
101
+ title: s.selectedRow['title__c'],
102
+ status: s.selectedRow['status__c'],
103
+ priority: s.selectedRow['priority__c'],
104
+ notes: s.selectedRow['notes__c'],
105
+ }),
106
+ SDUI.actions.set(s.showEditModal, true),
107
+ ],
108
+ onRowClick: [
109
+ SDUI.actions.set(s.form, {
110
+ title: s.selectedRow['title__c'],
111
+ status: s.selectedRow['status__c'],
112
+ priority: s.selectedRow['priority__c'],
113
+ notes: s.selectedRow['notes__c'],
114
+ }),
115
+ SDUI.actions.set(s.showEditModal, true),
116
+ ],
117
+ }),
118
+ ],
119
+ });
120
+
121
+ const root = SDUI.components.VStack({
122
+ align: 'center',
123
+ gap: 'space0',
124
+ children: [
125
+ SDUI.components.Layout({
126
+ maxWidth: '1080px',
127
+ width: '100%',
128
+ margin: { left: 'auto', right: 'auto' },
129
+ padding: { top: 32, bottom: 40, left: 24, right: 24 },
130
+ children: [mainColumn],
131
+ }),
132
+
133
+ SDUI.components.Modal({
134
+ title: 'New Todo',
135
+ isOpen: s.showCreateModal.$bind,
136
+ onClose: SDUI.actions.set(s.showCreateModal, false),
137
+ children: [
138
+ SDUI.components.VStack({
139
+ gap: 'space300',
140
+ children: [
141
+ SDUI.components.TextInput({
142
+ label: 'Title',
143
+ name: 'title',
144
+ value: s.form.title.$bind,
145
+ isRequired: true,
146
+ }),
147
+ SDUI.components.Select({
148
+ label: 'Status',
149
+ name: 'status',
150
+ value: s.form.status.$bind,
151
+ options: [
152
+ { label: 'To Do', value: 'To Do' },
153
+ { label: 'In Progress', value: 'In Progress' },
154
+ { label: 'Done', value: 'Done' },
155
+ ],
156
+ }),
157
+ SDUI.components.Select({
158
+ label: 'Priority',
159
+ name: 'priority',
160
+ value: s.form.priority.$bind,
161
+ options: [
162
+ { label: 'Low', value: 'Low' },
163
+ { label: 'Medium', value: 'Medium' },
164
+ { label: 'High', value: 'High' },
165
+ ],
166
+ }),
167
+ SDUI.components.Textarea({
168
+ label: 'Notes',
169
+ name: 'notes',
170
+ value: s.form.notes.$bind,
171
+ }),
172
+ ],
173
+ }),
174
+ SDUI.components.HStack({
175
+ gap: 'space200',
176
+ justify: 'flex-end',
177
+ children: [
178
+ SDUI.components.Button({
179
+ label: 'Cancel',
180
+ appearance: 'OUTLINE',
181
+ onPress: SDUI.actions.set(s.showCreateModal, false),
182
+ }),
183
+ SDUI.components.Button({
184
+ label: 'Create',
185
+ appearance: 'PRIMARY',
186
+ onPress: SDUI.actions.callFunction(functionId, 'create', {
187
+ title: s.form.title,
188
+ status: s.form.status,
189
+ priority: s.form.priority,
190
+ notes: s.form.notes,
191
+ responsePath: '/todos',
192
+ onSuccess: { set: { '/showCreateModal': false } },
193
+ }),
194
+ }),
195
+ ],
196
+ }),
197
+ ],
198
+ }),
199
+
200
+ SDUI.components.Modal({
201
+ title: 'Edit Todo',
202
+ isOpen: s.showEditModal.$bind,
203
+ onClose: SDUI.actions.set(s.showEditModal, false),
204
+ children: [
205
+ SDUI.components.VStack({
206
+ gap: 'space300',
207
+ children: [
208
+ SDUI.components.TextInput({
209
+ label: 'Title',
210
+ name: 'edit_title',
211
+ value: s.form.title.$bind,
212
+ isRequired: true,
213
+ }),
214
+ SDUI.components.Select({
215
+ label: 'Status',
216
+ name: 'edit_status',
217
+ value: s.form.status.$bind,
218
+ options: [
219
+ { label: 'To Do', value: 'To Do' },
220
+ { label: 'In Progress', value: 'In Progress' },
221
+ { label: 'Done', value: 'Done' },
222
+ ],
223
+ }),
224
+ SDUI.components.Select({
225
+ label: 'Priority',
226
+ name: 'edit_priority',
227
+ value: s.form.priority.$bind,
228
+ options: [
229
+ { label: 'Low', value: 'Low' },
230
+ { label: 'Medium', value: 'Medium' },
231
+ { label: 'High', value: 'High' },
232
+ ],
233
+ }),
234
+ SDUI.components.Textarea({
235
+ label: 'Notes',
236
+ name: 'edit_notes',
237
+ value: s.form.notes.$bind,
238
+ }),
239
+ ],
240
+ }),
241
+ SDUI.components.HStack({
242
+ gap: 'space200',
243
+ justify: 'flex-end',
244
+ children: [
245
+ SDUI.components.Button({
246
+ label: 'Delete',
247
+ appearance: 'DESTRUCTIVE',
248
+ onPress: SDUI.actions.callFunction(functionId, 'delete', {
249
+ id: s.selectedRow['id'],
250
+ responsePath: '/todos',
251
+ onSuccess: { set: { '/showEditModal': false } },
252
+ }),
253
+ }),
254
+ SDUI.components.Button({
255
+ label: 'Cancel',
256
+ appearance: 'OUTLINE',
257
+ onPress: SDUI.actions.set(s.showEditModal, false),
258
+ }),
259
+ SDUI.components.Button({
260
+ label: 'Save',
261
+ appearance: 'PRIMARY',
262
+ onPress: SDUI.actions.callFunction(functionId, 'update', {
263
+ id: s.selectedRow['id'],
264
+ title: s.form.title,
265
+ status: s.form.status,
266
+ priority: s.form.priority,
267
+ notes: s.form.notes,
268
+ responsePath: '/todos',
269
+ onSuccess: { set: { '/showEditModal': false } },
270
+ }),
271
+ }),
272
+ ],
273
+ }),
274
+ ],
275
+ }),
276
+ ],
277
+ });
278
+
279
+ return SDUI.render(s, root);
280
+ }
281
+
282
+ console.log(JSON.stringify(renderSpec('demo-fn'), null, 2));