@epistola.app/valtimo-plugin 0.3.0 → 0.3.2
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/fesm2022/epistola.app-valtimo-plugin.mjs +646 -342
- package/fesm2022/epistola.app-valtimo-plugin.mjs.map +1 -1
- package/lib/components/array-field/array-field.component.d.ts +27 -0
- package/lib/components/data-mapping-tree/data-mapping-tree.component.d.ts +7 -12
- package/lib/components/epistola-document-preview/epistola-document-preview.component.d.ts +41 -0
- package/lib/components/epistola-document-preview/epistola-document-preview.formio.d.ts +4 -0
- package/lib/components/field-tree/field-tree.component.d.ts +4 -51
- package/lib/components/generate-document-configuration/generate-document-configuration.component.d.ts +7 -15
- package/lib/components/scalar-field/scalar-field.component.d.ts +16 -0
- package/lib/components/value-input/value-input.component.d.ts +34 -0
- package/lib/epistola.module.d.ts +11 -5
- package/lib/models/async-resource.d.ts +9 -0
- package/lib/models/config.d.ts +10 -0
- package/lib/models/index.d.ts +1 -0
- package/lib/services/epistola-plugin.service.d.ts +5 -1
- package/package.json +1 -1
- package/public_api.d.ts +5 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, EventEmitter, Output, Input, Component, ChangeDetectionStrategy, NgModule } from '@angular/core';
|
|
2
|
+
import { Injectable, EventEmitter, Output, Input, Component, ChangeDetectionStrategy, forwardRef, ENVIRONMENT_INITIALIZER, inject, Injector, NgModule } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common/http';
|
|
4
|
-
import { HttpClientModule } from '@angular/common/http';
|
|
4
|
+
import { HttpHeaders, HttpClientModule } from '@angular/common/http';
|
|
5
5
|
import * as i2 from '@valtimo/shared';
|
|
6
6
|
import * as i1$1 from '@angular/common';
|
|
7
7
|
import { CommonModule } from '@angular/common';
|
|
@@ -10,13 +10,26 @@ import { PluginTranslatePipeModule } from '@valtimo/plugin';
|
|
|
10
10
|
import * as i3 from '@valtimo/components';
|
|
11
11
|
import { FormModule, InputModule, ValuePathSelectorPrefix, ValuePathSelectorComponent, SelectModule, registerCustomFormioComponent } from '@valtimo/components';
|
|
12
12
|
import { BehaviorSubject, combineLatest, take, Subject, merge, of } from 'rxjs';
|
|
13
|
-
import { startWith, delay, takeUntil, filter, map, catchError, take as take$1, debounceTime } from 'rxjs/operators';
|
|
14
|
-
import * as
|
|
13
|
+
import { startWith, delay, takeUntil, filter, map, tap, switchMap, catchError, take as take$1, debounceTime } from 'rxjs/operators';
|
|
14
|
+
import * as i2$2 from '@angular/forms';
|
|
15
15
|
import { FormsModule } from '@angular/forms';
|
|
16
|
-
import * as i2$
|
|
16
|
+
import * as i2$3 from '@valtimo/process-link';
|
|
17
17
|
import * as i7 from '@formio/angular';
|
|
18
18
|
import { FormioModule } from '@formio/angular';
|
|
19
|
-
import * as i4
|
|
19
|
+
import * as i4 from '@angular/platform-browser';
|
|
20
|
+
|
|
21
|
+
function initialResource(empty) {
|
|
22
|
+
return { data: empty, loading: false, error: null };
|
|
23
|
+
}
|
|
24
|
+
function loadingResource(current) {
|
|
25
|
+
return { data: current, loading: true, error: null };
|
|
26
|
+
}
|
|
27
|
+
function successResource(data) {
|
|
28
|
+
return { data, loading: false, error: null };
|
|
29
|
+
}
|
|
30
|
+
function errorResource(current, error) {
|
|
31
|
+
return { data: current, loading: false, error };
|
|
32
|
+
}
|
|
20
33
|
|
|
21
34
|
/**
|
|
22
35
|
* Service for interacting with Epistola plugin API endpoints.
|
|
@@ -81,6 +94,12 @@ class EpistolaPluginService {
|
|
|
81
94
|
}
|
|
82
95
|
return this.http.get(`${this.apiEndpoint}/retry-form`, { params });
|
|
83
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Discover all previewable document sources for a given Valtimo document.
|
|
99
|
+
*/
|
|
100
|
+
getPreviewSources(documentId) {
|
|
101
|
+
return this.http.get(`${this.apiEndpoint}/preview-sources`, { params: { documentId } });
|
|
102
|
+
}
|
|
84
103
|
/**
|
|
85
104
|
* Preview a document by dry-running the generate-document process link.
|
|
86
105
|
* Returns the resolved data as a mock preview (Phase 1).
|
|
@@ -217,149 +236,203 @@ function countRequiredMapped(fields, mapping) {
|
|
|
217
236
|
}
|
|
218
237
|
|
|
219
238
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
* - SCALAR fields render as a label + input row with a 3-mode selector (browse / pv / expression)
|
|
223
|
-
* - OBJECT fields render as a collapsible section with children indented inside
|
|
224
|
-
* - ARRAY fields render as a collapsible section with source collection input and optional per-item field mappings
|
|
225
|
-
*
|
|
226
|
-
* Input modes:
|
|
227
|
-
* - Browse (⊞): ValuePathSelector for doc:/case: paths
|
|
228
|
-
* - PV (pv): Dropdown of discovered process variables (text fallback when none found)
|
|
229
|
-
* - Expression (fx): Free-text input for manual expressions
|
|
239
|
+
* Reusable 3-mode input (browse / pv / expression) for value resolver expressions.
|
|
240
|
+
* Used by both ScalarFieldComponent and ArrayFieldComponent for source mapping.
|
|
230
241
|
*/
|
|
231
|
-
class
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
242
|
+
class ValueInputComponent {
|
|
243
|
+
cdr;
|
|
244
|
+
name = '';
|
|
245
|
+
value = '';
|
|
246
|
+
pluginId = '';
|
|
235
247
|
caseDefinitionKey = null;
|
|
236
248
|
processVariables = [];
|
|
237
249
|
disabled = false;
|
|
250
|
+
placeholder = 'e.g. pv:variableName or doc:path.to.field';
|
|
238
251
|
valueChange = new EventEmitter();
|
|
239
252
|
ValuePathSelectorPrefix = ValuePathSelectorPrefix;
|
|
240
253
|
inputMode = 'browse';
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
totalRequired = 0;
|
|
254
|
+
selectedPv = '';
|
|
255
|
+
browseDefault = '';
|
|
256
|
+
constructor(cdr) {
|
|
257
|
+
this.cdr = cdr;
|
|
258
|
+
}
|
|
247
259
|
ngOnChanges(changes) {
|
|
248
|
-
if (changes['value']
|
|
249
|
-
this.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
this.expanded = true;
|
|
253
|
-
}
|
|
260
|
+
if (changes['value']) {
|
|
261
|
+
this.inputMode = this.detectInputMode(this.value);
|
|
262
|
+
this.browseDefault = normalizeToDots(this.value);
|
|
263
|
+
this.selectedPv = extractPvName(this.value);
|
|
254
264
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (this.field?.fieldType === 'SCALAR') {
|
|
258
|
-
this.inputMode = this.detectInputMode(this.value);
|
|
259
|
-
}
|
|
260
|
-
else if (this.field?.fieldType === 'ARRAY') {
|
|
261
|
-
const sourceValue = this.getSourceValue();
|
|
262
|
-
if (sourceValue) {
|
|
263
|
-
this.inputMode = this.detectInputMode(sourceValue);
|
|
264
|
-
}
|
|
265
|
-
// Detect per-field mode from value shape
|
|
266
|
-
if (typeof this.value === 'object' && this.value !== null && '_source' in this.value) {
|
|
267
|
-
this.arrayPerFieldMode = true;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
265
|
+
if (changes['processVariables']) {
|
|
266
|
+
this.cdr.markForCheck();
|
|
270
267
|
}
|
|
271
268
|
}
|
|
272
|
-
toggleExpanded() {
|
|
273
|
-
this.expanded = !this.expanded;
|
|
274
|
-
}
|
|
275
269
|
setInputMode(mode) {
|
|
276
270
|
this.inputMode = mode;
|
|
277
271
|
}
|
|
278
|
-
/** Handle value change from ValuePathSelector (browse mode) */
|
|
279
272
|
onBrowseValueChange(newValue) {
|
|
280
|
-
this.
|
|
273
|
+
this.valueChange.emit(normalizeToDots(newValue));
|
|
281
274
|
}
|
|
282
|
-
/** Handle value change from PV dropdown */
|
|
283
275
|
onPvChange(newValue) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
this.emitScalarValue('');
|
|
289
|
-
}
|
|
276
|
+
this.selectedPv = newValue;
|
|
277
|
+
this.valueChange.emit(newValue ? 'pv:' + newValue : '');
|
|
290
278
|
}
|
|
291
|
-
/** Handle value change from text input (expression mode) */
|
|
292
279
|
onExpressionValueChange(newValue) {
|
|
293
|
-
this.
|
|
280
|
+
this.valueChange.emit(newValue);
|
|
294
281
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if (
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
282
|
+
detectInputMode(value) {
|
|
283
|
+
if (!value)
|
|
284
|
+
return 'browse';
|
|
285
|
+
if (value.startsWith('doc:') || value.startsWith('case:'))
|
|
286
|
+
return 'browse';
|
|
287
|
+
if (value.startsWith('pv:'))
|
|
288
|
+
return 'pv';
|
|
289
|
+
if (value.length > 0)
|
|
290
|
+
return 'expression';
|
|
291
|
+
return 'browse';
|
|
305
292
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
293
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ValueInputComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
294
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: ValueInputComponent, isStandalone: true, selector: "epistola-value-input", inputs: { name: "name", value: "value", pluginId: "pluginId", caseDefinitionKey: "caseDefinitionKey", processVariables: "processVariables", disabled: "disabled", placeholder: "placeholder" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"value-input\">\n <div class=\"input-mode-group\">\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'browse'\" [disabled]=\"disabled\" (click)=\"setInputMode('browse')\" [title]=\"'browseMode' | pluginTranslate: pluginId | async\">\u229E</button>\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'pv'\" [disabled]=\"disabled\" (click)=\"setInputMode('pv')\" [title]=\"'pvMode' | pluginTranslate: pluginId | async\">pv</button>\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'expression'\" [disabled]=\"disabled\" (click)=\"setInputMode('expression')\" [title]=\"'expressionMode' | pluginTranslate: pluginId | async\">fx</button>\n </div>\n\n <!-- Browse mode: ValuePathSelector -->\n <div class=\"input-control\" *ngIf=\"inputMode === 'browse'\">\n <valtimo-value-path-selector\n [name]=\"name\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [prefixes]=\"[ValuePathSelectorPrefix.DOC, ValuePathSelectorPrefix.CASE]\"\n [notation]=\"'dots'\"\n [disabled]=\"disabled\"\n [defaultValue]=\"browseDefault\"\n [showCaseDefinitionSelector]=\"!caseDefinitionKey\"\n (valueChangeEvent)=\"onBrowseValueChange($event)\"\n ></valtimo-value-path-selector>\n </div>\n\n <!-- PV mode: dropdown (when available) or text fallback -->\n <div class=\"input-control\" *ngIf=\"inputMode === 'pv'\">\n <select\n *ngIf=\"processVariables.length > 0; else pvFallback\"\n class=\"pv-select\"\n [disabled]=\"disabled\"\n [(ngModel)]=\"selectedPv\"\n (ngModelChange)=\"onPvChange($event)\"\n >\n <option value=\"\">{{ 'pvPlaceholder' | pluginTranslate: pluginId | async }}</option>\n <option *ngFor=\"let pv of processVariables\" [value]=\"pv\">{{ pv }}</option>\n </select>\n <ng-template #pvFallback>\n <v-input\n [name]=\"'pvfb_' + name\"\n [defaultValue]=\"selectedPv\"\n [disabled]=\"disabled\"\n [placeholder]=\"'pvPlaceholder' | pluginTranslate: pluginId | async\"\n (valueChange)=\"onPvChange($event)\"\n ></v-input>\n </ng-template>\n </div>\n\n <!-- Expression mode: text input -->\n <div class=\"input-control\" *ngIf=\"inputMode === 'expression'\">\n <v-input\n [name]=\"'fx_' + name\"\n [defaultValue]=\"value\"\n [disabled]=\"disabled\"\n [placeholder]=\"placeholder\"\n (valueChange)=\"onExpressionValueChange($event)\"\n ></v-input>\n </div>\n</div>\n", styles: [".value-input{display:flex;align-items:flex-start;gap:.5rem;min-width:0}.value-input ::ng-deep [data-test-id=valuePathSelectorToggle]{display:none!important}.input-control{flex:1;min-width:0}.input-mode-group{flex:0 0 auto;display:flex;margin-top:.25rem;border:1px solid #c6c6c6;border-radius:4px;overflow:hidden}.input-mode-group .mode-btn{width:32px;height:32px;padding:0;border:none;border-right:1px solid #c6c6c6;background:#f4f4f4;color:#525252;font-size:.75rem;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background-color .15s}.input-mode-group .mode-btn:last-child{border-right:none}.input-mode-group .mode-btn:hover:not(:disabled){background:#e0e0e0}.input-mode-group .mode-btn.mode-active{background:#0f62fe;color:#fff}.input-mode-group .mode-btn.mode-active:hover:not(:disabled){background:#0353e9}.input-mode-group .mode-btn:disabled{opacity:.5;cursor:not-allowed}.pv-select{width:100%;height:2.5rem;padding:0 .75rem;border:1px solid #c6c6c6;border-radius:4px;background:#fff;color:#161616;font-size:.875rem;cursor:pointer}.pv-select:focus{outline:2px solid #0f62fe;outline-offset:-2px}.pv-select:disabled{opacity:.5;cursor:not-allowed;background:#f4f4f4}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PluginTranslatePipeModule }, { kind: "pipe", type: i2$1.PluginTranslatePipe, name: "pluginTranslate" }, { kind: "ngmodule", type: InputModule }, { kind: "component", type: i3.InputComponent, selector: "v-input", inputs: ["name", "type", "title", "titleTranslationKey", "defaultValue", "widthPx", "fullWidth", "margin", "smallMargin", "disabled", "step", "min", "maxLength", "tooltip", "required", "hideNumberSpinBox", "smallLabel", "rows", "clear$", "carbonTheme", "placeholder", "dataTestId", "trim", "presetsTitle", "presetOptions"], outputs: ["valueChange"] }, { kind: "component", type: ValuePathSelectorComponent, selector: "valtimo-value-path-selector", inputs: ["name", "appendInline", "margin", "marginLg", "marginXl", "disabled", "caseDefinitionKey", "caseDefinitionVersionTag", "buildingBlockDefinitionKey", "buildingBlockDefinitionVersionTag", "prefixes", "label", "tooltip", "required", "showCaseDefinitionSelector", "notation", "dropUp", "defaultValue", "type", "parentItem", "filterItems"], outputs: ["valueChangeEvent", "collectionSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
295
|
+
}
|
|
296
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ValueInputComponent, decorators: [{
|
|
297
|
+
type: Component,
|
|
298
|
+
args: [{ selector: 'epistola-value-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
299
|
+
CommonModule,
|
|
300
|
+
FormsModule,
|
|
301
|
+
PluginTranslatePipeModule,
|
|
302
|
+
InputModule,
|
|
303
|
+
ValuePathSelectorComponent
|
|
304
|
+
], template: "<div class=\"value-input\">\n <div class=\"input-mode-group\">\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'browse'\" [disabled]=\"disabled\" (click)=\"setInputMode('browse')\" [title]=\"'browseMode' | pluginTranslate: pluginId | async\">\u229E</button>\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'pv'\" [disabled]=\"disabled\" (click)=\"setInputMode('pv')\" [title]=\"'pvMode' | pluginTranslate: pluginId | async\">pv</button>\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'expression'\" [disabled]=\"disabled\" (click)=\"setInputMode('expression')\" [title]=\"'expressionMode' | pluginTranslate: pluginId | async\">fx</button>\n </div>\n\n <!-- Browse mode: ValuePathSelector -->\n <div class=\"input-control\" *ngIf=\"inputMode === 'browse'\">\n <valtimo-value-path-selector\n [name]=\"name\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [prefixes]=\"[ValuePathSelectorPrefix.DOC, ValuePathSelectorPrefix.CASE]\"\n [notation]=\"'dots'\"\n [disabled]=\"disabled\"\n [defaultValue]=\"browseDefault\"\n [showCaseDefinitionSelector]=\"!caseDefinitionKey\"\n (valueChangeEvent)=\"onBrowseValueChange($event)\"\n ></valtimo-value-path-selector>\n </div>\n\n <!-- PV mode: dropdown (when available) or text fallback -->\n <div class=\"input-control\" *ngIf=\"inputMode === 'pv'\">\n <select\n *ngIf=\"processVariables.length > 0; else pvFallback\"\n class=\"pv-select\"\n [disabled]=\"disabled\"\n [(ngModel)]=\"selectedPv\"\n (ngModelChange)=\"onPvChange($event)\"\n >\n <option value=\"\">{{ 'pvPlaceholder' | pluginTranslate: pluginId | async }}</option>\n <option *ngFor=\"let pv of processVariables\" [value]=\"pv\">{{ pv }}</option>\n </select>\n <ng-template #pvFallback>\n <v-input\n [name]=\"'pvfb_' + name\"\n [defaultValue]=\"selectedPv\"\n [disabled]=\"disabled\"\n [placeholder]=\"'pvPlaceholder' | pluginTranslate: pluginId | async\"\n (valueChange)=\"onPvChange($event)\"\n ></v-input>\n </ng-template>\n </div>\n\n <!-- Expression mode: text input -->\n <div class=\"input-control\" *ngIf=\"inputMode === 'expression'\">\n <v-input\n [name]=\"'fx_' + name\"\n [defaultValue]=\"value\"\n [disabled]=\"disabled\"\n [placeholder]=\"placeholder\"\n (valueChange)=\"onExpressionValueChange($event)\"\n ></v-input>\n </div>\n</div>\n", styles: [".value-input{display:flex;align-items:flex-start;gap:.5rem;min-width:0}.value-input ::ng-deep [data-test-id=valuePathSelectorToggle]{display:none!important}.input-control{flex:1;min-width:0}.input-mode-group{flex:0 0 auto;display:flex;margin-top:.25rem;border:1px solid #c6c6c6;border-radius:4px;overflow:hidden}.input-mode-group .mode-btn{width:32px;height:32px;padding:0;border:none;border-right:1px solid #c6c6c6;background:#f4f4f4;color:#525252;font-size:.75rem;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background-color .15s}.input-mode-group .mode-btn:last-child{border-right:none}.input-mode-group .mode-btn:hover:not(:disabled){background:#e0e0e0}.input-mode-group .mode-btn.mode-active{background:#0f62fe;color:#fff}.input-mode-group .mode-btn.mode-active:hover:not(:disabled){background:#0353e9}.input-mode-group .mode-btn:disabled{opacity:.5;cursor:not-allowed}.pv-select{width:100%;height:2.5rem;padding:0 .75rem;border:1px solid #c6c6c6;border-radius:4px;background:#fff;color:#161616;font-size:.875rem;cursor:pointer}.pv-select:focus{outline:2px solid #0f62fe;outline-offset:-2px}.pv-select:disabled{opacity:.5;cursor:not-allowed;background:#f4f4f4}\n"] }]
|
|
305
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { name: [{
|
|
306
|
+
type: Input
|
|
307
|
+
}], value: [{
|
|
308
|
+
type: Input
|
|
309
|
+
}], pluginId: [{
|
|
310
|
+
type: Input
|
|
311
|
+
}], caseDefinitionKey: [{
|
|
312
|
+
type: Input
|
|
313
|
+
}], processVariables: [{
|
|
314
|
+
type: Input
|
|
315
|
+
}], disabled: [{
|
|
316
|
+
type: Input
|
|
317
|
+
}], placeholder: [{
|
|
318
|
+
type: Input
|
|
319
|
+
}], valueChange: [{
|
|
320
|
+
type: Output
|
|
321
|
+
}] } });
|
|
322
|
+
/** Convert slash-notation paths (e.g. doc:/a/b) to dot notation (doc:a.b). */
|
|
323
|
+
function normalizeToDots(value) {
|
|
324
|
+
if (typeof value !== 'string')
|
|
325
|
+
return value;
|
|
326
|
+
const colonIndex = value.indexOf(':');
|
|
327
|
+
if (colonIndex < 0)
|
|
328
|
+
return value;
|
|
329
|
+
const prefix = value.substring(0, colonIndex);
|
|
330
|
+
const path = value.substring(colonIndex + 1);
|
|
331
|
+
if (!path.includes('/'))
|
|
332
|
+
return value;
|
|
333
|
+
const normalized = path.split('/').filter(p => p.length > 0).join('.');
|
|
334
|
+
return `${prefix}:${normalized}`;
|
|
335
|
+
}
|
|
336
|
+
function extractPvName(value) {
|
|
337
|
+
if (typeof value === 'string' && value.startsWith('pv:')) {
|
|
338
|
+
return value.substring(3);
|
|
312
339
|
}
|
|
313
|
-
|
|
314
|
-
|
|
340
|
+
return '';
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
class ScalarFieldComponent {
|
|
344
|
+
field;
|
|
345
|
+
value = undefined;
|
|
346
|
+
pluginId;
|
|
347
|
+
caseDefinitionKey = null;
|
|
348
|
+
processVariables = [];
|
|
349
|
+
disabled = false;
|
|
350
|
+
valueChange = new EventEmitter();
|
|
351
|
+
get stringValue() {
|
|
315
352
|
return typeof this.value === 'string' ? this.value : '';
|
|
316
353
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
354
|
+
onValueChange(newValue) {
|
|
355
|
+
this.valueChange.emit(newValue || undefined);
|
|
356
|
+
}
|
|
357
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ScalarFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
358
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: ScalarFieldComponent, isStandalone: true, selector: "epistola-scalar-field", inputs: { field: "field", value: "value", pluginId: "pluginId", caseDefinitionKey: "caseDefinitionKey", processVariables: "processVariables", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"field-row\" [class.field-required-unmapped]=\"field.required && !stringValue\">\n <div class=\"field-label\">\n <span class=\"field-name\">{{ field.name }}</span>\n <span class=\"field-meta\">({{ field.type }}{{ field.required ? ', required' : '' }})</span>\n </div>\n <epistola-value-input\n [name]=\"'field_' + field.path\"\n [value]=\"stringValue\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"onValueChange($event)\"\n ></epistola-value-input>\n</div>\n", styles: [".field-row{display:flex;align-items:flex-start;gap:.75rem;padding:.5rem 0;border-left:3px solid transparent}.field-row.field-required-unmapped{border-left-color:#dc3545;background-color:#fff5f5;padding-left:.5rem}.field-label{flex:0 0 200px;min-width:140px;padding-top:.5rem;word-break:break-word}.field-label .field-name{font-weight:500}.field-label .field-meta{font-size:.8125rem;color:#6c757d;margin-left:.25rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: PluginTranslatePipeModule }, { kind: "component", type: ValueInputComponent, selector: "epistola-value-input", inputs: ["name", "value", "pluginId", "caseDefinitionKey", "processVariables", "disabled", "placeholder"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
359
|
+
}
|
|
360
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ScalarFieldComponent, decorators: [{
|
|
361
|
+
type: Component,
|
|
362
|
+
args: [{ selector: 'epistola-scalar-field', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, PluginTranslatePipeModule, ValueInputComponent], template: "<div class=\"field-row\" [class.field-required-unmapped]=\"field.required && !stringValue\">\n <div class=\"field-label\">\n <span class=\"field-name\">{{ field.name }}</span>\n <span class=\"field-meta\">({{ field.type }}{{ field.required ? ', required' : '' }})</span>\n </div>\n <epistola-value-input\n [name]=\"'field_' + field.path\"\n [value]=\"stringValue\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"onValueChange($event)\"\n ></epistola-value-input>\n</div>\n", styles: [".field-row{display:flex;align-items:flex-start;gap:.75rem;padding:.5rem 0;border-left:3px solid transparent}.field-row.field-required-unmapped{border-left-color:#dc3545;background-color:#fff5f5;padding-left:.5rem}.field-label{flex:0 0 200px;min-width:140px;padding-top:.5rem;word-break:break-word}.field-label .field-name{font-weight:500}.field-label .field-meta{font-size:.8125rem;color:#6c757d;margin-left:.25rem}\n"] }]
|
|
363
|
+
}], propDecorators: { field: [{
|
|
364
|
+
type: Input
|
|
365
|
+
}], value: [{
|
|
366
|
+
type: Input
|
|
367
|
+
}], pluginId: [{
|
|
368
|
+
type: Input
|
|
369
|
+
}], caseDefinitionKey: [{
|
|
370
|
+
type: Input
|
|
371
|
+
}], processVariables: [{
|
|
372
|
+
type: Input
|
|
373
|
+
}], disabled: [{
|
|
374
|
+
type: Input
|
|
375
|
+
}], valueChange: [{
|
|
376
|
+
type: Output
|
|
377
|
+
}] } });
|
|
378
|
+
|
|
379
|
+
class ArrayFieldComponent {
|
|
380
|
+
field;
|
|
381
|
+
value = undefined;
|
|
382
|
+
pluginId;
|
|
383
|
+
caseDefinitionKey = null;
|
|
384
|
+
processVariables = [];
|
|
385
|
+
disabled = false;
|
|
386
|
+
valueChange = new EventEmitter();
|
|
387
|
+
expanded = false;
|
|
388
|
+
arrayPerFieldMode = false;
|
|
389
|
+
mappedCount = 0;
|
|
390
|
+
totalRequired = 0;
|
|
391
|
+
ngOnChanges(changes) {
|
|
392
|
+
if (changes['value'] || changes['field']) {
|
|
393
|
+
this.updateCompleteness();
|
|
394
|
+
if (!this.expanded && this.totalRequired > 0 && this.mappedCount < this.totalRequired) {
|
|
395
|
+
this.expanded = true;
|
|
396
|
+
}
|
|
397
|
+
// Detect per-field mode from value shape
|
|
398
|
+
if (changes['value'] && typeof this.value === 'object' && this.value !== null && '_source' in this.value) {
|
|
399
|
+
this.arrayPerFieldMode = true;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
toggleExpanded() {
|
|
404
|
+
this.expanded = !this.expanded;
|
|
321
405
|
}
|
|
322
|
-
// --- ARRAY-specific methods ---
|
|
323
|
-
/** Get the source collection value (works for both direct string and _source format) */
|
|
324
406
|
getSourceValue() {
|
|
325
407
|
if (typeof this.value === 'string') {
|
|
326
|
-
return this.value;
|
|
408
|
+
return normalizeToDots(this.value);
|
|
327
409
|
}
|
|
328
410
|
if (typeof this.value === 'object' && this.value !== null && '_source' in this.value) {
|
|
329
|
-
return this.value['_source'] || '';
|
|
411
|
+
return normalizeToDots(this.value['_source'] || '');
|
|
330
412
|
}
|
|
331
413
|
return '';
|
|
332
414
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
415
|
+
onSourceValueChange(newValue) {
|
|
416
|
+
if (this.arrayPerFieldMode) {
|
|
417
|
+
const current = (typeof this.value === 'object' && this.value !== null) ? { ...this.value } : {};
|
|
418
|
+
current['_source'] = newValue || '';
|
|
419
|
+
this.valueChange.emit(current);
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
this.valueChange.emit(newValue || undefined);
|
|
423
|
+
}
|
|
337
424
|
}
|
|
338
425
|
toggleArrayPerFieldMode() {
|
|
339
426
|
this.arrayPerFieldMode = !this.arrayPerFieldMode;
|
|
340
427
|
if (this.arrayPerFieldMode) {
|
|
341
|
-
// Switch from direct to per-field: convert string value to _source object
|
|
342
428
|
const currentSource = this.getSourceValue();
|
|
343
|
-
|
|
344
|
-
this.valueChange.emit(obj);
|
|
429
|
+
this.valueChange.emit({ _source: currentSource });
|
|
345
430
|
}
|
|
346
431
|
else {
|
|
347
|
-
// Switch from per-field to direct: extract _source as string value
|
|
348
432
|
const source = this.getSourceValue();
|
|
349
433
|
this.valueChange.emit(source || undefined);
|
|
350
434
|
}
|
|
351
435
|
}
|
|
352
|
-
/** Handle source collection value change (used in ARRAY mode) */
|
|
353
|
-
onSourceBrowseChange(newValue) {
|
|
354
|
-
this.updateSourceValue(newValue);
|
|
355
|
-
}
|
|
356
|
-
onSourcePvChange(newValue) {
|
|
357
|
-
this.updateSourceValue(newValue ? 'pv:' + newValue : '');
|
|
358
|
-
}
|
|
359
|
-
onSourceExpressionChange(newValue) {
|
|
360
|
-
this.updateSourceValue(newValue);
|
|
361
|
-
}
|
|
362
|
-
/** Handle per-item field mapping change */
|
|
363
436
|
onItemFieldChange(childName, sourceFieldName) {
|
|
364
437
|
const current = (typeof this.value === 'object' && this.value !== null) ? { ...this.value } : { _source: '' };
|
|
365
438
|
if (sourceFieldName && sourceFieldName.trim().length > 0) {
|
|
@@ -370,59 +443,30 @@ class FieldTreeComponent {
|
|
|
370
443
|
}
|
|
371
444
|
this.valueChange.emit(current);
|
|
372
445
|
}
|
|
373
|
-
/** Get the current source field name mapping for a child */
|
|
374
446
|
getItemFieldValue(childName) {
|
|
375
447
|
if (typeof this.value === 'object' && this.value !== null) {
|
|
376
448
|
return this.value[childName] || '';
|
|
377
449
|
}
|
|
378
450
|
return '';
|
|
379
451
|
}
|
|
380
|
-
/** Check if the array has any children that can be mapped per-item */
|
|
381
452
|
hasArrayChildren() {
|
|
382
453
|
return !!(this.field?.children && this.field.children.length > 0);
|
|
383
454
|
}
|
|
384
|
-
emitScalarValue(newValue) {
|
|
385
|
-
this.valueChange.emit(newValue || undefined);
|
|
386
|
-
}
|
|
387
|
-
updateSourceValue(newValue) {
|
|
388
|
-
if (this.arrayPerFieldMode) {
|
|
389
|
-
const current = (typeof this.value === 'object' && this.value !== null) ? { ...this.value } : {};
|
|
390
|
-
current['_source'] = newValue || '';
|
|
391
|
-
this.valueChange.emit(current);
|
|
392
|
-
}
|
|
393
|
-
else {
|
|
394
|
-
this.valueChange.emit(newValue || undefined);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
455
|
updateCompleteness() {
|
|
398
|
-
if (this.field?.fieldType === 'OBJECT' && this.field.children) {
|
|
399
|
-
const stats = countRequiredMapped(this.field.children, this.value || {});
|
|
400
|
-
this.mappedCount = stats.mapped;
|
|
401
|
-
this.totalRequired = stats.total;
|
|
402
|
-
}
|
|
403
|
-
else if (this.field?.fieldType === 'ARRAY') {
|
|
404
|
-
this.updateArrayCompleteness();
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
updateArrayCompleteness() {
|
|
408
456
|
if (!this.field?.children || this.field.children.length === 0) {
|
|
409
|
-
// No children: just check if source is set
|
|
410
457
|
this.totalRequired = this.field?.required ? 1 : 0;
|
|
411
458
|
this.mappedCount = this.getSourceValue() ? (this.field?.required ? 1 : 0) : 0;
|
|
412
459
|
return;
|
|
413
460
|
}
|
|
414
461
|
if (typeof this.value === 'object' && this.value !== null && '_source' in this.value) {
|
|
415
|
-
// Per-field mode: count source + required children
|
|
416
462
|
let total = 0;
|
|
417
463
|
let mapped = 0;
|
|
418
|
-
// Source counts as 1 required
|
|
419
464
|
if (this.field?.required) {
|
|
420
465
|
total++;
|
|
421
466
|
if (this.value['_source'] && this.value['_source'].trim().length > 0) {
|
|
422
467
|
mapped++;
|
|
423
468
|
}
|
|
424
469
|
}
|
|
425
|
-
// Count required children
|
|
426
470
|
for (const child of this.field.children) {
|
|
427
471
|
if (child.required) {
|
|
428
472
|
total++;
|
|
@@ -436,37 +480,87 @@ class FieldTreeComponent {
|
|
|
436
480
|
this.totalRequired = total;
|
|
437
481
|
}
|
|
438
482
|
else {
|
|
439
|
-
// Direct mode: just check if source is set
|
|
440
483
|
this.totalRequired = this.field?.required ? 1 : 0;
|
|
441
484
|
this.mappedCount = this.getSourceValue() ? (this.field?.required ? 1 : 0) : 0;
|
|
442
485
|
}
|
|
443
486
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
487
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ArrayFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
488
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: ArrayFieldComponent, isStandalone: true, selector: "epistola-array-field", inputs: { field: "field", value: "value", pluginId: "pluginId", caseDefinitionKey: "caseDefinitionKey", processVariables: "processVariables", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"field-array\">\n <div class=\"field-array-header\" (click)=\"toggleExpanded()\" [class.field-required-unmapped]=\"field.required && !getSourceValue()\">\n <span class=\"expand-icon\">{{ expanded ? '\u25BC' : '\u25B6' }}</span>\n <span class=\"field-name\">{{ field.name }}</span>\n <span class=\"field-meta\">(array{{ field.required ? ', required' : '' }})</span>\n <span class=\"completeness-badge\" *ngIf=\"totalRequired > 0 && !expanded\">\n {{ mappedCount }}/{{ totalRequired }}\n </span>\n <span class=\"mapped-indicator\" *ngIf=\"totalRequired === 0 && getSourceValue() && !expanded\">\u2713</span>\n </div>\n <div class=\"field-array-content\" *ngIf=\"expanded\">\n <!-- Source collection input -->\n <div class=\"field-row\">\n <div class=\"field-label\">\n <span class=\"field-name\">{{ 'mapCollectionTo' | pluginTranslate: pluginId | async }}</span>\n </div>\n <epistola-value-input\n [name]=\"'field_' + field.path\"\n [value]=\"getSourceValue()\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"onSourceValueChange($event)\"\n ></epistola-value-input>\n </div>\n\n <!-- Per-item field mapping toggle -->\n <div class=\"array-per-field-toggle\" *ngIf=\"hasArrayChildren()\">\n <label class=\"toggle-label\">\n <input\n type=\"checkbox\"\n [checked]=\"arrayPerFieldMode\"\n [disabled]=\"disabled\"\n (change)=\"toggleArrayPerFieldMode()\"\n />\n <span>{{ 'itemFieldMapping' | pluginTranslate: pluginId | async }}</span>\n </label>\n </div>\n\n <!-- Per-item field mappings -->\n <div class=\"array-item-fields\" *ngIf=\"arrayPerFieldMode && hasArrayChildren()\">\n <div class=\"item-fields-header\">\n <span class=\"item-fields-title\">{{ 'itemFieldMappingTitle' | pluginTranslate: pluginId | async }}</span>\n </div>\n <div class=\"item-field-row\" *ngFor=\"let child of field.children\">\n <div class=\"item-field-label\">\n <span class=\"field-name\">{{ child.name }}</span>\n <span class=\"field-meta\">({{ child.type }}{{ child.required ? ', required' : '' }})</span>\n </div>\n <div class=\"item-field-input\">\n <v-input\n [name]=\"'itemField_' + child.path\"\n [defaultValue]=\"getItemFieldValue(child.name)\"\n [disabled]=\"disabled\"\n [placeholder]=\"'sourceFieldPlaceholder' | pluginTranslate: pluginId | async\"\n (valueChange)=\"onItemFieldChange(child.name, $event)\"\n ></v-input>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".field-array{margin:.25rem 0}.field-array-header{display:flex;align-items:center;gap:.5rem;padding:.5rem .25rem;cursor:pointer;-webkit-user-select:none;user-select:none;border-left:3px solid transparent;border-radius:2px}.field-array-header:hover{background:#f4f4f4}.field-array-header.field-required-unmapped{border-left-color:#dc3545;background-color:#fff5f5}.field-array-header .expand-icon{flex:0 0 1rem;font-size:.75rem;color:#525252}.field-array-header .field-name{font-weight:500}.field-array-header .field-meta{font-size:.8125rem;color:#6c757d}.field-array-header .completeness-badge{margin-left:auto;font-size:.75rem;padding:.125rem .5rem;border-radius:10px;background:#e0e0e0;color:#525252;font-weight:500}.field-array-header .mapped-indicator{margin-left:auto;color:#198754;font-weight:600}.field-array-content{padding-left:1.25rem;border-left:1px solid #e0e0e0;margin-left:.5rem}.field-row{display:flex;align-items:flex-start;gap:.75rem;padding:.5rem 0}.field-label{flex:0 0 200px;min-width:140px;padding-top:.5rem;word-break:break-word}.field-label .field-name{font-weight:500}.array-per-field-toggle{padding:.5rem 0}.array-per-field-toggle .toggle-label{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.875rem;color:#525252}.array-per-field-toggle .toggle-label input[type=checkbox]{cursor:pointer}.array-item-fields{margin-left:.5rem;border-left:1px dashed #c6c6c6;padding:.25rem 0 .5rem 1rem}.item-fields-header{padding-bottom:.25rem}.item-fields-header .item-fields-title{font-size:.8125rem;font-weight:500;color:#6c757d}.item-field-row{display:flex;align-items:center;gap:.75rem;padding:.25rem 0}.item-field-label{flex:0 0 180px;min-width:120px;word-break:break-word}.item-field-label .field-name{font-weight:500;font-size:.875rem}.item-field-label .field-meta{font-size:.75rem;color:#6c757d;margin-left:.25rem}.item-field-input{flex:1;min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: PluginTranslatePipeModule }, { kind: "pipe", type: i2$1.PluginTranslatePipe, name: "pluginTranslate" }, { kind: "ngmodule", type: InputModule }, { kind: "component", type: i3.InputComponent, selector: "v-input", inputs: ["name", "type", "title", "titleTranslationKey", "defaultValue", "widthPx", "fullWidth", "margin", "smallMargin", "disabled", "step", "min", "maxLength", "tooltip", "required", "hideNumberSpinBox", "smallLabel", "rows", "clear$", "carbonTheme", "placeholder", "dataTestId", "trim", "presetsTitle", "presetOptions"], outputs: ["valueChange"] }, { kind: "component", type: ValueInputComponent, selector: "epistola-value-input", inputs: ["name", "value", "pluginId", "caseDefinitionKey", "processVariables", "disabled", "placeholder"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
489
|
+
}
|
|
490
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ArrayFieldComponent, decorators: [{
|
|
491
|
+
type: Component,
|
|
492
|
+
args: [{ selector: 'epistola-array-field', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, PluginTranslatePipeModule, InputModule, ValueInputComponent], template: "<div class=\"field-array\">\n <div class=\"field-array-header\" (click)=\"toggleExpanded()\" [class.field-required-unmapped]=\"field.required && !getSourceValue()\">\n <span class=\"expand-icon\">{{ expanded ? '\u25BC' : '\u25B6' }}</span>\n <span class=\"field-name\">{{ field.name }}</span>\n <span class=\"field-meta\">(array{{ field.required ? ', required' : '' }})</span>\n <span class=\"completeness-badge\" *ngIf=\"totalRequired > 0 && !expanded\">\n {{ mappedCount }}/{{ totalRequired }}\n </span>\n <span class=\"mapped-indicator\" *ngIf=\"totalRequired === 0 && getSourceValue() && !expanded\">\u2713</span>\n </div>\n <div class=\"field-array-content\" *ngIf=\"expanded\">\n <!-- Source collection input -->\n <div class=\"field-row\">\n <div class=\"field-label\">\n <span class=\"field-name\">{{ 'mapCollectionTo' | pluginTranslate: pluginId | async }}</span>\n </div>\n <epistola-value-input\n [name]=\"'field_' + field.path\"\n [value]=\"getSourceValue()\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"onSourceValueChange($event)\"\n ></epistola-value-input>\n </div>\n\n <!-- Per-item field mapping toggle -->\n <div class=\"array-per-field-toggle\" *ngIf=\"hasArrayChildren()\">\n <label class=\"toggle-label\">\n <input\n type=\"checkbox\"\n [checked]=\"arrayPerFieldMode\"\n [disabled]=\"disabled\"\n (change)=\"toggleArrayPerFieldMode()\"\n />\n <span>{{ 'itemFieldMapping' | pluginTranslate: pluginId | async }}</span>\n </label>\n </div>\n\n <!-- Per-item field mappings -->\n <div class=\"array-item-fields\" *ngIf=\"arrayPerFieldMode && hasArrayChildren()\">\n <div class=\"item-fields-header\">\n <span class=\"item-fields-title\">{{ 'itemFieldMappingTitle' | pluginTranslate: pluginId | async }}</span>\n </div>\n <div class=\"item-field-row\" *ngFor=\"let child of field.children\">\n <div class=\"item-field-label\">\n <span class=\"field-name\">{{ child.name }}</span>\n <span class=\"field-meta\">({{ child.type }}{{ child.required ? ', required' : '' }})</span>\n </div>\n <div class=\"item-field-input\">\n <v-input\n [name]=\"'itemField_' + child.path\"\n [defaultValue]=\"getItemFieldValue(child.name)\"\n [disabled]=\"disabled\"\n [placeholder]=\"'sourceFieldPlaceholder' | pluginTranslate: pluginId | async\"\n (valueChange)=\"onItemFieldChange(child.name, $event)\"\n ></v-input>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".field-array{margin:.25rem 0}.field-array-header{display:flex;align-items:center;gap:.5rem;padding:.5rem .25rem;cursor:pointer;-webkit-user-select:none;user-select:none;border-left:3px solid transparent;border-radius:2px}.field-array-header:hover{background:#f4f4f4}.field-array-header.field-required-unmapped{border-left-color:#dc3545;background-color:#fff5f5}.field-array-header .expand-icon{flex:0 0 1rem;font-size:.75rem;color:#525252}.field-array-header .field-name{font-weight:500}.field-array-header .field-meta{font-size:.8125rem;color:#6c757d}.field-array-header .completeness-badge{margin-left:auto;font-size:.75rem;padding:.125rem .5rem;border-radius:10px;background:#e0e0e0;color:#525252;font-weight:500}.field-array-header .mapped-indicator{margin-left:auto;color:#198754;font-weight:600}.field-array-content{padding-left:1.25rem;border-left:1px solid #e0e0e0;margin-left:.5rem}.field-row{display:flex;align-items:flex-start;gap:.75rem;padding:.5rem 0}.field-label{flex:0 0 200px;min-width:140px;padding-top:.5rem;word-break:break-word}.field-label .field-name{font-weight:500}.array-per-field-toggle{padding:.5rem 0}.array-per-field-toggle .toggle-label{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.875rem;color:#525252}.array-per-field-toggle .toggle-label input[type=checkbox]{cursor:pointer}.array-item-fields{margin-left:.5rem;border-left:1px dashed #c6c6c6;padding:.25rem 0 .5rem 1rem}.item-fields-header{padding-bottom:.25rem}.item-fields-header .item-fields-title{font-size:.8125rem;font-weight:500;color:#6c757d}.item-field-row{display:flex;align-items:center;gap:.75rem;padding:.25rem 0}.item-field-label{flex:0 0 180px;min-width:120px;word-break:break-word}.item-field-label .field-name{font-weight:500;font-size:.875rem}.item-field-label .field-meta{font-size:.75rem;color:#6c757d;margin-left:.25rem}.item-field-input{flex:1;min-width:0}\n"] }]
|
|
493
|
+
}], propDecorators: { field: [{
|
|
494
|
+
type: Input
|
|
495
|
+
}], value: [{
|
|
496
|
+
type: Input
|
|
497
|
+
}], pluginId: [{
|
|
498
|
+
type: Input
|
|
499
|
+
}], caseDefinitionKey: [{
|
|
500
|
+
type: Input
|
|
501
|
+
}], processVariables: [{
|
|
502
|
+
type: Input
|
|
503
|
+
}], disabled: [{
|
|
504
|
+
type: Input
|
|
505
|
+
}], valueChange: [{
|
|
506
|
+
type: Output
|
|
507
|
+
}] } });
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Recursive field tree component.
|
|
511
|
+
* Dispatches SCALAR and ARRAY to dedicated sub-components.
|
|
512
|
+
* Handles OBJECT inline to avoid circular import issues (OBJECT children recurse back to this component).
|
|
513
|
+
* Uses forwardRef(() => FieldTreeComponent) in imports to allow self-referencing in the template.
|
|
514
|
+
*/
|
|
515
|
+
class FieldTreeComponent {
|
|
516
|
+
field;
|
|
517
|
+
value = undefined;
|
|
518
|
+
pluginId;
|
|
519
|
+
caseDefinitionKey = null;
|
|
520
|
+
processVariables = [];
|
|
521
|
+
disabled = false;
|
|
522
|
+
valueChange = new EventEmitter();
|
|
523
|
+
// OBJECT-specific state
|
|
524
|
+
expanded = false;
|
|
525
|
+
mappedCount = 0;
|
|
526
|
+
totalRequired = 0;
|
|
527
|
+
ngOnChanges(changes) {
|
|
528
|
+
if (this.field?.fieldType === 'OBJECT' && (changes['value'] || changes['field'])) {
|
|
529
|
+
if (this.field.children) {
|
|
530
|
+
const stats = countRequiredMapped(this.field.children, this.value || {});
|
|
531
|
+
this.mappedCount = stats.mapped;
|
|
532
|
+
this.totalRequired = stats.total;
|
|
533
|
+
}
|
|
534
|
+
if (!this.expanded && this.totalRequired > 0 && this.mappedCount < this.totalRequired) {
|
|
535
|
+
this.expanded = true;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
454
538
|
}
|
|
455
|
-
|
|
456
|
-
|
|
539
|
+
toggleExpanded() {
|
|
540
|
+
this.expanded = !this.expanded;
|
|
541
|
+
}
|
|
542
|
+
onChildChange(childName, childValue) {
|
|
543
|
+
const current = (typeof this.value === 'object' && this.value !== null) ? { ...this.value } : {};
|
|
544
|
+
if (childValue === undefined || childValue === null || childValue === '') {
|
|
545
|
+
delete current[childName];
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
current[childName] = childValue;
|
|
549
|
+
}
|
|
550
|
+
this.valueChange.emit(Object.keys(current).length > 0 ? current : undefined);
|
|
551
|
+
}
|
|
552
|
+
getChildValue(childName) {
|
|
553
|
+
if (typeof this.value === 'object' && this.value !== null) {
|
|
554
|
+
return this.value[childName];
|
|
555
|
+
}
|
|
556
|
+
return undefined;
|
|
457
557
|
}
|
|
458
558
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: FieldTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
459
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: FieldTreeComponent, isStandalone: true, selector: "epistola-field-tree", inputs: { field: "field", value: "value", pluginId: "pluginId", caseDefinitionKey: "caseDefinitionKey", processVariables: "processVariables", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<!-- SCALAR field: label row + input with 3-mode selector -->\n<div *ngIf=\"field.fieldType === 'SCALAR'\" class=\"field-row\" [class.field-required-unmapped]=\"field.required && !getStringValue()\">\n <div class=\"field-label\">\n <span class=\"field-name\">{{ field.name }}</span>\n <span class=\"field-meta\">({{ field.type }}{{ field.required ? ', required' : '' }})</span>\n </div>\n <div class=\"field-input\">\n <div class=\"input-mode-group\">\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'browse'\" [disabled]=\"disabled\" (click)=\"setInputMode('browse')\" [title]=\"'browseMode' | pluginTranslate: pluginId | async\">\u229E</button>\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'pv'\" [disabled]=\"disabled\" (click)=\"setInputMode('pv')\" [title]=\"'pvMode' | pluginTranslate: pluginId | async\">pv</button>\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'expression'\" [disabled]=\"disabled\" (click)=\"setInputMode('expression')\" [title]=\"'expressionMode' | pluginTranslate: pluginId | async\">fx</button>\n </div>\n\n <!-- Browse mode: ValuePathSelector -->\n <div class=\"field-input-control\" *ngIf=\"inputMode === 'browse'\">\n <valtimo-value-path-selector\n [name]=\"'field_' + field.path\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [prefixes]=\"[ValuePathSelectorPrefix.DOC, ValuePathSelectorPrefix.CASE]\"\n [notation]=\"'dots'\"\n [disabled]=\"disabled\"\n [defaultValue]=\"getStringValue()\"\n [showCaseDefinitionSelector]=\"!caseDefinitionKey\"\n (valueChangeEvent)=\"onBrowseValueChange($event)\"\n ></valtimo-value-path-selector>\n </div>\n\n <!-- PV mode: dropdown (when available) or text fallback -->\n <div class=\"field-input-control\" *ngIf=\"inputMode === 'pv'\">\n <select\n *ngIf=\"processVariables.length > 0; else pvFallback\"\n class=\"pv-select\"\n [disabled]=\"disabled\"\n (change)=\"onPvChange($any($event.target).value)\"\n >\n <option value=\"\">{{ 'pvPlaceholder' | pluginTranslate: pluginId | async }}</option>\n <option *ngFor=\"let pv of processVariables\" [value]=\"pv\" [selected]=\"pv === getPvName()\">{{ pv }}</option>\n </select>\n <ng-template #pvFallback>\n <v-input\n [name]=\"'pvfb_' + field.path\"\n [defaultValue]=\"getPvName()\"\n [disabled]=\"disabled\"\n [placeholder]=\"'pvPlaceholder' | pluginTranslate: pluginId | async\"\n (valueChange)=\"onPvChange($event)\"\n ></v-input>\n </ng-template>\n </div>\n\n <!-- Expression mode: text input -->\n <div class=\"field-input-control\" *ngIf=\"inputMode === 'expression'\">\n <v-input\n [name]=\"'fx_' + field.path\"\n [defaultValue]=\"getStringValue()\"\n [disabled]=\"disabled\"\n [placeholder]=\"'e.g. pv:variableName or doc:path.to.field'\"\n (valueChange)=\"onExpressionValueChange($event)\"\n ></v-input>\n </div>\n </div>\n</div>\n\n<!-- OBJECT field: collapsible section with children -->\n<div *ngIf=\"field.fieldType === 'OBJECT'\" class=\"field-object\">\n <div class=\"field-object-header\" (click)=\"toggleExpanded()\" [class.field-required-unmapped]=\"totalRequired > 0 && mappedCount < totalRequired\">\n <span class=\"expand-icon\">{{ expanded ? '\u25BC' : '\u25B6' }}</span>\n <span class=\"field-name\">{{ field.name }}</span>\n <span class=\"field-meta\">(object{{ field.required ? ', required' : '' }})</span>\n <span class=\"completeness-badge\" *ngIf=\"totalRequired > 0 && !expanded\">\n {{ mappedCount }}/{{ totalRequired }}\n </span>\n </div>\n <div class=\"field-object-children\" *ngIf=\"expanded\">\n <epistola-field-tree\n *ngFor=\"let child of field.children\"\n [field]=\"child\"\n [value]=\"getChildValue(child.name)\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"onChildChange(child.name, $event)\"\n ></epistola-field-tree>\n </div>\n</div>\n\n<!-- ARRAY field: collapsible section with source collection + optional per-item mapping -->\n<div *ngIf=\"field.fieldType === 'ARRAY'\" class=\"field-array\">\n <div class=\"field-array-header\" (click)=\"toggleExpanded()\" [class.field-required-unmapped]=\"field.required && !getSourceValue()\">\n <span class=\"expand-icon\">{{ expanded ? '\u25BC' : '\u25B6' }}</span>\n <span class=\"field-name\">{{ field.name }}</span>\n <span class=\"field-meta\">(array{{ field.required ? ', required' : '' }})</span>\n <span class=\"completeness-badge\" *ngIf=\"totalRequired > 0 && !expanded\">\n {{ mappedCount }}/{{ totalRequired }}\n </span>\n <span class=\"mapped-indicator\" *ngIf=\"totalRequired === 0 && getSourceValue() && !expanded\">\u2713</span>\n </div>\n <div class=\"field-array-content\" *ngIf=\"expanded\">\n <!-- Source collection input -->\n <div class=\"field-row\">\n <div class=\"field-label\">\n <span class=\"field-name\">{{ 'mapCollectionTo' | pluginTranslate: pluginId | async }}</span>\n </div>\n <div class=\"field-input\">\n <div class=\"input-mode-group\">\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'browse'\" [disabled]=\"disabled\" (click)=\"setInputMode('browse')\" [title]=\"'browseMode' | pluginTranslate: pluginId | async\">\u229E</button>\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'pv'\" [disabled]=\"disabled\" (click)=\"setInputMode('pv')\" [title]=\"'pvMode' | pluginTranslate: pluginId | async\">pv</button>\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'expression'\" [disabled]=\"disabled\" (click)=\"setInputMode('expression')\" [title]=\"'expressionMode' | pluginTranslate: pluginId | async\">fx</button>\n </div>\n\n <div class=\"field-input-control\" *ngIf=\"inputMode === 'browse'\">\n <valtimo-value-path-selector\n [name]=\"'field_' + field.path\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [prefixes]=\"[ValuePathSelectorPrefix.DOC, ValuePathSelectorPrefix.CASE]\"\n [notation]=\"'dots'\"\n [disabled]=\"disabled\"\n [defaultValue]=\"getSourceValue()\"\n [showCaseDefinitionSelector]=\"!caseDefinitionKey\"\n (valueChangeEvent)=\"onSourceBrowseChange($event)\"\n ></valtimo-value-path-selector>\n </div>\n\n <div class=\"field-input-control\" *ngIf=\"inputMode === 'pv'\">\n <select\n *ngIf=\"processVariables.length > 0; else pvSourceFallback\"\n class=\"pv-select\"\n [disabled]=\"disabled\"\n (change)=\"onSourcePvChange($any($event.target).value)\"\n >\n <option value=\"\">{{ 'pvPlaceholder' | pluginTranslate: pluginId | async }}</option>\n <option *ngFor=\"let pv of processVariables\" [value]=\"pv\" [selected]=\"pv === getSourcePvName()\">{{ pv }}</option>\n </select>\n <ng-template #pvSourceFallback>\n <v-input\n [name]=\"'pvfb_' + field.path\"\n [defaultValue]=\"getSourcePvName()\"\n [disabled]=\"disabled\"\n [placeholder]=\"'pvPlaceholder' | pluginTranslate: pluginId | async\"\n (valueChange)=\"onSourcePvChange($event)\"\n ></v-input>\n </ng-template>\n </div>\n\n <div class=\"field-input-control\" *ngIf=\"inputMode === 'expression'\">\n <v-input\n [name]=\"'fx_' + field.path\"\n [defaultValue]=\"getSourceValue()\"\n [disabled]=\"disabled\"\n [placeholder]=\"'e.g. pv:variableName or doc:path.to.field'\"\n (valueChange)=\"onSourceExpressionChange($event)\"\n ></v-input>\n </div>\n </div>\n </div>\n\n <!-- Per-item field mapping toggle (only shown when array has children) -->\n <div class=\"array-per-field-toggle\" *ngIf=\"hasArrayChildren()\">\n <label class=\"toggle-label\">\n <input\n type=\"checkbox\"\n [checked]=\"arrayPerFieldMode\"\n [disabled]=\"disabled\"\n (change)=\"toggleArrayPerFieldMode()\"\n />\n <span>{{ 'itemFieldMapping' | pluginTranslate: pluginId | async }}</span>\n </label>\n </div>\n\n <!-- Per-item field mappings -->\n <div class=\"array-item-fields\" *ngIf=\"arrayPerFieldMode && hasArrayChildren()\">\n <div class=\"item-fields-header\">\n <span class=\"item-fields-title\">{{ 'itemFieldMappingTitle' | pluginTranslate: pluginId | async }}</span>\n </div>\n <div class=\"item-field-row\" *ngFor=\"let child of field.children\">\n <div class=\"item-field-label\">\n <span class=\"field-name\">{{ child.name }}</span>\n <span class=\"field-meta\">({{ child.type }}{{ child.required ? ', required' : '' }})</span>\n </div>\n <div class=\"item-field-input\">\n <v-input\n [name]=\"'itemField_' + child.path\"\n [defaultValue]=\"getItemFieldValue(child.name)\"\n [disabled]=\"disabled\"\n [placeholder]=\"'sourceFieldPlaceholder' | pluginTranslate: pluginId | async\"\n (valueChange)=\"onItemFieldChange(child.name, $event)\"\n ></v-input>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".field-row{display:flex;align-items:flex-start;gap:.75rem;padding:.5rem 0;border-left:3px solid transparent}.field-row.field-required-unmapped{border-left-color:#dc3545;background-color:#fff5f5;padding-left:.5rem}.field-label{flex:0 0 200px;min-width:140px;padding-top:.5rem;word-break:break-word}.field-label .field-name{font-weight:500}.field-label .field-meta{font-size:.8125rem;color:#6c757d;margin-left:.25rem}.field-input{flex:1;display:flex;align-items:flex-start;gap:.5rem;min-width:0}.field-input-control{flex:1;min-width:0}.input-mode-group{flex:0 0 auto;display:flex;margin-top:.25rem;border:1px solid #c6c6c6;border-radius:4px;overflow:hidden}.input-mode-group .mode-btn{width:32px;height:32px;padding:0;border:none;border-right:1px solid #c6c6c6;background:#f4f4f4;color:#525252;font-size:.75rem;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background-color .15s}.input-mode-group .mode-btn:last-child{border-right:none}.input-mode-group .mode-btn:hover:not(:disabled){background:#e0e0e0}.input-mode-group .mode-btn.mode-active{background:#0f62fe;color:#fff}.input-mode-group .mode-btn.mode-active:hover:not(:disabled){background:#0353e9}.input-mode-group .mode-btn:disabled{opacity:.5;cursor:not-allowed}.pv-select{width:100%;height:2.5rem;padding:0 .75rem;border:1px solid #c6c6c6;border-radius:4px;background:#fff;color:#161616;font-size:.875rem;cursor:pointer}.pv-select:focus{outline:2px solid #0f62fe;outline-offset:-2px}.pv-select:disabled{opacity:.5;cursor:not-allowed;background:#f4f4f4}.field-object{margin:.25rem 0}.field-object-header,.field-array-header{display:flex;align-items:center;gap:.5rem;padding:.5rem .25rem;cursor:pointer;-webkit-user-select:none;user-select:none;border-left:3px solid transparent;border-radius:2px}.field-object-header:hover,.field-array-header:hover{background:#f4f4f4}.field-object-header.field-required-unmapped,.field-array-header.field-required-unmapped{border-left-color:#dc3545;background-color:#fff5f5}.field-object-header .expand-icon,.field-array-header .expand-icon{flex:0 0 1rem;font-size:.75rem;color:#525252}.field-object-header .field-name,.field-array-header .field-name{font-weight:500}.field-object-header .field-meta,.field-array-header .field-meta{font-size:.8125rem;color:#6c757d}.field-object-header .completeness-badge,.field-array-header .completeness-badge{margin-left:auto;font-size:.75rem;padding:.125rem .5rem;border-radius:10px;background:#e0e0e0;color:#525252;font-weight:500}.field-object-header .mapped-indicator,.field-array-header .mapped-indicator{margin-left:auto;color:#198754;font-weight:600}.field-object-children{padding-left:1.25rem;border-left:1px solid #e0e0e0;margin-left:.5rem}.field-array{margin:.25rem 0}.field-array-content{padding-left:1.25rem;border-left:1px solid #e0e0e0;margin-left:.5rem}.array-per-field-toggle{padding:.5rem 0}.array-per-field-toggle .toggle-label{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.875rem;color:#525252}.array-per-field-toggle .toggle-label input[type=checkbox]{cursor:pointer}.array-item-fields{margin-left:.5rem;border-left:1px dashed #c6c6c6;padding:.25rem 0 .5rem 1rem}.item-fields-header{padding-bottom:.25rem}.item-fields-header .item-fields-title{font-size:.8125rem;font-weight:500;color:#6c757d}.item-field-row{display:flex;align-items:center;gap:.75rem;padding:.25rem 0}.item-field-label{flex:0 0 180px;min-width:120px;word-break:break-word}.item-field-label .field-name{font-weight:500;font-size:.875rem}.item-field-label .field-meta{font-size:.75rem;color:#6c757d;margin-left:.25rem}.item-field-input{flex:1;min-width:0}\n"], dependencies: [{ kind: "component", type: FieldTreeComponent, selector: "epistola-field-tree", inputs: ["field", "value", "pluginId", "caseDefinitionKey", "processVariables", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "ngmodule", type: PluginTranslatePipeModule }, { kind: "pipe", type: i2$1.PluginTranslatePipe, name: "pluginTranslate" }, { kind: "ngmodule", type: InputModule }, { kind: "component", type: i3.InputComponent, selector: "v-input", inputs: ["name", "type", "title", "titleTranslationKey", "defaultValue", "widthPx", "fullWidth", "margin", "smallMargin", "disabled", "step", "min", "maxLength", "tooltip", "required", "hideNumberSpinBox", "smallLabel", "rows", "clear$", "carbonTheme", "placeholder", "dataTestId", "trim", "presetsTitle", "presetOptions"], outputs: ["valueChange"] }, { kind: "component", type: ValuePathSelectorComponent, selector: "valtimo-value-path-selector", inputs: ["name", "appendInline", "margin", "marginLg", "marginXl", "disabled", "caseDefinitionKey", "caseDefinitionVersionTag", "buildingBlockDefinitionKey", "buildingBlockDefinitionVersionTag", "prefixes", "label", "tooltip", "required", "showCaseDefinitionSelector", "notation", "dropUp", "defaultValue", "type", "parentItem", "filterItems"], outputs: ["valueChangeEvent", "collectionSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
559
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.20", type: FieldTreeComponent, isStandalone: true, selector: "epistola-field-tree", inputs: { field: "field", value: "value", pluginId: "pluginId", caseDefinitionKey: "caseDefinitionKey", processVariables: "processVariables", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "@switch (field.fieldType) {\n @case ('SCALAR') {\n <epistola-scalar-field\n [field]=\"field\"\n [value]=\"value\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"valueChange.emit($event)\"\n ></epistola-scalar-field>\n }\n @case ('OBJECT') {\n <div class=\"field-object\">\n <div class=\"field-object-header\" (click)=\"toggleExpanded()\" [class.field-required-unmapped]=\"totalRequired > 0 && mappedCount < totalRequired\">\n <span class=\"expand-icon\">{{ expanded ? '\u25BC' : '\u25B6' }}</span>\n <span class=\"field-name\">{{ field.name }}</span>\n <span class=\"field-meta\">(object{{ field.required ? ', required' : '' }})</span>\n <span class=\"completeness-badge\" *ngIf=\"totalRequired > 0 && !expanded\">\n {{ mappedCount }}/{{ totalRequired }}\n </span>\n </div>\n <div class=\"field-object-children\" *ngIf=\"expanded\">\n <epistola-field-tree\n *ngFor=\"let child of field.children\"\n [field]=\"child\"\n [value]=\"getChildValue(child.name)\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"onChildChange(child.name, $event)\"\n ></epistola-field-tree>\n </div>\n </div>\n }\n @case ('ARRAY') {\n <epistola-array-field\n [field]=\"field\"\n [value]=\"value\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"valueChange.emit($event)\"\n ></epistola-array-field>\n }\n}\n", styles: [".field-object{margin:.25rem 0}.field-object-header{display:flex;align-items:center;gap:.5rem;padding:.5rem .25rem;cursor:pointer;-webkit-user-select:none;user-select:none;border-left:3px solid transparent;border-radius:2px}.field-object-header:hover{background:#f4f4f4}.field-object-header.field-required-unmapped{border-left-color:#dc3545;background-color:#fff5f5}.field-object-header .expand-icon{flex:0 0 1rem;font-size:.75rem;color:#525252}.field-object-header .field-name{font-weight:500}.field-object-header .field-meta{font-size:.8125rem;color:#6c757d}.field-object-header .completeness-badge{margin-left:auto;font-size:.75rem;padding:.125rem .5rem;border-radius:10px;background:#e0e0e0;color:#525252;font-weight:500}.field-object-children{padding-left:1.25rem;border-left:1px solid #e0e0e0;margin-left:.5rem}\n"], dependencies: [{ kind: "component", type: i0.forwardRef(() => FieldTreeComponent), selector: "epistola-field-tree", inputs: ["field", "value", "pluginId", "caseDefinitionKey", "processVariables", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: i0.forwardRef(() => CommonModule) }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgForOf), selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgIf), selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: i0.forwardRef(() => PluginTranslatePipeModule) }, { kind: "component", type: i0.forwardRef(() => ScalarFieldComponent), selector: "epistola-scalar-field", inputs: ["field", "value", "pluginId", "caseDefinitionKey", "processVariables", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: i0.forwardRef(() => ArrayFieldComponent), selector: "epistola-array-field", inputs: ["field", "value", "pluginId", "caseDefinitionKey", "processVariables", "disabled"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
460
560
|
}
|
|
461
561
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: FieldTreeComponent, decorators: [{
|
|
462
562
|
type: Component,
|
|
463
|
-
args: [{ selector: 'epistola-field-tree', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
464
|
-
CommonModule,
|
|
465
|
-
FormsModule,
|
|
466
|
-
PluginTranslatePipeModule,
|
|
467
|
-
InputModule,
|
|
468
|
-
ValuePathSelectorComponent
|
|
469
|
-
], template: "<!-- SCALAR field: label row + input with 3-mode selector -->\n<div *ngIf=\"field.fieldType === 'SCALAR'\" class=\"field-row\" [class.field-required-unmapped]=\"field.required && !getStringValue()\">\n <div class=\"field-label\">\n <span class=\"field-name\">{{ field.name }}</span>\n <span class=\"field-meta\">({{ field.type }}{{ field.required ? ', required' : '' }})</span>\n </div>\n <div class=\"field-input\">\n <div class=\"input-mode-group\">\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'browse'\" [disabled]=\"disabled\" (click)=\"setInputMode('browse')\" [title]=\"'browseMode' | pluginTranslate: pluginId | async\">\u229E</button>\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'pv'\" [disabled]=\"disabled\" (click)=\"setInputMode('pv')\" [title]=\"'pvMode' | pluginTranslate: pluginId | async\">pv</button>\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'expression'\" [disabled]=\"disabled\" (click)=\"setInputMode('expression')\" [title]=\"'expressionMode' | pluginTranslate: pluginId | async\">fx</button>\n </div>\n\n <!-- Browse mode: ValuePathSelector -->\n <div class=\"field-input-control\" *ngIf=\"inputMode === 'browse'\">\n <valtimo-value-path-selector\n [name]=\"'field_' + field.path\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [prefixes]=\"[ValuePathSelectorPrefix.DOC, ValuePathSelectorPrefix.CASE]\"\n [notation]=\"'dots'\"\n [disabled]=\"disabled\"\n [defaultValue]=\"getStringValue()\"\n [showCaseDefinitionSelector]=\"!caseDefinitionKey\"\n (valueChangeEvent)=\"onBrowseValueChange($event)\"\n ></valtimo-value-path-selector>\n </div>\n\n <!-- PV mode: dropdown (when available) or text fallback -->\n <div class=\"field-input-control\" *ngIf=\"inputMode === 'pv'\">\n <select\n *ngIf=\"processVariables.length > 0; else pvFallback\"\n class=\"pv-select\"\n [disabled]=\"disabled\"\n (change)=\"onPvChange($any($event.target).value)\"\n >\n <option value=\"\">{{ 'pvPlaceholder' | pluginTranslate: pluginId | async }}</option>\n <option *ngFor=\"let pv of processVariables\" [value]=\"pv\" [selected]=\"pv === getPvName()\">{{ pv }}</option>\n </select>\n <ng-template #pvFallback>\n <v-input\n [name]=\"'pvfb_' + field.path\"\n [defaultValue]=\"getPvName()\"\n [disabled]=\"disabled\"\n [placeholder]=\"'pvPlaceholder' | pluginTranslate: pluginId | async\"\n (valueChange)=\"onPvChange($event)\"\n ></v-input>\n </ng-template>\n </div>\n\n <!-- Expression mode: text input -->\n <div class=\"field-input-control\" *ngIf=\"inputMode === 'expression'\">\n <v-input\n [name]=\"'fx_' + field.path\"\n [defaultValue]=\"getStringValue()\"\n [disabled]=\"disabled\"\n [placeholder]=\"'e.g. pv:variableName or doc:path.to.field'\"\n (valueChange)=\"onExpressionValueChange($event)\"\n ></v-input>\n </div>\n </div>\n</div>\n\n<!-- OBJECT field: collapsible section with children -->\n<div *ngIf=\"field.fieldType === 'OBJECT'\" class=\"field-object\">\n <div class=\"field-object-header\" (click)=\"toggleExpanded()\" [class.field-required-unmapped]=\"totalRequired > 0 && mappedCount < totalRequired\">\n <span class=\"expand-icon\">{{ expanded ? '\u25BC' : '\u25B6' }}</span>\n <span class=\"field-name\">{{ field.name }}</span>\n <span class=\"field-meta\">(object{{ field.required ? ', required' : '' }})</span>\n <span class=\"completeness-badge\" *ngIf=\"totalRequired > 0 && !expanded\">\n {{ mappedCount }}/{{ totalRequired }}\n </span>\n </div>\n <div class=\"field-object-children\" *ngIf=\"expanded\">\n <epistola-field-tree\n *ngFor=\"let child of field.children\"\n [field]=\"child\"\n [value]=\"getChildValue(child.name)\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"onChildChange(child.name, $event)\"\n ></epistola-field-tree>\n </div>\n</div>\n\n<!-- ARRAY field: collapsible section with source collection + optional per-item mapping -->\n<div *ngIf=\"field.fieldType === 'ARRAY'\" class=\"field-array\">\n <div class=\"field-array-header\" (click)=\"toggleExpanded()\" [class.field-required-unmapped]=\"field.required && !getSourceValue()\">\n <span class=\"expand-icon\">{{ expanded ? '\u25BC' : '\u25B6' }}</span>\n <span class=\"field-name\">{{ field.name }}</span>\n <span class=\"field-meta\">(array{{ field.required ? ', required' : '' }})</span>\n <span class=\"completeness-badge\" *ngIf=\"totalRequired > 0 && !expanded\">\n {{ mappedCount }}/{{ totalRequired }}\n </span>\n <span class=\"mapped-indicator\" *ngIf=\"totalRequired === 0 && getSourceValue() && !expanded\">\u2713</span>\n </div>\n <div class=\"field-array-content\" *ngIf=\"expanded\">\n <!-- Source collection input -->\n <div class=\"field-row\">\n <div class=\"field-label\">\n <span class=\"field-name\">{{ 'mapCollectionTo' | pluginTranslate: pluginId | async }}</span>\n </div>\n <div class=\"field-input\">\n <div class=\"input-mode-group\">\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'browse'\" [disabled]=\"disabled\" (click)=\"setInputMode('browse')\" [title]=\"'browseMode' | pluginTranslate: pluginId | async\">\u229E</button>\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'pv'\" [disabled]=\"disabled\" (click)=\"setInputMode('pv')\" [title]=\"'pvMode' | pluginTranslate: pluginId | async\">pv</button>\n <button type=\"button\" class=\"mode-btn\" [class.mode-active]=\"inputMode === 'expression'\" [disabled]=\"disabled\" (click)=\"setInputMode('expression')\" [title]=\"'expressionMode' | pluginTranslate: pluginId | async\">fx</button>\n </div>\n\n <div class=\"field-input-control\" *ngIf=\"inputMode === 'browse'\">\n <valtimo-value-path-selector\n [name]=\"'field_' + field.path\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [prefixes]=\"[ValuePathSelectorPrefix.DOC, ValuePathSelectorPrefix.CASE]\"\n [notation]=\"'dots'\"\n [disabled]=\"disabled\"\n [defaultValue]=\"getSourceValue()\"\n [showCaseDefinitionSelector]=\"!caseDefinitionKey\"\n (valueChangeEvent)=\"onSourceBrowseChange($event)\"\n ></valtimo-value-path-selector>\n </div>\n\n <div class=\"field-input-control\" *ngIf=\"inputMode === 'pv'\">\n <select\n *ngIf=\"processVariables.length > 0; else pvSourceFallback\"\n class=\"pv-select\"\n [disabled]=\"disabled\"\n (change)=\"onSourcePvChange($any($event.target).value)\"\n >\n <option value=\"\">{{ 'pvPlaceholder' | pluginTranslate: pluginId | async }}</option>\n <option *ngFor=\"let pv of processVariables\" [value]=\"pv\" [selected]=\"pv === getSourcePvName()\">{{ pv }}</option>\n </select>\n <ng-template #pvSourceFallback>\n <v-input\n [name]=\"'pvfb_' + field.path\"\n [defaultValue]=\"getSourcePvName()\"\n [disabled]=\"disabled\"\n [placeholder]=\"'pvPlaceholder' | pluginTranslate: pluginId | async\"\n (valueChange)=\"onSourcePvChange($event)\"\n ></v-input>\n </ng-template>\n </div>\n\n <div class=\"field-input-control\" *ngIf=\"inputMode === 'expression'\">\n <v-input\n [name]=\"'fx_' + field.path\"\n [defaultValue]=\"getSourceValue()\"\n [disabled]=\"disabled\"\n [placeholder]=\"'e.g. pv:variableName or doc:path.to.field'\"\n (valueChange)=\"onSourceExpressionChange($event)\"\n ></v-input>\n </div>\n </div>\n </div>\n\n <!-- Per-item field mapping toggle (only shown when array has children) -->\n <div class=\"array-per-field-toggle\" *ngIf=\"hasArrayChildren()\">\n <label class=\"toggle-label\">\n <input\n type=\"checkbox\"\n [checked]=\"arrayPerFieldMode\"\n [disabled]=\"disabled\"\n (change)=\"toggleArrayPerFieldMode()\"\n />\n <span>{{ 'itemFieldMapping' | pluginTranslate: pluginId | async }}</span>\n </label>\n </div>\n\n <!-- Per-item field mappings -->\n <div class=\"array-item-fields\" *ngIf=\"arrayPerFieldMode && hasArrayChildren()\">\n <div class=\"item-fields-header\">\n <span class=\"item-fields-title\">{{ 'itemFieldMappingTitle' | pluginTranslate: pluginId | async }}</span>\n </div>\n <div class=\"item-field-row\" *ngFor=\"let child of field.children\">\n <div class=\"item-field-label\">\n <span class=\"field-name\">{{ child.name }}</span>\n <span class=\"field-meta\">({{ child.type }}{{ child.required ? ', required' : '' }})</span>\n </div>\n <div class=\"item-field-input\">\n <v-input\n [name]=\"'itemField_' + child.path\"\n [defaultValue]=\"getItemFieldValue(child.name)\"\n [disabled]=\"disabled\"\n [placeholder]=\"'sourceFieldPlaceholder' | pluginTranslate: pluginId | async\"\n (valueChange)=\"onItemFieldChange(child.name, $event)\"\n ></v-input>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".field-row{display:flex;align-items:flex-start;gap:.75rem;padding:.5rem 0;border-left:3px solid transparent}.field-row.field-required-unmapped{border-left-color:#dc3545;background-color:#fff5f5;padding-left:.5rem}.field-label{flex:0 0 200px;min-width:140px;padding-top:.5rem;word-break:break-word}.field-label .field-name{font-weight:500}.field-label .field-meta{font-size:.8125rem;color:#6c757d;margin-left:.25rem}.field-input{flex:1;display:flex;align-items:flex-start;gap:.5rem;min-width:0}.field-input-control{flex:1;min-width:0}.input-mode-group{flex:0 0 auto;display:flex;margin-top:.25rem;border:1px solid #c6c6c6;border-radius:4px;overflow:hidden}.input-mode-group .mode-btn{width:32px;height:32px;padding:0;border:none;border-right:1px solid #c6c6c6;background:#f4f4f4;color:#525252;font-size:.75rem;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background-color .15s}.input-mode-group .mode-btn:last-child{border-right:none}.input-mode-group .mode-btn:hover:not(:disabled){background:#e0e0e0}.input-mode-group .mode-btn.mode-active{background:#0f62fe;color:#fff}.input-mode-group .mode-btn.mode-active:hover:not(:disabled){background:#0353e9}.input-mode-group .mode-btn:disabled{opacity:.5;cursor:not-allowed}.pv-select{width:100%;height:2.5rem;padding:0 .75rem;border:1px solid #c6c6c6;border-radius:4px;background:#fff;color:#161616;font-size:.875rem;cursor:pointer}.pv-select:focus{outline:2px solid #0f62fe;outline-offset:-2px}.pv-select:disabled{opacity:.5;cursor:not-allowed;background:#f4f4f4}.field-object{margin:.25rem 0}.field-object-header,.field-array-header{display:flex;align-items:center;gap:.5rem;padding:.5rem .25rem;cursor:pointer;-webkit-user-select:none;user-select:none;border-left:3px solid transparent;border-radius:2px}.field-object-header:hover,.field-array-header:hover{background:#f4f4f4}.field-object-header.field-required-unmapped,.field-array-header.field-required-unmapped{border-left-color:#dc3545;background-color:#fff5f5}.field-object-header .expand-icon,.field-array-header .expand-icon{flex:0 0 1rem;font-size:.75rem;color:#525252}.field-object-header .field-name,.field-array-header .field-name{font-weight:500}.field-object-header .field-meta,.field-array-header .field-meta{font-size:.8125rem;color:#6c757d}.field-object-header .completeness-badge,.field-array-header .completeness-badge{margin-left:auto;font-size:.75rem;padding:.125rem .5rem;border-radius:10px;background:#e0e0e0;color:#525252;font-weight:500}.field-object-header .mapped-indicator,.field-array-header .mapped-indicator{margin-left:auto;color:#198754;font-weight:600}.field-object-children{padding-left:1.25rem;border-left:1px solid #e0e0e0;margin-left:.5rem}.field-array{margin:.25rem 0}.field-array-content{padding-left:1.25rem;border-left:1px solid #e0e0e0;margin-left:.5rem}.array-per-field-toggle{padding:.5rem 0}.array-per-field-toggle .toggle-label{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.875rem;color:#525252}.array-per-field-toggle .toggle-label input[type=checkbox]{cursor:pointer}.array-item-fields{margin-left:.5rem;border-left:1px dashed #c6c6c6;padding:.25rem 0 .5rem 1rem}.item-fields-header{padding-bottom:.25rem}.item-fields-header .item-fields-title{font-size:.8125rem;font-weight:500;color:#6c757d}.item-field-row{display:flex;align-items:center;gap:.75rem;padding:.25rem 0}.item-field-label{flex:0 0 180px;min-width:120px;word-break:break-word}.item-field-label .field-name{font-weight:500;font-size:.875rem}.item-field-label .field-meta{font-size:.75rem;color:#6c757d;margin-left:.25rem}.item-field-input{flex:1;min-width:0}\n"] }]
|
|
563
|
+
args: [{ selector: 'epistola-field-tree', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, PluginTranslatePipeModule, ScalarFieldComponent, ArrayFieldComponent, forwardRef(() => FieldTreeComponent)], template: "@switch (field.fieldType) {\n @case ('SCALAR') {\n <epistola-scalar-field\n [field]=\"field\"\n [value]=\"value\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"valueChange.emit($event)\"\n ></epistola-scalar-field>\n }\n @case ('OBJECT') {\n <div class=\"field-object\">\n <div class=\"field-object-header\" (click)=\"toggleExpanded()\" [class.field-required-unmapped]=\"totalRequired > 0 && mappedCount < totalRequired\">\n <span class=\"expand-icon\">{{ expanded ? '\u25BC' : '\u25B6' }}</span>\n <span class=\"field-name\">{{ field.name }}</span>\n <span class=\"field-meta\">(object{{ field.required ? ', required' : '' }})</span>\n <span class=\"completeness-badge\" *ngIf=\"totalRequired > 0 && !expanded\">\n {{ mappedCount }}/{{ totalRequired }}\n </span>\n </div>\n <div class=\"field-object-children\" *ngIf=\"expanded\">\n <epistola-field-tree\n *ngFor=\"let child of field.children\"\n [field]=\"child\"\n [value]=\"getChildValue(child.name)\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"onChildChange(child.name, $event)\"\n ></epistola-field-tree>\n </div>\n </div>\n }\n @case ('ARRAY') {\n <epistola-array-field\n [field]=\"field\"\n [value]=\"value\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"valueChange.emit($event)\"\n ></epistola-array-field>\n }\n}\n", styles: [".field-object{margin:.25rem 0}.field-object-header{display:flex;align-items:center;gap:.5rem;padding:.5rem .25rem;cursor:pointer;-webkit-user-select:none;user-select:none;border-left:3px solid transparent;border-radius:2px}.field-object-header:hover{background:#f4f4f4}.field-object-header.field-required-unmapped{border-left-color:#dc3545;background-color:#fff5f5}.field-object-header .expand-icon{flex:0 0 1rem;font-size:.75rem;color:#525252}.field-object-header .field-name{font-weight:500}.field-object-header .field-meta{font-size:.8125rem;color:#6c757d}.field-object-header .completeness-badge{margin-left:auto;font-size:.75rem;padding:.125rem .5rem;border-radius:10px;background:#e0e0e0;color:#525252;font-weight:500}.field-object-children{padding-left:1.25rem;border-left:1px solid #e0e0e0;margin-left:.5rem}\n"] }]
|
|
470
564
|
}], propDecorators: { field: [{
|
|
471
565
|
type: Input
|
|
472
566
|
}], value: [{
|
|
@@ -489,35 +583,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
489
583
|
*/
|
|
490
584
|
class DataMappingTreeComponent {
|
|
491
585
|
pluginId;
|
|
492
|
-
templateFields
|
|
493
|
-
prefillMapping
|
|
494
|
-
disabled
|
|
586
|
+
templateFields = [];
|
|
587
|
+
prefillMapping = {};
|
|
588
|
+
disabled = false;
|
|
495
589
|
caseDefinitionKey = null;
|
|
496
590
|
processVariables = [];
|
|
497
591
|
mappingChange = new EventEmitter();
|
|
498
592
|
requiredFieldsStatus = new EventEmitter();
|
|
499
|
-
templateFields = [];
|
|
500
593
|
mapping = {};
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
this.templateFields$.pipe(takeUntil(this.destroy$)).subscribe(fields => {
|
|
505
|
-
this.templateFields = fields;
|
|
506
|
-
this.emitRequiredFieldsStatus();
|
|
507
|
-
});
|
|
508
|
-
this.prefillMapping$.pipe(takeUntil(this.destroy$)).subscribe(mapping => {
|
|
594
|
+
ngOnChanges(changes) {
|
|
595
|
+
if (changes['prefillMapping']) {
|
|
596
|
+
const mapping = this.prefillMapping;
|
|
509
597
|
if (mapping && Object.keys(mapping).length > 0) {
|
|
510
598
|
this.mapping = { ...mapping };
|
|
511
599
|
}
|
|
600
|
+
}
|
|
601
|
+
if (changes['templateFields'] || changes['prefillMapping']) {
|
|
512
602
|
this.emitRequiredFieldsStatus();
|
|
513
|
-
}
|
|
514
|
-
this.disabled$.pipe(takeUntil(this.destroy$)).subscribe(disabled => {
|
|
515
|
-
this.disabled = disabled;
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
ngOnDestroy() {
|
|
519
|
-
this.destroy$.next();
|
|
520
|
-
this.destroy$.complete();
|
|
603
|
+
}
|
|
521
604
|
}
|
|
522
605
|
onFieldValueChange(fieldName, value) {
|
|
523
606
|
if (value === undefined || value === null || value === '') {
|
|
@@ -538,7 +621,7 @@ class DataMappingTreeComponent {
|
|
|
538
621
|
this.requiredFieldsStatus.emit(stats);
|
|
539
622
|
}
|
|
540
623
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DataMappingTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
541
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: DataMappingTreeComponent, isStandalone: true, selector: "epistola-data-mapping-tree", inputs: { pluginId: "pluginId", templateFields
|
|
624
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: DataMappingTreeComponent, isStandalone: true, selector: "epistola-data-mapping-tree", inputs: { pluginId: "pluginId", templateFields: "templateFields", prefillMapping: "prefillMapping", disabled: "disabled", caseDefinitionKey: "caseDefinitionKey", processVariables: "processVariables" }, outputs: { mappingChange: "mappingChange", requiredFieldsStatus: "requiredFieldsStatus" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"data-mapping-tree\">\n <div class=\"mapping-header\">\n <h5>{{ 'dataMappingTitle' | pluginTranslate: pluginId | async }}</h5>\n <p class=\"helper-text\">{{ 'dataMappingDescription' | pluginTranslate: pluginId | async }}</p>\n </div>\n\n <div class=\"field-tree-root\" *ngIf=\"templateFields.length > 0\">\n <epistola-field-tree\n *ngFor=\"let field of templateFields\"\n [field]=\"field\"\n [value]=\"getFieldValue(field.name)\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"onFieldValueChange(field.name, $event)\"\n ></epistola-field-tree>\n </div>\n\n <div class=\"no-fields\" *ngIf=\"templateFields.length === 0\">\n <p>{{ 'noTemplateFields' | pluginTranslate: pluginId | async }}</p>\n </div>\n</div>\n", styles: [".data-mapping-tree{margin-top:1rem;margin-bottom:1rem}.mapping-header{margin-bottom:.75rem}.mapping-header h5{margin-bottom:.25rem;font-weight:600}.mapping-header .helper-text{color:#6c757d;font-size:.875rem;margin-bottom:0}.field-tree-root{border:1px solid #e0e0e0;border-radius:4px;padding:.5rem .75rem}.no-fields{padding:1rem;text-align:center;color:#6c757d;background-color:#f8f9fa;border:1px solid #dee2e6;border-radius:4px}.no-fields p{margin-bottom:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: PluginTranslatePipeModule }, { kind: "pipe", type: i2$1.PluginTranslatePipe, name: "pluginTranslate" }, { kind: "component", type: FieldTreeComponent, selector: "epistola-field-tree", inputs: ["field", "value", "pluginId", "caseDefinitionKey", "processVariables", "disabled"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
542
625
|
}
|
|
543
626
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DataMappingTreeComponent, decorators: [{
|
|
544
627
|
type: Component,
|
|
@@ -549,11 +632,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
549
632
|
], template: "<div class=\"data-mapping-tree\">\n <div class=\"mapping-header\">\n <h5>{{ 'dataMappingTitle' | pluginTranslate: pluginId | async }}</h5>\n <p class=\"helper-text\">{{ 'dataMappingDescription' | pluginTranslate: pluginId | async }}</p>\n </div>\n\n <div class=\"field-tree-root\" *ngIf=\"templateFields.length > 0\">\n <epistola-field-tree\n *ngFor=\"let field of templateFields\"\n [field]=\"field\"\n [value]=\"getFieldValue(field.name)\"\n [pluginId]=\"pluginId\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n [disabled]=\"disabled\"\n (valueChange)=\"onFieldValueChange(field.name, $event)\"\n ></epistola-field-tree>\n </div>\n\n <div class=\"no-fields\" *ngIf=\"templateFields.length === 0\">\n <p>{{ 'noTemplateFields' | pluginTranslate: pluginId | async }}</p>\n </div>\n</div>\n", styles: [".data-mapping-tree{margin-top:1rem;margin-bottom:1rem}.mapping-header{margin-bottom:.75rem}.mapping-header h5{margin-bottom:.25rem;font-weight:600}.mapping-header .helper-text{color:#6c757d;font-size:.875rem;margin-bottom:0}.field-tree-root{border:1px solid #e0e0e0;border-radius:4px;padding:.5rem .75rem}.no-fields{padding:1rem;text-align:center;color:#6c757d;background-color:#f8f9fa;border:1px solid #dee2e6;border-radius:4px}.no-fields p{margin-bottom:0}\n"] }]
|
|
550
633
|
}], propDecorators: { pluginId: [{
|
|
551
634
|
type: Input
|
|
552
|
-
}], templateFields
|
|
635
|
+
}], templateFields: [{
|
|
553
636
|
type: Input
|
|
554
|
-
}], prefillMapping
|
|
637
|
+
}], prefillMapping: [{
|
|
555
638
|
type: Input
|
|
556
|
-
}], disabled
|
|
639
|
+
}], disabled: [{
|
|
557
640
|
type: Input
|
|
558
641
|
}], caseDefinitionKey: [{
|
|
559
642
|
type: Input
|
|
@@ -569,53 +652,31 @@ class GenerateDocumentConfigurationComponent {
|
|
|
569
652
|
epistolaPluginService;
|
|
570
653
|
processLinkStateService;
|
|
571
654
|
cdr;
|
|
572
|
-
// Required inputs from FunctionConfigurationComponent
|
|
573
655
|
save$;
|
|
574
656
|
disabled$;
|
|
575
657
|
pluginId;
|
|
576
658
|
prefillConfiguration$;
|
|
577
|
-
// Optional inputs from FunctionConfigurationComponent
|
|
578
659
|
selectedPluginConfigurationData$;
|
|
579
660
|
context$;
|
|
580
661
|
valid = new EventEmitter();
|
|
581
662
|
configuration = new EventEmitter();
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
// Variant options loaded based on selected template
|
|
587
|
-
variantOptions$ = new BehaviorSubject([]);
|
|
588
|
-
variantsLoading$ = new BehaviorSubject(false);
|
|
589
|
-
variantsError$ = new BehaviorSubject(null);
|
|
590
|
-
// Environment options loaded from API
|
|
591
|
-
environmentOptions$ = new BehaviorSubject([]);
|
|
592
|
-
environmentsLoading$ = new BehaviorSubject(false);
|
|
593
|
-
environmentsError$ = new BehaviorSubject(null);
|
|
594
|
-
// Template fields for data mapping
|
|
595
|
-
templateFields$ = new BehaviorSubject([]);
|
|
596
|
-
templateFieldsLoading$ = new BehaviorSubject(false);
|
|
597
|
-
templateFieldsError$ = new BehaviorSubject(null);
|
|
598
|
-
// Current data mapping (nested structure mirroring template schema)
|
|
663
|
+
templates$ = new BehaviorSubject(initialResource([]));
|
|
664
|
+
variants$ = new BehaviorSubject(initialResource([]));
|
|
665
|
+
environments$ = new BehaviorSubject(initialResource([]));
|
|
666
|
+
templateFields$ = new BehaviorSubject(initialResource([]));
|
|
599
667
|
dataMapping$ = new BehaviorSubject({});
|
|
600
|
-
// Prefill data mapping observable for the tree
|
|
601
|
-
prefillDataMapping$;
|
|
602
668
|
outputFormatOptions = [
|
|
603
669
|
{ id: 'PDF', text: 'PDF' },
|
|
604
670
|
{ id: 'HTML', text: 'HTML' }
|
|
605
671
|
];
|
|
606
|
-
// Show data mapping builder only when template is selected
|
|
607
672
|
selectedTemplateId$ = new BehaviorSubject('');
|
|
608
673
|
selectedVariantId$ = new BehaviorSubject('');
|
|
609
|
-
// Variant selection mode: 'explicit' (dropdown) or 'attributes' (key-value pairs)
|
|
610
674
|
variantSelectionMode = 'explicit';
|
|
611
|
-
// Variant attributes for attribute-based selection
|
|
612
675
|
variantAttributeEntries = [];
|
|
613
|
-
// Case definition key from context (for ValuePathSelector)
|
|
614
676
|
caseDefinitionKey = null;
|
|
615
|
-
// Discovered process variables
|
|
616
677
|
processVariables = [];
|
|
617
|
-
// Required fields status
|
|
618
678
|
requiredFieldsStatus = { mapped: 0, total: 0 };
|
|
679
|
+
prefillDataMapping = {};
|
|
619
680
|
destroy$ = new Subject();
|
|
620
681
|
saveSubscription;
|
|
621
682
|
formValue$ = new BehaviorSubject(null);
|
|
@@ -628,7 +689,7 @@ class GenerateDocumentConfigurationComponent {
|
|
|
628
689
|
}
|
|
629
690
|
ngOnInit() {
|
|
630
691
|
this.initContext();
|
|
631
|
-
this.
|
|
692
|
+
this.initPrefill();
|
|
632
693
|
this.initPluginConfiguration();
|
|
633
694
|
this.initTemplatesLoading();
|
|
634
695
|
this.initEnvironmentsLoading();
|
|
@@ -644,12 +705,10 @@ class GenerateDocumentConfigurationComponent {
|
|
|
644
705
|
formValueChange(formOutput) {
|
|
645
706
|
const formValue = formOutput;
|
|
646
707
|
this.formValue$.next(formValue);
|
|
647
|
-
// Update selected template if changed (also clears variant selection)
|
|
648
708
|
if (formValue.templateId && formValue.templateId !== this.selectedTemplateId$.getValue()) {
|
|
649
709
|
this.selectedTemplateId$.next(formValue.templateId);
|
|
650
710
|
this.selectedVariantId$.next('');
|
|
651
711
|
}
|
|
652
|
-
// Update selected variant if changed
|
|
653
712
|
if (formValue.variantId && formValue.variantId !== this.selectedVariantId$.getValue()) {
|
|
654
713
|
this.selectedVariantId$.next(formValue.variantId);
|
|
655
714
|
}
|
|
@@ -657,7 +716,6 @@ class GenerateDocumentConfigurationComponent {
|
|
|
657
716
|
}
|
|
658
717
|
onDataMappingChange(mapping) {
|
|
659
718
|
this.dataMapping$.next(mapping);
|
|
660
|
-
// Re-validate when data mapping changes
|
|
661
719
|
const currentFormValue = this.formValue$.getValue();
|
|
662
720
|
if (currentFormValue) {
|
|
663
721
|
this.handleValid(currentFormValue);
|
|
@@ -665,7 +723,6 @@ class GenerateDocumentConfigurationComponent {
|
|
|
665
723
|
}
|
|
666
724
|
onRequiredFieldsStatusChange(status) {
|
|
667
725
|
this.requiredFieldsStatus = status;
|
|
668
|
-
// Re-validate when required fields status changes
|
|
669
726
|
const currentFormValue = this.formValue$.getValue();
|
|
670
727
|
if (currentFormValue) {
|
|
671
728
|
this.handleValid(currentFormValue);
|
|
@@ -676,11 +733,7 @@ class GenerateDocumentConfigurationComponent {
|
|
|
676
733
|
if (mode === 'attributes' && this.variantAttributeEntries.length === 0) {
|
|
677
734
|
this.variantAttributeEntries = [{ key: '', value: '' }];
|
|
678
735
|
}
|
|
679
|
-
|
|
680
|
-
const currentFormValue = this.formValue$.getValue();
|
|
681
|
-
if (currentFormValue) {
|
|
682
|
-
this.handleValid(currentFormValue);
|
|
683
|
-
}
|
|
736
|
+
this.revalidate();
|
|
684
737
|
}
|
|
685
738
|
addAttributeEntry() {
|
|
686
739
|
this.variantAttributeEntries = [...this.variantAttributeEntries, { key: '', value: '' }];
|
|
@@ -713,10 +766,8 @@ class GenerateDocumentConfigurationComponent {
|
|
|
713
766
|
});
|
|
714
767
|
}
|
|
715
768
|
}
|
|
716
|
-
|
|
769
|
+
initPrefill() {
|
|
717
770
|
if (this.prefillConfiguration$) {
|
|
718
|
-
this.prefillDataMapping$ = this.prefillConfiguration$.pipe(map(config => config?.dataMapping || {}));
|
|
719
|
-
// Also set initial selected template, variant mode, and variant
|
|
720
771
|
this.prefillConfiguration$.pipe(takeUntil(this.destroy$), filter(config => !!config?.templateId)).subscribe(config => {
|
|
721
772
|
this.selectedTemplateId$.next(config.templateId);
|
|
722
773
|
if (config.variantAttributes && Object.keys(config.variantAttributes).length > 0) {
|
|
@@ -729,102 +780,65 @@ class GenerateDocumentConfigurationComponent {
|
|
|
729
780
|
this.selectedVariantId$.next(config.variantId);
|
|
730
781
|
}
|
|
731
782
|
if (config.dataMapping) {
|
|
783
|
+
this.prefillDataMapping = config.dataMapping;
|
|
732
784
|
this.dataMapping$.next(config.dataMapping);
|
|
733
785
|
}
|
|
734
786
|
this.cdr.markForCheck();
|
|
735
787
|
});
|
|
736
788
|
}
|
|
737
|
-
else {
|
|
738
|
-
this.prefillDataMapping$ = new BehaviorSubject({}).asObservable();
|
|
739
|
-
}
|
|
740
789
|
}
|
|
741
790
|
initPluginConfiguration() {
|
|
742
791
|
const sources = [];
|
|
743
|
-
// Create mode: framework emits when user selects a plugin configuration
|
|
744
792
|
if (this.selectedPluginConfigurationData$) {
|
|
745
793
|
sources.push(this.selectedPluginConfigurationData$.pipe(filter(config => !!config?.configurationId), map(config => config.configurationId)));
|
|
746
794
|
}
|
|
747
|
-
// Edit mode: read pluginConfigurationId from the ProcessLink entity
|
|
748
|
-
// (selectedPluginConfigurationData$ does not emit in edit mode)
|
|
749
795
|
sources.push(this.processLinkStateService.selectedProcessLink$.pipe(filter(processLink => !!processLink?.pluginConfigurationId), map(processLink => processLink.pluginConfigurationId)));
|
|
750
796
|
merge(...sources).pipe(takeUntil(this.destroy$)).subscribe(configurationId => {
|
|
751
797
|
this.pluginConfigurationId$.next(configurationId);
|
|
752
798
|
});
|
|
753
799
|
}
|
|
754
800
|
initTemplatesLoading() {
|
|
755
|
-
this.pluginConfigurationId$.pipe(takeUntil(this.destroy$), filter(id => !!id)).
|
|
756
|
-
this.
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
return of([]);
|
|
761
|
-
})).subscribe(templates => {
|
|
762
|
-
const options = templates.map(t => ({
|
|
763
|
-
id: t.id,
|
|
764
|
-
text: t.name
|
|
765
|
-
}));
|
|
766
|
-
this.templateOptions$.next(options);
|
|
767
|
-
this.templatesLoading$.next(false);
|
|
768
|
-
});
|
|
801
|
+
this.pluginConfigurationId$.pipe(takeUntil(this.destroy$), filter(id => !!id), tap(() => this.templates$.next(loadingResource(this.templates$.getValue().data))), switchMap(configurationId => this.epistolaPluginService.getTemplates(configurationId).pipe(catchError(() => {
|
|
802
|
+
this.templates$.next(errorResource([], 'Failed to load templates'));
|
|
803
|
+
return of(null);
|
|
804
|
+
}))), filter(result => result !== null)).subscribe(templates => {
|
|
805
|
+
this.templates$.next(successResource(templates.map(t => ({ id: t.id, text: t.name }))));
|
|
769
806
|
});
|
|
770
807
|
}
|
|
771
808
|
initEnvironmentsLoading() {
|
|
772
|
-
this.pluginConfigurationId$.pipe(takeUntil(this.destroy$), filter(id => !!id)).
|
|
773
|
-
this.
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
return of([]);
|
|
778
|
-
})).subscribe(environments => {
|
|
779
|
-
const options = environments.map(e => ({
|
|
780
|
-
id: e.id,
|
|
781
|
-
text: e.name
|
|
782
|
-
}));
|
|
783
|
-
this.environmentOptions$.next(options);
|
|
784
|
-
this.environmentsLoading$.next(false);
|
|
785
|
-
});
|
|
809
|
+
this.pluginConfigurationId$.pipe(takeUntil(this.destroy$), filter(id => !!id), tap(() => this.environments$.next(loadingResource(this.environments$.getValue().data))), switchMap(configurationId => this.epistolaPluginService.getEnvironments(configurationId).pipe(catchError(() => {
|
|
810
|
+
this.environments$.next(errorResource([], 'Failed to load environments'));
|
|
811
|
+
return of(null);
|
|
812
|
+
}))), filter(result => result !== null)).subscribe(environments => {
|
|
813
|
+
this.environments$.next(successResource(environments.map(e => ({ id: e.id, text: e.name }))));
|
|
786
814
|
});
|
|
787
815
|
}
|
|
788
816
|
initVariantsLoading() {
|
|
789
817
|
combineLatest([
|
|
790
818
|
this.pluginConfigurationId$,
|
|
791
819
|
this.selectedTemplateId$
|
|
792
|
-
]).pipe(takeUntil(this.destroy$), filter(([configId, templateId]) => !!configId && !!templateId)).
|
|
793
|
-
this.
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
return of([]);
|
|
798
|
-
})).subscribe(variants => {
|
|
799
|
-
const options = variants.map(v => ({
|
|
800
|
-
id: v.id,
|
|
801
|
-
text: v.name + this.formatAttributes(v.attributes)
|
|
802
|
-
}));
|
|
803
|
-
this.variantOptions$.next(options);
|
|
804
|
-
this.variantsLoading$.next(false);
|
|
805
|
-
});
|
|
820
|
+
]).pipe(takeUntil(this.destroy$), filter(([configId, templateId]) => !!configId && !!templateId), tap(() => this.variants$.next(loadingResource(this.variants$.getValue().data))), switchMap(([configurationId, templateId]) => this.epistolaPluginService.getVariants(configurationId, templateId).pipe(catchError(() => {
|
|
821
|
+
this.variants$.next(errorResource([], 'Failed to load variants'));
|
|
822
|
+
return of(null);
|
|
823
|
+
}))), filter(result => result !== null)).subscribe(variants => {
|
|
824
|
+
this.variants$.next(successResource(variants.map(v => ({ id: v.id, text: v.name + this.formatAttributes(v.attributes) }))));
|
|
806
825
|
});
|
|
807
826
|
}
|
|
808
827
|
initTemplateFieldsLoading() {
|
|
809
828
|
combineLatest([
|
|
810
829
|
this.pluginConfigurationId$,
|
|
811
830
|
this.selectedTemplateId$
|
|
812
|
-
]).pipe(takeUntil(this.destroy$), filter(([configId, templateId]) => !!configId && !!templateId)
|
|
813
|
-
this.
|
|
814
|
-
this.templateFieldsError$.next(null);
|
|
815
|
-
this.epistolaPluginService.getTemplateDetails(configurationId, templateId).pipe(takeUntil(this.destroy$), catchError(() => {
|
|
816
|
-
this.templateFieldsError$.next('Failed to load template fields');
|
|
817
|
-
return of({ fields: [] });
|
|
818
|
-
})).subscribe(details => {
|
|
819
|
-
this.templateFields$.next(details.fields || []);
|
|
820
|
-
this.templateFieldsLoading$.next(false);
|
|
821
|
-
});
|
|
822
|
-
// Also load process variables if we have a case context
|
|
831
|
+
]).pipe(takeUntil(this.destroy$), filter(([configId, templateId]) => !!configId && !!templateId), tap(() => {
|
|
832
|
+
this.templateFields$.next(loadingResource(this.templateFields$.getValue().data));
|
|
823
833
|
this.loadProcessVariables();
|
|
834
|
+
}), switchMap(([configurationId, templateId]) => this.epistolaPluginService.getTemplateDetails(configurationId, templateId).pipe(catchError(() => {
|
|
835
|
+
this.templateFields$.next(errorResource([], 'Failed to load template fields'));
|
|
836
|
+
return of(null);
|
|
837
|
+
}))), filter(result => result !== null)).subscribe(details => {
|
|
838
|
+
this.templateFields$.next(successResource(details.fields || []));
|
|
824
839
|
});
|
|
825
840
|
}
|
|
826
841
|
loadProcessVariables() {
|
|
827
|
-
// Try to discover process variables (best-effort, may not always have context)
|
|
828
842
|
if (this.caseDefinitionKey) {
|
|
829
843
|
this.epistolaPluginService.getProcessVariables(this.caseDefinitionKey).pipe(takeUntil(this.destroy$), catchError(() => of([]))).subscribe(variables => {
|
|
830
844
|
this.processVariables = variables;
|
|
@@ -837,13 +851,10 @@ class GenerateDocumentConfigurationComponent {
|
|
|
837
851
|
formValue?.outputFormat &&
|
|
838
852
|
formValue?.filename &&
|
|
839
853
|
formValue?.resultProcessVariable);
|
|
840
|
-
// Variant selection: if attribute mode is used, all entries must have both key and value filled.
|
|
841
|
-
// Neither variant nor attributes are required — omitting both uses the template's default variant.
|
|
842
854
|
let variantValid = true;
|
|
843
855
|
if (this.variantSelectionMode === 'attributes' && this.variantAttributeEntries.length > 0) {
|
|
844
856
|
variantValid = this.variantAttributeEntries.every(e => !!e.key && !!e.value);
|
|
845
857
|
}
|
|
846
|
-
// Check if all required template fields are mapped
|
|
847
858
|
const requiredFieldsMapped = this.requiredFieldsStatus.total === 0 ||
|
|
848
859
|
this.requiredFieldsStatus.mapped === this.requiredFieldsStatus.total;
|
|
849
860
|
const valid = baseComplete && variantValid && requiredFieldsMapped;
|
|
@@ -881,8 +892,8 @@ class GenerateDocumentConfigurationComponent {
|
|
|
881
892
|
});
|
|
882
893
|
});
|
|
883
894
|
}
|
|
884
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GenerateDocumentConfigurationComponent, deps: [{ token: EpistolaPluginService }, { token: i2$
|
|
885
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: GenerateDocumentConfigurationComponent, isStandalone: true, selector: "epistola-generate-document-configuration", inputs: { save$: "save$", disabled$: "disabled$", pluginId: "pluginId", prefillConfiguration$: "prefillConfiguration$", selectedPluginConfigurationData$: "selectedPluginConfigurationData$", context$: "context$" }, outputs: { valid: "valid", configuration: "configuration" }, ngImport: i0, template: "<v-form\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"{\n disabled: disabled$ | async,\n prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,\n templateOptions: templateOptions$ | async,\n templatesLoading: templatesLoading$ | async,\n templatesError: templatesError$ | async,\n variantOptions: variantOptions$ | async,\n variantsLoading: variantsLoading$ | async,\n variantsError: variantsError$ | async,\n environmentOptions: environmentOptions$ | async,\n environmentsLoading: environmentsLoading$ | async,\n environmentsError: environmentsError$ | async,\n templateFieldsLoading: templateFieldsLoading$ | async,\n selectedTemplateId: selectedTemplateId$ | async\n } as obs\"\n>\n <v-select\n name=\"templateId\"\n [title]=\"'templateId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'templateIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.templateOptions\"\n [defaultSelectionId]=\"obs.prefill?.templateId\"\n [disabled]=\"obs.disabled || obs.templatesLoading\"\n [required]=\"true\"\n [loading]=\"obs.templatesLoading\"\n >\n </v-select>\n <div *ngIf=\"obs.templatesError\" class=\"loading-error\">{{ obs.templatesError }}</div>\n\n <!-- Variant selection mode toggle -->\n <div class=\"variant-mode-toggle\" *ngIf=\"obs.selectedTemplateId\">\n <label class=\"variant-mode-label\">{{ 'variantSelectionMode' | pluginTranslate: pluginId | async }}</label>\n <div class=\"variant-mode-buttons\">\n <button\n type=\"button\"\n class=\"variant-mode-btn\"\n [class.active]=\"variantSelectionMode === 'explicit'\"\n (click)=\"onVariantSelectionModeChange('explicit')\"\n [disabled]=\"obs.disabled\"\n >{{ 'selectByVariant' | pluginTranslate: pluginId | async }}</button>\n <button\n type=\"button\"\n class=\"variant-mode-btn\"\n [class.active]=\"variantSelectionMode === 'attributes'\"\n (click)=\"onVariantSelectionModeChange('attributes')\"\n [disabled]=\"obs.disabled\"\n >{{ 'selectByAttributes' | pluginTranslate: pluginId | async }}</button>\n </div>\n </div>\n\n <!-- Explicit variant selection (dropdown) -->\n <v-select\n *ngIf=\"variantSelectionMode === 'explicit'\"\n name=\"variantId\"\n [title]=\"'variantId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'variantIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.variantOptions\"\n [defaultSelectionId]=\"obs.prefill?.variantId\"\n [disabled]=\"obs.disabled || obs.variantsLoading || !obs.selectedTemplateId\"\n [required]=\"false\"\n [loading]=\"obs.variantsLoading\"\n >\n </v-select>\n <div *ngIf=\"obs.variantsError\" class=\"loading-error\">{{ obs.variantsError }}</div>\n\n <!-- Attribute-based variant selection -->\n <div *ngIf=\"variantSelectionMode === 'attributes' && obs.selectedTemplateId\" class=\"variant-attributes-section\">\n <label class=\"variant-attributes-label\">{{ 'variantAttributes' | pluginTranslate: pluginId | async }}</label>\n <div class=\"variant-attributes-list\">\n <div *ngFor=\"let entry of variantAttributeEntries; let i = index\" class=\"variant-attribute-row\">\n <input\n type=\"text\"\n class=\"variant-attribute-input\"\n [placeholder]=\"'attributeKey' | pluginTranslate: pluginId | async\"\n [(ngModel)]=\"entry.key\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <input\n type=\"text\"\n class=\"variant-attribute-input\"\n [placeholder]=\"'attributeValue' | pluginTranslate: pluginId | async\"\n [(ngModel)]=\"entry.value\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <button\n type=\"button\"\n class=\"variant-attribute-remove-btn\"\n (click)=\"removeAttributeEntry(i)\"\n [disabled]=\"obs.disabled\"\n title=\"{{ 'removeAttribute' | pluginTranslate: pluginId | async }}\"\n >×</button>\n </div>\n </div>\n <button\n type=\"button\"\n class=\"variant-attribute-add-btn\"\n (click)=\"addAttributeEntry()\"\n [disabled]=\"obs.disabled\"\n >+ {{ 'addAttribute' | pluginTranslate: pluginId | async }}</button>\n </div>\n\n <v-select\n name=\"environmentId\"\n [title]=\"'environmentId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'environmentIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.environmentOptions\"\n [defaultSelectionId]=\"obs.prefill?.environmentId\"\n [disabled]=\"obs.disabled || obs.environmentsLoading\"\n [required]=\"false\"\n [loading]=\"obs.environmentsLoading\"\n >\n </v-select>\n <div *ngIf=\"obs.environmentsError\" class=\"loading-error\">{{ obs.environmentsError }}</div>\n\n <v-select\n name=\"outputFormat\"\n [title]=\"'outputFormat' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'outputFormatTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"outputFormatOptions\"\n [defaultSelectionId]=\"obs.prefill?.outputFormat || 'PDF'\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-select>\n\n <v-input\n name=\"filename\"\n [title]=\"'filename' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'filenameTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.filename\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"correlationId\"\n [title]=\"'correlationId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'correlationIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.correlationId\"\n [disabled]=\"obs.disabled\"\n [required]=\"false\"\n >\n </v-input>\n\n <v-input\n name=\"resultProcessVariable\"\n [title]=\"'resultProcessVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'resultProcessVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.resultProcessVariable\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n</v-form>\n\n<div *ngIf=\"templateFieldsError$ | async as templateFieldsError\" class=\"loading-error\">{{ templateFieldsError }}</div>\n\n<epistola-data-mapping-tree\n *ngIf=\"(selectedTemplateId$ | async)\"\n [pluginId]=\"pluginId\"\n [templateFields$]=\"templateFields$\"\n [disabled$]=\"disabled$\"\n [prefillMapping$]=\"prefillDataMapping$\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n (mappingChange)=\"onDataMappingChange($event)\"\n (requiredFieldsStatus)=\"onRequiredFieldsStatusChange($event)\"\n></epistola-data-mapping-tree>\n\n<div class=\"validation-summary\" *ngIf=\"(selectedTemplateId$ | async) && requiredFieldsStatus.total > 0\">\n <span *ngIf=\"requiredFieldsStatus.mapped === requiredFieldsStatus.total\" class=\"validation-complete\">\n {{ 'requiredFieldsComplete' | pluginTranslate: pluginId | async }}\n </span>\n <span *ngIf=\"requiredFieldsStatus.mapped < requiredFieldsStatus.total\" class=\"validation-incomplete\">\n {{ requiredFieldsStatus.mapped }} / {{ requiredFieldsStatus.total }}\n {{ 'validationSummary' | pluginTranslate: pluginId | async }}\n </span>\n</div>\n", styles: [".loading-error{padding:.25rem .75rem;font-size:.8125rem;color:#dc3545}.validation-summary{margin-top:.5rem;padding:.5rem .75rem;border-radius:4px;font-size:.875rem}.validation-summary .validation-complete{color:#198754}.validation-summary .validation-incomplete{color:#dc3545;font-weight:500}.variant-mode-toggle{margin-bottom:1rem;padding:0 .75rem}.variant-mode-label{display:block;font-size:.875rem;font-weight:500;margin-bottom:.375rem}.variant-mode-buttons{display:flex;gap:0;border:1px solid #d1d5db;border-radius:4px;overflow:hidden;width:fit-content}.variant-mode-btn{padding:.375rem .75rem;font-size:.8125rem;background:#fff;border:none;border-right:1px solid #d1d5db;cursor:pointer;color:#374151;transition:background-color .15s,color .15s}.variant-mode-btn:last-child{border-right:none}.variant-mode-btn:hover:not([disabled]){background:#f3f4f6}.variant-mode-btn.active{background:#2563eb;color:#fff}.variant-mode-btn[disabled]{opacity:.5;cursor:not-allowed}.variant-attributes-section{margin-bottom:1rem;padding:0 .75rem}.variant-attributes-label{display:block;font-size:.875rem;font-weight:500;margin-bottom:.375rem}.variant-attributes-list{display:flex;flex-direction:column;gap:.375rem}.variant-attribute-row{display:flex;gap:.375rem;align-items:center}.variant-attribute-input{flex:1;padding:.375rem .5rem;font-size:.8125rem;border:1px solid #d1d5db;border-radius:4px;outline:none}.variant-attribute-input:focus{border-color:#2563eb;box-shadow:0 0 0 1px #2563eb}.variant-attribute-input[disabled]{opacity:.5;background:#f9fafb}.variant-attribute-remove-btn{padding:.25rem .5rem;font-size:1rem;line-height:1;background:none;border:1px solid #d1d5db;border-radius:4px;cursor:pointer;color:#6b7280}.variant-attribute-remove-btn:hover:not([disabled]){color:#dc3545;border-color:#dc3545}.variant-attribute-remove-btn[disabled]{opacity:.5;cursor:not-allowed}.variant-attribute-add-btn{margin-top:.375rem;padding:.25rem .5rem;font-size:.8125rem;background:none;border:1px dashed #d1d5db;border-radius:4px;cursor:pointer;color:#6b7280}.variant-attribute-add-btn:hover:not([disabled]){color:#2563eb;border-color:#2563eb}.variant-attribute-add-btn[disabled]{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PluginTranslatePipeModule }, { kind: "pipe", type: i2$1.PluginTranslatePipe, name: "pluginTranslate" }, { kind: "ngmodule", type: FormModule }, { kind: "component", type: i3.FormComponent, selector: "v-form", inputs: ["className"], outputs: ["valueChange"] }, { kind: "ngmodule", type: InputModule }, { kind: "component", type: i3.InputComponent, selector: "v-input", inputs: ["name", "type", "title", "titleTranslationKey", "defaultValue", "widthPx", "fullWidth", "margin", "smallMargin", "disabled", "step", "min", "maxLength", "tooltip", "required", "hideNumberSpinBox", "smallLabel", "rows", "clear$", "carbonTheme", "placeholder", "dataTestId", "trim", "presetsTitle", "presetOptions"], outputs: ["valueChange"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3.SelectComponent, selector: "v-select", inputs: ["items", "defaultSelection", "defaultSelectionId", "defaultSelectionIds", "disabled", "dropUp", "invalid", "multiple", "margin", "widthInPx", "notFoundText", "clearAllText", "clearText", "clearable", "name", "title", "titleTranslationKey", "clearSelectionSubject$", "tooltip", "required", "loading", "loadingText", "placeholder", "smallMargin", "carbonTheme", "appendInline", "warn", "warnText", "dataTestId"], outputs: ["selectedChange"] }, { kind: "component", type: DataMappingTreeComponent, selector: "epistola-data-mapping-tree", inputs: ["pluginId", "templateFields$", "prefillMapping$", "disabled$", "caseDefinitionKey", "processVariables"], outputs: ["mappingChange", "requiredFieldsStatus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
895
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GenerateDocumentConfigurationComponent, deps: [{ token: EpistolaPluginService }, { token: i2$3.ProcessLinkStateService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
896
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: GenerateDocumentConfigurationComponent, isStandalone: true, selector: "epistola-generate-document-configuration", inputs: { save$: "save$", disabled$: "disabled$", pluginId: "pluginId", prefillConfiguration$: "prefillConfiguration$", selectedPluginConfigurationData$: "selectedPluginConfigurationData$", context$: "context$" }, outputs: { valid: "valid", configuration: "configuration" }, ngImport: i0, template: "<v-form\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"{\n disabled: disabled$ | async,\n prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,\n templates: templates$ | async,\n variants: variants$ | async,\n environments: environments$ | async,\n templateFields: templateFields$ | async,\n selectedTemplateId: selectedTemplateId$ | async\n } as obs\"\n>\n <v-select\n name=\"templateId\"\n [title]=\"'templateId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'templateIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.templates.data\"\n [defaultSelectionId]=\"obs.prefill?.templateId\"\n [disabled]=\"obs.disabled || obs.templates.loading\"\n [required]=\"true\"\n [loading]=\"obs.templates.loading\"\n >\n </v-select>\n <div *ngIf=\"obs.templates.error\" class=\"loading-error\">{{ obs.templates.error }}</div>\n\n <!-- Variant selection mode toggle -->\n <div class=\"variant-mode-toggle\" *ngIf=\"obs.selectedTemplateId\">\n <label class=\"variant-mode-label\">{{ 'variantSelectionMode' | pluginTranslate: pluginId | async }}</label>\n <div class=\"variant-mode-buttons\">\n <button\n type=\"button\"\n class=\"variant-mode-btn\"\n [class.active]=\"variantSelectionMode === 'explicit'\"\n (click)=\"onVariantSelectionModeChange('explicit')\"\n [disabled]=\"obs.disabled\"\n >{{ 'selectByVariant' | pluginTranslate: pluginId | async }}</button>\n <button\n type=\"button\"\n class=\"variant-mode-btn\"\n [class.active]=\"variantSelectionMode === 'attributes'\"\n (click)=\"onVariantSelectionModeChange('attributes')\"\n [disabled]=\"obs.disabled\"\n >{{ 'selectByAttributes' | pluginTranslate: pluginId | async }}</button>\n </div>\n </div>\n\n <!-- Explicit variant selection (dropdown) -->\n <v-select\n *ngIf=\"variantSelectionMode === 'explicit'\"\n name=\"variantId\"\n [title]=\"'variantId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'variantIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.variants.data\"\n [defaultSelectionId]=\"obs.prefill?.variantId\"\n [disabled]=\"obs.disabled || obs.variants.loading || !obs.selectedTemplateId\"\n [required]=\"false\"\n [loading]=\"obs.variants.loading\"\n >\n </v-select>\n <div *ngIf=\"obs.variants.error\" class=\"loading-error\">{{ obs.variants.error }}</div>\n\n <!-- Attribute-based variant selection -->\n <div *ngIf=\"variantSelectionMode === 'attributes' && obs.selectedTemplateId\" class=\"variant-attributes-section\">\n <label class=\"variant-attributes-label\">{{ 'variantAttributes' | pluginTranslate: pluginId | async }}</label>\n <div class=\"variant-attributes-list\">\n <div *ngFor=\"let entry of variantAttributeEntries; let i = index\" class=\"variant-attribute-row\">\n <input\n type=\"text\"\n class=\"variant-attribute-input\"\n [placeholder]=\"'attributeKey' | pluginTranslate: pluginId | async\"\n [(ngModel)]=\"entry.key\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <input\n type=\"text\"\n class=\"variant-attribute-input\"\n [placeholder]=\"'attributeValue' | pluginTranslate: pluginId | async\"\n [(ngModel)]=\"entry.value\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <button\n type=\"button\"\n class=\"variant-attribute-remove-btn\"\n (click)=\"removeAttributeEntry(i)\"\n [disabled]=\"obs.disabled\"\n title=\"{{ 'removeAttribute' | pluginTranslate: pluginId | async }}\"\n >×</button>\n </div>\n </div>\n <button\n type=\"button\"\n class=\"variant-attribute-add-btn\"\n (click)=\"addAttributeEntry()\"\n [disabled]=\"obs.disabled\"\n >+ {{ 'addAttribute' | pluginTranslate: pluginId | async }}</button>\n </div>\n\n <v-select\n name=\"environmentId\"\n [title]=\"'environmentId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'environmentIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.environments.data\"\n [defaultSelectionId]=\"obs.prefill?.environmentId\"\n [disabled]=\"obs.disabled || obs.environments.loading\"\n [required]=\"false\"\n [loading]=\"obs.environments.loading\"\n >\n </v-select>\n <div *ngIf=\"obs.environments.error\" class=\"loading-error\">{{ obs.environments.error }}</div>\n\n <v-select\n name=\"outputFormat\"\n [title]=\"'outputFormat' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'outputFormatTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"outputFormatOptions\"\n [defaultSelectionId]=\"obs.prefill?.outputFormat || 'PDF'\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-select>\n\n <v-input\n name=\"filename\"\n [title]=\"'filename' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'filenameTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.filename\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"correlationId\"\n [title]=\"'correlationId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'correlationIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.correlationId\"\n [disabled]=\"obs.disabled\"\n [required]=\"false\"\n >\n </v-input>\n\n <v-input\n name=\"resultProcessVariable\"\n [title]=\"'resultProcessVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'resultProcessVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.resultProcessVariable\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n</v-form>\n\n<div *ngIf=\"(templateFields$ | async)?.error as templateFieldsError\" class=\"loading-error\">{{ templateFieldsError }}</div>\n\n<epistola-data-mapping-tree\n *ngIf=\"(selectedTemplateId$ | async)\"\n [pluginId]=\"pluginId\"\n [templateFields]=\"(templateFields$ | async)?.data ?? []\"\n [disabled]=\"!!(disabled$ | async)\"\n [prefillMapping]=\"prefillDataMapping\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n (mappingChange)=\"onDataMappingChange($event)\"\n (requiredFieldsStatus)=\"onRequiredFieldsStatusChange($event)\"\n></epistola-data-mapping-tree>\n\n<div class=\"validation-summary\" *ngIf=\"(selectedTemplateId$ | async) && requiredFieldsStatus.total > 0\">\n <span *ngIf=\"requiredFieldsStatus.mapped === requiredFieldsStatus.total\" class=\"validation-complete\">\n {{ 'requiredFieldsComplete' | pluginTranslate: pluginId | async }}\n </span>\n <span *ngIf=\"requiredFieldsStatus.mapped < requiredFieldsStatus.total\" class=\"validation-incomplete\">\n {{ requiredFieldsStatus.mapped }} / {{ requiredFieldsStatus.total }}\n {{ 'validationSummary' | pluginTranslate: pluginId | async }}\n </span>\n</div>\n", styles: [".loading-error{padding:.25rem .75rem;font-size:.8125rem;color:#dc3545}.validation-summary{margin-top:.5rem;padding:.5rem .75rem;border-radius:4px;font-size:.875rem}.validation-summary .validation-complete{color:#198754}.validation-summary .validation-incomplete{color:#dc3545;font-weight:500}.variant-mode-toggle{margin-bottom:1rem;padding:0 .75rem}.variant-mode-label{display:block;font-size:.875rem;font-weight:500;margin-bottom:.375rem}.variant-mode-buttons{display:flex;gap:0;border:1px solid #d1d5db;border-radius:4px;overflow:hidden;width:fit-content}.variant-mode-btn{padding:.375rem .75rem;font-size:.8125rem;background:#fff;border:none;border-right:1px solid #d1d5db;cursor:pointer;color:#374151;transition:background-color .15s,color .15s}.variant-mode-btn:last-child{border-right:none}.variant-mode-btn:hover:not([disabled]){background:#f3f4f6}.variant-mode-btn.active{background:#2563eb;color:#fff}.variant-mode-btn[disabled]{opacity:.5;cursor:not-allowed}.variant-attributes-section{margin-bottom:1rem;padding:0 .75rem}.variant-attributes-label{display:block;font-size:.875rem;font-weight:500;margin-bottom:.375rem}.variant-attributes-list{display:flex;flex-direction:column;gap:.375rem}.variant-attribute-row{display:flex;gap:.375rem;align-items:center}.variant-attribute-input{flex:1;padding:.375rem .5rem;font-size:.8125rem;border:1px solid #d1d5db;border-radius:4px;outline:none}.variant-attribute-input:focus{border-color:#2563eb;box-shadow:0 0 0 1px #2563eb}.variant-attribute-input[disabled]{opacity:.5;background:#f9fafb}.variant-attribute-remove-btn{padding:.25rem .5rem;font-size:1rem;line-height:1;background:none;border:1px solid #d1d5db;border-radius:4px;cursor:pointer;color:#6b7280}.variant-attribute-remove-btn:hover:not([disabled]){color:#dc3545;border-color:#dc3545}.variant-attribute-remove-btn[disabled]{opacity:.5;cursor:not-allowed}.variant-attribute-add-btn{margin-top:.375rem;padding:.25rem .5rem;font-size:.8125rem;background:none;border:1px dashed #d1d5db;border-radius:4px;cursor:pointer;color:#6b7280}.variant-attribute-add-btn:hover:not([disabled]){color:#2563eb;border-color:#2563eb}.variant-attribute-add-btn[disabled]{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PluginTranslatePipeModule }, { kind: "pipe", type: i2$1.PluginTranslatePipe, name: "pluginTranslate" }, { kind: "ngmodule", type: FormModule }, { kind: "component", type: i3.FormComponent, selector: "v-form", inputs: ["className"], outputs: ["valueChange"] }, { kind: "ngmodule", type: InputModule }, { kind: "component", type: i3.InputComponent, selector: "v-input", inputs: ["name", "type", "title", "titleTranslationKey", "defaultValue", "widthPx", "fullWidth", "margin", "smallMargin", "disabled", "step", "min", "maxLength", "tooltip", "required", "hideNumberSpinBox", "smallLabel", "rows", "clear$", "carbonTheme", "placeholder", "dataTestId", "trim", "presetsTitle", "presetOptions"], outputs: ["valueChange"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3.SelectComponent, selector: "v-select", inputs: ["items", "defaultSelection", "defaultSelectionId", "defaultSelectionIds", "disabled", "dropUp", "invalid", "multiple", "margin", "widthInPx", "notFoundText", "clearAllText", "clearText", "clearable", "name", "title", "titleTranslationKey", "clearSelectionSubject$", "tooltip", "required", "loading", "loadingText", "placeholder", "smallMargin", "carbonTheme", "appendInline", "warn", "warnText", "dataTestId"], outputs: ["selectedChange"] }, { kind: "component", type: DataMappingTreeComponent, selector: "epistola-data-mapping-tree", inputs: ["pluginId", "templateFields", "prefillMapping", "disabled", "caseDefinitionKey", "processVariables"], outputs: ["mappingChange", "requiredFieldsStatus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
886
897
|
}
|
|
887
898
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GenerateDocumentConfigurationComponent, decorators: [{
|
|
888
899
|
type: Component,
|
|
@@ -894,8 +905,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
894
905
|
InputModule,
|
|
895
906
|
SelectModule,
|
|
896
907
|
DataMappingTreeComponent
|
|
897
|
-
], template: "<v-form\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"{\n disabled: disabled$ | async,\n prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,\n
|
|
898
|
-
}], ctorParameters: () => [{ type: EpistolaPluginService }, { type: i2$
|
|
908
|
+
], template: "<v-form\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"{\n disabled: disabled$ | async,\n prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,\n templates: templates$ | async,\n variants: variants$ | async,\n environments: environments$ | async,\n templateFields: templateFields$ | async,\n selectedTemplateId: selectedTemplateId$ | async\n } as obs\"\n>\n <v-select\n name=\"templateId\"\n [title]=\"'templateId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'templateIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.templates.data\"\n [defaultSelectionId]=\"obs.prefill?.templateId\"\n [disabled]=\"obs.disabled || obs.templates.loading\"\n [required]=\"true\"\n [loading]=\"obs.templates.loading\"\n >\n </v-select>\n <div *ngIf=\"obs.templates.error\" class=\"loading-error\">{{ obs.templates.error }}</div>\n\n <!-- Variant selection mode toggle -->\n <div class=\"variant-mode-toggle\" *ngIf=\"obs.selectedTemplateId\">\n <label class=\"variant-mode-label\">{{ 'variantSelectionMode' | pluginTranslate: pluginId | async }}</label>\n <div class=\"variant-mode-buttons\">\n <button\n type=\"button\"\n class=\"variant-mode-btn\"\n [class.active]=\"variantSelectionMode === 'explicit'\"\n (click)=\"onVariantSelectionModeChange('explicit')\"\n [disabled]=\"obs.disabled\"\n >{{ 'selectByVariant' | pluginTranslate: pluginId | async }}</button>\n <button\n type=\"button\"\n class=\"variant-mode-btn\"\n [class.active]=\"variantSelectionMode === 'attributes'\"\n (click)=\"onVariantSelectionModeChange('attributes')\"\n [disabled]=\"obs.disabled\"\n >{{ 'selectByAttributes' | pluginTranslate: pluginId | async }}</button>\n </div>\n </div>\n\n <!-- Explicit variant selection (dropdown) -->\n <v-select\n *ngIf=\"variantSelectionMode === 'explicit'\"\n name=\"variantId\"\n [title]=\"'variantId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'variantIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.variants.data\"\n [defaultSelectionId]=\"obs.prefill?.variantId\"\n [disabled]=\"obs.disabled || obs.variants.loading || !obs.selectedTemplateId\"\n [required]=\"false\"\n [loading]=\"obs.variants.loading\"\n >\n </v-select>\n <div *ngIf=\"obs.variants.error\" class=\"loading-error\">{{ obs.variants.error }}</div>\n\n <!-- Attribute-based variant selection -->\n <div *ngIf=\"variantSelectionMode === 'attributes' && obs.selectedTemplateId\" class=\"variant-attributes-section\">\n <label class=\"variant-attributes-label\">{{ 'variantAttributes' | pluginTranslate: pluginId | async }}</label>\n <div class=\"variant-attributes-list\">\n <div *ngFor=\"let entry of variantAttributeEntries; let i = index\" class=\"variant-attribute-row\">\n <input\n type=\"text\"\n class=\"variant-attribute-input\"\n [placeholder]=\"'attributeKey' | pluginTranslate: pluginId | async\"\n [(ngModel)]=\"entry.key\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <input\n type=\"text\"\n class=\"variant-attribute-input\"\n [placeholder]=\"'attributeValue' | pluginTranslate: pluginId | async\"\n [(ngModel)]=\"entry.value\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <button\n type=\"button\"\n class=\"variant-attribute-remove-btn\"\n (click)=\"removeAttributeEntry(i)\"\n [disabled]=\"obs.disabled\"\n title=\"{{ 'removeAttribute' | pluginTranslate: pluginId | async }}\"\n >×</button>\n </div>\n </div>\n <button\n type=\"button\"\n class=\"variant-attribute-add-btn\"\n (click)=\"addAttributeEntry()\"\n [disabled]=\"obs.disabled\"\n >+ {{ 'addAttribute' | pluginTranslate: pluginId | async }}</button>\n </div>\n\n <v-select\n name=\"environmentId\"\n [title]=\"'environmentId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'environmentIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.environments.data\"\n [defaultSelectionId]=\"obs.prefill?.environmentId\"\n [disabled]=\"obs.disabled || obs.environments.loading\"\n [required]=\"false\"\n [loading]=\"obs.environments.loading\"\n >\n </v-select>\n <div *ngIf=\"obs.environments.error\" class=\"loading-error\">{{ obs.environments.error }}</div>\n\n <v-select\n name=\"outputFormat\"\n [title]=\"'outputFormat' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'outputFormatTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"outputFormatOptions\"\n [defaultSelectionId]=\"obs.prefill?.outputFormat || 'PDF'\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-select>\n\n <v-input\n name=\"filename\"\n [title]=\"'filename' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'filenameTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.filename\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"correlationId\"\n [title]=\"'correlationId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'correlationIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.correlationId\"\n [disabled]=\"obs.disabled\"\n [required]=\"false\"\n >\n </v-input>\n\n <v-input\n name=\"resultProcessVariable\"\n [title]=\"'resultProcessVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'resultProcessVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.resultProcessVariable\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n</v-form>\n\n<div *ngIf=\"(templateFields$ | async)?.error as templateFieldsError\" class=\"loading-error\">{{ templateFieldsError }}</div>\n\n<epistola-data-mapping-tree\n *ngIf=\"(selectedTemplateId$ | async)\"\n [pluginId]=\"pluginId\"\n [templateFields]=\"(templateFields$ | async)?.data ?? []\"\n [disabled]=\"!!(disabled$ | async)\"\n [prefillMapping]=\"prefillDataMapping\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n [processVariables]=\"processVariables\"\n (mappingChange)=\"onDataMappingChange($event)\"\n (requiredFieldsStatus)=\"onRequiredFieldsStatusChange($event)\"\n></epistola-data-mapping-tree>\n\n<div class=\"validation-summary\" *ngIf=\"(selectedTemplateId$ | async) && requiredFieldsStatus.total > 0\">\n <span *ngIf=\"requiredFieldsStatus.mapped === requiredFieldsStatus.total\" class=\"validation-complete\">\n {{ 'requiredFieldsComplete' | pluginTranslate: pluginId | async }}\n </span>\n <span *ngIf=\"requiredFieldsStatus.mapped < requiredFieldsStatus.total\" class=\"validation-incomplete\">\n {{ requiredFieldsStatus.mapped }} / {{ requiredFieldsStatus.total }}\n {{ 'validationSummary' | pluginTranslate: pluginId | async }}\n </span>\n</div>\n", styles: [".loading-error{padding:.25rem .75rem;font-size:.8125rem;color:#dc3545}.validation-summary{margin-top:.5rem;padding:.5rem .75rem;border-radius:4px;font-size:.875rem}.validation-summary .validation-complete{color:#198754}.validation-summary .validation-incomplete{color:#dc3545;font-weight:500}.variant-mode-toggle{margin-bottom:1rem;padding:0 .75rem}.variant-mode-label{display:block;font-size:.875rem;font-weight:500;margin-bottom:.375rem}.variant-mode-buttons{display:flex;gap:0;border:1px solid #d1d5db;border-radius:4px;overflow:hidden;width:fit-content}.variant-mode-btn{padding:.375rem .75rem;font-size:.8125rem;background:#fff;border:none;border-right:1px solid #d1d5db;cursor:pointer;color:#374151;transition:background-color .15s,color .15s}.variant-mode-btn:last-child{border-right:none}.variant-mode-btn:hover:not([disabled]){background:#f3f4f6}.variant-mode-btn.active{background:#2563eb;color:#fff}.variant-mode-btn[disabled]{opacity:.5;cursor:not-allowed}.variant-attributes-section{margin-bottom:1rem;padding:0 .75rem}.variant-attributes-label{display:block;font-size:.875rem;font-weight:500;margin-bottom:.375rem}.variant-attributes-list{display:flex;flex-direction:column;gap:.375rem}.variant-attribute-row{display:flex;gap:.375rem;align-items:center}.variant-attribute-input{flex:1;padding:.375rem .5rem;font-size:.8125rem;border:1px solid #d1d5db;border-radius:4px;outline:none}.variant-attribute-input:focus{border-color:#2563eb;box-shadow:0 0 0 1px #2563eb}.variant-attribute-input[disabled]{opacity:.5;background:#f9fafb}.variant-attribute-remove-btn{padding:.25rem .5rem;font-size:1rem;line-height:1;background:none;border:1px solid #d1d5db;border-radius:4px;cursor:pointer;color:#6b7280}.variant-attribute-remove-btn:hover:not([disabled]){color:#dc3545;border-color:#dc3545}.variant-attribute-remove-btn[disabled]{opacity:.5;cursor:not-allowed}.variant-attribute-add-btn{margin-top:.375rem;padding:.25rem .5rem;font-size:.8125rem;background:none;border:1px dashed #d1d5db;border-radius:4px;cursor:pointer;color:#6b7280}.variant-attribute-add-btn:hover:not([disabled]){color:#2563eb;border-color:#2563eb}.variant-attribute-add-btn[disabled]{opacity:.5;cursor:not-allowed}\n"] }]
|
|
909
|
+
}], ctorParameters: () => [{ type: EpistolaPluginService }, { type: i2$3.ProcessLinkStateService }, { type: i0.ChangeDetectorRef }], propDecorators: { save$: [{
|
|
899
910
|
type: Input
|
|
900
911
|
}], disabled$: [{
|
|
901
912
|
type: Input
|
|
@@ -1275,7 +1286,7 @@ class EpistolaRetryFormComponent {
|
|
|
1275
1286
|
}
|
|
1276
1287
|
});
|
|
1277
1288
|
}
|
|
1278
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaRetryFormComponent, deps: [{ token: EpistolaPluginService }, { token: i3.FormIoStateService }, { token: i0.ChangeDetectorRef }, { token: i1.HttpClient }, { token: i4
|
|
1289
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaRetryFormComponent, deps: [{ token: EpistolaPluginService }, { token: i3.FormIoStateService }, { token: i0.ChangeDetectorRef }, { token: i1.HttpClient }, { token: i4.DomSanitizer }, { token: i2.ConfigService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1279
1290
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaRetryFormComponent, isStandalone: true, selector: "epistola-retry-form-component", inputs: { value: "value", disabled: "disabled", label: "label", sourceActivityId: "sourceActivityId" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
1280
1291
|
<div *ngIf="loading" class="epistola-retry-loading">Loading form...</div>
|
|
1281
1292
|
<div *ngIf="error" class="epistola-retry-error">{{ error }}</div>
|
|
@@ -1339,7 +1350,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1339
1350
|
</div>
|
|
1340
1351
|
</div>
|
|
1341
1352
|
`, styles: [".epistola-retry-loading{padding:1rem;color:#6c757d}.epistola-retry-error{padding:.5rem;color:#dc3545}.epistola-retry-container{display:flex;gap:1rem}.epistola-retry-form{flex:2;min-width:0}.epistola-retry-preview{flex:1;min-width:0;border:1px solid #dee2e6;border-radius:4px;padding:1rem;background:#f8f9fa;display:flex;flex-direction:column}.preview-expanded .epistola-retry-preview{flex:1}.preview-header{display:flex;justify-content:space-between;align-items:center;font-weight:700;margin-bottom:.5rem;color:#495057}.preview-toggle{background:none;border:1px solid #6c757d;border-radius:4px;color:#6c757d;padding:.2rem .5rem;font-size:.75rem;cursor:pointer}.preview-toggle:hover{background:#e9ecef}.preview-loading{color:#6c757d;font-style:italic}.preview-pdf{width:100%;flex:1;min-height:500px}.preview-expanded .preview-pdf{min-height:80vh}.preview-error{color:#dc3545}.preview-empty{color:#6c757d;font-style:italic}\n"] }]
|
|
1342
|
-
}], ctorParameters: () => [{ type: EpistolaPluginService }, { type: i3.FormIoStateService }, { type: i0.ChangeDetectorRef }, { type: i1.HttpClient }, { type: i4
|
|
1353
|
+
}], ctorParameters: () => [{ type: EpistolaPluginService }, { type: i3.FormIoStateService }, { type: i0.ChangeDetectorRef }, { type: i1.HttpClient }, { type: i4.DomSanitizer }, { type: i2.ConfigService }], propDecorators: { value: [{
|
|
1343
1354
|
type: Input
|
|
1344
1355
|
}], valueChange: [{
|
|
1345
1356
|
type: Output
|
|
@@ -1416,7 +1427,7 @@ class EpistolaPreviewButtonComponent {
|
|
|
1416
1427
|
this.currentBlobUrl = null;
|
|
1417
1428
|
}
|
|
1418
1429
|
}
|
|
1419
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPreviewButtonComponent, deps: [{ token: i1.HttpClient }, { token: i4
|
|
1430
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPreviewButtonComponent, deps: [{ token: i1.HttpClient }, { token: i4.DomSanitizer }, { token: i2.ConfigService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1420
1431
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaPreviewButtonComponent, isStandalone: true, selector: "epistola-preview-button-component", inputs: { value: "value", disabled: "disabled", label: "label" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: `
|
|
1421
1432
|
<button
|
|
1422
1433
|
type="button"
|
|
@@ -1484,7 +1495,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1484
1495
|
</div>
|
|
1485
1496
|
</div>
|
|
1486
1497
|
`, styles: [".preview-modal-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:10000}.preview-modal-content{background:#fff;border-radius:8px;width:90vw;height:90vh;max-width:1200px;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 8px 32px #0000004d}.preview-modal-header{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;border-bottom:1px solid #dee2e6;font-weight:700;font-size:1rem}.preview-modal-close{background:none;border:none;font-size:1.5rem;cursor:pointer;color:#6c757d;line-height:1;padding:0 .25rem}.preview-modal-close:hover{color:#333}.preview-modal-body{flex:1;overflow:hidden;display:flex;flex-direction:column}.preview-loading,.preview-error{padding:2rem;text-align:center}.preview-error{color:#dc3545}.preview-pdf{width:100%;flex:1}\n"] }]
|
|
1487
|
-
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i4
|
|
1498
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i4.DomSanitizer }, { type: i2.ConfigService }], propDecorators: { value: [{
|
|
1488
1499
|
type: Input
|
|
1489
1500
|
}], valueChange: [{
|
|
1490
1501
|
type: Output
|
|
@@ -1494,7 +1505,325 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1494
1505
|
type: Input
|
|
1495
1506
|
}] } });
|
|
1496
1507
|
|
|
1508
|
+
class EpistolaDocumentPreviewComponent {
|
|
1509
|
+
epistolaPluginService;
|
|
1510
|
+
http;
|
|
1511
|
+
sanitizer;
|
|
1512
|
+
configService;
|
|
1513
|
+
formIoStateService;
|
|
1514
|
+
cdr;
|
|
1515
|
+
value;
|
|
1516
|
+
valueChange = new EventEmitter();
|
|
1517
|
+
disabled = false;
|
|
1518
|
+
label = 'Document Preview';
|
|
1519
|
+
sources = [];
|
|
1520
|
+
selectedIndex = 0;
|
|
1521
|
+
discovering = false;
|
|
1522
|
+
loading = false;
|
|
1523
|
+
error = null;
|
|
1524
|
+
previewUrl = null;
|
|
1525
|
+
initialized = false;
|
|
1526
|
+
currentBlobUrl = null;
|
|
1527
|
+
discoverSubscription;
|
|
1528
|
+
previewSubscription;
|
|
1529
|
+
apiEndpoint;
|
|
1530
|
+
constructor(epistolaPluginService, http, sanitizer, configService, formIoStateService, cdr) {
|
|
1531
|
+
this.epistolaPluginService = epistolaPluginService;
|
|
1532
|
+
this.http = http;
|
|
1533
|
+
this.sanitizer = sanitizer;
|
|
1534
|
+
this.configService = configService;
|
|
1535
|
+
this.formIoStateService = formIoStateService;
|
|
1536
|
+
this.cdr = cdr;
|
|
1537
|
+
this.apiEndpoint = `${this.configService.config.valtimoApi.endpointUri}v1/plugin/epistola`;
|
|
1538
|
+
}
|
|
1539
|
+
ngOnChanges(changes) {
|
|
1540
|
+
if (!this.initialized) {
|
|
1541
|
+
this.initialized = true;
|
|
1542
|
+
this.discoverSources();
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
ngOnDestroy() {
|
|
1546
|
+
this.discoverSubscription?.unsubscribe();
|
|
1547
|
+
this.previewSubscription?.unsubscribe();
|
|
1548
|
+
this.revokeBlobUrl();
|
|
1549
|
+
}
|
|
1550
|
+
onSourceChange(event) {
|
|
1551
|
+
this.selectedIndex = +event.target.value;
|
|
1552
|
+
this.loadPreview();
|
|
1553
|
+
}
|
|
1554
|
+
refresh() {
|
|
1555
|
+
this.loadPreview();
|
|
1556
|
+
}
|
|
1557
|
+
discoverSources() {
|
|
1558
|
+
const documentId = this.formIoStateService.documentId;
|
|
1559
|
+
if (!documentId) {
|
|
1560
|
+
this.error = 'Could not determine document ID from context.';
|
|
1561
|
+
this.cdr.markForCheck();
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
this.discovering = true;
|
|
1565
|
+
this.error = null;
|
|
1566
|
+
this.cdr.markForCheck();
|
|
1567
|
+
this.discoverSubscription = this.epistolaPluginService.getPreviewSources(documentId).subscribe({
|
|
1568
|
+
next: (sources) => {
|
|
1569
|
+
this.sources = sources;
|
|
1570
|
+
this.discovering = false;
|
|
1571
|
+
this.cdr.markForCheck();
|
|
1572
|
+
if (sources.length > 0) {
|
|
1573
|
+
this.selectedIndex = 0;
|
|
1574
|
+
this.loadPreview();
|
|
1575
|
+
}
|
|
1576
|
+
},
|
|
1577
|
+
error: (err) => {
|
|
1578
|
+
this.error = err.error?.error || 'Failed to discover preview sources';
|
|
1579
|
+
this.discovering = false;
|
|
1580
|
+
this.cdr.markForCheck();
|
|
1581
|
+
}
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
loadPreview() {
|
|
1585
|
+
const source = this.sources[this.selectedIndex];
|
|
1586
|
+
if (!source)
|
|
1587
|
+
return;
|
|
1588
|
+
const documentId = this.formIoStateService.documentId;
|
|
1589
|
+
if (!documentId)
|
|
1590
|
+
return;
|
|
1591
|
+
this.loading = true;
|
|
1592
|
+
this.error = null;
|
|
1593
|
+
this.cdr.markForCheck();
|
|
1594
|
+
this.revokeBlobUrl();
|
|
1595
|
+
this.previewSubscription?.unsubscribe();
|
|
1596
|
+
this.previewSubscription = this.http.post(`${this.apiEndpoint}/preview`, {
|
|
1597
|
+
documentId,
|
|
1598
|
+
processInstanceId: source.processInstanceId,
|
|
1599
|
+
sourceActivityId: source.activityId,
|
|
1600
|
+
overrides: null
|
|
1601
|
+
}, {
|
|
1602
|
+
responseType: 'blob',
|
|
1603
|
+
headers: new HttpHeaders().set('X-Skip-Interceptor', '422')
|
|
1604
|
+
}).subscribe({
|
|
1605
|
+
next: (blob) => {
|
|
1606
|
+
this.currentBlobUrl = URL.createObjectURL(blob);
|
|
1607
|
+
this.previewUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.currentBlobUrl);
|
|
1608
|
+
this.error = null;
|
|
1609
|
+
this.loading = false;
|
|
1610
|
+
this.cdr.markForCheck();
|
|
1611
|
+
},
|
|
1612
|
+
error: (err) => {
|
|
1613
|
+
this.previewUrl = null;
|
|
1614
|
+
if (err.error instanceof Blob) {
|
|
1615
|
+
err.error.text().then((text) => {
|
|
1616
|
+
try {
|
|
1617
|
+
const body = JSON.parse(text);
|
|
1618
|
+
this.error = body.details || body.error || 'Preview could not be generated';
|
|
1619
|
+
}
|
|
1620
|
+
catch {
|
|
1621
|
+
this.error = 'Preview could not be generated';
|
|
1622
|
+
}
|
|
1623
|
+
this.loading = false;
|
|
1624
|
+
this.cdr.markForCheck();
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
else {
|
|
1628
|
+
this.error = err.error?.error || 'Preview could not be generated';
|
|
1629
|
+
this.loading = false;
|
|
1630
|
+
this.cdr.markForCheck();
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
revokeBlobUrl() {
|
|
1636
|
+
if (this.currentBlobUrl) {
|
|
1637
|
+
URL.revokeObjectURL(this.currentBlobUrl);
|
|
1638
|
+
this.currentBlobUrl = null;
|
|
1639
|
+
this.previewUrl = null;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaDocumentPreviewComponent, deps: [{ token: EpistolaPluginService }, { token: i1.HttpClient }, { token: i4.DomSanitizer }, { token: i2.ConfigService }, { token: i3.FormIoStateService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
1643
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaDocumentPreviewComponent, isStandalone: true, selector: "epistola-document-preview-component", inputs: { value: "value", disabled: "disabled", label: "label" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
1644
|
+
<div class="epistola-preview-panel">
|
|
1645
|
+
<div class="preview-header">
|
|
1646
|
+
<span>{{ label || 'Document Preview' }}</span>
|
|
1647
|
+
<div class="preview-controls">
|
|
1648
|
+
<select
|
|
1649
|
+
*ngIf="sources.length > 1"
|
|
1650
|
+
class="preview-select"
|
|
1651
|
+
[value]="selectedIndex"
|
|
1652
|
+
(change)="onSourceChange($event)"
|
|
1653
|
+
>
|
|
1654
|
+
<option *ngFor="let source of sources; let i = index" [value]="i">
|
|
1655
|
+
{{ source.templateName }} ({{ source.activityId }})
|
|
1656
|
+
</option>
|
|
1657
|
+
</select>
|
|
1658
|
+
<button type="button" class="preview-refresh" [disabled]="loading || discovering" (click)="refresh()">
|
|
1659
|
+
<i class="mdi mdi-refresh mr-1"></i>
|
|
1660
|
+
{{ loading ? 'Generating...' : 'Refresh' }}
|
|
1661
|
+
</button>
|
|
1662
|
+
</div>
|
|
1663
|
+
</div>
|
|
1664
|
+
<div class="preview-body">
|
|
1665
|
+
<div *ngIf="discovering" class="preview-loading">
|
|
1666
|
+
Discovering documents...
|
|
1667
|
+
</div>
|
|
1668
|
+
<div *ngIf="loading && !discovering" class="preview-loading">
|
|
1669
|
+
Generating preview...
|
|
1670
|
+
</div>
|
|
1671
|
+
<div *ngIf="error && !loading && !discovering" class="preview-unavailable">
|
|
1672
|
+
<i class="mdi mdi-information-outline"></i>
|
|
1673
|
+
Preview is niet beschikbaar — niet alle gegevens zijn al ingevuld.
|
|
1674
|
+
</div>
|
|
1675
|
+
<object
|
|
1676
|
+
*ngIf="previewUrl && !loading && !discovering"
|
|
1677
|
+
[data]="previewUrl"
|
|
1678
|
+
type="application/pdf"
|
|
1679
|
+
class="preview-pdf"
|
|
1680
|
+
>
|
|
1681
|
+
PDF preview is not supported in this browser.
|
|
1682
|
+
</object>
|
|
1683
|
+
<div *ngIf="!previewUrl && !loading && !discovering && !error && sources.length === 0" class="preview-empty">
|
|
1684
|
+
No previewable documents found
|
|
1685
|
+
</div>
|
|
1686
|
+
</div>
|
|
1687
|
+
</div>
|
|
1688
|
+
`, isInline: true, styles: [".epistola-preview-panel{border:1px solid #dee2e6;border-radius:4px;background:#f8f9fa;display:flex;flex-direction:column}.preview-header{display:flex;justify-content:space-between;align-items:center;padding:.5rem 1rem;border-bottom:1px solid #dee2e6;font-weight:700;color:#495057;flex-wrap:wrap;gap:.5rem}.preview-controls{display:flex;align-items:center;gap:.5rem}.preview-select{border:1px solid #ced4da;border-radius:4px;padding:.25rem .5rem;font-size:.8rem;background:#fff;max-width:300px}.preview-refresh{background:none;border:1px solid #6c757d;border-radius:4px;color:#6c757d;padding:.25rem .75rem;font-size:.8rem;cursor:pointer;display:flex;align-items:center;white-space:nowrap}.preview-refresh:hover:not(:disabled){background:#e9ecef}.preview-refresh:disabled{opacity:.5;cursor:not-allowed}.preview-body{display:flex;flex-direction:column;min-height:500px}.preview-loading{padding:2rem;text-align:center;color:#6c757d;font-style:italic}.preview-unavailable{padding:1.5rem;text-align:center;color:#6c757d;font-style:italic}.preview-unavailable i{margin-right:.25rem}.preview-pdf{width:100%;flex:1;min-height:500px}.preview-empty{padding:2rem;text-align:center;color:#6c757d;font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1689
|
+
}
|
|
1690
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaDocumentPreviewComponent, decorators: [{
|
|
1691
|
+
type: Component,
|
|
1692
|
+
args: [{ standalone: true, imports: [CommonModule], selector: 'epistola-document-preview-component', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1693
|
+
<div class="epistola-preview-panel">
|
|
1694
|
+
<div class="preview-header">
|
|
1695
|
+
<span>{{ label || 'Document Preview' }}</span>
|
|
1696
|
+
<div class="preview-controls">
|
|
1697
|
+
<select
|
|
1698
|
+
*ngIf="sources.length > 1"
|
|
1699
|
+
class="preview-select"
|
|
1700
|
+
[value]="selectedIndex"
|
|
1701
|
+
(change)="onSourceChange($event)"
|
|
1702
|
+
>
|
|
1703
|
+
<option *ngFor="let source of sources; let i = index" [value]="i">
|
|
1704
|
+
{{ source.templateName }} ({{ source.activityId }})
|
|
1705
|
+
</option>
|
|
1706
|
+
</select>
|
|
1707
|
+
<button type="button" class="preview-refresh" [disabled]="loading || discovering" (click)="refresh()">
|
|
1708
|
+
<i class="mdi mdi-refresh mr-1"></i>
|
|
1709
|
+
{{ loading ? 'Generating...' : 'Refresh' }}
|
|
1710
|
+
</button>
|
|
1711
|
+
</div>
|
|
1712
|
+
</div>
|
|
1713
|
+
<div class="preview-body">
|
|
1714
|
+
<div *ngIf="discovering" class="preview-loading">
|
|
1715
|
+
Discovering documents...
|
|
1716
|
+
</div>
|
|
1717
|
+
<div *ngIf="loading && !discovering" class="preview-loading">
|
|
1718
|
+
Generating preview...
|
|
1719
|
+
</div>
|
|
1720
|
+
<div *ngIf="error && !loading && !discovering" class="preview-unavailable">
|
|
1721
|
+
<i class="mdi mdi-information-outline"></i>
|
|
1722
|
+
Preview is niet beschikbaar — niet alle gegevens zijn al ingevuld.
|
|
1723
|
+
</div>
|
|
1724
|
+
<object
|
|
1725
|
+
*ngIf="previewUrl && !loading && !discovering"
|
|
1726
|
+
[data]="previewUrl"
|
|
1727
|
+
type="application/pdf"
|
|
1728
|
+
class="preview-pdf"
|
|
1729
|
+
>
|
|
1730
|
+
PDF preview is not supported in this browser.
|
|
1731
|
+
</object>
|
|
1732
|
+
<div *ngIf="!previewUrl && !loading && !discovering && !error && sources.length === 0" class="preview-empty">
|
|
1733
|
+
No previewable documents found
|
|
1734
|
+
</div>
|
|
1735
|
+
</div>
|
|
1736
|
+
</div>
|
|
1737
|
+
`, styles: [".epistola-preview-panel{border:1px solid #dee2e6;border-radius:4px;background:#f8f9fa;display:flex;flex-direction:column}.preview-header{display:flex;justify-content:space-between;align-items:center;padding:.5rem 1rem;border-bottom:1px solid #dee2e6;font-weight:700;color:#495057;flex-wrap:wrap;gap:.5rem}.preview-controls{display:flex;align-items:center;gap:.5rem}.preview-select{border:1px solid #ced4da;border-radius:4px;padding:.25rem .5rem;font-size:.8rem;background:#fff;max-width:300px}.preview-refresh{background:none;border:1px solid #6c757d;border-radius:4px;color:#6c757d;padding:.25rem .75rem;font-size:.8rem;cursor:pointer;display:flex;align-items:center;white-space:nowrap}.preview-refresh:hover:not(:disabled){background:#e9ecef}.preview-refresh:disabled{opacity:.5;cursor:not-allowed}.preview-body{display:flex;flex-direction:column;min-height:500px}.preview-loading{padding:2rem;text-align:center;color:#6c757d;font-style:italic}.preview-unavailable{padding:1.5rem;text-align:center;color:#6c757d;font-style:italic}.preview-unavailable i{margin-right:.25rem}.preview-pdf{width:100%;flex:1;min-height:500px}.preview-empty{padding:2rem;text-align:center;color:#6c757d;font-style:italic}\n"] }]
|
|
1738
|
+
}], ctorParameters: () => [{ type: EpistolaPluginService }, { type: i1.HttpClient }, { type: i4.DomSanitizer }, { type: i2.ConfigService }, { type: i3.FormIoStateService }, { type: i0.ChangeDetectorRef }], propDecorators: { value: [{
|
|
1739
|
+
type: Input
|
|
1740
|
+
}], valueChange: [{
|
|
1741
|
+
type: Output
|
|
1742
|
+
}], disabled: [{
|
|
1743
|
+
type: Input
|
|
1744
|
+
}], label: [{
|
|
1745
|
+
type: Input
|
|
1746
|
+
}] } });
|
|
1747
|
+
|
|
1748
|
+
const EPISTOLA_DOWNLOAD_OPTIONS = {
|
|
1749
|
+
type: 'epistola-download',
|
|
1750
|
+
selector: 'epistola-download-button',
|
|
1751
|
+
title: 'Epistola Download',
|
|
1752
|
+
group: 'basic',
|
|
1753
|
+
icon: 'download',
|
|
1754
|
+
emptyValue: null,
|
|
1755
|
+
fieldOptions: ['filename', 'label'],
|
|
1756
|
+
};
|
|
1757
|
+
function registerEpistolaDownloadComponent(injector) {
|
|
1758
|
+
if (!customElements.get(EPISTOLA_DOWNLOAD_OPTIONS.selector)) {
|
|
1759
|
+
registerCustomFormioComponent(EPISTOLA_DOWNLOAD_OPTIONS, EpistolaDownloadComponent, injector);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
const EPISTOLA_RETRY_FORM_OPTIONS = {
|
|
1764
|
+
type: 'epistola-retry-form',
|
|
1765
|
+
selector: 'epistola-retry-form-element',
|
|
1766
|
+
title: 'Epistola Retry Form',
|
|
1767
|
+
group: 'basic',
|
|
1768
|
+
icon: 'refresh',
|
|
1769
|
+
emptyValue: null,
|
|
1770
|
+
fieldOptions: ['sourceActivityId', 'label'], // sourceActivityId is optional (set via BPMN input parameter)
|
|
1771
|
+
};
|
|
1772
|
+
function registerEpistolaRetryFormComponent(injector) {
|
|
1773
|
+
if (!customElements.get(EPISTOLA_RETRY_FORM_OPTIONS.selector)) {
|
|
1774
|
+
registerCustomFormioComponent(EPISTOLA_RETRY_FORM_OPTIONS, EpistolaRetryFormComponent, injector);
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
const EPISTOLA_PREVIEW_BUTTON_OPTIONS = {
|
|
1779
|
+
type: 'epistola-preview-button',
|
|
1780
|
+
selector: 'epistola-preview-button-element',
|
|
1781
|
+
title: 'Epistola Preview',
|
|
1782
|
+
group: 'basic',
|
|
1783
|
+
icon: 'eye',
|
|
1784
|
+
emptyValue: null,
|
|
1785
|
+
fieldOptions: ['label'],
|
|
1786
|
+
};
|
|
1787
|
+
function registerEpistolaPreviewButtonComponent(injector) {
|
|
1788
|
+
if (!customElements.get(EPISTOLA_PREVIEW_BUTTON_OPTIONS.selector)) {
|
|
1789
|
+
registerCustomFormioComponent(EPISTOLA_PREVIEW_BUTTON_OPTIONS, EpistolaPreviewButtonComponent, injector);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
const EPISTOLA_DOCUMENT_PREVIEW_OPTIONS = {
|
|
1794
|
+
type: 'epistola-document-preview',
|
|
1795
|
+
selector: 'epistola-document-preview-element',
|
|
1796
|
+
title: 'Epistola Document Preview',
|
|
1797
|
+
group: 'basic',
|
|
1798
|
+
icon: 'file-pdf-o',
|
|
1799
|
+
emptyValue: null,
|
|
1800
|
+
fieldOptions: ['label'],
|
|
1801
|
+
};
|
|
1802
|
+
function registerEpistolaDocumentPreviewComponent(injector) {
|
|
1803
|
+
if (!customElements.get(EPISTOLA_DOCUMENT_PREVIEW_OPTIONS.selector)) {
|
|
1804
|
+
registerCustomFormioComponent(EPISTOLA_DOCUMENT_PREVIEW_OPTIONS, EpistolaDocumentPreviewComponent, injector);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1497
1808
|
class EpistolaPluginModule {
|
|
1809
|
+
static forRoot() {
|
|
1810
|
+
return {
|
|
1811
|
+
ngModule: EpistolaPluginModule,
|
|
1812
|
+
providers: [
|
|
1813
|
+
{
|
|
1814
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
1815
|
+
multi: true,
|
|
1816
|
+
useValue: () => {
|
|
1817
|
+
const injector = inject(Injector);
|
|
1818
|
+
registerEpistolaDownloadComponent(injector);
|
|
1819
|
+
registerEpistolaRetryFormComponent(injector);
|
|
1820
|
+
registerEpistolaPreviewButtonComponent(injector);
|
|
1821
|
+
registerEpistolaDocumentPreviewComponent(injector);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
]
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1498
1827
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPluginModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1499
1828
|
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPluginModule, imports: [CommonModule,
|
|
1500
1829
|
HttpClientModule,
|
|
@@ -1507,18 +1836,26 @@ class EpistolaPluginModule {
|
|
|
1507
1836
|
CheckJobStatusConfigurationComponent,
|
|
1508
1837
|
DownloadDocumentConfigurationComponent,
|
|
1509
1838
|
DataMappingTreeComponent,
|
|
1839
|
+
ValueInputComponent,
|
|
1840
|
+
ScalarFieldComponent,
|
|
1841
|
+
ArrayFieldComponent,
|
|
1510
1842
|
FieldTreeComponent,
|
|
1511
1843
|
EpistolaDownloadComponent,
|
|
1512
1844
|
EpistolaRetryFormComponent,
|
|
1513
|
-
EpistolaPreviewButtonComponent
|
|
1845
|
+
EpistolaPreviewButtonComponent,
|
|
1846
|
+
EpistolaDocumentPreviewComponent], exports: [EpistolaConfigurationComponent,
|
|
1514
1847
|
GenerateDocumentConfigurationComponent,
|
|
1515
1848
|
CheckJobStatusConfigurationComponent,
|
|
1516
1849
|
DownloadDocumentConfigurationComponent,
|
|
1517
1850
|
DataMappingTreeComponent,
|
|
1851
|
+
ValueInputComponent,
|
|
1852
|
+
ScalarFieldComponent,
|
|
1853
|
+
ArrayFieldComponent,
|
|
1518
1854
|
FieldTreeComponent,
|
|
1519
1855
|
EpistolaDownloadComponent,
|
|
1520
1856
|
EpistolaRetryFormComponent,
|
|
1521
|
-
EpistolaPreviewButtonComponent
|
|
1857
|
+
EpistolaPreviewButtonComponent,
|
|
1858
|
+
EpistolaDocumentPreviewComponent] });
|
|
1522
1859
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPluginModule, providers: [
|
|
1523
1860
|
EpistolaPluginService
|
|
1524
1861
|
], imports: [CommonModule,
|
|
@@ -1532,10 +1869,14 @@ class EpistolaPluginModule {
|
|
|
1532
1869
|
CheckJobStatusConfigurationComponent,
|
|
1533
1870
|
DownloadDocumentConfigurationComponent,
|
|
1534
1871
|
DataMappingTreeComponent,
|
|
1872
|
+
ValueInputComponent,
|
|
1873
|
+
ScalarFieldComponent,
|
|
1874
|
+
ArrayFieldComponent,
|
|
1535
1875
|
FieldTreeComponent,
|
|
1536
1876
|
EpistolaDownloadComponent,
|
|
1537
1877
|
EpistolaRetryFormComponent,
|
|
1538
|
-
EpistolaPreviewButtonComponent
|
|
1878
|
+
EpistolaPreviewButtonComponent,
|
|
1879
|
+
EpistolaDocumentPreviewComponent] });
|
|
1539
1880
|
}
|
|
1540
1881
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPluginModule, decorators: [{
|
|
1541
1882
|
type: NgModule,
|
|
@@ -1552,10 +1893,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1552
1893
|
CheckJobStatusConfigurationComponent,
|
|
1553
1894
|
DownloadDocumentConfigurationComponent,
|
|
1554
1895
|
DataMappingTreeComponent,
|
|
1896
|
+
ValueInputComponent,
|
|
1897
|
+
ScalarFieldComponent,
|
|
1898
|
+
ArrayFieldComponent,
|
|
1555
1899
|
FieldTreeComponent,
|
|
1556
1900
|
EpistolaDownloadComponent,
|
|
1557
1901
|
EpistolaRetryFormComponent,
|
|
1558
|
-
EpistolaPreviewButtonComponent
|
|
1902
|
+
EpistolaPreviewButtonComponent,
|
|
1903
|
+
EpistolaDocumentPreviewComponent
|
|
1559
1904
|
],
|
|
1560
1905
|
exports: [
|
|
1561
1906
|
EpistolaConfigurationComponent,
|
|
@@ -1563,10 +1908,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1563
1908
|
CheckJobStatusConfigurationComponent,
|
|
1564
1909
|
DownloadDocumentConfigurationComponent,
|
|
1565
1910
|
DataMappingTreeComponent,
|
|
1911
|
+
ValueInputComponent,
|
|
1912
|
+
ScalarFieldComponent,
|
|
1913
|
+
ArrayFieldComponent,
|
|
1566
1914
|
FieldTreeComponent,
|
|
1567
1915
|
EpistolaDownloadComponent,
|
|
1568
1916
|
EpistolaRetryFormComponent,
|
|
1569
|
-
EpistolaPreviewButtonComponent
|
|
1917
|
+
EpistolaPreviewButtonComponent,
|
|
1918
|
+
EpistolaDocumentPreviewComponent
|
|
1570
1919
|
],
|
|
1571
1920
|
providers: [
|
|
1572
1921
|
EpistolaPluginService
|
|
@@ -1768,51 +2117,6 @@ const epistolaPluginSpecification = {
|
|
|
1768
2117
|
}
|
|
1769
2118
|
};
|
|
1770
2119
|
|
|
1771
|
-
const EPISTOLA_DOWNLOAD_OPTIONS = {
|
|
1772
|
-
type: 'epistola-download',
|
|
1773
|
-
selector: 'epistola-download-button',
|
|
1774
|
-
title: 'Epistola Download',
|
|
1775
|
-
group: 'basic',
|
|
1776
|
-
icon: 'download',
|
|
1777
|
-
emptyValue: null,
|
|
1778
|
-
fieldOptions: ['filename', 'label'],
|
|
1779
|
-
};
|
|
1780
|
-
function registerEpistolaDownloadComponent(injector) {
|
|
1781
|
-
if (!customElements.get(EPISTOLA_DOWNLOAD_OPTIONS.selector)) {
|
|
1782
|
-
registerCustomFormioComponent(EPISTOLA_DOWNLOAD_OPTIONS, EpistolaDownloadComponent, injector);
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
|
-
const EPISTOLA_RETRY_FORM_OPTIONS = {
|
|
1787
|
-
type: 'epistola-retry-form',
|
|
1788
|
-
selector: 'epistola-retry-form-element',
|
|
1789
|
-
title: 'Epistola Retry Form',
|
|
1790
|
-
group: 'basic',
|
|
1791
|
-
icon: 'refresh',
|
|
1792
|
-
emptyValue: null,
|
|
1793
|
-
fieldOptions: ['sourceActivityId', 'label'], // sourceActivityId is optional (set via BPMN input parameter)
|
|
1794
|
-
};
|
|
1795
|
-
function registerEpistolaRetryFormComponent(injector) {
|
|
1796
|
-
if (!customElements.get(EPISTOLA_RETRY_FORM_OPTIONS.selector)) {
|
|
1797
|
-
registerCustomFormioComponent(EPISTOLA_RETRY_FORM_OPTIONS, EpistolaRetryFormComponent, injector);
|
|
1798
|
-
}
|
|
1799
|
-
}
|
|
1800
|
-
|
|
1801
|
-
const EPISTOLA_PREVIEW_BUTTON_OPTIONS = {
|
|
1802
|
-
type: 'epistola-preview-button',
|
|
1803
|
-
selector: 'epistola-preview-button-element',
|
|
1804
|
-
title: 'Epistola Preview',
|
|
1805
|
-
group: 'basic',
|
|
1806
|
-
icon: 'eye',
|
|
1807
|
-
emptyValue: null,
|
|
1808
|
-
fieldOptions: ['label'],
|
|
1809
|
-
};
|
|
1810
|
-
function registerEpistolaPreviewButtonComponent(injector) {
|
|
1811
|
-
if (!customElements.get(EPISTOLA_PREVIEW_BUTTON_OPTIONS.selector)) {
|
|
1812
|
-
registerCustomFormioComponent(EPISTOLA_PREVIEW_BUTTON_OPTIONS, EpistolaPreviewButtonComponent, injector);
|
|
1813
|
-
}
|
|
1814
|
-
}
|
|
1815
|
-
|
|
1816
2120
|
/*
|
|
1817
2121
|
* Public API Surface of epistola plugin
|
|
1818
2122
|
*/
|
|
@@ -1821,5 +2125,5 @@ function registerEpistolaPreviewButtonComponent(injector) {
|
|
|
1821
2125
|
* Generated bundle index. Do not edit.
|
|
1822
2126
|
*/
|
|
1823
2127
|
|
|
1824
|
-
export { CheckJobStatusConfigurationComponent, DataMappingTreeComponent, DownloadDocumentConfigurationComponent, EPISTOLA_DOWNLOAD_OPTIONS, EPISTOLA_PREVIEW_BUTTON_OPTIONS, EPISTOLA_RETRY_FORM_OPTIONS, EpistolaConfigurationComponent, EpistolaDownloadComponent, EpistolaPluginModule, EpistolaPluginService, EpistolaPreviewButtonComponent, EpistolaRetryFormComponent, FieldTreeComponent, GenerateDocumentConfigurationComponent, epistolaPluginSpecification, registerEpistolaDownloadComponent, registerEpistolaPreviewButtonComponent, registerEpistolaRetryFormComponent };
|
|
2128
|
+
export { ArrayFieldComponent, CheckJobStatusConfigurationComponent, DataMappingTreeComponent, DownloadDocumentConfigurationComponent, EPISTOLA_DOCUMENT_PREVIEW_OPTIONS, EPISTOLA_DOWNLOAD_OPTIONS, EPISTOLA_PREVIEW_BUTTON_OPTIONS, EPISTOLA_RETRY_FORM_OPTIONS, EpistolaConfigurationComponent, EpistolaDocumentPreviewComponent, EpistolaDownloadComponent, EpistolaPluginModule, EpistolaPluginService, EpistolaPreviewButtonComponent, EpistolaRetryFormComponent, FieldTreeComponent, GenerateDocumentConfigurationComponent, ScalarFieldComponent, ValueInputComponent, epistolaPluginSpecification, errorResource, initialResource, loadingResource, normalizeToDots, registerEpistolaDocumentPreviewComponent, registerEpistolaDownloadComponent, registerEpistolaPreviewButtonComponent, registerEpistolaRetryFormComponent, successResource };
|
|
1825
2129
|
//# sourceMappingURL=epistola.app-valtimo-plugin.mjs.map
|