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
+ }
+ };
+ }
+}