Don't wanna be here? Send us removal request.
Text
JSON to Dictionary
[ { "key": "Summary", "value": { "row": 3, "heading": "Summary heading" } }, { "key": "Opening Balance", "value": { "row": 3, "heading": "Opening Balance heading" } } ]
0 notes
Text
Contact Search
export interface ContactSearchRespEntity {
id: string;
firstName: string;
lastName: string;
email: string;
userTypeCode: string;
addedByRoleCode: string;
updatedByRoleCode: string;
userRoles: string[] | null;
csId: string;
caseId: string;
rsvpId: string;
}
export interface ContactResult {
id: string;
firstName: string;
lastName: string;
csId: string;
email?: string;
caseId?: string;
userRoles: string[] | null;
}
----------
<section class="mfp-hide modal-dialog modal-content overlay-def">
<header class="modal-header">
<h2 class="modal-title">{{ data.title }}</h2>
</header>
<div class="modal-body">
<div class="loading" *ngIf="isLoading; else showData">
<mat-progress-spinner mode="indeterminate"></mat-progress-spinner>
</div>
<ng-template #showData>
<table
*ngIf="showedContact && showedContact.length > 0; else noData"
class="table table-condensed table-hover result-table"
>
<tbody>
<tr
tabindex="0"
*ngFor="let contact of showedContact"
[mat-dialog-close]="contact"
>
<th>
{{ contact.lastName }} {{ contact.firstName }}
<ng-container *ngIf="getPrimaryRole(contact)"
>({{ getPrimaryRole(contact) | translate }})</ng-container
>
</th>
<td *ngIf="contact.csId">{{ contact.csId }}</td>
</tr>
</tbody>
</table>
</ng-template>
<ng-template #noData>
<p translate>ui.body.searchContact.noContact</p>
</ng-template>
</div>
<div class="modal-footer">
<button
title="Close overlay"
class="btn btn-sm btn-primary pull-left popup-modal-dismiss"
type="button"
(click)="close()"
translate
>
ui.commUi.btn.close
</button>
<button
title="Close overlay (escape key)"
type="button"
class="mfp-close"
(click)="close()"
>
×
</button>
</div>
</section>
//////////
import { Component, Inject, OnInit } from "@angular/core";
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
import { SecureMsgContactService } from "./service/secure-msg-contact.service";
import { ContactResult } from "./model/contact.model";
@Component({
selector: "app-contact-modal",
templateUrl: "./contact-modal.component.html",
styleUrls: [
"./contact-modal.component.css",
"../../../assets/GCWeb/css/theme.min.css",
"../../../assets/wet-boew/css/noscript.min.css",
],
providers: [SecureMsgContactService],
})
export class ContactModalComponent implements OnInit {
showedContact: ContactResult[] = [];
isLoading: boolean;
constructor(
public dialogRef: MatDialogRef<ContactModalComponent>,
private searchContactService: SecureMsgContactService,
@Inject(MAT_DIALOG_DATA)
public data: {
title: string;
inputPlaceholder: string;
participantCaseId?: string; // Needed to search a recipient in the API
}
) {}
getPrimaryRole(contact: ContactResult): string {
return contact.userRoles && contact.userRoles.length > 0
? `options.RoleCode.${contact.userRoles[0]}`
: "";
}
ngOnInit(): void {
const participantCaseId = this.data.participantCaseId;
if (participantCaseId) {
this.isLoading = true;
this.searchContactService
.getContactByCaseId(participantCaseId)
.subscribe((contacts) => {
this.showedContact = contacts;
this.isLoading = false;
});
}
}
close(): void {
this.dialogRef.close();
}
}
/// Service
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable, map } from 'rxjs';
import { AzureAuthNotificationService } from "src/app/services";
import { EnvUtil } from "src/app/utils";
import { ContactSearchRespEntity, ContactResult } from "../model/contact.model";
@Injectable()
export class SecureMsgContactService {
private commonSegment = "/securemessages";
private baseURLWithCommonSeg =
EnvUtil.getSharedSvcApiURL() + this.commonSegment;
private searchRecipientsUrl = `${this.baseURLWithCommonSeg}/recipients`;
constructor(
private httpClient: HttpClient,
private azureAuthSvc: AzureAuthNotificationService
) {}
getContactByCaseId(caseId: string): Observable<ContactResult[]> {
const CMSUserId = this.azureAuthSvc.getCMSUserID();
if (CMSUserId) {
return this.httpClient
.post<{ data: ContactSearchRespEntity[] }>(this.searchRecipientsUrl, {
caseId,
senderId: CMSUserId,
})
.pipe(
map((res) => {
if (res && res.data) {
return res.data.map((contact) => ({
id: contact.id,
csId: contact.csId,
firstName: contact.firstName,
lastName: contact.lastName,
email: contact.email,
userRoles: contact.userRoles
}));
} else {
return [];
}
})
);
} else {
//:: error handling
throw Error("CMS ID undefined");
}
}
}
0 notes
Text
Type ahead Search
<div class="input-group col-md-5 mrgn-rght-sm">
<label class="wb-inv" for="searchString"
>Search by CSDN, Participant name,To,From</label
>
<input
[(ngModel)]="formModel.searchString"
type="text"
#searchString
name="searchString"
class="form-control"
id="searchString"
placeholder="{{
'ui.body.secMsg.srchstrplaceHolder' | translate
}}"
/>
</div>
Component
----------
@ViewChild("searchString") input: ElementRef;
callApi(searchTerm: string) {
return of(["1", "2", "3"]);
}
ngAfterViewInit() {
this.dataSource.sort = this.sort;
fromEvent<any>(this.input.nativeElement, "keyup")
.pipe(
map((event: any) => event.target.value),
debounceTime(400),
distinctUntilChanged(),
switchMap((search) => this.callApi(search))
)
.subscribe(console.log);
}
0 notes
Text
Re-write URL
default.confg
server { listen 8080; server_name localhost;#access_log /var/log/nginx/host.access.log main; location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri/ /index.html; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #}
}
docker
stage 1
FROM node:14.17.6 as builder WORKDIR /app COPY . . RUN npm install RUN npm run build --prod
stage 2
FROM nginx:alpine
WORKDIR /usr/share/nginx/html RUN rm -rf ./*
COPY --from=builder /app/dist/frontend-vac . EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]
When the container starts, replace the env.js with values from environment variables
CMD ["/bin/sh", "-c", "envsubst /usr/share/nginx/html/assets/javascript/env.template.js /usr/share/nginx/html/assets/javascript/env.js && exec nginx -g 'daemon off;'"]
env.js
(function (window) { window["env"] = window["env"] || {}; // Environment variables window["env"]["bffSvcApiUrl"] = "https://3232"; // "https://localhost:7101/api/portal"; // window["env"]["sharedSvcApiUrl"] = "https://api-dev/api"; // "https://localhost:7221/api"; // window["env"]["bffSvcApiKeyValue"] = "7aae"; window["env"]["sharedSvcApiKeyValue"] = "7aa"; window["env"]["bffSvcScope"] = "api://1ee8d2ed-/api.scope"; window["env"]["sharedSvcScope"] = "api://e7451fa3-/api.scope"; window["env"]["graphApiUrl"] = "https://graph.microsoft.com"; window["env"]["graphSvcScope"] = "user.read"; window["env"]["clientId"] = ""; window["env"]["authority"] = "https://login.microsoftonline.com/57b68a03-66d0834b1516/."; })(this);
(function(window) { window["env"] = window["env"] || {}; // Environment variables window["env"]["bffSvcApiUrl"] = "${BS_API_URL}"; window["env"]["sharedSvcApiUrl"] = "${SS_API_URL}"; window["env"]["bffSvcApiKeyValue"] = "${BS_API_KEY}"; window["env"]["sharedSvcApiKeyValue"] = "${SS_API_KEY}"; window["env"]["bffSvcScope"] ="${BS_SVC_SCOPE}"; window["env"]["sharedSvcScope"] = "${SS_SVC_SCOPE}"; window["env"]["graphApiUrl"] = "${GRAPH_API_URL}"; window["env"]["graphSvcScope"] = "${GRAPH_SVC_SCOPE}"; window["env"]["clientId"] = "${CLIENT_ID}"; window["env"]["authority"] = "${AUTHORITY}"; })(this);
index
!doctype html html lang="en" head meta charset="utf-8" titleVacPortal/title base href="/" meta name="viewport" content="width=device-width, initial-scale=1" link rel="icon" type="image/x-icon" href="favicon.ico" link rel="preconnect" href="https://fonts.gstatic.com" link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet" link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" !-- Load environment variables -- script src="assets/javascript/env.js"/script /head body class="mat-typography" app-root/app-root !-- Start MSAL Integration -- app-redirect/app-redirect !-- Ends MSAL Integration -- /body /html
0 notes
Text
Dropdown from a JSON File
Dropdown from a JSON File
// Note : Do not change the order of the contents of JSON file "CaseTypesJson" below import CaseTypesJson from "../../../assets/dropdowns/case-types.json";
originalCaseTypes: ArrayCaseType = CaseTypesJson;
loadDataWithSelectedCaseType() { switch (this.selectedCase.value) { // "value": "claims" case CaseTypesJson[0].value: { this.setupModelsForClaims(); break; }
onCaseTypeSelection(event: Event) { let selectedStringValue = (event.target as HTMLSelectElement).value;if (selectedStringValue === "") { this.resetCaseTypeControl(); } else { this.selectedCase = JSON.parse(selectedStringValue); this.caseNotes = ""; }
}
JSON
[ { "displayText": "Claims", "value": "claims", "enableOption":true }, { "displayText": "Assessments", "value": "assessments", "enableOption":true }, ] select ngModel #caseType="ngModel" class="form-control" id="caseType" name="caseType" autocomplete="honorific-prefix" required="required" (change)="onCaseTypeSelection($event)" option value="" {{ 'casePage.caseTypes.select' | translate }}/option option class="case-options" *ngFor="let caseType of filteredCaseTypes" [value]="getCaseType(caseType)" [selected]="selectedCase.displayText === caseType.displayText" {{ 'casePage.caseTypes.' + caseType.value | translate }} /option /select app-simple-table [columnDefinitionList]="columnDefinitions" [columnsToDisplay]="displayedColumns" [dataSource]="mappedDS" [ariaLabel]="ariaLabelInfo" [noRecordsFoundMessage]="noRecordsFoundMessage" (tableRowClick)="onTableRowClick($event)" /app-simple-table
0 notes
Text
Common Mat Table
table { width: 100%; }
.textWrap { word-break: break-word; }
.table-cell-padding { padding: 5px; }
html
table mat-table id="mt" [dataSource]="mtDataSource" matSort class="table table-striped table-hover force-style-gcweb-4-0-29" [attr.aria-label]="ariaLabel"
ng-container *ngFor="let col of columnDefinitionList" [matColumnDef]="col.matColumnDef" th mat-header-cell *matHeaderCellDef mat-sort-header [ngClass]="col.cssClass" span translate{{col.headerText}}/span /th td mat-cell *matCellDef="let element" [ngClass]="col.cssClass"{{element[col.matColumnDef]}}/td /ng-container tr mat-header-row *matHeaderRowDef="columnsToDisplay"/tr tr tabindex="0" mat-row *matRowDef="let row; columns: columnsToDisplay" (click)="onDataRowClick(row)"/tr tr class="mat-row" *cdkNoDataRow td class="mat-cell" colspan="9999"{{noRecordsFoundMessage}}/td /tr /table
ts
import { CcColumnDef } from './../../model/common-components/cc-column-def'; import {Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { MatSort } from '@angular/material/sort'; import { MatTableDataSource} from '@angular/material/table';
@Component({ selector: 'app-table', templateUrl: './table.component.html', styleUrls: [''] }) export class TableComponent implements OnInit, AfterViewInit {
mtDataSource = new MatTableDataSourceany(); @ViewChild(MatSort) matSort: MatSort;
private _dataSource: Arrayany = []; get dataSource():Arrayany{ return this._dataSource; } @Input() set dataSource(value: Arrayany){ this._dataSource=value; this.mtDataSource.data=this.dataSource; }
private _columnsToDisplay: string[]; get columnsToDisplay():string[]{ return this._columnsToDisplay; } @Input() set columnsToDisplay(value:string[]){ this._columnsToDisplay=value; }
private _columnDefinitionList: ArrayCcColumnDef; get columnDefinitionList():ArrayCcColumnDef{ return this._columnDefinitionList; } @Input() set columnDefinitionList(value:ArrayCcColumnDef){ this._columnDefinitionList=value; }
@Input() ariaLabel:string; @Input() noRecordsFoundMessage:string;
@Output() tableRowClick: EventEmitterany = new EventEmitterany()
constructor() { } ngAfterViewInit(): void { this.mtDataSource.sort=this.matSort; }
ngOnInit(): void { }
onDataRowClick(data:Event){ this.tableRowClick.next(data); } }
Client
div *ngIf=" caseDetail && caseDetail.phase && participantSessionStorage && filteredCaseTypes && filteredCaseTypes.length 0 " class="container"
h1 tabindex="0" {{ participantSessionStorage?.participantName }} - {{ participantSessionStorage?.displayedCaseId }} /h1 div class="panel panel-default force-style-gcweb-4-0-29" header class="panel-heading" h6 class="panel-title" div *ngIf="caseDetail" span tabindex="0" {{ "phase" | translate }}: {{ caseDetail.phase | titlecase }}/span span tabindex="0" *ngIf="caseDetail.rss" class="pull-right" {{ "assignedRss" | translate }}: {{ caseDetail.rss.firstName }} {{ caseDetail.rss.lastName }}/span /div /h6 /header div class="panel-body" div class="wb-frmvld mrgn-tp-md" form-error [validateForm]="casePageForm" [appFormErrors]="appFormErrors" /form-error form #casePageForm="ngForm" class="form-inline" div class="form-group input-group col-md-9 mrgn-bttm-md" label tabindex="0" for="caseType" class="required" span class="field-name"{{ "caseType" | translate }}/span strong class="required" ({{ "required" | translate }})/strong /label select ngModel #caseType="ngModel" class="form-control" id="caseType" name="caseType" autocomplete="honorific-prefix" required="required" (change)="onCaseTypeSelection($event)" option value=""Select/option option class="case-options" *ngFor="let caseType of filteredCaseTypes" [value]="getCaseType(caseType)" [selected]="selectedCase.displayText === caseType.displayText" {{ caseType.displayText }} /option /select /div div class="table-responsive table table-striped table-hover force-style-gcweb-4-0-29" ng-container *ngIf="selectedCase?.value !== ''" app-simple-table [columnDefinitionList]="columnDefinitions" [columnsToDisplay]="displayedColumns" [dataSource]="mappedDS" [ariaLabel]="ariaLabelInfo" [noRecordsFoundMessage]="noRecordsFoundMessage" (tableRowClick)="onTableRowClick($event)" /app-simple-table /ng-container !-- Need to remove this if condition once the pagination is done for all case types (BE)-- app-pagination *ngIf="selectedCase?.value === 'caseNotes'|| selectedCase?.value === 'assessments' || selectedCase?.value === 'serviceRequests'" [index]="page" [data]="mappedDS" [pageSize]="pageSize" [totalCount]="totalCount" [rulerSize]="ruleSize" (pageChange)="paginate($event)" /app-pagination /div div *ngIf="selectedCase?.value === CASE_NOTES" class="input-group col-md-12 caseNotesTextSection" label for="caseNotesText" class="required" span class="field-name" {{ "addNote" | translate }} /span strong class="required"(required)/strong/label textarea title="{{ 'addCaseNoteHere' | translate }}" id="caseNotesText" name="caseNotesText" (input)="onCaseNotesInput($event)" #caseNotesText="ngModel" [(ngModel)]="caseNotes" required [ng-maxlength]="maxCaseNoteLength" [maxlength]="maxCaseNoteLength + 1" placeholder="{{ 'addCaseNoteHere' | translate }}" class="form-control case-note-text-area" [class]="{ 'case-note-text-area-error': !caseNotesText.valid && caseNotesText.dirty }" /textarea /div div span{{ caseNotes ? caseNotes.length + "/" + maxCaseNoteLength : "" }}/span /div /form /div /div footer *ngIf="selectedCase?.value === CASE_NOTES" class="panel-footer" a href="#" (click)="openAddNoteConfirmation($event)" class="wb-lbx btn btn-primary" {{ "addNote" | translate }}/a /footer
/div /div
ts
import { Sort } from '@angular/material/sort'; import { AppFormError } from './../../common-components/app-form-error/model/app-form-error'; import { ModalDialogComponent, ModalActionButton, ModalActionTypeEnum, ModalAlertCssEnum } from 'src/app/common-components' import { ActivatedRoute, Router } from '@angular/router'; import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
// Note : Do not change the order of the contents of JSON file "CaseTypesJson" below import CaseTypesJson from "../../../assets/dropdowns/case-types.json"; import { MatDialog } from "@angular/material/dialog"; import { ModalDialogConfig } from "src/app/common-components/modal-dialog/modal-dialog-config"; import { Subject, takeUntil } from "rxjs"; import { NgForm, FormGroup } from "@angular/forms"; import { CaseTypesRequest, CaseType, CcColumnDef, } from "src/app/model"; @Component({ selector: "app-case", templateUrl: "./case.component.html", styleUrls: [ "./case.component.css", ], })
export class CaseComponent implements OnInit, OnDestroy { noRecordsFoundMessage = ""; ariaLabelInfo: string; CASE_NOTES = CaseTypesJson[11].value; // Case notes dropdown value maxCaseNoteLength = AppSettings.CASE_NOTE_MAX_LENGTH; caseNotes = ""; caseDetail: CaseDetail; //breadcrumbs displayCaseId: string = ""; participantSessionStorage: ParticipantInfoSessionStorage; // Data for "Drop down" originalCaseTypes: Array = CaseTypesJson; filteredCaseTypes: Array = []; selectedCase: CaseType = ModelUtil.getDefaultModelForCaseType(); columnDefinitions: Array = [];
currentSort: Sort; pageSize: number = AppSettings.DEFAULT_PAGE_SIZE; page = 1; pageIndex: number = 1; ruleSize: number = AppSettings.RULE_SIZE; totalCount: number = 0; paginationDetails = { PageNumber: AppSettings.DEFAULT_PAGE_NUMBER, PageSize: AppSettings.DEFAULT_PAGE_SIZE, };
//#region "Object Properties" displayedColumns: string[]; apiOriginalData: Array; mappedDS: Array = []; //#endregion "Object Properties"
//#region App form erros get appFormErrors(): AppFormError[] { return this.getErrorMessages(); } //#endregion
private readonly _destroying$ = new Subject();
@ViewChild("caseNotesText") caseNotesText: NgForm; @ViewChild("casePageForm") casePageForm: FormGroup;
private roles: string[] = [];
constructor( private router: Router, private route: ActivatedRoute, ) { }
ngOnInit(): void { this.subscribeToLanguageChange(); this.setUpCaseTypes();// get participant session storage this.participantSessionStorage = JSON.parse( this.sessionStorageSrv.getItem( SessionStorageKeys.PARTICIPANT_INFO_SESSION_STORAGE ) ); if (this.participantSessionStorage) { //setting a value for breadcrumbs label this.displayCaseId = this.participantSessionStorage.displayedCaseId; this.breadcrumbService.set("@displayCaseId", this.displayCaseId); // get case detail this.caseService .getCaseDetail(this.participantSessionStorage.caseId) .subscribe({ next: (res) => { if (res) { this.caseDetail = res; } }, error: (error: Error) => { //TODO : Error Handling console.log("test", error); }, }); }
}
ngOnDestroy(): void { this._destroying$.next(undefined); this._destroying$.complete(); }
onCaseTypeSelection(event: Event) { let selectedStringValue = (event.target as HTMLSelectElement).value;if (selectedStringValue === "") { this.resetCaseTypeControl(); } else { this.selectedCase = JSON.parse(selectedStringValue); this.caseNotes = ""; this.loadDataWithSelectedCaseType(); }
}
getCaseType(type: CaseType) { return JSON.stringify(type); }
loadDataWithSelectedCaseType() { switch (this.selectedCase.value) { // "value": "claims" case CaseTypesJson[0].value: { this.setupModelsForClaims(); break; } // "value": "assessments" case CaseTypesJson[2].value: { this.setupModelsForAssessments(); break; } // "value": "rehabilitationPlans" case CaseTypesJson[3].value: { this.setupModelsForRehabilitationPlan(); break; } // "value": "vocationalTrainingPlans" case CaseTypesJson[4].value: { this.setupModelsForVocationalTrainingPlan(); break; } // "value": "progressUpdates" case CaseTypesJson[5].value: { this.setupModelsForPrgUpdates(); break; } // "value": "serviceRequests" case CaseTypesJson[7].value: { this.setupModelsForSrvRequests(); break; } // "value": "closureRequests" case CaseTypesJson[8].value: { this.setupModelsForClosureRequests(); break; } // "value": "authorizationTravelRequests" case CaseTypesJson[10].value: { this.setupModelsForAuthTrvRequests(); break; } // "value": "caseNotes" case CaseTypesJson[11].value: { this.setupModelsForCaseNotes(); break; } // "value": "participantExperienceFeedback" case CaseTypesJson[12].value: { this.setupModelsForPartExpFeedback(); break; } // "value": "referralToRSP" case CaseTypesJson[13].value: { this.setupModelsForReferralToRSP(); break; } // TODO : This would need some thought // when "Select" is selected default: { // this.resetCaseTypeControl(); break; } }
}
resetCaseTypeControl() { this.mappedDS = []; this.ariaLabelInfo = ""; this.noRecordsFoundMessage = ""; this.displayedColumns = []; this.selectedCase = ModelUtil.getDefaultModelForCaseType(); this.caseNotes = ""; }
setNoRecordsFoundMessage(mappedDS: Array) { if (mappedDS && mappedDS.length === 0) { this.noRecordsFoundMessage = this.translateFilter.transform("casePage.noDataRow"); } }
setAriaPropertyForCases(displayText: string) { if (displayText) { this.ariaLabelInfo = displayText; } }
setupModelsForAssessments() { this.columnDefinitions = CaseTypeAssessmentUtil.getColumnDefinitionListForAssessments(); if (this.columnDefinitions) { this.displayedColumns = this.columnDefinitions.map((c) => c.columnName); }if (this.displayedColumns && this.displayedColumns.length > 0) { this.mappedDS = []; // TODO: need to remove hardcoded case id, once we are ready to point to real CMS endpoint for assemments this.caseService .getCaseDetailByType( "8709a1d6-d3a6-41b7-615b-08da433495bd", "assessment", this.paginationDetails ) .subscribe({ next: (res: any) => { if (res && res.data && res.data.length > 0) { this.mappedDS = CaseTypeAssessmentUtil.transformDSForAssessment( res.data ); this.pageSize = res.pageSize; this.totalCount = res.totalRecords; this.paginate(this.pageIndex); } this.setNoRecordsFoundMessage(this.mappedDS); this.setAriaPropertyForCases(CaseTypesJson[2].displayText); }, error: (error: Error) => { //TODO : Error Handling console.log("test", error); }, }); }
}
util
import { DateTimeUtil } from 'src/app//utils/datetime-util'; import { CcColumnDef } from 'src/app/model'; export class CaseTypeAssessmentUtil{// TODO: need to remove this once BE integration is done static getData() { return [{ "requestNumber": 'RQ-001', "service": 'IVA', "referralDate": DateTimeUtil.isoToYYYY_MM_DD(new Date()), "reports": 'TBD'},{ "requestNumber": 'RQ-002', "service": 'IVA', "referralDate": DateTimeUtil.isoToYYYY_MM_DD(new Date()), "reports": 'TBD' }]; } static getColumnDefinitionListForAssessments(): Array<CcColumnDef>{ return [ { "cssClass": "widthFlex1", "matColumnDef": "requestNumber", "headerText": "caseTypeAssessmentTable.requestNumber", "columnName": "requestNumber" }, { "cssClass": "widthFlex1", "matColumnDef": "service", "headerText": "caseTypeAssessmentTable.service", "columnName": "service" }, { "cssClass": "widthFlex1", "matColumnDef": "referralDate", "headerText": "caseTypeAssessmentTable.referralDate", "columnName": "referralDate" }, { "cssClass": "widthFlex1", "matColumnDef": "reports", "headerText": "caseTypeAssessmentTable.reports", "columnName": "reports" } ]; } static transformDSForAssessment(apiOriginalData:Array<any>) : Array<any>{ let dataArray:Array<any>=[]; if(apiOriginalData && apiOriginalData.length>0){ dataArray = apiOriginalData.map((row) => { return { "requestNumber": row.requestNumber, "service": row.service, "referralDate": DateTimeUtil.isoToYYYY_MM_DD(new Date(row.referralDate)), "reports": row.reports, "assessmentId": row.assessmentId }; }); } return dataArray; }
}
int
export interface CcColumnDef { cssClass: string; matColumnDef:string; headerText?:string; columnName: string; }
export interface CaseType { displayText: string; value: string; enableOption?:boolean; }
0 notes
Text
Routing & Relative
{
path: "secureMessages",
component: SecureMsgShellComponent,
children: [
{
path: "",
component: SecureMsgListComponent,
},
{
path: "newSecureMsg",
component: NewSecureMsgComponent,
},
{
path: "replySecureMsg",
component: ReplySecureMsgComponent,
},
{
path: ":id",
component: SecureMsgDetailsComponent,
},
],
},
--------
routeToSecureMsgDetails = (msgId: string) => {
this.router.navigate([msgId], { relativeTo: this.route });
};
0 notes
Text
AZ B2B - AD - MSAL- Ang 13
Index.html
!-- !doctype html html lang="en" head meta charset="utf-8" titleVacPortal/title base href="/" meta name="viewport" content="width=device-width, initial-scale=1" link rel="icon" type="image/x-icon" href="favicon.ico" link rel="preconnect" href="https://fonts.gstatic.com" link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet" link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" !-- Load environment variables -- script src="assets/javascript/env.js"/script /head body class="mat-typography" app-rootapp-root !-- Start MSAL Integration -- app-redirect app-redirect !-- Ends MSAL Integration -- body
html
App Module
//#region "MSAL Integration" // import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { IPublicClientApplication, PublicClientApplication, InteractionType, BrowserCacheLocation, LogLevel } from '@azure/msal-browser'; import { MsalGuard, MsalInterceptor, MsalBroadcastService, MsalInterceptorConfiguration, MsalModule, MsalService, MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG, MsalGuardConfiguration, MsalRedirectComponent,ProtectedResourceScopes } from '@azure/msal-angular';
const isIE = window.navigator.userAgent.indexOf("MSIE ") -1 || window.navigator.userAgent.indexOf("Trident/") -1; // Remove this line to use Angular Universal
export function loggerCallback(logLevel: LogLevel, message: string) { console.log(message); }
export function MSALInstanceFactory(): IPublicClientApplication { return new PublicClientApplication({ auth: { clientId: environment.clientId, authority: environment.authority, redirectUri: '/', postLogoutRedirectUri: '/' }, cache: { cacheLocation: BrowserCacheLocation.LocalStorage, storeAuthStateInCookie: isIE, // set to true for IE 11. Remove this line to use Angular Universal }, system: { loggerOptions: { loggerCallback, logLevel: LogLevel.Info, piiLoggingEnabled: false } } }); }
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration { const protectedResourceMap = new Mapstring, Arraystring|ProtectedResourceScopes | null([ [EnvUtil.getGraphSvcApiURL(), [environment.graphSvcScope]], [EnvUtil.getBFFServicesApiURL(), [environment.bffSvcScope]], [EnvUtil.getSharedSvcApiURL(), [environment.sharedSvcScope]], ]); return { interactionType: InteractionType.Redirect, protectedResourceMap }; }
export function MSALGuardConfigFactory(): MsalGuardConfiguration { return { interactionType: InteractionType.Redirect, authRequest: { scopes: [] }, loginFailedRoute: '/' }; }
//#endregion "MSAL Integration"
export function rootLoaderFactory(http: HttpClient) { return new TranslateHttpLoader(http); }
@NgModule({ declarations: [ AppComponent, ], imports: [ BrowserModule, FormsModule, AppRoutingModule, BrowserAnimationsModule, FormsModule, ReactiveFormsModule, ], providers: [ { provide: HTTP_INTERCEPTORS, useClass: HeaderInterceptor, multi: true }, //#region "MSAL Integration" { provide: HTTP_INTERCEPTORS, useClass: MsalInterceptor, multi: true }, { provide: MSAL_INSTANCE, useFactory: MSALInstanceFactory }, { provide: MSAL_GUARD_CONFIG, useFactory: MSALGuardConfigFactory }, { provide: MSAL_INTERCEPTOR_CONFIG, useFactory: MSALInterceptorConfigFactory }, MsalService, MsalGuard, MsalBroadcastService, //#endregion "MSAL Integration" TranslatePipe ], bootstrap: [AppComponent,MsalRedirectComponent], }) export class AppModule {}
Routing
// Starts - MSAL Integration import { BrowserUtils } from "@azure/msal-browser"; import { MsalGuard, MsalRedirectComponent } from "@azure/msal-angular"; // Ends - MSAL Integration
const routes: Routes = [ { path: "", pathMatch: "full", redirectTo: "/userWelcome", }, { path: "userWelcome", component: UserWelcomeComponent, canActivate: [MsalGuard], data: { breadcrumb: "breadcrumb.userWelcome", }, }, { path: "auth", component: MsalRedirectComponent, }, { path: "**", component: PageNotFoundComponent, }, ];
@NgModule({ imports: [ RouterModule.forRoot(routes, { initialNavigation: "enabled" }), ], exports: [RouterModule], }) export class AppRoutingModule {}
App Comp
//#region "MSAL Integration" import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration, } from "@azure/msal-angular"; import { AccountInfo, EventMessage, EventType, InteractionStatus, RedirectRequest, } from "@azure/msal-browser"; //#endregion "MSAL Integration"
@Component({ selector: "app-root", templateUrl: "./app.component.html", styleUrls: [ "./app.component.css", ], }) export class AppComponent implements OnInit, OnDestroy { //#region "MSAL Integration" isIframe = false; isUserLoggedIn = false; private readonly _destroying$ = new Subjectvoid(); //#endregion "MSAL Integration"
constructor( @Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, ) { }
ngOnInit(): void { this.isIframe = window !== window.parent && !window.opener; // Remove this line to use Angular Universal // this.setLoginDisplay(); //:: Do not remove pleasethis.msalAuthService.instance.enableAccountStorageEvents(); // Optional - This will enable ACCOUNT_ADDED and ACCOUNT_REMOVED events emitted when a user logs in or out of another tab or window this.msalBroadcastService.msalSubject$ .pipe( filter( (msg: EventMessage) = msg.eventType === EventType.ACCOUNT_ADDED || msg.eventType === EventType.ACCOUNT_REMOVED ) ) .subscribe((_result: EventMessage) = { if (this.msalAuthService.instance.getAllAccounts().length === 0) { window.location.pathname = "/"; } else { this.setLoginDisplay(); } }); this.msalBroadcastService.inProgress$ .pipe( filter( (status: InteractionStatus) = status === InteractionStatus.None ), takeUntil(this._destroying$) ) .subscribe(() = { this.checkAndSetActiveAccount(); this.setLoginDisplay(); });
}
setLoginDisplay() { this.isUserLoggedIn = this.msalAuthService.instance.getAllAccounts().length 0; this.azureAuthNotSrv.pushLoginDisplayNotification(this.isUserLoggedIn); }
checkAndSetActiveAccount() { /** * If no active account set but there are accounts signed in, sets first account to active account * To use active account set here, subscribe to inProgress$ first in your component * Note: Basic usage demonstrated. Your app may require more complicated account selection logic */ let activeAccount = this.msalAuthService.instance.getActiveAccount();if ( !activeAccount && this.msalAuthService.instance.getAllAccounts().length 0 ) { let accounts = this.msalAuthService.instance.getAllAccounts(); if (accounts && accounts.length 0) { this.msalAuthService.instance.setActiveAccount(accounts[0]); this.setAzureADID(accounts[0]); } }
}
loginRedirect() { if (this.msalGuardConfig.authRequest) { this.msalAuthService.loginRedirect({ …this.msalGuardConfig.authRequest, } as RedirectRequest); } else { this.msalAuthService.loginRedirect(); } }
// Event handling from Header Component onLoginButtonClick(data: boolean) { if (data) { this.loginRedirect(); } }
logoutRedirect() { this.msalAuthService.logoutRedirect(); }
// Event handling from Header Component onLogoutButtonClick(data: boolean) { if (data) { this.logoutRedirect(); } }
ngOnDestroy(): void { this._destroying$.next(undefined); this._destroying$.complete(); } }
Welcome
// Start - MSAL Integration import { MsalBroadcastService, MsalService } from "@azure/msal-angular"; import { AuthenticationResult, EventMessage, EventType, } from "@azure/msal-browser"; // Ends - MSAL Integration
@Component({ selector: "app-welcome", templateUrl: "./welcome.component.html", styleUrls: [ "./welcome.component.css", ], }) export class WelcomeComponent implements OnInit { doubleSpace = " "; userFullName$: Observablestring; isUserLoggedIn$: Observableboolean;
constructor( private authService: MsalService, private msalBroadcastService: MsalBroadcastService, private router: Router ) {}
ngOnInit(): void { this.setUpLoginDisplaySubscribtion(); this.setUpUserFullNameSubscribtion(); this.setUpLastloginSubscription(); this.userRole = this.azureAuthSvc.getUserRoles()[0];//:: MSAL Integration this.msalBroadcastService.msalSubject$ .pipe( filter((msg: EventMessage) = msg.eventType === EventType.LOGIN_SUCCESS) ) .subscribe((result: EventMessage) = { const payload = result.payload as AuthenticationResult; this.authService.instance.setActiveAccount(payload.account); });
}
setUpLoginDisplaySubscribtion() { this.isUserLoggedIn$ = this.azureAuthNotSrv.getLoginDisplayNotification(); }
}
welcome html
div *ngIf="(isUserLoggedIn$ | async) as isUserLoggedIn" div *ngIf="isUserLoggedIn && (userFullName$ | async) as userFullName" h1 property="name" id="wb-cont" translatelandingPage.welcomespan{{ ' ' + userFullName}}/span/h1 div class="panel panel-default" div class="panel-body" /div /div /div /div
Header
section id="wb-so" div class="container" div class="row" div class="col-md-12" *ngIf="(isUserLoggedIn$ | async) === true; else elseBlock" span *ngIf="(userFullName$ | async) as userFullName" class="mrgn-rght-md h4" translate header.loginLabel /span button class="btn btn-primary" type="button" (click)="logoutButtonClick($event)" signOut /button /div ng-template #elseBlock div class="col-md-12" button class="btn btn-primary" type="button" *ngIf="(isUserLoggedIn$ | async) === false" (click)="loginButtonClick($event)" login /button /div /ng-template /div /div
/section
@Component({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.css', }) export class HeaderComponent implements OnInit { @Output() logIn: EventEmitterany = new EventEmitterany(); @Output() logout: EventEmitterany = new EventEmitterany();
isUserLoggedIn$:Observableboolean;
constructor( private azureAuthNotSrv:AzureAuthNotificationService, private router: Router ) { }
ngOnInit(): void { this.isUserLoggedIn$=this.azureAuthNotSrv.getLoginDisplayNotification(); }
loginButtonClick(data:Event){ this.logIn.next(true); }
logoutButtonClick(data:Event){ this.logout.next(true); }
}
package
{ "name": "portal", "version": "0.0.1", "scripts": { "ng": "ng", "start": "ng serve --port=8200", "build": "ng build --configuration production", "watch": "ng build --watch --configuration development", "test": "ng test" }, "private": true, "dependencies": { "@angular/animations": "~13.1.0", "@angular/cdk": "^13.2.0", "@angular/common": "~13.1.0", "@angular/compiler": "~13.1.0", "@angular/core": "~13.1.0", "@angular/forms": "~13.1.0", "@angular/material": "^13.2.0", "@angular/platform-browser": "~13.1.0", "@angular/platform-browser-dynamic": "~13.1.0", "@angular/router": "~13.1.0", "@azure/msal-angular": "^2.2.0", "@azure/msal-browser": "^2.23.0", "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", "jquery": "^3.6.0", "object-mapper": "^6.2.0", "rxjs": "~7.4.0", "scriptjs": "^2.5.9", "tslib": "^2.3.0", "xng-breadcrumb": "^6.8.3", "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~13.1.2", "@angular/cli": "~13.1.2", "@angular/compiler-cli": "~13.1.0", "@types/jasmine": "~3.10.0", "@types/scriptjs": "^0.0.2", "jasmine-core": "~3.10.0", "karma": "~6.3.0", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.1.0", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "~1.7.0", "typescript": "~4.5.2" } }
0 notes
Text
Angular JSON
{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "cli": { "analytics": "d447b6ac-9993-4dce-9a0c-c5c64ffa6f18" }, "version": 1, "newProjectRoot": "projects", "projects": { "vac-portal": { "projectType": "application", "schematics": { "@schematics/angular:application": { "strict": true } }, "root": "", "sourceRoot": "src", "prefix": "app", "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "dist/frontend", "index": "src/index.html", "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", "assets": [ "src/favicon.ico", "src/assets" ], "styles": [ "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "src/styles.css" ], "scripts": [ "src/assets/GCWeb/js/jquery.js" ] }, "configurations": { "production": { "budgets": [ { "type": "initial", "maximumWarning": "1mb", "maximumError": "15mb" }, { "type": "anyComponentStyle", "maximumWarning": "400kb", "maximumError": "600kb" } ], "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ], "outputHashing": "all" }, "development": { "buildOptimizer": false, "optimization": false, "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true } }, "defaultConfiguration": "development" }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", "configurations": { "production": { "browserTarget": "vac-portal:build:production" }, "development": { "browserTarget": "vac-portal:build:development" } }, "defaultConfiguration": "development" }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { "browserTarget": "vac-portal:build" } }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { "main": "src/test.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.spec.json", "karmaConfig": "karma.conf.js", "assets": [ "src/favicon.ico", "src/assets" ], "styles": [ "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "src/styles.css" ], "scripts": [], "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/app/test/mock-environment.ts" } ] } } } } }, "defaultProject": "vac-portal" }
0 notes
Text
TS Config Settings
/* To learn more about this file see: https://angular.io/config/tsconfig. / { "compileOnSave": false, "compilerOptions": { "baseUrl": "./", "outDir": "./dist/out-tsc", "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "sourceMap": true, "declaration": false, "downlevelIteration": true, "experimentalDecorators": true, "strictPropertyInitialization": false, "noImplicitAny": false, "resolveJsonModule": true, "esModuleInterop": true, "moduleResolution": "node", "importHelpers": true, "target": "es2017", "module": "es2020", "lib": [ "es2020", "dom" ], "paths": { "@angular/": [ "./node_modules/@angular/*" ] } }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, "strictInjectionParameters": true, "strictInputAccessModifiers": true, "strictTemplates": true } }
0 notes
Text
Interceptors
export class HeaderInterceptor implements HttpInterceptor {
private clonedReq: HttpRequest;
private headers = new HttpHeaders();
private azureADID: string | null;
constructor(private azureAuthSvc: AzureAuthNotificationService) {}
intercept(
request: HttpRequest,
next: HttpHandler
): Observable> {
this.headers = request.headers;
if (InterceptorUtil.IsAPIURLIsForThisApp(request)) {
this.azureADID = this.azureAuthSvc.getAzureADID();
this.buildCommonHeaders();
this.buildHeadersForPCPSvc(request);
this.buildHeadersForSharedSvc(request);
}
if (this.headers) {
this.clonedReq = request.clone({ headers: this.headers });
}
return next.handle(this.clonedReq);
}
private buildHeadersForSharedSvc(request: HttpRequest) {
if (InterceptorUtil.IsApiUrlIsForSharedSvc(request)) {
this.headers = InterceptorUtil.addPortalRequestOriginHeader(
this.headers,
RequestOriginEnum.PCP_PORTAL
);
}
}
private buildHeadersForPCPSvc(request: HttpRequest) {
if (InterceptorUtil.IsApiUrlIsForPCPSvc(request)) {
this.headers = InterceptorUtil.addCustomApiKeyInHeaderforPCP(
this.headers
);
}
}
private buildCommonHeaders() {
if (this.azureADID && this.azureADID.length > 0) {
this.headers = InterceptorUtil.addPortalUserHeader(
this.headers,
this.azureADID
);
} else {
// As we have auth guard, this scenario would not appear
console.log("Azure AD ID is null in interceptor");
}
}
}
------
UTIL
-------
export class InterceptorUtil {
static IsApiUrlIsForPCPSvc(request: HttpRequest): boolean {
return request.url.startsWith(EnvUtil.getBFFServicesApiURL());
}
static IsApiUrlIsForSharedSvc(request: HttpRequest): boolean {
return request.url.startsWith(EnvUtil.getSharedSvcApiURL());
}
static IsAPIURLIsForThisApp(request: HttpRequest): boolean {
let bffApiResult = this.IsApiUrlIsForPCPSvc(request);
let SharedApiResult = this.IsApiUrlIsForSharedSvc(request);
if (bffApiResult === false && SharedApiResult === false) {
return false;
}
return true;
}
static addCustomApiKeyInHeaderforPCP(header: HttpHeaders): HttpHeaders {
return header.set(
CustomHeaderEnum.API_KEY_NAME,
EnvUtil.getBFFServicesApiKeyValue()
);
}
static addCustomApiKeyInHeaderforShared(header: HttpHeaders): HttpHeaders {
return header.set(
CustomHeaderEnum.API_KEY_NAME,
EnvUtil.getSharedServicesApiKeyValue()
);
}
// x-portal-user
static addPortalUserHeader(
header: HttpHeaders,
azureAdId: string
): HttpHeaders {
return header.set(CustomHeaderEnum.PORTAL_USER, azureAdId);
}
// rsvp-req-org
static addPortalRequestOriginHeader(
header: HttpHeaders,
requestOrigin: string
): HttpHeaders {
return header.set(CustomHeaderEnum.PORTAL_REQUEST_ORIGIN, requestOrigin);
}
}
0 notes
Text
How to make a service call after every 5 minutes?
const obj$ = interval(1000);
obj$.subscribe(()=>{
this.getCount();
});
getCount() {
this.notSvc.getNotificationsCount().subscribe(() => {
},error => {
//TODO Error handling
console.log(error);
});
}
0 notes
Text
Angular Post Request
getMenu(reqBody:any):Observable{
const request=new HttpRequest('Post',this.endPoint,reqBody);
return this.httpClient.request(request)
.pipe(
map((resp:any) =>{
if(resp && resp.body){
return resp.body ;
}
})
);
}
export interface MenuResponse {
name: string;
route: string;
toolTip: string;
icon?: string;
}
ngOnInit(): void {
const userLoggedIn$ = this.azureAuthSvc.getLoginDisplayNotification();
const role$ = this.azureAuthSvc.getUserRolesNotification();
combineLatest([userLoggedIn$, role$]).subscribe(([userLoggedIn, roles]) => {
if (userLoggedIn && roles && roles.length > 0) {
const menuRequest: MenuRequest | null = this.menuSvc.getMenuRequestBody(
roles,
PortalIDEnum.VAC
);
if (menuRequest) {
this.menuSvc
.getMenu(menuRequest)
.subscribe((data: MenuResponse[]) => {
if (data && data.length > 0) {
this.vacMenu = this.menuSvc.getMenuItemCollection(data);
this.vacMenu[4].route = "secureMessages"; //TODO: TO BE Removed
}
});
}
}
});
}
1 note
·
View note