Merge branch 'develop' of https://git.it.hs-heilbronn.de/tjohn/cc-data into feature/flights
This commit is contained in:
commit
18463c3ca2
5
frontend/package-lock.json
generated
5
frontend/package-lock.json
generated
@ -5338,6 +5338,11 @@
|
|||||||
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
|
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"hammerjs": {
|
||||||
|
"version": "2.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
||||||
|
"integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
|
||||||
|
},
|
||||||
"handle-thing": {
|
"handle-thing": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
"@angular/platform-browser-dynamic": "~8.2.14",
|
"@angular/platform-browser-dynamic": "~8.2.14",
|
||||||
"@angular/router": "~8.2.14",
|
"@angular/router": "~8.2.14",
|
||||||
"@ngx-translate/core": "^12.1.2",
|
"@ngx-translate/core": "^12.1.2",
|
||||||
|
"hammerjs": "^2.0.8",
|
||||||
"ngx-device-detector": "^1.4.5",
|
"ngx-device-detector": "^1.4.5",
|
||||||
"rxjs": "~6.4.0",
|
"rxjs": "~6.4.0",
|
||||||
"tslib": "^1.10.0",
|
"tslib": "^1.10.0",
|
||||||
|
|||||||
@ -22,10 +22,15 @@ import {TranslateModule, TranslateService} from '@ngx-translate/core';
|
|||||||
import * as enLang from '../assets/i18n/en.json';
|
import * as enLang from '../assets/i18n/en.json';
|
||||||
import {HttpClientModule} from '@angular/common/http';
|
import {HttpClientModule} from '@angular/common/http';
|
||||||
import {
|
import {
|
||||||
|
MatBadgeModule,
|
||||||
MatButtonToggleModule,
|
MatButtonToggleModule,
|
||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
MatDividerModule,
|
MatDividerModule,
|
||||||
|
MatRadioModule,
|
||||||
|
MatSliderModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatStepperModule,
|
||||||
MatTabsModule,
|
MatTabsModule,
|
||||||
MatTooltipModule
|
MatTooltipModule
|
||||||
} from '@angular/material';
|
} from '@angular/material';
|
||||||
@ -41,6 +46,7 @@ import {ShareButtonComponent} from './components/share-button/share-button.compo
|
|||||||
import {ShareDialogComponent} from './dialogs/share-dialog/share-dialog.component';
|
import {ShareDialogComponent} from './dialogs/share-dialog/share-dialog.component';
|
||||||
import {TeamComponent} from './containers/team/team.component';
|
import {TeamComponent} from './containers/team/team.component';
|
||||||
import {DeviceDetectorModule} from 'ngx-device-detector';
|
import {DeviceDetectorModule} from 'ngx-device-detector';
|
||||||
|
import {ToggleSliderComponent} from './components/toggle-slider/toggle-slider.component';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -59,7 +65,8 @@ import {DeviceDetectorModule} from 'ngx-device-detector';
|
|||||||
BookmarkListComponent,
|
BookmarkListComponent,
|
||||||
ShareButtonComponent,
|
ShareButtonComponent,
|
||||||
ShareDialogComponent,
|
ShareDialogComponent,
|
||||||
TeamComponent
|
TeamComponent,
|
||||||
|
ToggleSliderComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -83,7 +90,12 @@ import {DeviceDetectorModule} from 'ngx-device-detector';
|
|||||||
MatTooltipModule,
|
MatTooltipModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
DeviceDetectorModule,
|
DeviceDetectorModule,
|
||||||
MatTabsModule
|
MatTabsModule,
|
||||||
|
MatBadgeModule,
|
||||||
|
MatStepperModule,
|
||||||
|
MatRadioModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatSliderModule
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
<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>
|
||||||
<div class="cell space">
|
<div class="cell space">
|
||||||
<mat-icon>{{PROPERTY_VIS_DEF[score.type].icon}}</mat-icon>
|
<mat-icon>{{PROPERTY_VIS_DEF[score.type] ? PROPERTY_VIS_DEF[score.type].icon : 'bar_chart'}}</mat-icon>
|
||||||
<span>{{score.type|translate}}:</span>
|
<span>{{score.type|translate}}:</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -25,7 +25,12 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<span>{{PROPERTY_VIS_DEF[score.type].unit}}</span>
|
<span>{{PROPERTY_VIS_DEF[score.type] ? PROPERTY_VIS_DEF[score.type].unit : ''}}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="cell">
|
||||||
|
<span>({{score.score}})</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -1,42 +1,106 @@
|
|||||||
<mat-card class="search-container">
|
<mat-card class="search-container">
|
||||||
<section class="group">
|
|
||||||
<h2>When is your trip?</h2>
|
|
||||||
<mat-form-field appearance="outline">
|
|
||||||
<mat-label>Start</mat-label>
|
|
||||||
<input (change)="checkDates()" [(ngModel)]="from" [min]="today" matInput required type="date">
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-form-field appearance="outline">
|
|
||||||
<mat-label>End</mat-label>
|
|
||||||
<input (change)="checkDates()" [(ngModel)]="to" [min]="from" matInput required type="date">
|
|
||||||
</mat-form-field>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
<span matCardTitle>Search</span>
|
||||||
<h2>What would you prefer?</h2>
|
|
||||||
<div *ngFor="let key of multiPresetsKeys" class="sub-group">
|
|
||||||
<span class="label">{{key|translate}}:</span><br>
|
|
||||||
<mat-button-toggle-group [ngModel]="multiPresetSelection[key]" [value]="undefined">
|
|
||||||
<mat-button-toggle
|
|
||||||
#btn
|
|
||||||
(click)="btn.checked = onMultiPresetSelect(preset)"
|
|
||||||
*ngFor="let preset of multiPresets.get(key)"
|
|
||||||
[value]="preset.preset_id"
|
|
||||||
>
|
|
||||||
{{preset.tag_label|translate}}
|
|
||||||
</mat-button-toggle>
|
|
||||||
</mat-button-toggle-group>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
<mat-tab-group #tabGroup [animationDuration]="'0'" [selectedIndex]="selectedTab">
|
||||||
<h2>Whats most important to you?</h2>
|
|
||||||
<div class="vertical">
|
|
||||||
<mat-checkbox *ngFor="let preset of singlePresets"
|
|
||||||
[(ngModel)]="singlePresetSelection[preset.preset_id]">{{preset.tag_label|translate}}</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<button (click)="onSearch()" [disabled]="!from || !to" class="search-btn" color="primary" mat-flat-button>Search
|
<!-- Guided Search Tab -->
|
||||||
|
<mat-tab label="Guided">
|
||||||
|
|
||||||
|
<mat-vertical-stepper>
|
||||||
|
|
||||||
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>When is your trip?</ng-template>
|
||||||
|
<div class="vertical-wrap">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Start</mat-label>
|
||||||
|
<input (change)="checkDates()" [(ngModel)]="from" [min]="today" matInput required type="date">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>End</mat-label>
|
||||||
|
<input (change)="checkDates()" [(ngModel)]="to" [min]="from" matInput required type="date">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>Which climate would you prefer?</ng-template>
|
||||||
|
<div *ngFor="let key of multiPresetsKeys" class="sub-group">
|
||||||
|
<span class="label">{{key|translate}}:</span><br>
|
||||||
|
<mat-radio-group [ngModel]="multiPresetSelection[key]" [value]="undefined">
|
||||||
|
<mat-radio-button
|
||||||
|
#btn
|
||||||
|
(click)="btn.checked = onMultiPresetSelect(preset)"
|
||||||
|
*ngFor="let preset of multiPresets.get(key)"
|
||||||
|
[value]="preset.preset_id"
|
||||||
|
>{{preset.tag_label|translate}}</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>What else is important to you?</ng-template>
|
||||||
|
<div class="vertical">
|
||||||
|
<mat-checkbox *ngFor="let preset of singlePresets"
|
||||||
|
[(ngModel)]="singlePresetSelection[preset.preset_id]">{{preset.tag_label|translate}}</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
</mat-vertical-stepper>
|
||||||
|
</mat-tab>
|
||||||
|
|
||||||
|
<!-- Advanced Search Tab -->
|
||||||
|
<mat-tab label="Advanced">
|
||||||
|
<!-- Date -->
|
||||||
|
<section class="group">
|
||||||
|
<span class="title">Date</span>
|
||||||
|
<div class=" content vertical-wrap">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Start</mat-label>
|
||||||
|
<input (change)="checkDates()" [(ngModel)]="from" [min]="today" matInput required type="date">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>End</mat-label>
|
||||||
|
<input (change)="checkDates()" [(ngModel)]="to" [min]="from" matInput required type="date">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!-- Text Filter -->
|
||||||
|
<section class="group">
|
||||||
|
<span class="title">Text</span>
|
||||||
|
<div class="content vertical-wrap">
|
||||||
|
<mat-form-field class="text-input">
|
||||||
|
<mat-label>Text search</mat-label>
|
||||||
|
<input [(ngModel)]="textFilter" matInput>
|
||||||
|
<button (click)="textFilter = ''" *ngIf="textFilter.length" mat-icon-button matSuffix>
|
||||||
|
<mat-icon>clear</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-form-field>
|
||||||
|
<div class="horizontal space">
|
||||||
|
<span>Search in description </span>
|
||||||
|
<mat-slide-toggle [(ngModel)]="fullText"></mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!-- Climate Params -->
|
||||||
|
<section class="group">
|
||||||
|
<span class="title">Climate</span>
|
||||||
|
<app-toggle-slider [(model)]="temperatureMeanMax" [label]="'Max Temp'" [max]="45" [min]="0"></app-toggle-slider>
|
||||||
|
<app-toggle-slider [(model)]="precipitation" [label]="'Precipitation'" [max]="500"
|
||||||
|
[min]="0"></app-toggle-slider>
|
||||||
|
</section>
|
||||||
|
<!-- Financial -->
|
||||||
|
<section class="group">
|
||||||
|
<span class="title">Fincancial</span>
|
||||||
|
<app-toggle-slider [(model)]="accommodation" [label]="'Accommodation'" [max]="60" [min]="0"></app-toggle-slider>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</mat-tab>
|
||||||
|
|
||||||
|
</mat-tab-group>
|
||||||
|
|
||||||
|
<button (click)="onSearch(tabGroup.selectedIndex === 1)" [disabled]="!from || !to" class="search-btn" color="primary"
|
||||||
|
mat-flat-button>Search
|
||||||
<mat-icon matSuffix>search</mat-icon>
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|||||||
@ -2,27 +2,72 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
> .group {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .search-btn {
|
> .search-btn {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub-group {
|
.sub-group {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 2rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
> .label {
|
.label {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mat-radio-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-radio-button {
|
||||||
|
margin: 0 1rem 0.5rem 0;
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 1rem 0;
|
||||||
|
|
||||||
|
> .title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .content {
|
||||||
|
margin: 0 2.5rem;
|
||||||
|
|
||||||
|
.text-input {
|
||||||
|
min-width: 14rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.space {
|
||||||
|
> * {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.vertical {
|
.vertical {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vertical-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,9 @@ import {SearchService} from '../../services/search.service';
|
|||||||
})
|
})
|
||||||
export class SearchInputComponent implements OnInit {
|
export class SearchInputComponent implements OnInit {
|
||||||
|
|
||||||
|
|
||||||
|
selectedTab = 0;
|
||||||
|
|
||||||
presets: Preset[];
|
presets: Preset[];
|
||||||
singlePresets: Preset[];
|
singlePresets: Preset[];
|
||||||
multiPresets: Map<string, Preset[]>;
|
multiPresets: Map<string, Preset[]>;
|
||||||
@ -21,9 +24,18 @@ export class SearchInputComponent implements OnInit {
|
|||||||
|
|
||||||
from: string;
|
from: string;
|
||||||
to: string;
|
to: string;
|
||||||
|
|
||||||
|
// Guided Search
|
||||||
singlePresetSelection = {};
|
singlePresetSelection = {};
|
||||||
multiPresetSelection = {};
|
multiPresetSelection = {};
|
||||||
|
|
||||||
|
// Advanced Search
|
||||||
|
textFilter = '';
|
||||||
|
fullText = false;
|
||||||
|
temperatureMeanMax: number;
|
||||||
|
precipitation: number;
|
||||||
|
accommodation: number;
|
||||||
|
|
||||||
readonly today = this.from = formatDate(new Date(), 'yyyy-MM-dd', 'en-GB');
|
readonly today = this.from = formatDate(new Date(), 'yyyy-MM-dd', 'en-GB');
|
||||||
|
|
||||||
constructor(private router: Router, private ps: PresetService, private ss: SearchService) {
|
constructor(private router: Router, private ps: PresetService, private ss: SearchService) {
|
||||||
@ -43,41 +55,13 @@ export class SearchInputComponent implements OnInit {
|
|||||||
this.multiPresets = this.ps.multiPresets;
|
this.multiPresets = this.ps.multiPresets;
|
||||||
this.multiPresetsKeys = [...this.multiPresets.keys()];
|
this.multiPresetsKeys = [...this.multiPresets.keys()];
|
||||||
|
|
||||||
const prevInput = this.ss.loadSearchInput();
|
this.loadSearch();
|
||||||
|
|
||||||
if (prevInput) {
|
|
||||||
this.from = prevInput.from;
|
|
||||||
this.to = prevInput.to;
|
|
||||||
this.singlePresetSelection = prevInput.singlePresetSelection;
|
|
||||||
this.multiPresetSelection = prevInput.multiPresetSelection;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onSearch() {
|
async onSearch(isAdvanced: boolean) {
|
||||||
const query: Query = {
|
this.saveSearch(isAdvanced);
|
||||||
from: new Date(this.from).getTime(),
|
|
||||||
to: new Date(this.to).getTime(),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const preset of this.singlePresets) {
|
|
||||||
if (this.singlePresetSelection[preset.preset_id]) {
|
|
||||||
query[preset.parameter] = preset.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of this.multiPresetsKeys) {
|
|
||||||
if (this.multiPresetSelection[key]) {
|
|
||||||
query[key] = this.presets.find(preset => preset.preset_id === this.multiPresetSelection[key]).value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ss.saveSearchInput({
|
|
||||||
from: this.from,
|
|
||||||
to: this.to,
|
|
||||||
singlePresetSelection: this.singlePresetSelection,
|
|
||||||
multiPresetSelection: this.multiPresetSelection
|
|
||||||
});
|
|
||||||
|
|
||||||
|
const query = isAdvanced ? this.getQueryFromAdvanced() : this.getQueryFromGuided();
|
||||||
|
|
||||||
await this.router.navigate(['/search'], {queryParams: {q: objToBase64(query)}});
|
await this.router.navigate(['/search'], {queryParams: {q: objToBase64(query)}});
|
||||||
}
|
}
|
||||||
@ -107,4 +91,76 @@ export class SearchInputComponent implements OnInit {
|
|||||||
this.to = formatDate(newToDate, 'yyyy-MM-dd', 'en-GB');
|
this.to = formatDate(newToDate, 'yyyy-MM-dd', 'en-GB');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getQueryFromGuided(): Query {
|
||||||
|
const query: Query = {
|
||||||
|
from: new Date(this.from).getTime(),
|
||||||
|
to: new Date(this.to).getTime(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const preset of this.singlePresets) {
|
||||||
|
if (this.singlePresetSelection[preset.preset_id]) {
|
||||||
|
query[preset.parameter] = preset.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of this.multiPresetsKeys) {
|
||||||
|
if (this.multiPresetSelection[key]) {
|
||||||
|
query[key] = this.presets.find(preset => preset.preset_id === this.multiPresetSelection[key]).value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getQueryFromAdvanced(): Query {
|
||||||
|
const query: Query = {
|
||||||
|
from: new Date(this.from).getTime(),
|
||||||
|
to: new Date(this.to).getTime(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.textFilter.length > 0) {
|
||||||
|
query.fulltext = this.fullText;
|
||||||
|
query.textfilter = this.textFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
query.temperature_mean_max = this.temperatureMeanMax ? [this.temperatureMeanMax, this.temperatureMeanMax] : undefined;
|
||||||
|
query.precipitation = this.precipitation ? [this.precipitation, this.precipitation] : undefined;
|
||||||
|
query.accommodation_costs = this.accommodation ? [this.accommodation, this.accommodation] : undefined;
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
private saveSearch(isAdvanced: boolean) {
|
||||||
|
this.ss.saveSearchInput({
|
||||||
|
wasAdvanced: isAdvanced,
|
||||||
|
from: this.from,
|
||||||
|
to: this.to,
|
||||||
|
singlePresetSelection: this.singlePresetSelection,
|
||||||
|
multiPresetSelection: this.multiPresetSelection,
|
||||||
|
fullText: this.fullText,
|
||||||
|
textFiler: this.textFilter,
|
||||||
|
tempMeanMax: this.temperatureMeanMax,
|
||||||
|
precipitation: this.precipitation,
|
||||||
|
accommodation: this.accommodation,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadSearch() {
|
||||||
|
const prevInput = this.ss.loadSearchInput();
|
||||||
|
|
||||||
|
if (prevInput) {
|
||||||
|
this.from = prevInput.from;
|
||||||
|
this.to = prevInput.to;
|
||||||
|
this.singlePresetSelection = prevInput.singlePresetSelection;
|
||||||
|
this.multiPresetSelection = prevInput.multiPresetSelection;
|
||||||
|
this.textFilter = prevInput.textFiler;
|
||||||
|
this.fullText = prevInput.fullText;
|
||||||
|
this.selectedTab = prevInput.wasAdvanced ? 1 : 0;
|
||||||
|
this.temperatureMeanMax = prevInput.tempMeanMax;
|
||||||
|
this.precipitation = prevInput.precipitation;
|
||||||
|
this.accommodation = prevInput.accommodation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
<span>{{label}}</span>
|
||||||
|
<mat-slide-toggle (change)="onSlideToggleChange($event)" [(ngModel)]="enabled"></mat-slide-toggle>
|
||||||
|
<mat-slider
|
||||||
|
(click)="enabled=true"
|
||||||
|
[(value)]="value"
|
||||||
|
[disabled]="!enabled"
|
||||||
|
[max]="max"
|
||||||
|
[min]="min"
|
||||||
|
[step]="step"
|
||||||
|
[thumbLabel]="true"
|
||||||
|
></mat-slider>
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
width: 33%;
|
||||||
|
min-width: 8rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
overflow-x: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-slide-toggle {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-slider {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {ToggleSliderComponent} from './toggle-slider.component';
|
||||||
|
|
||||||
|
describe('ToggleSliderComponent', () => {
|
||||||
|
let component: ToggleSliderComponent;
|
||||||
|
let fixture: ComponentFixture<ToggleSliderComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ToggleSliderComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ToggleSliderComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||||
|
import {MatSlideToggleChange} from '@angular/material';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-toggle-slider',
|
||||||
|
templateUrl: './toggle-slider.component.html',
|
||||||
|
styleUrls: ['./toggle-slider.component.scss']
|
||||||
|
})
|
||||||
|
export class ToggleSliderComponent implements OnInit {
|
||||||
|
|
||||||
|
enabled = false;
|
||||||
|
rawValue: number;
|
||||||
|
@Output() modelChange: EventEmitter<number> = new EventEmitter<number>();
|
||||||
|
@Input() min = 0;
|
||||||
|
@Input() max = 100;
|
||||||
|
@Input() step = 1;
|
||||||
|
@Input() label: string;
|
||||||
|
|
||||||
|
get value(): number {
|
||||||
|
return this.rawValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(value: number) {
|
||||||
|
this.rawValue = value;
|
||||||
|
this.modelChange.emit(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set model(value: number) {
|
||||||
|
this.rawValue = value;
|
||||||
|
this.enabled = value !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
onSlideToggleChange(event: MatSlideToggleChange) {
|
||||||
|
if (event.checked === false) {
|
||||||
|
this.modelChange.emit(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,6 +9,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="results && results.length === 0" class="note">
|
||||||
|
<mat-icon>error</mat-icon>
|
||||||
|
<span>No match found!</span>
|
||||||
|
</div>
|
||||||
<div *ngIf="!results" class="spinner">
|
<div *ngIf="!results" class="spinner">
|
||||||
<mat-spinner></mat-spinner>
|
<mat-spinner></mat-spinner>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -15,6 +15,21 @@
|
|||||||
> app-result {
|
> app-result {
|
||||||
margin-bottom: 3rem;
|
margin-bottom: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
align-self: center;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
|
|||||||
@ -3,10 +3,16 @@ import {Result} from '../interfaces/result.interface';
|
|||||||
import {DataService} from './data.service';
|
import {DataService} from './data.service';
|
||||||
|
|
||||||
export interface SearchInput {
|
export interface SearchInput {
|
||||||
|
wasAdvanced: boolean;
|
||||||
from: string;
|
from: string;
|
||||||
to: string;
|
to: string;
|
||||||
singlePresetSelection: object;
|
singlePresetSelection: object;
|
||||||
multiPresetSelection: object;
|
multiPresetSelection: object;
|
||||||
|
textFiler: string;
|
||||||
|
fullText: boolean;
|
||||||
|
tempMeanMax: number;
|
||||||
|
precipitation: number;
|
||||||
|
accommodation: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import { enableProdMode } from '@angular/core';
|
import {enableProdMode} from '@angular/core';
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
import { AppModule } from './app/app.module';
|
import {AppModule} from './app/app.module';
|
||||||
import { environment } from './environments/environment';
|
import {environment} from './environments/environment';
|
||||||
|
|
||||||
|
import 'hammerjs';
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
|
|||||||
@ -55,20 +55,20 @@ $travopti-green: (
|
|||||||
A400: #006400,
|
A400: #006400,
|
||||||
A700: #004d00,
|
A700: #004d00,
|
||||||
contrast: (
|
contrast: (
|
||||||
50: $dark-primary-text,
|
50: $light-primary-text,
|
||||||
100: $dark-primary-text,
|
100: $light-primary-text,
|
||||||
200: $dark-primary-text,
|
200: $light-primary-text,
|
||||||
300: $dark-primary-text,
|
300: $light-primary-text,
|
||||||
400: $dark-primary-text,
|
400: $light-primary-text,
|
||||||
500: $dark-primary-text,
|
500: $light-primary-text,
|
||||||
600: $light-primary-text,
|
600: $light-primary-text,
|
||||||
700: $light-primary-text,
|
700: $light-primary-text,
|
||||||
800: $light-primary-text,
|
800: $light-primary-text,
|
||||||
900: $light-primary-text,
|
900: $light-primary-text,
|
||||||
A100: $dark-primary-text,
|
A100: $light-primary-text,
|
||||||
A200: $dark-primary-text,
|
A200: $light-primary-text,
|
||||||
A400: $dark-primary-text,
|
A400: $light-primary-text,
|
||||||
A700: $dark-primary-text,
|
A700: $light-primary-text,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user