Compare commits

...

1 Commits

Author SHA1 Message Date
d19a76939b wording and some layout 2020-06-24 23:28:13 +02:00
11 changed files with 199 additions and 91 deletions

View File

@ -1,5 +1,5 @@
<mat-chip-list> <mat-chip-list>
<!--suppress AngularInvalidExpressionResultType --> <!--suppress AngularInvalidExpressionResultType -->
<mat-chip (click)="onChipClick(tag)" *ngFor="let tag of availableTags" [color]="isSelected(tag) ? 'accent' : 'none'" <mat-chip (click)="onChipClick(tag)" *ngFor="let tag of availableTags" [color]="isSelected(tag) ? 'accent' : 'none'"
selected>{{tag}}</mat-chip> selected>{{tag|translate}}</mat-chip>
</mat-chip-list> </mat-chip-list>

View File

@ -1,6 +1,6 @@
<div class="region-mat-card"> <div class="region-mat-card">
<img alt="Picture of {{region.name}}" class="region-img" <img alt="Picture of {{region.name}}" class="region-img"
src="https://travopti.de/api/v1/regions/{{region.region_id}}/image"> src="https://travopti.de/api/v1/regions/{{region.region_id}}/image">
<div class="region-footer"> <div class="region-footer">
<div class="region-title"> <div class="region-title">
<span class="region-name">{{region.name}}</span> <span class="region-name">{{region.name}}</span>
@ -9,4 +9,4 @@
<app-bookmark-button [region]="region"></app-bookmark-button> <app-bookmark-button [region]="region"></app-bookmark-button>
<app-share-button [region]="region"></app-share-button> <app-share-button [region]="region"></app-share-button>
</div> </div>
</div> </div>

View File

@ -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>
@ -17,6 +17,7 @@
</div> </div>
<mat-divider *ngIf="result.scores.length > 0"></mat-divider> <mat-divider *ngIf="result.scores.length > 0"></mat-divider>
<div class="searched-params"> <div class="searched-params">
<div class="result-desc">Estimated values for your choosen travel dates</div>
<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>
@ -27,7 +28,7 @@
</td> </td>
<td> <td>
<div class="cell right"> <div class="cell right">
<span>{{score.value != undefined ? (score.value|number:'1.2-2') : 'N/A'}}</span> <span>{{score.value != undefined ? (score.value|number:PROPERTY_VIS_DEF[score.type].decimals) : 'N/A'}}</span>
</div> </div>
</td> </td>
<td> <td>
@ -44,4 +45,4 @@
</table> </table>
</div> </div>
</div> </div>
</div> </div>

View File

@ -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,32 +41,43 @@
} }
} }
> .result-details { >.result-details {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
> .total-accommodation { >.total-accommodation {
margin: 0.5rem 0; margin: 0.5rem 0;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
font-weight: bold; font-weight: bold;
> mat-icon { >mat-icon {
margin-right: 0.5rem; margin-right: 0.5rem;
margin-left: 3px; margin-left: 3px;
} }
} }
> .searched-params { >.searched-params {
margin: 0.5rem 0 margin: 0.5rem 0
} }
.undefined { .undefined {
color: #8f8f8f; color: #8f8f8f;
} }
.result-desc {
text-transform: uppercase;
font-size: small;
margin-right: 0.25rem;
margin-bottom: 0.5rem;
overflow-y: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
} }
} }
@ -84,7 +95,7 @@
margin-right: 1rem; margin-right: 1rem;
} }
> mat-icon { >mat-icon {
margin-right: 0.5rem; margin-right: 0.5rem;
} }
} }

View File

@ -10,7 +10,7 @@
<mat-vertical-stepper> <mat-vertical-stepper>
<!-- Date input --> <!-- Date input -->
<mat-step> <mat-step>
<ng-template matStepLabel>When is your trip?</ng-template> <ng-template matStepLabel>When do you want to travel?</ng-template>
<div class="vertical-wrap"> <div class="vertical-wrap">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Start</mat-label> <mat-label>Start</mat-label>
@ -24,7 +24,7 @@
</mat-step> </mat-step>
<!-- Multi presets --> <!-- Multi presets -->
<mat-step> <mat-step>
<ng-template matStepLabel>Which climate would you prefer?</ng-template> <ng-template matStepLabel>Which climate makes you feel comfortable?</ng-template>
<div *ngFor="let key of multiPresetsKeys" class="sub-group"> <div *ngFor="let key of multiPresetsKeys" class="sub-group">
<span class="label">{{key|translate}}:</span><br> <span class="label">{{key|translate}}:</span><br>
<mat-radio-group [ngModel]="multiPresetSelection[key]" [value]="undefined"> <mat-radio-group [ngModel]="multiPresetSelection[key]" [value]="undefined">
@ -89,14 +89,16 @@
</section> </section>
<!-- Climate Params --> <!-- Climate Params -->
<section class="group"> <section class="group">
<span class="title">Climate</span> <span class="title small-bottom-margin">Climate</span>
<app-toggle-slider [(model)]="temperatureMeanMax" [label]="'Max Temp'" [max]="45" [min]="0"></app-toggle-slider> <span class="desc">Choose your sweetspot</span>
<app-toggle-slider [(model)]="temperatureMeanMax" [label]="'Temperature'" [max]="45" [min]="0"></app-toggle-slider>
<app-toggle-slider [(model)]="precipitation" [label]="'Precipitation'" [max]="500" <app-toggle-slider [(model)]="precipitation" [label]="'Precipitation'" [max]="500"
[min]="0"></app-toggle-slider> [min]="0"></app-toggle-slider>
</section> </section>
<!-- Financial --> <!-- Financial -->
<section class="group"> <section class="group">
<span class="title">Fincancial</span> <span class="title small-bottom-margin">Fincancial</span>
<span class="desc">Choose your sweetspot</span>
<app-toggle-slider [(model)]="accommodation" [label]="'Accommodation'" [max]="60" [min]="0"></app-toggle-slider> <app-toggle-slider [(model)]="accommodation" [label]="'Accommodation'" [max]="60" [min]="0"></app-toggle-slider>
</section> </section>
</mat-tab> </mat-tab>

View File

@ -83,3 +83,10 @@
} }
} }
.desc {
font-size: smaller;
}
.small-bottom-margin {
margin-bottom: 4px !important;
}

View File

@ -1,7 +1,7 @@
import {Component, Input, OnInit} from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import {Region} from '../../interfaces/region.interface'; import { Region } from '../../interfaces/region.interface';
import {ShareDialogComponent} from '../../dialogs/share-dialog/share-dialog.component'; import { ShareDialogComponent } from '../../dialogs/share-dialog/share-dialog.component';
import {MatDialog} from '@angular/material'; import { MatDialog } from '@angular/material';
@Component({ @Component({
selector: 'app-share-button', selector: 'app-share-button',

View File

@ -1,6 +1,6 @@
<div #container *ngIf="region"> <div #container *ngIf="region">
<img alt="Picture of {{region.name}}" class="region-img" <img alt="Picture of {{region.name}}" class="region-img"
src="https://travopti.de/api/v1/regions/{{region.region_id}}/image"> src="https://travopti.de/api/v1/regions/{{region.region_id}}/image">
<div class="region-details-header"> <div class="region-details-header">
<div class="region-title"> <div class="region-title">
<span class="region-country">{{region.country}}</span> <span class="region-country">{{region.country}}</span>
@ -8,7 +8,7 @@
</div> </div>
<app-bookmark-button [region]="region"></app-bookmark-button> <app-bookmark-button [region]="region"></app-bookmark-button>
<a href="https://www.google.com/flights?q=flight+to+{{uriRegionName}}" mat-icon-button matTooltip="Search flights" <a href="https://www.google.com/flights?q=flight+to+{{uriRegionName}}" mat-icon-button matTooltip="Search flights"
rel="noopener" target="_blank"> rel="noopener" target="_blank">
<mat-icon>flight_takeoff</mat-icon> <mat-icon>flight_takeoff</mat-icon>
</a> </a>
<app-share-button [region]="region"></app-share-button> <app-share-button [region]="region"></app-share-button>
@ -16,7 +16,7 @@
<p *ngIf="region.description" class="region-decsription"> <p *ngIf="region.description" class="region-decsription">
<span>{{region.description.substr(0, DESC_CUT_POINT)}}</span> <span>{{region.description.substr(0, DESC_CUT_POINT)}}</span>
<span (click)="isDescExtended=true" *ngIf="!isDescExtended && region.description.length > DESC_CUT_POINT" <span (click)="isDescExtended=true" *ngIf="!isDescExtended && region.description.length > DESC_CUT_POINT"
class="more-btn"> ...more</span> class="more-btn"> ...more</span>
<span *ngIf="isDescExtended">{{region.description.substr(DESC_CUT_POINT)}}</span> <span *ngIf="isDescExtended">{{region.description.substr(DESC_CUT_POINT)}}</span>
</p> </p>
<div class="region-stats-group"> <div class="region-stats-group">
@ -27,40 +27,27 @@
<mat-tab-group> <mat-tab-group>
<mat-tab *ngIf="region.avg_price_relative" label="Price Deviation"> <mat-tab *ngIf="region.avg_price_relative" label="Price Deviation">
<ng-template matTabContent> <ng-template matTabContent>
<app-graph <app-graph [monthlyDatas]="[region.avg_price_relative]" class="graph" formatSting="##,##%" graphType="column">
[monthlyDatas]="[region.avg_price_relative]"
class="graph"
formatSting="##,##%"
graphType="column">
</app-graph> </app-graph>
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<mat-tab *ngIf="region.temperature_mean_max && region.temperature_mean_max[0]" label="Temperatures"> <mat-tab *ngIf="region.temperature_mean_max && region.temperature_mean_max[0]" label="Temperatures">
<ng-template matTabContent> <ng-template matTabContent>
<app-graph [colors]="['blue', 'red']" <app-graph [colors]="['blue', 'red']" [labels]="['Min', 'Max']"
[labels]="['Min', 'Max']" [monthlyDatas]="[region.temperature_mean_min, region.temperature_mean_max]" class="graph"
[monthlyDatas]="[region.temperature_mean_min, region.temperature_mean_max]" formatSting="##,##°C">
class="graph"
formatSting="##,##°C">
</app-graph> </app-graph>
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<mat-tab *ngIf="region.precipitation && region.precipitation[0]" label="Precipitation"> <mat-tab *ngIf="region.precipitation && region.precipitation[0]" label="Precipitation">
<ng-template matTabContent> <ng-template matTabContent>
<app-graph <app-graph [monthlyDatas]="[region.precipitation]" class="graph" formatSting="####mm">
[monthlyDatas]="[region.precipitation]"
class="graph"
formatSting="####mm">
</app-graph> </app-graph>
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<mat-tab *ngIf="region.rain_days && region.rain_days[0]" label="Rain days"> <mat-tab *ngIf="region.rain_days && region.rain_days[0]" label="Rain days">
<ng-template matTabContent> <ng-template matTabContent>
<app-graph <app-graph [monthlyDatas]="[region.rain_days]" class="graph" formatSting="####" graphType="column">
[monthlyDatas]="[region.rain_days]"
class="graph"
formatSting="####"
graphType="column">
</app-graph> </app-graph>
</ng-template> </ng-template>
</mat-tab> </mat-tab>
@ -76,4 +63,4 @@
<div *ngIf="!region" class="spinner"> <div *ngIf="!region" class="spinner">
<mat-spinner></mat-spinner> <mat-spinner></mat-spinner>
</div> </div>

View File

@ -53,8 +53,8 @@ export class SearchComponent implements OnInit {
this.sortOptions = [ this.sortOptions = [
{name: 'Relevance', property: 'score', descending: true}, {name: 'Relevance', property: 'score', descending: true},
{name: 'Name', property: 'name'}, {name: 'Region', property: 'name'},
{name: 'Accommodation', property: 'accommodation_costs'} {name: 'Accommodation price', property: 'accommodation_costs'}
]; ];
if (this.results.length > 0) { if (this.results.length > 0) {

View File

@ -1,9 +1,9 @@
import {Injectable} from '@angular/core'; import { Injectable } from '@angular/core';
import {HttpClient, HttpParams} from '@angular/common/http'; import { HttpClient, HttpParams } from '@angular/common/http';
import {Preset} from '../interfaces/preset.interface'; import { Preset } from '../interfaces/preset.interface';
import {Result} from '../interfaces/result.interface'; import { Result } from '../interfaces/result.interface';
import {Region} from '../interfaces/region.interface'; import { Region } from '../interfaces/region.interface';
import {Place} from '../interfaces/places.interface'; import { Place } from '../interfaces/places.interface';
/** /**
* The data service handles all API interactions. * The data service handles all API interactions.
@ -27,7 +27,7 @@ export class DataService {
public searchRegions(query: string): Promise<Result[]> { public searchRegions(query: string): Promise<Result[]> {
const params = new HttpParams().set('q', query); const params = new HttpParams().set('q', query);
return this.http.get<Result[]>(this.API_URL + '/search', {params}).toPromise(); return this.http.get<Result[]>(this.API_URL + '/search', { params }).toPromise();
} }
/** /**
@ -46,7 +46,7 @@ export class DataService {
if (max) { if (max) {
params = params.set('randomize', max.toString(10)); params = params.set('randomize', max.toString(10));
} }
const regions = await this.http.get<Region[]>(this.API_URL + '/regions', {params}).toPromise(); const regions = await this.http.get<Region[]>(this.API_URL + '/regions', { params }).toPromise();
regions.forEach(region => this.regionCache.set(region.region_id, region)); regions.forEach(region => this.regionCache.set(region.region_id, region));
@ -90,60 +90,144 @@ export class DataService {
export const REGION_PARAM_VIS: RegionParamVisLookup = { export const REGION_PARAM_VIS: RegionParamVisLookup = {
temperature_mean: { temperature_mean: {
icon: 'wb_sunny', icon: 'wb_sunny',
unit: '°C' unit: '°C',
decimals: '1.0-0'
}, },
temperature_mean_min: { temperature_mean_min: {
icon: 'wb_sunny', icon: 'wb_sunny',
unit: '°C' unit: '°C',
decimals: '1.0-0'
}, },
temperature_mean_max: { temperature_mean_max: {
icon: 'wb_sunny', icon: 'wb_sunny',
unit: '°C' unit: '°C',
decimals: '1.0-0'
}, },
precipitation: { precipitation: {
icon: 'opacity', icon: 'opacity',
unit: 'mm' unit: 'mm',
decimals: '1.0-0'
}, },
humidity: { humidity: {
icon: 'grain', icon: 'grain',
unit: '%' unit: '%',
decimals: '1.0-0'
}, },
sun_hours: { sun_hours: {
icon: 'flare', icon: 'flare',
unit: 'h' unit: 'h',
decimals: '1.0-0'
}, },
rain_days: { rain_days: {
icon: 'date_range', icon: 'date_range',
unit: '' unit: '',
decimals: '1.0-0'
}, },
food_costs: { food_costs: {
icon: 'local_dining', icon: 'local_dining',
unit: '€/day' unit: '€/day',
decimals: '1.0-0'
}, },
alcohol_costs: { alcohol_costs: {
icon: 'local_bar', icon: 'local_bar',
unit: '€/day' unit: '€/day',
decimals: '1.2-2'
}, },
water_costs: { water_costs: {
icon: 'local_cafe', icon: 'local_cafe',
unit: '€/day' unit: '€/day',
decimals: '1.2-2'
}, },
local_transportation_costs: { local_transportation_costs: {
icon: 'commute', icon: 'commute',
unit: '€/day' unit: '€/day',
decimals: '1.2-2'
}, },
entertainment_costs: { entertainment_costs: {
icon: 'local_activity', icon: 'local_activity',
unit: '€/day' unit: '€/day',
decimals: '1.2-2'
}, },
accommodation_costs: { accommodation_costs: {
icon: 'hotel', icon: 'hotel',
unit: '€/day' unit: '€/day',
decimals: '1.2-2'
}, },
average_per_day_costs: { average_per_day_costs: {
icon: 'euro', icon: 'euro',
unit: '€/day' unit: '€/day',
} decimals: '1.2-2'
},
avg_price_relative: {
icon: 'hotel',
unit: '%',
decimals: '1.1-1'
},
beach: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
history: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
nature: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
art: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
culture: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
mountains: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
architecture: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
rainforest: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
nightlife: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
desert: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
food: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
shopping: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
volcanoes: {
icon: 'stars',
unit: '%',
decimals: '1.0-0'
},
}; };
export interface RegionParamVisLookup { export interface RegionParamVisLookup {
@ -153,4 +237,5 @@ export interface RegionParamVisLookup {
export interface RegionParamVis { export interface RegionParamVis {
icon: string; icon: string;
unit: string; unit: string;
decimals: string;
} }

View File

@ -1,33 +1,48 @@
{ {
"temperature_mean_max": "Max Temperature Average", "temperature_mean_max": "Day temperature",
"temperature": "Temperature", "temperature": "Temperature",
"rain_days": "Rainy days", "rain_days": "Rain days",
"sun_hours": "Sunny hours", "sun_hours": "Sunshine",
"precipitation": "Precipitation", "precipitation": "Precipitation",
"humidity": "Humidity", "humidity": "Humidity",
"alcohol_costs": "Alcohol", "alcohol_costs": "Alcohol",
"food_costs": "Food", "food_costs": "Food",
"water_costs": "Water", "water_costs": "Water",
"cheap_alcohol": "Cheap alcohol", "cheap_alcohol": "cheap alcohol",
"local_transportation_costs": "Public transport", "local_transportation_costs": "Public transport",
"average_per_day_costs": "Total costs", "average_per_day_costs": "Total costs",
"entertainment_costs": "Entertainment", "entertainment_costs": "Entertainment",
"accommodation_costs": "Accommodation", "accommodation_costs": "Accommodation price",
"cheap_food": "Cheap food", "avg_price_relative": "Price tendency",
"cheap_water": "Cheap water", "cheap_food": "cheap food",
"cheap_transportations": "Cheap public transport", "cheap_water": "cheap water",
"cheap_entertainment": "Cheap entertainment", "cheap_transportations": "cheap public transport",
"cheap_accommodation": "Cheap accommodation", "cheap_entertainment": "cheap entertainment",
"off_season": "Off-season", "cheap_accommodation": "cheap accommodation",
"off_season": "low season",
"warm": "warm", "warm": "warm",
"chilly": "cold", "chilly": "cold",
"mild:": "mild", "mild:": "mild",
"cold": "freezing", "cold": "freezing",
"sunny": "sunny", "sunny": "all day long",
"dark": "dark", "dark": "the sun scares me",
"almost_no_rain": "almost none", "normal": "better not so much",
"little_rain": "little", "almost_no_rain": "no rain",
"floodlike_rain": "flooding", "little_rain": "a little is good",
"floodlike_rain": "i like it pouring",
"few_raindays": "few", "few_raindays": "few",
"many_raindays": "many" "many_raindays": "many",
"art": "Art",
"beach": "Beach",
"history": "History",
"nature": "Nature",
"culture": "Culture",
"mountains": "Mountains",
"architecture": "Architecture",
"rainforest": "Rainforest",
"desert": "Desert",
"food": "Food",
"shopping": "Shopping",
"volcanoes": "Volcanoes",
"nightlife": "Nightlife"
} }