Merge branch 'feature/search-tag-ui' into 'develop'
Add tag search UI See merge request tjohn/cc-data!29
This commit is contained in:
commit
384992634d
@ -50,6 +50,7 @@ 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';
|
import {ToggleSliderComponent} from './components/toggle-slider/toggle-slider.component';
|
||||||
import {PlaceComponent} from './components/place/place.component';
|
import {PlaceComponent} from './components/place/place.component';
|
||||||
|
import {MultiTagSelectComponent} from './components/multi-tag-select/multi-tag-select.component';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -70,7 +71,8 @@ import {PlaceComponent} from './components/place/place.component';
|
|||||||
ShareDialogComponent,
|
ShareDialogComponent,
|
||||||
TeamComponent,
|
TeamComponent,
|
||||||
ToggleSliderComponent,
|
ToggleSliderComponent,
|
||||||
PlaceComponent
|
PlaceComponent,
|
||||||
|
MultiTagSelectComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
<mat-chip-list>
|
||||||
|
<!--suppress AngularInvalidExpressionResultType -->
|
||||||
|
<mat-chip (click)="onChipClick(tag)" *ngFor="let tag of availableTags" [color]="isSelected(tag) ? 'accent' : 'none'"
|
||||||
|
selected>{{tag}}</mat-chip>
|
||||||
|
</mat-chip-list>
|
||||||
@ -0,0 +1 @@
|
|||||||
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {MultiTagSelectComponent} from './multi-tag-select.component';
|
||||||
|
|
||||||
|
describe('MultiTagSelectComponent', () => {
|
||||||
|
let component: MultiTagSelectComponent;
|
||||||
|
let fixture: ComponentFixture<MultiTagSelectComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [MultiTagSelectComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(MultiTagSelectComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-multi-tag-select',
|
||||||
|
templateUrl: './multi-tag-select.component.html',
|
||||||
|
styleUrls: ['./multi-tag-select.component.scss']
|
||||||
|
})
|
||||||
|
export class MultiTagSelectComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
availableTags: string[] = [];
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
selection = new EventEmitter<string[]>();
|
||||||
|
|
||||||
|
|
||||||
|
rawValue: string[] = [];
|
||||||
|
@Output() modelChange: EventEmitter<string[]> = new EventEmitter<string[]>();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
get value(): string[] {
|
||||||
|
return this.rawValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(value: string[]) {
|
||||||
|
this.rawValue = value;
|
||||||
|
this.modelChange.emit(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set model(value: string[]) {
|
||||||
|
this.rawValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
onChipClick(tag: string) {
|
||||||
|
if (this.isSelected(tag)) {
|
||||||
|
this.deselectTag(tag);
|
||||||
|
} else {
|
||||||
|
this.selectTag(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isSelected(tag: string) {
|
||||||
|
return this.value && this.value.includes(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private deselectTag(tag: string) {
|
||||||
|
this.value = this.value.filter(i => i !== tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private selectTag(tag: string) {
|
||||||
|
this.value = this.value.concat(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@
|
|||||||
<mat-tab label="Guided">
|
<mat-tab label="Guided">
|
||||||
|
|
||||||
<mat-vertical-stepper>
|
<mat-vertical-stepper>
|
||||||
|
<!-- Date input -->
|
||||||
<mat-step>
|
<mat-step>
|
||||||
<ng-template matStepLabel>When is your trip?</ng-template>
|
<ng-template matStepLabel>When is your trip?</ng-template>
|
||||||
<div class="vertical-wrap">
|
<div class="vertical-wrap">
|
||||||
@ -22,7 +22,7 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
<!-- Multi presets -->
|
||||||
<mat-step>
|
<mat-step>
|
||||||
<ng-template matStepLabel>Which climate would you prefer?</ng-template>
|
<ng-template matStepLabel>Which climate would you prefer?</ng-template>
|
||||||
<div *ngFor="let key of multiPresetsKeys" class="sub-group">
|
<div *ngFor="let key of multiPresetsKeys" class="sub-group">
|
||||||
@ -37,7 +37,7 @@
|
|||||||
</mat-radio-group>
|
</mat-radio-group>
|
||||||
</div>
|
</div>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
<!-- Single presets -->
|
||||||
<mat-step>
|
<mat-step>
|
||||||
<ng-template matStepLabel>What else is important to you?</ng-template>
|
<ng-template matStepLabel>What else is important to you?</ng-template>
|
||||||
<div class="vertical">
|
<div class="vertical">
|
||||||
@ -45,6 +45,12 @@
|
|||||||
[(ngModel)]="singlePresetSelection[preset.preset_id]">{{preset.tag_label|translate}}</mat-checkbox>
|
[(ngModel)]="singlePresetSelection[preset.preset_id]">{{preset.tag_label|translate}}</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
<!-- Tag selection -->
|
||||||
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>What needs to be fulfilled?</ng-template>
|
||||||
|
<app-multi-tag-select [(model)]="selectedTags" [availableTags]="tags"></app-multi-tag-select>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
</mat-vertical-stepper>
|
</mat-vertical-stepper>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
|
|
||||||
@ -93,7 +99,6 @@
|
|||||||
<span class="title">Fincancial</span>
|
<span class="title">Fincancial</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>
|
||||||
|
|
||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
|
|||||||
@ -21,6 +21,9 @@ export class SearchInputComponent implements OnInit {
|
|||||||
singlePresets: Preset[];
|
singlePresets: Preset[];
|
||||||
multiPresets: Map<string, Preset[]>;
|
multiPresets: Map<string, Preset[]>;
|
||||||
multiPresetsKeys: string[];
|
multiPresetsKeys: string[];
|
||||||
|
selectedTags: string[] = [];
|
||||||
|
|
||||||
|
tags: string[];
|
||||||
|
|
||||||
from: string;
|
from: string;
|
||||||
to: string;
|
to: string;
|
||||||
@ -55,6 +58,8 @@ 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()];
|
||||||
|
|
||||||
|
this.tags = await this.ss.getAvailableTags();
|
||||||
|
|
||||||
this.loadSearch();
|
this.loadSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +67,7 @@ export class SearchInputComponent implements OnInit {
|
|||||||
this.saveSearch(isAdvanced);
|
this.saveSearch(isAdvanced);
|
||||||
|
|
||||||
const query = isAdvanced ? this.getQueryFromAdvanced() : this.getQueryFromGuided();
|
const query = isAdvanced ? this.getQueryFromAdvanced() : this.getQueryFromGuided();
|
||||||
|
console.log(query);
|
||||||
|
|
||||||
await this.router.navigate(['/search'], {queryParams: {q: objToBase64(query)}});
|
await this.router.navigate(['/search'], {queryParams: {q: objToBase64(query)}});
|
||||||
}
|
}
|
||||||
@ -96,6 +102,7 @@ export class SearchInputComponent implements OnInit {
|
|||||||
const query: Query = {
|
const query: Query = {
|
||||||
from: new Date(this.from).getTime(),
|
from: new Date(this.from).getTime(),
|
||||||
to: new Date(this.to).getTime(),
|
to: new Date(this.to).getTime(),
|
||||||
|
tags: this.selectedTags
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const preset of this.singlePresets) {
|
for (const preset of this.singlePresets) {
|
||||||
@ -138,6 +145,7 @@ export class SearchInputComponent implements OnInit {
|
|||||||
to: this.to,
|
to: this.to,
|
||||||
singlePresetSelection: this.singlePresetSelection,
|
singlePresetSelection: this.singlePresetSelection,
|
||||||
multiPresetSelection: this.multiPresetSelection,
|
multiPresetSelection: this.multiPresetSelection,
|
||||||
|
tags: this.selectedTags,
|
||||||
fullText: this.fullText,
|
fullText: this.fullText,
|
||||||
textFiler: this.textFilter,
|
textFiler: this.textFilter,
|
||||||
tempMeanMax: this.temperatureMeanMax,
|
tempMeanMax: this.temperatureMeanMax,
|
||||||
@ -154,6 +162,7 @@ export class SearchInputComponent implements OnInit {
|
|||||||
this.to = prevInput.to;
|
this.to = prevInput.to;
|
||||||
this.singlePresetSelection = prevInput.singlePresetSelection;
|
this.singlePresetSelection = prevInput.singlePresetSelection;
|
||||||
this.multiPresetSelection = prevInput.multiPresetSelection;
|
this.multiPresetSelection = prevInput.multiPresetSelection;
|
||||||
|
this.selectedTags = prevInput.tags;
|
||||||
this.textFilter = prevInput.textFiler;
|
this.textFilter = prevInput.textFiler;
|
||||||
this.fullText = prevInput.fullText;
|
this.fullText = prevInput.fullText;
|
||||||
this.selectedTab = prevInput.wasAdvanced ? 1 : 0;
|
this.selectedTab = prevInput.wasAdvanced ? 1 : 0;
|
||||||
|
|||||||
@ -15,6 +15,7 @@ export interface Query {
|
|||||||
fulltext?: boolean;
|
fulltext?: boolean;
|
||||||
textfilter?: string;
|
textfilter?: string;
|
||||||
showRegionsWithNullScore?: boolean;
|
showRegionsWithNullScore?: boolean;
|
||||||
|
tags?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SearchParameter {
|
export enum SearchParameter {
|
||||||
|
|||||||
@ -75,6 +75,13 @@ export class DataService {
|
|||||||
public getPlacesByRegion(id: number): Promise<Place[]> {
|
public getPlacesByRegion(id: number): Promise<Place[]> {
|
||||||
return this.http.get<Place[]>(`${this.API_URL}/regions/${id}/nearby`).toPromise();
|
return this.http.get<Place[]>(`${this.API_URL}/regions/${id}/nearby`).toPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all available search tags.
|
||||||
|
*/
|
||||||
|
public getAllTags(): Promise<string[]> {
|
||||||
|
return this.http.get<string[]>(`${this.API_URL}/search/tags`).toPromise();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -8,6 +8,7 @@ export interface SearchInput {
|
|||||||
to: string;
|
to: string;
|
||||||
singlePresetSelection: object;
|
singlePresetSelection: object;
|
||||||
multiPresetSelection: object;
|
multiPresetSelection: object;
|
||||||
|
tags: string[];
|
||||||
textFiler: string;
|
textFiler: string;
|
||||||
fullText: boolean;
|
fullText: boolean;
|
||||||
tempMeanMax: number;
|
tempMeanMax: number;
|
||||||
@ -21,6 +22,7 @@ export interface SearchInput {
|
|||||||
export class SearchService {
|
export class SearchService {
|
||||||
|
|
||||||
private searchInput: SearchInput;
|
private searchInput: SearchInput;
|
||||||
|
private cachedTags: string[];
|
||||||
|
|
||||||
constructor(private ds: DataService) {
|
constructor(private ds: DataService) {
|
||||||
}
|
}
|
||||||
@ -36,4 +38,13 @@ export class SearchService {
|
|||||||
public loadSearchInput(): SearchInput {
|
public loadSearchInput(): SearchInput {
|
||||||
return this.searchInput;
|
return this.searchInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getAvailableTags(): Promise<string[]> {
|
||||||
|
if (this.cachedTags) {
|
||||||
|
return this.cachedTags;
|
||||||
|
}
|
||||||
|
const tags: string[] = await this.ds.getAllTags();
|
||||||
|
this.cachedTags = tags;
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user