@pega/dx-component-builder-sdk 0.23.12 → 0.23.14

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 (50) hide show
  1. package/package.json +1 -1
  2. package/src/constants.js +12 -0
  3. package/src/tasks/create/templates/SDK-React/field/Boolean/index.tsx.mustache +23 -21
  4. package/src/tasks/create/templates/SDK-React/field/Date/index.tsx.mustache +24 -26
  5. package/src/tasks/create/templates/SDK-React/field/DateTime/index.tsx.mustache +20 -22
  6. package/src/tasks/create/templates/SDK-React/field/Decimal/index.tsx.mustache +3 -4
  7. package/src/tasks/create/templates/SDK-React/field/Decimal-Currency/index.tsx.mustache +3 -4
  8. package/src/tasks/create/templates/SDK-React/field/Decimal-Percentage/index.tsx.mustache +3 -4
  9. package/src/tasks/create/templates/SDK-React/field/Integer/index.tsx.mustache +3 -4
  10. package/src/tasks/create/templates/SDK-React/field/Picklist/index.tsx.mustache +3 -4
  11. package/src/tasks/create/templates/SDK-React/field/Picklist-Autocomplete/index.tsx.mustache +31 -32
  12. package/src/tasks/create/templates/SDK-React/field/Picklist-RadioButtons/index.tsx.mustache +22 -23
  13. package/src/tasks/create/templates/SDK-React/field/Text/index.tsx.mustache +18 -19
  14. package/src/tasks/create/templates/SDK-React/field/Text-Email/index.tsx.mustache +26 -27
  15. package/src/tasks/create/templates/SDK-React/field/Text-Input/index.tsx.mustache +18 -19
  16. package/src/tasks/create/templates/SDK-React/field/Text-Paragraph/index.tsx.mustache +21 -22
  17. package/src/tasks/create/templates/SDK-React/field/Text-Phone/index.tsx.mustache +39 -40
  18. package/src/tasks/create/templates/SDK-React/field/Text-URL/index.tsx.mustache +19 -20
  19. package/src/tasks/create/templates/SDK-React/field/TimeOfDay/index.tsx.mustache +4 -5
  20. package/src/tasks/create/templates/SDK-React/template/DETAILS/index.tsx.mustache +19 -20
  21. package/src/tasks/create/templates/SDK-React/template/DETAILS-region/index.tsx.mustache +19 -20
  22. package/src/tasks/create/templates/SDK-React/template/FORM/index.tsx.mustache +6 -7
  23. package/src/tasks/create/templates/SDK-React/template/FORM-region/index.tsx.mustache +8 -9
  24. package/src/tasks/create/templates/SDK-React/template/PAGE/index.tsx.mustache +4 -5
  25. package/src/tasks/create/templates/SDK-React/widget/CASE/index.tsx.mustache +10 -11
  26. package/src/tasks/create/templates/SDK-React/widget/PAGE/index.tsx.mustache +23 -24
  27. package/src/tasks/create/templates/SDK-React/widget/PAGE-CASE/index.tsx.mustache +37 -37
  28. package/src/tasks/validator/index.js +52 -47
  29. package/tests/assets/sdk-a/components/create/override-sdk/designSystemExtension/material-summary-item/material-summary-item.component.html +2 -2
  30. package/tests/assets/sdk-a/components/create/override-sdk/designSystemExtension/rich-text-editor/rich-text-editor.component.html +33 -0
  31. package/tests/assets/sdk-a/components/create/override-sdk/designSystemExtension/rich-text-editor/rich-text-editor.component.scss +19 -0
  32. package/tests/assets/sdk-a/components/create/override-sdk/designSystemExtension/rich-text-editor/rich-text-editor.component.spec.ts +24 -0
  33. package/tests/assets/sdk-a/components/create/override-sdk/designSystemExtension/rich-text-editor/rich-text-editor.component.ts +89 -0
  34. package/tests/assets/sdk-a/components/create/override-sdk/field/dropdown/dropdown.component.ts +1 -1
  35. package/tests/assets/sdk-a/components/create/override-sdk/field/rich-text/rich-text.component.html +20 -0
  36. package/tests/assets/sdk-a/components/create/override-sdk/field/rich-text/rich-text.component.scss +0 -0
  37. package/tests/assets/sdk-a/components/create/override-sdk/field/rich-text/rich-text.component.spec.ts +24 -0
  38. package/tests/assets/sdk-a/components/create/override-sdk/field/rich-text/rich-text.component.ts +131 -0
  39. package/tests/assets/sdk-a/components/create/override-sdk/widget/file-utility/file-utility.component.ts +31 -18
  40. package/tests/assets/sdk-a/components/createAll/override-sdk/designSystemExtension/material-summary-item/material-summary-item.component.html +2 -2
  41. package/tests/assets/sdk-a/components/createAll/override-sdk/designSystemExtension/rich-text-editor/rich-text-editor.component.html +33 -0
  42. package/tests/assets/sdk-a/components/createAll/override-sdk/designSystemExtension/rich-text-editor/rich-text-editor.component.scss +19 -0
  43. package/tests/assets/sdk-a/components/createAll/override-sdk/designSystemExtension/rich-text-editor/rich-text-editor.component.spec.ts +24 -0
  44. package/tests/assets/sdk-a/components/createAll/override-sdk/designSystemExtension/rich-text-editor/rich-text-editor.component.ts +89 -0
  45. package/tests/assets/sdk-a/components/createAll/override-sdk/field/dropdown/dropdown.component.ts +1 -1
  46. package/tests/assets/sdk-a/components/createAll/override-sdk/field/rich-text/rich-text.component.html +20 -0
  47. package/tests/assets/sdk-a/components/createAll/override-sdk/field/rich-text/rich-text.component.scss +0 -0
  48. package/tests/assets/sdk-a/components/createAll/override-sdk/field/rich-text/rich-text.component.spec.ts +24 -0
  49. package/tests/assets/sdk-a/components/createAll/override-sdk/field/rich-text/rich-text.component.ts +131 -0
  50. package/tests/assets/sdk-a/components/createAll/override-sdk/widget/file-utility/file-utility.component.ts +31 -18
@@ -0,0 +1,131 @@
1
+ import { Component, OnInit, Input, forwardRef, OnDestroy } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormGroup, ReactiveFormsModule } from '@angular/forms';
4
+ import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
5
+ import { Utils } from '@pega/angular-sdk-components';
6
+ import { ComponentMapperComponent } from '@pega/angular-sdk-components';
7
+ import { handleEvent } from '@pega/angular-sdk-components';
8
+ import { PConnFieldProps } from '@pega/angular-sdk-components';
9
+
10
+ interface RichTextProps extends PConnFieldProps {
11
+ // If any, enter additional props that only exist on RichText here
12
+ }
13
+
14
+ @Component({
15
+ selector: 'app-rich-text',
16
+ templateUrl: './rich-text.component.html',
17
+ styleUrls: ['./rich-text.component.scss'],
18
+ standalone: true,
19
+ imports: [CommonModule, ReactiveFormsModule, forwardRef(() => ComponentMapperComponent)]
20
+ })
21
+ export class RichTextComponent implements OnInit, OnDestroy {
22
+ @Input() pConn$: typeof PConnect;
23
+ @Input() formGroup$: FormGroup;
24
+
25
+ // Used with AngularPConnect
26
+ angularPConnectData: AngularPConnectData = {};
27
+ configProps$: RichTextProps;
28
+
29
+ label$ = '';
30
+ value$ = '';
31
+ bRequired$ = false;
32
+ bReadonly$ = false;
33
+ bDisabled$ = false;
34
+ bVisible$ = true;
35
+ displayMode$?: string = '';
36
+ controlName$: string;
37
+ testId: string;
38
+ helperText: string;
39
+ placeholder: any;
40
+ info: any;
41
+ error: boolean;
42
+ status: any;
43
+
44
+ constructor(
45
+ private angularPConnect: AngularPConnectService,
46
+ private utils: Utils
47
+ ) {}
48
+
49
+ ngOnInit(): void {
50
+ // First thing in initialization is registering and subscribing to the AngularPConnect service
51
+ this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange);
52
+
53
+ // call updateSelf when initializing
54
+ this.checkAndUpdate();
55
+ }
56
+
57
+ ngOnDestroy(): void {
58
+ if (this.angularPConnectData.unsubscribeFn) {
59
+ this.angularPConnectData.unsubscribeFn();
60
+ }
61
+ }
62
+
63
+ // Callback passed when subscribing to store change
64
+ onStateChange() {
65
+ this.checkAndUpdate();
66
+ }
67
+
68
+ checkAndUpdate() {
69
+ // Should always check the bridge to see if the component should
70
+ // update itself (re-render)
71
+ const bUpdateSelf = this.angularPConnect.shouldComponentUpdate(this);
72
+
73
+ // ONLY call updateSelf when the component should update
74
+ if (bUpdateSelf) {
75
+ this.updateSelf();
76
+ }
77
+ }
78
+
79
+ // updateSelf
80
+ updateSelf(): void {
81
+ // moved this from ngOnInit() and call this from there instead...
82
+ this.configProps$ = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as RichTextProps;
83
+ const stateProps: any = this.pConn$.getStateProps();
84
+ this.status = stateProps?.status;
85
+
86
+ if (this.configProps$.value != undefined) {
87
+ this.value$ = this.configProps$.value;
88
+ }
89
+
90
+ this.testId = this.configProps$.testId;
91
+ this.displayMode$ = this.configProps$.displayMode;
92
+ this.label$ = this.configProps$.label;
93
+ this.placeholder = this.configProps$.placeholder;
94
+ this.info = stateProps?.validatemessage || this.configProps$.helperText;
95
+ this.error = stateProps?.status === 'error';
96
+
97
+ if (this.configProps$.required != null) {
98
+ this.bRequired$ = this.utils.getBooleanValue(this.configProps$.required);
99
+ }
100
+
101
+ if (this.configProps$.visibility != null) {
102
+ this.bVisible$ = this.utils.getBooleanValue(this.configProps$.visibility);
103
+ }
104
+
105
+ if (this.configProps$.disabled != undefined) {
106
+ this.bDisabled$ = this.utils.getBooleanValue(this.configProps$.disabled);
107
+ }
108
+
109
+ if (this.configProps$.readOnly != null) {
110
+ this.bReadonly$ = this.utils.getBooleanValue(this.configProps$.readOnly);
111
+ }
112
+ }
113
+
114
+ fieldOnChange() {
115
+ if (this.status === 'error') {
116
+ const property = (this.pConn$.getStateProps() as any).value;
117
+ this.pConn$.clearErrorMessages({
118
+ property,
119
+ category: '',
120
+ context: ''
121
+ });
122
+ }
123
+ }
124
+
125
+ fieldOnBlur(editorValue: any) {
126
+ // PConnect wants to use eventHandler for onBlur
127
+ const actionsApi = this.pConn$?.getActionsApi();
128
+ const propName = (this.pConn$?.getStateProps() as any).value;
129
+ handleEvent(actionsApi, 'changeNblur', propName, editorValue);
130
+ }
131
+ }
@@ -149,24 +149,30 @@ export class FileUtilityComponent implements OnInit, OnDestroy {
149
149
  this.lu_bLoading$ = true;
150
150
  }
151
151
 
152
- // const uploadedFiles = [];
153
-
154
- for (const file of files) {
155
- attachmentUtils
156
- .uploadAttachment(file, this.onUploadProgress, this.errorHandler, this.pConn$.getContextName())
157
- .then((fileResponse: any) => {
158
- if (fileResponse.type === 'File') {
159
- (attachmentUtils.linkAttachmentsToCase(caseID, [fileResponse], 'File', this.pConn$.getContextName()) as Promise<any>)
160
- .then(() => {
161
- this.refreshAttachments();
162
- })
163
- .catch(console.error);
152
+ const arFiles: any = Array.from(files);
153
+
154
+ Promise.allSettled(
155
+ arFiles.map(file => attachmentUtils.uploadAttachment(file, this.onUploadProgress, this.errorHandler, this.pConn$.getContextName()))
156
+ )
157
+ .then((fileResponses: any) => {
158
+ const uploadedFiles: any = [];
159
+ fileResponses.forEach(fileResponse => {
160
+ if (fileResponse.status === 'fulfilled') {
161
+ uploadedFiles.push(fileResponse.value);
164
162
  }
165
- })
166
- .catch(console.error);
167
- }
163
+ });
164
+ if (uploadedFiles.length > 0) {
165
+ (attachmentUtils.linkAttachmentsToCase(caseID, uploadedFiles, 'File', this.pConn$.getContextName()) as Promise<any>)
166
+ .then(() => {
167
+ this.refreshAttachments();
168
+ })
169
+ .catch();
170
+ }
171
+ })
172
+ .catch();
168
173
 
169
174
  this.arFileList$ = [];
175
+ this.lu_bLoading$ = false;
170
176
  }
171
177
 
172
178
  refreshAttachments() {
@@ -691,9 +697,16 @@ export class FileUtilityComponent implements OnInit, OnDestroy {
691
697
  if (caseID) {
692
698
  this.lu_bLoading$ = true;
693
699
 
694
- this.fetchCaseAttachments(caseID).then(resp => {
695
- this.handleAttachmentsResponse(resp);
696
- });
700
+ this.fetchCaseAttachments(caseID)
701
+ .then(resp => {
702
+ this.handleAttachmentsResponse(resp);
703
+ })
704
+ .catch(err => {
705
+ console.log(err);
706
+ })
707
+ .finally(() => {
708
+ this.lu_bLoading$ = false;
709
+ });
697
710
  }
698
711
  }
699
712
 
@@ -16,7 +16,7 @@
16
16
  </div>
17
17
  <div class="psdk-utility-card-actions">
18
18
  <div *ngIf="menuIconOverride$ == ''; else showCustom">
19
- <button mat-icon-button [matMenuTriggerFor]="actionMenu">
19
+ <button id="setting-button" mat-icon-button [matMenuTriggerFor]="actionMenu">
20
20
  <img class="psdk-utility-card-action-svg-icon" src="{{ settingsSvgIcon$ }}" />
21
21
  </button>
22
22
  <mat-menu #actionMenu="matMenu" overlapTrigger="false">
@@ -29,7 +29,7 @@
29
29
  </mat-menu>
30
30
  </div>
31
31
  <ng-template #showCustom>
32
- <button mat-icon-button (click)="menuIconOverrideAction$.onClick(item$)">
32
+ <button id="delete-attachment" mat-icon-button (click)="menuIconOverrideAction$.onClick(item$)">
33
33
  <img class="psdk-utility-card-action-svg-icon" src="{{ menuIconOverride$ }}" />
34
34
  </button>
35
35
  </ng-template>
@@ -0,0 +1,33 @@
1
+ <div [attr.data-test-id]="testId">
2
+ <label [ngClass]="{ 'label-required': required === true }" class="rich-text-label">{{ label }}</label>
3
+ <div class="rich-text-editor" *ngIf="!readonly">
4
+ <editor
5
+ [formControl]="richText"
6
+ [attr.disabled]="disabled"
7
+ [initialValue]="value"
8
+ [init]="{
9
+ base_url: '/tinymce',
10
+ suffix: '.min',
11
+ menubar: false,
12
+ placeholder,
13
+ statusbar: false,
14
+ min_height: 130,
15
+ plugins: ['lists', 'advlist', 'autolink', 'image', 'link', 'autoresize'],
16
+ autoresize_bottom_margin: 0,
17
+ toolbar: disabled ? false : 'blocks | bold italic strikethrough | bullist numlist outdent indent | link image',
18
+ toolbar_location: 'bottom',
19
+ content_style: 'body { font-family:Helvetica, Arial,sans-serif; font-size:14px }',
20
+ branding: false,
21
+ paste_data_images: true,
22
+ file_picker_types: 'image',
23
+ file_picker_callback: filePickerCallback
24
+ }"
25
+ (onBlur)="blur()"
26
+ (onChange)="change($event)"
27
+ ></editor>
28
+ <p *ngIf="richText.invalid" [ngClass]="'text-editor-error'">{{ info }}</p>
29
+ </div>
30
+ <div *ngIf="readonly">
31
+ <div class="readonly-richtext-editor" style="margin: 10px 5px" [innerHTML]="value || '--'"></div>
32
+ </div>
33
+ </div>
@@ -0,0 +1,19 @@
1
+ .rich-text-label {
2
+ margin: 5px;
3
+ font-size: 16px;
4
+ }
5
+
6
+ .rich-text-editor {
7
+ margin: 10px 0px;
8
+ }
9
+
10
+ .label-required::after {
11
+ display: inline;
12
+ content: ' *';
13
+ vertical-align: top;
14
+ color: var(--app-neutral-dark-color);
15
+ }
16
+
17
+ .text-editor-error {
18
+ color: var(--app-error-light-color);
19
+ }
@@ -0,0 +1,24 @@
1
+ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2
+
3
+ import { RichTextEditorComponent } from './rich-text-editor.component';
4
+
5
+ describe('RichTextComponent', () => {
6
+ let component: RichTextEditorComponent;
7
+ let fixture: ComponentFixture<RichTextEditorComponent>;
8
+
9
+ beforeEach(waitForAsync(() => {
10
+ TestBed.configureTestingModule({
11
+ declarations: [RichTextEditorComponent]
12
+ }).compileComponents();
13
+ }));
14
+
15
+ beforeEach(() => {
16
+ fixture = TestBed.createComponent(RichTextEditorComponent);
17
+ component = fixture.componentInstance;
18
+ fixture.detectChanges();
19
+ });
20
+
21
+ it('should create', () => {
22
+ expect(component).toBeTruthy();
23
+ });
24
+ });
@@ -0,0 +1,89 @@
1
+ import { Component, Input, Output, EventEmitter, OnChanges } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
4
+ import { EditorModule, TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular';
5
+
6
+ declare let tinymce: any;
7
+
8
+ @Component({
9
+ selector: 'app-rich-text-editor',
10
+ templateUrl: './rich-text-editor.component.html',
11
+ styleUrls: ['./rich-text-editor.component.scss'],
12
+ standalone: true,
13
+ imports: [CommonModule, EditorModule, ReactiveFormsModule],
14
+ providers: [{ provide: TINYMCE_SCRIPT_SRC, useValue: 'tinymce/tinymce.min.js' }]
15
+ })
16
+ export class RichTextEditorComponent implements OnChanges {
17
+ @Input() placeholder;
18
+ @Input() disabled;
19
+ @Input() readonly;
20
+ @Input() value;
21
+ @Input() label;
22
+ @Input() required;
23
+ @Input() info;
24
+ @Input() error;
25
+ @Input() testId;
26
+
27
+ @Output() onBlur: EventEmitter<any> = new EventEmitter();
28
+ @Output() onChange: EventEmitter<any> = new EventEmitter();
29
+
30
+ richText = new FormControl();
31
+
32
+ ngOnChanges() {
33
+ if (this.required) {
34
+ this.richText.addValidators(Validators.required);
35
+ }
36
+
37
+ if (this.disabled) {
38
+ this.richText.disable();
39
+ } else {
40
+ this.richText.enable();
41
+ }
42
+
43
+ if (this.value) {
44
+ this.richText.setValue(this.value);
45
+ }
46
+ }
47
+
48
+ filePickerCallback = cb => {
49
+ const input = document.createElement('input');
50
+ input.setAttribute('type', 'file');
51
+ input.setAttribute('accept', 'image/*');
52
+
53
+ input.addEventListener('change', (e: any) => {
54
+ const file = e.target.files[0];
55
+
56
+ const reader: any = new FileReader();
57
+ reader.addEventListener('load', () => {
58
+ /*
59
+ Note: Now we need to register the blob in TinyMCEs image blob
60
+ registry. In the next release this part hopefully won't be
61
+ necessary, as we are looking to handle it internally.
62
+ */
63
+ const blobId = `blobid${new Date().getTime()}`;
64
+ console.log('editorRef', tinymce.activeEditor);
65
+ const blobCache = tinymce.activeEditor.editorUpload.blobCache;
66
+ const base64 = reader.result.split(',')[1];
67
+ const blobInfo = blobCache.create(blobId, file, base64);
68
+ blobCache.add(blobInfo);
69
+
70
+ /* call the callback and populate the Title field with the file name */
71
+ cb(blobInfo.blobUri(), { title: file.name });
72
+ });
73
+ reader.readAsDataURL(file);
74
+ });
75
+
76
+ input.click();
77
+ };
78
+
79
+ blur() {
80
+ if (tinymce.activeEditor) {
81
+ const editorValue = tinymce.activeEditor.getContent({ format: 'html' });
82
+ this.onBlur.emit(editorValue);
83
+ }
84
+ }
85
+
86
+ change(event) {
87
+ this.onChange.emit(event);
88
+ }
89
+ }
@@ -160,7 +160,7 @@ export class DropdownComponent implements OnInit, OnDestroy {
160
160
  this.componentReference = (this.pConn$.getStateProps() as any).value;
161
161
 
162
162
  // @ts-ignore - parameter “contextName” for getDataObject method should be optional
163
- const optionsList = this.utils.getOptionList(this.configProps$, this.pConn$.getDataObject());
163
+ const optionsList = [...this.utils.getOptionList(this.configProps$, this.pConn$.getDataObject())];
164
164
  optionsList?.unshift({ key: 'Select', value: this.pConn$.getLocalizedValue('Select...', '', '') });
165
165
  this.options$ = optionsList;
166
166
  if (this.value$ === '' && !this.bReadonly$) {
@@ -0,0 +1,20 @@
1
+ <div *ngIf="displayMode$; else noDisplayMode">
2
+ <component-mapper *ngIf="bVisible$ !== false" name="FieldValueList" [props]="{ label$, value$, displayMode$ }"></component-mapper>
3
+ </div>
4
+ <ng-template #noDisplayMode>
5
+ <div *ngIf="!bReadonly$; else noEdit">
6
+ <div *ngIf="bVisible$">
7
+ <component-mapper
8
+ name="RichTextEditor"
9
+ [props]="{ placeholder, required: bRequired$, disabled: bDisabled$, label: label$, readonly: false, error, info, testId, value: value$ }"
10
+ [parent]="this"
11
+ [outputEvents]="{ onBlur: fieldOnBlur, onChange: fieldOnChange }"
12
+ ></component-mapper>
13
+ </div>
14
+ </div>
15
+ </ng-template>
16
+ <ng-template #noEdit>
17
+ <div *ngIf="bVisible$ !== false">
18
+ <component-mapper name="RichTextEditor" [props]="{ label: label$, value: value$, readonly: true, testId }" [parent]="this"></component-mapper>
19
+ </div>
20
+ </ng-template>
@@ -0,0 +1,24 @@
1
+ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2
+
3
+ import { RichTextComponent } from './rich-text.component';
4
+
5
+ describe('RichTextComponent', () => {
6
+ let component: RichTextComponent;
7
+ let fixture: ComponentFixture<RichTextComponent>;
8
+
9
+ beforeEach(waitForAsync(() => {
10
+ TestBed.configureTestingModule({
11
+ declarations: [RichTextComponent]
12
+ }).compileComponents();
13
+ }));
14
+
15
+ beforeEach(() => {
16
+ fixture = TestBed.createComponent(RichTextComponent);
17
+ component = fixture.componentInstance;
18
+ fixture.detectChanges();
19
+ });
20
+
21
+ it('should create', () => {
22
+ expect(component).toBeTruthy();
23
+ });
24
+ });
@@ -0,0 +1,131 @@
1
+ import { Component, OnInit, Input, forwardRef, OnDestroy } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormGroup, ReactiveFormsModule } from '@angular/forms';
4
+ import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
5
+ import { Utils } from '@pega/angular-sdk-components';
6
+ import { ComponentMapperComponent } from '@pega/angular-sdk-components';
7
+ import { handleEvent } from '@pega/angular-sdk-components';
8
+ import { PConnFieldProps } from '@pega/angular-sdk-components';
9
+
10
+ interface RichTextProps extends PConnFieldProps {
11
+ // If any, enter additional props that only exist on RichText here
12
+ }
13
+
14
+ @Component({
15
+ selector: 'app-rich-text',
16
+ templateUrl: './rich-text.component.html',
17
+ styleUrls: ['./rich-text.component.scss'],
18
+ standalone: true,
19
+ imports: [CommonModule, ReactiveFormsModule, forwardRef(() => ComponentMapperComponent)]
20
+ })
21
+ export class RichTextComponent implements OnInit, OnDestroy {
22
+ @Input() pConn$: typeof PConnect;
23
+ @Input() formGroup$: FormGroup;
24
+
25
+ // Used with AngularPConnect
26
+ angularPConnectData: AngularPConnectData = {};
27
+ configProps$: RichTextProps;
28
+
29
+ label$ = '';
30
+ value$ = '';
31
+ bRequired$ = false;
32
+ bReadonly$ = false;
33
+ bDisabled$ = false;
34
+ bVisible$ = true;
35
+ displayMode$?: string = '';
36
+ controlName$: string;
37
+ testId: string;
38
+ helperText: string;
39
+ placeholder: any;
40
+ info: any;
41
+ error: boolean;
42
+ status: any;
43
+
44
+ constructor(
45
+ private angularPConnect: AngularPConnectService,
46
+ private utils: Utils
47
+ ) {}
48
+
49
+ ngOnInit(): void {
50
+ // First thing in initialization is registering and subscribing to the AngularPConnect service
51
+ this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange);
52
+
53
+ // call updateSelf when initializing
54
+ this.checkAndUpdate();
55
+ }
56
+
57
+ ngOnDestroy(): void {
58
+ if (this.angularPConnectData.unsubscribeFn) {
59
+ this.angularPConnectData.unsubscribeFn();
60
+ }
61
+ }
62
+
63
+ // Callback passed when subscribing to store change
64
+ onStateChange() {
65
+ this.checkAndUpdate();
66
+ }
67
+
68
+ checkAndUpdate() {
69
+ // Should always check the bridge to see if the component should
70
+ // update itself (re-render)
71
+ const bUpdateSelf = this.angularPConnect.shouldComponentUpdate(this);
72
+
73
+ // ONLY call updateSelf when the component should update
74
+ if (bUpdateSelf) {
75
+ this.updateSelf();
76
+ }
77
+ }
78
+
79
+ // updateSelf
80
+ updateSelf(): void {
81
+ // moved this from ngOnInit() and call this from there instead...
82
+ this.configProps$ = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as RichTextProps;
83
+ const stateProps: any = this.pConn$.getStateProps();
84
+ this.status = stateProps?.status;
85
+
86
+ if (this.configProps$.value != undefined) {
87
+ this.value$ = this.configProps$.value;
88
+ }
89
+
90
+ this.testId = this.configProps$.testId;
91
+ this.displayMode$ = this.configProps$.displayMode;
92
+ this.label$ = this.configProps$.label;
93
+ this.placeholder = this.configProps$.placeholder;
94
+ this.info = stateProps?.validatemessage || this.configProps$.helperText;
95
+ this.error = stateProps?.status === 'error';
96
+
97
+ if (this.configProps$.required != null) {
98
+ this.bRequired$ = this.utils.getBooleanValue(this.configProps$.required);
99
+ }
100
+
101
+ if (this.configProps$.visibility != null) {
102
+ this.bVisible$ = this.utils.getBooleanValue(this.configProps$.visibility);
103
+ }
104
+
105
+ if (this.configProps$.disabled != undefined) {
106
+ this.bDisabled$ = this.utils.getBooleanValue(this.configProps$.disabled);
107
+ }
108
+
109
+ if (this.configProps$.readOnly != null) {
110
+ this.bReadonly$ = this.utils.getBooleanValue(this.configProps$.readOnly);
111
+ }
112
+ }
113
+
114
+ fieldOnChange() {
115
+ if (this.status === 'error') {
116
+ const property = (this.pConn$.getStateProps() as any).value;
117
+ this.pConn$.clearErrorMessages({
118
+ property,
119
+ category: '',
120
+ context: ''
121
+ });
122
+ }
123
+ }
124
+
125
+ fieldOnBlur(editorValue: any) {
126
+ // PConnect wants to use eventHandler for onBlur
127
+ const actionsApi = this.pConn$?.getActionsApi();
128
+ const propName = (this.pConn$?.getStateProps() as any).value;
129
+ handleEvent(actionsApi, 'changeNblur', propName, editorValue);
130
+ }
131
+ }
@@ -149,24 +149,30 @@ export class FileUtilityComponent implements OnInit, OnDestroy {
149
149
  this.lu_bLoading$ = true;
150
150
  }
151
151
 
152
- // const uploadedFiles = [];
153
-
154
- for (const file of files) {
155
- attachmentUtils
156
- .uploadAttachment(file, this.onUploadProgress, this.errorHandler, this.pConn$.getContextName())
157
- .then((fileResponse: any) => {
158
- if (fileResponse.type === 'File') {
159
- (attachmentUtils.linkAttachmentsToCase(caseID, [fileResponse], 'File', this.pConn$.getContextName()) as Promise<any>)
160
- .then(() => {
161
- this.refreshAttachments();
162
- })
163
- .catch(console.error);
152
+ const arFiles: any = Array.from(files);
153
+
154
+ Promise.allSettled(
155
+ arFiles.map(file => attachmentUtils.uploadAttachment(file, this.onUploadProgress, this.errorHandler, this.pConn$.getContextName()))
156
+ )
157
+ .then((fileResponses: any) => {
158
+ const uploadedFiles: any = [];
159
+ fileResponses.forEach(fileResponse => {
160
+ if (fileResponse.status === 'fulfilled') {
161
+ uploadedFiles.push(fileResponse.value);
164
162
  }
165
- })
166
- .catch(console.error);
167
- }
163
+ });
164
+ if (uploadedFiles.length > 0) {
165
+ (attachmentUtils.linkAttachmentsToCase(caseID, uploadedFiles, 'File', this.pConn$.getContextName()) as Promise<any>)
166
+ .then(() => {
167
+ this.refreshAttachments();
168
+ })
169
+ .catch();
170
+ }
171
+ })
172
+ .catch();
168
173
 
169
174
  this.arFileList$ = [];
175
+ this.lu_bLoading$ = false;
170
176
  }
171
177
 
172
178
  refreshAttachments() {
@@ -691,9 +697,16 @@ export class FileUtilityComponent implements OnInit, OnDestroy {
691
697
  if (caseID) {
692
698
  this.lu_bLoading$ = true;
693
699
 
694
- this.fetchCaseAttachments(caseID).then(resp => {
695
- this.handleAttachmentsResponse(resp);
696
- });
700
+ this.fetchCaseAttachments(caseID)
701
+ .then(resp => {
702
+ this.handleAttachmentsResponse(resp);
703
+ })
704
+ .catch(err => {
705
+ console.log(err);
706
+ })
707
+ .finally(() => {
708
+ this.lu_bLoading$ = false;
709
+ });
697
710
  }
698
711
  }
699
712