@cyprnet/node-red-contrib-uibuilder-formgen 0.4.12 → 0.5.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.
package/README.md CHANGED
@@ -35,9 +35,19 @@ Then restart Node-RED.
35
35
  ### Quick start
36
36
 
37
37
  1. Create a `uibuilder` instance (example URL: `form-jobapp`) and deploy.
38
- 2. Send a schema into `uibuilder-formgen` as `msg.schema` (or use the example flow).
38
+ 2. Send a schema into `uibuilder-formgen` (legacy Vue 2) or `uibuilder-formgen-v3` (Vue 3) as `msg.schema` (or use the example flow).
39
39
  3. Open `/uibuilder/<instance>/` in your browser.
40
40
 
41
+ ### Node differences (legacy vs v3)
42
+
43
+ - **`uibuilder-formgen` (legacy)**: generates portals using **Vue 2 + Bootstrap 4**
44
+ - **`uibuilder-formgen-v3`**: generates portals using **Vue 3 + Bootstrap 5**
45
+
46
+ The two portals are intentionally very similar in appearance; the difference is the runtime stack and long-term supportability.
47
+
48
+ To confirm which node generated a portal, open the generated `portalsmith.runtime.json` and check:
49
+ - `generatorNode`: `uibuilder-formgen` or `uibuilder-formgen-v3`
50
+
41
51
  ### Licensing (offline-first)
42
52
 
43
53
  - **Free (default)**: watermarked portal + default PortalSmith branding.
@@ -434,8 +434,14 @@
434
434
  </section>
435
435
 
436
436
  <section id="generate">
437
- <h2>Generating a portal (uibuilder-formgen)</h2>
438
- <p>You generate portals by sending a schema into the <code>uibuilder-formgen</code> node.</p>
437
+ <h2>Generating a portal (uibuilder-formgen / uibuilder-formgen-v3)</h2>
438
+ <p>You generate portals by sending a schema into either the <code>uibuilder-formgen</code> node (legacy Vue 2) or the <code>uibuilder-formgen-v3</code> node (Vue 3).</p>
439
+ <div class="note ok">
440
+ <strong>Which one should I use?</strong> Use <code>uibuilder-formgen-v3</code> for new portals (Vue 3 + Bootstrap 5). Use <code>uibuilder-formgen</code> only for legacy compatibility.
441
+ </div>
442
+ <div class="note">
443
+ <strong>How to confirm which runtime generated a portal:</strong> open the generated <code>portalsmith.runtime.json</code> file and check <code>generatorNode</code>.
444
+ </div>
439
445
 
440
446
  <h3>Minimum input</h3>
441
447
  <pre>{
package/index.js CHANGED
@@ -8,5 +8,6 @@
8
8
  module.exports = function(RED) {
9
9
  require("./nodes/portalsmith-license")(RED);
10
10
  require("./nodes/uibuilder-formgen")(RED);
11
+ require("./nodes/uibuilder-formgen-v3")(RED);
11
12
  };
12
13
 
@@ -0,0 +1,237 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('uibuilder-formgen-v3', {
3
+ category: 'PortalSmith',
4
+ color: '#E2D96E',
5
+ defaults: {
6
+ name: { value: "" },
7
+ uibuilderUrl: { value: "/uibuilder" },
8
+ instanceName: { value: "formgen" },
9
+ projectName: { value: "" },
10
+ uibRootDir: { value: "" },
11
+ instanceRootDir: { value: "" },
12
+ themeMode: { value: "auto" },
13
+ logoPath: { value: "" },
14
+ logoAlt: { value: "Logo" },
15
+ licenseConfig: { value: "", type: "portalsmith-license", required: false },
16
+ submitMode: { value: "uibuilder" },
17
+ submitUrl: { value: "" },
18
+ submitHeadersJson: { value: "" },
19
+ apiUrl: { value: "" },
20
+ apiMethod: { value: "POST" },
21
+ apiHeadersJson: { value: "" },
22
+ apiInsecureTls: { value: false },
23
+ apiTimeoutMs: { value: 15000 },
24
+ overwrite: { value: true },
25
+ storageMode: { value: "file", validate: function(val) { return val === "file" || val === "localstorage"; } },
26
+ exportFormats: { value: '["json","csv","html"]' }
27
+ },
28
+ credentials: {
29
+ licenseKey: { type: "password" }
30
+ },
31
+ inputs: 1,
32
+ outputs: 1,
33
+ icon: "font-awesome/fa-magic",
34
+ label: function() {
35
+ return this.name || "uibuilder-formgen-v3";
36
+ },
37
+ oneditprepare: function() {
38
+ var statusEl = $("#ps-license-status");
39
+ var timer = null;
40
+ function setStatus(text, cls) {
41
+ statusEl.removeClass("text-success text-warning text-danger text-muted");
42
+ if (cls) statusEl.addClass(cls);
43
+ statusEl.text(text);
44
+ }
45
+ function validate() {
46
+ var licenseKey = $("#node-input-licenseKey").val() || "";
47
+ var licenseConfigId = $("#node-input-licenseConfig").val() || "";
48
+ setStatus("Checking...", "text-muted");
49
+ $.ajax({
50
+ url: "portalsmith/license/validate",
51
+ type: "POST",
52
+ contentType: "application/json",
53
+ data: JSON.stringify({ licenseKey: licenseKey, licenseConfigId: licenseConfigId })
54
+ })
55
+ .done(function(resp) {
56
+ if (!resp || resp.ok !== true) { setStatus("Unknown", "text-muted"); return; }
57
+ var keyInfo = "";
58
+ if (resp.verifier && resp.verifier.source) {
59
+ keyInfo = " (key: " + resp.verifier.source + (resp.verifier.fingerprint ? " " + resp.verifier.fingerprint : "") + ")";
60
+ }
61
+ if (resp.licensed) setStatus((resp.displayName || "Licensed") + keyInfo, "text-success");
62
+ else if (resp.reason && resp.reason !== "missing") setStatus("Invalid/Expired (" + resp.reason + ")" + keyInfo, "text-warning");
63
+ else setStatus("Free (Watermarked)" + keyInfo, "text-muted");
64
+ })
65
+ .fail(function() {
66
+ setStatus("Status unavailable (deploy/restart may be needed)", "text-muted");
67
+ });
68
+ }
69
+ function scheduleValidate() {
70
+ if (timer) clearTimeout(timer);
71
+ timer = setTimeout(validate, 250);
72
+ }
73
+ $("#node-input-licenseKey").on("keyup change", scheduleValidate);
74
+ $("#node-input-licenseConfig").on("change", scheduleValidate);
75
+ scheduleValidate();
76
+ }
77
+ });
78
+ </script>
79
+
80
+ <script type="text/html" data-template-name="uibuilder-formgen-v3">
81
+ <div class="form-row">
82
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
83
+ <input type="text" id="node-input-name" placeholder="PortalSmith FormGen (Vue 3)" />
84
+ </div>
85
+
86
+ <div class="form-row">
87
+ <label for="node-input-uibuilderUrl"><i class="fa fa-link"></i> uibuilder base</label>
88
+ <input type="text" id="node-input-uibuilderUrl" placeholder="/uibuilder" />
89
+ </div>
90
+
91
+ <div class="form-row">
92
+ <label for="node-input-instanceName"><i class="fa fa-folder"></i> Instance</label>
93
+ <input type="text" id="node-input-instanceName" placeholder="form-jobapp" />
94
+ </div>
95
+
96
+ <div class="form-row">
97
+ <label for="node-input-projectName"><i class="fa fa-briefcase"></i> Project (optional)</label>
98
+ <input type="text" id="node-input-projectName" placeholder="my-project" />
99
+ </div>
100
+
101
+ <div class="form-row">
102
+ <label for="node-input-uibRootDir"><i class="fa fa-folder-open"></i> uibuilder root dir (optional)</label>
103
+ <input type="text" id="node-input-uibRootDir" placeholder="/opt/node-red-home/projects/my-project/uibuilder" />
104
+ </div>
105
+
106
+ <div class="form-row">
107
+ <label for="node-input-instanceRootDir"><i class="fa fa-map-marker"></i> Instance root dir (optional)</label>
108
+ <input type="text" id="node-input-instanceRootDir" placeholder="/opt/node-red-home/projects/my-project/uibuilder/form-jobapp" />
109
+ </div>
110
+
111
+ <div class="form-row">
112
+ <label for="node-input-themeMode"><i class="fa fa-adjust"></i> Theme</label>
113
+ <select id="node-input-themeMode">
114
+ <option value="auto">Auto (prefers-color-scheme)</option>
115
+ <option value="light">Light</option>
116
+ <option value="dark">Dark</option>
117
+ </select>
118
+ </div>
119
+
120
+ <div class="form-row">
121
+ <label for="node-input-logoPath"><i class="fa fa-image"></i> Logo path (optional)</label>
122
+ <input type="text" id="node-input-logoPath" placeholder="Either /abs/path/to/logo.png OR just logo.png (assumes src/images/)" />
123
+ </div>
124
+
125
+ <div class="form-row">
126
+ <label for="node-input-logoAlt"><i class="fa fa-font"></i> Logo alt text</label>
127
+ <input type="text" id="node-input-logoAlt" placeholder="Logo" />
128
+ </div>
129
+
130
+ <hr/>
131
+ <div class="form-row">
132
+ <strong>Licensing (offline-first)</strong>
133
+ </div>
134
+
135
+ <div class="form-row">
136
+ <label for="node-input-licenseConfig"><i class="fa fa-id-card"></i> License (shared config)</label>
137
+ <input type="text" id="node-input-licenseConfig" />
138
+ </div>
139
+
140
+ <div class="form-row">
141
+ <label for="node-input-licenseKey"><i class="fa fa-key"></i> License Key (optional)</label>
142
+ <input type="password" id="node-input-licenseKey" placeholder="(provided by CyprNet Solutions)" />
143
+ </div>
144
+
145
+ <div class="form-tips">
146
+ Leave license fields blank to run in <strong>Free (Watermarked)</strong> mode.
147
+ </div>
148
+
149
+ <div class="form-row">
150
+ <label>License Status</label>
151
+ <span id="ps-license-status" class="text-muted">Free (Watermarked)</span>
152
+ </div>
153
+
154
+ <div class="form-row">
155
+ <label for="node-input-overwrite"><i class="fa fa-refresh"></i> Overwrite</label>
156
+ <input type="checkbox" id="node-input-overwrite" />
157
+ </div>
158
+
159
+ <div class="form-row">
160
+ <label for="node-input-storageMode"><i class="fa fa-hdd-o"></i> Storage</label>
161
+ <select id="node-input-storageMode">
162
+ <option value="file">File (recommended)</option>
163
+ <option value="localstorage">Browser localStorage</option>
164
+ </select>
165
+ </div>
166
+
167
+ <div class="form-row">
168
+ <label for="node-input-exportFormats"><i class="fa fa-download"></i> Export Formats (JSON array)</label>
169
+ <input type="text" id="node-input-exportFormats" placeholder='["json","csv","html"]' />
170
+ </div>
171
+
172
+ <div class="form-row">
173
+ <label for="node-input-submitMode"><i class="fa fa-paper-plane"></i> Submit mode</label>
174
+ <select id="node-input-submitMode">
175
+ <option value="uibuilder">uibuilder message (default)</option>
176
+ <option value="http">HTTP POST (API / Node-RED http in)</option>
177
+ </select>
178
+ </div>
179
+
180
+ <div class="form-row">
181
+ <label for="node-input-submitUrl"><i class="fa fa-globe"></i> Submit URL (http mode)</label>
182
+ <input type="text" id="node-input-submitUrl" placeholder="e.g. /api/forms/submit or https://api.example.com/submit" />
183
+ </div>
184
+
185
+ <div class="form-row">
186
+ <label for="node-input-submitHeadersJson"><i class="fa fa-code"></i> Submit headers (JSON, optional)</label>
187
+ <input type="text" id="node-input-submitHeadersJson" placeholder='e.g. {"Authorization":"Bearer ..."}' />
188
+ </div>
189
+
190
+ <hr/>
191
+ <div class="form-row">
192
+ <strong>Server-side API proxy (for uibuilder submit)</strong>
193
+ </div>
194
+
195
+ <div class="form-row">
196
+ <label for="node-input-apiUrl"><i class="fa fa-exchange"></i> API URL</label>
197
+ <input type="text" id="node-input-apiUrl" placeholder="https://api.example.com/submit (leave blank to just emit submit msg)" />
198
+ </div>
199
+
200
+ <div class="form-row">
201
+ <label for="node-input-apiMethod"><i class="fa fa-bolt"></i> API method</label>
202
+ <select id="node-input-apiMethod">
203
+ <option value="POST">POST</option>
204
+ <option value="PUT">PUT</option>
205
+ <option value="PATCH">PATCH</option>
206
+ </select>
207
+ </div>
208
+
209
+ <div class="form-row">
210
+ <label for="node-input-apiHeadersJson"><i class="fa fa-code"></i> API headers (JSON, optional)</label>
211
+ <input type="text" id="node-input-apiHeadersJson" placeholder='e.g. {"Authorization":"Bearer ..."}' />
212
+ </div>
213
+
214
+ <div class="form-row">
215
+ <label for="node-input-apiInsecureTls"><i class="fa fa-shield"></i> Allow insecure TLS (self-signed)</label>
216
+ <input type="checkbox" id="node-input-apiInsecureTls" />
217
+ </div>
218
+
219
+ <div class="form-row">
220
+ <label for="node-input-apiTimeoutMs"><i class="fa fa-clock-o"></i> API timeout (ms)</label>
221
+ <input type="number" id="node-input-apiTimeoutMs" min="1000" step="500" placeholder="15000" />
222
+ </div>
223
+
224
+ <div class="form-tips">
225
+ <strong>Runtime:</strong> Vue 3 + Bootstrap 5 (native HTML controls).<br/>
226
+ Input: <code>msg.schema</code> (required), optional <code>msg.uibuilder</code>, <code>msg.options</code><br/>
227
+ Output: generated file list + URL
228
+ </div>
229
+ </script>
230
+
231
+ <script type="text/markdown" data-help-name="uibuilder-formgen-v3">
232
+ Generates a uibuilder instance (`src/index.html`, `src/index.js`, `src/form.schema.json`) from `msg.schema`.
233
+
234
+ This node generates portals using **Vue 3 + Bootstrap 5** (native HTML controls).
235
+ </script>
236
+
237
+