@epistola.app/valtimo-plugin 0.11.0 → 0.12.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.
|
@@ -721,11 +721,11 @@ class EpistolaConfigurationComponent {
|
|
|
721
721
|
});
|
|
722
722
|
}
|
|
723
723
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaConfigurationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
724
|
-
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"] }] });
|
|
724
|
+
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 data-testid=\"epistola-config-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 data-testid=\"epistola-config-title\"\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 data-testid=\"epistola-config-base-url\"\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 data-testid=\"epistola-config-api-key\"\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 data-testid=\"epistola-config-tenant-id\"\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 data-testid=\"epistola-config-default-environment-id\"\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 data-testid=\"epistola-config-template-sync-enabled\"\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"] }] });
|
|
725
725
|
}
|
|
726
726
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaConfigurationComponent, decorators: [{
|
|
727
727
|
type: Component,
|
|
728
|
-
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" }]
|
|
728
|
+
args: [{ selector: 'epistola-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form\n data-testid=\"epistola-config-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 data-testid=\"epistola-config-title\"\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 data-testid=\"epistola-config-base-url\"\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 data-testid=\"epistola-config-api-key\"\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 data-testid=\"epistola-config-tenant-id\"\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 data-testid=\"epistola-config-default-environment-id\"\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 data-testid=\"epistola-config-template-sync-enabled\"\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" }]
|
|
729
729
|
}], propDecorators: { save$: [{
|
|
730
730
|
type: Input
|
|
731
731
|
}], disabled$: [{
|
|
@@ -1039,8 +1039,9 @@ class JsonataEditorComponent {
|
|
|
1039
1039
|
}
|
|
1040
1040
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: JsonataEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1041
1041
|
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", contextVariables: "contextVariables", functions: "functions", variablesHint: "variablesHint" }, outputs: { expressionChange: "expressionChange", validChange: "validChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
1042
|
-
<div class="jsonata-editor">
|
|
1042
|
+
<div class="jsonata-editor" data-testid="epistola-jsonata-editor">
|
|
1043
1043
|
<valtimo-editor
|
|
1044
|
+
data-testid="epistola-jsonata-editor-input"
|
|
1044
1045
|
[model]="editorModel"
|
|
1045
1046
|
[editorOptions]="editorOptions"
|
|
1046
1047
|
[disabled]="disabled"
|
|
@@ -1048,10 +1049,19 @@ class JsonataEditorComponent {
|
|
|
1048
1049
|
[formatOnLoad]="false"
|
|
1049
1050
|
(valueChangeEvent)="onEditorValueChange($event)"
|
|
1050
1051
|
></valtimo-editor>
|
|
1051
|
-
<div class="jsonata-editor__footer">
|
|
1052
|
-
<span *ngIf="error" class="jsonata-editor__error">{{
|
|
1053
|
-
|
|
1054
|
-
|
|
1052
|
+
<div class="jsonata-editor__footer" data-testid="epistola-jsonata-footer">
|
|
1053
|
+
<span *ngIf="error" class="jsonata-editor__error" data-testid="epistola-jsonata-error">{{
|
|
1054
|
+
error
|
|
1055
|
+
}}</span>
|
|
1056
|
+
<span
|
|
1057
|
+
*ngIf="!error && expression"
|
|
1058
|
+
class="jsonata-editor__valid"
|
|
1059
|
+
data-testid="epistola-jsonata-valid"
|
|
1060
|
+
>✓</span
|
|
1061
|
+
>
|
|
1062
|
+
<span class="jsonata-editor__variables" data-testid="epistola-jsonata-variables-hint">{{
|
|
1063
|
+
variablesHint
|
|
1064
|
+
}}</span>
|
|
1055
1065
|
</div>
|
|
1056
1066
|
</div>
|
|
1057
1067
|
`, 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"] }] });
|
|
@@ -1059,8 +1069,9 @@ class JsonataEditorComponent {
|
|
|
1059
1069
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: JsonataEditorComponent, decorators: [{
|
|
1060
1070
|
type: Component,
|
|
1061
1071
|
args: [{ selector: 'epistola-jsonata-editor', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, EditorModule], template: `
|
|
1062
|
-
<div class="jsonata-editor">
|
|
1072
|
+
<div class="jsonata-editor" data-testid="epistola-jsonata-editor">
|
|
1063
1073
|
<valtimo-editor
|
|
1074
|
+
data-testid="epistola-jsonata-editor-input"
|
|
1064
1075
|
[model]="editorModel"
|
|
1065
1076
|
[editorOptions]="editorOptions"
|
|
1066
1077
|
[disabled]="disabled"
|
|
@@ -1068,10 +1079,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1068
1079
|
[formatOnLoad]="false"
|
|
1069
1080
|
(valueChangeEvent)="onEditorValueChange($event)"
|
|
1070
1081
|
></valtimo-editor>
|
|
1071
|
-
<div class="jsonata-editor__footer">
|
|
1072
|
-
<span *ngIf="error" class="jsonata-editor__error">{{
|
|
1073
|
-
|
|
1074
|
-
|
|
1082
|
+
<div class="jsonata-editor__footer" data-testid="epistola-jsonata-footer">
|
|
1083
|
+
<span *ngIf="error" class="jsonata-editor__error" data-testid="epistola-jsonata-error">{{
|
|
1084
|
+
error
|
|
1085
|
+
}}</span>
|
|
1086
|
+
<span
|
|
1087
|
+
*ngIf="!error && expression"
|
|
1088
|
+
class="jsonata-editor__valid"
|
|
1089
|
+
data-testid="epistola-jsonata-valid"
|
|
1090
|
+
>✓</span
|
|
1091
|
+
>
|
|
1092
|
+
<span class="jsonata-editor__variables" data-testid="epistola-jsonata-variables-hint">{{
|
|
1093
|
+
variablesHint
|
|
1094
|
+
}}</span>
|
|
1075
1095
|
</div>
|
|
1076
1096
|
</div>
|
|
1077
1097
|
`, 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"] }]
|
|
@@ -1140,32 +1160,46 @@ class ExpectedStructureComponent {
|
|
|
1140
1160
|
}
|
|
1141
1161
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ExpectedStructureComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1142
1162
|
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: `
|
|
1143
|
-
<div class="expected">
|
|
1144
|
-
<div class="expected__header">
|
|
1163
|
+
<div class="expected" data-testid="epistola-schema-panel">
|
|
1164
|
+
<div class="expected__header" data-testid="epistola-schema-header">
|
|
1145
1165
|
{{ 'expectedStructure' | pluginTranslate: 'epistola' | async }}
|
|
1146
1166
|
</div>
|
|
1147
|
-
<div
|
|
1167
|
+
<div
|
|
1168
|
+
*ngIf="!templateFields || templateFields.length === 0"
|
|
1169
|
+
class="expected__empty"
|
|
1170
|
+
data-testid="epistola-schema-empty"
|
|
1171
|
+
>
|
|
1148
1172
|
{{ 'expectedStructureLoading' | pluginTranslate: 'epistola' | async }}
|
|
1149
1173
|
</div>
|
|
1150
|
-
<pre
|
|
1151
|
-
|
|
1152
|
-
|
|
1174
|
+
<pre
|
|
1175
|
+
*ngIf="templateFields && templateFields.length > 0"
|
|
1176
|
+
class="expected__code"
|
|
1177
|
+
data-testid="epistola-schema-code"
|
|
1178
|
+
>{{ structureText }}</pre
|
|
1179
|
+
>
|
|
1153
1180
|
</div>
|
|
1154
1181
|
`, 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" }] });
|
|
1155
1182
|
}
|
|
1156
1183
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ExpectedStructureComponent, decorators: [{
|
|
1157
1184
|
type: Component,
|
|
1158
1185
|
args: [{ selector: 'epistola-expected-structure', standalone: true, imports: [CommonModule, PluginTranslatePipeModule], template: `
|
|
1159
|
-
<div class="expected">
|
|
1160
|
-
<div class="expected__header">
|
|
1186
|
+
<div class="expected" data-testid="epistola-schema-panel">
|
|
1187
|
+
<div class="expected__header" data-testid="epistola-schema-header">
|
|
1161
1188
|
{{ 'expectedStructure' | pluginTranslate: 'epistola' | async }}
|
|
1162
1189
|
</div>
|
|
1163
|
-
<div
|
|
1190
|
+
<div
|
|
1191
|
+
*ngIf="!templateFields || templateFields.length === 0"
|
|
1192
|
+
class="expected__empty"
|
|
1193
|
+
data-testid="epistola-schema-empty"
|
|
1194
|
+
>
|
|
1164
1195
|
{{ 'expectedStructureLoading' | pluginTranslate: 'epistola' | async }}
|
|
1165
1196
|
</div>
|
|
1166
|
-
<pre
|
|
1167
|
-
|
|
1168
|
-
|
|
1197
|
+
<pre
|
|
1198
|
+
*ngIf="templateFields && templateFields.length > 0"
|
|
1199
|
+
class="expected__code"
|
|
1200
|
+
data-testid="epistola-schema-code"
|
|
1201
|
+
>{{ structureText }}</pre
|
|
1202
|
+
>
|
|
1169
1203
|
</div>
|
|
1170
1204
|
`, 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"] }]
|
|
1171
1205
|
}], propDecorators: { templateFields: [{
|
|
@@ -1205,21 +1239,41 @@ class BuilderFieldComponent {
|
|
|
1205
1239
|
}
|
|
1206
1240
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: BuilderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1207
1241
|
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: `
|
|
1208
|
-
<div class="builder-field">
|
|
1242
|
+
<div class="builder-field" [attr.data-testid]="'epistola-mapping-field-' + field.name">
|
|
1209
1243
|
<div
|
|
1210
1244
|
class="builder-field__name"
|
|
1211
1245
|
[class.builder-field__name--clickable]="field.children"
|
|
1212
1246
|
(click)="field.children && collapseToggle.emit(path)"
|
|
1247
|
+
[attr.data-testid]="'epistola-mapping-field-name-' + field.name"
|
|
1213
1248
|
>
|
|
1214
|
-
<span
|
|
1215
|
-
|
|
1249
|
+
<span
|
|
1250
|
+
*ngIf="field.children"
|
|
1251
|
+
class="builder-field__chevron"
|
|
1252
|
+
[attr.data-testid]="'epistola-mapping-field-chevron-' + field.name"
|
|
1253
|
+
>{{ collapsed ? '▶' : '▼' }}</span
|
|
1254
|
+
>
|
|
1255
|
+
<span class="builder-field__label" [attr.data-testid]="'epistola-mapping-field-label-' + field.name">{{
|
|
1256
|
+
field.name
|
|
1216
1257
|
}}</span>
|
|
1217
|
-
<span
|
|
1218
|
-
|
|
1219
|
-
|
|
1258
|
+
<span
|
|
1259
|
+
*ngIf="required"
|
|
1260
|
+
class="builder-field__required"
|
|
1261
|
+
[attr.data-testid]="'epistola-mapping-field-required-' + field.name"
|
|
1262
|
+
>*</span
|
|
1263
|
+
>
|
|
1264
|
+
<span
|
|
1265
|
+
*ngIf="field.children"
|
|
1266
|
+
class="builder-field__type"
|
|
1267
|
+
[attr.data-testid]="'epistola-mapping-field-type-' + field.name"
|
|
1268
|
+
>(object)</span
|
|
1269
|
+
>
|
|
1220
1270
|
</div>
|
|
1221
1271
|
|
|
1222
|
-
<div
|
|
1272
|
+
<div
|
|
1273
|
+
class="builder-field__value"
|
|
1274
|
+
*ngIf="!field.children"
|
|
1275
|
+
[attr.data-testid]="'epistola-mapping-field-value-' + field.name"
|
|
1276
|
+
>
|
|
1223
1277
|
<input
|
|
1224
1278
|
*ngIf="field.mode === 'ref'"
|
|
1225
1279
|
type="text"
|
|
@@ -1229,8 +1283,13 @@ class BuilderFieldComponent {
|
|
|
1229
1283
|
[disabled]="disabled"
|
|
1230
1284
|
placeholder="$doc.path.to.field"
|
|
1231
1285
|
[attr.list]="'suggestions-' + path.join('-')"
|
|
1286
|
+
[attr.data-testid]="'epistola-mapping-field-input-ref-' + field.name"
|
|
1232
1287
|
/>
|
|
1233
|
-
<datalist
|
|
1288
|
+
<datalist
|
|
1289
|
+
*ngIf="field.mode === 'ref'"
|
|
1290
|
+
[id]="'suggestions-' + path.join('-')"
|
|
1291
|
+
[attr.data-testid]="'epistola-mapping-field-suggestions-' + field.name"
|
|
1292
|
+
>
|
|
1234
1293
|
<option *ngFor="let s of suggestions" [value]="s"></option>
|
|
1235
1294
|
</datalist>
|
|
1236
1295
|
<input
|
|
@@ -1241,18 +1300,24 @@ class BuilderFieldComponent {
|
|
|
1241
1300
|
(ngModelChange)="valueChange.emit({ path: path, value: $event })"
|
|
1242
1301
|
[disabled]="disabled"
|
|
1243
1302
|
placeholder="JSONata expression"
|
|
1303
|
+
[attr.data-testid]="'epistola-mapping-field-input-raw-' + field.name"
|
|
1244
1304
|
/>
|
|
1245
1305
|
<button
|
|
1246
1306
|
class="builder-field__mode-toggle"
|
|
1247
1307
|
(click)="modeToggle.emit(path)"
|
|
1248
1308
|
[disabled]="disabled"
|
|
1249
1309
|
[title]="field.mode === 'ref' ? 'Switch to raw JSONata' : 'Switch to reference'"
|
|
1310
|
+
[attr.data-testid]="'epistola-mapping-field-mode-toggle-' + field.name"
|
|
1250
1311
|
>
|
|
1251
1312
|
{{ field.mode === 'ref' ? 'fx' : '·' }}
|
|
1252
1313
|
</button>
|
|
1253
1314
|
</div>
|
|
1254
1315
|
|
|
1255
|
-
<div
|
|
1316
|
+
<div
|
|
1317
|
+
*ngIf="field.children && !collapsed"
|
|
1318
|
+
class="builder-field__children"
|
|
1319
|
+
[attr.data-testid]="'epistola-mapping-field-children-' + field.name"
|
|
1320
|
+
>
|
|
1256
1321
|
<epistola-builder-field
|
|
1257
1322
|
*ngFor="let child of field.children; let j = index"
|
|
1258
1323
|
[field]="child"
|
|
@@ -1273,21 +1338,41 @@ class BuilderFieldComponent {
|
|
|
1273
1338
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: BuilderFieldComponent, decorators: [{
|
|
1274
1339
|
type: Component,
|
|
1275
1340
|
args: [{ selector: 'epistola-builder-field', standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
1276
|
-
<div class="builder-field">
|
|
1341
|
+
<div class="builder-field" [attr.data-testid]="'epistola-mapping-field-' + field.name">
|
|
1277
1342
|
<div
|
|
1278
1343
|
class="builder-field__name"
|
|
1279
1344
|
[class.builder-field__name--clickable]="field.children"
|
|
1280
1345
|
(click)="field.children && collapseToggle.emit(path)"
|
|
1346
|
+
[attr.data-testid]="'epistola-mapping-field-name-' + field.name"
|
|
1281
1347
|
>
|
|
1282
|
-
<span
|
|
1283
|
-
|
|
1348
|
+
<span
|
|
1349
|
+
*ngIf="field.children"
|
|
1350
|
+
class="builder-field__chevron"
|
|
1351
|
+
[attr.data-testid]="'epistola-mapping-field-chevron-' + field.name"
|
|
1352
|
+
>{{ collapsed ? '▶' : '▼' }}</span
|
|
1353
|
+
>
|
|
1354
|
+
<span class="builder-field__label" [attr.data-testid]="'epistola-mapping-field-label-' + field.name">{{
|
|
1355
|
+
field.name
|
|
1284
1356
|
}}</span>
|
|
1285
|
-
<span
|
|
1286
|
-
|
|
1287
|
-
|
|
1357
|
+
<span
|
|
1358
|
+
*ngIf="required"
|
|
1359
|
+
class="builder-field__required"
|
|
1360
|
+
[attr.data-testid]="'epistola-mapping-field-required-' + field.name"
|
|
1361
|
+
>*</span
|
|
1362
|
+
>
|
|
1363
|
+
<span
|
|
1364
|
+
*ngIf="field.children"
|
|
1365
|
+
class="builder-field__type"
|
|
1366
|
+
[attr.data-testid]="'epistola-mapping-field-type-' + field.name"
|
|
1367
|
+
>(object)</span
|
|
1368
|
+
>
|
|
1288
1369
|
</div>
|
|
1289
1370
|
|
|
1290
|
-
<div
|
|
1371
|
+
<div
|
|
1372
|
+
class="builder-field__value"
|
|
1373
|
+
*ngIf="!field.children"
|
|
1374
|
+
[attr.data-testid]="'epistola-mapping-field-value-' + field.name"
|
|
1375
|
+
>
|
|
1291
1376
|
<input
|
|
1292
1377
|
*ngIf="field.mode === 'ref'"
|
|
1293
1378
|
type="text"
|
|
@@ -1297,8 +1382,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1297
1382
|
[disabled]="disabled"
|
|
1298
1383
|
placeholder="$doc.path.to.field"
|
|
1299
1384
|
[attr.list]="'suggestions-' + path.join('-')"
|
|
1385
|
+
[attr.data-testid]="'epistola-mapping-field-input-ref-' + field.name"
|
|
1300
1386
|
/>
|
|
1301
|
-
<datalist
|
|
1387
|
+
<datalist
|
|
1388
|
+
*ngIf="field.mode === 'ref'"
|
|
1389
|
+
[id]="'suggestions-' + path.join('-')"
|
|
1390
|
+
[attr.data-testid]="'epistola-mapping-field-suggestions-' + field.name"
|
|
1391
|
+
>
|
|
1302
1392
|
<option *ngFor="let s of suggestions" [value]="s"></option>
|
|
1303
1393
|
</datalist>
|
|
1304
1394
|
<input
|
|
@@ -1309,18 +1399,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1309
1399
|
(ngModelChange)="valueChange.emit({ path: path, value: $event })"
|
|
1310
1400
|
[disabled]="disabled"
|
|
1311
1401
|
placeholder="JSONata expression"
|
|
1402
|
+
[attr.data-testid]="'epistola-mapping-field-input-raw-' + field.name"
|
|
1312
1403
|
/>
|
|
1313
1404
|
<button
|
|
1314
1405
|
class="builder-field__mode-toggle"
|
|
1315
1406
|
(click)="modeToggle.emit(path)"
|
|
1316
1407
|
[disabled]="disabled"
|
|
1317
1408
|
[title]="field.mode === 'ref' ? 'Switch to raw JSONata' : 'Switch to reference'"
|
|
1409
|
+
[attr.data-testid]="'epistola-mapping-field-mode-toggle-' + field.name"
|
|
1318
1410
|
>
|
|
1319
1411
|
{{ field.mode === 'ref' ? 'fx' : '·' }}
|
|
1320
1412
|
</button>
|
|
1321
1413
|
</div>
|
|
1322
1414
|
|
|
1323
|
-
<div
|
|
1415
|
+
<div
|
|
1416
|
+
*ngIf="field.children && !collapsed"
|
|
1417
|
+
class="builder-field__children"
|
|
1418
|
+
[attr.data-testid]="'epistola-mapping-field-children-' + field.name"
|
|
1419
|
+
>
|
|
1324
1420
|
<epistola-builder-field
|
|
1325
1421
|
*ngFor="let child of field.children; let j = index"
|
|
1326
1422
|
[field]="child"
|
|
@@ -1689,16 +1785,18 @@ class MappingBuilderComponent {
|
|
|
1689
1785
|
}
|
|
1690
1786
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: MappingBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1691
1787
|
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: `
|
|
1692
|
-
<div class="mapping-builder">
|
|
1788
|
+
<div class="mapping-builder" data-testid="epistola-mapping-builder">
|
|
1693
1789
|
<div
|
|
1694
1790
|
*ngIf="fields.length === 0 && (!templateFields || templateFields.length === 0)"
|
|
1695
1791
|
class="mapping-builder__empty"
|
|
1792
|
+
data-testid="epistola-mapping-empty"
|
|
1696
1793
|
>
|
|
1697
1794
|
{{ 'noTemplateFields' | pluginTranslate: 'epistola' | async }}
|
|
1698
1795
|
</div>
|
|
1699
1796
|
|
|
1700
1797
|
<epistola-builder-field
|
|
1701
1798
|
*ngFor="let field of fields; let i = index"
|
|
1799
|
+
[attr.data-testid]="'epistola-mapping-row-' + field.name"
|
|
1702
1800
|
[field]="field"
|
|
1703
1801
|
[path]="[i]"
|
|
1704
1802
|
[suggestions]="allSuggestions"
|
|
@@ -1716,16 +1814,18 @@ class MappingBuilderComponent {
|
|
|
1716
1814
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: MappingBuilderComponent, decorators: [{
|
|
1717
1815
|
type: Component,
|
|
1718
1816
|
args: [{ selector: 'epistola-mapping-builder', standalone: true, imports: [CommonModule, FormsModule, PluginTranslatePipeModule, BuilderFieldComponent], template: `
|
|
1719
|
-
<div class="mapping-builder">
|
|
1817
|
+
<div class="mapping-builder" data-testid="epistola-mapping-builder">
|
|
1720
1818
|
<div
|
|
1721
1819
|
*ngIf="fields.length === 0 && (!templateFields || templateFields.length === 0)"
|
|
1722
1820
|
class="mapping-builder__empty"
|
|
1821
|
+
data-testid="epistola-mapping-empty"
|
|
1723
1822
|
>
|
|
1724
1823
|
{{ 'noTemplateFields' | pluginTranslate: 'epistola' | async }}
|
|
1725
1824
|
</div>
|
|
1726
1825
|
|
|
1727
1826
|
<epistola-builder-field
|
|
1728
1827
|
*ngFor="let field of fields; let i = index"
|
|
1828
|
+
[attr.data-testid]="'epistola-mapping-row-' + field.name"
|
|
1729
1829
|
[field]="field"
|
|
1730
1830
|
[path]="[i]"
|
|
1731
1831
|
[suggestions]="allSuggestions"
|
|
@@ -1845,21 +1945,23 @@ class MappingPreviewComponent {
|
|
|
1845
1945
|
}
|
|
1846
1946
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: MappingPreviewComponent, deps: [{ token: EpistolaPluginService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1847
1947
|
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: `
|
|
1848
|
-
<div class="preview">
|
|
1948
|
+
<div class="preview" data-testid="epistola-mapping-preview-root">
|
|
1849
1949
|
<div class="preview__header">
|
|
1850
|
-
<span class="preview__title">{{
|
|
1950
|
+
<span class="preview__title" data-testid="epistola-mapping-preview-title">{{
|
|
1851
1951
|
'previewTitle' | pluginTranslate: 'epistola' | async
|
|
1852
1952
|
}}</span>
|
|
1853
1953
|
<div class="preview__controls">
|
|
1854
1954
|
<input
|
|
1855
1955
|
type="text"
|
|
1856
1956
|
class="preview__doc-input"
|
|
1957
|
+
data-testid="epistola-mapping-preview-doc-input"
|
|
1857
1958
|
[ngModel]="documentId"
|
|
1858
1959
|
(ngModelChange)="documentId = $event"
|
|
1859
1960
|
[placeholder]="'previewDocPlaceholder' | pluginTranslate: 'epistola' | async"
|
|
1860
1961
|
/>
|
|
1861
1962
|
<button
|
|
1862
1963
|
class="preview__run-btn"
|
|
1964
|
+
data-testid="epistola-mapping-preview-run-btn"
|
|
1863
1965
|
(click)="runPreview()"
|
|
1864
1966
|
[disabled]="!documentId || !expression || loading"
|
|
1865
1967
|
>
|
|
@@ -1870,33 +1972,56 @@ class MappingPreviewComponent {
|
|
|
1870
1972
|
|
|
1871
1973
|
<div class="preview__panels">
|
|
1872
1974
|
<!-- Expected structure -->
|
|
1873
|
-
<div class="preview__panel">
|
|
1975
|
+
<div class="preview__panel" data-testid="epistola-mapping-preview-panel-expected">
|
|
1874
1976
|
<div class="preview__panel-label">
|
|
1875
1977
|
{{ 'previewExpected' | pluginTranslate: 'epistola' | async }}
|
|
1876
1978
|
</div>
|
|
1877
|
-
<pre class="preview__code">{{
|
|
1979
|
+
<pre class="preview__code" data-testid="epistola-mapping-preview-expected-code">{{
|
|
1980
|
+
expectedJson
|
|
1981
|
+
}}</pre>
|
|
1878
1982
|
</div>
|
|
1879
1983
|
|
|
1880
1984
|
<!-- Produced output -->
|
|
1881
|
-
<div class="preview__panel">
|
|
1985
|
+
<div class="preview__panel" data-testid="epistola-mapping-preview-panel-produced">
|
|
1882
1986
|
<div class="preview__panel-label">
|
|
1883
1987
|
{{ 'previewProduced' | pluginTranslate: 'epistola' | async }}
|
|
1884
1988
|
</div>
|
|
1885
|
-
<div
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1989
|
+
<div
|
|
1990
|
+
*ngIf="loading"
|
|
1991
|
+
class="preview__loading"
|
|
1992
|
+
data-testid="epistola-mapping-preview-loading"
|
|
1993
|
+
>
|
|
1994
|
+
...
|
|
1995
|
+
</div>
|
|
1996
|
+
<pre
|
|
1997
|
+
*ngIf="!loading && result?.success"
|
|
1998
|
+
class="preview__code"
|
|
1999
|
+
data-testid="epistola-mapping-preview-produced-code"
|
|
2000
|
+
>{{ result.result | json }}</pre
|
|
2001
|
+
>
|
|
2002
|
+
<div
|
|
2003
|
+
*ngIf="!loading && result && !result.success"
|
|
2004
|
+
class="preview__error"
|
|
2005
|
+
data-testid="epistola-mapping-preview-error"
|
|
2006
|
+
>
|
|
1890
2007
|
{{ result.error }}
|
|
1891
2008
|
</div>
|
|
1892
|
-
<div
|
|
2009
|
+
<div
|
|
2010
|
+
*ngIf="!loading && !result"
|
|
2011
|
+
class="preview__placeholder"
|
|
2012
|
+
data-testid="epistola-mapping-preview-placeholder"
|
|
2013
|
+
>
|
|
1893
2014
|
{{ 'previewRunHint' | pluginTranslate: 'epistola' | async }}
|
|
1894
2015
|
</div>
|
|
1895
2016
|
</div>
|
|
1896
2017
|
</div>
|
|
1897
2018
|
|
|
1898
2019
|
<!-- Missing fields warning -->
|
|
1899
|
-
<div
|
|
2020
|
+
<div
|
|
2021
|
+
*ngIf="missingRequired.length > 0"
|
|
2022
|
+
class="preview__warnings"
|
|
2023
|
+
data-testid="epistola-mapping-preview-warnings"
|
|
2024
|
+
>
|
|
1900
2025
|
<span class="preview__warning-icon">⚠</span>
|
|
1901
2026
|
{{ 'previewMissing' | pluginTranslate: 'epistola' | async }}:
|
|
1902
2027
|
<strong>{{ missingRequired.join(', ') }}</strong>
|
|
@@ -1907,21 +2032,23 @@ class MappingPreviewComponent {
|
|
|
1907
2032
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: MappingPreviewComponent, decorators: [{
|
|
1908
2033
|
type: Component,
|
|
1909
2034
|
args: [{ selector: 'epistola-mapping-preview', standalone: true, imports: [CommonModule, FormsModule, PluginTranslatePipeModule], template: `
|
|
1910
|
-
<div class="preview">
|
|
2035
|
+
<div class="preview" data-testid="epistola-mapping-preview-root">
|
|
1911
2036
|
<div class="preview__header">
|
|
1912
|
-
<span class="preview__title">{{
|
|
2037
|
+
<span class="preview__title" data-testid="epistola-mapping-preview-title">{{
|
|
1913
2038
|
'previewTitle' | pluginTranslate: 'epistola' | async
|
|
1914
2039
|
}}</span>
|
|
1915
2040
|
<div class="preview__controls">
|
|
1916
2041
|
<input
|
|
1917
2042
|
type="text"
|
|
1918
2043
|
class="preview__doc-input"
|
|
2044
|
+
data-testid="epistola-mapping-preview-doc-input"
|
|
1919
2045
|
[ngModel]="documentId"
|
|
1920
2046
|
(ngModelChange)="documentId = $event"
|
|
1921
2047
|
[placeholder]="'previewDocPlaceholder' | pluginTranslate: 'epistola' | async"
|
|
1922
2048
|
/>
|
|
1923
2049
|
<button
|
|
1924
2050
|
class="preview__run-btn"
|
|
2051
|
+
data-testid="epistola-mapping-preview-run-btn"
|
|
1925
2052
|
(click)="runPreview()"
|
|
1926
2053
|
[disabled]="!documentId || !expression || loading"
|
|
1927
2054
|
>
|
|
@@ -1932,33 +2059,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
1932
2059
|
|
|
1933
2060
|
<div class="preview__panels">
|
|
1934
2061
|
<!-- Expected structure -->
|
|
1935
|
-
<div class="preview__panel">
|
|
2062
|
+
<div class="preview__panel" data-testid="epistola-mapping-preview-panel-expected">
|
|
1936
2063
|
<div class="preview__panel-label">
|
|
1937
2064
|
{{ 'previewExpected' | pluginTranslate: 'epistola' | async }}
|
|
1938
2065
|
</div>
|
|
1939
|
-
<pre class="preview__code">{{
|
|
2066
|
+
<pre class="preview__code" data-testid="epistola-mapping-preview-expected-code">{{
|
|
2067
|
+
expectedJson
|
|
2068
|
+
}}</pre>
|
|
1940
2069
|
</div>
|
|
1941
2070
|
|
|
1942
2071
|
<!-- Produced output -->
|
|
1943
|
-
<div class="preview__panel">
|
|
2072
|
+
<div class="preview__panel" data-testid="epistola-mapping-preview-panel-produced">
|
|
1944
2073
|
<div class="preview__panel-label">
|
|
1945
2074
|
{{ 'previewProduced' | pluginTranslate: 'epistola' | async }}
|
|
1946
2075
|
</div>
|
|
1947
|
-
<div
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
2076
|
+
<div
|
|
2077
|
+
*ngIf="loading"
|
|
2078
|
+
class="preview__loading"
|
|
2079
|
+
data-testid="epistola-mapping-preview-loading"
|
|
2080
|
+
>
|
|
2081
|
+
...
|
|
2082
|
+
</div>
|
|
2083
|
+
<pre
|
|
2084
|
+
*ngIf="!loading && result?.success"
|
|
2085
|
+
class="preview__code"
|
|
2086
|
+
data-testid="epistola-mapping-preview-produced-code"
|
|
2087
|
+
>{{ result.result | json }}</pre
|
|
2088
|
+
>
|
|
2089
|
+
<div
|
|
2090
|
+
*ngIf="!loading && result && !result.success"
|
|
2091
|
+
class="preview__error"
|
|
2092
|
+
data-testid="epistola-mapping-preview-error"
|
|
2093
|
+
>
|
|
1952
2094
|
{{ result.error }}
|
|
1953
2095
|
</div>
|
|
1954
|
-
<div
|
|
2096
|
+
<div
|
|
2097
|
+
*ngIf="!loading && !result"
|
|
2098
|
+
class="preview__placeholder"
|
|
2099
|
+
data-testid="epistola-mapping-preview-placeholder"
|
|
2100
|
+
>
|
|
1955
2101
|
{{ 'previewRunHint' | pluginTranslate: 'epistola' | async }}
|
|
1956
2102
|
</div>
|
|
1957
2103
|
</div>
|
|
1958
2104
|
</div>
|
|
1959
2105
|
|
|
1960
2106
|
<!-- Missing fields warning -->
|
|
1961
|
-
<div
|
|
2107
|
+
<div
|
|
2108
|
+
*ngIf="missingRequired.length > 0"
|
|
2109
|
+
class="preview__warnings"
|
|
2110
|
+
data-testid="epistola-mapping-preview-warnings"
|
|
2111
|
+
>
|
|
1962
2112
|
<span class="preview__warning-icon">⚠</span>
|
|
1963
2113
|
{{ 'previewMissing' | pluginTranslate: 'epistola' | async }}:
|
|
1964
2114
|
<strong>{{ missingRequired.join(', ') }}</strong>
|
|
@@ -2786,7 +2936,7 @@ class GenerateDocumentConfigurationComponent {
|
|
|
2786
2936
|
});
|
|
2787
2937
|
}
|
|
2788
2938
|
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 });
|
|
2789
|
-
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 validationErrors: validationErrors$ | async,\n } as obs\"\n>\n <div\n *ngIf=\"obs.validationErrors && obs.validationErrors.length > 0\"\n class=\"jsonata-validation-errors\"\n >\n <strong>{{ 'jsonataValidationErrorsHeading' | pluginTranslate: pluginId | async }}</strong>\n <ul>\n <li *ngFor=\"let err of obs.validationErrors\">\n <code>{{ err.field }}</code\n >: {{ err.message }}\n </li>\n </ul>\n </div>\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 [clearSelectionSubject$]=\"clearTemplateId$\"\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 or expression) -->\n <!--\n The v-select / v-input here is intentionally NOT named into the v-form: v-form's\n @ContentChildren queries default to descendants:false and would skip anything nested\n in this `field-with-fx` wrapper. The value is tracked via (selectedChange)/(valueChange)\n on the component, see variantIdValue / filenameValue.\n -->\n <div *ngIf=\"variantSelectionMode === 'explicit'\" class=\"field-with-fx\">\n <v-select\n *ngIf=\"!variantIdExpressionMode\"\n [title]=\"'variantId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'variantIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.variants.data\"\n [defaultSelectionId]=\"variantIdValue\"\n [clearSelectionSubject$]=\"clearVariantId$\"\n (selectedChange)=\"onVariantIdValueChange($event)\"\n [disabled]=\"obs.disabled || obs.variants.loading || !obs.selectedTemplateId\"\n [required]=\"false\"\n [loading]=\"obs.variants.loading\"\n >\n </v-select>\n <div *ngIf=\"variantIdExpressionMode\" class=\"fx-input-group\">\n <label class=\"fx-input-label\">{{ 'variantId' | pluginTranslate: pluginId | async }}</label>\n <input\n type=\"text\"\n class=\"fx-input\"\n [ngModel]=\"variantIdExpression\"\n (ngModelChange)=\"variantIdExpression = $event; onVariantIdExpressionChange()\"\n [disabled]=\"obs.disabled\"\n placeholder=\"$pv.letterType\"\n />\n </div>\n <button\n type=\"button\"\n class=\"fx-toggle\"\n (click)=\"toggleVariantIdExpressionMode()\"\n [disabled]=\"obs.disabled\"\n [title]=\"variantIdExpressionMode ? 'Switch to dropdown' : 'Switch to expression'\"\n >\n {{ variantIdExpressionMode ? '\u00B7' : 'fx' }}\n </button>\n </div>\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 <div class=\"attribute-value-with-fx\">\n <input\n type=\"text\"\n class=\"variant-attribute-input\"\n [class.fx-input]=\"entry._expressionMode\"\n [placeholder]=\"\n entry._expressionMode\n ? '$pv.language'\n : ('attributeValue' | pluginTranslate: pluginId | async)\n \"\n [(ngModel)]=\"entry.value\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <button\n type=\"button\"\n class=\"fx-toggle fx-toggle--inline\"\n (click)=\"entry._expressionMode = !entry._expressionMode\"\n [disabled]=\"obs.disabled\"\n [title]=\"entry._expressionMode ? 'Switch to plain value' : 'Switch to expression'\"\n >\n {{ entry._expressionMode ? '\u00B7' : 'fx' }}\n </button>\n </div>\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 <div class=\"field-with-fx\">\n <v-input\n *ngIf=\"!filenameExpressionMode\"\n [title]=\"'filename' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'filenameTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"filenameValue\"\n (valueChange)=\"onFilenameValueChange($event)\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n <div *ngIf=\"filenameExpressionMode\" class=\"fx-input-group\">\n <label class=\"fx-input-label\">{{ 'filename' | pluginTranslate: pluginId | async }}</label>\n <input\n type=\"text\"\n class=\"fx-input\"\n [ngModel]=\"filenameExpression\"\n (ngModelChange)=\"filenameExpression = $event; onFilenameExpressionChange()\"\n [disabled]=\"obs.disabled\"\n placeholder='\"besluit-\" & $doc.name & \".pdf\"'\n />\n </div>\n <button\n type=\"button\"\n class=\"fx-toggle\"\n (click)=\"toggleFilenameExpressionMode()\"\n [disabled]=\"obs.disabled\"\n [title]=\"filenameExpressionMode ? 'Switch to plain input' : 'Switch to expression'\"\n >\n {{ filenameExpressionMode ? '\u00B7' : 'fx' }}\n </button>\n </div>\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 [contextVariables]=\"editorContextVariables\"\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}.jsonata-validation-errors{margin-bottom:1rem;padding:.75rem 1rem;border:1px solid #dc3545;border-radius:4px;background:#fdf3f4;color:#dc3545;font-size:.875rem}.jsonata-validation-errors ul{margin:.5rem 0 0;padding-left:1.25rem}.jsonata-validation-errors code{background:#dc35451a;padding:0 .25rem;border-radius:2px;font-family:monospace}.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}.field-with-fx{display:flex;align-items:flex-start;gap:4px}.field-with-fx>*:first-child{flex:1;min-width:0}.fx-toggle{width:28px;height:28px;margin-top:22px;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;flex-shrink:0}.fx-toggle:hover{background:#f4f4f4}.fx-toggle--inline{margin-top:0}.fx-input-group{flex:1;min-width:0;margin-bottom:.75rem}.fx-input-label{display:block;font-size:.875rem;margin-bottom:.25rem;color:#525252}.fx-input{width:100%;border:1px solid #8d8d8d;border-radius:0;padding:.4rem .75rem;font-family:monospace;font-size:.875rem;background:#f4f4f4}.attribute-value-with-fx{display:flex;align-items:center;gap:4px;flex:1;min-width:0}.attribute-value-with-fx>input{flex:1;min-width:0}.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: i3$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3$1.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: i3$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i3$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$1.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", "contextVariables", "functions", "variablesHint"], 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 });
|
|
2939
|
+
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 data-testid=\"epistola-generate-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 validationErrors: validationErrors$ | async,\n } as obs\"\n>\n <div\n *ngIf=\"obs.validationErrors && obs.validationErrors.length > 0\"\n class=\"jsonata-validation-errors\"\n data-testid=\"epistola-generate-validation-errors\"\n >\n <strong>{{ 'jsonataValidationErrorsHeading' | pluginTranslate: pluginId | async }}</strong>\n <ul>\n <li *ngFor=\"let err of obs.validationErrors\">\n <code>{{ err.field }}</code\n >: {{ err.message }}\n </li>\n </ul>\n </div>\n <v-select\n name=\"catalogId\"\n data-testid=\"epistola-generate-catalog-id\"\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\n *ngIf=\"obs.catalogs.error\"\n class=\"loading-error\"\n data-testid=\"epistola-generate-catalog-error\"\n >\n {{ obs.catalogs.error }}\n </div>\n\n <v-select\n name=\"templateId\"\n data-testid=\"epistola-generate-template-id\"\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 [clearSelectionSubject$]=\"clearTemplateId$\"\n [disabled]=\"obs.disabled || obs.templates.loading || !obs.selectedCatalogId\"\n [required]=\"true\"\n [loading]=\"obs.templates.loading\"\n >\n </v-select>\n <div\n *ngIf=\"obs.templates.error\"\n class=\"loading-error\"\n data-testid=\"epistola-generate-template-error\"\n >\n {{ obs.templates.error }}\n </div>\n\n <!-- Variant selection mode toggle -->\n <div\n class=\"variant-mode-toggle\"\n *ngIf=\"obs.selectedTemplateId\"\n data-testid=\"epistola-generate-variant-mode-toggle\"\n >\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 data-testid=\"epistola-generate-variant-mode-explicit\"\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 data-testid=\"epistola-generate-variant-mode-attributes\"\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 or expression) -->\n <!--\n The v-select / v-input here is intentionally NOT named into the v-form: v-form's\n @ContentChildren queries default to descendants:false and would skip anything nested\n in this `field-with-fx` wrapper. The value is tracked via (selectedChange)/(valueChange)\n on the component, see variantIdValue / filenameValue.\n -->\n <div *ngIf=\"variantSelectionMode === 'explicit'\" class=\"field-with-fx\">\n <v-select\n *ngIf=\"!variantIdExpressionMode\"\n data-testid=\"epistola-generate-variant-id\"\n [title]=\"'variantId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'variantIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.variants.data\"\n [defaultSelectionId]=\"variantIdValue\"\n [clearSelectionSubject$]=\"clearVariantId$\"\n (selectedChange)=\"onVariantIdValueChange($event)\"\n [disabled]=\"obs.disabled || obs.variants.loading || !obs.selectedTemplateId\"\n [required]=\"false\"\n [loading]=\"obs.variants.loading\"\n >\n </v-select>\n <div *ngIf=\"variantIdExpressionMode\" class=\"fx-input-group\">\n <label class=\"fx-input-label\">{{ 'variantId' | pluginTranslate: pluginId | async }}</label>\n <input\n type=\"text\"\n class=\"fx-input\"\n data-testid=\"epistola-generate-variant-id-expression\"\n [ngModel]=\"variantIdExpression\"\n (ngModelChange)=\"variantIdExpression = $event; onVariantIdExpressionChange()\"\n [disabled]=\"obs.disabled\"\n placeholder=\"$pv.letterType\"\n />\n </div>\n <button\n type=\"button\"\n class=\"fx-toggle\"\n data-testid=\"epistola-generate-variant-id-fx-toggle\"\n (click)=\"toggleVariantIdExpressionMode()\"\n [disabled]=\"obs.disabled\"\n [title]=\"variantIdExpressionMode ? 'Switch to dropdown' : 'Switch to expression'\"\n >\n {{ variantIdExpressionMode ? '\u00B7' : 'fx' }}\n </button>\n </div>\n <div\n *ngIf=\"obs.variants.error\"\n class=\"loading-error\"\n data-testid=\"epistola-generate-variant-error\"\n >\n {{ obs.variants.error }}\n </div>\n\n <!-- Attribute-based variant selection -->\n <div\n *ngIf=\"variantSelectionMode === 'attributes' && obs.selectedTemplateId\"\n class=\"variant-attributes-section\"\n data-testid=\"epistola-generate-variant-attributes\"\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 [attr.data-testid]=\"'epistola-generate-attribute-row-' + i\"\n >\n <select\n *ngIf=\"!entry._customKey\"\n class=\"variant-attribute-input\"\n [attr.data-testid]=\"'epistola-generate-attribute-key-' + i\"\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 [attr.data-testid]=\"'epistola-generate-attribute-key-custom-' + i\"\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 [attr.data-testid]=\"'epistola-generate-attribute-key-custom-cancel-' + i\"\n (click)=\"cancelCustomKey(entry)\"\n [disabled]=\"obs.disabled\"\n >\n ×\n </button>\n </div>\n <div class=\"attribute-value-with-fx\">\n <input\n type=\"text\"\n class=\"variant-attribute-input\"\n [attr.data-testid]=\"'epistola-generate-attribute-value-' + i\"\n [class.fx-input]=\"entry._expressionMode\"\n [placeholder]=\"\n entry._expressionMode\n ? '$pv.language'\n : ('attributeValue' | pluginTranslate: pluginId | async)\n \"\n [(ngModel)]=\"entry.value\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <button\n type=\"button\"\n class=\"fx-toggle fx-toggle--inline\"\n [attr.data-testid]=\"'epistola-generate-attribute-value-fx-toggle-' + i\"\n (click)=\"entry._expressionMode = !entry._expressionMode\"\n [disabled]=\"obs.disabled\"\n [title]=\"entry._expressionMode ? 'Switch to plain value' : 'Switch to expression'\"\n >\n {{ entry._expressionMode ? '\u00B7' : 'fx' }}\n </button>\n </div>\n <label class=\"variant-attribute-required-toggle\">\n <input\n type=\"checkbox\"\n [attr.data-testid]=\"'epistola-generate-attribute-required-' + i\"\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 [attr.data-testid]=\"'epistola-generate-attribute-remove-' + i\"\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 data-testid=\"epistola-generate-attribute-add\"\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 data-testid=\"epistola-generate-environment-id\"\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\n *ngIf=\"obs.environments.error\"\n class=\"loading-error\"\n data-testid=\"epistola-generate-environment-error\"\n >\n {{ obs.environments.error }}\n </div>\n\n <v-select\n name=\"outputFormat\"\n data-testid=\"epistola-generate-output-format\"\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 <div class=\"field-with-fx\">\n <v-input\n *ngIf=\"!filenameExpressionMode\"\n data-testid=\"epistola-generate-filename\"\n [title]=\"'filename' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'filenameTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"filenameValue\"\n (valueChange)=\"onFilenameValueChange($event)\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n <div *ngIf=\"filenameExpressionMode\" class=\"fx-input-group\">\n <label class=\"fx-input-label\">{{ 'filename' | pluginTranslate: pluginId | async }}</label>\n <input\n type=\"text\"\n class=\"fx-input\"\n data-testid=\"epistola-generate-filename-expression\"\n [ngModel]=\"filenameExpression\"\n (ngModelChange)=\"filenameExpression = $event; onFilenameExpressionChange()\"\n [disabled]=\"obs.disabled\"\n placeholder='\"besluit-\" & $doc.name & \".pdf\"'\n />\n </div>\n <button\n type=\"button\"\n class=\"fx-toggle\"\n data-testid=\"epistola-generate-filename-fx-toggle\"\n (click)=\"toggleFilenameExpressionMode()\"\n [disabled]=\"obs.disabled\"\n [title]=\"filenameExpressionMode ? 'Switch to plain input' : 'Switch to expression'\"\n >\n {{ filenameExpressionMode ? '\u00B7' : 'fx' }}\n </button>\n </div>\n\n <v-input\n name=\"correlationId\"\n data-testid=\"epistola-generate-correlation-id\"\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 data-testid=\"epistola-generate-result-process-variable\"\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 data-testid=\"epistola-generate-mapping-mode-simple\"\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 data-testid=\"epistola-generate-mapping-mode-advanced\"\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 [contextVariables]=\"editorContextVariables\"\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\n class=\"mapping-tools__header\"\n data-testid=\"epistola-generate-mapping-tools-header\"\n (click)=\"toolsCollapsed = !toolsCollapsed\"\n >\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 data-testid=\"epistola-generate-mapping-tab-schema\"\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 data-testid=\"epistola-generate-mapping-tab-preview\"\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}.jsonata-validation-errors{margin-bottom:1rem;padding:.75rem 1rem;border:1px solid #dc3545;border-radius:4px;background:#fdf3f4;color:#dc3545;font-size:.875rem}.jsonata-validation-errors ul{margin:.5rem 0 0;padding-left:1.25rem}.jsonata-validation-errors code{background:#dc35451a;padding:0 .25rem;border-radius:2px;font-family:monospace}.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}.field-with-fx{display:flex;align-items:flex-start;gap:4px}.field-with-fx>*:first-child{flex:1;min-width:0}.fx-toggle{width:28px;height:28px;margin-top:22px;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;flex-shrink:0}.fx-toggle:hover{background:#f4f4f4}.fx-toggle--inline{margin-top:0}.fx-input-group{flex:1;min-width:0;margin-bottom:.75rem}.fx-input-label{display:block;font-size:.875rem;margin-bottom:.25rem;color:#525252}.fx-input{width:100%;border:1px solid #8d8d8d;border-radius:0;padding:.4rem .75rem;font-family:monospace;font-size:.875rem;background:#f4f4f4}.attribute-value-with-fx{display:flex;align-items:center;gap:4px;flex:1;min-width:0}.attribute-value-with-fx>input{flex:1;min-width:0}.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: i3$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3$1.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: i3$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i3$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$1.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", "contextVariables", "functions", "variablesHint"], 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 });
|
|
2790
2940
|
}
|
|
2791
2941
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GenerateDocumentConfigurationComponent, decorators: [{
|
|
2792
2942
|
type: Component,
|
|
@@ -2801,7 +2951,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
2801
2951
|
JsonataEditorComponent,
|
|
2802
2952
|
MappingBuilderComponent,
|
|
2803
2953
|
MappingPreviewComponent,
|
|
2804
|
-
], 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 validationErrors: validationErrors$ | async,\n } as obs\"\n>\n <div\n *ngIf=\"obs.validationErrors && obs.validationErrors.length > 0\"\n class=\"jsonata-validation-errors\"\n >\n <strong>{{ 'jsonataValidationErrorsHeading' | pluginTranslate: pluginId | async }}</strong>\n <ul>\n <li *ngFor=\"let err of obs.validationErrors\">\n <code>{{ err.field }}</code\n >: {{ err.message }}\n </li>\n </ul>\n </div>\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 [clearSelectionSubject$]=\"clearTemplateId$\"\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 or expression) -->\n <!--\n The v-select / v-input here is intentionally NOT named into the v-form: v-form's\n @ContentChildren queries default to descendants:false and would skip anything nested\n in this `field-with-fx` wrapper. The value is tracked via (selectedChange)/(valueChange)\n on the component, see variantIdValue / filenameValue.\n -->\n <div *ngIf=\"variantSelectionMode === 'explicit'\" class=\"field-with-fx\">\n <v-select\n *ngIf=\"!variantIdExpressionMode\"\n [title]=\"'variantId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'variantIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.variants.data\"\n [defaultSelectionId]=\"variantIdValue\"\n [clearSelectionSubject$]=\"clearVariantId$\"\n (selectedChange)=\"onVariantIdValueChange($event)\"\n [disabled]=\"obs.disabled || obs.variants.loading || !obs.selectedTemplateId\"\n [required]=\"false\"\n [loading]=\"obs.variants.loading\"\n >\n </v-select>\n <div *ngIf=\"variantIdExpressionMode\" class=\"fx-input-group\">\n <label class=\"fx-input-label\">{{ 'variantId' | pluginTranslate: pluginId | async }}</label>\n <input\n type=\"text\"\n class=\"fx-input\"\n [ngModel]=\"variantIdExpression\"\n (ngModelChange)=\"variantIdExpression = $event; onVariantIdExpressionChange()\"\n [disabled]=\"obs.disabled\"\n placeholder=\"$pv.letterType\"\n />\n </div>\n <button\n type=\"button\"\n class=\"fx-toggle\"\n (click)=\"toggleVariantIdExpressionMode()\"\n [disabled]=\"obs.disabled\"\n [title]=\"variantIdExpressionMode ? 'Switch to dropdown' : 'Switch to expression'\"\n >\n {{ variantIdExpressionMode ? '\u00B7' : 'fx' }}\n </button>\n </div>\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 <div class=\"attribute-value-with-fx\">\n <input\n type=\"text\"\n class=\"variant-attribute-input\"\n [class.fx-input]=\"entry._expressionMode\"\n [placeholder]=\"\n entry._expressionMode\n ? '$pv.language'\n : ('attributeValue' | pluginTranslate: pluginId | async)\n \"\n [(ngModel)]=\"entry.value\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <button\n type=\"button\"\n class=\"fx-toggle fx-toggle--inline\"\n (click)=\"entry._expressionMode = !entry._expressionMode\"\n [disabled]=\"obs.disabled\"\n [title]=\"entry._expressionMode ? 'Switch to plain value' : 'Switch to expression'\"\n >\n {{ entry._expressionMode ? '\u00B7' : 'fx' }}\n </button>\n </div>\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 <div class=\"field-with-fx\">\n <v-input\n *ngIf=\"!filenameExpressionMode\"\n [title]=\"'filename' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'filenameTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"filenameValue\"\n (valueChange)=\"onFilenameValueChange($event)\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n <div *ngIf=\"filenameExpressionMode\" class=\"fx-input-group\">\n <label class=\"fx-input-label\">{{ 'filename' | pluginTranslate: pluginId | async }}</label>\n <input\n type=\"text\"\n class=\"fx-input\"\n [ngModel]=\"filenameExpression\"\n (ngModelChange)=\"filenameExpression = $event; onFilenameExpressionChange()\"\n [disabled]=\"obs.disabled\"\n placeholder='\"besluit-\" & $doc.name & \".pdf\"'\n />\n </div>\n <button\n type=\"button\"\n class=\"fx-toggle\"\n (click)=\"toggleFilenameExpressionMode()\"\n [disabled]=\"obs.disabled\"\n [title]=\"filenameExpressionMode ? 'Switch to plain input' : 'Switch to expression'\"\n >\n {{ filenameExpressionMode ? '\u00B7' : 'fx' }}\n </button>\n </div>\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 [contextVariables]=\"editorContextVariables\"\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}.jsonata-validation-errors{margin-bottom:1rem;padding:.75rem 1rem;border:1px solid #dc3545;border-radius:4px;background:#fdf3f4;color:#dc3545;font-size:.875rem}.jsonata-validation-errors ul{margin:.5rem 0 0;padding-left:1.25rem}.jsonata-validation-errors code{background:#dc35451a;padding:0 .25rem;border-radius:2px;font-family:monospace}.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}.field-with-fx{display:flex;align-items:flex-start;gap:4px}.field-with-fx>*:first-child{flex:1;min-width:0}.fx-toggle{width:28px;height:28px;margin-top:22px;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;flex-shrink:0}.fx-toggle:hover{background:#f4f4f4}.fx-toggle--inline{margin-top:0}.fx-input-group{flex:1;min-width:0;margin-bottom:.75rem}.fx-input-label{display:block;font-size:.875rem;margin-bottom:.25rem;color:#525252}.fx-input{width:100%;border:1px solid #8d8d8d;border-radius:0;padding:.4rem .75rem;font-family:monospace;font-size:.875rem;background:#f4f4f4}.attribute-value-with-fx{display:flex;align-items:center;gap:4px;flex:1;min-width:0}.attribute-value-with-fx>input{flex:1;min-width:0}.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"] }]
|
|
2954
|
+
], template: "<v-form\n data-testid=\"epistola-generate-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 validationErrors: validationErrors$ | async,\n } as obs\"\n>\n <div\n *ngIf=\"obs.validationErrors && obs.validationErrors.length > 0\"\n class=\"jsonata-validation-errors\"\n data-testid=\"epistola-generate-validation-errors\"\n >\n <strong>{{ 'jsonataValidationErrorsHeading' | pluginTranslate: pluginId | async }}</strong>\n <ul>\n <li *ngFor=\"let err of obs.validationErrors\">\n <code>{{ err.field }}</code\n >: {{ err.message }}\n </li>\n </ul>\n </div>\n <v-select\n name=\"catalogId\"\n data-testid=\"epistola-generate-catalog-id\"\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\n *ngIf=\"obs.catalogs.error\"\n class=\"loading-error\"\n data-testid=\"epistola-generate-catalog-error\"\n >\n {{ obs.catalogs.error }}\n </div>\n\n <v-select\n name=\"templateId\"\n data-testid=\"epistola-generate-template-id\"\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 [clearSelectionSubject$]=\"clearTemplateId$\"\n [disabled]=\"obs.disabled || obs.templates.loading || !obs.selectedCatalogId\"\n [required]=\"true\"\n [loading]=\"obs.templates.loading\"\n >\n </v-select>\n <div\n *ngIf=\"obs.templates.error\"\n class=\"loading-error\"\n data-testid=\"epistola-generate-template-error\"\n >\n {{ obs.templates.error }}\n </div>\n\n <!-- Variant selection mode toggle -->\n <div\n class=\"variant-mode-toggle\"\n *ngIf=\"obs.selectedTemplateId\"\n data-testid=\"epistola-generate-variant-mode-toggle\"\n >\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 data-testid=\"epistola-generate-variant-mode-explicit\"\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 data-testid=\"epistola-generate-variant-mode-attributes\"\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 or expression) -->\n <!--\n The v-select / v-input here is intentionally NOT named into the v-form: v-form's\n @ContentChildren queries default to descendants:false and would skip anything nested\n in this `field-with-fx` wrapper. The value is tracked via (selectedChange)/(valueChange)\n on the component, see variantIdValue / filenameValue.\n -->\n <div *ngIf=\"variantSelectionMode === 'explicit'\" class=\"field-with-fx\">\n <v-select\n *ngIf=\"!variantIdExpressionMode\"\n data-testid=\"epistola-generate-variant-id\"\n [title]=\"'variantId' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'variantIdTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"obs.variants.data\"\n [defaultSelectionId]=\"variantIdValue\"\n [clearSelectionSubject$]=\"clearVariantId$\"\n (selectedChange)=\"onVariantIdValueChange($event)\"\n [disabled]=\"obs.disabled || obs.variants.loading || !obs.selectedTemplateId\"\n [required]=\"false\"\n [loading]=\"obs.variants.loading\"\n >\n </v-select>\n <div *ngIf=\"variantIdExpressionMode\" class=\"fx-input-group\">\n <label class=\"fx-input-label\">{{ 'variantId' | pluginTranslate: pluginId | async }}</label>\n <input\n type=\"text\"\n class=\"fx-input\"\n data-testid=\"epistola-generate-variant-id-expression\"\n [ngModel]=\"variantIdExpression\"\n (ngModelChange)=\"variantIdExpression = $event; onVariantIdExpressionChange()\"\n [disabled]=\"obs.disabled\"\n placeholder=\"$pv.letterType\"\n />\n </div>\n <button\n type=\"button\"\n class=\"fx-toggle\"\n data-testid=\"epistola-generate-variant-id-fx-toggle\"\n (click)=\"toggleVariantIdExpressionMode()\"\n [disabled]=\"obs.disabled\"\n [title]=\"variantIdExpressionMode ? 'Switch to dropdown' : 'Switch to expression'\"\n >\n {{ variantIdExpressionMode ? '\u00B7' : 'fx' }}\n </button>\n </div>\n <div\n *ngIf=\"obs.variants.error\"\n class=\"loading-error\"\n data-testid=\"epistola-generate-variant-error\"\n >\n {{ obs.variants.error }}\n </div>\n\n <!-- Attribute-based variant selection -->\n <div\n *ngIf=\"variantSelectionMode === 'attributes' && obs.selectedTemplateId\"\n class=\"variant-attributes-section\"\n data-testid=\"epistola-generate-variant-attributes\"\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 [attr.data-testid]=\"'epistola-generate-attribute-row-' + i\"\n >\n <select\n *ngIf=\"!entry._customKey\"\n class=\"variant-attribute-input\"\n [attr.data-testid]=\"'epistola-generate-attribute-key-' + i\"\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 [attr.data-testid]=\"'epistola-generate-attribute-key-custom-' + i\"\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 [attr.data-testid]=\"'epistola-generate-attribute-key-custom-cancel-' + i\"\n (click)=\"cancelCustomKey(entry)\"\n [disabled]=\"obs.disabled\"\n >\n ×\n </button>\n </div>\n <div class=\"attribute-value-with-fx\">\n <input\n type=\"text\"\n class=\"variant-attribute-input\"\n [attr.data-testid]=\"'epistola-generate-attribute-value-' + i\"\n [class.fx-input]=\"entry._expressionMode\"\n [placeholder]=\"\n entry._expressionMode\n ? '$pv.language'\n : ('attributeValue' | pluginTranslate: pluginId | async)\n \"\n [(ngModel)]=\"entry.value\"\n (ngModelChange)=\"onAttributeEntryChange()\"\n [disabled]=\"obs.disabled\"\n />\n <button\n type=\"button\"\n class=\"fx-toggle fx-toggle--inline\"\n [attr.data-testid]=\"'epistola-generate-attribute-value-fx-toggle-' + i\"\n (click)=\"entry._expressionMode = !entry._expressionMode\"\n [disabled]=\"obs.disabled\"\n [title]=\"entry._expressionMode ? 'Switch to plain value' : 'Switch to expression'\"\n >\n {{ entry._expressionMode ? '\u00B7' : 'fx' }}\n </button>\n </div>\n <label class=\"variant-attribute-required-toggle\">\n <input\n type=\"checkbox\"\n [attr.data-testid]=\"'epistola-generate-attribute-required-' + i\"\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 [attr.data-testid]=\"'epistola-generate-attribute-remove-' + i\"\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 data-testid=\"epistola-generate-attribute-add\"\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 data-testid=\"epistola-generate-environment-id\"\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\n *ngIf=\"obs.environments.error\"\n class=\"loading-error\"\n data-testid=\"epistola-generate-environment-error\"\n >\n {{ obs.environments.error }}\n </div>\n\n <v-select\n name=\"outputFormat\"\n data-testid=\"epistola-generate-output-format\"\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 <div class=\"field-with-fx\">\n <v-input\n *ngIf=\"!filenameExpressionMode\"\n data-testid=\"epistola-generate-filename\"\n [title]=\"'filename' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'filenameTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"filenameValue\"\n (valueChange)=\"onFilenameValueChange($event)\"\n [disabled]=\"obs.disabled\"\n [required]=\"true\"\n >\n </v-input>\n <div *ngIf=\"filenameExpressionMode\" class=\"fx-input-group\">\n <label class=\"fx-input-label\">{{ 'filename' | pluginTranslate: pluginId | async }}</label>\n <input\n type=\"text\"\n class=\"fx-input\"\n data-testid=\"epistola-generate-filename-expression\"\n [ngModel]=\"filenameExpression\"\n (ngModelChange)=\"filenameExpression = $event; onFilenameExpressionChange()\"\n [disabled]=\"obs.disabled\"\n placeholder='\"besluit-\" & $doc.name & \".pdf\"'\n />\n </div>\n <button\n type=\"button\"\n class=\"fx-toggle\"\n data-testid=\"epistola-generate-filename-fx-toggle\"\n (click)=\"toggleFilenameExpressionMode()\"\n [disabled]=\"obs.disabled\"\n [title]=\"filenameExpressionMode ? 'Switch to plain input' : 'Switch to expression'\"\n >\n {{ filenameExpressionMode ? '\u00B7' : 'fx' }}\n </button>\n </div>\n\n <v-input\n name=\"correlationId\"\n data-testid=\"epistola-generate-correlation-id\"\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 data-testid=\"epistola-generate-result-process-variable\"\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 data-testid=\"epistola-generate-mapping-mode-simple\"\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 data-testid=\"epistola-generate-mapping-mode-advanced\"\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 [contextVariables]=\"editorContextVariables\"\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\n class=\"mapping-tools__header\"\n data-testid=\"epistola-generate-mapping-tools-header\"\n (click)=\"toolsCollapsed = !toolsCollapsed\"\n >\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 data-testid=\"epistola-generate-mapping-tab-schema\"\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 data-testid=\"epistola-generate-mapping-tab-preview\"\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}.jsonata-validation-errors{margin-bottom:1rem;padding:.75rem 1rem;border:1px solid #dc3545;border-radius:4px;background:#fdf3f4;color:#dc3545;font-size:.875rem}.jsonata-validation-errors ul{margin:.5rem 0 0;padding-left:1.25rem}.jsonata-validation-errors code{background:#dc35451a;padding:0 .25rem;border-radius:2px;font-family:monospace}.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}.field-with-fx{display:flex;align-items:flex-start;gap:4px}.field-with-fx>*:first-child{flex:1;min-width:0}.fx-toggle{width:28px;height:28px;margin-top:22px;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;flex-shrink:0}.fx-toggle:hover{background:#f4f4f4}.fx-toggle--inline{margin-top:0}.fx-input-group{flex:1;min-width:0;margin-bottom:.75rem}.fx-input-label{display:block;font-size:.875rem;margin-bottom:.25rem;color:#525252}.fx-input{width:100%;border:1px solid #8d8d8d;border-radius:0;padding:.4rem .75rem;font-family:monospace;font-size:.875rem;background:#f4f4f4}.attribute-value-with-fx{display:flex;align-items:center;gap:4px;flex:1;min-width:0}.attribute-value-with-fx>input{flex:1;min-width:0}.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"] }]
|
|
2805
2955
|
}], ctorParameters: () => [{ type: EpistolaPluginService }, { type: i2$2.ProcessLinkStateService }, { type: i0.ChangeDetectorRef }], propDecorators: { save$: [{
|
|
2806
2956
|
type: Input
|
|
2807
2957
|
}], disabled$: [{
|
|
@@ -2888,11 +3038,11 @@ class CheckJobStatusConfigurationComponent {
|
|
|
2888
3038
|
});
|
|
2889
3039
|
}
|
|
2890
3040
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CheckJobStatusConfigurationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2891
|
-
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
|
|
3041
|
+
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 data-testid=\"epistola-check-status-form\"\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"prefillResolved$ | async\"\n>\n <v-input\n name=\"requestIdVariable\"\n data-testid=\"epistola-check-status-request-id-variable\"\n [title]=\"'requestIdVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'requestIdVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.requestIdVariable\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"statusVariable\"\n data-testid=\"epistola-check-status-status-variable\"\n [title]=\"'statusVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'statusVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.statusVariable\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"documentIdVariable\"\n data-testid=\"epistola-check-status-document-id-variable\"\n [title]=\"'documentIdVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'documentIdVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.documentIdVariable\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"false\"\n >\n </v-input>\n\n <v-input\n name=\"errorMessageVariable\"\n data-testid=\"epistola-check-status-error-message-variable\"\n [title]=\"'errorMessageVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'errorMessageVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.errorMessageVariable\"\n [disabled]=\"safeDisabled$ | async\"\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"] }] });
|
|
2892
3042
|
}
|
|
2893
3043
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CheckJobStatusConfigurationComponent, decorators: [{
|
|
2894
3044
|
type: Component,
|
|
2895
|
-
args: [{ selector: 'epistola-check-job-status-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form
|
|
3045
|
+
args: [{ selector: 'epistola-check-job-status-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form\n data-testid=\"epistola-check-status-form\"\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"prefillResolved$ | async\"\n>\n <v-input\n name=\"requestIdVariable\"\n data-testid=\"epistola-check-status-request-id-variable\"\n [title]=\"'requestIdVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'requestIdVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.requestIdVariable\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"statusVariable\"\n data-testid=\"epistola-check-status-status-variable\"\n [title]=\"'statusVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'statusVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.statusVariable\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n name=\"documentIdVariable\"\n data-testid=\"epistola-check-status-document-id-variable\"\n [title]=\"'documentIdVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'documentIdVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.documentIdVariable\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"false\"\n >\n </v-input>\n\n <v-input\n name=\"errorMessageVariable\"\n data-testid=\"epistola-check-status-error-message-variable\"\n [title]=\"'errorMessageVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'errorMessageVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.errorMessageVariable\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"false\"\n >\n </v-input>\n</v-form>\n" }]
|
|
2896
3046
|
}], propDecorators: { save$: [{
|
|
2897
3047
|
type: Input
|
|
2898
3048
|
}], disabled$: [{
|
|
@@ -3029,11 +3179,11 @@ class DownloadDocumentConfigurationComponent {
|
|
|
3029
3179
|
});
|
|
3030
3180
|
}
|
|
3031
3181
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DownloadDocumentConfigurationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3032
|
-
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
|
|
3182
|
+
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 data-testid=\"epistola-download-form\"\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"prefillResolved$ | async\"\n>\n <v-input\n name=\"documentVariable\"\n data-testid=\"epistola-download-document-variable\"\n [title]=\"'documentVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'documentVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.documentVariable\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-select\n name=\"storageTarget\"\n data-testid=\"epistola-download-storage-target\"\n [title]=\"'storageTarget' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'storageTargetTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"storageTargetOptions\"\n [defaultSelectionId]=\"resolvedPrefill?.storageTarget || defaultStorageTarget\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"true\"\n >\n </v-select>\n\n <v-input\n *ngIf=\"(selectedTarget$ | async) === 'TEMPORARY_RESOURCE'\"\n name=\"resourceIdVariable\"\n data-testid=\"epistola-download-resource-id-variable\"\n [title]=\"'resourceIdVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'resourceIdVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.resourceIdVariable\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n *ngIf=\"(selectedTarget$ | async) === 'PROCESS_VARIABLE'\"\n name=\"contentVariable\"\n data-testid=\"epistola-download-content-variable\"\n [title]=\"'contentVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'contentVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.contentVariable\"\n [disabled]=\"safeDisabled$ | async\"\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"] }, { 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"] }] });
|
|
3033
3183
|
}
|
|
3034
3184
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DownloadDocumentConfigurationComponent, decorators: [{
|
|
3035
3185
|
type: Component,
|
|
3036
|
-
args: [{ selector: 'epistola-download-document-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule, SelectModule], template: "<v-form
|
|
3186
|
+
args: [{ selector: 'epistola-download-document-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule, SelectModule], template: "<v-form\n data-testid=\"epistola-download-form\"\n (valueChange)=\"formValueChange($event)\"\n *ngIf=\"prefillResolved$ | async\"\n>\n <v-input\n name=\"documentVariable\"\n data-testid=\"epistola-download-document-variable\"\n [title]=\"'documentVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'documentVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.documentVariable\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-select\n name=\"storageTarget\"\n data-testid=\"epistola-download-storage-target\"\n [title]=\"'storageTarget' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'storageTargetTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [items]=\"storageTargetOptions\"\n [defaultSelectionId]=\"resolvedPrefill?.storageTarget || defaultStorageTarget\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"true\"\n >\n </v-select>\n\n <v-input\n *ngIf=\"(selectedTarget$ | async) === 'TEMPORARY_RESOURCE'\"\n name=\"resourceIdVariable\"\n data-testid=\"epistola-download-resource-id-variable\"\n [title]=\"'resourceIdVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'resourceIdVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.resourceIdVariable\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"true\"\n >\n </v-input>\n\n <v-input\n *ngIf=\"(selectedTarget$ | async) === 'PROCESS_VARIABLE'\"\n name=\"contentVariable\"\n data-testid=\"epistola-download-content-variable\"\n [title]=\"'contentVariable' | pluginTranslate: pluginId | async\"\n [tooltip]=\"'contentVariableTooltip' | pluginTranslate: pluginId | async\"\n [margin]=\"true\"\n [defaultValue]=\"resolvedPrefill?.contentVariable\"\n [disabled]=\"safeDisabled$ | async\"\n [required]=\"true\"\n >\n </v-input>\n</v-form>\n" }]
|
|
3037
3187
|
}], propDecorators: { save$: [{
|
|
3038
3188
|
type: Input
|
|
3039
3189
|
}], disabled$: [{
|
|
@@ -3226,7 +3376,7 @@ class EpistolaDocumentComponent {
|
|
|
3226
3376
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaDocumentComponent, deps: [{ token: EpistolaPluginService }, { token: i2$3.DomSanitizer }, { token: i3.FormIoStateService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3227
3377
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaDocumentComponent, isStandalone: true, selector: "epistola-document-component", inputs: { value: "value", disabled: "disabled", label: "label", display: "display", documentVariable: "documentVariable", tenantIdVariable: "tenantIdVariable", filename: "filename", taskInstanceId: "taskInstanceId" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
3228
3378
|
<!-- Design-time placeholder -->
|
|
3229
|
-
<div *ngIf="designMode" class="epistola-doc-panel">
|
|
3379
|
+
<div *ngIf="designMode" class="epistola-doc-panel" data-testid="epistola-document-design">
|
|
3230
3380
|
<div class="doc-header">
|
|
3231
3381
|
<span>{{ label || 'Document' }}</span>
|
|
3232
3382
|
<span class="design-tag">design mode</span>
|
|
@@ -3244,21 +3394,28 @@ class EpistolaDocumentComponent {
|
|
|
3244
3394
|
</div>
|
|
3245
3395
|
|
|
3246
3396
|
<!-- Button-only display -->
|
|
3247
|
-
<div *ngIf="!designMode && display === 'button'">
|
|
3397
|
+
<div *ngIf="!designMode && display === 'button'" data-testid="epistola-document-button-view">
|
|
3248
3398
|
<button
|
|
3249
3399
|
type="button"
|
|
3250
3400
|
class="btn btn-outline-primary"
|
|
3401
|
+
data-testid="epistola-document-download-button"
|
|
3251
3402
|
[disabled]="disabled || downloading"
|
|
3252
3403
|
(click)="download()"
|
|
3253
3404
|
>
|
|
3254
3405
|
<i class="mdi mdi-download mr-1"></i>
|
|
3255
3406
|
{{ downloading ? 'Downloading...' : label || 'Download PDF' }}
|
|
3256
3407
|
</button>
|
|
3257
|
-
<span *ngIf="error" class="text-danger ml-2">{{
|
|
3408
|
+
<span *ngIf="error" class="text-danger ml-2" data-testid="epistola-document-button-error">{{
|
|
3409
|
+
error
|
|
3410
|
+
}}</span>
|
|
3258
3411
|
</div>
|
|
3259
3412
|
|
|
3260
3413
|
<!-- Inline / both: panel with optional download icon -->
|
|
3261
|
-
<div
|
|
3414
|
+
<div
|
|
3415
|
+
*ngIf="!designMode && display !== 'button'"
|
|
3416
|
+
class="epistola-doc-panel"
|
|
3417
|
+
data-testid="epistola-document-inline-view"
|
|
3418
|
+
>
|
|
3262
3419
|
<div class="doc-header">
|
|
3263
3420
|
<span>{{ label || 'Document' }}</span>
|
|
3264
3421
|
<div class="doc-controls">
|
|
@@ -3266,6 +3423,7 @@ class EpistolaDocumentComponent {
|
|
|
3266
3423
|
*ngIf="display === 'both'"
|
|
3267
3424
|
type="button"
|
|
3268
3425
|
class="doc-icon-btn"
|
|
3426
|
+
data-testid="epistola-document-download-icon"
|
|
3269
3427
|
[disabled]="disabled || downloading"
|
|
3270
3428
|
(click)="download()"
|
|
3271
3429
|
[title]="downloading ? 'Downloading...' : 'Download'"
|
|
@@ -3275,6 +3433,7 @@ class EpistolaDocumentComponent {
|
|
|
3275
3433
|
<button
|
|
3276
3434
|
type="button"
|
|
3277
3435
|
class="doc-icon-btn"
|
|
3436
|
+
data-testid="epistola-document-refresh"
|
|
3278
3437
|
[disabled]="loading"
|
|
3279
3438
|
(click)="refresh()"
|
|
3280
3439
|
title="Refresh"
|
|
@@ -3284,8 +3443,14 @@ class EpistolaDocumentComponent {
|
|
|
3284
3443
|
</div>
|
|
3285
3444
|
</div>
|
|
3286
3445
|
<div class="doc-body">
|
|
3287
|
-
<div *ngIf="loading" class="doc-loading"
|
|
3288
|
-
|
|
3446
|
+
<div *ngIf="loading" class="doc-loading" data-testid="epistola-document-loading">
|
|
3447
|
+
Loading document...
|
|
3448
|
+
</div>
|
|
3449
|
+
<div
|
|
3450
|
+
*ngIf="error && !loading"
|
|
3451
|
+
class="doc-unavailable"
|
|
3452
|
+
data-testid="epistola-document-error"
|
|
3453
|
+
>
|
|
3289
3454
|
<i class="mdi mdi-information-outline"></i>
|
|
3290
3455
|
{{ error }}
|
|
3291
3456
|
</div>
|
|
@@ -3294,6 +3459,7 @@ class EpistolaDocumentComponent {
|
|
|
3294
3459
|
[data]="previewUrl"
|
|
3295
3460
|
type="application/pdf"
|
|
3296
3461
|
class="doc-pdf"
|
|
3462
|
+
data-testid="epistola-document-pdf"
|
|
3297
3463
|
>
|
|
3298
3464
|
PDF preview is not supported in this browser.
|
|
3299
3465
|
</object>
|
|
@@ -3305,7 +3471,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3305
3471
|
type: Component,
|
|
3306
3472
|
args: [{ standalone: true, imports: [CommonModule], selector: 'epistola-document-component', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
3307
3473
|
<!-- Design-time placeholder -->
|
|
3308
|
-
<div *ngIf="designMode" class="epistola-doc-panel">
|
|
3474
|
+
<div *ngIf="designMode" class="epistola-doc-panel" data-testid="epistola-document-design">
|
|
3309
3475
|
<div class="doc-header">
|
|
3310
3476
|
<span>{{ label || 'Document' }}</span>
|
|
3311
3477
|
<span class="design-tag">design mode</span>
|
|
@@ -3323,21 +3489,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3323
3489
|
</div>
|
|
3324
3490
|
|
|
3325
3491
|
<!-- Button-only display -->
|
|
3326
|
-
<div *ngIf="!designMode && display === 'button'">
|
|
3492
|
+
<div *ngIf="!designMode && display === 'button'" data-testid="epistola-document-button-view">
|
|
3327
3493
|
<button
|
|
3328
3494
|
type="button"
|
|
3329
3495
|
class="btn btn-outline-primary"
|
|
3496
|
+
data-testid="epistola-document-download-button"
|
|
3330
3497
|
[disabled]="disabled || downloading"
|
|
3331
3498
|
(click)="download()"
|
|
3332
3499
|
>
|
|
3333
3500
|
<i class="mdi mdi-download mr-1"></i>
|
|
3334
3501
|
{{ downloading ? 'Downloading...' : label || 'Download PDF' }}
|
|
3335
3502
|
</button>
|
|
3336
|
-
<span *ngIf="error" class="text-danger ml-2">{{
|
|
3503
|
+
<span *ngIf="error" class="text-danger ml-2" data-testid="epistola-document-button-error">{{
|
|
3504
|
+
error
|
|
3505
|
+
}}</span>
|
|
3337
3506
|
</div>
|
|
3338
3507
|
|
|
3339
3508
|
<!-- Inline / both: panel with optional download icon -->
|
|
3340
|
-
<div
|
|
3509
|
+
<div
|
|
3510
|
+
*ngIf="!designMode && display !== 'button'"
|
|
3511
|
+
class="epistola-doc-panel"
|
|
3512
|
+
data-testid="epistola-document-inline-view"
|
|
3513
|
+
>
|
|
3341
3514
|
<div class="doc-header">
|
|
3342
3515
|
<span>{{ label || 'Document' }}</span>
|
|
3343
3516
|
<div class="doc-controls">
|
|
@@ -3345,6 +3518,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3345
3518
|
*ngIf="display === 'both'"
|
|
3346
3519
|
type="button"
|
|
3347
3520
|
class="doc-icon-btn"
|
|
3521
|
+
data-testid="epistola-document-download-icon"
|
|
3348
3522
|
[disabled]="disabled || downloading"
|
|
3349
3523
|
(click)="download()"
|
|
3350
3524
|
[title]="downloading ? 'Downloading...' : 'Download'"
|
|
@@ -3354,6 +3528,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3354
3528
|
<button
|
|
3355
3529
|
type="button"
|
|
3356
3530
|
class="doc-icon-btn"
|
|
3531
|
+
data-testid="epistola-document-refresh"
|
|
3357
3532
|
[disabled]="loading"
|
|
3358
3533
|
(click)="refresh()"
|
|
3359
3534
|
title="Refresh"
|
|
@@ -3363,8 +3538,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3363
3538
|
</div>
|
|
3364
3539
|
</div>
|
|
3365
3540
|
<div class="doc-body">
|
|
3366
|
-
<div *ngIf="loading" class="doc-loading"
|
|
3367
|
-
|
|
3541
|
+
<div *ngIf="loading" class="doc-loading" data-testid="epistola-document-loading">
|
|
3542
|
+
Loading document...
|
|
3543
|
+
</div>
|
|
3544
|
+
<div
|
|
3545
|
+
*ngIf="error && !loading"
|
|
3546
|
+
class="doc-unavailable"
|
|
3547
|
+
data-testid="epistola-document-error"
|
|
3548
|
+
>
|
|
3368
3549
|
<i class="mdi mdi-information-outline"></i>
|
|
3369
3550
|
{{ error }}
|
|
3370
3551
|
</div>
|
|
@@ -3373,6 +3554,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3373
3554
|
[data]="previewUrl"
|
|
3374
3555
|
type="application/pdf"
|
|
3375
3556
|
class="doc-pdf"
|
|
3557
|
+
data-testid="epistola-document-pdf"
|
|
3376
3558
|
>
|
|
3377
3559
|
PDF preview is not supported in this browser.
|
|
3378
3560
|
</object>
|
|
@@ -3579,39 +3761,63 @@ class EpistolaRetryFormComponent {
|
|
|
3579
3761
|
}
|
|
3580
3762
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaRetryFormComponent, deps: [{ token: EpistolaPluginService }, { token: i0.ChangeDetectorRef }, { token: i2$3.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
|
|
3581
3763
|
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", taskInstanceId: "taskInstanceId" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
3582
|
-
<div *ngIf="loading" class="epistola-retry-loading"
|
|
3583
|
-
|
|
3764
|
+
<div *ngIf="loading" class="epistola-retry-loading" data-testid="epistola-retry-loading">
|
|
3765
|
+
Loading form...
|
|
3766
|
+
</div>
|
|
3767
|
+
<div *ngIf="error" class="epistola-retry-error" data-testid="epistola-retry-error">
|
|
3768
|
+
{{ error }}
|
|
3769
|
+
</div>
|
|
3584
3770
|
<div
|
|
3585
3771
|
*ngIf="formDefinition && !loading"
|
|
3586
3772
|
class="epistola-retry-container"
|
|
3587
3773
|
[class.preview-expanded]="previewExpanded"
|
|
3774
|
+
data-testid="epistola-retry-container"
|
|
3588
3775
|
>
|
|
3589
|
-
<div class="epistola-retry-form" [hidden]="previewExpanded">
|
|
3776
|
+
<div class="epistola-retry-form" [hidden]="previewExpanded" data-testid="epistola-retry-form">
|
|
3590
3777
|
<formio
|
|
3591
3778
|
[form]="formDefinition"
|
|
3592
3779
|
[submission]="submission"
|
|
3593
3780
|
(change)="onFormChange($event)"
|
|
3594
3781
|
[options]="formOptions"
|
|
3782
|
+
data-testid="epistola-retry-formio"
|
|
3595
3783
|
></formio>
|
|
3596
3784
|
</div>
|
|
3597
|
-
<div class="epistola-retry-preview">
|
|
3598
|
-
<div class="preview-header">
|
|
3785
|
+
<div class="epistola-retry-preview" data-testid="epistola-retry-preview">
|
|
3786
|
+
<div class="preview-header" data-testid="epistola-retry-preview-header">
|
|
3599
3787
|
<span>Preview</span>
|
|
3600
|
-
<button
|
|
3788
|
+
<button
|
|
3789
|
+
type="button"
|
|
3790
|
+
class="preview-toggle"
|
|
3791
|
+
(click)="togglePreview()"
|
|
3792
|
+
data-testid="epistola-retry-preview-toggle"
|
|
3793
|
+
>
|
|
3601
3794
|
{{ previewExpanded ? 'Show form' : 'Expand' }}
|
|
3602
3795
|
</button>
|
|
3603
3796
|
</div>
|
|
3604
|
-
<div
|
|
3797
|
+
<div
|
|
3798
|
+
*ngIf="previewLoading"
|
|
3799
|
+
class="preview-loading"
|
|
3800
|
+
data-testid="epistola-retry-preview-loading"
|
|
3801
|
+
>
|
|
3802
|
+
Generating preview...
|
|
3803
|
+
</div>
|
|
3605
3804
|
<object
|
|
3606
3805
|
*ngIf="previewUrl && !previewLoading"
|
|
3607
3806
|
[data]="previewUrl"
|
|
3608
3807
|
type="application/pdf"
|
|
3609
3808
|
class="preview-pdf"
|
|
3809
|
+
data-testid="epistola-retry-preview-pdf"
|
|
3610
3810
|
>
|
|
3611
3811
|
PDF preview not supported in this browser.
|
|
3612
3812
|
</object>
|
|
3613
|
-
<div *ngIf="previewError" class="preview-error"
|
|
3614
|
-
|
|
3813
|
+
<div *ngIf="previewError" class="preview-error" data-testid="epistola-retry-preview-error">
|
|
3814
|
+
{{ previewError }}
|
|
3815
|
+
</div>
|
|
3816
|
+
<div
|
|
3817
|
+
*ngIf="!previewUrl && !previewLoading && !previewError"
|
|
3818
|
+
class="preview-empty"
|
|
3819
|
+
data-testid="epistola-retry-preview-empty"
|
|
3820
|
+
>
|
|
3615
3821
|
Edit fields to see a preview
|
|
3616
3822
|
</div>
|
|
3617
3823
|
</div>
|
|
@@ -3621,39 +3827,63 @@ class EpistolaRetryFormComponent {
|
|
|
3621
3827
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaRetryFormComponent, decorators: [{
|
|
3622
3828
|
type: Component,
|
|
3623
3829
|
args: [{ standalone: true, imports: [CommonModule, FormioModule], selector: 'epistola-retry-form-component', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
3624
|
-
<div *ngIf="loading" class="epistola-retry-loading"
|
|
3625
|
-
|
|
3830
|
+
<div *ngIf="loading" class="epistola-retry-loading" data-testid="epistola-retry-loading">
|
|
3831
|
+
Loading form...
|
|
3832
|
+
</div>
|
|
3833
|
+
<div *ngIf="error" class="epistola-retry-error" data-testid="epistola-retry-error">
|
|
3834
|
+
{{ error }}
|
|
3835
|
+
</div>
|
|
3626
3836
|
<div
|
|
3627
3837
|
*ngIf="formDefinition && !loading"
|
|
3628
3838
|
class="epistola-retry-container"
|
|
3629
3839
|
[class.preview-expanded]="previewExpanded"
|
|
3840
|
+
data-testid="epistola-retry-container"
|
|
3630
3841
|
>
|
|
3631
|
-
<div class="epistola-retry-form" [hidden]="previewExpanded">
|
|
3842
|
+
<div class="epistola-retry-form" [hidden]="previewExpanded" data-testid="epistola-retry-form">
|
|
3632
3843
|
<formio
|
|
3633
3844
|
[form]="formDefinition"
|
|
3634
3845
|
[submission]="submission"
|
|
3635
3846
|
(change)="onFormChange($event)"
|
|
3636
3847
|
[options]="formOptions"
|
|
3848
|
+
data-testid="epistola-retry-formio"
|
|
3637
3849
|
></formio>
|
|
3638
3850
|
</div>
|
|
3639
|
-
<div class="epistola-retry-preview">
|
|
3640
|
-
<div class="preview-header">
|
|
3851
|
+
<div class="epistola-retry-preview" data-testid="epistola-retry-preview">
|
|
3852
|
+
<div class="preview-header" data-testid="epistola-retry-preview-header">
|
|
3641
3853
|
<span>Preview</span>
|
|
3642
|
-
<button
|
|
3854
|
+
<button
|
|
3855
|
+
type="button"
|
|
3856
|
+
class="preview-toggle"
|
|
3857
|
+
(click)="togglePreview()"
|
|
3858
|
+
data-testid="epistola-retry-preview-toggle"
|
|
3859
|
+
>
|
|
3643
3860
|
{{ previewExpanded ? 'Show form' : 'Expand' }}
|
|
3644
3861
|
</button>
|
|
3645
3862
|
</div>
|
|
3646
|
-
<div
|
|
3863
|
+
<div
|
|
3864
|
+
*ngIf="previewLoading"
|
|
3865
|
+
class="preview-loading"
|
|
3866
|
+
data-testid="epistola-retry-preview-loading"
|
|
3867
|
+
>
|
|
3868
|
+
Generating preview...
|
|
3869
|
+
</div>
|
|
3647
3870
|
<object
|
|
3648
3871
|
*ngIf="previewUrl && !previewLoading"
|
|
3649
3872
|
[data]="previewUrl"
|
|
3650
3873
|
type="application/pdf"
|
|
3651
3874
|
class="preview-pdf"
|
|
3875
|
+
data-testid="epistola-retry-preview-pdf"
|
|
3652
3876
|
>
|
|
3653
3877
|
PDF preview not supported in this browser.
|
|
3654
3878
|
</object>
|
|
3655
|
-
<div *ngIf="previewError" class="preview-error"
|
|
3656
|
-
|
|
3879
|
+
<div *ngIf="previewError" class="preview-error" data-testid="epistola-retry-preview-error">
|
|
3880
|
+
{{ previewError }}
|
|
3881
|
+
</div>
|
|
3882
|
+
<div
|
|
3883
|
+
*ngIf="!previewUrl && !previewLoading && !previewError"
|
|
3884
|
+
class="preview-empty"
|
|
3885
|
+
data-testid="epistola-retry-preview-empty"
|
|
3886
|
+
>
|
|
3657
3887
|
Edit fields to see a preview
|
|
3658
3888
|
</div>
|
|
3659
3889
|
</div>
|
|
@@ -3918,7 +4148,7 @@ class EpistolaDocumentPreviewComponent {
|
|
|
3918
4148
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaDocumentPreviewComponent, deps: [{ token: EpistolaPluginService }, { token: i2$3.DomSanitizer }, { token: i3.FormIoStateService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3919
4149
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaDocumentPreviewComponent, isStandalone: true, selector: "epistola-document-preview-component", inputs: { value: "value", disabled: "disabled", label: "label", processDefinitionKey: "processDefinitionKey", sourceActivityId: "sourceActivityId", overrideMapping: "overrideMapping", taskInstanceId: "taskInstanceId", inputOverrides: "inputOverrides", requestOverrides: "requestOverrides", autoRefresh: "autoRefresh", setAutoRefresh: "setAutoRefresh" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
3920
4150
|
<!-- Design-time view: show configuration summary when no runtime context -->
|
|
3921
|
-
<div *ngIf="designMode" class="epistola-preview-panel">
|
|
4151
|
+
<div *ngIf="designMode" class="epistola-preview-panel" data-testid="epistola-preview-design">
|
|
3922
4152
|
<div class="preview-header">
|
|
3923
4153
|
<span>{{ label || 'Document Preview' }}</span>
|
|
3924
4154
|
</div>
|
|
@@ -3940,7 +4170,7 @@ class EpistolaDocumentPreviewComponent {
|
|
|
3940
4170
|
</div>
|
|
3941
4171
|
|
|
3942
4172
|
<!-- Runtime view: actual preview -->
|
|
3943
|
-
<div *ngIf="!designMode" class="epistola-preview-panel">
|
|
4173
|
+
<div *ngIf="!designMode" class="epistola-preview-panel" data-testid="epistola-preview-runtime">
|
|
3944
4174
|
<div class="preview-header">
|
|
3945
4175
|
<span>{{ label || 'Document Preview' }}</span>
|
|
3946
4176
|
<div class="preview-controls">
|
|
@@ -3951,20 +4181,33 @@ class EpistolaDocumentPreviewComponent {
|
|
|
3951
4181
|
>
|
|
3952
4182
|
<input
|
|
3953
4183
|
type="checkbox"
|
|
4184
|
+
data-testid="epistola-preview-autorefresh"
|
|
3954
4185
|
[checked]="autoRefreshEnabled"
|
|
3955
4186
|
(change)="onToggleAutoRefresh($event)"
|
|
3956
4187
|
/>
|
|
3957
4188
|
Auto-refresh
|
|
3958
4189
|
</label>
|
|
3959
|
-
<button
|
|
4190
|
+
<button
|
|
4191
|
+
type="button"
|
|
4192
|
+
class="preview-refresh"
|
|
4193
|
+
data-testid="epistola-preview-refresh"
|
|
4194
|
+
[disabled]="loading"
|
|
4195
|
+
(click)="refresh()"
|
|
4196
|
+
>
|
|
3960
4197
|
<i class="mdi mdi-refresh mr-1"></i>
|
|
3961
4198
|
{{ loading ? 'Generating...' : 'Refresh' }}
|
|
3962
4199
|
</button>
|
|
3963
4200
|
</div>
|
|
3964
4201
|
</div>
|
|
3965
4202
|
<div class="preview-body">
|
|
3966
|
-
<div *ngIf="loading" class="preview-loading"
|
|
3967
|
-
|
|
4203
|
+
<div *ngIf="loading" class="preview-loading" data-testid="epistola-preview-loading">
|
|
4204
|
+
Generating preview...
|
|
4205
|
+
</div>
|
|
4206
|
+
<div
|
|
4207
|
+
*ngIf="error && !loading"
|
|
4208
|
+
class="preview-unavailable"
|
|
4209
|
+
data-testid="epistola-preview-error"
|
|
4210
|
+
>
|
|
3968
4211
|
<i class="mdi mdi-information-outline"></i>
|
|
3969
4212
|
{{ error }}
|
|
3970
4213
|
</div>
|
|
@@ -3973,6 +4216,7 @@ class EpistolaDocumentPreviewComponent {
|
|
|
3973
4216
|
[data]="previewUrl"
|
|
3974
4217
|
type="application/pdf"
|
|
3975
4218
|
class="preview-pdf"
|
|
4219
|
+
data-testid="epistola-preview-pdf"
|
|
3976
4220
|
>
|
|
3977
4221
|
PDF preview is not supported in this browser.
|
|
3978
4222
|
</object>
|
|
@@ -3984,7 +4228,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3984
4228
|
type: Component,
|
|
3985
4229
|
args: [{ standalone: true, imports: [CommonModule], selector: 'epistola-document-preview-component', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
3986
4230
|
<!-- Design-time view: show configuration summary when no runtime context -->
|
|
3987
|
-
<div *ngIf="designMode" class="epistola-preview-panel">
|
|
4231
|
+
<div *ngIf="designMode" class="epistola-preview-panel" data-testid="epistola-preview-design">
|
|
3988
4232
|
<div class="preview-header">
|
|
3989
4233
|
<span>{{ label || 'Document Preview' }}</span>
|
|
3990
4234
|
</div>
|
|
@@ -4006,7 +4250,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
4006
4250
|
</div>
|
|
4007
4251
|
|
|
4008
4252
|
<!-- Runtime view: actual preview -->
|
|
4009
|
-
<div *ngIf="!designMode" class="epistola-preview-panel">
|
|
4253
|
+
<div *ngIf="!designMode" class="epistola-preview-panel" data-testid="epistola-preview-runtime">
|
|
4010
4254
|
<div class="preview-header">
|
|
4011
4255
|
<span>{{ label || 'Document Preview' }}</span>
|
|
4012
4256
|
<div class="preview-controls">
|
|
@@ -4017,20 +4261,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
4017
4261
|
>
|
|
4018
4262
|
<input
|
|
4019
4263
|
type="checkbox"
|
|
4264
|
+
data-testid="epistola-preview-autorefresh"
|
|
4020
4265
|
[checked]="autoRefreshEnabled"
|
|
4021
4266
|
(change)="onToggleAutoRefresh($event)"
|
|
4022
4267
|
/>
|
|
4023
4268
|
Auto-refresh
|
|
4024
4269
|
</label>
|
|
4025
|
-
<button
|
|
4270
|
+
<button
|
|
4271
|
+
type="button"
|
|
4272
|
+
class="preview-refresh"
|
|
4273
|
+
data-testid="epistola-preview-refresh"
|
|
4274
|
+
[disabled]="loading"
|
|
4275
|
+
(click)="refresh()"
|
|
4276
|
+
>
|
|
4026
4277
|
<i class="mdi mdi-refresh mr-1"></i>
|
|
4027
4278
|
{{ loading ? 'Generating...' : 'Refresh' }}
|
|
4028
4279
|
</button>
|
|
4029
4280
|
</div>
|
|
4030
4281
|
</div>
|
|
4031
4282
|
<div class="preview-body">
|
|
4032
|
-
<div *ngIf="loading" class="preview-loading"
|
|
4033
|
-
|
|
4283
|
+
<div *ngIf="loading" class="preview-loading" data-testid="epistola-preview-loading">
|
|
4284
|
+
Generating preview...
|
|
4285
|
+
</div>
|
|
4286
|
+
<div
|
|
4287
|
+
*ngIf="error && !loading"
|
|
4288
|
+
class="preview-unavailable"
|
|
4289
|
+
data-testid="epistola-preview-error"
|
|
4290
|
+
>
|
|
4034
4291
|
<i class="mdi mdi-information-outline"></i>
|
|
4035
4292
|
{{ error }}
|
|
4036
4293
|
</div>
|
|
@@ -4039,6 +4296,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
4039
4296
|
[data]="previewUrl"
|
|
4040
4297
|
type="application/pdf"
|
|
4041
4298
|
class="preview-pdf"
|
|
4299
|
+
data-testid="epistola-preview-pdf"
|
|
4042
4300
|
>
|
|
4043
4301
|
PDF preview is not supported in this browser.
|
|
4044
4302
|
</object>
|
|
@@ -4501,11 +4759,11 @@ class EpistolaAdminPageComponent {
|
|
|
4501
4759
|
});
|
|
4502
4760
|
}
|
|
4503
4761
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminPageComponent, deps: [{ token: EpistolaAdminService }, { token: i2$4.ActivatedRoute }, { token: i2$4.Router }], target: i0.ɵɵFactoryTarget.Component });
|
|
4504
|
-
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: tabs (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 <ng-template #configurationsHeading>\n {{ 'epistolaAdminConfigurations' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" type=\"gray\" class=\"ms-1\">{{ cards.length }}</cds-tag>\n </ng-template>\n\n <ng-template #validationsHeading>\n {{ 'epistolaAdminValidations' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" [type]=\"validationViolations.length > 0 ? 'red' : 'gray'\" class=\"ms-1\">\n {{ validationViolations.length }}\n </cds-tag>\n </ng-template>\n\n <ng-template #changelogHeading>\n {{ 'epistolaAdminChangelog' | pluginTranslate: 'epistola' | async }}\n </ng-template>\n\n <!-- TEMPORARY (removed in 1.0.0): forms missing the task-id carrier -->\n <ng-template #formsHeading>\n {{ 'epistolaAdminForms' | pluginTranslate: 'epistola' | async }}\n <cds-tag\n *ngIf=\"formsScanLoaded\"\n size=\"sm\"\n [type]=\"formsAttentionCount > 0 ? 'red' : 'gray'\"\n class=\"ms-1\"\n >{{ formsAttentionCount }}</cds-tag\n >\n </ng-template>\n\n <cds-tabs [cacheActive]=\"true\" type=\"contained\">\n <cds-tab\n [heading]=\"configurationsHeading\"\n [active]=\"overviewTab === 'configurations'\"\n (selected)=\"setOverviewTab('configurations')\"\n >\n <div *ngIf=\"loading\" class=\"text-muted mt-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!loading && cards.length === 0\" class=\"text-muted mt-3\">\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 </cds-tab>\n\n <cds-tab\n [heading]=\"validationsHeading\"\n [active]=\"overviewTab === 'validations'\"\n (selected)=\"setOverviewTab('validations')\"\n >\n <div class=\"text-muted small mt-3\">\n <div>\n {{ 'epistolaAdminValidationLastChecked' | pluginTranslate: 'epistola' | async }}:\n <ng-container *ngIf=\"validationReport?.lastCheckedAt; else notYetRun\">\n {{ validationReport?.lastCheckedAt | date: 'medium' }}\n </ng-container>\n <ng-template #notYetRun>\n {{ 'epistolaAdminValidationNotYetRun' | pluginTranslate: 'epistola' | async }}\n </ng-template>\n \u00B7 {{ 'epistolaAdminValidationAutoRefresh' | pluginTranslate: 'epistola' | async }}\n {{ refreshIntervalMinutes }} min.\n </div>\n <div>\n {{ 'epistolaAdminValidationLatestVersionNote' | pluginTranslate: 'epistola' | async }}\n </div>\n </div>\n\n <div *ngIf=\"validationViolations.length === 0\" class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminNoValidations' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"validationViolations.length > 0\" class=\"mt-3\">\n <p class=\"text-muted mb-3\">\n {{ 'epistolaAdminValidationWarningBody' | pluginTranslate: 'epistola' | async }}\n </p>\n <table class=\"table table-striped\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminProcess' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminActivity' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminValidationCode' | pluginTranslate: 'epistola' | async }}</th>\n <th>\n {{ 'epistolaAdminValidationMessage' | pluginTranslate: 'epistola' | async }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let v of validationViolations\">\n <td>\n {{ v.processDefinitionName || v.processDefinitionKey }}\n <div *ngIf=\"v.processDefinitionName\" class=\"text-muted small\">\n <code>{{ v.processDefinitionKey }}</code>\n </div>\n </td>\n <td>\n <code>{{ v.activityId }}</code>\n </td>\n <td>\n <cds-tag size=\"sm\" type=\"red\">{{ v.code }}</cds-tag>\n </td>\n <td>{{ v.message }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </cds-tab>\n\n <cds-tab\n [heading]=\"changelogHeading\"\n [active]=\"overviewTab === 'changelog'\"\n (selected)=\"setOverviewTab('changelog')\"\n >\n <div class=\"d-flex align-items-center mt-3 mb-3\">\n <span class=\"text-muted me-2\">{{\n 'epistolaAdminRunningVersion' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" type=\"blue\">v{{ pluginVersion || '\u2014' }}</cds-tag>\n </div>\n\n <div *ngIf=\"changelogLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!changelogLoading && changelog && changelog.length === 0\"\n class=\"text-muted mb-3\"\n >\n {{ 'epistolaAdminNoChangelog' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!changelogLoading && changelog && changelog.length > 0\"\n class=\"epistola-changelog\"\n >\n <section *ngFor=\"let release of changelog\" class=\"changelog-release\">\n <div class=\"changelog-release__header\">\n <cds-tag size=\"sm\" type=\"purple\">{{ release.version }}</cds-tag>\n <span *ngIf=\"release.date\" class=\"text-muted ms-2\">{{ release.date }}</span>\n </div>\n <div *ngFor=\"let section of release.sections\" class=\"changelog-section\">\n <h6 class=\"changelog-section__title\">{{ section.title }}</h6>\n <ul class=\"changelog-section__items\">\n <li *ngFor=\"let item of section.items\">{{ item }}</li>\n </ul>\n </div>\n </section>\n </div>\n </cds-tab>\n\n <!-- TEMPORARY (removed in 1.0.0): forms missing the task-id carrier -->\n <cds-tab\n [heading]=\"formsHeading\"\n [active]=\"overviewTab === 'forms'\"\n (selected)=\"setOverviewTab('forms')\"\n >\n <p class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminFormsIntro' | pluginTranslate: 'epistola' | async }}\n </p>\n\n <div *ngIf=\"formIssuesLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!formIssuesLoading && formIssues && formIssues.length === 0\"\n class=\"text-muted mb-3\"\n >\n {{ 'epistolaAdminNoFormIssues' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!formIssuesLoading && formIssues && formIssues.length > 0\">\n <div class=\"mb-2\">\n <button\n class=\"btn btn-sm btn-primary\"\n (click)=\"repairAllForms()\"\n [disabled]=\"repairingAll\"\n >\n {{\n (repairingAll ? 'epistolaAdminRepairing' : 'epistolaAdminRepairAll')\n | pluginTranslate: 'epistola'\n | async\n }}\n </button>\n <span\n *ngIf=\"formFeedback && formFeedback.formId === 'all'\"\n class=\"small ms-2\"\n [class.text-success]=\"formFeedback.type === 'success'\"\n [class.text-danger]=\"formFeedback.type === 'error'\"\n >{{ formFeedback.message }}</span\n >\n </div>\n\n <table class=\"table table-striped\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminFormName' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminFormMissing' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let issue of formIssues\">\n <td>\n <code>{{ issue.name }}</code>\n <cds-tag\n *ngIf=\"issue.readOnly\"\n size=\"sm\"\n type=\"warm-gray\"\n class=\"ms-1\"\n [title]=\"'epistolaAdminFormReadOnlyHint' | pluginTranslate: 'epistola' | async\"\n >{{\n 'epistolaAdminFormReadOnly' | pluginTranslate: 'epistola' | async\n }}</cds-tag\n >\n </td>\n <td>{{ issue.missingComponents }}</td>\n <td class=\"text-end\">\n <button\n class=\"btn btn-sm btn-outline-primary\"\n (click)=\"repairForm(issue)\"\n [disabled]=\"isRepairingForm(issue)\"\n [title]=\"'epistolaAdminRepairTooltip' | pluginTranslate: 'epistola' | async\"\n >\n {{\n (isRepairingForm(issue) ? 'epistolaAdminRepairing' : 'epistolaAdminRepair')\n | pluginTranslate: 'epistola'\n | async\n }}\n </button>\n <div\n *ngIf=\"formFeedback && formFeedback.formId === issue.formId\"\n class=\"small mt-1\"\n [class.text-success]=\"formFeedback.type === 'success'\"\n [class.text-danger]=\"formFeedback.type === 'error'\"\n >\n {{ formFeedback.message }}\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- TEMPORARY: forms still using the legacy override-mapping object format -->\n <hr class=\"my-4\" />\n <h5 class=\"mb-1\">\n {{ 'epistolaAdminLegacyOverrideTitle' | pluginTranslate: 'epistola' | async }}\n </h5>\n <p class=\"text-muted mb-3\">\n {{ 'epistolaAdminLegacyOverrideIntro' | pluginTranslate: 'epistola' | async }}\n </p>\n\n <div *ngIf=\"legacyOverrideLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!legacyOverrideLoading && legacyOverrideForms && legacyOverrideForms.length === 0\"\n class=\"text-muted mb-3\"\n >\n {{ 'epistolaAdminNoLegacyOverride' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table\n *ngIf=\"!legacyOverrideLoading && legacyOverrideForms && legacyOverrideForms.length > 0\"\n class=\"table table-striped\"\n >\n <thead>\n <tr>\n <th>{{ 'epistolaAdminFormName' | pluginTranslate: 'epistola' | async }}</th>\n <th>\n {{ 'epistolaAdminLegacyOverrideComponents' | pluginTranslate: 'epistola' | async }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let form of legacyOverrideForms\">\n <td>\n <code>{{ form.name }}</code>\n <cds-tag\n *ngIf=\"form.readOnly\"\n size=\"sm\"\n type=\"warm-gray\"\n class=\"ms-1\"\n [title]=\"'epistolaAdminFormReadOnlyHint' | pluginTranslate: 'epistola' | async\"\n >{{ 'epistolaAdminFormReadOnly' | pluginTranslate: 'epistola' | async }}</cds-tag\n >\n </td>\n <td>{{ form.legacyComponents }}</td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n </cds-tabs>\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 <ng-template #catalogsHeading>\n {{ 'epistolaAdminCatalogs' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" type=\"gray\" class=\"ms-1\">{{ catalogs.length }}</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>{{ 'epistolaAdminStatus' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminRequestId' | pluginTranslate: 'epistola' | async }}</th>\n <th></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 <cds-tag\n *ngIf=\"job.status === 'UNWIRED'; else waitingTag\"\n size=\"sm\"\n type=\"red\"\n [title]=\"'epistolaAdminUnwiredTooltip' | pluginTranslate: 'epistola' | async\"\n >\n {{ 'epistolaAdminStatusUnwired' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n <ng-template #waitingTag>\n <cds-tag size=\"sm\" type=\"blue\">\n {{ 'epistolaAdminStatusWaiting' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n </ng-template>\n </td>\n <td>\n <code *ngIf=\"job.status !== 'UNWIRED'\">{{ job.requestId }}</code>\n <span *ngIf=\"job.status === 'UNWIRED'\" class=\"text-muted\">\u2014</span>\n </td>\n <td class=\"text-end\">\n <button\n *ngIf=\"job.status !== 'UNWIRED'\"\n class=\"btn btn-sm btn-outline-primary\"\n (click)=\"reconcilePending(job)\"\n [disabled]=\"isReconciling(job)\"\n [title]=\"'epistolaAdminReconcileTooltip' | pluginTranslate: 'epistola' | async\"\n >\n <span *ngIf=\"!isReconciling(job)\">\n {{ 'epistolaAdminReconcile' | pluginTranslate: 'epistola' | async }}\n </span>\n <span *ngIf=\"isReconciling(job)\">\n {{ 'epistolaAdminReconciling' | pluginTranslate: 'epistola' | async }}\n </span>\n </button>\n <span\n *ngIf=\"job.status === 'UNWIRED'\"\n class=\"text-muted small\"\n [title]=\"'epistolaAdminUnwiredTooltip' | pluginTranslate: 'epistola' | async\"\n >\n {{ 'epistolaAdminUnwiredHint' | pluginTranslate: 'epistola' | async }}\n </span>\n <div\n *ngIf=\"reconcileFeedback && reconcileFeedback.executionId === job.executionId\"\n class=\"reconcile-feedback small mt-1\"\n [class.text-success]=\"reconcileFeedback.type === 'success'\"\n [class.text-warning]=\"reconcileFeedback.type === 'pending'\"\n [class.text-danger]=\"reconcileFeedback.type === 'error'\"\n >\n {{ reconcileFeedback.message }}\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n\n <cds-tab\n [heading]=\"catalogsHeading\"\n [active]=\"activeTab === 'catalogs'\"\n (selected)=\"setActiveTab('catalogs')\"\n >\n <p class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminCatalogsIntro' | pluginTranslate: 'epistola' | async }}\n </p>\n\n <div *ngIf=\"catalogsLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!catalogsLoading && catalogs.length === 0\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminNoCatalogs' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table *ngIf=\"!catalogsLoading && catalogs.length > 0\" class=\"table table-striped\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminCatalogSlug' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminCatalogVersion' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminCatalogStatus' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let catalog of catalogs\">\n <td>\n <code>{{ catalog.slug }}</code>\n </td>\n <td>{{ catalog.version }}</td>\n <td>\n <cds-tag *ngIf=\"catalog.status === 'IN_EPISTOLA'\" size=\"sm\" type=\"green\">\n {{ 'epistolaAdminCatalogInEpistola' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n <cds-tag *ngIf=\"catalog.status === 'NOT_IN_EPISTOLA'\" size=\"sm\" type=\"red\">\n {{ 'epistolaAdminCatalogNotInEpistola' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n <cds-tag *ngIf=\"catalog.status === 'UNKNOWN'\" size=\"sm\" type=\"gray\">\n {{ 'epistolaAdminCatalogStatusUnknown' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n </td>\n <td class=\"text-end\">\n <button\n class=\"btn btn-sm btn-outline-primary\"\n (click)=\"redeployCatalog(catalog)\"\n [disabled]=\"isRedeploying(catalog)\"\n [title]=\"'epistolaAdminRedeployTooltip' | pluginTranslate: 'epistola' | async\"\n >\n <span *ngIf=\"!isRedeploying(catalog)\">\n {{ 'epistolaAdminRedeploy' | pluginTranslate: 'epistola' | async }}\n </span>\n <span *ngIf=\"isRedeploying(catalog)\">\n {{ 'epistolaAdminRedeploying' | pluginTranslate: 'epistola' | async }}\n </span>\n </button>\n <div\n *ngIf=\"catalogFeedback && catalogFeedback.slug === catalog.slug\"\n class=\"reconcile-feedback small mt-1\"\n [class.text-success]=\"catalogFeedback.type === 'success'\"\n [class.text-danger]=\"catalogFeedback.type === 'error'\"\n >\n {{ catalogFeedback.message }}\n </div>\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}.epistola-admin .epistola-changelog{max-height:60vh;overflow:auto;padding:.5rem 1rem;border:1px solid #dee2e6;border-radius:4px}.epistola-admin .epistola-changelog .changelog-release{padding:.75rem 0}.epistola-admin .epistola-changelog .changelog-release+.changelog-release{border-top:1px solid #eee}.epistola-admin .epistola-changelog .changelog-release__header{display:flex;align-items:center;margin-bottom:.5rem}.epistola-admin .epistola-changelog .changelog-section{margin:.25rem 0 .75rem}.epistola-admin .epistola-changelog .changelog-section__title{font-size:.8125rem;font-weight:600;text-transform:uppercase;letter-spacing:.02em;color:#6f6f6f;margin-bottom:.25rem}.epistola-admin .epistola-changelog .changelog-section__items{margin:0;padding-left:1.25rem;font-size:.8125rem;line-height:1.5}.epistola-admin .epistola-changelog .changelog-section__items li{margin-bottom:.25rem;word-break:break-word}\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: "pipe", type: i1$1.DatePipe, name: "date" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$4.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"] }] });
|
|
4762
|
+
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\" data-testid=\"epistola-admin\">\n <!-- Overview: tabs (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\n class=\"btn btn-outline-primary btn-sm\"\n data-testid=\"epistola-admin-refresh\"\n (click)=\"refresh()\"\n [disabled]=\"loading\"\n >\n {{ 'epistolaAdminRefresh' | pluginTranslate: 'epistola' | async }}\n </button>\n </div>\n\n <ng-template #configurationsHeading>\n {{ 'epistolaAdminConfigurations' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" type=\"gray\" class=\"ms-1\">{{ cards.length }}</cds-tag>\n </ng-template>\n\n <ng-template #validationsHeading>\n {{ 'epistolaAdminValidations' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" [type]=\"validationViolations.length > 0 ? 'red' : 'gray'\" class=\"ms-1\">\n {{ validationViolations.length }}\n </cds-tag>\n </ng-template>\n\n <ng-template #changelogHeading>\n {{ 'epistolaAdminChangelog' | pluginTranslate: 'epistola' | async }}\n </ng-template>\n\n <!-- TEMPORARY (removed in 1.0.0): forms missing the task-id carrier -->\n <ng-template #formsHeading>\n {{ 'epistolaAdminForms' | pluginTranslate: 'epistola' | async }}\n <cds-tag\n *ngIf=\"formsScanLoaded\"\n size=\"sm\"\n [type]=\"formsAttentionCount > 0 ? 'red' : 'gray'\"\n class=\"ms-1\"\n >{{ formsAttentionCount }}</cds-tag\n >\n </ng-template>\n\n <cds-tabs [cacheActive]=\"true\" type=\"contained\" data-testid=\"epistola-admin-overview-tabs\">\n <cds-tab\n [heading]=\"configurationsHeading\"\n data-testid=\"epistola-admin-tab-configurations\"\n [active]=\"overviewTab === 'configurations'\"\n (selected)=\"setOverviewTab('configurations')\"\n >\n <div\n *ngIf=\"loading\"\n class=\"text-muted mt-3\"\n data-testid=\"epistola-admin-configurations-loading\"\n >\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!loading && cards.length === 0\"\n class=\"text-muted mt-3\"\n data-testid=\"epistola-admin-configurations-empty\"\n >\n {{ 'epistolaAdminNoConfigurations' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!loading && cards.length > 0\"\n class=\"card-grid\"\n data-testid=\"epistola-admin-card-grid\"\n >\n <div\n *ngFor=\"let card of cards\"\n class=\"config-card\"\n [attr.data-testid]=\"'epistola-admin-config-card-' + card.tenantId\"\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 </cds-tab>\n\n <cds-tab\n [heading]=\"validationsHeading\"\n data-testid=\"epistola-admin-tab-validations\"\n [active]=\"overviewTab === 'validations'\"\n (selected)=\"setOverviewTab('validations')\"\n >\n <div class=\"text-muted small mt-3\">\n <div>\n {{ 'epistolaAdminValidationLastChecked' | pluginTranslate: 'epistola' | async }}:\n <ng-container *ngIf=\"validationReport?.lastCheckedAt; else notYetRun\">\n {{ validationReport?.lastCheckedAt | date: 'medium' }}\n </ng-container>\n <ng-template #notYetRun>\n {{ 'epistolaAdminValidationNotYetRun' | pluginTranslate: 'epistola' | async }}\n </ng-template>\n \u00B7 {{ 'epistolaAdminValidationAutoRefresh' | pluginTranslate: 'epistola' | async }}\n {{ refreshIntervalMinutes }} min.\n </div>\n <div>\n {{ 'epistolaAdminValidationLatestVersionNote' | pluginTranslate: 'epistola' | async }}\n </div>\n </div>\n\n <div *ngIf=\"validationViolations.length === 0\" class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminNoValidations' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"validationViolations.length > 0\" class=\"mt-3\">\n <p class=\"text-muted mb-3\">\n {{ 'epistolaAdminValidationWarningBody' | pluginTranslate: 'epistola' | async }}\n </p>\n <table class=\"table table-striped\" data-testid=\"epistola-admin-validations-table\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminProcess' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminActivity' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminValidationCode' | pluginTranslate: 'epistola' | async }}</th>\n <th>\n {{ 'epistolaAdminValidationMessage' | pluginTranslate: 'epistola' | async }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr\n *ngFor=\"let v of validationViolations; let i = index\"\n [attr.data-testid]=\"'epistola-admin-validation-row-' + i\"\n >\n <td>\n {{ v.processDefinitionName || v.processDefinitionKey }}\n <div *ngIf=\"v.processDefinitionName\" class=\"text-muted small\">\n <code>{{ v.processDefinitionKey }}</code>\n </div>\n </td>\n <td>\n <code>{{ v.activityId }}</code>\n </td>\n <td>\n <cds-tag size=\"sm\" type=\"red\">{{ v.code }}</cds-tag>\n </td>\n <td>{{ v.message }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </cds-tab>\n\n <cds-tab\n [heading]=\"changelogHeading\"\n data-testid=\"epistola-admin-tab-changelog\"\n [active]=\"overviewTab === 'changelog'\"\n (selected)=\"setOverviewTab('changelog')\"\n >\n <div class=\"d-flex align-items-center mt-3 mb-3\">\n <span class=\"text-muted me-2\">{{\n 'epistolaAdminRunningVersion' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" type=\"blue\">v{{ pluginVersion || '\u2014' }}</cds-tag>\n </div>\n\n <div *ngIf=\"changelogLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!changelogLoading && changelog && changelog.length === 0\"\n class=\"text-muted mb-3\"\n >\n {{ 'epistolaAdminNoChangelog' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!changelogLoading && changelog && changelog.length > 0\"\n class=\"epistola-changelog\"\n >\n <section *ngFor=\"let release of changelog\" class=\"changelog-release\">\n <div class=\"changelog-release__header\">\n <cds-tag size=\"sm\" type=\"purple\">{{ release.version }}</cds-tag>\n <span *ngIf=\"release.date\" class=\"text-muted ms-2\">{{ release.date }}</span>\n </div>\n <div *ngFor=\"let section of release.sections\" class=\"changelog-section\">\n <h6 class=\"changelog-section__title\">{{ section.title }}</h6>\n <ul class=\"changelog-section__items\">\n <li *ngFor=\"let item of section.items\">{{ item }}</li>\n </ul>\n </div>\n </section>\n </div>\n </cds-tab>\n\n <!-- TEMPORARY (removed in 1.0.0): forms missing the task-id carrier -->\n <cds-tab\n [heading]=\"formsHeading\"\n data-testid=\"epistola-admin-tab-forms\"\n [active]=\"overviewTab === 'forms'\"\n (selected)=\"setOverviewTab('forms')\"\n >\n <p class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminFormsIntro' | pluginTranslate: 'epistola' | async }}\n </p>\n\n <div *ngIf=\"formIssuesLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!formIssuesLoading && formIssues && formIssues.length === 0\"\n class=\"text-muted mb-3\"\n >\n {{ 'epistolaAdminNoFormIssues' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!formIssuesLoading && formIssues && formIssues.length > 0\">\n <div class=\"mb-2\">\n <button\n class=\"btn btn-sm btn-primary\"\n data-testid=\"epistola-admin-repair-all\"\n (click)=\"repairAllForms()\"\n [disabled]=\"repairingAll\"\n >\n {{\n (repairingAll ? 'epistolaAdminRepairing' : 'epistolaAdminRepairAll')\n | pluginTranslate: 'epistola'\n | async\n }}\n </button>\n <span\n *ngIf=\"formFeedback && formFeedback.formId === 'all'\"\n class=\"small ms-2\"\n [class.text-success]=\"formFeedback.type === 'success'\"\n [class.text-danger]=\"formFeedback.type === 'error'\"\n >{{ formFeedback.message }}</span\n >\n </div>\n\n <table class=\"table table-striped\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminFormName' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminFormMissing' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr\n *ngFor=\"let issue of formIssues\"\n [attr.data-testid]=\"'epistola-admin-form-issue-' + issue.formId\"\n >\n <td>\n <code>{{ issue.name }}</code>\n <cds-tag\n *ngIf=\"issue.readOnly\"\n size=\"sm\"\n type=\"warm-gray\"\n class=\"ms-1\"\n [title]=\"'epistolaAdminFormReadOnlyHint' | pluginTranslate: 'epistola' | async\"\n >{{\n 'epistolaAdminFormReadOnly' | pluginTranslate: 'epistola' | async\n }}</cds-tag\n >\n </td>\n <td>{{ issue.missingComponents }}</td>\n <td class=\"text-end\">\n <button\n class=\"btn btn-sm btn-outline-primary\"\n [attr.data-testid]=\"'epistola-admin-repair-form-' + issue.formId\"\n (click)=\"repairForm(issue)\"\n [disabled]=\"isRepairingForm(issue)\"\n [title]=\"'epistolaAdminRepairTooltip' | pluginTranslate: 'epistola' | async\"\n >\n {{\n (isRepairingForm(issue) ? 'epistolaAdminRepairing' : 'epistolaAdminRepair')\n | pluginTranslate: 'epistola'\n | async\n }}\n </button>\n <div\n *ngIf=\"formFeedback && formFeedback.formId === issue.formId\"\n class=\"small mt-1\"\n [class.text-success]=\"formFeedback.type === 'success'\"\n [class.text-danger]=\"formFeedback.type === 'error'\"\n >\n {{ formFeedback.message }}\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- TEMPORARY: forms still using the legacy override-mapping object format -->\n <hr class=\"my-4\" />\n <h5 class=\"mb-1\">\n {{ 'epistolaAdminLegacyOverrideTitle' | pluginTranslate: 'epistola' | async }}\n </h5>\n <p class=\"text-muted mb-3\">\n {{ 'epistolaAdminLegacyOverrideIntro' | pluginTranslate: 'epistola' | async }}\n </p>\n\n <div *ngIf=\"legacyOverrideLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!legacyOverrideLoading && legacyOverrideForms && legacyOverrideForms.length === 0\"\n class=\"text-muted mb-3\"\n >\n {{ 'epistolaAdminNoLegacyOverride' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table\n *ngIf=\"!legacyOverrideLoading && legacyOverrideForms && legacyOverrideForms.length > 0\"\n class=\"table table-striped\"\n >\n <thead>\n <tr>\n <th>{{ 'epistolaAdminFormName' | pluginTranslate: 'epistola' | async }}</th>\n <th>\n {{ 'epistolaAdminLegacyOverrideComponents' | pluginTranslate: 'epistola' | async }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let form of legacyOverrideForms\">\n <td>\n <code>{{ form.name }}</code>\n <cds-tag\n *ngIf=\"form.readOnly\"\n size=\"sm\"\n type=\"warm-gray\"\n class=\"ms-1\"\n [title]=\"'epistolaAdminFormReadOnlyHint' | pluginTranslate: 'epistola' | async\"\n >{{ 'epistolaAdminFormReadOnly' | pluginTranslate: 'epistola' | async }}</cds-tag\n >\n </td>\n <td>{{ form.legacyComponents }}</td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n </cds-tabs>\n </ng-container>\n\n <!-- Detail view: selected configuration -->\n <ng-container *ngIf=\"selectedCard\">\n <div class=\"detail-header mb-3\">\n <button\n class=\"btn btn-link btn-sm p-0\"\n data-testid=\"epistola-admin-back\"\n (click)=\"backToOverview()\"\n >\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 <ng-template #catalogsHeading>\n {{ 'epistolaAdminCatalogs' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" type=\"gray\" class=\"ms-1\">{{ catalogs.length }}</cds-tag>\n </ng-template>\n\n <cds-tabs [cacheActive]=\"true\" type=\"contained\" data-testid=\"epistola-admin-detail-tabs\">\n <cds-tab\n [heading]=\"actionsHeading\"\n data-testid=\"epistola-admin-detail-tab-actions\"\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; let i = index\"\n [attr.data-testid]=\"'epistola-admin-usage-row-' + i\"\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 [attr.data-testid]=\"'epistola-admin-export-' + i\"\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 data-testid=\"epistola-admin-detail-tab-pending\"\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>{{ 'epistolaAdminStatus' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminRequestId' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr\n *ngFor=\"let job of selectedCard.pendingJobs\"\n [attr.data-testid]=\"'epistola-admin-pending-row-' + job.executionId\"\n >\n <td>{{ job.processDefinitionName }}</td>\n <td>{{ job.activityName }}</td>\n <td>\n <cds-tag\n *ngIf=\"job.status === 'UNWIRED'; else waitingTag\"\n size=\"sm\"\n type=\"red\"\n [title]=\"'epistolaAdminUnwiredTooltip' | pluginTranslate: 'epistola' | async\"\n >\n {{ 'epistolaAdminStatusUnwired' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n <ng-template #waitingTag>\n <cds-tag size=\"sm\" type=\"blue\">\n {{ 'epistolaAdminStatusWaiting' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n </ng-template>\n </td>\n <td>\n <code *ngIf=\"job.status !== 'UNWIRED'\">{{ job.requestId }}</code>\n <span *ngIf=\"job.status === 'UNWIRED'\" class=\"text-muted\">\u2014</span>\n </td>\n <td class=\"text-end\">\n <button\n *ngIf=\"job.status !== 'UNWIRED'\"\n class=\"btn btn-sm btn-outline-primary\"\n [attr.data-testid]=\"'epistola-admin-reconcile-' + job.executionId\"\n (click)=\"reconcilePending(job)\"\n [disabled]=\"isReconciling(job)\"\n [title]=\"'epistolaAdminReconcileTooltip' | pluginTranslate: 'epistola' | async\"\n >\n <span *ngIf=\"!isReconciling(job)\">\n {{ 'epistolaAdminReconcile' | pluginTranslate: 'epistola' | async }}\n </span>\n <span *ngIf=\"isReconciling(job)\">\n {{ 'epistolaAdminReconciling' | pluginTranslate: 'epistola' | async }}\n </span>\n </button>\n <span\n *ngIf=\"job.status === 'UNWIRED'\"\n class=\"text-muted small\"\n [title]=\"'epistolaAdminUnwiredTooltip' | pluginTranslate: 'epistola' | async\"\n >\n {{ 'epistolaAdminUnwiredHint' | pluginTranslate: 'epistola' | async }}\n </span>\n <div\n *ngIf=\"reconcileFeedback && reconcileFeedback.executionId === job.executionId\"\n class=\"reconcile-feedback small mt-1\"\n [class.text-success]=\"reconcileFeedback.type === 'success'\"\n [class.text-warning]=\"reconcileFeedback.type === 'pending'\"\n [class.text-danger]=\"reconcileFeedback.type === 'error'\"\n >\n {{ reconcileFeedback.message }}\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n\n <cds-tab\n [heading]=\"catalogsHeading\"\n data-testid=\"epistola-admin-detail-tab-catalogs\"\n [active]=\"activeTab === 'catalogs'\"\n (selected)=\"setActiveTab('catalogs')\"\n >\n <p class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminCatalogsIntro' | pluginTranslate: 'epistola' | async }}\n </p>\n\n <div *ngIf=\"catalogsLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!catalogsLoading && catalogs.length === 0\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminNoCatalogs' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table *ngIf=\"!catalogsLoading && catalogs.length > 0\" class=\"table table-striped\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminCatalogSlug' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminCatalogVersion' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminCatalogStatus' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr\n *ngFor=\"let catalog of catalogs\"\n [attr.data-testid]=\"'epistola-admin-catalog-row-' + catalog.slug\"\n >\n <td>\n <code>{{ catalog.slug }}</code>\n </td>\n <td>{{ catalog.version }}</td>\n <td>\n <cds-tag *ngIf=\"catalog.status === 'IN_EPISTOLA'\" size=\"sm\" type=\"green\">\n {{ 'epistolaAdminCatalogInEpistola' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n <cds-tag *ngIf=\"catalog.status === 'NOT_IN_EPISTOLA'\" size=\"sm\" type=\"red\">\n {{ 'epistolaAdminCatalogNotInEpistola' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n <cds-tag *ngIf=\"catalog.status === 'UNKNOWN'\" size=\"sm\" type=\"gray\">\n {{ 'epistolaAdminCatalogStatusUnknown' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n </td>\n <td class=\"text-end\">\n <button\n class=\"btn btn-sm btn-outline-primary\"\n [attr.data-testid]=\"'epistola-admin-redeploy-' + catalog.slug\"\n (click)=\"redeployCatalog(catalog)\"\n [disabled]=\"isRedeploying(catalog)\"\n [title]=\"'epistolaAdminRedeployTooltip' | pluginTranslate: 'epistola' | async\"\n >\n <span *ngIf=\"!isRedeploying(catalog)\">\n {{ 'epistolaAdminRedeploy' | pluginTranslate: 'epistola' | async }}\n </span>\n <span *ngIf=\"isRedeploying(catalog)\">\n {{ 'epistolaAdminRedeploying' | pluginTranslate: 'epistola' | async }}\n </span>\n </button>\n <div\n *ngIf=\"catalogFeedback && catalogFeedback.slug === catalog.slug\"\n class=\"reconcile-feedback small mt-1\"\n [class.text-success]=\"catalogFeedback.type === 'success'\"\n [class.text-danger]=\"catalogFeedback.type === 'error'\"\n >\n {{ catalogFeedback.message }}\n </div>\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}.epistola-admin .epistola-changelog{max-height:60vh;overflow:auto;padding:.5rem 1rem;border:1px solid #dee2e6;border-radius:4px}.epistola-admin .epistola-changelog .changelog-release{padding:.75rem 0}.epistola-admin .epistola-changelog .changelog-release+.changelog-release{border-top:1px solid #eee}.epistola-admin .epistola-changelog .changelog-release__header{display:flex;align-items:center;margin-bottom:.5rem}.epistola-admin .epistola-changelog .changelog-section{margin:.25rem 0 .75rem}.epistola-admin .epistola-changelog .changelog-section__title{font-size:.8125rem;font-weight:600;text-transform:uppercase;letter-spacing:.02em;color:#6f6f6f;margin-bottom:.25rem}.epistola-admin .epistola-changelog .changelog-section__items{margin:0;padding-left:1.25rem;font-size:.8125rem;line-height:1.5}.epistola-admin .epistola-changelog .changelog-section__items li{margin-bottom:.25rem;word-break:break-word}\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: "pipe", type: i1$1.DatePipe, name: "date" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$4.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"] }] });
|
|
4505
4763
|
}
|
|
4506
4764
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminPageComponent, decorators: [{
|
|
4507
4765
|
type: Component,
|
|
4508
|
-
args: [{ selector: 'epistola-admin-page', standalone: true, imports: [CommonModule, RouterModule, PluginTranslatePipeModule, TabsModule, TagModule], template: "<div class=\"epistola-admin\">\n <!-- Overview: tabs (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 <ng-template #configurationsHeading>\n {{ 'epistolaAdminConfigurations' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" type=\"gray\" class=\"ms-1\">{{ cards.length }}</cds-tag>\n </ng-template>\n\n <ng-template #validationsHeading>\n {{ 'epistolaAdminValidations' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" [type]=\"validationViolations.length > 0 ? 'red' : 'gray'\" class=\"ms-1\">\n {{ validationViolations.length }}\n </cds-tag>\n </ng-template>\n\n <ng-template #changelogHeading>\n {{ 'epistolaAdminChangelog' | pluginTranslate: 'epistola' | async }}\n </ng-template>\n\n <!-- TEMPORARY (removed in 1.0.0): forms missing the task-id carrier -->\n <ng-template #formsHeading>\n {{ 'epistolaAdminForms' | pluginTranslate: 'epistola' | async }}\n <cds-tag\n *ngIf=\"formsScanLoaded\"\n size=\"sm\"\n [type]=\"formsAttentionCount > 0 ? 'red' : 'gray'\"\n class=\"ms-1\"\n >{{ formsAttentionCount }}</cds-tag\n >\n </ng-template>\n\n <cds-tabs [cacheActive]=\"true\" type=\"contained\">\n <cds-tab\n [heading]=\"configurationsHeading\"\n [active]=\"overviewTab === 'configurations'\"\n (selected)=\"setOverviewTab('configurations')\"\n >\n <div *ngIf=\"loading\" class=\"text-muted mt-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!loading && cards.length === 0\" class=\"text-muted mt-3\">\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 </cds-tab>\n\n <cds-tab\n [heading]=\"validationsHeading\"\n [active]=\"overviewTab === 'validations'\"\n (selected)=\"setOverviewTab('validations')\"\n >\n <div class=\"text-muted small mt-3\">\n <div>\n {{ 'epistolaAdminValidationLastChecked' | pluginTranslate: 'epistola' | async }}:\n <ng-container *ngIf=\"validationReport?.lastCheckedAt; else notYetRun\">\n {{ validationReport?.lastCheckedAt | date: 'medium' }}\n </ng-container>\n <ng-template #notYetRun>\n {{ 'epistolaAdminValidationNotYetRun' | pluginTranslate: 'epistola' | async }}\n </ng-template>\n \u00B7 {{ 'epistolaAdminValidationAutoRefresh' | pluginTranslate: 'epistola' | async }}\n {{ refreshIntervalMinutes }} min.\n </div>\n <div>\n {{ 'epistolaAdminValidationLatestVersionNote' | pluginTranslate: 'epistola' | async }}\n </div>\n </div>\n\n <div *ngIf=\"validationViolations.length === 0\" class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminNoValidations' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"validationViolations.length > 0\" class=\"mt-3\">\n <p class=\"text-muted mb-3\">\n {{ 'epistolaAdminValidationWarningBody' | pluginTranslate: 'epistola' | async }}\n </p>\n <table class=\"table table-striped\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminProcess' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminActivity' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminValidationCode' | pluginTranslate: 'epistola' | async }}</th>\n <th>\n {{ 'epistolaAdminValidationMessage' | pluginTranslate: 'epistola' | async }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let v of validationViolations\">\n <td>\n {{ v.processDefinitionName || v.processDefinitionKey }}\n <div *ngIf=\"v.processDefinitionName\" class=\"text-muted small\">\n <code>{{ v.processDefinitionKey }}</code>\n </div>\n </td>\n <td>\n <code>{{ v.activityId }}</code>\n </td>\n <td>\n <cds-tag size=\"sm\" type=\"red\">{{ v.code }}</cds-tag>\n </td>\n <td>{{ v.message }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </cds-tab>\n\n <cds-tab\n [heading]=\"changelogHeading\"\n [active]=\"overviewTab === 'changelog'\"\n (selected)=\"setOverviewTab('changelog')\"\n >\n <div class=\"d-flex align-items-center mt-3 mb-3\">\n <span class=\"text-muted me-2\">{{\n 'epistolaAdminRunningVersion' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" type=\"blue\">v{{ pluginVersion || '\u2014' }}</cds-tag>\n </div>\n\n <div *ngIf=\"changelogLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!changelogLoading && changelog && changelog.length === 0\"\n class=\"text-muted mb-3\"\n >\n {{ 'epistolaAdminNoChangelog' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!changelogLoading && changelog && changelog.length > 0\"\n class=\"epistola-changelog\"\n >\n <section *ngFor=\"let release of changelog\" class=\"changelog-release\">\n <div class=\"changelog-release__header\">\n <cds-tag size=\"sm\" type=\"purple\">{{ release.version }}</cds-tag>\n <span *ngIf=\"release.date\" class=\"text-muted ms-2\">{{ release.date }}</span>\n </div>\n <div *ngFor=\"let section of release.sections\" class=\"changelog-section\">\n <h6 class=\"changelog-section__title\">{{ section.title }}</h6>\n <ul class=\"changelog-section__items\">\n <li *ngFor=\"let item of section.items\">{{ item }}</li>\n </ul>\n </div>\n </section>\n </div>\n </cds-tab>\n\n <!-- TEMPORARY (removed in 1.0.0): forms missing the task-id carrier -->\n <cds-tab\n [heading]=\"formsHeading\"\n [active]=\"overviewTab === 'forms'\"\n (selected)=\"setOverviewTab('forms')\"\n >\n <p class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminFormsIntro' | pluginTranslate: 'epistola' | async }}\n </p>\n\n <div *ngIf=\"formIssuesLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!formIssuesLoading && formIssues && formIssues.length === 0\"\n class=\"text-muted mb-3\"\n >\n {{ 'epistolaAdminNoFormIssues' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!formIssuesLoading && formIssues && formIssues.length > 0\">\n <div class=\"mb-2\">\n <button\n class=\"btn btn-sm btn-primary\"\n (click)=\"repairAllForms()\"\n [disabled]=\"repairingAll\"\n >\n {{\n (repairingAll ? 'epistolaAdminRepairing' : 'epistolaAdminRepairAll')\n | pluginTranslate: 'epistola'\n | async\n }}\n </button>\n <span\n *ngIf=\"formFeedback && formFeedback.formId === 'all'\"\n class=\"small ms-2\"\n [class.text-success]=\"formFeedback.type === 'success'\"\n [class.text-danger]=\"formFeedback.type === 'error'\"\n >{{ formFeedback.message }}</span\n >\n </div>\n\n <table class=\"table table-striped\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminFormName' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminFormMissing' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let issue of formIssues\">\n <td>\n <code>{{ issue.name }}</code>\n <cds-tag\n *ngIf=\"issue.readOnly\"\n size=\"sm\"\n type=\"warm-gray\"\n class=\"ms-1\"\n [title]=\"'epistolaAdminFormReadOnlyHint' | pluginTranslate: 'epistola' | async\"\n >{{\n 'epistolaAdminFormReadOnly' | pluginTranslate: 'epistola' | async\n }}</cds-tag\n >\n </td>\n <td>{{ issue.missingComponents }}</td>\n <td class=\"text-end\">\n <button\n class=\"btn btn-sm btn-outline-primary\"\n (click)=\"repairForm(issue)\"\n [disabled]=\"isRepairingForm(issue)\"\n [title]=\"'epistolaAdminRepairTooltip' | pluginTranslate: 'epistola' | async\"\n >\n {{\n (isRepairingForm(issue) ? 'epistolaAdminRepairing' : 'epistolaAdminRepair')\n | pluginTranslate: 'epistola'\n | async\n }}\n </button>\n <div\n *ngIf=\"formFeedback && formFeedback.formId === issue.formId\"\n class=\"small mt-1\"\n [class.text-success]=\"formFeedback.type === 'success'\"\n [class.text-danger]=\"formFeedback.type === 'error'\"\n >\n {{ formFeedback.message }}\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- TEMPORARY: forms still using the legacy override-mapping object format -->\n <hr class=\"my-4\" />\n <h5 class=\"mb-1\">\n {{ 'epistolaAdminLegacyOverrideTitle' | pluginTranslate: 'epistola' | async }}\n </h5>\n <p class=\"text-muted mb-3\">\n {{ 'epistolaAdminLegacyOverrideIntro' | pluginTranslate: 'epistola' | async }}\n </p>\n\n <div *ngIf=\"legacyOverrideLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!legacyOverrideLoading && legacyOverrideForms && legacyOverrideForms.length === 0\"\n class=\"text-muted mb-3\"\n >\n {{ 'epistolaAdminNoLegacyOverride' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table\n *ngIf=\"!legacyOverrideLoading && legacyOverrideForms && legacyOverrideForms.length > 0\"\n class=\"table table-striped\"\n >\n <thead>\n <tr>\n <th>{{ 'epistolaAdminFormName' | pluginTranslate: 'epistola' | async }}</th>\n <th>\n {{ 'epistolaAdminLegacyOverrideComponents' | pluginTranslate: 'epistola' | async }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let form of legacyOverrideForms\">\n <td>\n <code>{{ form.name }}</code>\n <cds-tag\n *ngIf=\"form.readOnly\"\n size=\"sm\"\n type=\"warm-gray\"\n class=\"ms-1\"\n [title]=\"'epistolaAdminFormReadOnlyHint' | pluginTranslate: 'epistola' | async\"\n >{{ 'epistolaAdminFormReadOnly' | pluginTranslate: 'epistola' | async }}</cds-tag\n >\n </td>\n <td>{{ form.legacyComponents }}</td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n </cds-tabs>\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 <ng-template #catalogsHeading>\n {{ 'epistolaAdminCatalogs' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" type=\"gray\" class=\"ms-1\">{{ catalogs.length }}</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>{{ 'epistolaAdminStatus' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminRequestId' | pluginTranslate: 'epistola' | async }}</th>\n <th></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 <cds-tag\n *ngIf=\"job.status === 'UNWIRED'; else waitingTag\"\n size=\"sm\"\n type=\"red\"\n [title]=\"'epistolaAdminUnwiredTooltip' | pluginTranslate: 'epistola' | async\"\n >\n {{ 'epistolaAdminStatusUnwired' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n <ng-template #waitingTag>\n <cds-tag size=\"sm\" type=\"blue\">\n {{ 'epistolaAdminStatusWaiting' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n </ng-template>\n </td>\n <td>\n <code *ngIf=\"job.status !== 'UNWIRED'\">{{ job.requestId }}</code>\n <span *ngIf=\"job.status === 'UNWIRED'\" class=\"text-muted\">\u2014</span>\n </td>\n <td class=\"text-end\">\n <button\n *ngIf=\"job.status !== 'UNWIRED'\"\n class=\"btn btn-sm btn-outline-primary\"\n (click)=\"reconcilePending(job)\"\n [disabled]=\"isReconciling(job)\"\n [title]=\"'epistolaAdminReconcileTooltip' | pluginTranslate: 'epistola' | async\"\n >\n <span *ngIf=\"!isReconciling(job)\">\n {{ 'epistolaAdminReconcile' | pluginTranslate: 'epistola' | async }}\n </span>\n <span *ngIf=\"isReconciling(job)\">\n {{ 'epistolaAdminReconciling' | pluginTranslate: 'epistola' | async }}\n </span>\n </button>\n <span\n *ngIf=\"job.status === 'UNWIRED'\"\n class=\"text-muted small\"\n [title]=\"'epistolaAdminUnwiredTooltip' | pluginTranslate: 'epistola' | async\"\n >\n {{ 'epistolaAdminUnwiredHint' | pluginTranslate: 'epistola' | async }}\n </span>\n <div\n *ngIf=\"reconcileFeedback && reconcileFeedback.executionId === job.executionId\"\n class=\"reconcile-feedback small mt-1\"\n [class.text-success]=\"reconcileFeedback.type === 'success'\"\n [class.text-warning]=\"reconcileFeedback.type === 'pending'\"\n [class.text-danger]=\"reconcileFeedback.type === 'error'\"\n >\n {{ reconcileFeedback.message }}\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n\n <cds-tab\n [heading]=\"catalogsHeading\"\n [active]=\"activeTab === 'catalogs'\"\n (selected)=\"setActiveTab('catalogs')\"\n >\n <p class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminCatalogsIntro' | pluginTranslate: 'epistola' | async }}\n </p>\n\n <div *ngIf=\"catalogsLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!catalogsLoading && catalogs.length === 0\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminNoCatalogs' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table *ngIf=\"!catalogsLoading && catalogs.length > 0\" class=\"table table-striped\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminCatalogSlug' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminCatalogVersion' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminCatalogStatus' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let catalog of catalogs\">\n <td>\n <code>{{ catalog.slug }}</code>\n </td>\n <td>{{ catalog.version }}</td>\n <td>\n <cds-tag *ngIf=\"catalog.status === 'IN_EPISTOLA'\" size=\"sm\" type=\"green\">\n {{ 'epistolaAdminCatalogInEpistola' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n <cds-tag *ngIf=\"catalog.status === 'NOT_IN_EPISTOLA'\" size=\"sm\" type=\"red\">\n {{ 'epistolaAdminCatalogNotInEpistola' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n <cds-tag *ngIf=\"catalog.status === 'UNKNOWN'\" size=\"sm\" type=\"gray\">\n {{ 'epistolaAdminCatalogStatusUnknown' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n </td>\n <td class=\"text-end\">\n <button\n class=\"btn btn-sm btn-outline-primary\"\n (click)=\"redeployCatalog(catalog)\"\n [disabled]=\"isRedeploying(catalog)\"\n [title]=\"'epistolaAdminRedeployTooltip' | pluginTranslate: 'epistola' | async\"\n >\n <span *ngIf=\"!isRedeploying(catalog)\">\n {{ 'epistolaAdminRedeploy' | pluginTranslate: 'epistola' | async }}\n </span>\n <span *ngIf=\"isRedeploying(catalog)\">\n {{ 'epistolaAdminRedeploying' | pluginTranslate: 'epistola' | async }}\n </span>\n </button>\n <div\n *ngIf=\"catalogFeedback && catalogFeedback.slug === catalog.slug\"\n class=\"reconcile-feedback small mt-1\"\n [class.text-success]=\"catalogFeedback.type === 'success'\"\n [class.text-danger]=\"catalogFeedback.type === 'error'\"\n >\n {{ catalogFeedback.message }}\n </div>\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}.epistola-admin .epistola-changelog{max-height:60vh;overflow:auto;padding:.5rem 1rem;border:1px solid #dee2e6;border-radius:4px}.epistola-admin .epistola-changelog .changelog-release{padding:.75rem 0}.epistola-admin .epistola-changelog .changelog-release+.changelog-release{border-top:1px solid #eee}.epistola-admin .epistola-changelog .changelog-release__header{display:flex;align-items:center;margin-bottom:.5rem}.epistola-admin .epistola-changelog .changelog-section{margin:.25rem 0 .75rem}.epistola-admin .epistola-changelog .changelog-section__title{font-size:.8125rem;font-weight:600;text-transform:uppercase;letter-spacing:.02em;color:#6f6f6f;margin-bottom:.25rem}.epistola-admin .epistola-changelog .changelog-section__items{margin:0;padding-left:1.25rem;font-size:.8125rem;line-height:1.5}.epistola-admin .epistola-changelog .changelog-section__items li{margin-bottom:.25rem;word-break:break-word}\n"] }]
|
|
4766
|
+
args: [{ selector: 'epistola-admin-page', standalone: true, imports: [CommonModule, RouterModule, PluginTranslatePipeModule, TabsModule, TagModule], template: "<div class=\"epistola-admin\" data-testid=\"epistola-admin\">\n <!-- Overview: tabs (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\n class=\"btn btn-outline-primary btn-sm\"\n data-testid=\"epistola-admin-refresh\"\n (click)=\"refresh()\"\n [disabled]=\"loading\"\n >\n {{ 'epistolaAdminRefresh' | pluginTranslate: 'epistola' | async }}\n </button>\n </div>\n\n <ng-template #configurationsHeading>\n {{ 'epistolaAdminConfigurations' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" type=\"gray\" class=\"ms-1\">{{ cards.length }}</cds-tag>\n </ng-template>\n\n <ng-template #validationsHeading>\n {{ 'epistolaAdminValidations' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" [type]=\"validationViolations.length > 0 ? 'red' : 'gray'\" class=\"ms-1\">\n {{ validationViolations.length }}\n </cds-tag>\n </ng-template>\n\n <ng-template #changelogHeading>\n {{ 'epistolaAdminChangelog' | pluginTranslate: 'epistola' | async }}\n </ng-template>\n\n <!-- TEMPORARY (removed in 1.0.0): forms missing the task-id carrier -->\n <ng-template #formsHeading>\n {{ 'epistolaAdminForms' | pluginTranslate: 'epistola' | async }}\n <cds-tag\n *ngIf=\"formsScanLoaded\"\n size=\"sm\"\n [type]=\"formsAttentionCount > 0 ? 'red' : 'gray'\"\n class=\"ms-1\"\n >{{ formsAttentionCount }}</cds-tag\n >\n </ng-template>\n\n <cds-tabs [cacheActive]=\"true\" type=\"contained\" data-testid=\"epistola-admin-overview-tabs\">\n <cds-tab\n [heading]=\"configurationsHeading\"\n data-testid=\"epistola-admin-tab-configurations\"\n [active]=\"overviewTab === 'configurations'\"\n (selected)=\"setOverviewTab('configurations')\"\n >\n <div\n *ngIf=\"loading\"\n class=\"text-muted mt-3\"\n data-testid=\"epistola-admin-configurations-loading\"\n >\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!loading && cards.length === 0\"\n class=\"text-muted mt-3\"\n data-testid=\"epistola-admin-configurations-empty\"\n >\n {{ 'epistolaAdminNoConfigurations' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!loading && cards.length > 0\"\n class=\"card-grid\"\n data-testid=\"epistola-admin-card-grid\"\n >\n <div\n *ngFor=\"let card of cards\"\n class=\"config-card\"\n [attr.data-testid]=\"'epistola-admin-config-card-' + card.tenantId\"\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 </cds-tab>\n\n <cds-tab\n [heading]=\"validationsHeading\"\n data-testid=\"epistola-admin-tab-validations\"\n [active]=\"overviewTab === 'validations'\"\n (selected)=\"setOverviewTab('validations')\"\n >\n <div class=\"text-muted small mt-3\">\n <div>\n {{ 'epistolaAdminValidationLastChecked' | pluginTranslate: 'epistola' | async }}:\n <ng-container *ngIf=\"validationReport?.lastCheckedAt; else notYetRun\">\n {{ validationReport?.lastCheckedAt | date: 'medium' }}\n </ng-container>\n <ng-template #notYetRun>\n {{ 'epistolaAdminValidationNotYetRun' | pluginTranslate: 'epistola' | async }}\n </ng-template>\n \u00B7 {{ 'epistolaAdminValidationAutoRefresh' | pluginTranslate: 'epistola' | async }}\n {{ refreshIntervalMinutes }} min.\n </div>\n <div>\n {{ 'epistolaAdminValidationLatestVersionNote' | pluginTranslate: 'epistola' | async }}\n </div>\n </div>\n\n <div *ngIf=\"validationViolations.length === 0\" class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminNoValidations' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"validationViolations.length > 0\" class=\"mt-3\">\n <p class=\"text-muted mb-3\">\n {{ 'epistolaAdminValidationWarningBody' | pluginTranslate: 'epistola' | async }}\n </p>\n <table class=\"table table-striped\" data-testid=\"epistola-admin-validations-table\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminProcess' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminActivity' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminValidationCode' | pluginTranslate: 'epistola' | async }}</th>\n <th>\n {{ 'epistolaAdminValidationMessage' | pluginTranslate: 'epistola' | async }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr\n *ngFor=\"let v of validationViolations; let i = index\"\n [attr.data-testid]=\"'epistola-admin-validation-row-' + i\"\n >\n <td>\n {{ v.processDefinitionName || v.processDefinitionKey }}\n <div *ngIf=\"v.processDefinitionName\" class=\"text-muted small\">\n <code>{{ v.processDefinitionKey }}</code>\n </div>\n </td>\n <td>\n <code>{{ v.activityId }}</code>\n </td>\n <td>\n <cds-tag size=\"sm\" type=\"red\">{{ v.code }}</cds-tag>\n </td>\n <td>{{ v.message }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </cds-tab>\n\n <cds-tab\n [heading]=\"changelogHeading\"\n data-testid=\"epistola-admin-tab-changelog\"\n [active]=\"overviewTab === 'changelog'\"\n (selected)=\"setOverviewTab('changelog')\"\n >\n <div class=\"d-flex align-items-center mt-3 mb-3\">\n <span class=\"text-muted me-2\">{{\n 'epistolaAdminRunningVersion' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" type=\"blue\">v{{ pluginVersion || '\u2014' }}</cds-tag>\n </div>\n\n <div *ngIf=\"changelogLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!changelogLoading && changelog && changelog.length === 0\"\n class=\"text-muted mb-3\"\n >\n {{ 'epistolaAdminNoChangelog' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!changelogLoading && changelog && changelog.length > 0\"\n class=\"epistola-changelog\"\n >\n <section *ngFor=\"let release of changelog\" class=\"changelog-release\">\n <div class=\"changelog-release__header\">\n <cds-tag size=\"sm\" type=\"purple\">{{ release.version }}</cds-tag>\n <span *ngIf=\"release.date\" class=\"text-muted ms-2\">{{ release.date }}</span>\n </div>\n <div *ngFor=\"let section of release.sections\" class=\"changelog-section\">\n <h6 class=\"changelog-section__title\">{{ section.title }}</h6>\n <ul class=\"changelog-section__items\">\n <li *ngFor=\"let item of section.items\">{{ item }}</li>\n </ul>\n </div>\n </section>\n </div>\n </cds-tab>\n\n <!-- TEMPORARY (removed in 1.0.0): forms missing the task-id carrier -->\n <cds-tab\n [heading]=\"formsHeading\"\n data-testid=\"epistola-admin-tab-forms\"\n [active]=\"overviewTab === 'forms'\"\n (selected)=\"setOverviewTab('forms')\"\n >\n <p class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminFormsIntro' | pluginTranslate: 'epistola' | async }}\n </p>\n\n <div *ngIf=\"formIssuesLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!formIssuesLoading && formIssues && formIssues.length === 0\"\n class=\"text-muted mb-3\"\n >\n {{ 'epistolaAdminNoFormIssues' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!formIssuesLoading && formIssues && formIssues.length > 0\">\n <div class=\"mb-2\">\n <button\n class=\"btn btn-sm btn-primary\"\n data-testid=\"epistola-admin-repair-all\"\n (click)=\"repairAllForms()\"\n [disabled]=\"repairingAll\"\n >\n {{\n (repairingAll ? 'epistolaAdminRepairing' : 'epistolaAdminRepairAll')\n | pluginTranslate: 'epistola'\n | async\n }}\n </button>\n <span\n *ngIf=\"formFeedback && formFeedback.formId === 'all'\"\n class=\"small ms-2\"\n [class.text-success]=\"formFeedback.type === 'success'\"\n [class.text-danger]=\"formFeedback.type === 'error'\"\n >{{ formFeedback.message }}</span\n >\n </div>\n\n <table class=\"table table-striped\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminFormName' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminFormMissing' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr\n *ngFor=\"let issue of formIssues\"\n [attr.data-testid]=\"'epistola-admin-form-issue-' + issue.formId\"\n >\n <td>\n <code>{{ issue.name }}</code>\n <cds-tag\n *ngIf=\"issue.readOnly\"\n size=\"sm\"\n type=\"warm-gray\"\n class=\"ms-1\"\n [title]=\"'epistolaAdminFormReadOnlyHint' | pluginTranslate: 'epistola' | async\"\n >{{\n 'epistolaAdminFormReadOnly' | pluginTranslate: 'epistola' | async\n }}</cds-tag\n >\n </td>\n <td>{{ issue.missingComponents }}</td>\n <td class=\"text-end\">\n <button\n class=\"btn btn-sm btn-outline-primary\"\n [attr.data-testid]=\"'epistola-admin-repair-form-' + issue.formId\"\n (click)=\"repairForm(issue)\"\n [disabled]=\"isRepairingForm(issue)\"\n [title]=\"'epistolaAdminRepairTooltip' | pluginTranslate: 'epistola' | async\"\n >\n {{\n (isRepairingForm(issue) ? 'epistolaAdminRepairing' : 'epistolaAdminRepair')\n | pluginTranslate: 'epistola'\n | async\n }}\n </button>\n <div\n *ngIf=\"formFeedback && formFeedback.formId === issue.formId\"\n class=\"small mt-1\"\n [class.text-success]=\"formFeedback.type === 'success'\"\n [class.text-danger]=\"formFeedback.type === 'error'\"\n >\n {{ formFeedback.message }}\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- TEMPORARY: forms still using the legacy override-mapping object format -->\n <hr class=\"my-4\" />\n <h5 class=\"mb-1\">\n {{ 'epistolaAdminLegacyOverrideTitle' | pluginTranslate: 'epistola' | async }}\n </h5>\n <p class=\"text-muted mb-3\">\n {{ 'epistolaAdminLegacyOverrideIntro' | pluginTranslate: 'epistola' | async }}\n </p>\n\n <div *ngIf=\"legacyOverrideLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div\n *ngIf=\"!legacyOverrideLoading && legacyOverrideForms && legacyOverrideForms.length === 0\"\n class=\"text-muted mb-3\"\n >\n {{ 'epistolaAdminNoLegacyOverride' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table\n *ngIf=\"!legacyOverrideLoading && legacyOverrideForms && legacyOverrideForms.length > 0\"\n class=\"table table-striped\"\n >\n <thead>\n <tr>\n <th>{{ 'epistolaAdminFormName' | pluginTranslate: 'epistola' | async }}</th>\n <th>\n {{ 'epistolaAdminLegacyOverrideComponents' | pluginTranslate: 'epistola' | async }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let form of legacyOverrideForms\">\n <td>\n <code>{{ form.name }}</code>\n <cds-tag\n *ngIf=\"form.readOnly\"\n size=\"sm\"\n type=\"warm-gray\"\n class=\"ms-1\"\n [title]=\"'epistolaAdminFormReadOnlyHint' | pluginTranslate: 'epistola' | async\"\n >{{ 'epistolaAdminFormReadOnly' | pluginTranslate: 'epistola' | async }}</cds-tag\n >\n </td>\n <td>{{ form.legacyComponents }}</td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n </cds-tabs>\n </ng-container>\n\n <!-- Detail view: selected configuration -->\n <ng-container *ngIf=\"selectedCard\">\n <div class=\"detail-header mb-3\">\n <button\n class=\"btn btn-link btn-sm p-0\"\n data-testid=\"epistola-admin-back\"\n (click)=\"backToOverview()\"\n >\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 <ng-template #catalogsHeading>\n {{ 'epistolaAdminCatalogs' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" type=\"gray\" class=\"ms-1\">{{ catalogs.length }}</cds-tag>\n </ng-template>\n\n <cds-tabs [cacheActive]=\"true\" type=\"contained\" data-testid=\"epistola-admin-detail-tabs\">\n <cds-tab\n [heading]=\"actionsHeading\"\n data-testid=\"epistola-admin-detail-tab-actions\"\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; let i = index\"\n [attr.data-testid]=\"'epistola-admin-usage-row-' + i\"\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 [attr.data-testid]=\"'epistola-admin-export-' + i\"\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 data-testid=\"epistola-admin-detail-tab-pending\"\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>{{ 'epistolaAdminStatus' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminRequestId' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr\n *ngFor=\"let job of selectedCard.pendingJobs\"\n [attr.data-testid]=\"'epistola-admin-pending-row-' + job.executionId\"\n >\n <td>{{ job.processDefinitionName }}</td>\n <td>{{ job.activityName }}</td>\n <td>\n <cds-tag\n *ngIf=\"job.status === 'UNWIRED'; else waitingTag\"\n size=\"sm\"\n type=\"red\"\n [title]=\"'epistolaAdminUnwiredTooltip' | pluginTranslate: 'epistola' | async\"\n >\n {{ 'epistolaAdminStatusUnwired' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n <ng-template #waitingTag>\n <cds-tag size=\"sm\" type=\"blue\">\n {{ 'epistolaAdminStatusWaiting' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n </ng-template>\n </td>\n <td>\n <code *ngIf=\"job.status !== 'UNWIRED'\">{{ job.requestId }}</code>\n <span *ngIf=\"job.status === 'UNWIRED'\" class=\"text-muted\">\u2014</span>\n </td>\n <td class=\"text-end\">\n <button\n *ngIf=\"job.status !== 'UNWIRED'\"\n class=\"btn btn-sm btn-outline-primary\"\n [attr.data-testid]=\"'epistola-admin-reconcile-' + job.executionId\"\n (click)=\"reconcilePending(job)\"\n [disabled]=\"isReconciling(job)\"\n [title]=\"'epistolaAdminReconcileTooltip' | pluginTranslate: 'epistola' | async\"\n >\n <span *ngIf=\"!isReconciling(job)\">\n {{ 'epistolaAdminReconcile' | pluginTranslate: 'epistola' | async }}\n </span>\n <span *ngIf=\"isReconciling(job)\">\n {{ 'epistolaAdminReconciling' | pluginTranslate: 'epistola' | async }}\n </span>\n </button>\n <span\n *ngIf=\"job.status === 'UNWIRED'\"\n class=\"text-muted small\"\n [title]=\"'epistolaAdminUnwiredTooltip' | pluginTranslate: 'epistola' | async\"\n >\n {{ 'epistolaAdminUnwiredHint' | pluginTranslate: 'epistola' | async }}\n </span>\n <div\n *ngIf=\"reconcileFeedback && reconcileFeedback.executionId === job.executionId\"\n class=\"reconcile-feedback small mt-1\"\n [class.text-success]=\"reconcileFeedback.type === 'success'\"\n [class.text-warning]=\"reconcileFeedback.type === 'pending'\"\n [class.text-danger]=\"reconcileFeedback.type === 'error'\"\n >\n {{ reconcileFeedback.message }}\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n\n <cds-tab\n [heading]=\"catalogsHeading\"\n data-testid=\"epistola-admin-detail-tab-catalogs\"\n [active]=\"activeTab === 'catalogs'\"\n (selected)=\"setActiveTab('catalogs')\"\n >\n <p class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminCatalogsIntro' | pluginTranslate: 'epistola' | async }}\n </p>\n\n <div *ngIf=\"catalogsLoading\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!catalogsLoading && catalogs.length === 0\" class=\"text-muted mb-3\">\n {{ 'epistolaAdminNoCatalogs' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table *ngIf=\"!catalogsLoading && catalogs.length > 0\" class=\"table table-striped\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminCatalogSlug' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminCatalogVersion' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminCatalogStatus' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr\n *ngFor=\"let catalog of catalogs\"\n [attr.data-testid]=\"'epistola-admin-catalog-row-' + catalog.slug\"\n >\n <td>\n <code>{{ catalog.slug }}</code>\n </td>\n <td>{{ catalog.version }}</td>\n <td>\n <cds-tag *ngIf=\"catalog.status === 'IN_EPISTOLA'\" size=\"sm\" type=\"green\">\n {{ 'epistolaAdminCatalogInEpistola' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n <cds-tag *ngIf=\"catalog.status === 'NOT_IN_EPISTOLA'\" size=\"sm\" type=\"red\">\n {{ 'epistolaAdminCatalogNotInEpistola' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n <cds-tag *ngIf=\"catalog.status === 'UNKNOWN'\" size=\"sm\" type=\"gray\">\n {{ 'epistolaAdminCatalogStatusUnknown' | pluginTranslate: 'epistola' | async }}\n </cds-tag>\n </td>\n <td class=\"text-end\">\n <button\n class=\"btn btn-sm btn-outline-primary\"\n [attr.data-testid]=\"'epistola-admin-redeploy-' + catalog.slug\"\n (click)=\"redeployCatalog(catalog)\"\n [disabled]=\"isRedeploying(catalog)\"\n [title]=\"'epistolaAdminRedeployTooltip' | pluginTranslate: 'epistola' | async\"\n >\n <span *ngIf=\"!isRedeploying(catalog)\">\n {{ 'epistolaAdminRedeploy' | pluginTranslate: 'epistola' | async }}\n </span>\n <span *ngIf=\"isRedeploying(catalog)\">\n {{ 'epistolaAdminRedeploying' | pluginTranslate: 'epistola' | async }}\n </span>\n </button>\n <div\n *ngIf=\"catalogFeedback && catalogFeedback.slug === catalog.slug\"\n class=\"reconcile-feedback small mt-1\"\n [class.text-success]=\"catalogFeedback.type === 'success'\"\n [class.text-danger]=\"catalogFeedback.type === 'error'\"\n >\n {{ catalogFeedback.message }}\n </div>\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}.epistola-admin .epistola-changelog{max-height:60vh;overflow:auto;padding:.5rem 1rem;border:1px solid #dee2e6;border-radius:4px}.epistola-admin .epistola-changelog .changelog-release{padding:.75rem 0}.epistola-admin .epistola-changelog .changelog-release+.changelog-release{border-top:1px solid #eee}.epistola-admin .epistola-changelog .changelog-release__header{display:flex;align-items:center;margin-bottom:.5rem}.epistola-admin .epistola-changelog .changelog-section{margin:.25rem 0 .75rem}.epistola-admin .epistola-changelog .changelog-section__title{font-size:.8125rem;font-weight:600;text-transform:uppercase;letter-spacing:.02em;color:#6f6f6f;margin-bottom:.25rem}.epistola-admin .epistola-changelog .changelog-section__items{margin:0;padding-left:1.25rem;font-size:.8125rem;line-height:1.5}.epistola-admin .epistola-changelog .changelog-section__items li{margin-bottom:.25rem;word-break:break-word}\n"] }]
|
|
4509
4767
|
}], ctorParameters: () => [{ type: EpistolaAdminService }, { type: i2$4.ActivatedRoute }, { type: i2$4.Router }] });
|
|
4510
4768
|
|
|
4511
4769
|
/*
|
|
@@ -5426,12 +5684,15 @@ class EpistolaOverrideBuilderComponent {
|
|
|
5426
5684
|
}
|
|
5427
5685
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaOverrideBuilderComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: EpistolaPluginService }], target: i0.ɵɵFactoryTarget.Component });
|
|
5428
5686
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaOverrideBuilderComponent, isStandalone: true, selector: "epistola-override-builder-component", inputs: { value: "value", disabled: "disabled", label: "label", availableFields: "availableFields", processDefinitionKey: "processDefinitionKey", sourceActivityId: "sourceActivityId" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
5429
|
-
<div class="override-builder">
|
|
5687
|
+
<div class="override-builder" data-testid="epistola-override-root">
|
|
5430
5688
|
<div class="builder-header">
|
|
5431
|
-
<span class="builder-label">{{
|
|
5689
|
+
<span class="builder-label" data-testid="epistola-override-label">{{
|
|
5690
|
+
label || 'Input Overrides'
|
|
5691
|
+
}}</span>
|
|
5432
5692
|
<button
|
|
5433
5693
|
type="button"
|
|
5434
5694
|
class="mode-toggle"
|
|
5695
|
+
data-testid="epistola-override-mode-toggle"
|
|
5435
5696
|
[disabled]="simpleUnavailable && !advancedMode"
|
|
5436
5697
|
(click)="toggleMode()"
|
|
5437
5698
|
>
|
|
@@ -5444,8 +5705,8 @@ class EpistolaOverrideBuilderComponent {
|
|
|
5444
5705
|
Make the preview reflect what the user is typing — <em>before</em> they submit — by feeding
|
|
5445
5706
|
live form values into the document inputs.
|
|
5446
5707
|
</p>
|
|
5447
|
-
<details class="builder-help">
|
|
5448
|
-
<summary>When should I map a field?</summary>
|
|
5708
|
+
<details class="builder-help" data-testid="epistola-override-help">
|
|
5709
|
+
<summary data-testid="epistola-override-help-summary">When should I map a field?</summary>
|
|
5449
5710
|
<ul>
|
|
5450
5711
|
<li>
|
|
5451
5712
|
<strong>Map</strong> a field when its value ends up in the generated document — i.e. the
|
|
@@ -5471,17 +5732,26 @@ class EpistolaOverrideBuilderComponent {
|
|
|
5471
5732
|
</details>
|
|
5472
5733
|
|
|
5473
5734
|
<!-- Variables the selected template's mapping consumes (read-only guidance) -->
|
|
5474
|
-
<details
|
|
5475
|
-
|
|
5735
|
+
<details
|
|
5736
|
+
*ngIf="hasReferencedPaths"
|
|
5737
|
+
class="used-by-template"
|
|
5738
|
+
data-testid="epistola-override-used-by-template"
|
|
5739
|
+
>
|
|
5740
|
+
<summary
|
|
5741
|
+
class="used-by-template__label"
|
|
5742
|
+
data-testid="epistola-override-used-by-template-summary"
|
|
5743
|
+
>
|
|
5476
5744
|
Used by this template ({{ referencedPaths.length }})
|
|
5477
5745
|
</summary>
|
|
5478
5746
|
<p class="used-by-template__hint">
|
|
5479
5747
|
This template's data mapping reads these inputs — the paths worth overriding for the
|
|
5480
5748
|
preview.
|
|
5481
5749
|
</p>
|
|
5482
|
-
<ul class="used-by-template__list">
|
|
5483
|
-
<li *ngFor="let ref of referencedPaths">
|
|
5484
|
-
<code>{{
|
|
5750
|
+
<ul class="used-by-template__list" data-testid="epistola-override-used-by-template-list">
|
|
5751
|
+
<li *ngFor="let ref of referencedPaths; let i = index">
|
|
5752
|
+
<code [attr.data-testid]="'epistola-override-used-by-template-item-' + i">{{
|
|
5753
|
+
formatReferencedPath(ref)
|
|
5754
|
+
}}</code>
|
|
5485
5755
|
</li>
|
|
5486
5756
|
</ul>
|
|
5487
5757
|
</details>
|
|
@@ -5495,21 +5765,35 @@ class EpistolaOverrideBuilderComponent {
|
|
|
5495
5765
|
</datalist>
|
|
5496
5766
|
|
|
5497
5767
|
<!-- Simple mode: table -->
|
|
5498
|
-
<div *ngIf="!advancedMode" class="builder-table">
|
|
5499
|
-
<div
|
|
5768
|
+
<div *ngIf="!advancedMode" class="builder-table" data-testid="epistola-override-table">
|
|
5769
|
+
<div
|
|
5770
|
+
*ngIf="rows.length > 0"
|
|
5771
|
+
class="table-header"
|
|
5772
|
+
data-testid="epistola-override-table-header"
|
|
5773
|
+
>
|
|
5500
5774
|
<span class="col-scope">Scope</span>
|
|
5501
5775
|
<span class="col-path">Input Path</span>
|
|
5502
5776
|
<span class="col-field">Form Field</span>
|
|
5503
5777
|
<span class="col-action"></span>
|
|
5504
5778
|
</div>
|
|
5505
|
-
<div
|
|
5506
|
-
|
|
5779
|
+
<div
|
|
5780
|
+
*ngFor="let row of rows; let i = index"
|
|
5781
|
+
class="table-row"
|
|
5782
|
+
[attr.data-testid]="'epistola-override-row-' + i"
|
|
5783
|
+
>
|
|
5784
|
+
<select
|
|
5785
|
+
class="col-scope"
|
|
5786
|
+
[attr.data-testid]="'epistola-override-row-scope-' + i"
|
|
5787
|
+
[(ngModel)]="row.scope"
|
|
5788
|
+
(ngModelChange)="emitChange()"
|
|
5789
|
+
>
|
|
5507
5790
|
<option value="doc">doc</option>
|
|
5508
5791
|
<option value="pv">pv</option>
|
|
5509
5792
|
</select>
|
|
5510
5793
|
<input
|
|
5511
5794
|
class="col-path"
|
|
5512
5795
|
type="text"
|
|
5796
|
+
[attr.data-testid]="'epistola-override-row-path-' + i"
|
|
5513
5797
|
[(ngModel)]="row.inputPath"
|
|
5514
5798
|
(ngModelChange)="emitChange()"
|
|
5515
5799
|
[attr.list]="'epistola-override-paths-' + row.scope"
|
|
@@ -5519,6 +5803,7 @@ class EpistolaOverrideBuilderComponent {
|
|
|
5519
5803
|
<select
|
|
5520
5804
|
*ngIf="availableFields.length > 0"
|
|
5521
5805
|
class="col-field"
|
|
5806
|
+
[attr.data-testid]="'epistola-override-row-field-' + i"
|
|
5522
5807
|
[(ngModel)]="row.formFieldKey"
|
|
5523
5808
|
(ngModelChange)="emitChange()"
|
|
5524
5809
|
>
|
|
@@ -5531,25 +5816,41 @@ class EpistolaOverrideBuilderComponent {
|
|
|
5531
5816
|
*ngIf="availableFields.length === 0"
|
|
5532
5817
|
class="col-field"
|
|
5533
5818
|
type="text"
|
|
5819
|
+
[attr.data-testid]="'epistola-override-row-field-' + i"
|
|
5534
5820
|
[(ngModel)]="row.formFieldKey"
|
|
5535
5821
|
(ngModelChange)="emitChange()"
|
|
5536
5822
|
placeholder="form field key"
|
|
5537
5823
|
/>
|
|
5538
|
-
<button
|
|
5824
|
+
<button
|
|
5825
|
+
type="button"
|
|
5826
|
+
class="col-action remove-btn"
|
|
5827
|
+
[attr.data-testid]="'epistola-override-row-remove-' + i"
|
|
5828
|
+
(click)="removeRow(i)"
|
|
5829
|
+
>
|
|
5539
5830
|
<i class="mdi mdi-close"></i>
|
|
5540
5831
|
</button>
|
|
5541
5832
|
</div>
|
|
5542
|
-
<button
|
|
5833
|
+
<button
|
|
5834
|
+
type="button"
|
|
5835
|
+
class="add-btn"
|
|
5836
|
+
data-testid="epistola-override-add"
|
|
5837
|
+
(click)="addRow()"
|
|
5838
|
+
>
|
|
5543
5839
|
<i class="mdi mdi-plus mr-1"></i> Add override
|
|
5544
5840
|
</button>
|
|
5545
5841
|
</div>
|
|
5546
5842
|
|
|
5547
5843
|
<!-- Advanced mode: JSONata editor over $form -->
|
|
5548
|
-
<div *ngIf="advancedMode" class="builder-advanced">
|
|
5549
|
-
<div
|
|
5844
|
+
<div *ngIf="advancedMode" class="builder-advanced" data-testid="epistola-override-advanced">
|
|
5845
|
+
<div
|
|
5846
|
+
*ngIf="simpleUnavailable"
|
|
5847
|
+
class="advanced-note"
|
|
5848
|
+
data-testid="epistola-override-advanced-note"
|
|
5849
|
+
>
|
|
5550
5850
|
This expression is too rich for the simple table — edit it here.
|
|
5551
5851
|
</div>
|
|
5552
5852
|
<epistola-jsonata-editor
|
|
5853
|
+
data-testid="epistola-override-jsonata-editor"
|
|
5553
5854
|
[expression]="expression"
|
|
5554
5855
|
[contextVariables]="editorContextVariables"
|
|
5555
5856
|
variablesHint="$form"
|
|
@@ -5566,12 +5867,15 @@ class EpistolaOverrideBuilderComponent {
|
|
|
5566
5867
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaOverrideBuilderComponent, decorators: [{
|
|
5567
5868
|
type: Component,
|
|
5568
5869
|
args: [{ standalone: true, imports: [CommonModule, FormsModule, JsonataEditorComponent], selector: 'epistola-override-builder-component', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
5569
|
-
<div class="override-builder">
|
|
5870
|
+
<div class="override-builder" data-testid="epistola-override-root">
|
|
5570
5871
|
<div class="builder-header">
|
|
5571
|
-
<span class="builder-label">{{
|
|
5872
|
+
<span class="builder-label" data-testid="epistola-override-label">{{
|
|
5873
|
+
label || 'Input Overrides'
|
|
5874
|
+
}}</span>
|
|
5572
5875
|
<button
|
|
5573
5876
|
type="button"
|
|
5574
5877
|
class="mode-toggle"
|
|
5878
|
+
data-testid="epistola-override-mode-toggle"
|
|
5575
5879
|
[disabled]="simpleUnavailable && !advancedMode"
|
|
5576
5880
|
(click)="toggleMode()"
|
|
5577
5881
|
>
|
|
@@ -5584,8 +5888,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
5584
5888
|
Make the preview reflect what the user is typing — <em>before</em> they submit — by feeding
|
|
5585
5889
|
live form values into the document inputs.
|
|
5586
5890
|
</p>
|
|
5587
|
-
<details class="builder-help">
|
|
5588
|
-
<summary>When should I map a field?</summary>
|
|
5891
|
+
<details class="builder-help" data-testid="epistola-override-help">
|
|
5892
|
+
<summary data-testid="epistola-override-help-summary">When should I map a field?</summary>
|
|
5589
5893
|
<ul>
|
|
5590
5894
|
<li>
|
|
5591
5895
|
<strong>Map</strong> a field when its value ends up in the generated document — i.e. the
|
|
@@ -5611,17 +5915,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
5611
5915
|
</details>
|
|
5612
5916
|
|
|
5613
5917
|
<!-- Variables the selected template's mapping consumes (read-only guidance) -->
|
|
5614
|
-
<details
|
|
5615
|
-
|
|
5918
|
+
<details
|
|
5919
|
+
*ngIf="hasReferencedPaths"
|
|
5920
|
+
class="used-by-template"
|
|
5921
|
+
data-testid="epistola-override-used-by-template"
|
|
5922
|
+
>
|
|
5923
|
+
<summary
|
|
5924
|
+
class="used-by-template__label"
|
|
5925
|
+
data-testid="epistola-override-used-by-template-summary"
|
|
5926
|
+
>
|
|
5616
5927
|
Used by this template ({{ referencedPaths.length }})
|
|
5617
5928
|
</summary>
|
|
5618
5929
|
<p class="used-by-template__hint">
|
|
5619
5930
|
This template's data mapping reads these inputs — the paths worth overriding for the
|
|
5620
5931
|
preview.
|
|
5621
5932
|
</p>
|
|
5622
|
-
<ul class="used-by-template__list">
|
|
5623
|
-
<li *ngFor="let ref of referencedPaths">
|
|
5624
|
-
<code>{{
|
|
5933
|
+
<ul class="used-by-template__list" data-testid="epistola-override-used-by-template-list">
|
|
5934
|
+
<li *ngFor="let ref of referencedPaths; let i = index">
|
|
5935
|
+
<code [attr.data-testid]="'epistola-override-used-by-template-item-' + i">{{
|
|
5936
|
+
formatReferencedPath(ref)
|
|
5937
|
+
}}</code>
|
|
5625
5938
|
</li>
|
|
5626
5939
|
</ul>
|
|
5627
5940
|
</details>
|
|
@@ -5635,21 +5948,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
5635
5948
|
</datalist>
|
|
5636
5949
|
|
|
5637
5950
|
<!-- Simple mode: table -->
|
|
5638
|
-
<div *ngIf="!advancedMode" class="builder-table">
|
|
5639
|
-
<div
|
|
5951
|
+
<div *ngIf="!advancedMode" class="builder-table" data-testid="epistola-override-table">
|
|
5952
|
+
<div
|
|
5953
|
+
*ngIf="rows.length > 0"
|
|
5954
|
+
class="table-header"
|
|
5955
|
+
data-testid="epistola-override-table-header"
|
|
5956
|
+
>
|
|
5640
5957
|
<span class="col-scope">Scope</span>
|
|
5641
5958
|
<span class="col-path">Input Path</span>
|
|
5642
5959
|
<span class="col-field">Form Field</span>
|
|
5643
5960
|
<span class="col-action"></span>
|
|
5644
5961
|
</div>
|
|
5645
|
-
<div
|
|
5646
|
-
|
|
5962
|
+
<div
|
|
5963
|
+
*ngFor="let row of rows; let i = index"
|
|
5964
|
+
class="table-row"
|
|
5965
|
+
[attr.data-testid]="'epistola-override-row-' + i"
|
|
5966
|
+
>
|
|
5967
|
+
<select
|
|
5968
|
+
class="col-scope"
|
|
5969
|
+
[attr.data-testid]="'epistola-override-row-scope-' + i"
|
|
5970
|
+
[(ngModel)]="row.scope"
|
|
5971
|
+
(ngModelChange)="emitChange()"
|
|
5972
|
+
>
|
|
5647
5973
|
<option value="doc">doc</option>
|
|
5648
5974
|
<option value="pv">pv</option>
|
|
5649
5975
|
</select>
|
|
5650
5976
|
<input
|
|
5651
5977
|
class="col-path"
|
|
5652
5978
|
type="text"
|
|
5979
|
+
[attr.data-testid]="'epistola-override-row-path-' + i"
|
|
5653
5980
|
[(ngModel)]="row.inputPath"
|
|
5654
5981
|
(ngModelChange)="emitChange()"
|
|
5655
5982
|
[attr.list]="'epistola-override-paths-' + row.scope"
|
|
@@ -5659,6 +5986,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
5659
5986
|
<select
|
|
5660
5987
|
*ngIf="availableFields.length > 0"
|
|
5661
5988
|
class="col-field"
|
|
5989
|
+
[attr.data-testid]="'epistola-override-row-field-' + i"
|
|
5662
5990
|
[(ngModel)]="row.formFieldKey"
|
|
5663
5991
|
(ngModelChange)="emitChange()"
|
|
5664
5992
|
>
|
|
@@ -5671,25 +5999,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
5671
5999
|
*ngIf="availableFields.length === 0"
|
|
5672
6000
|
class="col-field"
|
|
5673
6001
|
type="text"
|
|
6002
|
+
[attr.data-testid]="'epistola-override-row-field-' + i"
|
|
5674
6003
|
[(ngModel)]="row.formFieldKey"
|
|
5675
6004
|
(ngModelChange)="emitChange()"
|
|
5676
6005
|
placeholder="form field key"
|
|
5677
6006
|
/>
|
|
5678
|
-
<button
|
|
6007
|
+
<button
|
|
6008
|
+
type="button"
|
|
6009
|
+
class="col-action remove-btn"
|
|
6010
|
+
[attr.data-testid]="'epistola-override-row-remove-' + i"
|
|
6011
|
+
(click)="removeRow(i)"
|
|
6012
|
+
>
|
|
5679
6013
|
<i class="mdi mdi-close"></i>
|
|
5680
6014
|
</button>
|
|
5681
6015
|
</div>
|
|
5682
|
-
<button
|
|
6016
|
+
<button
|
|
6017
|
+
type="button"
|
|
6018
|
+
class="add-btn"
|
|
6019
|
+
data-testid="epistola-override-add"
|
|
6020
|
+
(click)="addRow()"
|
|
6021
|
+
>
|
|
5683
6022
|
<i class="mdi mdi-plus mr-1"></i> Add override
|
|
5684
6023
|
</button>
|
|
5685
6024
|
</div>
|
|
5686
6025
|
|
|
5687
6026
|
<!-- Advanced mode: JSONata editor over $form -->
|
|
5688
|
-
<div *ngIf="advancedMode" class="builder-advanced">
|
|
5689
|
-
<div
|
|
6027
|
+
<div *ngIf="advancedMode" class="builder-advanced" data-testid="epistola-override-advanced">
|
|
6028
|
+
<div
|
|
6029
|
+
*ngIf="simpleUnavailable"
|
|
6030
|
+
class="advanced-note"
|
|
6031
|
+
data-testid="epistola-override-advanced-note"
|
|
6032
|
+
>
|
|
5690
6033
|
This expression is too rich for the simple table — edit it here.
|
|
5691
6034
|
</div>
|
|
5692
6035
|
<epistola-jsonata-editor
|
|
6036
|
+
data-testid="epistola-override-jsonata-editor"
|
|
5693
6037
|
[expression]="expression"
|
|
5694
6038
|
[contextVariables]="editorContextVariables"
|
|
5695
6039
|
variablesHint="$form"
|
|
@@ -5949,40 +6293,62 @@ class EpistolaProcessLinkSelectorComponent {
|
|
|
5949
6293
|
}
|
|
5950
6294
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaProcessLinkSelectorComponent, deps: [{ token: EpistolaAdminService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
5951
6295
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaProcessLinkSelectorComponent, isStandalone: true, selector: "epistola-process-link-selector-component", inputs: { value: "value", disabled: "disabled", label: "label" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
5952
|
-
<div class="process-link-selector">
|
|
5953
|
-
<label class="selector-label">{{
|
|
6296
|
+
<div class="process-link-selector" data-testid="epistola-process-link-container">
|
|
6297
|
+
<label class="selector-label" data-testid="epistola-process-link-label">{{
|
|
6298
|
+
label || 'Process Link'
|
|
6299
|
+
}}</label>
|
|
5954
6300
|
<select
|
|
5955
6301
|
class="selector-dropdown"
|
|
6302
|
+
data-testid="epistola-process-link-select"
|
|
5956
6303
|
[ngModel]="selectedKey"
|
|
5957
6304
|
(ngModelChange)="onSelect($event)"
|
|
5958
6305
|
[disabled]="disabled || loading"
|
|
5959
6306
|
>
|
|
5960
|
-
<option value=""
|
|
5961
|
-
|
|
6307
|
+
<option value="" data-testid="epistola-process-link-option-empty">
|
|
6308
|
+
{{ loading ? 'Loading...' : '-- Select a process link --' }}
|
|
6309
|
+
</option>
|
|
6310
|
+
<option
|
|
6311
|
+
*ngFor="let entry of filteredEntries"
|
|
6312
|
+
[value]="entryKey(entry)"
|
|
6313
|
+
[attr.data-testid]="'epistola-process-link-option-' + entryKey(entry)"
|
|
6314
|
+
>
|
|
5962
6315
|
{{ entry.processDefinitionName }} / {{ entry.activityName }} ({{ entry.activityId }})
|
|
5963
6316
|
</option>
|
|
5964
6317
|
</select>
|
|
5965
|
-
<div *ngIf="error" class="selector-error"
|
|
6318
|
+
<div *ngIf="error" class="selector-error" data-testid="epistola-process-link-error">
|
|
6319
|
+
{{ error }}
|
|
6320
|
+
</div>
|
|
5966
6321
|
</div>
|
|
5967
6322
|
`, isInline: true, styles: [".process-link-selector{margin-bottom:.5rem}.selector-label{display:block;font-weight:600;font-size:.85rem;color:#495057;margin-bottom:.25rem}.selector-dropdown{width:100%;border:1px solid #ced4da;border-radius:4px;padding:.4rem .5rem;font-size:.85rem;background:#fff}.selector-error{color:#dc3545;font-size:.75rem;margin-top:.25rem}\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: "ngmodule", type: FormsModule }, { kind: "directive", type: i3$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5968
6323
|
}
|
|
5969
6324
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaProcessLinkSelectorComponent, decorators: [{
|
|
5970
6325
|
type: Component,
|
|
5971
6326
|
args: [{ standalone: true, imports: [CommonModule, FormsModule], selector: 'epistola-process-link-selector-component', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
5972
|
-
<div class="process-link-selector">
|
|
5973
|
-
<label class="selector-label">{{
|
|
6327
|
+
<div class="process-link-selector" data-testid="epistola-process-link-container">
|
|
6328
|
+
<label class="selector-label" data-testid="epistola-process-link-label">{{
|
|
6329
|
+
label || 'Process Link'
|
|
6330
|
+
}}</label>
|
|
5974
6331
|
<select
|
|
5975
6332
|
class="selector-dropdown"
|
|
6333
|
+
data-testid="epistola-process-link-select"
|
|
5976
6334
|
[ngModel]="selectedKey"
|
|
5977
6335
|
(ngModelChange)="onSelect($event)"
|
|
5978
6336
|
[disabled]="disabled || loading"
|
|
5979
6337
|
>
|
|
5980
|
-
<option value=""
|
|
5981
|
-
|
|
6338
|
+
<option value="" data-testid="epistola-process-link-option-empty">
|
|
6339
|
+
{{ loading ? 'Loading...' : '-- Select a process link --' }}
|
|
6340
|
+
</option>
|
|
6341
|
+
<option
|
|
6342
|
+
*ngFor="let entry of filteredEntries"
|
|
6343
|
+
[value]="entryKey(entry)"
|
|
6344
|
+
[attr.data-testid]="'epistola-process-link-option-' + entryKey(entry)"
|
|
6345
|
+
>
|
|
5982
6346
|
{{ entry.processDefinitionName }} / {{ entry.activityName }} ({{ entry.activityId }})
|
|
5983
6347
|
</option>
|
|
5984
6348
|
</select>
|
|
5985
|
-
<div *ngIf="error" class="selector-error"
|
|
6349
|
+
<div *ngIf="error" class="selector-error" data-testid="epistola-process-link-error">
|
|
6350
|
+
{{ error }}
|
|
6351
|
+
</div>
|
|
5986
6352
|
</div>
|
|
5987
6353
|
`, styles: [".process-link-selector{margin-bottom:.5rem}.selector-label{display:block;font-weight:600;font-size:.85rem;color:#495057;margin-bottom:.25rem}.selector-dropdown{width:100%;border:1px solid #ced4da;border-radius:4px;padding:.4rem .5rem;font-size:.85rem;background:#fff}.selector-error{color:#dc3545;font-size:.75rem;margin-top:.25rem}\n"] }]
|
|
5988
6354
|
}], ctorParameters: () => [{ type: EpistolaAdminService }, { type: i0.ChangeDetectorRef }], propDecorators: { value: [{
|