From 3168150171bc1d41b1f549b476a2cda28d85fd61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 31 Jan 2024 18:47:14 +0100 Subject: [PATCH 01/16] Update package-lock.json --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 1e4593041b24..f1466debb888 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "invoiceninja", + "name": "@invoiceninja/invoiceninja", "lockfileVersion": 2, "requires": true, "packages": { From ea6be8d6987b51f4f90da762783f160f619f3b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 31 Jan 2024 18:47:21 +0100 Subject: [PATCH 02/16] Add showRff method to ClientContact model --- app/Models/ClientContact.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/Models/ClientContact.php b/app/Models/ClientContact.php index c2a7e340fc84..04307dce367e 100644 --- a/app/Models/ClientContact.php +++ b/app/Models/ClientContact.php @@ -351,5 +351,12 @@ class ClientContact extends Authenticatable implements HasLocalePreference return config('ninja.react_url')."/#/clients/{$this->client->hashed_id}"; } + public function showRff(): bool + { + if (\strlen($this->first_name) === 0 || \strlen($this->last_name) === 0 || \strlen($this->email) === 0) { + return true; + } + return false; + } } From 539451319f2b4c54a662e21ac7e8b65b72169d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 31 Jan 2024 18:47:26 +0100 Subject: [PATCH 03/16] Update contact information in InstantPayment class --- app/Services/ClientPortal/InstantPayment.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/Services/ClientPortal/InstantPayment.php b/app/Services/ClientPortal/InstantPayment.php index 7ce624d5f270..8868a3df8ddc 100644 --- a/app/Services/ClientPortal/InstantPayment.php +++ b/app/Services/ClientPortal/InstantPayment.php @@ -45,6 +45,15 @@ class InstantPayment public function run() { nlog($this->request->all()); + + $cc = auth()->guard('contact')->user(); + + $cc->first_name = $this->request->contact_first_name; + $cc->last_name = $this->request->contact_last_name; + $cc->email = $this->request->contact_email; + + $cc->save(); + $is_credit_payment = false; $tokens = []; From ff3683f5bd18f9099a5661ca396b2273093a408d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 31 Jan 2024 18:47:30 +0100 Subject: [PATCH 04/16] Add validation for contact information in payment processing --- app/Http/Controllers/ClientPortal/PaymentController.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index 3cc4cf331f10..84a238757632 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -106,6 +106,12 @@ class PaymentController extends Controller */ public function process(Request $request) { + $request->validate([ + 'contact_first_name' => ['required'], + 'contact_last_name' => ['required'], + 'contact_email' => ['required', 'email'], + ]); + return (new InstantPayment($request))->run(); } From a93b452c3944499ff9aa949b04752c8716e4b938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 31 Jan 2024 18:47:39 +0100 Subject: [PATCH 05/16] Production build --- public/build/assets/payment-0ace5bfa.js | 9 --------- public/build/assets/payment-1e2ec1a7.js | 9 +++++++++ public/build/manifest.json | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 public/build/assets/payment-0ace5bfa.js create mode 100644 public/build/assets/payment-1e2ec1a7.js diff --git a/public/build/assets/payment-0ace5bfa.js b/public/build/assets/payment-0ace5bfa.js deleted file mode 100644 index afdebe889399..000000000000 --- a/public/build/assets/payment-0ace5bfa.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Invoice Ninja (https://invoiceninja.com) - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */class s{constructor(e,t){this.shouldDisplayTerms=e,this.shouldDisplaySignature=t,this.termsAccepted=!1,this.submitting=!1}handleMethodSelect(e){document.getElementById("company_gateway_id").value=e.dataset.companyGatewayId,document.getElementById("payment_method_id").value=e.dataset.gatewayTypeId,this.shouldDisplaySignature&&!this.shouldDisplayTerms&&(this.signaturePad&&this.signaturePad.isEmpty()&&alert("Please sign"),this.displayTerms(),document.getElementById("accept-terms-button").addEventListener("click",()=>{this.termsAccepted=!0,this.submitForm()})),!this.shouldDisplaySignature&&this.shouldDisplayTerms&&(this.displaySignature(),document.getElementById("signature-next-step").addEventListener("click",()=>{document.querySelector('input[name="signature"').value=this.signaturePad.toDataURL(),this.submitForm()})),this.shouldDisplaySignature&&this.shouldDisplayTerms&&(this.displaySignature(),document.getElementById("signature-next-step").addEventListener("click",()=>{this.displayTerms(),document.getElementById("accept-terms-button").addEventListener("click",()=>{document.querySelector('input[name="signature"').value=this.signaturePad.toDataURL(),this.termsAccepted=!0,this.submitForm()})})),!this.shouldDisplaySignature&&!this.shouldDisplayTerms&&this.submitForm()}submitForm(){this.submitting=!0,document.getElementById("payment-form").submit()}displayTerms(){document.getElementById("displayTermsModal").removeAttribute("style")}displaySignature(){document.getElementById("signature-next-step").disabled=!0,document.getElementById("displaySignatureModal").removeAttribute("style");const t=new SignaturePad(document.getElementById("signature-pad"),{penColor:"rgb(0, 0, 0)"});t.onEnd=function(){document.getElementById("signature-next-step").disabled=!1},this.signaturePad=t}handle(){document.querySelectorAll(".dropdown-gateway-button").forEach(e=>{e.addEventListener("click",()=>{this.submitting||this.handleMethodSelect(e)})})}}const i=document.querySelector('meta[name="require-invoice-signature"]').content,a=document.querySelector('meta[name="show-invoice-terms"]').content;new s(!!+i,!!+a).handle(); diff --git a/public/build/assets/payment-1e2ec1a7.js b/public/build/assets/payment-1e2ec1a7.js new file mode 100644 index 000000000000..f64be772aa47 --- /dev/null +++ b/public/build/assets/payment-1e2ec1a7.js @@ -0,0 +1,9 @@ +/** + * Invoice Ninja (https://invoiceninja.com) + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */class n{constructor(t,e,s){this.shouldDisplayTerms=t,this.shouldDisplaySignature=e,this.shouldDisplayRff=s,this.submitting=!1,this.steps=new Map,this.shouldDisplayRff&&this.steps.set("rff",{element:document.getElementById("displayRequiredFieldsModal"),callback:()=>{const a={firstName:document.querySelector('input[name="rff_first_name"]'),lastName:document.querySelector('input[name="rff_last_name"]'),email:document.querySelector('input[name="rff_email"]')};a.firstName&&(document.querySelector('input[name="contact_first_name"]').value=a.firstName.value),a.lastName&&(document.querySelector('input[name="contact_last_name"]').value=a.lastName.value),a.email&&(document.querySelector('input[name="contact_email"]').value=a.email.value)}}),this.shouldDisplaySignature&&this.steps.set("signature",{element:document.getElementById("displaySignatureModal"),boot:()=>this.signaturePad=new SignaturePad(document.getElementById("signature-pad"),{penColor:"rgb(0, 0, 0)"}),callback:()=>document.querySelector('input[name="signature"').value=this.signaturePad.toDataURL()}),this.shouldDisplayTerms&&this.steps.set("terms",{element:document.getElementById("displayTermsModal")})}handleMethodSelect(t){if(document.getElementById("company_gateway_id").value=t.dataset.companyGatewayId,document.getElementById("payment_method_id").value=t.dataset.gatewayTypeId,this.steps.size===0)return this.submitForm();const e=this.steps.values().next().value;e.element.removeAttribute("style"),e.boot&&e.boot(),e.element.querySelector("#next-step").addEventListener("click",()=>{e.element.setAttribute("style","display: none;"),this.steps=new Map(Array.from(this.steps.entries()).slice(1)),e.callback&&e.callback(),this.handleMethodSelect(t)})}submitForm(){this.submitting=!0,document.getElementById("payment-form").submit()}handle(){document.querySelectorAll(".dropdown-gateway-button").forEach(t=>{t.addEventListener("click",()=>{this.submitting||this.handleMethodSelect(t)})})}}const i=document.querySelector('meta[name="require-invoice-signature"]').content,l=document.querySelector('meta[name="show-invoice-terms"]').content,o=document.querySelector('meta[name="show-required-fields-form"]').content;new n(!!+l,!!+i,!!+o).handle(); diff --git a/public/build/manifest.json b/public/build/manifest.json index a63b3254e562..a1a49305cf23 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -23,7 +23,7 @@ "src": "resources/js/clients/invoices/action-selectors.js" }, "resources/js/clients/invoices/payment.js": { - "file": "assets/payment-0ace5bfa.js", + "file": "assets/payment-1e2ec1a7.js", "isEntry": true, "src": "resources/js/clients/invoices/payment.js" }, From 290827b2d8c4c3fedda7d63a22d88a0e1fdd2d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 31 Jan 2024 18:47:43 +0100 Subject: [PATCH 06/16] Refactor Payment class to handle additional steps --- resources/js/clients/invoices/payment.js | 136 +++++++++++------------ 1 file changed, 66 insertions(+), 70 deletions(-) diff --git a/resources/js/clients/invoices/payment.js b/resources/js/clients/invoices/payment.js index 588a3dfcfc58..7009d8abe044 100644 --- a/resources/js/clients/invoices/payment.js +++ b/resources/js/clients/invoices/payment.js @@ -9,11 +9,57 @@ */ class Payment { - constructor(displayTerms, displaySignature) { + constructor(displayTerms, displaySignature, displayRff) { this.shouldDisplayTerms = displayTerms; this.shouldDisplaySignature = displaySignature; - this.termsAccepted = false; + this.shouldDisplayRff = displayRff; + this.submitting = false; + this.steps = new Map() + + if (this.shouldDisplayRff) { + this.steps.set("rff", { + element: document.getElementById('displayRequiredFieldsModal'), + callback: () => { + const fields = { + firstName: document.querySelector('input[name="rff_first_name"]'), + lastName: document.querySelector('input[name="rff_last_name"]'), + email: document.querySelector('input[name="rff_email"]'), + } + + if (fields.firstName) { + document.querySelector('input[name="contact_first_name"]').value = fields.firstName.value; + } + + if (fields.lastName) { + document.querySelector('input[name="contact_last_name"]').value = fields.lastName.value; + } + + if (fields.email) { + document.querySelector('input[name="contact_email"]').value = fields.email.value; + } + } + }); + } + + if (this.shouldDisplaySignature) { + this.steps.set("signature", { + element: document.getElementById('displaySignatureModal'), + boot: () => this.signaturePad = new SignaturePad( + document.getElementById("signature-pad"), + { + penColor: "rgb(0, 0, 0)" + } + ), + callback: () => document.querySelector('input[name="signature"').value = this.signaturePad.toDataURL(), + }); + } + + if (this.shouldDisplayTerms) { + this.steps.set("terms", { + element: document.getElementById('displayTermsModal'), + }); + } } handleMethodSelect(element) { @@ -22,54 +68,30 @@ class Payment { element.dataset.companyGatewayId; document.getElementById("payment_method_id").value = element.dataset.gatewayTypeId; - - if (this.shouldDisplaySignature && !this.shouldDisplayTerms) { - - if(this.signaturePad && this.signaturePad.isEmpty()) - alert("Please sign"); - - this.displayTerms(); - - document - .getElementById("accept-terms-button") - .addEventListener("click", () => { - this.termsAccepted = true; - this.submitForm(); - }); + + if (this.steps.size === 0) { + return this.submitForm(); } - if (!this.shouldDisplaySignature && this.shouldDisplayTerms) { - this.displaySignature(); + const next = this.steps.values().next().value; - document - .getElementById("signature-next-step") - .addEventListener("click", () => { - document.querySelector('input[name="signature"').value = this.signaturePad.toDataURL(); - this.submitForm(); - }); + next.element.removeAttribute("style"); + + if (next.boot) { + next.boot(); } - if (this.shouldDisplaySignature && this.shouldDisplayTerms) { - this.displaySignature(); + next.element.querySelector('#next-step').addEventListener('click', () => { + next.element.setAttribute("style", "display: none;"); - document - .getElementById("signature-next-step") - .addEventListener("click", () => { - this.displayTerms(); + this.steps = new Map(Array.from(this.steps.entries()).slice(1)); - document - .getElementById("accept-terms-button") - .addEventListener("click", () => { - document.querySelector('input[name="signature"').value = this.signaturePad.toDataURL(); - this.termsAccepted = true; - this.submitForm(); - }); - }); - } + if (next.callback) { + next.callback(); + } - if (!this.shouldDisplaySignature && !this.shouldDisplayTerms) { - this.submitForm(); - } + this.handleMethodSelect(element); + }); } submitForm() { @@ -78,33 +100,6 @@ class Payment { document.getElementById("payment-form").submit(); } - displayTerms() { - let displayTermsModal = document.getElementById("displayTermsModal"); - displayTermsModal.removeAttribute("style"); - } - - displaySignature() { - document.getElementById("signature-next-step").disabled = true; - - let displaySignatureModal = document.getElementById( - "displaySignatureModal" - ); - displaySignatureModal.removeAttribute("style"); - - const signaturePad = new SignaturePad( - document.getElementById("signature-pad"), - { - penColor: "rgb(0, 0, 0)" - } - ); - - signaturePad.onEnd = function(){ - document.getElementById("signature-next-step").disabled = false; - }; - - this.signaturePad = signaturePad; - } - handle() { document @@ -124,5 +119,6 @@ const signature = document.querySelector( ).content; const terms = document.querySelector('meta[name="show-invoice-terms"]').content; +const rff = document.querySelector('meta[name="show-required-fields-form"]').content; -new Payment(Boolean(+signature), Boolean(+terms)).handle(); +new Payment(Boolean(+terms), Boolean(+signature), Boolean(+rff)).handle(); From 35587cdab46b699667f4b49207c3933f1b682d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 31 Jan 2024 18:47:46 +0100 Subject: [PATCH 07/16] Add required contact fields to payment form --- resources/views/portal/ninja2020/invoices/payment.blade.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/resources/views/portal/ninja2020/invoices/payment.blade.php b/resources/views/portal/ninja2020/invoices/payment.blade.php index 55780424eb16..f11b69c72361 100644 --- a/resources/views/portal/ninja2020/invoices/payment.blade.php +++ b/resources/views/portal/ninja2020/invoices/payment.blade.php @@ -4,6 +4,7 @@ @push('head') + @endpush @@ -17,6 +18,9 @@ + + +
@@ -150,6 +154,7 @@
+@include('portal.ninja2020.invoices.includes.required-fields') @include('portal.ninja2020.invoices.includes.terms', ['entities' => $invoices, 'variables' => $variables, 'entity_type' => ctrans('texts.invoice')]) @include('portal.ninja2020.invoices.includes.signature') From d70c07bb5ce9ddd21fb8ce9d8f7ffa57b43f6648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 31 Jan 2024 18:47:52 +0100 Subject: [PATCH 08/16] Add required fields modal to invoices --- .../includes/required-fields.blade.php | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 resources/views/portal/ninja2020/invoices/includes/required-fields.blade.php diff --git a/resources/views/portal/ninja2020/invoices/includes/required-fields.blade.php b/resources/views/portal/ninja2020/invoices/includes/required-fields.blade.php new file mode 100644 index 000000000000..e46d5a13fde4 --- /dev/null +++ b/resources/views/portal/ninja2020/invoices/includes/required-fields.blade.php @@ -0,0 +1,104 @@ + From 724ffca9f6aef51f1cbb3b8045825ad6a842db93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 31 Jan 2024 18:47:55 +0100 Subject: [PATCH 09/16] Update signature button ID --- .../ninja2020/invoices/includes/signature.blade.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/resources/views/portal/ninja2020/invoices/includes/signature.blade.php b/resources/views/portal/ninja2020/invoices/includes/signature.blade.php index 3f9ce3c5b779..773e40a94d58 100644 --- a/resources/views/portal/ninja2020/invoices/includes/signature.blade.php +++ b/resources/views/portal/ninja2020/invoices/includes/signature.blade.php @@ -27,7 +27,7 @@
-
@@ -40,3 +40,12 @@
+ + \ No newline at end of file From b61d68847b9c0ffccb392d52ede51344baea20e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 31 Jan 2024 18:47:58 +0100 Subject: [PATCH 10/16] Update accept terms button ID --- .../views/portal/ninja2020/invoices/includes/terms.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/portal/ninja2020/invoices/includes/terms.blade.php b/resources/views/portal/ninja2020/invoices/includes/terms.blade.php index c66af59f992c..d1e3d67b35c2 100644 --- a/resources/views/portal/ninja2020/invoices/includes/terms.blade.php +++ b/resources/views/portal/ninja2020/invoices/includes/terms.blade.php @@ -29,7 +29,7 @@
-
@@ -40,12 +40,3 @@
- - \ No newline at end of file From d4ea1ee83992a454feaec41398e2cac86617befa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 31 Jan 2024 18:59:40 +0100 Subject: [PATCH 13/16] Update accept terms button ID --- .../views/portal/ninja2020/invoices/includes/terms.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/portal/ninja2020/invoices/includes/terms.blade.php b/resources/views/portal/ninja2020/invoices/includes/terms.blade.php index d1e3d67b35c2..c66af59f992c 100644 --- a/resources/views/portal/ninja2020/invoices/includes/terms.blade.php +++ b/resources/views/portal/ninja2020/invoices/includes/terms.blade.php @@ -29,7 +29,7 @@