@pega/angular-sdk-overrides 0.25.1 → 0.25.2

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 (160) hide show
  1. package/lib/designSystemExtension/alert/alert.component.scss +3 -3
  2. package/lib/designSystemExtension/banner/banner.component.scss +12 -2
  3. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.html +6 -3
  4. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.scss +5 -34
  5. package/lib/designSystemExtension/material-details/material-details.component.scss +0 -5
  6. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.html +2 -2
  7. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.scss +4 -3
  8. package/lib/designSystemExtension/material-summary-item/material-summary-item.component.scss +2 -17
  9. package/lib/designSystemExtension/material-utility/material-utility.component.scss +1 -2
  10. package/lib/designSystemExtension/material-vertical-tabs/material-vertical-tabs.component.scss +1 -1
  11. package/lib/designSystemExtension/operator/operator.component.html +1 -1
  12. package/lib/designSystemExtension/operator/operator.component.scss +3 -10
  13. package/lib/designSystemExtension/operator/operator.component.ts +0 -1
  14. package/lib/designSystemExtension/pulse/pulse.component.scss +2 -2
  15. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.scss +0 -1
  16. package/lib/designSystemExtension/wss-quick-create/wss-quick-create.component.scss +16 -8
  17. package/lib/field/auto-complete/auto-complete.component.html +0 -1
  18. package/lib/field/auto-complete/auto-complete.component.ts +35 -172
  19. package/lib/field/cancel-alert/cancel-alert.component.html +8 -12
  20. package/lib/field/cancel-alert/cancel-alert.component.scss +2 -3
  21. package/lib/field/cancel-alert/cancel-alert.component.ts +24 -36
  22. package/lib/field/check-box/check-box.component.html +0 -1
  23. package/lib/field/check-box/check-box.component.scss +0 -1
  24. package/lib/field/check-box/check-box.component.ts +19 -149
  25. package/lib/field/currency/currency.component.ts +36 -168
  26. package/lib/field/date/date.component.html +1 -1
  27. package/lib/field/date/date.component.ts +30 -150
  28. package/lib/field/date-time/date-time.component.ts +31 -149
  29. package/lib/field/decimal/decimal.component.ts +38 -163
  30. package/lib/field/dropdown/dropdown.component.ts +29 -151
  31. package/lib/field/email/email.component.ts +16 -155
  32. package/lib/field/field.base.ts +149 -0
  33. package/lib/field/group/group.component.ts +7 -4
  34. package/lib/field/integer/integer.component.ts +18 -157
  35. package/lib/field/location/location.component.ts +1 -1
  36. package/lib/field/multiselect/multiselect.component.ts +46 -148
  37. package/lib/field/multiselect/utils.ts +55 -47
  38. package/lib/field/object-reference/object-reference.component.html +17 -0
  39. package/lib/field/object-reference/object-reference.component.scss +0 -0
  40. package/lib/field/object-reference/object-reference.component.spec.ts +22 -0
  41. package/lib/field/object-reference/object-reference.component.ts +237 -0
  42. package/lib/field/percentage/percentage.component.ts +37 -154
  43. package/lib/field/phone/phone.component.ts +28 -142
  44. package/lib/field/radio-buttons/radio-buttons.component.scss +4 -2
  45. package/lib/field/radio-buttons/radio-buttons.component.ts +35 -161
  46. package/lib/field/rich-text/rich-text.component.ts +19 -90
  47. package/lib/field/scalar-list/scalar-list.component.ts +17 -72
  48. package/lib/field/selectable-card/selectable-card.component.html +54 -24
  49. package/lib/field/selectable-card/selectable-card.component.scss +11 -0
  50. package/lib/field/selectable-card/selectable-card.component.ts +16 -52
  51. package/lib/field/semantic-link/semantic-link.component.html +4 -8
  52. package/lib/field/semantic-link/semantic-link.component.scss +0 -13
  53. package/lib/field/semantic-link/semantic-link.component.ts +165 -5
  54. package/lib/field/text/text.component.scss +0 -1
  55. package/lib/field/text-area/text-area.component.ts +18 -152
  56. package/lib/field/text-input/text-input.component.ts +16 -155
  57. package/lib/field/time/time.component.ts +17 -151
  58. package/lib/field/url/url.component.ts +16 -154
  59. package/lib/field/user-reference/user-reference.component.scss +0 -1
  60. package/lib/field/user-reference/user-reference.component.ts +2 -3
  61. package/lib/infra/Containers/flow-container/flow-container.component.ts +5 -7
  62. package/lib/infra/Containers/modal-view-container/modal-view-container.component.ts +5 -10
  63. package/lib/infra/Containers/view-container/helper.ts +35 -2
  64. package/lib/infra/Containers/view-container/view-container.component.ts +1 -1
  65. package/lib/infra/action-buttons/action-buttons.component.html +13 -8
  66. package/lib/infra/action-buttons/action-buttons.component.scss +23 -0
  67. package/lib/infra/action-buttons/action-buttons.component.ts +1 -2
  68. package/lib/infra/assignment/assignment.component.ts +8 -6
  69. package/lib/infra/assignment-card/assignment-card.component.html +1 -2
  70. package/lib/infra/assignment-card/assignment-card.component.scss +0 -4
  71. package/lib/infra/assignment-card/assignment-card.component.ts +21 -4
  72. package/lib/infra/defer-load/defer-load.component.html +6 -2
  73. package/lib/infra/defer-load/defer-load.component.ts +16 -10
  74. package/lib/infra/multi-step/multi-step.component.scss +1 -21
  75. package/lib/infra/navbar/navbar.component.html +25 -28
  76. package/lib/infra/navbar/navbar.component.scss +16 -4
  77. package/lib/infra/navbar/navbar.component.ts +8 -3
  78. package/lib/infra/root-container/root-container.component.scss +0 -1
  79. package/lib/infra/root-container/root-container.component.ts +1 -2
  80. package/lib/infra/stages/stages.component.html +2 -2
  81. package/lib/infra/stages/stages.component.scss +7 -35
  82. package/lib/infra/stages/stages.component.ts +4 -2
  83. package/lib/infra/view/view.component.html +1 -1
  84. package/lib/infra/view/view.component.ts +0 -2
  85. package/lib/template/advanced-search/advanced-search.component.html +12 -0
  86. package/lib/template/advanced-search/advanced-search.component.scss +0 -0
  87. package/lib/template/advanced-search/advanced-search.component.spec.ts +0 -0
  88. package/lib/template/advanced-search/advanced-search.component.ts +112 -0
  89. package/lib/template/advanced-search/advanced-search.service.ts +27 -0
  90. package/lib/template/advanced-search/search-group/persist-utils.ts +56 -0
  91. package/lib/template/advanced-search/search-groups/search-groups.component.html +32 -0
  92. package/lib/template/advanced-search/search-groups/search-groups.component.scss +0 -0
  93. package/lib/template/advanced-search/search-groups/search-groups.component.spec.ts +0 -0
  94. package/lib/template/advanced-search/search-groups/search-groups.component.ts +294 -0
  95. package/lib/template/advanced-search/search-groups/utils.ts +29 -0
  96. package/lib/template/app-shell/app-shell.component.html +4 -1
  97. package/lib/template/app-shell/app-shell.component.scss +0 -3
  98. package/lib/template/app-shell/app-shell.component.ts +46 -7
  99. package/lib/template/case-summary/case-summary.component.scss +0 -2
  100. package/lib/template/case-view/case-view.component.html +3 -3
  101. package/lib/template/case-view/case-view.component.scss +18 -10
  102. package/lib/template/case-view/case-view.component.ts +1 -1
  103. package/lib/template/data-reference/data-reference-advanced-search.service.ts +16 -0
  104. package/lib/template/data-reference/data-reference.component.html +11 -8
  105. package/lib/template/data-reference/data-reference.component.ts +346 -112
  106. package/lib/template/data-reference/search-form/search-form.component.html +39 -0
  107. package/lib/template/data-reference/search-form/search-form.component.scss +11 -0
  108. package/lib/template/data-reference/search-form/search-form.component.spec.ts +0 -0
  109. package/lib/template/data-reference/search-form/search-form.component.ts +167 -0
  110. package/lib/template/data-reference/search-form/tabsData.ts +160 -0
  111. package/lib/template/data-reference/utils.ts +92 -0
  112. package/lib/template/default-form/default-form.component.ts +10 -2
  113. package/lib/template/default-page/default-page.component.html +34 -0
  114. package/lib/template/default-page/default-page.component.scss +31 -0
  115. package/lib/template/default-page/default-page.component.spec.ts +24 -0
  116. package/lib/template/default-page/default-page.component.ts +64 -0
  117. package/lib/template/field-group-list/field-group-list.component.scss +0 -1
  118. package/lib/template/inline-dashboard-page/inline-dashboard-page.component.ts +1 -1
  119. package/lib/template/list-view/list-view.component.html +9 -4
  120. package/lib/template/list-view/list-view.component.scss +21 -21
  121. package/lib/template/list-view/list-view.component.ts +154 -84
  122. package/lib/template/list-view/utils.ts +25 -2
  123. package/lib/template/object-page/object-page.component.html +1 -0
  124. package/lib/template/object-page/object-page.component.scss +0 -0
  125. package/lib/template/object-page/object-page.component.spec.ts +22 -0
  126. package/lib/template/object-page/object-page.component.ts +14 -0
  127. package/lib/template/one-column-tab/one-column-tab.component.scss +1 -1
  128. package/lib/template/repeating-structures/repeating-structures.component.ts +0 -1
  129. package/lib/template/self-service-case-view/self-service-case-view.component.html +80 -0
  130. package/lib/template/self-service-case-view/self-service-case-view.component.scss +124 -0
  131. package/lib/template/self-service-case-view/self-service-case-view.component.spec.ts +24 -0
  132. package/lib/template/self-service-case-view/self-service-case-view.component.ts +216 -0
  133. package/lib/template/simple-table/simple-table.component.ts +0 -1
  134. package/lib/template/simple-table-manual/helpers.ts +2 -2
  135. package/lib/template/simple-table-manual/simple-table-manual.component.html +4 -4
  136. package/lib/template/simple-table-manual/simple-table-manual.component.scss +4 -14
  137. package/lib/template/simple-table-manual/simple-table-manual.component.ts +8 -4
  138. package/lib/template/single-reference-readonly/single-reference-readonly.component.html +4 -1
  139. package/lib/template/single-reference-readonly/single-reference-readonly.component.scss +21 -0
  140. package/lib/template/single-reference-readonly/single-reference-readonly.component.ts +104 -3
  141. package/lib/template/utils.ts +42 -0
  142. package/lib/template/wss-nav-bar/wss-nav-bar.component.html +5 -4
  143. package/lib/template/wss-nav-bar/wss-nav-bar.component.scss +2 -8
  144. package/lib/template/wss-nav-bar/wss-nav-bar.component.ts +1 -8
  145. package/lib/widget/app-announcement/app-announcement.component.html +1 -2
  146. package/lib/widget/app-announcement/app-announcement.component.scss +2 -2
  147. package/lib/widget/attachment/Attachment.types.ts +92 -0
  148. package/lib/widget/attachment/AttachmentUtils.ts +287 -0
  149. package/lib/widget/attachment/attachment.component.html +3 -3
  150. package/lib/widget/attachment/attachment.component.scss +2 -5
  151. package/lib/widget/attachment/attachment.component.ts +255 -254
  152. package/lib/widget/feed-container/feed-container.component.scss +3 -9
  153. package/lib/widget/feed-container/feed-container.component.ts +2 -2
  154. package/lib/widget/file-utility/file-utility.component.html +3 -3
  155. package/lib/widget/file-utility/file-utility.component.scss +5 -16
  156. package/lib/widget/list-utility/list-utility.component.scss +3 -5
  157. package/lib/widget/todo/todo.component.html +8 -5
  158. package/lib/widget/todo/todo.component.scss +10 -11
  159. package/lib/widget/todo/todo.component.ts +6 -2
  160. package/package.json +1 -1
@@ -1,14 +1,115 @@
1
- import { Component, Input, forwardRef } from '@angular/core';
1
+ import { CommonModule } from '@angular/common';
2
+ import { Component, Input, forwardRef, OnInit, OnDestroy } from '@angular/core';
2
3
  import { FormGroup } from '@angular/forms';
4
+ import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
3
5
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
6
+ import { getDataRelationshipContextFromKey } from '@pega/angular-sdk-components';
4
7
 
5
8
  @Component({
6
9
  selector: 'app-single-reference-readonly',
7
10
  templateUrl: './single-reference-readonly.component.html',
8
11
  styleUrls: ['./single-reference-readonly.component.scss'],
9
- imports: [forwardRef(() => ComponentMapperComponent)]
12
+ imports: [CommonModule, forwardRef(() => ComponentMapperComponent)]
10
13
  })
11
- export class SingleReferenceReadonlyComponent {
14
+ export class SingleReferenceReadonlyComponent implements OnInit, OnDestroy {
12
15
  @Input() pConn$: typeof PConnect;
13
16
  @Input() formGroup$: FormGroup;
17
+ @Input() dataRelationshipContext?: any;
18
+
19
+ angularPConnectData: AngularPConnectData = {};
20
+ configProps: any;
21
+ component: any;
22
+ label: string;
23
+ newPconn: typeof PConnect;
24
+ displayMode: string;
25
+
26
+ constructor(private angularPConnect: AngularPConnectService) {}
27
+
28
+ ngOnInit(): void {
29
+ // First thing in initialization is registering and subscribing to the AngularPConnect service
30
+ this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange);
31
+ this.checkAndUpdate();
32
+ }
33
+
34
+ ngOnDestroy(): void {
35
+ if (this.angularPConnectData.unsubscribeFn) {
36
+ this.angularPConnectData.unsubscribeFn();
37
+ }
38
+ }
39
+
40
+ // Callback passed when subscribing to store change
41
+ onStateChange() {
42
+ this.checkAndUpdate();
43
+ }
44
+
45
+ checkAndUpdate() {
46
+ // Should always check the bridge to see if the component should
47
+ // update itself (re-render)
48
+ const bUpdateSelf = this.angularPConnect.shouldComponentUpdate(this);
49
+
50
+ // ONLY call updateSelf when the component should update
51
+ if (bUpdateSelf) {
52
+ this.updateSelf();
53
+ }
54
+ }
55
+
56
+ updateSelf() {
57
+ this.configProps = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps());
58
+ const rawViewMetadata = this.pConn$.getRawMetadata();
59
+ const label = this.configProps.label;
60
+ const showLabel = this.configProps.showLabel;
61
+ const propsToUse = { label, showLabel, ...this.pConn$.getInheritedProps() };
62
+ const type = (rawViewMetadata?.config as any)?.componentType;
63
+ this.displayMode = this.configProps.displayMode;
64
+ const targetObjectType = this.configProps.targetObjectType;
65
+ const referenceType = targetObjectType === 'case' ? 'Case' : 'Data';
66
+ const hideLabel = this.configProps.hideLabel;
67
+ // const additionalFields = this.configProps.additionalFields;
68
+ const displayAs = this.configProps.displayAs ?? 'readonly';
69
+ const dataRelationshipContext = (rawViewMetadata?.config as any)?.displayField
70
+ ? getDataRelationshipContextFromKey((rawViewMetadata?.config as any)?.displayField)
71
+ : this.dataRelationshipContext;
72
+ if (propsToUse.showLabel === false) {
73
+ propsToUse.label = '';
74
+ }
75
+ this.label = propsToUse.label;
76
+
77
+ const editableComponents = ['AutoComplete', 'SimpleTableSelect', 'Dropdown', 'RadioButtons'];
78
+ const config: any = {
79
+ ...rawViewMetadata?.config,
80
+ primaryField: (rawViewMetadata?.config as any)?.displayField,
81
+ label: this.label
82
+ };
83
+
84
+ const activeViewRuleClass = (rawViewMetadata?.config as any)?.targetObjectClass;
85
+ if (editableComponents.includes(type)) {
86
+ config.caseClass = activeViewRuleClass;
87
+ config.text = config.primaryField;
88
+ config.caseID = config.value;
89
+ config.contextPage = `@P .${dataRelationshipContext}`;
90
+ config.resourceParams = {
91
+ workID: displayAs === 'table' ? (config as any)?.selectionKey : config.value
92
+ };
93
+ config.resourcePayload = {
94
+ caseClassName: activeViewRuleClass
95
+ };
96
+ }
97
+
98
+ this.component = this.pConn$.createComponent(
99
+ {
100
+ type: 'SemanticLink',
101
+ config: {
102
+ ...config,
103
+ displayMode: this.displayMode,
104
+ referenceType,
105
+ hideLabel,
106
+ dataRelationshipContext
107
+ }
108
+ },
109
+ '',
110
+ 0,
111
+ {}
112
+ );
113
+ this.newPconn = this.component.getPConnect();
114
+ }
14
115
  }
@@ -37,3 +37,45 @@ export const evaluateAllowRowAction = (allowRowDelete, rowData) => {
37
37
  }
38
38
  return false;
39
39
  };
40
+
41
+ export function prepareCaseSummaryData(caseSummaryRegion, portalSpecificVisibilityChecker?) {
42
+ const filterVisibleChildren = children => {
43
+ return children
44
+ ?.getPConnect()
45
+ ?.getChildren()
46
+ ?.filter(child => {
47
+ const configProps = child.getPConnect().getConfigProps();
48
+ const defaultVisibilityCn = !('visibility' in configProps) || configProps.visibility === true;
49
+ return defaultVisibilityCn && (portalSpecificVisibilityChecker?.(configProps) ?? true);
50
+ });
51
+ };
52
+ const convertChildrenToSummaryData = children => {
53
+ return children?.map(childItem => {
54
+ const childPConnData = childItem.getPConnect().resolveConfigProps(childItem.getPConnect().getRawMetadata());
55
+ childPConnData.kid = childItem.getPConnect();
56
+ return childPConnData;
57
+ });
58
+ };
59
+
60
+ const summaryFieldChildren = caseSummaryRegion
61
+ .getPConnect()
62
+ .getChildren()[0]
63
+ ?.getPConnect()
64
+ ?.getReferencedViewPConnect()
65
+ ?.getPConnect()
66
+ ?.getChildren();
67
+
68
+ const primarySummaryFields =
69
+ summaryFieldChildren && summaryFieldChildren.length > 0
70
+ ? convertChildrenToSummaryData(filterVisibleChildren(summaryFieldChildren[0]))
71
+ : undefined;
72
+ const secondarySummaryFields =
73
+ summaryFieldChildren && summaryFieldChildren.length > 1
74
+ ? convertChildrenToSummaryData(filterVisibleChildren(summaryFieldChildren[1]))
75
+ : undefined;
76
+
77
+ return {
78
+ primarySummaryFields,
79
+ secondarySummaryFields
80
+ };
81
+ }
@@ -5,17 +5,19 @@
5
5
  <img src="{{ portalLogoImage$ }}" class="psdk-nav-logo" />
6
6
  </div>
7
7
  <div class="psdk-nav-portal-info">
8
- <div class="psdk-nav-portal-app">{{ portalApp$ }}</div>
8
+ <div class="psdk-nav-portal-app">{{ appName$ }}</div>
9
9
  </div>
10
10
  </div>
11
11
 
12
+ <span class="spacer"></span>
13
+
12
14
  <div *ngFor="let page of navPages$">
13
- <div class="flex-box mat-list-item" style="cursor: pointer" (click)="navPanelButtonClick(page)">
15
+ <div class="flex-box mat-list-item" style="cursor: pointer; font-size: 1rem; text-transform: capitalize" (click)="navPanelButtonClick(page)">
14
16
  <div mat-button class="psdk-nav-button-span">{{ page.pyLabel }}</div>
15
17
  </div>
16
18
  </div>
17
19
 
18
- <span class="spacer"></span>
20
+ <!-- <span class="spacer"></span> -->
19
21
 
20
22
  <mat-list>
21
23
  <mat-list-item [matMenuTriggerFor]="menu" class="psdk-profile-list-item">
@@ -24,7 +26,6 @@
24
26
  </div>
25
27
  </mat-list-item>
26
28
  <mat-menu #menu="matMenu">
27
- <button mat-menu-item>Profile</button>
28
29
  <button mat-menu-item (click)="navPanelLogoutClick()">{{ localizedVal('Log off', localeCategory) }}</button>
29
30
  </mat-menu>
30
31
  </mat-list>
@@ -16,15 +16,13 @@
16
16
  cursor: pointer;
17
17
  padding-right: 1rem;
18
18
  &:hover {
19
- background-color: var(--app-neutral-color);
20
19
  }
21
20
  align-items: center;
22
21
  }
23
22
 
24
23
  .psdk-nav-logo {
25
- width: 3.75rem;
26
- padding: 0.625rem;
27
- margin-right: 1.25rem;
24
+ max-width: 100%;
25
+ height: 3rem;
28
26
  }
29
27
 
30
28
  .psdk-nav-svg-icon {
@@ -111,7 +109,6 @@
111
109
  color: var(--app-nav-color);
112
110
 
113
111
  &:hover {
114
- background-color: var(--app-neutral-color);
115
112
  }
116
113
  }
117
114
 
@@ -127,8 +124,6 @@
127
124
  align-items: center;
128
125
  text-align: center;
129
126
  display: inline-flex;
130
- background: var(--app-neutral-color);
131
- color: var(--app-form-color);
132
127
  font-weight: normal;
133
128
  font-size: 1rem;
134
129
  }
@@ -148,7 +143,6 @@ mat-list-item {
148
143
  height: auto !important;
149
144
 
150
145
  &:hover {
151
- // background-color: rgba(0, 0, 0, 0.5);
152
146
  }
153
147
 
154
148
  .flex-box {
@@ -27,6 +27,7 @@ export class WssNavBarComponent implements OnInit, OnDestroy {
27
27
  @Input() pages$: any[];
28
28
  @Input() caseTypes$: any[];
29
29
  @Input() homePage: any;
30
+ @Input() portalLogoImage$: string;
30
31
 
31
32
  // For interaction with AngularPConnect
32
33
  angularPConnectData: AngularPConnectData = {};
@@ -36,10 +37,6 @@ export class WssNavBarComponent implements OnInit, OnDestroy {
36
37
  navExpandCollapse$: string;
37
38
  bShowCaseTypes$ = false;
38
39
 
39
- portalApp$: string | undefined = '';
40
- portalLogoImage$: string;
41
- showAppName$ = false;
42
-
43
40
  portalOperator$: string | undefined;
44
41
  portalOperatorInitials$: string;
45
42
 
@@ -126,12 +123,8 @@ export class WssNavBarComponent implements OnInit, OnDestroy {
126
123
 
127
124
  // const oData = this.pConn$.getDataObject();
128
125
 
129
- this.portalLogoImage$ = this.utils.getSDKStaticContentUrl().concat('assets/pzpega-logo-mark.svg');
130
126
  this.portalOperator$ = PCore.getEnvironmentInfo().getOperatorName();
131
127
  this.portalOperatorInitials$ = this.utils.getInitials(this.portalOperator$ ?? '');
132
- this.showAppName$ = this.configProps$.showAppName;
133
-
134
- this.portalApp$ = PCore.getEnvironmentInfo().getApplicationLabel();
135
128
  });
136
129
  }
137
130
 
@@ -3,11 +3,10 @@
3
3
  <div>
4
4
  <p>{{ configProps$.description }}</p>
5
5
  <div>
6
- <h3>{{ configProps$.label }}</h3>
7
6
  <ul>
8
7
  <li *ngFor="let detail of details$">{{ detail }}</li>
9
8
  </ul>
10
9
  </div>
11
10
  </div>
12
- <a *ngIf="configProps$.whatsnewlink != ''" mat-raised-button color="primary" [href]="configProps$.whatsnewlink">See what's new</a>
11
+ <a *ngIf="configProps$.whatsnewlink != ''" mat-raised-button [href]="configProps$.whatsnewlink">See what's new</a>
13
12
  </article>
@@ -10,10 +10,10 @@ ul {
10
10
  padding-inline-start: 20px;
11
11
  }
12
12
  .psdk-announcement {
13
- background-color: var(--app-form-color);
13
+ background-color: var(--mat-sys-surface-container);
14
14
  padding: 1rem;
15
15
  margin: 1rem 0;
16
16
  border-radius: 0.6125rem;
17
17
  border-left: 6px solid;
18
- border-left-color: var(--app-primary-color);
18
+ border-left-color: var(--mat-sys-primary);
19
19
  }
@@ -0,0 +1,92 @@
1
+ export interface ResponseProps {
2
+ ID: string;
3
+ extension: string;
4
+ createDateTime?: Date | string | number;
5
+ createUser?: string;
6
+ name: string;
7
+ }
8
+
9
+ export interface AttachmentActions {
10
+ rel: string;
11
+ href: string;
12
+ title: string;
13
+ type: string;
14
+ }
15
+
16
+ export interface AttachmentLinks {
17
+ delete: AttachmentActions;
18
+ download: AttachmentActions;
19
+ edit: AttachmentActions;
20
+ }
21
+ export interface FileObject extends File {
22
+ icon?: string;
23
+ ID: string;
24
+ fileName: string;
25
+ category: string;
26
+ responseType: string;
27
+ fileType: string;
28
+ mimeType: string;
29
+ extension: string;
30
+ thumbnail?: string;
31
+ nameWithExt: string;
32
+ inProgress?: boolean;
33
+ progress?: number;
34
+ handle: string;
35
+ label: string;
36
+ delete?: boolean;
37
+ error?: boolean;
38
+ description: string;
39
+
40
+ props: {
41
+ icon?: string;
42
+ id: string;
43
+ error?: string;
44
+ format?: string;
45
+ name: string;
46
+ thumbnail?: string;
47
+ onPreview?: () => void;
48
+ onDelete?: () => void;
49
+ onOpen?: () => void;
50
+ onEdit?: () => void;
51
+ onCancel?: () => void;
52
+ };
53
+ responseProps: ResponseProps;
54
+ value?: {
55
+ filename: string;
56
+ ID: string;
57
+ thumbnail: string;
58
+ };
59
+ categoryName: string;
60
+ createTime: string;
61
+ createdBy: string;
62
+ createdByName: string;
63
+ links: AttachmentLinks;
64
+ name: string;
65
+ meta?: any;
66
+ }
67
+
68
+ export interface ReduxAttachments {
69
+ ID?: string;
70
+ pzInsKey?: string;
71
+ FileName: string;
72
+ Category: string;
73
+ MimeType?: string;
74
+ FileExtension: string;
75
+ error: string | null;
76
+ localAttachment: boolean;
77
+ thumbnail?: string;
78
+ fileIndex?: number;
79
+ instruction?: string;
80
+ }
81
+
82
+ export interface PageInstructionOptions {
83
+ allowMultiple: boolean;
84
+ isMultiAttachmentInInlineEditTable: boolean;
85
+ attachmentCount: number;
86
+ insertPageInstruction: boolean;
87
+ deletePageInstruction: boolean;
88
+ deleteIndex: number;
89
+ insertRedux: boolean;
90
+ isOldAttachment: boolean;
91
+ deleteRedux: boolean;
92
+ }
@@ -0,0 +1,287 @@
1
+ import download from 'downloadjs';
2
+
3
+ import type { FileObject, PageInstructionOptions, ReduxAttachments } from './Attachment.types';
4
+
5
+ const megabyteSize = 1048576;
6
+
7
+ export const isContentBinary = (headers: Record<string, string>) => {
8
+ return headers && headers['content-transfer-encoding'] === 'binary';
9
+ };
10
+
11
+ export const isContentBase64 = (headers: Record<string, string>) => {
12
+ return headers && headers['content-transfer-encoding'] === 'base64';
13
+ };
14
+
15
+ export const validateMaxSize = (fileObj: Record<string, number>, maxSizeInMB: string) => {
16
+ const fileSize = (fileObj['size'] / megabyteSize).toFixed(2);
17
+ return parseFloat(fileSize) < parseFloat(maxSizeInMB);
18
+ };
19
+
20
+ export const validateFileExtension = (fileObj: Record<string, string>, allowedExtensions: string) => {
21
+ if (!allowedExtensions) {
22
+ return true;
23
+ }
24
+ const allowedExtensionList = allowedExtensions
25
+ .toLowerCase()
26
+ .split(',')
27
+ .map(item => item.replaceAll('.', '').trim());
28
+ const extension = fileObj['name'].split('.').pop()?.toLowerCase() || '';
29
+ return allowedExtensionList.includes(extension);
30
+ };
31
+
32
+ export const fileDownload = (data: string | Blob, fileName: string, ext: string | null, headers: Record<string, string>) => {
33
+ const name = ext ? `${fileName}.${ext}` : fileName;
34
+ // Temp fix: downloading EMAIl type attachment as html file
35
+ if (ext === 'html') {
36
+ download(isContentBase64(headers) ? atob(data as string) : data, name, 'text/html');
37
+ } else if (isContentBinary(headers)) {
38
+ download(data, name);
39
+ } else {
40
+ download(atob(data as string), name);
41
+ }
42
+ };
43
+
44
+ export const fileDownloadVar = (content: { data: string; headers: Record<string, string> }, type: string, name: string, extension: string) => {
45
+ if (type === 'FILE' || type === undefined) {
46
+ fileDownload(content.data, name, extension, content.headers);
47
+ } else if (type === 'URL') {
48
+ let { data } = content;
49
+ if (!/^(http|https):\/\//.test(data)) {
50
+ data = `//${data}`;
51
+ }
52
+ window.open(content.data, '_blank');
53
+ } else if (type === 'EMAIL') {
54
+ // Temp Fix: for EMAIL type attachment
55
+ fileDownload(content.data, name, 'html', content.headers);
56
+ }
57
+ };
58
+
59
+ export const getMappedValue = (value: string): string => {
60
+ return PCore.getEnvironmentInfo().getKeyMapping(value) ?? value;
61
+ };
62
+
63
+ const generateInstructions = (
64
+ files: FileObject[],
65
+ pConn: typeof PConnect,
66
+ attachmentsInModal: ReduxAttachments[] | Pick<ReduxAttachments, 'instruction' | 'fileIndex'>[],
67
+ options: {
68
+ allowMultiple: boolean;
69
+ isMultiAttachmentInInlineEditTable: boolean;
70
+ attachmentCount: number;
71
+ insertPageInstruction: boolean;
72
+ deletePageInstruction: boolean;
73
+ deleteIndex: number;
74
+ }
75
+ ) => {
76
+ const { allowMultiple, isMultiAttachmentInInlineEditTable, attachmentCount, insertPageInstruction, deletePageInstruction, deleteIndex } = options;
77
+ const transformedAttachments: ReduxAttachments[] = [];
78
+ let valueRef = pConn.getStateProps().value;
79
+ valueRef = valueRef?.indexOf('.') === 0 ? valueRef.substring(1) : valueRef;
80
+ const uniqueKey = getMappedValue('pzInsKey');
81
+ files.forEach((file, index) => {
82
+ const filename = file.value?.filename || file.props?.name || '';
83
+ const payload = {
84
+ [uniqueKey]: file.value?.ID || file.props?.id,
85
+ FileName: filename,
86
+ Category: '',
87
+ // MimeType: getMimeTypeFromFile(filename),
88
+ FileExtension: filename.split('.').pop() ?? filename,
89
+ error: file.props?.error || null,
90
+ localAttachment: true,
91
+ thumbnail: file.value?.thumbnail
92
+ };
93
+ transformedAttachments.push(payload);
94
+ if (payload.error) {
95
+ return; // Don't process page instructions for error files, skip current iteration
96
+ }
97
+ if (allowMultiple) {
98
+ if (isMultiAttachmentInInlineEditTable) {
99
+ if (insertPageInstruction) {
100
+ attachmentsInModal.push({ ...payload, instruction: 'insert' } as any);
101
+ } else if (deletePageInstruction) {
102
+ (attachmentsInModal as Pick<ReduxAttachments, 'instruction' | 'fileIndex'>[]).push({
103
+ instruction: 'delete',
104
+ fileIndex: deleteIndex
105
+ });
106
+ }
107
+ } else if (insertPageInstruction) {
108
+ pConn.getListActions().insert({ ID: payload[uniqueKey] }, attachmentCount + index, undefined, {
109
+ skipStateUpdate: true
110
+ });
111
+ } else if (deletePageInstruction) {
112
+ pConn.getListActions().deleteEntry(deleteIndex, undefined, { skipStateUpdate: true });
113
+ }
114
+ } else if (insertPageInstruction) {
115
+ pConn.getListActions().replacePage(`.${valueRef}`, { ID: payload[uniqueKey] }, { skipStateUpdate: true });
116
+ } else if (deletePageInstruction) {
117
+ pConn.getListActions().deletePage(`.${valueRef}`, { skipStateUpdate: true });
118
+ }
119
+ });
120
+ return transformedAttachments;
121
+ };
122
+
123
+ export const updateReduxState = (
124
+ transformedAttachments: ReduxAttachments[],
125
+ pConn: typeof PConnect,
126
+ valueRef: string,
127
+ options: PageInstructionOptions
128
+ ) => {
129
+ const { allowMultiple, isOldAttachment, insertRedux, deleteRedux } = options;
130
+ let deleteIndex = -1;
131
+
132
+ if (allowMultiple || isOldAttachment) {
133
+ transformedAttachments.forEach(attachment => {
134
+ const key = isOldAttachment ? `${valueRef}.pxResults` : valueRef;
135
+ const existingAttachments: ReduxAttachments[] = PCore.getStoreValue(`.${key}`, pConn.getPageReference(), pConn.getContextName()) || [];
136
+
137
+ if (insertRedux) {
138
+ const actionPayLoad = {
139
+ type: 'LIST_ACTION',
140
+ payload: {
141
+ instruction: 'INSERT',
142
+ context: pConn.getContextName(),
143
+ referenceList: `${pConn.getPageReference()}.${key}`,
144
+ listIndex: existingAttachments.length,
145
+ content: attachment
146
+ }
147
+ };
148
+ PCore.getStore()?.dispatch(actionPayLoad);
149
+ } else if (deleteRedux) {
150
+ const uniqueKey = getMappedValue('pzInsKey');
151
+ deleteIndex = existingAttachments.findIndex(
152
+ existingAttachment =>
153
+ existingAttachment[uniqueKey as keyof ReduxAttachments] === transformedAttachments[0][uniqueKey as keyof ReduxAttachments]
154
+ );
155
+ const actionPayLoad = {
156
+ type: 'LIST_ACTION',
157
+ payload: {
158
+ instruction: 'DELETE',
159
+ context: pConn.getContextName(),
160
+ referenceList: `${pConn.getPageReference()}.${key}`,
161
+ listIndex: deleteIndex
162
+ }
163
+ };
164
+ PCore.getStore()?.dispatch(actionPayLoad);
165
+ }
166
+ });
167
+ } else if (insertRedux) {
168
+ const actionPayLoad = {
169
+ type: 'LIST_ACTION',
170
+ payload: {
171
+ instruction: 'REPLACE',
172
+ context: pConn.getContextName(),
173
+ referenceList: `${pConn.getPageReference()}.${valueRef}`,
174
+ content: transformedAttachments[0]
175
+ }
176
+ };
177
+ PCore.getStore()?.dispatch(actionPayLoad);
178
+ } else if (deleteRedux) {
179
+ const actionPayLoad = {
180
+ type: 'LIST_ACTION',
181
+ payload: {
182
+ instruction: 'DELETEPAGE',
183
+ context: pConn.getContextName(),
184
+ referenceList: `${pConn.getPageReference()}.${valueRef}`
185
+ }
186
+ };
187
+ PCore.getStore()?.dispatch(actionPayLoad);
188
+ }
189
+ };
190
+
191
+ export const insertAttachments = (
192
+ files: FileObject[],
193
+ pConn: typeof PConnect,
194
+ attachmentsInModal: ReduxAttachments[],
195
+ options: PageInstructionOptions
196
+ ) => {
197
+ const { isMultiAttachmentInInlineEditTable } = options;
198
+ let valueRef = pConn.getStateProps().value;
199
+ valueRef = valueRef?.indexOf('.') === 0 ? valueRef.substring(1) : valueRef;
200
+ const transformedAttachments = generateInstructions(files, pConn, attachmentsInModal, {
201
+ ...options,
202
+ insertPageInstruction: true
203
+ });
204
+
205
+ if (isMultiAttachmentInInlineEditTable) {
206
+ return; // For attachments within modal, redux update is not necessary yet, as modal isn't submitted at this stage
207
+ }
208
+ updateReduxState(transformedAttachments, pConn, valueRef, { ...options, insertRedux: true });
209
+ };
210
+
211
+ export const deleteAttachments = (
212
+ files: FileObject[],
213
+ pConn: typeof PConnect,
214
+ attachmentsInModal: Pick<ReduxAttachments, 'instruction' | 'fileIndex'>[],
215
+ options: PageInstructionOptions
216
+ ) => {
217
+ const { isMultiAttachmentInInlineEditTable } = options;
218
+ let valueRef = pConn.getStateProps().value;
219
+ valueRef = valueRef?.indexOf('.') === 0 ? valueRef.substring(1) : valueRef;
220
+ const transformedAttachments = generateInstructions(files, pConn, attachmentsInModal, {
221
+ ...options,
222
+ deletePageInstruction: true
223
+ });
224
+
225
+ if (isMultiAttachmentInInlineEditTable) {
226
+ return; // For attachments within modal, redux update is not necessary yet, as modal isn't submitted at this stage
227
+ }
228
+ updateReduxState(transformedAttachments, pConn, valueRef, { ...options, deleteRedux: true });
229
+ };
230
+
231
+ export const clearFieldErrorMessages = (pConn: typeof PConnect) => {
232
+ const fieldName = pConn.getStateProps().value;
233
+ PCore.getMessageManager().clearMessages({
234
+ type: PCore.getConstants().MESSAGES.MESSAGES_TYPE_ERROR,
235
+ property: fieldName,
236
+ pageReference: pConn.getPageReference(),
237
+ context: pConn.getContextName()
238
+ });
239
+ };
240
+
241
+ export const onFileDownload = (responseProps, context) => {
242
+ const { ID, name, extension, type, category, responseType } = responseProps;
243
+
244
+ if (category !== 'pxDocument') {
245
+ (
246
+ PCore.getAttachmentUtils().downloadAttachment(ID, context, responseType) as Promise<{
247
+ data: string;
248
+ headers: Record<string, string>;
249
+ }>
250
+ )
251
+ .then(content => {
252
+ fileDownloadVar(content, type, name, extension);
253
+ })
254
+
255
+ .catch(console.error);
256
+ } else {
257
+ (
258
+ PCore.getAttachmentUtils().downloadDocument(ID, context) as Promise<{
259
+ data: string;
260
+ headers: Record<string, string>;
261
+ }>
262
+ )
263
+ .then(content => {
264
+ fileDownloadVar(content, type, name, extension);
265
+ })
266
+
267
+ .catch(console.error);
268
+ }
269
+ };
270
+
271
+ // Prepares new structure as per Cosmos component
272
+ export const transformAttachments = attachments => {
273
+ const transformedFiles = [...attachments];
274
+ let deleteIndex = -1;
275
+ transformedFiles.forEach(attachment => {
276
+ attachment.props.id = attachment.responseProps.ID;
277
+ attachment.props.format = attachment.props.name.split('.').pop();
278
+ if (attachment.props.error) {
279
+ attachment.responseProps.deleteIndex = deleteIndex;
280
+ } else {
281
+ deleteIndex += 1;
282
+ attachment.responseProps.deleteIndex = deleteIndex;
283
+ }
284
+ });
285
+
286
+ return transformedFiles;
287
+ };