Add nearby places
This commit is contained in:
parent
846410e6bf
commit
56b8c06b84
@ -28,6 +28,7 @@ import {
|
||||
MatChipsModule,
|
||||
MatDialogModule,
|
||||
MatDividerModule,
|
||||
MatListModule,
|
||||
MatRadioModule,
|
||||
MatSliderModule,
|
||||
MatSlideToggleModule,
|
||||
@ -48,6 +49,7 @@ import {ShareDialogComponent} from './dialogs/share-dialog/share-dialog.componen
|
||||
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';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@ -67,7 +69,8 @@ import {ToggleSliderComponent} from './components/toggle-slider/toggle-slider.co
|
||||
ShareButtonComponent,
|
||||
ShareDialogComponent,
|
||||
TeamComponent,
|
||||
ToggleSliderComponent
|
||||
ToggleSliderComponent,
|
||||
PlaceComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@ -97,7 +100,8 @@ import {ToggleSliderComponent} from './components/toggle-slider/toggle-slider.co
|
||||
MatRadioModule,
|
||||
MatSlideToggleModule,
|
||||
MatSliderModule,
|
||||
MatChipsModule
|
||||
MatChipsModule,
|
||||
MatListModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent],
|
||||
|
||||
6
frontend/src/app/components/place/place.component.html
Normal file
6
frontend/src/app/components/place/place.component.html
Normal file
@ -0,0 +1,6 @@
|
||||
<mat-card>
|
||||
<img [src]="place.img_url" alt="Image of {{place.place_name}}">
|
||||
<div class="footer">
|
||||
<span class="name">{{place.place_name}}</span>
|
||||
</div>
|
||||
</mat-card>
|
||||
28
frontend/src/app/components/place/place.component.scss
Normal file
28
frontend/src/app/components/place/place.component.scss
Normal file
@ -0,0 +1,28 @@
|
||||
mat-card {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
img {
|
||||
flex: 0 0 auto;
|
||||
width: 20rem;
|
||||
height: 11.25rem;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: 20rem;
|
||||
padding: 0.5rem;
|
||||
box-sizing: border-box;
|
||||
flex: 0 1 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.name {
|
||||
flex: 0 1 auto;
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
25
frontend/src/app/components/place/place.component.spec.ts
Normal file
25
frontend/src/app/components/place/place.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {PlaceComponent} from './place.component';
|
||||
|
||||
describe('PlaceComponent', () => {
|
||||
let component: PlaceComponent;
|
||||
let fixture: ComponentFixture<PlaceComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [PlaceComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PlaceComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
20
frontend/src/app/components/place/place.component.ts
Normal file
20
frontend/src/app/components/place/place.component.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {Place} from '../../interfaces/places.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'app-place',
|
||||
templateUrl: './place.component.html',
|
||||
styleUrls: ['./place.component.scss']
|
||||
})
|
||||
export class PlaceComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
place: Place;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@ -66,6 +66,12 @@
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
<div class="places-container">
|
||||
<span class="group-title">Places</span>
|
||||
<div class="places">
|
||||
<app-place *ngFor="let place of places" [place]="place"></app-place>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!region" class="spinner">
|
||||
|
||||
@ -42,6 +42,7 @@
|
||||
|
||||
> .more-btn {
|
||||
color: #8f8f8f;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,6 +67,27 @@
|
||||
|
||||
}
|
||||
|
||||
.places-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> .group-title {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
> .places {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-evenly;
|
||||
|
||||
app-place {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.spinner {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
|
||||
@ -4,6 +4,7 @@ import {ActivatedRoute, ParamMap} from '@angular/router';
|
||||
import {DataService} from '../../services/data.service';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
import {SearchParameter} from '../../interfaces/search-request.interface';
|
||||
import {Place} from '../../interfaces/places.interface';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -36,6 +37,8 @@ export class RegionDetailsComponent implements AfterViewInit {
|
||||
/** Extend the description text */
|
||||
isDescExtended = false;
|
||||
|
||||
places: Place[] = [];
|
||||
|
||||
constructor(private route: ActivatedRoute, private ds: DataService) {
|
||||
}
|
||||
|
||||
@ -45,6 +48,7 @@ export class RegionDetailsComponent implements AfterViewInit {
|
||||
).subscribe((region: Region) => {
|
||||
this.region = region;
|
||||
this.uriRegionName = encodeURI(this.region.name.toLowerCase());
|
||||
this.ds.getPlacesByRegion(this.region.region_id).then(places => this.places = places);
|
||||
setTimeout(() => {
|
||||
if (this.container) {
|
||||
this.container.nativeElement.scrollIntoView();
|
||||
|
||||
20
frontend/src/app/interfaces/places.interface.ts
Normal file
20
frontend/src/app/interfaces/places.interface.ts
Normal file
@ -0,0 +1,20 @@
|
||||
export interface Place {
|
||||
/** The place id */
|
||||
place_id: number;
|
||||
/** The id of the parent region */
|
||||
region_id: number;
|
||||
/** The name of the place in english */
|
||||
place_name: string;
|
||||
/** The longitude position */
|
||||
lon: number;
|
||||
/** The latitude position */
|
||||
lat: number;
|
||||
/** The user rating between 0 and 5 */
|
||||
rating: number;
|
||||
/** Nearby address */
|
||||
vicinity: string;
|
||||
/** Google photo reference */
|
||||
photo_reference: string;
|
||||
/** URL to the image */
|
||||
img_url: string;
|
||||
}
|
||||
@ -3,7 +3,11 @@ import {HttpClient, HttpParams} from '@angular/common/http';
|
||||
import {Preset} from '../interfaces/preset.interface';
|
||||
import {Result} from '../interfaces/result.interface';
|
||||
import {Region} from '../interfaces/region.interface';
|
||||
import {Place} from '../interfaces/places.interface';
|
||||
|
||||
/**
|
||||
* The data service handles all API interactions.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
@ -16,16 +20,27 @@ export class DataService {
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get results for specific search query.
|
||||
* @param query The search query
|
||||
*/
|
||||
public searchRegions(query: string): Promise<Result[]> {
|
||||
const params = new HttpParams().set('q', query);
|
||||
|
||||
return this.http.get<Result[]>(this.API_URL + '/search', {params}).toPromise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all search presets.
|
||||
*/
|
||||
public getAllPresets(): Promise<Preset[]> {
|
||||
return this.http.get<Preset[]>(this.API_URL + '/search/presets').toPromise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all regions.
|
||||
* @param max Limit the returned regions. Selected regions are random.
|
||||
*/
|
||||
public async getAllRegions(max?: number): Promise<Region[]> {
|
||||
let params = new HttpParams();
|
||||
if (max) {
|
||||
@ -38,6 +53,10 @@ export class DataService {
|
||||
return regions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets one region by its id.
|
||||
* @param id The region id
|
||||
*/
|
||||
public async getRegion(id: number): Promise<Region> {
|
||||
if (this.regionCache.has(id)) {
|
||||
return this.regionCache.get(id);
|
||||
@ -48,8 +67,19 @@ export class DataService {
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns POIs near the region.
|
||||
* @param id The regions id
|
||||
*/
|
||||
public getPlacesByRegion(id: number): Promise<Place[]> {
|
||||
return this.http.get<Place[]>(`${this.API_URL}/regions/${id}/nearby`).toPromise();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines meta data for all region parameters.
|
||||
*/
|
||||
export const REGION_PARAM_VIS: RegionParamVisLookup = {
|
||||
temperature_mean: {
|
||||
icon: 'wb_sunny',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user