working on dashboard

* borrow-duration is fix
* to and from is fix
* map-load with marker is fix

missing: dashboard-chart-borrow-time
This commit is contained in:
Tim Herbst 2020-12-22 22:49:26 +01:00
parent 8dd31f3703
commit 678272ef8a
7 changed files with 308 additions and 76 deletions

View File

@ -18,23 +18,40 @@
</mat-toolbar> </mat-toolbar>
<mat-sidenav-container class="sidenav-container"> <mat-sidenav-container class="sidenav-container">
<mat-sidenav #sidenav class="sidenav" mode="side" opened role="region"> <mat-sidenav #sidenav class="sidenav" mode="side" opened role="region">
<mat-form-field appearance="fill" class="datepicker-start" fxLayout="column" fxLayoutAlign="center center"> <form [formGroup]="form">
<mat-label>Start der Zeitmessung</mat-label> <mat-form-field appearance="fill" class="datepicker" fxLayout="column" fxLayoutAlign="center center">
<input (dateChange)="addStartDate('input', $event)" <mat-label>Enter a range</mat-label>
[matDatepicker]="picker" [max]="maxEndDate" <mat-date-range-input [max]="maxEndDate" [min]="maxStartDate" [rangePicker]="picker" formGroupName="daterange">
[min]="maxStartDate" matInput> <input formControlName="start" matStartDate placeholder="Start date">
<mat-datepicker-toggle [for]="picker" matSuffix></mat-datepicker-toggle> <input formControlName="end" matEndDate placeholder="End date">
<mat-datepicker #picker></mat-datepicker> </mat-date-range-input>
</mat-form-field> <mat-datepicker-toggle [for]="picker" matSuffix></mat-datepicker-toggle>
<mat-form-field appearance="fill" class="datepicker-end" fxLayout="column" fxLayoutAlign="center center"> <mat-date-range-picker #picker></mat-date-range-picker>
<mat-label>Ende der Zeitmessung</mat-label> </mat-form-field>
<input (dateChange)="addEndDate('input', $event)" </form>
[matDatepicker]="picker1" [max]="maxEndDate" [min]="maxStartDate" matInput> <div class="submit-date" fxLayout="row" fxLayoutAlign="center">
<mat-datepicker-toggle [for]="picker1" matSuffix></mat-datepicker-toggle> <button (click)="onSubmit()" color="primary" mat-raised-button>
<mat-datepicker #picker1></mat-datepicker> <mat-icon>dashboard</mat-icon>
</mat-form-field> <span id="submit-date-span"> reload dashboard</span>
</button>
</div>
</mat-sidenav> </mat-sidenav>
<mat-sidenav-content> <mat-sidenav-content>
<div class="container containter-map"
fxLayout
fxLayout.xs="column"
fxLayoutAlign="center"
fxLayoutGap="10px"
fxLayoutGap.xs="0"
>
<div class="mini-map"
fxFlex
>
<div class="map-frame" fxFlex>
<div fxFill id="map"></div>
</div>
</div>
</div>
<div class="container container-top" <div class="container container-top"
fxLayout fxLayout
fxLayout.xs="column" fxLayout.xs="column"
@ -43,32 +60,76 @@
fxLayoutGap.xs="0" fxLayoutGap.xs="0"
> >
<div class="dashboard-table-to" <div class="dashboard-table-to"
fxFlex="35%" fxFlex="40%"
> >
<table [dataSource]="stationToSource" class="mat-elevation-z8" fxFill mat-table>
<ng-container matColumnDef="endStationName">
<th *matHeaderCellDef mat-header-cell> station of lend destination</th>
<td *matCellDef="let element" mat-cell> {{element.endStationName}} </td>
</ng-container>
<ng-container matColumnDef="number">
<th *matHeaderCellDef mat-header-cell> number of drives</th>
<td *matCellDef="let element" mat-cell> {{element.number}} </td>
</ng-container>
<ng-container matColumnDef="avgDuration">
<th *matHeaderCellDef mat-header-cell> average Lend duration</th>
<td *matCellDef="let element" mat-cell> {{element.avgDuration}} </td>
</ng-container>
<tr *matHeaderRowDef="displayedColumnsTo" mat-header-row></tr>
<tr *matRowDef="let row; columns: displayedColumnsTo;" mat-row></tr>
</table>
</div> </div>
<div class="dashboard-table-from" <div class="dashboard-table-from"
fxFlex="35%" fxFlex="40%"
> >
dashboard-table-from <table [dataSource]="stationFromSource" class="mat-elevation-z9" fxFill mat-table>
</div> <ng-container matColumnDef="startStationName">
<div class="mini-map" <th *matHeaderCellDef mat-header-cell> station of lend origin</th>
fxFlex <td *matCellDef="let element" mat-cell> {{element.startStationName}} </td>
> </ng-container>
mini-map
<ng-container matColumnDef="number">
<th *matHeaderCellDef mat-header-cell> number of drives</th>
<td *matCellDef="let element" mat-cell> {{element.number}} </td>
</ng-container>
<ng-container matColumnDef="avgDuration">
<th *matHeaderCellDef mat-header-cell> average lend duration</th>
<td *matCellDef="let element" mat-cell> {{element.avgDuration}} </td>
</ng-container>
<tr *matHeaderRowDef="displayedColumnsFrom" mat-header-row></tr>
<tr *matRowDef="let row; columns: displayedColumnsFrom;" mat-row></tr>
</table>
</div> </div>
</div> </div>
<div class="container container-middle" <div class="container container-middle"
fxLayout fxLayout
fxLayout.xs="column" fxLayout.xs="column"
fxLayoutAlign="center" fxLayoutAlign="center center"
fxLayoutGap="10px" fxLayoutGap="10px"
fxLayoutGap.xs="0" fxLayoutGap.xs="0"
> >
<div class="dashboard-chart-borrow-duration" <div class="dashboard-chart-borrow-duration"
fxFlex fxFlex="80%"
> >
dashboard-chart-borrow-duration <div id="Station-Dashboard-Borrow-Duration">
<apx-chart
[chart]="durationChartOptions.chart"
[dataLabels]="durationChartOptions.dataLabels"
[fill]="durationChartOptions.fill"
[legend]="durationChartOptions.legend"
[plotOptions]="durationChartOptions.plotOptions"
[series]="durationChartOptions.series"
[stroke]="durationChartOptions.stroke"
[xaxis]="durationChartOptions.xaxis"
[yaxis]="durationChartOptions.yaxis"
></apx-chart>
</div>
</div> </div>
</div> </div>
<div class="container container-bottom" <div class="container container-bottom"

View File

@ -16,26 +16,28 @@ mat-sidenav-container, mat-sidenav-content, mat-sidenav {
background: #5a34a0; background: #5a34a0;
} }
#submit-date-span {
padding: 10px;
}
.button-wiki:hover { .button-wiki:hover {
background: #5a34a0; background: #5a34a0;
} }
.datepicker-start { .datepicker {
margin: 5px; margin-top: 100px;
margin-top: 50px;
} }
.datepicker-end { .containter-map {
margin: 5px; height: 40vh;
margin-top: 20px;
} }
.container-top { .container-top {
height: 30vh; height: 20vh;
} }
.container-middle { .container-middle {
height: 30vh height: 40vh
} }
.container-bottom { .container-bottom {
@ -44,22 +46,18 @@ mat-sidenav-container, mat-sidenav-content, mat-sidenav {
.dashboard-table-to { .dashboard-table-to {
margin: 5px; margin: 5px;
background: gray;
} }
.dashboard-table-from { .dashboard-table-from {
margin: 5px; margin: 5px;
background: aquamarine;
} }
.mini-map { .mini-map {
margin: 5px; margin: 5px;
background: blueviolet;
} }
.dashboard-chart-borrow-duration { .dashboard-chart-borrow-duration {
margin: 5px; margin: 5px;
background: blue;
} }
.dashboard-chart-borrow-time { .dashboard-chart-borrow-time {

View File

@ -1,45 +1,218 @@
import {Component, OnInit} from '@angular/core'; import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import {DashboardService} from '../service/dashboard.service'; import {DashboardService} from '../service/dashboard.service';
import {IDashboardCommonBikePoint} from '../service/domain/dashboard-common-bike-point'; import {IDashboardCommonBikePoint} from '../service/domain/dashboard-common-bike-point';
import {MatDatepickerInputEvent} from '@angular/material/datepicker'; import {MatTableDataSource} from '@angular/material/table';
import {FormBuilder, FormControl, FormGroup} from '@angular/forms';
import {MapService} from '../service/map.service';
import {
ApexAxisChartSeries,
ApexChart,
ApexDataLabels,
ApexFill,
ApexLegend,
ApexNoData,
ApexPlotOptions,
ApexStroke,
ApexTooltip,
ApexXAxis,
ApexYAxis,
ChartComponent
} from 'ng-apexcharts';
export type ChartOptions = {
series: ApexAxisChartSeries;
chart: ApexChart;
dataLabels: ApexDataLabels;
plotOptions: ApexPlotOptions;
yaxis: ApexYAxis;
xaxis: ApexXAxis;
fill: ApexFill;
tooltip: ApexTooltip;
stroke: ApexStroke;
legend: ApexLegend;
noData: ApexNoData;
};
@Component({ @Component({
selector: 'app-dashboard', selector: 'app-dashboard',
templateUrl: './dashboard.component.html', templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'] styleUrls: ['./dashboard.component.scss'],
changeDetection: ChangeDetectionStrategy.Default
}) })
export class DashboardComponent implements OnInit { export class DashboardComponent implements OnInit {
@ViewChild('Station-Dashboard-Borrow-Duration') chart: ChartComponent;
public durationChartOptions: Partial<ChartOptions>;
displayedColumnsTo: string[] = ['endStationName', 'number', 'avgDuration'];
displayedColumnsFrom: string[] = ['startStationName', 'number', 'avgDuration'];
stationToSource = new MatTableDataSource<any>();
stationFromSource = new MatTableDataSource<any>();
station: IDashboardCommonBikePoint; station: IDashboardCommonBikePoint;
maxStartDate: Date; maxStartDate: Date;
maxEndDate: Date; maxEndDate: Date;
actualStartDate: Date; actualStartDate: Date;
actualEndDate: Date; actualEndDate: Date;
form: FormGroup;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
public service: DashboardService private service: DashboardService,
private map: MapService,
private changeDetectorRefs: ChangeDetectorRef,
private fb: FormBuilder
) { ) {
this.durationChartOptions = {
series: [],
chart: {
type: 'bar'
},
dataLabels: {
enabled: false
},
noData: {
text: 'Loading...'
}
};
} }
ngOnInit(): void { ngOnInit(): void {
this.service.initialDashboardStationFetch(this.route.snapshot.paramMap.get('id')).then(data => { this.service.fetch_dashboard_init(this.route.snapshot.paramMap.get('id')).then(data => {
this.station = data; this.station = data;
this.maxStartDate = new Date(data.maxStartDate); this.maxStartDate = new Date(data.maxStartDate);
this.maxEndDate = new Date(data.maxEndDate); this.maxEndDate = new Date(data.maxEndDate);
console.log(data); this.init_dashboard();
});
this.form = this.fb.group({
daterange: new FormGroup({
start: new FormControl(),
end: new FormControl()
})
}); });
} }
addStartDate(type: string, event: MatDatepickerInputEvent<Date>): void { init_dashboard(): void {
this.actualStartDate = event.value; const initDate = this.maxEndDate.toISOString().substring(0, 10);
console.log(this.actualStartDate); this.service.fetch_dashboard_station_to(this.station.id, initDate, initDate).then((source) => {
this.stationToSource = source;
this.changeDetectorRefs.detectChanges();
});
this.service.fetch_dashboard_station_from(this.station.id, initDate, initDate).then((source) => {
this.stationFromSource = source;
this.changeDetectorRefs.detectChanges();
});
this.service.fetch_dashboard_station_duration(this.station.id, initDate, initDate).then((source) => {
const numbers = [];
const minutesGroup = [];
source.forEach(value => {
numbers.push(value.number);
minutesGroup.push(value.minutesGroup);
});
this.durationChartOptions = {
series: [
{
name: 'borrow-duration',
data: [...numbers]
}
],
chart: {
type: 'bar',
height: 450
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: '55%',
endingShape: 'rounded'
}
},
dataLabels: {
enabled: false
},
stroke: {
show: true,
width: 2,
colors: ['transparent']
},
xaxis: {
categories: [...minutesGroup]
},
yaxis: {
title: {
text: 'minutes'
}
},
fill: {
opacity: 1
}
};
});
this.map.init_map(this.station.lat, this.station.lon, 17);
this.map.draw_dashboard_station_marker(this.station.lat, this.station.lon);
} }
addEndDate(type: string, event: MatDatepickerInputEvent<Date>): void { onSubmit(): void {
this.actualEndDate = event.value; this.actualStartDate = this.form.get('daterange').value.start;
console.log(this.actualEndDate); this.actualEndDate = this.form.get('daterange').value.end;
} this.service.fetch_dashboard_station_to(this.station.id, this.actualStartDate.toISOString().substring(0, 10), this.actualEndDate.toISOString().substring(0, 10)).then((source) => {
this.stationToSource = source;
this.changeDetectorRefs.detectChanges();
});
this.service.fetch_dashboard_station_from(this.station.id, this.actualStartDate.toISOString().substring(0, 10), this.actualStartDate.toISOString().substring(0, 10)).then((source) => {
this.stationFromSource = source;
this.changeDetectorRefs.detectChanges();
});
this.service.fetch_dashboard_station_duration(this.station.id, this.actualStartDate.toISOString().substring(0, 10), this.actualStartDate.toISOString().substring(0, 10)).then((source) => {
const numbers = [];
const minutesGroup = [];
source.forEach(value => {
numbers.push(value.number);
minutesGroup.push(value.minutesGroup);
});
this.durationChartOptions = {
series: [
{
name: 'borrow-duration',
data: [...numbers]
}
],
chart: {
type: 'bar',
height: 450
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: '55%',
endingShape: 'rounded'
}
},
dataLabels: {
enabled: false
},
stroke: {
show: true,
width: 2,
colors: ['transparent']
},
xaxis: {
categories: [...minutesGroup]
},
yaxis: {
title: {
text: 'minutes'
}
},
fill: {
opacity: 1
}
};
});
}
} }

View File

@ -13,8 +13,8 @@ export class MapComponent implements AfterViewInit {
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
this.service.initMap(); this.service.init_map(51.509865, -0.118092, 14);
this.service.makeStationMarkers(); this.service.make_station_markers();
} }

View File

@ -10,7 +10,19 @@ export class DashboardService {
constructor(private client: HttpClient) { constructor(private client: HttpClient) {
} }
public async initialDashboardStationFetch(id: string): Promise<any> { public async fetch_dashboard_init(id: string): Promise<any> {
return await this.client.get(environment.apiUrl + 'latest/dashboard/' + id + '/').toPromise(); return await this.client.get(environment.apiUrl + `latest/dashboard/${id}/`).toPromise();
}
public async fetch_dashboard_station_to(id: string, startDate: string, endDate: string): Promise<any> {
return await this.client.get(environment.apiUrl + `latest/dashboard/${id}/to?start_date=${startDate}&end_date=${endDate}`).toPromise();
}
public async fetch_dashboard_station_from(id: string, startDate: string, endDate: string): Promise<any> {
return await this.client.get(environment.apiUrl + `latest/dashboard/${id}/from?start_date=${startDate}&end_date=${endDate}`).toPromise();
}
public async fetch_dashboard_station_duration(id: string, startDate: string, endDate: string): Promise<any> {
return await this.client.get(environment.apiUrl + `latest/dashboard/${id}/duration?start_date=${startDate}&end_date=${endDate}`).toPromise();
} }
} }

View File

@ -1,16 +0,0 @@
export interface IDashboardStationTo {
startStationName?: string;
endStationName?: string;
stationNumber?: number;
avgDuration?: number;
}
export class DashboardStationTo implements IDashboardStationTo {
constructor(
public startStationName?: string,
public endStationName?: string,
public stationNumber?: number,
public avgDuration?: number
) {
}
}

View File

@ -25,8 +25,8 @@ export class MapService {
) { ) {
} }
public initMap(): void { public init_map(lat: number, lon: number, zoom: number): void {
this.map = L.map('map').setView([51.509865, -0.118092], 14); this.map = L.map('map').setView([lat, lon], zoom);
this.map.addLayer(new L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { this.map.addLayer(new L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data <a href="https://openstreetmap.org">OpenStreetMap</a> contributors', attribution: 'Map data <a href="https://openstreetmap.org">OpenStreetMap</a> contributors',
minZoom: 0, minZoom: 0,
@ -34,8 +34,8 @@ export class MapService {
})); }));
} }
public makeStationMarkers(): void { public make_station_markers(): void {
this.fetchStationGeoData().then((data) => { this.fetch_station_geo_data().then((data) => {
const markerClusters = L.markerClusterGroup({ const markerClusters = L.markerClusterGroup({
spiderflyOnMaxZoom: true, spiderflyOnMaxZoom: true,
showCoverageOnHover: true, showCoverageOnHover: true,
@ -56,8 +56,12 @@ export class MapService {
}); });
} }
public draw_dashboard_station_marker(lat: number, lon: number): void {
L.marker([lat, lon], {icon: createIcon}).addTo(this.map);
}
private async fetchStationGeoData(): Promise<any> {
private async fetch_station_geo_data(): Promise<any> {
return await this.client.get(environment.apiUrl + 'latest/bikepoints/').toPromise(); return await this.client.get(environment.apiUrl + 'latest/bikepoints/').toPromise();
} }
} }