@epistola.app/valtimo-plugin 0.5.2 → 0.6.0
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 +1622 -508
- package/fesm2022/epistola.app-valtimo-plugin.mjs.map +1 -1
- package/lib/components/epistola-admin-page/epistola-admin-page.component.d.ts +52 -0
- package/lib/components/expected-structure/expected-structure.component.d.ts +11 -0
- package/lib/components/generate-document-configuration/generate-document-configuration.component.d.ts +10 -3
- package/lib/components/jsonata-editor/jsonata-editor.component.d.ts +29 -0
- package/lib/components/mapping-builder/builder-field/builder-field.component.d.ts +21 -0
- package/lib/components/mapping-builder/mapping-builder.component.d.ts +37 -0
- package/lib/components/mapping-preview/mapping-preview.component.d.ts +26 -0
- package/lib/epistola-admin-routing.module.d.ts +7 -0
- package/lib/epistola.module.d.ts +11 -14
- package/lib/models/admin.d.ts +49 -0
- package/lib/models/config.d.ts +10 -1
- package/lib/models/expression.d.ts +13 -0
- package/lib/models/index.d.ts +2 -0
- package/lib/models/template.d.ts +0 -6
- package/lib/services/epistola-admin.service.d.ts +37 -0
- package/lib/services/epistola-menu.service.d.ts +13 -0
- package/lib/services/epistola-plugin.service.d.ts +11 -3
- package/lib/services/index.d.ts +2 -0
- package/lib/utils/jsonata-converter.d.ts +26 -0
- package/lib/utils/jsonata-monaco.d.ts +14 -0
- package/package.json +7 -5
- package/public_api.d.ts +4 -5
- package/lib/components/array-field/array-field.component.d.ts +0 -27
- package/lib/components/data-mapping-tree/data-mapping-tree.component.d.ts +0 -27
- package/lib/components/field-tree/field-tree.component.d.ts +0 -27
- package/lib/components/scalar-field/scalar-field.component.d.ts +0 -16
- package/lib/components/value-input/value-input.component.d.ts +0 -34
- package/lib/utils/template-field-utils.d.ts +0 -6
|
@@ -1,22 +1,31 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, EventEmitter, Output, Input, Component, ChangeDetectionStrategy,
|
|
2
|
+
import { Injectable, EventEmitter, Output, Input, Component, ChangeDetectionStrategy, NgModule, ENVIRONMENT_INITIALIZER, inject, Injector } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common/http';
|
|
4
4
|
import { HttpHeaders, HttpClientModule } from '@angular/common/http';
|
|
5
5
|
import * as i2 from '@valtimo/shared';
|
|
6
|
+
import { ROLE_ADMIN } from '@valtimo/shared';
|
|
7
|
+
import { of, BehaviorSubject, combineLatest, take, Subject, debounceTime, takeUntil, merge } from 'rxjs';
|
|
8
|
+
import * as i3 from '@valtimo/components';
|
|
9
|
+
import { FormModule, InputModule, EditorModule, SelectModule, registerCustomFormioComponent } from '@valtimo/components';
|
|
6
10
|
import * as i1$1 from '@angular/common';
|
|
7
11
|
import { CommonModule } from '@angular/common';
|
|
8
12
|
import * as i2$1 from '@valtimo/plugin';
|
|
9
13
|
import { PluginTranslatePipeModule } from '@valtimo/plugin';
|
|
10
|
-
import
|
|
11
|
-
import { FormModule, InputModule, ValuePathSelectorPrefix, ValuePathSelectorComponent, SelectModule, registerCustomFormioComponent } from '@valtimo/components';
|
|
12
|
-
import { BehaviorSubject, combineLatest, take, Subject, of, merge } from 'rxjs';
|
|
13
|
-
import { startWith, delay, shareReplay, take as take$1, takeUntil, filter, map, distinctUntilChanged, tap, switchMap, catchError, debounceTime } from 'rxjs/operators';
|
|
14
|
+
import { startWith, delay, shareReplay, take as take$1, takeUntil as takeUntil$1, filter, map, distinctUntilChanged, tap, switchMap, catchError, debounceTime as debounceTime$1 } from 'rxjs/operators';
|
|
14
15
|
import * as i4 from '@angular/forms';
|
|
15
16
|
import { FormsModule } from '@angular/forms';
|
|
17
|
+
import * as _jsonata from 'jsonata';
|
|
16
18
|
import * as i2$2 from '@valtimo/process-link';
|
|
17
19
|
import * as i7 from '@formio/angular';
|
|
18
20
|
import { FormioModule } from '@formio/angular';
|
|
19
21
|
import * as i4$1 from '@angular/platform-browser';
|
|
22
|
+
import * as i2$3 from '@angular/router';
|
|
23
|
+
import { RouterModule } from '@angular/router';
|
|
24
|
+
import * as i5 from 'carbon-components-angular/tabs';
|
|
25
|
+
import { TabsModule } from 'carbon-components-angular/tabs';
|
|
26
|
+
import * as i6 from 'carbon-components-angular/tag';
|
|
27
|
+
import { TagModule } from 'carbon-components-angular/tag';
|
|
28
|
+
import { AuthGuardService } from '@valtimo/security';
|
|
20
29
|
|
|
21
30
|
function initialResource(empty) {
|
|
22
31
|
return { data: empty, loading: false, error: null };
|
|
@@ -31,6 +40,94 @@ function errorResource(current, error) {
|
|
|
31
40
|
return { data: current, loading: false, error };
|
|
32
41
|
}
|
|
33
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Service for Epistola plugin administrative operations.
|
|
45
|
+
* Provides health checks, version info, and usage overview.
|
|
46
|
+
*/
|
|
47
|
+
class EpistolaAdminService {
|
|
48
|
+
http;
|
|
49
|
+
configService;
|
|
50
|
+
apiEndpoint;
|
|
51
|
+
constructor(http, configService) {
|
|
52
|
+
this.http = http;
|
|
53
|
+
this.configService = configService;
|
|
54
|
+
this.apiEndpoint = `${this.configService.config.valtimoApi.endpointUri}v1/plugin/epistola/admin`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check connectivity to Epistola for all plugin configurations.
|
|
58
|
+
*/
|
|
59
|
+
getConnectionStatus() {
|
|
60
|
+
return this.http.get(`${this.apiEndpoint}/health`);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get version information for the plugin and connected Epistola server.
|
|
64
|
+
*/
|
|
65
|
+
getVersions() {
|
|
66
|
+
return this.http.get(`${this.apiEndpoint}/versions`);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get an overview of all Epistola plugin usages across process definitions.
|
|
70
|
+
*/
|
|
71
|
+
getPluginUsage() {
|
|
72
|
+
return this.http.get(`${this.apiEndpoint}/usage`);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get all process instances currently waiting for an Epistola document generation result.
|
|
76
|
+
*/
|
|
77
|
+
getPendingJobs() {
|
|
78
|
+
return this.http.get(`${this.apiEndpoint}/pending`);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Export a single process link as a .process-link.json file.
|
|
82
|
+
*/
|
|
83
|
+
exportProcessLink(processLinkId) {
|
|
84
|
+
return this.http.get(`${this.apiEndpoint}/export/${encodeURIComponent(processLinkId)}`, {
|
|
85
|
+
responseType: 'blob',
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminService, deps: [{ token: i1.HttpClient }, { token: i2.ConfigService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
89
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminService });
|
|
90
|
+
}
|
|
91
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminService, decorators: [{
|
|
92
|
+
type: Injectable
|
|
93
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.ConfigService }] });
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Registers the Epistola admin page menu item under the Admin > Other section.
|
|
97
|
+
* Instantiated eagerly via ENVIRONMENT_INITIALIZER so the menu item
|
|
98
|
+
* appears without any manual configuration in the host application.
|
|
99
|
+
*/
|
|
100
|
+
class EpistolaMenuService {
|
|
101
|
+
menuService;
|
|
102
|
+
constructor(menuService) {
|
|
103
|
+
this.menuService = menuService;
|
|
104
|
+
this.menuService.registerAppendMenuItemsFunction((items) => {
|
|
105
|
+
return of(items.map((item) => {
|
|
106
|
+
const isAdminMenu = item.roles?.includes(ROLE_ADMIN) && item.children;
|
|
107
|
+
if (!isAdminMenu) {
|
|
108
|
+
return item;
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
...item,
|
|
112
|
+
children: [
|
|
113
|
+
...item.children,
|
|
114
|
+
{
|
|
115
|
+
link: ['/epistola'],
|
|
116
|
+
title: 'Epistola',
|
|
117
|
+
sequence: 18,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
};
|
|
121
|
+
}));
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaMenuService, deps: [{ token: i3.MenuService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
125
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaMenuService });
|
|
126
|
+
}
|
|
127
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaMenuService, decorators: [{
|
|
128
|
+
type: Injectable
|
|
129
|
+
}], ctorParameters: () => [{ type: i3.MenuService }] });
|
|
130
|
+
|
|
34
131
|
/**
|
|
35
132
|
* Service for interacting with Epistola plugin API endpoints.
|
|
36
133
|
* Provides methods to fetch templates, environments, variants,
|
|
@@ -94,13 +191,32 @@ class EpistolaPluginService {
|
|
|
94
191
|
* Discover process variable names for a given process definition.
|
|
95
192
|
*/
|
|
96
193
|
getProcessVariables(processDefinitionKey) {
|
|
97
|
-
return this.http.get(`${this.apiEndpoint}/process-variables`, {
|
|
194
|
+
return this.http.get(`${this.apiEndpoint}/process-variables`, {
|
|
195
|
+
params: { processDefinitionKey },
|
|
196
|
+
});
|
|
98
197
|
}
|
|
99
198
|
/**
|
|
100
|
-
*
|
|
199
|
+
* Get variable suggestions for JSONata autocompletion.
|
|
101
200
|
*/
|
|
102
|
-
|
|
103
|
-
|
|
201
|
+
getVariableSuggestions(caseDefinitionKey, processDefinitionKey) {
|
|
202
|
+
const params = {};
|
|
203
|
+
if (caseDefinitionKey)
|
|
204
|
+
params['caseDefinitionKey'] = caseDefinitionKey;
|
|
205
|
+
if (processDefinitionKey)
|
|
206
|
+
params['processDefinitionKey'] = processDefinitionKey;
|
|
207
|
+
return this.http.get(`${this.apiEndpoint}/variable-suggestions`, {
|
|
208
|
+
params,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Evaluate a JSONata expression against a real document.
|
|
213
|
+
*/
|
|
214
|
+
evaluateMapping(expression, documentId, processInstanceId) {
|
|
215
|
+
return this.http.post(`${this.apiEndpoint}/evaluate-mapping`, {
|
|
216
|
+
expression,
|
|
217
|
+
documentId,
|
|
218
|
+
processInstanceId: processInstanceId ?? null,
|
|
219
|
+
});
|
|
104
220
|
}
|
|
105
221
|
/**
|
|
106
222
|
* Get a dynamically generated Formio form for retrying a failed document generation.
|
|
@@ -115,11 +231,19 @@ class EpistolaPluginService {
|
|
|
115
231
|
}
|
|
116
232
|
return this.http.get(`${this.apiEndpoint}/retry-form`, { params });
|
|
117
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* List all available expression functions for expr: data mapping values.
|
|
236
|
+
*/
|
|
237
|
+
getExpressionFunctions() {
|
|
238
|
+
return this.http.get(`${this.apiEndpoint}/expression-functions`);
|
|
239
|
+
}
|
|
118
240
|
/**
|
|
119
241
|
* Discover all previewable document sources for a given Valtimo document.
|
|
120
242
|
*/
|
|
121
243
|
getPreviewSources(documentId) {
|
|
122
|
-
return this.http.get(`${this.apiEndpoint}/preview-sources`, {
|
|
244
|
+
return this.http.get(`${this.apiEndpoint}/preview-sources`, {
|
|
245
|
+
params: { documentId },
|
|
246
|
+
});
|
|
123
247
|
}
|
|
124
248
|
/**
|
|
125
249
|
* Preview a document by dry-running the generate-document process link.
|
|
@@ -131,7 +255,7 @@ class EpistolaPluginService {
|
|
|
131
255
|
processDefinitionKey,
|
|
132
256
|
sourceActivityId,
|
|
133
257
|
processInstanceId: processInstanceId || null,
|
|
134
|
-
overrides: overrides || null
|
|
258
|
+
overrides: overrides || null,
|
|
135
259
|
});
|
|
136
260
|
}
|
|
137
261
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPluginService, deps: [{ token: i1.HttpClient }, { token: i2.ConfigService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
@@ -202,11 +326,11 @@ class EpistolaConfigurationComponent {
|
|
|
202
326
|
});
|
|
203
327
|
}
|
|
204
328
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaConfigurationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
205
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaConfigurationComponent, isStandalone: true, selector: "epistola-configuration", inputs: { save$: "save$", disabled$: "disabled$", pluginId: "pluginId", prefillConfiguration$: "prefillConfiguration$" }, outputs: { valid: "valid", configuration: "configuration" }, ngImport: i0, template: "<v-form\n
|
|
329
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaConfigurationComponent, isStandalone: true, selector: "epistola-configuration", inputs: { save$: "save$", disabled$: "disabled$", pluginId: "pluginId", prefillConfiguration$: "prefillConfiguration$" }, outputs: { valid: "valid", configuration: "configuration" }, ngImport: i0, template: "<v-form\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"{\n disabled: safeDisabled$ | async,\n prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,\n } as obs\"\n>\n <v-input\n name=\"configurationTitle\"\n [title]=\"'configurationTitle' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.configurationTitle\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"baseUrl\"\n [title]=\"'baseUrl' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'baseUrlTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.baseUrl\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"apiKey\"\n [title]=\"'apiKey' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'apiKeyTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.apiKey\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n type=\"password\"\n >\n </v-input>\n\n <v-input\n name=\"tenantId\"\n [title]=\"'tenantId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'tenantIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.tenantId\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"defaultEnvironmentId\"\n [title]=\"'defaultEnvironmentId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'defaultEnvironmentIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.defaultEnvironmentId\"\n [disabled]=\"obs.disabled\"\n [required]=\"false\"\n >\n </v-input>\n\n <v-input\n name=\"templateSyncEnabled\"\n type=\"checkbox\"\n [title]=\"'templateSyncEnabled' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'templateSyncEnabledTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.templateSyncEnabled ? 'true' : ''\"\n [disabled]=\"obs.disabled\"\n [required]=\"false\"\n >\n </v-input>\n</v-form>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: 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"] }] });
|
|
206
330
|
}
|
|
207
331
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaConfigurationComponent, decorators: [{
|
|
208
332
|
type: Component,
|
|
209
|
-
args: [{ selector: 'epistola-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form\n
|
|
333
|
+
args: [{ selector: 'epistola-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"{\n disabled: safeDisabled$ | async,\n prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,\n } as obs\"\n>\n <v-input\n name=\"configurationTitle\"\n [title]=\"'configurationTitle' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.configurationTitle\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"baseUrl\"\n [title]=\"'baseUrl' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'baseUrlTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.baseUrl\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"apiKey\"\n [title]=\"'apiKey' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'apiKeyTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.apiKey\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n type=\"password\"\n >\n </v-input>\n\n <v-input\n name=\"tenantId\"\n [title]=\"'tenantId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'tenantIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.tenantId\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"defaultEnvironmentId\"\n [title]=\"'defaultEnvironmentId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'defaultEnvironmentIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.defaultEnvironmentId\"\n [disabled]=\"obs.disabled\"\n [required]=\"false\"\n >\n </v-input>\n\n <v-input\n name=\"templateSyncEnabled\"\n type=\"checkbox\"\n [title]=\"'templateSyncEnabled' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'templateSyncEnabledTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.templateSyncEnabled ? 'true' : ''\"\n [disabled]=\"obs.disabled\"\n [required]=\"false\"\n >\n </v-input>\n</v-form>\n" }]
|
|
210
334
|
}], propDecorators: { save$: [{
|
|
211
335
|
type: Input
|
|
212
336
|
}], disabled$: [{
|
|
@@ -221,452 +345,1109 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
221
345
|
type: Output
|
|
222
346
|
}] } });
|
|
223
347
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
348
|
+
/**
|
|
349
|
+
* Shared state for the JSONata completion provider.
|
|
350
|
+
* Updated by the editor component when suggestions/functions change.
|
|
351
|
+
*/
|
|
352
|
+
const jsonataCompletionData = {
|
|
353
|
+
suggestions: null,
|
|
354
|
+
functions: [],
|
|
355
|
+
};
|
|
356
|
+
/**
|
|
357
|
+
* Register the JSONata language in Monaco editor.
|
|
358
|
+
* Call this once when Monaco is available (e.g., in editor component OnInit).
|
|
359
|
+
*/
|
|
360
|
+
function registerJsonataLanguage(monaco) {
|
|
361
|
+
// Only register once
|
|
362
|
+
if (monaco.languages.getLanguages().some((lang) => lang.id === 'jsonata')) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
monaco.languages.register({ id: 'jsonata' });
|
|
366
|
+
// Syntax highlighting via Monarch tokenizer
|
|
367
|
+
monaco.languages.setMonarchTokensProvider('jsonata', {
|
|
368
|
+
defaultToken: '',
|
|
369
|
+
tokenPostfix: '.jsonata',
|
|
370
|
+
keywords: ['true', 'false', 'null', 'in', 'and', 'or', 'not'],
|
|
371
|
+
operators: ['&', '?', ':', '=', '!=', '>', '<', '>=', '<=', '+', '-', '*', '/', '%', '~>'],
|
|
372
|
+
symbols: /[=><!~?:&|+\-*/^%]+/,
|
|
373
|
+
tokenizer: {
|
|
374
|
+
root: [
|
|
375
|
+
// Variables: $identifier
|
|
376
|
+
[/\$[a-zA-Z_]\w*/, 'variable'],
|
|
377
|
+
// Identifiers and keywords
|
|
378
|
+
[
|
|
379
|
+
/[a-zA-Z_]\w*/,
|
|
380
|
+
{
|
|
381
|
+
cases: {
|
|
382
|
+
'@keywords': 'keyword',
|
|
383
|
+
'@default': 'identifier',
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
// Whitespace
|
|
388
|
+
{ include: '@whitespace' },
|
|
389
|
+
// Strings
|
|
390
|
+
[/"([^"\\]|\\.)*$/, 'string.invalid'],
|
|
391
|
+
[/"/, 'string', '@string_double'],
|
|
392
|
+
[/'([^'\\]|\\.)*$/, 'string.invalid'],
|
|
393
|
+
[/'/, 'string', '@string_single'],
|
|
394
|
+
// Numbers
|
|
395
|
+
[/\d+(\.\d+)?([eE][-+]?\d+)?/, 'number'],
|
|
396
|
+
// Delimiters and operators
|
|
397
|
+
[/[{}()\[\]]/, '@brackets'],
|
|
398
|
+
[/[,;.]/, 'delimiter'],
|
|
399
|
+
[
|
|
400
|
+
/@symbols/,
|
|
401
|
+
{
|
|
402
|
+
cases: {
|
|
403
|
+
'@operators': 'operator',
|
|
404
|
+
'@default': '',
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
],
|
|
409
|
+
string_double: [
|
|
410
|
+
[/[^\\"]+/, 'string'],
|
|
411
|
+
[/\\./, 'string.escape'],
|
|
412
|
+
[/"/, 'string', '@pop'],
|
|
413
|
+
],
|
|
414
|
+
string_single: [
|
|
415
|
+
[/[^\\']+/, 'string'],
|
|
416
|
+
[/\\./, 'string.escape'],
|
|
417
|
+
[/'/, 'string', '@pop'],
|
|
418
|
+
],
|
|
419
|
+
whitespace: [[/[ \t\r\n]+/, 'white']],
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
// Autocomplete provider
|
|
423
|
+
monaco.languages.registerCompletionItemProvider('jsonata', {
|
|
424
|
+
triggerCharacters: ['$', '.'],
|
|
425
|
+
provideCompletionItems: (model, position) => {
|
|
426
|
+
const textUntilPosition = model.getValueInRange({
|
|
427
|
+
startLineNumber: position.lineNumber,
|
|
428
|
+
startColumn: 1,
|
|
429
|
+
endLineNumber: position.lineNumber,
|
|
430
|
+
endColumn: position.column,
|
|
431
|
+
});
|
|
432
|
+
const suggestions = [];
|
|
433
|
+
const CompletionItemKind = monaco.languages.CompletionItemKind;
|
|
434
|
+
// After "$" — suggest variables and functions
|
|
435
|
+
if (textUntilPosition.endsWith('$')) {
|
|
436
|
+
suggestions.push(...['doc', 'pv', 'case'].map((v) => ({
|
|
437
|
+
label: `$${v}`,
|
|
438
|
+
kind: CompletionItemKind.Variable,
|
|
439
|
+
insertText: v,
|
|
440
|
+
detail: `Context variable`,
|
|
441
|
+
})));
|
|
442
|
+
// Custom functions
|
|
443
|
+
for (const func of jsonataCompletionData.functions) {
|
|
444
|
+
suggestions.push({
|
|
445
|
+
label: `$${func.name}`,
|
|
446
|
+
kind: CompletionItemKind.Function,
|
|
447
|
+
insertText: `${func.name}($0)`,
|
|
448
|
+
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
449
|
+
detail: func.description || 'Custom function',
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
// Built-in JSONata functions
|
|
453
|
+
const builtins = [
|
|
454
|
+
'string',
|
|
455
|
+
'number',
|
|
456
|
+
'boolean',
|
|
457
|
+
'length',
|
|
458
|
+
'substring',
|
|
459
|
+
'uppercase',
|
|
460
|
+
'lowercase',
|
|
461
|
+
'trim',
|
|
462
|
+
'contains',
|
|
463
|
+
'split',
|
|
464
|
+
'join',
|
|
465
|
+
'sum',
|
|
466
|
+
'count',
|
|
467
|
+
'max',
|
|
468
|
+
'min',
|
|
469
|
+
'average',
|
|
470
|
+
'append',
|
|
471
|
+
'sort',
|
|
472
|
+
'reverse',
|
|
473
|
+
'keys',
|
|
474
|
+
'values',
|
|
475
|
+
'lookup',
|
|
476
|
+
'now',
|
|
477
|
+
'exists',
|
|
478
|
+
'type',
|
|
479
|
+
'not',
|
|
480
|
+
];
|
|
481
|
+
for (const fn of builtins) {
|
|
482
|
+
suggestions.push({
|
|
483
|
+
label: `$${fn}`,
|
|
484
|
+
kind: CompletionItemKind.Function,
|
|
485
|
+
insertText: `${fn}($0)`,
|
|
486
|
+
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
487
|
+
detail: 'Built-in JSONata function',
|
|
488
|
+
});
|
|
489
|
+
}
|
|
233
490
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
491
|
+
// After "$doc." — suggest document paths
|
|
492
|
+
if (/\$doc\.\s*$/.test(textUntilPosition) || /\$doc\.[a-zA-Z_]*$/.test(textUntilPosition)) {
|
|
493
|
+
const docPaths = jsonataCompletionData.suggestions?.doc || [];
|
|
494
|
+
for (const path of docPaths) {
|
|
495
|
+
suggestions.push({
|
|
496
|
+
label: path,
|
|
497
|
+
kind: CompletionItemKind.Field,
|
|
498
|
+
insertText: path,
|
|
499
|
+
detail: 'Document field',
|
|
500
|
+
});
|
|
501
|
+
}
|
|
240
502
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
503
|
+
// After "$pv." — suggest process variables
|
|
504
|
+
if (/\$pv\.\s*$/.test(textUntilPosition) || /\$pv\.[a-zA-Z_]*$/.test(textUntilPosition)) {
|
|
505
|
+
const pvNames = jsonataCompletionData.suggestions?.pv || [];
|
|
506
|
+
for (const name of pvNames) {
|
|
507
|
+
suggestions.push({
|
|
508
|
+
label: name,
|
|
509
|
+
kind: CompletionItemKind.Variable,
|
|
510
|
+
insertText: name,
|
|
511
|
+
detail: 'Process variable',
|
|
512
|
+
});
|
|
244
513
|
}
|
|
245
514
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
? mapping[field.name]
|
|
250
|
-
: {};
|
|
251
|
-
const childStats = countRequiredMapped(field.children, nested);
|
|
252
|
-
mapped += childStats.mapped;
|
|
253
|
-
total += childStats.total;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return { mapped, total };
|
|
515
|
+
return { suggestions };
|
|
516
|
+
},
|
|
517
|
+
});
|
|
257
518
|
}
|
|
258
519
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
*/
|
|
263
|
-
class ValueInputComponent {
|
|
264
|
-
cdr;
|
|
265
|
-
name = '';
|
|
266
|
-
value = '';
|
|
267
|
-
pluginId = '';
|
|
268
|
-
caseDefinitionKey = null;
|
|
269
|
-
processVariables = [];
|
|
520
|
+
const jsonata$1 = _jsonata.default || _jsonata;
|
|
521
|
+
class JsonataEditorComponent {
|
|
522
|
+
expression = '';
|
|
270
523
|
disabled = false;
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
524
|
+
suggestions = null;
|
|
525
|
+
functions = [];
|
|
526
|
+
expressionChange = new EventEmitter();
|
|
527
|
+
validChange = new EventEmitter();
|
|
528
|
+
editorModel = { value: '', language: 'jsonata' };
|
|
529
|
+
editorOptions = {
|
|
530
|
+
minimap: { enabled: false },
|
|
531
|
+
lineNumbers: 'on',
|
|
532
|
+
scrollBeyondLastLine: false,
|
|
533
|
+
fontSize: 13,
|
|
534
|
+
tabSize: 2,
|
|
535
|
+
wordWrap: 'on',
|
|
536
|
+
renderWhitespace: 'none',
|
|
537
|
+
};
|
|
538
|
+
error = null;
|
|
539
|
+
destroy$ = new Subject();
|
|
540
|
+
validate$ = new Subject();
|
|
541
|
+
suppressChange = false;
|
|
542
|
+
languageRegistered = false;
|
|
543
|
+
constructor() {
|
|
544
|
+
this.validate$.pipe(debounceTime(300), takeUntil(this.destroy$)).subscribe((value) => {
|
|
545
|
+
this.validateExpression(value);
|
|
546
|
+
});
|
|
547
|
+
// Try to register language eagerly if Monaco is already loaded
|
|
548
|
+
this.tryRegisterLanguage();
|
|
279
549
|
}
|
|
280
550
|
ngOnChanges(changes) {
|
|
281
|
-
if (changes['
|
|
282
|
-
this.
|
|
283
|
-
this.
|
|
284
|
-
this.selectedPv = extractPvName(this.value);
|
|
551
|
+
if (changes['expression'] && !this.suppressChange) {
|
|
552
|
+
this.editorModel = { value: this.expression || '', language: 'jsonata' };
|
|
553
|
+
this.validate$.next(this.expression);
|
|
285
554
|
}
|
|
286
|
-
if (changes['
|
|
287
|
-
this.
|
|
555
|
+
if (changes['suggestions']) {
|
|
556
|
+
jsonataCompletionData.suggestions = this.suggestions;
|
|
557
|
+
}
|
|
558
|
+
if (changes['functions']) {
|
|
559
|
+
jsonataCompletionData.functions = this.functions;
|
|
288
560
|
}
|
|
289
561
|
}
|
|
290
|
-
|
|
291
|
-
this.
|
|
292
|
-
|
|
293
|
-
onBrowseValueChange(newValue) {
|
|
294
|
-
this.valueChange.emit(normalizeToDots(newValue));
|
|
295
|
-
}
|
|
296
|
-
onPvChange(newValue) {
|
|
297
|
-
this.selectedPv = newValue;
|
|
298
|
-
this.valueChange.emit(newValue ? 'pv:' + newValue : '');
|
|
562
|
+
ngOnDestroy() {
|
|
563
|
+
this.destroy$.next();
|
|
564
|
+
this.destroy$.complete();
|
|
299
565
|
}
|
|
300
|
-
|
|
301
|
-
|
|
566
|
+
onEditorValueChange(value) {
|
|
567
|
+
// Register language on first editor event (Monaco is now loaded)
|
|
568
|
+
if (!this.languageRegistered) {
|
|
569
|
+
this.tryRegisterLanguage();
|
|
570
|
+
}
|
|
571
|
+
if (this.suppressChange)
|
|
572
|
+
return;
|
|
573
|
+
this.suppressChange = true;
|
|
574
|
+
this.expression = value;
|
|
575
|
+
this.expressionChange.emit(value);
|
|
576
|
+
this.validate$.next(value);
|
|
577
|
+
setTimeout(() => (this.suppressChange = false));
|
|
578
|
+
}
|
|
579
|
+
tryRegisterLanguage() {
|
|
580
|
+
const m = window.monaco;
|
|
581
|
+
if (m) {
|
|
582
|
+
registerJsonataLanguage(m);
|
|
583
|
+
this.languageRegistered = true;
|
|
584
|
+
jsonataCompletionData.suggestions = this.suggestions;
|
|
585
|
+
jsonataCompletionData.functions = this.functions;
|
|
586
|
+
}
|
|
302
587
|
}
|
|
303
|
-
|
|
304
|
-
if (!value)
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
588
|
+
validateExpression(value) {
|
|
589
|
+
if (!value || !value.trim()) {
|
|
590
|
+
this.error = null;
|
|
591
|
+
this.validChange.emit(true);
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
try {
|
|
595
|
+
jsonata$1(value);
|
|
596
|
+
this.error = null;
|
|
597
|
+
this.validChange.emit(true);
|
|
598
|
+
}
|
|
599
|
+
catch (e) {
|
|
600
|
+
this.error = e.message || 'Invalid expression';
|
|
601
|
+
this.validChange.emit(false);
|
|
602
|
+
}
|
|
313
603
|
}
|
|
314
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type:
|
|
315
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type:
|
|
604
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: JsonataEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
605
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: JsonataEditorComponent, isStandalone: true, selector: "epistola-jsonata-editor", inputs: { expression: "expression", disabled: "disabled", suggestions: "suggestions", functions: "functions" }, outputs: { expressionChange: "expressionChange", validChange: "validChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
606
|
+
<div class="jsonata-editor">
|
|
607
|
+
<valtimo-editor
|
|
608
|
+
[model]="editorModel"
|
|
609
|
+
[editorOptions]="editorOptions"
|
|
610
|
+
[disabled]="disabled"
|
|
611
|
+
[heightPx]="250"
|
|
612
|
+
[formatOnLoad]="false"
|
|
613
|
+
(valueChangeEvent)="onEditorValueChange($event)"
|
|
614
|
+
></valtimo-editor>
|
|
615
|
+
<div class="jsonata-editor__footer">
|
|
616
|
+
<span *ngIf="error" class="jsonata-editor__error">{{ error }}</span>
|
|
617
|
+
<span *ngIf="!error && expression" class="jsonata-editor__valid">✓</span>
|
|
618
|
+
<span class="jsonata-editor__variables">$doc · $pv · $case</span>
|
|
619
|
+
</div>
|
|
620
|
+
</div>
|
|
621
|
+
`, isInline: true, styles: [".jsonata-editor__footer{display:flex;align-items:center;gap:8px;margin-top:4px;font-size:.8em}.jsonata-editor__error{color:#da1e28}.jsonata-editor__valid{color:#198038}.jsonata-editor__variables{margin-left:auto;color:#8d8d8d;font-family:monospace}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: PluginTranslatePipeModule }, { kind: "ngmodule", type: EditorModule }, { kind: "component", type: i3.EditorComponent, selector: "valtimo-editor", inputs: ["editorOptions", "model", "disabled", "formatOnLoad", "widthPx", "heightPx", "heightStyle", "jsonSchema", "fitPage", "fitPageSpaceAdjustment"], outputs: ["validEvent", "valueChangeEvent"] }] });
|
|
316
622
|
}
|
|
317
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type:
|
|
623
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: JsonataEditorComponent, decorators: [{
|
|
318
624
|
type: Component,
|
|
319
|
-
args: [{ selector: 'epistola-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
625
|
+
args: [{ selector: 'epistola-jsonata-editor', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, EditorModule], template: `
|
|
626
|
+
<div class="jsonata-editor">
|
|
627
|
+
<valtimo-editor
|
|
628
|
+
[model]="editorModel"
|
|
629
|
+
[editorOptions]="editorOptions"
|
|
630
|
+
[disabled]="disabled"
|
|
631
|
+
[heightPx]="250"
|
|
632
|
+
[formatOnLoad]="false"
|
|
633
|
+
(valueChangeEvent)="onEditorValueChange($event)"
|
|
634
|
+
></valtimo-editor>
|
|
635
|
+
<div class="jsonata-editor__footer">
|
|
636
|
+
<span *ngIf="error" class="jsonata-editor__error">{{ error }}</span>
|
|
637
|
+
<span *ngIf="!error && expression" class="jsonata-editor__valid">✓</span>
|
|
638
|
+
<span class="jsonata-editor__variables">$doc · $pv · $case</span>
|
|
639
|
+
</div>
|
|
640
|
+
</div>
|
|
641
|
+
`, styles: [".jsonata-editor__footer{display:flex;align-items:center;gap:8px;margin-top:4px;font-size:.8em}.jsonata-editor__error{color:#da1e28}.jsonata-editor__valid{color:#198038}.jsonata-editor__variables{margin-left:auto;color:#8d8d8d;font-family:monospace}\n"] }]
|
|
642
|
+
}], ctorParameters: () => [], propDecorators: { expression: [{
|
|
335
643
|
type: Input
|
|
336
644
|
}], disabled: [{
|
|
337
645
|
type: Input
|
|
338
|
-
}],
|
|
646
|
+
}], suggestions: [{
|
|
339
647
|
type: Input
|
|
340
|
-
}],
|
|
648
|
+
}], functions: [{
|
|
649
|
+
type: Input
|
|
650
|
+
}], expressionChange: [{
|
|
651
|
+
type: Output
|
|
652
|
+
}], validChange: [{
|
|
341
653
|
type: Output
|
|
342
654
|
}] } });
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const path = value.substring(colonIndex + 1);
|
|
352
|
-
if (!path.includes('/'))
|
|
353
|
-
return value;
|
|
354
|
-
const normalized = path.split('/').filter(p => p.length > 0).join('.');
|
|
355
|
-
return `${prefix}:${normalized}`;
|
|
356
|
-
}
|
|
357
|
-
function extractPvName(value) {
|
|
358
|
-
if (typeof value === 'string' && value.startsWith('pv:')) {
|
|
359
|
-
return value.substring(3);
|
|
655
|
+
|
|
656
|
+
class ExpectedStructureComponent {
|
|
657
|
+
templateFields = [];
|
|
658
|
+
structureText = '{}';
|
|
659
|
+
ngOnChanges(changes) {
|
|
660
|
+
if (changes['templateFields']) {
|
|
661
|
+
this.structureText = this.buildStructure(this.templateFields, 0);
|
|
662
|
+
}
|
|
360
663
|
}
|
|
361
|
-
|
|
664
|
+
buildStructure(fields, depth) {
|
|
665
|
+
if (!fields || fields.length === 0)
|
|
666
|
+
return '{}';
|
|
667
|
+
const indent = ' '.repeat(depth + 1);
|
|
668
|
+
const closing = ' '.repeat(depth);
|
|
669
|
+
const lines = fields.map((f) => {
|
|
670
|
+
const req = f.required ? ' (required)' : '';
|
|
671
|
+
if (f.fieldType === 'OBJECT' && f.children?.length) {
|
|
672
|
+
const nested = this.buildStructure(f.children, depth + 1);
|
|
673
|
+
return `${indent}"${f.name}": ${nested}${req}`;
|
|
674
|
+
}
|
|
675
|
+
if (f.fieldType === 'ARRAY') {
|
|
676
|
+
if (f.children?.length) {
|
|
677
|
+
const itemStructure = this.buildStructure(f.children, depth + 2);
|
|
678
|
+
return `${indent}"${f.name}": [${itemStructure}]${req}`;
|
|
679
|
+
}
|
|
680
|
+
return `${indent}"${f.name}": array${req}`;
|
|
681
|
+
}
|
|
682
|
+
return `${indent}"${f.name}": ${f.type || 'any'}${req}`;
|
|
683
|
+
});
|
|
684
|
+
return `{\n${lines.join(',\n')}\n${closing}}`;
|
|
685
|
+
}
|
|
686
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ExpectedStructureComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
687
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: ExpectedStructureComponent, isStandalone: true, selector: "epistola-expected-structure", inputs: { templateFields: "templateFields" }, usesOnChanges: true, ngImport: i0, template: `
|
|
688
|
+
<div class="expected">
|
|
689
|
+
<div class="expected__header">
|
|
690
|
+
{{ 'expectedStructure' | pluginTranslate: 'epistola' | async }}
|
|
691
|
+
</div>
|
|
692
|
+
<div *ngIf="!templateFields || templateFields.length === 0" class="expected__empty">
|
|
693
|
+
{{ 'expectedStructureLoading' | pluginTranslate: 'epistola' | async }}
|
|
694
|
+
</div>
|
|
695
|
+
<pre *ngIf="templateFields && templateFields.length > 0" class="expected__code">{{
|
|
696
|
+
structureText
|
|
697
|
+
}}</pre>
|
|
698
|
+
</div>
|
|
699
|
+
`, isInline: true, styles: [".expected{border:1px solid #e0e0e0;border-radius:4px;overflow:hidden;height:100%;display:flex;flex-direction:column}.expected__header{padding:6px 12px;background:#f4f4f4;border-bottom:1px solid #e0e0e0;font-size:.75em;color:#6f6f6f;text-transform:uppercase;letter-spacing:.5px}.expected__code{flex:1;font-family:IBM Plex Mono,monospace;font-size:.8em;line-height:1.5;margin:0;padding:8px 12px;white-space:pre-wrap;overflow-y:auto}.expected__empty{padding:8px 12px;color:#8d8d8d;font-size:.85em;font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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" }] });
|
|
362
700
|
}
|
|
701
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ExpectedStructureComponent, decorators: [{
|
|
702
|
+
type: Component,
|
|
703
|
+
args: [{ selector: 'epistola-expected-structure', standalone: true, imports: [CommonModule, PluginTranslatePipeModule], template: `
|
|
704
|
+
<div class="expected">
|
|
705
|
+
<div class="expected__header">
|
|
706
|
+
{{ 'expectedStructure' | pluginTranslate: 'epistola' | async }}
|
|
707
|
+
</div>
|
|
708
|
+
<div *ngIf="!templateFields || templateFields.length === 0" class="expected__empty">
|
|
709
|
+
{{ 'expectedStructureLoading' | pluginTranslate: 'epistola' | async }}
|
|
710
|
+
</div>
|
|
711
|
+
<pre *ngIf="templateFields && templateFields.length > 0" class="expected__code">{{
|
|
712
|
+
structureText
|
|
713
|
+
}}</pre>
|
|
714
|
+
</div>
|
|
715
|
+
`, styles: [".expected{border:1px solid #e0e0e0;border-radius:4px;overflow:hidden;height:100%;display:flex;flex-direction:column}.expected__header{padding:6px 12px;background:#f4f4f4;border-bottom:1px solid #e0e0e0;font-size:.75em;color:#6f6f6f;text-transform:uppercase;letter-spacing:.5px}.expected__code{flex:1;font-family:IBM Plex Mono,monospace;font-size:.8em;line-height:1.5;margin:0;padding:8px 12px;white-space:pre-wrap;overflow-y:auto}.expected__empty{padding:8px 12px;color:#8d8d8d;font-size:.85em;font-style:italic}\n"] }]
|
|
716
|
+
}], propDecorators: { templateFields: [{
|
|
717
|
+
type: Input
|
|
718
|
+
}] } });
|
|
363
719
|
|
|
364
|
-
class
|
|
720
|
+
class BuilderFieldComponent {
|
|
365
721
|
field;
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
caseDefinitionKey = null;
|
|
369
|
-
processVariables = [];
|
|
722
|
+
path = [];
|
|
723
|
+
suggestions = [];
|
|
370
724
|
disabled = false;
|
|
725
|
+
collapsed = false;
|
|
726
|
+
required = false;
|
|
727
|
+
collapsedPaths = new Set();
|
|
371
728
|
valueChange = new EventEmitter();
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
static
|
|
379
|
-
|
|
729
|
+
modeToggle = new EventEmitter();
|
|
730
|
+
collapseToggle = new EventEmitter();
|
|
731
|
+
isChildCollapsed(childIndex) {
|
|
732
|
+
return this.collapsedPaths.has(this.path.concat(childIndex).join('.'));
|
|
733
|
+
}
|
|
734
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: BuilderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
735
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: BuilderFieldComponent, isStandalone: true, selector: "epistola-builder-field", inputs: { field: "field", path: "path", suggestions: "suggestions", disabled: "disabled", collapsed: "collapsed", required: "required", collapsedPaths: "collapsedPaths" }, outputs: { valueChange: "valueChange", modeToggle: "modeToggle", collapseToggle: "collapseToggle" }, ngImport: i0, template: `
|
|
736
|
+
<div class="builder-field">
|
|
737
|
+
<div
|
|
738
|
+
class="builder-field__name"
|
|
739
|
+
[class.builder-field__name--clickable]="field.children"
|
|
740
|
+
(click)="field.children && collapseToggle.emit(path)"
|
|
741
|
+
>
|
|
742
|
+
<span *ngIf="field.children" class="builder-field__chevron">{{
|
|
743
|
+
collapsed ? '▶' : '▼'
|
|
744
|
+
}}</span>
|
|
745
|
+
<span class="builder-field__label">{{ field.name }}</span>
|
|
746
|
+
<span *ngIf="required" class="builder-field__required">*</span>
|
|
747
|
+
<span *ngIf="field.children" class="builder-field__type">(object)</span>
|
|
748
|
+
</div>
|
|
749
|
+
|
|
750
|
+
<div class="builder-field__value" *ngIf="!field.children">
|
|
751
|
+
<input
|
|
752
|
+
*ngIf="field.mode === 'ref'"
|
|
753
|
+
type="text"
|
|
754
|
+
class="builder-field__input"
|
|
755
|
+
[ngModel]="field.value"
|
|
756
|
+
(ngModelChange)="valueChange.emit({ path: path, value: $event })"
|
|
757
|
+
[disabled]="disabled"
|
|
758
|
+
placeholder="$doc.path.to.field"
|
|
759
|
+
[attr.list]="'suggestions-' + path.join('-')"
|
|
760
|
+
/>
|
|
761
|
+
<datalist *ngIf="field.mode === 'ref'" [id]="'suggestions-' + path.join('-')">
|
|
762
|
+
<option *ngFor="let s of suggestions" [value]="s"></option>
|
|
763
|
+
</datalist>
|
|
764
|
+
<input
|
|
765
|
+
*ngIf="field.mode === 'raw'"
|
|
766
|
+
type="text"
|
|
767
|
+
class="builder-field__input builder-field__input--raw"
|
|
768
|
+
[ngModel]="field.value"
|
|
769
|
+
(ngModelChange)="valueChange.emit({ path: path, value: $event })"
|
|
770
|
+
[disabled]="disabled"
|
|
771
|
+
placeholder="JSONata expression"
|
|
772
|
+
/>
|
|
773
|
+
<button
|
|
774
|
+
class="builder-field__mode-toggle"
|
|
775
|
+
(click)="modeToggle.emit(path)"
|
|
776
|
+
[disabled]="disabled"
|
|
777
|
+
[title]="field.mode === 'ref' ? 'Switch to raw JSONata' : 'Switch to reference'"
|
|
778
|
+
>
|
|
779
|
+
{{ field.mode === 'ref' ? 'fx' : '·' }}
|
|
780
|
+
</button>
|
|
781
|
+
</div>
|
|
782
|
+
|
|
783
|
+
<div *ngIf="field.children && !collapsed" class="builder-field__children">
|
|
784
|
+
<epistola-builder-field
|
|
785
|
+
*ngFor="let child of field.children; let j = index"
|
|
786
|
+
[field]="child"
|
|
787
|
+
[path]="path.concat(j)"
|
|
788
|
+
[suggestions]="suggestions"
|
|
789
|
+
[disabled]="disabled"
|
|
790
|
+
[collapsed]="isChildCollapsed(j)"
|
|
791
|
+
[collapsedPaths]="collapsedPaths"
|
|
792
|
+
[required]="false"
|
|
793
|
+
(valueChange)="valueChange.emit($event)"
|
|
794
|
+
(modeToggle)="modeToggle.emit($event)"
|
|
795
|
+
(collapseToggle)="collapseToggle.emit($event)"
|
|
796
|
+
></epistola-builder-field>
|
|
797
|
+
</div>
|
|
798
|
+
</div>
|
|
799
|
+
`, isInline: true, styles: [".builder-field{margin-bottom:4px}.builder-field__name{margin-bottom:2px}.builder-field__name--clickable{cursor:pointer;-webkit-user-select:none;user-select:none}.builder-field__name--clickable:hover{color:#0f62fe}.builder-field__chevron{font-size:.7em;margin-right:4px}.builder-field__label{font-weight:500;font-size:.9em}.builder-field__required{color:#da1e28;margin-left:2px}.builder-field__type{color:#8d8d8d;font-size:.8em;margin-left:4px}.builder-field__value{display:flex;align-items:center;gap:4px}.builder-field__input{flex:1;padding:6px 8px;border:1px solid #e0e0e0;border-radius:4px;font-size:.85em;font-family:IBM Plex Mono,monospace}.builder-field__input:focus{outline:2px solid #0f62fe;border-color:#0f62fe}.builder-field__input--raw{background:#f4f4f4}.builder-field__mode-toggle{width:28px;height:28px;border:1px solid #e0e0e0;border-radius:4px;background:#fff;cursor:pointer;font-family:monospace;font-size:.8em;display:flex;align-items:center;justify-content:center}.builder-field__mode-toggle:hover{background:#f4f4f4}.builder-field__children{border-left:2px solid #e0e0e0;padding-left:12px;margin-top:4px}\n"], dependencies: [{ kind: "component", type: BuilderFieldComponent, selector: "epistola-builder-field", inputs: ["field", "path", "suggestions", "disabled", "collapsed", "required", "collapsedPaths"], outputs: ["valueChange", "modeToggle", "collapseToggle"] }, { 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: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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"] }] });
|
|
380
800
|
}
|
|
381
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type:
|
|
801
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: BuilderFieldComponent, decorators: [{
|
|
382
802
|
type: Component,
|
|
383
|
-
args: [{ selector: 'epistola-
|
|
803
|
+
args: [{ selector: 'epistola-builder-field', standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
804
|
+
<div class="builder-field">
|
|
805
|
+
<div
|
|
806
|
+
class="builder-field__name"
|
|
807
|
+
[class.builder-field__name--clickable]="field.children"
|
|
808
|
+
(click)="field.children && collapseToggle.emit(path)"
|
|
809
|
+
>
|
|
810
|
+
<span *ngIf="field.children" class="builder-field__chevron">{{
|
|
811
|
+
collapsed ? '▶' : '▼'
|
|
812
|
+
}}</span>
|
|
813
|
+
<span class="builder-field__label">{{ field.name }}</span>
|
|
814
|
+
<span *ngIf="required" class="builder-field__required">*</span>
|
|
815
|
+
<span *ngIf="field.children" class="builder-field__type">(object)</span>
|
|
816
|
+
</div>
|
|
817
|
+
|
|
818
|
+
<div class="builder-field__value" *ngIf="!field.children">
|
|
819
|
+
<input
|
|
820
|
+
*ngIf="field.mode === 'ref'"
|
|
821
|
+
type="text"
|
|
822
|
+
class="builder-field__input"
|
|
823
|
+
[ngModel]="field.value"
|
|
824
|
+
(ngModelChange)="valueChange.emit({ path: path, value: $event })"
|
|
825
|
+
[disabled]="disabled"
|
|
826
|
+
placeholder="$doc.path.to.field"
|
|
827
|
+
[attr.list]="'suggestions-' + path.join('-')"
|
|
828
|
+
/>
|
|
829
|
+
<datalist *ngIf="field.mode === 'ref'" [id]="'suggestions-' + path.join('-')">
|
|
830
|
+
<option *ngFor="let s of suggestions" [value]="s"></option>
|
|
831
|
+
</datalist>
|
|
832
|
+
<input
|
|
833
|
+
*ngIf="field.mode === 'raw'"
|
|
834
|
+
type="text"
|
|
835
|
+
class="builder-field__input builder-field__input--raw"
|
|
836
|
+
[ngModel]="field.value"
|
|
837
|
+
(ngModelChange)="valueChange.emit({ path: path, value: $event })"
|
|
838
|
+
[disabled]="disabled"
|
|
839
|
+
placeholder="JSONata expression"
|
|
840
|
+
/>
|
|
841
|
+
<button
|
|
842
|
+
class="builder-field__mode-toggle"
|
|
843
|
+
(click)="modeToggle.emit(path)"
|
|
844
|
+
[disabled]="disabled"
|
|
845
|
+
[title]="field.mode === 'ref' ? 'Switch to raw JSONata' : 'Switch to reference'"
|
|
846
|
+
>
|
|
847
|
+
{{ field.mode === 'ref' ? 'fx' : '·' }}
|
|
848
|
+
</button>
|
|
849
|
+
</div>
|
|
850
|
+
|
|
851
|
+
<div *ngIf="field.children && !collapsed" class="builder-field__children">
|
|
852
|
+
<epistola-builder-field
|
|
853
|
+
*ngFor="let child of field.children; let j = index"
|
|
854
|
+
[field]="child"
|
|
855
|
+
[path]="path.concat(j)"
|
|
856
|
+
[suggestions]="suggestions"
|
|
857
|
+
[disabled]="disabled"
|
|
858
|
+
[collapsed]="isChildCollapsed(j)"
|
|
859
|
+
[collapsedPaths]="collapsedPaths"
|
|
860
|
+
[required]="false"
|
|
861
|
+
(valueChange)="valueChange.emit($event)"
|
|
862
|
+
(modeToggle)="modeToggle.emit($event)"
|
|
863
|
+
(collapseToggle)="collapseToggle.emit($event)"
|
|
864
|
+
></epistola-builder-field>
|
|
865
|
+
</div>
|
|
866
|
+
</div>
|
|
867
|
+
`, styles: [".builder-field{margin-bottom:4px}.builder-field__name{margin-bottom:2px}.builder-field__name--clickable{cursor:pointer;-webkit-user-select:none;user-select:none}.builder-field__name--clickable:hover{color:#0f62fe}.builder-field__chevron{font-size:.7em;margin-right:4px}.builder-field__label{font-weight:500;font-size:.9em}.builder-field__required{color:#da1e28;margin-left:2px}.builder-field__type{color:#8d8d8d;font-size:.8em;margin-left:4px}.builder-field__value{display:flex;align-items:center;gap:4px}.builder-field__input{flex:1;padding:6px 8px;border:1px solid #e0e0e0;border-radius:4px;font-size:.85em;font-family:IBM Plex Mono,monospace}.builder-field__input:focus{outline:2px solid #0f62fe;border-color:#0f62fe}.builder-field__input--raw{background:#f4f4f4}.builder-field__mode-toggle{width:28px;height:28px;border:1px solid #e0e0e0;border-radius:4px;background:#fff;cursor:pointer;font-family:monospace;font-size:.8em;display:flex;align-items:center;justify-content:center}.builder-field__mode-toggle:hover{background:#f4f4f4}.builder-field__children{border-left:2px solid #e0e0e0;padding-left:12px;margin-top:4px}\n"] }]
|
|
384
868
|
}], propDecorators: { field: [{
|
|
385
869
|
type: Input
|
|
386
|
-
}],
|
|
870
|
+
}], path: [{
|
|
387
871
|
type: Input
|
|
388
|
-
}],
|
|
872
|
+
}], suggestions: [{
|
|
389
873
|
type: Input
|
|
390
|
-
}],
|
|
874
|
+
}], disabled: [{
|
|
391
875
|
type: Input
|
|
392
|
-
}],
|
|
876
|
+
}], collapsed: [{
|
|
393
877
|
type: Input
|
|
394
|
-
}],
|
|
878
|
+
}], required: [{
|
|
879
|
+
type: Input
|
|
880
|
+
}], collapsedPaths: [{
|
|
395
881
|
type: Input
|
|
396
882
|
}], valueChange: [{
|
|
397
883
|
type: Output
|
|
884
|
+
}], modeToggle: [{
|
|
885
|
+
type: Output
|
|
886
|
+
}], collapseToggle: [{
|
|
887
|
+
type: Output
|
|
398
888
|
}] } });
|
|
399
889
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
this.updateCompleteness();
|
|
415
|
-
if (!this.expanded && this.totalRequired > 0 && this.mappedCount < this.totalRequired) {
|
|
416
|
-
this.expanded = true;
|
|
417
|
-
}
|
|
418
|
-
// Detect per-field mode from value shape
|
|
419
|
-
if (changes['value'] && typeof this.value === 'object' && this.value !== null && '_source' in this.value) {
|
|
420
|
-
this.arrayPerFieldMode = true;
|
|
421
|
-
}
|
|
890
|
+
const jsonata = _jsonata.default || _jsonata;
|
|
891
|
+
/**
|
|
892
|
+
* Parse a JSONata expression into BuilderField array.
|
|
893
|
+
* Only supports top-level object literals with simple path references or nested objects.
|
|
894
|
+
* Anything else is stored as raw JSONata text.
|
|
895
|
+
*/
|
|
896
|
+
function parseJsonataToBuilder(expression) {
|
|
897
|
+
if (!expression || !expression.trim()) {
|
|
898
|
+
return [];
|
|
899
|
+
}
|
|
900
|
+
try {
|
|
901
|
+
const ast = jsonata(expression).ast();
|
|
902
|
+
if (ast.type === 'unary' && ast.value === '{') {
|
|
903
|
+
return parseObjectEntries(ast.lhs, expression);
|
|
422
904
|
}
|
|
905
|
+
// Not a top-level object — can't represent in builder
|
|
906
|
+
return [{ name: '_root', mode: 'raw', value: expression }];
|
|
423
907
|
}
|
|
424
|
-
|
|
425
|
-
|
|
908
|
+
catch {
|
|
909
|
+
// Invalid JSONata — return as single raw field
|
|
910
|
+
return [{ name: '_root', mode: 'raw', value: expression }];
|
|
426
911
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
}
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Convert BuilderField array back to a JSONata expression string.
|
|
915
|
+
*/
|
|
916
|
+
function builderToJsonata(fields) {
|
|
917
|
+
if (fields.length === 0) {
|
|
434
918
|
return '';
|
|
435
919
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
920
|
+
// Special case: single _root raw field means the whole expression is raw
|
|
921
|
+
if (fields.length === 1 && fields[0].name === '_root' && fields[0].mode === 'raw') {
|
|
922
|
+
return fields[0].value;
|
|
923
|
+
}
|
|
924
|
+
const entries = fields.map((field) => formatFieldEntry(field)).filter(Boolean);
|
|
925
|
+
return `{\n${entries.join(',\n')}\n}`;
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Check if a JSONata expression can be fully represented by the builder
|
|
929
|
+
* (i.e., all fields are simple refs or nested objects of simple refs).
|
|
930
|
+
*/
|
|
931
|
+
function isBuilderCompatible(expression) {
|
|
932
|
+
const fields = parseJsonataToBuilder(expression);
|
|
933
|
+
return fields.every((f) => f.mode === 'ref' || (f.children && f.children.every(isFieldSimple)));
|
|
934
|
+
}
|
|
935
|
+
function isFieldSimple(field) {
|
|
936
|
+
if (field.mode === 'raw')
|
|
937
|
+
return false;
|
|
938
|
+
if (field.children)
|
|
939
|
+
return field.children.every(isFieldSimple);
|
|
940
|
+
return true;
|
|
941
|
+
}
|
|
942
|
+
function parseObjectEntries(entries, source) {
|
|
943
|
+
return entries.map(([keyNode, valueNode]) => {
|
|
944
|
+
const name = keyNode.value;
|
|
945
|
+
return classifyValue(name, valueNode, source);
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
function classifyValue(name, node, source) {
|
|
949
|
+
// Simple path reference: $doc.x.y, $pv.x, $case.x — store as JSONata directly
|
|
950
|
+
if (node.type === 'path' && node.steps?.length > 0 && node.steps[0].type === 'variable') {
|
|
951
|
+
const varName = node.steps[0].value; // doc, pv, case
|
|
952
|
+
if (['doc', 'pv', 'case'].includes(varName)) {
|
|
953
|
+
const path = node.steps
|
|
954
|
+
.slice(1)
|
|
955
|
+
.map((s) => s.value)
|
|
956
|
+
.join('.');
|
|
957
|
+
return { name, mode: 'ref', value: `$${varName}.${path}` };
|
|
444
958
|
}
|
|
445
959
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
960
|
+
// String literal
|
|
961
|
+
if (node.type === 'string') {
|
|
962
|
+
return { name, mode: 'ref', value: `"${node.value}"` };
|
|
963
|
+
}
|
|
964
|
+
// Number literal
|
|
965
|
+
if (node.type === 'number') {
|
|
966
|
+
return { name, mode: 'ref', value: String(node.value) };
|
|
967
|
+
}
|
|
968
|
+
// Boolean literal (value node)
|
|
969
|
+
if (node.type === 'value' && typeof node.value === 'boolean') {
|
|
970
|
+
return { name, mode: 'ref', value: String(node.value) };
|
|
971
|
+
}
|
|
972
|
+
// Nested object
|
|
973
|
+
if (node.type === 'unary' && node.value === '{' && node.lhs) {
|
|
974
|
+
const children = parseObjectEntries(node.lhs, source);
|
|
975
|
+
return { name, mode: 'ref', value: '', children };
|
|
976
|
+
}
|
|
977
|
+
// Anything else: extract raw source text
|
|
978
|
+
const raw = extractSourceFragment(node, source);
|
|
979
|
+
return { name, mode: 'raw', value: raw };
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Extract the source text for a node using position info.
|
|
983
|
+
* Falls back to a generic representation if positions aren't useful.
|
|
984
|
+
*/
|
|
985
|
+
function extractSourceFragment(node, source) {
|
|
986
|
+
// Try to reconstruct from AST for common patterns
|
|
987
|
+
if (node.type === 'condition') {
|
|
988
|
+
const cond = reconstructExpression(node.condition);
|
|
989
|
+
const then = reconstructExpression(node.then);
|
|
990
|
+
const els = reconstructExpression(node.else);
|
|
991
|
+
return `${cond} ? ${then} : ${els}`;
|
|
992
|
+
}
|
|
993
|
+
if (node.type === 'binary') {
|
|
994
|
+
const left = reconstructExpression(node.lhs);
|
|
995
|
+
const right = reconstructExpression(node.rhs);
|
|
996
|
+
return `${left} ${node.value} ${right}`;
|
|
997
|
+
}
|
|
998
|
+
if (node.type === 'function') {
|
|
999
|
+
const name = reconstructExpression(node.procedure);
|
|
1000
|
+
const args = (node.arguments || []).map(reconstructExpression).join(', ');
|
|
1001
|
+
return `${name}(${args})`;
|
|
1002
|
+
}
|
|
1003
|
+
// Generic fallback
|
|
1004
|
+
return reconstructExpression(node);
|
|
1005
|
+
}
|
|
1006
|
+
function reconstructExpression(node) {
|
|
1007
|
+
if (!node)
|
|
1008
|
+
return '';
|
|
1009
|
+
if (node.type === 'string')
|
|
1010
|
+
return `"${node.value}"`;
|
|
1011
|
+
if (node.type === 'number')
|
|
1012
|
+
return String(node.value);
|
|
1013
|
+
if (node.type === 'value')
|
|
1014
|
+
return String(node.value);
|
|
1015
|
+
if (node.type === 'path' && node.steps) {
|
|
1016
|
+
return node.steps.map((s) => (s.type === 'variable' ? `$${s.value}` : s.value)).join('.');
|
|
1017
|
+
}
|
|
1018
|
+
if (node.type === 'binary') {
|
|
1019
|
+
return `${reconstructExpression(node.lhs)} ${node.value} ${reconstructExpression(node.rhs)}`;
|
|
1020
|
+
}
|
|
1021
|
+
if (node.type === 'condition') {
|
|
1022
|
+
return `${reconstructExpression(node.condition)} ? ${reconstructExpression(node.then)} : ${reconstructExpression(node.else)}`;
|
|
1023
|
+
}
|
|
1024
|
+
if (node.type === 'function') {
|
|
1025
|
+
const proc = reconstructExpression(node.procedure);
|
|
1026
|
+
const args = (node.arguments || []).map(reconstructExpression).join(', ');
|
|
1027
|
+
return `${proc}(${args})`;
|
|
1028
|
+
}
|
|
1029
|
+
if (node.type === 'unary' && node.value === '{') {
|
|
1030
|
+
const entries = (node.lhs || [])
|
|
1031
|
+
.map(([k, v]) => `"${k.value}": ${reconstructExpression(v)}`)
|
|
1032
|
+
.join(', ');
|
|
1033
|
+
return `{${entries}}`;
|
|
1034
|
+
}
|
|
1035
|
+
return '...';
|
|
1036
|
+
}
|
|
1037
|
+
function formatFieldEntry(field, indent = ' ') {
|
|
1038
|
+
if (field.children && field.children.length > 0) {
|
|
1039
|
+
const childEntries = field.children.map((c) => formatFieldEntry(c, indent + ' ')).join(',\n');
|
|
1040
|
+
return `${indent}"${field.name}": {\n${childEntries}\n${indent}}`;
|
|
1041
|
+
}
|
|
1042
|
+
// Value is already valid JSONata (e.g. $doc.x.y, "string", 42, or raw expression)
|
|
1043
|
+
const value = field.value || 'null';
|
|
1044
|
+
return `${indent}"${field.name}": ${value}`;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
class MappingBuilderComponent {
|
|
1048
|
+
expression = '';
|
|
1049
|
+
templateFields = [];
|
|
1050
|
+
suggestions = null;
|
|
1051
|
+
disabled = false;
|
|
1052
|
+
expressionChange = new EventEmitter();
|
|
1053
|
+
fields = [];
|
|
1054
|
+
allSuggestions = [];
|
|
1055
|
+
collapsedPaths = new Set();
|
|
1056
|
+
initialCollapseApplied = false;
|
|
1057
|
+
ngOnChanges(changes) {
|
|
1058
|
+
// Skip re-parse only when expression alone changed (from our own emit)
|
|
1059
|
+
const expressionChanged = !!changes['expression'];
|
|
1060
|
+
const templateFieldsChanged = !!changes['templateFields'];
|
|
1061
|
+
if (expressionChanged && !templateFieldsChanged && !changes['expression'].firstChange) {
|
|
1062
|
+
return; // Don't re-parse when we emit changes ourselves
|
|
451
1063
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
this.
|
|
1064
|
+
if (expressionChanged || templateFieldsChanged) {
|
|
1065
|
+
this.rebuildFields();
|
|
1066
|
+
if (!this.initialCollapseApplied && this.fields.length > 0) {
|
|
1067
|
+
this.collapseAll();
|
|
1068
|
+
this.initialCollapseApplied = true;
|
|
1069
|
+
}
|
|
455
1070
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const current = (typeof this.value === 'object' && this.value !== null) ? { ...this.value } : { _source: '' };
|
|
459
|
-
if (sourceFieldName && sourceFieldName.trim().length > 0) {
|
|
460
|
-
current[childName] = sourceFieldName;
|
|
1071
|
+
if (changes['suggestions']) {
|
|
1072
|
+
this.buildSuggestionList();
|
|
461
1073
|
}
|
|
462
|
-
|
|
463
|
-
|
|
1074
|
+
}
|
|
1075
|
+
onNestedValueChange(path, value) {
|
|
1076
|
+
const field = this.getFieldAtPath(path);
|
|
1077
|
+
if (field) {
|
|
1078
|
+
field.value = value;
|
|
1079
|
+
this.emit();
|
|
464
1080
|
}
|
|
465
|
-
this.valueChange.emit(current);
|
|
466
1081
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
1082
|
+
onNestedModeToggle(path) {
|
|
1083
|
+
const field = this.getFieldAtPath(path);
|
|
1084
|
+
if (field) {
|
|
1085
|
+
field.mode = field.mode === 'ref' ? 'raw' : 'ref';
|
|
1086
|
+
this.emit();
|
|
470
1087
|
}
|
|
471
|
-
return '';
|
|
472
1088
|
}
|
|
473
|
-
|
|
474
|
-
return
|
|
1089
|
+
isRequired(fieldName) {
|
|
1090
|
+
return this.templateFields?.find((tf) => tf.name === fieldName)?.required ?? false;
|
|
475
1091
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
let total = 0;
|
|
484
|
-
let mapped = 0;
|
|
485
|
-
if (this.field?.required) {
|
|
486
|
-
total++;
|
|
487
|
-
if (this.value['_source'] && this.value['_source'].trim().length > 0) {
|
|
488
|
-
mapped++;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
for (const child of this.field.children) {
|
|
492
|
-
if (child.required) {
|
|
493
|
-
total++;
|
|
494
|
-
const val = this.value[child.name];
|
|
495
|
-
if (typeof val === 'string' && val.trim().length > 0) {
|
|
496
|
-
mapped++;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
this.mappedCount = mapped;
|
|
501
|
-
this.totalRequired = total;
|
|
1092
|
+
isCollapsed(path) {
|
|
1093
|
+
return this.collapsedPaths.has(path.join('.'));
|
|
1094
|
+
}
|
|
1095
|
+
toggleCollapse(path) {
|
|
1096
|
+
const key = path.join('.');
|
|
1097
|
+
if (this.collapsedPaths.has(key)) {
|
|
1098
|
+
this.collapsedPaths.delete(key);
|
|
502
1099
|
}
|
|
503
1100
|
else {
|
|
504
|
-
this.
|
|
505
|
-
this.mappedCount = this.getSourceValue() ? (this.field?.required ? 1 : 0) : 0;
|
|
1101
|
+
this.collapsedPaths.add(key);
|
|
506
1102
|
}
|
|
507
1103
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}], propDecorators: { field: [{
|
|
515
|
-
type: Input
|
|
516
|
-
}], value: [{
|
|
517
|
-
type: Input
|
|
518
|
-
}], pluginId: [{
|
|
519
|
-
type: Input
|
|
520
|
-
}], caseDefinitionKey: [{
|
|
521
|
-
type: Input
|
|
522
|
-
}], processVariables: [{
|
|
523
|
-
type: Input
|
|
524
|
-
}], disabled: [{
|
|
525
|
-
type: Input
|
|
526
|
-
}], valueChange: [{
|
|
527
|
-
type: Output
|
|
528
|
-
}] } });
|
|
529
|
-
|
|
530
|
-
/**
|
|
531
|
-
* Recursive field tree component.
|
|
532
|
-
* Dispatches SCALAR and ARRAY to dedicated sub-components.
|
|
533
|
-
* Handles OBJECT inline to avoid circular import issues (OBJECT children recurse back to this component).
|
|
534
|
-
* Uses forwardRef(() => FieldTreeComponent) in imports to allow self-referencing in the template.
|
|
535
|
-
*/
|
|
536
|
-
class FieldTreeComponent {
|
|
537
|
-
field;
|
|
538
|
-
value = undefined;
|
|
539
|
-
pluginId;
|
|
540
|
-
caseDefinitionKey = null;
|
|
541
|
-
processVariables = [];
|
|
542
|
-
disabled = false;
|
|
543
|
-
valueChange = new EventEmitter();
|
|
544
|
-
// OBJECT-specific state
|
|
545
|
-
expanded = false;
|
|
546
|
-
mappedCount = 0;
|
|
547
|
-
totalRequired = 0;
|
|
548
|
-
ngOnChanges(changes) {
|
|
549
|
-
if (this.field?.fieldType === 'OBJECT' && (changes['value'] || changes['field'])) {
|
|
550
|
-
if (this.field.children) {
|
|
551
|
-
const stats = countRequiredMapped(this.field.children, this.value || {});
|
|
552
|
-
this.mappedCount = stats.mapped;
|
|
553
|
-
this.totalRequired = stats.total;
|
|
1104
|
+
collapseAll() {
|
|
1105
|
+
this.collapsedPaths.clear();
|
|
1106
|
+
this.fields.forEach((field, i) => {
|
|
1107
|
+
if (field.children) {
|
|
1108
|
+
this.collapsedPaths.add(String(i));
|
|
1109
|
+
this.collapseChildren(field.children, [i]);
|
|
554
1110
|
}
|
|
555
|
-
|
|
556
|
-
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
collapseChildren(children, parentPath) {
|
|
1114
|
+
children.forEach((child, j) => {
|
|
1115
|
+
if (child.children) {
|
|
1116
|
+
this.collapsedPaths.add([...parentPath, j].join('.'));
|
|
1117
|
+
this.collapseChildren(child.children, [...parentPath, j]);
|
|
557
1118
|
}
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
getFieldAtPath(path) {
|
|
1122
|
+
if (path.length === 0)
|
|
1123
|
+
return null;
|
|
1124
|
+
let current = this.fields[path[0]];
|
|
1125
|
+
for (let i = 1; i < path.length; i++) {
|
|
1126
|
+
if (!current.children)
|
|
1127
|
+
return null;
|
|
1128
|
+
current = current.children[path[i]];
|
|
558
1129
|
}
|
|
1130
|
+
return current;
|
|
559
1131
|
}
|
|
560
|
-
|
|
561
|
-
|
|
1132
|
+
emit() {
|
|
1133
|
+
const jsonata = builderToJsonata(this.fields);
|
|
1134
|
+
this.expressionChange.emit(jsonata);
|
|
562
1135
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
1136
|
+
/**
|
|
1137
|
+
* Ensure all template fields have a corresponding builder field.
|
|
1138
|
+
* Adds missing fields with empty values.
|
|
1139
|
+
*/
|
|
1140
|
+
buildSuggestionList() {
|
|
1141
|
+
if (!this.suggestions) {
|
|
1142
|
+
this.allSuggestions = [];
|
|
1143
|
+
return;
|
|
570
1144
|
}
|
|
571
|
-
this.
|
|
1145
|
+
const docSuggestions = (this.suggestions.doc || []).map((p) => `$doc.${p}`);
|
|
1146
|
+
const pvSuggestions = (this.suggestions.pv || []).map((p) => `$pv.${p}`);
|
|
1147
|
+
this.allSuggestions = [...docSuggestions, ...pvSuggestions];
|
|
572
1148
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
1149
|
+
/**
|
|
1150
|
+
* Rebuild fields using template fields as the source of truth.
|
|
1151
|
+
* Expression values fill in where available; unmapped fields show empty.
|
|
1152
|
+
*/
|
|
1153
|
+
rebuildFields() {
|
|
1154
|
+
const parsed = parseJsonataToBuilder(this.expression);
|
|
1155
|
+
const parsedByName = new Map(parsed.map((f) => [f.name, f]));
|
|
1156
|
+
if (!this.templateFields || this.templateFields.length === 0) {
|
|
1157
|
+
// No template fields yet — use whatever we parsed
|
|
1158
|
+
this.fields = parsed;
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
// Template fields drive the structure
|
|
1162
|
+
this.fields = this.templateFields.map((tf) => {
|
|
1163
|
+
const existing = parsedByName.get(tf.name);
|
|
1164
|
+
if (existing) {
|
|
1165
|
+
return existing;
|
|
1166
|
+
}
|
|
1167
|
+
if (tf.fieldType === 'OBJECT' && tf.children?.length) {
|
|
1168
|
+
return {
|
|
1169
|
+
name: tf.name,
|
|
1170
|
+
mode: 'ref',
|
|
1171
|
+
value: '',
|
|
1172
|
+
children: tf.children.map((c) => ({ name: c.name, mode: 'ref', value: '' })),
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
return { name: tf.name, mode: 'ref', value: '' };
|
|
1176
|
+
});
|
|
1177
|
+
// Include extra fields from expression not in the template schema
|
|
1178
|
+
for (const p of parsed) {
|
|
1179
|
+
if (!this.templateFields.find((tf) => tf.name === p.name)) {
|
|
1180
|
+
this.fields.push(p);
|
|
1181
|
+
}
|
|
576
1182
|
}
|
|
577
|
-
return undefined;
|
|
578
1183
|
}
|
|
579
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type:
|
|
580
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
1184
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: MappingBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1185
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: MappingBuilderComponent, isStandalone: true, selector: "epistola-mapping-builder", inputs: { expression: "expression", templateFields: "templateFields", suggestions: "suggestions", disabled: "disabled" }, outputs: { expressionChange: "expressionChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
1186
|
+
<div class="mapping-builder">
|
|
1187
|
+
<div
|
|
1188
|
+
*ngIf="fields.length === 0 && (!templateFields || templateFields.length === 0)"
|
|
1189
|
+
class="mapping-builder__empty"
|
|
1190
|
+
>
|
|
1191
|
+
{{ 'noTemplateFields' | pluginTranslate: 'epistola' | async }}
|
|
1192
|
+
</div>
|
|
1193
|
+
|
|
1194
|
+
<epistola-builder-field
|
|
1195
|
+
*ngFor="let field of fields; let i = index"
|
|
1196
|
+
[field]="field"
|
|
1197
|
+
[path]="[i]"
|
|
1198
|
+
[suggestions]="allSuggestions"
|
|
1199
|
+
[disabled]="disabled"
|
|
1200
|
+
[collapsed]="isCollapsed([i])"
|
|
1201
|
+
[collapsedPaths]="collapsedPaths"
|
|
1202
|
+
[required]="isRequired(field.name)"
|
|
1203
|
+
(valueChange)="onNestedValueChange($event.path, $event.value)"
|
|
1204
|
+
(modeToggle)="onNestedModeToggle($event)"
|
|
1205
|
+
(collapseToggle)="toggleCollapse($event)"
|
|
1206
|
+
></epistola-builder-field>
|
|
1207
|
+
</div>
|
|
1208
|
+
`, isInline: true, styles: [".mapping-builder__empty{color:#6f6f6f;font-size:.9em;padding:12px 0}.mapping-builder__row{margin-bottom:8px}.mapping-builder__row--child{margin-left:20px;margin-bottom:4px}.mapping-builder__name{margin-bottom:2px}.mapping-builder__name--clickable{cursor:pointer;-webkit-user-select:none;user-select:none}.mapping-builder__name--clickable:hover{color:#0f62fe}.mapping-builder__chevron{font-size:.7em;margin-right:4px}.mapping-builder__field-name{font-weight:500;font-size:.9em}.mapping-builder__required{color:#da1e28;margin-left:2px}.mapping-builder__type{color:#8d8d8d;font-size:.8em;margin-left:4px}.mapping-builder__value{display:flex;align-items:center;gap:4px}.mapping-builder__input{flex:1;padding:6px 8px;border:1px solid #e0e0e0;border-radius:4px;font-size:.85em;font-family:IBM Plex Mono,monospace}.mapping-builder__input:focus{outline:2px solid #0f62fe;border-color:#0f62fe}.mapping-builder__input--raw{background:#f4f4f4}.mapping-builder__mode-toggle{width:28px;height:28px;border:1px solid #e0e0e0;border-radius:4px;background:#fff;cursor:pointer;font-family:monospace;font-size:.8em;display:flex;align-items:center;justify-content:center}.mapping-builder__mode-toggle:hover{background:#f4f4f4}.mapping-builder__children{border-left:2px solid #e0e0e0;padding-left:12px;margin-top:4px}\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: "ngmodule", type: PluginTranslatePipeModule }, { kind: "pipe", type: i2$1.PluginTranslatePipe, name: "pluginTranslate" }, { kind: "component", type: BuilderFieldComponent, selector: "epistola-builder-field", inputs: ["field", "path", "suggestions", "disabled", "collapsed", "required", "collapsedPaths"], outputs: ["valueChange", "modeToggle", "collapseToggle"] }] });
|
|
581
1209
|
}
|
|
582
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type:
|
|
1210
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: MappingBuilderComponent, decorators: [{
|
|
583
1211
|
type: Component,
|
|
584
|
-
args: [{ selector: 'epistola-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
1212
|
+
args: [{ selector: 'epistola-mapping-builder', standalone: true, imports: [CommonModule, FormsModule, PluginTranslatePipeModule, BuilderFieldComponent], template: `
|
|
1213
|
+
<div class="mapping-builder">
|
|
1214
|
+
<div
|
|
1215
|
+
*ngIf="fields.length === 0 && (!templateFields || templateFields.length === 0)"
|
|
1216
|
+
class="mapping-builder__empty"
|
|
1217
|
+
>
|
|
1218
|
+
{{ 'noTemplateFields' | pluginTranslate: 'epistola' | async }}
|
|
1219
|
+
</div>
|
|
1220
|
+
|
|
1221
|
+
<epistola-builder-field
|
|
1222
|
+
*ngFor="let field of fields; let i = index"
|
|
1223
|
+
[field]="field"
|
|
1224
|
+
[path]="[i]"
|
|
1225
|
+
[suggestions]="allSuggestions"
|
|
1226
|
+
[disabled]="disabled"
|
|
1227
|
+
[collapsed]="isCollapsed([i])"
|
|
1228
|
+
[collapsedPaths]="collapsedPaths"
|
|
1229
|
+
[required]="isRequired(field.name)"
|
|
1230
|
+
(valueChange)="onNestedValueChange($event.path, $event.value)"
|
|
1231
|
+
(modeToggle)="onNestedModeToggle($event)"
|
|
1232
|
+
(collapseToggle)="toggleCollapse($event)"
|
|
1233
|
+
></epistola-builder-field>
|
|
1234
|
+
</div>
|
|
1235
|
+
`, styles: [".mapping-builder__empty{color:#6f6f6f;font-size:.9em;padding:12px 0}.mapping-builder__row{margin-bottom:8px}.mapping-builder__row--child{margin-left:20px;margin-bottom:4px}.mapping-builder__name{margin-bottom:2px}.mapping-builder__name--clickable{cursor:pointer;-webkit-user-select:none;user-select:none}.mapping-builder__name--clickable:hover{color:#0f62fe}.mapping-builder__chevron{font-size:.7em;margin-right:4px}.mapping-builder__field-name{font-weight:500;font-size:.9em}.mapping-builder__required{color:#da1e28;margin-left:2px}.mapping-builder__type{color:#8d8d8d;font-size:.8em;margin-left:4px}.mapping-builder__value{display:flex;align-items:center;gap:4px}.mapping-builder__input{flex:1;padding:6px 8px;border:1px solid #e0e0e0;border-radius:4px;font-size:.85em;font-family:IBM Plex Mono,monospace}.mapping-builder__input:focus{outline:2px solid #0f62fe;border-color:#0f62fe}.mapping-builder__input--raw{background:#f4f4f4}.mapping-builder__mode-toggle{width:28px;height:28px;border:1px solid #e0e0e0;border-radius:4px;background:#fff;cursor:pointer;font-family:monospace;font-size:.8em;display:flex;align-items:center;justify-content:center}.mapping-builder__mode-toggle:hover{background:#f4f4f4}.mapping-builder__children{border-left:2px solid #e0e0e0;padding-left:12px;margin-top:4px}\n"] }]
|
|
1236
|
+
}], propDecorators: { expression: [{
|
|
590
1237
|
type: Input
|
|
591
|
-
}],
|
|
1238
|
+
}], templateFields: [{
|
|
592
1239
|
type: Input
|
|
593
|
-
}],
|
|
1240
|
+
}], suggestions: [{
|
|
594
1241
|
type: Input
|
|
595
1242
|
}], disabled: [{
|
|
596
1243
|
type: Input
|
|
597
|
-
}],
|
|
1244
|
+
}], expressionChange: [{
|
|
598
1245
|
type: Output
|
|
599
1246
|
}] } });
|
|
600
1247
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
*/
|
|
605
|
-
class DataMappingTreeComponent {
|
|
606
|
-
pluginId;
|
|
1248
|
+
class MappingPreviewComponent {
|
|
1249
|
+
epistolaPluginService;
|
|
1250
|
+
expression = '';
|
|
607
1251
|
templateFields = [];
|
|
608
|
-
prefillMapping = {};
|
|
609
|
-
disabled = false;
|
|
610
1252
|
caseDefinitionKey = null;
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
1253
|
+
documentId = '';
|
|
1254
|
+
loading = false;
|
|
1255
|
+
result = null;
|
|
1256
|
+
expectedJson = '';
|
|
1257
|
+
missingRequired = [];
|
|
1258
|
+
destroy$ = new Subject();
|
|
1259
|
+
evaluate$ = new Subject();
|
|
1260
|
+
constructor(epistolaPluginService) {
|
|
1261
|
+
this.epistolaPluginService = epistolaPluginService;
|
|
1262
|
+
this.evaluate$.pipe(debounceTime(300), takeUntil(this.destroy$)).subscribe(() => {
|
|
1263
|
+
this.doEvaluate();
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
615
1266
|
ngOnChanges(changes) {
|
|
616
|
-
if (changes['
|
|
617
|
-
|
|
618
|
-
if (mapping && Object.keys(mapping).length > 0) {
|
|
619
|
-
this.mapping = { ...mapping };
|
|
620
|
-
}
|
|
1267
|
+
if (changes['templateFields']) {
|
|
1268
|
+
this.expectedJson = this.buildExpectedJson();
|
|
621
1269
|
}
|
|
622
|
-
if (changes['
|
|
623
|
-
this.
|
|
1270
|
+
if (changes['expression'] || changes['templateFields']) {
|
|
1271
|
+
this.checkMissingRequired();
|
|
624
1272
|
}
|
|
625
1273
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
this.mapping = rest;
|
|
630
|
-
}
|
|
631
|
-
else {
|
|
632
|
-
this.mapping = { ...this.mapping, [fieldName]: value };
|
|
633
|
-
}
|
|
634
|
-
this.mappingChange.emit(this.mapping);
|
|
635
|
-
this.emitRequiredFieldsStatus();
|
|
1274
|
+
ngOnDestroy() {
|
|
1275
|
+
this.destroy$.next();
|
|
1276
|
+
this.destroy$.complete();
|
|
636
1277
|
}
|
|
637
|
-
|
|
638
|
-
|
|
1278
|
+
runPreview() {
|
|
1279
|
+
this.evaluate$.next();
|
|
639
1280
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
1281
|
+
doEvaluate() {
|
|
1282
|
+
if (!this.documentId || !this.expression)
|
|
1283
|
+
return;
|
|
1284
|
+
this.loading = true;
|
|
1285
|
+
this.epistolaPluginService.evaluateMapping(this.expression, this.documentId).subscribe({
|
|
1286
|
+
next: (result) => {
|
|
1287
|
+
this.result = result;
|
|
1288
|
+
this.loading = false;
|
|
1289
|
+
if (result.success) {
|
|
1290
|
+
this.checkMissingRequired();
|
|
1291
|
+
}
|
|
1292
|
+
},
|
|
1293
|
+
error: (err) => {
|
|
1294
|
+
this.result = { success: false, result: null, error: err.message || 'Request failed' };
|
|
1295
|
+
this.loading = false;
|
|
1296
|
+
},
|
|
1297
|
+
});
|
|
643
1298
|
}
|
|
644
|
-
|
|
645
|
-
|
|
1299
|
+
buildExpectedJson() {
|
|
1300
|
+
if (!this.templateFields || this.templateFields.length === 0)
|
|
1301
|
+
return '{}';
|
|
1302
|
+
const obj = {};
|
|
1303
|
+
for (const field of this.templateFields) {
|
|
1304
|
+
const type = field.type || 'any';
|
|
1305
|
+
obj[field.name] = field.required ? `${type} (required)` : type;
|
|
1306
|
+
}
|
|
1307
|
+
return JSON.stringify(obj, null, 2);
|
|
1308
|
+
}
|
|
1309
|
+
checkMissingRequired() {
|
|
1310
|
+
if (!this.templateFields) {
|
|
1311
|
+
this.missingRequired = [];
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
const requiredFields = this.templateFields.filter((f) => f.required).map((f) => f.name);
|
|
1315
|
+
if (!this.result?.success || !this.result.result) {
|
|
1316
|
+
// If no evaluation result yet, check statically from expression
|
|
1317
|
+
this.missingRequired = requiredFields;
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
const producedKeys = new Set(Object.keys(this.result.result));
|
|
1321
|
+
this.missingRequired = requiredFields.filter((f) => !producedKeys.has(f));
|
|
1322
|
+
}
|
|
1323
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: MappingPreviewComponent, deps: [{ token: EpistolaPluginService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1324
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: MappingPreviewComponent, isStandalone: true, selector: "epistola-mapping-preview", inputs: { expression: "expression", templateFields: "templateFields", caseDefinitionKey: "caseDefinitionKey" }, usesOnChanges: true, ngImport: i0, template: `
|
|
1325
|
+
<div class="preview">
|
|
1326
|
+
<div class="preview__header">
|
|
1327
|
+
<span class="preview__title">{{
|
|
1328
|
+
'previewTitle' | pluginTranslate: 'epistola' | async
|
|
1329
|
+
}}</span>
|
|
1330
|
+
<div class="preview__controls">
|
|
1331
|
+
<input
|
|
1332
|
+
type="text"
|
|
1333
|
+
class="preview__doc-input"
|
|
1334
|
+
[ngModel]="documentId"
|
|
1335
|
+
(ngModelChange)="documentId = $event"
|
|
1336
|
+
[placeholder]="'previewDocPlaceholder' | pluginTranslate: 'epistola' | async"
|
|
1337
|
+
/>
|
|
1338
|
+
<button
|
|
1339
|
+
class="preview__run-btn"
|
|
1340
|
+
(click)="runPreview()"
|
|
1341
|
+
[disabled]="!documentId || !expression || loading"
|
|
1342
|
+
>
|
|
1343
|
+
▶
|
|
1344
|
+
</button>
|
|
1345
|
+
</div>
|
|
1346
|
+
</div>
|
|
1347
|
+
|
|
1348
|
+
<div class="preview__panels">
|
|
1349
|
+
<!-- Expected structure -->
|
|
1350
|
+
<div class="preview__panel">
|
|
1351
|
+
<div class="preview__panel-label">
|
|
1352
|
+
{{ 'previewExpected' | pluginTranslate: 'epistola' | async }}
|
|
1353
|
+
</div>
|
|
1354
|
+
<pre class="preview__code">{{ expectedJson }}</pre>
|
|
1355
|
+
</div>
|
|
1356
|
+
|
|
1357
|
+
<!-- Produced output -->
|
|
1358
|
+
<div class="preview__panel">
|
|
1359
|
+
<div class="preview__panel-label">
|
|
1360
|
+
{{ 'previewProduced' | pluginTranslate: 'epistola' | async }}
|
|
1361
|
+
</div>
|
|
1362
|
+
<div *ngIf="loading" class="preview__loading">...</div>
|
|
1363
|
+
<pre *ngIf="!loading && result?.success" class="preview__code">{{
|
|
1364
|
+
result.result | json
|
|
1365
|
+
}}</pre>
|
|
1366
|
+
<div *ngIf="!loading && result && !result.success" class="preview__error">
|
|
1367
|
+
{{ result.error }}
|
|
1368
|
+
</div>
|
|
1369
|
+
<div *ngIf="!loading && !result" class="preview__placeholder">
|
|
1370
|
+
{{ 'previewRunHint' | pluginTranslate: 'epistola' | async }}
|
|
1371
|
+
</div>
|
|
1372
|
+
</div>
|
|
1373
|
+
</div>
|
|
1374
|
+
|
|
1375
|
+
<!-- Missing fields warning -->
|
|
1376
|
+
<div *ngIf="missingRequired.length > 0" class="preview__warnings">
|
|
1377
|
+
<span class="preview__warning-icon">⚠</span>
|
|
1378
|
+
{{ 'previewMissing' | pluginTranslate: 'epistola' | async }}:
|
|
1379
|
+
<strong>{{ missingRequired.join(', ') }}</strong>
|
|
1380
|
+
</div>
|
|
1381
|
+
</div>
|
|
1382
|
+
`, isInline: true, styles: [".preview{border:1px solid #e0e0e0;border-radius:4px;margin-top:16px;overflow:hidden}.preview__header{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#f4f4f4;border-bottom:1px solid #e0e0e0}.preview__title{font-weight:600;font-size:.85em}.preview__controls{display:flex;gap:4px}.preview__doc-input{padding:4px 8px;border:1px solid #e0e0e0;border-radius:4px;font-size:.8em;width:220px;font-family:monospace}.preview__run-btn{padding:4px 10px;border:1px solid #0f62fe;border-radius:4px;background:#0f62fe;color:#fff;cursor:pointer;font-size:.8em}.preview__run-btn:disabled{opacity:.4;cursor:not-allowed}.preview__panels{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:#e0e0e0}.preview__panel{background:#fff;padding:8px 12px;min-height:80px}.preview__panel-label{font-size:.75em;color:#6f6f6f;text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px}.preview__code{font-family:IBM Plex Mono,monospace;font-size:.8em;line-height:1.4;margin:0;white-space:pre-wrap;word-break:break-word}.preview__loading{color:#8d8d8d}.preview__error{color:#da1e28;font-size:.85em}.preview__placeholder{color:#8d8d8d;font-size:.85em;font-style:italic}.preview__warnings{padding:8px 12px;background:#fff8e1;border-top:1px solid #e0e0e0;font-size:.85em;color:#663c00}.preview__warning-icon{margin-right:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }, { 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" }] });
|
|
646
1383
|
}
|
|
647
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type:
|
|
1384
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: MappingPreviewComponent, decorators: [{
|
|
648
1385
|
type: Component,
|
|
649
|
-
args: [{ selector: 'epistola-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
}
|
|
1386
|
+
args: [{ selector: 'epistola-mapping-preview', standalone: true, imports: [CommonModule, FormsModule, PluginTranslatePipeModule], template: `
|
|
1387
|
+
<div class="preview">
|
|
1388
|
+
<div class="preview__header">
|
|
1389
|
+
<span class="preview__title">{{
|
|
1390
|
+
'previewTitle' | pluginTranslate: 'epistola' | async
|
|
1391
|
+
}}</span>
|
|
1392
|
+
<div class="preview__controls">
|
|
1393
|
+
<input
|
|
1394
|
+
type="text"
|
|
1395
|
+
class="preview__doc-input"
|
|
1396
|
+
[ngModel]="documentId"
|
|
1397
|
+
(ngModelChange)="documentId = $event"
|
|
1398
|
+
[placeholder]="'previewDocPlaceholder' | pluginTranslate: 'epistola' | async"
|
|
1399
|
+
/>
|
|
1400
|
+
<button
|
|
1401
|
+
class="preview__run-btn"
|
|
1402
|
+
(click)="runPreview()"
|
|
1403
|
+
[disabled]="!documentId || !expression || loading"
|
|
1404
|
+
>
|
|
1405
|
+
▶
|
|
1406
|
+
</button>
|
|
1407
|
+
</div>
|
|
1408
|
+
</div>
|
|
1409
|
+
|
|
1410
|
+
<div class="preview__panels">
|
|
1411
|
+
<!-- Expected structure -->
|
|
1412
|
+
<div class="preview__panel">
|
|
1413
|
+
<div class="preview__panel-label">
|
|
1414
|
+
{{ 'previewExpected' | pluginTranslate: 'epistola' | async }}
|
|
1415
|
+
</div>
|
|
1416
|
+
<pre class="preview__code">{{ expectedJson }}</pre>
|
|
1417
|
+
</div>
|
|
1418
|
+
|
|
1419
|
+
<!-- Produced output -->
|
|
1420
|
+
<div class="preview__panel">
|
|
1421
|
+
<div class="preview__panel-label">
|
|
1422
|
+
{{ 'previewProduced' | pluginTranslate: 'epistola' | async }}
|
|
1423
|
+
</div>
|
|
1424
|
+
<div *ngIf="loading" class="preview__loading">...</div>
|
|
1425
|
+
<pre *ngIf="!loading && result?.success" class="preview__code">{{
|
|
1426
|
+
result.result | json
|
|
1427
|
+
}}</pre>
|
|
1428
|
+
<div *ngIf="!loading && result && !result.success" class="preview__error">
|
|
1429
|
+
{{ result.error }}
|
|
1430
|
+
</div>
|
|
1431
|
+
<div *ngIf="!loading && !result" class="preview__placeholder">
|
|
1432
|
+
{{ 'previewRunHint' | pluginTranslate: 'epistola' | async }}
|
|
1433
|
+
</div>
|
|
1434
|
+
</div>
|
|
1435
|
+
</div>
|
|
1436
|
+
|
|
1437
|
+
<!-- Missing fields warning -->
|
|
1438
|
+
<div *ngIf="missingRequired.length > 0" class="preview__warnings">
|
|
1439
|
+
<span class="preview__warning-icon">⚠</span>
|
|
1440
|
+
{{ 'previewMissing' | pluginTranslate: 'epistola' | async }}:
|
|
1441
|
+
<strong>{{ missingRequired.join(', ') }}</strong>
|
|
1442
|
+
</div>
|
|
1443
|
+
</div>
|
|
1444
|
+
`, styles: [".preview{border:1px solid #e0e0e0;border-radius:4px;margin-top:16px;overflow:hidden}.preview__header{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#f4f4f4;border-bottom:1px solid #e0e0e0}.preview__title{font-weight:600;font-size:.85em}.preview__controls{display:flex;gap:4px}.preview__doc-input{padding:4px 8px;border:1px solid #e0e0e0;border-radius:4px;font-size:.8em;width:220px;font-family:monospace}.preview__run-btn{padding:4px 10px;border:1px solid #0f62fe;border-radius:4px;background:#0f62fe;color:#fff;cursor:pointer;font-size:.8em}.preview__run-btn:disabled{opacity:.4;cursor:not-allowed}.preview__panels{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:#e0e0e0}.preview__panel{background:#fff;padding:8px 12px;min-height:80px}.preview__panel-label{font-size:.75em;color:#6f6f6f;text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px}.preview__code{font-family:IBM Plex Mono,monospace;font-size:.8em;line-height:1.4;margin:0;white-space:pre-wrap;word-break:break-word}.preview__loading{color:#8d8d8d}.preview__error{color:#da1e28;font-size:.85em}.preview__placeholder{color:#8d8d8d;font-size:.85em;font-style:italic}.preview__warnings{padding:8px 12px;background:#fff8e1;border-top:1px solid #e0e0e0;font-size:.85em;color:#663c00}.preview__warning-icon{margin-right:4px}\n"] }]
|
|
1445
|
+
}], ctorParameters: () => [{ type: EpistolaPluginService }], propDecorators: { expression: [{
|
|
655
1446
|
type: Input
|
|
656
1447
|
}], templateFields: [{
|
|
657
1448
|
type: Input
|
|
658
|
-
}], prefillMapping: [{
|
|
659
|
-
type: Input
|
|
660
|
-
}], disabled: [{
|
|
661
|
-
type: Input
|
|
662
1449
|
}], caseDefinitionKey: [{
|
|
663
1450
|
type: Input
|
|
664
|
-
}], processVariables: [{
|
|
665
|
-
type: Input
|
|
666
|
-
}], mappingChange: [{
|
|
667
|
-
type: Output
|
|
668
|
-
}], requiredFieldsStatus: [{
|
|
669
|
-
type: Output
|
|
670
1451
|
}] } });
|
|
671
1452
|
|
|
672
1453
|
class GenerateDocumentConfigurationComponent {
|
|
@@ -686,10 +1467,13 @@ class GenerateDocumentConfigurationComponent {
|
|
|
686
1467
|
variants$ = new BehaviorSubject(initialResource([]));
|
|
687
1468
|
environments$ = new BehaviorSubject(initialResource([]));
|
|
688
1469
|
templateFields$ = new BehaviorSubject(initialResource([]));
|
|
689
|
-
dataMapping$ = new BehaviorSubject(
|
|
1470
|
+
dataMapping$ = new BehaviorSubject('');
|
|
1471
|
+
mappingMode = 'simple';
|
|
1472
|
+
toolsCollapsed = true;
|
|
1473
|
+
activeToolTab = 'preview';
|
|
690
1474
|
outputFormatOptions = [
|
|
691
1475
|
{ id: 'PDF', text: 'PDF' },
|
|
692
|
-
{ id: 'HTML', text: 'HTML' }
|
|
1476
|
+
{ id: 'HTML', text: 'HTML' },
|
|
693
1477
|
];
|
|
694
1478
|
selectedCatalogId$ = new BehaviorSubject('');
|
|
695
1479
|
/** Composite ID: "catalogId/templateId" */
|
|
@@ -700,6 +1484,8 @@ class GenerateDocumentConfigurationComponent {
|
|
|
700
1484
|
availableAttributeKeys = [];
|
|
701
1485
|
caseDefinitionKey = null;
|
|
702
1486
|
processVariables = [];
|
|
1487
|
+
expressionFunctions = [];
|
|
1488
|
+
variableSuggestions = null;
|
|
703
1489
|
requiredFieldsStatus = { mapped: 0, total: 0 };
|
|
704
1490
|
prefillDataMapping = {};
|
|
705
1491
|
destroy$ = new Subject();
|
|
@@ -719,6 +1505,7 @@ class GenerateDocumentConfigurationComponent {
|
|
|
719
1505
|
this.initContext();
|
|
720
1506
|
this.initPluginConfiguration();
|
|
721
1507
|
this.initCascade();
|
|
1508
|
+
this.loadExpressionFunctions();
|
|
722
1509
|
this.openSaveSubscription();
|
|
723
1510
|
}
|
|
724
1511
|
ngOnDestroy() {
|
|
@@ -745,8 +1532,8 @@ class GenerateDocumentConfigurationComponent {
|
|
|
745
1532
|
}
|
|
746
1533
|
this.handleValid(formValue);
|
|
747
1534
|
}
|
|
748
|
-
onDataMappingChange(
|
|
749
|
-
this.dataMapping$.next(
|
|
1535
|
+
onDataMappingChange(expression) {
|
|
1536
|
+
this.dataMapping$.next(expression);
|
|
750
1537
|
const currentFormValue = this.formValue$.getValue();
|
|
751
1538
|
if (currentFormValue) {
|
|
752
1539
|
this.handleValid(currentFormValue);
|
|
@@ -767,7 +1554,10 @@ class GenerateDocumentConfigurationComponent {
|
|
|
767
1554
|
this.revalidate();
|
|
768
1555
|
}
|
|
769
1556
|
addAttributeEntry() {
|
|
770
|
-
this.variantAttributeEntries = [
|
|
1557
|
+
this.variantAttributeEntries = [
|
|
1558
|
+
...this.variantAttributeEntries,
|
|
1559
|
+
{ key: '', value: '', required: true },
|
|
1560
|
+
];
|
|
771
1561
|
this.revalidate();
|
|
772
1562
|
}
|
|
773
1563
|
removeAttributeEntry(index) {
|
|
@@ -817,7 +1607,9 @@ class GenerateDocumentConfigurationComponent {
|
|
|
817
1607
|
}
|
|
818
1608
|
initContext() {
|
|
819
1609
|
if (this.context$) {
|
|
820
|
-
this.context
|
|
1610
|
+
this.context$
|
|
1611
|
+
.pipe(takeUntil$1(this.destroy$), filter(([context]) => context === 'case'))
|
|
1612
|
+
.subscribe(([, params]) => {
|
|
821
1613
|
this.caseDefinitionKey = params.caseDefinitionKey;
|
|
822
1614
|
this.cdr.markForCheck();
|
|
823
1615
|
});
|
|
@@ -826,10 +1618,12 @@ class GenerateDocumentConfigurationComponent {
|
|
|
826
1618
|
initPluginConfiguration() {
|
|
827
1619
|
const sources = [];
|
|
828
1620
|
if (this.selectedPluginConfigurationData$) {
|
|
829
|
-
sources.push(this.selectedPluginConfigurationData$.pipe(filter(config => !!config?.configurationId), map(config => config.configurationId)));
|
|
1621
|
+
sources.push(this.selectedPluginConfigurationData$.pipe(filter((config) => !!config?.configurationId), map((config) => config.configurationId)));
|
|
830
1622
|
}
|
|
831
|
-
sources.push(this.processLinkStateService.selectedProcessLink$.pipe(filter(processLink => !!processLink?.pluginConfigurationId), map(processLink => processLink.pluginConfigurationId)));
|
|
832
|
-
merge(...sources)
|
|
1623
|
+
sources.push(this.processLinkStateService.selectedProcessLink$.pipe(filter((processLink) => !!processLink?.pluginConfigurationId), map((processLink) => processLink.pluginConfigurationId)));
|
|
1624
|
+
merge(...sources)
|
|
1625
|
+
.pipe(takeUntil$1(this.destroy$))
|
|
1626
|
+
.subscribe((configurationId) => {
|
|
833
1627
|
this.pluginConfigurationId$.next(configurationId);
|
|
834
1628
|
});
|
|
835
1629
|
}
|
|
@@ -844,87 +1638,134 @@ class GenerateDocumentConfigurationComponent {
|
|
|
844
1638
|
* prefill + templateFields loaded → seed dataMapping
|
|
845
1639
|
*/
|
|
846
1640
|
initCascade() {
|
|
847
|
-
const configId$ = this.pluginConfigurationId$.pipe(filter(id => !!id), distinctUntilChanged());
|
|
1641
|
+
const configId$ = this.pluginConfigurationId$.pipe(filter((id) => !!id), distinctUntilChanged());
|
|
848
1642
|
// ── Catalogs: load when pluginConfigurationId changes ──
|
|
849
|
-
configId
|
|
1643
|
+
configId$
|
|
1644
|
+
.pipe(takeUntil$1(this.destroy$), tap(() => this.catalogs$.next(loadingResource(this.catalogs$.getValue().data))), switchMap((configurationId) => this.epistolaPluginService.getCatalogs(configurationId).pipe(map((catalogs) => successResource(catalogs.map((c) => ({ id: c.id, text: c.name })))), catchError(() => of(errorResource([], 'Failed to load catalogs'))))))
|
|
1645
|
+
.subscribe((resource) => this.catalogs$.next(resource));
|
|
850
1646
|
// ── Environments: load when pluginConfigurationId changes (independent) ──
|
|
851
|
-
configId
|
|
1647
|
+
configId$
|
|
1648
|
+
.pipe(takeUntil$1(this.destroy$), tap(() => this.environments$.next(loadingResource(this.environments$.getValue().data))), switchMap((configurationId) => this.epistolaPluginService.getEnvironments(configurationId).pipe(map((envs) => successResource(envs.map((e) => ({ id: e.id, text: e.name })))), catchError(() => of(errorResource([], 'Failed to load environments'))))))
|
|
1649
|
+
.subscribe((resource) => this.environments$.next(resource));
|
|
852
1650
|
// ── Seed selectedCatalogId$ from prefill once catalogs are loaded ──
|
|
853
1651
|
combineLatest([
|
|
854
|
-
this.prefill$.pipe(filter(config => !!config?.catalogId)),
|
|
855
|
-
this.catalogs$.pipe(filter(c => !c.loading && c.data.length > 0))
|
|
856
|
-
])
|
|
1652
|
+
this.prefill$.pipe(filter((config) => !!config?.catalogId)),
|
|
1653
|
+
this.catalogs$.pipe(filter((c) => !c.loading && c.data.length > 0)),
|
|
1654
|
+
])
|
|
1655
|
+
.pipe(takeUntil$1(this.destroy$), take$1(1))
|
|
1656
|
+
.subscribe(([config]) => {
|
|
857
1657
|
this.selectedCatalogId$.next(config.catalogId);
|
|
858
1658
|
});
|
|
859
1659
|
// ── Templates: load when catalogId changes ──
|
|
860
|
-
const catalogId$ = this.selectedCatalogId$.pipe(filter(id => !!id), distinctUntilChanged());
|
|
861
|
-
combineLatest([configId$, catalogId$])
|
|
1660
|
+
const catalogId$ = this.selectedCatalogId$.pipe(filter((id) => !!id), distinctUntilChanged());
|
|
1661
|
+
combineLatest([configId$, catalogId$])
|
|
1662
|
+
.pipe(takeUntil$1(this.destroy$), tap(() => this.templates$.next(loadingResource(this.templates$.getValue().data))), switchMap(([configurationId, catalogId]) => this.epistolaPluginService.getTemplates(configurationId, catalogId).pipe(map((templates) => successResource(templates.map((t) => ({ id: t.id, text: t.name })))), catchError(() => of(errorResource([], 'Failed to load templates'))))))
|
|
1663
|
+
.subscribe((resource) => this.templates$.next(resource));
|
|
862
1664
|
// ── Attributes: load when catalogId changes ──
|
|
863
|
-
combineLatest([configId$, catalogId$])
|
|
864
|
-
this.
|
|
1665
|
+
combineLatest([configId$, catalogId$])
|
|
1666
|
+
.pipe(takeUntil$1(this.destroy$), switchMap(([configurationId, catalogId]) => this.epistolaPluginService
|
|
1667
|
+
.getAttributes(configurationId, catalogId)
|
|
1668
|
+
.pipe(catchError(() => of([])))))
|
|
1669
|
+
.subscribe((attributes) => {
|
|
1670
|
+
this.availableAttributeKeys = attributes.map((a) => a.key).sort();
|
|
865
1671
|
this.cdr.markForCheck();
|
|
866
1672
|
});
|
|
867
1673
|
// ── Seed selectedTemplateId$ from prefill once templates are loaded ──
|
|
868
1674
|
combineLatest([
|
|
869
|
-
this.prefill$.pipe(filter(config => !!config?.templateId)),
|
|
870
|
-
this.templates$.pipe(filter(t => !t.loading && t.data.length > 0))
|
|
871
|
-
])
|
|
1675
|
+
this.prefill$.pipe(filter((config) => !!config?.templateId)),
|
|
1676
|
+
this.templates$.pipe(filter((t) => !t.loading && t.data.length > 0)),
|
|
1677
|
+
])
|
|
1678
|
+
.pipe(takeUntil$1(this.destroy$), take$1(1))
|
|
1679
|
+
.subscribe(([config]) => {
|
|
872
1680
|
this.selectedTemplateId$.next(config.templateId);
|
|
873
1681
|
});
|
|
874
1682
|
// ── Variants: load when templateId changes ──
|
|
875
|
-
const templateId$ = this.selectedTemplateId$.pipe(filter(id => !!id), distinctUntilChanged());
|
|
876
|
-
combineLatest([configId$, catalogId$, templateId$])
|
|
1683
|
+
const templateId$ = this.selectedTemplateId$.pipe(filter((id) => !!id), distinctUntilChanged());
|
|
1684
|
+
combineLatest([configId$, catalogId$, templateId$])
|
|
1685
|
+
.pipe(takeUntil$1(this.destroy$), tap(() => this.variants$.next(loadingResource(this.variants$.getValue().data))), switchMap(([configurationId, catalogId, templateId]) => this.epistolaPluginService.getVariants(configurationId, templateId, catalogId).pipe(map((variants) => successResource(variants.map((v) => ({
|
|
1686
|
+
id: v.id,
|
|
1687
|
+
text: v.name + this.formatAttributes(v.attributes),
|
|
1688
|
+
})))), catchError(() => of(errorResource([], 'Failed to load variants'))))))
|
|
1689
|
+
.subscribe((resource) => this.variants$.next(resource));
|
|
877
1690
|
// ── Template fields: load when templateId changes ──
|
|
878
|
-
combineLatest([configId$, catalogId$, templateId$])
|
|
1691
|
+
combineLatest([configId$, catalogId$, templateId$])
|
|
1692
|
+
.pipe(takeUntil$1(this.destroy$), tap(() => {
|
|
879
1693
|
this.templateFields$.next(loadingResource(this.templateFields$.getValue().data));
|
|
880
1694
|
this.loadProcessVariables();
|
|
881
|
-
|
|
1695
|
+
this.loadVariableSuggestions();
|
|
1696
|
+
}), switchMap(([configurationId, catalogId, templateId]) => this.epistolaPluginService
|
|
1697
|
+
.getTemplateDetails(configurationId, templateId, catalogId)
|
|
1698
|
+
.pipe(map((details) => successResource(details.fields || [])), catchError(() => of(errorResource([], 'Failed to load template fields'))))))
|
|
1699
|
+
.subscribe((resource) => this.templateFields$.next(resource));
|
|
882
1700
|
// ── Seed variant + dataMapping from prefill once templateFields are loaded ──
|
|
883
1701
|
combineLatest([
|
|
884
|
-
this.prefill$.pipe(filter(config => !!config?.templateId)),
|
|
885
|
-
this.templateFields$.pipe(filter(tf => !tf.loading && tf.data.length > 0))
|
|
886
|
-
])
|
|
1702
|
+
this.prefill$.pipe(filter((config) => !!config?.templateId)),
|
|
1703
|
+
this.templateFields$.pipe(filter((tf) => !tf.loading && tf.data.length > 0)),
|
|
1704
|
+
])
|
|
1705
|
+
.pipe(takeUntil$1(this.destroy$), take$1(1))
|
|
1706
|
+
.subscribe(([config]) => {
|
|
887
1707
|
if (!config)
|
|
888
1708
|
return;
|
|
889
1709
|
// Apply variant prefill
|
|
890
|
-
if (config.variantAttributes &&
|
|
1710
|
+
if (config.variantAttributes &&
|
|
1711
|
+
(Array.isArray(config.variantAttributes)
|
|
1712
|
+
? config.variantAttributes.length > 0
|
|
1713
|
+
: Object.keys(config.variantAttributes).length > 0)) {
|
|
891
1714
|
this.variantSelectionMode = 'attributes';
|
|
892
1715
|
if (Array.isArray(config.variantAttributes)) {
|
|
893
|
-
this.variantAttributeEntries = config.variantAttributes
|
|
894
|
-
|
|
1716
|
+
this.variantAttributeEntries = config.variantAttributes.map((e) => ({
|
|
1717
|
+
key: e.key,
|
|
1718
|
+
value: e.value,
|
|
1719
|
+
required: e.required !== false,
|
|
1720
|
+
}));
|
|
895
1721
|
}
|
|
896
1722
|
else {
|
|
897
|
-
this.variantAttributeEntries = Object.entries(config.variantAttributes)
|
|
898
|
-
.map(([key, value]) => ({ key, value: String(value), required: true }));
|
|
1723
|
+
this.variantAttributeEntries = Object.entries(config.variantAttributes).map(([key, value]) => ({ key, value: String(value), required: true }));
|
|
899
1724
|
}
|
|
900
1725
|
}
|
|
901
1726
|
else if (config.variantId) {
|
|
902
1727
|
this.variantSelectionMode = 'explicit';
|
|
903
1728
|
this.selectedVariantId$.next(config.variantId);
|
|
904
1729
|
}
|
|
905
|
-
// Apply dataMapping prefill
|
|
906
|
-
// Use setTimeout to ensure the tree component exists in the DOM (after *ngIf resolves)
|
|
907
|
-
// before setting the prefill, so ngOnChanges fires correctly on the child.
|
|
1730
|
+
// Apply dataMapping prefill (JSONata expression string)
|
|
908
1731
|
if (config.dataMapping) {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
this.prefillDataMapping = { ...config.dataMapping };
|
|
912
|
-
this.cdr.detectChanges();
|
|
913
|
-
});
|
|
1732
|
+
const expr = typeof config.dataMapping === 'string' ? config.dataMapping : '';
|
|
1733
|
+
this.dataMapping$.next(expr);
|
|
914
1734
|
}
|
|
915
1735
|
else {
|
|
916
1736
|
this.cdr.detectChanges();
|
|
917
1737
|
}
|
|
918
1738
|
});
|
|
919
1739
|
}
|
|
1740
|
+
loadExpressionFunctions() {
|
|
1741
|
+
this.epistolaPluginService
|
|
1742
|
+
.getExpressionFunctions()
|
|
1743
|
+
.pipe(takeUntil$1(this.destroy$), catchError(() => of([])))
|
|
1744
|
+
.subscribe((functions) => {
|
|
1745
|
+
this.expressionFunctions = functions;
|
|
1746
|
+
this.cdr.markForCheck();
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
920
1749
|
loadProcessVariables() {
|
|
921
1750
|
if (this.caseDefinitionKey) {
|
|
922
|
-
this.epistolaPluginService
|
|
1751
|
+
this.epistolaPluginService
|
|
1752
|
+
.getProcessVariables(this.caseDefinitionKey)
|
|
1753
|
+
.pipe(takeUntil$1(this.destroy$), catchError(() => of([])))
|
|
1754
|
+
.subscribe((variables) => {
|
|
923
1755
|
this.processVariables = variables;
|
|
924
1756
|
this.cdr.markForCheck();
|
|
925
1757
|
});
|
|
926
1758
|
}
|
|
927
1759
|
}
|
|
1760
|
+
loadVariableSuggestions() {
|
|
1761
|
+
this.epistolaPluginService
|
|
1762
|
+
.getVariableSuggestions(this.caseDefinitionKey ?? undefined, this.caseDefinitionKey ?? undefined)
|
|
1763
|
+
.pipe(takeUntil$1(this.destroy$), catchError(() => of({ doc: [], pv: [] })))
|
|
1764
|
+
.subscribe((suggestions) => {
|
|
1765
|
+
this.variableSuggestions = suggestions;
|
|
1766
|
+
this.cdr.markForCheck();
|
|
1767
|
+
});
|
|
1768
|
+
}
|
|
928
1769
|
handleValid(formValue) {
|
|
929
1770
|
const baseComplete = !!(this.selectedCatalogId$.getValue() &&
|
|
930
1771
|
formValue?.templateId &&
|
|
@@ -933,7 +1774,7 @@ class GenerateDocumentConfigurationComponent {
|
|
|
933
1774
|
formValue?.resultProcessVariable);
|
|
934
1775
|
let variantValid = true;
|
|
935
1776
|
if (this.variantSelectionMode === 'attributes' && this.variantAttributeEntries.length > 0) {
|
|
936
|
-
variantValid = this.variantAttributeEntries.every(e => !!e.key && !!e.value);
|
|
1777
|
+
variantValid = this.variantAttributeEntries.every((e) => !!e.key && !!e.value);
|
|
937
1778
|
}
|
|
938
1779
|
const requiredFieldsMapped = this.requiredFieldsStatus.total === 0 ||
|
|
939
1780
|
this.requiredFieldsStatus.mapped === this.requiredFieldsStatus.total;
|
|
@@ -957,15 +1798,15 @@ class GenerateDocumentConfigurationComponent {
|
|
|
957
1798
|
outputFormat: formValue.outputFormat,
|
|
958
1799
|
filename: formValue.filename,
|
|
959
1800
|
correlationId: formValue.correlationId || undefined,
|
|
960
|
-
resultProcessVariable: formValue.resultProcessVariable
|
|
1801
|
+
resultProcessVariable: formValue.resultProcessVariable,
|
|
961
1802
|
};
|
|
962
1803
|
if (this.variantSelectionMode === 'explicit') {
|
|
963
1804
|
config.variantId = formValue.variantId;
|
|
964
1805
|
}
|
|
965
1806
|
else {
|
|
966
1807
|
config.variantAttributes = this.variantAttributeEntries
|
|
967
|
-
.filter(e => e.key && e.value)
|
|
968
|
-
.map(e => ({ key: e.key, value: e.value, required: e.required }));
|
|
1808
|
+
.filter((e) => e.key && e.value)
|
|
1809
|
+
.map((e) => ({ key: e.key, value: e.value, required: e.required }));
|
|
969
1810
|
}
|
|
970
1811
|
this.configuration.emit(config);
|
|
971
1812
|
}
|
|
@@ -973,7 +1814,7 @@ class GenerateDocumentConfigurationComponent {
|
|
|
973
1814
|
});
|
|
974
1815
|
}
|
|
975
1816
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GenerateDocumentConfigurationComponent, deps: [{ token: EpistolaPluginService }, { token: i2$2.ProcessLinkStateService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
976
|
-
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 catalogs: catalogs$ | async,\n templates: templates$ | async,\n variants: variants$ | async,\n environments: environments$ | async,\n templateFields: templateFields$ | async,\n selectedCatalogId: selectedCatalogId$ | async,\n selectedTemplateId: selectedTemplateId$ | async\n } as obs\"\n>\n <v-select\n name=\"catalogId\"\n [title]=\"'catalogId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'catalogIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.catalogs.data\"\n [defaultSelectionId]=\"obs.prefill?.catalogId\"\n [disabled]=\"obs.disabled || obs.catalogs.loading\"\n [required]=\"true\"\n [loading]=\"obs.catalogs.loading\"\n >\n </v-select>\n <div *ngIf=\"obs.catalogs.error\" class=\"loading-error\">{{ obs.catalogs.error }}</div>\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 || !obs.selectedCatalogId\"\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 <select\n *ngIf=\"!entry._customKey\"\n class=\"variant-attribute-input\"\n [ngModel]=\"entry.key\"\n (ngModelChange)=\"onKeySelected(entry, $event)\"\n [disabled]=\"obs.disabled\"\n >\n <option value=\"\" disabled>{{ 'attributeKey' | pluginTranslate: pluginId | async }}</option>\n <option *ngFor=\"let key of availableAttributeKeys\" [value]=\"key\">{{ key }}</option>\n <option value=\"__custom__\">{{ 'attributeKeyCustom' | pluginTranslate: pluginId | async }}</option>\n </select>\n <div *ngIf=\"entry._customKey\" class=\"custom-key-input\">\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 <button type=\"button\" class=\"custom-key-cancel\" (click)=\"cancelCustomKey(entry)\" [disabled]=\"obs.disabled\">×</button>\n </div>\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 <label class=\"variant-attribute-required-toggle\">\n <input\n type=\"checkbox\"\n [(ngModel)]=\"entry.required\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <span class=\"required-label\">{{ (entry.required ? 'attributeRequired' : 'attributePreferred') | pluginTranslate: pluginId | async }}</span>\n </label>\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}.custom-key-input{display:flex;flex:1;gap:.25rem}.custom-key-input .variant-attribute-input{flex:1}.custom-key-cancel{padding:.25rem .5rem;font-size:1rem;line-height:1;background:none;border:1px solid #d1d5db;border-radius:4px;cursor:pointer;color:#6b7280}.custom-key-cancel:hover:not([disabled]){color:#dc3545;border-color:#dc3545}.custom-key-cancel[disabled]{opacity:.5;cursor:not-allowed}.variant-attribute-required-toggle{display:flex;align-items:center;gap:.25rem;font-size:.75rem;color:#374151;white-space:nowrap;cursor:pointer}.variant-attribute-required-toggle input[type=checkbox]{margin:0;cursor:pointer}.variant-attribute-required-toggle .required-label{-webkit-user-select:none;user-select:none}.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.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { 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 });
|
|
1817
|
+
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 catalogs: catalogs$ | async,\n templates: templates$ | async,\n variants: variants$ | async,\n environments: environments$ | async,\n templateFields: templateFields$ | async,\n selectedCatalogId: selectedCatalogId$ | async,\n selectedTemplateId: selectedTemplateId$ | async,\n } as obs\"\n>\n <v-select\n name=\"catalogId\"\n [title]=\"'catalogId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'catalogIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.catalogs.data\"\n [defaultSelectionId]=\"obs.prefill?.catalogId\"\n [disabled]=\"obs.disabled || obs.catalogs.loading\"\n [required]=\"true\"\n [loading]=\"obs.catalogs.loading\"\n >\n </v-select>\n <div *ngIf=\"obs.catalogs.error\" class=\"loading-error\">{{ obs.catalogs.error }}</div>\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 || !obs.selectedCatalogId\"\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\">{{\n 'variantSelectionMode' | pluginTranslate: pluginId | async\n }}</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 >\n {{ 'selectByVariant' | pluginTranslate: pluginId | async }}\n </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 >\n {{ 'selectByAttributes' | pluginTranslate: pluginId | async }}\n </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\n *ngIf=\"variantSelectionMode === 'attributes' && obs.selectedTemplateId\"\n class=\"variant-attributes-section\"\n >\n <label class=\"variant-attributes-label\">{{\n 'variantAttributes' | pluginTranslate: pluginId | async\n }}</label>\n <div class=\"variant-attributes-list\">\n <div\n *ngFor=\"let entry of variantAttributeEntries; let i = index\"\n class=\"variant-attribute-row\"\n >\n <select\n *ngIf=\"!entry._customKey\"\n class=\"variant-attribute-input\"\n [ngModel]=\"entry.key\"\n (ngModelChange)=\"onKeySelected(entry, $event)\"\n [disabled]=\"obs.disabled\"\n >\n <option value=\"\" disabled>\n {{ 'attributeKey' | pluginTranslate: pluginId | async }}\n </option>\n <option *ngFor=\"let key of availableAttributeKeys\" [value]=\"key\">{{ key }}</option>\n <option value=\"__custom__\">\n {{ 'attributeKeyCustom' | pluginTranslate: pluginId | async }}\n </option>\n </select>\n <div *ngIf=\"entry._customKey\" class=\"custom-key-input\">\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 <button\n type=\"button\"\n class=\"custom-key-cancel\"\n (click)=\"cancelCustomKey(entry)\"\n [disabled]=\"obs.disabled\"\n >\n ×\n </button>\n </div>\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 <label class=\"variant-attribute-required-toggle\">\n <input\n type=\"checkbox\"\n [(ngModel)]=\"entry.required\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <span class=\"required-label\">{{\n (entry.required ? 'attributeRequired' : 'attributePreferred')\n | pluginTranslate: pluginId\n | async\n }}</span>\n </label>\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 >\n ×\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 >\n + {{ 'addAttribute' | pluginTranslate: pluginId | async }}\n </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\">\n {{ templateFieldsError }}\n</div>\n\n<div *ngIf=\"selectedTemplateId$ | async\" class=\"mapping-section\">\n <h5 class=\"mapping-section__title\">\n {{ 'dataMappingTitle' | pluginTranslate: pluginId | async }}\n </h5>\n <p class=\"mapping-section__description\">\n {{ 'dataMappingDescription' | pluginTranslate: pluginId | async }}\n </p>\n <div class=\"mapping-mode-toggle\">\n <button\n class=\"mapping-mode-toggle__btn\"\n [class.mapping-mode-toggle__btn--active]=\"mappingMode === 'simple'\"\n (click)=\"mappingMode = 'simple'\"\n >\n {{ 'mappingModeSimple' | pluginTranslate: pluginId | async }}\n </button>\n <button\n class=\"mapping-mode-toggle__btn\"\n [class.mapping-mode-toggle__btn--active]=\"mappingMode === 'advanced'\"\n (click)=\"mappingMode = 'advanced'\"\n >\n {{ 'mappingModeAdvanced' | pluginTranslate: pluginId | async }}\n </button>\n </div>\n\n <!-- Editor area (full width) -->\n <epistola-mapping-builder\n *ngIf=\"mappingMode === 'simple'\"\n [expression]=\"dataMapping$ | async\"\n [templateFields]=\"(templateFields$ | async)?.data ?? []\"\n [suggestions]=\"variableSuggestions\"\n [disabled]=\"!!(disabled$ | async)\"\n (expressionChange)=\"onDataMappingChange($event)\"\n ></epistola-mapping-builder>\n\n <epistola-jsonata-editor\n *ngIf=\"mappingMode === 'advanced'\"\n [expression]=\"dataMapping$ | async\"\n [disabled]=\"!!(disabled$ | async)\"\n [suggestions]=\"variableSuggestions\"\n [functions]=\"expressionFunctions\"\n (expressionChange)=\"onDataMappingChange($event)\"\n ></epistola-jsonata-editor>\n\n <!-- Bottom tabs: Schema + Preview (collapsible) -->\n <div class=\"mapping-tools\" [class.mapping-tools--collapsed]=\"toolsCollapsed\">\n <div class=\"mapping-tools__header\" (click)=\"toolsCollapsed = !toolsCollapsed\">\n <span class=\"mapping-tools__chevron\">{{ toolsCollapsed ? '▶' : '▼' }}</span>\n <span>{{ 'mappingTools' | pluginTranslate: pluginId | async }}</span>\n </div>\n <div *ngIf=\"!toolsCollapsed\" class=\"mapping-tools__content\">\n <div class=\"mapping-tools__tabs\">\n <button\n class=\"mapping-tools__tab\"\n [class.mapping-tools__tab--active]=\"activeToolTab === 'schema'\"\n (click)=\"activeToolTab = 'schema'\"\n >\n {{ 'expectedStructure' | pluginTranslate: pluginId | async }}\n </button>\n <button\n class=\"mapping-tools__tab\"\n [class.mapping-tools__tab--active]=\"activeToolTab === 'preview'\"\n (click)=\"activeToolTab = 'preview'\"\n >\n {{ 'previewTitle' | pluginTranslate: pluginId | async }}\n </button>\n </div>\n\n <epistola-expected-structure\n *ngIf=\"activeToolTab === 'schema'\"\n [templateFields]=\"(templateFields$ | async)?.data ?? []\"\n ></epistola-expected-structure>\n\n <epistola-mapping-preview\n *ngIf=\"activeToolTab === 'preview'\"\n [expression]=\"dataMapping$ | async\"\n [templateFields]=\"(templateFields$ | async)?.data ?? []\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n ></epistola-mapping-preview>\n </div>\n </div>\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}.custom-key-input{display:flex;flex:1;gap:.25rem}.custom-key-input .variant-attribute-input{flex:1}.custom-key-cancel{padding:.25rem .5rem;font-size:1rem;line-height:1;background:none;border:1px solid #d1d5db;border-radius:4px;cursor:pointer;color:#6b7280}.custom-key-cancel:hover:not([disabled]){color:#dc3545;border-color:#dc3545}.custom-key-cancel[disabled]{opacity:.5;cursor:not-allowed}.variant-attribute-required-toggle{display:flex;align-items:center;gap:.25rem;font-size:.75rem;color:#374151;white-space:nowrap;cursor:pointer}.variant-attribute-required-toggle input[type=checkbox]{margin:0;cursor:pointer}.variant-attribute-required-toggle .required-label{-webkit-user-select:none;user-select:none}.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}.mapping-section{margin-top:1rem}.mapping-section__title{font-size:1rem;font-weight:600;margin:0 0 4px}.mapping-section__description{font-size:.85em;color:#6f6f6f;margin:0 0 12px}.mapping-mode-toggle{display:flex;gap:0;margin-bottom:12px}.mapping-mode-toggle__btn{padding:6px 16px;border:1px solid #e0e0e0;background:#fff;font-size:.85em;cursor:pointer}.mapping-mode-toggle__btn:first-child{border-radius:4px 0 0 4px}.mapping-mode-toggle__btn:last-child{border-radius:0 4px 4px 0;border-left:none}.mapping-mode-toggle__btn--active{background:#0f62fe;color:#fff;border-color:#0f62fe}.mapping-tools{margin-top:12px;border:1px solid #e0e0e0;border-radius:4px;overflow:hidden}.mapping-tools__header{display:flex;align-items:center;gap:6px;padding:8px 12px;background:#f4f4f4;cursor:pointer;font-size:.85em;font-weight:500;-webkit-user-select:none;user-select:none}.mapping-tools__header:hover{background:#e8e8e8}.mapping-tools__chevron{font-size:.7em}.mapping-tools__content{border-top:1px solid #e0e0e0}.mapping-tools__tabs{display:flex;border-bottom:1px solid #e0e0e0}.mapping-tools__tab{padding:6px 16px;border:none;background:transparent;font-size:.8em;cursor:pointer;border-bottom:2px solid transparent}.mapping-tools__tab--active{border-bottom-color:#0f62fe;font-weight:500}\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.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { 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: ExpectedStructureComponent, selector: "epistola-expected-structure", inputs: ["templateFields"] }, { kind: "component", type: JsonataEditorComponent, selector: "epistola-jsonata-editor", inputs: ["expression", "disabled", "suggestions", "functions"], outputs: ["expressionChange", "validChange"] }, { kind: "component", type: MappingBuilderComponent, selector: "epistola-mapping-builder", inputs: ["expression", "templateFields", "suggestions", "disabled"], outputs: ["expressionChange"] }, { kind: "component", type: MappingPreviewComponent, selector: "epistola-mapping-preview", inputs: ["expression", "templateFields", "caseDefinitionKey"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
977
1818
|
}
|
|
978
1819
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GenerateDocumentConfigurationComponent, decorators: [{
|
|
979
1820
|
type: Component,
|
|
@@ -984,8 +1825,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
984
1825
|
FormModule,
|
|
985
1826
|
InputModule,
|
|
986
1827
|
SelectModule,
|
|
987
|
-
|
|
988
|
-
], template: "<v-form\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"{\n disabled: disabled$ | async,\n prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,\n catalogs: catalogs$ | async,\n templates: templates$ | async,\n variants: variants$ | async,\n environments: environments$ | async,\n templateFields: templateFields$ | async,\n selectedCatalogId: selectedCatalogId$ | async,\n selectedTemplateId: selectedTemplateId$ | async\n } as obs\"\n>\n <v-select\n name=\"catalogId\"\n [title]=\"'catalogId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'catalogIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.catalogs.data\"\n [defaultSelectionId]=\"obs.prefill?.catalogId\"\n [disabled]=\"obs.disabled || obs.catalogs.loading\"\n [required]=\"true\"\n [loading]=\"obs.catalogs.loading\"\n >\n </v-select>\n <div *ngIf=\"obs.catalogs.error\" class=\"loading-error\">{{ obs.catalogs.error }}</div>\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 || !obs.selectedCatalogId\"\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 <select\n *ngIf=\"!entry._customKey\"\n class=\"variant-attribute-input\"\n [ngModel]=\"entry.key\"\n (ngModelChange)=\"onKeySelected(entry, $event)\"\n [disabled]=\"obs.disabled\"\n >\n <option value=\"\" disabled>{{ 'attributeKey' | pluginTranslate: pluginId | async }}</option>\n <option *ngFor=\"let key of availableAttributeKeys\" [value]=\"key\">{{ key }}</option>\n <option value=\"__custom__\">{{ 'attributeKeyCustom' | pluginTranslate: pluginId | async }}</option>\n </select>\n <div *ngIf=\"entry._customKey\" class=\"custom-key-input\">\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 <button type=\"button\" class=\"custom-key-cancel\" (click)=\"cancelCustomKey(entry)\" [disabled]=\"obs.disabled\">×</button>\n </div>\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 <label class=\"variant-attribute-required-toggle\">\n <input\n type=\"checkbox\"\n [(ngModel)]=\"entry.required\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <span class=\"required-label\">{{ (entry.required ? 'attributeRequired' : 'attributePreferred') | pluginTranslate: pluginId | async }}</span>\n </label>\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}.custom-key-input{display:flex;flex:1;gap:.25rem}.custom-key-input .variant-attribute-input{flex:1}.custom-key-cancel{padding:.25rem .5rem;font-size:1rem;line-height:1;background:none;border:1px solid #d1d5db;border-radius:4px;cursor:pointer;color:#6b7280}.custom-key-cancel:hover:not([disabled]){color:#dc3545;border-color:#dc3545}.custom-key-cancel[disabled]{opacity:.5;cursor:not-allowed}.variant-attribute-required-toggle{display:flex;align-items:center;gap:.25rem;font-size:.75rem;color:#374151;white-space:nowrap;cursor:pointer}.variant-attribute-required-toggle input[type=checkbox]{margin:0;cursor:pointer}.variant-attribute-required-toggle .required-label{-webkit-user-select:none;user-select:none}.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"] }]
|
|
1828
|
+
ExpectedStructureComponent,
|
|
1829
|
+
JsonataEditorComponent,
|
|
1830
|
+
MappingBuilderComponent,
|
|
1831
|
+
MappingPreviewComponent,
|
|
1832
|
+
], template: "<v-form\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"{\n disabled: disabled$ | async,\n prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,\n catalogs: catalogs$ | async,\n templates: templates$ | async,\n variants: variants$ | async,\n environments: environments$ | async,\n templateFields: templateFields$ | async,\n selectedCatalogId: selectedCatalogId$ | async,\n selectedTemplateId: selectedTemplateId$ | async,\n } as obs\"\n>\n <v-select\n name=\"catalogId\"\n [title]=\"'catalogId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'catalogIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.catalogs.data\"\n [defaultSelectionId]=\"obs.prefill?.catalogId\"\n [disabled]=\"obs.disabled || obs.catalogs.loading\"\n [required]=\"true\"\n [loading]=\"obs.catalogs.loading\"\n >\n </v-select>\n <div *ngIf=\"obs.catalogs.error\" class=\"loading-error\">{{ obs.catalogs.error }}</div>\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 || !obs.selectedCatalogId\"\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\">{{\n 'variantSelectionMode' | pluginTranslate: pluginId | async\n }}</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 >\n {{ 'selectByVariant' | pluginTranslate: pluginId | async }}\n </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 >\n {{ 'selectByAttributes' | pluginTranslate: pluginId | async }}\n </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\n *ngIf=\"variantSelectionMode === 'attributes' && obs.selectedTemplateId\"\n class=\"variant-attributes-section\"\n >\n <label class=\"variant-attributes-label\">{{\n 'variantAttributes' | pluginTranslate: pluginId | async\n }}</label>\n <div class=\"variant-attributes-list\">\n <div\n *ngFor=\"let entry of variantAttributeEntries; let i = index\"\n class=\"variant-attribute-row\"\n >\n <select\n *ngIf=\"!entry._customKey\"\n class=\"variant-attribute-input\"\n [ngModel]=\"entry.key\"\n (ngModelChange)=\"onKeySelected(entry, $event)\"\n [disabled]=\"obs.disabled\"\n >\n <option value=\"\" disabled>\n {{ 'attributeKey' | pluginTranslate: pluginId | async }}\n </option>\n <option *ngFor=\"let key of availableAttributeKeys\" [value]=\"key\">{{ key }}</option>\n <option value=\"__custom__\">\n {{ 'attributeKeyCustom' | pluginTranslate: pluginId | async }}\n </option>\n </select>\n <div *ngIf=\"entry._customKey\" class=\"custom-key-input\">\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 <button\n type=\"button\"\n class=\"custom-key-cancel\"\n (click)=\"cancelCustomKey(entry)\"\n [disabled]=\"obs.disabled\"\n >\n ×\n </button>\n </div>\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 <label class=\"variant-attribute-required-toggle\">\n <input\n type=\"checkbox\"\n [(ngModel)]=\"entry.required\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <span class=\"required-label\">{{\n (entry.required ? 'attributeRequired' : 'attributePreferred')\n | pluginTranslate: pluginId\n | async\n }}</span>\n </label>\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 >\n ×\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 >\n + {{ 'addAttribute' | pluginTranslate: pluginId | async }}\n </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\">\n {{ templateFieldsError }}\n</div>\n\n<div *ngIf=\"selectedTemplateId$ | async\" class=\"mapping-section\">\n <h5 class=\"mapping-section__title\">\n {{ 'dataMappingTitle' | pluginTranslate: pluginId | async }}\n </h5>\n <p class=\"mapping-section__description\">\n {{ 'dataMappingDescription' | pluginTranslate: pluginId | async }}\n </p>\n <div class=\"mapping-mode-toggle\">\n <button\n class=\"mapping-mode-toggle__btn\"\n [class.mapping-mode-toggle__btn--active]=\"mappingMode === 'simple'\"\n (click)=\"mappingMode = 'simple'\"\n >\n {{ 'mappingModeSimple' | pluginTranslate: pluginId | async }}\n </button>\n <button\n class=\"mapping-mode-toggle__btn\"\n [class.mapping-mode-toggle__btn--active]=\"mappingMode === 'advanced'\"\n (click)=\"mappingMode = 'advanced'\"\n >\n {{ 'mappingModeAdvanced' | pluginTranslate: pluginId | async }}\n </button>\n </div>\n\n <!-- Editor area (full width) -->\n <epistola-mapping-builder\n *ngIf=\"mappingMode === 'simple'\"\n [expression]=\"dataMapping$ | async\"\n [templateFields]=\"(templateFields$ | async)?.data ?? []\"\n [suggestions]=\"variableSuggestions\"\n [disabled]=\"!!(disabled$ | async)\"\n (expressionChange)=\"onDataMappingChange($event)\"\n ></epistola-mapping-builder>\n\n <epistola-jsonata-editor\n *ngIf=\"mappingMode === 'advanced'\"\n [expression]=\"dataMapping$ | async\"\n [disabled]=\"!!(disabled$ | async)\"\n [suggestions]=\"variableSuggestions\"\n [functions]=\"expressionFunctions\"\n (expressionChange)=\"onDataMappingChange($event)\"\n ></epistola-jsonata-editor>\n\n <!-- Bottom tabs: Schema + Preview (collapsible) -->\n <div class=\"mapping-tools\" [class.mapping-tools--collapsed]=\"toolsCollapsed\">\n <div class=\"mapping-tools__header\" (click)=\"toolsCollapsed = !toolsCollapsed\">\n <span class=\"mapping-tools__chevron\">{{ toolsCollapsed ? '▶' : '▼' }}</span>\n <span>{{ 'mappingTools' | pluginTranslate: pluginId | async }}</span>\n </div>\n <div *ngIf=\"!toolsCollapsed\" class=\"mapping-tools__content\">\n <div class=\"mapping-tools__tabs\">\n <button\n class=\"mapping-tools__tab\"\n [class.mapping-tools__tab--active]=\"activeToolTab === 'schema'\"\n (click)=\"activeToolTab = 'schema'\"\n >\n {{ 'expectedStructure' | pluginTranslate: pluginId | async }}\n </button>\n <button\n class=\"mapping-tools__tab\"\n [class.mapping-tools__tab--active]=\"activeToolTab === 'preview'\"\n (click)=\"activeToolTab = 'preview'\"\n >\n {{ 'previewTitle' | pluginTranslate: pluginId | async }}\n </button>\n </div>\n\n <epistola-expected-structure\n *ngIf=\"activeToolTab === 'schema'\"\n [templateFields]=\"(templateFields$ | async)?.data ?? []\"\n ></epistola-expected-structure>\n\n <epistola-mapping-preview\n *ngIf=\"activeToolTab === 'preview'\"\n [expression]=\"dataMapping$ | async\"\n [templateFields]=\"(templateFields$ | async)?.data ?? []\"\n [caseDefinitionKey]=\"caseDefinitionKey\"\n ></epistola-mapping-preview>\n </div>\n </div>\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}.custom-key-input{display:flex;flex:1;gap:.25rem}.custom-key-input .variant-attribute-input{flex:1}.custom-key-cancel{padding:.25rem .5rem;font-size:1rem;line-height:1;background:none;border:1px solid #d1d5db;border-radius:4px;cursor:pointer;color:#6b7280}.custom-key-cancel:hover:not([disabled]){color:#dc3545;border-color:#dc3545}.custom-key-cancel[disabled]{opacity:.5;cursor:not-allowed}.variant-attribute-required-toggle{display:flex;align-items:center;gap:.25rem;font-size:.75rem;color:#374151;white-space:nowrap;cursor:pointer}.variant-attribute-required-toggle input[type=checkbox]{margin:0;cursor:pointer}.variant-attribute-required-toggle .required-label{-webkit-user-select:none;user-select:none}.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}.mapping-section{margin-top:1rem}.mapping-section__title{font-size:1rem;font-weight:600;margin:0 0 4px}.mapping-section__description{font-size:.85em;color:#6f6f6f;margin:0 0 12px}.mapping-mode-toggle{display:flex;gap:0;margin-bottom:12px}.mapping-mode-toggle__btn{padding:6px 16px;border:1px solid #e0e0e0;background:#fff;font-size:.85em;cursor:pointer}.mapping-mode-toggle__btn:first-child{border-radius:4px 0 0 4px}.mapping-mode-toggle__btn:last-child{border-radius:0 4px 4px 0;border-left:none}.mapping-mode-toggle__btn--active{background:#0f62fe;color:#fff;border-color:#0f62fe}.mapping-tools{margin-top:12px;border:1px solid #e0e0e0;border-radius:4px;overflow:hidden}.mapping-tools__header{display:flex;align-items:center;gap:6px;padding:8px 12px;background:#f4f4f4;cursor:pointer;font-size:.85em;font-weight:500;-webkit-user-select:none;user-select:none}.mapping-tools__header:hover{background:#e8e8e8}.mapping-tools__chevron{font-size:.7em}.mapping-tools__content{border-top:1px solid #e0e0e0}.mapping-tools__tabs{display:flex;border-bottom:1px solid #e0e0e0}.mapping-tools__tab{padding:6px 16px;border:none;background:transparent;font-size:.8em;cursor:pointer;border-bottom:2px solid transparent}.mapping-tools__tab--active{border-bottom-color:#0f62fe;font-weight:500}\n"] }]
|
|
989
1833
|
}], ctorParameters: () => [{ type: EpistolaPluginService }, { type: i2$2.ProcessLinkStateService }, { type: i0.ChangeDetectorRef }], propDecorators: { save$: [{
|
|
990
1834
|
type: Input
|
|
991
1835
|
}], disabled$: [{
|
|
@@ -1028,8 +1872,7 @@ class CheckJobStatusConfigurationComponent {
|
|
|
1028
1872
|
this.handleValid(formValue);
|
|
1029
1873
|
}
|
|
1030
1874
|
handleValid(formValue) {
|
|
1031
|
-
const valid = !!(formValue?.requestIdVariable &&
|
|
1032
|
-
formValue?.statusVariable);
|
|
1875
|
+
const valid = !!(formValue?.requestIdVariable && formValue?.statusVariable);
|
|
1033
1876
|
this.valid$.next(valid);
|
|
1034
1877
|
this.valid.emit(valid);
|
|
1035
1878
|
}
|
|
@@ -1045,11 +1888,11 @@ class CheckJobStatusConfigurationComponent {
|
|
|
1045
1888
|
});
|
|
1046
1889
|
}
|
|
1047
1890
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CheckJobStatusConfigurationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1048
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: CheckJobStatusConfigurationComponent, isStandalone: true, selector: "epistola-check-job-status-configuration", inputs: { save$: "save$", disabled$: "disabled$", pluginId: "pluginId", prefillConfiguration$: "prefillConfiguration$" }, outputs: { valid: "valid", configuration: "configuration" }, ngImport: i0, template: "<v-form\n
|
|
1891
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: CheckJobStatusConfigurationComponent, isStandalone: true, selector: "epistola-check-job-status-configuration", inputs: { save$: "save$", disabled$: "disabled$", pluginId: "pluginId", prefillConfiguration$: "prefillConfiguration$" }, outputs: { valid: "valid", configuration: "configuration" }, ngImport: i0, template: "<v-form\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"{\n disabled: safeDisabled$ | async,\n prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,\n } as obs\"\n>\n <v-input\n name=\"requestIdVariable\"\n [title]=\"'requestIdVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'requestIdVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.requestIdVariable || 'epistolaRequestId'\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"statusVariable\"\n [title]=\"'statusVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'statusVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.statusVariable || 'epistolaStatus'\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"documentIdVariable\"\n [title]=\"'documentIdVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'documentIdVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.documentIdVariable || 'epistolaDocumentId'\"\n [disabled]=\"obs.disabled\"\n [required]=\"false\"\n >\n </v-input>\n\n <v-input\n name=\"errorMessageVariable\"\n [title]=\"'errorMessageVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'errorMessageVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.errorMessageVariable || 'epistolaErrorMessage'\"\n [disabled]=\"obs.disabled\"\n [required]=\"false\"\n >\n </v-input>\n</v-form>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: 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"] }] });
|
|
1049
1892
|
}
|
|
1050
1893
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CheckJobStatusConfigurationComponent, decorators: [{
|
|
1051
1894
|
type: Component,
|
|
1052
|
-
args: [{ selector: 'epistola-check-job-status-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form\n
|
|
1895
|
+
args: [{ selector: 'epistola-check-job-status-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"{\n disabled: safeDisabled$ | async,\n prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,\n } as obs\"\n>\n <v-input\n name=\"requestIdVariable\"\n [title]=\"'requestIdVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'requestIdVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.requestIdVariable || 'epistolaRequestId'\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"statusVariable\"\n [title]=\"'statusVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'statusVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.statusVariable || 'epistolaStatus'\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"documentIdVariable\"\n [title]=\"'documentIdVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'documentIdVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.documentIdVariable || 'epistolaDocumentId'\"\n [disabled]=\"obs.disabled\"\n [required]=\"false\"\n >\n </v-input>\n\n <v-input\n name=\"errorMessageVariable\"\n [title]=\"'errorMessageVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'errorMessageVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.errorMessageVariable || 'epistolaErrorMessage'\"\n [disabled]=\"obs.disabled\"\n [required]=\"false\"\n >\n </v-input>\n</v-form>\n" }]
|
|
1053
1896
|
}], propDecorators: { save$: [{
|
|
1054
1897
|
type: Input
|
|
1055
1898
|
}], disabled$: [{
|
|
@@ -1088,8 +1931,7 @@ class DownloadDocumentConfigurationComponent {
|
|
|
1088
1931
|
this.handleValid(formValue);
|
|
1089
1932
|
}
|
|
1090
1933
|
handleValid(formValue) {
|
|
1091
|
-
const valid = !!(formValue?.documentIdVariable &&
|
|
1092
|
-
formValue?.contentVariable);
|
|
1934
|
+
const valid = !!(formValue?.documentIdVariable && formValue?.contentVariable);
|
|
1093
1935
|
this.valid$.next(valid);
|
|
1094
1936
|
this.valid.emit(valid);
|
|
1095
1937
|
}
|
|
@@ -1105,11 +1947,11 @@ class DownloadDocumentConfigurationComponent {
|
|
|
1105
1947
|
});
|
|
1106
1948
|
}
|
|
1107
1949
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DownloadDocumentConfigurationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1108
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: DownloadDocumentConfigurationComponent, isStandalone: true, selector: "epistola-download-document-configuration", inputs: { save$: "save$", disabled$: "disabled$", pluginId: "pluginId", prefillConfiguration$: "prefillConfiguration$" }, outputs: { valid: "valid", configuration: "configuration" }, ngImport: i0, template: "<v-form\n
|
|
1950
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: DownloadDocumentConfigurationComponent, isStandalone: true, selector: "epistola-download-document-configuration", inputs: { save$: "save$", disabled$: "disabled$", pluginId: "pluginId", prefillConfiguration$: "prefillConfiguration$" }, outputs: { valid: "valid", configuration: "configuration" }, ngImport: i0, template: "<v-form\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"{\n disabled: safeDisabled$ | async,\n prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,\n } as obs\"\n>\n <v-input\n name=\"documentIdVariable\"\n [title]=\"'documentIdVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'documentIdVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.documentIdVariable || 'epistolaDocumentId'\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"contentVariable\"\n [title]=\"'contentVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'contentVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.contentVariable || 'documentContent'\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n</v-form>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: 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"] }] });
|
|
1109
1951
|
}
|
|
1110
1952
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DownloadDocumentConfigurationComponent, decorators: [{
|
|
1111
1953
|
type: Component,
|
|
1112
|
-
args: [{ selector: 'epistola-download-document-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form\n
|
|
1954
|
+
args: [{ selector: 'epistola-download-document-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"{\n disabled: safeDisabled$ | async,\n prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,\n } as obs\"\n>\n <v-input\n name=\"documentIdVariable\"\n [title]=\"'documentIdVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'documentIdVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.documentIdVariable || 'epistolaDocumentId'\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"contentVariable\"\n [title]=\"'contentVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'contentVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"obs.prefill?.contentVariable || 'documentContent'\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n</v-form>\n" }]
|
|
1113
1955
|
}], propDecorators: { save$: [{
|
|
1114
1956
|
type: Input
|
|
1115
1957
|
}], disabled$: [{
|
|
@@ -1149,9 +1991,9 @@ class EpistolaDownloadComponent {
|
|
|
1149
1991
|
this.downloading = true;
|
|
1150
1992
|
this.error = null;
|
|
1151
1993
|
const { documentId, tenantId } = this.value;
|
|
1152
|
-
const url = `/api/v1/plugin/epistola/documents/${encodeURIComponent(documentId)}/download`
|
|
1153
|
-
|
|
1154
|
-
|
|
1994
|
+
const url = `/api/v1/plugin/epistola/documents/${encodeURIComponent(documentId)}/download` +
|
|
1995
|
+
`?tenantId=${encodeURIComponent(tenantId)}` +
|
|
1996
|
+
`&filename=${encodeURIComponent(this.filename)}`;
|
|
1155
1997
|
this.http.get(url, { responseType: 'blob' }).subscribe({
|
|
1156
1998
|
next: (blob) => {
|
|
1157
1999
|
const objectUrl = URL.createObjectURL(blob);
|
|
@@ -1244,7 +2086,7 @@ class EpistolaRetryFormComponent {
|
|
|
1244
2086
|
apiEndpoint;
|
|
1245
2087
|
formOptions = {
|
|
1246
2088
|
noAlerts: true,
|
|
1247
|
-
buttonSettings: { showCancel: false, showSubmit: false, showPrevious: false, showNext: false }
|
|
2089
|
+
buttonSettings: { showCancel: false, showSubmit: false, showPrevious: false, showNext: false },
|
|
1248
2090
|
};
|
|
1249
2091
|
constructor(epistolaPluginService, formIoStateService, cdr, http, sanitizer, configService) {
|
|
1250
2092
|
this.epistolaPluginService = epistolaPluginService;
|
|
@@ -1255,7 +2097,7 @@ class EpistolaRetryFormComponent {
|
|
|
1255
2097
|
this.configService = configService;
|
|
1256
2098
|
this.apiEndpoint = `${this.configService.config.valtimoApi.endpointUri}v1/plugin/epistola`;
|
|
1257
2099
|
// Debounce preview calls
|
|
1258
|
-
this.previewSubscription = this.previewSubject.pipe(debounceTime(1500)).subscribe(data => {
|
|
2100
|
+
this.previewSubscription = this.previewSubject.pipe(debounceTime$1(1500)).subscribe((data) => {
|
|
1259
2101
|
this.loadPreview(data);
|
|
1260
2102
|
});
|
|
1261
2103
|
}
|
|
@@ -1298,12 +2140,14 @@ class EpistolaRetryFormComponent {
|
|
|
1298
2140
|
URL.revokeObjectURL(this.currentBlobUrl);
|
|
1299
2141
|
this.currentBlobUrl = null;
|
|
1300
2142
|
}
|
|
1301
|
-
this.http
|
|
2143
|
+
this.http
|
|
2144
|
+
.post(`${this.apiEndpoint}/preview`, {
|
|
1302
2145
|
documentId,
|
|
1303
2146
|
processInstanceId,
|
|
1304
2147
|
sourceActivityId: this.sourceActivityId || null,
|
|
1305
|
-
overrides: formData
|
|
1306
|
-
}, { responseType: 'blob', headers: new HttpHeaders().set('X-Skip-Interceptor', '422') })
|
|
2148
|
+
overrides: formData,
|
|
2149
|
+
}, { responseType: 'blob', headers: new HttpHeaders().set('X-Skip-Interceptor', '422') })
|
|
2150
|
+
.subscribe({
|
|
1307
2151
|
next: (blob) => {
|
|
1308
2152
|
this.currentBlobUrl = URL.createObjectURL(blob);
|
|
1309
2153
|
this.previewUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.currentBlobUrl);
|
|
@@ -1332,7 +2176,7 @@ class EpistolaRetryFormComponent {
|
|
|
1332
2176
|
this.previewLoading = false;
|
|
1333
2177
|
this.cdr.markForCheck();
|
|
1334
2178
|
}
|
|
1335
|
-
}
|
|
2179
|
+
},
|
|
1336
2180
|
});
|
|
1337
2181
|
}
|
|
1338
2182
|
loadForm() {
|
|
@@ -1344,7 +2188,9 @@ class EpistolaRetryFormComponent {
|
|
|
1344
2188
|
this.cdr.markForCheck();
|
|
1345
2189
|
return;
|
|
1346
2190
|
}
|
|
1347
|
-
this.loadSubscription = this.epistolaPluginService
|
|
2191
|
+
this.loadSubscription = this.epistolaPluginService
|
|
2192
|
+
.getRetryForm(processInstanceId, documentId ?? undefined, this.sourceActivityId)
|
|
2193
|
+
.subscribe({
|
|
1348
2194
|
next: (form) => {
|
|
1349
2195
|
this.formDefinition = form;
|
|
1350
2196
|
if (this.value) {
|
|
@@ -1363,14 +2209,18 @@ class EpistolaRetryFormComponent {
|
|
|
1363
2209
|
this.error = 'Failed to load the retry form. Please try again.';
|
|
1364
2210
|
this.loading = false;
|
|
1365
2211
|
this.cdr.markForCheck();
|
|
1366
|
-
}
|
|
2212
|
+
},
|
|
1367
2213
|
});
|
|
1368
2214
|
}
|
|
1369
2215
|
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$1.DomSanitizer }, { token: i2.ConfigService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1370
2216
|
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: `
|
|
1371
2217
|
<div *ngIf="loading" class="epistola-retry-loading">Loading form...</div>
|
|
1372
2218
|
<div *ngIf="error" class="epistola-retry-error">{{ error }}</div>
|
|
1373
|
-
<div
|
|
2219
|
+
<div
|
|
2220
|
+
*ngIf="formDefinition && !loading"
|
|
2221
|
+
class="epistola-retry-container"
|
|
2222
|
+
[class.preview-expanded]="previewExpanded"
|
|
2223
|
+
>
|
|
1374
2224
|
<div class="epistola-retry-form" [hidden]="previewExpanded">
|
|
1375
2225
|
<formio
|
|
1376
2226
|
[form]="formDefinition"
|
|
@@ -1387,7 +2237,12 @@ class EpistolaRetryFormComponent {
|
|
|
1387
2237
|
</button>
|
|
1388
2238
|
</div>
|
|
1389
2239
|
<div *ngIf="previewLoading" class="preview-loading">Generating preview...</div>
|
|
1390
|
-
<object
|
|
2240
|
+
<object
|
|
2241
|
+
*ngIf="previewUrl && !previewLoading"
|
|
2242
|
+
[data]="previewUrl"
|
|
2243
|
+
type="application/pdf"
|
|
2244
|
+
class="preview-pdf"
|
|
2245
|
+
>
|
|
1391
2246
|
PDF preview not supported in this browser.
|
|
1392
2247
|
</object>
|
|
1393
2248
|
<div *ngIf="previewError" class="preview-error">{{ previewError }}</div>
|
|
@@ -1403,7 +2258,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1403
2258
|
args: [{ standalone: true, imports: [CommonModule, FormioModule], selector: 'epistola-retry-form-component', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1404
2259
|
<div *ngIf="loading" class="epistola-retry-loading">Loading form...</div>
|
|
1405
2260
|
<div *ngIf="error" class="epistola-retry-error">{{ error }}</div>
|
|
1406
|
-
<div
|
|
2261
|
+
<div
|
|
2262
|
+
*ngIf="formDefinition && !loading"
|
|
2263
|
+
class="epistola-retry-container"
|
|
2264
|
+
[class.preview-expanded]="previewExpanded"
|
|
2265
|
+
>
|
|
1407
2266
|
<div class="epistola-retry-form" [hidden]="previewExpanded">
|
|
1408
2267
|
<formio
|
|
1409
2268
|
[form]="formDefinition"
|
|
@@ -1420,7 +2279,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1420
2279
|
</button>
|
|
1421
2280
|
</div>
|
|
1422
2281
|
<div *ngIf="previewLoading" class="preview-loading">Generating preview...</div>
|
|
1423
|
-
<object
|
|
2282
|
+
<object
|
|
2283
|
+
*ngIf="previewUrl && !previewLoading"
|
|
2284
|
+
[data]="previewUrl"
|
|
2285
|
+
type="application/pdf"
|
|
2286
|
+
class="preview-pdf"
|
|
2287
|
+
>
|
|
1424
2288
|
PDF preview not supported in this browser.
|
|
1425
2289
|
</object>
|
|
1426
2290
|
<div *ngIf="previewError" class="preview-error">{{ previewError }}</div>
|
|
@@ -1480,9 +2344,9 @@ class EpistolaPreviewButtonComponent {
|
|
|
1480
2344
|
this.previewError = null;
|
|
1481
2345
|
this.revokeBlobUrl();
|
|
1482
2346
|
const { documentId, tenantId } = this.value;
|
|
1483
|
-
const url = `${this.apiEndpoint}/documents/${encodeURIComponent(documentId)}/download`
|
|
1484
|
-
|
|
1485
|
-
|
|
2347
|
+
const url = `${this.apiEndpoint}/documents/${encodeURIComponent(documentId)}/download` +
|
|
2348
|
+
`?tenantId=${encodeURIComponent(tenantId)}` +
|
|
2349
|
+
`&filename=preview.pdf`;
|
|
1486
2350
|
this.http.get(url, { responseType: 'blob' }).subscribe({
|
|
1487
2351
|
next: (blob) => {
|
|
1488
2352
|
this.currentBlobUrl = URL.createObjectURL(blob);
|
|
@@ -1492,7 +2356,7 @@ class EpistolaPreviewButtonComponent {
|
|
|
1492
2356
|
error: () => {
|
|
1493
2357
|
this.previewError = 'Could not load the document.';
|
|
1494
2358
|
this.previewLoading = false;
|
|
1495
|
-
}
|
|
2359
|
+
},
|
|
1496
2360
|
});
|
|
1497
2361
|
}
|
|
1498
2362
|
closePreview() {
|
|
@@ -1523,7 +2387,9 @@ class EpistolaPreviewButtonComponent {
|
|
|
1523
2387
|
<div class="preview-modal-content" (click)="$event.stopPropagation()">
|
|
1524
2388
|
<div class="preview-modal-header">
|
|
1525
2389
|
<span>Document Preview</span>
|
|
1526
|
-
<button type="button" class="preview-modal-close" (click)="closePreview()"
|
|
2390
|
+
<button type="button" class="preview-modal-close" (click)="closePreview()">
|
|
2391
|
+
×
|
|
2392
|
+
</button>
|
|
1527
2393
|
</div>
|
|
1528
2394
|
<div class="preview-modal-body">
|
|
1529
2395
|
<div *ngIf="previewLoading" class="preview-loading">Generating preview...</div>
|
|
@@ -1558,7 +2424,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1558
2424
|
<div class="preview-modal-content" (click)="$event.stopPropagation()">
|
|
1559
2425
|
<div class="preview-modal-header">
|
|
1560
2426
|
<span>Document Preview</span>
|
|
1561
|
-
<button type="button" class="preview-modal-close" (click)="closePreview()"
|
|
2427
|
+
<button type="button" class="preview-modal-close" (click)="closePreview()">
|
|
2428
|
+
×
|
|
2429
|
+
</button>
|
|
1562
2430
|
</div>
|
|
1563
2431
|
<div class="preview-modal-body">
|
|
1564
2432
|
<div *ngIf="previewLoading" class="preview-loading">Generating preview...</div>
|
|
@@ -1658,7 +2526,7 @@ class EpistolaDocumentPreviewComponent {
|
|
|
1658
2526
|
this.error = err.error?.error || 'Failed to discover preview sources';
|
|
1659
2527
|
this.discovering = false;
|
|
1660
2528
|
this.cdr.markForCheck();
|
|
1661
|
-
}
|
|
2529
|
+
},
|
|
1662
2530
|
});
|
|
1663
2531
|
}
|
|
1664
2532
|
loadPreview() {
|
|
@@ -1673,15 +2541,17 @@ class EpistolaDocumentPreviewComponent {
|
|
|
1673
2541
|
this.cdr.markForCheck();
|
|
1674
2542
|
this.revokeBlobUrl();
|
|
1675
2543
|
this.previewSubscription?.unsubscribe();
|
|
1676
|
-
this.previewSubscription = this.http
|
|
2544
|
+
this.previewSubscription = this.http
|
|
2545
|
+
.post(`${this.apiEndpoint}/preview`, {
|
|
1677
2546
|
documentId,
|
|
1678
2547
|
processInstanceId: source.processInstanceId,
|
|
1679
2548
|
sourceActivityId: source.activityId,
|
|
1680
|
-
overrides: null
|
|
2549
|
+
overrides: null,
|
|
1681
2550
|
}, {
|
|
1682
2551
|
responseType: 'blob',
|
|
1683
|
-
headers: new HttpHeaders().set('X-Skip-Interceptor', '422')
|
|
1684
|
-
})
|
|
2552
|
+
headers: new HttpHeaders().set('X-Skip-Interceptor', '422'),
|
|
2553
|
+
})
|
|
2554
|
+
.subscribe({
|
|
1685
2555
|
next: (blob) => {
|
|
1686
2556
|
this.currentBlobUrl = URL.createObjectURL(blob);
|
|
1687
2557
|
this.previewUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.currentBlobUrl);
|
|
@@ -1709,7 +2579,7 @@ class EpistolaDocumentPreviewComponent {
|
|
|
1709
2579
|
this.loading = false;
|
|
1710
2580
|
this.cdr.markForCheck();
|
|
1711
2581
|
}
|
|
1712
|
-
}
|
|
2582
|
+
},
|
|
1713
2583
|
});
|
|
1714
2584
|
}
|
|
1715
2585
|
revokeBlobUrl() {
|
|
@@ -1735,19 +2605,20 @@ class EpistolaDocumentPreviewComponent {
|
|
|
1735
2605
|
{{ source.templateName }} ({{ source.activityId }})
|
|
1736
2606
|
</option>
|
|
1737
2607
|
</select>
|
|
1738
|
-
<button
|
|
2608
|
+
<button
|
|
2609
|
+
type="button"
|
|
2610
|
+
class="preview-refresh"
|
|
2611
|
+
[disabled]="loading || discovering"
|
|
2612
|
+
(click)="refresh()"
|
|
2613
|
+
>
|
|
1739
2614
|
<i class="mdi mdi-refresh mr-1"></i>
|
|
1740
2615
|
{{ loading ? 'Generating...' : 'Refresh' }}
|
|
1741
2616
|
</button>
|
|
1742
2617
|
</div>
|
|
1743
2618
|
</div>
|
|
1744
2619
|
<div class="preview-body">
|
|
1745
|
-
<div *ngIf="discovering" class="preview-loading">
|
|
1746
|
-
|
|
1747
|
-
</div>
|
|
1748
|
-
<div *ngIf="loading && !discovering" class="preview-loading">
|
|
1749
|
-
Generating preview...
|
|
1750
|
-
</div>
|
|
2620
|
+
<div *ngIf="discovering" class="preview-loading">Discovering documents...</div>
|
|
2621
|
+
<div *ngIf="loading && !discovering" class="preview-loading">Generating preview...</div>
|
|
1751
2622
|
<div *ngIf="error && !loading && !discovering" class="preview-unavailable">
|
|
1752
2623
|
<i class="mdi mdi-information-outline"></i>
|
|
1753
2624
|
Preview is niet beschikbaar — niet alle gegevens zijn al ingevuld.
|
|
@@ -1760,7 +2631,10 @@ class EpistolaDocumentPreviewComponent {
|
|
|
1760
2631
|
>
|
|
1761
2632
|
PDF preview is not supported in this browser.
|
|
1762
2633
|
</object>
|
|
1763
|
-
<div
|
|
2634
|
+
<div
|
|
2635
|
+
*ngIf="!previewUrl && !loading && !discovering && !error && sources.length === 0"
|
|
2636
|
+
class="preview-empty"
|
|
2637
|
+
>
|
|
1764
2638
|
No previewable documents found
|
|
1765
2639
|
</div>
|
|
1766
2640
|
</div>
|
|
@@ -1784,19 +2658,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1784
2658
|
{{ source.templateName }} ({{ source.activityId }})
|
|
1785
2659
|
</option>
|
|
1786
2660
|
</select>
|
|
1787
|
-
<button
|
|
2661
|
+
<button
|
|
2662
|
+
type="button"
|
|
2663
|
+
class="preview-refresh"
|
|
2664
|
+
[disabled]="loading || discovering"
|
|
2665
|
+
(click)="refresh()"
|
|
2666
|
+
>
|
|
1788
2667
|
<i class="mdi mdi-refresh mr-1"></i>
|
|
1789
2668
|
{{ loading ? 'Generating...' : 'Refresh' }}
|
|
1790
2669
|
</button>
|
|
1791
2670
|
</div>
|
|
1792
2671
|
</div>
|
|
1793
2672
|
<div class="preview-body">
|
|
1794
|
-
<div *ngIf="discovering" class="preview-loading">
|
|
1795
|
-
|
|
1796
|
-
</div>
|
|
1797
|
-
<div *ngIf="loading && !discovering" class="preview-loading">
|
|
1798
|
-
Generating preview...
|
|
1799
|
-
</div>
|
|
2673
|
+
<div *ngIf="discovering" class="preview-loading">Discovering documents...</div>
|
|
2674
|
+
<div *ngIf="loading && !discovering" class="preview-loading">Generating preview...</div>
|
|
1800
2675
|
<div *ngIf="error && !loading && !discovering" class="preview-unavailable">
|
|
1801
2676
|
<i class="mdi mdi-information-outline"></i>
|
|
1802
2677
|
Preview is niet beschikbaar — niet alle gegevens zijn al ingevuld.
|
|
@@ -1809,7 +2684,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1809
2684
|
>
|
|
1810
2685
|
PDF preview is not supported in this browser.
|
|
1811
2686
|
</object>
|
|
1812
|
-
<div
|
|
2687
|
+
<div
|
|
2688
|
+
*ngIf="!previewUrl && !loading && !discovering && !error && sources.length === 0"
|
|
2689
|
+
class="preview-empty"
|
|
2690
|
+
>
|
|
1813
2691
|
No previewable documents found
|
|
1814
2692
|
</div>
|
|
1815
2693
|
</div>
|
|
@@ -1825,6 +2703,186 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1825
2703
|
type: Input
|
|
1826
2704
|
}] } });
|
|
1827
2705
|
|
|
2706
|
+
class EpistolaAdminPageComponent {
|
|
2707
|
+
adminService;
|
|
2708
|
+
route;
|
|
2709
|
+
router;
|
|
2710
|
+
cards = [];
|
|
2711
|
+
selectedCard = null;
|
|
2712
|
+
activeTab = 'actions';
|
|
2713
|
+
loading = false;
|
|
2714
|
+
pluginVersion = null;
|
|
2715
|
+
connectionStatuses = [];
|
|
2716
|
+
usageEntries = [];
|
|
2717
|
+
pendingJobs = [];
|
|
2718
|
+
connectionLoaded = false;
|
|
2719
|
+
usageLoaded = false;
|
|
2720
|
+
pendingLoaded = false;
|
|
2721
|
+
deepLinkConfigId = null;
|
|
2722
|
+
constructor(adminService, route, router) {
|
|
2723
|
+
this.adminService = adminService;
|
|
2724
|
+
this.route = route;
|
|
2725
|
+
this.router = router;
|
|
2726
|
+
}
|
|
2727
|
+
ngOnInit() {
|
|
2728
|
+
this.deepLinkConfigId = this.route.snapshot.queryParamMap.get('configurationId');
|
|
2729
|
+
const tab = this.route.snapshot.queryParamMap.get('tab');
|
|
2730
|
+
if (tab === 'pending' || tab === 'actions') {
|
|
2731
|
+
this.activeTab = tab;
|
|
2732
|
+
}
|
|
2733
|
+
this.loadData();
|
|
2734
|
+
this.loadPluginVersion();
|
|
2735
|
+
}
|
|
2736
|
+
selectConfiguration(card) {
|
|
2737
|
+
this.selectedCard = card;
|
|
2738
|
+
this.activeTab = 'actions';
|
|
2739
|
+
this.updateUrl(card.configurationId, this.activeTab);
|
|
2740
|
+
}
|
|
2741
|
+
backToOverview() {
|
|
2742
|
+
this.selectedCard = null;
|
|
2743
|
+
this.activeTab = 'actions';
|
|
2744
|
+
this.updateUrl(null, null);
|
|
2745
|
+
}
|
|
2746
|
+
setActiveTab(tab) {
|
|
2747
|
+
this.activeTab = tab;
|
|
2748
|
+
this.updateUrl(this.selectedCard?.configurationId ?? null, tab);
|
|
2749
|
+
}
|
|
2750
|
+
refresh() {
|
|
2751
|
+
this.selectedCard = null;
|
|
2752
|
+
this.loadData();
|
|
2753
|
+
}
|
|
2754
|
+
exportProcessLink(entry) {
|
|
2755
|
+
this.adminService.exportProcessLink(entry.processLinkId).subscribe({
|
|
2756
|
+
next: (blob) => {
|
|
2757
|
+
const url = URL.createObjectURL(blob);
|
|
2758
|
+
const anchor = document.createElement('a');
|
|
2759
|
+
anchor.href = url;
|
|
2760
|
+
anchor.download = `${entry.activityId}.process-link.json`;
|
|
2761
|
+
anchor.click();
|
|
2762
|
+
URL.revokeObjectURL(url);
|
|
2763
|
+
},
|
|
2764
|
+
});
|
|
2765
|
+
}
|
|
2766
|
+
updateUrl(configurationId, tab) {
|
|
2767
|
+
this.router.navigate([], {
|
|
2768
|
+
relativeTo: this.route,
|
|
2769
|
+
queryParams: {
|
|
2770
|
+
configurationId: configurationId ?? null,
|
|
2771
|
+
tab: tab ?? null,
|
|
2772
|
+
},
|
|
2773
|
+
replaceUrl: true,
|
|
2774
|
+
});
|
|
2775
|
+
}
|
|
2776
|
+
loadData() {
|
|
2777
|
+
this.loading = true;
|
|
2778
|
+
this.connectionLoaded = false;
|
|
2779
|
+
this.usageLoaded = false;
|
|
2780
|
+
this.pendingLoaded = false;
|
|
2781
|
+
this.adminService.getConnectionStatus().subscribe({
|
|
2782
|
+
next: (statuses) => {
|
|
2783
|
+
this.connectionStatuses = statuses;
|
|
2784
|
+
this.connectionLoaded = true;
|
|
2785
|
+
this.tryBuildCards();
|
|
2786
|
+
},
|
|
2787
|
+
error: () => {
|
|
2788
|
+
this.connectionStatuses = [];
|
|
2789
|
+
this.connectionLoaded = true;
|
|
2790
|
+
this.tryBuildCards();
|
|
2791
|
+
},
|
|
2792
|
+
});
|
|
2793
|
+
this.adminService.getPluginUsage().subscribe({
|
|
2794
|
+
next: (entries) => {
|
|
2795
|
+
this.usageEntries = entries;
|
|
2796
|
+
this.usageLoaded = true;
|
|
2797
|
+
this.tryBuildCards();
|
|
2798
|
+
},
|
|
2799
|
+
error: () => {
|
|
2800
|
+
this.usageEntries = [];
|
|
2801
|
+
this.usageLoaded = true;
|
|
2802
|
+
this.tryBuildCards();
|
|
2803
|
+
},
|
|
2804
|
+
});
|
|
2805
|
+
this.adminService.getPendingJobs().subscribe({
|
|
2806
|
+
next: (jobs) => {
|
|
2807
|
+
this.pendingJobs = jobs;
|
|
2808
|
+
this.pendingLoaded = true;
|
|
2809
|
+
this.tryBuildCards();
|
|
2810
|
+
},
|
|
2811
|
+
error: () => {
|
|
2812
|
+
this.pendingJobs = [];
|
|
2813
|
+
this.pendingLoaded = true;
|
|
2814
|
+
this.tryBuildCards();
|
|
2815
|
+
},
|
|
2816
|
+
});
|
|
2817
|
+
}
|
|
2818
|
+
tryBuildCards() {
|
|
2819
|
+
if (!this.connectionLoaded || !this.usageLoaded || !this.pendingLoaded) {
|
|
2820
|
+
return;
|
|
2821
|
+
}
|
|
2822
|
+
this.cards = this.connectionStatuses.map((status) => {
|
|
2823
|
+
const entries = this.usageEntries.filter((e) => e.configurationId === status.configurationId);
|
|
2824
|
+
const jobs = this.pendingJobs.filter((j) => j.tenantId === status.tenantId);
|
|
2825
|
+
const problemCount = entries.reduce((sum, e) => sum + e.problems.length, 0);
|
|
2826
|
+
return {
|
|
2827
|
+
configurationId: status.configurationId,
|
|
2828
|
+
configurationTitle: status.configurationTitle,
|
|
2829
|
+
tenantId: status.tenantId,
|
|
2830
|
+
reachable: status.reachable,
|
|
2831
|
+
latencyMs: status.latencyMs,
|
|
2832
|
+
errorMessage: status.errorMessage,
|
|
2833
|
+
serverVersion: status.serverVersion,
|
|
2834
|
+
usageCount: entries.length,
|
|
2835
|
+
problemCount,
|
|
2836
|
+
usageEntries: entries,
|
|
2837
|
+
pendingJobs: jobs,
|
|
2838
|
+
};
|
|
2839
|
+
});
|
|
2840
|
+
// Restore deep link selection
|
|
2841
|
+
if (this.deepLinkConfigId) {
|
|
2842
|
+
const match = this.cards.find((c) => c.configurationId === this.deepLinkConfigId);
|
|
2843
|
+
if (match) {
|
|
2844
|
+
this.selectedCard = match;
|
|
2845
|
+
}
|
|
2846
|
+
this.deepLinkConfigId = null;
|
|
2847
|
+
}
|
|
2848
|
+
this.loading = false;
|
|
2849
|
+
}
|
|
2850
|
+
loadPluginVersion() {
|
|
2851
|
+
this.adminService.getVersions().subscribe({
|
|
2852
|
+
next: (info) => {
|
|
2853
|
+
this.pluginVersion = info.pluginVersion;
|
|
2854
|
+
},
|
|
2855
|
+
});
|
|
2856
|
+
}
|
|
2857
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminPageComponent, deps: [{ token: EpistolaAdminService }, { token: i2$3.ActivatedRoute }, { token: i2$3.Router }], target: i0.ɵɵFactoryTarget.Component });
|
|
2858
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaAdminPageComponent, isStandalone: true, selector: "epistola-admin-page", ngImport: i0, template: "<div class=\"epistola-admin\">\n <!-- Overview: card grid (no configuration selected) -->\n <ng-container *ngIf=\"!selectedCard\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <div class=\"d-flex align-items-center\">\n <h5 class=\"mb-0\">{{ 'epistolaAdminOverview' | pluginTranslate: 'epistola' | async }}</h5>\n <span *ngIf=\"pluginVersion\" class=\"version-badge ms-2\">v{{ pluginVersion }}</span>\n </div>\n <button class=\"btn btn-outline-primary btn-sm\" (click)=\"refresh()\" [disabled]=\"loading\">\n {{ 'epistolaAdminRefresh' | pluginTranslate: 'epistola' | async }}\n </button>\n </div>\n\n <div *ngIf=\"loading\" class=\"text-muted\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!loading && cards.length === 0\" class=\"text-muted\">\n {{ 'epistolaAdminNoConfigurations' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!loading && cards.length > 0\" class=\"card-grid\">\n <div\n *ngFor=\"let card of cards\"\n class=\"config-card\"\n [class.config-card--ok]=\"card.reachable && card.problemCount === 0\"\n [class.config-card--warning]=\"card.reachable && card.problemCount > 0\"\n [class.config-card--error]=\"!card.reachable\"\n (click)=\"selectConfiguration(card)\"\n >\n <div class=\"config-card__header\">\n <span\n class=\"status-dot\"\n [class.status-dot--ok]=\"card.reachable\"\n [class.status-dot--error]=\"!card.reachable\"\n >\n </span>\n <h5 class=\"config-card__title\">{{ card.configurationTitle }}</h5>\n </div>\n\n <div class=\"config-card__body\">\n <div class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminTenantId' | pluginTranslate: 'epistola' | async\n }}</span>\n <code class=\"config-card__value\">{{ card.tenantId }}</code>\n </div>\n <div class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminStatus' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" [type]=\"card.reachable ? 'green' : 'red'\">\n {{\n card.reachable\n ? ('epistolaAdminConnected' | pluginTranslate: 'epistola' | async)\n : ('epistolaAdminUnreachable' | pluginTranslate: 'epistola' | async)\n }}\n </cds-tag>\n </div>\n <div *ngIf=\"card.serverVersion\" class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminServerVersion' | pluginTranslate: 'epistola' | async\n }}</span>\n <span class=\"config-card__value\">{{ card.serverVersion }}</span>\n </div>\n <div class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminPluginActions' | pluginTranslate: 'epistola' | async\n }}</span>\n <span class=\"config-card__value\">{{ card.usageCount }}</span>\n </div>\n <div *ngIf=\"card.pendingJobs.length > 0\" class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminPendingJobs' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" type=\"blue\">{{ card.pendingJobs.length }}</cds-tag>\n </div>\n <div *ngIf=\"card.problemCount > 0\" class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminProblems' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" type=\"red\">{{ card.problemCount }}</cds-tag>\n </div>\n </div>\n\n <div class=\"config-card__footer\">\n <span class=\"config-card__latency\">{{ card.latencyMs }} ms</span>\n </div>\n </div>\n </div>\n </ng-container>\n\n <!-- Detail view: selected configuration -->\n <ng-container *ngIf=\"selectedCard\">\n <div class=\"detail-header mb-3\">\n <button class=\"btn btn-link btn-sm p-0\" (click)=\"backToOverview()\">\n ← {{ 'epistolaAdminBackToOverview' | pluginTranslate: 'epistola' | async }}\n </button>\n </div>\n\n <div class=\"detail-summary mb-4\">\n <h4>\n <span\n class=\"status-dot me-2\"\n [class.status-dot--ok]=\"selectedCard.reachable\"\n [class.status-dot--error]=\"!selectedCard.reachable\"\n >\n </span>\n {{ selectedCard.configurationTitle }}\n </h4>\n\n <table class=\"table table-sm detail-info-table\">\n <tbody>\n <tr>\n <th>{{ 'epistolaAdminTenantId' | pluginTranslate: 'epistola' | async }}</th>\n <td>\n <code>{{ selectedCard.tenantId }}</code>\n </td>\n </tr>\n <tr>\n <th>{{ 'epistolaAdminStatus' | pluginTranslate: 'epistola' | async }}</th>\n <td>\n <cds-tag size=\"sm\" [type]=\"selectedCard.reachable ? 'green' : 'red'\">\n {{\n selectedCard.reachable\n ? ('epistolaAdminConnected' | pluginTranslate: 'epistola' | async)\n : ('epistolaAdminUnreachable' | pluginTranslate: 'epistola' | async)\n }}\n </cds-tag>\n <span class=\"text-muted ms-2\">{{ selectedCard.latencyMs }} ms</span>\n </td>\n </tr>\n <tr *ngIf=\"selectedCard.serverVersion\">\n <th>{{ 'epistolaAdminServerVersion' | pluginTranslate: 'epistola' | async }}</th>\n <td>{{ selectedCard.serverVersion }}</td>\n </tr>\n <tr *ngIf=\"selectedCard.errorMessage\">\n <th>{{ 'epistolaAdminError' | pluginTranslate: 'epistola' | async }}</th>\n <td class=\"text-danger\">{{ selectedCard.errorMessage }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- Tabs -->\n <ng-template #actionsHeading>\n {{ 'epistolaAdminPluginActions' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" type=\"gray\" class=\"ms-1\">{{ selectedCard.usageEntries.length }}</cds-tag>\n </ng-template>\n\n <ng-template #pendingHeading>\n {{ 'epistolaAdminPendingJobs' | pluginTranslate: 'epistola' | async }}\n <cds-tag\n size=\"sm\"\n [type]=\"selectedCard.pendingJobs.length > 0 ? 'blue' : 'gray'\"\n class=\"ms-1\"\n >\n {{ selectedCard.pendingJobs.length }}\n </cds-tag>\n </ng-template>\n\n <cds-tabs [cacheActive]=\"true\" type=\"contained\">\n <cds-tab\n [heading]=\"actionsHeading\"\n [active]=\"activeTab === 'actions'\"\n (selected)=\"setActiveTab('actions')\"\n >\n <div *ngIf=\"selectedCard.usageEntries.length === 0\" class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminNoUsageForConfig' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table *ngIf=\"selectedCard.usageEntries.length > 0\" class=\"table table-striped mt-3\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminCase' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminProcess' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminActivity' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminAction' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminProblems' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr\n *ngFor=\"let entry of selectedCard.usageEntries\"\n [class.table-warning]=\"entry.problems.length > 0\"\n >\n <td>{{ entry.caseDefinitionKey || '-' }}</td>\n <td>\n <a\n *ngIf=\"entry.caseDefinitionKey && entry.caseDefinitionVersionTag\"\n [routerLink]=\"[\n '/case-management',\n 'case',\n entry.caseDefinitionKey,\n 'version',\n entry.caseDefinitionVersionTag,\n 'processes',\n entry.processDefinitionKey,\n ]\"\n class=\"usage-link\"\n >\n {{ entry.processDefinitionName }}\n </a>\n <span *ngIf=\"!entry.caseDefinitionKey || !entry.caseDefinitionVersionTag\">\n {{ entry.processDefinitionName }}\n </span>\n </td>\n <td>{{ entry.activityName }}</td>\n <td>\n <code>{{ entry.actionKey }}</code>\n </td>\n <td>\n <cds-tag *ngIf=\"entry.problems.length === 0\" size=\"sm\" type=\"green\">OK</cds-tag>\n <cds-tag\n *ngFor=\"let problem of entry.problems\"\n size=\"sm\"\n type=\"red\"\n class=\"d-block mb-1\"\n >\n {{ problem }}\n </cds-tag>\n </td>\n <td>\n <button\n class=\"btn btn-sm btn-outline-secondary\"\n (click)=\"exportProcessLink(entry)\"\n [title]=\"'epistolaAdminExport' | pluginTranslate: 'epistola' | async\"\n >\n ⤓\n </button>\n </td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n\n <cds-tab\n [heading]=\"pendingHeading\"\n [active]=\"activeTab === 'pending'\"\n (selected)=\"setActiveTab('pending')\"\n >\n <div *ngIf=\"selectedCard.pendingJobs.length === 0\" class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminNoPendingJobs' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table *ngIf=\"selectedCard.pendingJobs.length > 0\" class=\"table table-striped mt-3\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminProcess' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminActivity' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminRequestId' | pluginTranslate: 'epistola' | async }}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let job of selectedCard.pendingJobs\">\n <td>{{ job.processDefinitionName }}</td>\n <td>{{ job.activityName }}</td>\n <td>\n <code>{{ job.requestId }}</code>\n </td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n </cds-tabs>\n </ng-container>\n</div>\n", styles: [".epistola-admin{padding:1.5rem}.epistola-admin .version-badge{font-size:.75rem;font-weight:500;padding:.2em .6em;border-radius:4px;background-color:#e8e8e8;color:#525252}.epistola-admin .badge{font-size:.85em;padding:.35em .65em}.epistola-admin code{font-size:.9em;color:#525252}.epistola-admin table th{font-weight:600;white-space:nowrap}.epistola-admin .status-dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:.5rem;flex-shrink:0;background-color:#adb5bd}.epistola-admin .status-dot--ok{background-color:#198754}.epistola-admin .status-dot--error{background-color:#dc3545}.epistola-admin .card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:1rem}.epistola-admin .config-card{border:1px solid #dee2e6;border-radius:8px;padding:1.25rem;cursor:pointer;transition:box-shadow .15s ease,border-color .15s ease;background:#fff}.epistola-admin .config-card:hover{box-shadow:0 2px 8px #0000001a}.epistola-admin .config-card--ok{border-left:4px solid #198754}.epistola-admin .config-card--warning{border-left:4px solid #ffc107}.epistola-admin .config-card--error{border-left:4px solid #dc3545}.epistola-admin .config-card__header{display:flex;align-items:center;margin-bottom:1rem}.epistola-admin .config-card__title{margin:0;font-size:1.05rem;font-weight:600;color:#161616}.epistola-admin .config-card__body{display:flex;flex-direction:column;gap:.5rem}.epistola-admin .config-card__field{display:flex;justify-content:space-between;align-items:center}.epistola-admin .config-card__label{font-size:.875rem;color:#6c757d}.epistola-admin .config-card__value{font-size:.875rem;color:#161616}.epistola-admin .config-card__footer{margin-top:1rem;padding-top:.75rem;border-top:1px solid #f0f0f0;text-align:right}.epistola-admin .config-card__latency{font-size:.8rem;color:#adb5bd}.epistola-admin .usage-link{color:#0f62fe;text-decoration:none}.epistola-admin .usage-link:hover{text-decoration:underline}.epistola-admin .detail-info-table{max-width:500px}.epistola-admin .detail-info-table th{width:140px}.epistola-admin .detail-summary{padding:1rem 0;border-bottom:1px solid #dee2e6}\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: RouterModule }, { kind: "directive", type: i2$3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PluginTranslatePipeModule }, { kind: "pipe", type: i2$1.PluginTranslatePipe, name: "pluginTranslate" }, { kind: "ngmodule", type: TabsModule }, { kind: "component", type: i5.Tabs, selector: "cds-tabs, ibm-tabs", inputs: ["position", "cacheActive", "followFocus", "isNavigation", "ariaLabel", "ariaLabelledby", "type", "theme", "skeleton"] }, { kind: "component", type: i5.Tab, selector: "cds-tab, ibm-tab", inputs: ["heading", "title", "context", "active", "disabled", "tabIndex", "id", "cacheActive", "tabContent", "templateContext"], outputs: ["selected"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i6.Tag, selector: "cds-tag, ibm-tag", inputs: ["type", "size", "class", "skeleton"] }] });
|
|
2859
|
+
}
|
|
2860
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminPageComponent, decorators: [{
|
|
2861
|
+
type: Component,
|
|
2862
|
+
args: [{ selector: 'epistola-admin-page', standalone: true, imports: [CommonModule, RouterModule, PluginTranslatePipeModule, TabsModule, TagModule], template: "<div class=\"epistola-admin\">\n <!-- Overview: card grid (no configuration selected) -->\n <ng-container *ngIf=\"!selectedCard\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <div class=\"d-flex align-items-center\">\n <h5 class=\"mb-0\">{{ 'epistolaAdminOverview' | pluginTranslate: 'epistola' | async }}</h5>\n <span *ngIf=\"pluginVersion\" class=\"version-badge ms-2\">v{{ pluginVersion }}</span>\n </div>\n <button class=\"btn btn-outline-primary btn-sm\" (click)=\"refresh()\" [disabled]=\"loading\">\n {{ 'epistolaAdminRefresh' | pluginTranslate: 'epistola' | async }}\n </button>\n </div>\n\n <div *ngIf=\"loading\" class=\"text-muted\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!loading && cards.length === 0\" class=\"text-muted\">\n {{ 'epistolaAdminNoConfigurations' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!loading && cards.length > 0\" class=\"card-grid\">\n <div\n *ngFor=\"let card of cards\"\n class=\"config-card\"\n [class.config-card--ok]=\"card.reachable && card.problemCount === 0\"\n [class.config-card--warning]=\"card.reachable && card.problemCount > 0\"\n [class.config-card--error]=\"!card.reachable\"\n (click)=\"selectConfiguration(card)\"\n >\n <div class=\"config-card__header\">\n <span\n class=\"status-dot\"\n [class.status-dot--ok]=\"card.reachable\"\n [class.status-dot--error]=\"!card.reachable\"\n >\n </span>\n <h5 class=\"config-card__title\">{{ card.configurationTitle }}</h5>\n </div>\n\n <div class=\"config-card__body\">\n <div class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminTenantId' | pluginTranslate: 'epistola' | async\n }}</span>\n <code class=\"config-card__value\">{{ card.tenantId }}</code>\n </div>\n <div class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminStatus' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" [type]=\"card.reachable ? 'green' : 'red'\">\n {{\n card.reachable\n ? ('epistolaAdminConnected' | pluginTranslate: 'epistola' | async)\n : ('epistolaAdminUnreachable' | pluginTranslate: 'epistola' | async)\n }}\n </cds-tag>\n </div>\n <div *ngIf=\"card.serverVersion\" class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminServerVersion' | pluginTranslate: 'epistola' | async\n }}</span>\n <span class=\"config-card__value\">{{ card.serverVersion }}</span>\n </div>\n <div class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminPluginActions' | pluginTranslate: 'epistola' | async\n }}</span>\n <span class=\"config-card__value\">{{ card.usageCount }}</span>\n </div>\n <div *ngIf=\"card.pendingJobs.length > 0\" class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminPendingJobs' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" type=\"blue\">{{ card.pendingJobs.length }}</cds-tag>\n </div>\n <div *ngIf=\"card.problemCount > 0\" class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminProblems' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" type=\"red\">{{ card.problemCount }}</cds-tag>\n </div>\n </div>\n\n <div class=\"config-card__footer\">\n <span class=\"config-card__latency\">{{ card.latencyMs }} ms</span>\n </div>\n </div>\n </div>\n </ng-container>\n\n <!-- Detail view: selected configuration -->\n <ng-container *ngIf=\"selectedCard\">\n <div class=\"detail-header mb-3\">\n <button class=\"btn btn-link btn-sm p-0\" (click)=\"backToOverview()\">\n ← {{ 'epistolaAdminBackToOverview' | pluginTranslate: 'epistola' | async }}\n </button>\n </div>\n\n <div class=\"detail-summary mb-4\">\n <h4>\n <span\n class=\"status-dot me-2\"\n [class.status-dot--ok]=\"selectedCard.reachable\"\n [class.status-dot--error]=\"!selectedCard.reachable\"\n >\n </span>\n {{ selectedCard.configurationTitle }}\n </h4>\n\n <table class=\"table table-sm detail-info-table\">\n <tbody>\n <tr>\n <th>{{ 'epistolaAdminTenantId' | pluginTranslate: 'epistola' | async }}</th>\n <td>\n <code>{{ selectedCard.tenantId }}</code>\n </td>\n </tr>\n <tr>\n <th>{{ 'epistolaAdminStatus' | pluginTranslate: 'epistola' | async }}</th>\n <td>\n <cds-tag size=\"sm\" [type]=\"selectedCard.reachable ? 'green' : 'red'\">\n {{\n selectedCard.reachable\n ? ('epistolaAdminConnected' | pluginTranslate: 'epistola' | async)\n : ('epistolaAdminUnreachable' | pluginTranslate: 'epistola' | async)\n }}\n </cds-tag>\n <span class=\"text-muted ms-2\">{{ selectedCard.latencyMs }} ms</span>\n </td>\n </tr>\n <tr *ngIf=\"selectedCard.serverVersion\">\n <th>{{ 'epistolaAdminServerVersion' | pluginTranslate: 'epistola' | async }}</th>\n <td>{{ selectedCard.serverVersion }}</td>\n </tr>\n <tr *ngIf=\"selectedCard.errorMessage\">\n <th>{{ 'epistolaAdminError' | pluginTranslate: 'epistola' | async }}</th>\n <td class=\"text-danger\">{{ selectedCard.errorMessage }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- Tabs -->\n <ng-template #actionsHeading>\n {{ 'epistolaAdminPluginActions' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" type=\"gray\" class=\"ms-1\">{{ selectedCard.usageEntries.length }}</cds-tag>\n </ng-template>\n\n <ng-template #pendingHeading>\n {{ 'epistolaAdminPendingJobs' | pluginTranslate: 'epistola' | async }}\n <cds-tag\n size=\"sm\"\n [type]=\"selectedCard.pendingJobs.length > 0 ? 'blue' : 'gray'\"\n class=\"ms-1\"\n >\n {{ selectedCard.pendingJobs.length }}\n </cds-tag>\n </ng-template>\n\n <cds-tabs [cacheActive]=\"true\" type=\"contained\">\n <cds-tab\n [heading]=\"actionsHeading\"\n [active]=\"activeTab === 'actions'\"\n (selected)=\"setActiveTab('actions')\"\n >\n <div *ngIf=\"selectedCard.usageEntries.length === 0\" class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminNoUsageForConfig' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table *ngIf=\"selectedCard.usageEntries.length > 0\" class=\"table table-striped mt-3\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminCase' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminProcess' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminActivity' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminAction' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminProblems' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr\n *ngFor=\"let entry of selectedCard.usageEntries\"\n [class.table-warning]=\"entry.problems.length > 0\"\n >\n <td>{{ entry.caseDefinitionKey || '-' }}</td>\n <td>\n <a\n *ngIf=\"entry.caseDefinitionKey && entry.caseDefinitionVersionTag\"\n [routerLink]=\"[\n '/case-management',\n 'case',\n entry.caseDefinitionKey,\n 'version',\n entry.caseDefinitionVersionTag,\n 'processes',\n entry.processDefinitionKey,\n ]\"\n class=\"usage-link\"\n >\n {{ entry.processDefinitionName }}\n </a>\n <span *ngIf=\"!entry.caseDefinitionKey || !entry.caseDefinitionVersionTag\">\n {{ entry.processDefinitionName }}\n </span>\n </td>\n <td>{{ entry.activityName }}</td>\n <td>\n <code>{{ entry.actionKey }}</code>\n </td>\n <td>\n <cds-tag *ngIf=\"entry.problems.length === 0\" size=\"sm\" type=\"green\">OK</cds-tag>\n <cds-tag\n *ngFor=\"let problem of entry.problems\"\n size=\"sm\"\n type=\"red\"\n class=\"d-block mb-1\"\n >\n {{ problem }}\n </cds-tag>\n </td>\n <td>\n <button\n class=\"btn btn-sm btn-outline-secondary\"\n (click)=\"exportProcessLink(entry)\"\n [title]=\"'epistolaAdminExport' | pluginTranslate: 'epistola' | async\"\n >\n ⤓\n </button>\n </td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n\n <cds-tab\n [heading]=\"pendingHeading\"\n [active]=\"activeTab === 'pending'\"\n (selected)=\"setActiveTab('pending')\"\n >\n <div *ngIf=\"selectedCard.pendingJobs.length === 0\" class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminNoPendingJobs' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table *ngIf=\"selectedCard.pendingJobs.length > 0\" class=\"table table-striped mt-3\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminProcess' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminActivity' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminRequestId' | pluginTranslate: 'epistola' | async }}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let job of selectedCard.pendingJobs\">\n <td>{{ job.processDefinitionName }}</td>\n <td>{{ job.activityName }}</td>\n <td>\n <code>{{ job.requestId }}</code>\n </td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n </cds-tabs>\n </ng-container>\n</div>\n", styles: [".epistola-admin{padding:1.5rem}.epistola-admin .version-badge{font-size:.75rem;font-weight:500;padding:.2em .6em;border-radius:4px;background-color:#e8e8e8;color:#525252}.epistola-admin .badge{font-size:.85em;padding:.35em .65em}.epistola-admin code{font-size:.9em;color:#525252}.epistola-admin table th{font-weight:600;white-space:nowrap}.epistola-admin .status-dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:.5rem;flex-shrink:0;background-color:#adb5bd}.epistola-admin .status-dot--ok{background-color:#198754}.epistola-admin .status-dot--error{background-color:#dc3545}.epistola-admin .card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:1rem}.epistola-admin .config-card{border:1px solid #dee2e6;border-radius:8px;padding:1.25rem;cursor:pointer;transition:box-shadow .15s ease,border-color .15s ease;background:#fff}.epistola-admin .config-card:hover{box-shadow:0 2px 8px #0000001a}.epistola-admin .config-card--ok{border-left:4px solid #198754}.epistola-admin .config-card--warning{border-left:4px solid #ffc107}.epistola-admin .config-card--error{border-left:4px solid #dc3545}.epistola-admin .config-card__header{display:flex;align-items:center;margin-bottom:1rem}.epistola-admin .config-card__title{margin:0;font-size:1.05rem;font-weight:600;color:#161616}.epistola-admin .config-card__body{display:flex;flex-direction:column;gap:.5rem}.epistola-admin .config-card__field{display:flex;justify-content:space-between;align-items:center}.epistola-admin .config-card__label{font-size:.875rem;color:#6c757d}.epistola-admin .config-card__value{font-size:.875rem;color:#161616}.epistola-admin .config-card__footer{margin-top:1rem;padding-top:.75rem;border-top:1px solid #f0f0f0;text-align:right}.epistola-admin .config-card__latency{font-size:.8rem;color:#adb5bd}.epistola-admin .usage-link{color:#0f62fe;text-decoration:none}.epistola-admin .usage-link:hover{text-decoration:underline}.epistola-admin .detail-info-table{max-width:500px}.epistola-admin .detail-info-table th{width:140px}.epistola-admin .detail-summary{padding:1rem 0;border-bottom:1px solid #dee2e6}\n"] }]
|
|
2863
|
+
}], ctorParameters: () => [{ type: EpistolaAdminService }, { type: i2$3.ActivatedRoute }, { type: i2$3.Router }] });
|
|
2864
|
+
|
|
2865
|
+
const routes = [
|
|
2866
|
+
{
|
|
2867
|
+
path: 'epistola',
|
|
2868
|
+
component: EpistolaAdminPageComponent,
|
|
2869
|
+
canActivate: [AuthGuardService],
|
|
2870
|
+
data: { title: 'Epistola', roles: ['ROLE_ADMIN'] },
|
|
2871
|
+
},
|
|
2872
|
+
];
|
|
2873
|
+
class EpistolaAdminRoutingModule {
|
|
2874
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminRoutingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
2875
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminRoutingModule, imports: [i2$3.RouterModule], exports: [RouterModule] });
|
|
2876
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminRoutingModule, imports: [RouterModule.forChild(routes), RouterModule] });
|
|
2877
|
+
}
|
|
2878
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminRoutingModule, decorators: [{
|
|
2879
|
+
type: NgModule,
|
|
2880
|
+
args: [{
|
|
2881
|
+
imports: [RouterModule.forChild(routes)],
|
|
2882
|
+
exports: [RouterModule],
|
|
2883
|
+
}]
|
|
2884
|
+
}] });
|
|
2885
|
+
|
|
1828
2886
|
const EPISTOLA_DOWNLOAD_OPTIONS = {
|
|
1829
2887
|
type: 'epistola-download',
|
|
1830
2888
|
selector: 'epistola-download-button',
|
|
@@ -1890,6 +2948,7 @@ class EpistolaPluginModule {
|
|
|
1890
2948
|
return {
|
|
1891
2949
|
ngModule: EpistolaPluginModule,
|
|
1892
2950
|
providers: [
|
|
2951
|
+
EpistolaMenuService,
|
|
1893
2952
|
{
|
|
1894
2953
|
provide: ENVIRONMENT_INITIALIZER,
|
|
1895
2954
|
multi: true,
|
|
@@ -1899,9 +2958,11 @@ class EpistolaPluginModule {
|
|
|
1899
2958
|
registerEpistolaRetryFormComponent(injector);
|
|
1900
2959
|
registerEpistolaPreviewButtonComponent(injector);
|
|
1901
2960
|
registerEpistolaDocumentPreviewComponent(injector);
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
2961
|
+
// Eagerly create EpistolaMenuService to trigger menu registration
|
|
2962
|
+
inject(EpistolaMenuService);
|
|
2963
|
+
},
|
|
2964
|
+
},
|
|
2965
|
+
],
|
|
1905
2966
|
};
|
|
1906
2967
|
}
|
|
1907
2968
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPluginModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
@@ -1911,52 +2972,40 @@ class EpistolaPluginModule {
|
|
|
1911
2972
|
FormModule,
|
|
1912
2973
|
InputModule,
|
|
1913
2974
|
SelectModule,
|
|
2975
|
+
EpistolaAdminRoutingModule,
|
|
1914
2976
|
EpistolaConfigurationComponent,
|
|
1915
2977
|
GenerateDocumentConfigurationComponent,
|
|
1916
2978
|
CheckJobStatusConfigurationComponent,
|
|
1917
2979
|
DownloadDocumentConfigurationComponent,
|
|
1918
|
-
DataMappingTreeComponent,
|
|
1919
|
-
ValueInputComponent,
|
|
1920
|
-
ScalarFieldComponent,
|
|
1921
|
-
ArrayFieldComponent,
|
|
1922
|
-
FieldTreeComponent,
|
|
1923
2980
|
EpistolaDownloadComponent,
|
|
1924
2981
|
EpistolaRetryFormComponent,
|
|
1925
2982
|
EpistolaPreviewButtonComponent,
|
|
1926
|
-
EpistolaDocumentPreviewComponent
|
|
2983
|
+
EpistolaDocumentPreviewComponent,
|
|
2984
|
+
EpistolaAdminPageComponent], exports: [EpistolaConfigurationComponent,
|
|
1927
2985
|
GenerateDocumentConfigurationComponent,
|
|
1928
2986
|
CheckJobStatusConfigurationComponent,
|
|
1929
2987
|
DownloadDocumentConfigurationComponent,
|
|
1930
|
-
DataMappingTreeComponent,
|
|
1931
|
-
ValueInputComponent,
|
|
1932
|
-
ScalarFieldComponent,
|
|
1933
|
-
ArrayFieldComponent,
|
|
1934
|
-
FieldTreeComponent,
|
|
1935
2988
|
EpistolaDownloadComponent,
|
|
1936
2989
|
EpistolaRetryFormComponent,
|
|
1937
2990
|
EpistolaPreviewButtonComponent,
|
|
1938
|
-
EpistolaDocumentPreviewComponent
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
], imports: [CommonModule,
|
|
2991
|
+
EpistolaDocumentPreviewComponent,
|
|
2992
|
+
EpistolaAdminPageComponent] });
|
|
2993
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPluginModule, providers: [EpistolaPluginService, EpistolaAdminService], imports: [CommonModule,
|
|
1942
2994
|
HttpClientModule,
|
|
1943
2995
|
PluginTranslatePipeModule,
|
|
1944
2996
|
FormModule,
|
|
1945
2997
|
InputModule,
|
|
1946
2998
|
SelectModule,
|
|
2999
|
+
EpistolaAdminRoutingModule,
|
|
1947
3000
|
EpistolaConfigurationComponent,
|
|
1948
3001
|
GenerateDocumentConfigurationComponent,
|
|
1949
3002
|
CheckJobStatusConfigurationComponent,
|
|
1950
3003
|
DownloadDocumentConfigurationComponent,
|
|
1951
|
-
DataMappingTreeComponent,
|
|
1952
|
-
ValueInputComponent,
|
|
1953
|
-
ScalarFieldComponent,
|
|
1954
|
-
ArrayFieldComponent,
|
|
1955
|
-
FieldTreeComponent,
|
|
1956
3004
|
EpistolaDownloadComponent,
|
|
1957
3005
|
EpistolaRetryFormComponent,
|
|
1958
3006
|
EpistolaPreviewButtonComponent,
|
|
1959
|
-
EpistolaDocumentPreviewComponent
|
|
3007
|
+
EpistolaDocumentPreviewComponent,
|
|
3008
|
+
EpistolaAdminPageComponent] });
|
|
1960
3009
|
}
|
|
1961
3010
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPluginModule, decorators: [{
|
|
1962
3011
|
type: NgModule,
|
|
@@ -1968,38 +3017,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1968
3017
|
FormModule,
|
|
1969
3018
|
InputModule,
|
|
1970
3019
|
SelectModule,
|
|
3020
|
+
EpistolaAdminRoutingModule,
|
|
1971
3021
|
EpistolaConfigurationComponent,
|
|
1972
3022
|
GenerateDocumentConfigurationComponent,
|
|
1973
3023
|
CheckJobStatusConfigurationComponent,
|
|
1974
3024
|
DownloadDocumentConfigurationComponent,
|
|
1975
|
-
DataMappingTreeComponent,
|
|
1976
|
-
ValueInputComponent,
|
|
1977
|
-
ScalarFieldComponent,
|
|
1978
|
-
ArrayFieldComponent,
|
|
1979
|
-
FieldTreeComponent,
|
|
1980
3025
|
EpistolaDownloadComponent,
|
|
1981
3026
|
EpistolaRetryFormComponent,
|
|
1982
3027
|
EpistolaPreviewButtonComponent,
|
|
1983
|
-
EpistolaDocumentPreviewComponent
|
|
3028
|
+
EpistolaDocumentPreviewComponent,
|
|
3029
|
+
EpistolaAdminPageComponent,
|
|
1984
3030
|
],
|
|
1985
3031
|
exports: [
|
|
1986
3032
|
EpistolaConfigurationComponent,
|
|
1987
3033
|
GenerateDocumentConfigurationComponent,
|
|
1988
3034
|
CheckJobStatusConfigurationComponent,
|
|
1989
3035
|
DownloadDocumentConfigurationComponent,
|
|
1990
|
-
DataMappingTreeComponent,
|
|
1991
|
-
ValueInputComponent,
|
|
1992
|
-
ScalarFieldComponent,
|
|
1993
|
-
ArrayFieldComponent,
|
|
1994
|
-
FieldTreeComponent,
|
|
1995
3036
|
EpistolaDownloadComponent,
|
|
1996
3037
|
EpistolaRetryFormComponent,
|
|
1997
3038
|
EpistolaPreviewButtonComponent,
|
|
1998
|
-
EpistolaDocumentPreviewComponent
|
|
3039
|
+
EpistolaDocumentPreviewComponent,
|
|
3040
|
+
EpistolaAdminPageComponent,
|
|
1999
3041
|
],
|
|
2000
|
-
providers: [
|
|
2001
|
-
EpistolaPluginService
|
|
2002
|
-
]
|
|
3042
|
+
providers: [EpistolaPluginService, EpistolaAdminService],
|
|
2003
3043
|
}]
|
|
2004
3044
|
}] });
|
|
2005
3045
|
|
|
@@ -2073,6 +3113,18 @@ const epistolaPluginSpecification = {
|
|
|
2073
3113
|
// Data mapping builder translations
|
|
2074
3114
|
dataMappingTitle: 'Data Mapping',
|
|
2075
3115
|
dataMappingDescription: 'Koppel template velden aan Valtimo data bronnen',
|
|
3116
|
+
jsonataDescription: 'JSONata expressie die de template data genereert. Gebruik $doc, $pv en $case voor toegang tot document-, procesvariabelen- en zaakdata.',
|
|
3117
|
+
mappingModeSimple: 'Eenvoudig',
|
|
3118
|
+
mappingModeAdvanced: 'Geavanceerd',
|
|
3119
|
+
mappingTools: 'Schema & Voorbeeld',
|
|
3120
|
+
expectedStructure: 'Verwacht schema',
|
|
3121
|
+
expectedStructureLoading: 'Schema laden...',
|
|
3122
|
+
previewTitle: 'Voorbeeld',
|
|
3123
|
+
previewDocPlaceholder: 'Document ID',
|
|
3124
|
+
previewExpected: 'Verwacht',
|
|
3125
|
+
previewProduced: 'Geproduceerd',
|
|
3126
|
+
previewRunHint: 'Voer een document ID in en klik ▶ om een voorbeeld te zien',
|
|
3127
|
+
previewMissing: 'Ontbrekende verplichte velden',
|
|
2076
3128
|
templateField: 'Template veld',
|
|
2077
3129
|
dataSource: 'Data bron',
|
|
2078
3130
|
addMapping: 'Mapping toevoegen',
|
|
@@ -2094,6 +3146,7 @@ const epistolaPluginSpecification = {
|
|
|
2094
3146
|
pvMode: 'Procesvariabele modus',
|
|
2095
3147
|
pvPlaceholder: 'Naam procesvariabele',
|
|
2096
3148
|
expressionMode: 'Expressiemodus',
|
|
3149
|
+
availableFunctions: 'Beschikbare functies',
|
|
2097
3150
|
itemFieldMapping: 'Veldnamen per item koppelen',
|
|
2098
3151
|
itemFieldMappingTitle: 'Veldkoppeling per item:',
|
|
2099
3152
|
sourceFieldPlaceholder: 'Bronveldnaam',
|
|
@@ -2111,7 +3164,31 @@ const epistolaPluginSpecification = {
|
|
|
2111
3164
|
// Download document action
|
|
2112
3165
|
'download-document': 'Download Document',
|
|
2113
3166
|
contentVariable: 'Inhoud Variabele',
|
|
2114
|
-
contentVariableTooltip: 'Naam van de procesvariabele waarin de documentinhoud (Base64) wordt opgeslagen'
|
|
3167
|
+
contentVariableTooltip: 'Naam van de procesvariabele waarin de documentinhoud (Base64) wordt opgeslagen',
|
|
3168
|
+
// Admin page
|
|
3169
|
+
epistolaAdminOverview: 'Overzicht',
|
|
3170
|
+
epistolaAdminRefresh: 'Vernieuwen',
|
|
3171
|
+
epistolaAdminLoading: 'Laden...',
|
|
3172
|
+
epistolaAdminNoConfigurations: 'Geen Epistola plugin configuraties gevonden.',
|
|
3173
|
+
epistolaAdminTenantId: 'Tenant ID',
|
|
3174
|
+
epistolaAdminStatus: 'Status',
|
|
3175
|
+
epistolaAdminConnected: 'Verbonden',
|
|
3176
|
+
epistolaAdminUnreachable: 'Onbereikbaar',
|
|
3177
|
+
epistolaAdminError: 'Fout',
|
|
3178
|
+
epistolaAdminPluginActions: 'Plugin acties',
|
|
3179
|
+
epistolaAdminProblems: 'Problemen',
|
|
3180
|
+
epistolaAdminBackToOverview: 'Terug naar overzicht',
|
|
3181
|
+
epistolaAdminNoUsageForConfig: 'Geen procesacties geconfigureerd voor deze verbinding.',
|
|
3182
|
+
epistolaAdminCase: 'Zaak',
|
|
3183
|
+
epistolaAdminProcess: 'Proces',
|
|
3184
|
+
epistolaAdminActivity: 'Activiteit',
|
|
3185
|
+
epistolaAdminAction: 'Actie',
|
|
3186
|
+
epistolaAdminServerVersion: 'Server versie',
|
|
3187
|
+
epistolaAdminExport: 'Exporteren',
|
|
3188
|
+
epistolaAdminPendingJobs: 'Wachtende taken',
|
|
3189
|
+
epistolaAdminNoPendingJobs: 'Geen wachtende taken voor deze verbinding.',
|
|
3190
|
+
epistolaAdminConfiguration: 'Configuratie',
|
|
3191
|
+
epistolaAdminRequestId: 'Request ID',
|
|
2115
3192
|
},
|
|
2116
3193
|
en: {
|
|
2117
3194
|
title: 'Epistola Document Suite',
|
|
@@ -2164,6 +3241,18 @@ const epistolaPluginSpecification = {
|
|
|
2164
3241
|
// Data mapping builder translations
|
|
2165
3242
|
dataMappingTitle: 'Data Mapping',
|
|
2166
3243
|
dataMappingDescription: 'Map template fields to Valtimo data sources',
|
|
3244
|
+
mappingModeSimple: 'Simple',
|
|
3245
|
+
mappingModeAdvanced: 'Advanced',
|
|
3246
|
+
mappingTools: 'Schema & Preview',
|
|
3247
|
+
expectedStructure: 'Expected schema',
|
|
3248
|
+
expectedStructureLoading: 'Loading schema...',
|
|
3249
|
+
previewTitle: 'Preview',
|
|
3250
|
+
previewDocPlaceholder: 'Document ID',
|
|
3251
|
+
previewExpected: 'Expected',
|
|
3252
|
+
previewProduced: 'Produced',
|
|
3253
|
+
previewRunHint: 'Enter a document ID and click ▶ to preview the output',
|
|
3254
|
+
previewMissing: 'Missing required fields',
|
|
3255
|
+
jsonataDescription: 'JSONata expression that generates the template data. Use $doc, $pv and $case to access document, process variable and case data.',
|
|
2167
3256
|
templateField: 'Template field',
|
|
2168
3257
|
dataSource: 'Data source',
|
|
2169
3258
|
addMapping: 'Add mapping',
|
|
@@ -2185,6 +3274,7 @@ const epistolaPluginSpecification = {
|
|
|
2185
3274
|
pvMode: 'Process variable mode',
|
|
2186
3275
|
pvPlaceholder: 'Process variable name',
|
|
2187
3276
|
expressionMode: 'Expression mode',
|
|
3277
|
+
availableFunctions: 'Available functions',
|
|
2188
3278
|
itemFieldMapping: 'Map field names per item',
|
|
2189
3279
|
itemFieldMappingTitle: 'Item field mapping:',
|
|
2190
3280
|
sourceFieldPlaceholder: 'Source field name',
|
|
@@ -2202,9 +3292,33 @@ const epistolaPluginSpecification = {
|
|
|
2202
3292
|
// Download document action
|
|
2203
3293
|
'download-document': 'Download Document',
|
|
2204
3294
|
contentVariable: 'Content Variable',
|
|
2205
|
-
contentVariableTooltip: 'Name of the process variable to store the document content (Base64) in'
|
|
2206
|
-
|
|
2207
|
-
|
|
3295
|
+
contentVariableTooltip: 'Name of the process variable to store the document content (Base64) in',
|
|
3296
|
+
// Admin page
|
|
3297
|
+
epistolaAdminOverview: 'Overview',
|
|
3298
|
+
epistolaAdminRefresh: 'Refresh',
|
|
3299
|
+
epistolaAdminLoading: 'Loading...',
|
|
3300
|
+
epistolaAdminNoConfigurations: 'No Epistola plugin configurations found.',
|
|
3301
|
+
epistolaAdminTenantId: 'Tenant ID',
|
|
3302
|
+
epistolaAdminStatus: 'Status',
|
|
3303
|
+
epistolaAdminConnected: 'Connected',
|
|
3304
|
+
epistolaAdminUnreachable: 'Unreachable',
|
|
3305
|
+
epistolaAdminError: 'Error',
|
|
3306
|
+
epistolaAdminPluginActions: 'Plugin actions',
|
|
3307
|
+
epistolaAdminProblems: 'Problems',
|
|
3308
|
+
epistolaAdminBackToOverview: 'Back to overview',
|
|
3309
|
+
epistolaAdminNoUsageForConfig: 'No process actions configured for this connection.',
|
|
3310
|
+
epistolaAdminCase: 'Case',
|
|
3311
|
+
epistolaAdminProcess: 'Process',
|
|
3312
|
+
epistolaAdminActivity: 'Activity',
|
|
3313
|
+
epistolaAdminAction: 'Action',
|
|
3314
|
+
epistolaAdminServerVersion: 'Server version',
|
|
3315
|
+
epistolaAdminExport: 'Export',
|
|
3316
|
+
epistolaAdminPendingJobs: 'Pending jobs',
|
|
3317
|
+
epistolaAdminNoPendingJobs: 'No pending jobs for this connection.',
|
|
3318
|
+
epistolaAdminConfiguration: 'Configuration',
|
|
3319
|
+
epistolaAdminRequestId: 'Request ID',
|
|
3320
|
+
},
|
|
3321
|
+
},
|
|
2208
3322
|
};
|
|
2209
3323
|
|
|
2210
3324
|
/*
|
|
@@ -2215,5 +3329,5 @@ const epistolaPluginSpecification = {
|
|
|
2215
3329
|
* Generated bundle index. Do not edit.
|
|
2216
3330
|
*/
|
|
2217
3331
|
|
|
2218
|
-
export {
|
|
3332
|
+
export { CheckJobStatusConfigurationComponent, DownloadDocumentConfigurationComponent, EPISTOLA_DOCUMENT_PREVIEW_OPTIONS, EPISTOLA_DOWNLOAD_OPTIONS, EPISTOLA_PREVIEW_BUTTON_OPTIONS, EPISTOLA_RETRY_FORM_OPTIONS, EpistolaAdminPageComponent, EpistolaAdminRoutingModule, EpistolaAdminService, EpistolaConfigurationComponent, EpistolaDocumentPreviewComponent, EpistolaDownloadComponent, EpistolaMenuService, EpistolaPluginModule, EpistolaPluginService, EpistolaPreviewButtonComponent, EpistolaRetryFormComponent, GenerateDocumentConfigurationComponent, JsonataEditorComponent, MappingBuilderComponent, epistolaPluginSpecification, errorResource, initialResource, loadingResource, registerEpistolaDocumentPreviewComponent, registerEpistolaDownloadComponent, registerEpistolaPreviewButtonComponent, registerEpistolaRetryFormComponent, successResource };
|
|
2219
3333
|
//# sourceMappingURL=epistola.app-valtimo-plugin.mjs.map
|