@cyprnet/node-red-contrib-uibuilder-formgen 0.4.11

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 (52) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +22 -0
  3. package/README.md +58 -0
  4. package/docs/user-guide.html +565 -0
  5. package/examples/formgen-builder/src/index.html +921 -0
  6. package/examples/formgen-builder/src/index.js +1338 -0
  7. package/examples/portalsmith-formgen-example.json +531 -0
  8. package/examples/schema-builder-integration.json +109 -0
  9. package/examples/schemas/Banking/banking_fraud_report.json +102 -0
  10. package/examples/schemas/Banking/banking_kyc_update.json +59 -0
  11. package/examples/schemas/Banking/banking_loan_application.json +113 -0
  12. package/examples/schemas/Banking/banking_new_account.json +98 -0
  13. package/examples/schemas/Banking/banking_wire_transfer_request.json +94 -0
  14. package/examples/schemas/HR/hr_employee_change_form.json +65 -0
  15. package/examples/schemas/HR/hr_exit_interview.json +105 -0
  16. package/examples/schemas/HR/hr_job_application.json +166 -0
  17. package/examples/schemas/HR/hr_onboarding_request.json +140 -0
  18. package/examples/schemas/HR/hr_time_off_request.json +95 -0
  19. package/examples/schemas/HR/hr_training_request.json +70 -0
  20. package/examples/schemas/Healthcare/health_appointment_request.json +103 -0
  21. package/examples/schemas/Healthcare/health_incident_report.json +82 -0
  22. package/examples/schemas/Healthcare/health_lab_order_request.json +72 -0
  23. package/examples/schemas/Healthcare/health_medication_refill.json +72 -0
  24. package/examples/schemas/Healthcare/health_patient_intake.json +113 -0
  25. package/examples/schemas/IT/it_access_request.json +145 -0
  26. package/examples/schemas/IT/it_dhcp_reservation.json +175 -0
  27. package/examples/schemas/IT/it_dns_domain_external.json +192 -0
  28. package/examples/schemas/IT/it_dns_domain_internal.json +171 -0
  29. package/examples/schemas/IT/it_network_change_request.json +126 -0
  30. package/examples/schemas/IT/it_network_request-form.json +299 -0
  31. package/examples/schemas/IT/it_new_hardware_request.json +155 -0
  32. package/examples/schemas/IT/it_password_reset.json +133 -0
  33. package/examples/schemas/IT/it_software_license_request.json +93 -0
  34. package/examples/schemas/IT/it_static_ip_request.json +199 -0
  35. package/examples/schemas/IT/it_subnet_request_form.json +216 -0
  36. package/examples/schemas/Maintenance/maint_checklist.json +176 -0
  37. package/examples/schemas/Maintenance/maint_facility_issue_report.json +127 -0
  38. package/examples/schemas/Maintenance/maint_incident_intake.json +174 -0
  39. package/examples/schemas/Maintenance/maint_inventory_restock.json +79 -0
  40. package/examples/schemas/Maintenance/maint_safety_audit.json +92 -0
  41. package/examples/schemas/Maintenance/maint_vehicle_inspection.json +112 -0
  42. package/examples/schemas/Maintenance/maint_work_order.json +134 -0
  43. package/index.js +12 -0
  44. package/lib/licensing.js +254 -0
  45. package/nodes/portalsmith-license.html +40 -0
  46. package/nodes/portalsmith-license.js +23 -0
  47. package/nodes/uibuilder-formgen.html +261 -0
  48. package/nodes/uibuilder-formgen.js +598 -0
  49. package/package.json +47 -0
  50. package/scripts/normalize_schema_titles.py +77 -0
  51. package/templates/index.html.mustache +541 -0
  52. package/templates/index.js.mustache +1135 -0
@@ -0,0 +1,531 @@
1
+ [
2
+ {
3
+ "id": "formgen-example-flow",
4
+ "type": "tab",
5
+ "label": "PortalSmith FormGen Example",
6
+ "disabled": false,
7
+ "info": "Example flow demonstrating PortalSmith form generation and message handling.\n\nSETUP:\n1. Inject node provides schema and triggers generation\n2. uibuilder-formgen node generates the portal\n3. IMPORTANT: You must add a uibuilder node manually:\n - Drag a uibuilder node onto the flow\n - Configure: name='form-jobapp', url='form-jobapp', sourceFolder='src'\n - Connect uibuilder OUTPUT to 'message-router' INPUT\n - Connect response nodes OUTPUT to uibuilder INPUT:\n * 'send-draft-response' output → uibuilder input\n * 'submit-success-response' output → uibuilder input\n * 'export-success-response' output → uibuilder input\n\nMESSAGE FLOW:\n- Frontend → uibuilder node → message-router → handlers → file nodes\n- Response handlers → uibuilder node → Frontend (via websocket)\n\nACTIONS:\n- Save Draft: saves to localStorage + sends draft:save to Node-RED\n- Load Draft: loads from localStorage or requests from Node-RED\n- Submit: validates form, sends submit message, waits for submit:ok\n- Export: sends export message, receives export:file with download link\n- CopyBlock: renders template with form data, copies to clipboard",
8
+ "env": []
9
+ },
10
+ {
11
+ "id": "inject-schema",
12
+ "type": "inject",
13
+ "z": "formgen-example-flow",
14
+ "name": "Generate Job App Form",
15
+ "props": [
16
+ {
17
+ "p": "payload"
18
+ },
19
+ {
20
+ "p": "schema",
21
+ "v": "",
22
+ "vt": "json"
23
+ },
24
+ {
25
+ "p": "uibuilder",
26
+ "v": "form-jobapp",
27
+ "vt": "str"
28
+ },
29
+ {
30
+ "p": "options",
31
+ "v": "{\"overwrite\":true,\"storageMode\":\"file\",\"exportFormats\":[\"json\",\"csv\",\"html\"]}",
32
+ "vt": "json"
33
+ }
34
+ ],
35
+ "repeat": "",
36
+ "crontab": "",
37
+ "once": false,
38
+ "onceDelay": 0.1,
39
+ "topic": "",
40
+ "payload": "",
41
+ "payloadType": "date",
42
+ "x": 150,
43
+ "y": 100,
44
+ "wires": [
45
+ [
46
+ "load-schema-file"
47
+ ]
48
+ ]
49
+ },
50
+ {
51
+ "id": "load-schema-file",
52
+ "type": "file in",
53
+ "z": "formgen-example-flow",
54
+ "name": "Load Schema",
55
+ "filename": "/home/david/dev/portalsmith/node-red-contrib-uibuilder-formgen/examples/schemas/job_application.json",
56
+ "format": "utf8",
57
+ "chunk": false,
58
+ "sendError": false,
59
+ "encoding": "none",
60
+ "allProps": false,
61
+ "x": 350,
62
+ "y": 100,
63
+ "wires": [
64
+ [
65
+ "parse-schema"
66
+ ]
67
+ ]
68
+ },
69
+ {
70
+ "id": "parse-schema",
71
+ "type": "function",
72
+ "z": "formgen-example-flow",
73
+ "name": "Parse Schema",
74
+ "func": "// Parse the schema file and set msg.schema\nmsg.schema = JSON.parse(msg.payload);\nreturn msg;",
75
+ "outputs": 1,
76
+ "noerr": 0,
77
+ "initialize": "",
78
+ "finalize": "",
79
+ "libs": [],
80
+ "x": 550,
81
+ "y": 100,
82
+ "wires": [
83
+ [
84
+ "uibuilder-formgen-node"
85
+ ]
86
+ ]
87
+ },
88
+ {
89
+ "id": "uibuilder-formgen-node",
90
+ "type": "uibuilder-formgen",
91
+ "z": "formgen-example-flow",
92
+ "name": "Generate Portal",
93
+ "uibuilderUrl": "/uibuilder",
94
+ "instanceName": "formgen",
95
+ "overwrite": true,
96
+ "storageMode": "file",
97
+ "exportFormats": "[\"json\",\"csv\",\"html\"]",
98
+ "x": 750,
99
+ "y": 100,
100
+ "wires": [
101
+ [
102
+ "debug-output"
103
+ ]
104
+ ]
105
+ },
106
+ {
107
+ "id": "debug-output",
108
+ "type": "debug",
109
+ "z": "formgen-example-flow",
110
+ "name": "Generation Output",
111
+ "active": true,
112
+ "tosidebar": true,
113
+ "console": false,
114
+ "tostatus": false,
115
+ "complete": "payload",
116
+ "targetType": "full",
117
+ "statusVal": "",
118
+ "statusType": "auto",
119
+ "x": 950,
120
+ "y": 100,
121
+ "wires": []
122
+ },
123
+ {
124
+ "id": "message-router",
125
+ "type": "switch",
126
+ "z": "formgen-example-flow",
127
+ "name": "Route by Type",
128
+ "property": "payload.type",
129
+ "propertyType": "msg",
130
+ "rules": [
131
+ {
132
+ "t": "eq",
133
+ "v": "draft:save",
134
+ "vt": "str"
135
+ },
136
+ {
137
+ "t": "eq",
138
+ "v": "draft:get",
139
+ "vt": "str"
140
+ },
141
+ {
142
+ "t": "eq",
143
+ "v": "submit",
144
+ "vt": "str"
145
+ },
146
+ {
147
+ "t": "eq",
148
+ "v": "export",
149
+ "vt": "str"
150
+ }
151
+ ],
152
+ "checkall": "true",
153
+ "repair": false,
154
+ "outputs": 4,
155
+ "x": 150,
156
+ "y": 200,
157
+ "wires": [
158
+ [
159
+ "save-draft"
160
+ ],
161
+ [
162
+ "get-draft"
163
+ ],
164
+ [
165
+ "handle-submit"
166
+ ],
167
+ [
168
+ "handle-export"
169
+ ]
170
+ ]
171
+ },
172
+ {
173
+ "id": "save-draft",
174
+ "type": "function",
175
+ "z": "formgen-example-flow",
176
+ "name": "Prepare Draft Save",
177
+ "func": "// Prepare draft file path\n// NOTE: Update userDir path to match your Node-RED userDir\n// You can find it in Node-RED settings or use: RED.settings.userDir\nconst formId = msg.payload.formId || 'unknown';\nconst userDir = process.env.NODE_RED_HOME || process.env.HOME + '/.node-red';\nconst draftsDir = userDir + '/portalsmith/drafts';\n\nmsg.filename = draftsDir + '/' + formId + '.json';\nmsg.payload = msg.payload.payload || msg.payload.data || {};\n\nreturn msg;",
178
+ "outputs": 1,
179
+ "noerr": 0,
180
+ "initialize": "",
181
+ "finalize": "",
182
+ "libs": [],
183
+ "x": 550,
184
+ "y": 160,
185
+ "wires": [
186
+ [
187
+ "ensure-drafts-dir",
188
+ "file-write-draft"
189
+ ]
190
+ ]
191
+ },
192
+ {
193
+ "id": "ensure-drafts-dir",
194
+ "type": "function",
195
+ "z": "formgen-example-flow",
196
+ "name": "Ensure Dir",
197
+ "func": "const fs = require('fs-extra');\nconst path = require('path');\nconst dir = path.dirname(msg.filename);\nfs.ensureDirSync(dir);\nreturn msg;",
198
+ "outputs": 1,
199
+ "noerr": 0,
200
+ "initialize": "",
201
+ "finalize": "",
202
+ "libs": [],
203
+ "x": 750,
204
+ "y": 160,
205
+ "wires": [
206
+ [
207
+ "file-write-draft"
208
+ ]
209
+ ]
210
+ },
211
+ {
212
+ "id": "file-write-draft",
213
+ "type": "file",
214
+ "z": "formgen-example-flow",
215
+ "name": "Write Draft",
216
+ "filename": "",
217
+ "appendNewline": true,
218
+ "createDir": true,
219
+ "overwriteFile": "true",
220
+ "encoding": "utf8",
221
+ "x": 950,
222
+ "y": 160,
223
+ "wires": [
224
+ [
225
+ "draft-saved-response"
226
+ ]
227
+ ]
228
+ },
229
+ {
230
+ "id": "draft-saved-response",
231
+ "type": "function",
232
+ "z": "formgen-example-flow",
233
+ "name": "Draft Saved",
234
+ "func": "// Draft saved successfully\nnode.warn('Draft saved: ' + msg.filename);\nreturn null;",
235
+ "outputs": 1,
236
+ "noerr": 0,
237
+ "initialize": "",
238
+ "finalize": "",
239
+ "libs": [],
240
+ "x": 1150,
241
+ "y": 160,
242
+ "wires": [
243
+ []
244
+ ]
245
+ },
246
+ {
247
+ "id": "get-draft",
248
+ "type": "function",
249
+ "z": "formgen-example-flow",
250
+ "name": "Prepare Draft Get",
251
+ "func": "// Prepare draft file path\n// NOTE: Update userDir path to match your Node-RED userDir\nconst formId = msg.payload.formId || 'unknown';\nconst userDir = process.env.NODE_RED_HOME || process.env.HOME + '/.node-red';\nconst draftsDir = userDir + '/portalsmith/drafts';\n\nmsg.filename = draftsDir + '/' + formId + '.json';\n\nreturn msg;",
252
+ "outputs": 1,
253
+ "noerr": 0,
254
+ "initialize": "",
255
+ "finalize": "",
256
+ "libs": [],
257
+ "x": 550,
258
+ "y": 200,
259
+ "wires": [
260
+ [
261
+ "file-read-draft"
262
+ ]
263
+ ]
264
+ },
265
+ {
266
+ "id": "file-read-draft",
267
+ "type": "file in",
268
+ "z": "formgen-example-flow",
269
+ "name": "Read Draft",
270
+ "filename": "",
271
+ "format": "utf8",
272
+ "chunk": false,
273
+ "sendError": false,
274
+ "encoding": "none",
275
+ "allProps": false,
276
+ "x": 750,
277
+ "y": 200,
278
+ "wires": [
279
+ [
280
+ "send-draft-response"
281
+ ]
282
+ ]
283
+ },
284
+ {
285
+ "id": "send-draft-response",
286
+ "type": "function",
287
+ "z": "formgen-example-flow",
288
+ "name": "Send Draft Response",
289
+ "func": "// Parse draft and send back to uibuilder\n// Get formId from the original request (stored in flow context or from filename)\nconst filename = msg.filename || '';\nconst formIdMatch = filename.match(/([^/]+)\\.json$/);\nconst formId = formIdMatch ? formIdMatch[1] : 'unknown';\n\ntry {\n const draftData = JSON.parse(msg.payload);\n \n // Send back to uibuilder instance\n return {\n payload: {\n type: 'draft',\n formId: formId,\n data: draftData.data || draftData\n }\n };\n} catch (e) {\n node.error('Failed to parse draft: ' + e.message);\n return null;\n}",
290
+ "outputs": 1,
291
+ "noerr": 0,
292
+ "initialize": "",
293
+ "finalize": "",
294
+ "libs": [],
295
+ "x": 950,
296
+ "y": 200,
297
+ "wires": [
298
+ []
299
+ ]
300
+ },
301
+ {
302
+ "id": "handle-submit",
303
+ "type": "function",
304
+ "z": "formgen-example-flow",
305
+ "name": "Prepare Submit",
306
+ "func": "// Prepare submission file path\n// NOTE: Update userDir path to match your Node-RED userDir\nconst formId = msg.payload.formId || 'unknown';\nconst userDir = process.env.NODE_RED_HOME || process.env.HOME + '/.node-red';\nconst submissionsDir = userDir + '/portalsmith/submissions';\n\n// Create NDJSON entry\nconst submission = {\n formId: formId,\n timestamp: msg.payload.meta?.timestamp || new Date().toISOString(),\n userAgent: msg.payload.meta?.userAgent || 'unknown',\n data: msg.payload.payload || {}\n};\n\nmsg.filename = submissionsDir + '/' + formId + '.ndjson';\nmsg.payload = JSON.stringify(submission) + '\\n';\n\nreturn msg;",
307
+ "outputs": 1,
308
+ "noerr": 0,
309
+ "initialize": "",
310
+ "finalize": "",
311
+ "libs": [],
312
+ "x": 550,
313
+ "y": 240,
314
+ "wires": [
315
+ [
316
+ "ensure-submissions-dir",
317
+ "file-append-submission"
318
+ ]
319
+ ]
320
+ },
321
+ {
322
+ "id": "ensure-submissions-dir",
323
+ "type": "function",
324
+ "z": "formgen-example-flow",
325
+ "name": "Ensure Dir",
326
+ "func": "const fs = require('fs-extra');\nconst path = require('path');\nconst dir = path.dirname(msg.filename);\nfs.ensureDirSync(dir);\nreturn msg;",
327
+ "outputs": 1,
328
+ "noerr": 0,
329
+ "initialize": "",
330
+ "finalize": "",
331
+ "libs": [],
332
+ "x": 750,
333
+ "y": 240,
334
+ "wires": [
335
+ [
336
+ "file-append-submission"
337
+ ]
338
+ ]
339
+ },
340
+ {
341
+ "id": "file-append-submission",
342
+ "type": "file",
343
+ "z": "formgen-example-flow",
344
+ "name": "Append Submission",
345
+ "filename": "",
346
+ "appendNewline": false,
347
+ "createDir": true,
348
+ "overwriteFile": "false",
349
+ "encoding": "utf8",
350
+ "x": 950,
351
+ "y": 240,
352
+ "wires": [
353
+ [
354
+ "submit-success-response"
355
+ ]
356
+ ]
357
+ },
358
+ {
359
+ "id": "submit-success-response",
360
+ "type": "function",
361
+ "z": "formgen-example-flow",
362
+ "name": "Submit Success",
363
+ "func": "// Submission saved successfully\nnode.warn('Submission saved: ' + msg.filename);\n\n// Extract formId from filename\nconst filename = msg.filename || '';\nconst formIdMatch = filename.match(/([^/]+)\\.ndjson$/);\nconst formId = formIdMatch ? formIdMatch[1] : 'unknown';\n\n// Send confirmation back to uibuilder\nreturn {\n payload: {\n type: 'submit:ok',\n formId: formId,\n message: 'Submission received successfully'\n }\n};",
364
+ "outputs": 1,
365
+ "noerr": 0,
366
+ "initialize": "",
367
+ "finalize": "",
368
+ "libs": [],
369
+ "x": 1150,
370
+ "y": 240,
371
+ "wires": [
372
+ []
373
+ ]
374
+ },
375
+ {
376
+ "id": "handle-export",
377
+ "type": "switch",
378
+ "z": "formgen-example-flow",
379
+ "name": "Route Export Format",
380
+ "property": "payload.format",
381
+ "propertyType": "msg",
382
+ "rules": [
383
+ {
384
+ "t": "eq",
385
+ "v": "json",
386
+ "vt": "str"
387
+ },
388
+ {
389
+ "t": "eq",
390
+ "v": "csv",
391
+ "vt": "str"
392
+ },
393
+ {
394
+ "t": "eq",
395
+ "v": "html",
396
+ "vt": "str"
397
+ }
398
+ ],
399
+ "checkall": "true",
400
+ "repair": false,
401
+ "outputs": 3,
402
+ "x": 550,
403
+ "y": 280,
404
+ "wires": [
405
+ [
406
+ "export-json"
407
+ ],
408
+ [
409
+ "export-csv"
410
+ ],
411
+ [
412
+ "export-html"
413
+ ]
414
+ ]
415
+ },
416
+ {
417
+ "id": "export-json",
418
+ "type": "function",
419
+ "z": "formgen-example-flow",
420
+ "name": "Export JSON",
421
+ "func": "// NOTE: Update userDir path to match your Node-RED userDir\nconst formId = msg.payload.formId || 'unknown';\nconst format = 'json';\nconst userDir = process.env.NODE_RED_HOME || process.env.HOME + '/.node-red';\nconst exportsDir = userDir + '/portalsmith/exports';\nconst timestamp = new Date().toISOString().replace(/[:.]/g, '-');\nconst filename = `${formId}_${timestamp}.json`;\n\nmsg.filename = exportsDir + '/' + filename;\nmsg.payload = JSON.stringify(msg.payload.payload || {}, null, 2);\n\nreturn msg;",
422
+ "outputs": 1,
423
+ "noerr": 0,
424
+ "initialize": "",
425
+ "finalize": "",
426
+ "libs": [],
427
+ "x": 750,
428
+ "y": 260,
429
+ "wires": [
430
+ [
431
+ "ensure-exports-dir",
432
+ "file-write-export"
433
+ ]
434
+ ]
435
+ },
436
+ {
437
+ "id": "export-csv",
438
+ "type": "function",
439
+ "z": "formgen-example-flow",
440
+ "name": "Export CSV",
441
+ "func": "// NOTE: Update userDir path to match your Node-RED userDir\nconst formId = msg.payload.formId || 'unknown';\nconst format = 'csv';\nconst userDir = process.env.NODE_RED_HOME || process.env.HOME + '/.node-red';\nconst exportsDir = userDir + '/portalsmith/exports';\nconst timestamp = new Date().toISOString().replace(/[:.]/g, '-');\nconst filename = `${formId}_${timestamp}.csv`;\n\nconst data = msg.payload.payload || {};\nconst keys = Object.keys(data);\nconst values = keys.map(k => {\n const v = data[k];\n if (typeof v === 'object') return JSON.stringify(v);\n return String(v).replace(/\"/g, '\"\"');\n});\n\n// Simple CSV: header row + data row\nmsg.filename = exportsDir + '/' + filename;\nmsg.payload = '\"' + keys.join('\",\"') + '\\n\"' + values.join('\",\"') + '\"';\n\nreturn msg;",
442
+ "outputs": 1,
443
+ "noerr": 0,
444
+ "initialize": "",
445
+ "finalize": "",
446
+ "libs": [],
447
+ "x": 750,
448
+ "y": 280,
449
+ "wires": [
450
+ [
451
+ "ensure-exports-dir",
452
+ "file-write-export"
453
+ ]
454
+ ]
455
+ },
456
+ {
457
+ "id": "export-html",
458
+ "type": "function",
459
+ "z": "formgen-example-flow",
460
+ "name": "Export HTML",
461
+ "func": "// NOTE: Update userDir path to match your Node-RED userDir\nconst formId = msg.payload.formId || 'unknown';\nconst format = 'html';\nconst userDir = process.env.NODE_RED_HOME || process.env.HOME + '/.node-red';\nconst exportsDir = userDir + '/portalsmith/exports';\nconst timestamp = new Date().toISOString().replace(/[:.]/g, '-');\nconst filename = `${formId}_${timestamp}.html`;\n\nconst data = msg.payload.payload || {};\n\n// Simple printable HTML\nlet html = '<!DOCTYPE html>\\n<html>\\n<head>\\n<meta charset=\"utf-8\">\\n<title>Form Export</title>\\n<style>body{font-family:Arial,sans-serif;padding:20px;}table{border-collapse:collapse;width:100%;}th,td{border:1px solid #ddd;padding:8px;text-align:left;}th{background-color:#f2f2f2;}</style>\\n</head>\\n<body>\\n<h1>Form Export</h1>\\n<table>\\n';\n\nfor (const [key, value] of Object.entries(data)) {\n html += `<tr><th>${escapeHtml(String(key))}</th><td>${escapeHtml(String(value))}</td></tr>\\n`;\n}\n\nhtml += '</table>\\n</body>\\n</html>';\n\nfunction escapeHtml(s) {\n return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;').replace(/'/g, '&#039;');\n}\n\nmsg.filename = exportsDir + '/' + filename;\nmsg.payload = html;\n\nreturn msg;",
462
+ "outputs": 1,
463
+ "noerr": 0,
464
+ "initialize": "",
465
+ "finalize": "",
466
+ "libs": [],
467
+ "x": 750,
468
+ "y": 300,
469
+ "wires": [
470
+ [
471
+ "ensure-exports-dir",
472
+ "file-write-export"
473
+ ]
474
+ ]
475
+ },
476
+ {
477
+ "id": "ensure-exports-dir",
478
+ "type": "function",
479
+ "z": "formgen-example-flow",
480
+ "name": "Ensure Dir",
481
+ "func": "const fs = require('fs-extra');\nconst path = require('path');\nconst dir = path.dirname(msg.filename);\nfs.ensureDirSync(dir);\nreturn msg;",
482
+ "outputs": 1,
483
+ "noerr": 0,
484
+ "initialize": "",
485
+ "finalize": "",
486
+ "libs": [],
487
+ "x": 950,
488
+ "y": 280,
489
+ "wires": [
490
+ [
491
+ "file-write-export"
492
+ ]
493
+ ]
494
+ },
495
+ {
496
+ "id": "file-write-export",
497
+ "type": "file",
498
+ "z": "formgen-example-flow",
499
+ "name": "Write Export",
500
+ "filename": "",
501
+ "appendNewline": true,
502
+ "createDir": true,
503
+ "overwriteFile": "true",
504
+ "encoding": "utf8",
505
+ "x": 1150,
506
+ "y": 280,
507
+ "wires": [
508
+ [
509
+ "export-success-response"
510
+ ]
511
+ ]
512
+ },
513
+ {
514
+ "id": "export-success-response",
515
+ "type": "function",
516
+ "z": "formgen-example-flow",
517
+ "name": "Export Success",
518
+ "func": "// Export saved successfully\nconst filename = msg.filename.split('/').pop();\nconst format = filename.split('.').pop();\n\nnode.warn('Export saved: ' + msg.filename);\n\n// Send file info back to uibuilder\nreturn {\n payload: {\n type: 'export:file',\n format: format,\n filename: filename,\n path: msg.filename\n }\n};",
519
+ "outputs": 1,
520
+ "noerr": 0,
521
+ "initialize": "",
522
+ "finalize": "",
523
+ "libs": [],
524
+ "x": 1350,
525
+ "y": 280,
526
+ "wires": [
527
+ []
528
+ ]
529
+ }
530
+ ]
531
+
@@ -0,0 +1,109 @@
1
+ [
2
+ {
3
+ "id": "schema-builder-flow",
4
+ "type": "tab",
5
+ "label": "Schema Builder Integration",
6
+ "disabled": false,
7
+ "info": "Example flow showing how to connect the schema builder to the formgen node.\n\nSETUP:\n1. Add a uibuilder node for 'formgen-builder' instance\n2. Add a uibuilder node for your target form instance (e.g., 'form-jobapp')\n3. Connect builder uibuilder OUTPUT to 'builder-router' INPUT\n4. Connect 'generate-form' OUTPUT to target uibuilder INPUT (for formgen node)\n5. Connect formgen node OUTPUT to builder uibuilder INPUT (for success response)\n\nFLOW:\n- Builder sends {type: 'generate', schema: {...}} to Node-RED\n- Router forwards to formgen node\n- Formgen generates form and sends response back\n- Response sent back to builder for confirmation",
8
+ "env": []
9
+ },
10
+ {
11
+ "id": "builder-router",
12
+ "type": "switch",
13
+ "z": "schema-builder-flow",
14
+ "name": "Route Builder Messages",
15
+ "property": "payload.type",
16
+ "propertyType": "msg",
17
+ "rules": [
18
+ {
19
+ "t": "eq",
20
+ "v": "generate",
21
+ "vt": "str"
22
+ }
23
+ ],
24
+ "checkall": "true",
25
+ "repair": false,
26
+ "outputs": 1,
27
+ "x": 350,
28
+ "y": 100,
29
+ "wires": [
30
+ [
31
+ "prepare-formgen"
32
+ ]
33
+ ]
34
+ },
35
+ {
36
+ "id": "prepare-formgen",
37
+ "type": "function",
38
+ "z": "schema-builder-flow",
39
+ "name": "Prepare for FormGen",
40
+ "func": "// Extract schema from builder message\n// Builder sends: {type: 'generate', schema: {...}}\n// FormGen expects: msg.schema or msg.payload\n\nmsg.schema = msg.payload.schema || msg.payload;\nmsg.uibuilder = msg.uibuilder || msg.targetInstance || 'form-jobapp'; // Target instance\nmsg.options = {\n overwrite: true,\n storageMode: 'file',\n exportFormats: ['json', 'csv', 'html']\n};\n\nreturn msg;",
41
+ "outputs": 1,
42
+ "noerr": 0,
43
+ "initialize": "",
44
+ "finalize": "",
45
+ "libs": [],
46
+ "x": 550,
47
+ "y": 100,
48
+ "wires": [
49
+ [
50
+ "uibuilder-formgen-node"
51
+ ]
52
+ ]
53
+ },
54
+ {
55
+ "id": "uibuilder-formgen-node",
56
+ "type": "uibuilder-formgen",
57
+ "z": "schema-builder-flow",
58
+ "name": "Generate Form",
59
+ "uibuilderUrl": "/uibuilder",
60
+ "instanceName": "form-jobapp",
61
+ "overwrite": true,
62
+ "storageMode": "file",
63
+ "exportFormats": "[\"json\",\"csv\",\"html\"]",
64
+ "x": 750,
65
+ "y": 100,
66
+ "wires": [
67
+ [
68
+ "format-response"
69
+ ]
70
+ ]
71
+ },
72
+ {
73
+ "id": "format-response",
74
+ "type": "function",
75
+ "z": "schema-builder-flow",
76
+ "name": "Format Response",
77
+ "func": "// Format response for builder\n// Send back to builder uibuilder instance\n\nreturn {\n payload: {\n type: 'generated',\n status: msg.payload.status,\n instance: msg.payload.instance,\n url: msg.payload.url,\n path: msg.payload.path,\n files: msg.payload.files,\n schemaSummary: msg.payload.schemaSummary\n }\n};",
78
+ "outputs": 1,
79
+ "noerr": 0,
80
+ "initialize": "",
81
+ "finalize": "",
82
+ "libs": [],
83
+ "x": 950,
84
+ "y": 100,
85
+ "wires": [
86
+ [
87
+ "debug-output"
88
+ ]
89
+ ]
90
+ },
91
+ {
92
+ "id": "debug-output",
93
+ "type": "debug",
94
+ "z": "schema-builder-flow",
95
+ "name": "Generation Result",
96
+ "active": true,
97
+ "tosidebar": true,
98
+ "console": false,
99
+ "tostatus": false,
100
+ "complete": "payload",
101
+ "targetType": "full",
102
+ "statusVal": "",
103
+ "statusType": "auto",
104
+ "x": 1150,
105
+ "y": 100,
106
+ "wires": []
107
+ }
108
+ ]
109
+