From 9af14630f123102cd82895ccb0e8219e8e3fe7ff Mon Sep 17 00:00:00 2001
From: Anurag Vats <anurag.vats@uibk.ac.at>
Date: Thu, 1 Dec 2022 12:39:05 +0100
Subject: [PATCH] Add pages and forms for the station

---
 src/app/app-routing.module.ts                 |  8 ++
 src/app/app.module.ts                         | 10 +-
 src/app/shared/api.service.ts                 | 12 ++-
 src/app/station/client/client.component.css   |  0
 src/app/station/client/client.component.html  | 75 ++++++++++++++
 .../station/client/client.component.spec.ts   | 23 +++++
 src/app/station/client/client.component.ts    | 99 +++++++++++++++++++
 src/app/station/client/client.model.ts        |  7 ++
 src/app/station/host/host.component.css       |  0
 src/app/station/host/host.component.html      | 64 ++++++++++++
 src/app/station/host/host.component.spec.ts   | 23 +++++
 src/app/station/host/host.component.ts        | 65 ++++++++++++
 src/app/station/host/host.model.ts            |  6 ++
 src/app/station/sensor/sensor.component.css   |  0
 src/app/station/sensor/sensor.component.html  | 87 ++++++++++++++++
 .../station/sensor/sensor.component.spec.ts   | 23 +++++
 src/app/station/sensor/sensor.component.ts    | 86 ++++++++++++++++
 src/app/station/sensor/sensor.model.ts        | 21 ++++
 src/app/station/station.component.css         |  0
 src/app/station/station.component.html        | 56 +++++++++++
 src/app/station/station.component.spec.ts     | 23 +++++
 src/app/station/station.component.ts          | 27 +++++
 src/app/station/station.model.ts              | 15 +++
 23 files changed, 725 insertions(+), 5 deletions(-)
 create mode 100644 src/app/station/client/client.component.css
 create mode 100644 src/app/station/client/client.component.html
 create mode 100644 src/app/station/client/client.component.spec.ts
 create mode 100644 src/app/station/client/client.component.ts
 create mode 100644 src/app/station/client/client.model.ts
 create mode 100644 src/app/station/host/host.component.css
 create mode 100644 src/app/station/host/host.component.html
 create mode 100644 src/app/station/host/host.component.spec.ts
 create mode 100644 src/app/station/host/host.component.ts
 create mode 100644 src/app/station/host/host.model.ts
 create mode 100644 src/app/station/sensor/sensor.component.css
 create mode 100644 src/app/station/sensor/sensor.component.html
 create mode 100644 src/app/station/sensor/sensor.component.spec.ts
 create mode 100644 src/app/station/sensor/sensor.component.ts
 create mode 100644 src/app/station/sensor/sensor.model.ts
 create mode 100644 src/app/station/station.component.css
 create mode 100644 src/app/station/station.component.html
 create mode 100644 src/app/station/station.component.spec.ts
 create mode 100644 src/app/station/station.component.ts
 create mode 100644 src/app/station/station.model.ts

diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 90f3b91..b72585a 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -5,12 +5,20 @@ import {SignupComponent} from "./signup/signup.component";
 import {AdminDashboardComponent} from "./admin-dashboard/admin-dashboard.component";
 import {AuthGuard} from "./shared/auth/auth.guard";
 import {RoleGuard} from "./shared/auth/role.guard";
+import {StationComponent} from "./station/station.component";
+import {SensorComponent} from "./station/sensor/sensor.component";
+import {ClientComponent} from "./station/client/client.component";
+import {HostComponent} from "./station/host/host.component";
 
 const routes: Routes = [
   {path: '', redirectTo: 'login', pathMatch: 'full'},
   {path: 'login', component: LoginComponent},
   {path: 'signup', component: SignupComponent},
   {path: 'admin', component: AdminDashboardComponent, canActivate: [RoleGuard], data: {roles: ['ROLE_ADMIN']}},
+  {path: 'station', component: StationComponent, canActivate: [RoleGuard], data: {roles: ['ROLE_ADMIN']}},
+  {path: 'station/sensor', component: SensorComponent, canActivate: [RoleGuard], data: {roles: ['ROLE_ADMIN']}},
+  {path: 'station/client', component: ClientComponent, canActivate: [RoleGuard], data: {roles: ['ROLE_ADMIN']}},
+  {path: 'station/host', component: HostComponent, canActivate: [RoleGuard], data: {roles: ['ROLE_ADMIN']}},
 ];
 
 @NgModule({
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 42bc1f6..99da334 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -8,13 +8,21 @@ import {ReactiveFormsModule} from "@angular/forms";
 import {HttpClientModule} from "@angular/common/http";
 import { LoginComponent } from './login/login.component';
 import { SignupComponent } from './signup/signup.component';
+import {StationComponent} from "./station/station.component";
+import {ClientComponent} from "./station/client/client.component";
+import {SensorComponent} from "./station/sensor/sensor.component";
+import {HostComponent} from "./station/host/host.component";
 
 @NgModule({
   declarations: [
     AppComponent,
     AdminDashboardComponent,
     LoginComponent,
-    SignupComponent
+    SignupComponent,
+    StationComponent,
+    HostComponent,
+    ClientComponent,
+    SensorComponent
   ],
   imports: [
     BrowserModule,
diff --git a/src/app/shared/api.service.ts b/src/app/shared/api.service.ts
index eaf7366..7b8a93e 100644
--- a/src/app/shared/api.service.ts
+++ b/src/app/shared/api.service.ts
@@ -11,30 +11,34 @@ export class ApiService {
   }
 
   get(url: string) {
-    return this.http.get(url)
+    return this.http.get(url, {headers: this.getHttpHeader()})
       .pipe(map((response: any) => {
         return response;
       }));
   }
 
   post(url: string, data: any) {
-    return this.http.post(url, data)
+    return this.http.post(url, data, {headers: this.getHttpHeader()})
       .pipe(map((response: any) => {
         return response;
       }));
   }
 
   delete(url: string, id: string) {
-    return this.http.delete(url + '/' + id)
+    return this.http.delete(url + '/' + id, {headers: this.getHttpHeader()})
       .pipe(map((response: any) => {
         return response;
       }));
   }
 
   put(url: string, id: string, data: any) {
-    return this.http.put(url + '/' + id, data)
+    return this.http.put(url + '/' + id, data, {headers: this.getHttpHeader()})
       .pipe(map((response: any) => {
         return response;
       }));
   }
+
+  private getHttpHeader() {
+    return {'Authorization': 'Bearer ' + localStorage.getItem('token')};
+  }
 }
diff --git a/src/app/station/client/client.component.css b/src/app/station/client/client.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/station/client/client.component.html b/src/app/station/client/client.component.html
new file mode 100644
index 0000000..a875d66
--- /dev/null
+++ b/src/app/station/client/client.component.html
@@ -0,0 +1,75 @@
+<div class="container">
+  <table class="table table-striped mt-3">
+    <thead>
+    <tr>
+      <th scope="col">Name</th>
+      <th scope="col">Mac</th>
+      <th scope="col">Connection type</th>
+      <th scope="col">Description</th>
+      <th scope="col">Sensors</th>
+      <th scope="col">
+        <div class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#clientModal"
+             (click)="onOpenAddClient()"
+        >Add Client</div>
+      </th>
+    </tr>
+    </thead>
+    <tbody>
+    <tr *ngFor="let row of clientData">
+      <td>{{row.name}}</td>
+      <td>{{row.mac}}</td>
+      <td>{{row.connectionType}}</td>
+      <td>{{row.description}}</td>
+      <td>
+        <div *ngFor="let id of row.sensors">{{getSensorName(id)}}</div>
+      </td>
+      <td>
+        <div class="btn btn-info m-1" type="button" data-bs-toggle="modal" data-bs-target="#clientModal"
+             (click)="onOpenEditClient(row)"
+        >Edit
+        </div>
+        <div class="btn btn-danger">Delete</div>
+      </td>
+    </tr>
+    </tbody>
+  </table>
+</div>
+
+
+<!-- Modal -->
+<div class="modal fade" id="clientModal" tabindex="-1" aria-labelledby="addClientModal" aria-hidden="true">
+  <div class="modal-dialog">
+    <div class="modal-content">
+      <div class="modal-header">
+        <h1 class="modal-title fs-5" id="addClientModal">Client Details</h1>
+        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+      </div>
+      <div class="modal-body">
+        <form [formGroup]="formClient">
+          <div class="mb-3">
+            <label for="name" class="form-label">name</label>
+            <input type="text" class="form-control" formControlName="name" id="name">
+          </div>
+          <div class="mb-3">
+            <label for="description" class="form-label">description</label>
+            <input type="text" class="form-control" formControlName="description" id="description">
+          </div>
+          <div class="mb-3">
+            <label for="mac" class="form-label">mac</label>
+            <input type="text" class="form-control" formControlName="mac" id="mac">
+          </div>
+          <div class="mb-3">
+            <label for="connectionType" class="form-label">connectionType</label>
+            <input type="text" class="form-control" formControlName="connectionType" id="connectionType">
+          </div>
+        </form>
+
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="cancel-btn">Close</button>
+        <button type="button" class="btn btn-primary" *ngIf="showAddButton" (click)="onAddClient()">Add</button>
+        <button type="button" class="btn btn-primary" *ngIf="showUpdateButton" (click)="onUpdateClient()">Update</button>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/src/app/station/client/client.component.spec.ts b/src/app/station/client/client.component.spec.ts
new file mode 100644
index 0000000..3002a16
--- /dev/null
+++ b/src/app/station/client/client.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ClientComponent } from './client.component';
+
+describe('ClientComponent', () => {
+  let component: ClientComponent;
+  let fixture: ComponentFixture<ClientComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ ClientComponent ]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(ClientComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/station/client/client.component.ts b/src/app/station/client/client.component.ts
new file mode 100644
index 0000000..eba9ceb
--- /dev/null
+++ b/src/app/station/client/client.component.ts
@@ -0,0 +1,99 @@
+import {Component} from '@angular/core';
+import {FormBuilder, FormGroup} from "@angular/forms";
+import {ApiService} from "../../shared/api.service";
+
+@Component({
+  selector: 'app-client',
+  templateUrl: './client.component.html',
+  styleUrls: ['./client.component.css']
+})
+export class ClientComponent {
+
+  formClient!: FormGroup;
+  clientData!: any;
+  showAddButton!: boolean;
+  showUpdateButton!: boolean;
+  sensorData!: any;
+  // dropdownList: any = [];
+  // selectedItems: any = [];
+  // dropdownSettings: any = {};
+
+  constructor(private api: ApiService, private fb: FormBuilder) {
+    this.getAllClients();
+    this.getAllSensors();
+    this.formClient = fb.group({
+      name: [''],
+      description: [''],
+      mac: [''],
+      connectionType: [''],
+      sensors: fb.array(['']),
+    });
+
+    // for (let i = 0; i < this.sensorData.length; i++) {
+    //   this.dropdownList.push({
+    //     id: i,
+    //     name: this.sensorData[i].name
+    //   });
+    // }
+    //
+    // this.sensorData.forEach((sensor: any) => {
+    //   this.dropdownList.push(sensor.name);
+    // });
+    //
+    // this.selectedItems = [];
+    //
+    // this.dropdownSettings = {
+    //   singleSelection: false,
+    //   idField: 'id',
+    //   textField: 'name',
+    //   selectAllText: 'Select All',
+    //   unSelectAllText: 'UnSelect All',
+    //   itemsShowLimit: 3,
+    //   allowSearchFilter: true
+    // };
+  }
+
+  private getAllClients() {
+    this.api.get('http://localhost:8080/api/v1/client/all')
+      .subscribe(res => {
+        this.clientData = res;
+      });
+  }
+
+  onAddClient() {
+    this.api.post('http://localhost:8080/api/v1/client/add', this.formClient.value)
+      .subscribe(res => {
+        this.getAllClients();
+        this.formClient.reset();
+        alert('Client added successfully');
+        document.getElementById('cancel-btn')?.click();
+      }, err => {
+        alert('An error has occurred.');
+      });
+  }
+
+  onOpenEditClient(row: any) {
+    this.showAddButton = false;
+    this.showUpdateButton = true;
+  }
+
+  onOpenAddClient() {
+    this.showAddButton = true;
+    this.showUpdateButton = false;
+  }
+
+  onUpdateClient() {
+
+  }
+
+  private getAllSensors() {
+    this.api.get('http://localhost:8080/api/v1/sensor/all')
+      .subscribe(res => {
+        this.sensorData = res;
+      });
+  }
+
+  getSensorName(sensorId: number) {
+    return this.sensorData.find((sensor: any) => sensor.id === sensorId).name;
+  }
+}
diff --git a/src/app/station/client/client.model.ts b/src/app/station/client/client.model.ts
new file mode 100644
index 0000000..c680cda
--- /dev/null
+++ b/src/app/station/client/client.model.ts
@@ -0,0 +1,7 @@
+export class ClientModel {
+  name!: string;
+  description!: string;
+  mac!: string;
+  connectionType!: string;
+  sensors!: string[];
+}
diff --git a/src/app/station/host/host.component.css b/src/app/station/host/host.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/station/host/host.component.html b/src/app/station/host/host.component.html
new file mode 100644
index 0000000..bfe9632
--- /dev/null
+++ b/src/app/station/host/host.component.html
@@ -0,0 +1,64 @@
+<div class="container">
+  <table class="table table-striped mt-3">
+    <thead>
+    <tr>
+      <th scope="col">Name</th>
+      <th scope="col">Sim Id</th>
+      <th scope="col">Mac Id</th>
+      <th scope="col">
+        <div class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#hostModal"
+        (click)="onOpenAddHost()"
+        >Add host</div>
+      </th>
+    </tr>
+    </thead>
+    <tbody>
+    <tr *ngFor="let row of hostData">
+      <td>{{row.name}}</td>
+      <td>{{row.simId}}</td>
+      <td>{{row.mac}}</td>
+      <td>
+        <div class="btn btn-info m-1" type="button" data-bs-toggle="modal" data-bs-target="#hostModal"
+        (click)="onOpenEditHost(row)"
+        >Edit
+        </div>
+        <div class="btn btn-danger">Delete</div>
+      </td>
+    </tr>
+    </tbody>
+  </table>
+</div>
+
+
+<!-- Modal -->
+<div class="modal fade" id="hostModal" tabindex="-1" aria-labelledby="addHostModal" aria-hidden="true">
+  <div class="modal-dialog">
+    <div class="modal-content">
+      <div class="modal-header">
+        <h1 class="modal-title fs-5" id="addHostModal">User Details</h1>
+        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+      </div>
+      <div class="modal-body">
+        <form [formGroup]="formHost">
+          <div class="mb-3">
+            <label for="name" class="form-label">name</label>
+            <input type="text" class="form-control" formControlName="name" id="name">
+          </div>
+          <div class="mb-3">
+            <label for="simId" class="form-label">simId</label>
+            <input type="text" class="form-control" formControlName="simId" id="simId">
+          </div>
+          <div class="mb-3">
+            <label for="mac" class="form-label">mac</label>
+            <input type="text" class="form-control" formControlName="mac" id="mac">
+          </div>
+        </form>
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="cancel-btn">Close</button>
+        <button type="button" class="btn btn-primary" *ngIf="showAddButton" (click)="onAddHost()">Add</button>
+        <button type="button" class="btn btn-primary" *ngIf="showUpdateButton" (click)="onUpdateHost()">Update</button>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/src/app/station/host/host.component.spec.ts b/src/app/station/host/host.component.spec.ts
new file mode 100644
index 0000000..acc684b
--- /dev/null
+++ b/src/app/station/host/host.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HostComponent } from './host.component';
+
+describe('HostComponent', () => {
+  let component: HostComponent;
+  let fixture: ComponentFixture<HostComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ HostComponent ]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(HostComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/station/host/host.component.ts b/src/app/station/host/host.component.ts
new file mode 100644
index 0000000..9cfcb0e
--- /dev/null
+++ b/src/app/station/host/host.component.ts
@@ -0,0 +1,65 @@
+import {Component} from '@angular/core';
+import {FormBuilder, FormGroup} from "@angular/forms";
+import {HostModel} from "./host.model";
+import {ApiService} from "../../shared/api.service";
+
+@Component({
+  selector: 'app-host',
+  templateUrl: './host.component.html',
+  styleUrls: ['./host.component.css']
+})
+export class HostComponent {
+
+  formHost!: FormGroup;
+  hostData!: any;
+  host: HostModel = new HostModel();
+  showAddButton!: boolean;
+  showUpdateButton!: boolean;
+
+  constructor(private api: ApiService, private formBuilder: FormBuilder) {
+    this.getAllHosts();
+    this.formHost = formBuilder.group({
+      name: [''],
+      simId: [''],
+      mac: [''],
+    });
+  }
+
+  // get all hosts
+  getAllHosts() {
+    this.api.get('http://localhost:8080/api/v1/host/all')
+      .subscribe(res => {
+        this.hostData = res;
+      });
+  }
+
+// add host
+  onAddHost() {
+    this.host.name = this.formHost.value.name;
+    this.host.simId = this.formHost.value.simId;
+    this.host.mac = this.formHost.value.mac;
+    this.api.post('http://localhost:8080/api/v1/host/add', this.host)
+      .subscribe(res => {
+        this.getAllHosts();
+        this.formHost.reset();
+        alert('Host added successfully');
+        document.getElementById('cancel-btn')?.click();
+      }, err => {
+        alert('An error has occurred.');
+      });
+  }
+
+  onOpenEditHost(row: any) {
+    this.showAddButton = false;
+    this.showUpdateButton = true;
+  }
+
+  onOpenAddHost() {
+    this.showAddButton = true;
+    this.showUpdateButton = false;
+  }
+
+  onUpdateHost() {
+
+  }
+}
diff --git a/src/app/station/host/host.model.ts b/src/app/station/host/host.model.ts
new file mode 100644
index 0000000..15b493e
--- /dev/null
+++ b/src/app/station/host/host.model.ts
@@ -0,0 +1,6 @@
+export class HostModel {
+  id!: number;
+  name!: string;
+  simId!: string;
+  mac!: string;
+}
diff --git a/src/app/station/sensor/sensor.component.css b/src/app/station/sensor/sensor.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/station/sensor/sensor.component.html b/src/app/station/sensor/sensor.component.html
new file mode 100644
index 0000000..91cc7f4
--- /dev/null
+++ b/src/app/station/sensor/sensor.component.html
@@ -0,0 +1,87 @@
+<div class="container">
+  <table class="table table-striped mt-3">
+    <thead>
+    <tr>
+      <th scope="col">Name</th>
+      <th scope="col">Protocol</th>
+      <th scope="col">Protocol Address</th>
+      <th scope="col">Description</th>
+      <th scope="col">Measures</th>
+      <th scope="col">
+        <div class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#sensorModal"
+             (click)="onOpenAddSensor()"
+        >Add sensor</div>
+      </th>
+    </tr>
+    </thead>
+    <tbody>
+    <tr *ngFor="let row of sensorData">
+      <td>{{row.name}}</td>
+      <td>{{row.protocol}}</td>
+      <td>{{row.protocolAddress}}</td>
+      <td>{{row.description}}</td>
+      <td>
+        <div *ngFor="let m of row.measurements">{{m.name}}</div>
+      </td>
+      <td>
+        <div class="btn btn-info m-1" data-bs-toggle="modal" data-bs-target="#sensorModal"
+             (click)="onOpenEditSensor(row)"
+        >Edit
+        </div>
+        <div class="btn btn-danger">Delete</div>
+      </td>
+    </tr>
+    </tbody>
+  </table>
+</div>
+
+
+<!-- Modal -->
+<div class="modal fade" id="sensorModal" tabindex="-1" aria-labelledby="addSensorModal" aria-hidden="true">
+  <div class="modal-dialog">
+    <div class="modal-content">
+      <div class="modal-header">
+        <h1 class="modal-title fs-5" id="addSensorModal">Sensor Def</h1>
+        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+      </div>
+      <div class="modal-body">
+        <form [formGroup]="sensorForm">
+          <div class="mb-3">
+            <label for="name" class="form-label">name</label>
+            <input type="text" class="form-control" formControlName="name" id="name">
+          </div>
+          <div class="mb-3">
+            <label for="protocol" class="form-label">protocol</label>
+            <input type="text" class="form-control" formControlName="protocol" id="protocol">
+          </div>
+          <div class="mb-3">
+            <label for="protocolAddress" class="form-label">protocolAddress</label>
+            <input type="text" class="form-control" formControlName="protocolAddress" id="protocolAddress">
+          </div>
+          <div class="mb-3">
+            <label for="description" class="form-label">description</label>
+            <input type="text" class="form-control" formControlName="description" id="description">
+          </div>
+          <div class="mb-3">
+            <label class="form-label">measurements</label>
+            <input type="text" class="form-control" formControlName="mName" placeholder="Name">
+<!--TODO: Make this a select-->
+            <input type="text" class="form-control" formControlName="type" placeholder="Type">
+            <input type="text" class="form-control" formControlName="maxValue" placeholder="Max Value">
+            <input type="text" class="form-control" formControlName="minValue" placeholder="Min Value">
+            <input type="text" class="form-control" formControlName="unit" placeholder="Unit">
+            <input type="text" class="form-control" formControlName="comment" placeholder="Comment">
+            <input type="text" class="form-control" formControlName="precision" placeholder="Precision">
+            <input type="text" class="form-control" formControlName="maxReadoutDifference" placeholder="Max diff b/w readout">
+            <input type="text" class="form-control" formControlName="maxFrozenTimeInSeconds" placeholder="Max time frozen for">
+          </div>
+        </form>
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="cancel-btn">Close</button>
+        <button type="button" class="btn btn-primary" *ngIf="showAddButton" (click)="onAddSensor()">Add</button>
+        <button type="button" class="btn btn-primary" *ngIf="showUpdateButton" (click)="onUpdateSensor()">Update</button>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/src/app/station/sensor/sensor.component.spec.ts b/src/app/station/sensor/sensor.component.spec.ts
new file mode 100644
index 0000000..578985b
--- /dev/null
+++ b/src/app/station/sensor/sensor.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SensorComponent } from './sensor.component';
+
+describe('SensorComponent', () => {
+  let component: SensorComponent;
+  let fixture: ComponentFixture<SensorComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ SensorComponent ]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(SensorComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/station/sensor/sensor.component.ts b/src/app/station/sensor/sensor.component.ts
new file mode 100644
index 0000000..9829ec9
--- /dev/null
+++ b/src/app/station/sensor/sensor.component.ts
@@ -0,0 +1,86 @@
+import {Component} from '@angular/core';
+import {ApiService} from "../../shared/api.service";
+import {FormBuilder, FormGroup} from "@angular/forms";
+import {SensorModel} from "./sensor.model";
+
+@Component({
+  selector: 'app-sensor',
+  templateUrl: './sensor.component.html',
+  styleUrls: ['./sensor.component.css']
+})
+export class SensorComponent {
+
+  sensorForm!: FormGroup;
+  sensorData!: any;
+
+  sensor: SensorModel = new SensorModel();
+  showAddButton!: boolean;
+  showUpdateButton!: boolean;
+
+  constructor(private api: ApiService, private formBuilder: FormBuilder) {
+    this.getAllSensors();
+    this.sensorForm = formBuilder.group({
+      name: [''],
+      protocol: [''],
+      protocolAddress: [''],
+      description: [''],
+      mName: [''],
+      type: [''],
+      maxValue: [''],
+      minValue: [''],
+      unit: [''],
+      comment: [''],
+      precision: [''],
+      maxReadoutDifference: [''],
+      maxFrozenTimeInSeconds: [''],
+    });
+  }
+
+  getAllSensors() {
+    this.api.get('http://localhost:8080/api/v1/sensor/all')
+      .subscribe(res => {
+        this.sensorData = res;
+      });
+  }
+
+  onAddSensor() {
+    this.sensor.name = this.sensorForm.value.name;
+    this.sensor.protocol = this.sensorForm.value.protocol;
+    this.sensor.protocolAddress = this.sensorForm.value.protocolAddress;
+    this.sensor.description = this.sensorForm.value.description;
+    this.sensor.measurements = [{
+      name: this.sensorForm.value.mName,
+      type: this.sensorForm.value.type,
+      maxValue: this.sensorForm.value.maxValue,
+      minValue: this.sensorForm.value.minValue,
+      unit: this.sensorForm.value.unit,
+      comment: this.sensorForm.value.comment,
+      precision: this.sensorForm.value.precision,
+      maxReadoutDifference: this.sensorForm.value.maxReadoutDifference,
+      maxFrozenTimeInSeconds: this.sensorForm.value.maxFrozenTimeInSeconds
+    }];
+
+    this.api.post('http://localhost:8080/api/v1/sensor/add', this.sensor)
+      .subscribe(res => {
+        alert('Sensor added successfully.');
+        this.getAllSensors();
+        this.sensorForm.reset();
+        document.getElementById('cancel-btn')?.click();
+      }, err => {
+        alert('An error has occurred.');
+      });
+  }
+
+  onUpdateSensor() {
+  }
+
+  onOpenAddSensor() {
+    this.showAddButton = true;
+    this.showUpdateButton = false;
+  }
+
+  onOpenEditSensor(row: any) {
+    this.showAddButton = false;
+    this.showUpdateButton = true;
+  }
+}
diff --git a/src/app/station/sensor/sensor.model.ts b/src/app/station/sensor/sensor.model.ts
new file mode 100644
index 0000000..1ba677b
--- /dev/null
+++ b/src/app/station/sensor/sensor.model.ts
@@ -0,0 +1,21 @@
+
+export interface Measurement {
+  name: string;
+  type: string;
+  unit: string;
+  maxValue: number;
+  minValue: number;
+  precision: number;
+  maxReadoutDifference: number;
+  maxFrozenTimeInSeconds: number;
+  comment: string;
+}
+export class SensorModel {
+
+  id: number=0;
+  name: string  = '';
+  description: string = '';
+  protocol: string  = '';
+  protocolAddress: string = '';
+  measurements: Measurement [] = [];
+}
diff --git a/src/app/station/station.component.css b/src/app/station/station.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/station/station.component.html b/src/app/station/station.component.html
new file mode 100644
index 0000000..a857e04
--- /dev/null
+++ b/src/app/station/station.component.html
@@ -0,0 +1,56 @@
+<!--<nav class="navbar navbar-dark bg-primary">-->
+<!--  <div class="container">-->
+<!--    <div class="navbar-header">-->
+<!--      <a class="navbar-brand" href="#"><h1 style="color: aliceblue">Forte</h1></a>-->
+<!--    </div>-->
+<!--    <div style="color: aliceblue" class="d-flex">-->
+<!--      <h3>Stations</h3>-->
+<!--    </div>-->
+<!--    <div class="d-flex">-->
+<!--      <button type="button" class="btn btn-info m-1">-->
+<!--        Stations-->
+<!--      </button>-->
+<!--      <button type="button" class="btn btn-success m-1" data-bs-toggle="modal" data-bs-target="#userModal">-->
+<!--        Add User-->
+<!--      </button>-->
+<!--      <button type="button" class="btn btn-danger m-1">-->
+<!--        Logout-->
+<!--      </button>-->
+<!--    </div>-->
+<!--  </div>-->
+<!--</nav>-->
+
+<div class="container">
+  <table class="table table-striped mt-3">
+    <thead>
+    <tr>
+      <th scope="col">Name</th>
+      <th scope="col">Diary</th>
+      <th scope="col">Created Date</th>
+      <th scope="col">Updated Date</th>
+      <th scope="col">Host</th>
+      <th scope="col">Clients</th>
+      <th scope="col">Location</th>
+      <th scope="col">
+        <div class="btn btn-primary">Add station</div>
+      </th>
+    </tr>
+    </thead>
+    <tbody>
+    <tr *ngFor="let row of stationsData">
+      <td>{{row.name}}</td>
+      <td>{{row.diary}}</td>
+      <td>{{Date(row.createdDate)}}</td>
+      <td>{{Date(row.updatedDate)}}</td>
+      <td>{{row.host}}</td>
+      <td>{{row.clients}}</td>
+      <td>{{row.location.latitude}}, {{row.location.longitude}}</td>
+      <td>
+        <div class="btn btn-info m-1">Edit
+        </div>
+        <div class="btn btn-danger">Delete</div>
+      </td>
+    </tr>
+    </tbody>
+  </table>
+</div>
diff --git a/src/app/station/station.component.spec.ts b/src/app/station/station.component.spec.ts
new file mode 100644
index 0000000..964719a
--- /dev/null
+++ b/src/app/station/station.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { StationComponent } from './station.component';
+
+describe('StationComponent', () => {
+  let component: StationComponent;
+  let fixture: ComponentFixture<StationComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ StationComponent ]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(StationComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/station/station.component.ts b/src/app/station/station.component.ts
new file mode 100644
index 0000000..1e4a465
--- /dev/null
+++ b/src/app/station/station.component.ts
@@ -0,0 +1,27 @@
+import {Component} from '@angular/core';
+import {ApiService} from "../shared/api.service";
+
+@Component({
+  selector: 'app-station',
+  templateUrl: './station.component.html',
+  styleUrls: ['./station.component.css']
+})
+export class StationComponent {
+
+  stationsData!: any;
+
+  constructor(private api: ApiService) {
+    this.getAllStations();
+  }
+
+  getAllStations() {
+    this.api.get('http://localhost:8080/api/v1/station/all')
+      .subscribe(res => {
+        this.stationsData = res;
+      });
+  }
+
+  Date(ISODate: any) {
+    return new Date(ISODate).toLocaleString();
+  }
+}
diff --git a/src/app/station/station.model.ts b/src/app/station/station.model.ts
new file mode 100644
index 0000000..2116ae9
--- /dev/null
+++ b/src/app/station/station.model.ts
@@ -0,0 +1,15 @@
+export class StationModel {
+  id: string = '';
+  name: string = '';
+  diary: string = '';
+  host: string = '';
+  clients: string[] = [];
+  status: string = '';
+  createdAt: string = '';
+  updatedAt: string = '';
+  location: object = {
+    latitude: 0,
+    longitude: 0,
+    altitude: 0
+  };
+}
-- 
GitLab