diff --git a/CHANGELOG.md b/CHANGELOG.md index ea00021..77a7652 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [1.4.0] + +### Added + +- Support for WooComerce: + - New tab in Settings to generate authentication token. + - Display of WooCommerce credentials for configuration. +- New service to interact with WooCommerce-related API endpoints. +- A "Return To Shop" button added to ZGo Invoice component ## [1.3.2] - 2022-10-11 diff --git a/angular.json b/angular.json index 9839d4f..2ded65f 100644 --- a/angular.json +++ b/angular.json @@ -39,8 +39,8 @@ "budgets": [ { "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" + "maximumWarning": "5mb", + "maximumError": "10mb" }, { "type": "anyComponentStyle", diff --git a/package.json b/package.json index bbbe622..3b16603 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zgo", - "version": "1.3.2", + "version": "1.4.0", "scripts": { "ng": "ng", "start": "ng serve", diff --git a/src/app/invoice/invoice.component.html b/src/app/invoice/invoice.component.html index d682959..23f8302 100644 --- a/src/app/invoice/invoice.component.html +++ b/src/app/invoice/invoice.component.html @@ -116,13 +116,35 @@ background-color: lightgray;" mat-raised-button (click)="copyAmount()">Copy Amount - + + mat-raised-button + (click)="copyMemo()" *ngIf="!isWCOrder">Copy Memo + +
+ + +
diff --git a/src/app/invoice/invoice.component.ts b/src/app/invoice/invoice.component.ts index a702b43..dbe710d 100644 --- a/src/app/invoice/invoice.component.ts +++ b/src/app/invoice/invoice.component.ts @@ -4,7 +4,7 @@ import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { ReceiptService } from '../receipt.service'; import { Order} from '../order/order.model'; import { Observable } from 'rxjs'; -import { faCheck, faHourglass } from '@fortawesome/free-solid-svg-icons'; +import { faCheck, faHourglass, faArrowUpRightFromSquare } from '@fortawesome/free-solid-svg-icons'; import { NotifierService } from '../notifier.service'; @@ -17,21 +17,22 @@ var Buffer = require('buffer/').Buffer; templateUrl: './invoice.component.html', styleUrls: ['./invoice.component.css'] }) + export class InvoiceComponent implements OnInit { faCheck = faCheck; faHourglass = faHourglass; + faArrowUpRightFromSquare = faArrowUpRightFromSquare; orderId; public orderUpdate: Observable; public nameUpdate: Observable; name: string = ''; error: boolean = false; codeString: string = 'Test'; - + public isWCOrder : boolean = false; zcashUrl: SafeUrl = ''; - - + externalURL: string = ''; order:Order = { - _id: '', + _id: '', address: '', session: '', timestamp: '', @@ -55,11 +56,12 @@ export class InvoiceComponent implements OnInit { constructor( private _ActiveRoute:ActivatedRoute, private router: Router, - private sanitizer: DomSanitizer, + private sanitizer: DomSanitizer, public receiptService: ReceiptService, - private notifierService : NotifierService + private notifierService : NotifierService ) { this.orderId = this._ActiveRoute.snapshot.paramMap.get("orderId"); + console.log('constructor - orderId -> ' + this.orderId); this.orderUpdate = receiptService.orderUpdate; this.nameUpdate = receiptService.nameUpdate; receiptService.getOrderById(this.orderId!).subscribe(response => { @@ -83,9 +85,11 @@ export class InvoiceComponent implements OnInit { }); this.orderUpdate.subscribe(order => { this.order = order; - this.codeString = `zcash:${this.order.address}?amount=${this.order.totalZec.toFixed(8)}&memo=${URLSafeBase64.encode(Buffer.from('ZGo Order::'.concat(this.orderId!)))}`; - this.zcashUrl = this.sanitizer.bypassSecurityTrustUrl(this.codeString); - + if ( order.session.substring(0,1) == 'W') { + this.isWCOrder = true; + } + this.codeString = `zcash:${this.order.address}?amount=${this.order.totalZec.toFixed(8)}&memo=${URLSafeBase64.encode(Buffer.from('ZGo Order::'.concat(this.orderId!)))}`; + this.zcashUrl = this.sanitizer.bypassSecurityTrustUrl(this.codeString); }); this.nameUpdate.subscribe(name => { this.name = name; @@ -93,6 +97,25 @@ export class InvoiceComponent implements OnInit { } ngOnInit(): void { + } + + backToShop() { + if ( this.isWCOrder ) { +// console.log('External Invoice -> ' + this.order.externalInvoice ); + const b64URL:string = this.order.externalInvoice.substring(0,this.order.externalInvoice.indexOf("-")); +// console.log('encodedURL -> ' + b64URL ); + const shopURL: string = Buffer.from(b64URL, 'base64').toString(); + const tmp_orderid = this.order.externalInvoice.substring(this.order.externalInvoice.indexOf('-')+1); + const wc_order_key = tmp_orderid.substring(tmp_orderid.indexOf('-')+1); + const wc_orderid = tmp_orderid.substring(0,tmp_orderid.indexOf('-')); +// console.log('wc_order_id -> ' + wc_orderid); +// console.log('wc_order_key -> ' + wc_order_key); +// console.log('new URL -> ' + shopURL + '/checkout/order-received/' + wc_orderid + '/?key=' + wc_order_key); + if ( shopURL ) { +// console.log('Opening URL....' + shopURL); + window.open( shopURL + '/checkout/order-received/' + wc_orderid + '/?key=' + wc_order_key,"_blank"); + } + } } getIconStyle(order : Order) { @@ -102,7 +125,7 @@ export class InvoiceComponent implements OnInit { } - copyAddress() { + copyAddress() { if (!navigator.clipboard) { // alert("Copy functionality not supported"); this.notifierService @@ -115,8 +138,9 @@ export class InvoiceComponent implements OnInit { .showNotification("Error copying address","Close","error"); // console.error("Error", err); } - } - copyAmount() { + } + + copyAmount() { if (!navigator.clipboard) { // alert("Copy functionality not supported"); this.notifierService @@ -129,9 +153,9 @@ export class InvoiceComponent implements OnInit { .showNotification("Error while copying ammount","Close","error"); // console.error("Error", err); } - } + } - copyMemo() { + copyMemo() { if (!navigator.clipboard) { // alert("Copy functionality not supported"); this.notifierService @@ -144,6 +168,6 @@ export class InvoiceComponent implements OnInit { .showNotification("Error while copying Memo","Close","error"); // console.error("Error", err); } - } + } } diff --git a/src/app/settings/settings.component.css b/src/app/settings/settings.component.css index 777aca3..2779c59 100644 --- a/src/app/settings/settings.component.css +++ b/src/app/settings/settings.component.css @@ -45,4 +45,15 @@ color: dodgerblue; } +.small { + font-size: 12px; + background: #dddddd; +} +.heading { + padding-top: 10px; +} + +.toolbar { + padding: 12px; +} diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index f829bdc..f44700b 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -77,98 +77,187 @@ -
- -
+ +
+ +
+
+
+

+ + {{ linkMsg }} + +

+ + + + + + + + + + + + +
Payment Service URL: +
+
+ +
+ +
+ + + + +
+
+
+ + Account Code + + + +
+
+
+
+ + +
+
-
-

- - {{ linkMsg }} - -

- - - + + +
+
+ +
+
+ - - + - - - + - - -
Payment Service URL: - Owner:
-
+
+
- -
- - - - -
-
-
- - Account Code - - - + + + + + + + + + + Token: + + + +
+ +
+ + + + + + + + + + URL: + + + +
+ +
+ + + + +
+ +
-
- -
-
- - -
-
-
+
+
+ diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index bdbe35f..0ea9ee6 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -5,6 +5,7 @@ import { UntypedFormBuilder, Validators, UntypedFormGroup, FormControl } from '@ import { Observable } from 'rxjs'; import { Owner } from '../owner.model'; import { XeroService } from '../xero.service'; +import { WoocommerceService } from '../woocommerce.service'; import { NotifierService } from '../notifier.service'; import { faCopy } from '@fortawesome/free-solid-svg-icons'; @@ -56,6 +57,13 @@ export class SettingsComponent implements OnInit { xeroLink: string = ''; localToken: string = ''; clientId: string = ''; + wooOwner: string = ''; + wooToken: string = ''; + wooUrl: string = ''; + wooOwnerUpdate: Observable; + wooTokenUpdate: Observable; + wooUrlUpdate: Observable; + clientIdUpdate: Observable; accCodeUpdate: Observable; linked2Xero : boolean = false; @@ -65,6 +73,7 @@ export class SettingsComponent implements OnInit { private notifierService : NotifierService, private fb: UntypedFormBuilder, public xeroService: XeroService, + public wooService: WoocommerceService, private dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: Owner) { this.useZats = data.zats; @@ -102,6 +111,19 @@ export class SettingsComponent implements OnInit { console.log("xeroAccCod -> [" + this.xeroAccCod + "]"); this.accCodForm.get('xAcc')!.setValue(this.xeroAccCod); }); + this.wooOwnerUpdate = wooService.ownerUpdate; + this.wooTokenUpdate = wooService.tokenUpdate; + this.wooUrlUpdate = wooService.siteurlUpdate; + wooService.getWooToken(this.owner._id!); + this.wooOwnerUpdate.subscribe(owData => { + this.wooOwner = owData; + }); + this.wooTokenUpdate.subscribe(tkData => { + this.wooToken = tkData; + }); + this.wooUrlUpdate.subscribe(uData => { + this.wooUrl = uData; + }); } ngOnInit() { @@ -128,7 +150,7 @@ export class SettingsComponent implements OnInit { closeIntegration() { if ( (this.xeroAccCod == '') && (this.linked2Xero) ) this.notifierService - .showNotification("Payment confirmation disabled!!","Close",'warning'); + .showNotification("Xero Payment confirmation disabled!!","Close",'warning'); this.dialogRef.close(); } @@ -179,6 +201,45 @@ export class SettingsComponent implements OnInit { } } + copyWooOwner(){ + try { + navigator.clipboard.writeText(this.wooOwner); + this.notifierService.showNotification("Owner ID copied to clipboard", "Close", "success"); + } catch (err) { + this.notifierService.showNotification("Copying not available in your browser", "Close", "error"); + } + } + + copyWooToken(){ + try { + navigator.clipboard.writeText(this.wooToken); + this.notifierService.showNotification("WooCommerce Token copied to clipboard", "Close", "success"); + } catch (err) { + this.notifierService.showNotification("Copying not available in your browser", "Close", "error"); + } + } + + generateWooToken(){ + this.wooService.createWooToken(this.owner._id!).subscribe(responseData => { + if (responseData.status == 202) { + this.notifierService.showNotification("WooCommerce Token generated!", "Close", "success"); + this.wooService.getWooToken(this.owner._id!); + this.wooOwnerUpdate.subscribe(owData => { + this.wooOwner = owData; + }); + this.wooTokenUpdate.subscribe(tkData => { + this.wooToken = tkData; + }); + this.wooUrlUpdate.subscribe(uData => { + this.wooUrl = uData; + }); + close(); + } else { + this.notifierService.showNotification("WooCommerce Token generation failed.", "Close", "error"); + } + }); + } + saveAccCod() { this.xeroAccCod = this.accCodForm.value.xAcc; diff --git a/src/app/woocommerce.service.spec.ts b/src/app/woocommerce.service.spec.ts new file mode 100644 index 0000000..500c1f1 --- /dev/null +++ b/src/app/woocommerce.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { WoocommerceService } from './woocommerce.service'; + +describe('WoocommerceService', () => { + let service: WoocommerceService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(WoocommerceService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/woocommerce.service.ts b/src/app/woocommerce.service.ts new file mode 100644 index 0000000..a25c13d --- /dev/null +++ b/src/app/woocommerce.service.ts @@ -0,0 +1,65 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http'; + +import { ConfigData } from './configdata'; + +var Buffer = require('buffer/').Buffer; + +@Injectable({ + providedIn: 'root' +}) +export class WoocommerceService { + beUrl = ConfigData.Be_URL; + private reqHeaders: HttpHeaders; + private ownerId: string = ''; + private token: string = ''; + private siteurl: string = ''; + private _ownerIdUpdated: BehaviorSubject = new BehaviorSubject(this.ownerId); + private _tokenUpdated: BehaviorSubject = new BehaviorSubject(this.token); + private _siteurlUpdated: BehaviorSubject = new BehaviorSubject(this.siteurl); + public readonly ownerUpdate: Observable = this._ownerIdUpdated.asObservable(); + public readonly tokenUpdate: Observable = this._tokenUpdated.asObservable(); + public readonly siteurlUpdate: Observable = this._siteurlUpdated.asObservable(); + + constructor( + private http: HttpClient + ) { + var auth = 'Basic ' + Buffer.from(ConfigData.UsrPwd).toString('base64'); + this.reqHeaders = new HttpHeaders().set('Authorization', auth); + this._ownerIdUpdated.next(Object.assign({}, this).ownerId); + this._tokenUpdated.next(Object.assign({}, this).token); + this._siteurlUpdated.next(Object.assign({}, this).siteurl); + } + + getWooToken(ownerId: string) { + const params = new HttpParams().append('ownerid', ownerId); + let obs = this.http.get<{ownerid: string, token: string, siteurl: string}>(this.beUrl + 'api/wootoken', {headers: this.reqHeaders, params: params, observe: 'response'}); + obs.subscribe(tokenResponse => { + if (tokenResponse.status == 200) { + this.ownerId = tokenResponse.body!.ownerid; + this.token = tokenResponse.body!.token; + this.siteurl = tokenResponse.body!.siteurl; + this._ownerIdUpdated.next(Object.assign({}, this).ownerId); + this._tokenUpdated.next(Object.assign({}, this).token); + this._siteurlUpdated.next(Object.assign({}, this).siteurl); + } else { + console.log('No WooCommerce token found'); + } + }); + return obs; + } + + createWooToken(ownerId: string) { + const params = new HttpParams().append('ownerid', ownerId); + let obs = this.http.post(this.beUrl+'api/wootoken', {}, {headers: this.reqHeaders, params: params, observe: 'response'}); + obs.subscribe(responseData => { + if (responseData.status == 202) { + console.log('WooToken created.'); + } else { + console.log('WooToken creation failed.'); + } + }); + return obs; + } +}