diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index a949db3..9113578 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -1,11 +1,13 @@ -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; +import {NgModule} from '@angular/core'; +import {RouterModule, Routes} from '@angular/router'; import {HomeComponent} from './containers/home/home.component'; import {NotfoundComponent} from './containers/notfound/notfound.component'; +import {SearchComponent} from './containers/search/search.component'; const routes: Routes = [ {path: 'home', component: HomeComponent}, + {path: 'search', component: SearchComponent}, {path: '', redirectTo: 'home', pathMatch: 'full'}, {path: '**', component: NotfoundComponent} ]; diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 1ab64de..914daa8 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -1,25 +1,30 @@ -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; +import {BrowserModule} from '@angular/platform-browser'; +import {NgModule} from '@angular/core'; -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import {AppRoutingModule} from './app-routing.module'; +import {AppComponent} from './app.component'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {MatToolbarModule} from '@angular/material/toolbar'; import {MatIconModule} from '@angular/material/icon'; import {MatButtonModule} from '@angular/material/button'; import {MatSidenavModule} from '@angular/material/sidenav'; -import { HomeComponent } from './containers/home/home.component'; +import {HomeComponent} from './containers/home/home.component'; import {MatCardModule} from '@angular/material/card'; import {MatFormFieldModule} from '@angular/material/form-field'; import {MatInputModule} from '@angular/material/input'; import {MatSelectModule} from '@angular/material/select'; -import { NotfoundComponent } from './containers/notfound/notfound.component'; +import {NotfoundComponent} from './containers/notfound/notfound.component'; +import {SearchComponent} from './containers/search/search.component'; +import {SearchInputComponent} from './components/search-input/search-input.component'; +import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; @NgModule({ declarations: [ AppComponent, HomeComponent, - NotfoundComponent + NotfoundComponent, + SearchComponent, + SearchInputComponent ], imports: [ BrowserModule, @@ -32,7 +37,8 @@ import { NotfoundComponent } from './containers/notfound/notfound.component'; MatCardModule, MatFormFieldModule, MatInputModule, - MatSelectModule + MatSelectModule, + MatProgressSpinnerModule ], providers: [], bootstrap: [AppComponent] diff --git a/frontend/src/app/components/search-input/search-input.component.html b/frontend/src/app/components/search-input/search-input.component.html new file mode 100644 index 0000000..69a84cd --- /dev/null +++ b/frontend/src/app/components/search-input/search-input.component.html @@ -0,0 +1,28 @@ + + + Search + + + + + Start + + + + End + + + + Price + + Budget + Mid-Range + Luxury + + + + diff --git a/frontend/src/app/components/search-input/search-input.component.scss b/frontend/src/app/components/search-input/search-input.component.scss new file mode 100644 index 0000000..886ce23 --- /dev/null +++ b/frontend/src/app/components/search-input/search-input.component.scss @@ -0,0 +1,4 @@ +.search-container { + display: flex; + flex-direction: column; +} diff --git a/frontend/src/app/components/search-input/search-input.component.spec.ts b/frontend/src/app/components/search-input/search-input.component.spec.ts new file mode 100644 index 0000000..4483c5d --- /dev/null +++ b/frontend/src/app/components/search-input/search-input.component.spec.ts @@ -0,0 +1,25 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {SearchInputComponent} from './search-input.component'; + +describe('SearchInputComponent', () => { + let component: SearchInputComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [SearchInputComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SearchInputComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/search-input/search-input.component.ts b/frontend/src/app/components/search-input/search-input.component.ts new file mode 100644 index 0000000..a341f28 --- /dev/null +++ b/frontend/src/app/components/search-input/search-input.component.ts @@ -0,0 +1,28 @@ +import {Component, OnInit} from '@angular/core'; +import {Router} from '@angular/router'; +import {Query} from '../../interfaces/search-request.interface'; +import {objToBase64} from '../../utils/base64conversion'; + +@Component({ + selector: 'app-search-input', + templateUrl: './search-input.component.html', + styleUrls: ['./search-input.component.scss'] +}) +export class SearchInputComponent implements OnInit { + + constructor(private router: Router) { + } + + ngOnInit() { + } + + async onSearch() { + const query: Query = { + from: Date.now(), + to: Date.now(), + }; + + await this.router.navigate(['/search'], {queryParams: {q: objToBase64(query)}}); + } + +} diff --git a/frontend/src/app/containers/home/home.component.html b/frontend/src/app/containers/home/home.component.html index 4a9c6b7..f4d1db7 100644 --- a/frontend/src/app/containers/home/home.component.html +++ b/frontend/src/app/containers/home/home.component.html @@ -1,28 +1 @@ - - - Search - - - - - Start - - - - End - - - - Price - - Budget - Mid-Range - Luxury - - - - + diff --git a/frontend/src/app/containers/home/home.component.scss b/frontend/src/app/containers/home/home.component.scss index 825a777..0bd77a1 100644 --- a/frontend/src/app/containers/home/home.component.scss +++ b/frontend/src/app/containers/home/home.component.scss @@ -2,8 +2,3 @@ margin: 0; box-sizing: border-box; } - -.search-container { - display: flex; - flex-direction: column; -} diff --git a/frontend/src/app/containers/search/search.component.html b/frontend/src/app/containers/search/search.component.html new file mode 100644 index 0000000..c1f8e36 --- /dev/null +++ b/frontend/src/app/containers/search/search.component.html @@ -0,0 +1,12 @@ + + + +

Suchergebnisse ({{results.length}}):

+ +
+ +
+ +
diff --git a/frontend/src/app/containers/search/search.component.scss b/frontend/src/app/containers/search/search.component.scss new file mode 100644 index 0000000..56f5c03 --- /dev/null +++ b/frontend/src/app/containers/search/search.component.scss @@ -0,0 +1,17 @@ +:host { + display: flex; + flex-direction: column; + height: 100%; + + > * { + margin-bottom: 1rem; + } +} + +.spinner { + flex: 1 1 auto; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; +} diff --git a/frontend/src/app/containers/search/search.component.spec.ts b/frontend/src/app/containers/search/search.component.spec.ts new file mode 100644 index 0000000..5a3cb0f --- /dev/null +++ b/frontend/src/app/containers/search/search.component.spec.ts @@ -0,0 +1,25 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {SearchComponent} from './search.component'; + +describe('SearchComponent', () => { + let component: SearchComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [SearchComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SearchComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/containers/search/search.component.ts b/frontend/src/app/containers/search/search.component.ts new file mode 100644 index 0000000..3ff301e --- /dev/null +++ b/frontend/src/app/containers/search/search.component.ts @@ -0,0 +1,30 @@ +import {Component, OnInit} from '@angular/core'; +import {ActivatedRoute} from '@angular/router'; +import {Result} from '../../interfaces/result.interface'; +import {MOCK_RESULT} from '../../mock/mock-data'; + +@Component({ + selector: 'app-search', + templateUrl: './search.component.html', + styleUrls: ['./search.component.scss'] +}) +export class SearchComponent implements OnInit { + + queryString: string; + results: Result[]; + + constructor(private route: ActivatedRoute) { + } + + ngOnInit() { + this.route.queryParams.subscribe(params => { + this.queryString = params.q; + }); + + // Mock results + setTimeout(() => { + this.results = MOCK_RESULT; + }, 1000); + + } +} diff --git a/frontend/src/app/interfaces/result.interface.ts b/frontend/src/app/interfaces/result.interface.ts index 1ca48be..260e03c 100644 --- a/frontend/src/app/interfaces/result.interface.ts +++ b/frontend/src/app/interfaces/result.interface.ts @@ -1,15 +1,14 @@ -import {SearchParam} from './search-request.interface'; - /** Represents the structure of one search result. */ export interface Result { - region_id: string; - region_name: string; + region_id: number; + name: string; + country_id: number; score: number; scores: Score[]; } export interface Score { - type: SearchParam; + type: string; value: number; score: number; } diff --git a/frontend/src/app/interfaces/search-request.interface.ts b/frontend/src/app/interfaces/search-request.interface.ts index 1095fb6..598f1f3 100644 --- a/frontend/src/app/interfaces/search-request.interface.ts +++ b/frontend/src/app/interfaces/search-request.interface.ts @@ -10,7 +10,7 @@ export enum SearchParam { FOOD = 'food' } -export interface SearchParams { +export interface Query { from: number; to: number; price?: number[]; diff --git a/frontend/src/app/mock/mock-data.ts b/frontend/src/app/mock/mock-data.ts new file mode 100644 index 0000000..c14c875 --- /dev/null +++ b/frontend/src/app/mock/mock-data.ts @@ -0,0 +1,242 @@ +import {Result} from '../interfaces/result.interface'; + +export const MOCK_RESULT: Result[] = [ + { + region_id: 24, + country_id: 20, + name: 'Kuala Lumpur', + scores: [ + { + type: 'temperature_mean', + value: 28.1, + score: 10 + }, + { + type: 'raindays', + value: 13.8, + score: 7.5 + } + ], + score: 8.75 + }, + { + region_id: 10, + country_id: 7, + name: 'Shanghai', + scores: [ + { + type: 'temperature_mean', + value: 24.8, + score: 6.3 + }, + { + type: 'raindays', + value: 8.6, + score: 9.4 + }, + { + type: 'sunhours', + value: 151.2, + score: 2.2 + } + ], + score: 5.97 + }, + { + region_id: 3, + country_id: 2, + name: 'Sydney', + scores: [ + { + type: 'temperature_mean', + value: 16.8, + score: 0 + }, + { + type: 'raindays', + value: 6.4, + score: 8.2 + }, + { + type: 'sunhours', + value: 245.8, + score: 9.5 + } + ], + score: 5.9 + }, + { + region_id: 9, + country_id: 7, + name: 'Peking', + scores: [ + { + type: 'temperature_mean', + value: 21.1, + score: 4.2 + }, + { + type: 'raindays', + value: 5.4, + score: 7.1 + }, + { + type: 'sunhours', + value: 199, + score: 6 + } + ], + score: 5.77 + }, + { + region_id: 7, + country_id: 5, + name: 'Toronto', + scores: [ + { + type: 'temperature_mean', + value: 16.8, + score: 1.2 + }, + { + type: 'raindays', + value: 8.4, + score: 9.3 + }, + { + type: 'sunhours', + value: 206.4, + score: 6.2 + } + ], + score: 5.57 + }, + { + region_id: 2, + country_id: 2, + name: 'Melbourne', + scores: [ + { + type: 'temperature_mean', + value: 12.3, + score: 0 + }, + { + type: 'raindays', + value: 8.3, + score: 10 + }, + { + type: 'sunhours', + value: 187.4, + score: 5.1 + } + ], + score: 5.03 + }, + { + region_id: 12, + country_id: 9, + name: 'Kairo', + scores: [ + { + type: 'temperature_mean', + value: 27.9, + score: 8.9 + }, + { + type: 'raindays', + value: 0, + score: 1.1 + } + ], + score: 5 + }, + { + region_id: 13, + country_id: 10, + name: 'London', + scores: [ + { + type: 'temperature_mean', + value: 16, + score: 0 + }, + { + type: 'raindays', + value: 8.6, + score: 9.6 + }, + { + type: 'sunhours', + value: 142.4, + score: 1.9 + } + ], + score: 3.83 + }, + { + region_id: 6, + country_id: 4, + name: 'Sao Paolo', + scores: [ + { + type: 'temperature_mean', + value: 19.6, + score: 0.8 + }, + { + type: 'raindays', + value: 4.6, + score: 6 + } + ], + score: 3.4 + }, + { + region_id: 11, + country_id: 8, + name: 'Bogota', + scores: [ + { + type: 'temperature_mean', + value: 13.7, + score: 0 + }, + { + type: 'raindays', + value: 9.1, + score: 9.8 + }, + { + type: 'sunhours', + value: 121.2, + score: 0.1 + } + ], + score: 3.3 + }, + { + region_id: 18, + country_id: 15, + name: 'Reykjavik', + scores: [ + { + type: 'temperature_mean', + value: 8.6, + score: 0 + }, + { + type: 'raindays', + value: 13.5, + score: 7.4 + }, + { + type: 'sunhours', + value: 135.1, + score: 1.7 + } + ], + score: 3.03 + } +]; diff --git a/frontend/src/app/services/data.service.ts b/frontend/src/app/services/data.service.ts index d9714a1..e29dbdf 100644 --- a/frontend/src/app/services/data.service.ts +++ b/frontend/src/app/services/data.service.ts @@ -2,7 +2,6 @@ import {Injectable} from '@angular/core'; import {HttpClient, HttpParams} from '@angular/common/http'; import {Preset} from '../interfaces/preset.interface'; import {Result} from '../interfaces/result.interface'; -import {SearchParams} from '../interfaces/search-request.interface'; import {Region} from '../interfaces/region.interface'; @Injectable({ @@ -15,9 +14,9 @@ export class DataService { constructor(private http: HttpClient) { } - public searchRegions(searchParams: SearchParams): Promise { + public searchRegions(query: string): Promise { const params = new HttpParams(); - params.append('q', btoa(JSON.stringify(searchParams))); + params.append('q', query); return this.http.get(this.API_URL + 'search', {params}).toPromise(); } diff --git a/frontend/src/app/utils/base64conversion.ts b/frontend/src/app/utils/base64conversion.ts new file mode 100644 index 0000000..e10d40d --- /dev/null +++ b/frontend/src/app/utils/base64conversion.ts @@ -0,0 +1,15 @@ +/** + * Encodes an object as base64 string. + * @param obj The object to encode + */ +export function objToBase64(obj: object): string { + return btoa(JSON.stringify(obj)); +} + +/** + * Decodes a base64 encoded object. + * @param base64 Encoded object + */ +export function base64ToObj(base64: string): object { + return JSON.parse(atob(base64)); +}