refactor user-input to component

This commit is contained in:
tim-herbst 2021-01-02 16:08:01 +01:00
parent cdea238830
commit e6f5407319
7 changed files with 346 additions and 216 deletions

View File

@ -32,6 +32,7 @@ import {MatProgressSpinnerModule} from "@angular/material/progress-spinner";
import { TableComponent } from './dashboard/table/table.component'; import { TableComponent } from './dashboard/table/table.component';
import { RentDurationChartComponent } from './dashboard/rent-duration-chart/rent-duration-chart.component'; import { RentDurationChartComponent } from './dashboard/rent-duration-chart/rent-duration-chart.component';
import { RentTimeChartComponent } from './dashboard/rent-time-chart/rent-time-chart.component'; import { RentTimeChartComponent } from './dashboard/rent-time-chart/rent-time-chart.component';
import { UserInputComponent } from './dashboard/user-input/user-input.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -42,7 +43,8 @@ import { RentTimeChartComponent } from './dashboard/rent-time-chart/rent-time-ch
AutoRefreshComponent, AutoRefreshComponent,
TableComponent, TableComponent,
RentDurationChartComponent, RentDurationChartComponent,
RentTimeChartComponent RentTimeChartComponent,
UserInputComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View File

@ -19,54 +19,10 @@
<div class="container-map" fxFlex="100%" fxLayout="row" fxLayoutAlign="center"> <div class="container-map" fxFlex="100%" fxLayout="row" fxLayoutAlign="center">
<div class="input-container" fxFlex="35%" fxLayout="column"> <div class="input-container" fxFlex="35%" fxLayout="column">
<mat-card fxFlex fxLayout="column"> <app-user-input
<mat-card-header fxFlex="15%"> fxFlex
<div class="example-header-image" mat-card-avatar></div> (startEndDate)="onSubmit($event)"
<mat-card-title style="margin-top: 1em; margin-left: 0">{{bikePoint?.commonName}}</mat-card-title> ></app-user-input>
</mat-card-header>
<mat-card-content fxFlex="70%" fxLayout="column" fxLayoutAlign="space-between">
<div class="user-input" fxFlex fxLayout="row" fxLayoutAlign="start center">
<form [formGroup]="form" style="margin-left: 2em">
<p>Select a range to analyze data</p>
<mat-form-field appearance="fill" class="datepicker">
<mat-label>Enter a range</mat-label>
<mat-date-range-input [max]="maxEndDate" [min]="maxStartDate" [rangePicker]="picker"
formGroupName="daterange">
<input formControlName="start" matStartDate placeholder="Start date">
<input formControlName="end" matEndDate placeholder="End date">
</mat-date-range-input>
<mat-datepicker-toggle [for]="picker" matSuffix></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker>
</mat-form-field>
</form>
<div class="submit-date">
<button (click)="onSubmit()" color="primary" mat-raised-button>
<mat-icon>dashboard</mat-icon>
<span id="submit-date-span"> reload</span>
</button>
</div>
</div>
<div class="chart-bikepoint-availability" fxFlex fxLayout="column">
<div id="chart">
<apx-chart
[chart]="bikePointChartOptions.chart"
[colors]="bikePointChartOptions.colors"
[dataLabels]="bikePointChartOptions.dataLabels"
[fill]="bikePointChartOptions.fill"
[legend]="bikePointChartOptions.legend"
[plotOptions]="bikePointChartOptions.plotOptions"
[series]="bikePointChartOptions.series"
[stroke]="bikePointChartOptions.stroke"
[subtitle]="bikePointChartOptions.subtitle"
[title]="bikePointChartOptions.title"
[tooltip]="bikePointChartOptions.tooltip"
[xaxis]="bikePointChartOptions.xaxis"
[yaxis]="bikePointChartOptions.yaxis"
></apx-chart>
</div>
</div>
</mat-card-content>
</mat-card>
</div> </div>
<mat-card class="mat-card-map" fxFlex> <mat-card class="mat-card-map" fxFlex>
<div class="mini-map" fxFlex> <div class="mini-map" fxFlex>

View File

@ -1,8 +1,7 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Injectable, OnInit, ViewChild} from '@angular/core'; import {ChangeDetectionStrategy, Component, Injectable, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} 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 {FormBuilder, FormControl, FormGroup} from '@angular/forms';
import {MapService} from '../service/map.service'; import {MapService} from '../service/map.service';
@ -20,12 +19,12 @@ import {
ApexXAxis, ApexXAxis,
ApexYAxis ApexYAxis
} from 'ng-apexcharts'; } from 'ng-apexcharts';
import {IMapBikePoint} from '../service/domain/map-bike-point';
import {DateAdapter, MAT_DATE_FORMATS, NativeDateAdapter} from '@angular/material/core'; import {DateAdapter, MAT_DATE_FORMATS, NativeDateAdapter} from '@angular/material/core';
import {formatDate} from '@angular/common'; import {formatDate} from '@angular/common';
import {TableComponent} from './table/table.component'; import {TableComponent} from './table/table.component';
import {RentDurationChartComponent} from "./rent-duration-chart/rent-duration-chart.component"; import {RentDurationChartComponent} from './rent-duration-chart/rent-duration-chart.component';
import {RentTimeChartComponent} from "./rent-time-chart/rent-time-chart.component"; import {RentTimeChartComponent} from './rent-time-chart/rent-time-chart.component';
import {StartEndDate} from './user-input/user-input.component';
export type ChartOptions = { export type ChartOptions = {
title: ApexTitleSubtitle; title: ApexTitleSubtitle;
@ -44,27 +43,6 @@ export type ChartOptions = {
noData: ApexNoData; noData: ApexNoData;
}; };
export const PICK_FORMATS = {
parse: {dateInput: {month: 'short', year: 'numeric', day: 'numeric'}},
display: {
dateInput: 'input',
monthYearLabel: {year: 'numeric', month: 'numeric'},
dateA11yLabel: {year: 'numeric', month: 'numeric', day: 'numeric'},
monthYearA11yLabel: {year: 'numeric', month: 'long'}
}
};
@Injectable()
class PickDateAdapter extends NativeDateAdapter {
format(date: Date, displayFormat: Object): string {
if (displayFormat === 'input') {
return formatDate(date, 'dd-MM-yyyy', this.locale);
} else {
return date.toDateString();
}
}
}
const chartHeight = 460; const chartHeight = 460;
@Component({ @Component({
@ -72,65 +50,27 @@ const chartHeight = 460;
templateUrl: './dashboard.component.html', templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'], styleUrls: ['./dashboard.component.scss'],
changeDetection: ChangeDetectionStrategy.Default, changeDetection: ChangeDetectionStrategy.Default,
providers: [
{provide: DateAdapter, useClass: PickDateAdapter},
{provide: MAT_DATE_FORMATS, useValue: PICK_FORMATS}
]
}) })
export class DashboardComponent implements OnInit { export class DashboardComponent implements OnInit {
@ViewChild(TableComponent) table: TableComponent; @ViewChild(TableComponent) table: TableComponent;
@ViewChild(RentDurationChartComponent) durationChart: RentDurationChartComponent; @ViewChild(RentDurationChartComponent) durationChart: RentDurationChartComponent;
@ViewChild(RentTimeChartComponent) timeChart: RentTimeChartComponent; @ViewChild(RentTimeChartComponent) timeChart: RentTimeChartComponent;
public timeChartOptions: Partial<ChartOptions>;
public bikePointChartOptions: Partial<ChartOptions>; public bikePointChartOptions: Partial<ChartOptions>;
station: IDashboardCommonBikePoint; station: IDashboardCommonBikePoint;
maxStartDate: Date; maxStartDate: Date;
maxEndDate: Date; maxEndDate: Date;
actualStartDate: Date;
actualEndDate: Date;
form: FormGroup;
bikePoint: IMapBikePoint;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private service: DashboardService, private service: DashboardService,
private map: MapService, private map: MapService,
private changeDetectorRefs: ChangeDetectorRef,
private fb: FormBuilder
) { ) {
this.timeChartOptions = {
series: [],
chart: {
type: 'line'
},
noData: {
text: 'Loading...'
}
};
this.bikePointChartOptions = {
series: [],
chart: {
type: 'bar'
},
noData: {
text: 'Loading...'
}
};
} }
ngOnInit(): void { ngOnInit(): void {
this.form = this.fb.group({
daterange: new FormGroup({
start: new FormControl(),
end: new FormControl()
})
});
this.changeDetectorRefs.detectChanges();
this.map.removeTableStationMarkerOnReload();
this.route.params.subscribe(params => { this.route.params.subscribe(params => {
this.service.fetchDashboardInit(params.id).then(data => { this.service.fetchDashboardInit(params.id).then(data => {
this.station = data; this.station = data;
@ -138,119 +78,26 @@ export class DashboardComponent implements OnInit {
this.maxEndDate = new Date(data.maxEndDate); this.maxEndDate = new Date(data.maxEndDate);
this.initDashboard(); this.initDashboard();
}); });
this.service.fetchBikePointForStatus(params.id).then(data => {
this.bikePoint = data;
const NbBlockedDocks = data.status.NbDocks - data.status.NbBikes - data.status.NbEmptyDocks;
this.bikePointChartOptions = {
subtitle: {
text: 'This chart visualizes the availability of the bikes',
offsetX: 20,
offsetY: 15,
style: {
fontSize: '15px'
}
},
series: [
{
name: 'Bikes',
data: [data.status.NbBikes]
},
{
name: 'Empty docks',
data: [data.status.NbEmptyDocks]
},
{
name: 'Blocked docks',
data: [NbBlockedDocks]
}
],
colors: ['#51ca49', '#8f8e8e', '#f00'],
chart: {
type: 'bar',
height: 180,
stacked: true,
toolbar: {
show: false
}
},
plotOptions: {
bar: {
horizontal: true,
dataLabels: {
position: 'center'
}
}
},
dataLabels: {
enabled: true,
style: {
fontSize: '20px',
colors: ['#fff']
}
},
stroke: {
show: false
},
xaxis: {
labels: {
show: false
},
axisBorder: {
show: false
},
axisTicks: {
show: false
}
},
yaxis: {
show: false,
title: {
text: undefined
},
axisBorder: {
show: false
},
min: 0,
max: data.status.NbDocks
},
tooltip: {
enabled: false,
},
fill: {
opacity: 1
},
legend: {
position: 'bottom',
horizontalAlign: 'right',
fontSize: '14px'
}
};
});
}); });
} }
async initDashboard(): Promise<any> { async initDashboard(): Promise<any> {
const initDate = this.maxEndDate.toISOString().substring(0, 10);
this.form.get('daterange').get('start').setValue(initDate);
this.form.get('daterange').get('end').setValue(initDate);
this.map.initDashboardMap(this.station.lat, this.station.lon, 17); this.map.initDashboardMap(this.station.lat, this.station.lon, 17);
this.map.drawDashboardStationMarker(this.station); this.map.drawDashboardStationMarker(this.station);
} }
async onSubmit(): Promise<any> { async onSubmit(startEndDate: StartEndDate): Promise<any> {
this.actualStartDate = this.form.get('daterange').value.start; await this.table.onSubmit(
this.actualEndDate = this.form.get('daterange').value.end; startEndDate.actualStartDate.toISOString().substring(0, 10),
this.table.onSubmit( startEndDate.actualEndDate.toISOString().substring(0, 10)
this.actualStartDate.toISOString().substring(0, 10),
this.actualEndDate.toISOString().substring(0, 10)
); );
this.durationChart.onSubmit( await this.durationChart.onSubmit(
this.actualStartDate.toISOString().substring(0, 10), startEndDate.actualStartDate.toISOString().substring(0, 10),
this.actualEndDate.toISOString().substring(0, 10) startEndDate.actualEndDate.toISOString().substring(0, 10)
); );
this.timeChart.onSubmit( await this.timeChart.onSubmit(
this.actualStartDate.toISOString().substring(0, 10), startEndDate.actualStartDate.toISOString().substring(0, 10),
this.actualEndDate.toISOString().substring(0, 10) startEndDate.actualEndDate.toISOString().substring(0, 10)
); );
} }

View File

@ -0,0 +1,48 @@
<mat-card fxFlex fxLayout="column">
<mat-card-header fxFlex="15%">
<div class="header-image" mat-card-avatar></div>
<mat-card-title style="margin-top: 1em; margin-left: 0">{{bikePoint?.commonName}}</mat-card-title>
</mat-card-header>
<mat-card-content fxFlex="70%" fxLayout="column" fxLayoutAlign="space-between">
<div class="user-input" fxFlex fxLayout="row" fxLayoutAlign="start center">
<form [formGroup]="form" style="margin-left: 2em">
<p>Select a range to analyze data</p>
<mat-form-field appearance="fill" class="datepicker">
<mat-label>Enter a range</mat-label>
<mat-date-range-input [max]="maxEndDate" [min]="maxStartDate" [rangePicker]="picker"
formGroupName="dateRange">
<input formControlName="start" matStartDate placeholder="Start date">
<input formControlName="end" matEndDate placeholder="End date">
</mat-date-range-input>
<mat-datepicker-toggle [for]="picker" matSuffix></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker>
</mat-form-field>
</form>
<div class="submit-date">
<button (click)="onSubmit()" color="primary" mat-raised-button>
<mat-icon>dashboard</mat-icon>
<span id="submit-date-span"> reload</span>
</button>
</div>
</div>
<div class="chart-bikepoint-availability" fxFlex fxLayout="column">
<div id="chart">
<apx-chart
[chart]="chartOptions.chart"
[colors]="chartOptions.colors"
[dataLabels]="chartOptions.dataLabels"
[fill]="chartOptions.fill"
[legend]="chartOptions.legend"
[plotOptions]="chartOptions.plotOptions"
[series]="chartOptions.series"
[stroke]="chartOptions.stroke"
[subtitle]="chartOptions.subtitle"
[title]="chartOptions.title"
[tooltip]="chartOptions.tooltip"
[xaxis]="chartOptions.xaxis"
[yaxis]="chartOptions.yaxis"
></apx-chart>
</div>
</div>
</mat-card-content>
</mat-card>

View File

@ -0,0 +1,30 @@
.header-image {
margin-top: 1em;
margin-left: 1em;
margin-bottom: 1em;
background-image: url('../../../assets/bike-point-blue.png');
background-size: cover;
}
.mat-card {
padding: 1px 1px 1px;
margin: 10px;
}
.mat-card-title {
margin-top: 1em;
margin-left: 2em;
}
.mat-card-subtitle {
margin-left: 39px;
}
#chart {
margin-right: 16px;
}
.submit-date {
margin-top: 1em;
margin-left: 4em;
}

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserInputComponent } from './user-input.component';
describe('UserInputComponent', () => {
let component: UserInputComponent;
let fixture: ComponentFixture<UserInputComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ UserInputComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(UserInputComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,222 @@
import {Component, EventEmitter, Injectable, OnInit, Output} from '@angular/core';
import {IDashboardCommonBikePoint} from '../../service/domain/dashboard-common-bike-point';
import {FormBuilder, FormControl, FormGroup} from '@angular/forms';
import {IMapBikePoint} from '../../service/domain/map-bike-point';
import {ActivatedRoute} from '@angular/router';
import {DashboardService} from '../../service/dashboard.service';
import {
ApexAxisChartSeries,
ApexChart,
ApexDataLabels,
ApexFill,
ApexLegend,
ApexNoData,
ApexPlotOptions,
ApexStroke,
ApexTitleSubtitle,
ApexTooltip,
ApexXAxis,
ApexYAxis
} from 'ng-apexcharts';
import {DateAdapter, MAT_DATE_FORMATS, NativeDateAdapter} from '@angular/material/core';
import {formatDate} from '@angular/common';
export type ChartOptions = {
title: ApexTitleSubtitle;
subtitle: ApexTitleSubtitle;
series: ApexAxisChartSeries;
chart: ApexChart;
colors: string[];
dataLabels: ApexDataLabels;
plotOptions: ApexPlotOptions;
yaxis: ApexYAxis | ApexYAxis[];
xaxis: ApexXAxis;
fill: ApexFill;
tooltip: ApexTooltip;
stroke: ApexStroke;
legend: ApexLegend;
noData: ApexNoData;
};
export const PICK_FORMATS = {
parse: {dateInput: {month: 'short', year: 'numeric', day: 'numeric'}},
display: {
dateInput: 'input',
monthYearLabel: {year: 'numeric', month: 'numeric'},
dateA11yLabel: {year: 'numeric', month: 'numeric', day: 'numeric'},
monthYearA11yLabel: {year: 'numeric', month: 'long'}
}
};
@Injectable()
class PickDateAdapter extends NativeDateAdapter {
format(date: Date, displayFormat: Object): string {
if (displayFormat === 'input') {
return formatDate(date, 'dd-MM-yyyy', this.locale);
} else {
return date.toDateString();
}
}
}
export interface StartEndDate {
actualStartDate: Date;
actualEndDate: Date;
}
@Component({
selector: 'app-user-input',
templateUrl: './user-input.component.html',
styleUrls: ['./user-input.component.scss'],
providers: [
{provide: DateAdapter, useClass: PickDateAdapter},
{provide: MAT_DATE_FORMATS, useValue: PICK_FORMATS}
]
})
export class UserInputComponent implements OnInit {
@Output() startEndDate: EventEmitter<StartEndDate> = new EventEmitter<StartEndDate>();
chartOptions: Partial<ChartOptions>;
station: IDashboardCommonBikePoint;
maxStartDate: Date;
maxEndDate: Date;
form: FormGroup;
bikePoint: IMapBikePoint;
constructor(
private route: ActivatedRoute,
private service: DashboardService,
private fb: FormBuilder
) {
this.chartOptions = {
series: [],
chart: {
type: 'bar'
},
noData: {
text: 'Loading...'
}
};
}
ngOnInit(): void {
this.form = this.fb.group({
dateRange: new FormGroup({
start: new FormControl(),
end: new FormControl()
})
});
this.route.params.subscribe(params => {
this.service.fetchDashboardInit(params.id).then(data => {
this.station = data;
this.maxStartDate = new Date(data.maxStartDate);
this.maxEndDate = new Date(data.maxEndDate);
this.initInput().catch(error => console.log(error));
});
this.service.fetchBikePointForStatus(params.id).then(data => {
this.bikePoint = data;
const NbBlockedDocks = data.status.NbDocks - data.status.NbBikes - data.status.NbEmptyDocks;
this.chartOptions = {
subtitle: {
text: 'This chart visualizes the availability of the bikes',
offsetX: 20,
offsetY: 15,
style: {
fontSize: '15px'
}
},
series: [
{
name: 'Bikes',
data: [data.status.NbBikes]
},
{
name: 'Empty docks',
data: [data.status.NbEmptyDocks]
},
{
name: 'Blocked docks',
data: [NbBlockedDocks]
}
],
colors: ['#51ca49', '#8f8e8e', '#f00'],
chart: {
type: 'bar',
height: 180,
stacked: true,
toolbar: {
show: false
}
},
plotOptions: {
bar: {
horizontal: true,
dataLabels: {
position: 'center'
}
}
},
dataLabels: {
enabled: true,
style: {
fontSize: '20px',
colors: ['#fff']
}
},
stroke: {
show: false
},
xaxis: {
labels: {
show: false
},
axisBorder: {
show: false
},
axisTicks: {
show: false
}
},
yaxis: {
show: false,
title: {
text: undefined
},
axisBorder: {
show: false
},
min: 0,
max: data.status.NbDocks
},
tooltip: {
enabled: false,
},
fill: {
opacity: 1
},
legend: {
position: 'bottom',
horizontalAlign: 'right',
fontSize: '14px'
}
};
});
});
}
async initInput(): Promise<void> {
const initDate = this.maxEndDate.toISOString().substring(0, 10);
this.form.get('dateRange').get('start').setValue(initDate);
this.form.get('dateRange').get('end').setValue(initDate);
}
async onSubmit(): Promise<any> {
this.startEndDate.emit({
actualStartDate: this.form.get('dateRange').value.start,
actualEndDate: this.form.get('dateRange').value.end
});
}
}