@cyprnet/node-red-contrib-uibuilder-formgen 0.5.6 → 0.5.13
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/CHANGELOG.md +28 -0
- package/docs/user-guide.html +159 -0
- package/examples/formgen-builder/src/index.html +245 -0
- package/examples/formgen-builder/src/index.js +381 -21
- package/examples/schemas/IT/it_os_compatibility_lookup.json +146 -0
- package/nodes/uibuilder-formgen-v3.js +1 -1
- package/nodes/uibuilder-formgen.js +1 -1
- package/package.json +1 -1
- package/templates/index.js.mustache +177 -0
- package/templates/index.v3.js.mustache +189 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this package will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.5.12
|
|
6
|
+
|
|
7
|
+
- Schema Builder: added a **Convert multiline → arrays** helper in the Lookups editor to migrate older lookup lists (newline strings) into arrays-of-strings.
|
|
8
|
+
|
|
9
|
+
## 0.5.13
|
|
10
|
+
|
|
11
|
+
- Schema Builder: Field editor now **stays open on validation errors** so dynamic Lookup/Auto-fill settings (e.g. Node-RED source requiring Lookup ID) don’t appear to “revert” after clicking OK.
|
|
12
|
+
|
|
13
|
+
## 0.5.11
|
|
14
|
+
|
|
15
|
+
- Lookup / Auto-fill: lookup items can now store lists as **arrays** (e.g. <code>["a","b"]</code>) and the portal will format arrays for textarea/keyvalue targets. Builder Lookups editor now includes a **Simple list** mode (one item per line → JSON array).
|
|
16
|
+
|
|
17
|
+
## 0.5.10
|
|
18
|
+
|
|
19
|
+
- Schema Builder: added a **Manage Lookups** UI to view/add/edit/delete <code>schema.lookups</code> lists for Lookup / Auto-fill.
|
|
20
|
+
|
|
21
|
+
## 0.5.9
|
|
22
|
+
|
|
23
|
+
- Docs: expanded the User Guide with a detailed **Lookup / Auto-fill** section (static + dynamic sources, multi-set mappings, builder steps, Node-RED message contract, troubleshooting).
|
|
24
|
+
|
|
25
|
+
## 0.5.8
|
|
26
|
+
|
|
27
|
+
- Added IT example schema demonstrating **Lookup / Auto-fill (multi-field)** using an OS selection to populate compatible devices and software bundle.
|
|
28
|
+
|
|
29
|
+
## 0.5.7
|
|
30
|
+
|
|
31
|
+
- Added **Lookup / Auto-fill** rules (with multi-set mappings) to the generated portals and Schema Builder. Supports static lookup lists stored in the schema and dynamic lookup lists fetched from Node-RED via uibuilder messages.
|
|
32
|
+
|
|
5
33
|
## 0.5.6
|
|
6
34
|
|
|
7
35
|
- Schema Builder: fixed saved-schema **load/delete selection** (was swapped due to index vs sorted list mismatch).
|
package/docs/user-guide.html
CHANGED
|
@@ -334,6 +334,7 @@
|
|
|
334
334
|
<li>Create/edit sections and fields visually</li>
|
|
335
335
|
<li>Validate schema structure</li>
|
|
336
336
|
<li>Generate JSON preview</li>
|
|
337
|
+
<li>Manage <strong>Lookup lists</strong> (<code>schema.lookups</code>) for Lookup / Auto-fill</li>
|
|
337
338
|
<li>Send schema to Node-RED (“Generate Form”)</li>
|
|
338
339
|
<li><strong>New Schema</strong> button resets your current schema</li>
|
|
339
340
|
</ul>
|
|
@@ -362,6 +363,12 @@
|
|
|
362
363
|
<li>or Projects: <code><userDir>/projects/<projectName>/uibuilder/formgen-builder/src/</code></li>
|
|
363
364
|
</ul>
|
|
364
365
|
</div>
|
|
366
|
+
|
|
367
|
+
<div class="note">
|
|
368
|
+
<strong>Managing lookup lists:</strong> Use the <strong>Lookups</strong> / <strong>Manage Lookups</strong> button to edit <code>schema.lookups</code>.
|
|
369
|
+
If you have older lookup lists that used multiline strings (with embedded <code>\n</code>), the Lookups editor includes
|
|
370
|
+
a <strong>Convert multiline → arrays</strong> button to migrate them to arrays of strings.
|
|
371
|
+
</div>
|
|
365
372
|
</section>
|
|
366
373
|
|
|
367
374
|
<section id="schemas">
|
|
@@ -431,6 +438,158 @@
|
|
|
431
438
|
"keyvalueMode": "delimiter",
|
|
432
439
|
"keyvalueDelimiter": "="
|
|
433
440
|
}</pre>
|
|
441
|
+
|
|
442
|
+
<h3>Lookup / Auto-fill (optional)</h3>
|
|
443
|
+
<p>
|
|
444
|
+
Lookup / Auto-fill lets you create “smart fields” that automatically populate other fields when a user chooses or types a value.
|
|
445
|
+
This is especially useful for <strong>templates</strong>, <strong>profiles</strong>, and <strong>standard configurations</strong>:
|
|
446
|
+
the user picks a name or select option, and the form fills multiple related fields for them.
|
|
447
|
+
</p>
|
|
448
|
+
|
|
449
|
+
<h4>What it does</h4>
|
|
450
|
+
<ul>
|
|
451
|
+
<li><strong>Watches a source field</strong> (<code>fromField</code>) for changes.</li>
|
|
452
|
+
<li><strong>Looks up a matching record</strong> from either:
|
|
453
|
+
<ul>
|
|
454
|
+
<li><strong>Schema (static):</strong> a list stored inside the schema under <code>lookups</code></li>
|
|
455
|
+
<li><strong>Node-RED (dynamic):</strong> a list fetched at runtime via uibuilder messages</li>
|
|
456
|
+
</ul>
|
|
457
|
+
</li>
|
|
458
|
+
<li><strong>Applies one or more mappings</strong> (<code>mappings[]</code>) to set multiple target fields from the matched record.</li>
|
|
459
|
+
</ul>
|
|
460
|
+
|
|
461
|
+
<h4>Key rules (important)</h4>
|
|
462
|
+
<ul>
|
|
463
|
+
<li><strong>Exact match</strong>: matching is strict string equality after trim. “Windows 11” ≠ “windows 11”.</li>
|
|
464
|
+
<li><strong>Match key</strong> (<code>matchKey</code>) can be nested using dot notation (example: <code>meta.id</code>).</li>
|
|
465
|
+
<li><strong>Value key</strong> (<code>valueKey</code>) in mappings can also be nested (example: <code>details.software.bundle</code>).</li>
|
|
466
|
+
<li><strong>clearOnNoMatch</strong>: if true, mapped target fields are cleared when the source value is blank or no record matches.</li>
|
|
467
|
+
<li><strong>Cycle safety</strong>: avoid rules that write back into the same field they watch, or create circular chains (A fills B, B fills A).</li>
|
|
468
|
+
</ul>
|
|
469
|
+
|
|
470
|
+
<h4>Schema shape</h4>
|
|
471
|
+
<p>
|
|
472
|
+
Lookup lists are stored under <code>lookups</code> (any structure, but arrays of objects are recommended).
|
|
473
|
+
Auto-fill rules live on a field as <code>autoFill</code>.
|
|
474
|
+
</p>
|
|
475
|
+
|
|
476
|
+
<h4>Static list stored in the schema (recommended for portable templates)</h4>
|
|
477
|
+
<pre>{
|
|
478
|
+
"lookups": {
|
|
479
|
+
"deviceTemplates": [
|
|
480
|
+
{ "name": "Azure Device Template", "id": "tmpl_123", "desc": "..." }
|
|
481
|
+
]
|
|
482
|
+
},
|
|
483
|
+
"sections": [{
|
|
484
|
+
"fields": [
|
|
485
|
+
{ "id": "templateName", "type": "text", "label": "Template Name" },
|
|
486
|
+
{
|
|
487
|
+
"id": "templateId",
|
|
488
|
+
"type": "text",
|
|
489
|
+
"label": "Template ID",
|
|
490
|
+
"autoFill": {
|
|
491
|
+
"fromField": "templateName",
|
|
492
|
+
"source": { "type": "schema", "path": "lookups.deviceTemplates" },
|
|
493
|
+
"matchKey": "name",
|
|
494
|
+
"clearOnNoMatch": true,
|
|
495
|
+
"mappings": [
|
|
496
|
+
{ "targetField": "templateId", "valueKey": "id" },
|
|
497
|
+
{ "targetField": "templateDesc", "valueKey": "desc" }
|
|
498
|
+
]
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
]
|
|
502
|
+
}]
|
|
503
|
+
}</pre>
|
|
504
|
+
|
|
505
|
+
<h4>Dynamic list fetched from Node-RED (recommended for centralized “source of truth”)</h4>
|
|
506
|
+
<p>
|
|
507
|
+
Use this when the lookup list should come from Node-RED (file, database, API, etc.) and you want updates without regenerating the portal.
|
|
508
|
+
</p>
|
|
509
|
+
<pre>{
|
|
510
|
+
"id": "templateId",
|
|
511
|
+
"type": "text",
|
|
512
|
+
"label": "Template ID",
|
|
513
|
+
"autoFill": {
|
|
514
|
+
"fromField": "templateName",
|
|
515
|
+
"source": { "type": "uibuilder", "lookupId": "deviceTemplates" },
|
|
516
|
+
"matchKey": "name",
|
|
517
|
+
"mappings": [{ "targetField": "templateId", "valueKey": "id" }]
|
|
518
|
+
}
|
|
519
|
+
}</pre>
|
|
520
|
+
|
|
521
|
+
<h4>Node-RED message contract (dynamic source)</h4>
|
|
522
|
+
<p>When a portal needs a dynamic list, it sends a message to Node-RED via uibuilder:</p>
|
|
523
|
+
<pre>{
|
|
524
|
+
"payload": { "type": "lookup:get", "lookupId": "deviceTemplates" }
|
|
525
|
+
}</pre>
|
|
526
|
+
<p>Your flow should return the list back to the portal via uibuilder as:</p>
|
|
527
|
+
<pre>{
|
|
528
|
+
"payload": { "type": "lookup:data", "lookupId": "deviceTemplates", "items": [ ... ] }
|
|
529
|
+
}</pre>
|
|
530
|
+
<p>If something fails, you can return:</p>
|
|
531
|
+
<pre>{
|
|
532
|
+
"payload": { "type": "lookup:error", "lookupId": "deviceTemplates", "error": "reason here" }
|
|
533
|
+
}</pre>
|
|
534
|
+
|
|
535
|
+
<h4>Minimal dynamic lookup handler (Function node example)</h4>
|
|
536
|
+
<p>
|
|
537
|
+
This is a simple pattern you can use in your flow: connect uibuilder output → Function → uibuilder input.
|
|
538
|
+
It responds to <code>lookup:get</code> requests with a static list (replace with DB/API/file lookup as needed).
|
|
539
|
+
</p>
|
|
540
|
+
<pre>// Function node
|
|
541
|
+
const p = msg.payload || {};
|
|
542
|
+
if (p.type !== "lookup:get") return null;
|
|
543
|
+
|
|
544
|
+
// Switch on lookupId so you can serve multiple lists
|
|
545
|
+
if (p.lookupId === "deviceTemplates") {
|
|
546
|
+
msg.payload = {
|
|
547
|
+
type: "lookup:data",
|
|
548
|
+
lookupId: p.lookupId,
|
|
549
|
+
items: [
|
|
550
|
+
{ name: "Azure Device Template", id: "tmpl_123", desc: "Example" },
|
|
551
|
+
{ name: "Azure Block Template", id: "tmpl_456", desc: "Example" }
|
|
552
|
+
]
|
|
553
|
+
};
|
|
554
|
+
return msg;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
msg.payload = { type: "lookup:error", lookupId: p.lookupId, error: "Unknown lookupId" };
|
|
558
|
+
return msg;</pre>
|
|
559
|
+
|
|
560
|
+
<h4>Schema Builder: how to configure Lookup / Auto-fill</h4>
|
|
561
|
+
<ol>
|
|
562
|
+
<li>Edit (or add) the field you want to <strong>receive</strong> auto-filled values.</li>
|
|
563
|
+
<li>Open the <strong>Lookup / Auto-fill</strong> card and enable it.</li>
|
|
564
|
+
<li>Pick <strong>From field</strong> (the user input that triggers the lookup, e.g., <code>os</code>).</li>
|
|
565
|
+
<li>Choose <strong>Lookup source</strong>:
|
|
566
|
+
<ul>
|
|
567
|
+
<li><strong>Schema</strong>: enter a <strong>List name</strong> (stored under <code>schema.lookups.<name></code>) and optionally paste JSON to store it in the schema</li>
|
|
568
|
+
<li><strong>Node-RED</strong>: enter a <strong>Lookup ID</strong> (used in uibuilder messages)</li>
|
|
569
|
+
</ul>
|
|
570
|
+
</li>
|
|
571
|
+
<li>Set <strong>Match key</strong> (the property to compare against the source field’s value).</li>
|
|
572
|
+
<li>Add one or more <strong>Mappings</strong>:
|
|
573
|
+
<ul>
|
|
574
|
+
<li><strong>Target field</strong>: which field should be set</li>
|
|
575
|
+
<li><strong>Value key</strong>: what property to copy from the matched record</li>
|
|
576
|
+
</ul>
|
|
577
|
+
</li>
|
|
578
|
+
<li>Optionally enable <strong>Clear on no match</strong>.</li>
|
|
579
|
+
</ol>
|
|
580
|
+
|
|
581
|
+
<h4>Troubleshooting</h4>
|
|
582
|
+
<ul>
|
|
583
|
+
<li><strong>Nothing fills</strong>: confirm the source field value exactly matches the record’s <code>matchKey</code> value (case/spacing matters).</li>
|
|
584
|
+
<li><strong>Dynamic lookups don’t work</strong>: confirm your uibuilder flow is replying with <code>lookup:data</code> for the same <code>lookupId</code>.</li>
|
|
585
|
+
<li><strong>Multi-set mapping sets some fields but not others</strong>: verify each mapping’s <code>targetField</code> exists in the schema and <code>valueKey</code> path exists in the lookup items.</li>
|
|
586
|
+
<li><strong>Unexpected clearing</strong>: check <code>clearOnNoMatch</code> and whether the source field becomes blank during edits.</li>
|
|
587
|
+
</ul>
|
|
588
|
+
<div class="note ok">
|
|
589
|
+
<strong>Tip (arrays are easier):</strong> For long lists, store them as <strong>arrays</strong> in your lookup items (e.g. <code>"devices": ["Dell...","HP..."]</code>)
|
|
590
|
+
instead of a single string with embedded newlines. When you map an array of strings into a <code>textarea</code> (or keyvalue delimiter field),
|
|
591
|
+
the generated portal will automatically join the array into lines for display.
|
|
592
|
+
</div>
|
|
434
593
|
</section>
|
|
435
594
|
|
|
436
595
|
<section id="generate">
|
|
@@ -106,6 +106,9 @@
|
|
|
106
106
|
<b-button variant="outline-danger" class="mr-2" @click="newSchema">
|
|
107
107
|
<i class="fa fa-file"></i> New Schema
|
|
108
108
|
</b-button>
|
|
109
|
+
<b-button variant="outline-primary" class="mr-2" @click="openLookupsManager">
|
|
110
|
+
<i class="fa fa-list"></i> Lookups
|
|
111
|
+
</b-button>
|
|
109
112
|
<b-button variant="info" @click="showHelp = true">
|
|
110
113
|
<i class="fa fa-question-circle"></i> Help
|
|
111
114
|
</b-button>
|
|
@@ -239,6 +242,9 @@
|
|
|
239
242
|
<b-button variant="info" @click="loadSchema" class="mr-2">
|
|
240
243
|
<i class="fa fa-folder-open"></i> Load Schema
|
|
241
244
|
</b-button>
|
|
245
|
+
<b-button variant="outline-primary" @click="openLookupsManager" class="mr-2">
|
|
246
|
+
<i class="fa fa-list"></i> Manage Lookups
|
|
247
|
+
</b-button>
|
|
242
248
|
<b-button variant="warning" @click="openPlainJsonGenerator" class="mr-2">
|
|
243
249
|
<i class="fa fa-bolt"></i> Generate Schema (Plain JSON)
|
|
244
250
|
</b-button>
|
|
@@ -400,6 +406,123 @@
|
|
|
400
406
|
rows="2"
|
|
401
407
|
/>
|
|
402
408
|
</b-form-group>
|
|
409
|
+
|
|
410
|
+
<!-- Lookup / Auto-fill -->
|
|
411
|
+
<b-card class="mt-3" header="Lookup / Auto-fill (optional)">
|
|
412
|
+
<b-form-checkbox v-model="currentField.autoFill.enabled">
|
|
413
|
+
Enable lookup/autofill (set one or more fields based on a matched record)
|
|
414
|
+
</b-form-checkbox>
|
|
415
|
+
|
|
416
|
+
<div v-if="currentField.autoFill.enabled" class="mt-3">
|
|
417
|
+
<b-form-row>
|
|
418
|
+
<b-col md="6">
|
|
419
|
+
<b-form-group label="From field (user input)" label-for="autofill-from">
|
|
420
|
+
<b-form-select
|
|
421
|
+
id="autofill-from"
|
|
422
|
+
v-model="currentField.autoFill.fromField"
|
|
423
|
+
:options="[{value:'', text:'(select field)'}].concat(allFieldIdOptions)"
|
|
424
|
+
/>
|
|
425
|
+
<small class="form-text text-muted">When this field changes, the lookup runs.</small>
|
|
426
|
+
</b-form-group>
|
|
427
|
+
</b-col>
|
|
428
|
+
<b-col md="6">
|
|
429
|
+
<b-form-group label="Lookup source" label-for="autofill-source">
|
|
430
|
+
<b-form-select
|
|
431
|
+
id="autofill-source"
|
|
432
|
+
v-model="currentField.autoFill.sourceType"
|
|
433
|
+
:options="[
|
|
434
|
+
{ value: 'schema', text: 'Schema (static list in schema.lookups)' },
|
|
435
|
+
{ value: 'uibuilder', text: 'Node-RED (dynamic via uibuilder messages)' }
|
|
436
|
+
]"
|
|
437
|
+
/>
|
|
438
|
+
</b-form-group>
|
|
439
|
+
</b-col>
|
|
440
|
+
</b-form-row>
|
|
441
|
+
|
|
442
|
+
<div v-if="currentField.autoFill.sourceType === 'schema'">
|
|
443
|
+
<b-form-row>
|
|
444
|
+
<b-col md="6">
|
|
445
|
+
<b-form-group label="List name" label-for="autofill-listname">
|
|
446
|
+
<b-form-input id="autofill-listname" v-model="currentField.autoFill.schemaListName" placeholder="e.g., deviceTemplates" />
|
|
447
|
+
<small class="form-text text-muted">Stored at <code>schema.lookups.<name></code>.</small>
|
|
448
|
+
</b-form-group>
|
|
449
|
+
</b-col>
|
|
450
|
+
<b-col md="6">
|
|
451
|
+
<b-form-group label="Match key (property in list item)" label-for="autofill-matchkey">
|
|
452
|
+
<b-form-input id="autofill-matchkey" v-model="currentField.autoFill.matchKey" placeholder="name" />
|
|
453
|
+
<small class="form-text text-muted">Example: <code>name</code> or <code>meta.id</code>.</small>
|
|
454
|
+
</b-form-group>
|
|
455
|
+
</b-col>
|
|
456
|
+
</b-form-row>
|
|
457
|
+
<b-form-group label="(Optional) List JSON to store in schema" label-for="autofill-listjson">
|
|
458
|
+
<b-form-textarea
|
|
459
|
+
id="autofill-listjson"
|
|
460
|
+
v-model="currentField.autoFill.schemaListJson"
|
|
461
|
+
rows="4"
|
|
462
|
+
placeholder='[{"name":"Azure Device Template","id":"tmpl_123"}]'
|
|
463
|
+
/>
|
|
464
|
+
<small class="form-text text-muted">Leave blank to reference an existing list already in the schema.</small>
|
|
465
|
+
</b-form-group>
|
|
466
|
+
</div>
|
|
467
|
+
|
|
468
|
+
<div v-else>
|
|
469
|
+
<b-form-row>
|
|
470
|
+
<b-col md="6">
|
|
471
|
+
<b-form-group label="Lookup ID" label-for="autofill-lookupid">
|
|
472
|
+
<b-form-input id="autofill-lookupid" v-model="currentField.autoFill.lookupId" placeholder="e.g., deviceTemplates" />
|
|
473
|
+
<small class="form-text text-muted">
|
|
474
|
+
The portal will send <code>{type:'lookup:get', lookupId:'...'}</code> to Node-RED and expects
|
|
475
|
+
<code>{type:'lookup:data', lookupId:'...', items:[...]}</code> back.
|
|
476
|
+
</small>
|
|
477
|
+
</b-form-group>
|
|
478
|
+
</b-col>
|
|
479
|
+
<b-col md="6">
|
|
480
|
+
<b-form-group label="Match key (property in list item)" label-for="autofill-matchkey2">
|
|
481
|
+
<b-form-input id="autofill-matchkey2" v-model="currentField.autoFill.matchKey" placeholder="name" />
|
|
482
|
+
</b-form-group>
|
|
483
|
+
</b-col>
|
|
484
|
+
</b-form-row>
|
|
485
|
+
</div>
|
|
486
|
+
|
|
487
|
+
<b-form-group>
|
|
488
|
+
<b-form-checkbox v-model="currentField.autoFill.clearOnNoMatch">
|
|
489
|
+
Clear mapped target fields when no match is found
|
|
490
|
+
</b-form-checkbox>
|
|
491
|
+
</b-form-group>
|
|
492
|
+
|
|
493
|
+
<b-form-group label="Mappings (multi-set)">
|
|
494
|
+
<small class="form-text text-muted mb-2">
|
|
495
|
+
Each mapping sets <strong>target field</strong> = <strong>value key</strong> from the matched record.
|
|
496
|
+
</small>
|
|
497
|
+
<div v-for="(m, midx) in currentField.autoFill.mappings" :key="'af-map-' + midx" class="border rounded p-2 mb-2 bg-light">
|
|
498
|
+
<b-form-row>
|
|
499
|
+
<b-col md="5">
|
|
500
|
+
<b-form-group label="Target field" :label-for="'af-target-' + midx" label-size="sm" class="mb-1">
|
|
501
|
+
<b-form-select
|
|
502
|
+
:id="'af-target-' + midx"
|
|
503
|
+
v-model="m.targetField"
|
|
504
|
+
:options="[{value:'', text:'(select field)'}].concat(allFieldIdOptions)"
|
|
505
|
+
/>
|
|
506
|
+
</b-form-group>
|
|
507
|
+
</b-col>
|
|
508
|
+
<b-col md="6">
|
|
509
|
+
<b-form-group label="Value key" :label-for="'af-valuekey-' + midx" label-size="sm" class="mb-1">
|
|
510
|
+
<b-form-input :id="'af-valuekey-' + midx" v-model="m.valueKey" placeholder="e.g., id or meta.desc" />
|
|
511
|
+
</b-form-group>
|
|
512
|
+
</b-col>
|
|
513
|
+
<b-col md="1" class="d-flex align-items-end">
|
|
514
|
+
<b-button variant="outline-danger" size="sm" @click="removeAutofillMapping(midx)" title="Remove mapping">
|
|
515
|
+
<i class="fa fa-trash"></i>
|
|
516
|
+
</b-button>
|
|
517
|
+
</b-col>
|
|
518
|
+
</b-form-row>
|
|
519
|
+
</div>
|
|
520
|
+
<b-button variant="outline-primary" size="sm" @click="addAutofillMapping">
|
|
521
|
+
<i class="fa fa-plus"></i> Add mapping
|
|
522
|
+
</b-button>
|
|
523
|
+
</b-form-group>
|
|
524
|
+
</div>
|
|
525
|
+
</b-card>
|
|
403
526
|
|
|
404
527
|
<b-form-row v-if="currentField.type === 'textarea'">
|
|
405
528
|
<b-col md="6">
|
|
@@ -831,6 +954,128 @@ email{{currentField.keyvalueDelimiter || '='}}john@example.com</code></pre>
|
|
|
831
954
|
No saved schemas found. Save a schema first to load it later.
|
|
832
955
|
</b-alert>
|
|
833
956
|
</b-modal>
|
|
957
|
+
|
|
958
|
+
<!-- Manage Lookups Modal -->
|
|
959
|
+
<b-modal
|
|
960
|
+
id="lookups-modal"
|
|
961
|
+
title="Manage Lookups (schema.lookups)"
|
|
962
|
+
size="xl"
|
|
963
|
+
hide-footer
|
|
964
|
+
>
|
|
965
|
+
<p class="text-muted mb-3">
|
|
966
|
+
Lookup lists are stored at the top level of your schema under <code>lookups</code>.
|
|
967
|
+
Fields can reference these lists using Lookup / Auto-fill rules.
|
|
968
|
+
</p>
|
|
969
|
+
|
|
970
|
+
<b-form-row>
|
|
971
|
+
<b-col md="4">
|
|
972
|
+
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
973
|
+
<strong>Lists</strong>
|
|
974
|
+
<b-button size="sm" variant="outline-primary" @click="newLookupList">
|
|
975
|
+
<i class="fa fa-plus"></i> New
|
|
976
|
+
</b-button>
|
|
977
|
+
</div>
|
|
978
|
+
|
|
979
|
+
<div v-if="lookupListNames.length === 0" class="text-muted">
|
|
980
|
+
No lookup lists yet. Click <strong>New</strong> to create one.
|
|
981
|
+
</div>
|
|
982
|
+
|
|
983
|
+
<ul v-else class="list-group">
|
|
984
|
+
<li
|
|
985
|
+
v-for="name in lookupListNames"
|
|
986
|
+
:key="'lk-' + name"
|
|
987
|
+
class="list-group-item d-flex justify-content-between align-items-center"
|
|
988
|
+
:class="(lookupEditor.originalName === name) ? 'active' : ''"
|
|
989
|
+
style="cursor:pointer;"
|
|
990
|
+
@click="selectLookupList(name)"
|
|
991
|
+
>
|
|
992
|
+
<div>
|
|
993
|
+
<div class="font-weight-bold">{{name}}</div>
|
|
994
|
+
<small :class="(lookupEditor.originalName === name) ? 'text-white-50' : 'text-muted'">
|
|
995
|
+
{{lookupSummary(name)}}
|
|
996
|
+
</small>
|
|
997
|
+
</div>
|
|
998
|
+
<b-button
|
|
999
|
+
size="sm"
|
|
1000
|
+
variant="link"
|
|
1001
|
+
class="p-0"
|
|
1002
|
+
:class="(lookupEditor.originalName === name) ? 'text-white' : 'text-danger'"
|
|
1003
|
+
@click.stop="deleteLookupList(name)"
|
|
1004
|
+
title="Delete list"
|
|
1005
|
+
>
|
|
1006
|
+
<i class="fa fa-trash"></i>
|
|
1007
|
+
</b-button>
|
|
1008
|
+
</li>
|
|
1009
|
+
</ul>
|
|
1010
|
+
</b-col>
|
|
1011
|
+
|
|
1012
|
+
<b-col md="8">
|
|
1013
|
+
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
1014
|
+
<strong>Editor</strong>
|
|
1015
|
+
<div>
|
|
1016
|
+
<b-button size="sm" variant="outline-info" class="mr-2" @click="convertLookupMultilineToArrays" :disabled="lookupEditor.mode !== 'auto'">
|
|
1017
|
+
<i class="fa fa-exchange"></i> Convert multiline → arrays
|
|
1018
|
+
</b-button>
|
|
1019
|
+
<b-button size="sm" variant="outline-secondary" class="mr-2" @click="formatLookupJson" :disabled="!lookupEditor.json">
|
|
1020
|
+
<i class="fa fa-indent"></i> Format
|
|
1021
|
+
</b-button>
|
|
1022
|
+
<b-button size="sm" variant="success" @click="saveLookupList">
|
|
1023
|
+
<i class="fa fa-save"></i> Save List
|
|
1024
|
+
</b-button>
|
|
1025
|
+
</div>
|
|
1026
|
+
</div>
|
|
1027
|
+
|
|
1028
|
+
<b-form-group label="List name" label-for="lookup-name">
|
|
1029
|
+
<b-form-input id="lookup-name" v-model="lookupEditor.name" placeholder="e.g., osProfiles" />
|
|
1030
|
+
<small class="form-text text-muted">
|
|
1031
|
+
This will be stored under <code>schema.lookups.<name></code>.
|
|
1032
|
+
</small>
|
|
1033
|
+
</b-form-group>
|
|
1034
|
+
|
|
1035
|
+
<b-form-group label="Edit mode" label-for="lookup-mode">
|
|
1036
|
+
<b-form-select
|
|
1037
|
+
id="lookup-mode"
|
|
1038
|
+
v-model="lookupEditor.mode"
|
|
1039
|
+
:options="[
|
|
1040
|
+
{ value: 'auto', text: 'Auto (JSON)' },
|
|
1041
|
+
{ value: 'lines', text: 'Simple list (one item per line → JSON array)' }
|
|
1042
|
+
]"
|
|
1043
|
+
/>
|
|
1044
|
+
<small class="form-text text-muted">
|
|
1045
|
+
Use <strong>Simple list</strong> for arrays of strings without JSON brackets/quotes.
|
|
1046
|
+
</small>
|
|
1047
|
+
</b-form-group>
|
|
1048
|
+
|
|
1049
|
+
<b-form-group v-if="lookupEditor.mode === 'lines'" label="List items (one per line)" label-for="lookup-lines">
|
|
1050
|
+
<b-form-textarea
|
|
1051
|
+
id="lookup-lines"
|
|
1052
|
+
v-model="lookupEditor.lines"
|
|
1053
|
+
rows="12"
|
|
1054
|
+
placeholder="Item 1 Item 2 Item 3"
|
|
1055
|
+
/>
|
|
1056
|
+
<small class="form-text text-muted">
|
|
1057
|
+
Empty lines are ignored. Saved as <code>["Item 1","Item 2",...]</code>.
|
|
1058
|
+
</small>
|
|
1059
|
+
</b-form-group>
|
|
1060
|
+
|
|
1061
|
+
<b-form-group v-else label="List JSON" label-for="lookup-json">
|
|
1062
|
+
<b-form-textarea
|
|
1063
|
+
id="lookup-json"
|
|
1064
|
+
v-model="lookupEditor.json"
|
|
1065
|
+
rows="12"
|
|
1066
|
+
placeholder='Example: [{"name":"Windows 11","value":"win11","devices":["A","B"],"software":["X","Y"]}]'
|
|
1067
|
+
/>
|
|
1068
|
+
<small class="form-text text-muted">
|
|
1069
|
+
Recommended shape is an <strong>array of objects</strong>. For a simple string list, switch to <strong>Simple list</strong> mode.
|
|
1070
|
+
</small>
|
|
1071
|
+
</b-form-group>
|
|
1072
|
+
|
|
1073
|
+
<div v-if="lookupEditorKeys.length" class="text-muted">
|
|
1074
|
+
<strong>Detected keys:</strong> {{lookupEditorKeys.join(', ')}}
|
|
1075
|
+
</div>
|
|
1076
|
+
</b-col>
|
|
1077
|
+
</b-form-row>
|
|
1078
|
+
</b-modal>
|
|
834
1079
|
|
|
835
1080
|
<!-- Help Modal -->
|
|
836
1081
|
<b-modal
|