@rippling/rippling-sdk 0.2.0-alpha.59 → 0.2.0-alpha.60

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 (127) hide show
  1. package/examples/manifest/dental-practice-management/dental-practice-management.ts +5 -4
  2. package/examples/manifest/dental-practice-management/functions/chair-schedule.ts +7 -10
  3. package/examples/manifest/dental-practice-management/functions/operatory-list.ts +7 -10
  4. package/examples/manifest/dental-practice-management/functions/patient-list.ts +7 -10
  5. package/examples/manifest/dental-practice-management/functions/treatment-plan-list.ts +7 -10
  6. package/examples/manifest/gym-membership-management/functions/check-in-desk.ts +7 -10
  7. package/examples/manifest/gym-membership-management/functions/member-list.ts +7 -10
  8. package/examples/manifest/gym-membership-management/functions/today-schedule.ts +7 -10
  9. package/examples/manifest/gym-membership-management/functions/trainer-list.ts +7 -10
  10. package/examples/manifest/gym-membership-management/gym-membership-management.ts +5 -4
  11. package/examples/manifest/simple-todo-app/functions/todo-list.ts +7 -10
  12. package/examples/manifest/simple-todo-app/simple-todo-app.ts +2 -2
  13. package/internal/tslib.js +0 -4
  14. package/lib/manifest/manifest-function.d.mts +3 -3
  15. package/lib/manifest/manifest-function.d.ts +3 -3
  16. package/lib/manifest/manifest-function.js +3 -3
  17. package/lib/manifest/manifest-function.mjs +3 -3
  18. package/package.json +1 -1
  19. package/src/lib/manifest/manifest-function.ts +3 -3
  20. package/src/version.ts +1 -1
  21. package/version.d.mts +1 -1
  22. package/version.d.ts +1 -1
  23. package/version.js +1 -1
  24. package/version.mjs +1 -1
  25. package/examples/sdui/sdui-crud-todo/rippling-function.ts +0 -358
  26. package/examples/sdui/sdui-showcase/rippling-function.ts +0 -366
  27. package/examples/sdui/sdui-tab-multi-view/rippling-function.ts +0 -1042
  28. package/examples/sdui/vote/rippling-function.ts +0 -315
  29. package/lib/sdui/actions.d.mts +0 -25
  30. package/lib/sdui/actions.d.mts.map +0 -1
  31. package/lib/sdui/actions.d.ts +0 -25
  32. package/lib/sdui/actions.d.ts.map +0 -1
  33. package/lib/sdui/actions.js +0 -63
  34. package/lib/sdui/actions.js.map +0 -1
  35. package/lib/sdui/actions.mjs +0 -52
  36. package/lib/sdui/actions.mjs.map +0 -1
  37. package/lib/sdui/component.d.mts +0 -58
  38. package/lib/sdui/component.d.mts.map +0 -1
  39. package/lib/sdui/component.d.ts +0 -58
  40. package/lib/sdui/component.d.ts.map +0 -1
  41. package/lib/sdui/component.js +0 -128
  42. package/lib/sdui/component.js.map +0 -1
  43. package/lib/sdui/component.mjs +0 -124
  44. package/lib/sdui/component.mjs.map +0 -1
  45. package/lib/sdui/components.d.mts +0 -1047
  46. package/lib/sdui/components.d.mts.map +0 -1
  47. package/lib/sdui/components.d.ts +0 -1047
  48. package/lib/sdui/components.d.ts.map +0 -1
  49. package/lib/sdui/components.js +0 -845
  50. package/lib/sdui/components.js.map +0 -1
  51. package/lib/sdui/components.mjs +0 -840
  52. package/lib/sdui/components.mjs.map +0 -1
  53. package/lib/sdui/conditions.d.mts +0 -25
  54. package/lib/sdui/conditions.d.mts.map +0 -1
  55. package/lib/sdui/conditions.d.ts +0 -25
  56. package/lib/sdui/conditions.d.ts.map +0 -1
  57. package/lib/sdui/conditions.js +0 -47
  58. package/lib/sdui/conditions.js.map +0 -1
  59. package/lib/sdui/conditions.mjs +0 -35
  60. package/lib/sdui/conditions.mjs.map +0 -1
  61. package/lib/sdui/directives.d.mts +0 -18
  62. package/lib/sdui/directives.d.mts.map +0 -1
  63. package/lib/sdui/directives.d.ts +0 -18
  64. package/lib/sdui/directives.d.ts.map +0 -1
  65. package/lib/sdui/directives.js +0 -31
  66. package/lib/sdui/directives.js.map +0 -1
  67. package/lib/sdui/directives.mjs +0 -24
  68. package/lib/sdui/directives.mjs.map +0 -1
  69. package/lib/sdui/index.d.mts +0 -375
  70. package/lib/sdui/index.d.mts.map +0 -1
  71. package/lib/sdui/index.d.ts +0 -375
  72. package/lib/sdui/index.d.ts.map +0 -1
  73. package/lib/sdui/index.js +0 -386
  74. package/lib/sdui/index.js.map +0 -1
  75. package/lib/sdui/index.mjs +0 -215
  76. package/lib/sdui/index.mjs.map +0 -1
  77. package/lib/sdui/propTypes.d.mts +0 -5971
  78. package/lib/sdui/propTypes.d.mts.map +0 -1
  79. package/lib/sdui/propTypes.d.ts +0 -5971
  80. package/lib/sdui/propTypes.d.ts.map +0 -1
  81. package/lib/sdui/propTypes.js +0 -14
  82. package/lib/sdui/propTypes.js.map +0 -1
  83. package/lib/sdui/propTypes.mjs +0 -13
  84. package/lib/sdui/propTypes.mjs.map +0 -1
  85. package/lib/sdui/props.d.mts +0 -12
  86. package/lib/sdui/props.d.mts.map +0 -1
  87. package/lib/sdui/props.d.ts +0 -12
  88. package/lib/sdui/props.d.ts.map +0 -1
  89. package/lib/sdui/props.js +0 -25
  90. package/lib/sdui/props.js.map +0 -1
  91. package/lib/sdui/props.mjs +0 -21
  92. package/lib/sdui/props.mjs.map +0 -1
  93. package/lib/sdui/render.d.mts +0 -23
  94. package/lib/sdui/render.d.mts.map +0 -1
  95. package/lib/sdui/render.d.ts +0 -23
  96. package/lib/sdui/render.d.ts.map +0 -1
  97. package/lib/sdui/render.js +0 -48
  98. package/lib/sdui/render.js.map +0 -1
  99. package/lib/sdui/render.mjs +0 -45
  100. package/lib/sdui/render.mjs.map +0 -1
  101. package/lib/sdui/state.d.mts +0 -22
  102. package/lib/sdui/state.d.mts.map +0 -1
  103. package/lib/sdui/state.d.ts +0 -22
  104. package/lib/sdui/state.d.ts.map +0 -1
  105. package/lib/sdui/state.js +0 -59
  106. package/lib/sdui/state.js.map +0 -1
  107. package/lib/sdui/state.mjs +0 -55
  108. package/lib/sdui/state.mjs.map +0 -1
  109. package/lib/sdui.d.mts +0 -3
  110. package/lib/sdui.d.mts.map +0 -1
  111. package/lib/sdui.d.ts +0 -3
  112. package/lib/sdui.d.ts.map +0 -1
  113. package/lib/sdui.js +0 -8
  114. package/lib/sdui.js.map +0 -1
  115. package/lib/sdui.mjs +0 -3
  116. package/lib/sdui.mjs.map +0 -1
  117. package/src/lib/sdui/actions.ts +0 -71
  118. package/src/lib/sdui/component.ts +0 -162
  119. package/src/lib/sdui/components.ts +0 -1572
  120. package/src/lib/sdui/conditions.ts +0 -59
  121. package/src/lib/sdui/directives.ts +0 -45
  122. package/src/lib/sdui/index.ts +0 -650
  123. package/src/lib/sdui/propTypes.ts +0 -8087
  124. package/src/lib/sdui/props.ts +0 -32
  125. package/src/lib/sdui/render.ts +0 -66
  126. package/src/lib/sdui/state.ts +0 -66
  127. package/src/lib/sdui.ts +0 -2
@@ -1,1042 +0,0 @@
1
- /** Bark & Train — Field HQ: multi-tab dashboard (home / dogs / schedule / today / resources) with dogs + sessions CRUD. Mock data only. */
2
-
3
- import SDUI from '@rippling/rippling-sdk/lib/sdui';
4
- import { FunctionContext, FunctionEvent, FunctionResponse } from '@rippling/rippling-sdk';
5
-
6
- const INITIAL_DOGS = [
7
- {
8
- id: 'dog_1',
9
- _key: 'dog_1',
10
- dog_name__c: 'Rex',
11
- breed__c: 'Labrador',
12
- age__c: 3,
13
- total_training_time__c: 120,
14
- },
15
- {
16
- id: 'dog_2',
17
- _key: 'dog_2',
18
- dog_name__c: 'Luna',
19
- breed__c: 'Border Collie',
20
- age__c: 5,
21
- total_training_time__c: 340,
22
- },
23
- ];
24
-
25
- const INITIAL_SESSIONS = [
26
- {
27
- id: 'sess_1',
28
- _key: 'sess_1',
29
- dog_id__c: 'dog_1',
30
- session_date__c: '2026-04-01',
31
- duration__c: 45,
32
- training_type__c: 'Obedience',
33
- notes__c: 'Recall work',
34
- },
35
- {
36
- id: 'sess_2',
37
- _key: 'sess_2',
38
- dog_id__c: 'dog_2',
39
- session_date__c: '2026-04-02',
40
- duration__c: 60,
41
- training_type__c: 'Agility',
42
- notes__c: 'Weaves',
43
- },
44
- ];
45
-
46
- const SESSION_TOTAL_MIN = INITIAL_SESSIONS.reduce((a, row) => a + Number(row.duration__c || 0), 0);
47
-
48
- const PCT_TIME_OBEDIENCE = 43;
49
- const PCT_TIME_AGILITY = 57;
50
-
51
- const SESSION_TIME_SEGMENTS = [
52
- { value: PCT_TIME_OBEDIENCE, segmentColor: '#7C3AED' },
53
- { value: PCT_TIME_AGILITY, segmentColor: '#F97316' },
54
- ];
55
-
56
- let mutableDogs = INITIAL_DOGS.map((r) => ({ ...r }));
57
- let mutableSessions = INITIAL_SESSIONS.map((r) => ({ ...r }));
58
-
59
- function renderSpec(functionId: string) {
60
- const s = SDUI.createState({
61
- dogs: mutableDogs,
62
- sessions: mutableSessions,
63
- stats: {
64
- dogCount: mutableDogs.length,
65
- sessionCount: mutableSessions.length,
66
- totalMinutes: SESSION_TOTAL_MIN,
67
- },
68
- selectedDog: null as unknown,
69
- selectedSession: null as unknown,
70
- showCreateDogModal: false,
71
- showEditDogModal: false,
72
- showCreateSessionModal: false,
73
- showEditSessionModal: false,
74
- dogForm: { dog_name: '', breed: '', age: '' },
75
- sessionForm: {
76
- dog_id: '',
77
- session_date: '',
78
- duration: '',
79
- training_type: '',
80
- notes: '',
81
- },
82
- activeTab: 'overview',
83
- });
84
-
85
- // ============================================================
86
- // HOME — redesigned: dashboard layout with hero stats on left,
87
- // chart + recent activity stacked on right. Single content
88
- // band, no purple hero background. Cleaner and more useful.
89
- // ============================================================
90
- const overviewPanel = SDUI.components.Layout({
91
- width: '100%',
92
- maxWidth: '1200px',
93
- margin: { left: 'auto', right: 'auto' },
94
- padding: { top: 40, bottom: 56, left: 28, right: 28 },
95
- children: [
96
- SDUI.components.VStack({
97
- gap: 'space600',
98
- align: 'stretch',
99
- children: [
100
- SDUI.components.VStack({
101
- gap: 'space200',
102
- align: 'flex-start',
103
- children: [
104
- SDUI.components.Text({
105
- typestyle: 'bodyMdEmphasis',
106
- color: 'secondary',
107
- text: 'BARK & TRAIN · FIELD HQ',
108
- }),
109
- SDUI.components.Heading({
110
- level: 1,
111
- text: 'Good morning, trainer 👋',
112
- }),
113
- SDUI.components.Text({
114
- typestyle: 'bodyLg',
115
- color: 'secondary',
116
- text: 'Here is what is happening across your roster and schedule.',
117
- }),
118
- ],
119
- }),
120
- SDUI.components.HStack({
121
- gap: 'space400',
122
- wrap: 'nowrap',
123
- align: 'stretch',
124
- justify: 'space-between',
125
- children: [
126
- SDUI.components.Layout({
127
- width: '32%',
128
- minWidth: '260px',
129
- children: [
130
- SDUI.components.Card({
131
- children: [
132
- SDUI.components.Layout({
133
- padding: { top: 24, bottom: 24, left: 24, right: 24 },
134
- backgroundColor: '#EEF2FF',
135
- radius: 12,
136
- children: [
137
- SDUI.components.VStack({
138
- gap: 'space300',
139
- align: 'flex-start',
140
- children: [
141
- SDUI.components.Text({
142
- text: 'DOGS ON ROSTER',
143
- typestyle: 'bodyXsEmphasis',
144
- color: '#4F46E5',
145
- }),
146
- SDUI.components.Heading({
147
- level: 1,
148
- text: SDUI.template`${s.stats.dogCount}`,
149
- }),
150
- SDUI.components.Text({
151
- text: 'Active clients',
152
- color: 'secondary',
153
- typestyle: 'bodySm',
154
- }),
155
- ],
156
- }),
157
- ],
158
- }),
159
- ],
160
- }),
161
- ],
162
- }),
163
- SDUI.components.Layout({
164
- width: '32%',
165
- minWidth: '260px',
166
- children: [
167
- SDUI.components.Card({
168
- children: [
169
- SDUI.components.Layout({
170
- padding: { top: 24, bottom: 24, left: 24, right: 24 },
171
- backgroundColor: '#ECFDF5',
172
- radius: 12,
173
- children: [
174
- SDUI.components.VStack({
175
- gap: 'space300',
176
- align: 'flex-start',
177
- children: [
178
- SDUI.components.Text({
179
- text: 'SESSIONS BOOKED',
180
- typestyle: 'bodyXsEmphasis',
181
- color: '#059669',
182
- }),
183
- SDUI.components.Heading({
184
- level: 1,
185
- text: SDUI.template`${s.stats.sessionCount}`,
186
- }),
187
- SDUI.components.Text({
188
- text: 'On the calendar',
189
- color: 'secondary',
190
- typestyle: 'bodySm',
191
- }),
192
- ],
193
- }),
194
- ],
195
- }),
196
- ],
197
- }),
198
- ],
199
- }),
200
- SDUI.components.Layout({
201
- width: '32%',
202
- minWidth: '260px',
203
- children: [
204
- SDUI.components.Card({
205
- children: [
206
- SDUI.components.Layout({
207
- padding: { top: 24, bottom: 24, left: 24, right: 24 },
208
- backgroundColor: '#FFF7ED',
209
- radius: 12,
210
- children: [
211
- SDUI.components.VStack({
212
- gap: 'space300',
213
- align: 'flex-start',
214
- children: [
215
- SDUI.components.Text({
216
- text: 'PRACTICE MINUTES',
217
- typestyle: 'bodyXsEmphasis',
218
- color: '#EA580C',
219
- }),
220
- SDUI.components.Heading({
221
- level: 1,
222
- text: SDUI.template`${s.stats.totalMinutes}`,
223
- }),
224
- SDUI.components.Text({
225
- text: 'Ring & field time',
226
- color: 'secondary',
227
- typestyle: 'bodySm',
228
- }),
229
- ],
230
- }),
231
- ],
232
- }),
233
- ],
234
- }),
235
- ],
236
- }),
237
- ],
238
- }),
239
- // Chart + Quick actions row — 66% / 32%
240
- SDUI.components.HStack({
241
- gap: 'space400',
242
- wrap: 'wrap',
243
- align: 'stretch',
244
- justify: 'space-between',
245
- children: [
246
- SDUI.components.Layout({
247
- width: '66%',
248
- minWidth: '420px',
249
- children: [
250
- SDUI.components.Card({
251
- title: 'Where practice time goes',
252
- children: [
253
- SDUI.components.VStack({
254
- gap: 'space400',
255
- align: 'stretch',
256
- children: [
257
- SDUI.components.Text({
258
- typestyle: 'bodyMd',
259
- color: 'secondary',
260
- text: 'Split across lesson types from your scheduled sessions.',
261
- }),
262
- // DistributionBar with no wrapping Layout — direct child of VStack
263
- SDUI.components.DistributionBar({
264
- segments: SESSION_TIME_SEGMENTS,
265
- totalValue: 100,
266
- size: 'standard',
267
- }),
268
- SDUI.components.HStack({
269
- gap: 'space300',
270
- wrap: 'wrap',
271
- justify: 'flex-start',
272
- children: [
273
- SDUI.components.Badge({
274
- text: SDUI.template`Obedience · ${PCT_TIME_OBEDIENCE}%`,
275
- appearance: 'PRIMARY_LIGHT',
276
- }),
277
- SDUI.components.Badge({
278
- text: SDUI.template`Agility · ${PCT_TIME_AGILITY}%`,
279
- appearance: 'WARNING_LIGHT',
280
- }),
281
- ],
282
- }),
283
- SDUI.components.Divider({}),
284
- SDUI.components.HStack({
285
- justify: 'space-between',
286
- align: 'center',
287
- children: [
288
- SDUI.components.Text({
289
- typestyle: 'bodySm',
290
- color: 'secondary',
291
- text: 'Want to dig in?',
292
- }),
293
- SDUI.components.Button({
294
- label: 'Open Schedule',
295
- appearance: 'OUTLINE',
296
- size: 'S',
297
- onPress: SDUI.actions.set(s.activeTab, 'sessions'),
298
- }),
299
- ],
300
- }),
301
- ],
302
- }),
303
- ],
304
- }),
305
- ],
306
- }),
307
- SDUI.components.Layout({
308
- width: '32%',
309
- minWidth: '280px',
310
- children: [
311
- SDUI.components.Card({
312
- title: 'Quick actions',
313
- children: [
314
- SDUI.components.VStack({
315
- gap: 'space300',
316
- align: 'stretch',
317
- children: [
318
- SDUI.components.Button({
319
- label: 'Add new dog',
320
- appearance: 'PRIMARY',
321
- size: 'M',
322
- isFluid: true,
323
- onPress: [
324
- SDUI.actions.set(s.dogForm, { dog_name: '', breed: '', age: '' }),
325
- SDUI.actions.set(s.showCreateDogModal, true),
326
- ],
327
- }),
328
- SDUI.components.Button({
329
- label: 'Schedule a session',
330
- appearance: 'OUTLINE',
331
- size: 'M',
332
- isFluid: true,
333
- onPress: [
334
- SDUI.actions.set(s.sessionForm, {
335
- dog_id: '',
336
- session_date: '',
337
- duration: '',
338
- training_type: '',
339
- notes: '',
340
- }),
341
- SDUI.actions.set(s.showCreateSessionModal, true),
342
- ],
343
- }),
344
- SDUI.components.Divider({}),
345
- // Use Link components instead of Button GHOST — they
346
- // naturally left-align as text and look cleaner here.
347
- SDUI.components.Text({
348
- typestyle: 'bodyXsEmphasis',
349
- color: 'secondary',
350
- text: 'JUMP TO',
351
- }),
352
- SDUI.components.Link({
353
- text: '→ View all dogs',
354
- href: '#',
355
- onPress: SDUI.actions.set(s.activeTab, 'dogs'),
356
- }),
357
- SDUI.components.Link({
358
- text: '→ View today',
359
- href: '#',
360
- onPress: SDUI.actions.set(s.activeTab, 'today'),
361
- }),
362
- ],
363
- }),
364
- ],
365
- }),
366
- ],
367
- }),
368
- ],
369
- }),
370
- ],
371
- }),
372
- ],
373
- });
374
- const dogsGrid = SDUI.components.Grid({
375
- data: s.dogs,
376
- height: 520,
377
- columns: [
378
- { id: 'dog_name__c', header: 'Name', data: 'dog_name__c', width: 220 },
379
- { id: 'breed__c', header: 'Breed', data: 'breed__c', width: 280 },
380
- { id: 'age__c', header: 'Age', data: 'age__c', width: 100 },
381
- { id: 'total_training_time__c', header: 'Training time', data: 'total_training_time__c', width: 160 },
382
- ],
383
- selectedRow: s.selectedDog.$bind,
384
- headerActions: [{ id: 'new_dog', label: 'Add Dog', icon: 'ADD', appearance: 'primary' }],
385
- onHeaderAction: [
386
- SDUI.actions.set(s.dogForm, { dog_name: '', breed: '', age: '' }),
387
- SDUI.actions.set(s.showCreateDogModal, true),
388
- ],
389
- rowActions: [
390
- { id: 'edit_dog', label: 'Edit', icon: 'EDIT_OUTLINE', placement: 'menu' },
391
- { id: 'delete_dog', label: 'Delete', icon: 'TRASH_OUTLINE', placement: 'menu' },
392
- ],
393
- onRowAction: [
394
- SDUI.actions.set(s.dogForm, {
395
- dog_name: s.selectedDog['dog_name__c'],
396
- breed: s.selectedDog['breed__c'],
397
- age: s.selectedDog['age__c'],
398
- }),
399
- SDUI.actions.set(s.showEditDogModal, true),
400
- ],
401
- });
402
-
403
- const sessionsGrid = SDUI.components.Grid({
404
- data: s.sessions,
405
- height: 520,
406
- columns: [
407
- { id: 'session_date__c', header: 'Date', data: 'session_date__c', width: 160 },
408
- { id: 'duration__c', header: 'Duration (min)', data: 'duration__c', width: 140 },
409
- { id: 'training_type__c', header: 'Type', data: 'training_type__c', type: 'text', width: 180 },
410
- { id: 'notes__c', header: 'Notes', data: 'notes__c', width: 320 },
411
- ],
412
- selectedRow: s.selectedSession.$bind,
413
- headerActions: [{ id: 'new_session', label: 'Add Session', icon: 'ADD', appearance: 'primary' }],
414
- onHeaderAction: [
415
- SDUI.actions.set(s.sessionForm, {
416
- dog_id: '',
417
- session_date: '',
418
- duration: '',
419
- training_type: '',
420
- notes: '',
421
- }),
422
- SDUI.actions.set(s.showCreateSessionModal, true),
423
- ],
424
- rowActions: [
425
- { id: 'edit_session', label: 'Edit', icon: 'EDIT_OUTLINE', placement: 'menu' },
426
- { id: 'delete_session', label: 'Delete', icon: 'TRASH_OUTLINE', placement: 'menu' },
427
- ],
428
- onRowAction: [
429
- SDUI.actions.set(s.sessionForm, {
430
- dog_id: s.selectedSession['dog_id__c'],
431
- session_date: s.selectedSession['session_date__c'],
432
- duration: s.selectedSession['duration__c'],
433
- training_type: s.selectedSession['training_type__c'],
434
- notes: s.selectedSession['notes__c'],
435
- }),
436
- SDUI.actions.set(s.showEditSessionModal, true),
437
- ],
438
- });
439
-
440
- const dogsPanel = SDUI.components.Layout({
441
- width: '100%',
442
- maxWidth: '1200px',
443
- margin: { left: 'auto', right: 'auto' },
444
- padding: { top: 32, bottom: 48, left: 28, right: 28 },
445
- children: [
446
- SDUI.components.VStack({
447
- gap: 'space500',
448
- align: 'stretch',
449
- children: [
450
- SDUI.components.VStack({
451
- gap: 'space200',
452
- children: [
453
- SDUI.components.Heading({
454
- level: 1,
455
- text: 'Your dogs',
456
- }),
457
- SDUI.components.Text({
458
- typestyle: 'bodyLg',
459
- color: 'secondary',
460
- text: 'Add Dog or use ⋯ on a row to edit or delete.',
461
- }),
462
- ],
463
- }),
464
- dogsGrid,
465
- ],
466
- }),
467
- ],
468
- });
469
-
470
- const sessionsPanel = SDUI.components.Layout({
471
- width: '100%',
472
- maxWidth: '1200px',
473
- margin: { left: 'auto', right: 'auto' },
474
- padding: { top: 32, bottom: 48, left: 28, right: 28 },
475
- children: [
476
- SDUI.components.VStack({
477
- gap: 'space500',
478
- align: 'stretch',
479
- children: [
480
- SDUI.components.VStack({
481
- gap: 'space200',
482
- children: [
483
- SDUI.components.Heading({
484
- level: 1,
485
- text: 'Session schedule',
486
- }),
487
- SDUI.components.Text({
488
- typestyle: 'bodyLg',
489
- color: 'secondary',
490
- text: 'Add Session or use ⋯ on a row. Dog ID must match a roster id (e.g. dog_1).',
491
- }),
492
- ],
493
- }),
494
- sessionsGrid,
495
- ],
496
- }),
497
- ],
498
- });
499
-
500
- const todayPanel = SDUI.components.Layout({
501
- width: '100%',
502
- maxWidth: '1200px',
503
- margin: { left: 'auto', right: 'auto' },
504
- padding: { top: 32, bottom: 48, left: 28, right: 28 },
505
- children: [
506
- SDUI.components.VStack({
507
- gap: 'space600',
508
- align: 'stretch',
509
- children: [
510
- SDUI.components.VStack({
511
- gap: 'space200',
512
- children: [
513
- SDUI.components.Heading({
514
- level: 1,
515
- text: 'Today & coming up',
516
- }),
517
- SDUI.components.Text({
518
- typestyle: 'bodyLg',
519
- color: 'secondary',
520
- text: 'Snapshot of upcoming lessons — production would filter by “today” from your sessions.',
521
- }),
522
- ],
523
- }),
524
- SDUI.components.TableBasic({
525
- title: 'Who’s on deck',
526
- width: 1100,
527
- columns: [
528
- { header: 'When', key: 'when', width: '22%' },
529
- { header: 'Dog', key: 'dog', width: '22%' },
530
- { header: 'Type', key: 'type', width: '18%' },
531
- { header: 'Focus', key: 'focus', width: '38%' },
532
- ],
533
- rows: [
534
- { when: 'Apr 1 · AM', dog: 'Rex', type: 'Obedience', focus: 'Recall · ring A' },
535
- { when: 'Apr 2 · PM', dog: 'Luna', type: 'Agility', focus: 'Weaves & contacts' },
536
- ],
537
- }),
538
- SDUI.components.ActionCard({
539
- title: 'Need to change something?',
540
- caption: 'Full calendar editing lives on Schedule; roster edits live under Dogs.',
541
- icon: 'CALENDAR',
542
- alignment: 'CENTER',
543
- primaryActionTitle: 'Open Schedule',
544
- onPressPrimary: SDUI.actions.set(s.activeTab, 'sessions'),
545
- secondaryActionTitle: 'Open Dogs',
546
- onPressSecondary: SDUI.actions.set(s.activeTab, 'dogs'),
547
- }),
548
- ],
549
- }),
550
- ],
551
- });
552
-
553
- const resourcesPanel = SDUI.components.Layout({
554
- width: '100%',
555
- maxWidth: '1200px',
556
- margin: { left: 'auto', right: 'auto' },
557
- padding: { top: 32, bottom: 48, left: 28, right: 28 },
558
- children: [
559
- SDUI.components.VStack({
560
- gap: 'space600',
561
- align: 'stretch',
562
- children: [
563
- SDUI.components.VStack({
564
- gap: 'space200',
565
- children: [
566
- SDUI.components.Heading({
567
- level: 1,
568
- text: 'Resources & safety',
569
- }),
570
- SDUI.components.Text({
571
- typestyle: 'bodyLg',
572
- color: 'secondary',
573
- text: 'Replace these with real PDFs and links — structure stays the same for handlers.',
574
- }),
575
- ],
576
- }),
577
- SDUI.components.Tip({
578
- title: 'Reactive dogs at the gate',
579
- content:
580
- 'Stagger arrivals so reactive pups aren’t face-to-face in the lobby; post your muzzle policy here.',
581
- }),
582
- SDUI.components.Tip({
583
- title: 'Emergency vet & after hours',
584
- content:
585
- 'Pin clinic phone, poison control, and directions — trainers shouldn’t hunt for them mid-class.',
586
- }),
587
- SDUI.components.Card({
588
- title: 'Files you’ll attach later',
589
- children: [
590
- SDUI.components.VStack({
591
- gap: 'space400',
592
- align: 'stretch',
593
- children: [
594
- SDUI.components.Text({
595
- typestyle: 'bodyLg',
596
- color: 'secondary',
597
- text: 'Typical uploads: liability waiver, vaccine proof, venue rules, incident report template.',
598
- }),
599
- SDUI.components.HStack({
600
- gap: 'space300',
601
- wrap: 'wrap',
602
- children: [
603
- SDUI.components.Badge({ text: 'Waivers', appearance: 'PRIMARY_LIGHT' }),
604
- SDUI.components.Badge({ text: 'Vaccination records', appearance: 'SUCCESS_LIGHT' }),
605
- SDUI.components.Badge({ text: 'Venue map', appearance: 'WARNING_LIGHT' }),
606
- SDUI.components.Badge({ text: 'Incident log', appearance: 'INFO_LIGHT' }),
607
- ],
608
- }),
609
- ],
610
- }),
611
- ],
612
- }),
613
- ],
614
- }),
615
- ],
616
- });
617
-
618
- const root = SDUI.components.Layout({
619
- width: '100%',
620
- minHeight: 920,
621
- children: [
622
- SDUI.components.VStack({
623
- align: 'stretch',
624
- gap: 'space500',
625
- children: [
626
- SDUI.components.Tab({
627
- value: s.activeTab.$bind,
628
- items: [
629
- { label: 'Home', value: 'overview' },
630
- { label: 'Dogs', value: 'dogs' },
631
- { label: 'Schedule', value: 'sessions' },
632
- { label: 'Today', value: 'today' },
633
- { label: 'Resources', value: 'resources' },
634
- ],
635
- }),
636
- SDUI.visible(SDUI.conditions.eq(s.activeTab, 'overview'), overviewPanel),
637
- SDUI.visible(SDUI.conditions.eq(s.activeTab, 'dogs'), dogsPanel),
638
- SDUI.visible(SDUI.conditions.eq(s.activeTab, 'sessions'), sessionsPanel),
639
- SDUI.visible(SDUI.conditions.eq(s.activeTab, 'today'), todayPanel),
640
- SDUI.visible(SDUI.conditions.eq(s.activeTab, 'resources'), resourcesPanel),
641
-
642
- SDUI.components.Modal({
643
- title: 'Add Dog',
644
- isOpen: s.showCreateDogModal.$bind,
645
- onClose: SDUI.actions.set(s.showCreateDogModal, false),
646
- children: [
647
- SDUI.components.VStack({
648
- gap: 'space300',
649
- children: [
650
- SDUI.components.TextInput({
651
- label: 'Dog Name',
652
- name: 'dog_name',
653
- value: s.dogForm.dog_name.$bind,
654
- isRequired: true,
655
- }),
656
- SDUI.components.TextInput({
657
- label: 'Breed',
658
- name: 'breed',
659
- value: s.dogForm.breed.$bind,
660
- }),
661
- SDUI.components.Number({
662
- label: 'Age (years)',
663
- name: 'age',
664
- value: s.dogForm.age.$bind,
665
- }),
666
- ],
667
- }),
668
- SDUI.components.HStack({
669
- gap: 'space200',
670
- justify: 'flex-end',
671
- children: [
672
- SDUI.components.Button({
673
- label: 'Cancel',
674
- appearance: 'OUTLINE',
675
- onPress: SDUI.actions.set(s.showCreateDogModal, false),
676
- }),
677
- SDUI.components.Button({
678
- label: 'Create',
679
- appearance: 'PRIMARY',
680
- onPress: SDUI.actions.callFunction(functionId, 'create_dog', {
681
- dog_name: s.dogForm.dog_name,
682
- breed: s.dogForm.breed,
683
- age: s.dogForm.age,
684
- responsePath: '/dogs',
685
- onSuccess: { set: { '/showCreateDogModal': false } },
686
- }),
687
- }),
688
- ],
689
- }),
690
- ],
691
- }),
692
-
693
- SDUI.components.Modal({
694
- title: 'Edit Dog',
695
- isOpen: s.showEditDogModal.$bind,
696
- onClose: SDUI.actions.set(s.showEditDogModal, false),
697
- children: [
698
- SDUI.components.VStack({
699
- gap: 'space300',
700
- children: [
701
- SDUI.components.TextInput({
702
- label: 'Dog Name',
703
- name: 'edit_dog_name',
704
- value: s.dogForm.dog_name.$bind,
705
- isRequired: true,
706
- }),
707
- SDUI.components.TextInput({
708
- label: 'Breed',
709
- name: 'edit_breed',
710
- value: s.dogForm.breed.$bind,
711
- }),
712
- SDUI.components.Number({
713
- label: 'Age (years)',
714
- name: 'edit_age',
715
- value: s.dogForm.age.$bind,
716
- }),
717
- ],
718
- }),
719
- SDUI.components.HStack({
720
- gap: 'space200',
721
- justify: 'flex-end',
722
- children: [
723
- SDUI.components.Button({
724
- label: 'Delete',
725
- appearance: 'DESTRUCTIVE',
726
- onPress: SDUI.actions.callFunction(functionId, 'delete_dog', {
727
- id: s.selectedDog['id'],
728
- responsePath: '/dogs',
729
- onSuccess: { set: { '/showEditDogModal': false } },
730
- }),
731
- }),
732
- SDUI.components.Button({
733
- label: 'Cancel',
734
- appearance: 'OUTLINE',
735
- onPress: SDUI.actions.set(s.showEditDogModal, false),
736
- }),
737
- SDUI.components.Button({
738
- label: 'Save',
739
- appearance: 'PRIMARY',
740
- onPress: SDUI.actions.callFunction(functionId, 'update_dog', {
741
- id: s.selectedDog['id'],
742
- dog_name: s.dogForm.dog_name,
743
- breed: s.dogForm.breed,
744
- age: s.dogForm.age,
745
- responsePath: '/dogs',
746
- onSuccess: { set: { '/showEditDogModal': false } },
747
- }),
748
- }),
749
- ],
750
- }),
751
- ],
752
- }),
753
-
754
- SDUI.components.Modal({
755
- title: 'Add Training Session',
756
- isOpen: s.showCreateSessionModal.$bind,
757
- onClose: SDUI.actions.set(s.showCreateSessionModal, false),
758
- children: [
759
- SDUI.components.VStack({
760
- gap: 'space300',
761
- children: [
762
- SDUI.components.TextInput({
763
- label: 'Dog ID',
764
- name: 'dog_id',
765
- value: s.sessionForm.dog_id.$bind,
766
- isRequired: true,
767
- }),
768
- SDUI.components.TextInput({
769
- label: 'Session Date',
770
- name: 'session_date',
771
- value: s.sessionForm.session_date.$bind,
772
- isRequired: true,
773
- }),
774
- SDUI.components.Number({
775
- label: 'Duration (min)',
776
- name: 'duration',
777
- value: s.sessionForm.duration.$bind,
778
- }),
779
- SDUI.components.Select({
780
- label: 'Training Type',
781
- name: 'training_type',
782
- value: s.sessionForm.training_type.$bind,
783
- options: [
784
- { label: 'Obedience', value: 'Obedience' },
785
- { label: 'Agility', value: 'Agility' },
786
- { label: 'Socialization', value: 'Socialization' },
787
- { label: 'Behavior', value: 'Behavior' },
788
- ],
789
- }),
790
- SDUI.components.Textarea({
791
- label: 'Notes',
792
- name: 'notes',
793
- value: s.sessionForm.notes.$bind,
794
- }),
795
- ],
796
- }),
797
- SDUI.components.HStack({
798
- gap: 'space200',
799
- justify: 'flex-end',
800
- children: [
801
- SDUI.components.Button({
802
- label: 'Cancel',
803
- appearance: 'OUTLINE',
804
- onPress: SDUI.actions.set(s.showCreateSessionModal, false),
805
- }),
806
- SDUI.components.Button({
807
- label: 'Create',
808
- appearance: 'PRIMARY',
809
- onPress: SDUI.actions.callFunction(functionId, 'create_session', {
810
- dog_id: s.sessionForm.dog_id,
811
- session_date: s.sessionForm.session_date,
812
- duration: s.sessionForm.duration,
813
- training_type: s.sessionForm.training_type,
814
- notes: s.sessionForm.notes,
815
- responsePath: '/sessions',
816
- onSuccess: { set: { '/showCreateSessionModal': false } },
817
- }),
818
- }),
819
- ],
820
- }),
821
- ],
822
- }),
823
-
824
- SDUI.components.Modal({
825
- title: 'Edit Training Session',
826
- isOpen: s.showEditSessionModal.$bind,
827
- onClose: SDUI.actions.set(s.showEditSessionModal, false),
828
- children: [
829
- SDUI.components.VStack({
830
- gap: 'space300',
831
- children: [
832
- SDUI.components.TextInput({
833
- label: 'Dog ID',
834
- name: 'edit_dog_id',
835
- value: s.sessionForm.dog_id.$bind,
836
- isRequired: true,
837
- }),
838
- SDUI.components.TextInput({
839
- label: 'Session Date',
840
- name: 'edit_session_date',
841
- value: s.sessionForm.session_date.$bind,
842
- isRequired: true,
843
- }),
844
- SDUI.components.Number({
845
- label: 'Duration (min)',
846
- name: 'edit_duration',
847
- value: s.sessionForm.duration.$bind,
848
- }),
849
- SDUI.components.Select({
850
- label: 'Training Type',
851
- name: 'edit_training_type',
852
- value: s.sessionForm.training_type.$bind,
853
- options: [
854
- { label: 'Obedience', value: 'Obedience' },
855
- { label: 'Agility', value: 'Agility' },
856
- { label: 'Socialization', value: 'Socialization' },
857
- { label: 'Behavior', value: 'Behavior' },
858
- ],
859
- }),
860
- SDUI.components.Textarea({
861
- label: 'Notes',
862
- name: 'edit_notes',
863
- value: s.sessionForm.notes.$bind,
864
- }),
865
- ],
866
- }),
867
- SDUI.components.HStack({
868
- gap: 'space200',
869
- justify: 'flex-end',
870
- children: [
871
- SDUI.components.Button({
872
- label: 'Delete',
873
- appearance: 'DESTRUCTIVE',
874
- onPress: SDUI.actions.callFunction(functionId, 'delete_session', {
875
- id: s.selectedSession['id'],
876
- responsePath: '/sessions',
877
- onSuccess: { set: { '/showEditSessionModal': false } },
878
- }),
879
- }),
880
- SDUI.components.Button({
881
- label: 'Cancel',
882
- appearance: 'OUTLINE',
883
- onPress: SDUI.actions.set(s.showEditSessionModal, false),
884
- }),
885
- SDUI.components.Button({
886
- label: 'Save',
887
- appearance: 'PRIMARY',
888
- onPress: SDUI.actions.callFunction(functionId, 'update_session', {
889
- id: s.selectedSession['id'],
890
- dog_id: s.sessionForm.dog_id,
891
- session_date: s.sessionForm.session_date,
892
- duration: s.sessionForm.duration,
893
- training_type: s.sessionForm.training_type,
894
- notes: s.sessionForm.notes,
895
- responsePath: '/sessions',
896
- onSuccess: { set: { '/showEditSessionModal': false } },
897
- }),
898
- }),
899
- ],
900
- }),
901
- ],
902
- }),
903
- ],
904
- }),
905
- ],
906
- });
907
-
908
- return SDUI.render(s, root);
909
- }
910
-
911
- export async function onRipplingEvent(
912
- event: FunctionEvent,
913
- context: FunctionContext,
914
- ): Promise<FunctionResponse> {
915
- const params = event['parameters'] as Record<string, unknown> | undefined;
916
- const action = params?.['action'] as string | undefined;
917
- const functionId = context.function?.function_id ?? 'demo-fn';
918
-
919
- if (!action || action === 'init') {
920
- mutableDogs = INITIAL_DOGS.map((r) => ({ ...r }));
921
- mutableSessions = INITIAL_SESSIONS.map((r) => ({ ...r }));
922
- const spec = renderSpec(functionId);
923
- return new FunctionResponse({
924
- body: { spec: JSON.stringify(spec) },
925
- statusCode: 200,
926
- headers: {},
927
- message: '',
928
- });
929
- }
930
-
931
- const p = params ?? {};
932
-
933
- if (action === 'create_dog') {
934
- const id = `dog_${mutableDogs.length + 1}`;
935
- mutableDogs = [
936
- ...mutableDogs,
937
- {
938
- id,
939
- _key: id,
940
- dog_name__c: String(p['dog_name'] ?? 'Unnamed'),
941
- breed__c: String(p['breed'] ?? ''),
942
- age__c: p['age'] !== '' && p['age'] != null ? Number(p['age']) : 0,
943
- total_training_time__c: 0,
944
- },
945
- ];
946
- return new FunctionResponse({
947
- body: mutableDogs as unknown as Record<string, unknown>,
948
- statusCode: 200,
949
- headers: {},
950
- message: '',
951
- });
952
- }
953
-
954
- if (action === 'update_dog') {
955
- mutableDogs = mutableDogs.map((r) =>
956
- r.id === p['id'] ?
957
- {
958
- ...r,
959
- dog_name__c: String(p['dog_name'] ?? r.dog_name__c),
960
- breed__c: String(p['breed'] ?? r.breed__c),
961
- age__c: p['age'] !== '' && p['age'] != null ? Number(p['age']) : r.age__c,
962
- }
963
- : r,
964
- );
965
- return new FunctionResponse({
966
- body: mutableDogs as unknown as Record<string, unknown>,
967
- statusCode: 200,
968
- headers: {},
969
- message: '',
970
- });
971
- }
972
-
973
- if (action === 'delete_dog') {
974
- mutableDogs = mutableDogs.filter((r) => r.id !== p['id']);
975
- return new FunctionResponse({
976
- body: mutableDogs as unknown as Record<string, unknown>,
977
- statusCode: 200,
978
- headers: {},
979
- message: '',
980
- });
981
- }
982
-
983
- if (action === 'create_session') {
984
- const id = `sess_${mutableSessions.length + 1}`;
985
- mutableSessions = [
986
- ...mutableSessions,
987
- {
988
- id,
989
- _key: id,
990
- dog_id__c: String(p['dog_id'] ?? ''),
991
- session_date__c: String(p['session_date'] ?? ''),
992
- duration__c: p['duration'] !== '' && p['duration'] != null ? Number(p['duration']) : 0,
993
- training_type__c: String(p['training_type'] ?? ''),
994
- notes__c: String(p['notes'] ?? ''),
995
- },
996
- ];
997
- return new FunctionResponse({
998
- body: mutableSessions as unknown as Record<string, unknown>,
999
- statusCode: 200,
1000
- headers: {},
1001
- message: '',
1002
- });
1003
- }
1004
-
1005
- if (action === 'update_session') {
1006
- mutableSessions = mutableSessions.map((r) =>
1007
- r.id === p['id'] ?
1008
- {
1009
- ...r,
1010
- dog_id__c: String(p['dog_id'] ?? r.dog_id__c),
1011
- session_date__c: String(p['session_date'] ?? r.session_date__c),
1012
- duration__c: p['duration'] !== '' && p['duration'] != null ? Number(p['duration']) : r.duration__c,
1013
- training_type__c: String(p['training_type'] ?? r.training_type__c),
1014
- notes__c: String(p['notes'] ?? r.notes__c),
1015
- }
1016
- : r,
1017
- );
1018
- return new FunctionResponse({
1019
- body: mutableSessions as unknown as Record<string, unknown>,
1020
- statusCode: 200,
1021
- headers: {},
1022
- message: '',
1023
- });
1024
- }
1025
-
1026
- if (action === 'delete_session') {
1027
- mutableSessions = mutableSessions.filter((r) => r.id !== p['id']);
1028
- return new FunctionResponse({
1029
- body: mutableSessions as unknown as Record<string, unknown>,
1030
- statusCode: 200,
1031
- headers: {},
1032
- message: '',
1033
- });
1034
- }
1035
-
1036
- return new FunctionResponse({
1037
- body: {},
1038
- statusCode: 400,
1039
- headers: {},
1040
- message: 'Unknown action',
1041
- });
1042
- }