From dba544c4f2df39982e3b5db354b5d22fbfff6f0b Mon Sep 17 00:00:00 2001 From: Oleksandr Bohuslavskyi Date: Mon, 1 Apr 2024 16:52:34 -0500 Subject: [PATCH] Added Search and server-side paging to a UserManagement component. Fixed DSS-218(error message persists even if all clear) --- .../src/app/common/models/dropdown-option.ts | 2 +- .../app/common/services/user-data.service.ts | 8 +- .../compliance-notice.component.ts | 3 + .../delisting-request.component.ts | 4 + .../user-management.component.html | 8 +- .../user-management.component.scss | 7 +- .../user-management.component.ts | 83 +++++++++++++------ frontend/src/styles.scss | 20 +++++ 8 files changed, 105 insertions(+), 30 deletions(-) diff --git a/frontend/src/app/common/models/dropdown-option.ts b/frontend/src/app/common/models/dropdown-option.ts index ce6e1ddb..119993b1 100644 --- a/frontend/src/app/common/models/dropdown-option.ts +++ b/frontend/src/app/common/models/dropdown-option.ts @@ -1,4 +1,4 @@ export interface DropdownOption { label: string, - value: number, + value: any, } diff --git a/frontend/src/app/common/services/user-data.service.ts b/frontend/src/app/common/services/user-data.service.ts index 86fd048a..f41d204e 100644 --- a/frontend/src/app/common/services/user-data.service.ts +++ b/frontend/src/app/common/services/user-data.service.ts @@ -12,7 +12,13 @@ export class UserDataService { constructor(private httpClient: HttpClient) { } getCurrentUser(): Observable { - return this.httpClient.get(`${environment.API_HOST}/users/currentuser`) + return this.httpClient.get(`${environment.API_HOST}/users/currentuser`); + } + + getUsers(status: string, search: string, organizationId: number | null, pageSize: number, pageNumber: number, orderBy: string, direction: 'asc' | 'desc'): Observable { + return this.httpClient.get( + `${environment.API_HOST}/users?status=${status ?? ''}&search=${search ?? ''}&organizationId=${organizationId ?? ''}&pageSize=${pageSize ?? ''}&pageNumber=${pageNumber ?? ''}&orderBy=${orderBy ?? ''}&direction=${direction ?? ''}` + ); } updateIsEnabled(userIdentityId: number, isEnabled: boolean, updDtm: string): Observable { diff --git a/frontend/src/app/features/components/compliance-notice/compliance-notice.component.ts b/frontend/src/app/features/components/compliance-notice/compliance-notice.component.ts index b82a684b..b203b9f2 100644 --- a/frontend/src/app/features/components/compliance-notice/compliance-notice.component.ts +++ b/frontend/src/app/features/components/compliance-notice/compliance-notice.component.ts @@ -88,6 +88,8 @@ export class ComplianceNoticeComponent implements OnInit { } onPreview(): void { + this.messages = []; + if (this.myForm.valid) { this.delistingService.complianceNoticePreview(this.prepareFormModel(this.myForm)) .subscribe( @@ -109,6 +111,7 @@ export class ComplianceNoticeComponent implements OnInit { onSubmit(comment: string, textAreaElement: HTMLTextAreaElement): void { this.messages = []; + if (this.myForm.valid) { const model: ComplianceNotice = this.prepareFormModel(this.myForm); model.comment = comment; diff --git a/frontend/src/app/features/components/delisting-request/delisting-request.component.ts b/frontend/src/app/features/components/delisting-request/delisting-request.component.ts index 07e742c7..92a98da3 100644 --- a/frontend/src/app/features/components/delisting-request/delisting-request.component.ts +++ b/frontend/src/app/features/components/delisting-request/delisting-request.component.ts @@ -76,6 +76,8 @@ export class DelistingRequestComponent implements OnInit { } onPreview(): void { + this.messages = []; + if (this.myForm.valid) { this.delistingService.delistingRequestPreview(this.prepareFormModel(this.myForm)) .subscribe( @@ -96,6 +98,8 @@ export class DelistingRequestComponent implements OnInit { } onSubmit(): void { + this.messages = []; + if (this.myForm.valid) { this.delistingService.createDelistingRequest(this.prepareFormModel(this.myForm)) .subscribe({ diff --git a/frontend/src/app/features/components/user-management/user-management.component.html b/frontend/src/app/features/components/user-management/user-management.component.html index 15ec67bb..b55cb52c 100644 --- a/frontend/src/app/features/components/user-management/user-management.component.html +++ b/frontend/src/app/features/components/user-management/user-management.component.html @@ -16,7 +16,7 @@
-
@@ -85,8 +85,10 @@ - + diff --git a/frontend/src/app/features/components/user-management/user-management.component.scss b/frontend/src/app/features/components/user-management/user-management.component.scss index 8e8bc458..45ee4f8b 100644 --- a/frontend/src/app/features/components/user-management/user-management.component.scss +++ b/frontend/src/app/features/components/user-management/user-management.component.scss @@ -1,7 +1,7 @@ :host { width: 100%; height: 100%; - padding: 24px; + padding: 22px; background-color: #FFFFFF; * { @@ -84,6 +84,11 @@ } } + p-paginator { + display: flex; + justify-content: end; + } + p-dialog { .actions { display: flex; diff --git a/frontend/src/app/features/components/user-management/user-management.component.ts b/frontend/src/app/features/components/user-management/user-management.component.ts index 35b7c35e..9f1816d6 100644 --- a/frontend/src/app/features/components/user-management/user-management.component.ts +++ b/frontend/src/app/features/components/user-management/user-management.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { Dropdown, DropdownModule } from 'primeng/dropdown'; import { DropdownOption } from '../../../common/models/dropdown-option'; import { CommonModule } from '@angular/common'; @@ -8,7 +8,7 @@ import { RequestAccessService } from '../../../common/services/request-access.se import { AccessRequestTableItem } from '../../../common/models/access-request-table-item'; import { PagingResponse, PagingResponsePageInfo } from '../../../common/models/paging-response'; import { DialogModule } from 'primeng/dialog'; -import { PaginatorModule } from 'primeng/paginator'; +import { Paginator, PaginatorModule } from 'primeng/paginator'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { DateFormatPipe } from '../../../common/pipes/date-format.pipe'; import { InputSwitchModule } from 'primeng/inputswitch'; @@ -40,16 +40,19 @@ import { ToastModule } from 'primeng/toast'; styleUrl: './user-management.component.scss' }) export class UserManagementComponent implements OnInit { + @ViewChild("paginator") paginator!: Paginator; + statuses = new Array(); organizationTypes = new Array(); organizations = new Array(); + organizationDropdown = new Array(); accessRequests = new Array(); currentPage!: PagingResponsePageInfo; searchParams = { searchStatus: '', - searchOrganization: '', + searchOrganization: null, searchTerm: '', } @@ -62,9 +65,6 @@ export class UserManagementComponent implements OnInit { myForm!: FormGroup; - first = 0; - total = 120; - constructor(private requestAccessService: RequestAccessService, private userDataService: UserDataService, private fb: FormBuilder, private confirmationService: ConfirmationService, private messageService: MessageService) { } ngOnInit(): void { @@ -73,7 +73,13 @@ export class UserManagementComponent implements OnInit { } onSearchModelChanged(): void { + if (this.paginator) { + this.paginator.changePage(0); + if (this.paginator.empty()) { + this.getUsers(); + } + } } onApprovePopup(accessRequest: AccessRequestTableItem): void { @@ -87,10 +93,10 @@ export class UserManagementComponent implements OnInit { } onPageChange(pagingEvent: any): void { - console.log('pagingEvent', pagingEvent); + this.getUsers(pagingEvent.page + 1); } - onApprove(orgTypeIdElem: Dropdown, orgId: Dropdown): void { + onApprove(_orgTypeIdElem: Dropdown, orgId: Dropdown): void { const model = { userIdentityId: this.currentTableItem.userIdentityId, representedByOrganizationId: orgId.value, @@ -104,13 +110,16 @@ export class UserManagementComponent implements OnInit { this.requestAccessService.approveAccessRequest(model).subscribe({ next: () => { this.getUsers(); - this.onPopupClose() + this.onPopupClose(); }, error: (msg) => { if (msg.error.status === 422) { this.handleConcurrencyError(msg); + } else { + this.showErrorToast('Error', 'Unable to change user\'s access status. Check console for additional details') } - this.onPopupClose() + console.error(msg); + this.onPopupClose(); } }); } @@ -124,13 +133,17 @@ export class UserManagementComponent implements OnInit { this.requestAccessService.denyAccessRequest(model).subscribe({ next: () => { this.getUsers(); - this.onPopupClose() + this.onPopupClose(); }, error: (msg) => { if (msg.error.status === 422) { this.handleConcurrencyError(msg); } - this.onPopupClose() + else { + this.showErrorToast('Error', 'Unable to change user\'s access status. Check console for additional details') + } + console.error(msg); + this.onPopupClose(); } }); } @@ -170,8 +183,14 @@ export class UserManagementComponent implements OnInit { next: () => { this.getUsers(); }, - error: (error) => { - console.error(error); + error: (msg) => { + if (msg.error.status === 422) { + this.handleConcurrencyError(msg); + } + else { + this.showErrorToast('Error', 'Unable to change user\'s active status. Check console for additional details') + } + console.error(msg); } }) accessRequest.isEnabled = !accessRequest.isEnabled; @@ -197,42 +216,58 @@ export class UserManagementComponent implements OnInit { private initData(): void { this.userDataService.getStatuses().subscribe({ next: (data: Array) => { - this.statuses = data; - } - }) + this.statuses = [{ label: 'All', value: '' }, ...data]; + }, + error: (error) => { + this.showErrorToast('Error', 'Unable to retrieve Statuses. Check console for additional details') + console.error(error); + }, + }); this.requestAccessService.getOrganizations().subscribe({ next: (data) => { this.organizations = data; + this.organizationDropdown = [{ label: 'All', value: '' }, ...data]; }, error: (error: any) => { + this.showErrorToast('Error', 'Unable to retrieve Organizations. Check console for additional details') console.log(error); } - }) + }); this.requestAccessService.getOrganizationTypes().subscribe({ next: (data) => { this.organizationTypes = data; }, error: (error: any) => { - console.log(error); + this.showErrorToast('Error', 'Unable to retrieve Organization Types. Check console for additional details') + console.error(error); } - }) + }); this.getUsers(); } - private getUsers(): void { - this.requestAccessService.getAccessRequests({ pageNumber: this.currentPage?.pageNumber || 1, pageSize: 10 }).subscribe({ + private getUsers(selectedPageNumber?: number): void { + const status = this.searchParams.searchStatus; + const search = this.searchParams.searchTerm; + const organizationId = this.searchParams.searchOrganization; + const pageSize = this.currentPage?.pageSize || 10; + const pageNumber = selectedPageNumber ?? (this.currentPage?.pageNumber || 0); + const orderBy = ''; + const direction = 'desc'; + + this.userDataService.getUsers(status, search, organizationId, pageSize, pageNumber, orderBy, direction).subscribe({ next: (response: PagingResponse) => { this.accessRequests = response.sourceList; this.currentPage = response.pageInfo; - console.log(response); + }, error: (error: any) => { + this.showErrorToast('Error', 'Unable to retrieve users. Check console for additional details') console.error(error); } - }) + }); } private showErrorToast(title: string, errorMsg: string) { diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 0e751dcd..9d4b0357 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -221,4 +221,24 @@ body { } } } + + p-paginator { + &.users-paginator { + .p-paginator.p-component { + height: 50px; + padding: 0; + } + + .p-paginator-element { + + &.p-paginator-prev, + &.p-paginator-next { + svg { + width: 26px; + height: 26px; + } + } + } + } + } } \ No newline at end of file