Skip to content

Commit

Permalink
Dynamic loading indicator and some long due UI polish
Browse files Browse the repository at this point in the history
  • Loading branch information
fabian-marquardt committed Nov 20, 2017
1 parent 4e15bce commit 8468d90
Show file tree
Hide file tree
Showing 14 changed files with 221 additions and 142 deletions.
2 changes: 1 addition & 1 deletion src/app/app.component.ts
Expand Up @@ -2,7 +2,7 @@ import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: '<flash-messages></flash-messages><router-outlet></router-outlet>'
template: '<wait-indicator></wait-indicator><flash-messages></flash-messages><router-outlet></router-outlet>'
})
export class AppComponent {
version = "1.0"
Expand Down
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Expand Up @@ -18,6 +18,7 @@ import {
SelfServicePointComponent,
NavbarComponent,
FlashMessageComponent,
WaitIndicatorComponent,
HomeComponent
} from "app/components";
import { ConfigService, FlashMessageService } from "app/services";
Expand All @@ -37,6 +38,7 @@ import { ConfigService, FlashMessageService } from "app/services";
SelfServicePointComponent,
NavbarComponent,
FlashMessageComponent,
WaitIndicatorComponent,
HomeComponent
],
imports: [
Expand Down
1 change: 1 addition & 0 deletions src/app/components/index.ts
Expand Up @@ -10,3 +10,4 @@ export { SelfServicePointComponent } from "./self-service-point.component"
export { UserListComponent } from "./user-list.component";
export { UserEditComponent } from "./user-edit.component";
export { UserImportComponent } from "./user-import.component";
export { WaitIndicatorComponent } from "./wait-indicator.component"
6 changes: 1 addition & 5 deletions src/app/components/product-edit.component.ts
Expand Up @@ -11,8 +11,7 @@ import { BackendService, FlashMessageService } from "app/services";
})
export class ProductEditComponent implements OnInit {

product: Product;
wait_save: boolean = false;
private product: Product;

constructor(
private backend_service: BackendService,
Expand Down Expand Up @@ -62,7 +61,6 @@ export class ProductEditComponent implements OnInit {
}

save(): void {
this.wait_save = true;
this.backend_service.saveProduct(this.product)
.subscribe(
product => {
Expand All @@ -71,8 +69,6 @@ export class ProductEditComponent implements OnInit {
},
error => {
this.flash_message_service.flash('Failed to save product!', 'alert-danger');
this.wait_save = false;
console.log(error);
}
);
}
Expand Down
6 changes: 1 addition & 5 deletions src/app/components/user-edit.component.ts
Expand Up @@ -11,8 +11,7 @@ import { BackendService, FlashMessageService } from "app/services";
})
export class UserEditComponent implements OnInit {

user: User;
wait_save: boolean = false;
private user: User;

constructor(
private backend_service: BackendService,
Expand Down Expand Up @@ -54,7 +53,6 @@ export class UserEditComponent implements OnInit {
}

save(): void {
this.wait_save = true;
this.backend_service.saveUser(this.user)
.subscribe(
user => {
Expand All @@ -63,8 +61,6 @@ export class UserEditComponent implements OnInit {
},
error => {
this.flashMessageService.flash('Failed to save user!', 'alert-danger');
this.wait_save = false;
console.log(error);
}
);
}
Expand Down
17 changes: 17 additions & 0 deletions src/app/components/wait-indicator.component.ts
@@ -0,0 +1,17 @@
import { Component, OnInit } from "@angular/core";
import { BackendService } from "app/services"

@Component({
selector: 'wait-indicator',
templateUrl: '../templates/wait-indicator.html',
providers: []
})
export class WaitIndicatorComponent implements OnInit{
requestActive: boolean;

constructor(private backendService: BackendService) {}

ngOnInit() {
this.backendService.getRequestActive().subscribe(value => this.requestActive = value);
}
}
5 changes: 5 additions & 0 deletions src/app/services/backend.service.ts
@@ -1,5 +1,6 @@
import { Product, User, Identifiable, Cart, PaymentTransaction, Transaction } from "../models";
import { Observable } from "rxjs/Observable";
import { BehaviorSubject } from "rxjs/BehaviorSubject";

export class BackendService{
getProducts(): Observable<Product[]> {
Expand Down Expand Up @@ -41,4 +42,8 @@ export class BackendService{
deposit(user: User, amount: number): Observable<Transaction> {
throw Error('Not implemented.');
}

getRequestActive(): BehaviorSubject<boolean> {
throw Error('Not implemented.');
}
}
31 changes: 30 additions & 1 deletion src/app/services/oskiosk-backend.service.ts
Expand Up @@ -2,37 +2,62 @@ import { Http, Response, Headers, RequestOptions } from "@angular/http";
import { deserialize, deserializeArray, serialize } from "class-transformer";

import { Observable } from "rxjs/Observable";
import { BehaviorSubject } from "rxjs/BehaviorSubject";
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/finally';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/throw';

import { Product, User, Identifiable, Cart, PaymentTransaction, Transaction } from "app/models";
import { BackendService } from "./backend.service";

export class OskioskBackendService extends BackendService{

private activeRequestCount: number = 0;
private requestActive: BehaviorSubject<boolean> = new BehaviorSubject(false);

constructor(private http: Http, private api_url: string, private api_token: string){
super();
}

private onRequestStart() {
this.activeRequestCount++;
this.requestActive.next(true);
console.log(this.activeRequestCount)
}

private onRequestEnd() {
this.activeRequestCount--;
if(this.activeRequestCount == 0) {
this.requestActive.next(false);
}
console.log(this.activeRequestCount)
}

private getDefaultRequestOptions(): RequestOptions {
let headers = new Headers({
'Authorization': 'Bearer ' + this.api_token,
'Content-Type': 'application/json'
});
return new RequestOptions({ headers: headers });
}
}

private httpGet(url: string): Observable<Response> {
this.onRequestStart();
return this.http.get(this.api_url + url, this.getDefaultRequestOptions())
.finally(() => this.onRequestEnd());
}

private httpPost(url: string, data: string): Observable<Response> {
this.onRequestStart();
return this.http.post(this.api_url + url, data, this.getDefaultRequestOptions())
.finally(() => this.onRequestEnd());
}

private httpPatch(url: string, data: string): Observable<Response> {
this.onRequestStart();
return this.http.patch(this.api_url + url, data, this.getDefaultRequestOptions())
.finally(() => this.onRequestEnd());
}

private handleError (error: Response | any) {
Expand All @@ -50,6 +75,10 @@ export class OskioskBackendService extends BackendService{
}
}

getRequestActive(): BehaviorSubject<boolean> {
return this.requestActive;
}

getProducts(): Observable<Product[]> {
return this.httpGet('/products.json')
.map((res: Response) => { return deserializeArray(Product, res.text()); })
Expand Down
106 changes: 51 additions & 55 deletions src/app/templates/product-edit.html
Expand Up @@ -4,13 +4,6 @@
<h1 *ngIf="product.id" class="mt-2">Edit product</h1>
<h1 *ngIf="!product.id" class="mt-2">Create product</h1>

<form class="form-inline">
<button type="submit" class="btn btn-success mb-2 mr-2" (click)="save()">
<i *ngIf="wait_save" class="fa fa-spinner fa-spin fa-fw"></i> Save changes
</button>
<a routerLink="/products" class="btn btn-warning mb-2 mr-2">Discard changes</a>
</form>

<div class="card mt-2">
<div class="card-body">
<h4>Product name</h4>
Expand All @@ -19,71 +12,74 @@ <h4>Product name</h4>
<input class="form-control" type="text" [(ngModel)]="product.name">
</div>
</div>
</div>
</div>


<div class="row" *ngIf="product">
<div class="col-lg-4 mt-2">
<div class="card">
<div class="card-body">
<h4>Tags</h4>
<div *ngFor="let tag of product.tags" class="form-group row">
<div class="col-12">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-tag" aria-hidden="true"></i></span>
<input class="form-control" type="text" [(ngModel)]="tag.name">
<span class="input-group-btn">
<button (click)="deleteTag(tag)" class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i></button>
</span>
<div class="row" *ngIf="product">
<div class="col-lg-4 mt-2">
<div class="card">
<div class="card-body">
<h4>Tags</h4>
<div *ngFor="let tag of product.tags" class="form-group row">
<div class="col-12">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-tag" aria-hidden="true"></i></span>
<input class="form-control" type="text" [(ngModel)]="tag.name">
<span class="input-group-btn">
<button (click)="deleteTag(tag)" class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i></button>
</span>
</div>
</div>
</div>
<button (click)="addTag()" class="btn btn-secondary"><i class="fa fa-plus" aria-hidden="true"></i> Add tag</button>
</div>
</div>
<button (click)="addTag()" class="btn btn-secondary"><i class="fa fa-plus" aria-hidden="true"></i> Add tag</button>
</div>
</div>
</div>
<div class="col-lg-4 mt-2">
<div class="card">
<div class="card-body">
<h4>Barcodes</h4>
<div *ngFor="let identifier of product.identifiers" class="form-group row">
<div class="col-12">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-barcode" aria-hidden="true"></i></span>
<input class="form-control" type="text" [(ngModel)]="identifier.code">
<span class="input-group-btn">
<button (click)="deleteIdentifier(identifier)" class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i></button>
</span>
<div class="col-lg-4 mt-2">
<div class="card">
<div class="card-body">
<h4>Barcodes</h4>
<div *ngFor="let identifier of product.identifiers" class="form-group row">
<div class="col-12">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-barcode" aria-hidden="true"></i></span>
<input class="form-control" type="text" [(ngModel)]="identifier.code">
<span class="input-group-btn">
<button (click)="deleteIdentifier(identifier)" class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i></button>
</span>
</div>
</div>
</div>
<button (click)="addIdentifier()" class="btn btn-secondary"><i class="fa fa-plus" aria-hidden="true"></i> Add barcode</button>
</div>
</div>
<button (click)="addIdentifier()" class="btn btn-secondary"><i class="fa fa-plus" aria-hidden="true"></i> Add barcode</button>
</div>
</div>
</div>
<div class="col-lg-4 mt-2">
<div class="card">
<div class="card-body">
<h4>Pricings</h4>
<div class="card mt-2" *ngFor="let pricing of product.pricings; let i = index">
<div class="col-lg-4 mt-2">
<div class="card">
<div class="card-body">
<div class="form-group">
<label for="quantity-{{ i }}">Quantity in stock</label>
<input id="quantity-{{ i }}" type="number" min="0" step="1" class="form-control" [(ngModel)]="pricing.quantity">
<label class="mt-2" for="price-{{ i }}">Unit price</label>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-euro" aria-hidden="true"></i></span>
<input id="price-{{ i }}" type="number" min="0" step="0.01" class="form-control" [(ngModel)]="pricing.decimal_price">
<h4>Pricings</h4>
<div class="card mt-2" *ngFor="let pricing of product.pricings; let i = index">
<div class="card-body">
<div class="form-group">
<label for="quantity-{{ i }}">Quantity in stock</label>
<input id="quantity-{{ i }}" type="number" min="0" step="1" class="form-control" [(ngModel)]="pricing.quantity">
<label class="mt-2" for="price-{{ i }}">Unit price</label>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-euro" aria-hidden="true"></i></span>
<input id="price-{{ i }}" type="number" min="0" step="0.01" class="form-control" [(ngModel)]="pricing.decimal_price">
</div>
<button (click)="deletePricing(pricing)" class="btn btn-danger mt-2"><i class="fa fa-trash" aria-hidden="true"></i> Delete</button>
</div>
</div>
<button (click)="deletePricing(pricing)" class="btn btn-danger mt-2"><i class="fa fa-trash" aria-hidden="true"></i> Delete</button>
</div>
<button (click)="addPricing()" class="btn btn-secondary mt-2"><i class="fa fa-plus" aria-hidden="true"></i> Add pricing</button>
</div>
</div>
<button (click)="addPricing()" class="btn btn-secondary mt-2"><i class="fa fa-plus" aria-hidden="true"></i> Add pricing</button>
</div>
</div>
</div>
<div class="card-footer">
<button type="submit" class="btn btn-success" (click)="save()"><i class="fa fa-check"></i> Save changes</button>
<a routerLink="/products" class="btn btn-warning"><i class="fa fa-undo"></i> Discard changes</a>
</div>
</div>
</div>

41 changes: 26 additions & 15 deletions src/app/templates/product-list.html
Expand Up @@ -3,21 +3,32 @@
<div class="container-fluid">
<h1 class="mt-2"> Products</h1>

<form class="form-inline">
<a routerLink="/product/new" class="btn btn-success mb-2 mr-2">New product</a>
<a routerLink="/product/import" class="btn btn-success mb-2 mr-2">Import products</a>
<input type="text" class="form-control mb-2" placeholder="Filter products" [(ngModel)]="filter" name="filter">
</form>

<div class="row">
<div class="col-md-3" *ngFor="let product of filteredProducts">
<div class="card" style="overflow: hidden">
<div class="card-body">
<h4 class="card-title">{{ product.name }}</h4>
<a routerLink="/product/{{ product.id }}" class="btn btn-outline-primary">Edit</a>
<a href="#" class="btn btn-outline-danger">Delete</a>
</div>
<div class="form-inline">
<a routerLink="/product/new" class="btn btn-success mr-2"><i class="fa fa-plus"></i> New product</a>
<a routerLink="/product/import" class="btn btn-success mr-2"><i class="fa fa-upload"></i> Import products</a>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-search"></i></span>
<input class="form-control" type="text" placeholder="Filter products" [(ngModel)]="filter">
</div>
</div>
</div>

<table class="table table-striped mt-2">
<thead>
<tr>
<th class="text-left w-90">Product name</th>
<th class="text-right w-10">Unit price</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of filteredProducts">
<td>
<a routerLink="/product/{{ item.id }}" class="btn btn-sm btn-outline-primary mr-2"><i class="fa fa-pencil"></i></a>
{{ item.name }}
<span *ngFor="let tag of item.tags" class="badge badge-secondary ml-1">{{ tag.name }}</span>
<td class="text-right">
<span *ngIf="item.pricings.length > 0">{{ item.pricings[0].decimal_price | currency:'EUR':true:'1.2-2' }}</span>
</td>
</tr>
</tbody>
</table>
</div>

0 comments on commit 8468d90

Please sign in to comment.