Skip to content

Commit a1e259a

Browse files
vishalshrm539Sharmasamhere06
authored
Added footer component for the new WSS design and other pieces to get it working. (#596)
* Added footer component for the new WSS design and other pieces to get it working * Updated the directory name * Fixed the path of the overridden component * Added component map for mediaco * Removed an SDK override * sdk-config changes * Moved mediaco comps inside the _samples * Overridden appshell,wssnavbar and banner to implement new material navigation bar * Changed component's directory structure anf few other changes * Reduced padding and the gap between the list links * added child for regular nav bar component --------- Co-authored-by: Sharma <[email protected]> Co-authored-by: samhere06 <[email protected]>
1 parent 240a6dd commit a1e259a

23 files changed

+1282
-13
lines changed

sdk-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"comment_sdk_config": "This is the configuration file for the Angular SDK",
33
"sdk_optional_configs_doc_comment": "See the document(link below) for more details on all the available config settings",
44
"sdk_optional_configs_doc": "https://docs.pega.com/bundle/constellation-sdk/page/constellation-sdks/sdks/configuring-sdk-config-json.html",
5-
"theme": "dark",
5+
"theme": "light",
66
"authConfig": {
77
"authService": "pega",
88

sdk-local-component-map.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
// Statically load all "local" components that aren't yet in the npm package
22

3+
// import sdkMediaCoComponentMap from './src/app/_samples/mediaco/sdk-mediaco-component-map';
34
/* import end - DO NOT REMOVE */
45

56
// localSdkComponentMap is the JSON object where we'll store the components that are
67
// found locally. If not found here, we'll look in the Pega-provided component map
78

89
const localSdkComponentMap = {
10+
// ...sdkMediaCoComponentMap
911
/* map end - DO NOT REMOVE */
1012
};
1113

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<div class="appshell-top">
2+
<div *ngIf="bShowAppShell$ && portalTemplate !== 'wss'">
3+
<component-mapper name="NavBar" [props]="{ pConn$, appName$, pages$, caseTypes$ }"></component-mapper>
4+
</div>
5+
<div *ngIf="bShowAppShell$ && portalTemplate === 'wss'">
6+
<component-mapper
7+
name="WssNavBar"
8+
[props]="{ pConn$, appName$, homePage: pages$[0], pages$: links, caseTypes$, arChildren$, portalLogoImage$: imageURL }"
9+
></component-mapper>
10+
</div>
11+
<div *ngIf="portalTemplate !== 'wss'" class="appshell-main">
12+
<div *ngFor="let kid of arChildren$">
13+
<div *ngIf="kid.getPConnect().getComponentName() == 'ViewContainer'">
14+
<component-mapper name="ViewContainer" [props]="{ pConn$: kid.getPConnect() }"></component-mapper>
15+
</div>
16+
</div>
17+
</div>
18+
19+
<!-- footer is part of WssNavBar component hence commented -->
20+
<!-- <div *ngIf="portalTemplate === 'wss'">
21+
<app-footer></app-footer>
22+
</div> -->
23+
</div>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
.appshell-top {
2+
background-color: var(--app-background-color);
3+
}
4+
5+
.appshell-top-wss {
6+
background-color: var(--app-background-color);
7+
}
8+
9+
.appshell-main {
10+
position: relative;
11+
margin-left: var(--app-nav-width);
12+
min-height: 100vh;
13+
display: block;
14+
}
15+
16+
.appshell-main-wss {
17+
position: relative;
18+
min-height: 100vh;
19+
display: block;
20+
width: 100%;
21+
}
22+
23+
.psdk-icon {
24+
padding: 0rem 0.125rem;
25+
min-width: unset;
26+
}
27+
28+
.progress-box {
29+
display: flex;
30+
flex-direction: column;
31+
justify-content: center;
32+
align-items: center;
33+
height: 100%;
34+
width: 100%;
35+
background-color: var(--app-background-color);
36+
position: fixed;
37+
z-index: 999;
38+
top: 0rem;
39+
left: 0rem;
40+
opacity: 0.5;
41+
}
42+
43+
.progress-spinner {
44+
text-align: center;
45+
}
46+
47+
::ng-deep snack-bar-container.snackbar-newline {
48+
white-space: pre-line;
49+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2+
3+
import { AppShellComponent } from './app-shell.component';
4+
5+
describe('AppShellComponent', () => {
6+
let component: AppShellComponent;
7+
let fixture: ComponentFixture<AppShellComponent>;
8+
9+
beforeEach(waitForAsync(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [AppShellComponent]
12+
}).compileComponents();
13+
}));
14+
15+
beforeEach(() => {
16+
fixture = TestBed.createComponent(AppShellComponent);
17+
component = fixture.componentInstance;
18+
fixture.detectChanges();
19+
});
20+
21+
it('should create', () => {
22+
expect(component).toBeTruthy();
23+
});
24+
});
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
import { Component, OnInit, Input, NgZone, forwardRef, OnDestroy } from '@angular/core';
2+
import { CommonModule } from '@angular/common';
3+
import { MatSnackBarModule, MatSnackBar } from '@angular/material/snack-bar';
4+
import { Subscription } from 'rxjs';
5+
import { FooterComponent } from '../footer/footer.component';
6+
import { AngularPConnectData, AngularPConnectService, Utils } from '@pega/angular-sdk-components';
7+
import { ErrorMessagesService } from '@pega/angular-sdk-components';
8+
import { ComponentMapperComponent } from '@pega/angular-sdk-components';
9+
10+
interface IPage {
11+
classID: string;
12+
pxPageViewIcon: string;
13+
pyClassName: string;
14+
pyLabel: string;
15+
pyRuleName: string;
16+
pyURLContent: string;
17+
}
18+
19+
interface AppShellProps {
20+
// If any, enter additional props that only exist on this component
21+
pages: IPage[];
22+
caseTypes?: object[];
23+
portalLogo: string;
24+
portalName: string;
25+
portalTemplate: string;
26+
readOnly?: boolean;
27+
showAppHeaderBar: boolean;
28+
showAppName: boolean;
29+
}
30+
31+
@Component({
32+
selector: 'app-app-shell',
33+
templateUrl: './app-shell.component.html',
34+
styleUrls: ['./app-shell.component.scss'],
35+
imports: [CommonModule, MatSnackBarModule, FooterComponent, forwardRef(() => ComponentMapperComponent)]
36+
})
37+
export class AppShellComponent implements OnInit, OnDestroy {
38+
@Input() pConn$: typeof PConnect;
39+
40+
// For interaction with AngularPConnect
41+
angularPConnectData: AngularPConnectData = {};
42+
configProps$: AppShellProps;
43+
44+
pages$: IPage[];
45+
caseTypes$?: object[];
46+
arChildren$: any[];
47+
bShowAppShell$ = false;
48+
appName$ = '';
49+
errorMessagesSubscription: Subscription;
50+
sErrorMessages = '';
51+
snackBarRef: any;
52+
bOkDisplayError = false;
53+
portalTemplate: string;
54+
links: any = [];
55+
imageURL: string | Blob;
56+
localizedVal = PCore.getLocaleUtils().getLocaleValue;
57+
58+
constructor(
59+
private angularPConnect: AngularPConnectService,
60+
private erService: ErrorMessagesService,
61+
private snackBar: MatSnackBar,
62+
private ngZone: NgZone,
63+
private utils: Utils
64+
) {}
65+
66+
ngOnInit() {
67+
// First thing in initialization is registering and subscribing to the AngularPConnect service
68+
this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange);
69+
70+
// Then, continue on with other initialization
71+
72+
this.configProps$ = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as AppShellProps;
73+
74+
this.portalTemplate = this.configProps$.portalTemplate;
75+
76+
// making a copy, so can add info
77+
this.pages$ = this.configProps$.pages;
78+
79+
this.links = this.pages$.filter((page, index) => {
80+
return index !== 0;
81+
});
82+
83+
if (this.pages$) {
84+
this.bShowAppShell$ = true;
85+
}
86+
87+
/* TODO: We're setting the `pyPortalTemplate` for now, this would be handled by the CoreJS in the future releases */
88+
if (this.portalTemplate === 'wss') {
89+
PCore.getEnvironmentInfo().setEnvironmentInfo({ ...PCore.getEnvironmentInfo().environmentInfoObject, pyPortalTemplate: 'wss' } as any);
90+
}
91+
92+
// @ts-ignore - Property 'pyCaseTypesAvailableToCreateDP' does not exist on type pxApplication
93+
const caseTypesAvailableToCreateDP = PCore.getEnvironmentInfo().environmentInfoObject?.pxApplication?.pyCaseTypesAvailableToCreateDP;
94+
if (caseTypesAvailableToCreateDP) {
95+
const portalID = this.pConn$.getValue('.pyOwner');
96+
PCore.getDataPageUtils()
97+
.getPageDataAsync(caseTypesAvailableToCreateDP, this.pConn$.getContextName(), {
98+
PortalName: portalID
99+
})
100+
.then((response: any) => {
101+
if (response?.pyCaseTypesAvailableToCreate) {
102+
this.pConn$.replaceState('.pyCaseTypesAvailableToCreate', response.pyCaseTypesAvailableToCreate, {
103+
skipDirtyValidation: true
104+
});
105+
}
106+
});
107+
}
108+
109+
this.caseTypes$ = this.configProps$.caseTypes;
110+
111+
this.arChildren$ = this.pConn$.getChildren();
112+
113+
// handle showing and hiding the progress spinner
114+
this.errorMessagesSubscription = this.erService.getMessage().subscribe(message => {
115+
this.showDismissErrorMessages(message);
116+
});
117+
118+
// cannot call checkAndUpdate becasue first time through, will call updateSelf and that is incorrect (causes issues).
119+
// however, need angularPConnect to be initialized with currentProps for future updates, so calling shouldComponentUpdate directly
120+
// without checking to update here in init, will initialize and this is correct
121+
this.angularPConnect.shouldComponentUpdate(this);
122+
}
123+
124+
ngOnDestroy(): void {
125+
if (this.angularPConnectData.unsubscribeFn) {
126+
this.angularPConnectData.unsubscribeFn();
127+
}
128+
}
129+
130+
// Callback passed when subscribing to store change
131+
onStateChange() {
132+
this.checkAndUpdate();
133+
}
134+
135+
checkAndUpdate() {
136+
// Should always check the bridge to see if the component should
137+
// update itself (re-render)
138+
const bUpdateSelf = this.angularPConnect.shouldComponentUpdate(this);
139+
140+
// ONLY call updateSelf when the component should update
141+
if (bUpdateSelf) {
142+
this.updateSelf();
143+
}
144+
}
145+
146+
updateSelf() {
147+
this.configProps$ = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as AppShellProps;
148+
149+
const showAppName = this.configProps$.showAppName;
150+
const envInfo = PCore.getEnvironmentInfo();
151+
const appNameToDisplay = showAppName ? envInfo.getApplicationLabel() : '';
152+
const portalClass = this.pConn$.getValue('.classID', ''); // 2nd arg empty string until typedef marked correctly
153+
const envPortalName = envInfo.getPortalName();
154+
155+
this.ngZone.run(() => {
156+
// making a copy, so can add info
157+
this.pages$ = this.configProps$.pages;
158+
159+
if (this.pages$) {
160+
this.bShowAppShell$ = true;
161+
}
162+
163+
this.caseTypes$ = this.configProps$.caseTypes;
164+
this.arChildren$ = this.pConn$.getChildren();
165+
});
166+
167+
const portalLogo = this.configProps$.portalLogo;
168+
// using the default icon then fetch it from the static folder (not auth involved)
169+
if (
170+
!portalLogo ||
171+
portalLogo.toLowerCase().includes('pzpega-logo-mark') ||
172+
portalLogo.toLowerCase().includes('py-logo') ||
173+
portalLogo.toLowerCase().includes('py-full-logo')
174+
) {
175+
const portalLogoImage = this.utils.getIconPath(this.utils.getSDKStaticContentUrl()).concat('pzpega-logo-mark.svg');
176+
this.imageURL = portalLogoImage;
177+
}
178+
// not using default icon to fetch it using the way which uses authentication
179+
else {
180+
PCore.getAssetLoader()
181+
.getSvcImageUrl(portalLogo)
182+
.then(data => {
183+
this.imageURL = data;
184+
})
185+
.catch(() => {
186+
console.error(`${this.localizedVal('Unable to load the image for the portal logo/icon with the insName', 'AppShell')}:${portalLogo}`);
187+
});
188+
}
189+
this.appName$ = this.localizedVal(appNameToDisplay || '', '', `${portalClass}!PORTAL!${envPortalName}`.toUpperCase());
190+
}
191+
192+
// fpr show/hiding error messages in the SnackBar component
193+
showDismissErrorMessages(errorMessages: any) {
194+
switch (errorMessages.action) {
195+
case 'update':
196+
// won't show unless publish is turned on
197+
198+
if (this.sErrorMessages.indexOf(errorMessages.actionMessage) < 0) {
199+
this.sErrorMessages = this.sErrorMessages.concat(errorMessages.actionMessage).concat('\n');
200+
201+
if (this.bOkDisplayError) {
202+
const config = { panelClass: ['snackbar-newline'], duration: 5000 };
203+
this.snackBarRef = this.snackBar.open(this.sErrorMessages, 'Ok', config);
204+
}
205+
}
206+
break;
207+
case 'show':
208+
// add error message if not in the list
209+
// won't show unless publish is turned on
210+
211+
if (this.sErrorMessages.indexOf(errorMessages.actionMessage) < 0) {
212+
this.sErrorMessages = this.sErrorMessages.concat(errorMessages.actionMessage).concat('\n');
213+
}
214+
215+
this.bOkDisplayError = true;
216+
217+
if (this.bOkDisplayError) {
218+
const config = { panelClass: ['snackbar-newline'], duration: 5000 };
219+
this.snackBarRef = this.snackBar.open(this.sErrorMessages, 'Ok', config);
220+
}
221+
// this.snackBarRef.afterDismissed().subscribe( info => {
222+
// this.sErrorMessages = "";
223+
// }
224+
// )
225+
break;
226+
case 'dismiss':
227+
// closes snack bar
228+
// turns publish off
229+
// clears out errors
230+
// should be called to dimiss and at "cancel"
231+
if (this.snackBarRef != null) {
232+
this.snackBarRef.dismiss();
233+
this.sErrorMessages = '';
234+
this.bOkDisplayError = false;
235+
}
236+
break;
237+
case 'publish':
238+
// allows errors to be shown, clears out existing ones
239+
// should be turned on at "submit" (finishAssignment, nextAssignment, etc.)
240+
241+
this.bOkDisplayError = true;
242+
this.sErrorMessages = '';
243+
break;
244+
default:
245+
break;
246+
}
247+
}
248+
}

0 commit comments

Comments
 (0)