@epistola.app/valtimo-plugin 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/package.json +9 -13
  2. package/ng-package.json +0 -17
  3. package/src/lib/assets/epistola-logo.ts +0 -4
  4. package/src/lib/assets/index.ts +0 -1
  5. package/src/lib/components/check-job-status-configuration/check-job-status-configuration.component.html +0 -51
  6. package/src/lib/components/check-job-status-configuration/check-job-status-configuration.component.scss +0 -1
  7. package/src/lib/components/check-job-status-configuration/check-job-status-configuration.component.ts +0 -71
  8. package/src/lib/components/data-mapping-tree/data-mapping-tree.component.html +0 -23
  9. package/src/lib/components/data-mapping-tree/data-mapping-tree.component.scss +0 -38
  10. package/src/lib/components/data-mapping-tree/data-mapping-tree.component.ts +0 -124
  11. package/src/lib/components/download-document-configuration/download-document-configuration.component.html +0 -29
  12. package/src/lib/components/download-document-configuration/download-document-configuration.component.scss +0 -1
  13. package/src/lib/components/download-document-configuration/download-document-configuration.component.ts +0 -71
  14. package/src/lib/components/epistola-configuration/epistola-configuration.component.html +0 -74
  15. package/src/lib/components/epistola-configuration/epistola-configuration.component.scss +0 -1
  16. package/src/lib/components/epistola-configuration/epistola-configuration.component.ts +0 -96
  17. package/src/lib/components/epistola-download/epistola-download.component.ts +0 -79
  18. package/src/lib/components/epistola-download/epistola-download.formio.ts +0 -19
  19. package/src/lib/components/field-tree/field-tree.component.html +0 -192
  20. package/src/lib/components/field-tree/field-tree.component.scss +0 -255
  21. package/src/lib/components/field-tree/field-tree.component.ts +0 -321
  22. package/src/lib/components/generate-document-configuration/generate-document-configuration.component.html +0 -182
  23. package/src/lib/components/generate-document-configuration/generate-document-configuration.component.scss +0 -150
  24. package/src/lib/components/generate-document-configuration/generate-document-configuration.component.ts +0 -422
  25. package/src/lib/epistola.module.ts +0 -50
  26. package/src/lib/epistola.specification.ts +0 -208
  27. package/src/lib/models/config.ts +0 -53
  28. package/src/lib/models/index.ts +0 -2
  29. package/src/lib/models/template.ts +0 -70
  30. package/src/lib/services/epistola-plugin.service.ts +0 -82
  31. package/src/lib/services/index.ts +0 -1
  32. package/src/public_api.ts +0 -16
  33. package/tsconfig.lib.json +0 -21
  34. /package/{dist/fesm2022 → fesm2022}/epistola.app-valtimo-plugin.mjs +0 -0
  35. /package/{dist/fesm2022 → fesm2022}/epistola.app-valtimo-plugin.mjs.map +0 -0
  36. /package/{dist/index.d.ts → index.d.ts} +0 -0
  37. /package/{dist/lib → lib}/assets/epistola-logo.d.ts +0 -0
  38. /package/{dist/lib → lib}/assets/index.d.ts +0 -0
  39. /package/{dist/lib → lib}/components/check-job-status-configuration/check-job-status-configuration.component.d.ts +0 -0
  40. /package/{dist/lib → lib}/components/data-mapping-tree/data-mapping-tree.component.d.ts +0 -0
  41. /package/{dist/lib → lib}/components/download-document-configuration/download-document-configuration.component.d.ts +0 -0
  42. /package/{dist/lib → lib}/components/epistola-configuration/epistola-configuration.component.d.ts +0 -0
  43. /package/{dist/lib → lib}/components/epistola-download/epistola-download.component.d.ts +0 -0
  44. /package/{dist/lib → lib}/components/epistola-download/epistola-download.formio.d.ts +0 -0
  45. /package/{dist/lib → lib}/components/field-tree/field-tree.component.d.ts +0 -0
  46. /package/{dist/lib → lib}/components/generate-document-configuration/generate-document-configuration.component.d.ts +0 -0
  47. /package/{dist/lib → lib}/epistola.module.d.ts +0 -0
  48. /package/{dist/lib → lib}/epistola.specification.d.ts +0 -0
  49. /package/{dist/lib → lib}/models/config.d.ts +0 -0
  50. /package/{dist/lib → lib}/models/index.d.ts +0 -0
  51. /package/{dist/lib → lib}/models/template.d.ts +0 -0
  52. /package/{dist/lib → lib}/services/epistola-plugin.service.d.ts +0 -0
  53. /package/{dist/lib → lib}/services/index.d.ts +0 -0
  54. /package/{dist/public_api.d.ts → public_api.d.ts} +0 -0
package/package.json CHANGED
@@ -1,25 +1,24 @@
1
1
  {
2
2
  "name": "@epistola.app/valtimo-plugin",
3
- "version": "0.0.1",
3
+ "version": "0.1.1",
4
4
  "description": "Epistola document generation plugin for Valtimo",
5
5
  "license": "EUPL-1.2",
6
- "module": "dist/fesm2022/epistola.app-valtimo-plugin.mjs",
7
- "typings": "dist/index.d.ts",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/epistola-app/valtimo-epistola-plugin"
9
+ },
10
+ "module": "fesm2022/epistola.app-valtimo-plugin.mjs",
11
+ "typings": "index.d.ts",
8
12
  "exports": {
9
13
  "./package.json": {
10
14
  "default": "./package.json"
11
15
  },
12
16
  ".": {
13
- "types": "./dist/index.d.ts",
14
- "default": "./dist/fesm2022/epistola.app-valtimo-plugin.mjs"
17
+ "types": "./index.d.ts",
18
+ "default": "./fesm2022/epistola.app-valtimo-plugin.mjs"
15
19
  }
16
20
  },
17
21
  "sideEffects": false,
18
- "scripts": {
19
- "build": "ng-packagr -p ng-package.json",
20
- "watch": "ng-packagr -p ng-package.json --watch",
21
- "test": "echo 'No tests configured yet'"
22
- },
23
22
  "peerDependencies": {
24
23
  "@angular/common": ">=17.0.0",
25
24
  "@angular/core": ">=17.0.0",
@@ -31,8 +30,5 @@
31
30
  },
32
31
  "dependencies": {
33
32
  "tslib": "^2.6.0"
34
- },
35
- "devDependencies": {
36
- "ng-packagr": "^19.0.0"
37
33
  }
38
34
  }
package/ng-package.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
- "dest": "dist",
4
- "lib": {
5
- "entryFile": "src/public_api.ts"
6
- },
7
- "allowedNonPeerDependencies": [
8
- "@angular/common",
9
- "@angular/core",
10
- "@angular/forms",
11
- "@valtimo/plugin",
12
- "@valtimo/components",
13
- "@ngx-translate/core",
14
- "rxjs",
15
- "tslib"
16
- ]
17
- }
@@ -1,4 +0,0 @@
1
- // Placeholder logo - a simple document icon in SVG format, base64 encoded
2
- // TODO: Replace with actual Epistola logo
3
- export const EPISTOLA_PLUGIN_LOGO_BASE64 =
4
- 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzMzNjZjYyI+PHBhdGggZD0iTTE0IDJINmMtMS4xIDAtMiAuOS0yIDJ2MTZjMCAxLjEuOSAyIDIgMmgxMmMxLjEgMCAyLS45IDItMlY4bC02LTZ6bTQgMThINlY0aDd2NWg1djExeiIvPjxwYXRoIGQ9Ik04IDEyaDh2Mkg4em0wIDRoOHYtMkg4em0wLThWNmg0djJ6Ii8+PC9zdmc+';
@@ -1 +0,0 @@
1
- export * from './epistola-logo';
@@ -1,51 +0,0 @@
1
- <v-form
2
- (valueChange)="formValueChange($event)"
3
- *ngIf="{
4
- disabled: safeDisabled$ | async,
5
- prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null
6
- } as obs"
7
- >
8
- <v-input
9
- name="requestIdVariable"
10
- [title]="'requestIdVariable' | pluginTranslate: pluginId | async"
11
- [tooltip]="'requestIdVariableTooltip' | pluginTranslate: pluginId | async"
12
- [margin]="true"
13
- [defaultValue]="obs.prefill?.requestIdVariable || 'epistolaRequestId'"
14
- [disabled]="obs.disabled"
15
- [required]="true"
16
- >
17
- </v-input>
18
-
19
- <v-input
20
- name="statusVariable"
21
- [title]="'statusVariable' | pluginTranslate: pluginId | async"
22
- [tooltip]="'statusVariableTooltip' | pluginTranslate: pluginId | async"
23
- [margin]="true"
24
- [defaultValue]="obs.prefill?.statusVariable || 'epistolaStatus'"
25
- [disabled]="obs.disabled"
26
- [required]="true"
27
- >
28
- </v-input>
29
-
30
- <v-input
31
- name="documentIdVariable"
32
- [title]="'documentIdVariable' | pluginTranslate: pluginId | async"
33
- [tooltip]="'documentIdVariableTooltip' | pluginTranslate: pluginId | async"
34
- [margin]="true"
35
- [defaultValue]="obs.prefill?.documentIdVariable || 'epistolaDocumentId'"
36
- [disabled]="obs.disabled"
37
- [required]="false"
38
- >
39
- </v-input>
40
-
41
- <v-input
42
- name="errorMessageVariable"
43
- [title]="'errorMessageVariable' | pluginTranslate: pluginId | async"
44
- [tooltip]="'errorMessageVariableTooltip' | pluginTranslate: pluginId | async"
45
- [margin]="true"
46
- [defaultValue]="obs.prefill?.errorMessageVariable || 'epistolaErrorMessage'"
47
- [disabled]="obs.disabled"
48
- [required]="false"
49
- >
50
- </v-input>
51
- </v-form>
@@ -1 +0,0 @@
1
- // Check job status configuration styles
@@ -1,71 +0,0 @@
1
- import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
2
- import {CommonModule} from '@angular/common';
3
- import {FunctionConfigurationComponent, PluginTranslatePipeModule} from '@valtimo/plugin';
4
- import {FormModule, FormOutput, InputModule} from '@valtimo/components';
5
- import {BehaviorSubject, combineLatest, Observable, Subscription, take} from 'rxjs';
6
- import {delay, startWith} from 'rxjs/operators';
7
- import {CheckJobStatusConfig} from '../../models';
8
-
9
- @Component({
10
- selector: 'epistola-check-job-status-configuration',
11
- templateUrl: './check-job-status-configuration.component.html',
12
- styleUrls: ['./check-job-status-configuration.component.scss'],
13
- standalone: true,
14
- imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule]
15
- })
16
- export class CheckJobStatusConfigurationComponent
17
- implements FunctionConfigurationComponent, OnInit, OnDestroy
18
- {
19
- @Input() save$!: Observable<void>;
20
- @Input() disabled$!: Observable<boolean>;
21
- @Input() pluginId!: string;
22
- @Input() prefillConfiguration$!: Observable<CheckJobStatusConfig>;
23
-
24
- @Output() valid: EventEmitter<boolean> = new EventEmitter<boolean>();
25
- @Output() configuration: EventEmitter<CheckJobStatusConfig> = new EventEmitter<CheckJobStatusConfig>();
26
-
27
- private saveSubscription!: Subscription;
28
- private readonly formValue$ = new BehaviorSubject<CheckJobStatusConfig | null>(null);
29
- private readonly valid$ = new BehaviorSubject<boolean>(false);
30
-
31
- safeDisabled$!: Observable<boolean>;
32
-
33
- ngOnInit(): void {
34
- this.safeDisabled$ = this.disabled$.pipe(
35
- startWith(true),
36
- delay(0)
37
- );
38
- this.openSaveSubscription();
39
- }
40
-
41
- ngOnDestroy() {
42
- this.saveSubscription?.unsubscribe();
43
- }
44
-
45
- formValueChange(formOutput: FormOutput): void {
46
- const formValue = formOutput as unknown as CheckJobStatusConfig;
47
- this.formValue$.next(formValue);
48
- this.handleValid(formValue);
49
- }
50
-
51
- private handleValid(formValue: CheckJobStatusConfig): void {
52
- const valid = !!(
53
- formValue?.requestIdVariable &&
54
- formValue?.statusVariable
55
- );
56
- this.valid$.next(valid);
57
- this.valid.emit(valid);
58
- }
59
-
60
- private openSaveSubscription(): void {
61
- this.saveSubscription = this.save$?.subscribe(() => {
62
- combineLatest([this.formValue$, this.valid$])
63
- .pipe(take(1))
64
- .subscribe(([formValue, valid]) => {
65
- if (valid && formValue) {
66
- this.configuration.emit(formValue);
67
- }
68
- });
69
- });
70
- }
71
- }
@@ -1,23 +0,0 @@
1
- <div class="data-mapping-tree">
2
- <div class="mapping-header">
3
- <h5>{{ 'dataMappingTitle' | pluginTranslate: pluginId | async }}</h5>
4
- <p class="helper-text">{{ 'dataMappingDescription' | pluginTranslate: pluginId | async }}</p>
5
- </div>
6
-
7
- <div class="field-tree-root" *ngIf="templateFields.length > 0">
8
- <epistola-field-tree
9
- *ngFor="let field of templateFields"
10
- [field]="field"
11
- [value]="getFieldValue(field.name)"
12
- [pluginId]="pluginId"
13
- [caseDefinitionKey]="caseDefinitionKey"
14
- [processVariables]="processVariables"
15
- [disabled]="disabled"
16
- (valueChange)="onFieldValueChange(field.name, $event)"
17
- ></epistola-field-tree>
18
- </div>
19
-
20
- <div class="no-fields" *ngIf="templateFields.length === 0">
21
- <p>{{ 'noTemplateFields' | pluginTranslate: pluginId | async }}</p>
22
- </div>
23
- </div>
@@ -1,38 +0,0 @@
1
- .data-mapping-tree {
2
- margin-top: 1rem;
3
- margin-bottom: 1rem;
4
- }
5
-
6
- .mapping-header {
7
- margin-bottom: 0.75rem;
8
-
9
- h5 {
10
- margin-bottom: 0.25rem;
11
- font-weight: 600;
12
- }
13
-
14
- .helper-text {
15
- color: #6c757d;
16
- font-size: 0.875rem;
17
- margin-bottom: 0;
18
- }
19
- }
20
-
21
- .field-tree-root {
22
- border: 1px solid #e0e0e0;
23
- border-radius: 4px;
24
- padding: 0.5rem 0.75rem;
25
- }
26
-
27
- .no-fields {
28
- padding: 1rem;
29
- text-align: center;
30
- color: #6c757d;
31
- background-color: #f8f9fa;
32
- border: 1px solid #dee2e6;
33
- border-radius: 4px;
34
-
35
- p {
36
- margin-bottom: 0;
37
- }
38
- }
@@ -1,124 +0,0 @@
1
- import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
2
- import {CommonModule} from '@angular/common';
3
- import {PluginTranslatePipeModule} from '@valtimo/plugin';
4
- import {Observable, Subject} from 'rxjs';
5
- import {takeUntil} from 'rxjs/operators';
6
- import {TemplateField} from '../../models';
7
- import {FieldTreeComponent} from '../field-tree/field-tree.component';
8
-
9
- /**
10
- * Top-level wrapper that hosts FieldTreeComponent instances for each top-level template field.
11
- * Manages the full nested mapping object, completeness tracking, and emits mapping changes.
12
- */
13
- @Component({
14
- selector: 'epistola-data-mapping-tree',
15
- templateUrl: './data-mapping-tree.component.html',
16
- styleUrls: ['./data-mapping-tree.component.scss'],
17
- standalone: true,
18
- imports: [
19
- CommonModule,
20
- PluginTranslatePipeModule,
21
- FieldTreeComponent
22
- ]
23
- })
24
- export class DataMappingTreeComponent implements OnInit, OnDestroy {
25
- @Input() pluginId!: string;
26
- @Input() templateFields$!: Observable<TemplateField[]>;
27
- @Input() prefillMapping$!: Observable<Record<string, any>>;
28
- @Input() disabled$!: Observable<boolean>;
29
- @Input() caseDefinitionKey: string | null = null;
30
- @Input() processVariables: string[] = [];
31
-
32
- @Output() mappingChange = new EventEmitter<Record<string, any>>();
33
- @Output() requiredFieldsStatus = new EventEmitter<{mapped: number; total: number}>();
34
-
35
- templateFields: TemplateField[] = [];
36
- mapping: Record<string, any> = {};
37
- disabled = false;
38
-
39
- private readonly destroy$ = new Subject<void>();
40
-
41
- ngOnInit(): void {
42
- this.templateFields$.pipe(
43
- takeUntil(this.destroy$)
44
- ).subscribe(fields => {
45
- this.templateFields = fields;
46
- this.emitRequiredFieldsStatus();
47
- });
48
-
49
- this.prefillMapping$.pipe(
50
- takeUntil(this.destroy$)
51
- ).subscribe(mapping => {
52
- if (mapping && Object.keys(mapping).length > 0) {
53
- this.mapping = {...mapping};
54
- }
55
- this.emitRequiredFieldsStatus();
56
- });
57
-
58
- this.disabled$.pipe(
59
- takeUntil(this.destroy$)
60
- ).subscribe(disabled => {
61
- this.disabled = disabled;
62
- });
63
- }
64
-
65
- ngOnDestroy(): void {
66
- this.destroy$.next();
67
- this.destroy$.complete();
68
- }
69
-
70
- onFieldValueChange(fieldName: string, value: any): void {
71
- if (value === undefined || value === null || value === '') {
72
- const {[fieldName]: _, ...rest} = this.mapping;
73
- this.mapping = rest;
74
- } else {
75
- this.mapping = {...this.mapping, [fieldName]: value};
76
- }
77
- this.mappingChange.emit(this.mapping);
78
- this.emitRequiredFieldsStatus();
79
- }
80
-
81
- getFieldValue(fieldName: string): any {
82
- return this.mapping[fieldName];
83
- }
84
-
85
- private emitRequiredFieldsStatus(): void {
86
- const stats = this.countRequiredMapped(this.templateFields, this.mapping);
87
- this.requiredFieldsStatus.emit(stats);
88
- }
89
-
90
- private countRequiredMapped(
91
- fields: TemplateField[],
92
- mapping: Record<string, any>
93
- ): {mapped: number; total: number} {
94
- let mapped = 0;
95
- let total = 0;
96
- for (const field of fields) {
97
- if (field.fieldType === 'SCALAR' && field.required) {
98
- total++;
99
- const val = mapping[field.name];
100
- if (typeof val === 'string' && val.trim().length > 0) {
101
- mapped++;
102
- }
103
- } else if (field.fieldType === 'ARRAY' && field.required) {
104
- total++;
105
- const val = mapping[field.name];
106
- if (typeof val === 'string' && val.trim().length > 0) {
107
- mapped++;
108
- } else if (typeof val === 'object' && val !== null && '_source' in val) {
109
- if (typeof val['_source'] === 'string' && val['_source'].trim().length > 0) {
110
- mapped++;
111
- }
112
- }
113
- } else if (field.fieldType === 'OBJECT' && field.children) {
114
- const nested = (typeof mapping[field.name] === 'object' && mapping[field.name] !== null)
115
- ? mapping[field.name]
116
- : {};
117
- const childStats = this.countRequiredMapped(field.children, nested);
118
- mapped += childStats.mapped;
119
- total += childStats.total;
120
- }
121
- }
122
- return {mapped, total};
123
- }
124
- }
@@ -1,29 +0,0 @@
1
- <v-form
2
- (valueChange)="formValueChange($event)"
3
- *ngIf="{
4
- disabled: safeDisabled$ | async,
5
- prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null
6
- } as obs"
7
- >
8
- <v-input
9
- name="documentIdVariable"
10
- [title]="'documentIdVariable' | pluginTranslate: pluginId | async"
11
- [tooltip]="'documentIdVariableTooltip' | pluginTranslate: pluginId | async"
12
- [margin]="true"
13
- [defaultValue]="obs.prefill?.documentIdVariable || 'epistolaDocumentId'"
14
- [disabled]="obs.disabled"
15
- [required]="true"
16
- >
17
- </v-input>
18
-
19
- <v-input
20
- name="contentVariable"
21
- [title]="'contentVariable' | pluginTranslate: pluginId | async"
22
- [tooltip]="'contentVariableTooltip' | pluginTranslate: pluginId | async"
23
- [margin]="true"
24
- [defaultValue]="obs.prefill?.contentVariable || 'documentContent'"
25
- [disabled]="obs.disabled"
26
- [required]="true"
27
- >
28
- </v-input>
29
- </v-form>
@@ -1 +0,0 @@
1
- // Download document configuration styles
@@ -1,71 +0,0 @@
1
- import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
2
- import {CommonModule} from '@angular/common';
3
- import {FunctionConfigurationComponent, PluginTranslatePipeModule} from '@valtimo/plugin';
4
- import {FormModule, FormOutput, InputModule} from '@valtimo/components';
5
- import {BehaviorSubject, combineLatest, Observable, Subscription, take} from 'rxjs';
6
- import {delay, startWith} from 'rxjs/operators';
7
- import {DownloadDocumentConfig} from '../../models';
8
-
9
- @Component({
10
- selector: 'epistola-download-document-configuration',
11
- templateUrl: './download-document-configuration.component.html',
12
- styleUrls: ['./download-document-configuration.component.scss'],
13
- standalone: true,
14
- imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule]
15
- })
16
- export class DownloadDocumentConfigurationComponent
17
- implements FunctionConfigurationComponent, OnInit, OnDestroy
18
- {
19
- @Input() save$!: Observable<void>;
20
- @Input() disabled$!: Observable<boolean>;
21
- @Input() pluginId!: string;
22
- @Input() prefillConfiguration$!: Observable<DownloadDocumentConfig>;
23
-
24
- @Output() valid: EventEmitter<boolean> = new EventEmitter<boolean>();
25
- @Output() configuration: EventEmitter<DownloadDocumentConfig> = new EventEmitter<DownloadDocumentConfig>();
26
-
27
- private saveSubscription!: Subscription;
28
- private readonly formValue$ = new BehaviorSubject<DownloadDocumentConfig | null>(null);
29
- private readonly valid$ = new BehaviorSubject<boolean>(false);
30
-
31
- safeDisabled$!: Observable<boolean>;
32
-
33
- ngOnInit(): void {
34
- this.safeDisabled$ = this.disabled$.pipe(
35
- startWith(true),
36
- delay(0)
37
- );
38
- this.openSaveSubscription();
39
- }
40
-
41
- ngOnDestroy() {
42
- this.saveSubscription?.unsubscribe();
43
- }
44
-
45
- formValueChange(formOutput: FormOutput): void {
46
- const formValue = formOutput as unknown as DownloadDocumentConfig;
47
- this.formValue$.next(formValue);
48
- this.handleValid(formValue);
49
- }
50
-
51
- private handleValid(formValue: DownloadDocumentConfig): void {
52
- const valid = !!(
53
- formValue?.documentIdVariable &&
54
- formValue?.contentVariable
55
- );
56
- this.valid$.next(valid);
57
- this.valid.emit(valid);
58
- }
59
-
60
- private openSaveSubscription(): void {
61
- this.saveSubscription = this.save$?.subscribe(() => {
62
- combineLatest([this.formValue$, this.valid$])
63
- .pipe(take(1))
64
- .subscribe(([formValue, valid]) => {
65
- if (valid && formValue) {
66
- this.configuration.emit(formValue);
67
- }
68
- });
69
- });
70
- }
71
- }
@@ -1,74 +0,0 @@
1
- <v-form
2
- (valueChange)="formValueChange($event)"
3
- *ngIf="{
4
- disabled: safeDisabled$ | async,
5
- prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null
6
- } as obs"
7
- >
8
- <v-input
9
- name="configurationTitle"
10
- [title]="'configurationTitle' | pluginTranslate: pluginId | async"
11
- [margin]="true"
12
- [defaultValue]="obs.prefill?.configurationTitle"
13
- [disabled]="obs.disabled"
14
- [required]="true"
15
- >
16
- </v-input>
17
-
18
- <v-input
19
- name="baseUrl"
20
- [title]="'baseUrl' | pluginTranslate: pluginId | async"
21
- [tooltip]="'baseUrlTooltip' | pluginTranslate: pluginId | async"
22
- [margin]="true"
23
- [defaultValue]="obs.prefill?.baseUrl"
24
- [disabled]="obs.disabled"
25
- [required]="true"
26
- >
27
- </v-input>
28
-
29
- <v-input
30
- name="apiKey"
31
- [title]="'apiKey' | pluginTranslate: pluginId | async"
32
- [tooltip]="'apiKeyTooltip' | pluginTranslate: pluginId | async"
33
- [margin]="true"
34
- [defaultValue]="obs.prefill?.apiKey"
35
- [disabled]="obs.disabled"
36
- [required]="true"
37
- type="password"
38
- >
39
- </v-input>
40
-
41
- <v-input
42
- name="tenantId"
43
- [title]="'tenantId' | pluginTranslate: pluginId | async"
44
- [tooltip]="'tenantIdTooltip' | pluginTranslate: pluginId | async"
45
- [margin]="true"
46
- [defaultValue]="obs.prefill?.tenantId"
47
- [disabled]="obs.disabled"
48
- [required]="true"
49
- >
50
- </v-input>
51
-
52
- <v-input
53
- name="defaultEnvironmentId"
54
- [title]="'defaultEnvironmentId' | pluginTranslate: pluginId | async"
55
- [tooltip]="'defaultEnvironmentIdTooltip' | pluginTranslate: pluginId | async"
56
- [margin]="true"
57
- [defaultValue]="obs.prefill?.defaultEnvironmentId"
58
- [disabled]="obs.disabled"
59
- [required]="false"
60
- >
61
- </v-input>
62
-
63
- <v-input
64
- name="templateSyncEnabled"
65
- type="checkbox"
66
- [title]="'templateSyncEnabled' | pluginTranslate: pluginId | async"
67
- [tooltip]="'templateSyncEnabledTooltip' | pluginTranslate: pluginId | async"
68
- [margin]="true"
69
- [defaultValue]="obs.prefill?.templateSyncEnabled ? 'true' : ''"
70
- [disabled]="obs.disabled"
71
- [required]="false"
72
- >
73
- </v-input>
74
- </v-form>
@@ -1 +0,0 @@
1
- // Epistola plugin configuration styles
@@ -1,96 +0,0 @@
1
- import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
2
- import {CommonModule} from '@angular/common';
3
- import {PluginConfigurationComponent, PluginTranslatePipeModule} from '@valtimo/plugin';
4
- import {FormModule, FormOutput, InputModule} from '@valtimo/components';
5
- import {BehaviorSubject, combineLatest, Observable, Subscription, take} from 'rxjs';
6
- import {delay, startWith} from 'rxjs/operators';
7
- import {EpistolaPluginConfig} from '../../models';
8
-
9
- @Component({
10
- selector: 'epistola-configuration',
11
- templateUrl: './epistola-configuration.component.html',
12
- styleUrls: ['./epistola-configuration.component.scss'],
13
- standalone: true,
14
- imports: [CommonModule, PluginTranslatePipeModule, FormModule, InputModule]
15
- })
16
- export class EpistolaConfigurationComponent
17
- implements PluginConfigurationComponent, OnInit, OnDestroy
18
- {
19
- @Input() save$!: Observable<void>;
20
- @Input() disabled$!: Observable<boolean>;
21
- @Input() pluginId!: string;
22
- @Input() prefillConfiguration$!: Observable<EpistolaPluginConfig>;
23
-
24
- @Output() valid: EventEmitter<boolean> = new EventEmitter<boolean>();
25
- @Output() configuration: EventEmitter<EpistolaPluginConfig> = new EventEmitter<EpistolaPluginConfig>();
26
-
27
- /** Epistola slug pattern: lowercase alphanumeric with hyphens, no leading/trailing hyphens. */
28
- private static readonly SLUG_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
29
-
30
- private saveSubscription!: Subscription;
31
- private readonly formValue$ = new BehaviorSubject<EpistolaPluginConfig | null>(null);
32
- private readonly valid$ = new BehaviorSubject<boolean>(false);
33
-
34
- safeDisabled$!: Observable<boolean>;
35
-
36
- ngOnInit(): void {
37
- // Wrap disabled$ with startWith and delay to prevent NG0100 ExpressionChangedAfterItHasBeenCheckedError
38
- // The disabled$ observable from Valtimo's plugin framework can emit value changes after Angular's
39
- // change detection cycle completes, causing the error.
40
- this.safeDisabled$ = this.disabled$.pipe(
41
- startWith(true),
42
- delay(0)
43
- );
44
- this.openSaveSubscription();
45
- }
46
-
47
- ngOnDestroy() {
48
- this.saveSubscription?.unsubscribe();
49
- }
50
-
51
- formValueChange(formOutput: FormOutput): void {
52
- const formValue = formOutput as unknown as EpistolaPluginConfig;
53
- this.formValue$.next(formValue);
54
- this.handleValid(formValue);
55
- }
56
-
57
- private handleValid(formValue: EpistolaPluginConfig): void {
58
- const valid = !!(
59
- formValue?.configurationTitle &&
60
- formValue?.baseUrl &&
61
- formValue?.apiKey &&
62
- formValue?.tenantId &&
63
- this.isValidSlug(formValue.tenantId, 3, 63) &&
64
- this.isValidOptionalSlug(formValue.defaultEnvironmentId, 3, 30)
65
- );
66
- this.valid$.next(valid);
67
- this.valid.emit(valid);
68
- }
69
-
70
- private isValidSlug(value: string, minLength: number, maxLength: number): boolean {
71
- return (
72
- value.length >= minLength &&
73
- value.length <= maxLength &&
74
- EpistolaConfigurationComponent.SLUG_PATTERN.test(value)
75
- );
76
- }
77
-
78
- private isValidOptionalSlug(value: string | undefined, minLength: number, maxLength: number): boolean {
79
- if (!value) {
80
- return true;
81
- }
82
- return this.isValidSlug(value, minLength, maxLength);
83
- }
84
-
85
- private openSaveSubscription(): void {
86
- this.saveSubscription = this.save$?.subscribe(() => {
87
- combineLatest([this.formValue$, this.valid$])
88
- .pipe(take(1))
89
- .subscribe(([formValue, valid]) => {
90
- if (valid) {
91
- this.configuration.emit(formValue);
92
- }
93
- });
94
- });
95
- }
96
- }