Some final touches Issue #4

* Wrap tables in mat cards
* Add accidents legend
* Adjust table column names
* Some CSS adjustments
This commit is contained in:
Marcel Schwarz 2021-01-11 22:21:56 +01:00
parent 47d53ecc54
commit 1545ce1a78
9 changed files with 178 additions and 104 deletions

View File

@ -9,6 +9,9 @@
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<div *ngIf="isLoading" class="col d-flex align-items-center justify-content-center">
<mat-progress-spinner color="primary" mode="indeterminate" [diameter]="300"></mat-progress-spinner>
</div>
<div *ngIf="!isLoading" class="station-dashboard-borrow-time"> <div *ngIf="!isLoading" class="station-dashboard-borrow-time">
<apx-chart <apx-chart
[chart]="chartOptions.chart" [chart]="chartOptions.chart"
@ -20,10 +23,8 @@
[stroke]="chartOptions.stroke" [stroke]="chartOptions.stroke"
[tooltip]="chartOptions.tooltip" [tooltip]="chartOptions.tooltip"
[xaxis]="chartOptions.xaxis" [xaxis]="chartOptions.xaxis"
[yaxis]="chartOptions.yaxis"></apx-chart> [yaxis]="chartOptions.yaxis">
</div> </apx-chart>
<div *ngIf="isLoading" class="col d-flex align-items-center justify-content-center">
<mat-progress-spinner color="primary" mode="indeterminate" [diameter]="300"></mat-progress-spinner>
</div> </div>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>

View File

@ -151,12 +151,11 @@ export class RentTimeChartComponent implements OnInit {
} }
}; };
}); });
} }
async onSubmit(actualStartDate: string, actualEndDate: string): Promise<void> { async onSubmit(actualStartDate: string, actualEndDate: string): Promise<void> {
this.isLoading = true; this.isLoading = true;
await this.service.fetchDashboardStationCharts( this.service.fetchDashboardStationCharts(
this.bikePoint.id, this.bikePoint.id,
actualStartDate, actualStartDate,
actualEndDate, actualEndDate,

View File

@ -1,88 +1,110 @@
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-12 mb-md-3 mb-sm-3 mb-3"> <div class="col-lg-6 col-md-12 mb-md-3 mb-sm-3 mb-3">
<table [dataSource]="stationToSource" class="mat-elevation-z0 w-100" mat-table> <mat-card>
<ng-container matColumnDef="select"> <mat-card-header>
<th *matHeaderCellDef mat-header-cell></th> <mat-card-title>Top-3 rental destination</mat-card-title>
<td *matCellDef="let row" class="p-3" mat-cell> <mat-card-subtitle>
<mat-checkbox (change)="$event ? selectRow($event, row) : null" This table shows the top-3 destinations of rentals from this station by number of drives.
(click)="$event.stopPropagation()" The Station can be sent to the map with the checkbox.
[checked]="selectionModel.isSelected(row)" </mat-card-subtitle>
[disabled]="isCheckBoxDisable(row)" </mat-card-header>
matTooltip="toggle to view marker on map" <mat-card-content>
matTooltipPosition="above"> <table [dataSource]="stationToSource" class="mat-elevation-z0 w-100" mat-table>
</mat-checkbox> <ng-container matColumnDef="select">
</td> <th *matHeaderCellDef mat-header-cell></th>
</ng-container> <td *matCellDef="let row" class="p-3" mat-cell>
<ng-container matColumnDef="endStationName"> <mat-checkbox (change)="$event ? selectRow($event, row) : null"
<th *matHeaderCellDef mat-header-cell> station of rental destination</th> (click)="$event.stopPropagation()"
<td *matCellDef="let element" mat-cell> [checked]="selectionModel.isSelected(row)"
<a [routerLink]="['/dashboard/', element.stationId]">{{element.stationName}}</a> [disabled]="isCheckBoxDisable(row)"
</td> matTooltip="toggle to view marker on map"
</ng-container> matTooltipPosition="above">
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="endStationName">
<th *matHeaderCellDef mat-header-cell>Destination</th>
<td *matCellDef="let element" mat-cell>
<a [routerLink]="['/dashboard/', element.stationId]">{{element.stationName}}</a>
</td>
</ng-container>
<ng-container matColumnDef="number"> <ng-container matColumnDef="number">
<th *matHeaderCellDef mat-header-cell> number of drives</th> <th *matHeaderCellDef mat-header-cell>Count</th>
<td *matCellDef="let element" mat-cell> {{element.number}} </td> <td *matCellDef="let element" mat-cell> {{element.number}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="avgDuration"> <ng-container matColumnDef="avgDuration">
<th *matHeaderCellDef mat-header-cell> average rental duration</th> <th *matHeaderCellDef mat-header-cell>Average duration</th>
<td *matCellDef="let element" mat-cell> {{humanizeAvgDuration(element.avgDuration)}} </td> <td *matCellDef="let element" mat-cell> {{humanizeAvgDuration(element.avgDuration)}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="marker"> <ng-container matColumnDef="marker">
<th *matHeaderCellDef mat-header-cell> icon on map</th> <th *matHeaderCellDef mat-header-cell>Icon</th>
<td *matCellDef="let element" mat-cell><img [src]="drawIconInTable(element)" alt="marker"></td> <td *matCellDef="let element" mat-cell><img [src]="drawIconInTable(element)" alt="marker"></td>
</ng-container> </ng-container>
<tr *matHeaderRowDef="displayedColumnsTo" mat-header-row></tr> <tr *matHeaderRowDef="displayedColumnsTo" mat-header-row></tr>
<tr *matRowDef="let row; columns: displayedColumnsTo;" mat-row></tr> <tr *matRowDef="let row; columns: displayedColumnsTo;" mat-row></tr>
</table> </table>
<div *ngIf="isLoadingToSource" class="col d-flex align-items-center justify-content-center"> <div *ngIf="isLoadingToSource" class="col d-flex align-items-center justify-content-center">
<mat-progress-spinner color="primary" mode="indeterminate"></mat-progress-spinner> <mat-progress-spinner color="primary" mode="indeterminate"></mat-progress-spinner>
</div> </div>
</mat-card-content>
</mat-card>
</div> </div>
<div class="col-lg-6 col-md-12"> <div class="col-lg-6 col-md-12">
<table [dataSource]="stationFromSource" class="mat-elevation-z0 w-100" mat-table> <mat-card>
<ng-container matColumnDef="select"> <mat-card-header>
<th *matHeaderCellDef mat-header-cell></th> <mat-card-title>Top-3 rental origin</mat-card-title>
<td *matCellDef="let row" class="p-3" mat-cell> <mat-card-subtitle>
<mat-checkbox (change)="$event ? selectRow($event, row) : null" This table shows the top-3 origins of rentals to this station by number of drives.
(click)="$event.stopPropagation()" The Station can be sent to the map with the checkbox.
[checked]="selectionModel.isSelected(row)" </mat-card-subtitle>
[disabled]="isCheckBoxDisable(row)" </mat-card-header>
matTooltip="toggle to view marker on map" <mat-card-content>
matTooltipPosition="above"> <table [dataSource]="stationFromSource" class="mat-elevation-z0 w-100" mat-table>
</mat-checkbox> <ng-container matColumnDef="select">
</td> <th *matHeaderCellDef mat-header-cell></th>
</ng-container> <td *matCellDef="let row" class="p-3" mat-cell>
<ng-container matColumnDef="startStationName"> <mat-checkbox (change)="$event ? selectRow($event, row) : null"
<th *matHeaderCellDef mat-header-cell> station of rental origin</th> (click)="$event.stopPropagation()"
<td *matCellDef="let element" mat-cell> [checked]="selectionModel.isSelected(row)"
<a [routerLink]="['/dashboard/', element.stationId]"> {{element.stationName}}</a> [disabled]="isCheckBoxDisable(row)"
</td> matTooltip="toggle to view marker on map"
</ng-container> matTooltipPosition="above">
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="startStationName">
<th *matHeaderCellDef mat-header-cell>Origin</th>
<td *matCellDef="let element" mat-cell>
<a [routerLink]="['/dashboard/', element.stationId]"> {{element.stationName}}</a>
</td>
</ng-container>
<ng-container matColumnDef="number"> <ng-container matColumnDef="number">
<th *matHeaderCellDef mat-header-cell> number of drives</th> <th *matHeaderCellDef mat-header-cell>Count</th>
<td *matCellDef="let element" mat-cell> {{element.number}} </td> <td *matCellDef="let element" mat-cell> {{element.number}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="avgDuration"> <ng-container matColumnDef="avgDuration">
<th *matHeaderCellDef mat-header-cell> average rental duration</th> <th *matHeaderCellDef mat-header-cell>Average duration</th>
<td *matCellDef="let element" mat-cell> {{humanizeAvgDuration(element.avgDuration)}} </td> <td *matCellDef="let element" mat-cell> {{humanizeAvgDuration(element.avgDuration)}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="marker"> <ng-container matColumnDef="marker">
<th *matHeaderCellDef mat-header-cell> icon on map</th> <th *matHeaderCellDef mat-header-cell>Icon</th>
<td *matCellDef="let element" mat-cell><img [src]="drawIconInTable(element)" alt="marker"></td> <td *matCellDef="let element" mat-cell><img [src]="drawIconInTable(element)" alt="marker"></td>
</ng-container> </ng-container>
<tr *matHeaderRowDef="displayedColumnsFrom" mat-header-row></tr> <tr *matHeaderRowDef="displayedColumnsFrom" mat-header-row></tr>
<tr *matRowDef="let row; columns: displayedColumnsFrom;" mat-row></tr> <tr *matRowDef="let row; columns: displayedColumnsFrom;" mat-row></tr>
</table> </table>
<div *ngIf="isLoadingFromSource" class="col d-flex align-items-center justify-content-center"> <div *ngIf="isLoadingFromSource" class="col d-flex align-items-center justify-content-center">
<mat-progress-spinner color="primary" mode="indeterminate"></mat-progress-spinner> <mat-progress-spinner color="primary" mode="indeterminate"></mat-progress-spinner>
</div> </div>
</mat-card-content>
</mat-card>
</div> </div>
</div> </div>

View File

@ -10,3 +10,8 @@ a {
.mat-checkbox-layout label { .mat-checkbox-layout label {
margin: 0 !important; margin: 0 !important;
} }
.mat-cell, .mat-header-cell {
padding-left: 8px;
padding-right: 8px;
}

View File

@ -8,7 +8,7 @@
<mat-card-content class="p-4 d-flex flex-column justify-content-center"> <mat-card-content class="p-4 d-flex flex-column justify-content-center">
<div> <div>
<p>Select a range to analyze data</p> <p>Select a range to analyze data</p>
<form [formGroup]="form" class="d-flex flex-row align-items-center justify-content-between"> <form [formGroup]="form" class="d-flex flex-row justify-content-between">
<mat-form-field appearance="fill"> <mat-form-field appearance="fill">
<mat-label>Enter a range</mat-label> <mat-label>Enter a range</mat-label>
<mat-date-range-input [max]="maxEndDate" [min]="maxStartDate" [rangePicker]="picker" <mat-date-range-input [max]="maxEndDate" [min]="maxStartDate" [rangePicker]="picker"
@ -20,7 +20,7 @@
<mat-date-range-picker #picker></mat-date-range-picker> <mat-date-range-picker #picker></mat-date-range-picker>
</mat-form-field> </mat-form-field>
<button (click)="onSubmit()" class="mt-n3" color="primary" mat-raised-button> <button (click)="onSubmit()" class="mt-n3" color="primary" mat-raised-button>
<mat-icon>dashboard</mat-icon> <mat-icon>cached</mat-icon>
reload reload
</button> </button>
</form> </form>

View File

@ -52,7 +52,7 @@ export const PICK_FORMATS = {
class PickDateAdapter extends NativeDateAdapter { class PickDateAdapter extends NativeDateAdapter {
format(date: Date, displayFormat: Object): string { format(date: Date, displayFormat: Object): string {
if (displayFormat === 'input') { if (displayFormat === 'input') {
return formatDate(date, 'dd-MM-yyyy', this.locale); return formatDate(date, 'dd.MM.yyyy', this.locale);
} else { } else {
return date.toDateString(); return date.toDateString();
} }

View File

@ -7,7 +7,6 @@ import {environment} from '../../environments/environment';
import {PopUpService} from './pop-up.service'; import {PopUpService} from './pop-up.service';
import {IMapBikePoint} from './domain/map-bike-point'; import {IMapBikePoint} from './domain/map-bike-point';
import {IDashboardCommonBikePoint} from './domain/dashboard-common-bike-point'; import {IDashboardCommonBikePoint} from './domain/dashboard-common-bike-point';
import {writeErrorToLogFile} from "@angular/cli/utilities/log-file";
const createIcon = color => L.icon({ const createIcon = color => L.icon({
@ -34,6 +33,7 @@ export class MapService {
dashBoardBikePoint: IDashboardCommonBikePoint; dashBoardBikePoint: IDashboardCommonBikePoint;
layerControl = L.control(null); layerControl = L.control(null);
legend = L.control({position: 'bottomleft'}); legend = L.control({position: 'bottomleft'});
accidentLegend = L.control({position: 'bottomleft'});
constructor( constructor(
private client: HttpClient, private client: HttpClient,
@ -66,6 +66,32 @@ export class MapService {
maxZoom: 19, maxZoom: 19,
preferCanvas: true preferCanvas: true
})); }));
this.accidentLegend.onAdd = () => {
const getCircle = (color) => {
return `
<svg height="16" width="16">
<circle cx="8" cy="8" r="8" stroke="black" stroke-width="1" fill=${color} />
</svg>`;
};
const div = L.DomUtil.create('div', 'legend legend-accidents');
div.innerHTML = `
<h4>Accident severities</h4>
<div>
${getCircle('yellow')}<span>Slight accident</span>
</div>
<div>
${getCircle('orange')}<span>Severe accident</span>
</div>
<div>
${getCircle('red')}<span>Fatal accident</span>
</div>
`;
return div;
};
this.map.on('overlayadd', e => e.name === 'Accidents' ? this.accidentLegend.addTo(this.map) : null);
this.map.on('overlayremove', e => e.name === 'Accidents' ? this.accidentLegend.remove() : null);
} }
public initDashboardMap(lat: number, lon: number, zoom: number): void { public initDashboardMap(lat: number, lon: number, zoom: number): void {

View File

@ -1,3 +1,4 @@
.button-wiki:hover { .button-wiki:hover {
background: #086ed2; background: #086ed2;
text-decoration: none;
} }

View File

@ -29,28 +29,48 @@ label.mat-checkbox-layout {
background: rgba(255, 255, 255, 0.8); background: rgba(255, 255, 255, 0.8);
line-height: 24px; line-height: 24px;
color: #555; color: #555;
}
.legend h4 { h4 {
text-align: center; text-align: center;
font-size: 16px; font-size: 16px;
margin: 2px 12px 8px; margin: 2px 12px 8px;
color: #777; color: #777;
}
span {
position: relative;
bottom: 3px;
}
i {
width: 18px;
height: 3px;
float: left;
margin: 7px 8px 0 0;
opacity: 0.7;
.icon {
background-size: 18px;
background-color: rgba(255, 255, 255, 1);
}
}
} }
.legend span { .legend-accidents {
position: relative; background: rgb(57, 57, 57);
bottom: 3px; color: white;
}
.legend i { h4 {
width: 18px; color: white;
height: 3px; }
float: left;
margin: 7px 8px 0 0;
opacity: 0.7;
}
.legend i.icon { div {
background-size: 18px; display: flex;
background-color: rgba(255, 255, 255, 1); justify-content: left;
align-items: baseline;
svg {
margin-right: 8px;
}
}
} }