@pega/react-sdk-overrides 0.24.3 → 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.
- package/lib/designSystemExtension/AlertBanner/AlertBanner.tsx +1 -1
- package/lib/designSystemExtension/Banner/Banner.tsx +1 -1
- package/lib/designSystemExtension/CaseSummaryFields/CaseSummaryFields.css +0 -1
- package/lib/designSystemExtension/CaseSummaryFields/CaseSummaryFields.tsx +11 -2
- package/lib/designSystemExtension/DetailsFields/DetailsFields.tsx +4 -4
- package/lib/designSystemExtension/FieldGroup/FieldGroup.tsx +4 -4
- package/lib/designSystemExtension/FieldGroupList/FieldGroupList.tsx +4 -4
- package/lib/designSystemExtension/FieldValueList/FieldValueList.tsx +6 -6
- package/lib/designSystemExtension/Operator/Operator.tsx +6 -5
- package/lib/designSystemExtension/Pulse/Pulse.tsx +2 -2
- package/lib/designSystemExtension/RichTextEditor/RichTextEditor.tsx +3 -2
- package/lib/designSystemExtension/WssQuickCreate/WssQuickCreate.tsx +1 -1
- package/lib/field/AutoComplete/AutoComplete.tsx +4 -4
- package/lib/field/CancelAlert/CancelAlert.tsx +4 -7
- package/lib/field/Checkbox/Checkbox.tsx +4 -4
- package/lib/field/Currency/Currency.tsx +10 -7
- package/lib/field/Date/Date.tsx +27 -42
- package/lib/field/DateTime/DateTime.tsx +39 -36
- package/lib/field/Decimal/Decimal.tsx +9 -4
- package/lib/field/Dropdown/Dropdown.tsx +29 -22
- package/lib/field/Email/Email.tsx +29 -8
- package/lib/field/Group/Group.tsx +2 -2
- package/lib/field/Integer/Integer.tsx +22 -8
- package/lib/field/Multiselect/Multiselect.tsx +8 -14
- package/lib/field/Multiselect/utils.ts +1 -1
- package/lib/field/Percentage/Percentage.tsx +8 -4
- package/lib/field/Phone/Phone.tsx +22 -12
- package/lib/field/Phone/config-ext.json +8 -0
- package/lib/field/RadioButtons/RadioButtons.tsx +3 -6
- package/lib/field/RichText/RichText.tsx +1 -1
- package/lib/field/RichText/config-ext.json +10 -0
- package/lib/field/ScalarList/ScalarList.tsx +3 -4
- package/lib/field/SemanticLink/SemanticLink.tsx +4 -4
- package/lib/field/TextArea/TextArea.tsx +26 -8
- package/lib/field/TextContent/TextContent.tsx +1 -1
- package/lib/field/TextInput/TextInput.tsx +2 -2
- package/lib/field/Time/Time.tsx +28 -21
- package/lib/field/URL/URL.tsx +26 -7
- package/lib/field/UserReference/UserReference.tsx +3 -5
- package/lib/helpers/common-utils.ts +24 -1
- package/lib/helpers/field-group-utils.ts +2 -2
- package/lib/helpers/formatters/Currency.ts +11 -16
- package/lib/helpers/formatters/common.ts +2 -1
- package/lib/helpers/formatters/index.ts +2 -4
- package/lib/helpers/simpleTableHelpers.ts +1 -1
- package/lib/infra/ActionButtons/ActionButtons.tsx +3 -3
- package/lib/infra/Assignment/Assignment.tsx +38 -12
- package/lib/infra/Containers/FlowContainer/FlowContainer.tsx +16 -28
- package/lib/infra/Containers/FlowContainer/helpers.ts +1 -5
- package/lib/infra/Containers/ModalViewContainer/ListViewActionButtons/ListViewActionButtons.tsx +9 -4
- package/lib/infra/Containers/ModalViewContainer/ModalViewContainer.tsx +8 -8
- package/lib/infra/Containers/SimpleView/helper.ts +1 -1
- package/lib/infra/Containers/ViewContainer/ViewContainer.tsx +1 -1
- package/lib/infra/DashboardFilter/DashboardFilter.tsx +4 -6
- package/lib/infra/DashboardFilter/filterUtils.tsx +3 -4
- package/lib/infra/DeferLoad/DeferLoad.tsx +8 -8
- package/lib/infra/MultiStep/MultiStep.tsx +15 -14
- package/lib/infra/NavBar/NavBar.css +1 -0
- package/lib/infra/NavBar/NavBar.tsx +25 -17
- package/lib/infra/RootContainer/RootContainer.tsx +5 -6
- package/lib/infra/Stages/Stages.tsx +4 -4
- package/lib/infra/VerticalTabs/LeftAlignVerticalTabs/LeftAlignVerticalTabs.tsx +4 -3
- package/lib/infra/VerticalTabs/VerticalTabs/VerticalTabs.tsx +2 -2
- package/lib/infra/View/View.tsx +37 -3
- package/lib/template/AdvancedSearch/AdvancedSearch.tsx +87 -0
- package/lib/template/AdvancedSearch/SearchGroup/persistUtils.ts +58 -0
- package/lib/template/AdvancedSearch/SearchGroups/SearchGroups.tsx +245 -0
- package/lib/template/AdvancedSearch/SearchGroups/hooks.ts +37 -0
- package/lib/template/AdvancedSearch/SearchGroups/index.tsx +1 -0
- package/lib/template/AdvancedSearch/SearchGroups/utils.ts +29 -0
- package/lib/template/AdvancedSearch/TemplateContext.ts +11 -0
- package/lib/template/AdvancedSearch/config-ext.json +9 -0
- package/lib/template/AdvancedSearch/index.tsx +1 -0
- package/lib/template/AppShell/AppShell.tsx +60 -10
- package/lib/template/BannerPage/config-ext.json +9 -0
- package/lib/template/CaseView/CaseView.tsx +10 -9
- package/lib/template/CaseViewActionsMenu/CaseViewActionsMenu.tsx +7 -7
- package/lib/template/Confirmation/Confirmation.tsx +3 -2
- package/lib/template/DataReference/DataReference.tsx +317 -107
- package/lib/template/DataReference/DataReferenceAdvancedSearchContext.js +10 -0
- package/lib/template/DataReference/SearchForm.tsx +148 -0
- package/lib/template/DataReference/utils.js +90 -0
- package/lib/template/DefaultForm/utils/index.ts +1 -3
- package/lib/template/Details/Details/Details.tsx +2 -2
- package/lib/template/Details/DetailsSubTabs/DetailsSubTabs.tsx +3 -3
- package/lib/template/Details/DetailsThreeColumn/DetailsThreeColumn.tsx +2 -2
- package/lib/template/Details/DetailsTwoColumn/DetailsTwoColumn.tsx +2 -2
- package/lib/template/Details/DynamicTabs/DynamicTabs.tsx +4 -4
- package/lib/template/FieldGroupTemplate/FieldGroupTemplate.tsx +10 -5
- package/lib/template/InlineDashboard/InlineDashboard.tsx +2 -2
- package/lib/template/InlineDashboardPage/config-ext.json +9 -0
- package/lib/template/ListView/ListView.tsx +216 -123
- package/lib/template/ListView/utils.ts +38 -6
- package/lib/template/NarrowWide/NarrowWideDetails/NarrowWideDetails.tsx +2 -2
- package/lib/template/OneColumn/OneColumn/OneColumn.tsx +2 -2
- package/lib/template/PromotedFilters/PromotedFilters.tsx +1 -2
- package/lib/template/SimpleTable/SimpleTable/SimpleTable.tsx +0 -2
- package/lib/template/SimpleTable/SimpleTableManual/SimpleTableManual.tsx +110 -86
- package/lib/template/SimpleTable/SimpleTableSelect/SimpleTableSelect.tsx +2 -4
- package/lib/template/SubTabs/SubTabs.tsx +2 -2
- package/lib/template/SubTabs/tabUtils.ts +118 -1
- package/lib/template/TwoColumn/TwoColumn/TwoColumn.tsx +2 -2
- package/lib/template/TwoColumn/TwoColumnTab/TwoColumnTab.tsx +2 -2
- package/lib/template/WideNarrow/WideNarrowDetails/WideNarrowDetails.tsx +2 -2
- package/lib/template/WssNavBar/WssNavBar.tsx +9 -9
- package/lib/widget/AppAnnouncement/AppAnnouncement.tsx +2 -2
- package/lib/widget/Attachment/Attachment.css +1 -0
- package/lib/widget/Attachment/Attachment.tsx +7 -9
- package/lib/widget/CaseHistory/CaseHistory.tsx +12 -10
- package/lib/widget/FileUtility/ActionButtonsForFileUtil/ActionButtonsForFileUtil.tsx +1 -1
- package/lib/widget/FileUtility/FileUtility/FileUtility.tsx +5 -4
- package/lib/widget/Followers/Followers.tsx +2 -2
- package/lib/widget/QuickCreate/QuickCreate.tsx +0 -1
- package/lib/widget/QuickCreate/config-ext.json +9 -0
- package/lib/widget/SummaryItem/SummaryItem.tsx +4 -3
- package/lib/widget/ToDo/ToDo.tsx +92 -22
- package/package.json +1 -1
- /package/lib/infra/Containers/{helpers.ts → container-helpers.ts} +0 -0
package/lib/infra/View/View.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PropsWithChildren } from 'react';
|
|
1
|
+
import { PropsWithChildren, useEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
import { getComponentFromMap } from '@pega/react-sdk-components/lib/bridge/helpers/sdk_component_map';
|
|
4
4
|
import { getAllFields } from '@pega/react-sdk-components/lib/components/helpers/template-utils';
|
|
@@ -18,6 +18,7 @@ interface ViewProps extends PConnProps {
|
|
|
18
18
|
visibility?: boolean;
|
|
19
19
|
name?: string;
|
|
20
20
|
bInForm?: boolean;
|
|
21
|
+
type?: any;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
//
|
|
@@ -36,12 +37,14 @@ const NO_HEADER_TEMPLATES = [
|
|
|
36
37
|
'NarrowWideDetails',
|
|
37
38
|
'WideNarrowDetails',
|
|
38
39
|
'Confirmation',
|
|
39
|
-
'DynamicTabs'
|
|
40
|
+
'DynamicTabs',
|
|
41
|
+
'DetailsSubTabs'
|
|
40
42
|
];
|
|
41
43
|
|
|
42
44
|
export default function View(props: PropsWithChildren<ViewProps>) {
|
|
43
|
-
const { children, template, getPConnect, mode, visibility, name: pageName } = props;
|
|
45
|
+
const { children, template, getPConnect, mode, visibility, name: pageName, type, title } = props;
|
|
44
46
|
let { label = '', showLabel = false } = props;
|
|
47
|
+
const { PAGE_TYPES: { PAGE, LANDINGPAGE, LISTPAGE } = {}, MODAL } = PCore.getConstants();
|
|
45
48
|
|
|
46
49
|
// Get the inherited props from the parent to determine label settings. For 8.6, this is only for embedded data form views
|
|
47
50
|
// Putting this logic here instead of copy/paste in every Form template index.js
|
|
@@ -49,12 +52,43 @@ export default function View(props: PropsWithChildren<ViewProps>) {
|
|
|
49
52
|
const inheritedProps: any = getPConnect().getInheritedProps(); // try to remove any when getInheritedProps typedefs are fixed
|
|
50
53
|
label = inheritedProps.label || label;
|
|
51
54
|
showLabel = inheritedProps.showLabel || showLabel;
|
|
55
|
+
const localeUtils = PCore.getLocaleUtils();
|
|
52
56
|
|
|
53
57
|
const isEmbeddedDataView = mode === 'editable'; // would be better to check the reference child for `context` attribute if possible
|
|
54
58
|
if (isEmbeddedDataView && showLabel === undefined) {
|
|
55
59
|
showLabel = true;
|
|
56
60
|
}
|
|
57
61
|
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
// Get the localized application label
|
|
64
|
+
let applicationLabel = PCore.getEnvironmentInfo().getApplicationLabel();
|
|
65
|
+
applicationLabel = localeUtils.getLocaleValue(`${applicationLabel}`, '', '');
|
|
66
|
+
const caseInfo = getPConnect().getCaseInfo();
|
|
67
|
+
const isAssignmentInCreateStage = caseInfo && caseInfo.isAssignmentInCreateStage();
|
|
68
|
+
const isRenderingInModal = getPConnect().getContainerName().includes(MODAL);
|
|
69
|
+
const isRenderingInPreviewPanel = getPConnect().getContainerName().includes('preview');
|
|
70
|
+
|
|
71
|
+
/* If assignment is in create stage and rendering in modal don't update the title.
|
|
72
|
+
Title will be updated on completion of create stage and when the assignment is rendered inline to the page.
|
|
73
|
+
*/
|
|
74
|
+
const canUpdateTitle =
|
|
75
|
+
!isRenderingInPreviewPanel &&
|
|
76
|
+
(type === PAGE || type === LANDINGPAGE || type === LISTPAGE) &&
|
|
77
|
+
!(isRenderingInModal && isAssignmentInCreateStage) &&
|
|
78
|
+
PCore.getEnvironmentInfo().getRenderingMode() === 'FULL_PORTAL';
|
|
79
|
+
// Incase of home route title is same as applicationLabel so setting to empty to just show applicationLabel
|
|
80
|
+
let titleVar = title === applicationLabel ? '' : title;
|
|
81
|
+
|
|
82
|
+
if (canUpdateTitle) {
|
|
83
|
+
if (caseInfo) {
|
|
84
|
+
const name = caseInfo.getName();
|
|
85
|
+
const id = caseInfo.getBusinessID();
|
|
86
|
+
titleVar = name && id ? `${name} (${id})` : titleVar;
|
|
87
|
+
}
|
|
88
|
+
document.title = titleVar ? `${titleVar} - ${applicationLabel}` : applicationLabel;
|
|
89
|
+
}
|
|
90
|
+
}, [type, title, getPConnect, PAGE, LANDINGPAGE, LISTPAGE]);
|
|
91
|
+
|
|
58
92
|
const key = `${getPConnect().getContextName()}_${getPConnect().getPageReference()}_${pageName}`;
|
|
59
93
|
// As long as the template is defined in the dependencies of the view
|
|
60
94
|
// it will be loaded, otherwise fall back to single column
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useContext, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { getComponentFromMap } from '@pega/react-sdk-components/lib/bridge/helpers/sdk_component_map';
|
|
4
|
+
import DataReferenceAdvancedSearchContext from '@pega/react-sdk-components/lib/components/template/DataReference/DataReferenceAdvancedSearchContext';
|
|
5
|
+
import { getFirstChildConfig } from '@pega/react-sdk-components/lib/components/template/DataReference/utils';
|
|
6
|
+
|
|
7
|
+
export default function AdvancedSearch(props) {
|
|
8
|
+
const { getPConnect, targetObjectClass, localeReference } = props;
|
|
9
|
+
const SearchGroups = getComponentFromMap('SearchGroups');
|
|
10
|
+
const { dataReferenceConfigToChild, isCreateNewReferenceEnabled, disableStartingFieldsForReference, pyID, searchSelectCacheKey } = useContext(
|
|
11
|
+
DataReferenceAdvancedSearchContext
|
|
12
|
+
) as any;
|
|
13
|
+
|
|
14
|
+
const { selectionMode, value: singleSelectFieldValue, readonlyContextList: multiSelectField } = dataReferenceConfigToChild;
|
|
15
|
+
|
|
16
|
+
let isSelectionExist = false;
|
|
17
|
+
const { MULTI } = PCore.getConstants().LIST_SELECTION_MODE;
|
|
18
|
+
|
|
19
|
+
if (selectionMode === MULTI) {
|
|
20
|
+
isSelectionExist = getPConnect().getValue(multiSelectField)?.length || false;
|
|
21
|
+
} else {
|
|
22
|
+
isSelectionExist = getPConnect().getValue(singleSelectFieldValue) || false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const [showRecords, setShowRecords] = useState(isSelectionExist);
|
|
26
|
+
|
|
27
|
+
const pConn = getPConnect();
|
|
28
|
+
const rawViewMetadata = pConn.getRawMetadata();
|
|
29
|
+
|
|
30
|
+
const searchFieldsSet = new Set();
|
|
31
|
+
const searchFields: any = [];
|
|
32
|
+
rawViewMetadata.config.searchGroups.forEach(group => {
|
|
33
|
+
group.children.forEach(child => {
|
|
34
|
+
if (!searchFieldsSet.has(child.config.value) && !child.config.validator) {
|
|
35
|
+
searchFields.push(child);
|
|
36
|
+
searchFieldsSet.add(child.config.value);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const firstChildPConnect = getPConnect().getChildren()[0].getPConnect;
|
|
42
|
+
const [firstChildMeta] = rawViewMetadata.children;
|
|
43
|
+
|
|
44
|
+
const localizedVal = PCore.getLocaleUtils().getLocaleValue;
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
const cache = PCore.getNavigationUtils().getComponentCache(searchSelectCacheKey) ?? {};
|
|
47
|
+
|
|
48
|
+
const editableFieldComp = firstChildPConnect().createComponent({
|
|
49
|
+
type: firstChildMeta.type,
|
|
50
|
+
config: {
|
|
51
|
+
...getFirstChildConfig({
|
|
52
|
+
firstChildMeta,
|
|
53
|
+
getPConnect,
|
|
54
|
+
rawViewMetadata,
|
|
55
|
+
contextClass: targetObjectClass,
|
|
56
|
+
dataReferenceConfigToChild,
|
|
57
|
+
isCreateNewReferenceEnabled,
|
|
58
|
+
disableStartingFieldsForReference,
|
|
59
|
+
pyID
|
|
60
|
+
}),
|
|
61
|
+
searchFields,
|
|
62
|
+
showRecords,
|
|
63
|
+
label: localizedVal('Search results', 'DataReference'),
|
|
64
|
+
searchSelectCacheKey,
|
|
65
|
+
cache
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const { selectionList, dataRelationshipContext } = editableFieldComp.props.getPConnect().getConfigProps();
|
|
70
|
+
const editableField = selectionMode === MULTI ? selectionList.substring(1) : dataRelationshipContext;
|
|
71
|
+
|
|
72
|
+
const searchGroupsProps = {
|
|
73
|
+
getPConnect,
|
|
74
|
+
editableField,
|
|
75
|
+
localeReference,
|
|
76
|
+
setShowRecords,
|
|
77
|
+
searchSelectCacheKey,
|
|
78
|
+
cache
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<>
|
|
83
|
+
<SearchGroups {...searchGroupsProps} />
|
|
84
|
+
{editableFieldComp}
|
|
85
|
+
</>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const SKIP_CACHE_KEY = '';
|
|
2
|
+
|
|
3
|
+
export function getMappedKey(key) {
|
|
4
|
+
const mappedKey = PCore.getEnvironmentInfo().getKeyMapping(key);
|
|
5
|
+
if (!mappedKey) {
|
|
6
|
+
return key;
|
|
7
|
+
}
|
|
8
|
+
return mappedKey;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const getComponentStateKey = (getPConnect, propertyName: string) => {
|
|
12
|
+
const pConnect = getPConnect();
|
|
13
|
+
const caseID = `.${getMappedKey('pyID')}`; // Enhance this later when use-case arrives for data objects using S&S.
|
|
14
|
+
const resolvedCaseID = pConnect.getValue(caseID);
|
|
15
|
+
|
|
16
|
+
if (!resolvedCaseID) {
|
|
17
|
+
return SKIP_CACHE_KEY;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return `Search-${resolvedCaseID}-${pConnect.getPageReference()}-${propertyName}-${pConnect.getCurrentView()}`;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const getComponentStateOptions = getPConnect => {
|
|
24
|
+
return { clearOnCancelForContext: getPConnect().getContextName() };
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
interface SearchCategory {
|
|
28
|
+
// tabId of search category selected
|
|
29
|
+
selectedCategory: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface SearchGroup {
|
|
33
|
+
// searchFields can be any object based on what fields are authored.
|
|
34
|
+
searchFields: unknown;
|
|
35
|
+
activeGroupId: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const setComponentCache = ({
|
|
39
|
+
cacheKey,
|
|
40
|
+
state,
|
|
41
|
+
options
|
|
42
|
+
}: {
|
|
43
|
+
cacheKey: string;
|
|
44
|
+
state: SearchCategory | SearchGroup;
|
|
45
|
+
options: ReturnType<typeof getComponentStateOptions>;
|
|
46
|
+
}) => {
|
|
47
|
+
if (cacheKey !== SKIP_CACHE_KEY) {
|
|
48
|
+
(PCore.getNavigationUtils() as any).setComponentCache(cacheKey, state, options);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const componentCachePersistUtils = {
|
|
53
|
+
getComponentStateKey,
|
|
54
|
+
getComponentStateOptions,
|
|
55
|
+
setComponentCache
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default componentCachePersistUtils;
|
|
@@ -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 @@
|
|
|
1
|
+
export { default } from './AdvancedSearch';
|