Add details to search results
This commit is contained in:
parent
c8c0b7f900
commit
afb4234ce4
@ -27,6 +27,7 @@ import {RegionComponent} from './components/region/region.component';
|
||||
import {ResultComponent} from './components/result/result.component';
|
||||
import {RegionDetailsComponent} from './containers/region-details/region-details.component';
|
||||
import {GraphComponent} from './components/graph/graph.component';
|
||||
import {RegionStatsComponent} from './components/region-stats/region-stats.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@ -39,7 +40,8 @@ import {GraphComponent} from './components/graph/graph.component';
|
||||
RegionComponent,
|
||||
ResultComponent,
|
||||
RegionDetailsComponent,
|
||||
GraphComponent
|
||||
GraphComponent,
|
||||
RegionStatsComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
<table>
|
||||
<tr *ngFor="let prop of shownKeys">
|
||||
<td>
|
||||
<div class="cell space">
|
||||
<mat-icon>{{PROPERTY_VIS_DEF[prop].icon}}</mat-icon>
|
||||
<span>{{prop|translate}}:</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="cell right">
|
||||
<span>{{region[prop] ? (region[prop]|number:'1.2-2') : 'N/A'}}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="cell">
|
||||
<span>{{PROPERTY_VIS_DEF[prop].unit}}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -0,0 +1,18 @@
|
||||
.cell {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
align-self: center;
|
||||
|
||||
&.right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&.space {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
> mat-icon {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {RegionStatsComponent} from './region-stats.component';
|
||||
|
||||
describe('RegionStatsComponent', () => {
|
||||
let component: RegionStatsComponent;
|
||||
let fixture: ComponentFixture<RegionStatsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [RegionStatsComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RegionStatsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,27 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {Region} from '../../interfaces/region.interface';
|
||||
import {SearchParameter} from '../../interfaces/search-request.interface';
|
||||
import {REGION_PARAM_VIS} from '../../services/data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-region-stats',
|
||||
templateUrl: './region-stats.component.html',
|
||||
styleUrls: ['./region-stats.component.scss']
|
||||
})
|
||||
export class RegionStatsComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
region: Region;
|
||||
@Input()
|
||||
shownKeys: SearchParameter[];
|
||||
|
||||
/** Contains the visual definitions */
|
||||
readonly PROPERTY_VIS_DEF = REGION_PARAM_VIS;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@ -29,7 +29,7 @@
|
||||
|
||||
> .region-name {
|
||||
font-weight: bold;
|
||||
font-size: large;
|
||||
font-size: larger;
|
||||
align-self: center;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<div class="result-mat-card">
|
||||
<img class="result-img" src="https://travopti.de/api/v1/regions/{{result.region_id}}/image">
|
||||
<div class="result-footer">
|
||||
<div class="result-title">
|
||||
<div class="result-title">
|
||||
<div class="result-name">
|
||||
<span class="result-name">{{result.name}}</span>
|
||||
<span class="result-country">| {{result.country}}</span>
|
||||
</div>
|
||||
@ -12,4 +12,26 @@
|
||||
<mat-icon>share</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="result-details">
|
||||
<table>
|
||||
<tr *ngFor="let score of result.scores">
|
||||
<td>
|
||||
<div class="cell space">
|
||||
<mat-icon>{{PROPERTY_VIS_DEF[score.type].icon}}</mat-icon>
|
||||
<span>{{score.type|translate}}:</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="cell right">
|
||||
<span>{{score.value ? (score.value|number:'1.2-2') : 'N/A'}}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="cell">
|
||||
<span>{{PROPERTY_VIS_DEF[score.type].unit}}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -10,12 +10,13 @@
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
> .result-footer {
|
||||
> .result-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
> .result-title {
|
||||
> .result-name {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -29,12 +30,32 @@
|
||||
|
||||
> .result-name {
|
||||
font-weight: bold;
|
||||
font-size: large;
|
||||
font-size: larger;
|
||||
align-self: center;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
> .result-details {
|
||||
}
|
||||
}
|
||||
|
||||
.cell {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
align-self: center;
|
||||
|
||||
&.right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&.space {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
> mat-icon {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {Result} from '../../interfaces/result.interface';
|
||||
import {REGION_PARAM_VIS} from '../../services/data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-result',
|
||||
@ -11,6 +12,9 @@ export class ResultComponent implements OnInit {
|
||||
@Input()
|
||||
result: Result;
|
||||
|
||||
/** Contains the visual definitions */
|
||||
readonly PROPERTY_VIS_DEF = REGION_PARAM_VIS;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
|
||||
@ -19,35 +19,13 @@
|
||||
<span *ngIf="isDescExtended">{{region.description.substr(DESC_CUT_POINT)}}</span>
|
||||
</p>
|
||||
<div class="region-stats-group">
|
||||
<div>
|
||||
<table>
|
||||
<tr *ngFor="let prop of SHOWN_PROPS">
|
||||
<td>
|
||||
<div class="cell">
|
||||
<mat-icon>{{prop.icon}}</mat-icon>
|
||||
<span>{{prop.property|translate}}:</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="cell right">
|
||||
<span>{{region[prop.property].toFixed(2)}}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="cell">
|
||||
<span>{{prop.unit}}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<app-region-stats [region]="region" [shownKeys]="SHOWN_PROPS"></app-region-stats>
|
||||
</div>
|
||||
<div class="region-stats-group">
|
||||
<div *ngIf="region.temperature_mean_max && region.temperature_mean_max[0]" class="region-stats-group">
|
||||
<span class="group-title">Max Temperatures [°C]</span>
|
||||
<app-graph [monthlyData]="region.temperature_mean_max" class="graph"></app-graph>
|
||||
</div>
|
||||
<div class="region-stats-group">
|
||||
<div *ngIf="region.precipitation && region.precipitation[0]" class="region-stats-group">
|
||||
<span class="group-title">Precipitation [mm]</span>
|
||||
<app-graph [monthlyData]="region.precipitation" class="graph"></app-graph>
|
||||
</div>
|
||||
|
||||
@ -59,21 +59,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.cell {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
align-self: center;
|
||||
|
||||
&.right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
> mat-icon {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.spinner {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
|
||||
@ -3,12 +3,8 @@ import {Region} from '../../interfaces/region.interface';
|
||||
import {ActivatedRoute, ParamMap} from '@angular/router';
|
||||
import {DataService} from '../../services/data.service';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
import {SearchParameter} from '../../interfaces/search-request.interface';
|
||||
|
||||
interface VisualRegionPropDef {
|
||||
property: string;
|
||||
icon: string;
|
||||
unit: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-region-details',
|
||||
@ -20,32 +16,14 @@ export class RegionDetailsComponent implements OnInit {
|
||||
/** Cut descriptions after x chars */
|
||||
readonly DESC_CUT_POINT = 300;
|
||||
/** Region property to show in view */
|
||||
readonly SHOWN_PROPS: VisualRegionPropDef[] = [
|
||||
{
|
||||
property: 'average_per_day_costs',
|
||||
icon: 'euro',
|
||||
unit: '€/day',
|
||||
},
|
||||
{
|
||||
property: 'food_costs',
|
||||
icon: 'local_dining',
|
||||
unit: '€/day',
|
||||
},
|
||||
{
|
||||
property: 'alcohol_costs',
|
||||
icon: 'local_bar',
|
||||
unit: '€/day',
|
||||
},
|
||||
{
|
||||
property: 'local_transportation_costs',
|
||||
icon: 'commute',
|
||||
unit: '€/day',
|
||||
},
|
||||
{
|
||||
property: 'entertainment_costs',
|
||||
icon: 'local_activity',
|
||||
unit: '€/day',
|
||||
}
|
||||
readonly SHOWN_PROPS: SearchParameter[] = [
|
||||
SearchParameter.AVERAGE_PER_DAY_COSTS,
|
||||
SearchParameter.ACCOMMODATION_COSTS,
|
||||
SearchParameter.FOOD_COSTS,
|
||||
SearchParameter.WATER_COSTS,
|
||||
SearchParameter.ALCOHOL_COSTS,
|
||||
SearchParameter.LOCAL_TRANSPORTATION_COSTS,
|
||||
SearchParameter.ENTERTAINMENT_COSTS,
|
||||
];
|
||||
|
||||
/** Current region */
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
flex-direction: column;
|
||||
|
||||
> app-result {
|
||||
margin-bottom: 2rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,25 +1,32 @@
|
||||
export enum SearchParam {
|
||||
FROM = 'from',
|
||||
TO = 'to',
|
||||
TEMPERATURE = 'temperature',
|
||||
PRECIPITATION = 'precipitation',
|
||||
RAINDAYS = 'raindays',
|
||||
HUMIDITY = 'humidity',
|
||||
SUNHOURS = 'sunhours',
|
||||
ALCOHOL = 'alcohol',
|
||||
FOOD = 'food'
|
||||
}
|
||||
|
||||
export interface Query {
|
||||
from: number;
|
||||
to: number;
|
||||
price?: number[];
|
||||
exclude_region_ids?: number[];
|
||||
temperature?: number[];
|
||||
temperature_mean_max?: number[];
|
||||
precipitation?: number[];
|
||||
raindays?: number[];
|
||||
rain_days?: number[];
|
||||
humidity?: number[];
|
||||
sunhours?: number[];
|
||||
alcohol?: number[];
|
||||
food?: number[];
|
||||
sun_hours?: number[];
|
||||
alcohol_costs?: number[];
|
||||
food_costs?: number[];
|
||||
local_transportation_costs: number[];
|
||||
entertainment_costs: number[];
|
||||
accommodation_costs: number[];
|
||||
average_per_day_costs: number[];
|
||||
}
|
||||
|
||||
export enum SearchParameter {
|
||||
TEMP_MEAN = 'temperature_mean',
|
||||
TEMP_MEAN_MIN = 'temperature_mean_min',
|
||||
TEMP_MEAN_MAX = 'temperature_mean_max',
|
||||
PRECIPITATION = 'precipitation',
|
||||
HUMIDITY = 'humidity',
|
||||
SUN_HOURS = 'sun_hours',
|
||||
RAIN_DAYS = 'rain_days',
|
||||
FOOD_COSTS = 'food_costs',
|
||||
ALCOHOL_COSTS = 'alcohol_costs',
|
||||
WATER_COSTS = 'water_costs',
|
||||
LOCAL_TRANSPORTATION_COSTS = 'local_transportation_costs',
|
||||
ENTERTAINMENT_COSTS = 'entertainment_costs',
|
||||
ACCOMMODATION_COSTS = 'accommodation_costs',
|
||||
AVERAGE_PER_DAY_COSTS = 'average_per_day_costs'
|
||||
}
|
||||
|
||||
@ -17,29 +17,106 @@ export class DataService {
|
||||
public searchRegions(query: string): Promise<Result[]> {
|
||||
const params = new HttpParams().set('q', query);
|
||||
|
||||
console.log(params);
|
||||
|
||||
return this.http.get<Result[]>(this.API_URL + '/search', {params}).toPromise();
|
||||
// return new Promise<Result[]>(resolve => {
|
||||
// resolve(MOCK_RESULT);
|
||||
// setTimeout(() => {
|
||||
// resolve(MOCK_RESULT);
|
||||
// }, 100);
|
||||
// });
|
||||
}
|
||||
|
||||
public getAllPresets(): Promise<Preset[]> {
|
||||
return this.http.get<Preset[]>(this.API_URL + '/search/presets').toPromise();
|
||||
// return new Promise<Preset[]>(resolve => {
|
||||
// resolve(MOCK_PRESETS);
|
||||
// setTimeout(() => {
|
||||
// resolve(MOCK_PRESETS);
|
||||
// }, 100);
|
||||
// });
|
||||
}
|
||||
|
||||
public getAllRegions(): Promise<Region[]> {
|
||||
return this.http.get<Region[]>(this.API_URL + '/regions').toPromise();
|
||||
// return new Promise<Region[]>(resolve => {
|
||||
// resolve(MOCK_REGIONS);
|
||||
// setTimeout(() => {
|
||||
// resolve(MOCK_REGIONS);
|
||||
// }, 100);
|
||||
// });
|
||||
}
|
||||
|
||||
public getRegion(id: number): Promise<Region> {
|
||||
return this.http.get<Region>(`${this.API_URL}/regions/${id}`).toPromise();
|
||||
// return new Promise<Region>(resolve => {
|
||||
// setTimeout(() => {
|
||||
// resolve(MOCK_REGIONS.find(region => region.region_id === id));
|
||||
// }, 100);
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
export const REGION_PARAM_VIS: RegionParamVisLookup = {
|
||||
temperature_mean: {
|
||||
icon: 'wb_sunny',
|
||||
unit: '°C'
|
||||
},
|
||||
temperature_mean_min: {
|
||||
icon: 'wb_sunny',
|
||||
unit: '°C'
|
||||
},
|
||||
temperature_mean_max: {
|
||||
icon: 'wb_sunny',
|
||||
unit: '°C'
|
||||
},
|
||||
precipitation: {
|
||||
icon: 'opacity',
|
||||
unit: 'mm'
|
||||
},
|
||||
humidity: {
|
||||
icon: 'grain',
|
||||
unit: '%'
|
||||
},
|
||||
sun_hours: {
|
||||
icon: 'flare',
|
||||
unit: 'h'
|
||||
},
|
||||
rain_days: {
|
||||
icon: 'date_range',
|
||||
unit: ''
|
||||
},
|
||||
food_costs: {
|
||||
icon: 'local_dining',
|
||||
unit: '€/day'
|
||||
},
|
||||
alcohol_costs: {
|
||||
icon: 'local_bar',
|
||||
unit: '€/day'
|
||||
},
|
||||
water_costs: {
|
||||
icon: 'local_cafe',
|
||||
unit: '€/day'
|
||||
},
|
||||
local_transportation_costs: {
|
||||
icon: 'commute',
|
||||
unit: '€/day'
|
||||
},
|
||||
entertainment_costs: {
|
||||
icon: 'local_activity',
|
||||
unit: '€/day'
|
||||
},
|
||||
accommodation_costs: {
|
||||
icon: 'hotel',
|
||||
unit: '€/day'
|
||||
},
|
||||
average_per_day_costs: {
|
||||
icon: 'euro',
|
||||
unit: '€/day'
|
||||
}
|
||||
};
|
||||
|
||||
export interface RegionParamVisLookup {
|
||||
[key: string]: RegionParamVis;
|
||||
}
|
||||
|
||||
export interface RegionParamVis {
|
||||
icon: string;
|
||||
unit: string;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"temperature_mean_max": "Temperature Average",
|
||||
"temperature_mean_max": "Max Temperature Average",
|
||||
"temperature": "Temperature",
|
||||
"rain_days": "Rainy days",
|
||||
"sun_hours": "Sunny hours",
|
||||
@ -7,10 +7,12 @@
|
||||
"humidity": "Humidity",
|
||||
"alcohol_costs": "Alcohol costs",
|
||||
"food_costs": "Food costs",
|
||||
"water_costs": "Water costs",
|
||||
"cheap_alcohol": "Cheap alcohol",
|
||||
"local_transportation_costs": "Public transport",
|
||||
"average_per_day_costs": "Average total costs",
|
||||
"entertainment_costs": "Entertainment costs",
|
||||
"accommodation_costs": "Accommodation costs",
|
||||
"cheap_food": "Cheap food",
|
||||
"cheap_water": "Cheap water",
|
||||
"cheap_transportations": "Cheap public transport",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user