Converted everything to use a store

This commit is contained in:
Zakary Timson 2018-07-24 19:47:45 -04:00
parent 8eff0a6191
commit 2cccac9569
25 changed files with 280 additions and 247 deletions

View File

@ -24,13 +24,13 @@
</li> </li>
</ul> </ul>
<div class="ml-auto"> <div class="ml-auto">
<button *ngIf="!user" mat-stroked-button color="primary" (click)="login()">Login</button> <button *ngIf="!store.user" mat-stroked-button color="primary" (click)="login()">Login</button>
<div *ngIf="user" class="d-inline"> <div *ngIf="store.user" class="d-inline">
<span class="text-muted mr-3">{{user.email}}</span> <span class="text-muted mr-3">{{store.user.email}}</span>
<button mat-stroked-button color="accent" (click)="afAuth.auth.signOut()">Logout</button> <button mat-stroked-button color="accent" (click)="afAuth.auth.signOut()">Logout</button>
</div> </div>
<button *ngIf="cart.length" mat-icon-button class="ml-3" [routerLink]="['/cart']"> <button *ngIf="cart.length" mat-icon-button class="ml-3" [routerLink]="['/cart']">
<mat-icon style="transform: translateY(6px)" [matBadge]="cartItemCount()" matBadgeColor="warn"> <mat-icon style="transform: translateY(6px)" [matBadge]="cartCount()" matBadgeColor="warn">
shopping_cart shopping_cart
</mat-icon> </mat-icon>
</button> </button>

View File

@ -1,12 +1,12 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {Router, NavigationEnd} from '@angular/router'; import {Router, NavigationEnd} from '@angular/router';
import {ElectronService} from 'ngx-electron'; import {ElectronService} from 'ngx-electron';
import {AngularFirestore} from 'angularfire2/firestore';
import {filter} from 'rxjs/operators'; import {filter} from 'rxjs/operators';
import {MatDialog} from '@angular/material'; import {MatDialog} from '@angular/material';
import {LoginComponent} from './login/login.component'; import {LoginComponent} from './login/login.component';
import {AngularFireAuth} from 'angularfire2/auth';
import {LocalStorage} from 'webstorage-decorators'; import {LocalStorage} from 'webstorage-decorators';
import {AppStore} from './app.store';
import {AngularFireAuth} from '../../node_modules/angularfire2/auth';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -16,29 +16,29 @@ export class AppComponent implements OnInit {
@LocalStorage({defaultValue: []}) @LocalStorage({defaultValue: []})
cart: {id: string; item: string; price: number; currency: 'CAD' | 'USD'; quantity: number}[]; cart: {id: string; item: string; price: number; currency: 'CAD' | 'USD'; quantity: number}[];
categories;
user;
constructor( constructor(
private router: Router, private router: Router,
private db: AngularFirestore,
private dialog: MatDialog, private dialog: MatDialog,
public afAuth: AngularFireAuth, public electron: ElectronService,
public electron: ElectronService public store: AppStore,
) { public afAuth: AngularFireAuth
this.categories = this.db.collection('categories').valueChanges(); ) {}
}
addToCart(id: string, item: string, price: number, currency: 'CAD' | 'USD', quantity: number) { cartAdd(id: string, item: string, price: number, currency: 'CAD' | 'USD', quantity: number) {
this.cart = [{id: id, item: item, price: Number(price), currency: currency, quantity: Number(quantity)}].concat( this.cart = [{id: id, item: item, price: Number(price), currency: currency, quantity: Number(quantity)}].concat(
this.cart this.cart
); );
} }
cartItemCount() { cartCount() {
return this.cart.map(row => row.quantity).reduce((acc, row) => acc + row, 0); return this.cart.map(row => row.quantity).reduce((acc, row) => acc + row, 0);
} }
cartRemove(i) {
let temp = this.cart;
this.cart = temp.slice(i, 1);
}
login() { login() {
this.dialog.open(LoginComponent); this.dialog.open(LoginComponent);
} }
@ -57,9 +57,5 @@ export class AppComponent implements OnInit {
if (this.electron.isElectronApp) { if (this.electron.isElectronApp) {
this.router.navigate(['/formulaManager']); this.router.navigate(['/formulaManager']);
} }
this.afAuth.user.subscribe(user => {
this.user = user;
});
} }
} }

View File

@ -15,7 +15,7 @@ import {ServiceWorkerModule} from '@angular/service-worker';
import {FormulaManagerComponent} from './formulaManager/formulaManager.component'; import {FormulaManagerComponent} from './formulaManager/formulaManager.component';
import {NgxElectronModule} from 'ngx-electron'; import {NgxElectronModule} from 'ngx-electron';
import {AboutComponent} from './about/about.component'; import {AboutComponent} from './about/about.component';
import {CategoriesComponent} from './store/categories/categories.component'; import {CategoriesComponent} from './store/categories.component';
import {AngularFireStorageModule} from 'angularfire2/storage'; import {AngularFireStorageModule} from 'angularfire2/storage';
import {LoginComponent} from './login/login.component'; import {LoginComponent} from './login/login.component';
import {AngularFireAuthModule} from 'angularfire2/auth'; import {AngularFireAuthModule} from 'angularfire2/auth';
@ -27,6 +27,8 @@ import {CartComponent} from './store/cart/cart.component';
import {ViewComponents} from './formulaManager/viewComponents/viewComponents.component'; import {ViewComponents} from './formulaManager/viewComponents/viewComponents.component';
import {NewComponentComponent} from './formulaManager/newComponent/newComponent.component'; import {NewComponentComponent} from './formulaManager/newComponent/newComponent.component';
import {HttpModule} from '@angular/http'; import {HttpModule} from '@angular/http';
import {NewFormulaComponent} from './formulaManager/newFormula/newFormula.component';
import {AppStore} from './app.store';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -42,6 +44,7 @@ import {HttpModule} from '@angular/http';
LoginComponent, LoginComponent,
NewCategoryComponent, NewCategoryComponent,
NewComponentComponent, NewComponentComponent,
NewFormulaComponent,
NewProductComponent, NewProductComponent,
ProductsComponent, ProductsComponent,
ScalePipe, ScalePipe,
@ -70,12 +73,13 @@ import {HttpModule} from '@angular/http';
]), ]),
ServiceWorkerModule.register('/ngsw-worker.js', {enabled: environment.production}) ServiceWorkerModule.register('/ngsw-worker.js', {enabled: environment.production})
], ],
providers: [], providers: [AppStore],
entryComponents: [ entryComponents: [
DeleteComponent, DeleteComponent,
LoginComponent, LoginComponent,
NewCategoryComponent, NewCategoryComponent,
NewComponentComponent, NewComponentComponent,
NewFormulaComponent,
NewProductComponent, NewProductComponent,
ViewComponents ViewComponents
], ],

90
src/app/app.store.ts Normal file
View File

@ -0,0 +1,90 @@
import {Injectable} from '@angular/core';
import {AngularFirestore} from 'angularfire2/firestore';
import {Category} from './store/category';
import {Observable, combineLatest} from 'rxjs';
import {map, shareReplay} from 'rxjs/operators';
import {DomSanitizer} from '@angular/platform-browser';
import {Product} from './store/product';
import {AngularFireAuth} from '../../node_modules/angularfire2/auth';
import {Component} from './formulaManager/component';
import {Formula} from './formulaManager/formula';
@Injectable()
export class AppStore {
categories: Observable<Category[]>;
components: Observable<Component[]>;
formulas: Observable<Formula[]>;
products: Observable<Product[]>;
user: any;
constructor(private domSanitizer: DomSanitizer, private db: AngularFirestore, private auth: AngularFireAuth) {
this.categories = this.db
.collection('categories', row => row.orderBy('name'))
.snapshotChanges()
.pipe(
map(rows =>
rows.map((row: any) => {
let temp = Object.assign({id: row.payload.doc.id, ref: row.payload.doc.ref}, row.payload.doc.data());
temp.image = this.domSanitizer.bypassSecurityTrustUrl(temp.image);
return <Category>temp;
})
),
shareReplay(1)
);
this.components = this.db
.collection('components', row => row.orderBy('name'))
.snapshotChanges()
.pipe(
map(rows =>
rows.map((row: any) => {
let temp = Object.assign({id: row.payload.doc.id, ref: row.payload.doc.ref}, row.payload.doc.data());
temp.created = temp.created.toDate();
return <Component>temp;
})
),
shareReplay(1)
);
this.formulas = combineLatest(
this.db.collection('formulas', row => row.orderBy('name')).snapshotChanges(),
this.components
).pipe(
map(data =>
data[0].map(row => {
let temp = <any>Object.assign({id: row.payload.doc.id, ref: row.payload.doc.ref}, row.payload.doc.data());
temp.created = temp.created.toDate();
let newComponents = [];
temp.components.forEach((row: any) => {
let c = <Component>data[1].filter(c => c.id == row.component.id)[0];
newComponents.push({component: c, quantity: row.quantity});
});
temp.components = newComponents;
return <Formula>temp;
})
),
shareReplay(1)
);
this.products = this.db
.collection('products', row => row.orderBy('name'))
.snapshotChanges()
.pipe(
map(rows =>
rows.map((row: any) => {
let temp = Object.assign({id: row.payload.doc.id, ref: row.payload.doc.ref}, row.payload.doc.data());
temp.image = this.domSanitizer.bypassSecurityTrustUrl(temp.image);
temp.description = this.domSanitizer.bypassSecurityTrustHtml(
temp.description.replace(/(\r\n|\r|\n)/g, '<br>')
);
return <Product>temp;
})
),
shareReplay(1)
);
this.auth.user.subscribe(row => (this.user = row));
}
}

View File

@ -0,0 +1,10 @@
import {DocumentReference} from 'angularfire2/firestore';
export interface Component {
cost: number;
created: Date;
description: string;
id: string;
name: string;
ref: DocumentReference;
}

View File

@ -0,0 +1,11 @@
import {Component} from '@angular/core';
import {DocumentReference} from 'angularfire2/firestore';
export interface Formula {
approved: boolean;
components: {component: Component; quantity: number}[];
created: Date;
id: string;
name: string;
ref: DocumentReference;
}

View File

@ -1,11 +1,11 @@
<div class="container"> <div class="container">
<div *ngIf="user" class="row"> <div *ngIf="store.user" class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<div class="float-right"> <div class="float-right">
<button mat-raised-button class="mr-3" (click)="openComponents()"> <button mat-raised-button class="mr-3" (click)="openComponents()">
<mat-icon>list</mat-icon> Components <mat-icon>list</mat-icon> Components
</button> </button>
<button mat-raised-button> <button mat-raised-button (click)="newFormula()">
<mat-icon>add</mat-icon> Formula <mat-icon>add</mat-icon> Formula
</button> </button>
</div> </div>
@ -18,7 +18,7 @@
<mat-icon matSuffix>search</mat-icon> <mat-icon matSuffix>search</mat-icon>
</mat-form-field> </mat-form-field>
<mat-list style="max-height: 250px; overflow: auto;"> <mat-list style="max-height: 250px; overflow: auto;">
<ng-container *ngFor="let f of formulas | async; let i = index"> <ng-container *ngFor="let f of store.formulas | async; let i = index">
<mat-divider *ngIf="f.name.toLowerCase().indexOf(search.value.toLowerCase()) != -1 && i > 0"></mat-divider> <mat-divider *ngIf="f.name.toLowerCase().indexOf(search.value.toLowerCase()) != -1 && i > 0"></mat-divider>
<mat-list-item *ngIf="f.name.toLowerCase().indexOf(search.value.toLowerCase()) != -1" (click)="displayFormula(f)">{{f.name}}</mat-list-item> <mat-list-item *ngIf="f.name.toLowerCase().indexOf(search.value.toLowerCase()) != -1" (click)="displayFormula(f)">{{f.name}}</mat-list-item>
</ng-container> </ng-container>

View File

@ -6,21 +6,17 @@ import {LocalStorage} from 'webstorage-decorators';
import {MatDialog} from '@angular/material'; import {MatDialog} from '@angular/material';
import {ViewComponents} from './viewComponents/viewComponents.component'; import {ViewComponents} from './viewComponents/viewComponents.component';
import {AngularFireAuth} from 'angularfire2/auth'; import {AngularFireAuth} from 'angularfire2/auth';
import {NewFormulaComponent} from './newFormula/newFormula.component';
import {AppStore} from '../app.store';
@Component({ @Component({
selector: 'formula-manager', selector: 'formula-manager',
templateUrl: './formulaManager.component.html' templateUrl: './formulaManager.component.html'
}) })
export class FormulaManagerComponent { export class FormulaManagerComponent {
@ViewChildren('cost') componentCosts: ElementRef[];
formulas;
formula; formula;
installPrompt;
components;
@LocalStorage({defaultValue: 'g'}) @LocalStorage({defaultValue: 'g'})
unit; unit;
user;
_newTotal: number = 0; _newTotal: number = 0;
get newTotal() { get newTotal() {
@ -30,36 +26,13 @@ export class FormulaManagerComponent {
this._newTotal = new ConvertToGPipe().transform(total, this.unit); this._newTotal = new ConvertToGPipe().transform(total, this.unit);
} }
constructor( constructor(public electron: ElectronService, private dialog: MatDialog, public store: AppStore) {}
private db: AngularFirestore,
public electron: ElectronService,
private dialog: MatDialog,
private afAuth: AngularFireAuth
) {
this.formulas = this.db.collection('formulas', ref => ref.orderBy('name')).valueChanges();
this.afAuth.user.subscribe(user => {
this.user = user;
});
}
create(row: string) {
let data = new RegExp(/(.+?)\t(.+?)\t(.+?)\t\$(\d\.\d\d)\s*?(\w.*)/).exec(row);
this.db.collection('components').add({
name: data[1],
vendor: 'GCm9FzeJ8NNpBl6G9BCu',
description: data[3],
cost: data[4],
created: new Date(data[5])
});
}
openComponents() { openComponents() {
this.dialog.open(ViewComponents, {height: '500px'}); this.dialog.open(ViewComponents, {height: '500px'});
} }
cost() { cost() {
if (!this.formula || this.formula.components.filter(row => typeof row.component.get == 'function').length > 0)
return 0;
let cost = 0; let cost = 0;
this.formula.components.forEach( this.formula.components.forEach(
row => (cost += (((row.quantity / this.formula.total) * this._newTotal) / 1000) * row.component.cost) row => (cost += (((row.quantity / this.formula.total) * this._newTotal) / 1000) * row.component.cost)
@ -68,21 +41,12 @@ export class FormulaManagerComponent {
} }
displayFormula(formula) { displayFormula(formula) {
formula.components
.filter(row => typeof row.component.get == 'function')
.forEach((row, i, arr) => row.component.get().then(row => (arr[i].component = row.data())));
formula.total = formula.components.reduce((acc, row) => (acc += row.quantity), 0); formula.total = formula.components.reduce((acc, row) => (acc += row.quantity), 0);
this.newTotal = new ConvertFromGPipe().transform(formula.total, this.unit); this.newTotal = new ConvertFromGPipe().transform(formula.total, this.unit);
this.formula = formula; this.formula = formula;
} }
prompt() { newFormula() {
if (this.installPrompt) this.installPrompt.prompt(); this.dialog.open(NewFormulaComponent);
}
@HostListener('beforeinstallprompt', ['$event'])
setPrompt(e) {
this.installPrompt = e;
this.installPrompt.preventDefault();
} }
} }

View File

@ -0,0 +1 @@
Test

View File

@ -0,0 +1,15 @@
import {Component, Inject} from '@angular/core';
import {MatDialogRef, MAT_DIALOG_DATA} from '@angular/material';
import {AngularFirestore} from 'angularfire2/firestore';
@Component({
selector: 'new-formula',
templateUrl: './newFormula.component.html'
})
export class NewFormulaComponent {
constructor(
private dialogRef: MatDialogRef<NewFormulaComponent>,
private db: AngularFirestore,
@Inject(MAT_DIALOG_DATA) public data
) {}
}

View File

@ -12,7 +12,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let c of components | async"> <tr *ngFor="let c of store.components | async">
<td>{{c.name}}</td> <td>{{c.name}}</td>
<td>{{c.description}}</td> <td>{{c.description}}</td>
<td>{{c.created | date}}</td> <td>{{c.created | date}}</td>

View File

@ -4,32 +4,18 @@ import {map} from 'rxjs/operators';
import {MatDialog} from '@angular/material'; import {MatDialog} from '@angular/material';
import {DeleteComponent} from '../../delete/delete.component'; import {DeleteComponent} from '../../delete/delete.component';
import {NewComponentComponent} from '../newComponent/newComponent.component'; import {NewComponentComponent} from '../newComponent/newComponent.component';
import {AppStore} from '../../app.store';
@Component({ @Component({
selector: '', selector: '',
templateUrl: './viewComponents.component.html' templateUrl: './viewComponents.component.html'
}) })
export class ViewComponents { export class ViewComponents {
components; constructor(private dialog: MatDialog, public store: AppStore) {}
constructor(private db: AngularFirestore, private dialog: MatDialog) {
this.components = this.db
.collection('components')
.snapshotChanges()
.pipe(
map(rows =>
rows.map((row: any) => {
row = Object.assign({id: row.payload.doc.id, ref: row.payload.doc.ref}, row.payload.doc.data());
row.created = row.created.toDate();
return row;
})
)
);
}
createComponent(component?) { createComponent(component?) {
if (component) { if (component) {
this.dialog.open(NewComponentComponent, { data: component }) this.dialog.open(NewComponentComponent, {data: component});
} else { } else {
this.dialog.open(NewComponentComponent); this.dialog.open(NewComponentComponent);
} }

View File

@ -11,14 +11,14 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let item of cart; let i = index"> <tr *ngFor="let item of app.cart; let i = index">
<td>{{i + 1}}</td> <td>{{i + 1}}</td>
<td> <td>
<a [routerLink]="['/products', item.item]">{{item.item}}</a> <a [routerLink]="['/products', item.item]">{{item.item}}</a>
</td> </td>
<td>{{item.quantity}}</td> <td>{{item.quantity}}</td>
<td>{{item.currency}} {{item.price | currency}}</td> <td>{{item.currency}} {{item.price | currency}}</td>
<td (click)="remove(i)" style="width: 50px"> <td (click)="app.cartRemove(i)" style="width: 50px">
<button mat-icon-button> <button mat-icon-button>
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</button> </button>
@ -26,6 +26,6 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div id="paypal-button" class="float-right mt-3"></div> <div *ngIf="app.cartCount()" id="paypal-button" class="float-right mt-3"></div>
</mat-card> </mat-card>
</div> </div>

View File

@ -1,27 +1,21 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {LocalStorage} from 'webstorage-decorators'; import {AppComponent} from '../../app.component';
import {access} from 'fs';
import {Http} from '../../../../node_modules/@angular/http';
import {Router} from '../../../../node_modules/@angular/router';
@Component({ @Component({
selector: 'cart', selector: 'cart',
templateUrl: 'cart.component.html' templateUrl: 'cart.component.html'
}) })
export class CartComponent { export class CartComponent {
@LocalStorage({defaultValue: []})
cart: {id: string; item: string; price: number; curency: 'CAD' | 'USD'; quantity: number}[];
address1: string; address1: string;
address2: string; address2: string;
city: string; city: string;
province: string; province: string;
postal: string; postal: string;
constructor(private http: Http, private router: Router) {} constructor(public app: AppComponent) {}
ngOnInit() { ngOnInit() {
if (this.cart.length > 0) { if (this.app.cartCount()) {
window['paypal'].Button.render( window['paypal'].Button.render(
{ {
env: 'sandbox', env: 'sandbox',
@ -43,15 +37,4 @@ export class CartComponent {
); );
} }
} }
remove(i: number) {
console.log('fire');
let c = this.cart;
c.splice(i, 1);
this.cart = c;
}
total() {
return this.cart.reduce((acc, row) => acc + row.price * row.quantity, 0);
}
} }

View File

@ -2,7 +2,7 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="container"> <div class="container">
<div class="row py-3"> <div class="row py-3">
<div *ngIf="app.user" class="ml-auto mr-3 mb-3"> <div *ngIf="store.user" class="ml-auto mr-3 mb-3">
<button mat-raised-button (click)="createCategory()"> <button mat-raised-button (click)="createCategory()">
<mat-icon>playlist_add</mat-icon> Category <mat-icon>playlist_add</mat-icon> Category
</button> </button>
@ -18,27 +18,7 @@
</ol> </ol>
</nav> </nav>
<ng-container *ngFor="let c of categories | async"> <ng-container *ngIf="!((categories | async)?.length || (products | async)?.length)">
<mat-card class="m-2 mx-auto curs-pointer">
<div [routerLink]="['/store', c.name]">
<img *ngIf="c.image" mat-card-image [src]="c.image" [alt]="c.name" style="width: 200px; height: 200px;" class="mx-auto">
<mat-divider *ngIf="c.image" class="custom-line"></mat-divider>
<mat-card-content class="text-center" style="max-width:200px">
<h5>{{c.name}}</h5>
</mat-card-content>
</div>
<mat-card-actions *ngIf="app.user">
<button mat-raised-button (click)="createCategory(c)">
<mat-icon>edit</mat-icon>
</button>
<button mat-raised-button class="ml-3" (click)="delete(c)">
<mat-icon>delete</mat-icon>
</button>
</mat-card-actions>
</mat-card>
</ng-container>
<ng-container *ngIf="(categories | async)?.length == 0 && (products | async)?.length <= 0">
<div class="row w-100"> <div class="row w-100">
<div class="col-12 p-5"> <div class="col-12 p-5">
<mat-card class="mx-auto"> <mat-card class="mx-auto">
@ -51,6 +31,26 @@
</div> </div>
</ng-container> </ng-container>
<ng-container *ngFor="let c of categories | async">
<mat-card class="m-2 mx-auto curs-pointer">
<div [routerLink]="['/store', c.name]">
<img *ngIf="c.image" mat-card-image [src]="c.image" [alt]="c.name" style="width: 200px; height: 200px;" class="mx-auto">
<mat-divider *ngIf="c.image" class="custom-line"></mat-divider>
<mat-card-content class="text-center" style="max-width:200px">
<h5>{{c.name}}</h5>
</mat-card-content>
</div>
<mat-card-actions *ngIf="store.user">
<button mat-raised-button (click)="createCategory(c)">
<mat-icon>edit</mat-icon>
</button>
<button mat-raised-button class="ml-3" (click)="delete(c)">
<mat-icon>delete</mat-icon>
</button>
</mat-card-actions>
</mat-card>
</ng-container>
<ng-container *ngFor="let p of products | async"> <ng-container *ngFor="let p of products | async">
<mat-card class="m-2 curs-pointer mx-auto"> <mat-card class="m-2 curs-pointer mx-auto">
<div [routerLink]="['/products', p.name]"> <div [routerLink]="['/products', p.name]">
@ -60,7 +60,7 @@
<h5>{{p.name}}</h5> <h5>{{p.name}}</h5>
</mat-card-content> </mat-card-content>
</div> </div>
<mat-card-actions *ngIf="app.user"> <mat-card-actions *ngIf="store.user">
<button mat-raised-button (click)="createProduct(p)"> <button mat-raised-button (click)="createProduct(p)">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>

View File

@ -0,0 +1,51 @@
import {Component} from '@angular/core';
import {map} from 'rxjs/operators';
import {ActivatedRoute} from '@angular/router';
import {MatDialog} from '@angular/material';
import {NewCategoryComponent} from './newCategory/newCategory.component';
import {AppComponent} from '../app.component';
import {NewProductComponent} from './newProduct/newProduct.component';
import {DeleteComponent} from '../delete/delete.component';
import {AppStore} from '../app.store';
import {Category} from './category';
import {Observable} from 'rxjs';
import {Product} from './product';
@Component({
selector: 'store',
templateUrl: 'categories.component.html'
})
export class CategoriesComponent {
category: string;
categories: Observable<Category[]>;
products: Observable<Product[]>;
constructor(
private store: AppStore,
private route: ActivatedRoute,
private dialog: MatDialog,
public app: AppComponent
) {}
ngOnInit() {
this.route.params.subscribe(params => {
this.category = params['category'];
this.categories = this.store.categories.pipe(map(rows => rows.filter(row => row.parent == this.category)));
this.products = this.store.products.pipe(map(rows => rows.filter(row => row.category == this.category)));
});
}
createCategory(category) {
this.dialog.open(NewCategoryComponent, {data: {category: category, currentCategory: this.category}});
}
createProduct(product) {
this.dialog.open(NewProductComponent, {data: {product: product, currentCategory: this.category}});
}
delete(obj) {
this.dialog.open(DeleteComponent, {data: obj});
}
}

View File

@ -1,81 +0,0 @@
import {Component} from '@angular/core';
import {AngularFirestore} from 'angularfire2/firestore';
import {map} from 'rxjs/operators';
import {ActivatedRoute, Router} from '@angular/router';
import {MatDialog} from '@angular/material';
import {NewCategoryComponent} from '../newCategory/newCategory.component';
import {AppComponent} from '../../app.component';
import {DomSanitizer} from '@angular/platform-browser';
import {NewProductComponent} from '../newProduct/newProduct.component';
import {DeleteComponent} from '../../delete/delete.component';
@Component({
selector: 'store',
templateUrl: 'categories.component.html'
})
export class CategoriesComponent {
category: string;
categories;
products;
constructor(
private db: AngularFirestore,
private router: Router,
private route: ActivatedRoute,
private dialog: MatDialog,
private domSanitizer: DomSanitizer,
public app: AppComponent
) {}
ngOnInit() {
this.route.params.subscribe(params => {
this.category = params['category'];
this.categories = this.db
.collection('categories', ref => ref.orderBy('name'))
.snapshotChanges()
.pipe(
map(rows =>
rows
.map((row: any) =>
Object.assign({id: row.payload.doc.id, ref: row.payload.doc.ref}, row.payload.doc.data())
)
.filter((row: any) => (!this.category && !row.parent) || (this.category && row.parent == this.category))
.map((row: any) => {
row.image = this.domSanitizer.bypassSecurityTrustUrl(row.image);
return row;
})
)
);
this.products = this.db
.collection('products', ref => ref.orderBy('name'))
.snapshotChanges()
.pipe(
map(rows =>
rows
.map((row: any) =>
Object.assign({id: row.payload.doc.id, ref: row.payload.doc.ref}, row.payload.doc.data())
)
.filter((row: any) => row.category == this.category)
.map((row: any) => {
row.image = this.domSanitizer.bypassSecurityTrustUrl(row.image);
return row;
})
)
);
});
}
createCategory(category) {
this.dialog.open(NewCategoryComponent, {data: {category: category, currentCategory: this.category}});
}
createProduct(product) {
this.dialog.open(NewProductComponent, {data: {product: product, currentCategory: this.category}});
}
delete(obj) {
this.dialog.open(DeleteComponent, {data: obj});
}
}

10
src/app/store/category.ts Normal file
View File

@ -0,0 +1,10 @@
import {DocumentReference} from 'angularfire2/firestore';
import {SafeUrl} from '@angular/platform-browser';
export interface Category {
image: SafeUrl;
id: string;
name: string;
parent: string;
ref: DocumentReference;
}

View File

@ -9,7 +9,7 @@
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-select placeholder="Parent" [(value)]="parent"> <mat-select placeholder="Parent" [(value)]="parent">
<mat-option value="root">root</mat-option> <mat-option value="root">root</mat-option>
<mat-option *ngFor="let c of categories | async" [value]="c.name">{{c.name}}</mat-option> <mat-option *ngFor="let c of store.categories | async" [value]="c.name">{{c.name}}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<input #fileInput type="file" (change)="imageChanged()" hidden> <input #fileInput type="file" (change)="imageChanged()" hidden>

View File

@ -1,6 +1,7 @@
import {Component, ViewChild, Inject} from '@angular/core'; import {Component, ViewChild, Inject} from '@angular/core';
import {AngularFirestore} from 'angularfire2/firestore'; import {AngularFirestore} from 'angularfire2/firestore';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material';
import {AppStore} from '../../app.store';
@Component({ @Component({
selector: 'new-category', selector: 'new-category',
@ -9,7 +10,6 @@ import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material';
export class NewCategoryComponent { export class NewCategoryComponent {
@ViewChild('fileInput') fileInput; @ViewChild('fileInput') fileInput;
categories;
parent: string = 'root'; parent: string = 'root';
name: string; name: string;
image: string; image: string;
@ -17,9 +17,9 @@ export class NewCategoryComponent {
constructor( constructor(
private dialogRef: MatDialogRef<NewCategoryComponent>, private dialogRef: MatDialogRef<NewCategoryComponent>,
private db: AngularFirestore, private db: AngularFirestore,
@Inject(MAT_DIALOG_DATA) public data @Inject(MAT_DIALOG_DATA) public data,
public store: AppStore
) { ) {
this.categories = this.db.collection('categories', ref => ref.orderBy('name')).valueChanges();
if (data.currentCategory) this.parent = data.currentCategory; if (data.currentCategory) this.parent = data.currentCategory;
if (data.category) { if (data.category) {

View File

@ -8,7 +8,7 @@
</mat-form-field> </mat-form-field>
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-select placeholder="Category" name="category" [(value)]="category"> <mat-select placeholder="Category" name="category" [(value)]="category">
<mat-option *ngFor="let c of categories | async" [value]="c.name">{{c.name}}</mat-option> <mat-option *ngFor="let c of store.categories | async" [value]="c.name">{{c.name}}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field class="w-100"> <mat-form-field class="w-100">

View File

@ -1,6 +1,7 @@
import {Component, ViewChild, Inject} from '@angular/core'; import {Component, ViewChild, Inject} from '@angular/core';
import {AngularFirestore} from 'angularfire2/firestore'; import {AngularFirestore} from 'angularfire2/firestore';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material';
import {AppStore} from '../../app.store';
@Component({ @Component({
selector: 'new-item', selector: 'new-item',
@ -9,7 +10,6 @@ import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material';
export class NewProductComponent { export class NewProductComponent {
@ViewChild('fileInput') fileInput; @ViewChild('fileInput') fileInput;
categories;
category; category;
currency = 'CAD'; currency = 'CAD';
description: string; description: string;
@ -20,9 +20,9 @@ export class NewProductComponent {
constructor( constructor(
private dialogRef: MatDialogRef<NewProductComponent>, private dialogRef: MatDialogRef<NewProductComponent>,
private db: AngularFirestore, private db: AngularFirestore,
@Inject(MAT_DIALOG_DATA) public data @Inject(MAT_DIALOG_DATA) public data,
public store: AppStore
) { ) {
this.categories = this.db.collection('categories', ref => ref.orderBy('name')).valueChanges();
if (data.currentCategory) this.category = data.currentCategory; if (data.currentCategory) this.category = data.currentCategory;
if (data.product) { if (data.product) {

13
src/app/store/product.ts Normal file
View File

@ -0,0 +1,13 @@
import {DocumentReference} from 'angularfire2/firestore';
import {SafeUrl, SafeHtml} from '@angular/platform-browser';
export interface Product {
category: string;
currency: 'CAD' | 'USD';
description: SafeHtml;
id: string;
image: SafeUrl;
name: string;
price: number;
ref: DocumentReference;
}

View File

@ -7,7 +7,7 @@
<mat-form-field class="mr-1" style="width: 40px"> <mat-form-field class="mr-1" style="width: 40px">
<input #quantity matInput type="number" value="1" min="1"> <input #quantity matInput type="number" value="1" min="1">
</mat-form-field> </mat-form-field>
<button mat-raised-button color="primary" [disabled]="quantity.value < 1" (click)="app.addToCart(product.id, product.name, product.price, product.currency, quantity.value)"> <button mat-raised-button color="primary" [disabled]="quantity.value < 1" (click)="app.cartAdd(product.id, product.name, product.price, product.currency, quantity.value)">
<mat-icon>add_shopping_cart</mat-icon> Buy <mat-icon>add_shopping_cart</mat-icon> Buy
</button> </button>
</div> </div>

View File

@ -1,9 +1,8 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {AngularFirestore} from 'angularfire2/firestore';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import {DomSanitizer} from '@angular/platform-browser';
import {AppComponent} from '../../app.component'; import {AppComponent} from '../../app.component';
import {map} from 'rxjs/operators'; import {map} from 'rxjs/operators';
import {AppStore} from '../../app.store';
@Component({ @Component({
selector: 'products', selector: 'products',
@ -12,30 +11,11 @@ import {map} from 'rxjs/operators';
export class ProductsComponent { export class ProductsComponent {
product; product;
constructor( constructor(private store: AppStore, private route: ActivatedRoute, public app: AppComponent) {}
private route: ActivatedRoute,
private db: AngularFirestore,
private domSanitizer: DomSanitizer,
public app: AppComponent
) {}
ngOnInit() { ngOnInit() {
this.route.params.subscribe(params => { this.route.params.subscribe(params => {
this.db this.store.products.pipe(map(rows => rows.filter(row => row.name == params['product']))).subscribe(data => {
.collection('products', ref => ref.where('name', '==', params['product']))
.snapshotChanges()
.pipe(
map(rows =>
rows.map((row: any) => Object.assign({id: row.payload.doc.id}, row.payload.doc.data())).map((row: any) => {
row.image = this.domSanitizer.bypassSecurityTrustUrl(row.image);
row.description = this.domSanitizer.bypassSecurityTrustHtml(
row.description.replace(/(\r\n|\r|\n)/g, '<br>')
);
return row;
})
)
)
.subscribe(data => {
this.product = data[0]; this.product = data[0];
}); });
}); });