Skip to content

Commit a9603f3

Browse files
IOT-1331: Moved column selector to own component + implemented column selection… (#150)
* Fixed routing of gateway list + fixed memory leak by unsubscribing properly from gateway fetches * Fixed routing errors in gateway list * Changed mqtt datatarget topic placeholder + added tooltip * Added additional text changes from Product Owner * Removed maxLenght from device AND gateway EUI, now removes non-hex digits on submit * Added sticky to name column on gateway status table * Implemented application table column selection * Removed unused controller name from select * Moved column selector to own component + implemented column selection on iot-device table
1 parent ae520ae commit a9603f3

File tree

12 files changed

+307
-157
lines changed

12 files changed

+307
-157
lines changed

src/app/applications/applications-list/applications-table/applications-table.component.html

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,8 @@
1-
<div class="select-container">
2-
<mat-select
3-
class="form-control table-select"
4-
multiple="true"
5-
[(value)]="optionalColumnsSelected"
6-
(selectionChange)="handleColumnSelection($event)"
7-
panelClass="tall"
8-
>
9-
<mat-option disabled>
10-
<button
11-
mat-raised-button
12-
class="mat-primary fill text-sm"
13-
(click)="selectAll()">
14-
{{'QUESTION.DATATARGET.SELECTALLDEVICES' | translate}}
15-
</button>
16-
<button
17-
mat-raised-button
18-
class="mat-primary fill text-sm"
19-
(click)="deSelectAll()">
20-
{{'QUESTION.DATATARGET.DESELECTALLDEVICES' | translate}}
21-
</button>
22-
</mat-option>
23-
<mat-option *ngFor="let option of optionalColumnOptions" [value]="option.id">
24-
{{option.display | translate}}
25-
</mat-option>
26-
</mat-select>
27-
</div>
1+
<app-column-selector
2+
[(displayedColumns)]="displayedColumns"
3+
[columnDefinitions]="columnDefinitions"
4+
[localStorageKey]="applicationSavedColumns"
5+
></app-column-selector>
286

297
<div class="mat-elevation-z8">
308
<div class="loading-shade" *ngIf="isLoadingResults">
@@ -69,15 +47,15 @@
6947

7048
<!-- Devices Column -->
7149
<ng-container matColumnDef="devices">
72-
<th mat-header-cell *matHeaderCellDef class="col-2">
50+
<th mat-header-cell *matHeaderCellDef mat-sort-header class="col-2">
7351
{{ 'APPLICATION-TABLE.IOT-DEVICES' | translate }}</th>
7452
<td mat-cell *matCellDef="let element">
7553
{{element?.iotDevices?.length ?? 0}}
7654
</td>
7755
</ng-container>
7856

7957
<ng-container matColumnDef="dataTargets">
80-
<th mat-header-cell *matHeaderCellDef class="col-1">
58+
<th mat-header-cell *matHeaderCellDef mat-sort-header class="col-1">
8159
{{ 'APPLICATION-TABLE.DATA-TARGETS' | translate}}
8260
</th>
8361
<td mat-cell *matCellDef="let application">
Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
.table-select {
2-
width: 180px;
3-
margin: 5px;
4-
5-
}
6-
7-
.select-container {
8-
display: flex;
9-
flex-direction: row;
10-
justify-content: flex-end;
11-
}
12-
131
.flag-icon {
142
color: red;
153
}

src/app/applications/applications-list/applications-table/applications-table.component.ts

Lines changed: 7 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
AfterViewInit,
3+
ChangeDetectorRef,
34
Component,
45
Input,
56
OnInit,
@@ -16,13 +17,12 @@ import { DeleteDialogService } from '@shared/components/delete-dialog/delete-dia
1617
import { merge, Observable, of as observableOf } from 'rxjs';
1718
import { catchError, map, startWith, switchMap } from 'rxjs/operators';
1819
import { DefaultPageSizeOptions } from '@shared/constants/page.constants';
19-
import { MatSelectChange } from '@angular/material/select';
2020
import { ControlledProperty } from '@shared/models/controlled-property.model';
2121
import { ApplicationDeviceTypeEntries } from '@shared/enums/device-type';
2222
import { ApplicationDeviceType } from '@applications/models/application-device-type.model';
2323
import { Datatarget } from '@applications/datatarget/datatarget.model';
2424
import { faFlag } from '@fortawesome/free-solid-svg-icons';
25-
import { FormControl, FormGroup } from '@angular/forms';
25+
import { TableColumn } from '@shared/types/table.type';
2626

2727
const columnDefinitions: TableColumn[] = [
2828
{
@@ -123,9 +123,6 @@ export class ApplicationsTableComponent implements AfterViewInit, OnInit {
123123
faFlagIcon = faFlag;
124124

125125
displayedColumns: string[] = [];
126-
optionalColumnsSelected: string[] = [];
127-
optionalColumnOptions: TableColumn[] = [];
128-
d;
129126

130127
data: Application[] = [];
131128

@@ -144,27 +141,13 @@ export class ApplicationsTableComponent implements AfterViewInit, OnInit {
144141
public translate: TranslateService,
145142
private applicationService: ApplicationService,
146143
private router: Router,
147-
private deleteDialogService: DeleteDialogService
144+
private deleteDialogService: DeleteDialogService,
145+
private cdRef: ChangeDetectorRef
148146
) {}
149147

150148
ngOnInit() {
151-
this.optionalColumnOptions = columnDefinitions.filter((o) => o.toggleable);
152-
153-
const userDisplayedColumns = localStorage.getItem(
154-
this.applicationSavedColumns
155-
);
156-
if (userDisplayedColumns) {
157-
const chosenColumns = userDisplayedColumns.split(',');
158-
this.displayedColumns = chosenColumns;
159-
this.optionalColumnsSelected = chosenColumns;
160-
} else {
161-
this.optionalColumnsSelected = columnDefinitions
162-
.filter((o) => o.toggleable && o.default)
163-
.map((o) => o.id);
164-
this.displayedColumns = columnDefinitions
165-
.filter((o) => o.default)
166-
.map((o) => o.id);
167-
}
149+
// Detect changes done by child column selector
150+
this.cdRef.detectChanges();
168151
}
169152

170153
ngAfterViewInit() {
@@ -235,20 +218,6 @@ export class ApplicationsTableComponent implements AfterViewInit, OnInit {
235218
this.router.navigate(['applications', 'edit-application', applicationId]);
236219
}
237220

238-
handleColumnSelection({
239-
source: { value: selectedColumns },
240-
}: MatSelectChange) {
241-
const displayedColumns = columnDefinitions
242-
.filter((o) => !o.toggleable || selectedColumns.includes(o.id))
243-
.map((o) => o.id);
244-
245-
localStorage.setItem(
246-
this.applicationSavedColumns,
247-
displayedColumns.join(',')
248-
);
249-
this.displayedColumns = displayedColumns;
250-
}
251-
252221
mapControlledProperties(value: ControlledProperty[]) {
253222
if (!value.length) return '-';
254223

@@ -282,23 +251,5 @@ export class ApplicationsTableComponent implements AfterViewInit, OnInit {
282251
return !!result;
283252
}
284253

285-
selectAll() {
286-
const allOptional = this.optionalColumnOptions.map((o) => o.id);
287-
this.handleColumnSelection({
288-
source: { value: allOptional },
289-
} as MatSelectChange);
290-
this.optionalColumnsSelected = allOptional;
291-
}
292-
293-
deSelectAll() {
294-
this.handleColumnSelection({ source: { value: [] } } as MatSelectChange);
295-
this.optionalColumnsSelected = [];
296-
}
297-
}
298-
299-
interface TableColumn {
300-
id: string;
301-
display: string;
302-
default: boolean;
303-
toggleable: boolean;
254+
protected readonly columnDefinitions = columnDefinitions;
304255
}

src/app/applications/iot-devices/iot-devices-table/iot-devices-table.component.html

Lines changed: 71 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
<app-column-selector
2+
[(displayedColumns)]="displayedColumns"
3+
[columnDefinitions]="columnDefinitions"
4+
[localStorageKey]="iotDeviceSavedColumns"
5+
[placeholder]=""
6+
></app-column-selector>
7+
18
<div class="mat-elevation-z8">
29
<div class="loading-shade" *ngIf="isLoadingResults">
310
<mat-spinner *ngIf="isLoadingResults"></mat-spinner>
@@ -15,60 +22,47 @@
1522
<th mat-sort-header="name" *matHeaderCellDef mat-header-cell>
1623
{{ 'APPLICATION-TABLE.NAME' | translate }}
1724
</th>
18-
<td mat-cell *matCellDef="let element">
19-
<a [routerLink]="['../iot-device', element.id]" routerLinkActive="active" class="device-link">{{element.name}}</a>
25+
<td mat-cell *matCellDef="let iotDevice">
26+
<a [routerLink]="['../iot-device', iotDevice.id]" routerLinkActive="active" class="device-link">{{iotDevice.name}}</a>
2027
</td>
2128
</ng-container>
2229

2330
<!-- Technology Column -->
24-
<ng-container matColumnDef="technology">
25-
<th mat-header-cell *matHeaderCellDef>{{ 'IOT-TABLE.NETWORK-TECHNOLOGY' | translate }}</th>
26-
<td mat-cell *matCellDef="let element">{{"IOT-DEVICE-TYPES." + element.type | translate}}</td>
31+
<ng-container matColumnDef="type">
32+
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'IOT-TABLE.NETWORK-TECHNOLOGY' | translate }}</th>
33+
<td mat-cell *matCellDef="let iotDevice">{{"IOT-DEVICE-TYPES." + iotDevice.type | translate }}</td>
2734
</ng-container>
2835

29-
<!-- Alarm Column -->
30-
<ng-container matColumnDef="alarm">
31-
<th mat-header-cell *matHeaderCellDef>{{ 'IOT-TABLE.ALARM' | translate }}</th>
32-
<td mat-cell *matCellDef="let element">
33-
N/A
34-
</td>
36+
<ng-container matColumnDef="deviceModel">
37+
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'IOTDEVICE.DEVICEMODEL' | translate }}</th>
38+
<td mat-cell *matCellDef="let iotDevice">{{ iotDevice.deviceModel?.body?.name ?? '-' }}</td>
3539
</ng-container>
3640

37-
<!-- Battery Column -->
38-
<ng-container matColumnDef="battery">
39-
<th *matHeaderCellDef mat-header-cell>
40-
{{ 'IOT-TABLE.BATTERY' | translate }}
41-
</th>
42-
<td mat-cell *matCellDef="let element">
43-
<div *ngIf="element.type === 'LORAWAN'; else noBatteryStatus">
44-
<app-batteri-status [color]="batteryStatusColor" [percentage]="getBatteryProcentage(element)">
45-
</app-batteri-status>
46-
</div>
47-
<ng-template #noBatteryStatus>
48-
<div>
49-
{{ 'IOTDEVICE-TABLE-ROW.NOT-SUPPORTED-SHORT' | translate }}
50-
</div>
51-
</ng-template>
52-
</td>
41+
<ng-container matColumnDef="deviceProfileName">
42+
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'IOTDEVICE.LORA.DEVICEPROFILE' | translate }}</th>
43+
<td mat-cell *matCellDef="let iotDevice">{{ iotDevice.deviceProfileName ?? '-'}}</td>
5344
</ng-container>
5445

55-
<!-- Active Column -->
56-
<ng-container matColumnDef="active">
57-
<th mat-header-cell *matHeaderCellDef mat-sort-header="active">
58-
{{ 'IOT-TABLE.ACTIVE' | translate }}
59-
</th>
60-
<td mat-cell *matCellDef="let element">{{lastActive(element)}}</td>
46+
<ng-container matColumnDef="deviceEUI">
47+
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'IOT-TABLE.DEV-EUI' | translate }}</th>
48+
<td mat-cell *matCellDef="let iotDevice">{{iotDevice.deviceEUI ?? '-'}}</td>
49+
</ng-container>
50+
51+
<ng-container matColumnDef="OTAAapplicationKey">
52+
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'IOT-TABLE.APP-KEY' | translate }}</th>
53+
<td mat-cell *matCellDef="let iotDevice">{{iotDevice.OTAAapplicationKey ?? '-'}}</td>
6154
</ng-container>
6255

56+
6357
<!-- RSSI column -->
6458
<ng-container matColumnDef="rssi">
6559
<th *matHeaderCellDef mat-header-cell mat-sort-header="rssi">
6660
{{ 'IOT-TABLE.RSSI' | translate }}
6761
</th>
68-
<td mat-cell *matCellDef="let element">
69-
<ng-container *ngIf="element.type === 'LORAWAN' || element.type === 'SIGFOX'; else notSupported">
70-
<div *ngIf="element.latestReceivedMessage?.rssi; else notAvailable">
71-
{{element.latestReceivedMessage.rssi}}
62+
<td mat-cell *matCellDef="let iotDevice">
63+
<ng-container *ngIf="iotDevice.type === 'LORAWAN' || iotDevice.type === 'SIGFOX'; else notSupported">
64+
<div *ngIf="iotDevice.latestReceivedMessage?.rssi; else notAvailable">
65+
{{iotDevice.latestReceivedMessage.rssi}}
7266
</div>
7367
</ng-container>
7468
</td>
@@ -79,30 +73,61 @@
7973
<th *matHeaderCellDef mat-header-cell mat-sort-header="snr">
8074
{{ 'IOT-TABLE.SNR' | translate }}
8175
</th>
82-
<td mat-cell *matCellDef="let element">
83-
<ng-container *ngIf="element.type === 'LORAWAN' || element.type === 'SIGFOX'; else notSupported">
84-
<div *ngIf="element.latestReceivedMessage?.snr; else notAvailable">
85-
{{element.latestReceivedMessage?.snr}}
76+
<td mat-cell *matCellDef="let iotDevice">
77+
<ng-container *ngIf="iotDevice.type === 'LORAWAN' || iotDevice.type === 'SIGFOX'; else notSupported">
78+
<div *ngIf="iotDevice.latestReceivedMessage?.snr; else notAvailable">
79+
{{iotDevice.latestReceivedMessage?.snr}}
8680
</div>
8781
</ng-container>
8882
</td>
8983
</ng-container>
9084

85+
<ng-container matColumnDef="dataTargets">
86+
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'APPLICATION-TABLE.DATA-TARGETS' | translate }}</th>
87+
<td mat-cell *matCellDef="let iotDevice">{{iotDevice.connections?.length ?? 0}}</td>
88+
</ng-container>
89+
90+
<!-- Battery Column -->
91+
<ng-container matColumnDef="battery">
92+
<th *matHeaderCellDef mat-header-cell>
93+
{{ 'IOT-TABLE.BATTERY' | translate }}
94+
</th>
95+
<td mat-cell *matCellDef="let iotDevice">
96+
<div *ngIf="iotDevice.type === 'LORAWAN'; else noBatteryStatus">
97+
<app-batteri-status [color]="batteryStatusColor" [percentage]="getBatteryProcentage(iotDevice)">
98+
</app-batteri-status>
99+
</div>
100+
<ng-template #noBatteryStatus>
101+
<div>
102+
{{ 'IOTDEVICE-TABLE-ROW.NOT-SUPPORTED-SHORT' | translate }}
103+
</div>
104+
</ng-template>
105+
</td>
106+
</ng-container>
107+
108+
<!-- Active Column -->
109+
<ng-container matColumnDef="active">
110+
<th mat-header-cell *matHeaderCellDef mat-sort-header="active">
111+
{{ 'IOT-TABLE.ACTIVE' | translate }}
112+
</th>
113+
<td mat-cell *matCellDef="let iotDevice">{{lastActive(iotDevice)}}</td>
114+
</ng-container>
115+
91116
<ng-container matColumnDef="menu">
92117
<th mat-header-cell *matHeaderCellDef></th>
93-
<td mat-cell *matCellDef="let element">
118+
<td mat-cell *matCellDef="let iotDevice">
94119
<div class="dropdown" *ngIf="canEdit">
95-
<a href="#" role="button" id="tableRowDropdown-{{element.id}}" class="applicationRow__edit dropdown-toggle"
120+
<a href="#" role="button" id="tableRowDropdown-{{iotDevice.id}}" class="applicationRow__edit dropdown-toggle"
96121
data-toggle="dropdown" aria-expanded="false"
97122
[attr.aria-label]="'APPLICATION-TABLE-ROW.SHOW-OPTIONS' | translate"></a>
98-
<ul class="dropdown-menu dropdown-menu--table" attr.aria-labelledby="tableRowDropdown-{{element.id}}">
123+
<ul class="dropdown-menu dropdown-menu--table" attr.aria-labelledby="tableRowDropdown-{{iotDevice.id}}">
99124
<li class="dropdown-item">
100-
<a [routerLink]="['../iot-device-edit', element.id]" routerLinkActive="active">{{ 'IOTDEVICE-TABLE-ROW.EDIT'
125+
<a [routerLink]="['../iot-device-edit', iotDevice.id]" routerLinkActive="active">{{ 'IOTDEVICE-TABLE-ROW.EDIT'
101126
| translate }}
102127
</a>
103128
</li>
104129
<li class="dropdown-item">
105-
<a (click)="clickDelete(element)" [routerLink]="[]">{{ 'IOTDEVICE-TABLE-ROW.DELETE' | translate }}</a>
130+
<a (click)="clickDelete(iotDevice)" [routerLink]="[]">{{ 'IOTDEVICE-TABLE-ROW.DELETE' | translate }}</a>
106131
</li>
107132
</ul>
108133
</div>

0 commit comments

Comments
 (0)