diff --git a/projects/project-3/frontend/src/app/dashboard/dashboard.component.html b/projects/project-3/frontend/src/app/dashboard/dashboard.component.html index a232561..d5f99d3 100644 --- a/projects/project-3/frontend/src/app/dashboard/dashboard.component.html +++ b/projects/project-3/frontend/src/app/dashboard/dashboard.component.html @@ -77,117 +77,12 @@ -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - station of rental destination{{element.stationName}} number of drives {{element.number}} average rental duration {{humanizeAvgDuration(element.avgDuration)}} icon on mapmarker
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - station of rental origin {{element.stationName}} number of drives {{element.number}} average rental duration {{humanizeAvgDuration(element.avgDuration)}} icon on mapmarker
- - - - -
- +
+
- - - Rental Duration - - This chart shows the rent duration based on the currently selected station. - The time it takes for a rent which has the current station as origin is displayed here. - - - -
- -
-
-
+
diff --git a/projects/project-3/frontend/src/app/dashboard/dashboard.component.scss b/projects/project-3/frontend/src/app/dashboard/dashboard.component.scss index ffb5e9f..6a0acba 100644 --- a/projects/project-3/frontend/src/app/dashboard/dashboard.component.scss +++ b/projects/project-3/frontend/src/app/dashboard/dashboard.component.scss @@ -6,14 +6,6 @@ mat-sidenav-content { flex: 1 1 auto; } -a { - color: black; -} - -img { - width: 60px; -} - .button-back:hover, .button-wiki:hover { background: #086ed2; } @@ -53,7 +45,6 @@ img { margin-left: 39px; } - .container-map { height: 40em; margin: 1em 2em; @@ -64,14 +55,6 @@ img { margin: 1em 3em; } -.dashboard-table-to { - margin-right: 1em; -} - -.dashboard-table-from { - margin-left: 1em; -} - .container-borrow-duration { height: 41em; margin: 1em 2em; diff --git a/projects/project-3/frontend/src/app/dashboard/dashboard.component.ts b/projects/project-3/frontend/src/app/dashboard/dashboard.component.ts index fd838da..e7344df 100644 --- a/projects/project-3/frontend/src/app/dashboard/dashboard.component.ts +++ b/projects/project-3/frontend/src/app/dashboard/dashboard.component.ts @@ -2,10 +2,8 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Injectable, OnIni import {ActivatedRoute, Router} from '@angular/router'; import {DashboardService} from '../service/dashboard.service'; import {IDashboardCommonBikePoint} from '../service/domain/dashboard-common-bike-point'; -import {MatTableDataSource} from '@angular/material/table'; import {FormBuilder, FormControl, FormGroup} from '@angular/forms'; import {MapService} from '../service/map.service'; -import stht from 'seconds-to-human-time'; import { @@ -20,14 +18,13 @@ import { ApexTitleSubtitle, ApexTooltip, ApexXAxis, - ApexYAxis, - ChartComponent + ApexYAxis } from 'ng-apexcharts'; import {IMapBikePoint} from '../service/domain/map-bike-point'; -import {SelectionModel} from '@angular/cdk/collections'; -import {MatCheckboxChange} from '@angular/material/checkbox'; import {DateAdapter, MAT_DATE_FORMATS, NativeDateAdapter} from '@angular/material/core'; import {formatDate} from '@angular/common'; +import {TableComponent} from './table/table.component'; +import {RentDurationChartComponent} from "./rent-duration-chart/rent-duration-chart.component"; export type ChartOptions = { title: ApexTitleSubtitle; @@ -80,19 +77,12 @@ const chartHeight = 460; ] }) export class DashboardComponent implements OnInit { - @ViewChild('Station-Dashboard-Borrow-Duration') chart: ChartComponent; + @ViewChild(TableComponent) table: TableComponent; + @ViewChild(RentDurationChartComponent) durationChart: RentDurationChartComponent; + public durationChartOptions: Partial; public timeChartOptions: Partial; public bikePointChartOptions: Partial; - displayedColumnsTo: string[] = ['select', 'endStationName', 'number', 'avgDuration', 'marker']; - displayedColumnsFrom: string[] = ['select', 'startStationName', 'number', 'avgDuration', 'marker']; - stationToSource = new MatTableDataSource(); - iterableToSource: any[]; - stationFromSource = new MatTableDataSource(); - iterableFromSource: any[]; - selectionModel = new SelectionModel(true, []); - colors = ['black', 'gray', 'green', 'orange', 'purple', 'red']; - isLoading: boolean; station: IDashboardCommonBikePoint; maxStartDate: Date; @@ -138,7 +128,6 @@ export class DashboardComponent implements OnInit { text: 'Loading...' } }; - this.isLoading = true; } ngOnInit(): void { @@ -151,8 +140,6 @@ export class DashboardComponent implements OnInit { this.changeDetectorRefs.detectChanges(); this.map.removeTableStationMarkerOnReload(); this.route.params.subscribe(params => { - this.selectionModel.clear(); - this.colors = ['black', 'gray', 'green', 'orange', 'purple', 'red']; this.service.fetchDashboardInit(params.id).then(data => { this.station = data; this.maxStartDate = new Date(data.maxStartDate); @@ -254,18 +241,6 @@ export class DashboardComponent implements OnInit { const initDate = this.maxEndDate.toISOString().substring(0, 10); this.form.get('daterange').get('start').setValue(initDate); this.form.get('daterange').get('end').setValue(initDate); - await this.service.fetchDashboardStationTo(this.station.id, initDate, initDate).then((source) => { - this.stationToSource = this.setBikePointColorToSource(source); - this.iterableToSource = source; - this.isLoading = false; - this.changeDetectorRefs.detectChanges(); - }); - await this.service.fetchDashboardStationFrom(this.station.id, initDate, initDate).then((source) => { - this.stationFromSource = this.setBikePointColorFromSource(source); - this.iterableFromSource = source; - this.isLoading = false; - this.changeDetectorRefs.detectChanges(); - }); this.service.fetchDashboardStationCharts(this.station.id, initDate, initDate, 'duration').then((source) => { const numbers = []; const minutesGroup = []; @@ -404,232 +379,17 @@ export class DashboardComponent implements OnInit { } async onSubmit(): Promise { - this.isLoading = false; this.actualStartDate = this.form.get('daterange').value.start; this.actualEndDate = this.form.get('daterange').value.end; - this.map.removeTableStationMarkerOnReload(); - this.selectionModel.clear(); - await this.service.fetchDashboardStationTo( - this.station.id, + this.table.onSubmit( this.actualStartDate.toISOString().substring(0, 10), this.actualEndDate.toISOString().substring(0, 10) - ).then((source) => { - this.colors = ['black', 'gray', 'green', 'orange', 'purple', 'red']; - this.stationToSource = this.setBikePointColorToSource(source); - this.iterableToSource = source; - this.isLoading = false; - this.changeDetectorRefs.detectChanges(); - }); - await this.service.fetchDashboardStationFrom( - this.station.id, + ); + this.durationChart.onSubmit( this.actualStartDate.toISOString().substring(0, 10), this.actualEndDate.toISOString().substring(0, 10) - ).then((source) => { - this.stationFromSource = this.setBikePointColorFromSource(source); - this.iterableFromSource = source; - this.isLoading = false; - this.changeDetectorRefs.detectChanges(); - }); - this.service.fetchDashboardStationCharts( - this.station.id, - this.actualStartDate.toISOString().substring(0, 10), - this.actualEndDate.toISOString().substring(0, 10), - 'duration' - ).then((source) => { - const numbers = []; - const minutesGroup = []; - source.forEach(value => { - numbers.push(value.number); - minutesGroup.push(value.minutesGroup); - }); - this.durationChartOptions = { - series: [ - { - name: 'amount of drives for given borrow duration', - data: numbers - } - ], - chart: { - type: 'bar', - height: chartHeight - }, - colors: ['#017bfe'], - plotOptions: { - bar: { - horizontal: false, - columnWidth: '55%', - endingShape: 'flat' - } - }, - dataLabels: { - enabled: false - }, - stroke: { - show: true, - width: 2, - colors: ['transparent'] - }, - xaxis: { - title: { - text: 'average rental duration' - }, - categories: minutesGroup, - labels: { - formatter: value => { - return value + ' min'; - } - } - }, - yaxis: { - title: { - text: 'amount of drives' - } - }, - fill: { - opacity: 1 - } - }; - }); - this.service.fetchDashboardStationCharts( - this.station.id, - this.actualStartDate.toISOString().substring(0, 10), - this.actualEndDate.toISOString().substring(0, 10), - 'time' - ).then((source) => { - const timeFrame = []; - const numbers = []; - const avgDuration = []; - source.forEach(value => { - timeFrame.push(value.timeFrame); - numbers.push(value.number); - avgDuration.push(Math.round(value.avgDuration / 60)); - }); - this.timeChartOptions = { - series: [ - { - name: 'amount of drives', - type: 'bar', - data: numbers - }, - { - name: 'average rental duration', - type: 'line', - data: avgDuration - } - ], - chart: { - toolbar: { - show: false - }, - type: 'line', - height: chartHeight, - zoom: { - enabled: true, - } - }, - colors: ['#017bfe', '#51ca49'], - dataLabels: { - enabled: false - }, - stroke: { - curve: 'straight' - }, - xaxis: { - title: { - text: 'time of the day' - }, - categories: timeFrame, - tickAmount: 24, - tickPlacement: 'between' - }, - yaxis: [{ - title: { - text: 'amount of drives', - }, - }, { - opposite: true, - title: { - text: 'average rental duration' - }, - labels: { - formatter: (val: number): string => { - return val + ' min'; - } - } - }], - legend: { - horizontalAlign: 'left' - }, - fill: { - opacity: 1 - } - }; - }); + ); } - humanizeAvgDuration(avgDuration: number): string { - return stht(avgDuration); - } - selectRow(selection: MatCheckboxChange, row): void { - const markerToDisplay = []; - this.iterableToSource.forEach(point => { - if (point.stationId === row.stationId) { - this.selectionModel.toggle(point); - } - }); - this.iterableFromSource.forEach(point => { - if (point.stationId === row.stationId) { - this.selectionModel.toggle(point); - } - }); - this.selectionModel.selected.forEach(point => { - markerToDisplay.push(point); - }); - this.map.drawTableStationMarker(markerToDisplay); - } - - public drawIconInTable(bikePoint: any): string { - return `../../assets/bike-point-${bikePoint.color}.png`; - } - - setBikePointColorToSource(source): any { - for (const station of source) { - if (station.stationId === this.station.id) { - station.color = 'blue'; - continue; - } - station.color = this.getRandomColor(); - } - return source; - } - - setBikePointColorFromSource(source): any { - for (const station of source) { - if (station.stationId === this.station.id) { - station.color = 'blue'; - continue; - } - for (const to of this.iterableToSource) { - if (station.stationId === to.stationId) { - station.color = to.color; - break; - } - } - if (!station.color) { - station.color = this.getRandomColor(); - } - } - return source; - } - - getRandomColor(): string { - const color = this.colors[Math.floor(Math.random() * this.colors.length)]; - this.colors = this.colors.filter(c => c !== color); - return color; - } - - isCheckBoxDisable(row): boolean { - return row.stationId === this.station.id; - } } diff --git a/projects/project-3/frontend/src/app/dashboard/rent-duration-chart/rent-duration-chart.component.html b/projects/project-3/frontend/src/app/dashboard/rent-duration-chart/rent-duration-chart.component.html new file mode 100644 index 0000000..2141b1d --- /dev/null +++ b/projects/project-3/frontend/src/app/dashboard/rent-duration-chart/rent-duration-chart.component.html @@ -0,0 +1,24 @@ + + + Rental Duration + + This chart shows the rent duration based on the currently selected station. + The time it takes for a rent which has the current station as origin is displayed here. + + + +
+ +
+
+
diff --git a/projects/project-3/frontend/src/app/dashboard/rent-duration-chart/rent-duration-chart.component.scss b/projects/project-3/frontend/src/app/dashboard/rent-duration-chart/rent-duration-chart.component.scss new file mode 100644 index 0000000..dd9f823 --- /dev/null +++ b/projects/project-3/frontend/src/app/dashboard/rent-duration-chart/rent-duration-chart.component.scss @@ -0,0 +1,17 @@ +.station-dashboard-borrow-duration { + margin: 1em; +} + +.mat-card { + padding: 1px 1px 1px; + margin: 10px; +} + +.mat-card-title { + margin-top: 1em; + margin-left: 2em; +} + +.mat-card-subtitle { + margin-left: 39px; +} diff --git a/projects/project-3/frontend/src/app/dashboard/rent-duration-chart/rent-duration-chart.component.spec.ts b/projects/project-3/frontend/src/app/dashboard/rent-duration-chart/rent-duration-chart.component.spec.ts new file mode 100644 index 0000000..9bdd08a --- /dev/null +++ b/projects/project-3/frontend/src/app/dashboard/rent-duration-chart/rent-duration-chart.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RentDurationChartComponent } from './rent-duration-chart.component'; + +describe('RentDurationChartComponent', () => { + let component: RentDurationChartComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ RentDurationChartComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RentDurationChartComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/project-3/frontend/src/app/dashboard/rent-duration-chart/rent-duration-chart.component.ts b/projects/project-3/frontend/src/app/dashboard/rent-duration-chart/rent-duration-chart.component.ts new file mode 100644 index 0000000..041a59b --- /dev/null +++ b/projects/project-3/frontend/src/app/dashboard/rent-duration-chart/rent-duration-chart.component.ts @@ -0,0 +1,149 @@ +import {Component, OnInit, ViewChild} from '@angular/core'; +import { + ApexAxisChartSeries, + ApexChart, + ApexDataLabels, + ApexFill, + ApexLegend, + ApexNoData, + ApexPlotOptions, + ApexStroke, + ApexTitleSubtitle, + ApexTooltip, + ApexXAxis, + ApexYAxis, + ChartComponent +} from 'ng-apexcharts'; +import {ActivatedRoute} from '@angular/router'; +import {DashboardService} from '../../service/dashboard.service'; +import {IDashboardCommonBikePoint} from '../../service/domain/dashboard-common-bike-point'; + +export type ChartOptions = { + title: ApexTitleSubtitle; + subtitle: ApexTitleSubtitle; + series: ApexAxisChartSeries; + chart: ApexChart; + colors: string[]; + dataLabels: ApexDataLabels; + plotOptions: ApexPlotOptions; + yaxis: ApexYAxis; + xaxis: ApexXAxis; + fill: ApexFill; + tooltip: ApexTooltip; + stroke: ApexStroke; + legend: ApexLegend; + noData: ApexNoData; +}; + +const chartType = 'duration'; + +@Component({ + selector: 'app-rent-duration-chart', + templateUrl: './rent-duration-chart.component.html', + styleUrls: ['./rent-duration-chart.component.scss'] +}) +export class RentDurationChartComponent implements OnInit { + @ViewChild(ChartComponent) chart: ChartComponent; + chartOptions: Partial; + + bikePoint: IDashboardCommonBikePoint; + maxStartDate: Date; + maxEndDate: Date; + + constructor( + private route: ActivatedRoute, + private service: DashboardService, + ) { + this.chartOptions = { + series: [], + chart: { + type: 'bar' + }, + noData: { + text: 'Loading...' + } + }; + } + + ngOnInit(): void { + this.route.params.subscribe(params => { + this.service.fetchDashboardInit(params.id).then(data => { + this.bikePoint = data; + this.maxStartDate = new Date(data.maxStartDate); + this.maxEndDate = new Date(data.maxEndDate); + this.initChart(); + }); + }); + } + + async initChart(): Promise { + const initDate = this.maxEndDate.toISOString().substring(0, 10); + await this.service.fetchDashboardStationCharts(this.bikePoint.id, initDate, initDate, chartType).then(source => { + this.chartOptions = { + series: [ + { + name: 'amount of drives', + data: source.map(value => value.number) + } + ], + chart: { + type: 'bar', + height: '460' + }, + colors: ['#017bfe'], + plotOptions: { + bar: { + horizontal: false, + columnWidth: '55%', + endingShape: 'flat' + } + }, + dataLabels: { + enabled: false + }, + stroke: { + show: true, + width: 2, + colors: ['transparent'] + }, + xaxis: { + title: { + text: 'average rental duration' + }, + categories: source.map(value => value.minutesGroup), + labels: { + formatter: value => { + return value + ' min'; + } + } + }, + yaxis: { + title: { + text: 'amount of drives' + } + }, + noData: { + text: 'loading' + }, + fill: { + opacity: 1 + } + }; + this.chart.updateOptions(this.chartOptions); + }); + } + + async onSubmit(actualStartDate: string, actualEndDate: string): Promise { + await this.service.fetchDashboardStationCharts( + this.bikePoint.id, + actualStartDate, + actualEndDate, + chartType + ).then(source => { + this.chart.updateSeries([{ + data: source.map(value => value.number) + }]); + }); + } + +}