Livebeat is now able to send, store and show beats
This commit is contained in:
16
frontend/src/app/api.service.spec.ts
Normal file
16
frontend/src/app/api.service.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { APIService } from './api.service';
|
||||
|
||||
describe('APIService', () => {
|
||||
let service: APIService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(APIService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
62
frontend/src/app/api.service.ts
Normal file
62
frontend/src/app/api.service.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
export interface ILogin {
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface IBeat {
|
||||
coordinate?: number[];
|
||||
accuracy: number;
|
||||
speed: number;
|
||||
battery?: number;
|
||||
phone: any;
|
||||
createdAt?: Date;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class APIService {
|
||||
|
||||
private token: string;
|
||||
|
||||
username: string;
|
||||
|
||||
API_ENDPOINT = 'http://192.168.178.26:8040'
|
||||
|
||||
constructor(private httpClient: HttpClient) { }
|
||||
|
||||
async login(username: string, password: string): Promise<ILogin> {
|
||||
return new Promise<ILogin>((resolve, reject) => {
|
||||
console.log('POST');
|
||||
|
||||
this.httpClient.post(this.API_ENDPOINT + '/user/login', { username, password }, { responseType: 'json' })
|
||||
.subscribe(token => {
|
||||
console.log(token);
|
||||
|
||||
this.token = (token as ILogin).token;
|
||||
this.username = username;
|
||||
resolve(token as ILogin);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async getBeats(): Promise<IBeat[]> {
|
||||
return new Promise<IBeat[]>((resolve, reject) => {
|
||||
if (this.token === undefined) { reject([]); }
|
||||
|
||||
const headers = new HttpHeaders({ token: this.token });
|
||||
|
||||
this.httpClient.get(this.API_ENDPOINT + '/beat', { responseType: 'json', headers })
|
||||
.subscribe(beats => {
|
||||
console.log(beats);
|
||||
resolve(beats as IBeat[]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
hasSession(): boolean {
|
||||
return this.token !== undefined;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,22 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { NbAuthComponent, NbLoginComponent } from '@nebular/auth';
|
||||
import { AppComponent } from './app.component';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: NbAuthComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: NbLoginComponent
|
||||
}
|
||||
]
|
||||
component: AppComponent
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
component: LoginComponent
|
||||
},
|
||||
{
|
||||
path: 'dashboard',
|
||||
component: DashboardComponent
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
<nb-auth-block>
|
||||
<nb-login></nb-login>
|
||||
</nb-auth-block>
|
||||
<div id="header">
|
||||
<p>Header</p>
|
||||
<div class="left">
|
||||
<span class="ident" *ngIf="this.api.hasSession()">Logged in as {{this.api.username}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<!-- Display start page -->
|
||||
<div id="startpage" *ngIf="false">
|
||||
<h1>Livebeat</h1>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
#header {
|
||||
width: 100vw;
|
||||
height: fit-content;
|
||||
padding-top: 0.4rem;
|
||||
padding-bottom: 0.4rem;
|
||||
background-color: #1d1d1dd9;
|
||||
backdrop-filter: blur(20px);
|
||||
box-shadow: 10px 10px 50px 0px rgba(0,0,0,0.85);
|
||||
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { APIService } from './api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -7,4 +9,7 @@ import { Component } from '@angular/core';
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'Livebeat';
|
||||
|
||||
constructor(public api: APIService, private router: Router) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,26 @@ import { NbCardModule, NbLayoutModule, NbSidebarModule, NbThemeModule } from '@n
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { NgxMapboxGLModule } from 'ngx-mapbox-gl';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
AppComponent,
|
||||
LoginComponent,
|
||||
DashboardComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
NgxMapboxGLModule.withConfig({
|
||||
accessToken: 'pk.eyJ1IjoibW9uZGVpMSIsImEiOiJja2dsY2ZtaG0xZ2o5MnR0ZWs0Mm82OTBpIn0.NzDWN3P6jJLmci_v3MM1tA'
|
||||
}),
|
||||
NbThemeModule.forRoot({ name: 'dark' }),
|
||||
NbLayoutModule,
|
||||
NbEvaIconsModule,
|
||||
|
||||
13
frontend/src/app/dashboard/dashboard.component.html
Normal file
13
frontend/src/app/dashboard/dashboard.component.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<mgl-map [style]="'mapbox://styles/mapbox/outdoors-v11'">
|
||||
<mgl-geojson-source id="locHistory" [data]="data"></mgl-geojson-source>
|
||||
<mgl-layer
|
||||
id="locHisotryLines"
|
||||
type="line"
|
||||
source="locHistory"
|
||||
[paint]="{
|
||||
'line-color': '#ff0000',
|
||||
'line-width': 4
|
||||
}"
|
||||
>
|
||||
</mgl-layer>
|
||||
</mgl-map>
|
||||
8
frontend/src/app/dashboard/dashboard.component.scss
Normal file
8
frontend/src/app/dashboard/dashboard.component.scss
Normal file
@@ -0,0 +1,8 @@
|
||||
mgl-map {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
25
frontend/src/app/dashboard/dashboard.component.spec.ts
Normal file
25
frontend/src/app/dashboard/dashboard.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DashboardComponent } from './dashboard.component';
|
||||
|
||||
describe('DashboardComponent', () => {
|
||||
let component: DashboardComponent;
|
||||
let fixture: ComponentFixture<DashboardComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ DashboardComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DashboardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
32
frontend/src/app/dashboard/dashboard.component.ts
Normal file
32
frontend/src/app/dashboard/dashboard.component.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { AfterViewInit, Component, OnInit } from '@angular/core';
|
||||
import { Map } from 'mapbox-gl';
|
||||
import { APIService } from '../api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrls: ['./dashboard.component.scss']
|
||||
})
|
||||
export class DashboardComponent implements AfterViewInit {
|
||||
map: Map;
|
||||
|
||||
data: GeoJSON.FeatureCollection<GeoJSON.LineString> = {
|
||||
type: 'FeatureCollection', features: [
|
||||
{
|
||||
type: 'Feature',
|
||||
properties: null,
|
||||
geometry: { type: 'LineString', coordinates: [] }
|
||||
}]
|
||||
};
|
||||
|
||||
constructor(private api: APIService) { }
|
||||
|
||||
async ngAfterViewInit(): Promise<void> {
|
||||
const beats = await this.api.getBeats();
|
||||
beats.forEach((beat) => {
|
||||
this.data.features[0].geometry.coordinates.push([beat.coordinate[1], beat.coordinate[0]]);
|
||||
});
|
||||
console.log("Now:", this.data.features);
|
||||
}
|
||||
|
||||
}
|
||||
16
frontend/src/app/login/login.component.html
Normal file
16
frontend/src/app/login/login.component.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<div id="login">
|
||||
<h2>Login</h2>
|
||||
<p>Please authenticate yourself to proceed.</p>
|
||||
|
||||
<form>
|
||||
<div id="username">
|
||||
<label>Username</label><br>
|
||||
<input type="text" name="username" placeholder="Username" [(ngModel)]="username">
|
||||
</div>
|
||||
<div id="password">
|
||||
<label>Password</label><br>
|
||||
<input type="password" name="password" placeholder="Password" [(ngModel)]="password">
|
||||
</div>
|
||||
<button (click)="perfomLogin()">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
13
frontend/src/app/login/login.component.scss
Normal file
13
frontend/src/app/login/login.component.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
#login {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
height: fit-content;
|
||||
width: fit-content;
|
||||
padding: 5rem;
|
||||
border-radius: 20px;
|
||||
background: #1d1d1d;
|
||||
box-shadow: 20px 20px 60px #191919,
|
||||
-20px -20px 60px #212121;
|
||||
|
||||
}
|
||||
25
frontend/src/app/login/login.component.spec.ts
Normal file
25
frontend/src/app/login/login.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginComponent } from './login.component';
|
||||
|
||||
describe('LoginComponent', () => {
|
||||
let component: LoginComponent;
|
||||
let fixture: ComponentFixture<LoginComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ LoginComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoginComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
31
frontend/src/app/login/login.component.ts
Normal file
31
frontend/src/app/login/login.component.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { APIService } from '../api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.scss']
|
||||
})
|
||||
export class LoginComponent implements OnInit {
|
||||
|
||||
username = '';
|
||||
password = '';
|
||||
|
||||
constructor(private api: APIService, private router: Router) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
async perfomLogin(): Promise<any> {
|
||||
console.log('Clicked!');
|
||||
|
||||
if ((await this.api.login(this.username, this.password)).token !== undefined) {
|
||||
console.log('Login was successful!');
|
||||
this.router.navigate(['dashboard']);
|
||||
} else {
|
||||
console.log('Login was not successful!');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -61,3 +61,4 @@ import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
||||
(window as any).global = window;
|
||||
|
||||
@@ -2,9 +2,18 @@
|
||||
|
||||
@import '~@nebular/auth/styles/globals';
|
||||
@import '~@nebular/theme/styles/globals';
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;700;900&display=swap');
|
||||
|
||||
@include nb-install() {
|
||||
@include nb-theme-global();
|
||||
@include nb-auth-global();
|
||||
@include nb-theme-global();
|
||||
};
|
||||
|
||||
body {
|
||||
background-color: #1d1d1d;
|
||||
color: #fff;
|
||||
font-family: 'Inter', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
|
||||
Reference in New Issue
Block a user