diff --git a/src/app/admin/permission/permission-edit/permission-edit.component.ts b/src/app/admin/permission/permission-edit/permission-edit.component.ts index 180d85f0d..ed0d4a864 100644 --- a/src/app/admin/permission/permission-edit/permission-edit.component.ts +++ b/src/app/admin/permission/permission-edit/permission-edit.component.ts @@ -6,7 +6,11 @@ import { TranslateService } from '@ngx-translate/core'; import { ReplaySubject, Subject, Subscription } from 'rxjs'; import { Location } from '@angular/common'; import { PermissionService } from '../permission.service'; -import { PermissionRequest, PermissionType, PermissionTypes } from '../permission.model'; +import { + PermissionRequest, + PermissionType, + PermissionTypes, +} from '../permission.model'; import { OrganisationResponse } from '../../organisation/organisation.model'; import { OrganisationService } from '../../organisation/organisation.service'; import { UserService } from '../../users/user.service'; @@ -80,8 +84,7 @@ export class PermissionEditComponent implements OnInit, OnDestroy { private location: Location, private errormEssageService: ErrorMessageService, private meService: MeService - ) { - } + ) {} ngOnInit(): void { this.getOrganizations(); @@ -167,7 +170,7 @@ export class PermissionEditComponent implements OnInit, OnDestroy { private getOrganizations() { this.organisationSubscription = this.organisationService - .getMultiple() + .getMultiple(1000, 0, 'name', 'asc') .subscribe( (orgs) => { this.organisations = orgs.data; @@ -179,15 +182,17 @@ export class PermissionEditComponent implements OnInit, OnDestroy { } private getUsers() { - this.userSubscription = this.userService.getMultiple().subscribe( - (users) => { - this.users = users.data; - this.filteredUsersMulti.next(this.users.slice()); - }, - (error: HttpErrorResponse) => { - this.showError(error); - } - ); + this.userSubscription = this.userService + .getMultiple(1000, 0, 'name', 'asc') + .subscribe( + (users) => { + this.users = users.data; + this.filteredUsersMulti.next(this.users.slice()); + }, + (error: HttpErrorResponse) => { + this.showError(error); + } + ); } public compare(o1: any, o2: any): boolean { @@ -207,7 +212,9 @@ export class PermissionEditComponent implements OnInit, OnDestroy { .getApplicationsByOrganizationId(organizationId) .subscribe( (res) => { - this.applications = res.data; + this.applications = res.data.sort((a, b) => + a.name.localeCompare(b.name, 'en', { numeric: true }) + ); this.filteredApplicationsMulti.next(this.applications.slice()); }, (error: HttpErrorResponse) => { @@ -251,7 +258,6 @@ export class PermissionEditComponent implements OnInit, OnDestroy { ); this.applicationMultiCtrl.setValue(this.permission.applicationIds); } - }, (error: HttpErrorResponse) => { this.showError(error); diff --git a/src/app/admin/users/user-list/awaiting-users-table/awaiting-users-table.component.html b/src/app/admin/users/user-list/awaiting-users-table/awaiting-users-table.component.html index 35f0e2638..88817e562 100644 --- a/src/app/admin/users/user-list/awaiting-users-table/awaiting-users-table.component.html +++ b/src/app/admin/users/user-list/awaiting-users-table/awaiting-users-table.component.html @@ -9,7 +9,7 @@ mat-table [dataSource]="users" matSort - matSortActive="id" + matSortActive="name" matSortDirection="asc" matSortDisableClear > diff --git a/src/app/admin/users/user-list/user-table/user-table.component.html b/src/app/admin/users/user-list/user-table/user-table.component.html index d729f65e0..56fe30c4f 100644 --- a/src/app/admin/users/user-list/user-table/user-table.component.html +++ b/src/app/admin/users/user-list/user-table/user-table.component.html @@ -2,7 +2,7 @@
- +
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 baac24d7e..547f8c6cd 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,117 +1,111 @@ -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)); - } -} +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 { catchError, map, startWith, switchMap } 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 { 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/applications/datatarget/datatarget-edit/datatarget-edit.component.ts b/src/app/applications/datatarget/datatarget-edit/datatarget-edit.component.ts index 432a5e2b4..e73d1aab3 100644 --- a/src/app/applications/datatarget/datatarget-edit/datatarget-edit.component.ts +++ b/src/app/applications/datatarget/datatarget-edit/datatarget-edit.component.ts @@ -1,4 +1,11 @@ -import { AfterViewInit, Component, ComponentFactoryResolver, OnDestroy, OnInit, QueryList, Type, ViewChild, ViewChildren } from '@angular/core'; +import { + Component, + ComponentFactoryResolver, + OnDestroy, + OnInit, + Type, + ViewChild, +} from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { DataTargetType } from '@shared/enums/datatarget-type'; import { DatatargetTypesService } from '../datatarget-types.service'; @@ -10,60 +17,68 @@ import { DatatargetEditTypeSelectorDirective } from './datatarget-edit-type-sele @Component({ selector: 'app-datatarget-edit', templateUrl: './datatarget-edit.component.html', - styleUrls: ['./datatarget-edit.component.scss'] + styleUrls: ['./datatarget-edit.component.scss'], }) export class DatatargetEditComponent implements OnInit, OnDestroy { - - @ViewChild(DatatargetEditTypeSelectorDirective, {static: true}) adHost!: DatatargetEditTypeSelectorDirective; + @ViewChild(DatatargetEditTypeSelectorDirective, { static: true }) + adHost!: DatatargetEditTypeSelectorDirective; public datatarget: Datatarget; private datatargetType: DataTargetType; - constructor(private componentFactoryResolver: ComponentFactoryResolver, - private datatargetService: DatatargetService, - private route: ActivatedRoute, - private datatargetTypesService: DatatargetTypesService - ) { } - + constructor( + private componentFactoryResolver: ComponentFactoryResolver, + private datatargetService: DatatargetService, + private route: ActivatedRoute, + private datatargetTypesService: DatatargetTypesService + ) {} loadComponent(componentType: Type) { - const viewContainerRef = this.adHost.viewContainerRef; - viewContainerRef.clear(); - const factory = this.componentFactoryResolver.resolveComponentFactory(componentType); - viewContainerRef.createComponent(factory); + const viewContainerRef = this.adHost.viewContainerRef; + viewContainerRef.clear(); + const factory = this.componentFactoryResolver.resolveComponentFactory( + componentType + ); + viewContainerRef.createComponent(factory); } ngOnInit(): void { + const id: number = +this.route.snapshot.paramMap.get('datatargetId'); - const id: number = +this.route.snapshot.paramMap.get('datatargetId'); - - if (id > 0) { - this.datatargetService.get(id) - .subscribe((dataTarget: Datatarget) => { - this.datatarget = dataTarget; - this.datatargetType = dataTarget.type; - const component = this.datatargetTypesService.getEditComponent(this.datatargetType); - this.loadComponent(component); - }); - } else { - let datatargetTypeParam = this.route.snapshot.paramMap.get('datatargetType'); - this.datatargetType = this.enumFromStringValue(DataTargetType, datatargetTypeParam); - if (this.datatargetType) { - const component = this.datatargetTypesService.getEditComponent(this.datatargetType); - this.loadComponent(component); - } + if (id > 0) { + this.datatargetService.get(id).subscribe((dataTarget: Datatarget) => { + this.datatarget = dataTarget; + this.datatargetType = dataTarget.type; + const component = this.datatargetTypesService.getEditComponent( + this.datatargetType + ); + this.loadComponent(component); + }); + } else { + let datatargetTypeParam = this.route.snapshot.paramMap.get( + 'datatargetType' + ); + this.datatargetType = this.enumFromStringValue( + DataTargetType, + datatargetTypeParam + ); + if (this.datatargetType) { + const component = this.datatargetTypesService.getEditComponent( + this.datatargetType + ); + this.loadComponent(component); } - - + } } - enumFromStringValue(enm: { [s: string]: T}, value: string): T | undefined { - return (Object.values(enm) as unknown as string[]).includes(value) - ? value as unknown as T + enumFromStringValue( + enm: { [s: string]: T }, + value: string + ): T | undefined { + return ((Object.values(enm) as unknown) as string[]).includes(value) + ? ((value as unknown) as T) : undefined; } - ngOnDestroy() { - - } + ngOnDestroy() {} } diff --git a/src/app/applications/datatarget/fiware/fiware-edit/fiware-edit.component.ts b/src/app/applications/datatarget/fiware/fiware-edit/fiware-edit.component.ts index 92567fe66..afc7d21c9 100644 --- a/src/app/applications/datatarget/fiware/fiware-edit/fiware-edit.component.ts +++ b/src/app/applications/datatarget/fiware/fiware-edit/fiware-edit.component.ts @@ -6,7 +6,10 @@ import { Observable, Subscription } from 'rxjs'; import { Application } from '@applications/application.model'; import { IotDevice } from '@applications/iot-devices/iot-device.model'; import { faTimesCircle } from '@fortawesome/free-solid-svg-icons'; -import { PayloadDeviceDatatarget, PayloadDeviceDatatargetGetByDataTargetResponse } from '@payload-decoder/payload-device-data.model'; +import { + PayloadDeviceDatatarget, + PayloadDeviceDatatargetGetByDataTargetResponse, +} from '@payload-decoder/payload-device-data.model'; import { DatatargetService } from '../../datatarget.service'; import { ApplicationService } from '@applications/application.service'; import { PayloadDecoderService } from '@payload-decoder/payload-decoder.service'; @@ -27,11 +30,9 @@ import { OrganizationAccessScope } from '@shared/enums/access-scopes'; @Component({ selector: 'app-fiware-edit', templateUrl: './fiware-edit.component.html', - styleUrls: ['./fiware-edit.component.scss'] + styleUrls: ['./fiware-edit.component.scss'], }) export class FiwareEditComponent implements DatatargetEdit, OnInit, OnDestroy { - - public multiPage = false; public title = ''; public sectionTitle = ''; @@ -69,13 +70,11 @@ export class FiwareEditComponent implements DatatargetEdit, OnInit, OnDestroy { private dialog: MatDialog, private errorMessageService: ErrorMessageService, private scrollToTopService: ScrollToTopService, - private meService: MeService, + private meService: MeService ) { translate.use('da'); } - - ngOnInit() { this.translate .get([ @@ -108,15 +107,21 @@ export class FiwareEditComponent implements DatatargetEdit, OnInit, OnDestroy { this.getDevices(); } this.getPayloadDecoders(); - this.canEdit = this.meService.hasAccessToTargetOrganization(OrganizationAccessScope.ApplicationWrite); + this.canEdit = this.meService.hasAccessToTargetOrganization( + OrganizationAccessScope.ApplicationWrite + ); } - addRow() { if (!this.payloadDeviceDatatarget) { this.payloadDeviceDatatarget = []; } - this.payloadDeviceDatatarget.push({ id: null, iotDeviceIds: [], payloadDecoderId: null, dataTargetId: this.datatargetid }); + this.payloadDeviceDatatarget.push({ + id: null, + iotDeviceIds: [], + payloadDecoderId: null, + dataTargetId: this.datatargetid, + }); } private deleteRow(index) { @@ -124,7 +129,8 @@ export class FiwareEditComponent implements DatatargetEdit, OnInit, OnDestroy { } else if (this.payloadDeviceDatatarget[index]?.id === null) { this.payloadDeviceDatatarget.splice(index, 1); } else { - this.payloadDeviceDataTargetService.delete(this.payloadDeviceDatatarget[index].id) + this.payloadDeviceDataTargetService + .delete(this.payloadDeviceDatatarget[index].id) .subscribe((response) => { this.payloadDeviceDatatarget.splice(index, 1); }); @@ -136,8 +142,8 @@ export class FiwareEditComponent implements DatatargetEdit, OnInit, OnDestroy { data: { showAccept: true, showCancel: true, - message: 'Er du sikker på at du vil slette?' - } + message: 'Er du sikker på at du vil slette?', + }, }); dialog.afterClosed().subscribe((result) => { @@ -163,28 +169,29 @@ export class FiwareEditComponent implements DatatargetEdit, OnInit, OnDestroy { updateDatatarget() { this.resetErrors(); - this.counter = 1 + (this.payloadDeviceDatatarget?.length ? this.payloadDeviceDatatarget?.length : 0); - this.datatargetService.update(this.datatarget) - .subscribe( - (response: Datatarget) => { - this.datatarget = response; - this.countToRedirect(); - }, - (error: HttpErrorResponse) => { - this.handleError(error); - this.formFailedSubmit = true; - } - ); + this.counter = + 1 + + (this.payloadDeviceDatatarget?.length + ? this.payloadDeviceDatatarget?.length + : 0); + this.datatargetService.update(this.datatarget).subscribe( + (response: Datatarget) => { + this.datatarget = response; + this.countToRedirect(); + }, + (error: HttpErrorResponse) => { + this.handleError(error); + this.formFailedSubmit = true; + } + ); } addPayloadDeviceDatatarget() { - this.payloadDeviceDatatarget.map( - pdd => { - if (pdd.payloadDecoderId === 0) { - pdd.payloadDecoderId = null; - } + this.payloadDeviceDatatarget.map((pdd) => { + if (pdd.payloadDecoderId === 0) { + pdd.payloadDecoderId = null; } - ); + }); this.payloadDeviceDatatarget.forEach((relation) => { if (relation.id) { this.payloadDeviceDataTargetService.put(relation).subscribe( @@ -227,17 +234,17 @@ export class FiwareEditComponent implements DatatargetEdit, OnInit, OnDestroy { createDatatarget() { this.resetErrors(); this.datatarget.applicationId = this.applicationId; - this.datatargetService.create(this.datatarget) - .subscribe((response: Datatarget) => { + this.datatargetService.create(this.datatarget).subscribe( + (response: Datatarget) => { this.datatargetid = response.id; this.datatarget = response; this.showSavedSnack(); }, - (error: HttpErrorResponse) => { - this.handleError(error); - this.formFailedSubmit = true; - }); - + (error: HttpErrorResponse) => { + this.handleError(error); + this.formFailedSubmit = true; + } + ); } private resetErrors() { @@ -246,17 +253,20 @@ export class FiwareEditComponent implements DatatargetEdit, OnInit, OnDestroy { this.formFailedSubmit = false; } - - getDevices(): void { - this.applicationSubscription = this.applicationService.getApplication(this.applicationId) + this.applicationSubscription = this.applicationService + .getApplication(this.applicationId) .subscribe((application: Application) => { - this.devices = application.iotDevices; + this.devices = application.iotDevices.sort((a, b) => + a.name.localeCompare(b.name, 'en', { numeric: true }) + ); }); } public selectAllDevices(index: number) { - this.payloadDeviceDatatarget[index].iotDeviceIds = this.devices.map(device => device.id); + this.payloadDeviceDatatarget[index].iotDeviceIds = this.devices.map( + (device) => device.id + ); } public deSelectAllDevices(index: number) { @@ -264,9 +274,12 @@ export class FiwareEditComponent implements DatatargetEdit, OnInit, OnDestroy { } getPayloadDecoders() { - this.payloadDecoderSubscription = this.payloadDecoderService.getMultiple(1000, 0, 'id', 'ASC') + this.payloadDecoderSubscription = this.payloadDecoderService + .getMultiple(1000, 0, 'id', 'ASC') .subscribe((response: PayloadDecoderMappedResponse) => { - this.payloadDecoders = response.data; + this.payloadDecoders = response.data.sort((a, b) => + a.name.localeCompare(b.name, 'en', { numeric: true }) + ); }); } @@ -283,10 +296,7 @@ export class FiwareEditComponent implements DatatargetEdit, OnInit, OnDestroy { onCoordinateKey(event: any) { if (event.target.value.length > event.target.maxLength) { - event.target.value = event.target.value.slice( - 0, - event.target.maxLength - ); + event.target.value = event.target.value.slice(0, event.target.maxLength); } } @@ -323,17 +333,20 @@ export class FiwareEditComponent implements DatatargetEdit, OnInit, OnDestroy { } } - private mapToDatatargetDevicePayload(dto: PayloadDeviceDatatargetGetByDataTargetResponse) { + private mapToDatatargetDevicePayload( + dto: PayloadDeviceDatatargetGetByDataTargetResponse + ) { this.payloadDeviceDatatarget = []; - dto.data.forEach( - (element) => { - this.payloadDeviceDatatarget.push({ - id: element.id, - iotDeviceIds: element.iotDevices.map((x) => x.id), - payloadDecoderId: element.payloadDecoder?.id === undefined ? 0 : element.payloadDecoder?.id, - dataTargetId: element.dataTarget.id - }); - } - ); + dto.data.forEach((element) => { + this.payloadDeviceDatatarget.push({ + id: element.id, + iotDeviceIds: element.iotDevices.map((x) => x.id), + payloadDecoderId: + element.payloadDecoder?.id === undefined + ? 0 + : element.payloadDecoder?.id, + dataTargetId: element.dataTarget.id, + }); + }); } } diff --git a/src/app/applications/datatarget/httppush/httppush-edit/httppush-edit.component.ts b/src/app/applications/datatarget/httppush/httppush-edit/httppush-edit.component.ts index c68dc4304..0ee5bac9a 100644 --- a/src/app/applications/datatarget/httppush/httppush-edit/httppush-edit.component.ts +++ b/src/app/applications/datatarget/httppush/httppush-edit/httppush-edit.component.ts @@ -78,7 +78,7 @@ export class HttppushEditComponent private opendatadkService: OpendatadkService, private opendatadkDialogService: OpendatadkDialogService, private scrollToTopService: ScrollToTopService, - private meService: MeService, + private meService: MeService ) { translate.use('da'); } @@ -114,7 +114,11 @@ export class HttppushEditComponent } this.getPayloadDecoders(); this.setDataSetExcists(); - this.canEdit = this.meService.hasAccessToTargetOrganization(OrganizationAccessScope.ApplicationWrite, undefined, this.applicationId); + this.canEdit = this.meService.hasAccessToTargetOrganization( + OrganizationAccessScope.ApplicationWrite, + undefined, + this.applicationId + ); } addRow() { @@ -279,7 +283,9 @@ export class HttppushEditComponent this.applicationSubscription = this.applicationService .getApplication(this.applicationId) .subscribe((application: Application) => { - this.devices = application.iotDevices; + this.devices = application.iotDevices.sort((a, b) => + a.name.localeCompare(b.name, 'en', { numeric: true }) + ); }); } @@ -297,7 +303,9 @@ export class HttppushEditComponent this.payloadDecoderSubscription = this.payloadDecoderService .getMultiple(1000, 0, 'id', 'ASC') .subscribe((response: PayloadDecoderMappedResponse) => { - this.payloadDecoders = response.data; + this.payloadDecoders = response.data.sort((a, b) => + a.name.localeCompare(b.name, 'en', { numeric: true }) + ); }); } diff --git a/src/app/applications/datatarget/mqtt-edit/mqtt-edit.component.ts b/src/app/applications/datatarget/mqtt-edit/mqtt-edit.component.ts index 7d3e8f62d..512ab6e10 100644 --- a/src/app/applications/datatarget/mqtt-edit/mqtt-edit.component.ts +++ b/src/app/applications/datatarget/mqtt-edit/mqtt-edit.component.ts @@ -86,7 +86,9 @@ export class MqttEditComponent implements DatatargetEdit, OnInit, OnDestroy { this.payloadDecoderSubscription = this.payloadDecoderService .getMultiple(1000, 0, 'id', 'ASC') .subscribe((response: PayloadDecoderMappedResponse) => { - this.payloadDecoders = response.data; + this.payloadDecoders = response.data.sort((a, b) => + a.name.localeCompare(b.name, 'en', { numeric: true }) + ); }); } @@ -113,7 +115,9 @@ export class MqttEditComponent implements DatatargetEdit, OnInit, OnDestroy { this.applicationSubscription = this.applicationService .getApplication(this.applicationId) .subscribe((application: Application) => { - this.devices = application.iotDevices; + this.devices = application.iotDevices.sort((a, b) => + a.name.localeCompare(b.name, 'en', { numeric: true }) + ); }); } diff --git a/src/app/applications/iot-devices/iot-device-edit/iot-device-edit.component.ts b/src/app/applications/iot-devices/iot-device-edit/iot-device-edit.component.ts index c1458f336..f39d2af56 100644 --- a/src/app/applications/iot-devices/iot-device-edit/iot-device-edit.component.ts +++ b/src/app/applications/iot-devices/iot-device-edit/iot-device-edit.component.ts @@ -10,7 +10,10 @@ import { DeviceModel } from '@app/device-model/device.model'; import { TranslateService } from '@ngx-translate/core'; import { DeviceProfile } from '@profiles/device-profiles/device-profile.model'; import { DeviceProfileService } from '@profiles/device-profiles/device-profile.service'; -import { ServiceProfile, ServiceProfileResponseMany } from '@profiles/service-profiles/service-profile.model'; +import { + ServiceProfile, + ServiceProfileResponseMany, +} from '@profiles/service-profiles/service-profile.model'; import { ServiceProfileService } from '@profiles/service-profiles/service-profile.service'; import { ActivationType } from '@shared/enums/activation-type'; import { DeviceType } from '@shared/enums/device-type'; @@ -25,345 +28,359 @@ import { IoTDeviceService } from '../iot-device.service'; import { MeService } from '@shared/services/me.service'; import { OrganizationAccessScope } from '@shared/enums/access-scopes'; - @Component({ - selector: 'app-iot-device-edit', - templateUrl: './iot-device-edit.component.html', - styleUrls: ['./iot-device-edit.component.scss'], + selector: 'app-iot-device-edit', + templateUrl: './iot-device-edit.component.html', + styleUrls: ['./iot-device-edit.component.scss'], }) export class IotDeviceEditComponent implements OnInit, OnDestroy { - public errorMessages: any; - public errorFields: string[]; - public formFailedSubmit = false; - public application: Application; - private deviceId: number; - public disableChoseApplication = true; - public loraDevice = DeviceType.LORAWAN; - public sigfoxDevice = DeviceType.SIGFOX; - public serviceProfiles: ServiceProfile[]; - public deviceProfiles: DeviceProfile[]; - public deviceModels: DeviceModel[]; - iotDevice = new IotDevice(); - editmode = false; - public OTAA = true; - metadataTags: {key?: string, value?: string}[] = []; - errorMetadataFieldId: string | undefined; - - public deviceSubscription: Subscription; - private applicationsSubscription: Subscription; - private serviceProfilesSubscription: Subscription; - private deviceProfileSubscription: Subscription; - private devicesProfileSubscription: Subscription; + public errorMessages: any; + public errorFields: string[]; + public formFailedSubmit = false; + public application: Application; + private deviceId: number; + public disableChoseApplication = true; + public loraDevice = DeviceType.LORAWAN; + public sigfoxDevice = DeviceType.SIGFOX; + public serviceProfiles: ServiceProfile[]; + public deviceProfiles: DeviceProfile[]; + public deviceModels: DeviceModel[]; + iotDevice = new IotDevice(); + editmode = false; + public OTAA = true; + metadataTags: { key?: string; value?: string }[] = []; + errorMetadataFieldId: string | undefined; + + public deviceSubscription: Subscription; + private applicationsSubscription: Subscription; + private serviceProfilesSubscription: Subscription; + private deviceProfileSubscription: Subscription; + private devicesProfileSubscription: Subscription; canEdit: boolean; - constructor( - private route: ActivatedRoute, - public translate: TranslateService, - private router: Router, - private serviceProfileService: ServiceProfileService, - private deviceProfileService: DeviceProfileService, - private applicationService: ApplicationService, - private iotDeviceService: IoTDeviceService, - private location: Location, - private shareVariable: SharedVariableService, - private deviceModelService: DeviceModelService, - private errorMessageService: ErrorMessageService, - private scrollToTopService: ScrollToTopService, - private titleService: Title, - private meService: MeService - ) { } - - ngOnInit(): void { - this.translate.use('da'); - this.iotDevice.applicationId = +this.route.snapshot.paramMap.get('id'); - this.deviceId = +this.route.snapshot.paramMap.get('deviceId'); - - if (this.iotDevice.applicationId && this.deviceId) { - this.editmode = true; - this.getDevice(this.deviceId); - this.disableChoseApplication = false; - } - - this.translate.get(['TITLE.IOTDEVICE']) - .subscribe(translations => { - this.titleService.setTitle(translations['TITLE.IOTDEVICE']); - }); - - this.getApplication(); - this.getServiceProfiles(); - this.getDeviceProfiles(); - this.getDeviceModels(); - this.canEdit = this.meService.hasAccessToTargetOrganization(OrganizationAccessScope.ApplicationWrite, undefined, this.iotDevice.applicationId); + constructor( + private route: ActivatedRoute, + public translate: TranslateService, + private router: Router, + private serviceProfileService: ServiceProfileService, + private deviceProfileService: DeviceProfileService, + private applicationService: ApplicationService, + private iotDeviceService: IoTDeviceService, + private location: Location, + private shareVariable: SharedVariableService, + private deviceModelService: DeviceModelService, + private errorMessageService: ErrorMessageService, + private scrollToTopService: ScrollToTopService, + private titleService: Title, + private meService: MeService + ) {} + + ngOnInit(): void { + this.translate.use('da'); + this.iotDevice.applicationId = +this.route.snapshot.paramMap.get('id'); + this.deviceId = +this.route.snapshot.paramMap.get('deviceId'); + + if (this.iotDevice.applicationId && this.deviceId) { + this.editmode = true; + this.getDevice(this.deviceId); + this.disableChoseApplication = false; } - public compare(o1: any, o2: any): boolean { - return o1 === o2; - } - - getDeviceModels() { - this.deviceModelService.getMultiple( - 1000, - 0, - 'id', - 'ASC', - this.shareVariable.getSelectedOrganisationId() - ).subscribe( - (response) => { - this.deviceModels = response.data; - } + this.translate.get(['TITLE.IOTDEVICE']).subscribe((translations) => { + this.titleService.setTitle(translations['TITLE.IOTDEVICE']); + }); + + this.getApplication(); + this.getServiceProfiles(); + this.getDeviceProfiles(); + this.getDeviceModels(); + this.canEdit = this.meService.hasAccessToTargetOrganization( + OrganizationAccessScope.ApplicationWrite, + undefined, + this.iotDevice.applicationId + ); + } + + public compare(o1: any, o2: any): boolean { + return o1 === o2; + } + + getDeviceModels() { + this.deviceModelService + .getMultiple( + 1000, + 0, + 'id', + 'ASC', + this.shareVariable.getSelectedOrganisationId() + ) + .subscribe((response) => { + this.deviceModels = response.data.sort((a, b) => + a.body.name.localeCompare(b.body.name, 'en', { numeric: true }) ); - } - - getApplication(): void { - this.applicationsSubscription = this.applicationService - .getApplication(this.iotDevice.applicationId) - .subscribe((application: Application) => { - this.application = application; - }); - } - - getDevice(id: number): void { - this.deviceSubscription = this.iotDeviceService - .getIoTDevice(id) - .subscribe((device: IotDevice) => { - this.iotDevice = device; - if (this.iotDevice?.application?.id) { - this.iotDevice.applicationId = device.application?.id; - } - if (device.location) { - this.iotDevice.longitude = device.location.coordinates[0]; - this.iotDevice.latitude = device.location.coordinates[1]; - } - this.OTAA = this.iotDevice.lorawanSettings?.OTAAapplicationKey ? true : false; - if (device.sigfoxSettings) { - } - if (!device.deviceModelId || device.deviceModelId === null) { - this.iotDevice.deviceModelId = 0; - } - if (device.metadata) { - this.metadataTags = jsonToList(device.metadata); - } - }); - } + }); + } - onChangeDeviceProfile(deviceProfileId: string) { - this.getDeviceProfile(deviceProfileId); - } + getApplication(): void { + this.applicationsSubscription = this.applicationService + .getApplication(this.iotDevice.applicationId) + .subscribe((application: Application) => { + this.application = application; + }); + } + + getDevice(id: number): void { + this.deviceSubscription = this.iotDeviceService + .getIoTDevice(id) + .subscribe((device: IotDevice) => { + this.iotDevice = device; + if (this.iotDevice?.application?.id) { + this.iotDevice.applicationId = device.application?.id; + } + if (device.location) { + this.iotDevice.longitude = device.location.coordinates[0]; + this.iotDevice.latitude = device.location.coordinates[1]; + } + this.OTAA = this.iotDevice.lorawanSettings?.OTAAapplicationKey + ? true + : false; + if (device.sigfoxSettings) { + } + if (!device.deviceModelId || device.deviceModelId === null) { + this.iotDevice.deviceModelId = 0; + } + if (device.metadata) { + this.metadataTags = jsonToList(device.metadata); + } + }); + } - getDeviceProfile(deviceProfileId: string) { - this.deviceProfileSubscription = this.deviceProfileService.getOne(deviceProfileId) - .subscribe((response) => { - this.OTAA = response.deviceProfile.supportsJoin; - }); - } + onChangeDeviceProfile(deviceProfileId: string) { + this.getDeviceProfile(deviceProfileId); + } - getServiceProfiles() { - this.serviceProfilesSubscription = this.serviceProfileService - .getMultiple().subscribe((result: ServiceProfileResponseMany) => { - this.serviceProfiles = result.result; - }); + getDeviceProfile(deviceProfileId: string) { + this.deviceProfileSubscription = this.deviceProfileService + .getOne(deviceProfileId) + .subscribe((response) => { + this.OTAA = response.deviceProfile.supportsJoin; + }); + } + + getServiceProfiles() { + this.serviceProfilesSubscription = this.serviceProfileService + .getMultiple() + .subscribe((result: ServiceProfileResponseMany) => { + this.serviceProfiles = result.result.sort((a, b) => + a.name.localeCompare(b.name, 'en', { numeric: true }) + ); + }); + } + + getDeviceProfiles() { + this.devicesProfileSubscription = this.deviceProfileService + .getMultiple() + .subscribe((result) => { + this.deviceProfiles = result.result.sort((a, b) => + a.name.localeCompare(b.name, 'en', { numeric: true }) + ); + }); + } + + getCoordinates() { + return { + longitude: this.iotDevice.longitude, + latitude: this.iotDevice.latitude, + draggable: true, + editEnabled: false, + useGeolocation: !this.editmode, + }; + } + + updateCoordinates(event: any) { + this.iotDevice.longitude = event.longitude; + this.iotDevice.latitude = event.latitude; + } + + onSubmit(): void { + this.adjustModelBasedOnType(); + + if (this.metadataTags.length === 0) { + this.iotDevice.metadata = JSON.stringify({}); + } else if (this.isMetadataSet()) { + const invalidKey = this.validateMetadata(); + + if (!invalidKey) { + this.setMetadata(); + } else { + this.handleMetadataError(invalidKey); + return; + } } - getDeviceProfiles() { - this.devicesProfileSubscription = this.deviceProfileService - .getMultiple().subscribe((result) => { - this.deviceProfiles = result.result; - }); + if (this.deviceId !== 0) { + this.updateIoTDevice(this.deviceId); + } else { + this.postIoTDevice(); } - - getCoordinates() { - return { - longitude: this.iotDevice.longitude, - latitude: this.iotDevice.latitude, - draggable: true, - editEnabled: false, - useGeolocation: !this.editmode - }; + } + + private handleMetadataError(invalidKey: string) { + this.handleError({ + error: { + message: [ + { + field: 'metadata', + message: 'MESSAGE.DUPLICATE-METADATA-KEY', + }, + ], + }, + }); + this.errorMetadataFieldId = invalidKey; + this.formFailedSubmit = true; + } + + setActivationType() { + if (this.OTAA) { + this.iotDevice.lorawanSettings.activationType = ActivationType.OTAA; + } else { + this.iotDevice.lorawanSettings.activationType = ActivationType.ABP; } + } - updateCoordinates(event: any) { - this.iotDevice.longitude = event.longitude; - this.iotDevice.latitude = event.latitude; + private adjustModelBasedOnType() { + if (this.iotDevice.deviceModelId === 0) { + this.iotDevice.deviceModelId = null; } - - onSubmit(): void { - this.adjustModelBasedOnType(); - - if (this.metadataTags.length === 0) { - this.iotDevice.metadata = JSON.stringify({}); - } else if (this.isMetadataSet()) { - const invalidKey = this.validateMetadata(); - - if (!invalidKey) { - this.setMetadata(); - } else { - this.handleMetadataError(invalidKey); - return; - } + switch (this.iotDevice.type) { + case DeviceType.GENERIC_HTTP: { + this.iotDevice.lorawanSettings = undefined; + this.iotDevice.sigfoxSettings = undefined; + break; + } + case DeviceType.LORAWAN: { + this.setActivationType(); + this.iotDevice.sigfoxSettings = undefined; + if (this.iotDevice.lorawanSettings.devEUI) { + this.iotDevice.lorawanSettings.devEUI = this.iotDevice.lorawanSettings.devEUI.toLowerCase(); } - - if (this.deviceId !== 0) { - this.updateIoTDevice(this.deviceId); - } else { - this.postIoTDevice(); + break; + } + case DeviceType.SIGFOX: { + this.iotDevice.lorawanSettings = undefined; + if (this.iotDevice.sigfoxSettings.endProductCertificate) { + this.iotDevice.sigfoxSettings.prototype = false; } + break; + } } + } - private handleMetadataError(invalidKey: string) { - this.handleError({ - error: { - message: [ - { - field: 'metadata', - message: 'MESSAGE.DUPLICATE-METADATA-KEY', - }, - ], - }, - }); - this.errorMetadataFieldId = invalidKey; - this.formFailedSubmit = true; - } - - setActivationType() { - if (this.OTAA) { - this.iotDevice.lorawanSettings.activationType = ActivationType.OTAA; - } else { - this.iotDevice.lorawanSettings.activationType = ActivationType.ABP; - } - } + private isMetadataSet(): boolean { + return ( + this.metadataTags.length && + this.metadataTags.some((tag) => tag.key && tag.value) + ); + } - private adjustModelBasedOnType() { - if (this.iotDevice.deviceModelId === 0) { - this.iotDevice.deviceModelId = null; - } - switch (this.iotDevice.type) { - case DeviceType.GENERIC_HTTP: { - this.iotDevice.lorawanSettings = undefined; - this.iotDevice.sigfoxSettings = undefined; - break; - } - case DeviceType.LORAWAN: { - this.setActivationType(); - this.iotDevice.sigfoxSettings = undefined; - if (this.iotDevice.lorawanSettings.devEUI) { - this.iotDevice.lorawanSettings.devEUI = this.iotDevice.lorawanSettings.devEUI.toLowerCase(); - } - break; - } - case DeviceType.SIGFOX: { - this.iotDevice.lorawanSettings = undefined; - if (this.iotDevice.sigfoxSettings.endProductCertificate) { - this.iotDevice.sigfoxSettings.prototype = false; - } - break; - } - } - } + private validateMetadata(): string | undefined { + const seen = new Set(); - private isMetadataSet(): boolean { - return this.metadataTags.length && this.metadataTags.some((tag) => tag.key && tag.value); + for (const tag of this.metadataTags) { + if (seen.size === seen.add(tag.key).size) { + return tag.key; + } } - - private validateMetadata(): string | undefined { - const seen = new Set(); - - for (const tag of this.metadataTags) { - if (seen.size === seen.add(tag.key).size) { - return tag.key; + } + + private setMetadata(): void { + if ( + this.metadataTags.length && + this.metadataTags.some((tag) => tag.key && tag.value) + ) { + const metadata: Record = {}; + this.metadataTags.forEach((tag) => { + if (!tag.key) { + return; } - } + metadata[tag.key] = tag.value; + }); + this.iotDevice.metadata = JSON.stringify(metadata); } - - private setMetadata(): void { - if ( - this.metadataTags.length && - this.metadataTags.some((tag) => tag.key && tag.value) - ) { - const metadata: Record = {}; - this.metadataTags.forEach((tag) => { - if (!tag.key) { - return; - } - metadata[tag.key] = tag.value; - }); - this.iotDevice.metadata = JSON.stringify(metadata); + } + + postIoTDevice() { + this.iotDeviceService.createIoTDevice(this.iotDevice).subscribe( + () => { + this.router.navigate(['applications/', this.iotDevice.applicationId]); + }, + (error: HttpErrorResponse) => { + this.handleError(error); + this.formFailedSubmit = true; + } + ); + } + + updateIoTDevice(id: number) { + this.iotDevice.applicationId = Number(this.iotDevice.applicationId); + this.iotDeviceService.updateIoTDevice(this.iotDevice, id).subscribe( + () => { + this.routeBack(); + }, + (error: HttpErrorResponse) => { + this.handleError(error); + this.formFailedSubmit = true; } + ); + } + + routeBack(): void { + this.location.back(); + } + + handleError(error: Pick) { + if (error?.error?.message === 'MESSAGE.OTAA-INFO-MISSING') { + this.errorFields = ['OTAAapplicationKey']; + this.errorMessages = [error?.error?.message]; + } else if ( + error?.error?.message === 'MESSAGE.ID-INVALID-OR-ALREADY-IN-USE' + ) { + this.errorFields = ['devEUI']; + this.errorMessages = [error?.error?.message]; + } else { + const errorMessage: ErrorMessage = this.errorMessageService.handleErrorMessageWithFields( + error + ); + this.errorFields = errorMessage.errorFields; + this.errorMessages = errorMessage.errorMessages; } - - postIoTDevice() { - this.iotDeviceService.createIoTDevice(this.iotDevice).subscribe( - () => { - this.router.navigate([ - 'applications/', - this.iotDevice.applicationId, - ]); - }, - (error: HttpErrorResponse) => { - this.handleError(error); - this.formFailedSubmit = true; - } - ); + this.scrollToTopService.scrollToTop(); + } + + onCoordinateKey(event: any) { + console.log(event.target.value); + console.log(event.target.maxLength); + if (event.target.value.length > event.target.maxLength) { + event.target.value = event.target.value.slice(0, event.target.maxLength); } + } - updateIoTDevice(id: number) { - this.iotDevice.applicationId = Number(this.iotDevice.applicationId); - this.iotDeviceService.updateIoTDevice(this.iotDevice, id).subscribe( - () => { - this.routeBack(); - }, - (error: HttpErrorResponse) => { - this.handleError(error); - this.formFailedSubmit = true; - } - ); + ngOnDestroy() { + // prevent memory leak by unsubscribing + if (this.applicationsSubscription) { + this.applicationsSubscription.unsubscribe(); } - - routeBack(): void { - this.location.back(); + if (this.deviceSubscription) { + this.deviceSubscription.unsubscribe(); } - - handleError(error: Pick) { - if (error?.error?.message === 'MESSAGE.OTAA-INFO-MISSING') { - this.errorFields = ['OTAAapplicationKey']; - this.errorMessages = [error?.error?.message]; - } else if (error?.error?.message === 'MESSAGE.ID-INVALID-OR-ALREADY-IN-USE') { - this.errorFields = ['devEUI']; - this.errorMessages = [error?.error?.message]; - } else { - const errorMessage: ErrorMessage = this.errorMessageService.handleErrorMessageWithFields(error); - this.errorFields = errorMessage.errorFields; - this.errorMessages = errorMessage.errorMessages; - } - this.scrollToTopService.scrollToTop(); + if (this.deviceProfileSubscription) { + this.deviceProfileSubscription.unsubscribe(); } - - onCoordinateKey(event: any) { - console.log(event.target.value); - console.log(event.target.maxLength); - if (event.target.value.length > event.target.maxLength) { - event.target.value = event.target.value.slice( - 0, - event.target.maxLength - ); - } + if (this.devicesProfileSubscription) { + this.devicesProfileSubscription.unsubscribe(); } - - ngOnDestroy() { - // prevent memory leak by unsubscribing - if (this.applicationsSubscription) { - this.applicationsSubscription.unsubscribe(); - } - if (this.deviceSubscription) { - this.deviceSubscription.unsubscribe(); - } - if (this.deviceProfileSubscription) { - this.deviceProfileSubscription.unsubscribe(); - } - if (this.devicesProfileSubscription) { - this.devicesProfileSubscription.unsubscribe(); - } - if (this.serviceProfilesSubscription) { - this.serviceProfilesSubscription.unsubscribe(); - } + if (this.serviceProfilesSubscription) { + this.serviceProfilesSubscription.unsubscribe(); } + } } diff --git a/src/app/device-model/device-model-table/device-model-table.component.ts b/src/app/device-model/device-model-table/device-model-table.component.ts index 18d575ef8..f997a1ef6 100644 --- a/src/app/device-model/device-model-table/device-model-table.component.ts +++ b/src/app/device-model/device-model-table/device-model-table.component.ts @@ -1,21 +1,13 @@ -import { Location } from '@angular/common'; -import { - AfterViewInit, - Component, - OnDestroy, - OnInit, - ViewChild, -} from '@angular/core'; +import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'; import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; -import { MatTableDataSource } from '@angular/material/table'; import { environment } from '@environments/environment'; import { TranslateService } from '@ngx-translate/core'; import { DeleteDialogService } from '@shared/components/delete-dialog/delete-dialog.service'; import { MeService } from '@shared/services/me.service'; import { SharedVariableService } from '@shared/shared-variable/shared-variable.service'; import { merge, Observable, of as observableOf, Subscription } from 'rxjs'; -import { startWith, switchMap, map, catchError } from 'rxjs/operators'; +import { catchError, map, startWith, switchMap } from 'rxjs/operators'; import { DeviceModelService } from '../device-model.service'; import { DeviceModel, DeviceModelResponse } from '../device.model'; import { OrganizationAccessScope } from '@shared/enums/access-scopes'; @@ -48,7 +40,9 @@ export class DeviceModelTableComponent implements OnInit, AfterViewInit { ) {} ngOnInit(): void { - this.canEdit = this.meService.hasAccessToTargetOrganization(OrganizationAccessScope.ApplicationWrite); + this.canEdit = this.meService.hasAccessToTargetOrganization( + OrganizationAccessScope.ApplicationWrite + ); this.translateService .get(['DEVICE-MODEL.DELETE-FAILED']) .subscribe((translations) => { @@ -72,7 +66,9 @@ export class DeviceModelTableComponent implements OnInit, AfterViewInit { this.isLoadingResults = false; this.resultsLength = data.count; - return data.data; + return data.data.sort((a, b) => + a.body.name.localeCompare(b.body.name, 'en', { numeric: true }) + ); }), catchError(() => { this.isLoadingResults = false; diff --git a/src/app/gateway/gateway-list/gateway-list.component.ts b/src/app/gateway/gateway-list/gateway-list.component.ts index ba7b67a7b..9c4d0f9e4 100644 --- a/src/app/gateway/gateway-list/gateway-list.component.ts +++ b/src/app/gateway/gateway-list/gateway-list.component.ts @@ -1,207 +1,209 @@ -import { Component, OnChanges, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { Organisation } from '@app/admin/organisation/organisation.model'; -import { TranslateService } from '@ngx-translate/core'; -import { Sort } from '@shared/models/sort.model'; -import { ChirpstackGatewayService } from '@shared/services/chirpstack-gateway.service'; -import moment from 'moment'; -import { Subject, Subscription } from 'rxjs'; -import { Gateway, GatewayResponseMany } from '../gateway.model'; -import { DeleteDialogService } from '@shared/components/delete-dialog/delete-dialog.service'; -import { MeService } from '@shared/services/me.service'; -import { SharedVariableService } from '@shared/shared-variable/shared-variable.service'; -import { environment } from '@environments/environment'; -import { Title } from '@angular/platform-browser'; -import { OrganizationAccessScope } from '@shared/enums/access-scopes'; -import { MatTabChangeEvent } from '@angular/material/tabs'; - -const gatewayStatusTabIndex = 2; - -@Component({ - selector: 'app-gateway-list', - templateUrl: './gateway-list.component.html', - styleUrls: ['./gateway-list.component.scss'] -}) -export class GatewayListComponent implements OnInit, OnChanges, OnDestroy { - isLoadingResults = true; - selectedOrg: number; - - public coordinateList = []; - public showmap = false; - public pageLimit = environment.tablePageSize; - public selectedSortId = 1; - public gateways: Gateway[]; - private gatewaySubscription: Subscription; - public selectedSortObject: Sort = { - id: 1, - dir: 'ASC', - col: 'name', - label: 'SORT.NAME-ASCENDING', - }; - organisations: Organisation[]; - orgSubscribtion: Subscription; - - private deleteDialogSubscription: Subscription; - public pageOffset = 0; - public pageTotal: number; - organisationId: number; - canEdit: boolean; - tabIndex = 0; - organisationChangeSubject: Subject = new Subject(); - isGatewayStatusVisibleSubject: Subject = new Subject(); - - constructor( - public translate: TranslateService, - private chirpstackGatewayService: ChirpstackGatewayService, - private deleteDialogService: DeleteDialogService, - private meService: MeService, - private titleService: Title, - private sharedVariableService: SharedVariableService, - ) { - translate.use('da'); - moment.locale('da'); - } - - ngOnInit(): void { - this.getGateways(); - this.organisations = this.sharedVariableService.getOrganizationInfo(); - this.translate.get(['TITLE.LORAWAN-GATEWAY']) - .subscribe(translations => { - this.titleService.setTitle(translations['TITLE.LORAWAN-GATEWAY']); - }); - this.canEdit = this.meService.hasAccessToTargetOrganization(OrganizationAccessScope.GatewayWrite); - } - - ngOnChanges() { - } - - public filterGatewayByOrgId(event: number) { - this.selectedOrg = event; - if (event) { - this.getGatewayWith(event); - } else { - this.getGateways(); - } - } - - setOrgIdFilter(orgId: number) { - this.organisationId = orgId; - this.organisationChangeSubject.next(orgId); - - if (this.tabIndex === gatewayStatusTabIndex) { - this.isGatewayStatusVisibleSubject.next(); - } - - this.filterGatewayByOrgId(orgId); - } - - private getGateways(): void { - this.gatewaySubscription = this.chirpstackGatewayService.getMultiple( - { - limit: this.pageLimit, - offset: this.pageOffset * this.pageLimit, - sort: this.selectedSortObject.dir, - orderOn: this.selectedSortObject.col, - } - ) - .subscribe( - (gateways: GatewayResponseMany) => { - this.gateways = gateways.result; - this.mapToCoordinateList(); - this.setCanEdit(); - this.isLoadingResults = false; - } - ); - } - - private getGatewayWith(orgId: number): void { - this.gatewaySubscription = this.chirpstackGatewayService.getMultiple( - { - limit: this.pageLimit, - offset: this.pageOffset * this.pageLimit, - sort: this.selectedSortObject.dir, - orderOn: this.selectedSortObject.col, - organizationId: orgId, - } - ) - .subscribe( - (gateways: GatewayResponseMany) => { - this.gateways = gateways.result; - this.mapToCoordinateList(); - this.setCanEdit(); - this.isLoadingResults = false; - } - ); - } - - selectedTabChange({index}: MatTabChangeEvent) { - this.tabIndex = index; - - if (index === 1) { - if (this.selectedOrg) { - this.getGatewayWith(this.selectedOrg); - } else { - this.getGateways(); - } - this.showmap = true; - } else if (index === gatewayStatusTabIndex) { - this.isGatewayStatusVisibleSubject.next(); - } - } - - private mapToCoordinateList() { - const tempcoordinateList = []; - this.gateways.map( - gateway => tempcoordinateList.push( - { - longitude: gateway.location.longitude, - latitude: gateway.location.latitude, - draggable: false, - editEnabled: false, - useGeolocation: false, - markerInfo: { - name: gateway.name, - active: this.gatewayStatus(gateway), - id: gateway.id, - internalOrganizationId: gateway.internalOrganizationId, - internalOrganizationName: gateway.internalOrganizationName - } - } - ), - ); - this.coordinateList = tempcoordinateList; - } - - gatewayStatus(gateway: Gateway): boolean { - return this.chirpstackGatewayService.isGatewayActive(gateway); - } - - deleteGateway(id: string) { - this.deleteDialogSubscription = this.deleteDialogService.showSimpleDialog().subscribe( - (response) => { - if (response) { - this.chirpstackGatewayService.delete(id).subscribe((response) => { - if (response.ok && response.body.success === true) { - this.getGateways(); - } - }); - } else { - console.log(response); - } - } - ); - } - - setCanEdit() { - this.gateways.forEach( - (gateway) => { - gateway.canEdit = this.meService.hasAccessToTargetOrganization(OrganizationAccessScope.GatewayWrite, gateway.internalOrganizationId); - } - ); - } - - ngOnDestroy() { - // prevent memory leak by unsubscribing - this.gatewaySubscription?.unsubscribe(); - this.deleteDialogSubscription?.unsubscribe(); - } -} +import { + Component, + OnChanges, + OnDestroy, + OnInit, + ViewChild, +} from '@angular/core'; +import { Organisation } from '@app/admin/organisation/organisation.model'; +import { TranslateService } from '@ngx-translate/core'; +import { Sort } from '@shared/models/sort.model'; +import { ChirpstackGatewayService } from '@shared/services/chirpstack-gateway.service'; +import moment from 'moment'; +import { Subject, Subscription } from 'rxjs'; +import { Gateway, GatewayResponseMany } from '../gateway.model'; +import { DeleteDialogService } from '@shared/components/delete-dialog/delete-dialog.service'; +import { MeService } from '@shared/services/me.service'; +import { SharedVariableService } from '@shared/shared-variable/shared-variable.service'; +import { environment } from '@environments/environment'; +import { Title } from '@angular/platform-browser'; +import { OrganizationAccessScope } from '@shared/enums/access-scopes'; +import { MatTabChangeEvent } from '@angular/material/tabs'; + +const gatewayStatusTabIndex = 2; + +@Component({ + selector: 'app-gateway-list', + templateUrl: './gateway-list.component.html', + styleUrls: ['./gateway-list.component.scss'], +}) +export class GatewayListComponent implements OnInit, OnChanges, OnDestroy { + isLoadingResults = true; + selectedOrg: number; + + public coordinateList = []; + public showmap = false; + public pageLimit = environment.tablePageSize; + public selectedSortId = 1; + public gateways: Gateway[]; + private gatewaySubscription: Subscription; + public selectedSortObject: Sort = { + id: 1, + dir: 'ASC', + col: 'name', + label: 'SORT.NAME-ASCENDING', + }; + organisations: Organisation[]; + orgSubscribtion: Subscription; + + private deleteDialogSubscription: Subscription; + public pageOffset = 0; + public pageTotal: number; + organisationId: number; + canEdit: boolean; + tabIndex = 0; + organisationChangeSubject: Subject = new Subject(); + isGatewayStatusVisibleSubject: Subject = new Subject(); + + constructor( + public translate: TranslateService, + private chirpstackGatewayService: ChirpstackGatewayService, + private deleteDialogService: DeleteDialogService, + private meService: MeService, + private titleService: Title, + private sharedVariableService: SharedVariableService + ) { + translate.use('da'); + moment.locale('da'); + } + + ngOnInit(): void { + this.getGateways(); + this.organisations = this.sharedVariableService.getOrganizationInfo(); + this.organisations.sort((a, b) => + a.name.localeCompare(b.name, 'en', { numeric: true }) + ); + this.translate.get(['TITLE.LORAWAN-GATEWAY']).subscribe((translations) => { + this.titleService.setTitle(translations['TITLE.LORAWAN-GATEWAY']); + }); + this.canEdit = this.meService.hasAccessToTargetOrganization( + OrganizationAccessScope.GatewayWrite + ); + } + + ngOnChanges() {} + + public filterGatewayByOrgId(event: number) { + this.selectedOrg = event; + if (event) { + this.getGatewayWith(event); + } else { + this.getGateways(); + } + } + + setOrgIdFilter(orgId: number) { + this.organisationId = orgId; + this.organisationChangeSubject.next(orgId); + + if (this.tabIndex === gatewayStatusTabIndex) { + this.isGatewayStatusVisibleSubject.next(); + } + + this.filterGatewayByOrgId(orgId); + } + + private getGateways(): void { + this.gatewaySubscription = this.chirpstackGatewayService + .getMultiple({ + limit: this.pageLimit, + offset: this.pageOffset * this.pageLimit, + sort: this.selectedSortObject.dir, + orderOn: this.selectedSortObject.col, + }) + .subscribe((gateways: GatewayResponseMany) => { + this.gateways = gateways.result; + this.mapToCoordinateList(); + this.setCanEdit(); + this.isLoadingResults = false; + }); + } + + private getGatewayWith(orgId: number): void { + this.gatewaySubscription = this.chirpstackGatewayService + .getMultiple({ + limit: this.pageLimit, + offset: this.pageOffset * this.pageLimit, + sort: this.selectedSortObject.dir, + orderOn: this.selectedSortObject.col, + organizationId: orgId, + }) + .subscribe((gateways: GatewayResponseMany) => { + this.gateways = gateways.result; + this.mapToCoordinateList(); + this.setCanEdit(); + this.isLoadingResults = false; + }); + } + + selectedTabChange({ index }: MatTabChangeEvent) { + this.tabIndex = index; + + if (index === 1) { + if (this.selectedOrg) { + this.getGatewayWith(this.selectedOrg); + } else { + this.getGateways(); + } + this.showmap = true; + } else if (index === gatewayStatusTabIndex) { + this.isGatewayStatusVisibleSubject.next(); + } + } + + private mapToCoordinateList() { + const tempcoordinateList = []; + this.gateways.map((gateway) => + tempcoordinateList.push({ + longitude: gateway.location.longitude, + latitude: gateway.location.latitude, + draggable: false, + editEnabled: false, + useGeolocation: false, + markerInfo: { + name: gateway.name, + active: this.gatewayStatus(gateway), + id: gateway.id, + internalOrganizationId: gateway.internalOrganizationId, + internalOrganizationName: gateway.internalOrganizationName, + }, + }) + ); + this.coordinateList = tempcoordinateList; + } + + gatewayStatus(gateway: Gateway): boolean { + return this.chirpstackGatewayService.isGatewayActive(gateway); + } + + deleteGateway(id: string) { + this.deleteDialogSubscription = this.deleteDialogService + .showSimpleDialog() + .subscribe((response) => { + if (response) { + this.chirpstackGatewayService.delete(id).subscribe((response) => { + if (response.ok && response.body.success === true) { + this.getGateways(); + } + }); + } else { + console.log(response); + } + }); + } + + setCanEdit() { + this.gateways.forEach((gateway) => { + gateway.canEdit = this.meService.hasAccessToTargetOrganization( + OrganizationAccessScope.GatewayWrite, + gateway.internalOrganizationId + ); + }); + } + + ngOnDestroy() { + // prevent memory leak by unsubscribing + this.gatewaySubscription?.unsubscribe(); + this.deleteDialogSubscription?.unsubscribe(); + } +} diff --git a/src/app/navbar/organisation-dropdown/organisation-dropdown.component.html b/src/app/navbar/organisation-dropdown/organisation-dropdown.component.html index 7e1dccfea..4f97fad63 100644 --- a/src/app/navbar/organisation-dropdown/organisation-dropdown.component.html +++ b/src/app/navbar/organisation-dropdown/organisation-dropdown.component.html @@ -26,7 +26,7 @@ - +