@rippling/rippling-sdk 0.2.0-alpha.34 → 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 (270) 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/lib/manifest/app.d.mts +81 -13
  23. package/lib/manifest/app.d.mts.map +1 -1
  24. package/lib/manifest/app.d.ts +81 -13
  25. package/lib/manifest/app.d.ts.map +1 -1
  26. package/lib/manifest/app.js +37 -16
  27. package/lib/manifest/app.js.map +1 -1
  28. package/lib/manifest/app.mjs +37 -16
  29. package/lib/manifest/app.mjs.map +1 -1
  30. package/lib/manifest/category.d.mts +63 -7
  31. package/lib/manifest/category.d.mts.map +1 -1
  32. package/lib/manifest/category.d.ts +63 -7
  33. package/lib/manifest/category.d.ts.map +1 -1
  34. package/lib/manifest/category.js +45 -10
  35. package/lib/manifest/category.js.map +1 -1
  36. package/lib/manifest/category.mjs +43 -8
  37. package/lib/manifest/category.mjs.map +1 -1
  38. package/lib/manifest/custom-object-field-section.d.mts +56 -6
  39. package/lib/manifest/custom-object-field-section.d.mts.map +1 -1
  40. package/lib/manifest/custom-object-field-section.d.ts +56 -6
  41. package/lib/manifest/custom-object-field-section.d.ts.map +1 -1
  42. package/lib/manifest/custom-object-field-section.js +41 -14
  43. package/lib/manifest/custom-object-field-section.js.map +1 -1
  44. package/lib/manifest/custom-object-field-section.mjs +39 -12
  45. package/lib/manifest/custom-object-field-section.mjs.map +1 -1
  46. package/lib/manifest/custom-object-page-layout.d.mts +201 -41
  47. package/lib/manifest/custom-object-page-layout.d.mts.map +1 -1
  48. package/lib/manifest/custom-object-page-layout.d.ts +201 -41
  49. package/lib/manifest/custom-object-page-layout.d.ts.map +1 -1
  50. package/lib/manifest/custom-object-page-layout.js +76 -30
  51. package/lib/manifest/custom-object-page-layout.js.map +1 -1
  52. package/lib/manifest/custom-object-page-layout.mjs +74 -28
  53. package/lib/manifest/custom-object-page-layout.mjs.map +1 -1
  54. package/lib/manifest/custom-object.d.mts +128 -15
  55. package/lib/manifest/custom-object.d.mts.map +1 -1
  56. package/lib/manifest/custom-object.d.ts +128 -15
  57. package/lib/manifest/custom-object.d.ts.map +1 -1
  58. package/lib/manifest/custom-object.js +61 -26
  59. package/lib/manifest/custom-object.js.map +1 -1
  60. package/lib/manifest/custom-object.mjs +61 -26
  61. package/lib/manifest/custom-object.mjs.map +1 -1
  62. package/lib/manifest/field.d.mts +265 -54
  63. package/lib/manifest/field.d.mts.map +1 -1
  64. package/lib/manifest/field.d.ts +265 -54
  65. package/lib/manifest/field.d.ts.map +1 -1
  66. package/lib/manifest/field.js +100 -57
  67. package/lib/manifest/field.js.map +1 -1
  68. package/lib/manifest/field.mjs +100 -57
  69. package/lib/manifest/field.mjs.map +1 -1
  70. package/lib/manifest/list-view.d.mts +105 -14
  71. package/lib/manifest/list-view.d.mts.map +1 -1
  72. package/lib/manifest/list-view.d.ts +105 -14
  73. package/lib/manifest/list-view.d.ts.map +1 -1
  74. package/lib/manifest/list-view.js +50 -14
  75. package/lib/manifest/list-view.js.map +1 -1
  76. package/lib/manifest/list-view.mjs +48 -12
  77. package/lib/manifest/list-view.mjs.map +1 -1
  78. package/lib/manifest/manifest-builder.d.mts +77 -11
  79. package/lib/manifest/manifest-builder.d.mts.map +1 -1
  80. package/lib/manifest/manifest-builder.d.ts +77 -11
  81. package/lib/manifest/manifest-builder.d.ts.map +1 -1
  82. package/lib/manifest/manifest-builder.js +46 -24
  83. package/lib/manifest/manifest-builder.js.map +1 -1
  84. package/lib/manifest/manifest-builder.mjs +44 -22
  85. package/lib/manifest/manifest-builder.mjs.map +1 -1
  86. package/lib/manifest/manifest-function.d.mts +92 -12
  87. package/lib/manifest/manifest-function.d.mts.map +1 -1
  88. package/lib/manifest/manifest-function.d.ts +92 -12
  89. package/lib/manifest/manifest-function.d.ts.map +1 -1
  90. package/lib/manifest/manifest-function.js +40 -11
  91. package/lib/manifest/manifest-function.js.map +1 -1
  92. package/lib/manifest/manifest-function.mjs +38 -9
  93. package/lib/manifest/manifest-function.mjs.map +1 -1
  94. package/lib/manifest/rule.d.mts +94 -16
  95. package/lib/manifest/rule.d.mts.map +1 -1
  96. package/lib/manifest/rule.d.ts +94 -16
  97. package/lib/manifest/rule.d.ts.map +1 -1
  98. package/lib/manifest/rule.js +36 -15
  99. package/lib/manifest/rule.js.map +1 -1
  100. package/lib/manifest/rule.mjs +36 -15
  101. package/lib/manifest/rule.mjs.map +1 -1
  102. package/lib/manifest/sdui-page.d.mts +67 -11
  103. package/lib/manifest/sdui-page.d.mts.map +1 -1
  104. package/lib/manifest/sdui-page.d.ts +67 -11
  105. package/lib/manifest/sdui-page.d.ts.map +1 -1
  106. package/lib/manifest/sdui-page.js +44 -13
  107. package/lib/manifest/sdui-page.js.map +1 -1
  108. package/lib/manifest/sdui-page.mjs +42 -11
  109. package/lib/manifest/sdui-page.mjs.map +1 -1
  110. package/lib/manifest/validation.d.mts +87 -12
  111. package/lib/manifest/validation.d.mts.map +1 -1
  112. package/lib/manifest/validation.d.ts +87 -12
  113. package/lib/manifest/validation.d.ts.map +1 -1
  114. package/lib/manifest/validation.js +41 -15
  115. package/lib/manifest/validation.js.map +1 -1
  116. package/lib/manifest/validation.mjs +39 -13
  117. package/lib/manifest/validation.mjs.map +1 -1
  118. package/lib/sdui/actions.d.mts +25 -0
  119. package/lib/sdui/actions.d.mts.map +1 -0
  120. package/lib/sdui/actions.d.ts +25 -0
  121. package/lib/sdui/actions.d.ts.map +1 -0
  122. package/lib/sdui/actions.js +53 -0
  123. package/lib/sdui/actions.js.map +1 -0
  124. package/lib/sdui/actions.mjs +42 -0
  125. package/lib/sdui/actions.mjs.map +1 -0
  126. package/lib/sdui/component.d.mts +41 -0
  127. package/lib/sdui/component.d.mts.map +1 -0
  128. package/lib/sdui/component.d.ts +41 -0
  129. package/lib/sdui/component.d.ts.map +1 -0
  130. package/lib/sdui/component.js +95 -0
  131. package/lib/sdui/component.js.map +1 -0
  132. package/lib/sdui/component.mjs +91 -0
  133. package/lib/sdui/component.mjs.map +1 -0
  134. package/lib/sdui/components.d.mts +149 -0
  135. package/lib/sdui/components.d.mts.map +1 -0
  136. package/lib/sdui/components.d.ts +149 -0
  137. package/lib/sdui/components.d.ts.map +1 -0
  138. package/lib/sdui/components.js +303 -0
  139. package/lib/sdui/components.js.map +1 -0
  140. package/lib/sdui/components.mjs +298 -0
  141. package/lib/sdui/components.mjs.map +1 -0
  142. package/lib/sdui/conditions.d.mts +25 -0
  143. package/lib/sdui/conditions.d.mts.map +1 -0
  144. package/lib/sdui/conditions.d.ts +25 -0
  145. package/lib/sdui/conditions.d.ts.map +1 -0
  146. package/lib/sdui/conditions.js +47 -0
  147. package/lib/sdui/conditions.js.map +1 -0
  148. package/lib/sdui/conditions.mjs +35 -0
  149. package/lib/sdui/conditions.mjs.map +1 -0
  150. package/lib/sdui/directives.d.mts +18 -0
  151. package/lib/sdui/directives.d.mts.map +1 -0
  152. package/lib/sdui/directives.d.ts +18 -0
  153. package/lib/sdui/directives.d.ts.map +1 -0
  154. package/lib/sdui/directives.js +28 -0
  155. package/lib/sdui/directives.js.map +1 -0
  156. package/lib/sdui/directives.mjs +21 -0
  157. package/lib/sdui/directives.mjs.map +1 -0
  158. package/lib/sdui/index.d.mts +362 -0
  159. package/lib/sdui/index.d.mts.map +1 -0
  160. package/lib/sdui/index.d.ts +362 -0
  161. package/lib/sdui/index.d.ts.map +1 -0
  162. package/lib/sdui/index.js +375 -0
  163. package/lib/sdui/index.js.map +1 -0
  164. package/lib/sdui/index.mjs +199 -0
  165. package/lib/sdui/index.mjs.map +1 -0
  166. package/lib/sdui/props.d.mts +12 -0
  167. package/lib/sdui/props.d.mts.map +1 -0
  168. package/lib/sdui/props.d.ts +12 -0
  169. package/lib/sdui/props.d.ts.map +1 -0
  170. package/lib/sdui/props.js +25 -0
  171. package/lib/sdui/props.js.map +1 -0
  172. package/lib/sdui/props.mjs +21 -0
  173. package/lib/sdui/props.mjs.map +1 -0
  174. package/lib/sdui/render.d.mts +23 -0
  175. package/lib/sdui/render.d.mts.map +1 -0
  176. package/lib/sdui/render.d.ts +23 -0
  177. package/lib/sdui/render.d.ts.map +1 -0
  178. package/lib/sdui/render.js +40 -0
  179. package/lib/sdui/render.js.map +1 -0
  180. package/lib/sdui/render.mjs +37 -0
  181. package/lib/sdui/render.mjs.map +1 -0
  182. package/lib/sdui/state.d.mts +22 -0
  183. package/lib/sdui/state.d.mts.map +1 -0
  184. package/lib/sdui/state.d.ts +22 -0
  185. package/lib/sdui/state.d.ts.map +1 -0
  186. package/lib/sdui/state.js +57 -0
  187. package/lib/sdui/state.js.map +1 -0
  188. package/lib/sdui/state.mjs +53 -0
  189. package/lib/sdui/state.mjs.map +1 -0
  190. package/package.json +11 -1
  191. package/resources/company-legal-entity-workers.d.mts +79 -0
  192. package/resources/company-legal-entity-workers.d.mts.map +1 -0
  193. package/resources/company-legal-entity-workers.d.ts +79 -0
  194. package/resources/company-legal-entity-workers.d.ts.map +1 -0
  195. package/resources/company-legal-entity-workers.js +30 -0
  196. package/resources/company-legal-entity-workers.js.map +1 -0
  197. package/resources/company-legal-entity-workers.mjs +26 -0
  198. package/resources/company-legal-entity-workers.mjs.map +1 -0
  199. package/resources/draft-hires.d.mts +4 -0
  200. package/resources/draft-hires.d.mts.map +1 -1
  201. package/resources/draft-hires.d.ts +4 -0
  202. package/resources/draft-hires.d.ts.map +1 -1
  203. package/resources/files.d.mts +16 -1
  204. package/resources/files.d.mts.map +1 -1
  205. package/resources/files.d.ts +16 -1
  206. package/resources/files.d.ts.map +1 -1
  207. package/resources/headcount-positions.d.mts +2 -2
  208. package/resources/headcount-positions.d.mts.map +1 -1
  209. package/resources/headcount-positions.d.ts +2 -2
  210. package/resources/headcount-positions.d.ts.map +1 -1
  211. package/resources/index.d.mts +1 -0
  212. package/resources/index.d.mts.map +1 -1
  213. package/resources/index.d.ts +1 -0
  214. package/resources/index.d.ts.map +1 -1
  215. package/resources/index.js +4 -2
  216. package/resources/index.js.map +1 -1
  217. package/resources/index.mjs +1 -0
  218. package/resources/index.mjs.map +1 -1
  219. package/resources/legal-entities.d.mts +1 -1
  220. package/resources/legal-entities.d.mts.map +1 -1
  221. package/resources/legal-entities.d.ts +1 -1
  222. package/resources/legal-entities.d.ts.map +1 -1
  223. package/resources/location-factors.d.mts +2 -2
  224. package/resources/location-factors.d.mts.map +1 -1
  225. package/resources/location-factors.d.ts +2 -2
  226. package/resources/location-factors.d.ts.map +1 -1
  227. package/resources/shared.d.mts +1 -1
  228. package/resources/shared.d.mts.map +1 -1
  229. package/resources/shared.d.ts +1 -1
  230. package/resources/shared.d.ts.map +1 -1
  231. package/resources/workers.d.mts +2 -2
  232. package/resources/workers.d.mts.map +1 -1
  233. package/resources/workers.d.ts +2 -2
  234. package/resources/workers.d.ts.map +1 -1
  235. package/src/client.ts +20 -0
  236. package/src/lib/manifest/app.ts +81 -26
  237. package/src/lib/manifest/category.ts +63 -14
  238. package/src/lib/manifest/custom-object-field-section.ts +56 -17
  239. package/src/lib/manifest/custom-object-page-layout.ts +201 -66
  240. package/src/lib/manifest/custom-object.ts +128 -37
  241. package/src/lib/manifest/field.ts +265 -75
  242. package/src/lib/manifest/list-view.ts +105 -25
  243. package/src/lib/manifest/manifest-builder.ts +75 -32
  244. package/src/lib/manifest/manifest-function.ts +92 -22
  245. package/src/lib/manifest/rule.ts +95 -29
  246. package/src/lib/manifest/sdui-page.ts +67 -21
  247. package/src/lib/manifest/validation.ts +87 -24
  248. package/src/lib/sdui/actions.ts +58 -0
  249. package/src/lib/sdui/component.ts +122 -0
  250. package/src/lib/sdui/components.ts +308 -0
  251. package/src/lib/sdui/conditions.ts +59 -0
  252. package/src/lib/sdui/directives.ts +42 -0
  253. package/src/lib/sdui/index.ts +365 -0
  254. package/src/lib/sdui/props.ts +32 -0
  255. package/src/lib/sdui/render.ts +58 -0
  256. package/src/lib/sdui/state.ts +60 -0
  257. package/src/resources/company-legal-entity-workers.ts +115 -0
  258. package/src/resources/draft-hires.ts +5 -0
  259. package/src/resources/files.ts +18 -1
  260. package/src/resources/headcount-positions.ts +2 -0
  261. package/src/resources/index.ts +7 -0
  262. package/src/resources/legal-entities.ts +1 -0
  263. package/src/resources/location-factors.ts +2 -0
  264. package/src/resources/shared.ts +1 -0
  265. package/src/resources/workers.ts +2 -0
  266. package/src/version.ts +1 -1
  267. package/version.d.mts +1 -1
  268. package/version.d.ts +1 -1
  269. package/version.js +1 -1
  270. 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));