diff --git a/src/app/admin/admin-routing.module.ts b/src/app/admin/admin-routing.module.ts index e3d9e8f5..bbdfaeac 100644 --- a/src/app/admin/admin-routing.module.ts +++ b/src/app/admin/admin-routing.module.ts @@ -30,6 +30,7 @@ const adminRoutes: Routes = [ { path: 'users', component: UsersComponent, children: [ { path: '', component: UserListComponent }, + { path: 'organization/:organization-id', component: UserListComponent }, { path: 'new-user', component: UserEditComponent }, { path: ':user-id', component: UserDetailComponent }, { path: ':user-id/edit-user', component: UserEditComponent }, diff --git a/src/app/admin/users/user-list/awaiting-users-table/awaiting-users-table.component.ts b/src/app/admin/users/user-list/awaiting-users-table/awaiting-users-table.component.ts index bf6dd4a8..a7665b97 100644 --- a/src/app/admin/users/user-list/awaiting-users-table/awaiting-users-table.component.ts +++ b/src/app/admin/users/user-list/awaiting-users-table/awaiting-users-table.component.ts @@ -1,134 +1,149 @@ -import { AfterViewInit, Component, Input, ViewChild } from '@angular/core'; -import { - RejectUserDto, - UserGetManyResponse, - UserResponse, - UserResponsePerRequestedOrganization, -} from '../../user.model'; -import { UserService } from '../../user.service'; -import { SharedVariableService } from '@shared/shared-variable/shared-variable.service'; -import { environment } from '@environments/environment'; -import { MatPaginator } from '@angular/material/paginator'; -import { MatSort } from '@angular/material/sort'; -import { TranslateService } from '@ngx-translate/core'; -import { merge, Observable, of as observableOf } from 'rxjs'; -import { catchError, map, startWith, switchMap } from 'rxjs/operators'; -import { DeleteDialogService } from '@shared/components/delete-dialog/delete-dialog.service'; -import { DefaultPageSizeOptions } from '@shared/constants/page.constants'; - -@Component({ - selector: 'app-awaiting-users-table', - templateUrl: './awaiting-users-table.component.html', - styleUrls: ['./awaiting-users-table.component.scss'], -}) -export class AwaitingUsersTableComponent implements AfterViewInit { - displayedColumns: string[] = ['name', 'email', 'menu']; - users: UserResponsePerRequestedOrganization[]; - - public pageSize = environment.tablePageSize; - pageSizeOptions = DefaultPageSizeOptions; - - resultsLength = 0; - public errorMessage: string; - isLoadingResults = true; - message: string; - infoTitle: string; - @ViewChild(MatPaginator) paginator: MatPaginator; - @ViewChild(MatSort) sort: MatSort; - - @Input() permissionId?: number; - @Input() canSort = true; - - constructor( - public translate: TranslateService, - private userService: UserService, - private sharedService: SharedVariableService, - private deleteDialogService: DeleteDialogService - ) {} - - ngAfterViewInit() { - // If the user changes the sort order, reset back to the first page. - this.sort.sortChange.subscribe(() => (this.paginator.pageIndex = 0)); - - merge(this.sort.sortChange, this.paginator.page) - .pipe( - startWith({}), - switchMap(() => { - this.isLoadingResults = true; - return this.getUsers(this.sort.active, this.sort.direction); - }), - map((data) => { - // Flip flag to show that loading has finished. - this.isLoadingResults = false; - this.resultsLength = data.count; - - return data.data; - }), - catchError(() => { - this.isLoadingResults = false; - return observableOf([] as UserResponse[]); - }) - ) - .subscribe((userResponses) => { - this.users = []; - - // Flatten users so each table row is exactly one request - for (const response of userResponses) { - const { requestedOrganizations, ...user} = response; - - for (const organizationId of response.requestedOrganizations) { - this.users.push({ - ...user, - requestedOrganization: organizationId, - }); - } - } - }); - - this.translate - .get(['USERS.DIALOG.QUESTION-REJECT', 'USERS.DIALOG.HEAD-REJECT']) - .subscribe((translations) => { - this.message = translations['USERS.DIALOG.QUESTION-REJECT']; - this.infoTitle = translations['USERS.DIALOG.HEAD-REJECT']; - }); - } - - getUsers( - orderByColumn: string, - orderByDirection: string - ): Observable { - return this.userService.getAwaitingUsers( - this.paginator.pageSize, - this.paginator.pageIndex * this.paginator.pageSize, - orderByColumn, - orderByDirection - ); - } - - rejectUser(userId: number, organizationId: number) { - this.deleteDialogService - .showSimpleDialog(this.message, false, true, false, this.infoTitle, true) - .subscribe((response) => { - if (response) { - const rejectUserOrgDto: RejectUserDto = { - orgId: organizationId, - userIdToReject: userId, - }; - - this.userService - .rejectUser(rejectUserOrgDto) - .subscribe((response) => { - if (response) { - this.paginator.page.emit({ - pageIndex: this.paginator.pageIndex, - pageSize: this.paginator.pageSize, - length: this.resultsLength, - }); - } else { - this.errorMessage = response?.name; - } - }); - } - }); - } -} +import { AfterViewInit, Component, Input, ViewChild } from '@angular/core'; +import { + RejectUserDto, + UserGetManyResponse, + UserResponse, + UserResponsePerRequestedOrganization, +} from '../../user.model'; +import { UserService } from '../../user.service'; +import { SharedVariableService } from '@shared/shared-variable/shared-variable.service'; +import { environment } from '@environments/environment'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { TranslateService } from '@ngx-translate/core'; +import { merge, Observable, of as observableOf } from 'rxjs'; +import { catchError, map, startWith, switchMap } from 'rxjs/operators'; +import { DeleteDialogService } from '@shared/components/delete-dialog/delete-dialog.service'; +import { DefaultPageSizeOptions } from '@shared/constants/page.constants'; +import { MeService } from '@shared/services/me.service'; + +@Component({ + selector: 'app-awaiting-users-table', + templateUrl: './awaiting-users-table.component.html', + styleUrls: ['./awaiting-users-table.component.scss'], +}) +export class AwaitingUsersTableComponent implements AfterViewInit { + displayedColumns: string[] = ['name', 'email', 'menu']; + users: UserResponsePerRequestedOrganization[]; + + public pageSize = environment.tablePageSize; + pageSizeOptions = DefaultPageSizeOptions; + + resultsLength = 0; + public errorMessage: string; + isLoadingResults = true; + message: string; + infoTitle: string; + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + @Input() permissionId?: number; + @Input() canSort = true; + organizationId: number; + + constructor( + public translate: TranslateService, + private userService: UserService, + private sharedService: SharedVariableService, + private deleteDialogService: DeleteDialogService, + private meService: MeService + ) { + this.organizationId = this.sharedService.getSelectedOrganisationId(); + } + + ngAfterViewInit() { + // If the user changes the sort order, reset back to the first page. + this.sort.sortChange.subscribe(() => (this.paginator.pageIndex = 0)); + + merge(this.sort.sortChange, this.paginator.page) + .pipe( + startWith({}), + switchMap(() => { + this.isLoadingResults = true; + return this.getUsers(this.sort.active, this.sort.direction); + }), + map((data) => { + // Flip flag to show that loading has finished. + this.isLoadingResults = false; + this.resultsLength = data.count; + + return data.data; + }), + catchError(() => { + this.isLoadingResults = false; + return observableOf([] as UserResponse[]); + }) + ) + .subscribe((userResponses) => { + this.users = []; + + // Flatten users so each table row is exactly one request + for (const response of userResponses) { + const { requestedOrganizations, ...user} = response; + + for (const organizationId of response.requestedOrganizations) { + this.users.push({ + ...user, + requestedOrganization: organizationId, + }); + } + } + }); + + this.translate + .get(['USERS.DIALOG.QUESTION-REJECT', 'USERS.DIALOG.HEAD-REJECT']) + .subscribe((translations) => { + this.message = translations['USERS.DIALOG.QUESTION-REJECT']; + this.infoTitle = translations['USERS.DIALOG.HEAD-REJECT']; + }); + } + + getUsers( + orderByColumn: string, + orderByDirection: string + ): Observable { + if(this.meService.hasGlobalAdmin()) { + return this.userService.getAwaitingUsers( + this.paginator.pageSize, + this.paginator.pageIndex * this.paginator.pageSize, + orderByColumn, + orderByDirection + ); + } else { + return this.userService.getAwaitingUsersForOrganization( + this.paginator.pageSize, + this.paginator.pageIndex * this.paginator.pageSize, + this.organizationId, + orderByColumn, + orderByDirection + ); + } + } + + rejectUser(userId: number, organizationId: number) { + this.deleteDialogService + .showSimpleDialog(this.message, false, true, false, this.infoTitle, true) + .subscribe((response) => { + if (response) { + const rejectUserOrgDto: RejectUserDto = { + orgId: organizationId, + userIdToReject: userId, + }; + + this.userService + .rejectUser(rejectUserOrgDto) + .subscribe((response) => { + if (response) { + this.paginator.page.emit({ + pageIndex: this.paginator.pageIndex, + pageSize: this.paginator.pageSize, + length: this.resultsLength, + }); + } else { + this.errorMessage = response?.name; + } + }); + } + }); + } +} diff --git a/src/app/admin/users/user-list/user-list.component.html b/src/app/admin/users/user-list/user-list.component.html index 4f2052a2..b7e2f5f9 100644 --- a/src/app/admin/users/user-list/user-list.component.html +++ b/src/app/admin/users/user-list/user-list.component.html @@ -14,7 +14,7 @@ [component]="true" [title]="'USERS.EXISTING-USERS' | translate" > - + diff --git a/src/app/admin/users/user-list/user-list.component.ts b/src/app/admin/users/user-list/user-list.component.ts index 17fc8848..a6c3c7db 100644 --- a/src/app/admin/users/user-list/user-list.component.ts +++ b/src/app/admin/users/user-list/user-list.component.ts @@ -1,23 +1,33 @@ -import { Component, OnInit } from '@angular/core'; -import { Title } from '@angular/platform-browser'; -import { TranslateService } from '@ngx-translate/core'; -import { OrganizationAccessScope } from '@shared/enums/access-scopes'; -import { MeService } from '@shared/services/me.service'; - -@Component({ - selector: 'app-user-list', - templateUrl: './user-list.component.html', - styleUrls: ['./user-list.component.scss'], -}) -export class UserListComponent implements OnInit { - canEdit: boolean; - constructor(private titleService: Title, private translate: TranslateService, private meService: MeService) {} - - ngOnInit(): void { - this.translate.get(['TITLE.USER']) - .subscribe(translations => { - this.titleService.setTitle(translations['TITLE.USER']); - }); - this.canEdit = this.meService.hasAccessToTargetOrganization(OrganizationAccessScope.UserAdministrationWrite); - } -} +import { Component, OnInit } from '@angular/core'; +import { Title } from '@angular/platform-browser'; +import { TranslateService } from '@ngx-translate/core'; +import { OrganizationAccessScope } from '@shared/enums/access-scopes'; +import { MeService } from '@shared/services/me.service'; +import { SharedVariableService } from '@shared/shared-variable/shared-variable.service'; + +@Component({ + selector: 'app-user-list', + templateUrl: './user-list.component.html', + styleUrls: ['./user-list.component.scss'], +}) +export class UserListComponent implements OnInit { + canEdit: boolean; + organizationId: number; + + constructor( + private titleService: Title, + private translate: TranslateService, + private meService: MeService, + private globalService: SharedVariableService + ) { + this.organizationId = this.globalService.getSelectedOrganisationId(); + } + + ngOnInit(): void { + this.translate.get(['TITLE.USER']) + .subscribe(translations => { + this.titleService.setTitle(translations['TITLE.USER']); + }); + this.canEdit = this.meService.hasAccessToTargetOrganization(OrganizationAccessScope.UserAdministrationWrite); + } +} diff --git a/src/app/admin/users/user-list/user-table/user-table.component.ts b/src/app/admin/users/user-list/user-table/user-table.component.ts index a5a44d90..baac24d7 100644 --- a/src/app/admin/users/user-list/user-table/user-table.component.ts +++ b/src/app/admin/users/user-list/user-table/user-table.component.ts @@ -1,86 +1,117 @@ -import { - AfterViewInit, - Component, - Input, - ViewChild, -} from '@angular/core'; -import { MatPaginator } from '@angular/material/paginator'; -import { MatSort } from '@angular/material/sort'; -import { TranslateService } from '@ngx-translate/core'; -import { startWith, switchMap, map, catchError } from 'rxjs/operators'; -import { UserGetManyResponse, UserResponse } from '../../user.model'; -import { UserService } from '../../user.service'; -import { merge, Observable, of as observableOf } from 'rxjs'; -import { environment } from '@environments/environment'; -import { DefaultPageSizeOptions } from '@shared/constants/page.constants'; - -@Component({ - selector: 'app-user-table', - templateUrl: './user-table.component.html', - styleUrls: ['./user-table.component.scss'], -}) -export class UserTableComponent implements AfterViewInit { - displayedColumns: string[] = [ - 'name', - 'email', - 'global', - 'status', - 'lastLogin', - 'menu', - ]; - data: UserResponse[]; - - public pageSize = environment.tablePageSize; - pageSizeOptions = DefaultPageSizeOptions; - resultsLength = 0; - isLoadingResults = true; - @ViewChild(MatPaginator) paginator: MatPaginator; - @ViewChild(MatSort) sort: MatSort; - - @Input() permissionId?: number; - @Input() canSort = true; - - constructor( - public translate: TranslateService, - private userService: UserService - ) {} - - getUsers( - orderByColumn: string, - orderByDirection: string - ): Observable { - return this.userService.getMultiple( - this.paginator.pageSize, - this.paginator.pageIndex * this.paginator.pageSize, - orderByColumn, - orderByDirection, - this.permissionId - ); - } - - ngAfterViewInit() { - // If the user changes the sort order, reset back to the first page. - this.sort.sortChange.subscribe(() => (this.paginator.pageIndex = 0)); - - merge(this.sort.sortChange, this.paginator.page) - .pipe( - startWith({}), - switchMap(() => { - this.isLoadingResults = true; - return this.getUsers(this.sort.active, this.sort.direction); - }), - map((data) => { - // Flip flag to show that loading has finished. - this.isLoadingResults = false; - this.resultsLength = data.count; - - return data.data; - }), - catchError(() => { - this.isLoadingResults = false; - return observableOf([]); - }) - ) - .subscribe((data) => (this.data = data)); - } -} +import { + AfterViewInit, + Component, + Input, + ViewChild, +} from '@angular/core'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { TranslateService } from '@ngx-translate/core'; +import { startWith, switchMap, map, catchError } from 'rxjs/operators'; +import { UserGetManyResponse, UserResponse } from '../../user.model'; +import { UserService } from '../../user.service'; +import { merge, Observable, of as observableOf } from 'rxjs'; +import { environment } from '@environments/environment'; +import { DefaultPageSizeOptions } from '@shared/constants/page.constants'; +import { ActivatedRoute } from '@angular/router'; +import { MeService } from '@shared/services/me.service'; + +@Component({ + selector: 'app-user-table', + templateUrl: './user-table.component.html', + styleUrls: ['./user-table.component.scss'], +}) +export class UserTableComponent implements AfterViewInit { + displayedColumns: string[] = [ + 'name', + 'email', + 'global', + 'status', + 'lastLogin', + 'menu', + ]; + data: UserResponse[]; + + public pageSize = environment.tablePageSize; + pageSizeOptions = DefaultPageSizeOptions; + resultsLength = 0; + isLoadingResults = true; + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + // If supplied, users will only be retrieved for the specified organization + // If supplied, permissionId will ignored, even if supplied + @Input() organizationId?: number; + + // If supplied, users will be retrieved on the permissionId (userGroup/brugerGruppe) + @Input() permissionId?: number; + + @Input() canSort = true; + isGlobalAdmin: boolean; + + constructor( + public translate: TranslateService, + private userService: UserService, + private meService: MeService + ) { + this.isGlobalAdmin = this.meService.hasGlobalAdmin(); + } + + getUsers( + orderByColumn: string, + orderByDirection: string + ): Observable { + if (this.organizationId !== null && this.organizationId !== undefined) { + if (this.isGlobalAdmin) { + return this.userService.getMultiple( + this.paginator.pageSize, + this.paginator.pageIndex * this.paginator.pageSize, + orderByColumn, + orderByDirection + ); + } else { + return this.userService.getMultipleByOrganization( + this.paginator.pageSize, + this.paginator.pageIndex * this.paginator.pageSize, + orderByColumn, + orderByDirection, + this.organizationId + ); + } + } else { + return this.userService.getMultiple( + this.paginator.pageSize, + this.paginator.pageIndex * this.paginator.pageSize, + orderByColumn, + orderByDirection, + this.permissionId + ); + } + } + + ngAfterViewInit() { + // If the user changes the sort order, reset back to the first page. + this.sort.sortChange.subscribe(() => (this.paginator.pageIndex = 0)); + + merge(this.sort.sortChange, this.paginator.page) + .pipe( + startWith({}), + switchMap(() => { + this.isLoadingResults = true; + return this.getUsers(this.sort.active, this.sort.direction); + }), + map((data) => { + // Flip flag to show that loading has finished. + this.isLoadingResults = false; + this.resultsLength = data.count; + + return data.data; + }), + catchError(() => { + this.isLoadingResults = false; + return observableOf([]); + }) + ) + .subscribe((data) => (this.data = data)); + } +} diff --git a/src/app/admin/users/user.service.ts b/src/app/admin/users/user.service.ts index 9bb278aa..45c75b71 100644 --- a/src/app/admin/users/user.service.ts +++ b/src/app/admin/users/user.service.ts @@ -57,7 +57,7 @@ export class UserService { orderByColumn?: string, orderByDirection?: string, permissionId?: number - ): Observable { + ): Observable { if (permissionId != null) { return this.restService.get(`permission/${permissionId}/users`, { limit, @@ -73,6 +73,21 @@ export class UserService { } } + getMultipleByOrganization( + limit: number = 1000, + offset: number = 0, + orderByColumn?: string, + orderByDirection?: string, + organizationId?: number + ): Observable { + return this.restService.get(this.URL + `/organizationUsers/${organizationId}`, { + limit, + offset, + orderOn: orderByColumn, + sort: orderByDirection, + }); + } + hideWelcome(id: number): Observable { return this.restService.put(`${this.URL}/${id}/hide-welcome`, null, null); } @@ -94,6 +109,24 @@ export class UserService { ); } + getAwaitingUsersForOrganization( + limit: number = 1000, + offset: number = 0, + organizationId: number, + orderByColumn?: string, + orderByDirection?: string + ): Observable { + return this.restService.get( + `${this.URL}/awaitingUsers/${organizationId}`, + { + limit, + offset, + orderOn: orderByColumn, + sort: orderByDirection, + }, + ); + } + getOneSimple(id: number): Observable { return this.restService.get(this.URL_NEW_KOMBIT, {}, id).pipe( map((response: UserResponse) => { @@ -101,6 +134,7 @@ export class UserService { }) ); } + updateNewKombit(body: CreateNewKombitUserDto): Observable { return this.restService.put( this.URL_NEW_KOMBIT + '/createNewKombitUser',