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

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