diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/api-revision-options/api-revision-options.component.spec.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/api-revision-options/api-revision-options.component.spec.ts index 0108ee0a8e8..e53d10d9ef6 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/api-revision-options/api-revision-options.component.spec.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/api-revision-options/api-revision-options.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ApiRevisionOptionsComponent } from './api-revision-options.component'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; -import { ReviewPageModule } from 'src/app/_modules/review-page/review-page.module'; +import { ReviewPageModule } from 'src/app/_modules/review-page.module'; import { SharedAppModule } from 'src/app/_modules/shared/shared-app.module'; describe('ApiRevisionOptionsComponent', () => { diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/code-panel/code-panel.component.spec.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/code-panel/code-panel.component.spec.ts index 83821d933d5..f5007f99937 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/code-panel/code-panel.component.spec.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/code-panel/code-panel.component.spec.ts @@ -5,7 +5,7 @@ import { CommentsService } from 'src/app/_services/comments/comments.service'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; import { SharedAppModule } from 'src/app/_modules/shared/shared-app.module'; -import { ReviewPageModule } from 'src/app/_modules/review-page/review-page.module'; +import { ReviewPageModule } from 'src/app/_modules/review-page.module'; import { MessageService } from 'primeng/api'; describe('CodePanelComponent', () => { 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 new file mode 100644 index 00000000000..5a02b9983ee --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.html @@ -0,0 +1,11 @@ + +
+ + +
+
\ No newline at end of file diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.scss b/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.scss new file mode 100644 index 00000000000..fabd44fa82a --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.scss @@ -0,0 +1,6 @@ +:host ::ng-deep { + .conversation-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/conversation-page/conversation-page.component.spec.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.spec.ts new file mode 100644 index 00000000000..2c7f843af35 --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.spec.ts @@ -0,0 +1,56 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ConversationPageComponent } from './conversation-page.component'; +import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ReviewPageLayoutComponent } from '../shared/review-page-layout/review-page-layout.component'; +import { ConversationsComponent } from '../conversations/conversations.component'; +import { NavBarComponent } from '../shared/nav-bar/nav-bar.component'; +import { ReviewInfoComponent } from '../shared/review-info/review-info.component'; +import { MenuModule } from 'primeng/menu'; +import { FooterComponent } from '../shared/footer/footer.component'; +import { MenubarModule } from 'primeng/menubar'; +import { LanguageNamesPipe } from 'src/app/_pipes/language-names.pipe'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; + +describe('ConversationPageComponent', () => { + let component: ConversationPageComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ + ConversationPageComponent, + ConversationsComponent, + NavBarComponent, + FooterComponent, + ReviewInfoComponent, + ReviewPageLayoutComponent, + LanguageNamesPipe + ], + imports: [ + BrowserAnimationsModule, + HttpClientTestingModule, + MenuModule, + MenubarModule + ], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { + paramMap: convertToParamMap({ reviewId: 'test' }) + } + } + } + ] + }); + fixture = TestBed.createComponent(ConversationPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.ts new file mode 100644 index 00000000000..1129b0c19a8 --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/conversation-page/conversation-page.component.ts @@ -0,0 +1,91 @@ +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 { CommentItemModel } from 'src/app/_models/commentItemModel'; +import { Review } from 'src/app/_models/review'; +import { APIRevision } from 'src/app/_models/revision'; +import { UserProfile } from 'src/app/_models/userProfile'; +import { CommentsService } from 'src/app/_services/comments/comments.service'; +import { ReviewsService } from 'src/app/_services/reviews/reviews.service'; +import { RevisionsService } from 'src/app/_services/revisions/revisions.service'; +import { UserProfileService } from 'src/app/_services/user-profile/user-profile.service'; + +@Component({ + selector: 'app-conversation-page', + templateUrl: './conversation-page.component.html', + styleUrls: ['./conversation-page.component.scss'] +}) +export class ConversationPageComponent { + reviewId : string | null = null; + review : Review | undefined = undefined; + userProfile : UserProfile | undefined; + sideMenu: MenuItem[] | undefined; + comments: CommentItemModel[] = []; + apiRevisions: APIRevision[] = []; + + apiRevisionPageSize = 50; + + private destroy$ = new Subject(); + + constructor(private route: ActivatedRoute, private reviewsService: ReviewsService, private userProfileService: UserProfileService, + private apiRevisionsService: RevisionsService, private commentsService: CommentsService + ) {} + + ngOnInit() { + 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!); + this.loadAPIRevisions(0, this.apiRevisionPageSize); + this.loadComments(); + } + + createSideMenu() { + this.sideMenu = [ + { + icon: 'bi bi-braces', + tooltip: 'API', + command: () => this.openLatestAPIReivisonForReview() + } + ]; + } + + loadReview(reviewId: string) { + this.reviewsService.getReview(reviewId) + .pipe(takeUntil(this.destroy$)).subscribe({ + next: (review: Review) => { + this.review = review; + } + }); + } + + loadAPIRevisions(noOfItemsRead : number, pageSize: number) { + this.apiRevisionsService.getAPIRevisions(noOfItemsRead, pageSize, this.reviewId!, undefined, undefined, + undefined, "createdOn", undefined, undefined, undefined, true) + .pipe(takeUntil(this.destroy$)).subscribe({ + next: (response: any) => { + this.apiRevisions = response.result; + } + }); + } + + loadComments() { + this.commentsService.getComments(this.reviewId!) + .pipe(takeUntil(this.destroy$)).subscribe({ + next: (comments: CommentItemModel[]) => { + this.comments = comments; + } + }); + } + + openLatestAPIReivisonForReview() { + const apiRevision = this.apiRevisions.find(x => x.apiRevisionType === "Automatic") ?? this.apiRevisions[0]; + this.apiRevisionsService.openAPIRevisionPage(apiRevision, this.route); + } +} 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 9964bbfc728..c27fc19f163 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,7 +1,8 @@ -

Conversations

- -

This Review has no comments

- +
+ Loading... +
+

This Review has no comments

+ diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/conversations/conversations.component.spec.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/conversations/conversations.component.spec.ts index 637f0ec8e84..0d52020296c 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/conversations/conversations.component.spec.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/conversations/conversations.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ConversationsComponent } from './conversations.component'; import { SharedAppModule } from 'src/app/_modules/shared/shared-app.module'; import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { ReviewPageModule } from 'src/app/_modules/review-page/review-page.module'; +import { ReviewPageModule } from 'src/app/_modules/review-page.module'; import { APIRevision } from 'src/app/_models/revision'; import { CommentItemModel } from 'src/app/_models/commentItemModel'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; 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 decee123a9b..8b2f42e97c4 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-options/review-page-options.component.spec.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.spec.ts index b0e151c8a4d..bdaac51e37e 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.spec.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.spec.ts @@ -8,7 +8,7 @@ import { HttpErrorInterceptorService } from 'src/app/_services/http-error-interc import { PageOptionsSectionComponent } from '../shared/page-options-section/page-options-section.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { SharedAppModule } from 'src/app/_modules/shared/shared-app.module'; -import { ReviewPageModule } from 'src/app/_modules/review-page/review-page.module'; +import { ReviewPageModule } from 'src/app/_modules/review-page.module'; import { UserProfile } from 'src/app/_models/userProfile'; describe('ReviewPageOptionsComponent', () => { 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 7ffb7a8b2df..ae1de606a4d 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 @@ -1,96 +1,87 @@ - -
- -
-
- - - - - - - - -
-
- - -
- -
-
- -
- -
-
- -
- -
-
-
-
-
-
- + + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+ + [revisionSidePanel]="revisionSidePanel!"> + - + + + + + (numberOfActiveThreadsEmitter)="handleNumberOfActiveThreadsEmitter($event)"> + - \ No newline at end of file diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.scss b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.scss index e9b8f815d47..e50fc439423 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.scss +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.scss @@ -12,46 +12,6 @@ min-width: 0; } - .side-menu { - .p-menuitem-link { - font-size: x-large; - } - - p-badge { - position: relative; - left: -1.2rem; - top: -1.4rem; - } - } - - .p-menu { - background: var(--base-fg-color); - color: var(--base-text-color); - padding: 0.5rem 0; - border: 1px solid var(--border-color); - border-radius: 4px; - width: 3.5rem; - } - - .p-menu ul { - margin: 0; - padding: 0; - list-style: none; - } - - .p-menu .p-menuitem > .p-menuitem-content .p-menuitem-link { - color: var(--base-text-color); - padding: 0.75rem 1rem; - } - - .p-menu .p-menuitem > .p-menuitem-content .p-menuitem-link .p-menuitem-icon { - color: var(--base-text-color);; - font-size: x-large; - &:hover, &:active { - color: var(--primary-color); - } - } - .revisions-sidebar, .conversation-sidebar { width: 75dvw; } diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.spec.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.spec.ts index 8962c30ca51..9d7c4b457d3 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.spec.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.spec.ts @@ -16,7 +16,7 @@ import { ReviewNavComponent } from '../review-nav/review-nav.component'; import { ReviewPageOptionsComponent } from '../review-page-options/review-page-options.component'; import { PageOptionsSectionComponent } from '../shared/page-options-section/page-options-section.component'; import { SharedAppModule } from 'src/app/_modules/shared/shared-app.module'; -import { ReviewPageModule } from 'src/app/_modules/review-page/review-page.module'; +import { ReviewPageModule } from 'src/app/_modules/review-page.module'; import { MessageService } from 'primeng/api'; describe('ReviewPageComponent', () => { 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 e929f583613..e4f656bee79 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 @@ -116,10 +116,12 @@ export class ReviewPageComponent implements OnInit { this.sideMenu = [ { icon: 'bi bi-clock-history', + tooltip: 'Revisions', command: () => { this.revisionSidePanel = !this.revisionSidePanel; } }, { icon: 'bi bi-chat-left-dots', + tooltip: 'Conversations', badge: (this.numberOfActiveConversation > 0) ? this.numberOfActiveConversation.toString() : undefined, command: () => { this.conversationSidePanel = !this.conversationSidePanel; } } @@ -496,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 new file mode 100644 index 00000000000..446b80efef2 --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.html @@ -0,0 +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 new file mode 100644 index 00000000000..31f139d1f8f --- /dev/null +++ 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.spec.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.spec.ts new file mode 100644 index 00000000000..3862ecf9502 --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.spec.ts @@ -0,0 +1,67 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RevisionPageComponent } from './revision-page.component'; +import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ReviewPageLayoutComponent } from '../shared/review-page-layout/review-page-layout.component'; +import { NavBarComponent } from '../shared/nav-bar/nav-bar.component'; +import { RevisionsListComponent } from '../revisions-list/revisions-list.component'; +import { MessageService } from 'primeng/api'; +import { ReviewInfoComponent } from '../shared/review-info/review-info.component'; +import { MenubarModule } from 'primeng/menubar'; +import { MenuModule } from 'primeng/menu'; +import { FooterComponent } from '../shared/footer/footer.component'; +import { ContextMenuModule } from 'primeng/contextmenu'; +import { SidebarModule } from 'primeng/sidebar'; +import { DropdownModule } from 'primeng/dropdown'; +import { LanguageNamesPipe } from 'src/app/_pipes/language-names.pipe'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + +describe('RevisionPageComponent', () => { + let component: RevisionPageComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ + RevisionPageComponent, + NavBarComponent, + FooterComponent, + ReviewInfoComponent, + RevisionsListComponent, + ReviewPageLayoutComponent, + LanguageNamesPipe + ], + imports: [ + BrowserAnimationsModule, + HttpClientTestingModule, + MenubarModule, + MenuModule, + ContextMenuModule, + DropdownModule, + SidebarModule, + ReactiveFormsModule, + FormsModule + ], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { + paramMap: convertToParamMap({ reviewId: 'test' }) + } + } + }, + MessageService + ] + }); + fixture = TestBed.createComponent(RevisionPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); 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 new file mode 100644 index 00000000000..6c97c961bdb --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/revision-page/revision-page.component.ts @@ -0,0 +1,64 @@ +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', + templateUrl: './revision-page.component.html', + 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.route); + } + + 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 2ad39962eba..f0df712fa66 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,6 +1,6 @@ -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 { ActivatedRoute, Router } from '@angular/router'; import { MenuItem, MessageService, SortEvent } from 'primeng/api'; import { FileSelectEvent, FileUpload } from 'primeng/fileupload'; import { Table, TableFilterEvent, TableLazyLoadEvent } from 'primeng/table'; @@ -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; @@ -75,7 +77,7 @@ export class RevisionsListComponent implements OnInit, OnChanges { constructor(private apiRevisionsService: RevisionsService, private userProfileService: UserProfileService, private configService: ConfigService, private fb: FormBuilder, private reviewsService: ReviewsService, - private router: Router, private messageService: MessageService) { } + private route: ActivatedRoute, private messageService: MessageService) { } ngOnInit(): void { this.createRevisionFilters(); @@ -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(); } } @@ -238,14 +240,14 @@ export class RevisionsListComponent implements OnInit, OnChanges { viewDiffOfSelectedAPIRevisions() { if (this.selectedRevisions.length == 2) { - this.apiRevisionsService.openDiffOfAPIRevisions(this.selectedRevisions[0], this.selectedRevisions[1], this.router.url); + this.apiRevisionsService.openDiffOfAPIRevisions(this.selectedRevisions[0], this.selectedRevisions[1], this.route); } } viewRevision(apiRevision: APIRevision) { if (!this.showDeletedAPIRevisions) { - this.apiRevisionsService.openAPIRevisionPage(apiRevision, this.router.url); + this.apiRevisionsService.openAPIRevisionPage(apiRevision, this.route); } } @@ -478,7 +480,7 @@ export class RevisionsListComponent implements OnInit, OnChanges { this.createRevisionSidebarVisible = false; this.creatingRevision = false; this.crButtonText = "Create Review"; - this.apiRevisionsService.openAPIRevisionPage(response, this.router.url); + this.apiRevisionsService.openAPIRevisionPage(response, this.route); } }, error: (error: any) => { diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/shared/comment-thread/comment-thread.component.scss b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/comment-thread/comment-thread.component.scss index 508363e8713..86e0a5b8870 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/shared/comment-thread/comment-thread.component.scss +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/comment-thread/comment-thread.component.scss @@ -89,5 +89,9 @@ .rendered-comment-content { overflow-wrap: anywhere; + pre { + max-width: 100%; + white-space: pre-wrap; + } } } \ No newline at end of file diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/shared/comment-thread/comment-thread.component.spec.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/comment-thread/comment-thread.component.spec.ts index 3f8cf2ccf42..5f725920a22 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/shared/comment-thread/comment-thread.component.spec.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/comment-thread/comment-thread.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CommentThreadComponent } from './comment-thread.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { SharedAppModule } from 'src/app/_modules/shared/shared-app.module'; -import { ReviewPageModule } from 'src/app/_modules/review-page/review-page.module'; +import { ReviewPageModule } from 'src/app/_modules/review-page.module'; import { CommentItemModel } from 'src/app/_models/commentItemModel'; import { CodePanelRowData } from 'src/app/_models/codePanelModels'; diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-info/review-info.component.html b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-info/review-info.component.html index e20aed5fe98..b73e797856a 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-info/review-info.component.html +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-info/review-info.component.html @@ -4,12 +4,12 @@ {{ review?.packageName}} - - + diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-info/review-info.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-info/review-info.component.ts index 29d78ee209f..518b310c608 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-info/review-info.component.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-info/review-info.component.ts @@ -14,7 +14,8 @@ export class ReviewInfoComponent { @Input() activeApiRevisionId: string | null = ''; @Input() diffApiRevisionId: string | null = ''; @Input() userProfile: UserProfile | undefined; - + @Input() showPageoptionsButton: boolean = false; + @Input() review : Review | undefined = undefined; @Output() pageOptionsEmitter : EventEmitter = new EventEmitter(); diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-page-layout/review-page-layout.component.html b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-page-layout/review-page-layout.component.html new file mode 100644 index 00000000000..1e1a117a5d4 --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-page-layout/review-page-layout.component.html @@ -0,0 +1,27 @@ + +
+ +
+
+ + + + + + + + +
+
+ +
+
+
+ \ No newline at end of file diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-page-layout/review-page-layout.component.scss b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-page-layout/review-page-layout.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-page-layout/review-page-layout.component.spec.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-page-layout/review-page-layout.component.spec.ts new file mode 100644 index 00000000000..0a75a2a0c8d --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-page-layout/review-page-layout.component.spec.ts @@ -0,0 +1,41 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ReviewPageLayoutComponent } from './review-page-layout.component'; +import { NavBarComponent } from '../nav-bar/nav-bar.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ReviewInfoComponent } from '../review-info/review-info.component'; +import { MenubarModule } from 'primeng/menubar'; +import { MenuModule } from 'primeng/menu'; +import { FooterComponent } from '../footer/footer.component'; +import { LanguageNamesPipe } from 'src/app/_pipes/language-names.pipe'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; + +describe('ReviewPageLayoutComponent', () => { + let component: ReviewPageLayoutComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ + ReviewPageLayoutComponent, + ReviewInfoComponent, + NavBarComponent, + FooterComponent, + LanguageNamesPipe + ], + imports: [ + BrowserAnimationsModule, + HttpClientTestingModule, + MenubarModule, + MenuModule + ], + }); + fixture = TestBed.createComponent(ReviewPageLayoutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-page-layout/review-page-layout.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-page-layout/review-page-layout.component.ts new file mode 100644 index 00000000000..903e1865c14 --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/shared/review-page-layout/review-page-layout.component.ts @@ -0,0 +1,26 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { MenuItem } from 'primeng/api'; +import { Review } from 'src/app/_models/review'; +import { APIRevision } from 'src/app/_models/revision'; +import { UserProfile } from 'src/app/_models/userProfile'; + +@Component({ + selector: 'app-review-page-layout', + templateUrl: './review-page-layout.component.html', + styleUrls: ['./review-page-layout.component.scss'] +}) +export class ReviewPageLayoutComponent { + @Input() review : Review | undefined = undefined; + @Input() userProfile : UserProfile | undefined; + @Input() sideMenu: MenuItem[] | undefined; + @Input() apiRevisions: APIRevision[] = []; + @Input() activeApiRevisionId: string | null = ''; + @Input() diffApiRevisionId: string | null = ''; + @Input() showPageoptionsButton: boolean = false; + + @Output() pageOptionsEmitter : EventEmitter = new EventEmitter(); + + handlePageOptionsEmitter(showPageOptions: boolean) { + this.pageOptionsEmitter.emit(showPageOptions); + } +} diff --git a/src/dotnet/APIView/ClientSPA/src/app/_helpers/common-helpers.ts b/src/dotnet/APIView/ClientSPA/src/app/_helpers/common-helpers.ts index e70ebfdfe4f..993cfb123ff 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_helpers/common-helpers.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_helpers/common-helpers.ts @@ -59,7 +59,6 @@ export function getTypeClass(type: string): string { return result; } - export function isDiffRow(row: CodePanelRowData) { return row.type === CodePanelRowDatatype.CodeLine && (row.diffKind === DIFF_REMOVED || row.diffKind === DIFF_ADDED); } \ No newline at end of file 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 10ac49c1f6f..c13c925dc9c 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,11 @@ import { ActivatedRoute } from "@angular/router"; import { SCROLL_TO_NODE_QUERY_PARAM } from "./common-helpers"; +export const INDEX_PAGE_NAME = "Index"; +export const REVIEW_PAGE_NAME = "Review"; +export const CONVERSATION_PAGE_NAME = "Conversation"; +export const REVISION_PAGE_NAME = "Revision"; + 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 new file mode 100644 index 00000000000..c49d4650ccf --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_modules/conversation-page.module.ts @@ -0,0 +1,21 @@ +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 { CommonModule } from '@angular/common'; + +const routes: Routes = [ + { path: '', component: ConversationPageComponent } +]; + +@NgModule({ + declarations: [ + ConversationPageComponent + ], + imports: [ + CommonModule, + ReviewPageLayoutModule, + RouterModule.forChild(routes), + ] +}) +export class ConversationPageModule { } diff --git a/src/dotnet/APIView/ClientSPA/src/app/_modules/review-page/review-page.module.ts b/src/dotnet/APIView/ClientSPA/src/app/_modules/review-page.module.ts similarity index 54% rename from src/dotnet/APIView/ClientSPA/src/app/_modules/review-page/review-page.module.ts rename to src/dotnet/APIView/ClientSPA/src/app/_modules/review-page.module.ts index cadbd3ecfa9..a8325f86f2e 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_modules/review-page/review-page.module.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_modules/review-page.module.ts @@ -3,26 +3,15 @@ import { CommonModule } from '@angular/common'; import { RouterModule, Routes } from '@angular/router'; import { ReviewPageComponent } from 'src/app/_components/review-page/review-page.component'; import { ReviewNavComponent } from 'src/app/_components/review-nav/review-nav.component'; -import { ReviewInfoComponent } from 'src/app/_components/shared/review-info/review-info.component'; import { CodePanelComponent } from 'src/app/_components/code-panel/code-panel.component'; -import { CommentThreadComponent } from 'src/app/_components/shared/comment-thread/comment-thread.component'; import { DialogModule } from 'primeng/dialog'; -import { EditorModule } from 'primeng/editor'; -import { PanelModule } from 'primeng/panel'; import { TreeSelectModule } from 'primeng/treeselect'; -import { MenuModule } from 'primeng/menu'; -import { TimelineModule } from 'primeng/timeline'; -import { SharedAppModule } from '../shared/shared-app.module'; import { ButtonModule } from 'primeng/button'; -import { DividerModule } from 'primeng/divider'; import { UiScrollModule } from 'ngx-ui-scroll' ; import { PageOptionsSectionComponent } from 'src/app/_components/shared/page-options-section/page-options-section.component'; -import { ApiRevisionOptionsComponent } from 'src/app/_components/api-revision-options/api-revision-options.component'; -import { MarkdownToHtmlPipe } from 'src/app/_pipes/markdown-to-html.pipe'; -import { EditorComponent } from 'src/app/_components/shared/editor/editor.component'; import { ReviewPageOptionsComponent } from 'src/app/_components/review-page-options/review-page-options.component'; import { InputSwitchModule } from 'primeng/inputswitch'; -import { ConversationsComponent } from 'src/app/_components/conversations/conversations.component'; +import { ReviewPageLayoutModule } from './shared/review-page-layout.module'; const routes: Routes = [ { path: '', component: ReviewPageComponent } @@ -32,29 +21,18 @@ const routes: Routes = [ declarations: [ ReviewPageComponent, ReviewNavComponent, - ReviewInfoComponent, CodePanelComponent, - CommentThreadComponent, - ConversationsComponent, PageOptionsSectionComponent, ReviewPageOptionsComponent, - ApiRevisionOptionsComponent, - MarkdownToHtmlPipe, - EditorComponent, ], imports: [ - SharedAppModule, CommonModule, - EditorModule, - PanelModule, DialogModule, TreeSelectModule, - MenuModule, - TimelineModule, ButtonModule, InputSwitchModule, UiScrollModule, - DividerModule, + ReviewPageLayoutModule, RouterModule.forChild(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 00000000000..7d7b5ba457f --- /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 new file mode 100644 index 00000000000..f414792389f --- /dev/null +++ b/src/dotnet/APIView/ClientSPA/src/app/_modules/shared/review-page-layout.module.ts @@ -0,0 +1,53 @@ +import { NgModule } from '@angular/core'; +import { ReviewInfoComponent } from 'src/app/_components/shared/review-info/review-info.component'; +import { ConversationsComponent } from 'src/app/_components/conversations/conversations.component'; +import { CommentThreadComponent } from 'src/app/_components/shared/comment-thread/comment-thread.component'; +import { ReviewPageLayoutComponent } from 'src/app/_components/shared/review-page-layout/review-page-layout.component'; +import { MarkdownToHtmlPipe } from 'src/app/_pipes/markdown-to-html.pipe'; +import { EditorComponent } from 'src/app/_components/shared/editor/editor.component'; +import { EditorModule } from 'primeng/editor'; +import { PanelModule } from 'primeng/panel'; +import { MenuModule } from 'primeng/menu'; +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 { RevisionPageComponent } from 'src/app/_components/revision-page/revision-page.component'; + +@NgModule({ + declarations: [ + ReviewInfoComponent, + CommentThreadComponent, + ConversationsComponent, + ReviewPageLayoutComponent, + ApiRevisionOptionsComponent, + MarkdownToHtmlPipe, + EditorComponent, + ], + exports: [ + ReviewInfoComponent, + CommentThreadComponent, + ConversationsComponent, + ReviewPageLayoutComponent, + ApiRevisionOptionsComponent, + MarkdownToHtmlPipe, + EditorComponent, + SharedAppModule, + EditorModule, + PanelModule, + MenuModule, + TimelineModule, + DividerModule + ], + imports: [ + CommonModule, + SharedAppModule, + EditorModule, + PanelModule, + MenuModule, + TimelineModule, + DividerModule + ] +}) +export class ReviewPageLayoutModule { } diff --git a/src/dotnet/APIView/ClientSPA/src/app/_modules/shared/shared-app.module.ts b/src/dotnet/APIView/ClientSPA/src/app/_modules/shared/shared-app.module.ts index d6e284d1e42..a7686561f87 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_modules/shared/shared-app.module.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_modules/shared/shared-app.module.ts @@ -21,7 +21,6 @@ import { FileUploadModule } from 'primeng/fileupload'; import { InputTextModule } from 'primeng/inputtext'; import { MessagesModule } from 'primeng/messages'; import { BadgeModule } from 'primeng/badge'; -import { ToastModule } from 'primeng/toast'; @NgModule({ @@ -58,8 +57,8 @@ import { ToastModule } from 'primeng/toast'; InputTextModule, ], imports: [ - BadgeModule, CommonModule, + BadgeModule, ContextMenuModule, TableModule, ChipModule, diff --git a/src/dotnet/APIView/ClientSPA/src/app/_services/reviews/reviews.service.ts b/src/dotnet/APIView/ClientSPA/src/app/_services/reviews/reviews.service.ts index abf4731980a..6d65dc1b37a 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_services/reviews/reviews.service.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_services/reviews/reviews.service.ts @@ -12,7 +12,7 @@ import { ConfigService } from '../config/config.service'; }) export class ReviewsService { baseUrl : string = this.configService.apiUrl + "reviews"; - paginatedResult: PaginatedResult = new PaginatedResult + paginatedResult: PaginatedResult = new PaginatedResult(); constructor(private http: HttpClient, private configService: ConfigService) { } 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 802759127d5..3cae431629c 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 @@ -1,12 +1,13 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable, map } from 'rxjs'; +import { Observable, map, take } from 'rxjs'; import { PaginatedResult } from 'src/app/_models/pagination'; import { APIRevision } from 'src/app/_models/revision'; import { ConfigService } from '../config/config.service'; - +import { ActivatedRoute } from '@angular/router'; +import { INDEX_PAGE_NAME } from 'src/app/_helpers/router-helpers'; @Injectable({ providedIn: 'root' @@ -126,24 +127,30 @@ export class RevisionsService { }); } - openDiffOfAPIRevisions(activeAPIRevision: APIRevision, diffAPIRevision: APIRevision, currentRoute: string) { - const target = (currentRoute.includes("review")) ? '_self' : '_blank'; - - if (activeAPIRevision.files[0].parserStyle === "tree") { - window.open(`/review/${activeAPIRevision.reviewId}?activeApiRevisionId=${activeAPIRevision.id}&diffApiRevisionId=${diffAPIRevision.id}`, target); - } else { - window.open(this.configService.webAppUrl + `Assemblies/Review/${activeAPIRevision.reviewId}?revisionId=${activeAPIRevision.id}&diffOnly=False&doc=False&diffRevisionId=${diffAPIRevision.id}`, target); - } + openDiffOfAPIRevisions(activeAPIRevision: APIRevision, diffAPIRevision: APIRevision, currentRoute: ActivatedRoute) { + this.isIndexPage(currentRoute).pipe(take(1)).subscribe( + isIndexPage => { + const target = isIndexPage ? '_blank' : '_self'; + if (activeAPIRevision.files[0].parserStyle === "tree") { + window.open(`/review/${activeAPIRevision.reviewId}?activeApiRevisionId=${activeAPIRevision.id}&diffApiRevisionId=${diffAPIRevision.id}`, target); + } else { + window.open(this.configService.webAppUrl + `Assemblies/Review/${activeAPIRevision.reviewId}?revisionId=${activeAPIRevision.id}&diffOnly=False&doc=False&diffRevisionId=${diffAPIRevision.id}`, target); + } + } + ); } - openAPIRevisionPage(apiRevision: APIRevision, currentRoute: string) { - const target = (currentRoute.includes("review")) ? '_self' : '_blank'; - - if (apiRevision.files[0].parserStyle === "tree") { - window.open(`/review/${apiRevision.reviewId}?activeApiRevisionId=${apiRevision.id}`, target); - } else { - window.open(this.configService.webAppUrl + `Assemblies/Review/${apiRevision.reviewId}?revisionId=${apiRevision.id}`, target); - } + openAPIRevisionPage(apiRevision: APIRevision, currentRoute: ActivatedRoute) { + this.isIndexPage(currentRoute).pipe(take(1)).subscribe( + isIndexPage => { + const target = isIndexPage ? '_blank' : '_self'; + if (apiRevision.files[0].parserStyle === "tree") { + window.open(`/review/${apiRevision.reviewId}?activeApiRevisionId=${apiRevision.id}`, target); + } else { + window.open(this.configService.webAppUrl + `Assemblies/Review/${apiRevision.reviewId}?revisionId=${apiRevision.id}`, target); + } + } + ); } updateSelectedReviewers(reviewId: string, apiRevisionId: string, reviewers: Set): Observable { @@ -158,4 +165,14 @@ export class RevisionsService { withCredentials: true, }); } + + private isIndexPage(currentRoute: ActivatedRoute): Observable { + return currentRoute.data.pipe( + map(data => { + const pageName = data['pageName']; + console.log("Page Name", pageName); + return data['pageName'] === INDEX_PAGE_NAME; + }) + ); + } } 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 626a1aa8824..b197b394fbc 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/app-routing.module.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/app-routing.module.ts @@ -4,14 +4,17 @@ 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 { CONVERSATION_PAGE_NAME, INDEX_PAGE_NAME, REVIEW_PAGE_NAME, REVISION_PAGE_NAME } from './_helpers/router-helpers'; const routes: Routes = [ - { path: '', component: IndexPageComponent, canActivate: [AuthGuard, FeaturesGuard] }, + { path: '', component: IndexPageComponent, canActivate: [AuthGuard, FeaturesGuard], data: { pageName: INDEX_PAGE_NAME } }, { path: '', runGuardsAndResolvers: 'always', canActivate: [AuthGuard], children: [ - { path: 'review/:reviewId', loadChildren: () => import('./_modules/review-page/review-page.module').then(m => m.ReviewPageModule) }, // Lazy load review page module + { path: 'review/:reviewId', loadChildren: () => import('./_modules/review-page.module').then(m => m.ReviewPageModule), data: { pageName: REVIEW_PAGE_NAME } }, // Lazy load review page module + { path: 'conversation/:reviewId', loadChildren: () => import('./_modules/conversation-page.module').then(m => m.ConversationPageModule), data: { pageName: CONVERSATION_PAGE_NAME} }, + { path: 'revision/:reviewId', loadChildren: () => import('./_modules/revision-page.module').then(m => m.RevisionPageModule), data: { pageName: REVISION_PAGE_NAME } } ] }, { path: '**', component: IndexPageComponent, pathMatch: 'full' } diff --git a/src/dotnet/APIView/ClientSPA/src/app/app.component.scss b/src/dotnet/APIView/ClientSPA/src/app/app.component.scss index 6050d41fa9e..14377090d49 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/app.component.scss +++ b/src/dotnet/APIView/ClientSPA/src/app/app.component.scss @@ -51,6 +51,46 @@ visibility: hidden; } + .side-menu { + .p-menuitem-link { + font-size: x-large; + } + + p-badge { + position: relative; + left: -1.2rem; + top: -1.4rem; + } + } + + .p-menu { + background: var(--base-fg-color); + color: var(--base-text-color); + padding: 0.5rem 0; + border: 1px solid var(--border-color); + border-radius: 4px; + width: 3.5rem; + } + + .p-menu ul { + margin: 0; + padding: 0; + list-style: none; + } + + .p-menu .p-menuitem > .p-menuitem-content .p-menuitem-link { + color: var(--base-text-color); + padding: 0.75rem 1rem; + } + + .p-menu .p-menuitem > .p-menuitem-content .p-menuitem-link .p-menuitem-icon { + color: var(--base-text-color);; + font-size: x-large; + &:hover, &:active { + color: var(--primary-color); + } + } + @include base-tags; @include webkit-scrollbar; } \ No newline at end of file diff --git a/src/dotnet/APIView/ClientSPA/src/app/app.module.ts b/src/dotnet/APIView/ClientSPA/src/app/app.module.ts index 99e75d2b868..0c7cff5a32a 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/app.module.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/app.module.ts @@ -17,6 +17,7 @@ import { CookieService } from 'ngx-cookie-service'; import { SharedAppModule } from './_modules/shared/shared-app.module'; import { HttpErrorInterceptorService } from './_services/http-error-interceptor/http-error-interceptor.service'; import { MessageService } from 'primeng/api'; +import { CommonModule } from '@angular/common'; export function initializeApp(configService: ConfigService) { return (): Observable => { @@ -32,6 +33,7 @@ export function initializeApp(configService: ConfigService) { ], imports: [ SharedAppModule, + CommonModule, AppRoutingModule, BadgeModule, BrowserModule, diff --git a/src/dotnet/APIView/ClientSPA/src/ng-prime-overrides.scss b/src/dotnet/APIView/ClientSPA/src/ng-prime-overrides.scss index 6c2f9a99341..2b8bb594ec7 100644 --- a/src/dotnet/APIView/ClientSPA/src/ng-prime-overrides.scss +++ b/src/dotnet/APIView/ClientSPA/src/ng-prime-overrides.scss @@ -376,7 +376,7 @@ p-contextmenusub { } .p-multiselect-panel .p-multiselect-items .p-multiselect-item:not(.p-highlight):not(.p-disabled).p-focus { - background: var(--base-fg-color);; + background: var(--base-fg-color); } .p-datatable .p-sortable-column.p-highlight, .p-datatable .p-sortable-column.p-highlight:hover { @@ -397,10 +397,22 @@ p-contextmenusub { color: var(--primary-btn-color) !important; } -.p-tooltip .p-tooltip-arrow { +.p-tooltip-top .p-tooltip-arrow { border-top-color: var(--primary-color) !important; } +.p-tooltip-bottom .p-tooltip-arrow { + border-bottom-color: var(--primary-color) !important; +} + +.p-tooltip-left .p-tooltip-arrow { + border-left-color: var(--primary-color) !important; +} + +.p-tooltip-right .p-tooltip-arrow { + border-right-color: var(--primary-color) !important; +} + .p-splitter { border: 0px; background: var(--base-fg-color);