pict-section-dataimport 1.1.0 → 1.1.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pict-section-dataimport",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Embeddable, configurable data-import wizard for Pict apps — upload a CSV/TSV/Excel/fixed-width file, validate + detect its schema, map columns to a Meadow (or host-config) entity schema, generate comprehensions, and push them (records via the browser EntityProvider, or one POST to a Comprehension endpoint). Composes pict-section-upload + pict-section-accordion + pict-section-form, reusing meadow-integration's browser-safe transform/push engine.",
|
|
5
5
|
"main": "source/Pict-Section-DataImport.js",
|
|
6
6
|
"files": [
|
|
@@ -79,6 +79,10 @@ module.exports = /*css*/`
|
|
|
79
79
|
.psd-guid-parent { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; padding-left: 0.5rem; }
|
|
80
80
|
.psd-guid-rm { padding: 0 0.45rem; line-height: 1.4; }
|
|
81
81
|
.psd-guid-add { padding-left: 0.5rem; }
|
|
82
|
+
.psd-guid-owncol { display: inline-flex; align-items: center; gap: 0.15rem; }
|
|
83
|
+
.psd-guid-owadd { max-width: 8rem; opacity: 0.85; }
|
|
84
|
+
.psd-guid-tpl { min-width: 16rem; font-family: var(--theme-typography-family-mono, monospace); }
|
|
85
|
+
.psd-guid-own-fx { padding: 0 0.5rem; font-size: var(--theme-typography-size-sm, 0.78rem); }
|
|
82
86
|
.psd-guid-preview { font-size: var(--theme-typography-size-sm, 0.82rem); color: var(--theme-color-text-secondary, #6b7686); }
|
|
83
87
|
.psd-guid-warn { display: flex; align-items: center; gap: 0.4rem; font-size: var(--theme-typography-size-sm, 0.82rem); color: var(--theme-color-status-warning, #b8860b); }
|
|
84
88
|
`;
|
|
@@ -96,6 +96,10 @@ function buildStrategyConfig(pUIEntities, pPrefix)
|
|
|
96
96
|
tmpEntities[pEntityName] = {
|
|
97
97
|
Mode: tmpUI.Mode || 'prefixed',
|
|
98
98
|
OwnKeyColumn: tmpUI.OwnKeyColumn,
|
|
99
|
+
// Combinatorial own key: several columns concatenated, or a user-typed pict template. The engine's
|
|
100
|
+
// ownValueTemplate() prefers OwnKeyTemplate, then OwnKeyColumns, then the single OwnKeyColumn.
|
|
101
|
+
OwnKeyColumns: Array.isArray(tmpUI.OwnKeyColumns) ? tmpUI.OwnKeyColumns : undefined,
|
|
102
|
+
OwnKeyTemplate: tmpUI.OwnKeyTemplate,
|
|
99
103
|
OwnGUIDColumn: tmpUI.OwnGUIDColumn,
|
|
100
104
|
ContextEntities: tmpContextEntities,
|
|
101
105
|
ContextKeyColumns: tmpContextKeyColumns,
|
|
@@ -128,16 +132,14 @@ function previewGUID(pComposeSpec, pSampleRow)
|
|
|
128
132
|
const tmpRow = pSampleRow || {};
|
|
129
133
|
const tmpSegments = pComposeSpec.segments.map((pSegment) =>
|
|
130
134
|
{
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
// Resolve EVERY `{~D:Record.<col>~}` tag in the segment (a segment can now be multiple columns or a
|
|
136
|
+
// typed template, not just one column) — a lightweight parseTemplate stand-in for the live preview.
|
|
137
|
+
// Any non-tag literal text in the template is kept verbatim.
|
|
138
|
+
const tmpValue = String(pSegment.valueTemplate || '').replace(/\{~D:Record\.([^~}]+)~\}/g, (pFull, pColumn) =>
|
|
134
139
|
{
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
{
|
|
139
|
-
tmpValue = pSegment.valueTemplate;
|
|
140
|
-
}
|
|
140
|
+
const tmpCell = tmpRow[pColumn];
|
|
141
|
+
return (tmpCell !== undefined && tmpCell !== null) ? String(tmpCell) : '';
|
|
142
|
+
});
|
|
141
143
|
return { abbrev: pSegment.abbrev, value: tmpValue };
|
|
142
144
|
});
|
|
143
145
|
return libEngine.composeGUID({ prefix: pComposeSpec.prefix, separator: pComposeSpec.separator, maxLength: pComposeSpec.maxLength, hashLength: pComposeSpec.hashLength, segments: tmpSegments });
|
|
@@ -104,14 +104,18 @@ const _DEFAULT_CONFIGURATION =
|
|
|
104
104
|
Hash: 'DI-GUID-Panel',
|
|
105
105
|
Template: /*html*/`
|
|
106
106
|
<div class="psd-guid">
|
|
107
|
-
<div class="psd-guid-line"><span class="psd-guid-label">Identifier</span><select class="psd-select" onchange="_Pict.views['{~D:Record.WizardHash~}'].setStrategyMode('{~D:Record.Entity~}',this.value)">{~TS:DI-Opt:Record.ModeOptions~}</select>{~TS:DI-GUID-
|
|
107
|
+
<div class="psd-guid-line"><span class="psd-guid-label">Identifier</span><select class="psd-select" onchange="_Pict.views['{~D:Record.WizardHash~}'].setStrategyMode('{~D:Record.Entity~}',this.value)">{~TS:DI-Opt:Record.ModeOptions~}</select>{~TS:DI-GUID-Own:Record.OwnKeySlot~}</div>
|
|
108
108
|
{~TS:DI-GUID-Parent:Record.Parents~}
|
|
109
109
|
{~TS:DI-GUID-AddParent:Record.AddParentSlot~}
|
|
110
110
|
{~TS:DI-GUID-Preview:Record.PreviewSlot~}
|
|
111
111
|
{~TS:DI-GUID-Warn:Record.Warnings~}
|
|
112
112
|
</div>`
|
|
113
113
|
},
|
|
114
|
-
{ Hash: 'DI-GUID-
|
|
114
|
+
{ Hash: 'DI-GUID-Own', Template: /*html*/`<span class="psd-guid-sub">keyed by</span>{~TS:DI-GUID-OwnCol:Record.ColumnItems~}{~TS:DI-GUID-OwnAdd:Record.AddColumnSlot~}{~TS:DI-GUID-OwnTpl:Record.TemplateSlot~}<button type="button" class="psd-btn psd-btn-ghost psd-guid-own-fx" onclick="_Pict.views['{~D:Record.WizardHash~}'].toggleStrategyOwnMode('{~D:Record.Entity~}')" title="Switch between columns and a typed template">{~D:Record.ToggleLabel~}</button>` },
|
|
115
|
+
{ Hash: 'DI-GUID-OwnCol', Template: /*html*/`<span class="psd-guid-owncol"><select class="psd-select" onchange="_Pict.views['{~D:Record.WizardHash~}'].setStrategyOwnKeyAt('{~D:Record.Entity~}',{~D:Record.Index~},this.value)">{~TS:DI-Opt:Record.Options~}</select>{~TS:DI-GUID-OwnColRm:Record.RemoveSlot~}</span>` },
|
|
116
|
+
{ Hash: 'DI-GUID-OwnColRm', Template: /*html*/`<button type="button" class="psd-btn psd-btn-ghost psd-guid-rm" onclick="_Pict.views['{~D:Record.WizardHash~}'].removeStrategyOwnKey('{~D:Record.Entity~}',{~D:Record.Index~})" title="Remove column">×</button>` },
|
|
117
|
+
{ Hash: 'DI-GUID-OwnAdd', Template: /*html*/`<select class="psd-select psd-guid-owadd" onchange="if(this.value){_Pict.views['{~D:Record.WizardHash~}'].addStrategyOwnKey('{~D:Record.Entity~}',this.value);this.value='';}">{~TS:DI-Opt:Record.Options~}</select>` },
|
|
118
|
+
{ Hash: 'DI-GUID-OwnTpl', Template: /*html*/`<input type="text" class="psd-input psd-guid-tpl" placeholder="{~D:Record.Placeholder~}" value="{~D:Record.Template~}" onchange="_Pict.views['{~D:Record.WizardHash~}'].setStrategyOwnKeyTemplate('{~D:Record.Entity~}',this.value)" />` },
|
|
115
119
|
{
|
|
116
120
|
Hash: 'DI-GUID-Parent',
|
|
117
121
|
Template: /*html*/`<div class="psd-guid-parent"><span class="psd-guid-sub">related to</span><select class="psd-select" onchange="_Pict.views['{~D:Record.WizardHash~}'].setParentEntity('{~D:Record.Entity~}',{~D:Record.Index~},this.value)">{~TS:DI-Opt:Record.EntityOptions~}</select><span class="psd-guid-sub">via</span><select class="psd-select" onchange="_Pict.views['{~D:Record.WizardHash~}'].setParentKey('{~D:Record.Entity~}',{~D:Record.Index~},this.value)">{~TS:DI-Opt:Record.KeyOptions~}</select><select class="psd-select" onchange="_Pict.views['{~D:Record.WizardHash~}'].setParentMode('{~D:Record.Entity~}',{~D:Record.Index~},this.value)">{~TS:DI-Opt:Record.ModeOptions~}</select><button type="button" class="psd-btn psd-btn-ghost psd-guid-rm" onclick="_Pict.views['{~D:Record.WizardHash~}'].removeStrategyParent('{~D:Record.Entity~}',{~D:Record.Index~})" title="Remove">×</button></div>`
|
|
@@ -465,7 +469,7 @@ class PictViewDataImportWizard extends libPictView
|
|
|
465
469
|
{
|
|
466
470
|
tmpSession.GUIDStrategyUI[pEntityName] = tmpDefaults[pEntityName]
|
|
467
471
|
? JSON.parse(JSON.stringify(tmpDefaults[pEntityName]))
|
|
468
|
-
: { Mode: 'prefixed', OwnKeyColumn: tmpFirstColumn, Parents: [] };
|
|
472
|
+
: { Mode: 'prefixed', OwnKeyColumn: tmpFirstColumn, OwnKeyColumns: tmpFirstColumn ? [ tmpFirstColumn ] : [], OwnKeyMode: 'columns', OwnKeyTemplate: '', Parents: [] };
|
|
469
473
|
}
|
|
470
474
|
});
|
|
471
475
|
}
|
|
@@ -489,7 +493,7 @@ class PictViewDataImportWizard extends libPictView
|
|
|
489
493
|
{
|
|
490
494
|
const tmpSession = this._session();
|
|
491
495
|
tmpSession.GUIDStrategyUI = tmpSession.GUIDStrategyUI || {};
|
|
492
|
-
if (!tmpSession.GUIDStrategyUI[pEntity]) { tmpSession.GUIDStrategyUI[pEntity] = { Mode: 'prefixed', OwnKeyColumn: '', Parents: [] }; }
|
|
496
|
+
if (!tmpSession.GUIDStrategyUI[pEntity]) { tmpSession.GUIDStrategyUI[pEntity] = { Mode: 'prefixed', OwnKeyColumn: '', OwnKeyColumns: [], OwnKeyMode: 'columns', OwnKeyTemplate: '', Parents: [] }; }
|
|
493
497
|
return tmpSession.GUIDStrategyUI[pEntity];
|
|
494
498
|
}
|
|
495
499
|
|
|
@@ -502,7 +506,49 @@ class PictViewDataImportWizard extends libPictView
|
|
|
502
506
|
}
|
|
503
507
|
|
|
504
508
|
setStrategyMode(pEntity, pMode) { this._strategyUIEntity(pEntity).Mode = pMode; this._afterStrategyEdit(); }
|
|
505
|
-
|
|
509
|
+
|
|
510
|
+
// Combinatorial own key (#1): the entity's GUID can be keyed by ONE column, SEVERAL columns concatenated,
|
|
511
|
+
// or a user-typed pict template. OwnKeyColumns is the column list; OwnKeyTemplate (template mode) wins in
|
|
512
|
+
// the engine's ownValueTemplate(). OwnKeyColumn is kept in sync for backward compatibility.
|
|
513
|
+
/** The own-key column list (migrating a legacy single OwnKeyColumn on first access). */
|
|
514
|
+
_ownColumns(pEntity)
|
|
515
|
+
{
|
|
516
|
+
const tmpEntity = this._strategyUIEntity(pEntity);
|
|
517
|
+
if (!Array.isArray(tmpEntity.OwnKeyColumns))
|
|
518
|
+
{
|
|
519
|
+
tmpEntity.OwnKeyColumns = tmpEntity.OwnKeyColumn ? [ tmpEntity.OwnKeyColumn ] : [];
|
|
520
|
+
}
|
|
521
|
+
return tmpEntity.OwnKeyColumns;
|
|
522
|
+
}
|
|
523
|
+
_syncOwnSingle(pEntity) { const tmpEntity = this._strategyUIEntity(pEntity); tmpEntity.OwnKeyColumn = this._ownColumns(pEntity)[0] || ''; }
|
|
524
|
+
setStrategyOwnKey(pEntity, pColumn) { const tmpEntity = this._strategyUIEntity(pEntity); tmpEntity.OwnKeyColumns = pColumn ? [ pColumn ] : []; tmpEntity.OwnKeyColumn = pColumn; this._afterStrategyEdit(); }
|
|
525
|
+
setStrategyOwnKeyAt(pEntity, pIndex, pColumn)
|
|
526
|
+
{
|
|
527
|
+
const tmpColumns = this._ownColumns(pEntity);
|
|
528
|
+
if (pColumn) { tmpColumns[Number(pIndex)] = pColumn; } else { tmpColumns.splice(Number(pIndex), 1); }
|
|
529
|
+
this._syncOwnSingle(pEntity); this._afterStrategyEdit();
|
|
530
|
+
}
|
|
531
|
+
addStrategyOwnKey(pEntity, pColumn) { this._ownColumns(pEntity).push(pColumn); this._syncOwnSingle(pEntity); this._afterStrategyEdit(); }
|
|
532
|
+
removeStrategyOwnKey(pEntity, pIndex) { this._ownColumns(pEntity).splice(Number(pIndex), 1); this._syncOwnSingle(pEntity); this._afterStrategyEdit(); }
|
|
533
|
+
setStrategyOwnKeyTemplate(pEntity, pTemplate) { this._strategyUIEntity(pEntity).OwnKeyTemplate = pTemplate; this._afterStrategyEdit(); }
|
|
534
|
+
toggleStrategyOwnMode(pEntity)
|
|
535
|
+
{
|
|
536
|
+
const tmpEntity = this._strategyUIEntity(pEntity);
|
|
537
|
+
if (tmpEntity.OwnKeyMode === 'template')
|
|
538
|
+
{
|
|
539
|
+
tmpEntity.OwnKeyMode = 'columns';
|
|
540
|
+
tmpEntity.OwnKeyTemplate = ''; // columns win once template is cleared
|
|
541
|
+
}
|
|
542
|
+
else
|
|
543
|
+
{
|
|
544
|
+
tmpEntity.OwnKeyMode = 'template';
|
|
545
|
+
if (!tmpEntity.OwnKeyTemplate)
|
|
546
|
+
{
|
|
547
|
+
tmpEntity.OwnKeyTemplate = this._ownColumns(pEntity).map((pColumn) => `{~D:Record.${pColumn}~}`).join('');
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
this._afterStrategyEdit();
|
|
551
|
+
}
|
|
506
552
|
addStrategyParent(pEntity, pParentEntity)
|
|
507
553
|
{
|
|
508
554
|
const tmpEntity = this._strategyUIEntity(pEntity);
|
|
@@ -525,9 +571,26 @@ class PictViewDataImportWizard extends libPictView
|
|
|
525
571
|
const tmpModeOptions = (pSelected) => [ { Value: 'prefixed', Label: 'Prefixed (auto)', Selected: pSelected === 'prefixed' }, { Value: 'raw', Label: 'Raw GUID', Selected: pSelected === 'raw' }, { Value: 'rawid', Label: 'Raw ID', Selected: pSelected === 'rawid' } ];
|
|
526
572
|
const tmpColumnOptions = (pSelected) => [ { Value: '', Label: '(column)', Selected: !pSelected } ].concat(pSourceNames.map((pName) => ({ Value: pName, Label: pName, Selected: pName === pSelected })));
|
|
527
573
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
574
|
+
// Own-key slot: only prefixed mode composes its own GUID. It can be one column, several columns
|
|
575
|
+
// (concatenated), or a typed template — see #1 combinatorial GUID.
|
|
576
|
+
let tmpOwnKeySlot = [];
|
|
577
|
+
if ((tmpUI.Mode || 'prefixed') === 'prefixed')
|
|
578
|
+
{
|
|
579
|
+
const tmpIsTemplate = (tmpUI.OwnKeyMode === 'template');
|
|
580
|
+
const tmpOwnCols = Array.isArray(tmpUI.OwnKeyColumns) ? tmpUI.OwnKeyColumns : (tmpUI.OwnKeyColumn ? [ tmpUI.OwnKeyColumn ] : []);
|
|
581
|
+
const tmpColumnItems = tmpIsTemplate ? [] : tmpOwnCols.map((pColumn, pIndex) => (
|
|
582
|
+
{
|
|
583
|
+
WizardHash: this.options.WizardHash, Entity: pEntityName, Index: pIndex,
|
|
584
|
+
Options: tmpColumnOptions(pColumn),
|
|
585
|
+
RemoveSlot: (tmpOwnCols.length > 1) ? [ { WizardHash: this.options.WizardHash, Entity: pEntityName, Index: pIndex } ] : [],
|
|
586
|
+
}));
|
|
587
|
+
const tmpUsedOwn = {};
|
|
588
|
+
tmpOwnCols.forEach((pColumn) => { if (pColumn) { tmpUsedOwn[pColumn] = true; } });
|
|
589
|
+
const tmpAddOwnOptions = [ { Value: '', Label: '+ column', Selected: true } ].concat(pSourceNames.filter((pName) => !tmpUsedOwn[pName]).map((pName) => ({ Value: pName, Label: pName, Selected: false })));
|
|
590
|
+
const tmpAddColumnSlot = (!tmpIsTemplate && (tmpAddOwnOptions.length > 1)) ? [ { WizardHash: this.options.WizardHash, Entity: pEntityName, Options: tmpAddOwnOptions } ] : [];
|
|
591
|
+
const tmpTemplateSlot = tmpIsTemplate ? [ { WizardHash: this.options.WizardHash, Entity: pEntityName, Template: tmpUI.OwnKeyTemplate || '', Placeholder: 'e.g. {~D:Record.District~}-{~D:Record.Code~}' } ] : [];
|
|
592
|
+
tmpOwnKeySlot = [ { WizardHash: this.options.WizardHash, Entity: pEntityName, ColumnItems: tmpColumnItems, AddColumnSlot: tmpAddColumnSlot, TemplateSlot: tmpTemplateSlot, ToggleLabel: tmpIsTemplate ? 'use columns' : 'ƒ template' } ];
|
|
593
|
+
}
|
|
531
594
|
|
|
532
595
|
const tmpParents = (tmpUI.Parents || []).map((pParent, pIndex) => (
|
|
533
596
|
{
|