Merge branch 'feature/total-price-details' into 'develop'
Add total costs to results See merge request tjohn/cc-data!32
This commit is contained in:
commit
218b508f43
@ -1,6 +1,6 @@
|
|||||||
<div class="result-mat-card">
|
<div class="result-mat-card">
|
||||||
<img alt="Picture of {{result.name}}" class="result-img"
|
<img alt="Picture of {{result.name}}" class="result-img"
|
||||||
src="https://travopti.de/api/v1/regions/{{result.region_id}}/image">
|
src="https://travopti.de/api/v1/regions/{{result.region_id}}/image">
|
||||||
<div class="result-title">
|
<div class="result-title">
|
||||||
<div class="result-header">
|
<div class="result-header">
|
||||||
<span class="result-name">{{result.name}}<span *ngIf="debug"> ({{result.score}})</span></span>
|
<span class="result-name">{{result.name}}<span *ngIf="debug"> ({{result.score}})</span></span>
|
||||||
@ -10,14 +10,27 @@
|
|||||||
<app-share-button [region]="result"></app-share-button>
|
<app-share-button [region]="result"></app-share-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="result-details">
|
<div class="result-details">
|
||||||
<mat-divider *ngIf="result.total_accommodation_costs"></mat-divider>
|
<mat-divider *ngIf="totalCosts"></mat-divider>
|
||||||
<div *ngIf="result.total_accommodation_costs" class="total-accommodation">
|
<div *ngIf="totalCosts" class="total-price-container">
|
||||||
<mat-icon>euro</mat-icon>
|
<div matTooltip="Total">
|
||||||
<span matTooltip="Total accommodation">{{result.total_accommodation_costs|number:'1.2-2'}}€</span>
|
<mat-icon>euro</mat-icon>
|
||||||
|
<span>{{totalCosts|number:'1.0-0'}}€</span>
|
||||||
|
</div>
|
||||||
|
<div matTooltip="Accommodation">
|
||||||
|
<mat-icon>hotel</mat-icon>
|
||||||
|
<span>{{totalAccommodation|number:'1.0-0'}}€</span>
|
||||||
|
</div>
|
||||||
|
<div matTooltip="Lifestyle">
|
||||||
|
<mat-icon>people</mat-icon>
|
||||||
|
<span>{{totalLifeStyle|number:'1.0-0'}}€</span>
|
||||||
|
</div>
|
||||||
|
<div (click)="onTravelCostRequest($event)" matTooltip="Travel (request)">
|
||||||
|
<mat-icon>commute</mat-icon>
|
||||||
|
<span>- - -</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider *ngIf="result.scores.length > 0"></mat-divider>
|
<mat-divider *ngIf="result.scores.length > 0"></mat-divider>
|
||||||
<div *ngIf="result.scores.length > 0" class="searched-params">
|
<div *ngIf="result.scores.length > 0" class="searched-params">
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr *ngFor="let score of result.scores" [ngClass]="{'undefined': score.value == undefined}">
|
<tr *ngFor="let score of result.scores" [ngClass]="{'undefined': score.value == undefined}">
|
||||||
<td>
|
<td>
|
||||||
@ -45,6 +58,6 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<div class="result-desc">Estimated values for your chosen travel dates</div>
|
<div class="result-desc">Estimated values for {{duration}} {{duration > 1 ? 'days' : 'day'}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,33 +4,33 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
>.result-img {
|
> .result-img {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 15rem;
|
height: 15rem;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
>.result-title {
|
> .result-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
|
|
||||||
>.result-header {
|
> .result-header {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
margin: 0.25rem 0;
|
margin: 0.25rem 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
>.result-name {
|
> .result-name {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: larger;
|
font-size: larger;
|
||||||
margin-right: 0.25rem;
|
margin-right: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
>.result-country {
|
> .result-country {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-size: small;
|
font-size: small;
|
||||||
margin-right: 0.25rem;
|
margin-right: 0.25rem;
|
||||||
@ -41,26 +41,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>.result-details {
|
> .result-details {
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
>.total-accommodation {
|
> .total-price-container {
|
||||||
margin: 0.5rem 0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
margin: 0.5rem 0;
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
>mat-icon {
|
> div {
|
||||||
margin-right: 0.5rem;
|
display: flex;
|
||||||
margin-left: 3px;
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 1rem;
|
||||||
|
|
||||||
|
> mat-icon {
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
>.searched-params {
|
> .searched-params {
|
||||||
margin: 0.5rem 0
|
margin: 0.5rem 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +99,7 @@
|
|||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
>mat-icon {
|
> mat-icon {
|
||||||
margin-right: 0.5rem;
|
margin-right: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Component, Input, OnInit} from '@angular/core';
|
import {Component, Input, OnChanges, OnInit, SimpleChanges} from '@angular/core';
|
||||||
import {Result} from '../../interfaces/result.interface';
|
import {Result} from '../../interfaces/result.interface';
|
||||||
import {REGION_PARAM_VIS} from '../../services/data.service';
|
import {REGION_PARAM_VIS} from '../../services/data.service';
|
||||||
|
|
||||||
@ -7,14 +7,23 @@ import {REGION_PARAM_VIS} from '../../services/data.service';
|
|||||||
templateUrl: './result.component.html',
|
templateUrl: './result.component.html',
|
||||||
styleUrls: ['./result.component.scss']
|
styleUrls: ['./result.component.scss']
|
||||||
})
|
})
|
||||||
export class ResultComponent implements OnInit {
|
export class ResultComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
result: Result;
|
result: Result;
|
||||||
|
|
||||||
|
/** Date difference in days */
|
||||||
|
@Input()
|
||||||
|
duration: number;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
debug = false;
|
debug = false;
|
||||||
|
|
||||||
|
totalCosts: number;
|
||||||
|
totalAccommodation: number;
|
||||||
|
totalLifeStyle: number;
|
||||||
|
totalTravel: number;
|
||||||
|
|
||||||
/** Contains the visual definitions */
|
/** Contains the visual definitions */
|
||||||
readonly PROPERTY_VIS_DEF = REGION_PARAM_VIS;
|
readonly PROPERTY_VIS_DEF = REGION_PARAM_VIS;
|
||||||
|
|
||||||
@ -24,4 +33,25 @@ export class ResultComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes.result || changes.duration) {
|
||||||
|
this.calculateTotalPrices();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTravelCostRequest(event: MouseEvent) {
|
||||||
|
event.stopPropagation();
|
||||||
|
window.open(`https://www.google.com/flights?q=flight+to+${encodeURI(this.result.name)}`, '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateTotalPrices() {
|
||||||
|
// Guard: undefined values
|
||||||
|
if (!this.result || !this.duration) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.totalCosts = Math.round(this.result.average_per_day_costs * this.duration);
|
||||||
|
this.totalAccommodation = Math.round(this.result.accommodation_costs * this.duration);
|
||||||
|
this.totalLifeStyle = this.totalCosts - this.totalAccommodation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="result-container">
|
<div class="result-container">
|
||||||
<app-result (click)="onResultClick(result)" *ngFor="let result of results" [debug]="debug"
|
<app-result (click)="onResultClick(result)" *ngFor="let result of results" [debug]="debug"
|
||||||
[result]="result"></app-result>
|
[duration]="duration" [result]="result"></app-result>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
|
|||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {Result} from '../../interfaces/result.interface';
|
import {Result} from '../../interfaces/result.interface';
|
||||||
import {SearchService} from '../../services/search.service';
|
import {SearchService} from '../../services/search.service';
|
||||||
|
import {base64ToObj} from '../../utils/base64conversion';
|
||||||
|
import {Query} from '../../interfaces/search-request.interface';
|
||||||
|
|
||||||
interface SortOption {
|
interface SortOption {
|
||||||
name: string;
|
name: string;
|
||||||
@ -28,6 +30,8 @@ export class SearchComponent implements OnInit {
|
|||||||
sortDes = true;
|
sortDes = true;
|
||||||
/** Available sort options */
|
/** Available sort options */
|
||||||
sortOptions: SortOption[] = [];
|
sortOptions: SortOption[] = [];
|
||||||
|
/** The difference between from and to in days */
|
||||||
|
duration: number;
|
||||||
|
|
||||||
debug = false;
|
debug = false;
|
||||||
|
|
||||||
@ -54,7 +58,7 @@ export class SearchComponent implements OnInit {
|
|||||||
this.sortOptions = [
|
this.sortOptions = [
|
||||||
{name: 'Relevance', property: 'score', descending: true},
|
{name: 'Relevance', property: 'score', descending: true},
|
||||||
{name: 'Region name', property: 'name'},
|
{name: 'Region name', property: 'name'},
|
||||||
{name: 'Accommodation price', property: 'accommodation_costs'}
|
{name: 'Price', property: 'average_per_day_costs'}
|
||||||
];
|
];
|
||||||
|
|
||||||
if (this.results.length > 0) {
|
if (this.results.length > 0) {
|
||||||
@ -65,6 +69,10 @@ export class SearchComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate duration
|
||||||
|
const {from, to} = base64ToObj(this.queryString) as Query;
|
||||||
|
const difference: number = (new Date(to)).getTime() - (new Date(from)).getTime();
|
||||||
|
this.duration = Math.round(difference / 1000 / 60 / 60 / 24);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user