diff --git a/src/samples/AppSelector/index.tsx b/src/samples/AppSelector/index.tsx index 0c3ae1cb..e7258c4f 100644 --- a/src/samples/AppSelector/index.tsx +++ b/src/samples/AppSelector/index.tsx @@ -1,6 +1,6 @@ import { Route, Routes } from 'react-router-dom'; -import EmbeddedTopLevel from '../Embedded/EmbeddedTopLevel'; +import Embedded from '../Embedded'; import FullPortal from '../FullPortal'; // NOTE: You should update this to be the same value that's in @@ -17,13 +17,13 @@ const AppSelector = () => { return (
- } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> } /> } /> - } /> + } />
); diff --git a/src/samples/Embedded/EmbeddedTopLevel/index.tsx b/src/samples/Embedded/EmbeddedTopLevel/index.tsx deleted file mode 100644 index df13fe99..00000000 --- a/src/samples/Embedded/EmbeddedTopLevel/index.tsx +++ /dev/null @@ -1,532 +0,0 @@ -/* eslint-disable react/button-has-type */ -import { useState, useEffect } from 'react'; -import ReactDOM from 'react-dom'; -import Typography from '@mui/material/Typography'; -import CssBaseline from '@mui/material/CssBaseline'; -import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles'; -import makeStyles from '@mui/styles/makeStyles'; -import { sdkIsLoggedIn, loginIfNecessary, sdkSetAuthHeader, sdkSetCustomTokenParamsCB, getSdkConfig } from '@pega/auth/lib/sdk-auth-manager'; - -import StoreContext from '@pega/react-sdk-components/lib/bridge/Context/StoreContext'; -import createPConnectComponent from '@pega/react-sdk-components/lib/bridge/react_pconnect'; - -import EmbeddedSwatch from '../EmbeddedSwatch'; -import { compareSdkPCoreVersions } from '@pega/react-sdk-components/lib/components/helpers/versionHelpers'; -import { getSdkComponentMap } from '@pega/react-sdk-components/lib/bridge/helpers/sdk_component_map'; -import localSdkComponentMap from '../../../../sdk-local-component-map'; -import { theme } from '../../../theme'; - -declare const myLoadMashup: any; - -const useStyles = makeStyles(() => ({ - embedTopRibbon: { - display: 'none', - alignItems: 'center', - height: '64px', - padding: '0px 20px', - backgroundColor: theme.palette.primary.main, - color: theme.palette.primary.contrastText - }, - embedTopIcon: { - width: '40px', - filter: 'invert(100%)' - }, - embedMainScreen: { - display: 'flex', - flexDirection: 'column', - width: '100%' - }, - embedBanner: { - textAlign: 'center', - width: '100%', - padding: '20px' - }, - embedShoppingOptions: { - display: 'flex', - justifyContent: 'space-evenly' - }, - pegaPartInfo: { - display: 'none', - flexDirection: 'row' - }, - pegaPartPega: { - width: '50%', - display: 'flex', - flexDirection: 'column' - }, - pegaPartText: { - paddingLeft: '50px' - }, - pegaPartAccompaniment: { - width: '50%', - display: 'flex', - flexDirection: 'column', - alignItems: 'center' - }, - pegaPartAccompanimentText: { - fontSize: '30px', - lineHeight: '40px', - padding: '20px 20px', - color: 'darkslategray' - }, - pegaPartAccompanimentImage: { - width: '700px', - margin: '20px', - borderRadius: '10px' - }, - resolutionPart: { - display: 'flex', - flexDirection: 'row' - }, - resolutionPartAccompanimentLeft: { - width: '50%', - alignItems: 'center' - }, - resolutionPartAccompanimentRight: { - width: '50%', - alignItems: 'center', - textAlign: 'center' - }, - resolutionPartAccompanimentText: { - fontSize: '28px', - lineHeight: '40px', - padding: '20px 20px', - color: 'darkslategray' - }, - resolutionButton: { - color: 'white', - backgroundColor: theme.palette.warning.main, - fontSize: '25px', - fontWeight: 'bold', - borderRadius: '25px', - border: '0px', - margin: '20px', - padding: '10px 30px' - } -})); - -export default function EmbeddedTopLevel() { - // Array of 3 shopping options to display - const shoppingOptions = [ - { - play: 'Triple Play', - level: 'Basic', - channels: '100+', - channels_full: '100+ (Basic +)', - banner: 'Value package', - price: '99.00', - internetSpeed: '100 Mbps', - calling: '' - }, - { - play: 'Triple Play', - level: 'Silver', - channels: '125+', - channels_full: '125+ (Deluxe)', - banner: 'Most popular', - price: '120.00', - internetSpeed: '300 Mbps', - calling: '' - }, - { - play: 'Triple Play', - level: 'Gold', - channels: '175+', - channels_full: '175+ (Premium)', - banner: 'All the channels you want', - price: '150.00', - internetSpeed: '1 Gbps', - calling: ' & International' - } - ]; - - const classes = useStyles(); - - // const [pConn, setPConn] = useState(null); - - const [bShowTriplePlayOptions, setShowTriplePlayOptions] = useState(false); - const [bShowPega, setShowPega] = useState(false); - const [bShowResolutionScreen, setShowResolutionScreen] = useState(false); - const [bShowAppName, setShowAppName] = useState(false); - - useEffect(() => { - // Update visibility of UI when bShowTriplePlayOptions changes - - // eslint-disable-next-line no-console - console.log(`EmbeddedTopLevel: bShowTriplePlayOptions set to ${bShowTriplePlayOptions}`); - const theTopLevelEl = document.getElementById('embedded-top-level-banner-buttons'); - const theTopLevelRibbon = document.getElementById('embedded-top-level-ribbon'); - - if (theTopLevelEl) { - if (bShowTriplePlayOptions && sdkIsLoggedIn()) { - // Only show when user is logged in and we're supposed to show it - theTopLevelEl.style.display = 'block'; - if (theTopLevelRibbon) { - theTopLevelRibbon.style.display = 'flex'; - } - } else { - theTopLevelEl.style.display = 'none'; - if (theTopLevelRibbon) { - theTopLevelRibbon.style.display = 'none'; - } - } - } - }, [bShowTriplePlayOptions]); - - useEffect(() => { - // Update visibility of UI when bShowPega changes - // eslint-disable-next-line no-console - console.log(`EmbeddedTopLevel: bShowPega set to ${bShowPega}`); - - const thePegaPartEl = document.getElementById('pega-part-of-page'); - const theTopLevelRibbon = document.getElementById('embedded-top-level-ribbon'); - - if (thePegaPartEl) { - if (bShowPega) { - thePegaPartEl.style.display = 'flex'; - if (theTopLevelRibbon) { - theTopLevelRibbon.style.display = 'flex'; - } - } else { - thePegaPartEl.style.display = 'none'; - } - } - }, [bShowPega]); - - useEffect(() => { - // Update visibility of UI when bShowResolutionScreen changes - // eslint-disable-next-line no-console - console.log(`EmbeddedTopLevel: bShowPega set to ${bShowPega}`); - - const theTopLevelEl = document.getElementById('embedded-top-level-resolution'); - const theTopLevelRibbon = document.getElementById('embedded-top-level-ribbon'); - - if (bShowResolutionScreen && sdkIsLoggedIn()) { - // Only show when user is logged in and we're supposed to show it - if (theTopLevelEl) { - theTopLevelEl.style.display = 'block'; - } - if (theTopLevelRibbon) { - theTopLevelRibbon.style.display = 'flex'; - } - } else { - if (theTopLevelEl) { - theTopLevelEl.style.display = 'none'; - } - if (theTopLevelRibbon) { - theTopLevelRibbon.style.display = 'none'; - } - } - }, [bShowResolutionScreen]); - - useEffect(() => { - // Update when bShowAppName changes - // If not logged in, we used to prompt for login. Now moved up to TopLevelApp - // If logged in, make the Triple Play Options visible - - if (!sdkIsLoggedIn()) { - // login(); // Login now handled at TopLevelApp - } else { - setShowTriplePlayOptions(true); - } - }, [bShowAppName]); - - // const outlet = document.getElementById("outlet"); - - function assignmentFinished() { - setShowTriplePlayOptions(false); - setShowPega(false); - setShowResolutionScreen(true); - } - - function cancelAssignment() { - setShowTriplePlayOptions(true); - setShowPega(false); - setShowResolutionScreen(false); - } - - function establishPCoreSubscriptions() { - PCore.getPubSubUtils().subscribe( - 'assignmentFinished', - () => { - assignmentFinished(); - }, - 'assignmentFinished' - ); - - PCore.getPubSubUtils().subscribe( - PCore.getConstants().PUB_SUB_EVENTS.EVENT_CANCEL, - () => { - cancelAssignment(); - }, - 'cancelAssignment' - ); - } - - // from react_root.js with some modifications - // eslint-disable-next-line react/no-unstable-nested-components - function RootComponent(props) { - const PegaConnectObj = createPConnectComponent(); - - // remove from Provider to work around compiler error for now: context={StoreContext} - // return ( - // - // - // - // ); - - // const thePConnObj =
the RootComponent
; - const thePConnObj = ; - - // NOTE: For Embedded mode, we add in displayOnlyFA to our React context - // so it is available to any component that may need it. - // VRS: Attempted to remove displayOnlyFA but it presently handles various components which - // SDK does not yet support, so all those need to be fixed up before it can be removed. To - // be done in a future sprint. - return ( - // eslint-disable-next-line react/jsx-no-constructed-context-values - {thePConnObj} - ); - } - - /** - * Callback from onPCoreReady that's called once the top-level render object - * is ready to be rendered - * @param inRenderObj the initial, top-level PConnect object to render - */ - function initialRender(inRenderObj) { - // loadMashup does its own thing so we don't need to do much/anything here - - // // modified from react_root.js render - const { props, domContainerID = null, componentName, portalTarget, styleSheetTarget } = inRenderObj; - - const thePConn = props.getPConnect(); - // setPConn(thePConn); - // eslint-disable-next-line no-console - console.log(`EmbeddedTopLevel: initialRender got a PConnect with ${thePConn.getComponentName()}`); - - let target: any = null; - - if (domContainerID !== null) { - target = document.getElementById(domContainerID); - } else if (portalTarget !== null) { - target = portalTarget; - } - - // Note: RootComponent is just a function (declared below) - const Component: any = RootComponent; - - if (componentName) { - Component.displayName = componentName; - } - - const theComponent = ( - - - - - - - ); - - // Initial render of component passed in (which should be a RootContainer) - ReactDOM.render(<>{theComponent}, target); - - // Initial render to show that we have a PConnect and can render in the target location - // render(
EmbeddedTopLevel initialRender in {domContainerID} with PConn of {componentName}
, target); - } - - /** - * kick off the application's portal that we're trying to serve up - */ - function startMashup() { - // NOTE: When loadMashup is complete, this will be called. - PCore.onPCoreReady(renderObj => { - // eslint-disable-next-line no-console - console.log(`PCore ready!`); - // Check that we're seeing the PCore version we expect - compareSdkPCoreVersions(); - - establishPCoreSubscriptions(); - setShowAppName(true); - - // Initialize the SdkComponentMap (local and pega-provided) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - getSdkComponentMap(localSdkComponentMap).then((theComponentMap: any) => { - // eslint-disable-next-line no-console - console.log(`SdkComponentMap initialized`); - - // Don't call initialRender until SdkComponentMap is fully initialized - initialRender(renderObj); - }); - }); - - // load the Mashup and handle the onPCoreEntry response that establishes the - // top level Pega root element (likely a RootContainer) - - myLoadMashup('pega-root', false); // this is defined in bootstrap shell that's been loaded already - } - - // One time (initialization) subscriptions and related unsubscribe - useEffect(() => { - getSdkConfig().then((sdkConfig: any) => { - const sdkConfigAuth = sdkConfig.authConfig; - - if ((sdkConfigAuth.mashupGrantType === 'none' || !sdkConfigAuth.mashupClientId) && sdkConfigAuth.customAuthType === 'Basic') { - // Service package to use custom auth with Basic - const sB64 = window.btoa(`${sdkConfigAuth.mashupUserIdentifier}:${window.atob(sdkConfigAuth.mashupPassword)}`); - sdkSetAuthHeader(`Basic ${sB64}`); - } - - if ((sdkConfigAuth.mashupGrantType === 'none' || !sdkConfigAuth.mashupClientId) && sdkConfigAuth.customAuthType === 'BasicTO') { - const now = new Date(); - const expTime = new Date(now.getTime() + 5 * 60 * 1000); - let sISOTime = `${expTime.toISOString().split('.')[0]}Z`; - const regex = /[-:]/g; - sISOTime = sISOTime.replace(regex, ''); - // Service package to use custom auth with Basic - const sB64 = window.btoa(`${sdkConfigAuth.mashupUserIdentifier}:${window.atob(sdkConfigAuth.mashupPassword)}:${sISOTime}`); - sdkSetAuthHeader(`Basic ${sB64}`); - } - - if (sdkConfigAuth.mashupGrantType === 'customBearer' && sdkConfigAuth.customAuthType === 'CustomIdentifier') { - // Use custom bearer with specific custom parameter to set the desired operator via - // a userIdentifier property. (Caution: highly insecure...being used for simple demonstration) - sdkSetCustomTokenParamsCB(() => { - return { userIdentifier: sdkConfigAuth.mashupUserIdentifier }; - }); - } - - document.addEventListener('SdkConstellationReady', () => { - // start the portal - startMashup(); - }); - - // Login if needed, without doing an initial main window redirect - loginIfNecessary({ appName: 'embedded', mainRedirect: false }); - }); - - // Subscriptions can't be done until onPCoreReady. - // So we subscribe there. But unsubscribe when this - // component is unmounted (in function returned from this effect) - - return function cleanupSubscriptions() { - PCore?.getPubSubUtils().unsubscribe(PCore.getConstants().PUB_SUB_EVENTS.EVENT_CANCEL, 'cancelAssignment'); - - PCore?.getPubSubUtils().unsubscribe('assignmentFinished', 'assignmentFinished'); - }; - }, []); - - function onShopNow(optionClicked: string) { - const sLevel = optionClicked; - - setShowTriplePlayOptions(false); - setShowPega(true); - - getSdkConfig().then(sdkConfig => { - let mashupCaseType = sdkConfig.serverConfig.appMashupCaseType; - if (!mashupCaseType) { - // @ts-ignore - Object is possibly 'null' - const caseTypes: any = PCore.getEnvironmentInfo().environmentInfoObject.pyCaseTypeList; - mashupCaseType = caseTypes[0].pyWorkTypeImplementationClassName; - } - - const options: any = { - pageName: 'pyEmbedAssignment', - startingFields: - mashupCaseType === 'DIXL-MediaCo-Work-NewService' - ? { - Package: sLevel - } - : {} - }; - (PCore.getMashupApi().createCase(mashupCaseType, PCore.getConstants().APP.APP, options) as Promise).then(() => { - // eslint-disable-next-line no-console - console.log('createCase rendering is complete'); - }); - }); - } - - function getShowTriplePlayOptionsMarkup() { - // return "Show Triple Play Options here!"; - const theBanner = ( -
-
- Combine TV, Internet, and Voice for the best deal -
-
- ); - - const theOptions = shoppingOptions.map((option, index) => { - return ( - - - - - - - ); - }); - - return ( - <> - {theBanner} -
{theOptions}
- - ); - } - - function getResolutionScreenMarkup() { - return ( -
-
-
- Welcome! -
-
- Thanks for selecting a package with us.
-
- A technician will contact you with in the next couple of days to schedule an installation. -
-
- If you have any questions, you can contact us directly at 1-800-555-1234 or you can chat with us. -
-
-
- -
- -
-
- ); - } - - return ( -
- {/*

React SDK: /embedded

*/} -
- {bShowAppName ? {PCore.getEnvironmentInfo().getApplicationLabel()} : null} -      - -
-
{bShowTriplePlayOptions ? getShowTriplePlayOptionsMarkup() : null}
-
{bShowResolutionScreen ? getResolutionScreenMarkup() : null}
- {/* The next div is the container for the Pega work and a corresponding image */} -
-
-
-
-
-
* - required fields
-
-
-
We need to gather a little information about you.
-
- -
-
-
-
-
- ); -} diff --git a/src/samples/Embedded/Header/index.tsx b/src/samples/Embedded/Header/index.tsx new file mode 100644 index 00000000..8ae1bda8 --- /dev/null +++ b/src/samples/Embedded/Header/index.tsx @@ -0,0 +1,29 @@ +import { Typography } from '@mui/material'; +import { makeStyles } from '@mui/styles'; + +const useStyles = makeStyles(theme => ({ + embeddedHeader: { + display: 'flex', + alignItems: 'center', + height: '64px', + padding: '0px 20px', + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText + }, + embedTopIcon: { + width: '40px', + filter: 'invert(100%)' + } +})); + +export default function Header() { + const classes = useStyles(); + + return ( +
+ {PCore.getEnvironmentInfo().getApplicationLabel()} +      + +
+ ); +} diff --git a/src/samples/Embedded/MainScreen/index.tsx b/src/samples/Embedded/MainScreen/index.tsx new file mode 100644 index 00000000..1f2e431b --- /dev/null +++ b/src/samples/Embedded/MainScreen/index.tsx @@ -0,0 +1,195 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import { useEffect, useMemo, useState } from 'react'; +import { Typography } from '@mui/material'; +import { makeStyles } from '@mui/styles'; +import { getSdkConfig } from '@pega/auth/lib/sdk-auth-manager'; + +import StoreContext from '@pega/react-sdk-components/lib/bridge/Context/StoreContext'; +import createPConnectComponent from '@pega/react-sdk-components/lib/bridge/react_pconnect'; + +import ShoppingOptionCard from '../ShoppingOptionCard'; +import ResolutionScreen from '../ResolutionScreen'; +import { shoppingOptions } from '../utils'; + +function RootComponent(props) { + const PegaConnectObj = createPConnectComponent(); + const thePConnObj = ; + + /** + * NOTE: For Embedded mode, we add in displayOnlyFA to our React context + * so it is available to any component that may need it. + * VRS: Attempted to remove displayOnlyFA but it presently handles various components which + * SDK does not yet support, so all those need to be fixed up before it can be removed. + * To be done in a future sprint. + */ + const contextValue = useMemo(() => { + return { store: PCore.getStore(), displayOnlyFA: true }; + }, [PCore.getStore()]); + + return {thePConnObj}; +} + +const useStyles = makeStyles(() => ({ + embedMainScreen: { + display: 'flex', + flexDirection: 'column', + width: '100%' + }, + embedBanner: { + textAlign: 'center', + width: '100%', + padding: '20px' + }, + embedShoppingOptions: { + display: 'flex', + justifyContent: 'space-evenly' + }, + pegaPartInfo: { + display: 'flex', + flexDirection: 'row' + }, + pegaPartPega: { + width: '50%', + display: 'flex', + flexDirection: 'column' + }, + pegaPartText: { + paddingLeft: '50px' + }, + pegaPartAccompaniment: { + width: '50%', + display: 'flex', + flexDirection: 'column', + alignItems: 'center' + }, + pegaPartAccompanimentText: { + fontSize: '30px', + lineHeight: '40px', + padding: '20px 20px', + color: 'darkslategray' + }, + pegaPartAccompanimentImage: { + width: '100%', + borderRadius: '10px' + } +})); + +interface MainScreenProps {} + +export default function MainScreen(props: MainScreenProps) { + const classes = useStyles(); + + const [showPega, setShowPega] = useState(false); + const [showTriplePlayOptions, setShowTriplePlayOptions] = useState(true); + const [showResolution, setShowResolution] = useState(false); + + useEffect(() => { + // Subscribe to the EVENT_CANCEL event to handle the assignment cancellation + PCore.getPubSubUtils().subscribe(PCore.getConstants().PUB_SUB_EVENTS.EVENT_CANCEL, () => cancelAssignment(), 'cancelAssignment'); + + // Subscribe to the 'assignmentFinished' event to handle assignment completion + PCore.getPubSubUtils().subscribe('assignmentFinished', () => assignmentFinished(), 'assignmentFinished'); + + return () => { + // unsubscribe to the events + PCore.getPubSubUtils().unsubscribe(PCore.getConstants().PUB_SUB_EVENTS.EVENT_CANCEL, 'cancelAssignment'); + PCore.getPubSubUtils().unsubscribe('assignmentFinished', 'assignmentFinished'); + }; + }); + + const cancelAssignment = () => { + setShowTriplePlayOptions(true); + setShowPega(false); + }; + + const assignmentFinished = () => { + setShowResolution(true); + setShowPega(false); + }; + + /** + * Handles the 'Shop Now' event by creating a new case using the mashup API. + * + * @param {Event} event - The event object triggered by the 'Shop Now' action. + */ + const onShopNow = async (optionClicked: string) => { + const sLevel = optionClicked; + + // Update the UI state to show pega container + setShowTriplePlayOptions(false); + setShowPega(true); + + // Get the SDK configuration + const sdkConfig = await getSdkConfig(); + let mashupCaseType = sdkConfig.serverConfig.appMashupCaseType; + + // If mashupCaseType is null or undefined, get the first case type from the environment info + if (!mashupCaseType) { + // @ts-ignore - Object is possibly 'null' + const caseTypes: any = PCore.getEnvironmentInfo().environmentInfoObject.pyCaseTypeList; + mashupCaseType = caseTypes[0].pyWorkTypeImplementationClassName; + } + + // Create options object with default values + const options: any = { + pageName: 'pyEmbedAssignment', + startingFields: {} + }; + + // If mashupCaseType is 'DIXL-MediaCo-Work-NewService', add Package field to startingFields + if (mashupCaseType === 'DIXL-MediaCo-Work-NewService') { + options.startingFields.Package = sLevel; + } + + // Create a new case using the mashup API + PCore.getMashupApi() + .createCase(mashupCaseType, PCore.getConstants().APP.APP, options) + .then(() => { + // eslint-disable-next-line no-console + console.log('createCase rendering is complete'); + }); + }; + + function getShowTriplePlayOptionsMarkup() { + const theBanner = ( +
+
+ Combine TV, Internet, and Voice for the best deal +
+
+ ); + + const theOptions = shoppingOptions.map((option, index) => { + return ; + }); + + return ( + <> + {theBanner} +
{theOptions}
+ + ); + } + + return ( + <> + {showTriplePlayOptions ? getShowTriplePlayOptionsMarkup() : null} + {showResolution ? : null} + {showPega ? ( +
+
+ +
+
* - required fields
+
+
+
We need to gather a little information about you.
+
+ +
+
+
+ ) : null} + + ); +} diff --git a/src/samples/Embedded/ResolutionScreen/index.tsx b/src/samples/Embedded/ResolutionScreen/index.tsx new file mode 100644 index 00000000..c3517fa0 --- /dev/null +++ b/src/samples/Embedded/ResolutionScreen/index.tsx @@ -0,0 +1,69 @@ +import makeStyles from '@mui/styles/makeStyles'; + +const useStyles = makeStyles(theme => ({ + resolutionPart: { + display: 'flex', + flexDirection: 'row' + }, + resolutionPartAccompanimentLeft: { + width: '50%', + alignItems: 'center' + }, + resolutionPartAccompanimentRight: { + width: '50%', + alignItems: 'center', + textAlign: 'center' + }, + resolutionPartAccompanimentText: { + fontSize: '28px', + lineHeight: '40px', + padding: '20px 20px', + color: 'darkslategray' + }, + pegaPartAccompanimentImage: { + width: '700px', + margin: '20px', + borderRadius: '10px' + }, + resolutionButton: { + color: 'white', + backgroundColor: theme.palette.warning.main, + fontSize: '25px', + fontWeight: 'bold', + borderRadius: '25px', + border: '0px', + margin: '20px', + padding: '10px 30px' + } +})); + +export default function ResolutionScreen() { + const classes = useStyles(); + + return ( +
+
+
+
+ Welcome! +
+
+ Thanks for selecting a package with us.
+
+ A technician will contact you with in the next couple of days to schedule an installation. +
+
+ If you have any questions, you can contact us directly at 1-800-555-1234 or you can chat with us. +
+
+
+ +
+ +
+
+
+ ); +} diff --git a/src/samples/Embedded/ShoppingOptionCard/index.tsx b/src/samples/Embedded/ShoppingOptionCard/index.tsx new file mode 100644 index 00000000..c15e079e --- /dev/null +++ b/src/samples/Embedded/ShoppingOptionCard/index.tsx @@ -0,0 +1,162 @@ +/* eslint-disable react/button-has-type */ +import makeStyles from '@mui/styles/makeStyles'; + +const useStyles = makeStyles(theme => ({ + swatchHeader: { + display: 'flex', + flexDirection: 'row', + backgroundColor: '#333000' + }, + swatchPackage: { + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-around', + width: '260px', + height: '70px', + backgroundColor: '#333000', + padding: '5px' + }, + swatchPlay: { + letterSpacing: 'normal', + color: 'white', + fontSize: '25px' + }, + swatchLevel: { + letterSpacing: 'normal', + color: 'white', + fontSize: '28px', + fontWeight: 'bold' + }, + swatchChannels: { + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-evenly', + letterSpacing: 'normal', + alignItems: 'center', + backgroundColor: theme.palette.primary.main, + width: '100px' + }, + swatchCount: { + letterSpacing: 'normal', + color: 'white', + fontSize: '40px', + fontWeight: 'bold' + }, + swatchLabel: { + letterSpacing: 'normal', + color: 'white', + fontSize: '17px' + }, + swatchBody: { + letterSpacing: 'normal', + border: '1px solid lightgray', + backgroundColor: '#fafafa', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + paddingBottom: '20px' + }, + swatchBanner: { + letterSpacing: 'normal', + fontWeight: 'bold', + fontSize: '15px', + padding: '5px' + }, + swatchPrice: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center' + }, + swatchFromGroup: { + height: '90px' + }, + swatchFrom: { + color: theme.palette.primary.main, + textAlign: 'right' + }, + swatchCurrency: { + letterSpacing: 'normal', + color: theme.palette.primary.main, + fontSize: '30px', + fontWeight: 'bold', + fontFamily: 'Tahoma' + }, + swatchDollars: { + letterSpacing: 'normal', + color: theme.palette.primary.main, + fontSize: '90px', + fontWeight: 'bold', + fontFamily: 'Tahoma' + }, + swatchCents: { + letterSpacing: 'normal', + color: theme.palette.primary.main, + fontSize: '20px', + fontWeight: 'bold', + fontFamily: 'Tahoma' + }, + swatchMonthly: { + display: 'flex', + flexDirection: 'column' + }, + swatchShopButton: { + color: 'white', + backgroundColor: theme.palette.warning.main, + fontSize: '25px', + fontWeight: 'bold', + borderRadius: '25px', + border: '0px', + margin: '20px', + padding: '10px 30px' + } +})); + +export default function ShoppingOptionCard(props) { + const classes = useStyles(); + + const { play, level, channels, channels_full: channelsFull, banner, price, internetSpeed, calling } = props; + + return ( +
+
+
+
{play}
+
{level}
+
+
+
{channels}
+
Channels
+
+
+
+
{banner}
+
    +
  • {channelsFull} channels plus FREE HD
  • +
  • Thousands of On Demand choices
  • +
  • Watch on the {PCore.getEnvironmentInfo().getApplicationLabel()} App
  • +
  • Up to {internetSpeed} Internet Speeds
  • +
  • Unlimited nationwide calling {calling}
  • +
+ +
+
+
From
+
$
+
+ +
{price.substring(0, price.indexOf('.'))}
+
+
{price.substring(price.indexOf('.') + 1)}
+
for 12 months
+
when bundled
+
+
+
+ +
+
+
+ ); +} diff --git a/src/samples/Embedded/index.tsx b/src/samples/Embedded/index.tsx new file mode 100644 index 00000000..d10b5ad0 --- /dev/null +++ b/src/samples/Embedded/index.tsx @@ -0,0 +1,84 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import { useEffect, useState } from 'react'; +import { CssBaseline, StyledEngineProvider, ThemeProvider } from '@mui/material'; +import { getSdkConfig, loginIfNecessary } from '@pega/auth/lib/sdk-auth-manager'; + +import { getSdkComponentMap } from '@pega/react-sdk-components/lib/bridge/helpers/sdk_component_map'; +import { compareSdkPCoreVersions } from '@pega/react-sdk-components/lib/components/helpers/versionHelpers'; + +import Header from './Header'; +import MainScreen from './MainScreen'; +import localSdkComponentMap from '../../../sdk-local-component-map'; +import { initializeAuthentication } from './utils'; +import { theme } from '../../theme'; + +declare const myLoadMashup: any; + +export default function Embedded() { + const [isLoggedIn, setIsLoggedIn] = useState(false); + const [rootProps, setRootProps] = useState({}); + + useEffect(() => { + initialize(); + }, []); + + const initialize = async () => { + try { + // Add event listener for when logged in and constellation bootstrap is loaded + document.addEventListener('SdkConstellationReady', () => handleSdkConstellationReady()); + + const { authConfig } = await getSdkConfig(); + initializeAuthentication(authConfig); + + // this function will handle login process, and SdkConstellationReady event will be fired once PCore is ready + loginIfNecessary({ appName: 'embedded', mainRedirect: false }); + } catch (error) { + // eslint-disable-next-line no-console + console.error('Something went wrong while login', error); + } + }; + + const initializeRootContainerProps = renderObj => { + const { props } = renderObj; + + setRootProps(props); + }; + + const startMashup = () => { + PCore.onPCoreReady(async renderObj => { + // Check that we're seeing the PCore version we expect + compareSdkPCoreVersions(); + + await getSdkComponentMap(localSdkComponentMap); + // eslint-disable-next-line no-console + console.log(`SdkComponentMap initialized`); + + // Don't call initializeRootContainerProps until SdkComponentMap is fully initialized + initializeRootContainerProps(renderObj); + }); + + myLoadMashup('pega-root', false); // this is defined in bootstrap shell that's been loaded already + }; + + const handleSdkConstellationReady = () => { + setIsLoggedIn(true); + + startMashup(); + }; + + return ( + + + + {isLoggedIn ? ( + <> +
+ + + ) : ( +
Loading...
+ )} + + + ); +} diff --git a/src/samples/Embedded/utils.ts b/src/samples/Embedded/utils.ts new file mode 100644 index 00000000..89181c7f --- /dev/null +++ b/src/samples/Embedded/utils.ts @@ -0,0 +1,61 @@ +import { sdkSetAuthHeader, sdkSetCustomTokenParamsCB } from '@pega/auth/lib/sdk-auth-manager'; + +export const shoppingOptions = [ + { + play: 'Triple Play', + level: 'Basic', + channels: '100+', + channels_full: '100+ (Basic +)', + banner: 'Value package', + price: '99.00', + internetSpeed: '100 Mbps', + calling: '' + }, + { + play: 'Triple Play', + level: 'Silver', + channels: '125+', + channels_full: '125+ (Deluxe)', + banner: 'Most popular', + price: '120.00', + internetSpeed: '300 Mbps', + calling: '' + }, + { + play: 'Triple Play', + level: 'Gold', + channels: '175+', + channels_full: '175+ (Premium)', + banner: 'All the channels you want', + price: '150.00', + internetSpeed: '1 Gbps', + calling: ' & International' + } +]; + +export function initializeAuthentication(sdkConfigAuth) { + if ((sdkConfigAuth.mashupGrantType === 'none' || !sdkConfigAuth.mashupClientId) && sdkConfigAuth.customAuthType === 'Basic') { + // Service package to use custom auth with Basic + const sB64 = window.btoa(`${sdkConfigAuth.mashupUserIdentifier}:${window.atob(sdkConfigAuth.mashupPassword)}`); + sdkSetAuthHeader(`Basic ${sB64}`); + } + + if ((sdkConfigAuth.mashupGrantType === 'none' || !sdkConfigAuth.mashupClientId) && sdkConfigAuth.customAuthType === 'BasicTO') { + const now = new Date(); + const expTime = new Date(now.getTime() + 5 * 60 * 1000); + let sISOTime = `${expTime.toISOString().split('.')[0]}Z`; + const regex = /[-:]/g; + sISOTime = sISOTime.replace(regex, ''); + // Service package to use custom auth with Basic + const sB64 = window.btoa(`${sdkConfigAuth.mashupUserIdentifier}:${window.atob(sdkConfigAuth.mashupPassword)}:${sISOTime}`); + sdkSetAuthHeader(`Basic ${sB64}`); + } + + if (sdkConfigAuth.mashupGrantType === 'customBearer' && sdkConfigAuth.customAuthType === 'CustomIdentifier') { + // Use custom bearer with specific custom parameter to set the desired operator via + // a userIdentifier property. (Caution: highly insecure...being used for simple demonstration) + sdkSetCustomTokenParamsCB(() => { + return { userIdentifier: sdkConfigAuth.mashupUserIdentifier }; + }); + } +}