diff --git a/projects/project-3/frontend/package-lock.json b/projects/project-3/frontend/package-lock.json index 725a805..a0a1c6c 100644 --- a/projects/project-3/frontend/package-lock.json +++ b/projects/project-3/frontend/package-lock.json @@ -2145,6 +2145,19 @@ "picomatch": "^2.0.4" } }, + "apexcharts": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.23.0.tgz", + "integrity": "sha512-1mV6qouuopvYR6UFSXi/Ge4jRMe//zyAN3aK05mAs4Iuet8mA0w31Q6OU6syD77bawt9p3YKNOmNF7OO2u9w0g==", + "requires": { + "svg.draggable.js": "^2.2.2", + "svg.easing.js": "^2.0.0", + "svg.filter.js": "^2.0.2", + "svg.pathmorphing.js": "^0.1.3", + "svg.resize.js": "^1.4.3", + "svg.select.js": "^3.0.1" + } + }, "app-root-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz", @@ -7885,6 +7898,21 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "ng-apexcharts": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.5.6.tgz", + "integrity": "sha512-78vmZvrT9iqfZXE00+T8NTvR+EHV0wo4qqf0Zfu1/2KiwazCU9S5EROcmgqMQ1eCO7Sz4GiR19rLTMdtWL/WmQ==", + "requires": { + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -11892,6 +11920,70 @@ "has-flag": "^3.0.0" } }, + "svg.draggable.js": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", + "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==", + "requires": { + "svg.js": "^2.0.1" + } + }, + "svg.easing.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz", + "integrity": "sha1-iqmUawqOJ4V6XEChDrpAkeVpHxI=", + "requires": { + "svg.js": ">=2.3.x" + } + }, + "svg.filter.js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz", + "integrity": "sha1-kQCOFROJ3ZIwd5/L5uLJo2LRwgM=", + "requires": { + "svg.js": "^2.2.5" + } + }, + "svg.js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz", + "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==" + }, + "svg.pathmorphing.js": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz", + "integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==", + "requires": { + "svg.js": "^2.4.0" + } + }, + "svg.resize.js": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz", + "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==", + "requires": { + "svg.js": "^2.6.5", + "svg.select.js": "^2.1.2" + }, + "dependencies": { + "svg.select.js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", + "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", + "requires": { + "svg.js": "^2.2.5" + } + } + } + }, + "svg.select.js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz", + "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==", + "requires": { + "svg.js": "^2.6.5" + } + }, "svgo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", diff --git a/projects/project-3/frontend/package.json b/projects/project-3/frontend/package.json index d88c95d..5a026d5 100644 --- a/projects/project-3/frontend/package.json +++ b/projects/project-3/frontend/package.json @@ -22,8 +22,10 @@ "@angular/platform-browser": "~10.2.0", "@angular/platform-browser-dynamic": "~10.2.0", "@angular/router": "~10.2.0", + "apexcharts": "^3.23.0", "leaflet": "^1.7.1", "leaflet.markercluster": "^1.4.1", + "ng-apexcharts": "^1.5.6", "rxjs": "~6.6.0", "tslib": "^2.0.0", "zone.js": "~0.10.2" diff --git a/projects/project-3/frontend/src/app/app.component.html b/projects/project-3/frontend/src/app/app.component.html index 8e220c2..0680b43 100644 --- a/projects/project-3/frontend/src/app/app.component.html +++ b/projects/project-3/frontend/src/app/app.component.html @@ -1 +1 @@ - + diff --git a/projects/project-3/frontend/src/app/app.module.ts b/projects/project-3/frontend/src/app/app.module.ts index 96b18e2..722b835 100644 --- a/projects/project-3/frontend/src/app/app.module.ts +++ b/projects/project-3/frontend/src/app/app.module.ts @@ -10,13 +10,20 @@ import {FlexLayoutModule} from '@angular/flex-layout'; import {MatIconModule} from '@angular/material/icon'; import {MatButtonModule} from '@angular/material/button'; import {HttpClientModule} from '@angular/common/http'; -import { PopupComponent } from './map/popup/popup.component'; +import {NgApexchartsModule} from 'ng-apexcharts'; +import {DashboardComponent} from './dashboard/dashboard.component'; +import {MatGridListModule} from '@angular/material/grid-list'; +import {MatCardModule} from '@angular/material/card'; +import {MatMenuModule} from '@angular/material/menu'; +import {LayoutModule} from '@angular/cdk/layout'; +import {PopUpComponent} from './map/pop-up/pop-up.component'; @NgModule({ declarations: [ AppComponent, MapComponent, - PopupComponent + DashboardComponent, + PopUpComponent ], imports: [ BrowserModule, @@ -26,7 +33,12 @@ import { PopupComponent } from './map/popup/popup.component'; MatIconModule, MatButtonModule, FlexLayoutModule, - HttpClientModule + HttpClientModule, + NgApexchartsModule, + MatGridListModule, + MatCardModule, + MatMenuModule, + LayoutModule ], providers: [], bootstrap: [AppComponent] diff --git a/projects/project-3/frontend/src/app/dashboard/dashboard.component.html b/projects/project-3/frontend/src/app/dashboard/dashboard.component.html new file mode 100644 index 0000000..df9e4bf --- /dev/null +++ b/projects/project-3/frontend/src/app/dashboard/dashboard.component.html @@ -0,0 +1,24 @@ +
+

Dashboard

+ + + + + + {{card.title}} + + + + + + + + +
Card Content 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 new file mode 100644 index 0000000..49a8146 --- /dev/null +++ b/projects/project-3/frontend/src/app/dashboard/dashboard.component.scss @@ -0,0 +1,21 @@ +.grid-container { + margin: 20px; +} + +.dashboard-card { + position: absolute; + top: 15px; + left: 15px; + right: 15px; + bottom: 15px; +} + +.more-button { + position: absolute; + top: 5px; + right: 10px; +} + +.dashboard-card-content { + text-align: center; +} diff --git a/projects/project-3/frontend/src/app/dashboard/dashboard.component.spec.ts b/projects/project-3/frontend/src/app/dashboard/dashboard.component.spec.ts new file mode 100644 index 0000000..980c24f --- /dev/null +++ b/projects/project-3/frontend/src/app/dashboard/dashboard.component.spec.ts @@ -0,0 +1,40 @@ +import { LayoutModule } from '@angular/cdk/layout'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatGridListModule } from '@angular/material/grid-list'; +import { MatIconModule } from '@angular/material/icon'; +import { MatMenuModule } from '@angular/material/menu'; + +import { DashboardComponent } from './dashboard.component'; + +describe('DashboardComponent', () => { + let component: DashboardComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [DashboardComponent], + imports: [ + NoopAnimationsModule, + LayoutModule, + MatButtonModule, + MatCardModule, + MatGridListModule, + MatIconModule, + MatMenuModule, + ] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should compile', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/project-3/frontend/src/app/dashboard/dashboard.component.ts b/projects/project-3/frontend/src/app/dashboard/dashboard.component.ts new file mode 100644 index 0000000..964d896 --- /dev/null +++ b/projects/project-3/frontend/src/app/dashboard/dashboard.component.ts @@ -0,0 +1,33 @@ +import { Component } from '@angular/core'; +import { map } from 'rxjs/operators'; +import { Breakpoints, BreakpointObserver } from '@angular/cdk/layout'; + +@Component({ + selector: 'app-dashboard', + templateUrl: './dashboard.component.html', + styleUrls: ['./dashboard.component.scss'] +}) +export class DashboardComponent { + /** Based on the screen size, switch from standard to one column per row */ + cards = this.breakpointObserver.observe(Breakpoints.Handset).pipe( + map(({ matches }) => { + if (matches) { + return [ + { title: 'Card 1', cols: 1, rows: 1 }, + { title: 'Card 2', cols: 1, rows: 1 }, + { title: 'Card 3', cols: 1, rows: 1 }, + { title: 'Card 4', cols: 1, rows: 1 } + ]; + } + + return [ + { title: 'Card 1', cols: 2, rows: 1 }, + { title: 'Card 2', cols: 1, rows: 1 }, + { title: 'Card 3', cols: 1, rows: 2 }, + { title: 'Card 4', cols: 1, rows: 1 } + ]; + }) + ); + + constructor(private breakpointObserver: BreakpointObserver) {} +} diff --git a/projects/project-3/frontend/src/app/map/pop-up/pop-up.component.html b/projects/project-3/frontend/src/app/map/pop-up/pop-up.component.html new file mode 100644 index 0000000..abf4389 --- /dev/null +++ b/projects/project-3/frontend/src/app/map/pop-up/pop-up.component.html @@ -0,0 +1,2 @@ +

{{station.commonName}}


+ diff --git a/projects/project-3/frontend/src/app/map/pop-up/pop-up.component.scss b/projects/project-3/frontend/src/app/map/pop-up/pop-up.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/projects/project-3/frontend/src/app/map/pop-up/pop-up.component.spec.ts b/projects/project-3/frontend/src/app/map/pop-up/pop-up.component.spec.ts new file mode 100644 index 0000000..cd101cb --- /dev/null +++ b/projects/project-3/frontend/src/app/map/pop-up/pop-up.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PopUpComponent } from './pop-up.component'; + +describe('PopUpComponent', () => { + let component: PopUpComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ PopUpComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PopUpComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/project-3/frontend/src/app/map/pop-up/pop-up.component.ts b/projects/project-3/frontend/src/app/map/pop-up/pop-up.component.ts new file mode 100644 index 0000000..b363980 --- /dev/null +++ b/projects/project-3/frontend/src/app/map/pop-up/pop-up.component.ts @@ -0,0 +1,25 @@ +import {Component, OnInit} from '@angular/core'; +import {IBikeStation} from '../../service/domain/bike-station'; +import {Router} from '@angular/router'; + +@Component({ + selector: 'app-pop-up', + templateUrl: './pop-up.component.html', + styleUrls: ['./pop-up.component.scss'] +}) +export class PopUpComponent implements OnInit { + text = 'test'; + station: IBikeStation; + + constructor(private router: Router) { + } + + ngOnInit(): void { + this.text = 'test'; + } + + public route(): void { + this.router.navigate(['/dashboard', this.station.id]); + } + +} diff --git a/projects/project-3/frontend/src/app/service/pop-up.service.spec.ts b/projects/project-3/frontend/src/app/service/pop-up.service.spec.ts new file mode 100644 index 0000000..efa3a6a --- /dev/null +++ b/projects/project-3/frontend/src/app/service/pop-up.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { PopUpService } from './pop-up.service'; + +describe('PopUpService', () => { + let service: PopUpService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PopUpService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/projects/project-3/frontend/src/app/service/pop-up.service.ts b/projects/project-3/frontend/src/app/service/pop-up.service.ts new file mode 100644 index 0000000..0f41fd8 --- /dev/null +++ b/projects/project-3/frontend/src/app/service/pop-up.service.ts @@ -0,0 +1,114 @@ +import {ComponentFactoryResolver, Injectable, Injector} from '@angular/core'; +import {IBikeStation} from './domain/bike-station'; +import { + ApexAxisChartSeries, + ApexChart, + ApexDataLabels, + ApexFill, + ApexLegend, + ApexPlotOptions, + ApexStroke, + ApexTitleSubtitle, + ApexXAxis +} from 'ng-apexcharts'; +import {Router} from '@angular/router'; +import {PopUpComponent} from '../map/pop-up/pop-up.component'; + +export type ChartOptions = { + series: ApexAxisChartSeries; + chart: ApexChart; + dataLabels: ApexDataLabels; + plotOptions: ApexPlotOptions; + xaxis: ApexXAxis; + stroke: ApexStroke; + title: ApexTitleSubtitle; + fill: ApexFill; + legend: ApexLegend; +}; + +@Injectable({ + providedIn: 'root' +}) +export class PopUpService { + public chartOptions: Partial; + + constructor( + private router: Router, + private componentFactoryResolver: ComponentFactoryResolver, + private injector: Injector + ) { + } + + // + + makeAvailabilityPopUp(station: IBikeStation): any { + const factory = this.componentFactoryResolver.resolveComponentFactory(PopUpComponent); + const component = factory.create(this.injector); + + component.instance.station = station; + component.changeDetectorRef.detectChanges(); + + return component.location.nativeElement; + } + + route(station: IBikeStation): void { + this.router.navigate(['/dashboard', station.id]); + } + + bindChartOptionsToGivenStation(station: IBikeStation): void { + this.chartOptions = { + series: [ + { + name: 'wenig vorhanden', + data: [44, 55, 41, 37, 22, 43, 21] + }, + { + name: 'moderat voll', + data: [53, 32, 33, 52, 13, 43, 32] + }, + { + name: 'voll', + data: [12, 17, 11, 9, 15, 11, 20] + } + ], + chart: { + type: 'bar', + height: 350, + stacked: true, + stackType: '100%' + }, + plotOptions: { + bar: { + horizontal: true + } + }, + stroke: { + width: 1, + colors: ['#fff'] + }, + title: { + text: 'Availability of Station: ' + station.commonName + }, + xaxis: { + categories: [] + }, + fill: { + opacity: 1 + }, + legend: { + position: 'top', + horizontalAlign: 'left', + offsetX: 40 + } + }; + } +}