Added the ability to have mutliple buy options for product
This commit is contained in:
parent
538c585cc1
commit
d94df9f206
@ -6,8 +6,9 @@ import { MatDialog } from '@angular/material';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { LocalStorage } from 'webstorage-decorators';
|
||||
import { AppStore } from './app.store';
|
||||
import { AngularFireAuth } from '../../node_modules/angularfire2/auth';
|
||||
import { SwUpdate } from '../../node_modules/@angular/service-worker';
|
||||
import { AngularFireAuth } from 'angularfire2/auth';
|
||||
import { SwUpdate } from '@angular/service-worker';
|
||||
import { POption } from './store/products/product';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@ -15,7 +16,7 @@ import { SwUpdate } from '../../node_modules/@angular/service-worker';
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
@LocalStorage({ defaultValue: [], encryptionKey: 'HmRoBFUEVWqW5uvy' })
|
||||
cart: { id: string; item: string; price: number; currency: 'CAD' | 'USD'; quantity: number }[];
|
||||
cart: { id: string; item: string; option: POption, quantity: number}[];
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
@ -30,8 +31,8 @@ export class AppComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
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(
|
||||
cartAdd(id: string, name: string, option: POption, quantity: number) {
|
||||
this.cart = [{ id: id, item: name, option: option, quantity: Number(quantity)}].concat(
|
||||
this.cart
|
||||
);
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
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';
|
||||
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 {
|
||||
@ -24,7 +24,7 @@ export class AppStore {
|
||||
.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());
|
||||
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;
|
||||
})
|
||||
@ -38,7 +38,7 @@ export class AppStore {
|
||||
.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());
|
||||
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;
|
||||
})
|
||||
@ -52,12 +52,12 @@ export class AppStore {
|
||||
).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());
|
||||
let temp = <any>Object.assign({ id: row.payload.doc.id, ref: row.payload.doc.ref }, row.payload.doc.data());
|
||||
temp.created = temp.created.toDate();
|
||||
|
||||
temp.components = temp.components.map(row => {
|
||||
let component = data[1].filter(c => c.id == row.component.id)[0];
|
||||
return {component: component, quantity: row.quantity};
|
||||
return { component: component, quantity: row.quantity };
|
||||
});
|
||||
|
||||
return <Formula>temp;
|
||||
@ -72,13 +72,12 @@ export class AppStore {
|
||||
.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());
|
||||
let temp = Object.assign({ id: row.payload.doc.id, ref: row.payload.doc.ref }, row.payload.doc.data());
|
||||
temp.originalImage = temp.image;
|
||||
temp.image = this.domSanitizer.bypassSecurityTrustUrl(temp.image);
|
||||
temp.originalDescription = temp.description;
|
||||
temp.description = this.domSanitizer.bypassSecurityTrustHtml(
|
||||
temp.description.replace(/(\r\n|\r|\n)/g, '<br>')
|
||||
);
|
||||
temp.description = this.domSanitizer.bypassSecurityTrustHtml(temp.description);
|
||||
|
||||
return <Product>temp;
|
||||
})
|
||||
),
|
||||
|
@ -1,9 +1,9 @@
|
||||
import {Component, Inject} from '@angular/core';
|
||||
import {MatDialogRef, MAT_DIALOG_DATA} from '@angular/material';
|
||||
import {AngularFirestore} from 'angularfire2/firestore';
|
||||
import {LocalStorage} from '../../../../node_modules/webstorage-decorators';
|
||||
import {AppStore} from '../../app.store';
|
||||
import {ConvertToGPipe} from '../units.pipe';
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||
import { AngularFirestore } from 'angularfire2/firestore';
|
||||
import { LocalStorage } from 'webstorage-decorators';
|
||||
import { AppStore } from '../../app.store';
|
||||
import { ConvertToGPipe } from '../units.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'new-formula',
|
||||
@ -14,9 +14,9 @@ export class NewFormulaComponent {
|
||||
amount: number;
|
||||
approved: boolean = false;
|
||||
component: string;
|
||||
components: {component: string; name: string; quantity: number}[] = [];
|
||||
components: { component: string; name: string; quantity: number }[] = [];
|
||||
componentsList = [];
|
||||
@LocalStorage({defaultValue: 'kg', fieldName: 'newFormulaUnit'})
|
||||
@LocalStorage({ defaultValue: 'kg', fieldName: 'newFormulaUnit' })
|
||||
unit;
|
||||
|
||||
constructor(
|
||||
@ -31,7 +31,7 @@ export class NewFormulaComponent {
|
||||
this.name = this.data.name;
|
||||
this.approved = this.data.approved;
|
||||
this.components = this.data.components.map(row => {
|
||||
return {component: row.component.id, name: row.component.name, quantity: row.quantity};
|
||||
return { component: row.component.id, name: row.component.name, quantity: row.quantity };
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -40,7 +40,7 @@ export class NewFormulaComponent {
|
||||
let id = this.componentsList.filter(row => row.name == this.component)[0].id;
|
||||
console.log(id);
|
||||
let amount = new ConvertToGPipe().transform(Number(this.amount), this.unit);
|
||||
this.components.push({component: id, name: this.component, quantity: amount});
|
||||
this.components.push({ component: id, name: this.component, quantity: amount });
|
||||
this.component = null;
|
||||
this.amount = null;
|
||||
}
|
||||
@ -54,7 +54,7 @@ export class NewFormulaComponent {
|
||||
name: this.name,
|
||||
approved: this.approved,
|
||||
components: this.components.map((row: any) => {
|
||||
return {component: this.db.collection('components').doc(row.component).ref, quantity: row.quantity};
|
||||
return { component: this.db.collection('components').doc(row.component).ref, quantity: row.quantity };
|
||||
})
|
||||
};
|
||||
|
||||
|
@ -15,6 +15,7 @@ export class CartComponent {
|
||||
constructor(public app: AppComponent) {}
|
||||
|
||||
ngOnInit() {
|
||||
console.log(this.app.cart);
|
||||
if (this.app.cartCount()) {
|
||||
window['paypal'].Button.render(
|
||||
{
|
||||
|
@ -13,20 +13,52 @@
|
||||
<mat-form-field class="w-100">
|
||||
<textarea matInput rows="5" placeholder="Description" name="description" [(ngModel)]="description"></textarea>
|
||||
</mat-form-field>
|
||||
<div class="mt-3 p-3 border rounded border-muted">
|
||||
<h5 mat-subheader class="mb-4 pl-0">Buying Options</h5>
|
||||
<button mat-stroked-button class="mb-4" color="accent" (click)="addOption()">Add</button>
|
||||
<mat-accordion>
|
||||
<mat-expansion-panel *ngFor="let o of options; let i = index">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
{{o.name || 'Option'}}
|
||||
</mat-panel-title>
|
||||
<mat-panel-description>
|
||||
{{o.currency}} {{o.price | currency}}
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Name" [(ngModel)]="o.name">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-4 text-center">
|
||||
<mat-form-field>
|
||||
<span matPrefix>$ </span>
|
||||
<input matInput placeholder="Price" type="number" name="price" [(ngModel)]="price">
|
||||
<input matInput placeholder="Price" type="number" name="price" [(ngModel)]="o.price">
|
||||
<mat-hint *ngIf="!price" align="start">0 will display "Contact For Price"</mat-hint>
|
||||
</mat-form-field>
|
||||
<mat-radio-group [(ngModel)]="currency" name="currency">
|
||||
<mat-radio-group [(ngModel)]="o.currency" name="currency">
|
||||
<mat-radio-button value="CAD" class="pl-3">CAD</mat-radio-button>
|
||||
<mat-radio-button value="USD" class="pl-3">USD</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<input #fileInput type="file" (change)="imageChanged($event)" hidden>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<mat-form-field class="float-right" style="width: 150px">
|
||||
<input matInput type="number" placeholder="Weight For Shipping" [(ngModel)]="weight">
|
||||
<input matInput type="number" placeholder="Weight For Shipping" [(ngModel)]="o.weight">
|
||||
<span matSuffix>lb</span>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<input #fileInput type="file" (change)="imageChanged($event)" hidden>
|
||||
<mat-action-row>
|
||||
<button mat-button color="warn" (click)="options.splice(i, 1)">
|
||||
Delete
|
||||
</button>
|
||||
</mat-action-row>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
</div>
|
||||
<div class="mt-3 p-3 border rounded border-muted">
|
||||
<h5 mat-subheader class="pl-0">Uploads</h5>
|
||||
<mat-progress-bar *ngIf="uploading" mode="indeterminate"></mat-progress-bar>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {Component, Inject} from '@angular/core';
|
||||
import {AngularFirestore} from 'angularfire2/firestore';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material';
|
||||
import {AppStore} from '../../app.store';
|
||||
import {AngularFireStorage} from '../../../../node_modules/angularfire2/storage';
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { AngularFirestore } from 'angularfire2/firestore';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
||||
import { AppStore } from '../../app.store';
|
||||
import { AngularFireStorage } from 'angularfire2/storage';
|
||||
|
||||
@Component({
|
||||
selector: 'new-item',
|
||||
@ -10,14 +10,12 @@ import {AngularFireStorage} from '../../../../node_modules/angularfire2/storage'
|
||||
})
|
||||
export class NewProductComponent {
|
||||
category;
|
||||
currency = 'CAD';
|
||||
description: string;
|
||||
files: {name: string; link: string; type: string}[] = [];
|
||||
files: { name: string; link: string; type: string }[] = [];
|
||||
image: string;
|
||||
linkError = false;
|
||||
name: string;
|
||||
price: number = 0.0;
|
||||
weight: number = 0;
|
||||
options: { name: string, price: number, currency: 'CAD' | 'USD', weight: number }[] = [];
|
||||
uploading = false;
|
||||
|
||||
constructor(
|
||||
@ -31,12 +29,12 @@ export class NewProductComponent {
|
||||
|
||||
if (data.product) {
|
||||
this.category = data.product.category;
|
||||
this.currency = data.product.currency;
|
||||
this.description = data.product.originalDescription;
|
||||
this.files = data.product.files;
|
||||
this.name = data.product.name;
|
||||
this.price = data.product.price;
|
||||
this.weight = data.product.weight;
|
||||
this.options = data.product.options;
|
||||
} else {
|
||||
this.addOption();
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +46,7 @@ export class NewProductComponent {
|
||||
let upload = await this.storage.ref(`${Math.round(Math.random() * 1000000)}/${file.name}`).put(file);
|
||||
this.uploading = false;
|
||||
if (upload.state == 'success')
|
||||
this.files.push({name: file.name, type: type, link: await upload.ref.getDownloadURL()});
|
||||
this.files.push({ name: file.name, type: type, link: await upload.ref.getDownloadURL() });
|
||||
}
|
||||
|
||||
addLink(link: string) {
|
||||
@ -57,13 +55,17 @@ export class NewProductComponent {
|
||||
).exec(link);
|
||||
if (!!valid) {
|
||||
if (!valid.groups['protocol']) link = `http://${link}`;
|
||||
this.files.push({name: valid.groups['name'], link: link, type: 'link'});
|
||||
this.files.push({ name: valid.groups['name'], link: link, type: 'link' });
|
||||
this.linkError = !valid;
|
||||
}
|
||||
|
||||
return !!valid;
|
||||
}
|
||||
|
||||
addOption() {
|
||||
this.options.push({ name: '', price: 0, currency: 'CAD', weight: 0 });
|
||||
}
|
||||
|
||||
imageChanged(event) {
|
||||
let reader = new FileReader();
|
||||
reader.readAsDataURL(event.target.files[0]);
|
||||
@ -73,12 +75,14 @@ export class NewProductComponent {
|
||||
submit() {
|
||||
let newProduct = {
|
||||
category: this.category,
|
||||
currency: this.currency,
|
||||
description: this.description,
|
||||
files: this.files,
|
||||
name: this.name,
|
||||
price: Number(this.price),
|
||||
weight: Number(this.weight) || 0
|
||||
options: this.options.map(row => {
|
||||
row.price = <number>row.price;
|
||||
row.weight = <number>row.weight;
|
||||
return row;
|
||||
})
|
||||
};
|
||||
if (this.image) newProduct['image'] = this.image;
|
||||
|
||||
|
25
src/app/store/products/product.ts
Normal file
25
src/app/store/products/product.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { SafeUrl, SafeHtml } from "@angular/platform-browser";
|
||||
|
||||
export interface PFile {
|
||||
link: string;
|
||||
name: string;
|
||||
type: 'link' | 'other' | 'preview';
|
||||
}
|
||||
|
||||
export interface POption {
|
||||
currency: 'CAD' | 'USD';
|
||||
name: string;
|
||||
price: number;
|
||||
weight: number;
|
||||
}
|
||||
|
||||
export interface Product {
|
||||
category: string;
|
||||
description: string | SafeHtml;
|
||||
files: any[]
|
||||
image: string | SafeUrl;
|
||||
name: string;
|
||||
options: POption[];
|
||||
originalDescription: string;
|
||||
originalImage: string;
|
||||
}
|
@ -16,18 +16,26 @@
|
||||
</div>
|
||||
<div class="col-12 col-md-9">
|
||||
<h2 class="roboto">{{product.name}}</h2>
|
||||
<h5 *ngIf="product.price" class="text-muted">{{product.currency}} {{product.price | currency}}</h5>
|
||||
<h5 *ngIf="!product.price" class="text-muted">Contact For Price</h5>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<p style="overflow: hidden;" [innerHtml]="product.description"></p>
|
||||
<div class="float-right">
|
||||
<mat-form-field *ngIf="product.options.length > 1">
|
||||
<mat-select placeholder="Buying Options" [(ngModel)]="option">
|
||||
<mat-option *ngFor="let o of product.options" [value]="o">
|
||||
{{o.name}} <span class="float-right text-muted">{{o.currency}} {{o.price | currency}}</span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<div class="d-inline ml-3">
|
||||
<mat-form-field class="mr-1" style="width: 40px">
|
||||
<span matPrefix>x</span>
|
||||
<input #quantity matInput type="number" value="1" min="1">
|
||||
</mat-form-field>
|
||||
<button mat-raised-button color="primary" [disabled]="quantity.value < 1" (click)="app.cartAdd(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, option, quantity.value)">
|
||||
<mat-icon>add_shopping_cart</mat-icon> Buy
|
||||
</button>
|
||||
</div>
|
||||
<h5 *ngIf="product.options.length == 1 && product.options[0].price > 0" class="text-muted">{{product.options[0].currency}} {{product.options[0].price | currency}}</h5>
|
||||
<h5 *ngIf="product.options.length == 1 && product.options[0].price == 0" class="text-muted">Contact For Price</h5>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<p style="overflow: hidden;" [innerHtml]="product.description"></p>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<div *ngIf="attachments?.length">
|
||||
<h5 class="ml-3">
|
||||
|
@ -1,10 +1,10 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {AppComponent} from '../../app.component';
|
||||
import {map} from 'rxjs/operators';
|
||||
import {AppStore} from '../../app.store';
|
||||
import {Product} from '../product';
|
||||
import {SafeUrl, DomSanitizer} from '../../../../node_modules/@angular/platform-browser';
|
||||
import { Component } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AppComponent } from '../../app.component';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { AppStore } from '../../app.store';
|
||||
import { Product } from '../product';
|
||||
import { SafeUrl, DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
@Component({
|
||||
selector: 'products',
|
||||
@ -13,15 +13,16 @@ import {SafeUrl, DomSanitizer} from '../../../../node_modules/@angular/platform-
|
||||
export class ProductsComponent {
|
||||
product: Product;
|
||||
preview: SafeUrl[];
|
||||
links: {name: string; link: string; type: string}[];
|
||||
attachments: {name: string; link: string; type: string}[];
|
||||
links: { name: string; link: string; type: string }[];
|
||||
attachments: { name: string; link: string; type: string }[];
|
||||
option;
|
||||
|
||||
constructor(
|
||||
private store: AppStore,
|
||||
private route: ActivatedRoute,
|
||||
private domSanitizer: DomSanitizer,
|
||||
public app: AppComponent
|
||||
) {}
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.route.params.subscribe(params => {
|
||||
|
Loading…
Reference in New Issue
Block a user