Implement basic ui concept
This commit is contained in:
parent
410b363713
commit
25f653008a
@ -94,7 +94,6 @@
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
@ -129,4 +128,4 @@
|
||||
}
|
||||
},
|
||||
"defaultProject": "frontend"
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<button mat-icon-button class="menu-btn" (click)="drawer.toggle()">
|
||||
<mat-icon>menu</mat-icon>
|
||||
</button>
|
||||
<h1>Travopti - Prototype</h1>
|
||||
<h1 class="title">Travopti - Prototype</h1>
|
||||
</mat-toolbar>
|
||||
<mat-drawer-container class="content">
|
||||
<mat-drawer #drawer class="drawer">
|
||||
|
||||
@ -13,6 +13,10 @@
|
||||
.menu-btn {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
flex: 1 1 auto
|
||||
}
|
||||
}
|
||||
|
||||
.drawer {
|
||||
|
||||
@ -7,6 +7,7 @@ import {PresetService} from './services/preset.service';
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
|
||||
constructor(private ps: PresetService) {
|
||||
}
|
||||
|
||||
|
||||
@ -21,8 +21,10 @@ import {TranslateModule, TranslateService} from '@ngx-translate/core';
|
||||
// @ts-ignore
|
||||
import * as enLang from '../assets/i18n/en.json';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {MatButtonToggleModule, MatCheckboxModule} from '@angular/material';
|
||||
import {MatButtonToggleModule, MatCheckboxModule, MatDividerModule} from '@angular/material';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {RegionComponent} from './components/region/region.component';
|
||||
import {ResultComponent} from './components/result/result.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@ -31,7 +33,9 @@ import {FormsModule} from '@angular/forms';
|
||||
HomeComponent,
|
||||
NotfoundComponent,
|
||||
SearchComponent,
|
||||
SearchInputComponent
|
||||
SearchInputComponent,
|
||||
RegionComponent,
|
||||
ResultComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@ -50,7 +54,8 @@ import {FormsModule} from '@angular/forms';
|
||||
HttpClientModule,
|
||||
MatCheckboxModule,
|
||||
FormsModule,
|
||||
MatButtonToggleModule
|
||||
MatButtonToggleModule,
|
||||
MatDividerModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
|
||||
15
frontend/src/app/components/region/region.component.html
Normal file
15
frontend/src/app/components/region/region.component.html
Normal file
@ -0,0 +1,15 @@
|
||||
<div class="region-mat-card">
|
||||
<img class="region-img" src="https://travopti.de/api/v1/regions/{{region.region_id}}/image">
|
||||
<div class="region-footer">
|
||||
<div class="region-title">
|
||||
<span class="region-name">{{region.name}}</span>
|
||||
<span class="region-country">| {{region.country}}</span>
|
||||
</div>
|
||||
<button mat-icon-button>
|
||||
<mat-icon>bookmark</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button>
|
||||
<mat-icon>share</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
39
frontend/src/app/components/region/region.component.scss
Normal file
39
frontend/src/app/components/region/region.component.scss
Normal file
@ -0,0 +1,39 @@
|
||||
.region-mat-card {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> .region-img {
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
> .region-footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
> .region-title {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 0.25rem 0;
|
||||
|
||||
> .region-country {
|
||||
text-transform: uppercase;
|
||||
font-size: small;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
> .region-name {
|
||||
font-weight: bold;
|
||||
font-size: large;
|
||||
align-self: center;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
25
frontend/src/app/components/region/region.component.spec.ts
Normal file
25
frontend/src/app/components/region/region.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {RegionComponent} from './region.component';
|
||||
|
||||
describe('RegionComponent', () => {
|
||||
let component: RegionComponent;
|
||||
let fixture: ComponentFixture<RegionComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [RegionComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RegionComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
20
frontend/src/app/components/region/region.component.ts
Normal file
20
frontend/src/app/components/region/region.component.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {Region} from '../../interfaces/region.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'app-region',
|
||||
templateUrl: './region.component.html',
|
||||
styleUrls: ['./region.component.scss']
|
||||
})
|
||||
export class RegionComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
region: Region;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
15
frontend/src/app/components/result/result.component.html
Normal file
15
frontend/src/app/components/result/result.component.html
Normal file
@ -0,0 +1,15 @@
|
||||
<div class="result-mat-card">
|
||||
<img class="result-img" src="https://travopti.de/api/v1/regions/{{result.region_id}}/image">
|
||||
<div class="result-footer">
|
||||
<div class="result-title">
|
||||
<span class="result-name">{{result.name}}</span>
|
||||
<span class="result-country">| {{result.country}}</span>
|
||||
</div>
|
||||
<button mat-icon-button>
|
||||
<mat-icon>bookmark</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button>
|
||||
<mat-icon>share</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
37
frontend/src/app/components/result/result.component.scss
Normal file
37
frontend/src/app/components/result/result.component.scss
Normal file
@ -0,0 +1,37 @@
|
||||
.result-mat-card {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> .result-img {
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
> .result-footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
> .result-title {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 0.25rem 0;
|
||||
|
||||
> .result-country {
|
||||
text-transform: uppercase;
|
||||
font-size: small;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
> .result-name {
|
||||
font-weight: bold;
|
||||
font-size: large;
|
||||
align-self: center;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
25
frontend/src/app/components/result/result.component.spec.ts
Normal file
25
frontend/src/app/components/result/result.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {ResultComponent} from './result.component';
|
||||
|
||||
describe('ResultComponent', () => {
|
||||
let component: ResultComponent;
|
||||
let fixture: ComponentFixture<ResultComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ResultComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ResultComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
20
frontend/src/app/components/result/result.component.ts
Normal file
20
frontend/src/app/components/result/result.component.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {Result} from '../../interfaces/result.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'app-result',
|
||||
templateUrl: './result.component.html',
|
||||
styleUrls: ['./result.component.scss']
|
||||
})
|
||||
export class ResultComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
result: Result;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,7 +14,7 @@
|
||||
<section>
|
||||
<h2>What would you prefer?</h2>
|
||||
<div *ngFor="let key of multiPresetsKeys" class="sub-group">
|
||||
<span class="lable">{{key|translate}}:</span><br>
|
||||
<span class="label">{{key|translate}}:</span><br>
|
||||
<mat-button-toggle-group [(ngModel)]="multiPresetSelection[key]" [value]="undefined">
|
||||
<mat-button-toggle *ngFor="let preset of multiPresets.get(key)"
|
||||
[value]="preset.preset_id">{{preset.tag_label|translate}}</mat-button-toggle>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
> .label {
|
||||
font-size: large;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1 +1,6 @@
|
||||
<app-search-input></app-search-input>
|
||||
<h2>Explore the world</h2>
|
||||
<div class="region-container">
|
||||
<app-region *ngFor="let region of regions" [region]="region"></app-region>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,4 +1,19 @@
|
||||
:host {
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
app-search-input {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.region-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> app-region {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Region} from '../../interfaces/region.interface';
|
||||
import {DataService} from '../../services/data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
@ -7,9 +9,13 @@ import { Component, OnInit } from '@angular/core';
|
||||
})
|
||||
export class HomeComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
regions: Region[];
|
||||
|
||||
ngOnInit() {
|
||||
constructor(private ds: DataService) {
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.regions = await this.ds.getAllRegions();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,15 +2,10 @@
|
||||
|
||||
<span #result></span>
|
||||
|
||||
<mat-card *ngIf="results">
|
||||
<h2 matCardTitle>Suchergebnisse ({{results.length}}):</h2>
|
||||
<mat-card *ngFor="let result of results" class="resultCard">
|
||||
<h2 matCardTitle>{{result.name}} ({{result.score}})</h2>
|
||||
<ul>
|
||||
<li *ngFor="let score of result.scores">{{score.type|translate}}: {{score.value}}</li>
|
||||
</ul>
|
||||
</mat-card>
|
||||
</mat-card>
|
||||
<div *ngIf="results">
|
||||
<h2>Results ({{results.length}}):</h2>
|
||||
<app-result *ngFor="let result of results" [result]="result"></app-result>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!results" class="spinner">
|
||||
<mat-spinner></mat-spinner>
|
||||
|
||||
@ -8,7 +8,7 @@ export interface Region {
|
||||
country: string;
|
||||
/** Short description of the region */
|
||||
description: string;
|
||||
/** Min temperature means per month */
|
||||
/** Max temperature means per month */
|
||||
temperature_mean_max: number[];
|
||||
/** Monthly precipitation */
|
||||
precipitation: number[];
|
||||
|
||||
@ -3,14 +3,13 @@ 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 {MOCK_PRESETS, MOCK_REGIONS, MOCK_RESULT} from '../mock/mock-data';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DataService {
|
||||
|
||||
private readonly API_URL = 'https://example.com/api/v1';
|
||||
private readonly API_URL = 'https://travopti.de/api/v1';
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
@ -19,24 +18,24 @@ export class DataService {
|
||||
const params = new HttpParams();
|
||||
params.append('q', query);
|
||||
|
||||
// return this.http.get<Result[]>(this.API_URL + '/search', {params}).toPromise();
|
||||
return new Promise<Result[]>(resolve => {
|
||||
resolve(MOCK_RESULT);
|
||||
});
|
||||
return this.http.get<Result[]>(this.API_URL + '/search', {params}).toPromise();
|
||||
// return new Promise<Result[]>(resolve => {
|
||||
// resolve(MOCK_RESULT);
|
||||
// });
|
||||
}
|
||||
|
||||
public getAllPresets(): Promise<Preset[]> {
|
||||
// return this.http.get<Preset[]>(this.API_URL + '/search/presets').toPromise();
|
||||
return new Promise<Preset[]>(resolve => {
|
||||
resolve(MOCK_PRESETS);
|
||||
});
|
||||
return this.http.get<Preset[]>(this.API_URL + '/search/presets').toPromise();
|
||||
// return new Promise<Preset[]>(resolve => {
|
||||
// resolve(MOCK_PRESETS);
|
||||
// });
|
||||
}
|
||||
|
||||
public getAllRegions(): Promise<Region[]> {
|
||||
// return this.http.get<Region[]>(this.API_URL + '/regions').toPromise();
|
||||
return new Promise<Region[]>(resolve => {
|
||||
resolve(MOCK_REGIONS);
|
||||
});
|
||||
return this.http.get<Region[]>(this.API_URL + '/regions').toPromise();
|
||||
// return new Promise<Region[]>(resolve => {
|
||||
// resolve(MOCK_REGIONS);
|
||||
// });
|
||||
}
|
||||
|
||||
public getRegion(id: number): Promise<Region> {
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
"cheap_alcohol": "Cheap alcohol",
|
||||
"cheap_food": "Cheap food",
|
||||
"cheap_water": "Cheap water",
|
||||
"cheap_transportations": "Cheap pubic transport",
|
||||
"cheap_transportations": "Cheap public transport",
|
||||
"cheap_entertainment": "Cheap entertainment",
|
||||
"warm": "warm",
|
||||
"chilly": "cold",
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
|
||||
@import "theme";
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Roboto, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
91
frontend/src/theme.scss
Normal file
91
frontend/src/theme.scss
Normal file
@ -0,0 +1,91 @@
|
||||
@import '~@angular/material/theming';
|
||||
// Plus imports for other components in your app.
|
||||
|
||||
// Include the common styles for Angular Material. We include this here so that you only
|
||||
// have to load a single css file for Angular Material in your app.
|
||||
// Be sure that you only ever include this mixin once!
|
||||
@include mat-core();
|
||||
|
||||
$travopti-black: (
|
||||
50: #AAAAAA,
|
||||
100: #999999,
|
||||
200: #777777,
|
||||
300: #555555,
|
||||
400: #333333,
|
||||
500: #000000,
|
||||
600: #000000,
|
||||
700: #000000,
|
||||
800: #000000,
|
||||
900: #000000,
|
||||
A100: #ffffff,
|
||||
A200: #eeeeee,
|
||||
A400: #bdbdbd,
|
||||
A700: #616161,
|
||||
contrast: (
|
||||
50: $dark-primary-text,
|
||||
100: $dark-primary-text,
|
||||
200: $dark-primary-text,
|
||||
300: $dark-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: $light-primary-text,
|
||||
)
|
||||
);
|
||||
|
||||
$travopti-green: (
|
||||
50: #016500,
|
||||
100: #006500,
|
||||
200: #006500,
|
||||
300: #005a00,
|
||||
400: #008000,
|
||||
500: #006000,
|
||||
600: #005a00,
|
||||
700: #005a00,
|
||||
800: #2e7d32,
|
||||
900: #1b5e20,
|
||||
A100: #00ae00,
|
||||
A200: #009000,
|
||||
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,
|
||||
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,
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
// Define the palettes for your theme using the Material Design palettes available in palette.scss
|
||||
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
|
||||
// hue. Available color palettes: https://material.io/design/color/
|
||||
$travopti-app-primary: mat-palette($travopti-black);
|
||||
$travopti-app-accent: mat-palette($travopti-green, A200, A100, A400);
|
||||
|
||||
// The warn palette is optional (defaults to red).
|
||||
$travopti-app-warn: mat-palette($mat-red);
|
||||
|
||||
// Create the theme object (a Sass map containing all of the palettes).
|
||||
$travopti-app-theme: mat-light-theme($travopti-app-primary, $travopti-app-accent, $travopti-app-warn);
|
||||
|
||||
// Include theme styles for core and each component used in your app.
|
||||
// Alternatively, you can import and @include the theme mixins for each component
|
||||
// that you are using.
|
||||
@include angular-material-theme($travopti-app-theme);
|
||||
Loading…
Reference in New Issue
Block a user