Implement order creation and cancellation

This commit is contained in:
Rene Vergara 2021-10-27 15:21:55 -05:00
parent af8ee5dc90
commit dce8da391e
19 changed files with 228 additions and 36 deletions

View file

@ -287,7 +287,35 @@ app.post('/api/order', (req, res, next) => {
const order = new ordermodel(req.body.order); const order = new ordermodel(req.body.order);
order.save(); order.save();
res.status(200).json({ 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'
});
}
}); });
}); });

View file

@ -7,7 +7,7 @@ const orderSchema = mongoose.Schema({
closed: { type: Boolean, required: true, default:false }, closed: { type: Boolean, required: true, default:false },
lines: [{ lines: [{
qty: {type: Number, required: true, default: 1}, qty: {type: Number, required: true, default: 1},
item: { type: String, required: true}, name: { type: String, required: true},
cost: { type: Number, required: true, default: 0} cost: { type: Number, required: true, default: 0}
}] }]
}); });

View file

@ -20,6 +20,7 @@ import { ItemCreateComponent } from './items/item-create/item-create.component';
import { ItemDeleteComponent } from './items/item-delete/item-delete.component'; import { ItemDeleteComponent } from './items/item-delete/item-delete.component';
import { ItemAddComponent} from './items/item-add/item-add.component'; import { ItemAddComponent} from './items/item-add/item-add.component';
import { OrderComponent } from './order/order.component'; import { OrderComponent } from './order/order.component';
import { CancelComponent } from './cancel/cancel.component';
//import { NameDialogComponent } from './namedialog/namedialog.component'; //import { NameDialogComponent } from './namedialog/namedialog.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@ -33,7 +34,8 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
OrderComponent, OrderComponent,
ItemCreateComponent, ItemCreateComponent,
ItemDeleteComponent, ItemDeleteComponent,
ItemAddComponent ItemAddComponent,
CancelComponent
//NameDialogComponent, //NameDialogComponent,
], ],
imports: [ imports: [
@ -56,6 +58,6 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
], ],
providers: [], providers: [],
bootstrap: [AppComponent], bootstrap: [AppComponent],
entryComponents: [ItemCreateComponent, ItemDeleteComponent, ItemAddComponent] entryComponents: [ItemCreateComponent, ItemDeleteComponent, ItemAddComponent, CancelComponent]
}) })
export class AppModule { } export class AppModule { }

View file

@ -0,0 +1,3 @@
.text {
font-family: "Roboto-Mono", monospace;
}

View file

@ -0,0 +1,24 @@
<h2 mat-dialog-title class="text">Cancel Order</h2>
<mat-dialog-content>
<p class="text">Are you sure you want to cancel the order?</p>
</mat-dialog-content>
<mat-dialog-actions>
<table cellspacing="0" width="100%">
<tr>
<td>
<button mat-raised-button color="primary" (click)="confirm()">
Yes
</button>
</td>
<td align="right">
<button mat-raised-button (click)="close()">
No
</button>
</td>
</tr>
</table>
</mat-dialog-actions>

View file

@ -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<CancelComponent>
) {}
confirm() {
this.dialogRef.close(true);
}
close() {
this.dialogRef.close(false);
}
}

View file

View file

View file

View file

@ -1,3 +1,7 @@
.text { .text {
font-family: "Roboto Mono", monospace; font-family: "Roboto Mono", monospace;
} }
input[type=number]{
text-align: right;
}

View file

@ -3,7 +3,7 @@
<mat-dialog-content [formGroup]="orderForm"> <mat-dialog-content [formGroup]="orderForm">
<p class="text">{{lineItem.name}}</p> <p class="text">{{lineItem.name}}</p>
<mat-form-field> <mat-form-field>
<input matInput placeholder="Quantity" formControlName="qty"> <input matInput type="number" placeholder="Quantity" formControlName="qty">
</mat-form-field> </mat-form-field>
</mat-dialog-content> </mat-dialog-content>

View file

@ -1,3 +1,7 @@
.text { .text {
font-family: 'Roboto-Mono', monospace; font-family: 'Roboto-Mono', monospace;
} }
input[type=number]{
text-align: right;
}

View file

@ -2,6 +2,12 @@
font-family: 'Roboto-Mono', monospace; font-family: 'Roboto-Mono', monospace;
} }
.text {
font-family: 'Roboto-Mono', monospace;
}
.small {
font-size: 75%;
}
.icons { .icons {
font-family: 'Material Icons'; font-family: 'Material Icons';
} }
@ -11,7 +17,7 @@
} }
img.icon{ img.icon{
margin-bottom: -1px; margin-bottom: -3px;
} }
div.card{ div.card{

View file

@ -1,20 +1,18 @@
<div *ngIf="items.length > 0"> <div *ngIf="items.length > 0">
<div class="card" *ngFor="let item of itemsUpdate | async"> <div class="card" *ngFor="let item of itemsUpdate | async">
<mat-card> <mat-card>
<mat-card-title class="card"> <table cellspacing="0" width="100%" class="text">
<table cellspacing="0" width="100%">
<tr> <tr>
<td>{{item.name}}</td> <td>{{item.name}}</td>
<td align="right"> <td align="right">
<p class="price">{{item.cost | currency: 'USD'}}</p> <p class="price">{{item.cost | currency: 'USD'}}</p>
<p class="price"><img class="icon" src="/assets/zec.png" width="12px" />{{(item.cost/price) | number: '1.0-6'}}</p> <p class="price"><img class="icon" src="/assets/zec-roboto.png" width="10px" />{{(item.cost/price) | number: '1.0-6'}}</p>
</td> </td>
</tr> </tr>
</table> </table>
</mat-card-title> <div align="center">
<mat-card-subtitle class="card"> <p class="text small">{{item.description}}</p>
<p>{{item.description}}</p> </div>
</mat-card-subtitle>
<mat-card-actions> <mat-card-actions>
<table cellspacing="0" width="100%"> <table cellspacing="0" width="100%">
<tr> <tr>

View file

@ -1,3 +1,7 @@
.text { .text {
font-family: 'Roboto Mono', monospace; font-family: "Roboto-Mono", monospace;
}
img.icon{
margin-bottom: -3px;
} }

View file

@ -1,13 +1,41 @@
<p *ngIf="order.address.length == 0">No open order!</p> <p *ngIf="order.address.length == 0">No open order!</p>
<mat-card class="text" *ngIf="order.address.length > 0"> <mat-card class="text" *ngIf="order.address.length > 0">
<div align="center">
<mat-card-title>
{{order._id}}
<table cellspacing="0" width="100%">
<tr>
<td>Order Total:</td>
<td align="right">
<p class="text">{{total | currency: 'USD'}}</p>
<p class="text"><img class="icon" src="/assets/zec-roboto.png" width="14px" />{{(total/price) | number: '1.0-6'}}</p>
</td>
</tr>
</table>
</mat-card-title>
<table> <table>
<tr> <tr>
<th>Qty.</th> <th>Qty.</th>
<th>Items</th> <th>Items</th>
</tr> </tr>
<tr *ngFor="let item of order.lines"> <tr *ngFor="let item of order.lines">
<td>{{item.qty}}</td> <td align="right">{{item.qty}}</td>
<td>{{item.name}}</td> <td>{{item.name}}</td>
</tr> </tr>
</table> </table>
</div>
<mat-card-actions>
<table cellspacing="0" width="100%">
<tr>
<td>
<button mat-raised-button class="text" (click)="cancelOrder()">Cancel</button>
</td>
<td align="right">
<button mat-raised-button class="text" color="primary">Checkout</button>
</td>
</tr>
</table>
</mat-card-actions>
</mat-card> </mat-card>

View file

@ -1,9 +1,11 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { MatDialog, MatDialogConfig} from '@angular/material/dialog';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Order } from './order.model'; import { Order } from './order.model';
import { FullnodeService } from '../fullnode.service'; import { FullnodeService } from '../fullnode.service';
import { UserService } from '../user.service'; import { UserService } from '../user.service';
import { OrderService } from './order.service'; import { OrderService } from './order.service';
import { CancelComponent } from '../cancel/cancel.component';
@Component({ @Component({
selector: 'app-order', selector: 'app-order',
@ -14,20 +16,50 @@ import { OrderService } from './order.service';
export class OrderComponent implements OnInit{ export class OrderComponent implements OnInit{
public order: Order = {address: '', session: '', timestamp: '', closed: false, lines: [{qty: 1, name: '', cost: 0}]}; public order: Order = {address: '', session: '', timestamp: '', closed: false, lines: [{qty: 1, name: '', cost: 0}]};
public price: number = 1; public price: number = 1;
public total: number = 0;
public orderUpdate: Observable<Order>; public orderUpdate: Observable<Order>;
public priceUpdate: Observable<number>; public priceUpdate: Observable<number>;
public totalUpdate: Observable<number>;
constructor( constructor(
public fullnodeService: FullnodeService, public fullnodeService: FullnodeService,
public orderService: OrderService public orderService: OrderService,
private dialog: MatDialog
) { ) {
this.priceUpdate = fullnodeService.priceUpdate; this.priceUpdate = fullnodeService.priceUpdate;
this.priceUpdate.subscribe((price) => {
this.price = price;
});
this.orderUpdate = orderService.orderUpdate; this.orderUpdate = orderService.orderUpdate;
this.orderUpdate.subscribe((order) => { this.orderUpdate.subscribe((order) => {
this.order = order; this.order = order;
}); });
this.totalUpdate = orderService.totalUpdate;
this.totalUpdate.subscribe((total) => {
this.total = total;
});
} }
ngOnInit() { 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();
});
}
} }

View file

@ -9,7 +9,8 @@ import { LineItem} from '../items/lineitem.model';
@Injectable({providedIn: 'root'}) @Injectable({providedIn: 'root'})
export class OrderService { export class OrderService {
private dataStore: { user: User, order: Order } = { private dataStore: { total:number, user: User, order: Order } = {
total: 0,
user:{ user:{
address: '', address: '',
session: '', session: '',
@ -31,6 +32,8 @@ export class OrderService {
}; };
private _orderUpdated: BehaviorSubject<Order> = new BehaviorSubject(this.dataStore.order); private _orderUpdated: BehaviorSubject<Order> = new BehaviorSubject(this.dataStore.order);
public readonly orderUpdate: Observable<Order> = this._orderUpdated.asObservable(); public readonly orderUpdate: Observable<Order> = this._orderUpdated.asObservable();
private _totalUpdated: BehaviorSubject<number> = new BehaviorSubject(this.dataStore.total);
public readonly totalUpdate: Observable<number> = this._totalUpdated.asObservable();
public userUpdate: Observable<User>; public userUpdate: Observable<User>;
constructor( constructor(
@ -41,11 +44,12 @@ export class OrderService {
this.userUpdate.subscribe((user) => { this.userUpdate.subscribe((user) => {
this.dataStore.user = user; this.dataStore.user = user;
//console.log('OS: const', 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); 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'}); 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) { if (OrderDataResponse.status == 200) {
this.dataStore.order = OrderDataResponse.body!.order; this.dataStore.order = OrderDataResponse.body!.order;
this._orderUpdated.next(Object.assign({}, this.dataStore).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 { } else {
console.log('No order found'); console.log('No order found');
} }
@ -62,30 +71,57 @@ export class OrderService {
} }
addToOrder(lineItem: LineItem) { addToOrder(lineItem: LineItem) {
if (this.dataStore.order.address.length == 0) { if(this.dataStore.order._id != null) {
console.log('No open order, creating...', lineItem); let obs = this.http.post<{message: string}>('http://localhost:3000/api/lineitem', { order_id: this.dataStore.order._id, line: lineItem });
this.createOrder(); obs.subscribe((orderData) => {
this.getOrder();
});
} else { } else {
console.log('Open order, adding...', lineItem); this.createOrder(lineItem);
} }
} }
createOrder() { createOrder(lineItem: LineItem) {
var order:Order = { var order:Order = {
address: this.dataStore.user.address, address: this.dataStore.user.address,
session: this.dataStore.user.session, session: this.dataStore.user.session,
closed: false, closed: false,
lines: [] 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) => { obs.subscribe((orderData) => {
console.log('Create order', 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; 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;
} }
} }

BIN
src/assets/zec-roboto.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 767 B