Skip to content

Commit 5e7b166

Browse files
authored
refactor: initialization of Pega logic in Embedded sample (#578)
1 parent 3c7bd20 commit 5e7b166

File tree

8 files changed

+286
-143
lines changed

8 files changed

+286
-143
lines changed

package-lock.json

Lines changed: 13 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,8 @@
5757
"@pega/cosmos-react-condition-builder": "^8.4.1",
5858
"@pega/cosmos-react-core": "^8.4.1",
5959
"@pega/cosmos-react-work": "^8.4.1",
60-
"@pega/react-sdk-components": "~0.25.4",
60+
"@pega/react-sdk-components": "~0.25.5",
6161
"@storybook/react-webpack5": "^8.6.14",
62-
"@types/react-dom": "^18.3.7",
63-
"@types/styled-components": "^5.1.34",
6462
"lodash.get": "^4.4.2",
6563
"lodash.isequal": "^4.5.0",
6664
"react": "^18.3.1",
@@ -77,7 +75,7 @@
7775
"@pega/constellationjs": "~25.1.0",
7876
"@pega/dx-component-builder-sdk": "~24.2.14",
7977
"@pega/pcore-pconnect-typedefs": "~4.1.0",
80-
"@pega/react-sdk-overrides": "~0.25.4",
78+
"@pega/react-sdk-overrides": "~0.25.5",
8179
"@playwright/test": "^1.54.2",
8280
"@storybook/addon-essentials": "^8.6.14",
8381
"@storybook/addon-links": "^8.6.14",
@@ -94,6 +92,8 @@
9492
"@types/jest": "^30.0.0",
9593
"@types/lodash.isequal": "^4.5.8",
9694
"@types/react": "^18.3.23",
95+
"@types/react-dom": "^18.3.7",
96+
"@types/styled-components": "^5.1.35",
9797
"@typescript-eslint/eslint-plugin": "^6.15.0",
9898
"@typescript-eslint/parser": "^8.34.1",
9999
"caniuse-lite": "^1.0.30001735",

src/common.css

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
.psdk-root {
2+
background-color: var(--app-form-bg-color);
3+
}
4+
15
.portal-load-error {
26
height: 100vh;
37
width: 100vw;
@@ -37,7 +41,7 @@
3741

3842
.logout-btn:hover {
3943
background: var(--app-warning-color-dark);
40-
color: var(--app-form-color);
44+
color: var(--app-form-bg-color);
4145
}
4246

4347
.portals-list {
@@ -58,8 +62,3 @@
5862
[class*='MuiInputAdornment-positionStart'] {
5963
margin-right: 0 !important;
6064
}
61-
62-
/* Support for google map autocomplete */
63-
.pac-container {
64-
z-index: 2147483647;
65-
}

src/samples/Embedded/MainScreen/index.tsx

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { useEffect, useMemo, useState } from 'react';
1+
import { useEffect, useState } from 'react';
22
import { getSdkConfig } from '@pega/auth/lib/sdk-auth-manager';
33
import { makeStyles } from '@mui/styles';
44

5-
import StoreContext from '@pega/react-sdk-components/lib/bridge/Context/StoreContext';
6-
import createPConnectComponent from '@pega/react-sdk-components/lib/bridge/react_pconnect';
5+
import { usePegaAuth } from '../context/PegaAuthProvider';
6+
import { usePega } from '../context/PegaReadyContext';
77

88
import ShoppingOptionCard from '../ShoppingOptionCard';
99
import ResolutionScreen from '../ResolutionScreen';
@@ -105,45 +105,35 @@ const useStyles = makeStyles(theme => ({
105105
pegaForm: {}
106106
}));
107107

108-
function RootComponent(props) {
109-
const PegaConnectObj = createPConnectComponent();
110-
const thePConnObj = <PegaConnectObj {...props} />;
108+
export default function MainScreen() {
109+
const { isAuthenticated } = usePegaAuth();
110+
const { isPegaReady, PegaContainer, createCase } = usePega();
111111

112-
/**
113-
* NOTE: For Embedded mode, we add in displayOnlyFA to our React context
114-
* so it is available to any component that may need it.
115-
* VRS: Attempted to remove displayOnlyFA but it presently handles various components which
116-
* SDK does not yet support, so all those need to be fixed up before it can be removed.
117-
* To be done in a future sprint.
118-
*/
119-
const contextValue = useMemo(() => {
120-
return { store: PCore.getStore(), displayOnlyFA: true };
121-
}, [PCore.getStore()]);
122-
123-
return <StoreContext.Provider value={contextValue}>{thePConnObj}</StoreContext.Provider>;
124-
}
125-
126-
export default function MainScreen(props) {
127112
const classes = useStyles();
113+
128114
const [showPega, setShowPega] = useState(false);
129115
const [showLandingPage, setShowLandingPage] = useState(true);
130116
const [showResolution, setShowResolution] = useState(false);
131117

132118
useEffect(() => {
133-
// Subscribe to the EVENT_CANCEL event to handle the assignment cancellation
134-
PCore.getPubSubUtils().subscribe(PCore.getConstants().PUB_SUB_EVENTS.EVENT_CANCEL, () => cancelAssignment(), 'cancelAssignment');
135-
// Subscribe to the END_OF_ASSIGNMENT_PROCESSING event to handle assignment completion
136-
PCore.getPubSubUtils().subscribe(
137-
PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.END_OF_ASSIGNMENT_PROCESSING,
138-
() => assignmentFinished(),
139-
'endOfAssignmentProcessing'
140-
);
119+
if (isPegaReady) {
120+
// Subscribe to the EVENT_CANCEL event to handle the assignment cancellation
121+
PCore.getPubSubUtils().subscribe(PCore.getConstants().PUB_SUB_EVENTS.EVENT_CANCEL, () => cancelAssignment(), 'cancelAssignment');
122+
123+
// Subscribe to the END_OF_ASSIGNMENT_PROCESSING event to handle assignment completion
124+
PCore.getPubSubUtils().subscribe(
125+
PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.END_OF_ASSIGNMENT_PROCESSING,
126+
() => assignmentFinished(),
127+
'endOfAssignmentProcessing'
128+
);
129+
}
130+
141131
return () => {
142132
// unsubscribe to the events
143133
PCore.getPubSubUtils().unsubscribe(PCore.getConstants().PUB_SUB_EVENTS.EVENT_CANCEL, 'cancelAssignment');
144134
PCore.getPubSubUtils().unsubscribe(PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.END_OF_ASSIGNMENT_PROCESSING, 'endOfAssignmentProcessing');
145135
};
146-
}, []);
136+
}, [isPegaReady]);
147137

148138
const cancelAssignment = () => {
149139
setShowLandingPage(true);
@@ -159,14 +149,8 @@ export default function MainScreen(props) {
159149
setShowLandingPage(false);
160150
setShowPega(true);
161151
const sdkConfig = await getSdkConfig();
162-
let mashupCaseType = sdkConfig.serverConfig.appMashupCaseType;
163-
// If mashupCaseType is null or undefined, get the first case type from the environment info
164-
if (!mashupCaseType) {
165-
const caseTypes = PCore.getEnvironmentInfo()?.environmentInfoObject?.pyCaseTypeList;
166-
if (caseTypes && caseTypes.length > 0) {
167-
mashupCaseType = caseTypes[0].pyWorkTypeImplementationClassName;
168-
}
169-
}
152+
const mashupCaseType = sdkConfig.serverConfig.appMashupCaseType;
153+
170154
let selectedPhoneGUID = '';
171155
const phoneName = optionClicked ? optionClicked.trim() : '';
172156
switch (phoneName) {
@@ -198,12 +182,10 @@ export default function MainScreen(props) {
198182
console.warn(`Unexpected case type: ${mashupCaseType}. PhoneModelss field not set.`);
199183
}
200184

201-
// Create a new case using the mashup API
202-
PCore.getMashupApi()
203-
.createCase(mashupCaseType, PCore.getConstants().APP.APP, options)
204-
.then(() => {
205-
console.log('createCase rendering is complete');
206-
});
185+
// Call the createCase function from context to create a new case using the mashup API
186+
createCase(mashupCaseType, options).then(() => {
187+
console.log('createCase rendering is complete');
188+
});
207189
};
208190

209191
function renderLandingPage() {
@@ -244,13 +226,15 @@ export default function MainScreen(props) {
244226
return (
245227
<div className={classes.pegaViewContainer}>
246228
<div className={classes.pegaForm} id='pega-part-of-page'>
247-
<RootComponent {...props} />
229+
<PegaContainer />
248230
<br />
249231
</div>
250232
</div>
251233
);
252234
}
253235

236+
if (!isAuthenticated) return <div style={{ textAlign: 'center' }}>Loading...</div>;
237+
254238
return (
255239
<div className={classes.appContainer}>
256240
{showLandingPage && renderLandingPage()}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { createContext, useContext, useEffect, useState } from 'react';
2+
import { getSdkConfig, loginIfNecessary, sdkSetAuthHeader, sdkSetCustomTokenParamsCB } from '@pega/auth/lib/sdk-auth-manager';
3+
4+
interface AuthContextType {
5+
isAuthenticated: boolean;
6+
}
7+
8+
const UserAuthContext = createContext<AuthContextType | undefined>(undefined);
9+
10+
const PegaAuthProvider: React.FC<React.PropsWithChildren> = ({ children }) => {
11+
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
12+
13+
const initialize = async () => {
14+
try {
15+
// Add event listener for when logged in and constellation bootstrap is loaded
16+
document.addEventListener('SdkConstellationReady', () => {
17+
setIsAuthenticated(true);
18+
});
19+
20+
// Initialize authentication settings
21+
await initializeAuthentication();
22+
23+
// this function will handle login process, and SdkConstellationReady event will be fired once PCore is ready
24+
loginIfNecessary({ appName: 'embedded', mainRedirect: false });
25+
} catch (error) {
26+
console.error('Something went wrong while login', error);
27+
}
28+
};
29+
30+
useEffect(() => {
31+
initialize();
32+
}, []);
33+
34+
return <UserAuthContext.Provider value={{ isAuthenticated }}>{children}</UserAuthContext.Provider>;
35+
};
36+
37+
export default PegaAuthProvider;
38+
39+
export const usePegaAuth = (): AuthContextType => {
40+
const context = useContext(UserAuthContext);
41+
if (context === undefined) {
42+
throw new Error('useAuth must be used within an AuthProvider');
43+
}
44+
return context;
45+
};
46+
47+
async function initializeAuthentication() {
48+
const { authConfig } = await getSdkConfig();
49+
50+
if ((authConfig.mashupGrantType === 'none' || !authConfig.mashupClientId) && authConfig.customAuthType === 'Basic') {
51+
// Service package to use custom auth with Basic
52+
const sB64 = window.btoa(`${authConfig.mashupUserIdentifier}:${window.atob(authConfig.mashupPassword)}`);
53+
sdkSetAuthHeader(`Basic ${sB64}`);
54+
}
55+
56+
if ((authConfig.mashupGrantType === 'none' || !authConfig.mashupClientId) && authConfig.customAuthType === 'BasicTO') {
57+
const now = new Date();
58+
const expTime = new Date(now.getTime() + 5 * 60 * 1000);
59+
let sISOTime = `${expTime.toISOString().split('.')[0]}Z`;
60+
const regex = /[-:]/g;
61+
sISOTime = sISOTime.replace(regex, '');
62+
// Service package to use custom auth with Basic
63+
const sB64 = window.btoa(`${authConfig.mashupUserIdentifier}:${window.atob(authConfig.mashupPassword)}:${sISOTime}`);
64+
sdkSetAuthHeader(`Basic ${sB64}`);
65+
}
66+
67+
if (authConfig.mashupGrantType === 'customBearer' && authConfig.customAuthType === 'CustomIdentifier') {
68+
// Use custom bearer with specific custom parameter to set the desired operator via
69+
// a userIdentifier property. (Caution: highly insecure...being used for simple demonstration)
70+
sdkSetCustomTokenParamsCB(() => {
71+
return { userIdentifier: authConfig.mashupUserIdentifier };
72+
});
73+
}
74+
}

0 commit comments

Comments
 (0)