Merge branch 'feature/search-tag-ui' into 'develop'

Add tag search UI

See merge request tjohn/cc-data!29
This commit is contained in:
Patrick Gebhardt 2020-06-24 15:36:31 +02:00
commit 384992634d
10 changed files with 130 additions and 5 deletions

View File

@ -50,6 +50,7 @@ import {TeamComponent} from './containers/team/team.component';
import {DeviceDetectorModule} from 'ngx-device-detector';
import {ToggleSliderComponent} from './components/toggle-slider/toggle-slider.component';
import {PlaceComponent} from './components/place/place.component';
import {MultiTagSelectComponent} from './components/multi-tag-select/multi-tag-select.component';
@NgModule({
@ -70,7 +71,8 @@ import {PlaceComponent} from './components/place/place.component';
ShareDialogComponent,
TeamComponent,
ToggleSliderComponent,
PlaceComponent
PlaceComponent,
MultiTagSelectComponent
],
imports: [
BrowserModule,

View File

@ -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>

View File

@ -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();
});
});

View File

@ -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);
}
}

View File

@ -8,7 +8,7 @@
<mat-tab label="Guided">
<mat-vertical-stepper>
<!-- Date input -->
<mat-step>
<ng-template matStepLabel>When is your trip?</ng-template>
<div class="vertical-wrap">
@ -22,7 +22,7 @@
</mat-form-field>
</div>
</mat-step>
<!-- Multi presets -->
<mat-step>
<ng-template matStepLabel>Which climate would you prefer?</ng-template>
<div *ngFor="let key of multiPresetsKeys" class="sub-group">
@ -37,7 +37,7 @@
</mat-radio-group>
</div>
</mat-step>
<!-- Single presets -->
<mat-step>
<ng-template matStepLabel>What else is important to you?</ng-template>
<div class="vertical">
@ -45,6 +45,12 @@
[(ngModel)]="singlePresetSelection[preset.preset_id]">{{preset.tag_label|translate}}</mat-checkbox>
</div>
</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-tab>
@ -93,7 +99,6 @@
<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>

View File

@ -21,6 +21,9 @@ export class SearchInputComponent implements OnInit {
singlePresets: Preset[];
multiPresets: Map<string, Preset[]>;
multiPresetsKeys: string[];
selectedTags: string[] = [];
tags: string[];
from: string;
to: string;
@ -55,6 +58,8 @@ export class SearchInputComponent implements OnInit {
this.multiPresets = this.ps.multiPresets;
this.multiPresetsKeys = [...this.multiPresets.keys()];
this.tags = await this.ss.getAvailableTags();
this.loadSearch();
}
@ -62,6 +67,7 @@ export class SearchInputComponent implements OnInit {
this.saveSearch(isAdvanced);
const query = isAdvanced ? this.getQueryFromAdvanced() : this.getQueryFromGuided();
console.log(query);
await this.router.navigate(['/search'], {queryParams: {q: objToBase64(query)}});
}
@ -96,6 +102,7 @@ export class SearchInputComponent implements OnInit {
const query: Query = {
from: new Date(this.from).getTime(),
to: new Date(this.to).getTime(),
tags: this.selectedTags
};
for (const preset of this.singlePresets) {
@ -138,6 +145,7 @@ export class SearchInputComponent implements OnInit {
to: this.to,
singlePresetSelection: this.singlePresetSelection,
multiPresetSelection: this.multiPresetSelection,
tags: this.selectedTags,
fullText: this.fullText,
textFiler: this.textFilter,
tempMeanMax: this.temperatureMeanMax,
@ -154,6 +162,7 @@ export class SearchInputComponent implements OnInit {
this.to = prevInput.to;
this.singlePresetSelection = prevInput.singlePresetSelection;
this.multiPresetSelection = prevInput.multiPresetSelection;
this.selectedTags = prevInput.tags;
this.textFilter = prevInput.textFiler;
this.fullText = prevInput.fullText;
this.selectedTab = prevInput.wasAdvanced ? 1 : 0;

View File

@ -15,6 +15,7 @@ export interface Query {
fulltext?: boolean;
textfilter?: string;
showRegionsWithNullScore?: boolean;
tags?: string[];
}
export enum SearchParameter {

View File

@ -75,6 +75,13 @@ export class DataService {
public getPlacesByRegion(id: number): Promise<Place[]> {
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();
}
}
/**

View File

@ -8,6 +8,7 @@ export interface SearchInput {
to: string;
singlePresetSelection: object;
multiPresetSelection: object;
tags: string[];
textFiler: string;
fullText: boolean;
tempMeanMax: number;
@ -21,6 +22,7 @@ export interface SearchInput {
export class SearchService {
private searchInput: SearchInput;
private cachedTags: string[];
constructor(private ds: DataService) {
}
@ -36,4 +38,13 @@ export class SearchService {
public loadSearchInput(): 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;
}
}