@genesislcap/blank-app-seed 5.22.2-prerelease.2 → 5.23.1

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 (194) hide show
  1. package/.genx/configure.js +11 -1
  2. package/.genx/package.json +1 -1
  3. package/.genx/scripts/generate-test-apps.sh +124 -0
  4. package/.genx/templates/angular/chart.hbs +19 -7
  5. package/.genx/templates/angular/component/component.column.defs.hbs +16 -4
  6. package/.genx/templates/angular/component/component.create.form.hbs +10 -2
  7. package/.genx/templates/angular/component/component.events.config.hbs +31 -6
  8. package/.genx/templates/angular/component/component.filter.form.hbs +1 -2
  9. package/.genx/templates/angular/component/component.gridOptions.hbs +15 -3
  10. package/.genx/templates/angular/component/component.hbs +77 -46
  11. package/.genx/templates/angular/component/component.index.hbs +1 -1
  12. package/.genx/templates/angular/component/component.update.form.hbs +1 -1
  13. package/.genx/templates/angular/entityManager.hbs +6 -6
  14. package/{client-tmp/angular/src/app/utils/fdc3.ts → .genx/templates/angular/fdc3.ts.hbs} +6 -8
  15. package/.genx/templates/angular/form.hbs +9 -12
  16. package/.genx/templates/angular/grid.hbs +43 -40
  17. package/.genx/templates/angular/route.hbs +8 -2
  18. package/.genx/templates/angular/route.template.hbs +7 -7
  19. package/.genx/templates/angular/slices/eventing.slice.hbs +21 -10
  20. package/.genx/templates/angular/store.hbs +1 -5
  21. package/.genx/templates/angular/tabsPanel.hbs +2 -2
  22. package/.genx/templates/react/chart.hbs +6 -15
  23. package/.genx/templates/react/component/component.column.defs.hbs +16 -10
  24. package/.genx/templates/react/component/component.create.form.hbs +2 -2
  25. package/.genx/templates/react/component/component.events.config.hbs +32 -5
  26. package/.genx/templates/react/component/component.filter.form.hbs +1 -2
  27. package/.genx/templates/react/component/component.gridOptions.hbs +17 -11
  28. package/.genx/templates/react/component/component.hbs +84 -42
  29. package/.genx/templates/react/component/component.update.form.hbs +1 -1
  30. package/.genx/templates/react/entityManager.hbs +8 -16
  31. package/{client-tmp/react/src/utils/fdc3.ts → .genx/templates/react/fdc3.ts.hbs} +7 -8
  32. package/.genx/templates/react/form.hbs +1 -15
  33. package/.genx/templates/react/grid.hbs +11 -11
  34. package/.genx/templates/react/gridLayout.hbs +44 -4
  35. package/.genx/templates/react/horizontalLayout.hbs +10 -1
  36. package/.genx/templates/react/route.hbs +23 -16
  37. package/.genx/templates/react/slices/eventing.slice.hbs +19 -8
  38. package/.genx/templates/react/store.hbs +1 -5
  39. package/.genx/templates/react/tabsLayout.hbs +5 -1
  40. package/.genx/templates/web-components/chart.hbs +25 -25
  41. package/.genx/templates/web-components/component/component.column.defs.hbs +11 -3
  42. package/.genx/templates/web-components/component/component.create.form.hbs +11 -4
  43. package/.genx/templates/web-components/component/component.events.config.hbs +33 -6
  44. package/.genx/templates/web-components/component/component.filter.form.hbs +1 -2
  45. package/.genx/templates/web-components/component/component.gridOptions.hbs +12 -3
  46. package/.genx/templates/web-components/component/component.hbs +19 -20
  47. package/.genx/templates/web-components/component/component.index.hbs +1 -1
  48. package/.genx/templates/web-components/component/component.template.hbs +9 -13
  49. package/.genx/templates/web-components/component/component.update.form.hbs +1 -1
  50. package/.genx/templates/web-components/entityManager.hbs +92 -73
  51. package/{client-tmp/web-components/src/utils/fdc3.ts → .genx/templates/web-components/fdc3.ts.hbs} +6 -7
  52. package/.genx/templates/web-components/form.hbs +16 -15
  53. package/.genx/templates/web-components/grid.hbs +49 -41
  54. package/.genx/templates/web-components/gridLayout.hbs +32 -28
  55. package/.genx/templates/web-components/horizontalLayout.hbs +5 -5
  56. package/.genx/templates/web-components/route.template.hbs +4 -6
  57. package/.genx/templates/web-components/slices/eventing.slice.hbs +23 -15
  58. package/.genx/templates/web-components/store.hbs +1 -9
  59. package/.genx/templates/web-components/tabsLayout.hbs +5 -5
  60. package/.genx/tests/fixtures/routes-full.json +299 -0
  61. package/.genx/tests/fixtures/testapp/client/src/pages/Home/Home.tsx +12 -0
  62. package/.genx/utils/formatJSONValue.js +38 -2
  63. package/.genx/utils/formatRouteData.js +67 -18
  64. package/.genx/utils/generateTile.js +10 -1
  65. package/.genx/utils/gridSerializers.js +63 -23
  66. package/.genx/utils/registerPartials.js +15 -12
  67. package/.genx/utils/toJsStringLiteral.js +37 -0
  68. package/.genx/versions.json +3 -3
  69. package/.github/workflows/build.yml +22 -1
  70. package/.husky/pre-commit +8 -0
  71. package/CHANGELOG.md +97 -23
  72. package/bdd-tests/gradle/wrapper/gradle-wrapper.properties +1 -1
  73. package/bdd-tests/gradle.properties +0 -1
  74. package/client-tmp/angular/.oxfmtrc.json +32 -0
  75. package/client-tmp/angular/.oxlintrc.json +4 -0
  76. package/client-tmp/angular/README.md +4 -4
  77. package/client-tmp/angular/jest.config.js +1 -1
  78. package/client-tmp/angular/lint-css.js +24 -3
  79. package/client-tmp/angular/oxlint-staged.js +45 -0
  80. package/client-tmp/angular/package.json +19 -9
  81. package/client-tmp/angular/playwright.config.ts +12 -11
  82. package/client-tmp/angular/src/app/app.component.html +0 -1
  83. package/client-tmp/angular/src/app/app.component.ts +8 -9
  84. package/client-tmp/angular/src/app/app.config.ts +2 -2
  85. package/client-tmp/angular/src/app/app.module.ts +6 -5
  86. package/client-tmp/angular/src/app/components/error-message/error-message.component.html +29 -15
  87. package/client-tmp/angular/src/app/components/error-message/error-message.component.spec.ts +2 -2
  88. package/client-tmp/angular/src/app/components/error-message/error-message.component.ts +3 -5
  89. package/client-tmp/angular/src/app/directive/app-lazy-load.directive.ts +1 -0
  90. package/client-tmp/angular/src/app/guards/chained.guard.ts +19 -19
  91. package/client-tmp/angular/src/app/guards/connection.guard.ts +12 -12
  92. package/client-tmp/angular/src/app/layouts/blank/blank.layout.spec.ts +1 -1
  93. package/client-tmp/angular/src/app/layouts/blank/blank.layout.ts +1 -5
  94. package/client-tmp/angular/src/app/layouts/default/default.layout.html +3 -4
  95. package/client-tmp/angular/src/app/layouts/default/default.layout.spec.ts +4 -4
  96. package/client-tmp/angular/src/app/layouts/default/default.layout.ts +1 -1
  97. package/client-tmp/angular/src/app/pages/auth-login/auth-login.component.ts +1 -2
  98. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.html +3 -3
  99. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.scss +1 -1
  100. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.spec.ts +5 -5
  101. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.ts +3 -4
  102. package/client-tmp/angular/src/app/services/route.service.ts +79 -74
  103. package/client-tmp/angular/src/app/share/foundation-auth.ts +7 -1
  104. package/client-tmp/angular/src/app/share/genesis-components.ts +66 -68
  105. package/client-tmp/angular/src/app/utils/index.ts +3 -1
  106. package/client-tmp/angular/src/app/utils/permissions.ts +7 -7
  107. package/client-tmp/angular/src/declaration.d.ts +1 -1
  108. package/client-tmp/angular/src/environments/environment.ts +1 -1
  109. package/client-tmp/angular/src/jest.setup.ts +8 -0
  110. package/client-tmp/angular/src/main.ts +2 -2
  111. package/client-tmp/angular/src/pbc/container.ts +28 -26
  112. package/client-tmp/angular/src/pbc/elementsRenderer.ts +49 -39
  113. package/client-tmp/angular/src/pbc/utils.ts +61 -60
  114. package/client-tmp/angular/src/styles/_mixins.scss +7 -8
  115. package/client-tmp/angular/src/styles/styles.css +6 -6
  116. package/client-tmp/angular/test/e2e/fixture.ts +2 -2
  117. package/client-tmp/angular/test/e2e/flows/001-protected.e2e.ts +1 -1
  118. package/client-tmp/angular/tsconfig.app.json +2 -8
  119. package/client-tmp/angular/tsconfig.json +1 -4
  120. package/client-tmp/angular/tsconfig.spec.json +4 -11
  121. package/client-tmp/angular/webpack.dev.config.js +3 -4
  122. package/client-tmp/angular/webpack.prod.config.js +3 -4
  123. package/client-tmp/angular/webpack.shared.config.js +20 -24
  124. package/client-tmp/react/.oxfmtrc.json +30 -0
  125. package/client-tmp/react/.oxlintrc.json +4 -0
  126. package/client-tmp/react/README.md +4 -4
  127. package/client-tmp/react/jest.config.ts +8 -4
  128. package/client-tmp/react/jest.setup.ts +85 -1
  129. package/client-tmp/react/oxlint-staged.js +45 -0
  130. package/client-tmp/react/package.json +24 -9
  131. package/client-tmp/react/playwright.config.ts +11 -10
  132. package/client-tmp/react/src/App.tsx +27 -29
  133. package/client-tmp/react/src/__mocks__/fileMock.js +1 -0
  134. package/client-tmp/react/src/components/ErrorMessage/ErrorMessage.test.tsx +16 -4
  135. package/client-tmp/react/src/components/ErrorMessage/ErrorMessage.tsx +32 -29
  136. package/client-tmp/react/src/components/error-boundary/ErrorBoundary.tsx +15 -6
  137. package/client-tmp/react/src/components/routes/AppRoutes.tsx +1 -1
  138. package/client-tmp/react/src/components/single-component/registry.ts +4 -0
  139. package/client-tmp/react/src/custom-elements.d.ts +4 -2
  140. package/client-tmp/react/src/helpers/hasPermissionHelper.tsx +1 -1
  141. package/client-tmp/react/src/helpers/isAuthenticatedHelper.tsx +1 -1
  142. package/client-tmp/react/src/helpers/isConnectedHelper.tsx +3 -3
  143. package/client-tmp/react/src/layouts/blank/BlankLayout.tsx +7 -11
  144. package/client-tmp/react/src/layouts/default/DefaultLayout.tsx +8 -8
  145. package/client-tmp/react/src/main.tsx +11 -11
  146. package/client-tmp/react/src/pages/AuthPage/AuthPage.test.tsx +13 -2
  147. package/client-tmp/react/src/pages/AuthPage/AuthPage.tsx +14 -13
  148. package/client-tmp/react/src/pages/NotFoundPage/NotFoundPage.tsx +2 -7
  149. package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.test.tsx +8 -4
  150. package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.tsx +1 -1
  151. package/client-tmp/react/src/pbc/container.tsx +9 -4
  152. package/client-tmp/react/src/pbc/elementsRenderer.tsx +18 -9
  153. package/client-tmp/react/src/pbc/utils.ts +60 -59
  154. package/client-tmp/react/src/services/auth.service.ts +1 -1
  155. package/client-tmp/react/src/services/store.service.ts +5 -3
  156. package/client-tmp/react/src/share/foundation-login.ts +2 -2
  157. package/client-tmp/react/src/share/genesis-components.ts +46 -47
  158. package/client-tmp/react/src/store/AuthContext.tsx +2 -10
  159. package/client-tmp/react/src/store/RoutesContext.test.tsx +99 -0
  160. package/client-tmp/react/src/store/RoutesContext.tsx +25 -5
  161. package/client-tmp/react/src/styles/styles.css +7 -7
  162. package/client-tmp/react/src/types/LayoutName.ts +1 -1
  163. package/client-tmp/react/src/utils/getLayoutNameByRoute.ts +1 -1
  164. package/client-tmp/react/src/utils/index.ts +2 -0
  165. package/client-tmp/react/src/utils/layout.ts +8 -1
  166. package/client-tmp/react/src/utils/permissions.ts +7 -7
  167. package/client-tmp/react/src/utils/useDocumentTitle.ts +5 -6
  168. package/client-tmp/react/test/e2e/fixture.ts +5 -2
  169. package/client-tmp/react/test/e2e/flows/001-protected.e2e.ts +3 -1
  170. package/client-tmp/react/tsconfig.app.json +1 -1
  171. package/client-tmp/react/tsconfig.json +1 -5
  172. package/client-tmp/web-components/.oxfmtrc.json +30 -0
  173. package/client-tmp/web-components/.oxlintrc.json +4 -0
  174. package/client-tmp/web-components/oxlint-staged.js +45 -0
  175. package/client-tmp/web-components/package.json +14 -22
  176. package/client-tmp/web-components/src/components/components.ts +9 -7
  177. package/client-tmp/web-components/src/main/main.css +1 -2
  178. package/client-tmp/web-components/src/main/main.ts +1 -1
  179. package/client-tmp/web-components/src/routes/config.ts +4 -0
  180. package/client-tmp/web-components/src/utils/index.ts +2 -0
  181. package/client-tmp/web-components/tsconfig.json +1 -4
  182. package/gradle/wrapper/gradle-wrapper.properties +1 -1
  183. package/package.json +1 -1
  184. package/server/gradle/wrapper/gradle-wrapper.properties +1 -1
  185. package/server/gradle.properties +0 -2
  186. package/.genx/templates/angular/gridLayout.hbs +0 -30
  187. package/.genx/templates/angular/horizontalLayout.hbs +0 -7
  188. package/.genx/templates/angular/tabsLayout.hbs +0 -7
  189. package/.genx/tests/fixtures/testapp/client/src/routes/home/home.template.ts +0 -6
  190. package/client-tmp/react/.babelrc +0 -10
  191. package/client-tmp/react/.eslintrc.cjs +0 -21
  192. package/client-tmp/react/git +0 -0
  193. package/client-tmp/react/webpack.config.js +0 -121
  194. package/client-tmp/web-components/.prettierignore +0 -14
@@ -0,0 +1,299 @@
1
+ [
2
+ {
3
+ "name": "Dashboard",
4
+ "icon": "chart-bar",
5
+ "tiles": [
6
+ {
7
+ "title": "Positions",
8
+ "type": "entity-manager",
9
+ "config": {
10
+ "resourceName": "ALL_POSITIONS",
11
+ "title": "Positions",
12
+ "entityName": "Position",
13
+ "createEvent": "EVENT_POSITION_INSERT",
14
+ "updateEvent": "EVENT_POSITION_MODIFY",
15
+ "deleteEvent": "EVENT_POSITION_DELETE",
16
+ "enableSearchBar": true,
17
+ "enableFilters": true,
18
+ "sizeColumnsToFit": true,
19
+ "modalPosition": "right",
20
+ "permissions": {
21
+ "viewRight": "ViewPosition",
22
+ "updateRight": "TradePosition"
23
+ },
24
+ "eventing": {
25
+ "publishEventName": "selectedPosition"
26
+ },
27
+ "columns": [
28
+ {
29
+ "field": "INSTRUMENT_ID",
30
+ "headerName": "Instrument",
31
+ "type": "string"
32
+ },
33
+ {
34
+ "field": "VALUE",
35
+ "headerName": "Value",
36
+ "type": "number"
37
+ },
38
+ {
39
+ "field": "LAST_UPDATED",
40
+ "headerName": "Updated",
41
+ "type": "date"
42
+ }
43
+ ],
44
+ "createFormUiSchema": {
45
+ "type": "VerticalLayout",
46
+ "elements": [
47
+ {
48
+ "type": "Control",
49
+ "scope": "#/properties/INSTRUMENT_ID"
50
+ }
51
+ ]
52
+ },
53
+ "updateFormUiSchema": {
54
+ "type": "VerticalLayout",
55
+ "elements": [
56
+ {
57
+ "type": "Control",
58
+ "scope": "#/properties/INSTRUMENT_ID"
59
+ }
60
+ ]
61
+ },
62
+ "filterFormUiSchema": {
63
+ "type": "VerticalLayout",
64
+ "elements": [
65
+ {
66
+ "type": "Control",
67
+ "scope": "#/properties/INSTRUMENT_ID"
68
+ }
69
+ ]
70
+ },
71
+ "customEvents": [
72
+ {
73
+ "name": "EVENT_POSITION_CLOSE",
74
+ "baseEvent": "POSITION_CLOSE",
75
+ "icon": "times",
76
+ "tooltip": "Close Position",
77
+ "position": "action",
78
+ "confirmSubmit": {
79
+ "state": "enabled",
80
+ "message": "Are you sure?"
81
+ },
82
+ "hasForm": true,
83
+ "uischema": {
84
+ "type": "VerticalLayout",
85
+ "elements": [
86
+ {
87
+ "type": "Control",
88
+ "scope": "#/properties/REASON"
89
+ }
90
+ ]
91
+ }
92
+ }
93
+ ],
94
+ "gridOptions": {
95
+ "onRowClicked": {
96
+ "category": "fdc3",
97
+ "channelName": "positions",
98
+ "type": "position"
99
+ }
100
+ }
101
+ }
102
+ },
103
+ {
104
+ "title": "Trades",
105
+ "type": "grid-pro",
106
+ "config": {
107
+ "resourceName": "ALL_TRADES",
108
+ "permissions": {
109
+ "viewRight": "ViewTrade"
110
+ },
111
+ "eventing": {
112
+ "listener": {
113
+ "eventName": "selectedPosition",
114
+ "mappings": [
115
+ {
116
+ "sourceField": "INSTRUMENT_ID",
117
+ "targetField": "INSTRUMENT_ID"
118
+ }
119
+ ]
120
+ }
121
+ },
122
+ "gridOptions": {
123
+ "columns": [
124
+ {
125
+ "field": "TRADE_ID",
126
+ "headerName": "Trade ID"
127
+ },
128
+ {
129
+ "field": "QUANTITY",
130
+ "headerName": "Qty",
131
+ "type": "number"
132
+ }
133
+ ],
134
+ "rowSelection": "single"
135
+ }
136
+ }
137
+ },
138
+ {
139
+ "title": "P&L Chart",
140
+ "type": "chart",
141
+ "config": {
142
+ "resourceName": "ALL_POSITIONS",
143
+ "type": "line",
144
+ "xField": "INSTRUMENT_ID",
145
+ "yField": "VALUE",
146
+ "permissions": {
147
+ "viewRight": "ViewPosition"
148
+ }
149
+ }
150
+ },
151
+ {
152
+ "title": "Allocation",
153
+ "type": "chart",
154
+ "config": {
155
+ "resourceName": "ALL_POSITIONS",
156
+ "type": "pie",
157
+ "xField": "INSTRUMENT_ID",
158
+ "yField": "VALUE"
159
+ }
160
+ },
161
+ {
162
+ "title": "New Trade",
163
+ "type": "smart-form",
164
+ "config": {
165
+ "resourceName": "ALL_TRADES",
166
+ "createEvent": "EVENT_TRADE_INSERT",
167
+ "permissions": {
168
+ "updateRight": "TradePosition"
169
+ },
170
+ "uischema": {
171
+ "type": "VerticalLayout",
172
+ "elements": [
173
+ {
174
+ "type": "Control",
175
+ "scope": "#/properties/INSTRUMENT_ID"
176
+ },
177
+ {
178
+ "type": "Control",
179
+ "scope": "#/properties/QUANTITY"
180
+ }
181
+ ]
182
+ }
183
+ }
184
+ }
185
+ ]
186
+ },
187
+ {
188
+ "name": "Analytics",
189
+ "icon": "chart-pie",
190
+ "tiles": [
191
+ {
192
+ "title": "Historical Positions",
193
+ "type": "entity-manager",
194
+ "config": {
195
+ "resourceName": "HISTORICAL_POSITIONS",
196
+ "title": "Historical Positions",
197
+ "reqrep": true,
198
+ "snapshot": true,
199
+ "columns": [
200
+ {
201
+ "field": "INSTRUMENT_ID",
202
+ "headerName": "Instrument"
203
+ },
204
+ {
205
+ "field": "VALUE",
206
+ "headerName": "Value",
207
+ "type": "number"
208
+ }
209
+ ]
210
+ }
211
+ },
212
+ {
213
+ "title": "Risk Grid",
214
+ "type": "grid-pro",
215
+ "config": {
216
+ "resourceName": "ALL_RISK",
217
+ "reqrep": true,
218
+ "gridOptions": {
219
+ "columns": [
220
+ {
221
+ "field": "RISK_VALUE",
222
+ "headerName": "Risk"
223
+ }
224
+ ]
225
+ }
226
+ }
227
+ },
228
+ {
229
+ "title": "Instrument Chart",
230
+ "type": "chart",
231
+ "config": {
232
+ "resourceName": "ALL_INSTRUMENTS",
233
+ "type": "bar",
234
+ "xField": "INSTRUMENT_ID",
235
+ "yField": "QUANTITY"
236
+ }
237
+ },
238
+ {
239
+ "title": "Instrument Form",
240
+ "type": "smart-form",
241
+ "config": {
242
+ "resourceName": "ALL_INSTRUMENTS",
243
+ "createEvent": "EVENT_INSTRUMENT_INSERT",
244
+ "uischema": {
245
+ "type": "VerticalLayout",
246
+ "elements": [
247
+ {
248
+ "type": "Control",
249
+ "scope": "#/properties/INSTRUMENT_ID"
250
+ },
251
+ {
252
+ "type": "Control",
253
+ "scope": "#/properties/QUANTITY"
254
+ }
255
+ ]
256
+ }
257
+ }
258
+ }
259
+ ]
260
+ },
261
+ {
262
+ "name": "Settings",
263
+ "icon": "cog",
264
+ "tiles": [
265
+ {
266
+ "title": "Users",
267
+ "type": "entity-manager",
268
+ "config": {
269
+ "resourceName": "ALL_USERS",
270
+ "title": "Users",
271
+ "createEvent": "EVENT_USER_INSERT",
272
+ "updateEvent": "EVENT_USER_MODIFY",
273
+ "deleteEvent": "EVENT_USER_DELETE"
274
+ }
275
+ },
276
+ {
277
+ "title": "User Form",
278
+ "type": "smart-form",
279
+ "config": {
280
+ "resourceName": "ALL_USERS",
281
+ "createEvent": "EVENT_USER_INSERT",
282
+ "uischema": {
283
+ "type": "VerticalLayout",
284
+ "elements": [
285
+ {
286
+ "type": "Control",
287
+ "scope": "#/properties/USER_NAME"
288
+ },
289
+ {
290
+ "type": "Control",
291
+ "scope": "#/properties/EMAIL"
292
+ }
293
+ ]
294
+ }
295
+ }
296
+ }
297
+ ]
298
+ }
299
+ ]
@@ -0,0 +1,12 @@
1
+ import { EntityManagement } from '@genesislcap/foundation-entity-management/react';
2
+ import './Home.css';
3
+
4
+ const Home = () => {
5
+ return (
6
+ <section className="home-page">
7
+ <EntityManagement prefix="rapid" resourceName="ALL_TRADES" createEvent="EVENT_TRADE_INSERT" />
8
+ </section>
9
+ );
10
+ };
11
+
12
+ export default Home;
@@ -1,6 +1,42 @@
1
- const formatJSONValue = (value) => {
1
+ const toJsStringLiteral = require('./toJsStringLiteral');
2
+
3
+ const IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
4
+
5
+ const formatTSValue = (value, indent = 0) => {
6
+ if (value === null) return 'null';
7
+ if (typeof value === 'boolean') return String(value);
8
+ if (typeof value === 'number') return String(value);
9
+ if (typeof value === 'string') return toJsStringLiteral(value);
10
+
11
+ const pad = ' '.repeat(indent + 2);
12
+ const closePad = ' '.repeat(indent);
13
+
14
+ if (Array.isArray(value)) {
15
+ if (value.length === 0) return '[]';
16
+ const items = value.map((v) => `${pad}${formatTSValue(v, indent + 2)}`).join(',\n');
17
+ return `[\n${items},\n${closePad}]`;
18
+ }
19
+
20
+ if (typeof value === 'object') {
21
+ const entries = Object.entries(value);
22
+ if (entries.length === 0) return '{}';
23
+ const items = entries
24
+ .map(([k, v]) => {
25
+ const key = IDENTIFIER_RE.test(k) ? k : toJsStringLiteral(k);
26
+ return `${pad}${key}: ${formatTSValue(v, indent + 2)}`;
27
+ })
28
+ .join(',\n');
29
+ return `{\n${items},\n${closePad}}`;
30
+ }
31
+
32
+ return String(value);
33
+ };
34
+
35
+ const formatJSONValue = (value, indent = 0) => {
2
36
  try {
3
- return value ? JSON.stringify(value, null, 2) : undefined;
37
+ // Only `undefined` means "nothing to emit". A truthiness test would swallow
38
+ // legitimate falsy config such as `suppressMenuHide: false` or `rowHeight: 0`.
39
+ return value === undefined ? undefined : formatTSValue(value, indent);
4
40
  } catch (e) {
5
41
  console.warn('Could not serialise value to JSON', value, e);
6
42
  }
@@ -3,17 +3,36 @@ const {
3
3
  gridColumnsSerializer,
4
4
  } = require('./gridSerializers');
5
5
  const formatJSONValue = require('./formatJSONValue');
6
+ const toJsStringLiteral = require('./toJsStringLiteral');
6
7
  const { getFormattedComment, getFormattedTodo } = require('./getTodosAndComments')
7
8
  const getLayoutType = require('./getLayoutType');
8
9
  const { COMPONENT_TYPE, FRAMEWORK_ANGULAR_ALIAS } = require('../static');
9
10
 
11
+ // The factory the grid serialisers emit for an FDC3 click marker. It lives in the
12
+ // generated src/utils/fdc3.ts, which is only written when FDC3EventHandlersEnabled
13
+ // is true — so detection has to key off the very same marker the serialisers act on.
14
+ const FDC3_EVENT_FACTORY = 'sendEventOnChannel';
15
+
16
+ const DEFAULT_CONFIRM_MESSAGE = 'Are you sure you want to proceed?';
17
+
10
18
  const formatCustomEvents = (customEvents) => {
11
19
  if (!customEvents) return;
12
20
 
13
21
  return customEvents.map(event => ({
14
22
  ...event,
23
+ // Always a real boolean: the templates emit `hasForm: {{this.hasForm}},`
24
+ // and an omitted flag would otherwise render `hasForm: ,`.
25
+ hasForm: !!event.hasForm,
15
26
  uischema: event.hasForm ? formatJSONValue(event.uischema) : undefined,
16
27
  defaultValues: event.defaultValues ? formatJSONValue(event.defaultValues) : undefined,
28
+ // Ready-made JS string literals (quotes included) for the sites that emit
29
+ // JS rather than markup. Handlebars can only HTML-escape or not escape at
30
+ // all, and neither is correct for a JS literal: `{{ }}` turns `'` into
31
+ // `&#x27;` and `{{{ }}}` leaves the apostrophe to terminate the string.
32
+ tooltipJs: event.tooltip ? toJsStringLiteral(event.tooltip) : undefined,
33
+ confirmSubmitMessageJs: event.confirmSubmit
34
+ ? toJsStringLiteral(event.confirmSubmit.message || DEFAULT_CONFIRM_MESSAGE)
35
+ : undefined,
17
36
  }));
18
37
  };
19
38
 
@@ -24,10 +43,7 @@ const formatRouteData = (framework, route) => {
24
43
  ? 'tabs-panel'
25
44
  : route?.layoutType || getLayoutType(route);
26
45
 
27
- const FDC3ClickCategory = 'fdc3';
28
- const FDC3EventHandlersEnabled = !!route.tiles?.find(
29
- (t) => t.config?.gridOptions?.onRowClicked?.category === FDC3ClickCategory,
30
- );
46
+ let FDC3EventHandlersEnabled = false;
31
47
  const tiles = route.tiles?.map((tile, index) => {
32
48
  const config = tile.config || {};
33
49
  const metadata = tile.metadata || {};
@@ -47,31 +63,64 @@ const formatRouteData = (framework, route) => {
47
63
  eventing,
48
64
  } = config;
49
65
 
50
- const hasFormatter = (name, colsOrOpts) => {
51
- const cols = Array.isArray(colsOrOpts) ? colsOrOpts : colsOrOpts?.columns;
52
- return cols?.some((col) => col.valueFormatter?.name === name);
53
- };
66
+ const isSmartForm = tile.type === 'smart-form';
67
+
68
+ // A smart-form IS a create form, so `createFormUiSchema` and `uischema` name the
69
+ // same thing there. Normalise to `uischema` — the key every framework's form
70
+ // partial actually consumes — so the schema is wired into the form instead of
71
+ // being emitted as an unread constant (and so both keys together cannot emit
72
+ // two `createFormSchema` exports).
73
+ const effectiveUischema = isSmartForm ? uischema || createFormUiSchema : uischema;
74
+ const effectiveCreateFormUiSchema = isSmartForm ? undefined : createFormUiSchema;
75
+
76
+ // Permission code the whole tile is gated on, or null when it always renders.
77
+ // A smart-form has no read-only mode, so it is gated on updateRight; every
78
+ // other tile type renders for view-only users and uses updateRight solely to
79
+ // disable its CRUD affordances.
80
+ const { viewRight, updateRight } = config.permissions || {};
81
+ const wholeTileRight = viewRight || (isSmartForm ? updateRight : undefined) || null;
82
+ // `getViewUpdateRightComponent(getUser(), code)` is needed by the whole-tile
83
+ // gate and by the entity-manager CRUD props; emitting it anywhere else would
84
+ // leave unused imports behind.
85
+ const usesPermissionHelper =
86
+ !!wholeTileRight ||
87
+ !!(
88
+ tile.type === 'entity-manager' &&
89
+ updateRight &&
90
+ (config.createEvent || config.updateEvent || config.deleteEvent)
91
+ );
92
+
93
+ // An FDC3 marker can sit on any grid handler (onRowClicked, onCellClicked, ...)
94
+ // in either gridOptions or columns, so detect it from the serialised output —
95
+ // exactly what the templates' `ifContains ... 'sendEventOnChannel'` guards see.
96
+ const serialisedGridOptions = gridOptionsSerializer(gridOptions);
97
+ const serialisedColumns = gridColumnsSerializer(columns);
98
+ if (
99
+ serialisedGridOptions?.includes(FDC3_EVENT_FACTORY) ||
100
+ serialisedColumns?.includes(FDC3_EVENT_FACTORY)
101
+ ) {
102
+ FDC3EventHandlersEnabled = true;
103
+ }
54
104
 
55
105
  return {
56
106
  ...tile,
57
107
  componentName,
58
108
  componentType,
109
+ // JS string literal (quotes included) for the flexlayout tab names, which
110
+ // are emitted into a TS object rather than into markup.
111
+ titleJs: toJsStringLiteral(tile.title),
59
112
  config: {
60
113
  ...config,
61
114
  index,
62
- gridOptions: gridOptionsSerializer(gridOptions),
115
+ wholeTileRight,
116
+ usesPermissionHelper,
117
+ gridOptions: serialisedGridOptions,
63
118
  useOnlyTemplateCols: !!gridOptions?.columns,
64
- createFormUiSchema: formatJSONValue(createFormUiSchema),
119
+ createFormUiSchema: formatJSONValue(effectiveCreateFormUiSchema),
65
120
  updateFormUiSchema: formatJSONValue(updateFormUiSchema),
66
121
  filterFormUiSchema: formatJSONValue(filterFormUiSchema),
67
- uischema: formatJSONValue(uischema),
68
- columns: gridColumnsSerializer(columns),
69
- hasNumberFormatter:
70
- hasFormatter('getNumberFormatter', columns) ||
71
- hasFormatter('getNumberFormatter', gridOptions),
72
- hasDateFormatter:
73
- hasFormatter('getDateFormatter', columns) ||
74
- hasFormatter('getDateFormatter', gridOptions),
122
+ uischema: formatJSONValue(effectiveUischema),
123
+ columns: serialisedColumns,
75
124
  customEvents: formatCustomEvents(customEvents),
76
125
  eventing: {
77
126
  publishEventName: eventing?.publishEventName || null,
@@ -176,7 +176,16 @@ const getFilesToWrite = (
176
176
  }
177
177
  break;
178
178
  case 'smart-form':
179
- filesToWrite.push(componentAddFormFile);
179
+ // Only write the schema module when there is a schema to export; a
180
+ // schema-less smart-form must not emit a file with unused imports. Both
181
+ // keys count: the component templates import createFormSchema for either
182
+ // one, so a file written for `uischema` alone would leave the
183
+ // `createFormUiSchema` variant importing a module that was never written.
184
+ // (formatRouteData normalises a smart-form's createFormUiSchema into
185
+ // uischema, so this is the belt to that braces.)
186
+ if (tileData.config?.uischema || tileData.config?.createFormUiSchema) {
187
+ filesToWrite.push(componentAddFormFile);
188
+ }
180
189
  break;
181
190
  default:
182
191
  break;
@@ -1,46 +1,86 @@
1
1
  const formatJSONValue = require('./formatJSONValue');
2
+ const toJsStringLiteral = require('./toJsStringLiteral');
2
3
 
3
- function gridColumnsSerializer(columns, pad) {
4
- if (!columns) {
5
- return undefined;
4
+ const FDC3_CLICK_CATEGORY = 'fdc3';
5
+
6
+ // Emit string literals in oxfmt's own shape (singleQuote: true, with the
7
+ // quote/escape choice prettier would make); JSON.stringify would always emit
8
+ // double quotes and fail `oxfmt --check` out of the box.
9
+ const serialiseFunctionArg = (arg) =>
10
+ typeof arg === 'string' ? toJsStringLiteral(arg) : JSON.stringify(arg);
11
+
12
+ /**
13
+ * Serialises one `key: value` pair of a grid options object or of a column
14
+ * definition. Shared by both serializers so the special value shapes
15
+ * (function/valueFormatter factories, FDC3 click markers) are handled
16
+ * identically wherever they are authored.
17
+ *
18
+ * @param {string} key property name
19
+ * @param {unknown} value property value
20
+ * @param {number} indent indentation of the emitted line
21
+ * @returns {string} the emitted line(s), trailing comma included
22
+ */
23
+ function serialiseEntry(key, value, indent) {
24
+ const pad = ' '.repeat(indent);
25
+
26
+ if (value?.type === 'function' || value?.type === 'valueFormatter') {
27
+ const args = value.arguments?.map(serialiseFunctionArg).join(', ');
28
+ return `${pad}${key}: ${value.name}(${args}),`;
29
+ }
30
+
31
+ if (value?.category === FDC3_CLICK_CATEGORY) {
32
+ // FDC3 click markers are authored as { category: 'fdc3', channelName, type } and are
33
+ // detected by formatRouteData (FDC3EventHandlersEnabled). AG Grid handlers such as
34
+ // onRowClicked must be functions, so emit the sendEventOnChannel factory call that the
35
+ // generated src/utils/fdc3.ts provides instead of leaking the raw marker object.
36
+ const args = [value.channelName, value.type].map(serialiseFunctionArg).join(', ');
37
+ // prettier-ignore pin: channel/type are user data, so the emitted call can exceed the
38
+ // print width; the pin keeps the single-line form stable either way.
39
+ return `${pad}// prettier-ignore\n${pad}${key}: sendEventOnChannel(${args}),`;
6
40
  }
7
41
 
42
+ return `${pad}${key}: ${formatJSONValue(value, indent)},`;
43
+ }
44
+
45
+ function gridColumnsSerializer(columns, indent = 0) {
46
+ if (!columns) return undefined;
47
+
8
48
  try {
9
- const columnsSerialized = columns.map((column) => {
10
- return gridOptionsSerializer(column, pad);
49
+ // An expanded empty array would serialise as `[\n,\n]`; emit the literal instead.
50
+ if (!columns.length) return '[]';
51
+ const pad = ' '.repeat(indent);
52
+ const itemPad = ' '.repeat(indent + 2);
53
+ const items = columns.map((column) => {
54
+ const fields = Object.entries(column)
55
+ .map(([k, v]) => serialiseEntry(k, v, indent + 4))
56
+ .join('\n');
57
+ return `${itemPad}{\n${fields}\n${itemPad}}`;
11
58
  });
12
- return `[${columnsSerialized}\n]`;
59
+ return `[\n${items.join(',\n')},\n${pad}]`;
13
60
  } catch (e) {
14
61
  console.error('Error serializing grid columns:', e.message);
15
62
  throw e;
16
63
  }
17
64
  }
18
65
 
19
- function gridOptionsSerializer(options, pad = ' ') {
20
- if (!options) {
21
- return undefined;
22
- }
66
+ function gridOptionsSerializer(options, indent = 0) {
67
+ if (!options) return undefined;
23
68
 
24
69
  try {
25
- let output = `\n${pad}{\n`;
70
+ const pad = ' '.repeat(indent);
71
+ const itemPad = ' '.repeat(indent + 2);
72
+ let fields = '';
26
73
  Object.keys(options).forEach((key) => {
27
74
  const value = options[key];
28
75
  if (key === 'columns') {
29
- output += `${pad}${pad}${'columnDefs'}: ${gridColumnsSerializer(value, ' ')},\n`;
30
- } else if (
31
- value?.type === 'function' ||
32
- value?.type === 'valueFormatter'
33
- ) {
34
- const args = value.arguments?.map(JSON.stringify).join(', ');
35
- output += `${pad}${pad}${key}: ${value.name}(${args}),\n`;
36
- } else if (key === 'hide') {
37
- output += `${pad}${pad}${key}: ${value},\n`;
76
+ fields += `${itemPad}columnDefs: ${gridColumnsSerializer(value, indent + 2)},\n`;
38
77
  } else {
39
- output += `${pad}${pad}${key}: ${formatJSONValue(value)},\n`;
78
+ fields += `${serialiseEntry(key, value, indent + 2)}\n`;
40
79
  }
41
80
  });
42
- output += `${pad}}`;
43
- return output;
81
+ // An expanded empty object is what oxfmt collapses to `{}`; match formatTSValue.
82
+ if (!fields) return '{}';
83
+ return `{\n${fields}${pad}}`;
44
84
  } catch (e) {
45
85
  console.error('Error serializing grid options:', e.message);
46
86
  throw e;
@@ -8,24 +8,27 @@ const registerPartials = ({ registerPartial }, framework) => {
8
8
  // It can be reverted after adding changes for angular
9
9
  const sourceTemplateDir = `../${DIR_TEMPLATE_BY_FRAMEWORK[framework]}`;
10
10
 
11
+ // Angular always renders through the 'tabs-panel' layout (see formatRouteData),
12
+ // so the grid/tabs/horizontal layout partials only exist for the other frameworks.
11
13
  if (framework === FRAMEWORK_ANGULAR_ALIAS) {
12
14
  registerPartial(
13
15
  'tabs-panel',
14
16
  resolve(__dirname, `${sourceTemplateDir}/tabsPanel.hbs`),
15
17
  );
18
+ } else {
19
+ registerPartial(
20
+ 'grid-layout',
21
+ resolve(__dirname, `${sourceTemplateDir}/gridLayout.hbs`),
22
+ );
23
+ registerPartial(
24
+ 'tabs-layout',
25
+ resolve(__dirname, `${sourceTemplateDir}/tabsLayout.hbs`),
26
+ );
27
+ registerPartial(
28
+ 'horizontal-layout',
29
+ resolve(__dirname, `${sourceTemplateDir}/horizontalLayout.hbs`),
30
+ );
16
31
  }
17
- registerPartial(
18
- 'grid-layout',
19
- resolve(__dirname, `${sourceTemplateDir}/gridLayout.hbs`),
20
- );
21
- registerPartial(
22
- 'tabs-layout',
23
- resolve(__dirname, `${sourceTemplateDir}/tabsLayout.hbs`),
24
- );
25
- registerPartial(
26
- 'horizontal-layout',
27
- resolve(__dirname, `${sourceTemplateDir}/horizontalLayout.hbs`),
28
- );
29
32
  registerPartial(
30
33
  'smart-form',
31
34
  resolve(__dirname, `${sourceTemplateDir}/form.hbs`),