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
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
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
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
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
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
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
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
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
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
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
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