Tumgik
angking · 1 year
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
angking · 2 years
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
angking · 2 years
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
angking · 2 years
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
angking · 2 years
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
angking · 2 years
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
angking · 2 years
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
angking · 2 years
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
angking · 2 years
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
angking · 2 years
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
angking · 2 years
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
angking · 2 years
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