Skip to content

Commit

Permalink
IOT-1507: More gateway metadata (#158)
Browse files Browse the repository at this point in the history
* Added new gateway metadata to table and details view

* Added minwidths to gateway table
  • Loading branch information
fcv-iteratorIt committed Apr 18, 2024
1 parent dcf75e1 commit 41b4140
Show file tree
Hide file tree
Showing 12 changed files with 431 additions and 145 deletions.
12 changes: 6 additions & 6 deletions src/app/applications/enums/status.enum.ts
@@ -1,11 +1,11 @@
import { recordToEntries } from '@shared/helpers/record.helper';
import { recordToEntries } from "@shared/helpers/record.helper";

export enum ApplicationStatus {
'NONE' = 'NONE',
'IN-OPERATION' = 'IN-OPERATION',
'PROJECT' = 'PROJECT',
'PROTOTYPE' = 'PROTOTYPE',
'OTHER' = 'OTHER',
"NONE" = "NONE",
"IN-OPERATION" = "IN-OPERATION",
"PROJECT" = "PROJECT",
"PROTOTYPE" = "PROTOTYPE",
"OTHER" = "OTHER",
}

export const ApplicationStatusEntries = recordToEntries(ApplicationStatus);
@@ -1,17 +1,17 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { DialogModel } from '@shared/models/dialog.model';
import { Component, Inject, OnInit } from "@angular/core";
import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog";
import { DialogModel } from "@shared/models/dialog.model";

@Component({
selector: 'export-csv-dialog',
templateUrl: './export-csv-dialog.component.html',
styleUrls: ['./export-csv-dialog.component.scss'],
selector: "export-csv-dialog",
templateUrl: "./export-csv-dialog.component.html",
styleUrls: ["./export-csv-dialog.component.scss"],
})
export class ExportCsvDialogComponent implements OnInit {
constructor(
public dialog: MatDialogRef<ExportCsvDialogComponent>,
@Inject(MAT_DIALOG_DATA) public dialogModel: DialogModel
) {}
constructor(
public dialog: MatDialogRef<ExportCsvDialogComponent>,
@Inject(MAT_DIALOG_DATA) public dialogModel: DialogModel
) {}

ngOnInit(): void {}
ngOnInit(): void {}
}
50 changes: 34 additions & 16 deletions src/app/gateway/enums/gateway-status-interval.enum.ts
@@ -1,22 +1,40 @@
import moment from 'moment';
import moment from "moment";
import { recordToEntries } from "@shared/helpers/record.helper";

export enum GatewayStatusInterval {
DAY = 'DAY',
WEEK = 'WEEK',
MONTH = 'MONTH',
DAY = "DAY",
WEEK = "WEEK",
MONTH = "MONTH",
}

export const gatewayStatusIntervalToDate = (
interval: GatewayStatusInterval
): Date => {
const now = new Date();
export const gatewayStatusIntervalToDate = (interval: GatewayStatusInterval): Date => {
const now = new Date();

switch (interval) {
case GatewayStatusInterval.WEEK:
return moment(now).subtract(7, 'days').toDate();
case GatewayStatusInterval.MONTH:
return moment(now).subtract(30, 'days').toDate();
default:
return moment(now).subtract(1, 'days').toDate();
}
switch (interval) {
case GatewayStatusInterval.WEEK:
return moment(now).subtract(7, "days").toDate();
case GatewayStatusInterval.MONTH:
return moment(now).subtract(30, "days").toDate();
default:
return moment(now).subtract(1, "days").toDate();
}
};

export enum GatewayPlacement {
"NONE" = "NONE",
"OUTDOORS" = "OUTDOORS",
"INDOORS" = "INDOORS",
"OTHER" = "OTHER",
}

export enum GatewaySetupStatus {
"NONE" = "NONE",
"IN-OPERATION" = "IN-OPERATION",
"PROJECT" = "PROJECT",
"PROTOTYPE" = "PROTOTYPE",
"OTHER" = "OTHER",
}

export const GatewayPlacementEntries = recordToEntries(GatewayPlacement);

export const GatewayStatusEntries = recordToEntries(GatewaySetupStatus);
9 changes: 9 additions & 0 deletions src/app/gateway/gateway-detail/gateway-detail.component.html
Expand Up @@ -10,6 +10,15 @@ <h3>{{ 'GATEWAY.DETAILS' | translate }}</h3>
<p><strong>{{ 'GATEWAY.ORGANIZATION' | translate }}</strong>{{gateway.organizationName}}</p>
<app-general-details [data]="gateway"></app-general-details>
<p><strong>{{ 'GATEWAY.TAGS' | translate }}</strong>{{gateway.tagsString}}</p>
<p *ngIf="gateway.placement"><strong>{{ 'LORA-GATEWAY-TABLE.PLACEMENT' | translate }}</strong>{{'GATEWAY.PLACEMENT.' + gateway.placement | translate}}</p>
<p *ngIf="gateway.modelName"><strong>{{ 'LORA-GATEWAY-TABLE.MODEL-NAME' | translate }}</strong>{{gateway.modelName}}</p>
<p *ngIf="gateway.antennaType"><strong>{{ 'LORA-GATEWAY-TABLE.ANTENNA-TYPE' | translate }}</strong>{{gateway.antennaType}}</p>
<p *ngIf="gateway.status"><strong>{{ 'LORA-GATEWAY-TABLE.STATUS' | translate }}</strong>{{'GATEWAY.STATUS.' + gateway.status | translate}}</p>
<p *ngIf="gateway.gatewayResponsibleName"><strong>{{ 'LORA-GATEWAY-TABLE.RESPONSIBLE-NAME' | translate }}</strong>{{gateway.gatewayResponsibleName}}</p>
<p *ngIf="gateway.gatewayResponsibleEmail"><strong>{{ 'LORA-GATEWAY-TABLE.RESPONSIBLE-EMAIL' | translate }}</strong>{{gateway.gatewayResponsibleEmail}}</p>
<p *ngIf="gateway.gatewayResponsiblePhoneNumber"><strong>{{ 'LORA-GATEWAY-TABLE.RESPONSIBLE-PHONE-NUMBER' | translate }}</strong>{{gateway.gatewayResponsiblePhoneNumber}}</p>
<p *ngIf="gateway.operationalResponsibleName"><strong>{{ 'LORA-GATEWAY-TABLE.OPERATIONAL-NAME' | translate }}</strong>{{gateway.operationalResponsibleName}}</p>
<p *ngIf="gateway.operationalResponsibleEmail"><strong>{{ 'LORA-GATEWAY-TABLE.OPERATIONAL-EMAIL' | translate }}</strong>{{gateway.operationalResponsibleEmail}}</p>
<p *ngIf="gateway.description">
<strong>{{ 'GATEWAY.DESCRIPTION' | translate }}</strong>
</p>
Expand Down
116 changes: 94 additions & 22 deletions src/app/gateway/gateway-edit/gateway-edit.component.html
Expand Up @@ -3,34 +3,35 @@
<div *ngIf="errorMessages" class="error-messages p-3">
<ul class="mb-0">
<li *ngFor="let error of errorMessages">
{{error | translate}}
{{ error | translate }}
</li>
</ul>
</div>

<div class="row">
<div class="form-group mt-3">
<label class="form-label" for="name">{{'QUESTION-LORA-GATEWAY.NAME' | translate}}</label>*
<label class="form-label" for="name">{{ 'QUESTION-LORA-GATEWAY.NAME' | translate }}</label>*
<input type="text" class="form-control" id="name" name="name"
[placeholder]="'QUESTION-LORA-GATEWAY.NAME-PLACEHOLDER' | translate" maxlength="50" required
[(ngModel)]="gateway.name"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('name'), 'is-valid' : formFailedSubmit && !errorFields.includes('name')}">
[placeholder]="'QUESTION-LORA-GATEWAY.NAME-PLACEHOLDER' | translate" maxlength="50" required
[(ngModel)]="gateway.name"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('name'), 'is-valid' : formFailedSubmit && !errorFields.includes('name')}">
</div>

<div class="form-group mt-3">
<label class="form-label" for="description">{{'QUESTION-LORA-GATEWAY.DESCRIPTION' | translate}}</label>
<label class="form-label" for="description">{{ 'QUESTION-LORA-GATEWAY.DESCRIPTION' | translate }}</label>
<textarea id="description" name="description"
[placeholder]="'QUESTION-LORA-GATEWAY.DESCRIPTION-PLACEHOLDER' | translate" [maxLength]="1024" [rows]="6"
class="form-control" [(ngModel)]="gateway.description"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('description'), 'is-valid' : formFailedSubmit && !errorFields.includes('description')}"></textarea>
[placeholder]="'QUESTION-LORA-GATEWAY.DESCRIPTION-PLACEHOLDER' | translate" [maxLength]="1024"
[rows]="6"
class="form-control" [(ngModel)]="gateway.description"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('description'), 'is-valid' : formFailedSubmit && !errorFields.includes('description')}"></textarea>
</div>

<div class="form-group mt-3">
<label class="form-label" for="gatewayId">{{'QUESTION-LORA-GATEWAY.GATEWAYID' | translate}}</label>*
<label class="form-label" for="gatewayId">{{ 'QUESTION-LORA-GATEWAY.GATEWAYID' | translate }}</label>*
<input type="text" class="form-control" id="gatewayId" name="gatewayId"
[placeholder]="'QUESTION-LORA-GATEWAY.GATEWAYID-PLACEHOLDER' | translate" required
[(ngModel)]="gateway.gatewayId" [readonly]="editMode"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('gatewayId'), 'is-valid' : formFailedSubmit && !errorFields.includes('gatewayId')}">
[placeholder]="'QUESTION-LORA-GATEWAY.GATEWAYID-PLACEHOLDER' | translate" required
[(ngModel)]="gateway.gatewayId" [readonly]="editMode"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('gatewayId'), 'is-valid' : formFailedSubmit && !errorFields.includes('gatewayId')}">
</div>

<div class="row thirty-height" *ngIf="gateway.location">
Expand All @@ -40,41 +41,112 @@

<div class="row mb-5">
<div class="form-group mt-3 col-6">
<label class="form-label" for="longitude">{{'GATEWAY.LONGITUDE' | translate}}</label>
<label class="form-label" for="longitude">{{ 'GATEWAY.LONGITUDE' | translate }}</label>
<input type="number" class="form-control" id="longitude" name="longitude" [placeholder]="'00'" required
[(ngModel)]="gateway.location.longitude" step=".000001" min="-180" max="180" maxlength="12"
(keyup)="onCoordinateKey($event)"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('longitude'), 'is-valid' : formFailedSubmit && !errorFields.includes('longitude')}">
</div>

<div class="form-group mt-3 col-6">
<label class="form-label" for="location.latitude">{{'GATEWAY.LATITUDE' | translate}}</label>
<label class="form-label" for="location.latitude">{{ 'GATEWAY.LATITUDE' | translate }}</label>
<input type="number" class="form-control" id="location.latitude" name="location.latitude" [placeholder]="'00'"
required [(ngModel)]="gateway.location.latitude" step=".000001" min="-180" max="180" maxlength="12"
(keyup)="onCoordinateKey($event)"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('latitude'), 'is-valid' : formFailedSubmit && !errorFields.includes('latitude')}">
</div>

<div class="form-group mt-3 col-6">
<label class="form-label" for="altitude">{{'GATEWAY.ALTITUDE' | translate}}</label>
<label class="form-label" for="altitude">{{ 'GATEWAY.ALTITUDE' | translate }}</label>
<input type="number" class="form-control" id="altitude" name="altitude" [placeholder]="00" required
[(ngModel)]="gateway.location.altitude" step=".000001" min="-180" max="180" maxlength="9"
(keyup)="onCoordinateKey($event)"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('altitude'), 'is-valid' : formFailedSubmit && !errorFields.includes('altitude')}">
</div>
<div class="form-group mt-3">
<label class="form-label" for="placement">{{ 'GATEWAY.PLACEMENT-LABEL' | translate }}</label>
<mat-select class="form-control" id="placement" name="gateway.placementOptions" [(ngModel)]="gateway.placement"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('placement'), 'is-valid' : formFailedSubmit && !errorFields.includes('placement')}">
<mat-option *ngFor="let placementOption of placements" [value]="placementOption.value">
{{ placementOption.label }}
</mat-option>
</mat-select>
</div>
<div class="form-group mt-3">
<label class="form-label" for="modelName">{{ 'QUESTION-LORA-GATEWAY.MODEL-NAME' | translate }}</label>
<input type="text" class="form-control" id="modelName" name="modelName"
[placeholder]="'QUESTION-LORA-GATEWAY.MODEL-NAME-PLACEHOLDER' | translate"
[(ngModel)]="gateway.modelName"
[ngClass]="{'is-invalid': formFailedSubmit && errorFields.includes('modelName'), 'is-valid': formFailedSubmit && !errorFields.includes('modelName')}">
</div>
<div class="form-group mt-3">
<label class="form-label" for="antennaType">{{ 'QUESTION-LORA-GATEWAY.ANTENNA-TYPE' | translate }}</label>
<input type="text" class="form-control" id="antennaType" name="antennaType"
[placeholder]="'QUESTION-LORA-GATEWAY.ANTENNA-TYPE-PLACEHOLDER' | translate"
[(ngModel)]="gateway.antennaType"
[ngClass]="{'is-invalid': formFailedSubmit && errorFields.includes('antennaType'), 'is-valid': formFailedSubmit && !errorFields.includes('antennaType')}">
</div>
<div class="form-group mt-3">
<label class="form-label" for="status">{{ 'GATEWAY.STATUS-NAME' | translate }}</label>
<mat-select class="form-control" id="status" name="gateway.statusOptions" [(ngModel)]="gateway.status"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('status'), 'is-valid' : formFailedSubmit && !errorFields.includes('status')}">
<mat-option *ngFor="let statusOption of statuses" [value]="statusOption.value">
{{ statusOption.label }}
</mat-option>
</mat-select>
</div>
<div class="form-group mt-3">
<label class="form-label"
for="gatewayResponsible">{{ 'QUESTION-LORA-GATEWAY.GATEWAY-RESPONSIBLE' | translate }}</label>
<input type="text" class="form-control" id="gatewayResponsible" name="gatewayResponsible"
[placeholder]="'QUESTION-LORA-GATEWAY.GATEWAY-RESPONSIBLE-PLACEHOLDER' | translate"
[(ngModel)]="gateway.gatewayResponsibleName"
[ngClass]="{'is-invalid': formFailedSubmit && errorFields.includes('gatewayResponsibleName'), 'is-valid': formFailedSubmit && !errorFields.includes('gatewayResponsibleName')}">
</div>
<div class="form-group mt-3">
<label class="form-label"
for="gatewayResponsibleEmail">{{ 'QUESTION-LORA-GATEWAY.GATEWAY-RESPONSIBLE-CONTACT' | translate }}</label>
<input type="text" class="form-control" id="gatewayResponsibleEmail" name="gatewayResponsibleEmail"
[placeholder]="'QUESTION-LORA-GATEWAY.GATEWAY-RESPONSIBLE-CONTACT-PLACEHOLDER' | translate"
[(ngModel)]="gateway.gatewayResponsibleEmail"
[ngClass]="{'is-invalid': formFailedSubmit && errorFields.includes('gatewayResponsibleEmail'), 'is-valid': formFailedSubmit && !errorFields.includes('gatewayResponsibleEmail')}">
</div>
<div class="form-group mt-3">
<label class="form-label"
for="gatewayResponsiblePhoneNumber">{{ 'QUESTION-LORA-GATEWAY.GATEWAY-RESPONSIBLE-CONTACT-NUMBER' | translate }}</label>
<input type="text" class="form-control" id="gatewayResponsiblePhoneNumber" name="gatewayResponsiblePhoneNumber"
[placeholder]="'QUESTION-LORA-GATEWAY.GATEWAY-RESPONSIBLE-CONTACT-NUMBER-PLACEHOLDER' | translate"
[(ngModel)]="gateway.gatewayResponsiblePhoneNumber"
[ngClass]="{'is-invalid': formFailedSubmit && errorFields.includes('gatewayResponsiblePhoneNumber'), 'is-valid': formFailedSubmit && !errorFields.includes('gatewayResponsiblePhoneNumber')}">
</div>
<div class="form-group mt-3">
<label class="form-label"
for="operationalResponsibleName">{{ 'QUESTION-LORA-GATEWAY.OPERATION-RESPONSIBLE' | translate }}</label>
<input type="text" class="form-control" id="operationalResponsibleName" name="operationalResponsibleName"
[placeholder]="'QUESTION-LORA-GATEWAY.OPERATION-RESPONSIBLE-PLACEHOLDER' | translate"
[(ngModel)]="gateway.operationalResponsibleName"
[ngClass]="{'is-invalid': formFailedSubmit && errorFields.includes('operationalResponsibleName'), 'is-valid': formFailedSubmit && !errorFields.includes('operationalResponsibleName')}">
</div>
<div class="form-group mt-3">
<label class="form-label"
for="operationalResponsibleEmail">{{ 'QUESTION-LORA-GATEWAY.OPERATION-RESPONSIBLE-CONTACT' | translate }}</label>
<input type="text" class="form-control" id="operationalResponsibleEmail" name="operationalResponsibleEmail"
[placeholder]="'QUESTION-LORA-GATEWAY.OPERATION-RESPONSIBLE-CONTACT-PLACEHOLDER' | translate"
[(ngModel)]="gateway.operationalResponsibleEmail"
[ngClass]="{'is-invalid': formFailedSubmit && errorFields.includes('operationalResponsibleEmail'), 'is-valid': formFailedSubmit && !errorFields.includes('operationalResponsibleEmail')}">
</div>
</div>
<div class="form-group mt-3">
<label class="form-label" for="tagsString">{{'QUESTION-LORA-GATEWAY.METADATA' | translate}}</label>
<label class="form-label" for="tagsString">{{ 'QUESTION-LORA-GATEWAY.METADATA' | translate }}</label>
<input type="text" class="form-control" id="tagsString" name="tagsString"
[placeholder]="'QUESTION-LORA-GATEWAY.METADATA-PLACEHOLDER' | translate" required
[(ngModel)]="gateway.tagsString"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('tagsString'), 'is-valid' : formFailedSubmit && !errorFields.includes('tagsString')}">
[placeholder]="'QUESTION-LORA-GATEWAY.METADATA-PLACEHOLDER' | translate" required
[(ngModel)]="gateway.tagsString"
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('tagsString'), 'is-valid' : formFailedSubmit && !errorFields.includes('tagsString')}">
</div>

</div>

<div class="form-group mt-5">
<button (click)="routeBack()" class="btn btn-secondary" type="button">{{ 'GEN.CANCEL' | translate}}</button>
<button (click)="routeBack()" class="btn btn-secondary" type="button">{{ 'GEN.CANCEL' | translate }}</button>
<button class="btn btn-primary ml-2" type="submit">{{ submitButton }}</button>
</div>
</form>
Expand Down
34 changes: 32 additions & 2 deletions src/app/gateway/gateway-edit/gateway-edit.component.ts
Expand Up @@ -8,6 +8,17 @@ import { ScrollToTopService } from "@shared/services/scroll-to-top.service";
import { Subscription } from "rxjs";
import { ChirpstackGatewayService } from "src/app/shared/services/chirpstack-gateway.service";
import { Gateway, GatewayResponse } from "../gateway.model";
import {
GatewayPlacement,
GatewayPlacementEntries,
GatewaySetupStatus,
GatewayStatusEntries,
} from "@app/gateway/enums/gateway-status-interval.enum";

interface DropdownOption {
label: string;
value: string | number;
}

@Component({
selector: "app-gateway-edit",
Expand All @@ -16,9 +27,7 @@ import { Gateway, GatewayResponse } from "../gateway.model";
})
export class GatewayEditComponent implements OnInit, OnDestroy {
public backButton: BackButton = { label: "", routerLink: ["gateways"] };
public multiPage = false;
public title = "";
public sectionTitle = "";
public submitButton = "";

public gatewaySubscription: Subscription;
Expand All @@ -31,6 +40,9 @@ export class GatewayEditComponent implements OnInit, OnDestroy {

gateway = new Gateway();

placements: DropdownOption[] = [];
statuses: DropdownOption[] = [];

constructor(
private route: ActivatedRoute,
public translate: TranslateService,
Expand All @@ -53,6 +65,24 @@ export class GatewayEditComponent implements OnInit, OnDestroy {
this.title = translations["FORM.EDIT-NEW-GATEWAY"];
this.submitButton = translations["GATEWAY.SAVE"];
});

const placementTranslationPrefix = "GATEWAY.PLACEMENT.";
const placementTranslationKeys = GatewayPlacementEntries.map(x => placementTranslationPrefix + x.key);
const statusTranslationPrefix = "GATEWAY.STATUS.";
const statusTranslationKeys = GatewayStatusEntries.map(x => statusTranslationPrefix + x.key);

this.translate.get([...statusTranslationKeys, ...placementTranslationKeys]).subscribe(translations => {
const placementOptions: DropdownOption[] = GatewayPlacementEntries.map(entry => ({
label: translations[placementTranslationPrefix + entry.key],
value: GatewayPlacement[entry.key],
}));
this.placements.push(...placementOptions);
const statusOptions: DropdownOption[] = GatewayStatusEntries.map(entry => ({
label: translations[statusTranslationPrefix + entry.key],
value: GatewaySetupStatus[entry.key],
}));
this.statuses.push(...statusOptions);
});
}

getGateway(gatewayId: string): void {
Expand Down

0 comments on commit 41b4140

Please sign in to comment.