Implement invoice display component

This commit is contained in:
Rene Vergara 2022-05-23 14:14:21 -05:00
parent f4caa11800
commit 37715c1902
Signed by: pitmutt
GPG key ID: 65122AD495A7F5B2
9 changed files with 363 additions and 4 deletions

148
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "zgo", "name": "zgo",
"version": "0.0.0", "version": "1.1.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "zgo", "name": "zgo",
"version": "0.0.0", "version": "1.1.0",
"dependencies": { "dependencies": {
"@angular/animations": "~13.0.2", "@angular/animations": "~13.0.2",
"@angular/cdk": "^12.2.8", "@angular/cdk": "^12.2.8",
@ -18,11 +18,18 @@
"@angular/platform-browser": "~13.0.2", "@angular/platform-browser": "~13.0.2",
"@angular/platform-browser-dynamic": "~13.0.2", "@angular/platform-browser-dynamic": "~13.0.2",
"@angular/router": "~13.0.2", "@angular/router": "~13.0.2",
"@fortawesome/angular-fontawesome": "^0.10.2",
"@fortawesome/fontawesome-free": "^6.1.1",
"@fortawesome/fontawesome-svg-core": "^6.1.0",
"@fortawesome/free-brands-svg-icons": "^6.1.0",
"@fortawesome/free-regular-svg-icons": "^6.1.0",
"@fortawesome/free-solid-svg-icons": "^6.1.0",
"@supercharge/request-ip": "^1.1.2", "@supercharge/request-ip": "^1.1.2",
"angular-local-storage": "^0.7.1", "angular-local-storage": "^0.7.1",
"async": "^3.2.2", "async": "^3.2.2",
"coingecko-api": "^1.0.10", "coingecko-api": "^1.0.10",
"easyqrcodejs": "^4.4.6", "easyqrcodejs": "^4.4.6",
"fortawesome": "^0.0.1-security",
"material-design-icons": "^3.0.1", "material-design-icons": "^3.0.1",
"mongoose": "^6.0.13", "mongoose": "^6.0.13",
"rxjs": "~6.6.0", "rxjs": "~6.6.0",
@ -2246,6 +2253,83 @@
"node": ">=10.0.0" "node": ">=10.0.0"
} }
}, },
"node_modules/@fortawesome/angular-fontawesome": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.10.2.tgz",
"integrity": "sha512-VxsCAo2lK74KwD236AKAhGpiethfz9yqCViIG2iRAZqgNmuZ6ihwumjbLW32n6hV4fFvCqLcHmpngoEl3TNiOg==",
"dependencies": {
"tslib": "^2.3.1"
},
"peerDependencies": {
"@fortawesome/fontawesome-svg-core": "~1.2.27 || ~1.3.0-beta2 || ^6.1.0"
}
},
"node_modules/@fortawesome/fontawesome-common-types": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.1.tgz",
"integrity": "sha512-wVn5WJPirFTnzN6tR95abCx+ocH+3IFLXAgyavnf9hUmN0CfWoDjPT/BAWsUVwSlYYVBeCLJxaqi7ZGe4uSjBA==",
"hasInstallScript": true,
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/fontawesome-free": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.1.1.tgz",
"integrity": "sha512-J/3yg2AIXc9wznaVqpHVX3Wa5jwKovVF0AMYSnbmcXTiL3PpRPfF58pzWucCwEiCJBp+hCNRLWClTomD8SseKg==",
"hasInstallScript": true,
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/fontawesome-svg-core": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.1.1.tgz",
"integrity": "sha512-NCg0w2YIp81f4V6cMGD9iomfsIj7GWrqmsa0ZsPh59G7PKiGN1KymZNxmF00ssuAlo/VZmpK6xazsGOwzKYUMg==",
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-brands-svg-icons": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.1.1.tgz",
"integrity": "sha512-mFbI/czjBZ+paUtw5NPr2IXjun5KAC8eFqh1hnxowjA4mMZxWz4GCIksq6j9ZSa6Uxj9JhjjDVEd77p2LN2Blg==",
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-regular-svg-icons": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.1.1.tgz",
"integrity": "sha512-xXiW7hcpgwmWtndKPOzG+43fPH7ZjxOaoeyooptSztGmJxCAflHZxXNK0GcT0uEsR4jTGQAfGklDZE5NHoBhKg==",
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-solid-svg-icons": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.1.1.tgz",
"integrity": "sha512-0/5exxavOhI/D4Ovm2r3vxNojGZioPwmFrKg0ZUH69Q68uFhFPs6+dhAToh6VEQBntxPRYPuT5Cg1tpNa9JUPg==",
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@gar/promisify": { "node_modules/@gar/promisify": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz",
@ -5581,6 +5665,11 @@
} }
} }
}, },
"node_modules/fortawesome": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fortawesome/-/fortawesome-0.0.1-security.tgz",
"integrity": "sha512-o/pwn9ZSnveFLYP0d5IejSwz0rpGMDQsBaYVvR8WXSvnxILIwh7IUJK0P90QMF4cnIyouVF2BGrz0LEVSyzimA=="
},
"node_modules/forwarded": { "node_modules/forwarded": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -14332,6 +14421,56 @@
"integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==", "integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==",
"dev": true "dev": true
}, },
"@fortawesome/angular-fontawesome": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.10.2.tgz",
"integrity": "sha512-VxsCAo2lK74KwD236AKAhGpiethfz9yqCViIG2iRAZqgNmuZ6ihwumjbLW32n6hV4fFvCqLcHmpngoEl3TNiOg==",
"requires": {
"tslib": "^2.3.1"
}
},
"@fortawesome/fontawesome-common-types": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.1.tgz",
"integrity": "sha512-wVn5WJPirFTnzN6tR95abCx+ocH+3IFLXAgyavnf9hUmN0CfWoDjPT/BAWsUVwSlYYVBeCLJxaqi7ZGe4uSjBA=="
},
"@fortawesome/fontawesome-free": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.1.1.tgz",
"integrity": "sha512-J/3yg2AIXc9wznaVqpHVX3Wa5jwKovVF0AMYSnbmcXTiL3PpRPfF58pzWucCwEiCJBp+hCNRLWClTomD8SseKg=="
},
"@fortawesome/fontawesome-svg-core": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.1.1.tgz",
"integrity": "sha512-NCg0w2YIp81f4V6cMGD9iomfsIj7GWrqmsa0ZsPh59G7PKiGN1KymZNxmF00ssuAlo/VZmpK6xazsGOwzKYUMg==",
"requires": {
"@fortawesome/fontawesome-common-types": "6.1.1"
}
},
"@fortawesome/free-brands-svg-icons": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.1.1.tgz",
"integrity": "sha512-mFbI/czjBZ+paUtw5NPr2IXjun5KAC8eFqh1hnxowjA4mMZxWz4GCIksq6j9ZSa6Uxj9JhjjDVEd77p2LN2Blg==",
"requires": {
"@fortawesome/fontawesome-common-types": "6.1.1"
}
},
"@fortawesome/free-regular-svg-icons": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.1.1.tgz",
"integrity": "sha512-xXiW7hcpgwmWtndKPOzG+43fPH7ZjxOaoeyooptSztGmJxCAflHZxXNK0GcT0uEsR4jTGQAfGklDZE5NHoBhKg==",
"requires": {
"@fortawesome/fontawesome-common-types": "6.1.1"
}
},
"@fortawesome/free-solid-svg-icons": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.1.1.tgz",
"integrity": "sha512-0/5exxavOhI/D4Ovm2r3vxNojGZioPwmFrKg0ZUH69Q68uFhFPs6+dhAToh6VEQBntxPRYPuT5Cg1tpNa9JUPg==",
"requires": {
"@fortawesome/fontawesome-common-types": "6.1.1"
}
},
"@gar/promisify": { "@gar/promisify": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz",
@ -16931,6 +17070,11 @@
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA=="
}, },
"fortawesome": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fortawesome/-/fortawesome-0.0.1-security.tgz",
"integrity": "sha512-o/pwn9ZSnveFLYP0d5IejSwz0rpGMDQsBaYVvR8WXSvnxILIwh7IUJK0P90QMF4cnIyouVF2BGrz0LEVSyzimA=="
},
"forwarded": { "forwarded": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",

View file

@ -20,11 +20,18 @@
"@angular/platform-browser": "~13.0.2", "@angular/platform-browser": "~13.0.2",
"@angular/platform-browser-dynamic": "~13.0.2", "@angular/platform-browser-dynamic": "~13.0.2",
"@angular/router": "~13.0.2", "@angular/router": "~13.0.2",
"@fortawesome/angular-fontawesome": "^0.10.2",
"@fortawesome/fontawesome-free": "^6.1.1",
"@fortawesome/fontawesome-svg-core": "^6.1.0",
"@fortawesome/free-brands-svg-icons": "^6.1.0",
"@fortawesome/free-regular-svg-icons": "^6.1.0",
"@fortawesome/free-solid-svg-icons": "^6.1.0",
"@supercharge/request-ip": "^1.1.2", "@supercharge/request-ip": "^1.1.2",
"angular-local-storage": "^0.7.1", "angular-local-storage": "^0.7.1",
"async": "^3.2.2", "async": "^3.2.2",
"coingecko-api": "^1.0.10", "coingecko-api": "^1.0.10",
"easyqrcodejs": "^4.4.6", "easyqrcodejs": "^4.4.6",
"fortawesome": "^0.0.1-security",
"material-design-icons": "^3.0.1", "material-design-icons": "^3.0.1",
"mongoose": "^6.0.13", "mongoose": "^6.0.13",
"rxjs": "~6.6.0", "rxjs": "~6.6.0",

View file

@ -4,6 +4,7 @@ import { ViewerComponent } from './viewer/viewer.component';
import { ReceiptComponent } from './receipt/receipt.component'; import { ReceiptComponent } from './receipt/receipt.component';
import { LoginComponent } from './login/login.component'; import { LoginComponent } from './login/login.component';
import { BusinessComponent } from './business/business.component'; import { BusinessComponent } from './business/business.component';
import { InvoiceComponent } from './invoice/invoice.component';
import { ListOrdersComponent } from './listorders/listorders.component'; import { ListOrdersComponent } from './listorders/listorders.component';
import { AuthGuardService } from './auth-guard.service'; import { AuthGuardService } from './auth-guard.service';
import { NodeResolverService } from './node-resolver.service'; import { NodeResolverService } from './node-resolver.service';
@ -15,6 +16,7 @@ const routes: Routes = [
{ path: 'orders', component: ListOrdersComponent, canActivate: [AuthGuardService]}, { path: 'orders', component: ListOrdersComponent, canActivate: [AuthGuardService]},
{ path: 'biz', component: BusinessComponent, canActivate: [AuthGuardService]}, { path: 'biz', component: BusinessComponent, canActivate: [AuthGuardService]},
{ path: 'receipt/:orderId', component: ReceiptComponent}, { path: 'receipt/:orderId', component: ReceiptComponent},
{ path: 'invoice/:orderId', component: InvoiceComponent},
{ path: 'login', component: LoginComponent, resolve: { response: NodeResolverService}} { path: 'login', component: LoginComponent, resolve: { response: NodeResolverService}}
]; ];

View file

@ -38,6 +38,8 @@ import { SearchOptionsPipe } from './searchoptions.pipe';
import { TermsComponent } from './terms/terms.component'; import { TermsComponent } from './terms/terms.component';
import { ReceiptComponent } from './receipt/receipt.component'; import { ReceiptComponent } from './receipt/receipt.component';
import { ReceiptQRComponent } from './receipt-qr/receipt-qr.component'; import { ReceiptQRComponent } from './receipt-qr/receipt-qr.component';
import { InvoiceComponent } from './invoice/invoice.component';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -59,7 +61,8 @@ import { ReceiptQRComponent } from './receipt-qr/receipt-qr.component';
SearchOptionsPipe, SearchOptionsPipe,
TermsComponent, TermsComponent,
ReceiptComponent, ReceiptComponent,
ReceiptQRComponent ReceiptQRComponent,
InvoiceComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
@ -81,7 +84,8 @@ import { ReceiptQRComponent } from './receipt-qr/receipt-qr.component';
MatStepperModule, MatStepperModule,
MatAutocompleteModule, MatAutocompleteModule,
MatSlideToggleModule, MatSlideToggleModule,
BrowserAnimationsModule BrowserAnimationsModule,
FontAwesomeModule
], ],
exports: [ exports: [
MatIconModule MatIconModule

View file

@ -0,0 +1,36 @@
* {
font-family: 'Spartan', sans-serif;
}
.spacer{
flex: 1 1 auto;
}
.mat-card{
font-family: 'Spartan', sans-serif;
border: 1px solid #FF7522;
width: 80%;
text-align: left;
margin: 5px;
max-width: 500px;
}
.mat-card-title{
font-size: 16px;
font-weight: 700;
}
.mat-card-subtitle{
font-size: 12px;
font-weight: 200;
}
.mat-card-content{
font-size: 14px;
text-align: justify;
}
span.tt{
font-family: 'Roboto-Mono', monospace;
}
img.total{
margin-bottom:-2px;
}
.small{
font-size: 10px;
}

View file

@ -0,0 +1,61 @@
<mat-toolbar color="primary">
<span align="center">
<img class="logo" src="/assets/logo-new-white.png" height="40px" />
</span>
<span class="spacer"></span>
<span align="center">
<p class="text">{{name}}</p>
</span>
</mat-toolbar>
<div align="center" *ngIf="!error" id="invoice">
<mat-card>
<mat-card-title>
Total: <img class="total" src="/assets/spartan-zec.png" height="18px" />{{order.totalZec | number: '1.0-6'}}
</mat-card-title>
<mat-card-subtitle>
{{order.timestamp | date}}
</mat-card-subtitle>
<mat-card-content>
<p class="small">Order ID: {{orderId}}</p>
<p class="small">Zcash Price: {{order.price | currency: order.currency.toUpperCase()}}</p>
<span *ngIf="order.closed"><fa-icon [icon]="faCheck"></fa-icon>Payment confirmed</span>
<span *ngIf="!order.closed"><fa-icon [icon]="faHourglass"></fa-icon>Payment pending</span>
<div align="center">
<table>
<tr>
<th align="left">
Item
</th>
<th align="center">
Qty.
</th>
<th align="right">
Price ({{order.currency.toUpperCase()}})
</th>
</tr>
<tr *ngFor="let item of order.lines">
<td align="left">{{item.name}}</td>
<td align="center">{{item.qty}}</td>
<td align="right">{{(item.qty * item.cost) | currency: order.currency.toUpperCase()}} </td>
<tr>
</table>
<div class="qrcode" id="payment-qr"></div>
</div>
</mat-card-content>
</mat-card>
</div>
<div align="center" *ngIf="error">
<mat-card>
<mat-card-title>
Incorrect Invoice ID.
</mat-card-title>
<mat-card-content>
No information available.
</mat-card-content>
<mat-card-actions>
<div align="center">
<button mat-raised-button [routerLink]="['/']" color="primary">OK</button>
</div>
</mat-card-actions>
</mat-card>
</div>

View file

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InvoiceComponent } from './invoice.component';
describe('InvoiceComponent', () => {
let component: InvoiceComponent;
let fixture: ComponentFixture<InvoiceComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ InvoiceComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(InvoiceComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,79 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ReceiptService } from '../receipt.service';
import { Order} from '../order/order.model';
import { Observable } from 'rxjs';
import { faCheck, faHourglass } from '@fortawesome/free-solid-svg-icons';
var QRCode = require('easyqrcodejs');
var URLSafeBase64 = require('urlsafe-base64');
var Buffer = require('buffer/').Buffer;
@Component({
selector: 'app-invoice',
templateUrl: './invoice.component.html',
styleUrls: ['./invoice.component.css']
})
export class InvoiceComponent implements OnInit {
faCheck = faCheck;
faHourglass = faHourglass;
orderId;
public orderUpdate: Observable<Order>;
public nameUpdate: Observable<string>;
name: string = '';
error: boolean = false;
codeString: string = 'Test';
order:Order = {
address: '',
session: '',
timestamp: '',
closed: false,
currency: '',
price: 0,
total: 0,
totalZec: 0,
lines: [
{
qty: 1,
name: '',
cost:0
}
]
};
constructor(
private _ActiveRoute:ActivatedRoute,
private router: Router,
public receiptService: ReceiptService
) {
this.orderId = this._ActiveRoute.snapshot.paramMap.get("orderId");
this.orderUpdate = receiptService.orderUpdate;
this.nameUpdate = receiptService.nameUpdate;
receiptService.getOrderById(this.orderId!).subscribe(response => {
if (response.status == 200){
this.error = false;
this.codeString = `zcash:${response.body!.order.address}?amount=${response.body!.order.totalZec.toFixed(8)}&memo=${URLSafeBase64.encode(Buffer.from('ZGo Order::'.concat(this.orderId!)))}`;
var qrcode = new QRCode(document.getElementById("payment-qr"), {
text: this.codeString,
logo: "/assets/zcash.png",
logoWidth: 80,
logoHeight: 80
});
} else {
this.error = true;
this.codeString = 'Test';
}
});
this.orderUpdate.subscribe(order => {
this.order = order;
});
this.nameUpdate.subscribe(name => {
this.name = name;
});
}
ngOnInit(): void {
}
}

View file

@ -86,6 +86,7 @@ export class ReceiptService {
this._nameUpdated.next(Object.assign({}, this.dataStore).owner.name); this._nameUpdated.next(Object.assign({}, this.dataStore).owner.name);
}); });
} else { } else {
this._orderUpdated.next(Object.assign({}, this.dataStore).order);
console.log('No order found'); console.log('No order found');
} }
}); });