Refactor AXO module

This commit is contained in:
Pedro Silva 2024-03-14 10:54:15 +00:00
parent a9d2f97a80
commit 578a5426dc
No known key found for this signature in database
GPG key ID: E2EE20C0669D24B3
7 changed files with 337 additions and 121 deletions

View file

@ -16,15 +16,20 @@ class AxoManager {
this.fastlane = new Fastlane();
this.$ = jQuery;
this.isConnectProfile = false;
this.isNewProfile = false;
this.hideGatewaySelection = false;
this.status = {
active: false,
validEmail: false,
hasProfile: false,
useEmailWidget: this.useEmailWidget()
};
this.data = {
billing: null,
shipping: null,
card: null,
}
};
this.el = new DomElementCollection();
@ -32,7 +37,7 @@ class AxoManager {
root: {
backgroundColorPrimary: '#ffffff'
}
}
};
this.locale = 'en_us';
@ -40,7 +45,11 @@ class AxoManager {
this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector, this.el);
this.billingView = new BillingView(this.el.billingAddressContainer.selector, this.el);
this.cardView = new CardView(this.el.paymentContainer.selector, this.el, this);
this.cardView = new CardView(this.el.paymentContainer.selector + '-details', this.el, this);
document.testAxoStatus = (key, value) => {
this.setStatus(key, value);
}
}
registerEventHandlers() {
@ -48,9 +57,9 @@ class AxoManager {
// Listen to Gateway Radio button changes.
this.el.gatewayRadioButton.on('change', (ev) => {
if (ev.target.checked) {
this.showAxo();
this.activateAxo();
} else {
this.hideAxo();
this.deactivateAxo();
}
});
@ -66,33 +75,27 @@ class AxoManager {
// Click change shipping address link.
this.el.changeShippingAddressLink.on('click', async () => {
if (this.isConnectProfile) {
console.log('profile', this.fastlane.profile);
//this.shippingView.deactivate();
if (this.status.hasProfile) {
const { selectionChanged, selectedAddress } = await this.fastlane.profile.showShippingAddressSelector();
console.log('selectedAddress', selectedAddress);
if (selectionChanged) {
this.setShipping(selectedAddress);
this.shippingView.activate();
this.shippingView.refresh();
}
}
});
// Click change billing address link.
this.el.changeBillingAddressLink.on('click', async () => {
if (this.isConnectProfile) {
if (this.status.hasProfile) {
this.el.changeCardLink.trigger('click');
}
});
// Click change card link.
this.el.changeCardLink.on('click', async () => {
console.log('profile', this.fastlane.profile);
const response = await this.fastlane.profile.showCardSelector();
console.log('card response', response);
@ -114,67 +117,229 @@ class AxoManager {
}
showAxo() {
this.initPlacements();
this.initFastlane();
rerender() {
/**
* active | 0 1 1 1
* validEmail | * 0 1 1
* hasProfile | * * 0 1
* --------------------------------
* defaultSubmitButton | 1 0 0 0
* defaultEmailField | 1 0 0 0
* defaultFormFields | 1 0 1 0
* extraFormFields | 0 0 0 1
* axoEmailField | 0 1 0 0
* axoProfileViews | 0 0 0 1
* axoPaymentContainer | 0 0 1 1
* axoSubmitButton | 0 0 1 1
*/
const scenario = this.identifyScenario(
this.status.active,
this.status.validEmail,
this.status.hasProfile
);
if (!this.isNewProfile && !this.isConnectProfile) {
this.el.allFields.hide();
log('Scenario', scenario);
// Reset some elements to a default status.
this.el.watermarkContainer.hide();
if (scenario.defaultSubmitButton) {
this.el.defaultSubmitButton.show();
} else {
this.el.defaultSubmitButton.hide();
}
if (this.useEmailWidget()) {
if (scenario.defaultEmailField) {
this.el.fieldBillingEmail.show();
} else {
this.el.fieldBillingEmail.hide();
}
if (scenario.defaultFormFields) {
this.el.customerDetails.show();
} else {
this.el.customerDetails.hide();
}
if (scenario.extraFormFields) {
this.el.customerDetails.show();
// Hiding of unwanted will be handled by the axoProfileViews handler.
}
if (scenario.axoEmailField) {
this.showAxoEmailField();
this.el.watermarkContainer.show();
// Move watermark to after email.
this.$(this.el.fieldBillingEmail.selector).append(
this.$(this.el.watermarkContainer.selector)
);
} else {
this.el.emailWidgetContainer.hide();
if (!scenario.defaultEmailField) {
this.el.fieldBillingEmail.hide();
}
}
if (scenario.axoProfileViews) {
this.shippingView.activate();
this.billingView.activate();
this.cardView.activate();
// Move watermark to after shipping.
this.$(this.el.shippingAddressContainer.selector).after(
this.$(this.el.watermarkContainer.selector)
);
this.el.watermarkContainer.show();
} else {
this.shippingView.deactivate();
this.billingView.deactivate();
this.cardView.deactivate();
}
if (scenario.axoPaymentContainer) {
this.el.paymentContainer.show();
} else {
this.el.paymentContainer.hide();
}
if (scenario.axoSubmitButton) {
this.el.submitButtonContainer.show();
} else {
this.el.submitButtonContainer.hide();
}
this.ensureBillingFieldsConsistency();
this.ensureShippingFieldsConsistency();
}
identifyScenario(active, validEmail, hasProfile) {
let response = {
defaultSubmitButton: false,
defaultEmailField: false,
defaultFormFields: false,
extraFormFields: false,
axoEmailField: false,
axoProfileViews: false,
axoPaymentContainer: false,
axoSubmitButton: false,
}
if (active && validEmail && hasProfile) {
response.extraFormFields = true;
response.axoProfileViews = true;
response.axoPaymentContainer = true;
response.axoSubmitButton = true;
return response;
}
if (active && validEmail && !hasProfile) {
response.defaultFormFields = true;
response.axoEmailField = true;
response.axoPaymentContainer = true;
response.axoSubmitButton = true;
return response;
}
if (active && !validEmail) {
response.axoEmailField = true;
return response;
}
if (!active) {
response.defaultSubmitButton = true;
response.defaultEmailField = true;
response.defaultFormFields = true;
return response;
}
throw new Error('Invalid scenario.');
}
ensureBillingFieldsConsistency() {
const $billingFields = this.$('.woocommerce-billing-fields .form-row:visible');
const $billingHeaders = this.$('.woocommerce-billing-fields h3');
if (this.billingView.isActive()) {
if ($billingFields.length) {
$billingHeaders.show();
} else {
$billingHeaders.hide();
}
} else {
$billingHeaders.show();
}
}
ensureShippingFieldsConsistency() {
const $shippingFields = this.$('.woocommerce-shipping-fields .form-row:visible');
const $shippingHeaders = this.$('.woocommerce-shipping-fields h3');
if (this.shippingView.isActive()) {
if ($shippingFields.length) {
$shippingHeaders.show();
} else {
$shippingHeaders.hide();
}
} else {
$shippingHeaders.show();
}
}
showAxoEmailField() {
if (this.status.useEmailWidget) {
this.el.emailWidgetContainer.show();
this.el.fieldBillingEmail.hide();
} else {
this.el.emailWidgetContainer.hide();
this.el.fieldBillingEmail.show();
}
if (this.isConnectProfile) {
this.shippingView.activate();
this.billingView.activate();
this.el.emailWidgetContainer.hide();
this.el.fieldBillingEmail.hide();
}
this.el.watermarkContainer.show();
this.el.paymentContainer.show();
this.el.submitButtonContainer.show();
this.el.defaultSubmitButton.hide();
}
hideAxo() {
this.el.allFields.show();
setStatus(key, value) {
this.status[key] = value;
this.shippingView.deactivate();
this.billingView.deactivate();
log('Status updated', JSON.parse(JSON.stringify(this.status)));
this.el.emailWidgetContainer.hide();
this.el.watermarkContainer.hide();
this.el.paymentContainer.hide();
this.el.submitButtonContainer.hide();
this.el.defaultSubmitButton.show();
this.rerender();
}
this.el.emailWidgetContainer.hide();
this.el.fieldBillingEmail.show();
activateAxo() {
this.initPlacements();
this.initFastlane();
this.setStatus('active', true);
const emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input');
if (emailInput && this.lastEmailCheckedIdentity !== emailInput.value) {
this.onChangeEmail();
}
}
deactivateAxo() {
this.setStatus('active', false);
}
initPlacements() {
let emailRow = document.querySelector(this.el.fieldBillingEmail.selector);
const wrapper = this.el.axoCustomerDetails;
// Customer details container.
if (!document.querySelector(wrapper.selector)) {
document.querySelector(wrapper.anchorSelector).insertAdjacentHTML('afterbegin', `
<div id="${wrapper.id}" class="${wrapper.className}"></div>
`);
}
const wrapperElement = document.querySelector(wrapper.selector);
// Billing view container.
const bc = this.el.billingAddressContainer;
const sc = this.el.shippingAddressContainer;
const ec = this.el.emailWidgetContainer;
if (!document.querySelector(bc.selector)) {
document.querySelector(bc.anchorSelector).insertAdjacentHTML('beforeend', `
wrapperElement.insertAdjacentHTML('beforeend', `
<div id="${bc.id}" class="${bc.className}"></div>
`);
}
// Shipping view container.
const sc = this.el.shippingAddressContainer;
if (!document.querySelector(sc.selector)) {
document.querySelector(sc.anchorSelector).insertAdjacentHTML('afterbegin', `
wrapperElement.insertAdjacentHTML('beforeend', `
<div id="${sc.id}" class="${sc.className}"></div>
`);
}
@ -182,8 +347,9 @@ class AxoManager {
if (this.useEmailWidget()) {
// Display email widget.
const ec = this.el.emailWidgetContainer;
if (!document.querySelector(ec.selector)) {
emailRow.parentNode.insertAdjacentHTML('afterbegin', `
wrapperElement.insertAdjacentHTML('afterbegin', `
<div id="${ec.id}" class="${ec.className}">
--- EMAIL WIDGET PLACEHOLDER ---
</div>
@ -192,8 +358,9 @@ class AxoManager {
} else {
// Move email row to first place.
emailRow.parentNode.prepend(emailRow);
// Move email to the AXO container.
let emailRow = document.querySelector(this.el.fieldBillingEmail.selector);
wrapperElement.prepend(emailRow);
emailRow.querySelector('input').focus();
}
}
@ -229,7 +396,10 @@ class AxoManager {
const gatewayPaymentContainer = document.querySelector('.payment_method_ppcp-axo-gateway');
gatewayPaymentContainer.insertAdjacentHTML('beforeend', `
<div id="${this.el.paymentContainer.id}" class="${this.el.paymentContainer.className} hidden"></div>
<div id="${this.el.paymentContainer.id}" class="${this.el.paymentContainer.className} hidden">
<div id="${this.el.paymentContainer.id}-form" class="${this.el.paymentContainer.className}-form"></div>
<div id="${this.el.paymentContainer.id}-details" class="${this.el.paymentContainer.className}-details"></div>
</div>
`);
}
@ -259,20 +429,33 @@ class AxoManager {
if (this.emailInput.value) {
this.onChangeEmail();
}
}
}
async onChangeEmail () {
log('Email changed: ' + this.emailInput.value);
if (!this.status.active) {
log('Email checking skipped, AXO not active.');
return;
}
if (!this.emailInput) {
log('Email field not initialized.');
return;
}
log('Email changed: ' + (this.emailInput ? this.emailInput.value : '<empty>'));
this.$(this.el.paymentContainer.selector + '-detail').html('');
this.$(this.el.paymentContainer.selector + '-form').html('');
this.setStatus('validEmail', false);
this.setStatus('hasProfile', false);
this.isConnectProfile = false;
this.isNewProfile = false;
this.hideGatewaySelection = false;
this.el.allFields.hide();
this.lastEmailCheckedIdentity = this.emailInput.value;
if (!this.emailInput.checkValidity()) {
if (!this.emailInput.value || !this.emailInput.checkValidity()) {
log('The email address is not valid.');
return;
}
@ -287,8 +470,6 @@ class AxoManager {
// Authenticate the customer to get access to their profile.
log('Email is associated with a Connect profile or a PayPal member');
// TODO : enter hideOtherGateways mode
const authResponse = await this.fastlane.identity.triggerAuthenticationFlow(lookupResponse.customerContextId);
log('AuthResponse', authResponse);
@ -296,25 +477,18 @@ class AxoManager {
if (authResponse.authenticationState === 'succeeded') {
log(JSON.stringify(authResponse));
this.el.allFields.show();
this.el.paymentContainer.show();
// document.querySelector(this.el.paymentContainer.selector).innerHTML =
// '<a href="javascript:void(0)" data-ppcp-axo-change-card>Change card</a>';
// Add addresses
this.setShipping(authResponse.profileData.shippingAddress);
// TODO : set billing
this.setCard(authResponse.profileData.card);
this.isConnectProfile = true;
this.setStatus('validEmail', true);
this.setStatus('hasProfile', true);
this.hideGatewaySelection = true;
this.$('.wc_payment_methods label').hide();
this.shippingView.activate();
this.billingView.activate();
this.cardView.activate();
this.rerender();
} else {
// authentication failed or canceled by the customer
@ -326,14 +500,12 @@ class AxoManager {
// This is a guest customer.
log('No profile found with this email address.');
this.el.allFields.show();
this.el.paymentContainer.show();
this.isNewProfile = true;
this.setStatus('validEmail', true);
this.setStatus('hasProfile', false);
this.cardComponent = await this.fastlane
.FastlaneCardComponent(MockData.cardComponent())
.render(this.el.paymentContainer.selector);
.render(this.el.paymentContainer.selector + '-form');
}
}

View file

@ -23,8 +23,15 @@ class DomElementCollection {
className: 'ppcp-axo-watermark-container'
});
this.allFields = new DomElement({
selector: '#customer_details .form-row, #customer_details .woocommerce-shipping-fields'
this.customerDetails = new DomElement({
selector: '#customer_details > *:not(#ppcp-axo-customer-details)'
});
this.axoCustomerDetails = new DomElement({
id: 'ppcp-axo-customer-details',
selector: '#ppcp-axo-customer-details',
className: 'ppcp-axo-customer-details',
anchorSelector: '#customer_details'
});
this.emailWidgetContainer = new DomElement({
@ -36,15 +43,13 @@ class DomElementCollection {
this.shippingAddressContainer = new DomElement({
id: 'ppcp-axo-shipping-address-container',
selector: '#ppcp-axo-shipping-address-container',
className: 'ppcp-axo-shipping-address-container',
anchorSelector: '.woocommerce-shipping-fields'
className: 'ppcp-axo-shipping-address-container'
});
this.billingAddressContainer = new DomElement({
id: 'ppcp-axo-billing-address-container',
selector: '#ppcp-axo-billing-address-container',
className: 'ppcp-axo-billing-address-container',
anchorSelector: '.woocommerce-billing-fields__field-wrapper'
className: 'ppcp-axo-billing-address-container'
});
this.fieldBillingEmail = new DomElement({

View file

@ -48,36 +48,40 @@ class MockData {
content.innerHTML = '';
if (!this.active) {
this.hideField(this.contentSelector);
} else {
this.showField(this.contentSelector);
}
Object.keys(this.fields).forEach((key) => {
const field = this.fields[key];
if (this.active) {
this.hideField(field.selector);
//this.showField(this.contentSelector);
} else {
this.showField(field.selector);
//this.hideField(this.contentSelector);
}
if (typeof this.template === 'function') {
content.innerHTML = this.template({
value: (valueKey) => {
return this.getDataValue(this.fields[valueKey].valuePath);
},
isEmpty: () => {
let isEmpty = true;
Object.values(this.fields).forEach((valueField) => {
if (this.getDataValue(valueField.valuePath)) {
isEmpty = false;
return false;
}
});
return isEmpty;
}
});
}
});
if (typeof this.template === 'function') {
content.innerHTML = this.template({
value: (valueKey) => {
return this.getDataValue(this.fields[valueKey].valuePath);
},
isEmpty: () => {
let isEmpty = true;
Object.values(this.fields).forEach((valueField) => {
if (this.getDataValue(valueField.valuePath)) {
isEmpty = false;
return false;
}
});
return isEmpty;
}
});
}
}
showField(selector) {

View file

@ -21,14 +21,14 @@ class BillingView {
if (data.isEmpty()) {
return `
<div style="margin-bottom: 20px;">
<h3>Billing details <a href="javascript:void(0)" ${this.el.changeBillingAddressLink.attributes} style="margin-left: 20px;">Edit</a></h3>
<h3>Billing <a href="javascript:void(0)" ${this.el.changeBillingAddressLink.attributes} style="margin-left: 20px;">Edit</a></h3>
<div>Please fill in your billing details.</div>
</div>
`;
}
return `
<div style="margin-bottom: 20px;">
<h3>Billing details <a href="javascript:void(0)" ${this.el.changeBillingAddressLink.attributes} style="margin-left: 20px;">Edit</a></h3>
<h3>Billing <a href="javascript:void(0)" ${this.el.changeBillingAddressLink.attributes} style="margin-left: 20px;">Edit</a></h3>
<div>${data.value('email')}</div>
<div>${data.value('company')}</div>
<div>${data.value('firstName')} ${data.value('lastName')}</div>
@ -89,6 +89,10 @@ class BillingView {
});
}
isActive() {
return this.billingFormFields.active;
}
activate() {
this.billingFormFields.activate();
}
@ -97,6 +101,10 @@ class BillingView {
this.billingFormFields.deactivate();
}
refresh() {
this.billingFormFields.refresh();
}
setData(data) {
this.billingFormFields.setData(data);
}

View file

@ -20,19 +20,40 @@ class CardView {
if (data.isEmpty()) {
return `
<div style="margin-bottom: 20px; text-align: center;">
<div>Please fill in your card details.</div>
<h4><a href="javascript:void(0)" ${this.el.changeCardLink.attributes}>Edit</a></h4>
<div style="border:2px solid #cccccc; border-radius: 10px; padding: 26px 20px; margin-bottom: 20px; background-color:#f6f6f6">
<div>Please fill in your card details.</div>
</div>
<h4><a href="javascript:void(0)" ${this.el.changeCardLink.attributes}>Add card details</a></h4>
${selectOtherPaymentMethod()}
</div>
`;
}
const expiry = data.value('expiry').split('-');
const cardIcons = {
'VISA': 'visa-dark.svg',
'MASTERCARD_CARD': 'mastercard-dark.svg',
'AMEX': 'amex.svg',
'DISCOVER': 'discover.svg',
};
return `
<div style="margin-bottom: 20px;">
<h3>Card Details <a href="javascript:void(0)" ${this.el.changeCardLink.attributes}>Edit</a></h3>
<div>${data.value('name')}</div>
<div>${data.value('brand')}</div>
<div>${data.value('lastDigits') ? '************' + data.value('lastDigits'): ''}</div>
<div>${data.value('expiry')}</div>
<h3>Card Details <a href="javascript:void(0)" ${this.el.changeCardLink.attributes} style="margin-left: 20px;">Edit</a></h3>
<div style="border:2px solid #cccccc; border-radius: 10px; padding: 16px 20px; background-color:#f6f6f6">
<div style="float: right;">
<img
class="ppcp-card-icon"
title="${data.value('brand')}"
src="/wp-content/plugins/woocommerce-paypal-payments/modules/ppcp-wc-gateway/assets/images/${cardIcons[data.value('brand')]}"
alt="${data.value('brand')}"
>
</div>
<div style="font-family: monospace; font-size: 1rem; margin-top: 10px;">${data.value('lastDigits') ? '**** **** **** ' + data.value('lastDigits'): ''}</div>
<div>${expiry[1]}/${expiry[0]}</div>
<div style="text-transform: uppercase">${data.value('name')}</div>
</div>
${selectOtherPaymentMethod()}
</div>
`;

View file

@ -21,14 +21,14 @@ class ShippingView {
if (data.isEmpty()) {
return `
<div style="margin-bottom: 20px;">
<h3>Shipping details <a href="javascript:void(0)" ${this.el.changeShippingAddressLink.attributes} style="margin-left: 20px;">Edit</a></h3>
<h3>Shipping <a href="javascript:void(0)" ${this.el.changeShippingAddressLink.attributes} style="margin-left: 20px;">Edit</a></h3>
<div>Please fill in your shipping details.</div>
</div>
`;
}
return `
<div style="margin-bottom: 20px;">
<h3>Shipping details <a href="javascript:void(0)" ${this.el.changeShippingAddressLink.attributes} style="margin-left: 20px;">Edit</a></h3>
<h3>Shipping <a href="javascript:void(0)" ${this.el.changeShippingAddressLink.attributes} style="margin-left: 20px;">Edit</a></h3>
<div>${data.value('company')}</div>
<div>${data.value('firstName')} ${data.value('lastName')}</div>
<div>${data.value('street1')}</div>
@ -85,6 +85,10 @@ class ShippingView {
});
}
isActive() {
return this.shippingFormFields.active;
}
activate() {
this.shippingFormFields.activate();
}
@ -93,6 +97,10 @@ class ShippingView {
this.shippingFormFields.deactivate();
}
refresh() {
this.shippingFormFields.refresh();
}
setData(data) {
this.shippingFormFields.setData(data);
}