import React, { createElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import { Button, Grid2, Select, MenuItem, Box } from '@mui/material'; import createPConnectComponent from '@pega/react-sdk-components/lib/bridge/react_pconnect'; import TemplateContext from '@pega/react-sdk-components/lib/components/template/AdvancedSearch/TemplateContext'; import componentCachePersistUtils from '@pega/react-sdk-components/lib/components/template/AdvancedSearch/SearchGroup/persistUtils'; import { getCacheInfo, isValidInput } from './utils'; import { useCacheWhenListViewReady } from './hooks'; export const initializeSearchFields = (searchFields, getPConnect, referenceListClassID, searchFieldRestoreValues = {}) => { const filtersProperties = {}; searchFields.forEach(field => { let val = ''; const { value, defaultValue = '' } = field.config; const propPath = PCore.getAnnotationUtils().getPropertyName(value); if (searchFieldRestoreValues[propPath]) { val = searchFieldRestoreValues[propPath]; } else if (PCore.getAnnotationUtils().isProperty(defaultValue)) { val = getPConnect().getValue(defaultValue.split(' ')[1]); } else if (defaultValue.startsWith('@L')) { val = defaultValue.split(' ')[1]; } else { val = defaultValue; } filtersProperties[propPath] = val; const valueSplit = value.split('@P ')[1]?.split('.').filter(Boolean) ?? []; valueSplit.pop(); if (valueSplit.length) { let path = ''; let currentClassID = referenceListClassID; valueSplit.forEach(item => { path = path.length ? `${path}.${item}` : item; currentClassID = (PCore.getMetadataUtils().getPropertyMetadata(item, currentClassID) as any).pageClass; if (currentClassID) { filtersProperties[`${path}.classID`] = currentClassID; } }); } }); return filtersProperties; }; const flattenObj = obj => { const result = {}; Object.keys(obj).forEach(key => { if (!['context_data', 'pageInstructions'].includes(key)) { if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) { const temp = flattenObj(obj[key]); Object.keys(temp).forEach(nestedKey => { result[`${key}.${nestedKey}`] = temp[nestedKey]; }); } else { result[key] = obj[key]; } } }); return result; }; export default function SearchGroups(props) { const localizedVal = PCore.getLocaleUtils().getLocaleValue; const { getPConnect, editableField, localeReference, setShowRecords, searchSelectCacheKey, cache } = props; const referenceFieldName = editableField.replaceAll('.', '_'); const state: any = useRef({ searchFields: {}, activeGroupId: '' }).current; const options = componentCachePersistUtils.getComponentStateOptions(getPConnect); const { searchGroups: groups, referenceList } = getPConnect().getConfigProps(); const { useCache, initialActiveGroupId } = getCacheInfo(cache, groups); const [activeGroupId, setActiveGroupId] = useState(initialActiveGroupId); const [transientItemID, setTransientItemID] = useState(null); const [previousFormValues, setPreviousFormValues] = useState(null); const viewName = getPConnect().getCurrentView(); const rawGroupsConfig = getPConnect().getRawConfigProps().searchGroups; const activeGroupIndex = groups.findIndex(group => group.config.id === activeGroupId); const { children: searchFieldsChildren = [] } = activeGroupIndex !== -1 ? rawGroupsConfig[activeGroupIndex] : {}; const searchFields = searchFieldsChildren.map(field => ({ ...field, config: { ...field.config, isSearchField: true } })); const searchByRef = useRef(null); const searchFieldsRef = useRef(null); const isValidatorField = searchFields.some(field => field.config.validator); const { classID: referenceListClassID } = PCore.getMetadataUtils().getDataPageMetadata(referenceList) as any; const initialSearchFields = useMemo( () => initializeSearchFields( searchFields, getPConnect, referenceListClassID, useCache && cache.activeGroupId === activeGroupId ? cache.searchFields : {} ), [activeGroupId, getPConnect, cache.searchFields] ); useEffect(() => { if (transientItemID) { const filtersWithClassID = { ...initialSearchFields, classID: referenceListClassID }; // @ts-ignore PCore.getContainerUtils().replaceTransientData({ transientItemID, data: filtersWithClassID }); } }, [activeGroupId]); useEffect(() => { const filtersWithClassID = { ...initialSearchFields, classID: referenceListClassID }; const transientId = getPConnect() .getContainerManager() .addTransientItem({ id: `${referenceFieldName}-${viewName}`, data: filtersWithClassID }); setTransientItemID(transientId); }, []); const getFilterData = useCallback(() => { // @ts-ignore let changes = PCore.getFormUtils().getSubmitData(transientItemID, { isTransientContext: true, includeDisabledFields: true }); if (Object.keys(cache.searchFields ?? {}).length > 0 && Object.keys(changes).length === 1) { changes = cache.searchFields; } const formValues = flattenObj(changes); if (!PCore.isDeepEqual(previousFormValues, formValues) && PCore.getFormUtils().isFormValid(transientItemID) && isValidInput(formValues)) { if (isValidatorField) { // @ts-ignore PCore.getMessageManager().clearContextMessages({ context: transientItemID }); } setPreviousFormValues(formValues); setShowRecords(true); PCore.getPubSubUtils().publish(PCore.getEvents().getTransientEvent().UPDATE_PROMOTED_FILTERS, { payload: formValues, showRecords: true, viewName }); } state.activeGroupId = activeGroupId; state.searchFields = changes; state.selectedCategory = viewName; componentCachePersistUtils.setComponentCache({ cacheKey: searchSelectCacheKey, state, options }); }, [transientItemID, setShowRecords, viewName, activeGroupId, previousFormValues]); const resetFilterData = useCallback(() => { // @ts-ignore PCore.getNavigationUtils().resetComponentCache(searchSelectCacheKey); const resetPayload = { transientItemID, data: initializeSearchFields(searchFields, getPConnect, referenceListClassID), options: { reset: true } }; // @ts-ignore PCore.getContainerUtils().updateTransientData(resetPayload); }, [transientItemID, initialSearchFields]); useCacheWhenListViewReady(cache, viewName, useCache, getFilterData, searchSelectCacheKey); const searchDropdown = groups.length > 1 && ( ); const actionButtons = ( ); const searchFieldsViewConfig = { name: 'SearchFields', type: 'View', config: { template: 'DefaultForm', NumCols: '3', contextName: transientItemID, readOnly: false, context: transientItemID, localeReference }, children: [ { name: 'Fields', type: 'Region', children: searchFields } ] }; const searchFieldsC11nEnv = PCore.createPConnect({ meta: searchFieldsViewConfig, options: { hasForm: true, contextName: transientItemID } }); const templateContext = useContext(TemplateContext); const templateContextValue = useMemo(() => ({ ...templateContext, outerColumnCount: undefined }), []); const searchFieldsViewComp = transientItemID ? (
{createElement(createPConnectComponent(), { ...searchFieldsC11nEnv })}
) : null; const childrenToRender = [searchDropdown, searchFieldsViewComp, actionButtons]; return ( {childrenToRender.map((child, index) => ( {child} ))} ); }