From 181c49eea7359d0dde08c046496e6ef1714f23a0 Mon Sep 17 00:00:00 2001 From: Chidozie Ononiwu Date: Mon, 16 Sep 2024 19:17:59 -0700 Subject: [PATCH] Add Stand alone Conversation Page --- .../conversation-page.component.html | 2 +- .../conversation-page.component.ts | 23 ++-- .../conversations.component.html | 7 +- .../conversations/conversations.component.ts | 100 +++++++++++------- .../review-page/review-page.component.html | 17 +-- .../review-page/review-page.component.ts | 4 +- .../revision-page.component.html | 11 +- .../revision-page.component.scss | 6 ++ .../revision-page/revision-page.component.ts | 54 ++++++++++ .../revisions-list.component.ts | 6 +- .../src/app/_helpers/router-helpers.ts | 2 + .../app/_modules/conversation-page.module.ts | 1 - .../src/app/_modules/review-page.module.ts | 1 - .../src/app/_modules/revision-page.module.ts | 21 ++++ .../shared/review-page-layout.module.ts | 4 +- .../_services/revisions/revisions.service.ts | 6 +- .../ClientSPA/src/app/app-routing.module.ts | 4 +- 17 files changed, 200 insertions(+), 69 deletions(-) create mode 100644 src/dotnet/APIView/ClientSPA/src/app/_modules/revision-page.module.ts diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.html b/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.html index 5687c1d3e15a..5a02b9983ee2 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.html +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.html @@ -1,7 +1,7 @@ -
+
(); constructor(private route: ActivatedRoute, private reviewsService: ReviewsService, private userProfileService: UserProfileService, - private apiRevisionsService: RevisionsService, private commentsService: CommentsService + private apiRevisionsService: RevisionsService, private commentsService: CommentsService, private router: Router ) {} ngOnInit() { - this.userProfileService.getUserProfile().subscribe(); + this.userProfileService.getUserProfile().subscribe( + (userProfile : any) => { + this.userProfile = userProfile; + } + ); this.reviewId = this.route.snapshot.paramMap.get(REVIEW_ID_ROUTE_PARAM); this.createSideMenu(); this.loadReview(this.reviewId!); @@ -47,12 +51,12 @@ export class ConversationPageComponent { { icon: 'bi bi-braces', tooltip: 'API', - //command: () => { this.conversationSidePanel = !this.conversationSidePanel; } + command: () => this.openLatestAPIReivisonForReview() }, { icon: 'bi bi-clock-history', tooltip: 'Revisions', - //command: () => { this.revisionSidePanel = !this.revisionSidePanel; } + command: () => this.router.navigate([`/revision/${this.reviewId}`]) } ]; } @@ -73,7 +77,7 @@ export class ConversationPageComponent { next: (response: any) => { this.apiRevisions = response.result; } - }); + }); } loadComments() { @@ -82,6 +86,11 @@ export class ConversationPageComponent { next: (comments: CommentItemModel[]) => { this.comments = comments; } - }); + }); + } + + openLatestAPIReivisonForReview() { + const apiRevision = this.apiRevisions.find(x => x.apiRevisionType === "Automatic") ?? this.apiRevisions[0]; + this.apiRevisionsService.openAPIRevisionPage(apiRevision, this.router.url); } } diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/conversations/conversations.component.html b/src/dotnet/APIView/ClientSPA/src/app/_components/conversations/conversations.component.html index 76b93cea91ac..c27fc19f163f 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/conversations/conversations.component.html +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/conversations/conversations.component.html @@ -1,5 +1,8 @@ -

This Review has no comments

- +
+ Loading... +
+

This Review has no comments

+ diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/conversations/conversations.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/conversations/conversations.component.ts index decee123a9b6..8b2f42e97c41 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/conversations/conversations.component.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/conversations/conversations.component.ts @@ -28,6 +28,10 @@ export class ConversationsComponent implements OnChanges { commentThreads: Map = new Map(); numberOfActiveThreads: number = 0; + apiRevisionsLoaded = false; + commentsLoaded = false; + isLoading: boolean = true; + destroy$ = new Subject(); constructor(private commentsService: CommentsService, private signalRService: SignalRService) { } @@ -37,62 +41,76 @@ export class ConversationsComponent implements OnChanges { } ngOnChanges(changes: SimpleChanges) { - if (changes['apiRevisions'] || changes['comments']) { - if (this.apiRevisions.length > 0 && this.comments.length > 0) { - this.createCommentThreads(); - } + if (changes['apiRevisions']) { + this.apiRevisionsLoaded = true; + } + + if (changes['comments']) { + this.commentsLoaded = true; + } + + if (this.apiRevisionsLoaded && this.commentsLoaded) { + this.createCommentThreads(); } } createCommentThreads() { - this.commentThreads = new Map(); - this.numberOfActiveThreads = 0; - const apiRevisionInOrder = this.apiRevisions.sort((a, b) => (new Date(b.createdOn) as any) - (new Date(a.createdOn) as any)); - const groupedComments = this.comments - .reduce((acc: { [key: string]: CommentItemModel[] }, comment) => { - const key = comment.elementId; - if (!acc[key]) { - acc[key] = []; - } - acc[key].push(comment); - return acc; - }, {}); + if (this.apiRevisions.length > 0 && this.comments.length > 0) { + this.commentThreads = new Map(); + this.numberOfActiveThreads = 0; + const apiRevisionInOrder = this.apiRevisions.sort((a, b) => (new Date(b.createdOn) as any) - (new Date(a.createdOn) as any)); + const groupedComments = this.comments + .reduce((acc: { [key: string]: CommentItemModel[] }, comment) => { + const key = comment.elementId; + if (!acc[key]) { + acc[key] = []; + } + acc[key].push(comment); + return acc; + }, {}); - for (const elementId in groupedComments) { - if (groupedComments.hasOwnProperty(elementId)) { - const comments = groupedComments[elementId]; - const apiRevisionIds = comments.map(c => c.apiRevisionId); + for (const elementId in groupedComments) { + if (groupedComments.hasOwnProperty(elementId)) { + const comments = groupedComments[elementId]; + const apiRevisionIds = comments.map(c => c.apiRevisionId); - let apiRevisionPostion = Number.MAX_SAFE_INTEGER; + let apiRevisionPostion = Number.MAX_SAFE_INTEGER; - for (const apiRevisionId of apiRevisionIds) { - const apiRevisionIdPosition = apiRevisionInOrder.findIndex(apiRevision => apiRevision.id === apiRevisionId); - if (apiRevisionIdPosition >= 0 && apiRevisionIdPosition < apiRevisionPostion) { - apiRevisionPostion = apiRevisionIdPosition; + for (const apiRevisionId of apiRevisionIds) { + const apiRevisionIdPosition = apiRevisionInOrder.findIndex(apiRevision => apiRevision.id === apiRevisionId); + if (apiRevisionIdPosition >= 0 && apiRevisionIdPosition < apiRevisionPostion) { + apiRevisionPostion = apiRevisionIdPosition; + } } - } - if (apiRevisionPostion >= 0 && apiRevisionPostion < apiRevisionInOrder.length) { - const apiRevisionIdForThread = apiRevisionInOrder[apiRevisionPostion].id; - const codePanelRowData = new CodePanelRowData(); - codePanelRowData.type = CodePanelRowDatatype.CommentThread; - codePanelRowData.comments = comments; - codePanelRowData.isResolvedCommentThread = comments.some(c => c.isResolved); + if (apiRevisionPostion >= 0 && apiRevisionPostion < apiRevisionInOrder.length) { + const apiRevisionIdForThread = apiRevisionInOrder[apiRevisionPostion].id; + const codePanelRowData = new CodePanelRowData(); + codePanelRowData.type = CodePanelRowDatatype.CommentThread; + codePanelRowData.comments = comments; + codePanelRowData.isResolvedCommentThread = comments.some(c => c.isResolved); - if (!codePanelRowData.isResolvedCommentThread) { - this.numberOfActiveThreads++; - } + if (!codePanelRowData.isResolvedCommentThread) { + this.numberOfActiveThreads++; + } - if (this.commentThreads.has(apiRevisionIdForThread)) { - this.commentThreads.get(apiRevisionIdForThread)?.push(codePanelRowData); - } - else { - this.commentThreads.set(apiRevisionIdForThread, [codePanelRowData]); + if (this.commentThreads.has(apiRevisionIdForThread)) { + this.commentThreads.get(apiRevisionIdForThread)?.push(codePanelRowData); + } + else { + this.commentThreads.set(apiRevisionIdForThread, [codePanelRowData]); + } } } } + this.numberOfActiveThreadsEmitter.emit(this.numberOfActiveThreads); + this.isLoading = false; + } + else if (this.apiRevisions.length > 0 && this.comments.length === 0) { + setTimeout(() => { + this.isLoading = false; + }, 1000); } - this.numberOfActiveThreadsEmitter.emit(this.numberOfActiveThreads); } getAPIRevisionWithComments() { diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.html b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.html index ed10f57e3324..ae1de606a4db 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.html +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.html @@ -64,19 +64,24 @@ - + + [revisionSidePanel]="revisionSidePanel!"> + - -

Conversations

- + + + + + (numberOfActiveThreadsEmitter)="handleNumberOfActiveThreadsEmitter($event)"> +
diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts index 212c47eea9aa..e4f656bee796 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts @@ -498,8 +498,8 @@ export class ReviewPageComponent implements OnInit { break; } } - } - + } + ngOnDestroy() { this.workerService.terminateWorker(); this.destroy$.next(); diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.html b/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.html index 8b137891791f..446b80efef2b 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.html +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.html @@ -1 +1,10 @@ - + +
+ + +
+
\ No newline at end of file diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.scss b/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.scss index e69de29bb2d1..31f139d1f8f3 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.scss +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.scss @@ -0,0 +1,6 @@ +:host ::ng-deep { + .revision-panel{ + height: calc(100vh - 130px); + background-color: var(--base-fg-color); + } +} \ No newline at end of file diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.ts index d700984c9d09..a60b66ebe76d 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.ts @@ -1,4 +1,12 @@ import { Component } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { MenuItem } from 'primeng/api'; +import { Subject, takeUntil } from 'rxjs'; +import { REVIEW_ID_ROUTE_PARAM } from 'src/app/_helpers/common-helpers'; +import { Review } from 'src/app/_models/review'; +import { APIRevision } from 'src/app/_models/revision'; +import { ReviewsService } from 'src/app/_services/reviews/reviews.service'; +import { RevisionsService } from 'src/app/_services/revisions/revisions.service'; @Component({ selector: 'app-revision-page', @@ -6,5 +14,51 @@ import { Component } from '@angular/core'; styleUrls: ['./revision-page.component.scss'] }) export class RevisionPageComponent { + reviewId : string | null = null; + review : Review | undefined = undefined; + sideMenu: MenuItem[] | undefined; + apiRevisions: APIRevision[] = []; + private destroy$ = new Subject(); + + constructor(private route: ActivatedRoute, private reviewsService: ReviewsService, private apiRevisionsService: RevisionsService, private router: Router) {} + + ngOnInit() { + this.reviewId = this.route.snapshot.paramMap.get(REVIEW_ID_ROUTE_PARAM); + this.createSideMenu(); + this.loadReview(this.reviewId!); + } + + createSideMenu() { + this.sideMenu = [ + { + icon: 'bi bi-braces', + tooltip: 'API', + command: () => this.openLatestAPIReivisonForReview() + }, + { + icon: 'bi bi-chat-left-dots', + tooltip: 'Conversations', + command: () => this.router.navigate([`/conversation/${this.reviewId}`]) + } + ]; + } + + loadReview(reviewId: string) { + this.reviewsService.getReview(reviewId) + .pipe(takeUntil(this.destroy$)).subscribe({ + next: (review: Review) => { + this.review = review; + } + }); + } + + openLatestAPIReivisonForReview() { + const apiRevision = this.apiRevisions.find(x => x.apiRevisionType === "Automatic") ?? this.apiRevisions[0]; + this.apiRevisionsService.openAPIRevisionPage(apiRevision, this.router.url); + } + + handleApiRevisionsEmitter(apiRevisions: APIRevision[]) { + this.apiRevisions = apiRevisions as APIRevision[]; + } } diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/revisions-list/revisions-list.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/revisions-list/revisions-list.component.ts index 2ad39962ebaf..2f5f6eedc026 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/revisions-list/revisions-list.component.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/revisions-list/revisions-list.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { MenuItem, MessageService, SortEvent } from 'primeng/api'; @@ -23,6 +23,8 @@ export class RevisionsListComponent implements OnInit, OnChanges { @Input() review : Review | undefined = undefined; @Input() revisionSidePanel : boolean = false; + @Output() apiRevisionsEmitter : EventEmitter = new EventEmitter(); + @ViewChild("revisionCreationFileUpload") revisionCreationFileUpload!: FileUpload; assetsPath : string = environment.assetsPath; @@ -152,7 +154,7 @@ export class RevisionsListComponent implements OnInit, OnChanges { this.pagination = response.pagination; this.totalNumberOfRevisions = this.pagination?.totalCount!; } - + this.apiRevisionsEmitter.emit(this.revisions); this.setCreateRevisionLanguageBasedOnReview(); } } diff --git a/src/dotnet/APIView/ClientSPA/src/app/_helpers/router-helpers.ts b/src/dotnet/APIView/ClientSPA/src/app/_helpers/router-helpers.ts index 10ac49c1f6f8..475f1ae30a37 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_helpers/router-helpers.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_helpers/router-helpers.ts @@ -1,6 +1,8 @@ import { ActivatedRoute } from "@angular/router"; import { SCROLL_TO_NODE_QUERY_PARAM } from "./common-helpers"; +export const REVIEW_PAGE_ROUTE_REGEX = "\/[a-z]+?\/[a-zA-Z0-9]+"; + export function getQueryParams(route: ActivatedRoute, excludedKeys: string[] = [SCROLL_TO_NODE_QUERY_PARAM]) { return route.snapshot.queryParamMap.keys.reduce((params: { [key: string]: any; }, key) => { if (!excludedKeys.includes(key)) { diff --git a/src/dotnet/APIView/ClientSPA/src/app/_modules/conversation-page.module.ts b/src/dotnet/APIView/ClientSPA/src/app/_modules/conversation-page.module.ts index baeb0f3d5e23..c49d4650ccfa 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_modules/conversation-page.module.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_modules/conversation-page.module.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ConversationPageComponent } from 'src/app/_components/conversation-page/conversation-page.component'; import { ReviewPageLayoutModule } from './shared/review-page-layout.module'; -import { SharedAppModule } from './shared/shared-app.module'; import { CommonModule } from '@angular/common'; const routes: Routes = [ diff --git a/src/dotnet/APIView/ClientSPA/src/app/_modules/review-page.module.ts b/src/dotnet/APIView/ClientSPA/src/app/_modules/review-page.module.ts index 59697a131a5a..a8325f86f2e9 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_modules/review-page.module.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_modules/review-page.module.ts @@ -11,7 +11,6 @@ import { UiScrollModule } from 'ngx-ui-scroll' ; import { PageOptionsSectionComponent } from 'src/app/_components/shared/page-options-section/page-options-section.component'; import { ReviewPageOptionsComponent } from 'src/app/_components/review-page-options/review-page-options.component'; import { InputSwitchModule } from 'primeng/inputswitch'; -import { SharedAppModule } from './shared/shared-app.module'; import { ReviewPageLayoutModule } from './shared/review-page-layout.module'; const routes: Routes = [ diff --git a/src/dotnet/APIView/ClientSPA/src/app/_modules/revision-page.module.ts b/src/dotnet/APIView/ClientSPA/src/app/_modules/revision-page.module.ts new file mode 100644 index 000000000000..7d7b5ba457f6 --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_modules/revision-page.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ReviewPageLayoutModule } from './shared/review-page-layout.module'; +import { RouterModule, Routes } from '@angular/router'; +import { RevisionPageComponent } from '../_components/revision-page/revision-page.component'; + +const routes: Routes = [ + { path: '', component: RevisionPageComponent } +]; + +@NgModule({ + declarations: [ + RevisionPageComponent + ], + imports: [ + CommonModule, + ReviewPageLayoutModule, + RouterModule.forChild(routes), + ] +}) +export class RevisionPageModule { } diff --git a/src/dotnet/APIView/ClientSPA/src/app/_modules/shared/review-page-layout.module.ts b/src/dotnet/APIView/ClientSPA/src/app/_modules/shared/review-page-layout.module.ts index cbdf21c08e36..f414792389f4 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_modules/shared/review-page-layout.module.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_modules/shared/review-page-layout.module.ts @@ -12,8 +12,8 @@ import { TimelineModule } from 'primeng/timeline'; import { DividerModule } from 'primeng/divider'; import { ApiRevisionOptionsComponent } from 'src/app/_components/api-revision-options/api-revision-options.component'; import { SharedAppModule } from './shared-app.module'; -import { CommonModule } from '@angular/common'; - +import { CommonModule } from '@angular/common'; +import { RevisionPageComponent } from 'src/app/_components/revision-page/revision-page.component'; @NgModule({ declarations: [ diff --git a/src/dotnet/APIView/ClientSPA/src/app/_services/revisions/revisions.service.ts b/src/dotnet/APIView/ClientSPA/src/app/_services/revisions/revisions.service.ts index 802759127d5f..cda335f3d831 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_services/revisions/revisions.service.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_services/revisions/revisions.service.ts @@ -6,6 +6,7 @@ import { Observable, map } from 'rxjs'; import { PaginatedResult } from 'src/app/_models/pagination'; import { APIRevision } from 'src/app/_models/revision'; import { ConfigService } from '../config/config.service'; +import { REVIEW_PAGE_ROUTE_REGEX } from 'src/app/_helpers/router-helpers'; @Injectable({ @@ -127,7 +128,7 @@ export class RevisionsService { } openDiffOfAPIRevisions(activeAPIRevision: APIRevision, diffAPIRevision: APIRevision, currentRoute: string) { - const target = (currentRoute.includes("review")) ? '_self' : '_blank'; + const target = (currentRoute.match(REVIEW_PAGE_ROUTE_REGEX)) ? '_self' : '_blank'; if (activeAPIRevision.files[0].parserStyle === "tree") { window.open(`/review/${activeAPIRevision.reviewId}?activeApiRevisionId=${activeAPIRevision.id}&diffApiRevisionId=${diffAPIRevision.id}`, target); @@ -137,7 +138,8 @@ export class RevisionsService { } openAPIRevisionPage(apiRevision: APIRevision, currentRoute: string) { - const target = (currentRoute.includes("review")) ? '_self' : '_blank'; + console.log(currentRoute); + const target = (currentRoute.match(REVIEW_PAGE_ROUTE_REGEX)) ? '_self' : '_blank'; if (apiRevision.files[0].parserStyle === "tree") { window.open(`/review/${apiRevision.reviewId}?activeApiRevisionId=${apiRevision.id}`, target); diff --git a/src/dotnet/APIView/ClientSPA/src/app/app-routing.module.ts b/src/dotnet/APIView/ClientSPA/src/app/app-routing.module.ts index 1e26fa239dbc..1d48b078d1bd 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/app-routing.module.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/app-routing.module.ts @@ -4,6 +4,7 @@ import { RouterModule, Routes } from '@angular/router'; import { IndexPageComponent } from './_components/index-page/index-page.component'; import { AuthGuard } from './_guards/auth.guard'; import { FeaturesGuard } from './_guards/features.guard'; +import { ReviewPageComponent } from './_components/review-page/review-page.component'; const routes: Routes = [ { path: '', component: IndexPageComponent, canActivate: [AuthGuard, FeaturesGuard] }, @@ -12,7 +13,8 @@ const routes: Routes = [ canActivate: [AuthGuard], children: [ { path: 'review/:reviewId', loadChildren: () => import('./_modules/review-page.module').then(m => m.ReviewPageModule) }, // Lazy load review page module - { path: 'conversation/:reviewId', loadChildren: () => import('./_modules/conversation-page.module').then(m => m.ConversationPageModule) } + { path: 'conversation/:reviewId', loadChildren: () => import('./_modules/conversation-page.module').then(m => m.ConversationPageModule) }, + { path: 'revision/:reviewId', loadChildren: () => import('./_modules/revision-page.module').then(m => m.RevisionPageModule) } ] }, { path: '**', component: IndexPageComponent, pathMatch: 'full' }