From dce8da391ee9704da2a543f3459c3c74b1b597f1 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 27 Oct 2021 15:21:55 -0500 Subject: [PATCH] Implement order creation and cancellation --- backend/app.js | 30 ++++++++- backend/models/order.js | 2 +- src/app/app.module.ts | 6 +- src/app/cancel/cancel.component.css | 3 + src/app/cancel/cancel.component.html | 24 ++++++++ src/app/cancel/cancel.component.ts | 23 +++++++ src/app/checkout/checkout.component.css | 0 src/app/checkout/checkout.component.html | 0 src/app/checkout/checkout.component.ts | 0 src/app/items/item-add/item-add.component.css | 4 ++ .../items/item-add/item-add.component.html | 2 +- .../item-create/item-create.component.css | 4 ++ .../items/item-list/item-list.component.css | 8 ++- .../items/item-list/item-list.component.html | 12 ++-- src/app/order/order.component.css | 6 +- src/app/order/order.component.html | 48 ++++++++++++--- src/app/order/order.component.ts | 34 +++++++++- src/app/order/order.service.ts | 58 ++++++++++++++---- src/assets/zec-roboto.png | Bin 0 -> 767 bytes 19 files changed, 228 insertions(+), 36 deletions(-) create mode 100644 src/app/cancel/cancel.component.css create mode 100644 src/app/cancel/cancel.component.html create mode 100644 src/app/cancel/cancel.component.ts create mode 100644 src/app/checkout/checkout.component.css create mode 100644 src/app/checkout/checkout.component.html create mode 100644 src/app/checkout/checkout.component.ts create mode 100644 src/assets/zec-roboto.png diff --git a/backend/app.js b/backend/app.js index c3a89a4..8b952c7 100644 --- a/backend/app.js +++ b/backend/app.js @@ -287,7 +287,35 @@ app.post('/api/order', (req, res, next) => { const order = new ordermodel(req.body.order); order.save(); res.status(200).json({ - message: 'Order added' + message: 'Order added', + order: order + }); +}); + +app.post('/api/lineitem', (req, res, next) => { + console.log('Post /api/lineitem'); + ordermodel.findByIdAndUpdate(req.body.order_id, { $push: {lines: req.body.line}}, function(err,docs) { + if (err) { + console.log(err); + } else { + res.status(200).json({ + message: 'Item added to order' + }); + } + }); +}); + +app.delete('/api/order/:id', (req, res, next) => { + console.log('delete order endpoint', req.params.id); + ordermodel.findByIdAndDelete(req.params.id, function (err, docs) { + if (err) { + console.log(err); + } else { + console.log(docs); + res.status(200).json({ + message: 'Order deleted' + }); + } }); }); diff --git a/backend/models/order.js b/backend/models/order.js index 64af430..19c644e 100644 --- a/backend/models/order.js +++ b/backend/models/order.js @@ -7,7 +7,7 @@ const orderSchema = mongoose.Schema({ closed: { type: Boolean, required: true, default:false }, lines: [{ qty: {type: Number, required: true, default: 1}, - item: { type: String, required: true}, + name: { type: String, required: true}, cost: { type: Number, required: true, default: 0} }] }); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 34e1e06..5e91973 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -20,6 +20,7 @@ import { ItemCreateComponent } from './items/item-create/item-create.component'; import { ItemDeleteComponent } from './items/item-delete/item-delete.component'; import { ItemAddComponent} from './items/item-add/item-add.component'; import { OrderComponent } from './order/order.component'; +import { CancelComponent } from './cancel/cancel.component'; //import { NameDialogComponent } from './namedialog/namedialog.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -33,7 +34,8 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; OrderComponent, ItemCreateComponent, ItemDeleteComponent, - ItemAddComponent + ItemAddComponent, + CancelComponent //NameDialogComponent, ], imports: [ @@ -56,6 +58,6 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; ], providers: [], bootstrap: [AppComponent], - entryComponents: [ItemCreateComponent, ItemDeleteComponent, ItemAddComponent] + entryComponents: [ItemCreateComponent, ItemDeleteComponent, ItemAddComponent, CancelComponent] }) export class AppModule { } diff --git a/src/app/cancel/cancel.component.css b/src/app/cancel/cancel.component.css new file mode 100644 index 0000000..8955a94 --- /dev/null +++ b/src/app/cancel/cancel.component.css @@ -0,0 +1,3 @@ +.text { + font-family: "Roboto-Mono", monospace; +} diff --git a/src/app/cancel/cancel.component.html b/src/app/cancel/cancel.component.html new file mode 100644 index 0000000..264bf4e --- /dev/null +++ b/src/app/cancel/cancel.component.html @@ -0,0 +1,24 @@ +

Cancel Order

+ + +

Are you sure you want to cancel the order?

+
+ + + + + + + +
+ + + + + +
+
diff --git a/src/app/cancel/cancel.component.ts b/src/app/cancel/cancel.component.ts new file mode 100644 index 0000000..2c71b59 --- /dev/null +++ b/src/app/cancel/cancel.component.ts @@ -0,0 +1,23 @@ +import { Inject, Component, OnInit, ViewEncapsulation} from '@angular/core'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog'; + +@Component({ + selector: 'app-cancel', + templateUrl: './cancel.component.html', + styleUrls: ['./cancel.component.css'] +}) + +export class CancelComponent { + + constructor( + private dialogRef: MatDialogRef + ) {} + + confirm() { + this.dialogRef.close(true); + } + + close() { + this.dialogRef.close(false); + } +} diff --git a/src/app/checkout/checkout.component.css b/src/app/checkout/checkout.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/checkout/checkout.component.html b/src/app/checkout/checkout.component.html new file mode 100644 index 0000000..e69de29 diff --git a/src/app/checkout/checkout.component.ts b/src/app/checkout/checkout.component.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/app/items/item-add/item-add.component.css b/src/app/items/item-add/item-add.component.css index c921a22..0e0c7d0 100644 --- a/src/app/items/item-add/item-add.component.css +++ b/src/app/items/item-add/item-add.component.css @@ -1,3 +1,7 @@ .text { font-family: "Roboto Mono", monospace; } + +input[type=number]{ + text-align: right; +} diff --git a/src/app/items/item-add/item-add.component.html b/src/app/items/item-add/item-add.component.html index 82a25be..dc706e4 100644 --- a/src/app/items/item-add/item-add.component.html +++ b/src/app/items/item-add/item-add.component.html @@ -3,7 +3,7 @@

{{lineItem.name}}

- +
diff --git a/src/app/items/item-create/item-create.component.css b/src/app/items/item-create/item-create.component.css index 3267c9b..bfe9e5d 100644 --- a/src/app/items/item-create/item-create.component.css +++ b/src/app/items/item-create/item-create.component.css @@ -1,3 +1,7 @@ .text { font-family: 'Roboto-Mono', monospace; } + +input[type=number]{ + text-align: right; +} diff --git a/src/app/items/item-list/item-list.component.css b/src/app/items/item-list/item-list.component.css index ffdd64b..ad0540a 100644 --- a/src/app/items/item-list/item-list.component.css +++ b/src/app/items/item-list/item-list.component.css @@ -2,6 +2,12 @@ font-family: 'Roboto-Mono', monospace; } +.text { + font-family: 'Roboto-Mono', monospace; +} +.small { + font-size: 75%; +} .icons { font-family: 'Material Icons'; } @@ -11,7 +17,7 @@ } img.icon{ - margin-bottom: -1px; + margin-bottom: -3px; } div.card{ diff --git a/src/app/items/item-list/item-list.component.html b/src/app/items/item-list/item-list.component.html index e57ede7..850f895 100644 --- a/src/app/items/item-list/item-list.component.html +++ b/src/app/items/item-list/item-list.component.html @@ -1,20 +1,18 @@
- - +
{{item.name}}

{{item.cost | currency: 'USD'}}

-

{{(item.cost/price) | number: '1.0-6'}}

+

{{(item.cost/price) | number: '1.0-6'}}

-
- -

{{item.description}}

-
+
+

{{item.description}}

+
diff --git a/src/app/order/order.component.css b/src/app/order/order.component.css index 57c4236..a0b4f5c 100644 --- a/src/app/order/order.component.css +++ b/src/app/order/order.component.css @@ -1,3 +1,7 @@ .text { - font-family: 'Roboto Mono', monospace; + font-family: "Roboto-Mono", monospace; +} + +img.icon{ + margin-bottom: -3px; } diff --git a/src/app/order/order.component.html b/src/app/order/order.component.html index 44ebe1e..7ad9f07 100644 --- a/src/app/order/order.component.html +++ b/src/app/order/order.component.html @@ -1,13 +1,41 @@

No open order!

-
- - - - - - - - -
Qty.Items
{{item.qty}}{{item.name}}
+
+ + {{order._id}} + + + + + +
Order Total: +

{{total | currency: 'USD'}}

+

{{(total/price) | number: '1.0-6'}}

+
+
+ + + + + + + + + +
Qty.Items
{{item.qty}}{{item.name}}
+
+ + + + + + +
+ + + + +
+
+ diff --git a/src/app/order/order.component.ts b/src/app/order/order.component.ts index 4fedf5f..2328c3a 100644 --- a/src/app/order/order.component.ts +++ b/src/app/order/order.component.ts @@ -1,9 +1,11 @@ import { Component, OnInit } from '@angular/core'; +import { MatDialog, MatDialogConfig} from '@angular/material/dialog'; import { Observable } from 'rxjs'; import { Order } from './order.model'; import { FullnodeService } from '../fullnode.service'; import { UserService } from '../user.service'; import { OrderService } from './order.service'; +import { CancelComponent } from '../cancel/cancel.component'; @Component({ selector: 'app-order', @@ -14,20 +16,50 @@ import { OrderService } from './order.service'; export class OrderComponent implements OnInit{ public order: Order = {address: '', session: '', timestamp: '', closed: false, lines: [{qty: 1, name: '', cost: 0}]}; public price: number = 1; + public total: number = 0; public orderUpdate: Observable; public priceUpdate: Observable; + public totalUpdate: Observable; constructor( public fullnodeService: FullnodeService, - public orderService: OrderService + public orderService: OrderService, + private dialog: MatDialog ) { this.priceUpdate = fullnodeService.priceUpdate; + this.priceUpdate.subscribe((price) => { + this.price = price; + }); this.orderUpdate = orderService.orderUpdate; this.orderUpdate.subscribe((order) => { this.order = order; }); + this.totalUpdate = orderService.totalUpdate; + this.totalUpdate.subscribe((total) => { + this.total = total; + }); } ngOnInit() { } + + cancelOrder() { + const dialogConfig = new MatDialogConfig(); + + dialogConfig.disableClose = true; + dialogConfig.autoFocus = true; + + const dialogRef = this.dialog.open(CancelComponent, dialogConfig); + dialogRef.afterClosed().subscribe((val) => { + if (val) { + console.log('Canceling'); + this.orderService.cancelOrder(this.order._id!).subscribe((response) => { + this.orderService.getOrder(); + });; + } else { + console.log('Returning to page'); + } + this.orderService.getOrder(); + }); + } } diff --git a/src/app/order/order.service.ts b/src/app/order/order.service.ts index 799e679..dbf868e 100644 --- a/src/app/order/order.service.ts +++ b/src/app/order/order.service.ts @@ -9,7 +9,8 @@ import { LineItem} from '../items/lineitem.model'; @Injectable({providedIn: 'root'}) export class OrderService { - private dataStore: { user: User, order: Order } = { + private dataStore: { total:number, user: User, order: Order } = { + total: 0, user:{ address: '', session: '', @@ -31,6 +32,8 @@ export class OrderService { }; private _orderUpdated: BehaviorSubject = new BehaviorSubject(this.dataStore.order); public readonly orderUpdate: Observable = this._orderUpdated.asObservable(); + private _totalUpdated: BehaviorSubject = new BehaviorSubject(this.dataStore.total); + public readonly totalUpdate: Observable = this._totalUpdated.asObservable(); public userUpdate: Observable; constructor( @@ -41,11 +44,12 @@ export class OrderService { this.userUpdate.subscribe((user) => { this.dataStore.user = user; //console.log('OS: const', user); - this.getOrder(this.dataStore.user.session); + this.getOrder(); }); } - getOrder(session: string) { + getOrder() { + var session = this.dataStore.user.session; const params = new HttpParams().append('session', session); let obs = this.http.get<{message: string, order: any}>('http://localhost:3000/api/order', { headers:{}, params:params, observe: 'response'}); @@ -53,6 +57,11 @@ export class OrderService { if (OrderDataResponse.status == 200) { this.dataStore.order = OrderDataResponse.body!.order; this._orderUpdated.next(Object.assign({}, this.dataStore).order); + this.dataStore.total = 0; + for(var line of this.dataStore.order.lines) { + this.dataStore.total += line.qty * line.cost; + } + this._totalUpdated.next(Object.assign({}, this.dataStore).total); } else { console.log('No order found'); } @@ -62,30 +71,57 @@ export class OrderService { } addToOrder(lineItem: LineItem) { - if (this.dataStore.order.address.length == 0) { - console.log('No open order, creating...', lineItem); - this.createOrder(); + if(this.dataStore.order._id != null) { + let obs = this.http.post<{message: string}>('http://localhost:3000/api/lineitem', { order_id: this.dataStore.order._id, line: lineItem }); + obs.subscribe((orderData) => { + this.getOrder(); + }); } else { - console.log('Open order, adding...', lineItem); + this.createOrder(lineItem); } } - createOrder() { + createOrder(lineItem: LineItem) { var order:Order = { address: this.dataStore.user.address, session: this.dataStore.user.session, closed: false, lines: [] }; - let obs = this.http.post<{message: string}>('http://localhost:3000/api/order', {order: order}); - + let obs = this.http.post<{message: string, order: Order}>('http://localhost:3000/api/order', {order: order}); obs.subscribe((orderData) => { console.log('Create order', orderData); - this.getOrder(this.dataStore.user.session); + this.dataStore.order = orderData.order; + this._orderUpdated.next(Object.assign({}, this.dataStore).order); + this.addToOrder(lineItem); }); return obs; + } + cancelOrder(id: string) { + let obs = this.http.delete<{message: string}>('http://localhost:3000/api/order/'+id); + + obs.subscribe((OrderResponse) => { + console.log('Order deleted'); + //this.getOrder(); + this.dataStore.order = { + address: '', + session: '', + timestamp: '', + closed: false, + lines: [ + { + qty: 1, + name: '', + cost:0 + } + ] + }; + this._orderUpdated.next(Object.assign({}, this.dataStore).order); + }); + + return obs; } } diff --git a/src/assets/zec-roboto.png b/src/assets/zec-roboto.png new file mode 100644 index 0000000000000000000000000000000000000000..f0c56a0e859770e2003701bea64b2683c03318c3 GIT binary patch literal 767 zcmVEX>4Tx04R}tkv&MmKpe$iQ>9WW9qb_LkfA!YAS&XhRVYG*P%E_RU~>J0CJjl7 zi=*ILaPVWX>fqw6tAnc`2!4RLxj8AiNQwVT3N2zhIPS;0dyl(!fY7Wm)$ADuRLwHd z$%L5At%`wH1Tl;-V(`n%)aN8A1<&zy4aWt=LlrLmm z&U4=4tXAu+eNXeSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{007rXL_t(Y$K91Z3IZ_@MjwI*HXcB>wz9C%%0mbqVr^~2 z3wQ$U?YxI4(8|VQ4`3mP7V2(nMi|84W>oz7U`PnbOC~d!z%R*(c~yADE*dgZySh6k zxE((M&9|JyqO;{