diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 1446cf0..ee849bd 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -5338,6 +5338,11 @@
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
"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": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 99df01f..cfe4a1e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -22,6 +22,7 @@
"@angular/platform-browser-dynamic": "~8.2.14",
"@angular/router": "~8.2.14",
"@ngx-translate/core": "^12.1.2",
+ "hammerjs": "^2.0.8",
"ngx-device-detector": "^1.4.5",
"rxjs": "~6.4.0",
"tslib": "^1.10.0",
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index da36f83..f4069d1 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -22,10 +22,15 @@ import {TranslateModule, TranslateService} from '@ngx-translate/core';
import * as enLang from '../assets/i18n/en.json';
import {HttpClientModule} from '@angular/common/http';
import {
+ MatBadgeModule,
MatButtonToggleModule,
MatCheckboxModule,
MatDialogModule,
MatDividerModule,
+ MatRadioModule,
+ MatSliderModule,
+ MatSlideToggleModule,
+ MatStepperModule,
MatTabsModule,
MatTooltipModule
} 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 {TeamComponent} from './containers/team/team.component';
import {DeviceDetectorModule} from 'ngx-device-detector';
+import {ToggleSliderComponent} from './components/toggle-slider/toggle-slider.component';
@NgModule({
@@ -59,7 +65,8 @@ import {DeviceDetectorModule} from 'ngx-device-detector';
BookmarkListComponent,
ShareButtonComponent,
ShareDialogComponent,
- TeamComponent
+ TeamComponent,
+ ToggleSliderComponent
],
imports: [
BrowserModule,
@@ -83,7 +90,12 @@ import {DeviceDetectorModule} from 'ngx-device-detector';
MatTooltipModule,
MatDialogModule,
DeviceDetectorModule,
- MatTabsModule
+ MatTabsModule,
+ MatBadgeModule,
+ MatStepperModule,
+ MatRadioModule,
+ MatSlideToggleModule,
+ MatSliderModule
],
providers: [],
bootstrap: [AppComponent],
diff --git a/frontend/src/app/components/result/result.component.html b/frontend/src/app/components/result/result.component.html
index fef4628..775f78b 100644
--- a/frontend/src/app/components/result/result.component.html
+++ b/frontend/src/app/components/result/result.component.html
@@ -14,7 +14,7 @@
|
- {{PROPERTY_VIS_DEF[score.type].icon}}
+ {{PROPERTY_VIS_DEF[score.type] ? PROPERTY_VIS_DEF[score.type].icon : 'bar_chart'}}
{{score.type|translate}}:
|
@@ -25,7 +25,12 @@
- {{PROPERTY_VIS_DEF[score.type].unit}}
+ {{PROPERTY_VIS_DEF[score.type] ? PROPERTY_VIS_DEF[score.type].unit : ''}}
+
+ |
+
+
+ ({{score.score}})
|
diff --git a/frontend/src/app/components/search-input/search-input.component.html b/frontend/src/app/components/search-input/search-input.component.html
index b596e30..ba983a0 100644
--- a/frontend/src/app/components/search-input/search-input.component.html
+++ b/frontend/src/app/components/search-input/search-input.component.html
@@ -1,42 +1,106 @@
-
-
- What would you prefer?
-
- {{key|translate}}:
-
-
- {{preset.tag_label|translate}}
-
-
-
-
+ Search
-
- Whats most important to you?
-
- {{preset.tag_label|translate}}
-
-
+
-
+
+ Search
search
+
diff --git a/frontend/src/app/components/search-input/search-input.component.scss b/frontend/src/app/components/search-input/search-input.component.scss
index af4bcc8..817a977 100644
--- a/frontend/src/app/components/search-input/search-input.component.scss
+++ b/frontend/src/app/components/search-input/search-input.component.scss
@@ -2,27 +2,72 @@
display: flex;
flex-direction: column;
- > .group {
- flex: 0 0 auto;
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
- }
-
> .search-btn {
margin-top: 1rem;
}
}
.sub-group {
- margin-bottom: 0.5rem;
+ margin-bottom: 2rem;
+ display: flex;
+ flex-direction: column;
- > .label {
+ .label {
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 {
display: flex;
flex-direction: column;
}
+
+.vertical-wrap {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: space-around;
+}
+
diff --git a/frontend/src/app/components/search-input/search-input.component.ts b/frontend/src/app/components/search-input/search-input.component.ts
index 12a1202..e27d705 100644
--- a/frontend/src/app/components/search-input/search-input.component.ts
+++ b/frontend/src/app/components/search-input/search-input.component.ts
@@ -14,6 +14,9 @@ import {SearchService} from '../../services/search.service';
})
export class SearchInputComponent implements OnInit {
+
+ selectedTab = 0;
+
presets: Preset[];
singlePresets: Preset[];
multiPresets: Map;
@@ -21,9 +24,18 @@ export class SearchInputComponent implements OnInit {
from: string;
to: string;
+
+ // Guided Search
singlePresetSelection = {};
multiPresetSelection = {};
+ // Advanced Search
+ textFilter = '';
+ fullText = false;
+ temperatureMeanMax: number;
+ precipitation: number;
+ accommodation: number;
+
readonly today = this.from = formatDate(new Date(), 'yyyy-MM-dd', 'en-GB');
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.multiPresetsKeys = [...this.multiPresets.keys()];
- const prevInput = this.ss.loadSearchInput();
-
- if (prevInput) {
- this.from = prevInput.from;
- this.to = prevInput.to;
- this.singlePresetSelection = prevInput.singlePresetSelection;
- this.multiPresetSelection = prevInput.multiPresetSelection;
- }
+ this.loadSearch();
}
- async onSearch() {
- 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;
- }
- }
-
- this.ss.saveSearchInput({
- from: this.from,
- to: this.to,
- singlePresetSelection: this.singlePresetSelection,
- multiPresetSelection: this.multiPresetSelection
- });
+ async onSearch(isAdvanced: boolean) {
+ this.saveSearch(isAdvanced);
+ const query = isAdvanced ? this.getQueryFromAdvanced() : this.getQueryFromGuided();
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');
}
}
+
+ 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;
+ }
+ }
+
}
diff --git a/frontend/src/app/components/toggle-slider/toggle-slider.component.html b/frontend/src/app/components/toggle-slider/toggle-slider.component.html
new file mode 100644
index 0000000..20527b8
--- /dev/null
+++ b/frontend/src/app/components/toggle-slider/toggle-slider.component.html
@@ -0,0 +1,11 @@
+{{label}}
+
+
diff --git a/frontend/src/app/components/toggle-slider/toggle-slider.component.scss b/frontend/src/app/components/toggle-slider/toggle-slider.component.scss
new file mode 100644
index 0000000..839782e
--- /dev/null
+++ b/frontend/src/app/components/toggle-slider/toggle-slider.component.scss
@@ -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;
+}
diff --git a/frontend/src/app/components/toggle-slider/toggle-slider.component.spec.ts b/frontend/src/app/components/toggle-slider/toggle-slider.component.spec.ts
new file mode 100644
index 0000000..085e7f7
--- /dev/null
+++ b/frontend/src/app/components/toggle-slider/toggle-slider.component.spec.ts
@@ -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;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ToggleSliderComponent]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ToggleSliderComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/components/toggle-slider/toggle-slider.component.ts b/frontend/src/app/components/toggle-slider/toggle-slider.component.ts
new file mode 100644
index 0000000..5a86204
--- /dev/null
+++ b/frontend/src/app/components/toggle-slider/toggle-slider.component.ts
@@ -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 = new EventEmitter();
+ @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);
+ }
+ }
+}
diff --git a/frontend/src/app/containers/search/search.component.html b/frontend/src/app/containers/search/search.component.html
index 90af0e8..f1c3247 100644
--- a/frontend/src/app/containers/search/search.component.html
+++ b/frontend/src/app/containers/search/search.component.html
@@ -9,6 +9,10 @@
+
+ error
+ No match found!
+
diff --git a/frontend/src/app/containers/search/search.component.scss b/frontend/src/app/containers/search/search.component.scss
index 86a7e39..097ba9d 100644
--- a/frontend/src/app/containers/search/search.component.scss
+++ b/frontend/src/app/containers/search/search.component.scss
@@ -15,6 +15,21 @@
> app-result {
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 {
diff --git a/frontend/src/app/services/search.service.ts b/frontend/src/app/services/search.service.ts
index 72bba81..a4d0f6f 100644
--- a/frontend/src/app/services/search.service.ts
+++ b/frontend/src/app/services/search.service.ts
@@ -3,10 +3,16 @@ import {Result} from '../interfaces/result.interface';
import {DataService} from './data.service';
export interface SearchInput {
+ wasAdvanced: boolean;
from: string;
to: string;
singlePresetSelection: object;
multiPresetSelection: object;
+ textFiler: string;
+ fullText: boolean;
+ tempMeanMax: number;
+ precipitation: number;
+ accommodation: number;
}
@Injectable({
diff --git a/frontend/src/main.ts b/frontend/src/main.ts
index c7b673c..5e54bee 100644
--- a/frontend/src/main.ts
+++ b/frontend/src/main.ts
@@ -1,8 +1,10 @@
-import { enableProdMode } from '@angular/core';
-import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import {enableProdMode} from '@angular/core';
+import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
-import { AppModule } from './app/app.module';
-import { environment } from './environments/environment';
+import {AppModule} from './app/app.module';
+import {environment} from './environments/environment';
+
+import 'hammerjs';
if (environment.production) {
enableProdMode();
diff --git a/frontend/src/theme.scss b/frontend/src/theme.scss
index 0856b36..e728b8e 100644
--- a/frontend/src/theme.scss
+++ b/frontend/src/theme.scss
@@ -55,20 +55,20 @@ $travopti-green: (
A400: #006400,
A700: #004d00,
contrast: (
- 50: $dark-primary-text,
- 100: $dark-primary-text,
- 200: $dark-primary-text,
- 300: $dark-primary-text,
- 400: $dark-primary-text,
- 500: $dark-primary-text,
+ 50: $light-primary-text,
+ 100: $light-primary-text,
+ 200: $light-primary-text,
+ 300: $light-primary-text,
+ 400: $light-primary-text,
+ 500: $light-primary-text,
600: $light-primary-text,
700: $light-primary-text,
800: $light-primary-text,
900: $light-primary-text,
- A100: $dark-primary-text,
- A200: $dark-primary-text,
- A400: $dark-primary-text,
- A700: $dark-primary-text,
+ A100: $light-primary-text,
+ A200: $light-primary-text,
+ A400: $light-primary-text,
+ A700: $light-primary-text,
)
);