@epistola.app/valtimo-plugin 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/epistola.app-valtimo-plugin.mjs +801 -459
- package/fesm2022/epistola.app-valtimo-plugin.mjs.map +1 -1
- package/lib/components/check-job-status-configuration/check-job-status-configuration.component.d.ts +4 -1
- package/lib/components/download-document-configuration/download-document-configuration.component.d.ts +8 -1
- package/lib/components/epistola-admin-page/epistola-admin-page.component.d.ts +44 -3
- package/lib/components/epistola-document/epistola-document.component.d.ts +65 -0
- package/lib/components/{epistola-download/epistola-download.formio.d.ts → epistola-document/epistola-document.formio.d.ts} +2 -2
- package/lib/components/epistola-document-preview/epistola-document-preview.component.d.ts +13 -25
- package/lib/components/epistola-retry-form/epistola-retry-form.component.d.ts +3 -7
- package/lib/epistola.module.d.ts +4 -5
- package/lib/models/admin.d.ts +72 -0
- package/lib/models/config.d.ts +7 -11
- package/lib/services/epistola-admin.service.d.ts +32 -1
- package/lib/services/epistola-plugin.service.d.ts +46 -7
- package/lib/services/epistola-task-context.interceptor.d.ts +29 -0
- package/lib/services/epistola-task-context.matcher.d.ts +19 -0
- package/lib/services/epistola-task-context.service.d.ts +26 -0
- package/lib/services/index.d.ts +2 -0
- package/package.json +1 -1
- package/public_api.d.ts +2 -4
- package/sbom.json +1 -0
- package/lib/components/epistola-download/epistola-download.component.d.ts +0 -24
- package/lib/components/epistola-preview-button/epistola-preview-button.component.d.ts +0 -35
- package/lib/components/epistola-preview-button/epistola-preview-button.formio.d.ts +0 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Injectable, EventEmitter, Output, Input, Component, ChangeDetectionStrategy, inject, NgModule, ENVIRONMENT_INITIALIZER, Injector } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common/http';
|
|
4
|
-
import { HttpHeaders, HttpClientModule } from '@angular/common/http';
|
|
4
|
+
import { HttpHeaders, HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
|
|
5
5
|
import * as i2 from '@valtimo/shared';
|
|
6
6
|
import { ROLE_ADMIN } from '@valtimo/shared';
|
|
7
7
|
import { of, BehaviorSubject, combineLatest, take, Subject, debounceTime, takeUntil, merge } from 'rxjs';
|
|
@@ -16,12 +16,12 @@ import * as i2$2 from '@angular/forms';
|
|
|
16
16
|
import { FormsModule } from '@angular/forms';
|
|
17
17
|
import * as _jsonata from 'jsonata';
|
|
18
18
|
import * as i2$3 from '@valtimo/process-link';
|
|
19
|
-
import * as
|
|
19
|
+
import * as i2$4 from '@angular/platform-browser';
|
|
20
|
+
import * as i5 from '@formio/angular';
|
|
20
21
|
import { FormioModule } from '@formio/angular';
|
|
21
|
-
import * as
|
|
22
|
-
import * as i2$4 from '@angular/router';
|
|
22
|
+
import * as i2$5 from '@angular/router';
|
|
23
23
|
import { RouterModule, Router } from '@angular/router';
|
|
24
|
-
import * as i5 from 'carbon-components-angular/tabs';
|
|
24
|
+
import * as i5$1 from 'carbon-components-angular/tabs';
|
|
25
25
|
import { TabsModule } from 'carbon-components-angular/tabs';
|
|
26
26
|
import * as i6 from 'carbon-components-angular/tag';
|
|
27
27
|
import { TagModule } from 'carbon-components-angular/tag';
|
|
@@ -77,6 +77,47 @@ class EpistolaAdminService {
|
|
|
77
77
|
getPendingJobs() {
|
|
78
78
|
return this.http.get(`${this.apiEndpoint}/pending`);
|
|
79
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Manually reconcile a stuck Epistola catch event by querying Epistola for the
|
|
82
|
+
* job's current status and re-running message correlation. Returns 200 with
|
|
83
|
+
* `correlated=true` on success, 409 with `correlated=false` when the job is
|
|
84
|
+
* still in flight, or surfaces a validation error when the execution can't be
|
|
85
|
+
* recovered (no subscription, missing variable, unknown tenant).
|
|
86
|
+
*/
|
|
87
|
+
reconcilePending(executionId) {
|
|
88
|
+
return this.http.post(`${this.apiEndpoint}/pending/${encodeURIComponent(executionId)}/reconcile`, null);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get the latest BPMN race-safety validation violations across deployed
|
|
92
|
+
* process definitions. Empty list = healthy.
|
|
93
|
+
*/
|
|
94
|
+
getValidationViolations() {
|
|
95
|
+
return this.http.get(`${this.apiEndpoint}/validations`);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* List the classpath catalogs available to manually redeploy for a plugin
|
|
99
|
+
* configuration, each annotated with the version last deployed in this
|
|
100
|
+
* backend process.
|
|
101
|
+
*/
|
|
102
|
+
getClasspathCatalogs(configurationId) {
|
|
103
|
+
return this.http.get(`${this.apiEndpoint}/configurations/${encodeURIComponent(configurationId)}/catalogs`);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Force-redeploy a single classpath catalog to the configuration's Epistola
|
|
107
|
+
* installation. Explicit operator action — bypasses the templateSyncEnabled
|
|
108
|
+
* gate and the version-skip check. Returns 200 with per-resource counts on
|
|
109
|
+
* success, or 502 (error callback, body carries `errorMessage`) on failure.
|
|
110
|
+
*/
|
|
111
|
+
redeployCatalog(configurationId, slug) {
|
|
112
|
+
return this.http.post(`${this.apiEndpoint}/configurations/${encodeURIComponent(configurationId)}/catalogs/${encodeURIComponent(slug)}/redeploy`, null);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the plugin CHANGELOG parsed (server-side) into structured releases for
|
|
116
|
+
* the admin Changelog tab — no markdown renderer needed on the client.
|
|
117
|
+
*/
|
|
118
|
+
getChangelog() {
|
|
119
|
+
return this.http.get(`${this.apiEndpoint}/changelog`);
|
|
120
|
+
}
|
|
80
121
|
/**
|
|
81
122
|
* Export a single process link as a .process-link.json file.
|
|
82
123
|
*/
|
|
@@ -220,9 +261,11 @@ class EpistolaPluginService {
|
|
|
220
261
|
}
|
|
221
262
|
/**
|
|
222
263
|
* Get a dynamically generated Formio form for retrying a failed document generation.
|
|
264
|
+
*
|
|
265
|
+
* @param taskId Operaton user task id (required — backend authorizes via OperatonTask:VIEW)
|
|
223
266
|
*/
|
|
224
|
-
getRetryForm(processInstanceId, documentId, sourceActivityId) {
|
|
225
|
-
const params = { processInstanceId };
|
|
267
|
+
getRetryForm(taskId, processInstanceId, documentId, sourceActivityId) {
|
|
268
|
+
const params = { taskId, processInstanceId };
|
|
226
269
|
if (documentId) {
|
|
227
270
|
params['documentId'] = documentId;
|
|
228
271
|
}
|
|
@@ -245,24 +288,36 @@ class EpistolaPluginService {
|
|
|
245
288
|
return this.http.post(`${this.apiEndpoint}/validate-jsonata`, request);
|
|
246
289
|
}
|
|
247
290
|
/**
|
|
248
|
-
*
|
|
291
|
+
* Preview a document by dry-running the {@code generate-document} process
|
|
292
|
+
* link. Returns the rendered PDF as a {@link Blob}.
|
|
293
|
+
*
|
|
294
|
+
* <p>The {@code X-Skip-Interceptor: 422} header tells the global Valtimo
|
|
295
|
+
* error interceptor to skip its toast for validation failures so the
|
|
296
|
+
* caller can render an inline error message.
|
|
249
297
|
*/
|
|
250
|
-
|
|
251
|
-
return this.http.
|
|
252
|
-
|
|
298
|
+
previewToBlob(request) {
|
|
299
|
+
return this.http.post(`${this.apiEndpoint}/preview`, request, {
|
|
300
|
+
responseType: 'blob',
|
|
301
|
+
headers: new HttpHeaders().set('X-Skip-Interceptor', '422'),
|
|
253
302
|
});
|
|
254
303
|
}
|
|
255
304
|
/**
|
|
256
|
-
*
|
|
257
|
-
*
|
|
305
|
+
* Stream an already-generated Epistola PDF for the caller's task. The
|
|
306
|
+
* backend resolves the Epistola document id and tenant id from the named
|
|
307
|
+
* process variables on the task's process instance, so the wire never
|
|
308
|
+
* carries a raw PDF id.
|
|
258
309
|
*/
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
310
|
+
downloadDocumentBlob(request) {
|
|
311
|
+
const params = new URLSearchParams({
|
|
312
|
+
taskId: request.taskId,
|
|
313
|
+
caseDocumentId: request.caseDocumentId,
|
|
314
|
+
documentVariable: request.documentVariable,
|
|
315
|
+
tenantIdVariable: request.tenantIdVariable,
|
|
316
|
+
filename: request.filename,
|
|
317
|
+
disposition: request.disposition,
|
|
318
|
+
});
|
|
319
|
+
return this.http.get(`${this.apiEndpoint}/documents/download?${params.toString()}`, {
|
|
320
|
+
responseType: 'blob',
|
|
266
321
|
});
|
|
267
322
|
}
|
|
268
323
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPluginService, deps: [{ token: i1.HttpClient }, { token: i2.ConfigService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
@@ -272,6 +327,101 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
272
327
|
type: Injectable
|
|
273
328
|
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.ConfigService }] });
|
|
274
329
|
|
|
330
|
+
/**
|
|
331
|
+
* Holds the currently-open Operaton user task instance id so that custom
|
|
332
|
+
* Formio components rendered inside a Valtimo task form (preview, download,
|
|
333
|
+
* retry-form) can include it in backend requests for PBAC.
|
|
334
|
+
*
|
|
335
|
+
* Populated by {@link EpistolaTaskContextInterceptor}, which sniffs Valtimo's
|
|
336
|
+
* canonical "load process link for task" GET (`/api/v2/process-link/task/{taskId}`)
|
|
337
|
+
* — the request always fires when a task opens, before the form renders.
|
|
338
|
+
*
|
|
339
|
+
* <p><b>Why this exists:</b> Valtimo 13.21 does not expose the active task
|
|
340
|
+
* instance id through any service that custom Formio components can inject
|
|
341
|
+
* (`FormIoStateService` carries documentId and processInstanceId only;
|
|
342
|
+
* `TaskDetailContentComponent.taskInstanceId$` is private to that component
|
|
343
|
+
* and Formio elements live in their own injector tree). This service is a
|
|
344
|
+
* workaround until upstream exposes `taskInstanceId` via `FormIoStateService`.
|
|
345
|
+
*/
|
|
346
|
+
class EpistolaTaskContextService {
|
|
347
|
+
_taskInstanceId$ = new BehaviorSubject(null);
|
|
348
|
+
taskInstanceId$ = this._taskInstanceId$.asObservable();
|
|
349
|
+
get taskInstanceId() {
|
|
350
|
+
return this._taskInstanceId$.value;
|
|
351
|
+
}
|
|
352
|
+
setTaskInstanceId(id) {
|
|
353
|
+
this._taskInstanceId$.next(id);
|
|
354
|
+
}
|
|
355
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaTaskContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
356
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaTaskContextService, providedIn: 'root' });
|
|
357
|
+
}
|
|
358
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaTaskContextService, decorators: [{
|
|
359
|
+
type: Injectable,
|
|
360
|
+
args: [{ providedIn: 'root' }]
|
|
361
|
+
}] });
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Pure helpers for {@link EpistolaTaskContextInterceptor}, extracted so they
|
|
365
|
+
* can be unit-tested without pulling in {@code @angular/core} (which ts-jest
|
|
366
|
+
* cannot transform without {@code jest-preset-angular}).
|
|
367
|
+
*/
|
|
368
|
+
/**
|
|
369
|
+
* Pattern Valtimo uses for the canonical task-open call:
|
|
370
|
+
* {@code GET /api/v2/process-link/task/{taskInstanceId}}.
|
|
371
|
+
*
|
|
372
|
+
* Captures the {@code taskInstanceId} (UUID v4-style 36-character hyphenated
|
|
373
|
+
* hex string). Anchored at the end of the URL or at a query-string delimiter
|
|
374
|
+
* so we don't accidentally match a longer trailing segment.
|
|
375
|
+
*/
|
|
376
|
+
const TASK_PROCESS_LINK_PATTERN = /\/api\/v2\/process-link\/task\/([0-9a-fA-F-]{36})(?:\?|$)/;
|
|
377
|
+
/**
|
|
378
|
+
* Returns the captured {@code taskInstanceId} from a Valtimo task-open
|
|
379
|
+
* request URL, or {@code null} if the request does not match.
|
|
380
|
+
*/
|
|
381
|
+
function extractTaskInstanceIdFromUrl(method, url) {
|
|
382
|
+
if (method !== 'GET')
|
|
383
|
+
return null;
|
|
384
|
+
const match = TASK_PROCESS_LINK_PATTERN.exec(url);
|
|
385
|
+
return match ? match[1] : null;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Sniffs Valtimo's task-open signal and pushes the active taskInstanceId into
|
|
390
|
+
* {@link EpistolaTaskContextService}. The signal is the canonical
|
|
391
|
+
* {@code GET /api/v2/process-link/task/{taskId}} call that
|
|
392
|
+
* {@code TaskDetailContentComponent.loadTaskDetails(...)} fires unconditionally
|
|
393
|
+
* before any task form is rendered (see @valtimo/task internals).
|
|
394
|
+
*
|
|
395
|
+
* <p>This interceptor does <b>not</b> modify the outgoing request. It only
|
|
396
|
+
* captures the taskId from the URL.
|
|
397
|
+
*
|
|
398
|
+
* <p>Workaround for Valtimo 13.21 not exposing taskInstanceId through any
|
|
399
|
+
* injectable service. Remove once upstream adds e.g.
|
|
400
|
+
* {@code FormIoStateService.setTaskInstanceId(...)}.
|
|
401
|
+
*
|
|
402
|
+
* <p>The actual URL-matching logic lives in
|
|
403
|
+
* {@link extractTaskInstanceIdFromUrl} so it can be unit-tested without an
|
|
404
|
+
* Angular harness.
|
|
405
|
+
*/
|
|
406
|
+
class EpistolaTaskContextInterceptor {
|
|
407
|
+
taskContext;
|
|
408
|
+
constructor(taskContext) {
|
|
409
|
+
this.taskContext = taskContext;
|
|
410
|
+
}
|
|
411
|
+
intercept(request, next) {
|
|
412
|
+
const taskId = extractTaskInstanceIdFromUrl(request.method, request.url);
|
|
413
|
+
if (taskId !== null && taskId !== this.taskContext.taskInstanceId) {
|
|
414
|
+
this.taskContext.setTaskInstanceId(taskId);
|
|
415
|
+
}
|
|
416
|
+
return next.handle(request);
|
|
417
|
+
}
|
|
418
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaTaskContextInterceptor, deps: [{ token: EpistolaTaskContextService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
419
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaTaskContextInterceptor });
|
|
420
|
+
}
|
|
421
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaTaskContextInterceptor, decorators: [{
|
|
422
|
+
type: Injectable
|
|
423
|
+
}], ctorParameters: () => [{ type: EpistolaTaskContextService }] });
|
|
424
|
+
|
|
275
425
|
class EpistolaConfigurationComponent {
|
|
276
426
|
save$;
|
|
277
427
|
disabled$;
|
|
@@ -1983,9 +2133,17 @@ class CheckJobStatusConfigurationComponent {
|
|
|
1983
2133
|
saveSubscription;
|
|
1984
2134
|
formValue$ = new BehaviorSubject(null);
|
|
1985
2135
|
valid$ = new BehaviorSubject(false);
|
|
2136
|
+
/** Resolved synchronously before the v-form renders — see download-document-configuration for the why. */
|
|
2137
|
+
resolvedPrefill = {};
|
|
2138
|
+
prefillResolved$ = new BehaviorSubject(false);
|
|
1986
2139
|
safeDisabled$;
|
|
1987
2140
|
ngOnInit() {
|
|
1988
2141
|
this.safeDisabled$ = this.disabled$.pipe(startWith(true), delay(0));
|
|
2142
|
+
const prefill$ = this.prefillConfiguration$ ?? of({});
|
|
2143
|
+
prefill$.pipe(take(1)).subscribe((prefill) => {
|
|
2144
|
+
this.resolvedPrefill = prefill ?? {};
|
|
2145
|
+
this.prefillResolved$.next(true);
|
|
2146
|
+
});
|
|
1989
2147
|
this.openSaveSubscription();
|
|
1990
2148
|
}
|
|
1991
2149
|
ngOnDestroy() {
|
|
@@ -2013,11 +2171,11 @@ class CheckJobStatusConfigurationComponent {
|
|
|
2013
2171
|
});
|
|
2014
2172
|
}
|
|
2015
2173
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CheckJobStatusConfigurationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2016
|
-
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
|
|
2174
|
+
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 (valueChange)=\"formValueChange($event)\" *ngIf=\"prefillResolved$ | async\">\n <v-input\n name=\"requestIdVariable\"\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 [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 [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 [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"] }] });
|
|
2017
2175
|
}
|
|
2018
2176
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CheckJobStatusConfigurationComponent, decorators: [{
|
|
2019
2177
|
type: Component,
|
|
2020
|
-
args: [{ selector: 'epistola-check-job-status-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form
|
|
2178
|
+
args: [{ selector: 'epistola-check-job-status-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form (valueChange)=\"formValueChange($event)\" *ngIf=\"prefillResolved$ | async\">\n <v-input\n name=\"requestIdVariable\"\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 [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 [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 [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" }]
|
|
2021
2179
|
}], propDecorators: { save$: [{
|
|
2022
2180
|
type: Input
|
|
2023
2181
|
}], disabled$: [{
|
|
@@ -2042,9 +2200,21 @@ class DownloadDocumentConfigurationComponent {
|
|
|
2042
2200
|
saveSubscription;
|
|
2043
2201
|
formValue$ = new BehaviorSubject(null);
|
|
2044
2202
|
valid$ = new BehaviorSubject(false);
|
|
2203
|
+
/**
|
|
2204
|
+
* Resolved prefill — populated synchronously before the v-form renders. Avoids the
|
|
2205
|
+
* v-input `[defaultValue]` async-binding race that otherwise drops one of the
|
|
2206
|
+
* fields when prefill arrives after mount.
|
|
2207
|
+
*/
|
|
2208
|
+
resolvedPrefill = {};
|
|
2209
|
+
prefillResolved$ = new BehaviorSubject(false);
|
|
2045
2210
|
safeDisabled$;
|
|
2046
2211
|
ngOnInit() {
|
|
2047
2212
|
this.safeDisabled$ = this.disabled$.pipe(startWith(true), delay(0));
|
|
2213
|
+
const prefill$ = this.prefillConfiguration$ ?? of({});
|
|
2214
|
+
prefill$.pipe(take(1)).subscribe((prefill) => {
|
|
2215
|
+
this.resolvedPrefill = prefill ?? {};
|
|
2216
|
+
this.prefillResolved$.next(true);
|
|
2217
|
+
});
|
|
2048
2218
|
this.openSaveSubscription();
|
|
2049
2219
|
}
|
|
2050
2220
|
ngOnDestroy() {
|
|
@@ -2056,7 +2226,7 @@ class DownloadDocumentConfigurationComponent {
|
|
|
2056
2226
|
this.handleValid(formValue);
|
|
2057
2227
|
}
|
|
2058
2228
|
handleValid(formValue) {
|
|
2059
|
-
const valid = !!(formValue?.
|
|
2229
|
+
const valid = !!(formValue?.documentVariable && formValue?.contentVariable);
|
|
2060
2230
|
this.valid$.next(valid);
|
|
2061
2231
|
this.valid.emit(valid);
|
|
2062
2232
|
}
|
|
@@ -2072,11 +2242,11 @@ class DownloadDocumentConfigurationComponent {
|
|
|
2072
2242
|
});
|
|
2073
2243
|
}
|
|
2074
2244
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DownloadDocumentConfigurationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2075
|
-
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
|
|
2245
|
+
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 (valueChange)=\"formValueChange($event)\" *ngIf=\"prefillResolved$ | async\">\n <v-input\n name=\"documentVariable\"\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-input\n name=\"contentVariable\"\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"] }] });
|
|
2076
2246
|
}
|
|
2077
2247
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DownloadDocumentConfigurationComponent, decorators: [{
|
|
2078
2248
|
type: Component,
|
|
2079
|
-
args: [{ selector: 'epistola-download-document-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form
|
|
2249
|
+
args: [{ selector: 'epistola-download-document-configuration', standalone: true, imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule], template: "<v-form (valueChange)=\"formValueChange($event)\" *ngIf=\"prefillResolved$ | async\">\n <v-input\n name=\"documentVariable\"\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-input\n name=\"contentVariable\"\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" }]
|
|
2080
2250
|
}], propDecorators: { save$: [{
|
|
2081
2251
|
type: Input
|
|
2082
2252
|
}], disabled$: [{
|
|
@@ -2091,35 +2261,89 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
2091
2261
|
type: Output
|
|
2092
2262
|
}] } });
|
|
2093
2263
|
|
|
2094
|
-
|
|
2095
|
-
|
|
2264
|
+
/**
|
|
2265
|
+
* Unified Formio component for the after-generation Epistola PDF UX. Reads
|
|
2266
|
+
* the PDF id and tenant id from named process variables on the caller's
|
|
2267
|
+
* task via the {@code /documents/download} endpoint.
|
|
2268
|
+
*
|
|
2269
|
+
* <p>Three presentations, all backed by the same backend call:
|
|
2270
|
+
* <ul>
|
|
2271
|
+
* <li>{@code display="inline"} — render the PDF inline in a panel.</li>
|
|
2272
|
+
* <li>{@code display="button"} — show a click-to-save download button only.</li>
|
|
2273
|
+
* <li>{@code display="both"} (default) — inline panel with a download icon
|
|
2274
|
+
* in the header.</li>
|
|
2275
|
+
* </ul>
|
|
2276
|
+
*
|
|
2277
|
+
* <p>For the dry-run / what-would-be-generated UX use
|
|
2278
|
+
* {@code epistola-document-preview} instead.
|
|
2279
|
+
*/
|
|
2280
|
+
class EpistolaDocumentComponent {
|
|
2281
|
+
epistolaPluginService;
|
|
2282
|
+
sanitizer;
|
|
2283
|
+
formIoStateService;
|
|
2284
|
+
taskContext;
|
|
2285
|
+
cdr;
|
|
2096
2286
|
value;
|
|
2097
2287
|
valueChange = new EventEmitter();
|
|
2098
2288
|
disabled = false;
|
|
2289
|
+
label = 'Document';
|
|
2290
|
+
/** How to present the document. `both` (default) shows an inline panel with a download icon. */
|
|
2291
|
+
display = 'both';
|
|
2292
|
+
/**
|
|
2293
|
+
* Process-variable name holding the Epistola result. Default: `epistolaResult`.
|
|
2294
|
+
*
|
|
2295
|
+
* Type-tolerant on the backend: if the named variable holds a rich result object
|
|
2296
|
+
* (`Map<String, Object>` written by `generate-document` and updated by the result
|
|
2297
|
+
* collector), the backend digs out the `documentId` key. If it holds a plain
|
|
2298
|
+
* String (custom flow that wrote a bare id), the backend uses it as-is.
|
|
2299
|
+
*/
|
|
2300
|
+
documentVariable = 'epistolaResult';
|
|
2301
|
+
/** Process-variable name holding the Epistola tenant id. Default: `epistolaTenantId`. */
|
|
2302
|
+
tenantIdVariable = 'epistolaTenantId';
|
|
2303
|
+
/** Filename used for the download disposition. */
|
|
2099
2304
|
filename = 'document.pdf';
|
|
2100
|
-
|
|
2305
|
+
loading = false;
|
|
2101
2306
|
downloading = false;
|
|
2102
2307
|
error = null;
|
|
2103
|
-
|
|
2104
|
-
|
|
2308
|
+
previewUrl = null;
|
|
2309
|
+
currentBlobUrl = null;
|
|
2310
|
+
subscription;
|
|
2311
|
+
get designMode() {
|
|
2312
|
+
return !this.formIoStateService.documentId;
|
|
2105
2313
|
}
|
|
2106
|
-
constructor(
|
|
2107
|
-
this.
|
|
2314
|
+
constructor(epistolaPluginService, sanitizer, formIoStateService, taskContext, cdr) {
|
|
2315
|
+
this.epistolaPluginService = epistolaPluginService;
|
|
2316
|
+
this.sanitizer = sanitizer;
|
|
2317
|
+
this.formIoStateService = formIoStateService;
|
|
2318
|
+
this.taskContext = taskContext;
|
|
2319
|
+
this.cdr = cdr;
|
|
2108
2320
|
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2321
|
+
ngOnInit() {
|
|
2322
|
+
if (this.designMode) {
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2325
|
+
if (this.display !== 'button') {
|
|
2326
|
+
this.loadInline();
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
ngOnDestroy() {
|
|
2330
|
+
this.subscription?.unsubscribe();
|
|
2331
|
+
this.revokeBlobUrl();
|
|
2332
|
+
}
|
|
2333
|
+
refresh() {
|
|
2334
|
+
this.loadInline();
|
|
2111
2335
|
}
|
|
2112
2336
|
download() {
|
|
2113
|
-
if (
|
|
2337
|
+
if (this.designMode || this.downloading) {
|
|
2114
2338
|
return;
|
|
2115
2339
|
}
|
|
2340
|
+
const request = this.buildRequest('attachment');
|
|
2341
|
+
if (!request)
|
|
2342
|
+
return;
|
|
2116
2343
|
this.downloading = true;
|
|
2117
2344
|
this.error = null;
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
`?tenantId=${encodeURIComponent(tenantId)}` +
|
|
2121
|
-
`&filename=${encodeURIComponent(this.filename)}`;
|
|
2122
|
-
this.http.get(url, { responseType: 'blob' }).subscribe({
|
|
2345
|
+
this.cdr.markForCheck();
|
|
2346
|
+
this.epistolaPluginService.downloadDocumentBlob(request).subscribe({
|
|
2123
2347
|
next: (blob) => {
|
|
2124
2348
|
const objectUrl = URL.createObjectURL(blob);
|
|
2125
2349
|
const anchor = document.createElement('a');
|
|
@@ -2128,66 +2352,246 @@ class EpistolaDownloadComponent {
|
|
|
2128
2352
|
anchor.click();
|
|
2129
2353
|
URL.revokeObjectURL(objectUrl);
|
|
2130
2354
|
this.downloading = false;
|
|
2355
|
+
this.cdr.markForCheck();
|
|
2131
2356
|
},
|
|
2132
2357
|
error: (err) => {
|
|
2133
|
-
|
|
2134
|
-
this.error = 'Download mislukt. Probeer opnieuw.';
|
|
2358
|
+
this.error = err.status === 404 ? 'Document is nog niet gegenereerd.' : 'Download mislukt.';
|
|
2135
2359
|
this.downloading = false;
|
|
2360
|
+
this.cdr.markForCheck();
|
|
2136
2361
|
},
|
|
2137
2362
|
});
|
|
2138
2363
|
}
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2364
|
+
loadInline() {
|
|
2365
|
+
const request = this.buildRequest('inline');
|
|
2366
|
+
if (!request)
|
|
2367
|
+
return;
|
|
2368
|
+
this.loading = true;
|
|
2369
|
+
this.error = null;
|
|
2370
|
+
this.cdr.markForCheck();
|
|
2371
|
+
this.revokeBlobUrl();
|
|
2372
|
+
this.subscription?.unsubscribe();
|
|
2373
|
+
this.subscription = this.epistolaPluginService.downloadDocumentBlob(request).subscribe({
|
|
2374
|
+
next: (blob) => {
|
|
2375
|
+
this.currentBlobUrl = URL.createObjectURL(blob);
|
|
2376
|
+
this.previewUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.currentBlobUrl);
|
|
2377
|
+
this.loading = false;
|
|
2378
|
+
this.cdr.markForCheck();
|
|
2379
|
+
},
|
|
2380
|
+
error: (err) => {
|
|
2381
|
+
this.previewUrl = null;
|
|
2382
|
+
this.error =
|
|
2383
|
+
err.status === 404
|
|
2384
|
+
? 'Document is nog niet gegenereerd.'
|
|
2385
|
+
: 'Document kon niet geladen worden.';
|
|
2386
|
+
this.loading = false;
|
|
2387
|
+
this.cdr.markForCheck();
|
|
2388
|
+
},
|
|
2389
|
+
});
|
|
2390
|
+
}
|
|
2391
|
+
buildRequest(disposition) {
|
|
2392
|
+
const taskId = this.taskContext.taskInstanceId;
|
|
2393
|
+
const caseDocumentId = this.formIoStateService.documentId;
|
|
2394
|
+
if (!taskId || !caseDocumentId) {
|
|
2395
|
+
this.error = 'Document is alleen beschikbaar binnen een taak.';
|
|
2396
|
+
this.cdr.markForCheck();
|
|
2397
|
+
return null;
|
|
2398
|
+
}
|
|
2399
|
+
return {
|
|
2400
|
+
taskId,
|
|
2401
|
+
caseDocumentId,
|
|
2402
|
+
documentVariable: this.documentVariable,
|
|
2403
|
+
tenantIdVariable: this.tenantIdVariable,
|
|
2404
|
+
filename: this.filename,
|
|
2405
|
+
disposition,
|
|
2406
|
+
};
|
|
2407
|
+
}
|
|
2408
|
+
revokeBlobUrl() {
|
|
2409
|
+
if (this.currentBlobUrl) {
|
|
2410
|
+
URL.revokeObjectURL(this.currentBlobUrl);
|
|
2411
|
+
this.currentBlobUrl = null;
|
|
2412
|
+
this.previewUrl = null;
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaDocumentComponent, deps: [{ token: EpistolaPluginService }, { token: i2$4.DomSanitizer }, { token: i3.FormIoStateService }, { token: EpistolaTaskContextService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
2416
|
+
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" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: `
|
|
2417
|
+
<!-- Design-time placeholder -->
|
|
2418
|
+
<div *ngIf="designMode" class="epistola-doc-panel">
|
|
2419
|
+
<div class="doc-header">
|
|
2420
|
+
<span>{{ label || 'Document' }}</span>
|
|
2421
|
+
<span class="design-tag">design mode</span>
|
|
2422
|
+
</div>
|
|
2423
|
+
<div class="doc-body design-info">
|
|
2424
|
+
<div class="design-section">
|
|
2425
|
+
<div class="design-label">Display</div>
|
|
2426
|
+
<div class="design-value">{{ display }}</div>
|
|
2427
|
+
<div class="design-label">Document variable</div>
|
|
2428
|
+
<div class="design-value">{{ documentVariable }}</div>
|
|
2429
|
+
<div class="design-label">Tenant ID variable</div>
|
|
2430
|
+
<div class="design-value">{{ tenantIdVariable }}</div>
|
|
2431
|
+
</div>
|
|
2432
|
+
</div>
|
|
2433
|
+
</div>
|
|
2434
|
+
|
|
2435
|
+
<!-- Button-only display -->
|
|
2436
|
+
<div *ngIf="!designMode && display === 'button'">
|
|
2437
|
+
<button
|
|
2438
|
+
type="button"
|
|
2439
|
+
class="btn btn-outline-primary"
|
|
2440
|
+
[disabled]="disabled || downloading"
|
|
2441
|
+
(click)="download()"
|
|
2442
|
+
>
|
|
2443
|
+
<i class="mdi mdi-download mr-1"></i>
|
|
2444
|
+
{{ downloading ? 'Downloading...' : label || 'Download PDF' }}
|
|
2445
|
+
</button>
|
|
2446
|
+
<span *ngIf="error" class="text-danger ml-2">{{ error }}</span>
|
|
2447
|
+
</div>
|
|
2448
|
+
|
|
2449
|
+
<!-- Inline / both: panel with optional download icon -->
|
|
2450
|
+
<div *ngIf="!designMode && display !== 'button'" class="epistola-doc-panel">
|
|
2451
|
+
<div class="doc-header">
|
|
2452
|
+
<span>{{ label || 'Document' }}</span>
|
|
2453
|
+
<div class="doc-controls">
|
|
2454
|
+
<button
|
|
2455
|
+
*ngIf="display === 'both'"
|
|
2456
|
+
type="button"
|
|
2457
|
+
class="doc-icon-btn"
|
|
2458
|
+
[disabled]="disabled || downloading"
|
|
2459
|
+
(click)="download()"
|
|
2460
|
+
[title]="downloading ? 'Downloading...' : 'Download'"
|
|
2461
|
+
>
|
|
2462
|
+
<i class="mdi mdi-download"></i>
|
|
2463
|
+
</button>
|
|
2464
|
+
<button
|
|
2465
|
+
type="button"
|
|
2466
|
+
class="doc-icon-btn"
|
|
2467
|
+
[disabled]="loading"
|
|
2468
|
+
(click)="refresh()"
|
|
2469
|
+
title="Refresh"
|
|
2470
|
+
>
|
|
2471
|
+
<i class="mdi mdi-refresh"></i>
|
|
2472
|
+
</button>
|
|
2473
|
+
</div>
|
|
2474
|
+
</div>
|
|
2475
|
+
<div class="doc-body">
|
|
2476
|
+
<div *ngIf="loading" class="doc-loading">Loading document...</div>
|
|
2477
|
+
<div *ngIf="error && !loading" class="doc-unavailable">
|
|
2478
|
+
<i class="mdi mdi-information-outline"></i>
|
|
2479
|
+
{{ error }}
|
|
2480
|
+
</div>
|
|
2481
|
+
<object
|
|
2482
|
+
*ngIf="previewUrl && !loading"
|
|
2483
|
+
[data]="previewUrl"
|
|
2484
|
+
type="application/pdf"
|
|
2485
|
+
class="doc-pdf"
|
|
2486
|
+
>
|
|
2487
|
+
PDF preview is not supported in this browser.
|
|
2488
|
+
</object>
|
|
2489
|
+
</div>
|
|
2490
|
+
</div>
|
|
2491
|
+
`, isInline: true, styles: [".epistola-doc-panel{border:1px solid #dee2e6;border-radius:4px;background:#f8f9fa;display:flex;flex-direction:column}.doc-header{display:flex;justify-content:space-between;align-items:center;padding:.5rem 1rem;border-bottom:1px solid #dee2e6;font-weight:700;color:#495057}.doc-controls{display:flex;gap:.25rem}.doc-icon-btn{background:none;border:1px solid #6c757d;border-radius:4px;color:#6c757d;padding:.25rem .5rem;font-size:.9rem;cursor:pointer}.doc-icon-btn:hover:not(:disabled){background:#e9ecef}.doc-icon-btn:disabled{opacity:.5;cursor:not-allowed}.doc-body{display:flex;flex-direction:column;min-height:500px}.doc-loading,.doc-unavailable{padding:2rem;text-align:center;color:#6c757d;font-style:italic}.doc-unavailable i{margin-right:.25rem}.doc-pdf{width:100%;flex:1;min-height:500px}.design-info{padding:1rem;min-height:auto}.design-section{margin-bottom:.5rem}.design-label{font-size:.7rem;text-transform:uppercase;color:#868e96;font-weight:600;letter-spacing:.05em}.design-value{font-family:monospace;font-size:.85rem;color:#212529;margin-bottom:.25rem}.design-tag{font-size:.7rem;font-weight:400;color:#6c757d;font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2152
2492
|
}
|
|
2153
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type:
|
|
2493
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaDocumentComponent, decorators: [{
|
|
2154
2494
|
type: Component,
|
|
2155
|
-
args: [{
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
class="
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2495
|
+
args: [{ standalone: true, imports: [CommonModule], selector: 'epistola-document-component', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
2496
|
+
<!-- Design-time placeholder -->
|
|
2497
|
+
<div *ngIf="designMode" class="epistola-doc-panel">
|
|
2498
|
+
<div class="doc-header">
|
|
2499
|
+
<span>{{ label || 'Document' }}</span>
|
|
2500
|
+
<span class="design-tag">design mode</span>
|
|
2501
|
+
</div>
|
|
2502
|
+
<div class="doc-body design-info">
|
|
2503
|
+
<div class="design-section">
|
|
2504
|
+
<div class="design-label">Display</div>
|
|
2505
|
+
<div class="design-value">{{ display }}</div>
|
|
2506
|
+
<div class="design-label">Document variable</div>
|
|
2507
|
+
<div class="design-value">{{ documentVariable }}</div>
|
|
2508
|
+
<div class="design-label">Tenant ID variable</div>
|
|
2509
|
+
<div class="design-value">{{ tenantIdVariable }}</div>
|
|
2510
|
+
</div>
|
|
2511
|
+
</div>
|
|
2512
|
+
</div>
|
|
2513
|
+
|
|
2514
|
+
<!-- Button-only display -->
|
|
2515
|
+
<div *ngIf="!designMode && display === 'button'">
|
|
2516
|
+
<button
|
|
2517
|
+
type="button"
|
|
2518
|
+
class="btn btn-outline-primary"
|
|
2519
|
+
[disabled]="disabled || downloading"
|
|
2520
|
+
(click)="download()"
|
|
2521
|
+
>
|
|
2522
|
+
<i class="mdi mdi-download mr-1"></i>
|
|
2523
|
+
{{ downloading ? 'Downloading...' : label || 'Download PDF' }}
|
|
2524
|
+
</button>
|
|
2525
|
+
<span *ngIf="error" class="text-danger ml-2">{{ error }}</span>
|
|
2526
|
+
</div>
|
|
2527
|
+
|
|
2528
|
+
<!-- Inline / both: panel with optional download icon -->
|
|
2529
|
+
<div *ngIf="!designMode && display !== 'button'" class="epistola-doc-panel">
|
|
2530
|
+
<div class="doc-header">
|
|
2531
|
+
<span>{{ label || 'Document' }}</span>
|
|
2532
|
+
<div class="doc-controls">
|
|
2533
|
+
<button
|
|
2534
|
+
*ngIf="display === 'both'"
|
|
2535
|
+
type="button"
|
|
2536
|
+
class="doc-icon-btn"
|
|
2537
|
+
[disabled]="disabled || downloading"
|
|
2538
|
+
(click)="download()"
|
|
2539
|
+
[title]="downloading ? 'Downloading...' : 'Download'"
|
|
2540
|
+
>
|
|
2541
|
+
<i class="mdi mdi-download"></i>
|
|
2542
|
+
</button>
|
|
2543
|
+
<button
|
|
2544
|
+
type="button"
|
|
2545
|
+
class="doc-icon-btn"
|
|
2546
|
+
[disabled]="loading"
|
|
2547
|
+
(click)="refresh()"
|
|
2548
|
+
title="Refresh"
|
|
2549
|
+
>
|
|
2550
|
+
<i class="mdi mdi-refresh"></i>
|
|
2551
|
+
</button>
|
|
2552
|
+
</div>
|
|
2553
|
+
</div>
|
|
2554
|
+
<div class="doc-body">
|
|
2555
|
+
<div *ngIf="loading" class="doc-loading">Loading document...</div>
|
|
2556
|
+
<div *ngIf="error && !loading" class="doc-unavailable">
|
|
2557
|
+
<i class="mdi mdi-information-outline"></i>
|
|
2558
|
+
{{ error }}
|
|
2559
|
+
</div>
|
|
2560
|
+
<object
|
|
2561
|
+
*ngIf="previewUrl && !loading"
|
|
2562
|
+
[data]="previewUrl"
|
|
2563
|
+
type="application/pdf"
|
|
2564
|
+
class="doc-pdf"
|
|
2565
|
+
>
|
|
2566
|
+
PDF preview is not supported in this browser.
|
|
2567
|
+
</object>
|
|
2568
|
+
</div>
|
|
2569
|
+
</div>
|
|
2570
|
+
`, styles: [".epistola-doc-panel{border:1px solid #dee2e6;border-radius:4px;background:#f8f9fa;display:flex;flex-direction:column}.doc-header{display:flex;justify-content:space-between;align-items:center;padding:.5rem 1rem;border-bottom:1px solid #dee2e6;font-weight:700;color:#495057}.doc-controls{display:flex;gap:.25rem}.doc-icon-btn{background:none;border:1px solid #6c757d;border-radius:4px;color:#6c757d;padding:.25rem .5rem;font-size:.9rem;cursor:pointer}.doc-icon-btn:hover:not(:disabled){background:#e9ecef}.doc-icon-btn:disabled{opacity:.5;cursor:not-allowed}.doc-body{display:flex;flex-direction:column;min-height:500px}.doc-loading,.doc-unavailable{padding:2rem;text-align:center;color:#6c757d;font-style:italic}.doc-unavailable i{margin-right:.25rem}.doc-pdf{width:100%;flex:1;min-height:500px}.design-info{padding:1rem;min-height:auto}.design-section{margin-bottom:.5rem}.design-label{font-size:.7rem;text-transform:uppercase;color:#868e96;font-weight:600;letter-spacing:.05em}.design-value{font-family:monospace;font-size:.85rem;color:#212529;margin-bottom:.25rem}.design-tag{font-size:.7rem;font-weight:400;color:#6c757d;font-style:italic}\n"] }]
|
|
2571
|
+
}], ctorParameters: () => [{ type: EpistolaPluginService }, { type: i2$4.DomSanitizer }, { type: i3.FormIoStateService }, { type: EpistolaTaskContextService }, { type: i0.ChangeDetectorRef }], propDecorators: { value: [{
|
|
2173
2572
|
type: Input
|
|
2174
2573
|
}], valueChange: [{
|
|
2175
2574
|
type: Output
|
|
2176
2575
|
}], disabled: [{
|
|
2177
2576
|
type: Input
|
|
2178
|
-
}], filename: [{
|
|
2179
|
-
type: Input
|
|
2180
2577
|
}], label: [{
|
|
2181
2578
|
type: Input
|
|
2579
|
+
}], display: [{
|
|
2580
|
+
type: Input
|
|
2581
|
+
}], documentVariable: [{
|
|
2582
|
+
type: Input
|
|
2583
|
+
}], tenantIdVariable: [{
|
|
2584
|
+
type: Input
|
|
2585
|
+
}], filename: [{
|
|
2586
|
+
type: Input
|
|
2182
2587
|
}] } });
|
|
2183
2588
|
|
|
2184
2589
|
class EpistolaRetryFormComponent {
|
|
2185
2590
|
epistolaPluginService;
|
|
2186
2591
|
formIoStateService;
|
|
2187
2592
|
cdr;
|
|
2188
|
-
http;
|
|
2189
2593
|
sanitizer;
|
|
2190
|
-
|
|
2594
|
+
taskContext;
|
|
2191
2595
|
value;
|
|
2192
2596
|
valueChange = new EventEmitter();
|
|
2193
2597
|
disabled = false;
|
|
@@ -2208,19 +2612,16 @@ class EpistolaRetryFormComponent {
|
|
|
2208
2612
|
currentBlobUrl = null;
|
|
2209
2613
|
resolvedSourceActivityId;
|
|
2210
2614
|
processDefinitionKey;
|
|
2211
|
-
apiEndpoint;
|
|
2212
2615
|
formOptions = {
|
|
2213
2616
|
noAlerts: true,
|
|
2214
2617
|
buttonSettings: { showCancel: false, showSubmit: false, showPrevious: false, showNext: false },
|
|
2215
2618
|
};
|
|
2216
|
-
constructor(epistolaPluginService, formIoStateService, cdr,
|
|
2619
|
+
constructor(epistolaPluginService, formIoStateService, cdr, sanitizer, taskContext) {
|
|
2217
2620
|
this.epistolaPluginService = epistolaPluginService;
|
|
2218
2621
|
this.formIoStateService = formIoStateService;
|
|
2219
2622
|
this.cdr = cdr;
|
|
2220
|
-
this.http = http;
|
|
2221
2623
|
this.sanitizer = sanitizer;
|
|
2222
|
-
this.
|
|
2223
|
-
this.apiEndpoint = `${this.configService.config.valtimoApi.endpointUri}v1/plugin/epistola`;
|
|
2624
|
+
this.taskContext = taskContext;
|
|
2224
2625
|
// Debounce preview calls
|
|
2225
2626
|
this.previewSubscription = this.previewSubject.pipe(debounceTime$1(1500)).subscribe((data) => {
|
|
2226
2627
|
this.loadPreview(data);
|
|
@@ -2257,6 +2658,12 @@ class EpistolaRetryFormComponent {
|
|
|
2257
2658
|
const processInstanceId = this.formIoStateService.processInstanceId;
|
|
2258
2659
|
if (!documentId || !processInstanceId)
|
|
2259
2660
|
return;
|
|
2661
|
+
const taskId = this.taskContext.taskInstanceId;
|
|
2662
|
+
if (!taskId) {
|
|
2663
|
+
this.previewError = 'Preview is only available from within a user task.';
|
|
2664
|
+
this.cdr.markForCheck();
|
|
2665
|
+
return;
|
|
2666
|
+
}
|
|
2260
2667
|
this.previewLoading = true;
|
|
2261
2668
|
this.previewError = null;
|
|
2262
2669
|
this.cdr.markForCheck();
|
|
@@ -2265,13 +2672,14 @@ class EpistolaRetryFormComponent {
|
|
|
2265
2672
|
URL.revokeObjectURL(this.currentBlobUrl);
|
|
2266
2673
|
this.currentBlobUrl = null;
|
|
2267
2674
|
}
|
|
2268
|
-
this.
|
|
2269
|
-
.
|
|
2675
|
+
this.epistolaPluginService
|
|
2676
|
+
.previewToBlob({
|
|
2677
|
+
taskId,
|
|
2270
2678
|
documentId,
|
|
2271
2679
|
processInstanceId,
|
|
2272
2680
|
sourceActivityId: this.sourceActivityId || null,
|
|
2273
2681
|
overrides: formData,
|
|
2274
|
-
}
|
|
2682
|
+
})
|
|
2275
2683
|
.subscribe({
|
|
2276
2684
|
next: (blob) => {
|
|
2277
2685
|
this.currentBlobUrl = URL.createObjectURL(blob);
|
|
@@ -2313,8 +2721,15 @@ class EpistolaRetryFormComponent {
|
|
|
2313
2721
|
this.cdr.markForCheck();
|
|
2314
2722
|
return;
|
|
2315
2723
|
}
|
|
2724
|
+
const taskId = this.taskContext.taskInstanceId;
|
|
2725
|
+
if (!taskId) {
|
|
2726
|
+
this.error = 'Retry form is only available from within a user task.';
|
|
2727
|
+
this.loading = false;
|
|
2728
|
+
this.cdr.markForCheck();
|
|
2729
|
+
return;
|
|
2730
|
+
}
|
|
2316
2731
|
this.loadSubscription = this.epistolaPluginService
|
|
2317
|
-
.getRetryForm(processInstanceId, documentId ?? undefined, this.sourceActivityId)
|
|
2732
|
+
.getRetryForm(taskId, processInstanceId, documentId ?? undefined, this.sourceActivityId)
|
|
2318
2733
|
.subscribe({
|
|
2319
2734
|
next: (form) => {
|
|
2320
2735
|
this.formDefinition = form;
|
|
@@ -2337,7 +2752,7 @@ class EpistolaRetryFormComponent {
|
|
|
2337
2752
|
},
|
|
2338
2753
|
});
|
|
2339
2754
|
}
|
|
2340
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaRetryFormComponent, deps: [{ token: EpistolaPluginService }, { token: i3.FormIoStateService }, { token: i0.ChangeDetectorRef }, { token:
|
|
2755
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaRetryFormComponent, deps: [{ token: EpistolaPluginService }, { token: i3.FormIoStateService }, { token: i0.ChangeDetectorRef }, { token: i2$4.DomSanitizer }, { token: EpistolaTaskContextService }], target: i0.ɵɵFactoryTarget.Component });
|
|
2341
2756
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaRetryFormComponent, isStandalone: true, selector: "epistola-retry-form-component", inputs: { value: "value", disabled: "disabled", label: "label", sourceActivityId: "sourceActivityId" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
2342
2757
|
<div *ngIf="loading" class="epistola-retry-loading">Loading form...</div>
|
|
2343
2758
|
<div *ngIf="error" class="epistola-retry-error">{{ error }}</div>
|
|
@@ -2376,7 +2791,7 @@ class EpistolaRetryFormComponent {
|
|
|
2376
2791
|
</div>
|
|
2377
2792
|
</div>
|
|
2378
2793
|
</div>
|
|
2379
|
-
`, isInline: true, styles: [".epistola-retry-loading{padding:1rem;color:#6c757d}.epistola-retry-error{padding:.5rem;color:#dc3545}.epistola-retry-container{display:flex;gap:1rem}.epistola-retry-form{flex:2;min-width:0}.epistola-retry-preview{flex:1;min-width:0;border:1px solid #dee2e6;border-radius:4px;padding:1rem;background:#f8f9fa;display:flex;flex-direction:column}.preview-expanded .epistola-retry-preview{flex:1}.preview-header{display:flex;justify-content:space-between;align-items:center;font-weight:700;margin-bottom:.5rem;color:#495057}.preview-toggle{background:none;border:1px solid #6c757d;border-radius:4px;color:#6c757d;padding:.2rem .5rem;font-size:.75rem;cursor:pointer}.preview-toggle:hover{background:#e9ecef}.preview-loading{color:#6c757d;font-style:italic}.preview-pdf{width:100%;flex:1;min-height:500px}.preview-expanded .preview-pdf{min-height:80vh}.preview-error{color:#dc3545}.preview-empty{color:#6c757d;font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormioModule }, { kind: "component", type:
|
|
2794
|
+
`, isInline: true, styles: [".epistola-retry-loading{padding:1rem;color:#6c757d}.epistola-retry-error{padding:.5rem;color:#dc3545}.epistola-retry-container{display:flex;gap:1rem}.epistola-retry-form{flex:2;min-width:0}.epistola-retry-preview{flex:1;min-width:0;border:1px solid #dee2e6;border-radius:4px;padding:1rem;background:#f8f9fa;display:flex;flex-direction:column}.preview-expanded .epistola-retry-preview{flex:1}.preview-header{display:flex;justify-content:space-between;align-items:center;font-weight:700;margin-bottom:.5rem;color:#495057}.preview-toggle{background:none;border:1px solid #6c757d;border-radius:4px;color:#6c757d;padding:.2rem .5rem;font-size:.75rem;cursor:pointer}.preview-toggle:hover{background:#e9ecef}.preview-loading{color:#6c757d;font-style:italic}.preview-pdf{width:100%;flex:1;min-height:500px}.preview-expanded .preview-pdf{min-height:80vh}.preview-error{color:#dc3545}.preview-empty{color:#6c757d;font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormioModule }, { kind: "component", type: i5.FormioComponent, selector: "formio" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2380
2795
|
}
|
|
2381
2796
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaRetryFormComponent, decorators: [{
|
|
2382
2797
|
type: Component,
|
|
@@ -2419,7 +2834,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
2419
2834
|
</div>
|
|
2420
2835
|
</div>
|
|
2421
2836
|
`, styles: [".epistola-retry-loading{padding:1rem;color:#6c757d}.epistola-retry-error{padding:.5rem;color:#dc3545}.epistola-retry-container{display:flex;gap:1rem}.epistola-retry-form{flex:2;min-width:0}.epistola-retry-preview{flex:1;min-width:0;border:1px solid #dee2e6;border-radius:4px;padding:1rem;background:#f8f9fa;display:flex;flex-direction:column}.preview-expanded .epistola-retry-preview{flex:1}.preview-header{display:flex;justify-content:space-between;align-items:center;font-weight:700;margin-bottom:.5rem;color:#495057}.preview-toggle{background:none;border:1px solid #6c757d;border-radius:4px;color:#6c757d;padding:.2rem .5rem;font-size:.75rem;cursor:pointer}.preview-toggle:hover{background:#e9ecef}.preview-loading{color:#6c757d;font-style:italic}.preview-pdf{width:100%;flex:1;min-height:500px}.preview-expanded .preview-pdf{min-height:80vh}.preview-error{color:#dc3545}.preview-empty{color:#6c757d;font-style:italic}\n"] }]
|
|
2422
|
-
}], ctorParameters: () => [{ type: EpistolaPluginService }, { type: i3.FormIoStateService }, { type: i0.ChangeDetectorRef }, { type:
|
|
2837
|
+
}], ctorParameters: () => [{ type: EpistolaPluginService }, { type: i3.FormIoStateService }, { type: i0.ChangeDetectorRef }, { type: i2$4.DomSanitizer }, { type: EpistolaTaskContextService }], propDecorators: { value: [{
|
|
2423
2838
|
type: Input
|
|
2424
2839
|
}], valueChange: [{
|
|
2425
2840
|
type: Output
|
|
@@ -2431,160 +2846,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
2431
2846
|
type: Input
|
|
2432
2847
|
}] } });
|
|
2433
2848
|
|
|
2434
|
-
class EpistolaPreviewButtonComponent {
|
|
2435
|
-
http;
|
|
2436
|
-
sanitizer;
|
|
2437
|
-
configService;
|
|
2438
|
-
value;
|
|
2439
|
-
valueChange = new EventEmitter();
|
|
2440
|
-
disabled = false;
|
|
2441
|
-
label = 'Preview PDF';
|
|
2442
|
-
modalOpen = false;
|
|
2443
|
-
loading = false;
|
|
2444
|
-
previewLoading = false;
|
|
2445
|
-
previewError = null;
|
|
2446
|
-
previewUrl = null;
|
|
2447
|
-
currentBlobUrl = null;
|
|
2448
|
-
apiEndpoint;
|
|
2449
|
-
get buttonLabel() {
|
|
2450
|
-
return this.label || 'Preview PDF';
|
|
2451
|
-
}
|
|
2452
|
-
constructor(http, sanitizer, configService) {
|
|
2453
|
-
this.http = http;
|
|
2454
|
-
this.sanitizer = sanitizer;
|
|
2455
|
-
this.configService = configService;
|
|
2456
|
-
this.apiEndpoint = `${this.configService.config.valtimoApi.endpointUri}v1/plugin/epistola`;
|
|
2457
|
-
}
|
|
2458
|
-
ngOnDestroy() {
|
|
2459
|
-
this.revokeBlobUrl();
|
|
2460
|
-
}
|
|
2461
|
-
hasRequiredData() {
|
|
2462
|
-
return !!(this.value?.documentId && this.value?.tenantId);
|
|
2463
|
-
}
|
|
2464
|
-
openPreview() {
|
|
2465
|
-
if (!this.hasRequiredData() || this.loading)
|
|
2466
|
-
return;
|
|
2467
|
-
this.modalOpen = true;
|
|
2468
|
-
this.previewLoading = true;
|
|
2469
|
-
this.previewError = null;
|
|
2470
|
-
this.revokeBlobUrl();
|
|
2471
|
-
const { documentId, tenantId } = this.value;
|
|
2472
|
-
const url = `${this.apiEndpoint}/documents/${encodeURIComponent(documentId)}/download` +
|
|
2473
|
-
`?tenantId=${encodeURIComponent(tenantId)}` +
|
|
2474
|
-
`&filename=preview.pdf`;
|
|
2475
|
-
this.http.get(url, { responseType: 'blob' }).subscribe({
|
|
2476
|
-
next: (blob) => {
|
|
2477
|
-
this.currentBlobUrl = URL.createObjectURL(blob);
|
|
2478
|
-
this.previewUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.currentBlobUrl);
|
|
2479
|
-
this.previewLoading = false;
|
|
2480
|
-
},
|
|
2481
|
-
error: () => {
|
|
2482
|
-
this.previewError = 'Could not load the document.';
|
|
2483
|
-
this.previewLoading = false;
|
|
2484
|
-
},
|
|
2485
|
-
});
|
|
2486
|
-
}
|
|
2487
|
-
closePreview() {
|
|
2488
|
-
this.modalOpen = false;
|
|
2489
|
-
this.revokeBlobUrl();
|
|
2490
|
-
this.previewUrl = null;
|
|
2491
|
-
this.previewError = null;
|
|
2492
|
-
}
|
|
2493
|
-
revokeBlobUrl() {
|
|
2494
|
-
if (this.currentBlobUrl) {
|
|
2495
|
-
URL.revokeObjectURL(this.currentBlobUrl);
|
|
2496
|
-
this.currentBlobUrl = null;
|
|
2497
|
-
}
|
|
2498
|
-
}
|
|
2499
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPreviewButtonComponent, deps: [{ token: i1.HttpClient }, { token: i4.DomSanitizer }, { token: i2.ConfigService }], target: i0.ɵɵFactoryTarget.Component });
|
|
2500
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaPreviewButtonComponent, isStandalone: true, selector: "epistola-preview-button-component", inputs: { value: "value", disabled: "disabled", label: "label" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: `
|
|
2501
|
-
<button
|
|
2502
|
-
type="button"
|
|
2503
|
-
class="btn btn-outline-secondary"
|
|
2504
|
-
[disabled]="disabled || loading || !hasRequiredData()"
|
|
2505
|
-
(click)="openPreview()"
|
|
2506
|
-
>
|
|
2507
|
-
<i class="mdi mdi-eye mr-1"></i>
|
|
2508
|
-
{{ loading ? 'Loading...' : buttonLabel }}
|
|
2509
|
-
</button>
|
|
2510
|
-
|
|
2511
|
-
<div *ngIf="modalOpen" class="preview-modal-overlay" (click)="closePreview()">
|
|
2512
|
-
<div class="preview-modal-content" (click)="$event.stopPropagation()">
|
|
2513
|
-
<div class="preview-modal-header">
|
|
2514
|
-
<span>Document Preview</span>
|
|
2515
|
-
<button type="button" class="preview-modal-close" (click)="closePreview()">
|
|
2516
|
-
×
|
|
2517
|
-
</button>
|
|
2518
|
-
</div>
|
|
2519
|
-
<div class="preview-modal-body">
|
|
2520
|
-
<div *ngIf="previewLoading" class="preview-loading">Generating preview...</div>
|
|
2521
|
-
<div *ngIf="previewError" class="preview-error">{{ previewError }}</div>
|
|
2522
|
-
<object
|
|
2523
|
-
*ngIf="previewUrl && !previewLoading"
|
|
2524
|
-
[data]="previewUrl"
|
|
2525
|
-
type="application/pdf"
|
|
2526
|
-
class="preview-pdf"
|
|
2527
|
-
>
|
|
2528
|
-
PDF preview is not supported in this browser.
|
|
2529
|
-
</object>
|
|
2530
|
-
</div>
|
|
2531
|
-
</div>
|
|
2532
|
-
</div>
|
|
2533
|
-
`, isInline: true, styles: [".preview-modal-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:10000}.preview-modal-content{background:#fff;border-radius:8px;width:90vw;height:90vh;max-width:1200px;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 8px 32px #0000004d}.preview-modal-header{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;border-bottom:1px solid #dee2e6;font-weight:700;font-size:1rem}.preview-modal-close{background:none;border:none;font-size:1.5rem;cursor:pointer;color:#6c757d;line-height:1;padding:0 .25rem}.preview-modal-close:hover{color:#333}.preview-modal-body{flex:1;overflow:hidden;display:flex;flex-direction:column}.preview-loading,.preview-error{padding:2rem;text-align:center}.preview-error{color:#dc3545}.preview-pdf{width:100%;flex:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
2534
|
-
}
|
|
2535
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPreviewButtonComponent, decorators: [{
|
|
2536
|
-
type: Component,
|
|
2537
|
-
args: [{ standalone: true, imports: [CommonModule], selector: 'epistola-preview-button-component', template: `
|
|
2538
|
-
<button
|
|
2539
|
-
type="button"
|
|
2540
|
-
class="btn btn-outline-secondary"
|
|
2541
|
-
[disabled]="disabled || loading || !hasRequiredData()"
|
|
2542
|
-
(click)="openPreview()"
|
|
2543
|
-
>
|
|
2544
|
-
<i class="mdi mdi-eye mr-1"></i>
|
|
2545
|
-
{{ loading ? 'Loading...' : buttonLabel }}
|
|
2546
|
-
</button>
|
|
2547
|
-
|
|
2548
|
-
<div *ngIf="modalOpen" class="preview-modal-overlay" (click)="closePreview()">
|
|
2549
|
-
<div class="preview-modal-content" (click)="$event.stopPropagation()">
|
|
2550
|
-
<div class="preview-modal-header">
|
|
2551
|
-
<span>Document Preview</span>
|
|
2552
|
-
<button type="button" class="preview-modal-close" (click)="closePreview()">
|
|
2553
|
-
×
|
|
2554
|
-
</button>
|
|
2555
|
-
</div>
|
|
2556
|
-
<div class="preview-modal-body">
|
|
2557
|
-
<div *ngIf="previewLoading" class="preview-loading">Generating preview...</div>
|
|
2558
|
-
<div *ngIf="previewError" class="preview-error">{{ previewError }}</div>
|
|
2559
|
-
<object
|
|
2560
|
-
*ngIf="previewUrl && !previewLoading"
|
|
2561
|
-
[data]="previewUrl"
|
|
2562
|
-
type="application/pdf"
|
|
2563
|
-
class="preview-pdf"
|
|
2564
|
-
>
|
|
2565
|
-
PDF preview is not supported in this browser.
|
|
2566
|
-
</object>
|
|
2567
|
-
</div>
|
|
2568
|
-
</div>
|
|
2569
|
-
</div>
|
|
2570
|
-
`, styles: [".preview-modal-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:10000}.preview-modal-content{background:#fff;border-radius:8px;width:90vw;height:90vh;max-width:1200px;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 8px 32px #0000004d}.preview-modal-header{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;border-bottom:1px solid #dee2e6;font-weight:700;font-size:1rem}.preview-modal-close{background:none;border:none;font-size:1.5rem;cursor:pointer;color:#6c757d;line-height:1;padding:0 .25rem}.preview-modal-close:hover{color:#333}.preview-modal-body{flex:1;overflow:hidden;display:flex;flex-direction:column}.preview-loading,.preview-error{padding:2rem;text-align:center}.preview-error{color:#dc3545}.preview-pdf{width:100%;flex:1}\n"] }]
|
|
2571
|
-
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i4.DomSanitizer }, { type: i2.ConfigService }], propDecorators: { value: [{
|
|
2572
|
-
type: Input
|
|
2573
|
-
}], valueChange: [{
|
|
2574
|
-
type: Output
|
|
2575
|
-
}], disabled: [{
|
|
2576
|
-
type: Input
|
|
2577
|
-
}], label: [{
|
|
2578
|
-
type: Input
|
|
2579
|
-
}] } });
|
|
2580
|
-
|
|
2581
2849
|
class EpistolaDocumentPreviewComponent {
|
|
2582
2850
|
epistolaPluginService;
|
|
2583
|
-
http;
|
|
2584
2851
|
sanitizer;
|
|
2585
|
-
configService;
|
|
2586
2852
|
formIoStateService;
|
|
2587
2853
|
cdr;
|
|
2854
|
+
taskContext;
|
|
2588
2855
|
value;
|
|
2589
2856
|
valueChange = new EventEmitter();
|
|
2590
2857
|
disabled = false;
|
|
@@ -2592,30 +2859,27 @@ class EpistolaDocumentPreviewComponent {
|
|
|
2592
2859
|
processDefinitionKey;
|
|
2593
2860
|
sourceActivityId;
|
|
2594
2861
|
overrideMapping;
|
|
2595
|
-
sources = [];
|
|
2596
|
-
selectedIndex = 0;
|
|
2597
|
-
discovering = false;
|
|
2598
2862
|
loading = false;
|
|
2599
2863
|
error = null;
|
|
2600
2864
|
previewUrl = null;
|
|
2601
2865
|
designMode = false;
|
|
2602
2866
|
initialized = false;
|
|
2603
2867
|
currentBlobUrl = null;
|
|
2604
|
-
discoverSubscription;
|
|
2605
2868
|
previewSubscription;
|
|
2606
|
-
|
|
2607
|
-
/** Whether the component is in configured mode (explicit process link) vs auto-discover mode */
|
|
2608
|
-
get configuredMode() {
|
|
2609
|
-
return !!this.sourceActivityId;
|
|
2610
|
-
}
|
|
2611
|
-
constructor(epistolaPluginService, http, sanitizer, configService, formIoStateService, cdr) {
|
|
2869
|
+
constructor(epistolaPluginService, sanitizer, formIoStateService, cdr, taskContext) {
|
|
2612
2870
|
this.epistolaPluginService = epistolaPluginService;
|
|
2613
|
-
this.http = http;
|
|
2614
2871
|
this.sanitizer = sanitizer;
|
|
2615
|
-
this.configService = configService;
|
|
2616
2872
|
this.formIoStateService = formIoStateService;
|
|
2617
2873
|
this.cdr = cdr;
|
|
2618
|
-
this.
|
|
2874
|
+
this.taskContext = taskContext;
|
|
2875
|
+
}
|
|
2876
|
+
/**
|
|
2877
|
+
* Resolve the active task id from {@link EpistolaTaskContextService}, populated
|
|
2878
|
+
* by {@code EpistolaTaskContextInterceptor} on the canonical Valtimo task-open
|
|
2879
|
+
* call. Returns null when used outside a task context (e.g. Formio builder).
|
|
2880
|
+
*/
|
|
2881
|
+
get currentTaskId() {
|
|
2882
|
+
return this.taskContext.taskInstanceId;
|
|
2619
2883
|
}
|
|
2620
2884
|
get overrideMappingScopes() {
|
|
2621
2885
|
return this.overrideMapping ? Object.keys(this.overrideMapping) : [];
|
|
@@ -2636,122 +2900,69 @@ class EpistolaDocumentPreviewComponent {
|
|
|
2636
2900
|
this.cdr.markForCheck();
|
|
2637
2901
|
return;
|
|
2638
2902
|
}
|
|
2639
|
-
if (this.
|
|
2640
|
-
this.
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
this.discoverSources();
|
|
2903
|
+
if (!this.sourceActivityId) {
|
|
2904
|
+
this.error = 'Preview is not configured: set the source activity on the form component.';
|
|
2905
|
+
this.cdr.markForCheck();
|
|
2906
|
+
return;
|
|
2644
2907
|
}
|
|
2908
|
+
this.loadPreview();
|
|
2645
2909
|
return;
|
|
2646
2910
|
}
|
|
2647
|
-
//
|
|
2648
|
-
if (
|
|
2649
|
-
this.
|
|
2911
|
+
// React to value changes (input overrides from the Formio wrapper).
|
|
2912
|
+
if (changes['value']) {
|
|
2913
|
+
this.loadPreview();
|
|
2650
2914
|
}
|
|
2651
2915
|
}
|
|
2652
2916
|
ngOnDestroy() {
|
|
2653
|
-
this.discoverSubscription?.unsubscribe();
|
|
2654
2917
|
this.previewSubscription?.unsubscribe();
|
|
2655
2918
|
this.revokeBlobUrl();
|
|
2656
2919
|
}
|
|
2657
|
-
onSourceChange(event) {
|
|
2658
|
-
this.selectedIndex = +event.target.value;
|
|
2659
|
-
this.loadDiscoveredPreview();
|
|
2660
|
-
}
|
|
2661
2920
|
refresh() {
|
|
2662
|
-
|
|
2663
|
-
this.loadConfiguredPreview();
|
|
2664
|
-
}
|
|
2665
|
-
else {
|
|
2666
|
-
this.loadDiscoveredPreview();
|
|
2667
|
-
}
|
|
2921
|
+
this.loadPreview();
|
|
2668
2922
|
}
|
|
2669
2923
|
/**
|
|
2670
|
-
*
|
|
2924
|
+
* Preview using the explicitly configured process link + input overrides.
|
|
2925
|
+
* Requires a runtime task context — the backend authorizes the request against
|
|
2926
|
+
* the task's process instance and case document, so all three ids must match.
|
|
2671
2927
|
*/
|
|
2672
|
-
|
|
2928
|
+
loadPreview() {
|
|
2673
2929
|
const documentId = this.formIoStateService.documentId;
|
|
2674
2930
|
if (!documentId) {
|
|
2675
2931
|
this.error = 'Could not determine document ID from context.';
|
|
2676
2932
|
this.cdr.markForCheck();
|
|
2677
2933
|
return;
|
|
2678
2934
|
}
|
|
2679
|
-
this.
|
|
2680
|
-
|
|
2681
|
-
this.cdr.markForCheck();
|
|
2682
|
-
this.revokeBlobUrl();
|
|
2683
|
-
this.previewSubscription?.unsubscribe();
|
|
2684
|
-
this.previewSubscription = this.http
|
|
2685
|
-
.post(`${this.apiEndpoint}/preview`, {
|
|
2686
|
-
documentId,
|
|
2687
|
-
processDefinitionKey: this.processDefinitionKey || null,
|
|
2688
|
-
processInstanceId: this.formIoStateService.processInstanceId || null,
|
|
2689
|
-
sourceActivityId: this.sourceActivityId,
|
|
2690
|
-
inputOverrides: this.value || null,
|
|
2691
|
-
overrides: null,
|
|
2692
|
-
}, {
|
|
2693
|
-
responseType: 'blob',
|
|
2694
|
-
headers: new HttpHeaders().set('X-Skip-Interceptor', '422'),
|
|
2695
|
-
})
|
|
2696
|
-
.subscribe({
|
|
2697
|
-
next: (blob) => this.handlePreviewSuccess(blob),
|
|
2698
|
-
error: (err) => this.handlePreviewError(err),
|
|
2699
|
-
});
|
|
2700
|
-
}
|
|
2701
|
-
/**
|
|
2702
|
-
* Auto-discover mode: discover sources from running process instances.
|
|
2703
|
-
*/
|
|
2704
|
-
discoverSources() {
|
|
2705
|
-
const documentId = this.formIoStateService.documentId;
|
|
2706
|
-
if (!documentId) {
|
|
2707
|
-
this.error = 'Could not determine document ID from context.';
|
|
2935
|
+
if (!this.sourceActivityId) {
|
|
2936
|
+
this.error = 'Preview is not configured: set the source activity on the form component.';
|
|
2708
2937
|
this.cdr.markForCheck();
|
|
2709
2938
|
return;
|
|
2710
2939
|
}
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
next: (sources) => {
|
|
2716
|
-
this.sources = sources;
|
|
2717
|
-
this.discovering = false;
|
|
2718
|
-
this.cdr.markForCheck();
|
|
2719
|
-
if (sources.length > 0) {
|
|
2720
|
-
this.selectedIndex = 0;
|
|
2721
|
-
this.loadDiscoveredPreview();
|
|
2722
|
-
}
|
|
2723
|
-
},
|
|
2724
|
-
error: (err) => {
|
|
2725
|
-
this.error = err.error?.error || 'Failed to discover preview sources';
|
|
2726
|
-
this.discovering = false;
|
|
2727
|
-
this.cdr.markForCheck();
|
|
2728
|
-
},
|
|
2729
|
-
});
|
|
2730
|
-
}
|
|
2731
|
-
/**
|
|
2732
|
-
* Auto-discover mode: load preview for the selected discovered source.
|
|
2733
|
-
*/
|
|
2734
|
-
loadDiscoveredPreview() {
|
|
2735
|
-
const source = this.sources[this.selectedIndex];
|
|
2736
|
-
if (!source)
|
|
2940
|
+
const processInstanceId = this.formIoStateService.processInstanceId;
|
|
2941
|
+
if (!processInstanceId) {
|
|
2942
|
+
this.error = 'Preview is only available from within a running process.';
|
|
2943
|
+
this.cdr.markForCheck();
|
|
2737
2944
|
return;
|
|
2738
|
-
|
|
2739
|
-
|
|
2945
|
+
}
|
|
2946
|
+
const taskId = this.currentTaskId;
|
|
2947
|
+
if (!taskId) {
|
|
2948
|
+
this.error = 'Preview is only available from within a user task.';
|
|
2949
|
+
this.cdr.markForCheck();
|
|
2740
2950
|
return;
|
|
2951
|
+
}
|
|
2741
2952
|
this.loading = true;
|
|
2742
2953
|
this.error = null;
|
|
2743
2954
|
this.cdr.markForCheck();
|
|
2744
2955
|
this.revokeBlobUrl();
|
|
2745
2956
|
this.previewSubscription?.unsubscribe();
|
|
2746
|
-
this.previewSubscription = this.
|
|
2747
|
-
.
|
|
2957
|
+
this.previewSubscription = this.epistolaPluginService
|
|
2958
|
+
.previewToBlob({
|
|
2959
|
+
taskId,
|
|
2748
2960
|
documentId,
|
|
2749
|
-
|
|
2750
|
-
|
|
2961
|
+
processDefinitionKey: this.processDefinitionKey || null,
|
|
2962
|
+
processInstanceId,
|
|
2963
|
+
sourceActivityId: this.sourceActivityId,
|
|
2964
|
+
inputOverrides: this.value || null,
|
|
2751
2965
|
overrides: null,
|
|
2752
|
-
}, {
|
|
2753
|
-
responseType: 'blob',
|
|
2754
|
-
headers: new HttpHeaders().set('X-Skip-Interceptor', '422'),
|
|
2755
2966
|
})
|
|
2756
2967
|
.subscribe({
|
|
2757
2968
|
next: (blob) => this.handlePreviewSuccess(blob),
|
|
@@ -2793,7 +3004,7 @@ class EpistolaDocumentPreviewComponent {
|
|
|
2793
3004
|
this.previewUrl = null;
|
|
2794
3005
|
}
|
|
2795
3006
|
}
|
|
2796
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaDocumentPreviewComponent, deps: [{ token: EpistolaPluginService }, { token:
|
|
3007
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaDocumentPreviewComponent, deps: [{ token: EpistolaPluginService }, { token: i2$4.DomSanitizer }, { token: i3.FormIoStateService }, { token: i0.ChangeDetectorRef }, { token: EpistolaTaskContextService }], target: i0.ɵɵFactoryTarget.Component });
|
|
2797
3008
|
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" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
2798
3009
|
<!-- Design-time view: show configuration summary when no runtime context -->
|
|
2799
3010
|
<div *ngIf="designMode" class="epistola-preview-panel">
|
|
@@ -2829,55 +3040,26 @@ class EpistolaDocumentPreviewComponent {
|
|
|
2829
3040
|
<div class="preview-header">
|
|
2830
3041
|
<span>{{ label || 'Document Preview' }}</span>
|
|
2831
3042
|
<div class="preview-controls">
|
|
2832
|
-
<
|
|
2833
|
-
*ngIf="!sourceActivityId && sources.length > 1"
|
|
2834
|
-
class="preview-select"
|
|
2835
|
-
[value]="selectedIndex"
|
|
2836
|
-
(change)="onSourceChange($event)"
|
|
2837
|
-
>
|
|
2838
|
-
<option *ngFor="let source of sources; let i = index" [value]="i">
|
|
2839
|
-
{{ source.templateName }} ({{ source.activityId }})
|
|
2840
|
-
</option>
|
|
2841
|
-
</select>
|
|
2842
|
-
<button
|
|
2843
|
-
type="button"
|
|
2844
|
-
class="preview-refresh"
|
|
2845
|
-
[disabled]="loading || discovering"
|
|
2846
|
-
(click)="refresh()"
|
|
2847
|
-
>
|
|
3043
|
+
<button type="button" class="preview-refresh" [disabled]="loading" (click)="refresh()">
|
|
2848
3044
|
<i class="mdi mdi-refresh mr-1"></i>
|
|
2849
3045
|
{{ loading ? 'Generating...' : 'Refresh' }}
|
|
2850
3046
|
</button>
|
|
2851
3047
|
</div>
|
|
2852
3048
|
</div>
|
|
2853
3049
|
<div class="preview-body">
|
|
2854
|
-
<div *ngIf="
|
|
2855
|
-
<div *ngIf="
|
|
2856
|
-
<div *ngIf="error && !loading && !discovering" class="preview-unavailable">
|
|
3050
|
+
<div *ngIf="loading" class="preview-loading">Generating preview...</div>
|
|
3051
|
+
<div *ngIf="error && !loading" class="preview-unavailable">
|
|
2857
3052
|
<i class="mdi mdi-information-outline"></i>
|
|
2858
|
-
|
|
3053
|
+
{{ error }}
|
|
2859
3054
|
</div>
|
|
2860
3055
|
<object
|
|
2861
|
-
*ngIf="previewUrl && !loading
|
|
3056
|
+
*ngIf="previewUrl && !loading"
|
|
2862
3057
|
[data]="previewUrl"
|
|
2863
3058
|
type="application/pdf"
|
|
2864
3059
|
class="preview-pdf"
|
|
2865
3060
|
>
|
|
2866
3061
|
PDF preview is not supported in this browser.
|
|
2867
3062
|
</object>
|
|
2868
|
-
<div
|
|
2869
|
-
*ngIf="
|
|
2870
|
-
!previewUrl &&
|
|
2871
|
-
!loading &&
|
|
2872
|
-
!discovering &&
|
|
2873
|
-
!error &&
|
|
2874
|
-
!sourceActivityId &&
|
|
2875
|
-
sources.length === 0
|
|
2876
|
-
"
|
|
2877
|
-
class="preview-empty"
|
|
2878
|
-
>
|
|
2879
|
-
No previewable documents found
|
|
2880
|
-
</div>
|
|
2881
3063
|
</div>
|
|
2882
3064
|
</div>
|
|
2883
3065
|
`, isInline: true, styles: [".epistola-preview-panel{border:1px solid #dee2e6;border-radius:4px;background:#f8f9fa;display:flex;flex-direction:column}.preview-header{display:flex;justify-content:space-between;align-items:center;padding:.5rem 1rem;border-bottom:1px solid #dee2e6;font-weight:700;color:#495057;flex-wrap:wrap;gap:.5rem}.preview-controls{display:flex;align-items:center;gap:.5rem}.preview-select{border:1px solid #ced4da;border-radius:4px;padding:.25rem .5rem;font-size:.8rem;background:#fff;max-width:300px}.preview-refresh{background:none;border:1px solid #6c757d;border-radius:4px;color:#6c757d;padding:.25rem .75rem;font-size:.8rem;cursor:pointer;display:flex;align-items:center;white-space:nowrap}.preview-refresh:hover:not(:disabled){background:#e9ecef}.preview-refresh:disabled{opacity:.5;cursor:not-allowed}.preview-body{display:flex;flex-direction:column;min-height:500px}.preview-loading{padding:2rem;text-align:center;color:#6c757d;font-style:italic}.preview-unavailable{padding:1.5rem;text-align:center;color:#6c757d;font-style:italic}.preview-unavailable i{margin-right:.25rem}.preview-pdf{width:100%;flex:1;min-height:500px}.preview-empty{padding:2rem;text-align:center;color:#6c757d;font-style:italic}.design-info{padding:1rem;min-height:auto}.design-section{margin-bottom:.75rem}.design-label{font-size:.7rem;text-transform:uppercase;color:#868e96;font-weight:600;letter-spacing:.05em}.design-value{font-family:monospace;font-size:.85rem;color:#212529;margin-bottom:.25rem}.design-mapping{margin-top:.25rem}.design-entry{font-family:monospace;font-size:.8rem;color:#495057;padding:.15rem 0}.design-scope{color:#0d6efd}.design-field{color:#198754}.design-entry i{font-size:.7rem;margin:0 .25rem;color:#adb5bd}.design-unconfigured{color:#6c757d;font-style:italic;font-size:.85rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
@@ -2919,59 +3101,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
2919
3101
|
<div class="preview-header">
|
|
2920
3102
|
<span>{{ label || 'Document Preview' }}</span>
|
|
2921
3103
|
<div class="preview-controls">
|
|
2922
|
-
<
|
|
2923
|
-
*ngIf="!sourceActivityId && sources.length > 1"
|
|
2924
|
-
class="preview-select"
|
|
2925
|
-
[value]="selectedIndex"
|
|
2926
|
-
(change)="onSourceChange($event)"
|
|
2927
|
-
>
|
|
2928
|
-
<option *ngFor="let source of sources; let i = index" [value]="i">
|
|
2929
|
-
{{ source.templateName }} ({{ source.activityId }})
|
|
2930
|
-
</option>
|
|
2931
|
-
</select>
|
|
2932
|
-
<button
|
|
2933
|
-
type="button"
|
|
2934
|
-
class="preview-refresh"
|
|
2935
|
-
[disabled]="loading || discovering"
|
|
2936
|
-
(click)="refresh()"
|
|
2937
|
-
>
|
|
3104
|
+
<button type="button" class="preview-refresh" [disabled]="loading" (click)="refresh()">
|
|
2938
3105
|
<i class="mdi mdi-refresh mr-1"></i>
|
|
2939
3106
|
{{ loading ? 'Generating...' : 'Refresh' }}
|
|
2940
3107
|
</button>
|
|
2941
3108
|
</div>
|
|
2942
3109
|
</div>
|
|
2943
3110
|
<div class="preview-body">
|
|
2944
|
-
<div *ngIf="
|
|
2945
|
-
<div *ngIf="
|
|
2946
|
-
<div *ngIf="error && !loading && !discovering" class="preview-unavailable">
|
|
3111
|
+
<div *ngIf="loading" class="preview-loading">Generating preview...</div>
|
|
3112
|
+
<div *ngIf="error && !loading" class="preview-unavailable">
|
|
2947
3113
|
<i class="mdi mdi-information-outline"></i>
|
|
2948
|
-
|
|
3114
|
+
{{ error }}
|
|
2949
3115
|
</div>
|
|
2950
3116
|
<object
|
|
2951
|
-
*ngIf="previewUrl && !loading
|
|
3117
|
+
*ngIf="previewUrl && !loading"
|
|
2952
3118
|
[data]="previewUrl"
|
|
2953
3119
|
type="application/pdf"
|
|
2954
3120
|
class="preview-pdf"
|
|
2955
3121
|
>
|
|
2956
3122
|
PDF preview is not supported in this browser.
|
|
2957
3123
|
</object>
|
|
2958
|
-
<div
|
|
2959
|
-
*ngIf="
|
|
2960
|
-
!previewUrl &&
|
|
2961
|
-
!loading &&
|
|
2962
|
-
!discovering &&
|
|
2963
|
-
!error &&
|
|
2964
|
-
!sourceActivityId &&
|
|
2965
|
-
sources.length === 0
|
|
2966
|
-
"
|
|
2967
|
-
class="preview-empty"
|
|
2968
|
-
>
|
|
2969
|
-
No previewable documents found
|
|
2970
|
-
</div>
|
|
2971
3124
|
</div>
|
|
2972
3125
|
</div>
|
|
2973
3126
|
`, styles: [".epistola-preview-panel{border:1px solid #dee2e6;border-radius:4px;background:#f8f9fa;display:flex;flex-direction:column}.preview-header{display:flex;justify-content:space-between;align-items:center;padding:.5rem 1rem;border-bottom:1px solid #dee2e6;font-weight:700;color:#495057;flex-wrap:wrap;gap:.5rem}.preview-controls{display:flex;align-items:center;gap:.5rem}.preview-select{border:1px solid #ced4da;border-radius:4px;padding:.25rem .5rem;font-size:.8rem;background:#fff;max-width:300px}.preview-refresh{background:none;border:1px solid #6c757d;border-radius:4px;color:#6c757d;padding:.25rem .75rem;font-size:.8rem;cursor:pointer;display:flex;align-items:center;white-space:nowrap}.preview-refresh:hover:not(:disabled){background:#e9ecef}.preview-refresh:disabled{opacity:.5;cursor:not-allowed}.preview-body{display:flex;flex-direction:column;min-height:500px}.preview-loading{padding:2rem;text-align:center;color:#6c757d;font-style:italic}.preview-unavailable{padding:1.5rem;text-align:center;color:#6c757d;font-style:italic}.preview-unavailable i{margin-right:.25rem}.preview-pdf{width:100%;flex:1;min-height:500px}.preview-empty{padding:2rem;text-align:center;color:#6c757d;font-style:italic}.design-info{padding:1rem;min-height:auto}.design-section{margin-bottom:.75rem}.design-label{font-size:.7rem;text-transform:uppercase;color:#868e96;font-weight:600;letter-spacing:.05em}.design-value{font-family:monospace;font-size:.85rem;color:#212529;margin-bottom:.25rem}.design-mapping{margin-top:.25rem}.design-entry{font-family:monospace;font-size:.8rem;color:#495057;padding:.15rem 0}.design-scope{color:#0d6efd}.design-field{color:#198754}.design-entry i{font-size:.7rem;margin:0 .25rem;color:#adb5bd}.design-unconfigured{color:#6c757d;font-style:italic;font-size:.85rem}\n"] }]
|
|
2974
|
-
}], ctorParameters: () => [{ type: EpistolaPluginService }, { type:
|
|
3127
|
+
}], ctorParameters: () => [{ type: EpistolaPluginService }, { type: i2$4.DomSanitizer }, { type: i3.FormIoStateService }, { type: i0.ChangeDetectorRef }, { type: EpistolaTaskContextService }], propDecorators: { value: [{
|
|
2975
3128
|
type: Input
|
|
2976
3129
|
}], valueChange: [{
|
|
2977
3130
|
type: Output
|
|
@@ -2994,8 +3147,18 @@ class EpistolaAdminPageComponent {
|
|
|
2994
3147
|
cards = [];
|
|
2995
3148
|
selectedCard = null;
|
|
2996
3149
|
activeTab = 'actions';
|
|
3150
|
+
overviewTab = 'configurations';
|
|
2997
3151
|
loading = false;
|
|
2998
3152
|
pluginVersion = null;
|
|
3153
|
+
changelog = null;
|
|
3154
|
+
changelogLoading = false;
|
|
3155
|
+
validationViolations = [];
|
|
3156
|
+
reconcilingExecutionIds = new Set();
|
|
3157
|
+
reconcileFeedback = null;
|
|
3158
|
+
catalogs = [];
|
|
3159
|
+
catalogsLoading = false;
|
|
3160
|
+
redeployingSlugs = new Set();
|
|
3161
|
+
catalogFeedback = null;
|
|
2999
3162
|
connectionStatuses = [];
|
|
3000
3163
|
usageEntries = [];
|
|
3001
3164
|
pendingJobs = [];
|
|
@@ -3011,7 +3174,7 @@ class EpistolaAdminPageComponent {
|
|
|
3011
3174
|
ngOnInit() {
|
|
3012
3175
|
this.deepLinkConfigId = this.route.snapshot.queryParamMap.get('configurationId');
|
|
3013
3176
|
const tab = this.route.snapshot.queryParamMap.get('tab');
|
|
3014
|
-
if (tab === 'pending' || tab === 'actions') {
|
|
3177
|
+
if (tab === 'pending' || tab === 'actions' || tab === 'catalogs') {
|
|
3015
3178
|
this.activeTab = tab;
|
|
3016
3179
|
}
|
|
3017
3180
|
this.loadData();
|
|
@@ -3021,16 +3184,38 @@ class EpistolaAdminPageComponent {
|
|
|
3021
3184
|
this.selectedCard = card;
|
|
3022
3185
|
this.activeTab = 'actions';
|
|
3023
3186
|
this.updateUrl(card.configurationId, this.activeTab);
|
|
3187
|
+
this.loadCatalogs(card.configurationId);
|
|
3024
3188
|
}
|
|
3025
3189
|
backToOverview() {
|
|
3026
3190
|
this.selectedCard = null;
|
|
3027
3191
|
this.activeTab = 'actions';
|
|
3192
|
+
this.catalogs = [];
|
|
3193
|
+
this.catalogFeedback = null;
|
|
3028
3194
|
this.updateUrl(null, null);
|
|
3029
3195
|
}
|
|
3030
3196
|
setActiveTab(tab) {
|
|
3031
3197
|
this.activeTab = tab;
|
|
3032
3198
|
this.updateUrl(this.selectedCard?.configurationId ?? null, tab);
|
|
3033
3199
|
}
|
|
3200
|
+
setOverviewTab(tab) {
|
|
3201
|
+
this.overviewTab = tab;
|
|
3202
|
+
if (tab === 'changelog' && this.changelog === null && !this.changelogLoading) {
|
|
3203
|
+
this.loadChangelog();
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
loadChangelog() {
|
|
3207
|
+
this.changelogLoading = true;
|
|
3208
|
+
this.adminService.getChangelog().subscribe({
|
|
3209
|
+
next: (releases) => {
|
|
3210
|
+
this.changelog = releases;
|
|
3211
|
+
this.changelogLoading = false;
|
|
3212
|
+
},
|
|
3213
|
+
error: () => {
|
|
3214
|
+
this.changelog = [];
|
|
3215
|
+
this.changelogLoading = false;
|
|
3216
|
+
},
|
|
3217
|
+
});
|
|
3218
|
+
}
|
|
3034
3219
|
refresh() {
|
|
3035
3220
|
this.selectedCard = null;
|
|
3036
3221
|
this.loadData();
|
|
@@ -3047,6 +3232,113 @@ class EpistolaAdminPageComponent {
|
|
|
3047
3232
|
},
|
|
3048
3233
|
});
|
|
3049
3234
|
}
|
|
3235
|
+
/**
|
|
3236
|
+
* Manually reconcile a single stuck catch event. Pulls the current Epistola job
|
|
3237
|
+
* status and re-runs message correlation if the job is in a terminal state.
|
|
3238
|
+
* Refreshes the Pending list on success so the row drops out of the table.
|
|
3239
|
+
*/
|
|
3240
|
+
reconcilePending(job) {
|
|
3241
|
+
if (this.reconcilingExecutionIds.has(job.executionId)) {
|
|
3242
|
+
return;
|
|
3243
|
+
}
|
|
3244
|
+
this.reconcilingExecutionIds.add(job.executionId);
|
|
3245
|
+
this.reconcileFeedback = null;
|
|
3246
|
+
this.adminService.reconcilePending(job.executionId).subscribe({
|
|
3247
|
+
next: (result) => {
|
|
3248
|
+
this.reconcilingExecutionIds.delete(job.executionId);
|
|
3249
|
+
this.reconcileFeedback = {
|
|
3250
|
+
executionId: job.executionId,
|
|
3251
|
+
type: 'success',
|
|
3252
|
+
message: `OK (${result.epistolaStatus}, ${result.correlatedCount ?? 0} correlated)`,
|
|
3253
|
+
};
|
|
3254
|
+
this.loadData();
|
|
3255
|
+
},
|
|
3256
|
+
error: (err) => {
|
|
3257
|
+
this.reconcilingExecutionIds.delete(job.executionId);
|
|
3258
|
+
// 409 from the backend = job is still PENDING/IN_PROGRESS, surface as
|
|
3259
|
+
// informational rather than an error.
|
|
3260
|
+
if (err?.status === 409) {
|
|
3261
|
+
const status = err.error?.epistolaStatus ?? 'still pending';
|
|
3262
|
+
this.reconcileFeedback = {
|
|
3263
|
+
executionId: job.executionId,
|
|
3264
|
+
type: 'pending',
|
|
3265
|
+
message: `Epistola: ${status}. Try again in a moment.`,
|
|
3266
|
+
};
|
|
3267
|
+
}
|
|
3268
|
+
else {
|
|
3269
|
+
const message = err?.error?.detail ?? err?.error?.message ?? err?.message ?? 'unknown error';
|
|
3270
|
+
this.reconcileFeedback = {
|
|
3271
|
+
executionId: job.executionId,
|
|
3272
|
+
type: 'error',
|
|
3273
|
+
message,
|
|
3274
|
+
};
|
|
3275
|
+
}
|
|
3276
|
+
},
|
|
3277
|
+
});
|
|
3278
|
+
}
|
|
3279
|
+
isReconciling(job) {
|
|
3280
|
+
return this.reconcilingExecutionIds.has(job.executionId);
|
|
3281
|
+
}
|
|
3282
|
+
/**
|
|
3283
|
+
* Load the classpath catalogs available to (re)deploy for the given
|
|
3284
|
+
* configuration. Lazy — only called when a configuration is opened, not for
|
|
3285
|
+
* every card in the overview.
|
|
3286
|
+
*/
|
|
3287
|
+
loadCatalogs(configurationId) {
|
|
3288
|
+
this.catalogsLoading = true;
|
|
3289
|
+
this.catalogFeedback = null;
|
|
3290
|
+
this.adminService.getClasspathCatalogs(configurationId).subscribe({
|
|
3291
|
+
next: (catalogs) => {
|
|
3292
|
+
this.catalogs = catalogs;
|
|
3293
|
+
this.catalogsLoading = false;
|
|
3294
|
+
},
|
|
3295
|
+
error: () => {
|
|
3296
|
+
this.catalogs = [];
|
|
3297
|
+
this.catalogsLoading = false;
|
|
3298
|
+
},
|
|
3299
|
+
});
|
|
3300
|
+
}
|
|
3301
|
+
/**
|
|
3302
|
+
* Force-redeploy a single classpath catalog to the selected configuration's
|
|
3303
|
+
* Epistola installation. Explicit operator action — the backend bypasses the
|
|
3304
|
+
* templateSyncEnabled gate and the version-skip check. Reloads the catalog
|
|
3305
|
+
* list on success so the deployed version / up-to-date hint refreshes.
|
|
3306
|
+
*/
|
|
3307
|
+
redeployCatalog(catalog) {
|
|
3308
|
+
if (!this.selectedCard || this.redeployingSlugs.has(catalog.slug)) {
|
|
3309
|
+
return;
|
|
3310
|
+
}
|
|
3311
|
+
const configurationId = this.selectedCard.configurationId;
|
|
3312
|
+
this.redeployingSlugs.add(catalog.slug);
|
|
3313
|
+
this.catalogFeedback = null;
|
|
3314
|
+
this.adminService.redeployCatalog(configurationId, catalog.slug).subscribe({
|
|
3315
|
+
next: (result) => {
|
|
3316
|
+
this.redeployingSlugs.delete(catalog.slug);
|
|
3317
|
+
this.catalogFeedback = {
|
|
3318
|
+
slug: catalog.slug,
|
|
3319
|
+
type: 'success',
|
|
3320
|
+
message: `OK — installed ${result.installed}, updated ${result.updated}, failed ${result.failed} (of ${result.total})`,
|
|
3321
|
+
};
|
|
3322
|
+
this.loadCatalogs(configurationId);
|
|
3323
|
+
},
|
|
3324
|
+
error: (err) => {
|
|
3325
|
+
this.redeployingSlugs.delete(catalog.slug);
|
|
3326
|
+
const message = err?.error?.errorMessage ??
|
|
3327
|
+
err?.error?.detail ??
|
|
3328
|
+
err?.error?.message ??
|
|
3329
|
+
err?.message ??
|
|
3330
|
+
'unknown error';
|
|
3331
|
+
this.catalogFeedback = {
|
|
3332
|
+
slug: catalog.slug,
|
|
3333
|
+
type: 'error',
|
|
3334
|
+
message,
|
|
3335
|
+
};
|
|
3336
|
+
},
|
|
3337
|
+
});
|
|
3338
|
+
}
|
|
3339
|
+
isRedeploying(catalog) {
|
|
3340
|
+
return this.redeployingSlugs.has(catalog.slug);
|
|
3341
|
+
}
|
|
3050
3342
|
updateUrl(configurationId, tab) {
|
|
3051
3343
|
this.router.navigate([], {
|
|
3052
3344
|
relativeTo: this.route,
|
|
@@ -3098,6 +3390,16 @@ class EpistolaAdminPageComponent {
|
|
|
3098
3390
|
this.tryBuildCards();
|
|
3099
3391
|
},
|
|
3100
3392
|
});
|
|
3393
|
+
// Validation violations are independent of cards — load alongside but don't
|
|
3394
|
+
// gate the loading flag on them.
|
|
3395
|
+
this.adminService.getValidationViolations().subscribe({
|
|
3396
|
+
next: (violations) => {
|
|
3397
|
+
this.validationViolations = violations;
|
|
3398
|
+
},
|
|
3399
|
+
error: () => {
|
|
3400
|
+
this.validationViolations = [];
|
|
3401
|
+
},
|
|
3402
|
+
});
|
|
3101
3403
|
}
|
|
3102
3404
|
tryBuildCards() {
|
|
3103
3405
|
if (!this.connectionLoaded || !this.usageLoaded || !this.pendingLoaded) {
|
|
@@ -3126,6 +3428,7 @@ class EpistolaAdminPageComponent {
|
|
|
3126
3428
|
const match = this.cards.find((c) => c.configurationId === this.deepLinkConfigId);
|
|
3127
3429
|
if (match) {
|
|
3128
3430
|
this.selectedCard = match;
|
|
3431
|
+
this.loadCatalogs(match.configurationId);
|
|
3129
3432
|
}
|
|
3130
3433
|
this.deepLinkConfigId = null;
|
|
3131
3434
|
}
|
|
@@ -3138,13 +3441,13 @@ class EpistolaAdminPageComponent {
|
|
|
3138
3441
|
},
|
|
3139
3442
|
});
|
|
3140
3443
|
}
|
|
3141
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminPageComponent, deps: [{ token: EpistolaAdminService }, { token: i2$
|
|
3142
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: EpistolaAdminPageComponent, isStandalone: true, selector: "epistola-admin-page", ngImport: i0, template: "<div class=\"epistola-admin\">\n <!-- Overview: card grid (no configuration selected) -->\n <ng-container *ngIf=\"!selectedCard\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <div class=\"d-flex align-items-center\">\n <h5 class=\"mb-0\">{{ 'epistolaAdminOverview' | pluginTranslate: 'epistola' | async }}</h5>\n <span *ngIf=\"pluginVersion\" class=\"version-badge ms-2\">v{{ pluginVersion }}</span>\n </div>\n <button class=\"btn btn-outline-primary btn-sm\" (click)=\"refresh()\" [disabled]=\"loading\">\n {{ 'epistolaAdminRefresh' | pluginTranslate: 'epistola' | async }}\n </button>\n </div>\n\n <div *ngIf=\"loading\" class=\"text-muted\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!loading && cards.length === 0\" class=\"text-muted\">\n {{ 'epistolaAdminNoConfigurations' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!loading && cards.length > 0\" class=\"card-grid\">\n <div\n *ngFor=\"let card of cards\"\n class=\"config-card\"\n [class.config-card--ok]=\"card.reachable && card.problemCount === 0\"\n [class.config-card--warning]=\"card.reachable && card.problemCount > 0\"\n [class.config-card--error]=\"!card.reachable\"\n (click)=\"selectConfiguration(card)\"\n >\n <div class=\"config-card__header\">\n <span\n class=\"status-dot\"\n [class.status-dot--ok]=\"card.reachable\"\n [class.status-dot--error]=\"!card.reachable\"\n >\n </span>\n <h5 class=\"config-card__title\">{{ card.configurationTitle }}</h5>\n </div>\n\n <div class=\"config-card__body\">\n <div class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminTenantId' | pluginTranslate: 'epistola' | async\n }}</span>\n <code class=\"config-card__value\">{{ card.tenantId }}</code>\n </div>\n <div class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminStatus' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" [type]=\"card.reachable ? 'green' : 'red'\">\n {{\n card.reachable\n ? ('epistolaAdminConnected' | pluginTranslate: 'epistola' | async)\n : ('epistolaAdminUnreachable' | pluginTranslate: 'epistola' | async)\n }}\n </cds-tag>\n </div>\n <div *ngIf=\"card.serverVersion\" class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminServerVersion' | pluginTranslate: 'epistola' | async\n }}</span>\n <span class=\"config-card__value\">{{ card.serverVersion }}</span>\n </div>\n <div class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminPluginActions' | pluginTranslate: 'epistola' | async\n }}</span>\n <span class=\"config-card__value\">{{ card.usageCount }}</span>\n </div>\n <div *ngIf=\"card.pendingJobs.length > 0\" class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminPendingJobs' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" type=\"blue\">{{ card.pendingJobs.length }}</cds-tag>\n </div>\n <div *ngIf=\"card.problemCount > 0\" class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminProblems' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" type=\"red\">{{ card.problemCount }}</cds-tag>\n </div>\n </div>\n\n <div class=\"config-card__footer\">\n <span class=\"config-card__latency\">{{ card.latencyMs }} ms</span>\n </div>\n </div>\n </div>\n </ng-container>\n\n <!-- Detail view: selected configuration -->\n <ng-container *ngIf=\"selectedCard\">\n <div class=\"detail-header mb-3\">\n <button class=\"btn btn-link btn-sm p-0\" (click)=\"backToOverview()\">\n ← {{ 'epistolaAdminBackToOverview' | pluginTranslate: 'epistola' | async }}\n </button>\n </div>\n\n <div class=\"detail-summary mb-4\">\n <h4>\n <span\n class=\"status-dot me-2\"\n [class.status-dot--ok]=\"selectedCard.reachable\"\n [class.status-dot--error]=\"!selectedCard.reachable\"\n >\n </span>\n {{ selectedCard.configurationTitle }}\n </h4>\n\n <table class=\"table table-sm detail-info-table\">\n <tbody>\n <tr>\n <th>{{ 'epistolaAdminTenantId' | pluginTranslate: 'epistola' | async }}</th>\n <td>\n <code>{{ selectedCard.tenantId }}</code>\n </td>\n </tr>\n <tr>\n <th>{{ 'epistolaAdminStatus' | pluginTranslate: 'epistola' | async }}</th>\n <td>\n <cds-tag size=\"sm\" [type]=\"selectedCard.reachable ? 'green' : 'red'\">\n {{\n selectedCard.reachable\n ? ('epistolaAdminConnected' | pluginTranslate: 'epistola' | async)\n : ('epistolaAdminUnreachable' | pluginTranslate: 'epistola' | async)\n }}\n </cds-tag>\n <span class=\"text-muted ms-2\">{{ selectedCard.latencyMs }} ms</span>\n </td>\n </tr>\n <tr *ngIf=\"selectedCard.serverVersion\">\n <th>{{ 'epistolaAdminServerVersion' | pluginTranslate: 'epistola' | async }}</th>\n <td>{{ selectedCard.serverVersion }}</td>\n </tr>\n <tr *ngIf=\"selectedCard.errorMessage\">\n <th>{{ 'epistolaAdminError' | pluginTranslate: 'epistola' | async }}</th>\n <td class=\"text-danger\">{{ selectedCard.errorMessage }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- Tabs -->\n <ng-template #actionsHeading>\n {{ 'epistolaAdminPluginActions' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" type=\"gray\" class=\"ms-1\">{{ selectedCard.usageEntries.length }}</cds-tag>\n </ng-template>\n\n <ng-template #pendingHeading>\n {{ 'epistolaAdminPendingJobs' | pluginTranslate: 'epistola' | async }}\n <cds-tag\n size=\"sm\"\n [type]=\"selectedCard.pendingJobs.length > 0 ? 'blue' : 'gray'\"\n class=\"ms-1\"\n >\n {{ selectedCard.pendingJobs.length }}\n </cds-tag>\n </ng-template>\n\n <cds-tabs [cacheActive]=\"true\" type=\"contained\">\n <cds-tab\n [heading]=\"actionsHeading\"\n [active]=\"activeTab === 'actions'\"\n (selected)=\"setActiveTab('actions')\"\n >\n <div *ngIf=\"selectedCard.usageEntries.length === 0\" class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminNoUsageForConfig' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table *ngIf=\"selectedCard.usageEntries.length > 0\" class=\"table table-striped mt-3\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminCase' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminProcess' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminActivity' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminAction' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminProblems' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr\n *ngFor=\"let entry of selectedCard.usageEntries\"\n [class.table-warning]=\"entry.problems.length > 0\"\n >\n <td>{{ entry.caseDefinitionKey || '-' }}</td>\n <td>\n <a\n *ngIf=\"entry.caseDefinitionKey && entry.caseDefinitionVersionTag\"\n [routerLink]=\"[\n '/case-management',\n 'case',\n entry.caseDefinitionKey,\n 'version',\n entry.caseDefinitionVersionTag,\n 'processes',\n entry.processDefinitionKey,\n ]\"\n class=\"usage-link\"\n >\n {{ entry.processDefinitionName }}\n </a>\n <span *ngIf=\"!entry.caseDefinitionKey || !entry.caseDefinitionVersionTag\">\n {{ entry.processDefinitionName }}\n </span>\n </td>\n <td>{{ entry.activityName }}</td>\n <td>\n <code>{{ entry.actionKey }}</code>\n </td>\n <td>\n <cds-tag *ngIf=\"entry.problems.length === 0\" size=\"sm\" type=\"green\">OK</cds-tag>\n <cds-tag\n *ngFor=\"let problem of entry.problems\"\n size=\"sm\"\n type=\"red\"\n class=\"d-block mb-1\"\n >\n {{ problem }}\n </cds-tag>\n </td>\n <td>\n <button\n class=\"btn btn-sm btn-outline-secondary\"\n (click)=\"exportProcessLink(entry)\"\n [title]=\"'epistolaAdminExport' | pluginTranslate: 'epistola' | async\"\n >\n ⤓\n </button>\n </td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n\n <cds-tab\n [heading]=\"pendingHeading\"\n [active]=\"activeTab === 'pending'\"\n (selected)=\"setActiveTab('pending')\"\n >\n <div *ngIf=\"selectedCard.pendingJobs.length === 0\" class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminNoPendingJobs' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table *ngIf=\"selectedCard.pendingJobs.length > 0\" class=\"table table-striped mt-3\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminProcess' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminActivity' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminRequestId' | pluginTranslate: 'epistola' | async }}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let job of selectedCard.pendingJobs\">\n <td>{{ job.processDefinitionName }}</td>\n <td>{{ job.activityName }}</td>\n <td>\n <code>{{ job.requestId }}</code>\n </td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n </cds-tabs>\n </ng-container>\n</div>\n", styles: [".epistola-admin{padding:1.5rem}.epistola-admin .version-badge{font-size:.75rem;font-weight:500;padding:.2em .6em;border-radius:4px;background-color:#e8e8e8;color:#525252}.epistola-admin .badge{font-size:.85em;padding:.35em .65em}.epistola-admin code{font-size:.9em;color:#525252}.epistola-admin table th{font-weight:600;white-space:nowrap}.epistola-admin .status-dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:.5rem;flex-shrink:0;background-color:#adb5bd}.epistola-admin .status-dot--ok{background-color:#198754}.epistola-admin .status-dot--error{background-color:#dc3545}.epistola-admin .card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:1rem}.epistola-admin .config-card{border:1px solid #dee2e6;border-radius:8px;padding:1.25rem;cursor:pointer;transition:box-shadow .15s ease,border-color .15s ease;background:#fff}.epistola-admin .config-card:hover{box-shadow:0 2px 8px #0000001a}.epistola-admin .config-card--ok{border-left:4px solid #198754}.epistola-admin .config-card--warning{border-left:4px solid #ffc107}.epistola-admin .config-card--error{border-left:4px solid #dc3545}.epistola-admin .config-card__header{display:flex;align-items:center;margin-bottom:1rem}.epistola-admin .config-card__title{margin:0;font-size:1.05rem;font-weight:600;color:#161616}.epistola-admin .config-card__body{display:flex;flex-direction:column;gap:.5rem}.epistola-admin .config-card__field{display:flex;justify-content:space-between;align-items:center}.epistola-admin .config-card__label{font-size:.875rem;color:#6c757d}.epistola-admin .config-card__value{font-size:.875rem;color:#161616}.epistola-admin .config-card__footer{margin-top:1rem;padding-top:.75rem;border-top:1px solid #f0f0f0;text-align:right}.epistola-admin .config-card__latency{font-size:.8rem;color:#adb5bd}.epistola-admin .usage-link{color:#0f62fe;text-decoration:none}.epistola-admin .usage-link:hover{text-decoration:underline}.epistola-admin .detail-info-table{max-width:500px}.epistola-admin .detail-info-table th{width:140px}.epistola-admin .detail-summary{padding:1rem 0;border-bottom:1px solid #dee2e6}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$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"] }] });
|
|
3444
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminPageComponent, deps: [{ token: EpistolaAdminService }, { token: i2$5.ActivatedRoute }, { token: i2$5.Router }], target: i0.ɵɵFactoryTarget.Component });
|
|
3445
|
+
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 <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 *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 </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>{{ '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 <code>{{ job.requestId }}</code>\n </td>\n <td class=\"text-end\">\n <button\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 <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: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$5.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$1.Tabs, selector: "cds-tabs, ibm-tabs", inputs: ["position", "cacheActive", "followFocus", "isNavigation", "ariaLabel", "ariaLabelledby", "type", "theme", "skeleton"] }, { kind: "component", type: i5$1.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"] }] });
|
|
3143
3446
|
}
|
|
3144
3447
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminPageComponent, decorators: [{
|
|
3145
3448
|
type: Component,
|
|
3146
|
-
args: [{ selector: 'epistola-admin-page', standalone: true, imports: [CommonModule, RouterModule, PluginTranslatePipeModule, TabsModule, TagModule], template: "<div class=\"epistola-admin\">\n <!-- Overview: card grid (no configuration selected) -->\n <ng-container *ngIf=\"!selectedCard\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <div class=\"d-flex align-items-center\">\n <h5 class=\"mb-0\">{{ 'epistolaAdminOverview' | pluginTranslate: 'epistola' | async }}</h5>\n <span *ngIf=\"pluginVersion\" class=\"version-badge ms-2\">v{{ pluginVersion }}</span>\n </div>\n <button class=\"btn btn-outline-primary btn-sm\" (click)=\"refresh()\" [disabled]=\"loading\">\n {{ 'epistolaAdminRefresh' | pluginTranslate: 'epistola' | async }}\n </button>\n </div>\n\n <div *ngIf=\"loading\" class=\"text-muted\">\n {{ 'epistolaAdminLoading' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!loading && cards.length === 0\" class=\"text-muted\">\n {{ 'epistolaAdminNoConfigurations' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <div *ngIf=\"!loading && cards.length > 0\" class=\"card-grid\">\n <div\n *ngFor=\"let card of cards\"\n class=\"config-card\"\n [class.config-card--ok]=\"card.reachable && card.problemCount === 0\"\n [class.config-card--warning]=\"card.reachable && card.problemCount > 0\"\n [class.config-card--error]=\"!card.reachable\"\n (click)=\"selectConfiguration(card)\"\n >\n <div class=\"config-card__header\">\n <span\n class=\"status-dot\"\n [class.status-dot--ok]=\"card.reachable\"\n [class.status-dot--error]=\"!card.reachable\"\n >\n </span>\n <h5 class=\"config-card__title\">{{ card.configurationTitle }}</h5>\n </div>\n\n <div class=\"config-card__body\">\n <div class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminTenantId' | pluginTranslate: 'epistola' | async\n }}</span>\n <code class=\"config-card__value\">{{ card.tenantId }}</code>\n </div>\n <div class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminStatus' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" [type]=\"card.reachable ? 'green' : 'red'\">\n {{\n card.reachable\n ? ('epistolaAdminConnected' | pluginTranslate: 'epistola' | async)\n : ('epistolaAdminUnreachable' | pluginTranslate: 'epistola' | async)\n }}\n </cds-tag>\n </div>\n <div *ngIf=\"card.serverVersion\" class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminServerVersion' | pluginTranslate: 'epistola' | async\n }}</span>\n <span class=\"config-card__value\">{{ card.serverVersion }}</span>\n </div>\n <div class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminPluginActions' | pluginTranslate: 'epistola' | async\n }}</span>\n <span class=\"config-card__value\">{{ card.usageCount }}</span>\n </div>\n <div *ngIf=\"card.pendingJobs.length > 0\" class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminPendingJobs' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" type=\"blue\">{{ card.pendingJobs.length }}</cds-tag>\n </div>\n <div *ngIf=\"card.problemCount > 0\" class=\"config-card__field\">\n <span class=\"config-card__label\">{{\n 'epistolaAdminProblems' | pluginTranslate: 'epistola' | async\n }}</span>\n <cds-tag size=\"sm\" type=\"red\">{{ card.problemCount }}</cds-tag>\n </div>\n </div>\n\n <div class=\"config-card__footer\">\n <span class=\"config-card__latency\">{{ card.latencyMs }} ms</span>\n </div>\n </div>\n </div>\n </ng-container>\n\n <!-- Detail view: selected configuration -->\n <ng-container *ngIf=\"selectedCard\">\n <div class=\"detail-header mb-3\">\n <button class=\"btn btn-link btn-sm p-0\" (click)=\"backToOverview()\">\n ← {{ 'epistolaAdminBackToOverview' | pluginTranslate: 'epistola' | async }}\n </button>\n </div>\n\n <div class=\"detail-summary mb-4\">\n <h4>\n <span\n class=\"status-dot me-2\"\n [class.status-dot--ok]=\"selectedCard.reachable\"\n [class.status-dot--error]=\"!selectedCard.reachable\"\n >\n </span>\n {{ selectedCard.configurationTitle }}\n </h4>\n\n <table class=\"table table-sm detail-info-table\">\n <tbody>\n <tr>\n <th>{{ 'epistolaAdminTenantId' | pluginTranslate: 'epistola' | async }}</th>\n <td>\n <code>{{ selectedCard.tenantId }}</code>\n </td>\n </tr>\n <tr>\n <th>{{ 'epistolaAdminStatus' | pluginTranslate: 'epistola' | async }}</th>\n <td>\n <cds-tag size=\"sm\" [type]=\"selectedCard.reachable ? 'green' : 'red'\">\n {{\n selectedCard.reachable\n ? ('epistolaAdminConnected' | pluginTranslate: 'epistola' | async)\n : ('epistolaAdminUnreachable' | pluginTranslate: 'epistola' | async)\n }}\n </cds-tag>\n <span class=\"text-muted ms-2\">{{ selectedCard.latencyMs }} ms</span>\n </td>\n </tr>\n <tr *ngIf=\"selectedCard.serverVersion\">\n <th>{{ 'epistolaAdminServerVersion' | pluginTranslate: 'epistola' | async }}</th>\n <td>{{ selectedCard.serverVersion }}</td>\n </tr>\n <tr *ngIf=\"selectedCard.errorMessage\">\n <th>{{ 'epistolaAdminError' | pluginTranslate: 'epistola' | async }}</th>\n <td class=\"text-danger\">{{ selectedCard.errorMessage }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- Tabs -->\n <ng-template #actionsHeading>\n {{ 'epistolaAdminPluginActions' | pluginTranslate: 'epistola' | async }}\n <cds-tag size=\"sm\" type=\"gray\" class=\"ms-1\">{{ selectedCard.usageEntries.length }}</cds-tag>\n </ng-template>\n\n <ng-template #pendingHeading>\n {{ 'epistolaAdminPendingJobs' | pluginTranslate: 'epistola' | async }}\n <cds-tag\n size=\"sm\"\n [type]=\"selectedCard.pendingJobs.length > 0 ? 'blue' : 'gray'\"\n class=\"ms-1\"\n >\n {{ selectedCard.pendingJobs.length }}\n </cds-tag>\n </ng-template>\n\n <cds-tabs [cacheActive]=\"true\" type=\"contained\">\n <cds-tab\n [heading]=\"actionsHeading\"\n [active]=\"activeTab === 'actions'\"\n (selected)=\"setActiveTab('actions')\"\n >\n <div *ngIf=\"selectedCard.usageEntries.length === 0\" class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminNoUsageForConfig' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table *ngIf=\"selectedCard.usageEntries.length > 0\" class=\"table table-striped mt-3\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminCase' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminProcess' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminActivity' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminAction' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminProblems' | pluginTranslate: 'epistola' | async }}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr\n *ngFor=\"let entry of selectedCard.usageEntries\"\n [class.table-warning]=\"entry.problems.length > 0\"\n >\n <td>{{ entry.caseDefinitionKey || '-' }}</td>\n <td>\n <a\n *ngIf=\"entry.caseDefinitionKey && entry.caseDefinitionVersionTag\"\n [routerLink]=\"[\n '/case-management',\n 'case',\n entry.caseDefinitionKey,\n 'version',\n entry.caseDefinitionVersionTag,\n 'processes',\n entry.processDefinitionKey,\n ]\"\n class=\"usage-link\"\n >\n {{ entry.processDefinitionName }}\n </a>\n <span *ngIf=\"!entry.caseDefinitionKey || !entry.caseDefinitionVersionTag\">\n {{ entry.processDefinitionName }}\n </span>\n </td>\n <td>{{ entry.activityName }}</td>\n <td>\n <code>{{ entry.actionKey }}</code>\n </td>\n <td>\n <cds-tag *ngIf=\"entry.problems.length === 0\" size=\"sm\" type=\"green\">OK</cds-tag>\n <cds-tag\n *ngFor=\"let problem of entry.problems\"\n size=\"sm\"\n type=\"red\"\n class=\"d-block mb-1\"\n >\n {{ problem }}\n </cds-tag>\n </td>\n <td>\n <button\n class=\"btn btn-sm btn-outline-secondary\"\n (click)=\"exportProcessLink(entry)\"\n [title]=\"'epistolaAdminExport' | pluginTranslate: 'epistola' | async\"\n >\n ⤓\n </button>\n </td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n\n <cds-tab\n [heading]=\"pendingHeading\"\n [active]=\"activeTab === 'pending'\"\n (selected)=\"setActiveTab('pending')\"\n >\n <div *ngIf=\"selectedCard.pendingJobs.length === 0\" class=\"text-muted mt-3 mb-3\">\n {{ 'epistolaAdminNoPendingJobs' | pluginTranslate: 'epistola' | async }}\n </div>\n\n <table *ngIf=\"selectedCard.pendingJobs.length > 0\" class=\"table table-striped mt-3\">\n <thead>\n <tr>\n <th>{{ 'epistolaAdminProcess' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminActivity' | pluginTranslate: 'epistola' | async }}</th>\n <th>{{ 'epistolaAdminRequestId' | pluginTranslate: 'epistola' | async }}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let job of selectedCard.pendingJobs\">\n <td>{{ job.processDefinitionName }}</td>\n <td>{{ job.activityName }}</td>\n <td>\n <code>{{ job.requestId }}</code>\n </td>\n </tr>\n </tbody>\n </table>\n </cds-tab>\n </cds-tabs>\n </ng-container>\n</div>\n", styles: [".epistola-admin{padding:1.5rem}.epistola-admin .version-badge{font-size:.75rem;font-weight:500;padding:.2em .6em;border-radius:4px;background-color:#e8e8e8;color:#525252}.epistola-admin .badge{font-size:.85em;padding:.35em .65em}.epistola-admin code{font-size:.9em;color:#525252}.epistola-admin table th{font-weight:600;white-space:nowrap}.epistola-admin .status-dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:.5rem;flex-shrink:0;background-color:#adb5bd}.epistola-admin .status-dot--ok{background-color:#198754}.epistola-admin .status-dot--error{background-color:#dc3545}.epistola-admin .card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:1rem}.epistola-admin .config-card{border:1px solid #dee2e6;border-radius:8px;padding:1.25rem;cursor:pointer;transition:box-shadow .15s ease,border-color .15s ease;background:#fff}.epistola-admin .config-card:hover{box-shadow:0 2px 8px #0000001a}.epistola-admin .config-card--ok{border-left:4px solid #198754}.epistola-admin .config-card--warning{border-left:4px solid #ffc107}.epistola-admin .config-card--error{border-left:4px solid #dc3545}.epistola-admin .config-card__header{display:flex;align-items:center;margin-bottom:1rem}.epistola-admin .config-card__title{margin:0;font-size:1.05rem;font-weight:600;color:#161616}.epistola-admin .config-card__body{display:flex;flex-direction:column;gap:.5rem}.epistola-admin .config-card__field{display:flex;justify-content:space-between;align-items:center}.epistola-admin .config-card__label{font-size:.875rem;color:#6c757d}.epistola-admin .config-card__value{font-size:.875rem;color:#161616}.epistola-admin .config-card__footer{margin-top:1rem;padding-top:.75rem;border-top:1px solid #f0f0f0;text-align:right}.epistola-admin .config-card__latency{font-size:.8rem;color:#adb5bd}.epistola-admin .usage-link{color:#0f62fe;text-decoration:none}.epistola-admin .usage-link:hover{text-decoration:underline}.epistola-admin .detail-info-table{max-width:500px}.epistola-admin .detail-info-table th{width:140px}.epistola-admin .detail-summary{padding:1rem 0;border-bottom:1px solid #dee2e6}\n"] }]
|
|
3147
|
-
}], ctorParameters: () => [{ type: EpistolaAdminService }, { type: i2$
|
|
3449
|
+
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 <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 *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 </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>{{ '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 <code>{{ job.requestId }}</code>\n </td>\n <td class=\"text-end\">\n <button\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 <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"] }]
|
|
3450
|
+
}], ctorParameters: () => [{ type: EpistolaAdminService }, { type: i2$5.ActivatedRoute }, { type: i2$5.Router }] });
|
|
3148
3451
|
|
|
3149
3452
|
function isRuntimeWindow(value) {
|
|
3150
3453
|
return typeof value === 'object' && value !== null;
|
|
@@ -3191,7 +3494,7 @@ const routes = [
|
|
|
3191
3494
|
];
|
|
3192
3495
|
class EpistolaAdminRoutingModule {
|
|
3193
3496
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminRoutingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
3194
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminRoutingModule, imports: [i2$
|
|
3497
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminRoutingModule, imports: [i2$5.RouterModule], exports: [RouterModule] });
|
|
3195
3498
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminRoutingModule, imports: [RouterModule.forChild(routes), RouterModule] });
|
|
3196
3499
|
}
|
|
3197
3500
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaAdminRoutingModule, decorators: [{
|
|
@@ -3202,19 +3505,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3202
3505
|
}]
|
|
3203
3506
|
}] });
|
|
3204
3507
|
|
|
3205
|
-
const
|
|
3206
|
-
type: 'epistola-
|
|
3207
|
-
selector: 'epistola-
|
|
3208
|
-
title: 'Epistola
|
|
3508
|
+
const EPISTOLA_DOCUMENT_OPTIONS = {
|
|
3509
|
+
type: 'epistola-document',
|
|
3510
|
+
selector: 'epistola-document-element',
|
|
3511
|
+
title: 'Epistola Document',
|
|
3209
3512
|
group: 'basic',
|
|
3210
|
-
icon: '
|
|
3513
|
+
icon: 'file-pdf-o',
|
|
3211
3514
|
emptyValue: null,
|
|
3212
|
-
fieldOptions: ['
|
|
3515
|
+
fieldOptions: ['label', 'display', 'documentVariable', 'tenantIdVariable', 'filename'],
|
|
3213
3516
|
};
|
|
3214
|
-
function
|
|
3215
|
-
if (
|
|
3216
|
-
|
|
3517
|
+
function registerEpistolaDocumentComponent(injector) {
|
|
3518
|
+
if (customElements.get(EPISTOLA_DOCUMENT_OPTIONS.selector)) {
|
|
3519
|
+
return;
|
|
3217
3520
|
}
|
|
3521
|
+
registerCustomFormioComponent(EPISTOLA_DOCUMENT_OPTIONS, EpistolaDocumentComponent, injector);
|
|
3218
3522
|
}
|
|
3219
3523
|
|
|
3220
3524
|
const EPISTOLA_RETRY_FORM_OPTIONS = {
|
|
@@ -3232,21 +3536,6 @@ function registerEpistolaRetryFormComponent(injector) {
|
|
|
3232
3536
|
}
|
|
3233
3537
|
}
|
|
3234
3538
|
|
|
3235
|
-
const EPISTOLA_PREVIEW_BUTTON_OPTIONS = {
|
|
3236
|
-
type: 'epistola-preview-button',
|
|
3237
|
-
selector: 'epistola-preview-button-element',
|
|
3238
|
-
title: 'Epistola Preview',
|
|
3239
|
-
group: 'basic',
|
|
3240
|
-
icon: 'eye',
|
|
3241
|
-
emptyValue: null,
|
|
3242
|
-
fieldOptions: ['label'],
|
|
3243
|
-
};
|
|
3244
|
-
function registerEpistolaPreviewButtonComponent(injector) {
|
|
3245
|
-
if (!customElements.get(EPISTOLA_PREVIEW_BUTTON_OPTIONS.selector)) {
|
|
3246
|
-
registerCustomFormioComponent(EPISTOLA_PREVIEW_BUTTON_OPTIONS, EpistolaPreviewButtonComponent, injector);
|
|
3247
|
-
}
|
|
3248
|
-
}
|
|
3249
|
-
|
|
3250
3539
|
const EPISTOLA_DOCUMENT_PREVIEW_OPTIONS = {
|
|
3251
3540
|
type: 'epistola-document-preview',
|
|
3252
3541
|
selector: 'epistola-document-preview-element',
|
|
@@ -3798,6 +4087,11 @@ class EpistolaPluginModule {
|
|
|
3798
4087
|
ngModule: EpistolaPluginModule,
|
|
3799
4088
|
providers: [
|
|
3800
4089
|
EpistolaMenuService,
|
|
4090
|
+
{
|
|
4091
|
+
provide: HTTP_INTERCEPTORS,
|
|
4092
|
+
useClass: EpistolaTaskContextInterceptor,
|
|
4093
|
+
multi: true,
|
|
4094
|
+
},
|
|
3801
4095
|
{
|
|
3802
4096
|
provide: ENVIRONMENT_INITIALIZER,
|
|
3803
4097
|
multi: true,
|
|
@@ -3805,9 +4099,8 @@ class EpistolaPluginModule {
|
|
|
3805
4099
|
if (!isEpistolaEnabled())
|
|
3806
4100
|
return;
|
|
3807
4101
|
const injector = inject(Injector);
|
|
3808
|
-
|
|
4102
|
+
registerEpistolaDocumentComponent(injector);
|
|
3809
4103
|
registerEpistolaRetryFormComponent(injector);
|
|
3810
|
-
registerEpistolaPreviewButtonComponent(injector);
|
|
3811
4104
|
registerEpistolaOverrideBuilderComponent(injector);
|
|
3812
4105
|
registerEpistolaProcessLinkSelectorComponent(injector);
|
|
3813
4106
|
registerEpistolaDocumentPreviewComponent(injector);
|
|
@@ -3830,17 +4123,15 @@ class EpistolaPluginModule {
|
|
|
3830
4123
|
GenerateDocumentConfigurationComponent,
|
|
3831
4124
|
CheckJobStatusConfigurationComponent,
|
|
3832
4125
|
DownloadDocumentConfigurationComponent,
|
|
3833
|
-
|
|
4126
|
+
EpistolaDocumentComponent,
|
|
3834
4127
|
EpistolaRetryFormComponent,
|
|
3835
|
-
EpistolaPreviewButtonComponent,
|
|
3836
4128
|
EpistolaDocumentPreviewComponent,
|
|
3837
4129
|
EpistolaAdminPageComponent], exports: [EpistolaConfigurationComponent,
|
|
3838
4130
|
GenerateDocumentConfigurationComponent,
|
|
3839
4131
|
CheckJobStatusConfigurationComponent,
|
|
3840
4132
|
DownloadDocumentConfigurationComponent,
|
|
3841
|
-
|
|
4133
|
+
EpistolaDocumentComponent,
|
|
3842
4134
|
EpistolaRetryFormComponent,
|
|
3843
|
-
EpistolaPreviewButtonComponent,
|
|
3844
4135
|
EpistolaDocumentPreviewComponent,
|
|
3845
4136
|
EpistolaAdminPageComponent] });
|
|
3846
4137
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EpistolaPluginModule, providers: [EpistolaPluginService, EpistolaAdminService], imports: [CommonModule,
|
|
@@ -3854,9 +4145,8 @@ class EpistolaPluginModule {
|
|
|
3854
4145
|
GenerateDocumentConfigurationComponent,
|
|
3855
4146
|
CheckJobStatusConfigurationComponent,
|
|
3856
4147
|
DownloadDocumentConfigurationComponent,
|
|
3857
|
-
|
|
4148
|
+
EpistolaDocumentComponent,
|
|
3858
4149
|
EpistolaRetryFormComponent,
|
|
3859
|
-
EpistolaPreviewButtonComponent,
|
|
3860
4150
|
EpistolaDocumentPreviewComponent,
|
|
3861
4151
|
EpistolaAdminPageComponent] });
|
|
3862
4152
|
}
|
|
@@ -3875,9 +4165,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3875
4165
|
GenerateDocumentConfigurationComponent,
|
|
3876
4166
|
CheckJobStatusConfigurationComponent,
|
|
3877
4167
|
DownloadDocumentConfigurationComponent,
|
|
3878
|
-
|
|
4168
|
+
EpistolaDocumentComponent,
|
|
3879
4169
|
EpistolaRetryFormComponent,
|
|
3880
|
-
EpistolaPreviewButtonComponent,
|
|
3881
4170
|
EpistolaDocumentPreviewComponent,
|
|
3882
4171
|
EpistolaAdminPageComponent,
|
|
3883
4172
|
],
|
|
@@ -3886,9 +4175,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3886
4175
|
GenerateDocumentConfigurationComponent,
|
|
3887
4176
|
CheckJobStatusConfigurationComponent,
|
|
3888
4177
|
DownloadDocumentConfigurationComponent,
|
|
3889
|
-
|
|
4178
|
+
EpistolaDocumentComponent,
|
|
3890
4179
|
EpistolaRetryFormComponent,
|
|
3891
|
-
EpistolaPreviewButtonComponent,
|
|
3892
4180
|
EpistolaDocumentPreviewComponent,
|
|
3893
4181
|
EpistolaAdminPageComponent,
|
|
3894
4182
|
],
|
|
@@ -3910,9 +4198,9 @@ const epistolaPluginSpecification = {
|
|
|
3910
4198
|
pluginLogoBase64: EPISTOLA_PLUGIN_LOGO_BASE64,
|
|
3911
4199
|
// Map action keys to their configuration components
|
|
3912
4200
|
functionConfigurationComponents: {
|
|
3913
|
-
'generate-document': GenerateDocumentConfigurationComponent,
|
|
3914
|
-
'check-job-status': CheckJobStatusConfigurationComponent,
|
|
3915
|
-
'download-document': DownloadDocumentConfigurationComponent,
|
|
4201
|
+
'epistola-generate-document': GenerateDocumentConfigurationComponent,
|
|
4202
|
+
'epistola-check-job-status': CheckJobStatusConfigurationComponent,
|
|
4203
|
+
'epistola-download-document': DownloadDocumentConfigurationComponent,
|
|
3916
4204
|
},
|
|
3917
4205
|
// Translations
|
|
3918
4206
|
pluginTranslations: {
|
|
@@ -3930,7 +4218,7 @@ const epistolaPluginSpecification = {
|
|
|
3930
4218
|
defaultEnvironmentIdTooltip: 'De standaard omgeving voor documentgeneratie (3-30 tekens, alleen kleine letters, cijfers en koppeltekens, bijv. "productie")',
|
|
3931
4219
|
templateSyncEnabled: 'Template synchronisatie',
|
|
3932
4220
|
templateSyncEnabledTooltip: 'Synchroniseer template definities automatisch van het classpath naar Epistola bij het opstarten',
|
|
3933
|
-
'generate-document': 'Genereer Document',
|
|
4221
|
+
'epistola-generate-document': 'Genereer Document',
|
|
3934
4222
|
catalogId: 'Catalogus',
|
|
3935
4223
|
catalogIdTooltip: 'Selecteer de catalogus waaruit een template gekozen wordt',
|
|
3936
4224
|
templateId: 'Template',
|
|
@@ -4007,7 +4295,7 @@ const epistolaPluginSpecification = {
|
|
|
4007
4295
|
sourceFieldPlaceholder: 'Bronveldnaam',
|
|
4008
4296
|
noTemplateFields: 'Geen template velden beschikbaar',
|
|
4009
4297
|
// Check job status action
|
|
4010
|
-
'check-job-status': 'Controleer Taakstatus',
|
|
4298
|
+
'epistola-check-job-status': 'Controleer Taakstatus',
|
|
4011
4299
|
requestIdVariable: 'Request ID Variabele',
|
|
4012
4300
|
requestIdVariableTooltip: 'Naam van de procesvariabele met het Epistola request ID',
|
|
4013
4301
|
statusVariable: 'Status Variabele',
|
|
@@ -4017,7 +4305,9 @@ const epistolaPluginSpecification = {
|
|
|
4017
4305
|
errorMessageVariable: 'Foutmelding Variabele',
|
|
4018
4306
|
errorMessageVariableTooltip: 'Naam van de procesvariabele waarin de foutmelding wordt opgeslagen (bij fout)',
|
|
4019
4307
|
// Download document action
|
|
4020
|
-
'download-document': 'Download Document',
|
|
4308
|
+
'epistola-download-document': 'Download Document',
|
|
4309
|
+
documentVariable: 'Document Variabele',
|
|
4310
|
+
documentVariableTooltip: 'Naam van de procesvariabele met het Epistola resultaat. Mag een String document ID zijn (legacy) of een rich-result object met een documentId-veld; de actie haalt het document ID eruit.',
|
|
4021
4311
|
contentVariable: 'Inhoud Variabele',
|
|
4022
4312
|
contentVariableTooltip: 'Naam van de procesvariabele waarin de documentinhoud (Base64) wordt opgeslagen',
|
|
4023
4313
|
// Admin page
|
|
@@ -4044,6 +4334,31 @@ const epistolaPluginSpecification = {
|
|
|
4044
4334
|
epistolaAdminNoPendingJobs: 'Geen wachtende taken voor deze verbinding.',
|
|
4045
4335
|
epistolaAdminConfiguration: 'Configuratie',
|
|
4046
4336
|
epistolaAdminRequestId: 'Request ID',
|
|
4337
|
+
epistolaAdminReconcile: 'Hersynchroniseer',
|
|
4338
|
+
epistolaAdminReconciling: 'Bezig...',
|
|
4339
|
+
epistolaAdminReconcileTooltip: 'Vraag de huidige status op bij Epistola en hervat het wachtende proces als het klaar is.',
|
|
4340
|
+
epistolaAdminConfigurations: 'Configuraties',
|
|
4341
|
+
epistolaAdminValidations: 'BPMN-validatie',
|
|
4342
|
+
epistolaAdminNoValidations: 'Geen race-onveilige procesdefinities gevonden. Alles ziet er goed uit.',
|
|
4343
|
+
epistolaAdminValidationWarningTitle: 'BPMN configuratie waarschuwing',
|
|
4344
|
+
epistolaAdminValidationWarningBody: 'In deze procesdefinities is de grens tussen de generate-document service task en de EpistolaDocumentGenerated catch event niet synchroon. Resultaten kunnen verloren gaan; gebruik in dat geval de Hersynchroniseer-knop in de Wachtende taken-tab.',
|
|
4345
|
+
epistolaAdminValidationCode: 'Code',
|
|
4346
|
+
epistolaAdminValidationMessage: 'Bericht',
|
|
4347
|
+
epistolaAdminCatalogs: 'Catalogi',
|
|
4348
|
+
epistolaAdminCatalogsIntro: 'Catalogi op het classpath van de applicatie. Deze worden bij het opstarten automatisch uitgerold; gebruik Opnieuw uitrollen om een catalogus handmatig (geforceerd) naar deze Epistola-omgeving te sturen, bijvoorbeeld als de automatische uitrol is mislukt.',
|
|
4349
|
+
epistolaAdminNoCatalogs: 'Geen catalogi gevonden op het classpath.',
|
|
4350
|
+
epistolaAdminCatalogSlug: 'Slug',
|
|
4351
|
+
epistolaAdminCatalogVersion: 'Versie',
|
|
4352
|
+
epistolaAdminCatalogStatus: 'Status in Epistola',
|
|
4353
|
+
epistolaAdminCatalogInEpistola: 'Aanwezig in Epistola',
|
|
4354
|
+
epistolaAdminCatalogNotInEpistola: 'Niet in Epistola',
|
|
4355
|
+
epistolaAdminCatalogStatusUnknown: 'Onbekend (Epistola niet bereikbaar)',
|
|
4356
|
+
epistolaAdminRedeploy: 'Opnieuw uitrollen',
|
|
4357
|
+
epistolaAdminRedeploying: 'Bezig...',
|
|
4358
|
+
epistolaAdminRedeployTooltip: 'Bouw deze catalogus opnieuw vanaf het classpath en stuur hem geforceerd naar Epistola, ongeacht de versie of de templateSyncEnabled-instelling.',
|
|
4359
|
+
epistolaAdminChangelog: 'Changelog',
|
|
4360
|
+
epistolaAdminRunningVersion: 'Actieve plugin-versie:',
|
|
4361
|
+
epistolaAdminNoChangelog: 'Geen changelog beschikbaar in deze build.',
|
|
4047
4362
|
},
|
|
4048
4363
|
en: {
|
|
4049
4364
|
title: 'Epistola Document Suite',
|
|
@@ -4059,7 +4374,7 @@ const epistolaPluginSpecification = {
|
|
|
4059
4374
|
defaultEnvironmentIdTooltip: 'The default environment for document generation (3-30 chars, lowercase letters, digits and hyphens only, e.g. "production")',
|
|
4060
4375
|
templateSyncEnabled: 'Template sync',
|
|
4061
4376
|
templateSyncEnabledTooltip: 'Automatically synchronize template definitions from classpath to Epistola on startup',
|
|
4062
|
-
'generate-document': 'Generate Document',
|
|
4377
|
+
'epistola-generate-document': 'Generate Document',
|
|
4063
4378
|
catalogId: 'Catalog',
|
|
4064
4379
|
catalogIdTooltip: 'Select the catalog to choose a template from',
|
|
4065
4380
|
templateId: 'Template',
|
|
@@ -4136,7 +4451,7 @@ const epistolaPluginSpecification = {
|
|
|
4136
4451
|
sourceFieldPlaceholder: 'Source field name',
|
|
4137
4452
|
noTemplateFields: 'No template fields available',
|
|
4138
4453
|
// Check job status action
|
|
4139
|
-
'check-job-status': 'Check Job Status',
|
|
4454
|
+
'epistola-check-job-status': 'Check Job Status',
|
|
4140
4455
|
requestIdVariable: 'Request ID Variable',
|
|
4141
4456
|
requestIdVariableTooltip: 'Name of the process variable containing the Epistola request ID',
|
|
4142
4457
|
statusVariable: 'Status Variable',
|
|
@@ -4146,7 +4461,9 @@ const epistolaPluginSpecification = {
|
|
|
4146
4461
|
errorMessageVariable: 'Error Message Variable',
|
|
4147
4462
|
errorMessageVariableTooltip: 'Name of the process variable to store the error message in (when failed)',
|
|
4148
4463
|
// Download document action
|
|
4149
|
-
'download-document': 'Download Document',
|
|
4464
|
+
'epistola-download-document': 'Download Document',
|
|
4465
|
+
documentVariable: 'Document Variable',
|
|
4466
|
+
documentVariableTooltip: 'Name of the process variable holding the Epistola result. May be a String document id (legacy) or a rich result object with a `documentId` key — the action extracts the document id from it.',
|
|
4150
4467
|
contentVariable: 'Content Variable',
|
|
4151
4468
|
contentVariableTooltip: 'Name of the process variable to store the document content (Base64) in',
|
|
4152
4469
|
// Admin page
|
|
@@ -4173,6 +4490,31 @@ const epistolaPluginSpecification = {
|
|
|
4173
4490
|
epistolaAdminNoPendingJobs: 'No pending jobs for this connection.',
|
|
4174
4491
|
epistolaAdminConfiguration: 'Configuration',
|
|
4175
4492
|
epistolaAdminRequestId: 'Request ID',
|
|
4493
|
+
epistolaAdminReconcile: 'Reconcile',
|
|
4494
|
+
epistolaAdminReconciling: 'Reconciling...',
|
|
4495
|
+
epistolaAdminReconcileTooltip: "Ask Epistola for this job's current status and resume the waiting process if it has finished.",
|
|
4496
|
+
epistolaAdminConfigurations: 'Configurations',
|
|
4497
|
+
epistolaAdminValidations: 'BPMN validation',
|
|
4498
|
+
epistolaAdminNoValidations: 'No race-unsafe process definitions detected. Everything looks good.',
|
|
4499
|
+
epistolaAdminValidationWarningTitle: 'BPMN configuration warning',
|
|
4500
|
+
epistolaAdminValidationWarningBody: 'These process definitions have a non-synchronous boundary between the generate-document service task and the EpistolaDocumentGenerated catch event. Results can be missed; use the Reconcile button on the Pending Jobs tab to recover.',
|
|
4501
|
+
epistolaAdminValidationCode: 'Code',
|
|
4502
|
+
epistolaAdminValidationMessage: 'Message',
|
|
4503
|
+
epistolaAdminCatalogs: 'Catalogs',
|
|
4504
|
+
epistolaAdminCatalogsIntro: 'Catalogs on the application classpath. These are deployed automatically on startup; use Redeploy to manually (force) push one to this Epistola installation — for example when the automatic startup deploy failed.',
|
|
4505
|
+
epistolaAdminNoCatalogs: 'No catalogs found on the classpath.',
|
|
4506
|
+
epistolaAdminCatalogSlug: 'Slug',
|
|
4507
|
+
epistolaAdminCatalogVersion: 'Version',
|
|
4508
|
+
epistolaAdminCatalogStatus: 'Status in Epistola',
|
|
4509
|
+
epistolaAdminCatalogInEpistola: 'Present in Epistola',
|
|
4510
|
+
epistolaAdminCatalogNotInEpistola: 'Not in Epistola',
|
|
4511
|
+
epistolaAdminCatalogStatusUnknown: 'Unknown (Epistola unreachable)',
|
|
4512
|
+
epistolaAdminRedeploy: 'Redeploy',
|
|
4513
|
+
epistolaAdminRedeploying: 'Redeploying...',
|
|
4514
|
+
epistolaAdminRedeployTooltip: 'Rebuild this catalog from the classpath and force-push it to Epistola, regardless of version or the templateSyncEnabled setting.',
|
|
4515
|
+
epistolaAdminChangelog: 'Changelog',
|
|
4516
|
+
epistolaAdminRunningVersion: 'Running plugin version:',
|
|
4517
|
+
epistolaAdminNoChangelog: 'No changelog available in this build.',
|
|
4176
4518
|
},
|
|
4177
4519
|
},
|
|
4178
4520
|
};
|
|
@@ -4185,5 +4527,5 @@ const epistolaPluginSpecification = {
|
|
|
4185
4527
|
* Generated bundle index. Do not edit.
|
|
4186
4528
|
*/
|
|
4187
4529
|
|
|
4188
|
-
export { CheckJobStatusConfigurationComponent, DownloadDocumentConfigurationComponent,
|
|
4530
|
+
export { CheckJobStatusConfigurationComponent, DownloadDocumentConfigurationComponent, EPISTOLA_DOCUMENT_OPTIONS, EPISTOLA_DOCUMENT_PREVIEW_OPTIONS, EPISTOLA_OVERRIDE_BUILDER_OPTIONS, EPISTOLA_PROCESS_LINK_SELECTOR_OPTIONS, EPISTOLA_RETRY_FORM_OPTIONS, EpistolaAdminPageComponent, EpistolaAdminRoutingModule, EpistolaAdminService, EpistolaConfigurationComponent, EpistolaDocumentComponent, EpistolaDocumentPreviewComponent, EpistolaMenuService, EpistolaOverrideBuilderComponent, EpistolaPluginModule, EpistolaPluginService, EpistolaProcessLinkSelectorComponent, EpistolaRetryFormComponent, EpistolaTaskContextInterceptor, EpistolaTaskContextService, GenerateDocumentConfigurationComponent, JsonataEditorComponent, MappingBuilderComponent, epistolaPluginSpecification, errorResource, initialResource, isEpistolaEnabled, loadingResource, registerEpistolaDocumentComponent, registerEpistolaDocumentPreviewComponent, registerEpistolaOverrideBuilderComponent, registerEpistolaProcessLinkSelectorComponent, registerEpistolaRetryFormComponent, successResource };
|
|
4189
4531
|
//# sourceMappingURL=epistola.app-valtimo-plugin.mjs.map
|