import { createElement, useEffect, useRef, useState } from 'react'; import Dialog from '@mui/material/Dialog'; import DialogContent from '@mui/material/DialogContent'; import DialogTitle from '@mui/material/DialogTitle'; import makeStyles from '@mui/styles/makeStyles'; import { LocalizationProvider } from '@mui/x-date-pickers'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import difference from 'lodash.difference'; import createPConnectComponent from '@pega/react-sdk-components/lib/bridge/react_pconnect'; // Need to get correct implementation from component map for Assignment and CancelAlert import { getComponentFromMap } from '@pega/react-sdk-components/lib/bridge/helpers/sdk_component_map'; import { getBanners } from '@pega/react-sdk-components/lib/components/helpers/case-utils'; import type { PConnProps } from '@pega/react-sdk-components/lib/types/PConnProps'; interface ModalViewContainerProps extends PConnProps { // If any, enter additional props that only exist on this component loadingInfo?: string; routingInfo?: any; pageMessages?: string[]; } function isOpenModalAction(prevModalCollection, currentModalList) { return prevModalCollection && currentModalList ? Object.keys(prevModalCollection).length < currentModalList.length : false; } function isUpdateModalAction(prevModalCollection, currentModalList) { return prevModalCollection && currentModalList ? Object.keys(prevModalCollection).length === currentModalList.length : false; } function isCloseModalAction(prevModalCollection, currentModalList) { return prevModalCollection && currentModalList ? Object.keys(prevModalCollection).length > currentModalList.length : false; } function buildName(pConnect, name = '') { const context = pConnect.getContextName(); return `${context}/${name}`; } function getKeyAndLatestItem(routinginfo, pConn, options) { const containerName = pConn.getContainerName(); const { acTertiary = false } = options || {}; if (PCore.getContainerUtils().hasContainerItems(buildName(pConn, containerName))) { const { accessedOrder, items } = routinginfo; let key; for (let i = accessedOrder.length - 1; i >= 0; i--) { const tempkey = accessedOrder[i]; if ((acTertiary && items[tempkey].acTertiary) || (!acTertiary && !items[tempkey].acTertiary)) { key = tempkey; break; } } const latestItem = items[key]; return { key, latestItem }; } return {}; } function getConfigObject(item, pConnect, isReverseCoexistence = false) { let config; if (isReverseCoexistence) { config = { options: { pageReference: pConnect?.getPageReference(), hasForm: true, containerName: pConnect?.getContainerName() || PCore.getConstants().MODAL } }; return PCore.createPConnect(config); } if (item) { const { context, view, isBulkAction } = item; const target = PCore.getContainerUtils().getTargetFromContainerItemID(context); config = { meta: view, options: { context, pageReference: view.config.context || pConnect.getPageReference(), hasForm: true, ...(isBulkAction && { isBulkAction }), containerName: pConnect?.getContainerName() || PCore.getConstants().MODAL, target } }; return PCore.createPConnect(config); } return null; } const useStyles = makeStyles(theme => ({ dlgTitle: { marginLeft: theme.spacing(2), marginRight: theme.spacing(2), marginTop: theme.spacing(2), marginBottom: theme.spacing(0) }, dlgContent: { paddingTop: `${theme.spacing(1)} !important`, marginLeft: theme.spacing(2), marginRight: theme.spacing(2), marginTop: theme.spacing(0), marginBottom: theme.spacing(2) } })); export default function ModalViewContainer(props: ModalViewContainerProps) { // Get the proper implementation (local or Pega-provided) for these components that are emitted below const Assignment = getComponentFromMap('Assignment'); const CancelAlert = getComponentFromMap('CancelAlert'); const ListViewActionButtons = getComponentFromMap('ListViewActionButtons'); const classes = useStyles(); const modalCollection = useRef({}); const routingInfoRef = useRef({}); const { getPConnect, routingInfo = null, pageMessages = [] } = props; const pConn = getPConnect(); const { acTertiary } = pConn.getConfigProps() as any; const { CONTAINER_TYPE: { MULTIPLE }, PUB_SUB_EVENTS: { EVENT_SHOW_CANCEL_ALERT } } = PCore.getConstants(); const { subscribe, unsubscribe } = PCore.getPubSubUtils(); const [bShowModal, setShowModal] = useState(false); const [bShowCancelAlert, setShowCancelAlert] = useState(false); const [createdView, setCreatedView] = useState(null); const [title, setTitle] = useState(''); const [arNewChildrenAsReact, setArNewChildrenAsReact] = useState([]); const [itemKey, setItemKey] = useState(''); const [cancelAlertProps, setCancelAlertProps] = useState({}); const [isMultiRecordData, setMultiRecordData] = useState(false); const localizedVal = PCore.getLocaleUtils().getLocaleValue; const localeCategory = 'ModalContainer'; const ERROR_WHILE_RENDERING = 'ERROR_WHILE_RENDERING'; function showAlert(payload) { const { latestItem } = getKeyAndLatestItem(routingInfoRef.current, pConn, { acTertiary }); const isReverseCoexistence = (PCore.getCoexistenceManager().getBroadcastUtils() as any).isReverseCoexistenceCaseLoaded(); const { isModalAction, hideDelete, isDataObject, skipReleaseLockRequest } = payload; /* If we are in create stage full page mode, created a new case and trying to click on cancel button it will show two alert dialogs which is not expected. Hence isModalAction flag to avoid that. */ if (latestItem && isModalAction) { const configObject = getConfigObject(latestItem, pConn, isReverseCoexistence); const contextName = configObject?.getPConnect().getContextName(); setCancelAlertProps({ heading: 'Discard unsaved changes?', content: 'You have unsaved changes. You can discard them or go back to keep working.', getPConnect: configObject?.getPConnect, itemKey: contextName, hideDelete, isDataObject, skipReleaseLockRequest }); setShowCancelAlert(true); } } function handleModalOpen(key) { modalCollection.current = { ...modalCollection.current, [key]: {} }; } function handleModalClose(accessedOrder) { const tempModalCollection = modalCollection.current; const [closedModalKey] = difference(Object.keys(tempModalCollection), accessedOrder); if (closedModalKey && tempModalCollection[closedModalKey]) { const modifiedModalCollection = { ...tempModalCollection }; delete modifiedModalCollection[closedModalKey]; modalCollection.current = modifiedModalCollection; setShowModal(false); } } const dismissCancelAlert = dismissAllModals => { setShowCancelAlert(false); if (dismissAllModals) { setShowModal(false); } }; function getModalHeading(dataObjectAction) { return dataObjectAction === PCore.getConstants().RESOURCE_STATUS.CREATE ? localizedVal('Add Record', localeCategory) : localizedVal('Edit Record', localeCategory); } function determineModalHeaderByAction(actionName, caseTypeName, ID, caseLocaleRef) { if (actionName) { return localizedVal(actionName, localeCategory); } return `${localizedVal('Create', localeCategory)} ${localizedVal(caseTypeName, undefined, caseLocaleRef)} (${ID})`; } useEffect(() => { // Establish the necessary containers const containerMgr = pConn.getContainerManager(); containerMgr.initializeContainers({ type: MULTIPLE }); }, [MULTIPLE, pConn]); useEffect(() => { // Persisting routing information between the renders in showAlert routingInfoRef.current = routingInfo; }); useEffect(() => { subscribe(EVENT_SHOW_CANCEL_ALERT, showAlert, EVENT_SHOW_CANCEL_ALERT /* Unique string for subscription */); subscribe( ERROR_WHILE_RENDERING, error => { // setError(true); console.error(error); }, `${ERROR_WHILE_RENDERING}-mc-${getPConnect().getContextName()}`, false, getPConnect().getContextName() ); // Unsubscribe on component unmount return () => { unsubscribe(EVENT_SHOW_CANCEL_ALERT, EVENT_SHOW_CANCEL_ALERT /* Should be same unique string passed during subscription */); }; }); useEffect(() => { if (routingInfo) { const { accessedOrder, type } = routingInfo; const { key, latestItem } = getKeyAndLatestItem(routingInfo, pConn, { acTertiary }); if ( latestItem && type === MULTIPLE && (isOpenModalAction(modalCollection.current, accessedOrder) || isUpdateModalAction(modalCollection.current, accessedOrder)) ) { const { actionName } = latestItem; // const { isDockable = false } = latestItem?.modalOptions || {}; const configObject: any = getConfigObject(latestItem, null, false); const pConnect = configObject.getPConnect(); const caseInfo: any = pConnect.getCaseInfo(); const caseName = caseInfo.getName(); const caseTypeName = caseInfo.getCaseTypeName(); const ID = caseInfo.getBusinessID() || caseInfo.getID(); const isDataObject = routingInfo.items[latestItem.context].resourceType === PCore.getConstants().RESOURCE_TYPES.DATA; const dataObjectAction = routingInfo.items[latestItem.context].resourceStatus; const isMultiRecord = routingInfo.items[latestItem.context].isMultiRecordData; const headingValue = isDataObject || isMultiRecord ? getModalHeading(dataObjectAction) : determineModalHeaderByAction(actionName, caseTypeName, ID, pConnect?.getCaseLocaleReference()); let arChildrenAsReact: any[] = []; if (pConnect.getComponentName() === 'reference') { // Reference component doesn't have children. It can build the View we want. // The Reference component getPConnect is in configObject arChildrenAsReact.push( createElement(createPConnectComponent(), { ...configObject, key: `${caseName}-${ID}` }) ); } else { // This is the 8.6 implementation. Leaving it in for reference for now. // And create a similar array of the children as React components // passed to Assignment component when rendered arChildrenAsReact = (pConnect.getChildren() as []).map((child: any) => { // Use Case Summary ID as the React element's key const caseSummaryID = child.getPConnect().getCaseSummary().ID; return createElement(createPConnectComponent(), { ...child, key: caseSummaryID }); }); } if (arChildrenAsReact.length > 0) setArNewChildrenAsReact(arChildrenAsReact); setMultiRecordData(isMultiRecord); setTitle(headingValue); setCreatedView({ configObject, latestItem }); setItemKey(key); setShowModal(true); // Update modal use case which happens when assignment in submitted in modal. if (isUpdateModalAction(modalCollection.current, accessedOrder)) { // handleModalUpdate(key); } else if (isOpenModalAction(modalCollection.current, accessedOrder)) { // New modal open scenario handleModalOpen(key); } } else if (isCloseModalAction(modalCollection.current, accessedOrder)) { handleModalClose(accessedOrder); } } }, [routingInfo]); function closeActionsDialog() { // actionsDialog.current = true; setShowModal(false); } return ( <> {title} {bShowModal ? ( {arNewChildrenAsReact} ) : null} {isMultiRecordData && ( )} {bShowCancelAlert && } ); }