@pega/react-sdk-overrides 0.24.4 → 0.25.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 (118) hide show
  1. package/lib/designSystemExtension/AlertBanner/AlertBanner.tsx +1 -1
  2. package/lib/designSystemExtension/Banner/Banner.tsx +1 -1
  3. package/lib/designSystemExtension/CaseSummaryFields/CaseSummaryFields.css +0 -1
  4. package/lib/designSystemExtension/CaseSummaryFields/CaseSummaryFields.tsx +11 -2
  5. package/lib/designSystemExtension/DetailsFields/DetailsFields.tsx +4 -4
  6. package/lib/designSystemExtension/FieldGroup/FieldGroup.tsx +4 -4
  7. package/lib/designSystemExtension/FieldGroupList/FieldGroupList.tsx +4 -4
  8. package/lib/designSystemExtension/FieldValueList/FieldValueList.tsx +6 -6
  9. package/lib/designSystemExtension/Operator/Operator.tsx +6 -5
  10. package/lib/designSystemExtension/Pulse/Pulse.tsx +2 -2
  11. package/lib/designSystemExtension/RichTextEditor/RichTextEditor.tsx +3 -2
  12. package/lib/designSystemExtension/WssQuickCreate/WssQuickCreate.tsx +1 -1
  13. package/lib/field/AutoComplete/AutoComplete.tsx +4 -4
  14. package/lib/field/CancelAlert/CancelAlert.tsx +4 -7
  15. package/lib/field/Checkbox/Checkbox.tsx +4 -4
  16. package/lib/field/Currency/Currency.tsx +10 -7
  17. package/lib/field/Date/Date.tsx +27 -42
  18. package/lib/field/DateTime/DateTime.tsx +39 -36
  19. package/lib/field/Decimal/Decimal.tsx +9 -4
  20. package/lib/field/Dropdown/Dropdown.tsx +29 -22
  21. package/lib/field/Email/Email.tsx +29 -8
  22. package/lib/field/Group/Group.tsx +2 -2
  23. package/lib/field/Integer/Integer.tsx +22 -8
  24. package/lib/field/Multiselect/Multiselect.tsx +8 -14
  25. package/lib/field/Multiselect/utils.ts +1 -1
  26. package/lib/field/Percentage/Percentage.tsx +8 -4
  27. package/lib/field/Phone/Phone.tsx +6 -5
  28. package/lib/field/Phone/config-ext.json +8 -0
  29. package/lib/field/RadioButtons/RadioButtons.tsx +3 -6
  30. package/lib/field/RichText/RichText.tsx +1 -1
  31. package/lib/field/RichText/config-ext.json +10 -0
  32. package/lib/field/ScalarList/ScalarList.tsx +3 -4
  33. package/lib/field/SemanticLink/SemanticLink.tsx +4 -4
  34. package/lib/field/TextArea/TextArea.tsx +26 -8
  35. package/lib/field/TextContent/TextContent.tsx +1 -1
  36. package/lib/field/TextInput/TextInput.tsx +2 -2
  37. package/lib/field/Time/Time.tsx +28 -21
  38. package/lib/field/URL/URL.tsx +26 -7
  39. package/lib/field/UserReference/UserReference.tsx +3 -5
  40. package/lib/helpers/common-utils.ts +24 -1
  41. package/lib/helpers/field-group-utils.ts +2 -2
  42. package/lib/helpers/formatters/Currency.ts +11 -16
  43. package/lib/helpers/formatters/common.ts +2 -1
  44. package/lib/helpers/formatters/index.ts +2 -4
  45. package/lib/helpers/simpleTableHelpers.ts +1 -1
  46. package/lib/infra/ActionButtons/ActionButtons.tsx +3 -3
  47. package/lib/infra/Assignment/Assignment.tsx +12 -12
  48. package/lib/infra/Containers/FlowContainer/FlowContainer.tsx +16 -28
  49. package/lib/infra/Containers/FlowContainer/helpers.ts +1 -5
  50. package/lib/infra/Containers/ModalViewContainer/ListViewActionButtons/ListViewActionButtons.tsx +9 -4
  51. package/lib/infra/Containers/ModalViewContainer/ModalViewContainer.tsx +8 -8
  52. package/lib/infra/Containers/SimpleView/helper.ts +1 -1
  53. package/lib/infra/Containers/ViewContainer/ViewContainer.tsx +1 -1
  54. package/lib/infra/DashboardFilter/DashboardFilter.tsx +4 -6
  55. package/lib/infra/DashboardFilter/filterUtils.tsx +3 -4
  56. package/lib/infra/DeferLoad/DeferLoad.tsx +8 -8
  57. package/lib/infra/MultiStep/MultiStep.tsx +15 -14
  58. package/lib/infra/NavBar/NavBar.css +1 -0
  59. package/lib/infra/NavBar/NavBar.tsx +25 -17
  60. package/lib/infra/RootContainer/RootContainer.tsx +5 -6
  61. package/lib/infra/Stages/Stages.tsx +4 -4
  62. package/lib/infra/VerticalTabs/LeftAlignVerticalTabs/LeftAlignVerticalTabs.tsx +4 -3
  63. package/lib/infra/VerticalTabs/VerticalTabs/VerticalTabs.tsx +2 -2
  64. package/lib/infra/View/View.tsx +37 -3
  65. package/lib/template/AdvancedSearch/AdvancedSearch.tsx +87 -0
  66. package/lib/template/AdvancedSearch/SearchGroup/persistUtils.ts +58 -0
  67. package/lib/template/AdvancedSearch/SearchGroups/SearchGroups.tsx +245 -0
  68. package/lib/template/AdvancedSearch/SearchGroups/hooks.ts +37 -0
  69. package/lib/template/AdvancedSearch/SearchGroups/index.tsx +1 -0
  70. package/lib/template/AdvancedSearch/SearchGroups/utils.ts +29 -0
  71. package/lib/template/AdvancedSearch/TemplateContext.ts +11 -0
  72. package/lib/template/AdvancedSearch/config-ext.json +9 -0
  73. package/lib/template/AdvancedSearch/index.tsx +1 -0
  74. package/lib/template/AppShell/AppShell.tsx +60 -10
  75. package/lib/template/BannerPage/config-ext.json +9 -0
  76. package/lib/template/CaseView/CaseView.tsx +10 -9
  77. package/lib/template/CaseViewActionsMenu/CaseViewActionsMenu.tsx +7 -7
  78. package/lib/template/Confirmation/Confirmation.tsx +3 -2
  79. package/lib/template/DataReference/DataReference.tsx +317 -107
  80. package/lib/template/DataReference/DataReferenceAdvancedSearchContext.js +10 -0
  81. package/lib/template/DataReference/SearchForm.tsx +148 -0
  82. package/lib/template/DataReference/utils.js +90 -0
  83. package/lib/template/DefaultForm/utils/index.ts +1 -3
  84. package/lib/template/Details/Details/Details.tsx +2 -2
  85. package/lib/template/Details/DetailsSubTabs/DetailsSubTabs.tsx +3 -3
  86. package/lib/template/Details/DetailsThreeColumn/DetailsThreeColumn.tsx +2 -2
  87. package/lib/template/Details/DetailsTwoColumn/DetailsTwoColumn.tsx +2 -2
  88. package/lib/template/Details/DynamicTabs/DynamicTabs.tsx +4 -4
  89. package/lib/template/FieldGroupTemplate/FieldGroupTemplate.tsx +10 -5
  90. package/lib/template/InlineDashboard/InlineDashboard.tsx +2 -2
  91. package/lib/template/InlineDashboardPage/config-ext.json +9 -0
  92. package/lib/template/ListView/ListView.tsx +200 -94
  93. package/lib/template/ListView/utils.ts +38 -6
  94. package/lib/template/NarrowWide/NarrowWideDetails/NarrowWideDetails.tsx +2 -2
  95. package/lib/template/OneColumn/OneColumn/OneColumn.tsx +2 -2
  96. package/lib/template/PromotedFilters/PromotedFilters.tsx +1 -2
  97. package/lib/template/SimpleTable/SimpleTable/SimpleTable.tsx +0 -2
  98. package/lib/template/SimpleTable/SimpleTableManual/SimpleTableManual.tsx +110 -86
  99. package/lib/template/SimpleTable/SimpleTableSelect/SimpleTableSelect.tsx +2 -4
  100. package/lib/template/SubTabs/SubTabs.tsx +2 -2
  101. package/lib/template/SubTabs/tabUtils.ts +118 -1
  102. package/lib/template/TwoColumn/TwoColumn/TwoColumn.tsx +2 -2
  103. package/lib/template/TwoColumn/TwoColumnTab/TwoColumnTab.tsx +2 -2
  104. package/lib/template/WideNarrow/WideNarrowDetails/WideNarrowDetails.tsx +2 -2
  105. package/lib/template/WssNavBar/WssNavBar.tsx +9 -9
  106. package/lib/widget/AppAnnouncement/AppAnnouncement.tsx +2 -2
  107. package/lib/widget/Attachment/Attachment.css +1 -0
  108. package/lib/widget/Attachment/Attachment.tsx +7 -9
  109. package/lib/widget/CaseHistory/CaseHistory.tsx +12 -10
  110. package/lib/widget/FileUtility/ActionButtonsForFileUtil/ActionButtonsForFileUtil.tsx +1 -1
  111. package/lib/widget/FileUtility/FileUtility/FileUtility.tsx +5 -4
  112. package/lib/widget/Followers/Followers.tsx +2 -2
  113. package/lib/widget/QuickCreate/QuickCreate.tsx +0 -1
  114. package/lib/widget/QuickCreate/config-ext.json +9 -0
  115. package/lib/widget/SummaryItem/SummaryItem.tsx +4 -3
  116. package/lib/widget/ToDo/ToDo.tsx +92 -22
  117. package/package.json +1 -1
  118. /package/lib/infra/Containers/{helpers.ts → container-helpers.ts} +0 -0
@@ -0,0 +1,245 @@
1
+ import React, { createElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { Button, Grid, Select, MenuItem, Box } from '@mui/material';
3
+
4
+ import createPConnectComponent from '@pega/react-sdk-components/lib/bridge/react_pconnect';
5
+ import TemplateContext from '@pega/react-sdk-components/lib/components/template/AdvancedSearch/TemplateContext';
6
+ import componentCachePersistUtils from '@pega/react-sdk-components/lib/components/template/AdvancedSearch/SearchGroup/persistUtils';
7
+
8
+ import { getCacheInfo, isValidInput } from './utils';
9
+ import { useCacheWhenListViewReady } from './hooks';
10
+
11
+ export const initializeSearchFields = (searchFields, getPConnect, referenceListClassID, searchFieldRestoreValues = {}) => {
12
+ const filtersProperties = {};
13
+ searchFields.forEach(field => {
14
+ let val = '';
15
+ const { value, defaultValue = '' } = field.config;
16
+ const propPath = PCore.getAnnotationUtils().getPropertyName(value);
17
+
18
+ if (searchFieldRestoreValues[propPath]) {
19
+ val = searchFieldRestoreValues[propPath];
20
+ } else if (PCore.getAnnotationUtils().isProperty(defaultValue)) {
21
+ val = getPConnect().getValue(defaultValue.split(' ')[1]);
22
+ } else if (defaultValue.startsWith('@L')) {
23
+ val = defaultValue.split(' ')[1];
24
+ } else {
25
+ val = defaultValue;
26
+ }
27
+
28
+ filtersProperties[propPath] = val;
29
+
30
+ const valueSplit = value.split('@P ')[1]?.split('.').filter(Boolean) ?? [];
31
+ valueSplit.pop();
32
+
33
+ if (valueSplit.length) {
34
+ let path = '';
35
+ let currentClassID = referenceListClassID;
36
+ valueSplit.forEach(item => {
37
+ path = path.length ? `${path}.${item}` : item;
38
+ currentClassID = (PCore.getMetadataUtils().getPropertyMetadata(item, currentClassID) as any).pageClass;
39
+ if (currentClassID) {
40
+ filtersProperties[`${path}.classID`] = currentClassID;
41
+ }
42
+ });
43
+ }
44
+ });
45
+ return filtersProperties;
46
+ };
47
+
48
+ const flattenObj = obj => {
49
+ const result = {};
50
+ Object.keys(obj).forEach(key => {
51
+ if (!['context_data', 'pageInstructions'].includes(key)) {
52
+ if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
53
+ const temp = flattenObj(obj[key]);
54
+ Object.keys(temp).forEach(nestedKey => {
55
+ result[`${key}.${nestedKey}`] = temp[nestedKey];
56
+ });
57
+ } else {
58
+ result[key] = obj[key];
59
+ }
60
+ }
61
+ });
62
+ return result;
63
+ };
64
+
65
+ export default function SearchGroups(props) {
66
+ const localizedVal = PCore.getLocaleUtils().getLocaleValue;
67
+ const { getPConnect, editableField, localeReference, setShowRecords, searchSelectCacheKey, cache } = props;
68
+ const referenceFieldName = editableField.replaceAll('.', '_');
69
+
70
+ const state: any = useRef({ searchFields: {}, activeGroupId: '' }).current;
71
+ const options = componentCachePersistUtils.getComponentStateOptions(getPConnect);
72
+
73
+ const { searchGroups: groups, referenceList } = getPConnect().getConfigProps();
74
+ const { useCache, initialActiveGroupId } = getCacheInfo(cache, groups);
75
+ const [activeGroupId, setActiveGroupId] = useState(initialActiveGroupId);
76
+ const [transientItemID, setTransientItemID] = useState<any>(null);
77
+ const [previousFormValues, setPreviousFormValues] = useState<any>(null);
78
+ const viewName = getPConnect().getCurrentView();
79
+
80
+ const rawGroupsConfig = getPConnect().getRawConfigProps().searchGroups;
81
+ const activeGroupIndex = groups.findIndex(group => group.config.id === activeGroupId);
82
+ const { children: searchFieldsChildren = [] } = activeGroupIndex !== -1 ? rawGroupsConfig[activeGroupIndex] : {};
83
+ const searchFields = searchFieldsChildren.map(field => ({
84
+ ...field,
85
+ config: { ...field.config, isSearchField: true }
86
+ }));
87
+
88
+ const searchByRef = useRef(null);
89
+ const searchFieldsRef = useRef(null);
90
+ const isValidatorField = searchFields.some(field => field.config.validator);
91
+ const { classID: referenceListClassID } = PCore.getMetadataUtils().getDataPageMetadata(referenceList) as any;
92
+
93
+ const initialSearchFields = useMemo(
94
+ () =>
95
+ initializeSearchFields(
96
+ searchFields,
97
+ getPConnect,
98
+ referenceListClassID,
99
+ useCache && cache.activeGroupId === activeGroupId ? cache.searchFields : {}
100
+ ),
101
+ [activeGroupId, getPConnect, cache.searchFields]
102
+ );
103
+
104
+ useEffect(() => {
105
+ if (transientItemID) {
106
+ const filtersWithClassID = {
107
+ ...initialSearchFields,
108
+ classID: referenceListClassID
109
+ };
110
+ // @ts-ignore
111
+ PCore.getContainerUtils().replaceTransientData({ transientItemID, data: filtersWithClassID });
112
+ }
113
+ }, [activeGroupId]);
114
+
115
+ useEffect(() => {
116
+ const filtersWithClassID = {
117
+ ...initialSearchFields,
118
+ classID: referenceListClassID
119
+ };
120
+
121
+ const transientId = getPConnect()
122
+ .getContainerManager()
123
+ .addTransientItem({ id: `${referenceFieldName}-${viewName}`, data: filtersWithClassID });
124
+ setTransientItemID(transientId);
125
+ }, []);
126
+
127
+ const getFilterData = useCallback(() => {
128
+ // @ts-ignore
129
+ let changes = PCore.getFormUtils().getSubmitData(transientItemID, {
130
+ isTransientContext: true,
131
+ includeDisabledFields: true
132
+ });
133
+
134
+ if (Object.keys(cache.searchFields ?? {}).length > 0 && Object.keys(changes).length === 1) {
135
+ changes = cache.searchFields;
136
+ }
137
+
138
+ const formValues = flattenObj(changes);
139
+
140
+ if (!PCore.isDeepEqual(previousFormValues, formValues) && PCore.getFormUtils().isFormValid(transientItemID) && isValidInput(formValues)) {
141
+ if (isValidatorField) {
142
+ // @ts-ignore
143
+ PCore.getMessageManager().clearContextMessages({ context: transientItemID });
144
+ }
145
+ setPreviousFormValues(formValues);
146
+ setShowRecords(true);
147
+ PCore.getPubSubUtils().publish(PCore.getEvents().getTransientEvent().UPDATE_PROMOTED_FILTERS, {
148
+ payload: formValues,
149
+ showRecords: true,
150
+ viewName
151
+ });
152
+ }
153
+
154
+ state.activeGroupId = activeGroupId;
155
+ state.searchFields = changes;
156
+ state.selectedCategory = viewName;
157
+
158
+ componentCachePersistUtils.setComponentCache({ cacheKey: searchSelectCacheKey, state, options });
159
+ }, [transientItemID, setShowRecords, viewName, activeGroupId, previousFormValues]);
160
+
161
+ const resetFilterData = useCallback(() => {
162
+ // @ts-ignore
163
+ PCore.getNavigationUtils().resetComponentCache(searchSelectCacheKey);
164
+ const resetPayload = {
165
+ transientItemID,
166
+ data: initializeSearchFields(searchFields, getPConnect, referenceListClassID),
167
+ options: { reset: true }
168
+ };
169
+ // @ts-ignore
170
+ PCore.getContainerUtils().updateTransientData(resetPayload);
171
+ }, [transientItemID, initialSearchFields]);
172
+
173
+ useCacheWhenListViewReady(cache, viewName, useCache, getFilterData, searchSelectCacheKey);
174
+
175
+ const searchDropdown = groups.length > 1 && (
176
+ <Grid container spacing={2}>
177
+ <Select value={activeGroupId} onChange={e => setActiveGroupId(e.target.value)} ref={searchByRef} fullWidth>
178
+ {groups.map(group => (
179
+ <MenuItem key={group.config.id} value={group.config.id}>
180
+ {group.config.label}
181
+ </MenuItem>
182
+ ))}
183
+ </Select>
184
+ </Grid>
185
+ );
186
+
187
+ const actionButtons = (
188
+ <Box display='flex' gap={2}>
189
+ <Button variant='outlined' onClick={resetFilterData}>
190
+ {localizedVal('Reset', 'SimpleTable')}
191
+ </Button>
192
+ <Button variant='contained' onClick={getFilterData}>
193
+ {localizedVal('Search', 'SimpleTable')}
194
+ </Button>
195
+ </Box>
196
+ );
197
+
198
+ const searchFieldsViewConfig = {
199
+ name: 'SearchFields',
200
+ type: 'View',
201
+ config: {
202
+ template: 'DefaultForm',
203
+ NumCols: '3',
204
+ contextName: transientItemID,
205
+ readOnly: false,
206
+ context: transientItemID,
207
+ localeReference
208
+ },
209
+ children: [
210
+ {
211
+ name: 'Fields',
212
+ type: 'Region',
213
+ children: searchFields
214
+ }
215
+ ]
216
+ };
217
+
218
+ const searchFieldsC11nEnv = PCore.createPConnect({
219
+ meta: searchFieldsViewConfig,
220
+ options: {
221
+ hasForm: true,
222
+ contextName: transientItemID
223
+ }
224
+ });
225
+
226
+ const templateContext = useContext(TemplateContext);
227
+ const templateContextValue = useMemo(() => ({ ...templateContext, outerColumnCount: undefined }), []);
228
+
229
+ const searchFieldsViewComp = transientItemID ? (
230
+ <TemplateContext.Provider value={templateContextValue}>
231
+ <div ref={searchFieldsRef}>{createElement(createPConnectComponent(), { ...searchFieldsC11nEnv })}</div>
232
+ </TemplateContext.Provider>
233
+ ) : null;
234
+
235
+ const childrenToRender = [searchDropdown, searchFieldsViewComp, actionButtons];
236
+
237
+ return (
238
+ <Box display='flex' flexDirection='column' gap={2}>
239
+ {childrenToRender.map((child, index) => (
240
+ // eslint-disable-next-line react/no-array-index-key
241
+ <React.Fragment key={index}>{child}</React.Fragment>
242
+ ))}
243
+ </Box>
244
+ );
245
+ }
@@ -0,0 +1,37 @@
1
+ import { useEffect } from 'react';
2
+
3
+ const listViewConstants = {
4
+ EVENTS: {
5
+ LIST_VIEW_READY: 'LIST_VIEW_READY'
6
+ }
7
+ };
8
+ /**
9
+ * This hook registers a callback for the whenever list view component is ready
10
+ * then makes a call to get the data using the search fields pre-filled with cache data.
11
+ */
12
+ // eslint-disable-next-line import/prefer-default-export
13
+ export function useCacheWhenListViewReady(
14
+ cache: { searchFields: unknown },
15
+ viewName: string,
16
+ useCache: boolean,
17
+ getFilterData: (params: { isCalledFromCache: boolean }) => void,
18
+ searchSelectCacheKey: string
19
+ ) {
20
+ useEffect(() => {
21
+ if (Object.keys(cache.searchFields ?? {}).length > 0) {
22
+ PCore.getPubSubUtils().subscribe(
23
+ listViewConstants.EVENTS.LIST_VIEW_READY,
24
+ ({ viewName: viewNameFromListView }: { viewName: string }) => {
25
+ if (viewNameFromListView === viewName && useCache) {
26
+ getFilterData({ isCalledFromCache: true });
27
+ }
28
+ },
29
+ `${searchSelectCacheKey}-listview-ready`
30
+ );
31
+ }
32
+
33
+ return () => {
34
+ PCore.getPubSubUtils().unsubscribe(listViewConstants.EVENTS.LIST_VIEW_READY, `${searchSelectCacheKey}-listview-ready`);
35
+ };
36
+ }, []);
37
+ }
@@ -0,0 +1 @@
1
+ export { default } from './SearchGroups';
@@ -0,0 +1,29 @@
1
+ function isEmpty(value: any): boolean {
2
+ return (
3
+ // null or undefined
4
+ value === null ||
5
+ value === undefined ||
6
+ ((Array.isArray(value) || typeof value === 'string') && value.length === 0) ||
7
+ // is an Object and has no keys
8
+ (value.constructor === Object && Object.keys(value).length === 0)
9
+ );
10
+ }
11
+
12
+ export function getCacheInfo(
13
+ cache: { selectedCategory: string; activeGroupId: string; searchFields: unknown },
14
+ groups: { config: { id: string } }[]
15
+ ) {
16
+ let initialActiveGroupId = groups.length ? groups[0].config.id : '';
17
+
18
+ let useCache = false;
19
+ if (cache.activeGroupId && groups?.find(group => group.config.id === cache.activeGroupId)) {
20
+ initialActiveGroupId = cache.activeGroupId;
21
+ useCache = true;
22
+ }
23
+
24
+ return { useCache, initialActiveGroupId };
25
+ }
26
+
27
+ export function isValidInput(input: { [s: string]: unknown }) {
28
+ return Object.values(input).some(value => !isEmpty(value));
29
+ }
@@ -0,0 +1,11 @@
1
+ import { createContext } from 'react';
2
+
3
+ const TemplateContext = createContext({
4
+ depth: 0,
5
+ columnCount: 1,
6
+ outerColumnCount: undefined,
7
+ templateOverrideMode: undefined,
8
+ inheritParentLayout: undefined,
9
+ lastContainerItem: undefined
10
+ });
11
+ export default TemplateContext;
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "AdvancedSearch",
3
+ "label": "Advanced search",
4
+ "type": "Template",
5
+ "icon": "AdvancedSearch.svg",
6
+ "subtype": "SEARCH",
7
+ "hideTemplateEdit": true,
8
+ "properties": []
9
+ }
@@ -0,0 +1 @@
1
+ export { default } from './AdvancedSearch';
@@ -1,6 +1,6 @@
1
1
  import { PropsWithChildren, useEffect, useState } from 'react';
2
- import { makeStyles } from '@material-ui/core/styles';
3
- import Avatar from '@material-ui/core/Avatar';
2
+ import makeStyles from '@mui/styles/makeStyles';
3
+ import Avatar from '@mui/material/Avatar';
4
4
 
5
5
  import { Utils } from '@pega/react-sdk-components/lib/components/helpers/utils';
6
6
  import { NavContext } from '@pega/react-sdk-components/lib/components/helpers/reactContextHelpers';
@@ -24,6 +24,8 @@ interface AppShellProps extends PConnProps {
24
24
  portalName: string;
25
25
  portalLogo: string;
26
26
  navDisplayOptions: { alignment: string; position: string };
27
+ httpMessages: string[];
28
+ pageMessages: string[];
27
29
  }
28
30
 
29
31
  const useStyles = makeStyles(theme => ({
@@ -49,8 +51,21 @@ export default function AppShell(props: PropsWithChildren<AppShellProps>) {
49
51
  // Get emitted components from map (so we can get any override that may exist)
50
52
  const NavBar = getComponentFromMap('NavBar');
51
53
  const WssNavBar = getComponentFromMap('WssNavBar');
54
+ const AlertBanner = getComponentFromMap('AlertBanner');
52
55
 
53
- const { pages = [], caseTypes = [], showAppName, children = [], getPConnect, portalTemplate, portalName, portalLogo, navDisplayOptions } = props;
56
+ const {
57
+ pages = [],
58
+ caseTypes = [],
59
+ showAppName,
60
+ children = [],
61
+ getPConnect,
62
+ httpMessages = [],
63
+ pageMessages = [],
64
+ portalTemplate,
65
+ portalName,
66
+ portalLogo,
67
+ navDisplayOptions
68
+ } = props;
54
69
 
55
70
  const [open, setOpen] = useState(true);
56
71
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -58,7 +73,7 @@ export default function AppShell(props: PropsWithChildren<AppShellProps>) {
58
73
  const pConn = getPConnect();
59
74
  const envInfo = PCore.getEnvironmentInfo();
60
75
  const imageKey = envInfo.getOperatorImageInsKey();
61
- const userName = envInfo.getOperatorName();
76
+ const userName = envInfo.getOperatorName() || '';
62
77
  const currentUserInitials = Utils.getInitials(userName);
63
78
  const appNameToDisplay = showAppName ? envInfo.getApplicationLabel() : '';
64
79
  const portalClass = pConn.getValue('.classID', ''); // 2nd arg empty string until typedef marked correctly
@@ -75,9 +90,18 @@ export default function AppShell(props: PropsWithChildren<AppShellProps>) {
75
90
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
76
91
  const [mapChildren, setMapChildren] = useState([]);
77
92
 
93
+ const messages = [...httpMessages, ...pageMessages];
94
+
95
+ const hasBanner = messages && messages.length ? messages.length > 0 : false;
96
+ let banners: any = null;
97
+ banners = hasBanner && (
98
+ <div style={{ display: 'flex', flexDirection: 'column', padding: '1em 0' }}>
99
+ <AlertBanner id='AppShell' variant='urgent' messages={messages} />
100
+ </div>
101
+ );
78
102
  // Initial setting of appName and mapChildren
79
103
  useEffect(() => {
80
- setAppName(PCore.getEnvironmentInfo().getApplicationName());
104
+ setAppName(PCore.getEnvironmentInfo().getApplicationName() || '');
81
105
 
82
106
  const tempMap: any = (pConn.getChildren() as any)?.map((child: any, index) => {
83
107
  const theChildComp = child.getPConnect().getComponentName();
@@ -92,6 +116,25 @@ export default function AppShell(props: PropsWithChildren<AppShellProps>) {
92
116
  setMapChildren(tempMap);
93
117
  }, []);
94
118
 
119
+ useEffect(() => {
120
+ // @ts-ignore
121
+ const caseTypesAvailableToCreateDP = PCore.getEnvironmentInfo().environmentInfoObject?.pxApplication?.pyCaseTypesAvailableToCreateDP;
122
+ if (caseTypesAvailableToCreateDP) {
123
+ const portalID = pConn.getValue('.pyOwner');
124
+ PCore.getDataPageUtils()
125
+ .getPageDataAsync(caseTypesAvailableToCreateDP, pConn.getContextName(), {
126
+ PortalName: portalID
127
+ })
128
+ .then((response: { pyCaseTypesAvailableToCreate?: any }) => {
129
+ if (response?.pyCaseTypesAvailableToCreate) {
130
+ pConn.replaceState('.pyCaseTypesAvailableToCreate', response.pyCaseTypesAvailableToCreate, {
131
+ skipDirtyValidation: true
132
+ });
133
+ }
134
+ });
135
+ }
136
+ }, []);
137
+
95
138
  const [iconURL, setIconURL] = useState('');
96
139
  const [fullIconURL, setFullIconURL] = useState('');
97
140
  useEffect(() => {
@@ -175,10 +218,10 @@ export default function AppShell(props: PropsWithChildren<AppShellProps>) {
175
218
  portalName={portalName}
176
219
  imageSrc={iconURL}
177
220
  fullImageSrc={fullIconURL}
178
- appName={localizedVal(appNameToDisplay, '', `${portalClass}!PORTAL!${envPortalName}`.toUpperCase())}
221
+ appName={localizedVal(appNameToDisplay || '', '', `${portalClass}!PORTAL!${envPortalName}`.toUpperCase())}
179
222
  appInfo={{
180
223
  imageSrc: iconURL,
181
- appName: localizedVal(appNameToDisplay, '', `${portalClass}!PORTAL!${envPortalName}`.toUpperCase()),
224
+ appName: localizedVal(appNameToDisplay || '', '', `${portalClass}!PORTAL!${envPortalName}`.toUpperCase()),
182
225
  onClick: links[0] && /* links[0].onClick ? */ links[0].onClick /* : undefined */
183
226
  }}
184
227
  navLinks={links.filter((link, index) => {
@@ -187,7 +230,10 @@ export default function AppShell(props: PropsWithChildren<AppShellProps>) {
187
230
  operator={getOperator()}
188
231
  navDisplayOptions={navDisplayOptions}
189
232
  />
190
- <div className={classes.wsscontent}>{children}</div>
233
+ <div className={classes.wsscontent}>
234
+ {banners}
235
+ {children}
236
+ </div>
191
237
  </div>
192
238
  );
193
239
  }
@@ -199,11 +245,15 @@ export default function AppShell(props: PropsWithChildren<AppShellProps>) {
199
245
  <NavBar
200
246
  getPConnect={getPConnect}
201
247
  pConn={getPConnect()}
202
- appName={localizedVal(appNameToDisplay, '', `${portalClass}!PORTAL!${envPortalName}`.toUpperCase())}
248
+ appName={localizedVal(appNameToDisplay || '', '', `${portalClass}!PORTAL!${envPortalName}`.toUpperCase())}
203
249
  pages={pages}
204
250
  caseTypes={caseTypes}
205
251
  />
206
- <div className={classes.content}>{children}</div>
252
+
253
+ <div className={classes.content}>
254
+ {banners}
255
+ {children}
256
+ </div>
207
257
  </div>
208
258
  </NavContext.Provider>
209
259
  );
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "BannerPage",
3
+ "label": "Banner",
4
+ "description": "Banner Page Template",
5
+ "type": "Template",
6
+ "subtype": "PAGE",
7
+ "icon": "BannerPage.svg",
8
+ "properties": []
9
+ }
@@ -1,11 +1,11 @@
1
1
  /* eslint-disable react/jsx-boolean-value */
2
2
 
3
3
  import { PropsWithChildren, ReactElement, useContext, useEffect, useState } from 'react';
4
- import { Avatar, Card, CardHeader, Divider, Typography } from '@material-ui/core';
5
- import { makeStyles } from '@material-ui/core/styles';
6
- import Box from '@material-ui/core/Box';
7
- import Button from '@material-ui/core/Button';
8
- import Grid from '@material-ui/core/Grid';
4
+ import { Avatar, Card, CardHeader, Divider, Typography } from '@mui/material';
5
+ import makeStyles from '@mui/styles/makeStyles';
6
+ import Box from '@mui/material/Box';
7
+ import Button from '@mui/material/Button';
8
+ import Grid from '@mui/material/Grid';
9
9
 
10
10
  import { Utils } from '@pega/react-sdk-components/lib/components/helpers/utils';
11
11
  import StoreContext from '@pega/react-sdk-components/lib/bridge/Context/StoreContext';
@@ -19,6 +19,7 @@ interface CaseViewProps extends PConnProps {
19
19
  header: string;
20
20
  showIconInHeader: boolean;
21
21
  caseInfo: any;
22
+ lastUpdateCaseTime: any;
22
23
  }
23
24
 
24
25
  const useStyles = makeStyles(theme => ({
@@ -64,10 +65,10 @@ export default function CaseView(props: PropsWithChildren<CaseViewProps>) {
64
65
  showIconInHeader = true,
65
66
  caseInfo: { availableActions = [], availableProcesses = [], hasNewAttachments, caseTypeID = '', caseTypeName = '' }
66
67
  } = props;
68
+ const { lastUpdateCaseTime = getPConnect().getValue('caseInfo.lastUpdateTime') } = props;
67
69
 
68
70
  const currentCaseID = props.caseInfo.ID;
69
71
  let isComponentMounted = true;
70
-
71
72
  const { displayOnlyFA } = useContext<any>(StoreContext);
72
73
 
73
74
  const thePConn = getPConnect();
@@ -79,7 +80,6 @@ export default function CaseView(props: PropsWithChildren<CaseViewProps>) {
79
80
  const localizedVal = PCore.getLocaleUtils().getLocaleValue;
80
81
  const localeCategory = 'CaseView';
81
82
  const localeKey = `${caseTypeID}!CASE!${caseTypeName}`.toUpperCase();
82
-
83
83
  /**
84
84
  *
85
85
  * @param inName the metadata <em>name</em> that will cause a region to be returned
@@ -169,7 +169,6 @@ export default function CaseView(props: PropsWithChildren<CaseViewProps>) {
169
169
 
170
170
  useEffect(() => {
171
171
  if (hasNewAttachments) {
172
- // @ts-ignore - Argument of type 'boolean' is not assignable to parameter of type 'object'
173
172
  PCore.getPubSubUtils().publish((PCore.getEvents().getCaseEvent() as any).CASE_ATTACHMENTS_UPDATED_FROM_CASEVIEW, true);
174
173
  }
175
174
  }, [hasNewAttachments]);
@@ -244,7 +243,9 @@ export default function CaseView(props: PropsWithChildren<CaseViewProps>) {
244
243
  <Grid item xs={6}>
245
244
  {theStagesRegion}
246
245
  {theTodoRegion}
247
- {deferLoadInfo.length > 0 && <DeferLoad getPConnect={getPConnect} name={deferLoadInfo[activeVertTab].config.name} isTab />}
246
+ {deferLoadInfo.length > 0 && (
247
+ <DeferLoad getPConnect={getPConnect} name={deferLoadInfo[activeVertTab].config.name} isTab lastUpdateCaseTime={lastUpdateCaseTime} />
248
+ )}
248
249
  </Grid>
249
250
 
250
251
  <Grid item xs={3}>
@@ -1,12 +1,12 @@
1
1
  import React, { useState } from 'react';
2
- import Button from '@material-ui/core/Button';
3
- import Menu from '@material-ui/core/Menu';
4
- import MenuItem from '@material-ui/core/MenuItem';
2
+ import Button from '@mui/material/Button';
3
+ import Menu from '@mui/material/Menu';
4
+ import MenuItem from '@mui/material/MenuItem';
5
5
 
6
6
  import { PConnProps } from '@pega/react-sdk-components/lib/types/PConnProps';
7
- import Snackbar from '@material-ui/core/Snackbar';
8
- import IconButton from '@material-ui/core/IconButton';
9
- import CloseIcon from '@material-ui/icons/Close';
7
+ import Snackbar from '@mui/material/Snackbar';
8
+ import IconButton from '@mui/material/IconButton';
9
+ import CloseIcon from '@mui/icons-material/Close';
10
10
 
11
11
  interface CaseViewActionsMenuProps extends PConnProps {
12
12
  // If any, enter additional props that only exist on this component
@@ -44,7 +44,7 @@ export default function CaseViewActionsMenu(props: CaseViewActionsMenuProps) {
44
44
  setShowSnackbar(true);
45
45
  }
46
46
 
47
- function handleSnackbarClose(event: React.SyntheticEvent | React.MouseEvent, reason?: string) {
47
+ function handleSnackbarClose(event: React.SyntheticEvent<any> | Event, reason?: string) {
48
48
  if (reason === 'clickaway') {
49
49
  return;
50
50
  }
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable no-nested-ternary */
2
2
  import { PropsWithChildren, useState } from 'react';
3
- import { Button, Card, makeStyles } from '@material-ui/core';
3
+ import { Button, Card } from '@mui/material';
4
+ import makeStyles from '@mui/styles/makeStyles';
4
5
 
5
6
  import { getToDoAssignments } from '@pega/react-sdk-components/lib/components/infra/Containers/FlowContainer/helpers';
6
7
  import { getComponentFromMap } from '@pega/react-sdk-components/lib/bridge/helpers/sdk_component_map';
@@ -40,7 +41,7 @@ export default function Confirmation(props: PropsWithChildren<ConfirmationProps>
40
41
  // Not using whatsNext at the moment, need to figure out the use of it
41
42
  // const whatsNext = datasource?.source;
42
43
  // const items = whatsNext.length > 0 ? whatsNext.map(item => item.label) : '';
43
- const activeContainerItemID = PCore.getContainerUtils().getActiveContainerItemName(getPConnect().getTarget());
44
+ const activeContainerItemID = PCore.getContainerUtils().getActiveContainerItemName(getPConnect().getTarget() || '');
44
45
  const rootInfo = PCore.getContainerUtils().getContainerItemData(getPConnect().getTarget(), activeContainerItemID);
45
46
  const onConfirmViewClose = () => {
46
47
  setShowConfirmView(false);