diff --git a/projects/project-3/frontend/src/app/app.module.ts b/projects/project-3/frontend/src/app/app.module.ts index 43484df..735ee4c 100644 --- a/projects/project-3/frontend/src/app/app.module.ts +++ b/projects/project-3/frontend/src/app/app.module.ts @@ -32,6 +32,7 @@ import {MatProgressSpinnerModule} from "@angular/material/progress-spinner"; import { TableComponent } from './dashboard/table/table.component'; import { RentDurationChartComponent } from './dashboard/rent-duration-chart/rent-duration-chart.component'; import { RentTimeChartComponent } from './dashboard/rent-time-chart/rent-time-chart.component'; +import { UserInputComponent } from './dashboard/user-input/user-input.component'; @NgModule({ declarations: [ @@ -42,7 +43,8 @@ import { RentTimeChartComponent } from './dashboard/rent-time-chart/rent-time-ch AutoRefreshComponent, TableComponent, RentDurationChartComponent, - RentTimeChartComponent + RentTimeChartComponent, + UserInputComponent ], imports: [ BrowserModule, 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 5b600b3..42ec912 100644 --- a/projects/project-3/frontend/src/app/dashboard/dashboard.component.html +++ b/projects/project-3/frontend/src/app/dashboard/dashboard.component.html @@ -19,54 +19,10 @@
- - -
- {{bikePoint?.commonName}} -
- -
-
-

Select a range to analyze data

- - Enter a range - - - - - - - -
-
- -
-
-
-
- -
-
-
-
+
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 c9d5d7c..50b22f7 100644 --- a/projects/project-3/frontend/src/app/dashboard/dashboard.component.ts +++ b/projects/project-3/frontend/src/app/dashboard/dashboard.component.ts @@ -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 {DashboardService} from '../service/dashboard.service'; import {IDashboardCommonBikePoint} from '../service/domain/dashboard-common-bike-point'; -import {FormBuilder, FormControl, FormGroup} from '@angular/forms'; import {MapService} from '../service/map.service'; @@ -20,12 +19,12 @@ import { ApexXAxis, ApexYAxis } from 'ng-apexcharts'; -import {IMapBikePoint} from '../service/domain/map-bike-point'; 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"; -import {RentTimeChartComponent} from "./rent-time-chart/rent-time-chart.component"; +import {RentDurationChartComponent} from './rent-duration-chart/rent-duration-chart.component'; +import {RentTimeChartComponent} from './rent-time-chart/rent-time-chart.component'; +import {StartEndDate} from './user-input/user-input.component'; export type ChartOptions = { title: ApexTitleSubtitle; @@ -44,27 +43,6 @@ export type ChartOptions = { 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; @Component({ @@ -72,65 +50,27 @@ const chartHeight = 460; templateUrl: './dashboard.component.html', styleUrls: ['./dashboard.component.scss'], changeDetection: ChangeDetectionStrategy.Default, - providers: [ - {provide: DateAdapter, useClass: PickDateAdapter}, - {provide: MAT_DATE_FORMATS, useValue: PICK_FORMATS} - ] }) export class DashboardComponent implements OnInit { @ViewChild(TableComponent) table: TableComponent; @ViewChild(RentDurationChartComponent) durationChart: RentDurationChartComponent; @ViewChild(RentTimeChartComponent) timeChart: RentTimeChartComponent; - public timeChartOptions: Partial; public bikePointChartOptions: Partial; station: IDashboardCommonBikePoint; maxStartDate: Date; maxEndDate: Date; - actualStartDate: Date; - actualEndDate: Date; - form: FormGroup; - - bikePoint: IMapBikePoint; constructor( private route: ActivatedRoute, private router: Router, private service: DashboardService, 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 { - 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.service.fetchDashboardInit(params.id).then(data => { this.station = data; @@ -138,119 +78,26 @@ export class DashboardComponent implements OnInit { this.maxEndDate = new Date(data.maxEndDate); 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 { - 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.drawDashboardStationMarker(this.station); } - async onSubmit(): Promise { - this.actualStartDate = this.form.get('daterange').value.start; - this.actualEndDate = this.form.get('daterange').value.end; - this.table.onSubmit( - this.actualStartDate.toISOString().substring(0, 10), - this.actualEndDate.toISOString().substring(0, 10) + async onSubmit(startEndDate: StartEndDate): Promise { + await this.table.onSubmit( + startEndDate.actualStartDate.toISOString().substring(0, 10), + startEndDate.actualEndDate.toISOString().substring(0, 10) ); - this.durationChart.onSubmit( - this.actualStartDate.toISOString().substring(0, 10), - this.actualEndDate.toISOString().substring(0, 10) + await this.durationChart.onSubmit( + startEndDate.actualStartDate.toISOString().substring(0, 10), + startEndDate.actualEndDate.toISOString().substring(0, 10) ); - this.timeChart.onSubmit( - this.actualStartDate.toISOString().substring(0, 10), - this.actualEndDate.toISOString().substring(0, 10) + await this.timeChart.onSubmit( + startEndDate.actualStartDate.toISOString().substring(0, 10), + startEndDate.actualEndDate.toISOString().substring(0, 10) ); } diff --git a/projects/project-3/frontend/src/app/dashboard/user-input/user-input.component.html b/projects/project-3/frontend/src/app/dashboard/user-input/user-input.component.html new file mode 100644 index 0000000..8133809 --- /dev/null +++ b/projects/project-3/frontend/src/app/dashboard/user-input/user-input.component.html @@ -0,0 +1,48 @@ + + +
+ {{bikePoint?.commonName}} +
+ +
+
+

Select a range to analyze data

+ + Enter a range + + + + + + + +
+
+ +
+
+
+
+ +
+
+
+
diff --git a/projects/project-3/frontend/src/app/dashboard/user-input/user-input.component.scss b/projects/project-3/frontend/src/app/dashboard/user-input/user-input.component.scss new file mode 100644 index 0000000..6f5429f --- /dev/null +++ b/projects/project-3/frontend/src/app/dashboard/user-input/user-input.component.scss @@ -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; +} diff --git a/projects/project-3/frontend/src/app/dashboard/user-input/user-input.component.spec.ts b/projects/project-3/frontend/src/app/dashboard/user-input/user-input.component.spec.ts new file mode 100644 index 0000000..e4a09b7 --- /dev/null +++ b/projects/project-3/frontend/src/app/dashboard/user-input/user-input.component.spec.ts @@ -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; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ UserInputComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(UserInputComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/project-3/frontend/src/app/dashboard/user-input/user-input.component.ts b/projects/project-3/frontend/src/app/dashboard/user-input/user-input.component.ts new file mode 100644 index 0000000..481bfb4 --- /dev/null +++ b/projects/project-3/frontend/src/app/dashboard/user-input/user-input.component.ts @@ -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 = new EventEmitter(); + + chartOptions: Partial; + + 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 { + 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 { + this.startEndDate.emit({ + actualStartDate: this.form.get('dateRange').value.start, + actualEndDate: this.form.get('dateRange').value.end + }); + } + +}