Add basic result page
This commit is contained in:
parent
a9eab54674
commit
56818c4bff
@ -1,11 +1,13 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import { Routes, RouterModule } from '@angular/router';
|
import {RouterModule, Routes} from '@angular/router';
|
||||||
import {HomeComponent} from './containers/home/home.component';
|
import {HomeComponent} from './containers/home/home.component';
|
||||||
import {NotfoundComponent} from './containers/notfound/notfound.component';
|
import {NotfoundComponent} from './containers/notfound/notfound.component';
|
||||||
|
import {SearchComponent} from './containers/search/search.component';
|
||||||
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: 'home', component: HomeComponent},
|
{path: 'home', component: HomeComponent},
|
||||||
|
{path: 'search', component: SearchComponent},
|
||||||
{path: '', redirectTo: 'home', pathMatch: 'full'},
|
{path: '', redirectTo: 'home', pathMatch: 'full'},
|
||||||
{path: '**', component: NotfoundComponent}
|
{path: '**', component: NotfoundComponent}
|
||||||
];
|
];
|
||||||
|
|||||||
@ -14,12 +14,17 @@ import {MatFormFieldModule} from '@angular/material/form-field';
|
|||||||
import {MatInputModule} from '@angular/material/input';
|
import {MatInputModule} from '@angular/material/input';
|
||||||
import {MatSelectModule} from '@angular/material/select';
|
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({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
HomeComponent,
|
HomeComponent,
|
||||||
NotfoundComponent
|
NotfoundComponent,
|
||||||
|
SearchComponent,
|
||||||
|
SearchInputComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -32,7 +37,8 @@ import { NotfoundComponent } from './containers/notfound/notfound.component';
|
|||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatSelectModule
|
MatSelectModule,
|
||||||
|
MatProgressSpinnerModule
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
<mat-card class="search-container">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Search</mat-label>
|
||||||
|
<input #textSearch matInput>
|
||||||
|
<button (click)="textSearch.value=''" *ngIf="textSearch.value.length>0" mat-icon-button matSuffix>
|
||||||
|
<mat-icon>clear</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Start</mat-label>
|
||||||
|
<input matInput type="date">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>End</mat-label>
|
||||||
|
<input matInput type="date">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>Price</mat-label>
|
||||||
|
<mat-select value="Budget">
|
||||||
|
<mat-option value="Budget">Budget</mat-option>
|
||||||
|
<mat-option value="Mid-Range">Mid-Range</mat-option>
|
||||||
|
<mat-option value="Luxury">Luxury</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<button (click)="onSearch()" color="primary" mat-flat-button>Search
|
||||||
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-card>
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
.search-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
@ -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<SearchInputComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SearchInputComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SearchInputComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -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)}});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,28 +1 @@
|
|||||||
<mat-card class="search-container">
|
<app-search-input></app-search-input>
|
||||||
<mat-form-field appearance="outline">
|
|
||||||
<mat-label>Search</mat-label>
|
|
||||||
<input #textSearch matInput>
|
|
||||||
<button *ngIf="textSearch.value.length>0" mat-icon-button matSuffix (click)="textSearch.value=''">
|
|
||||||
<mat-icon>clear</mat-icon>
|
|
||||||
</button>
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-form-field appearance="outline">
|
|
||||||
<mat-label>Start</mat-label>
|
|
||||||
<input matInput type="date">
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-form-field appearance="outline">
|
|
||||||
<mat-label>End</mat-label>
|
|
||||||
<input matInput type="date">
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-form-field>
|
|
||||||
<mat-label>Price</mat-label>
|
|
||||||
<mat-select value="Budget">
|
|
||||||
<mat-option value="Budget">Budget</mat-option>
|
|
||||||
<mat-option value="Mid-Range">Mid-Range</mat-option>
|
|
||||||
<mat-option value="Luxury">Luxury</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
<button mat-flat-button color="primary">Search
|
|
||||||
<mat-icon matSuffix>search</mat-icon>
|
|
||||||
</button>
|
|
||||||
</mat-card>
|
|
||||||
|
|||||||
@ -2,8 +2,3 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|||||||
12
frontend/src/app/containers/search/search.component.html
Normal file
12
frontend/src/app/containers/search/search.component.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<app-search-input></app-search-input>
|
||||||
|
|
||||||
|
<mat-card *ngIf="results">
|
||||||
|
<h2 matCardTitle>Suchergebnisse ({{results.length}}):</h2>
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let result of results">{{result.name}} ({{result.score}})</li>
|
||||||
|
</ul>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<div *ngIf="!results" class="spinner">
|
||||||
|
<mat-spinner></mat-spinner>
|
||||||
|
</div>
|
||||||
17
frontend/src/app/containers/search/search.component.scss
Normal file
17
frontend/src/app/containers/search/search.component.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
25
frontend/src/app/containers/search/search.component.spec.ts
Normal file
25
frontend/src/app/containers/search/search.component.spec.ts
Normal file
@ -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<SearchComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SearchComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SearchComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
30
frontend/src/app/containers/search/search.component.ts
Normal file
30
frontend/src/app/containers/search/search.component.ts
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,15 +1,14 @@
|
|||||||
import {SearchParam} from './search-request.interface';
|
|
||||||
|
|
||||||
/** Represents the structure of one search result. */
|
/** Represents the structure of one search result. */
|
||||||
export interface Result {
|
export interface Result {
|
||||||
region_id: string;
|
region_id: number;
|
||||||
region_name: string;
|
name: string;
|
||||||
|
country_id: number;
|
||||||
score: number;
|
score: number;
|
||||||
scores: Score[];
|
scores: Score[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Score {
|
export interface Score {
|
||||||
type: SearchParam;
|
type: string;
|
||||||
value: number;
|
value: number;
|
||||||
score: number;
|
score: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export enum SearchParam {
|
|||||||
FOOD = 'food'
|
FOOD = 'food'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchParams {
|
export interface Query {
|
||||||
from: number;
|
from: number;
|
||||||
to: number;
|
to: number;
|
||||||
price?: number[];
|
price?: number[];
|
||||||
|
|||||||
242
frontend/src/app/mock/mock-data.ts
Normal file
242
frontend/src/app/mock/mock-data.ts
Normal file
@ -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
|
||||||
|
}
|
||||||
|
];
|
||||||
@ -2,7 +2,6 @@ import {Injectable} from '@angular/core';
|
|||||||
import {HttpClient, HttpParams} from '@angular/common/http';
|
import {HttpClient, HttpParams} from '@angular/common/http';
|
||||||
import {Preset} from '../interfaces/preset.interface';
|
import {Preset} from '../interfaces/preset.interface';
|
||||||
import {Result} from '../interfaces/result.interface';
|
import {Result} from '../interfaces/result.interface';
|
||||||
import {SearchParams} from '../interfaces/search-request.interface';
|
|
||||||
import {Region} from '../interfaces/region.interface';
|
import {Region} from '../interfaces/region.interface';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@ -15,9 +14,9 @@ export class DataService {
|
|||||||
constructor(private http: HttpClient) {
|
constructor(private http: HttpClient) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public searchRegions(searchParams: SearchParams): Promise<Result[]> {
|
public searchRegions(query: string): Promise<Result[]> {
|
||||||
const params = new HttpParams();
|
const params = new HttpParams();
|
||||||
params.append('q', btoa(JSON.stringify(searchParams)));
|
params.append('q', query);
|
||||||
|
|
||||||
return this.http.get<Result[]>(this.API_URL + 'search', {params}).toPromise();
|
return this.http.get<Result[]>(this.API_URL + 'search', {params}).toPromise();
|
||||||
}
|
}
|
||||||
|
|||||||
15
frontend/src/app/utils/base64conversion.ts
Normal file
15
frontend/src/app/utils/base64conversion.ts
Normal file
@ -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));
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user