Merge branch 'trunk' into PCP-3891-Store-Manual-Connection-details-in-DB

This commit is contained in:
Philipp Stracker 2024-11-22 12:19:38 +01:00 committed by GitHub
commit 25d16d317c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
152 changed files with 5801 additions and 2656 deletions

View file

@ -61,19 +61,24 @@ class PaymentMethodTokensEndpoint {
* Creates a setup token.
*
* @param PaymentSource $payment_source The payment source.
* @param string $customer_id PayPal customer ID.
*
* @return stdClass
*
* @throws RuntimeException When something when wrong with the request.
* @throws PayPalApiException When something when wrong setting up the token.
*/
public function setup_tokens( PaymentSource $payment_source ): stdClass {
public function setup_tokens( PaymentSource $payment_source, string $customer_id = '' ): stdClass {
$data = array(
'payment_source' => array(
$payment_source->name() => $payment_source->properties(),
),
);
if ( $customer_id ) {
$data['customer']['id'] = $customer_id;
}
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v3/vault/setup-tokens';
@ -109,19 +114,24 @@ class PaymentMethodTokensEndpoint {
* Creates a payment token for the given payment source.
*
* @param PaymentSource $payment_source The payment source.
* @param string $customer_id PayPal customer ID.
*
* @return stdClass
*
* @throws RuntimeException When something when wrong with the request.
* @throws PayPalApiException When something when wrong setting up the token.
*/
public function create_payment_token( PaymentSource $payment_source ): stdClass {
public function create_payment_token( PaymentSource $payment_source, string $customer_id = '' ): stdClass {
$data = array(
'payment_source' => array(
$payment_source->name() => $payment_source->properties(),
),
);
if ( $customer_id ) {
$data['customer']['id'] = $customer_id;
}
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v3/vault/payment-tokens';

View file

@ -92,6 +92,24 @@ class ApplePayButton extends PaymentButton {
*/
#product = {};
/**
* The start time of the configuration process.
* @type {number}
*/
#configureStartTime = 0;
/**
* The maximum time to wait for buttonAttributes before proceeding with initialization.
* @type {number}
*/
#maxWaitTime = 1000;
/**
* The stored button attributes.
* @type {Object|null}
*/
#storedButtonAttributes = null;
/**
* @inheritDoc
*/
@ -125,7 +143,8 @@ class ApplePayButton extends PaymentButton {
externalHandler,
buttonConfig,
ppcpConfig,
contextHandler
contextHandler,
buttonAttributes
) {
// Disable debug output in the browser console:
// buttonConfig.is_debug = false;
@ -135,7 +154,8 @@ class ApplePayButton extends PaymentButton {
externalHandler,
buttonConfig,
ppcpConfig,
contextHandler
contextHandler,
buttonAttributes
);
this.init = this.init.bind( this );
@ -220,6 +240,20 @@ class ApplePayButton extends PaymentButton {
'No transactionInfo - missing configure() call?'
);
invalidIf(
() =>
this.buttonAttributes?.height &&
isNaN( parseInt( this.buttonAttributes.height ) ),
'Invalid height in buttonAttributes'
);
invalidIf(
() =>
this.buttonAttributes?.borderRadius &&
isNaN( parseInt( this.buttonAttributes.borderRadius ) ),
'Invalid borderRadius in buttonAttributes'
);
invalidIf(
() => ! this.contextHandler?.validateContext(),
`Invalid context handler.`
@ -229,12 +263,60 @@ class ApplePayButton extends PaymentButton {
/**
* Configures the button instance. Must be called before the initial `init()`.
*
* @param {Object} apiConfig - API configuration.
* @param {TransactionInfo} transactionInfo - Transaction details.
* @param {Object} apiConfig - API configuration.
* @param {TransactionInfo} transactionInfo - Transaction details.
* @param {Object} buttonAttributes - Button attributes.
*/
configure( apiConfig, transactionInfo ) {
configure( apiConfig, transactionInfo, buttonAttributes = {} ) {
// Start timing on first configure call
if ( ! this.#configureStartTime ) {
this.#configureStartTime = Date.now();
}
// If valid buttonAttributes, store them
if ( buttonAttributes?.height && buttonAttributes?.borderRadius ) {
this.#storedButtonAttributes = { ...buttonAttributes };
}
// Use stored attributes if current ones are missing
const attributes = buttonAttributes?.height
? buttonAttributes
: this.#storedButtonAttributes;
// Check if we've exceeded wait time
const timeWaited = Date.now() - this.#configureStartTime;
if ( timeWaited > this.#maxWaitTime ) {
this.log(
'ApplePay: Timeout waiting for buttonAttributes - proceeding with initialization'
);
this.#applePayConfig = apiConfig;
this.#transactionInfo = transactionInfo;
this.buttonAttributes = attributes || buttonAttributes;
this.init();
return;
}
// Block any initialization until we have valid buttonAttributes
if ( ! attributes?.height || ! attributes?.borderRadius ) {
setTimeout(
() =>
this.configure(
apiConfig,
transactionInfo,
buttonAttributes
),
100
);
return;
}
// Reset timer for future configure calls
this.#configureStartTime = 0;
this.#applePayConfig = apiConfig;
this.#transactionInfo = transactionInfo;
this.buttonAttributes = attributes;
this.init();
}
init() {
@ -321,17 +403,43 @@ class ApplePayButton extends PaymentButton {
applyWrapperStyles() {
super.applyWrapperStyles();
const { height } = this.style;
const wrapper = this.wrapperElement;
if ( ! wrapper ) {
return;
}
if ( height ) {
const wrapper = this.wrapperElement;
// Try stored attributes if current ones are missing
const attributes =
this.buttonAttributes?.height || this.buttonAttributes?.borderRadius
? this.buttonAttributes
: this.#storedButtonAttributes;
const defaultHeight = 48;
const defaultBorderRadius = 4;
const height = attributes?.height
? parseInt( attributes.height, 10 )
: defaultHeight;
if ( ! isNaN( height ) ) {
wrapper.style.setProperty(
'--apple-pay-button-height',
`${ height }px`
);
wrapper.style.height = `${ height }px`;
} else {
wrapper.style.setProperty(
'--apple-pay-button-height',
`${ defaultHeight }px`
);
wrapper.style.height = `${ defaultHeight }px`;
}
const borderRadius = attributes?.borderRadius
? parseInt( attributes.borderRadius, 10 )
: defaultBorderRadius;
if ( ! isNaN( borderRadius ) ) {
wrapper.style.borderRadius = `${ borderRadius }px`;
}
}
@ -342,12 +450,23 @@ class ApplePayButton extends PaymentButton {
addButton() {
const { color, type, language } = this.style;
// If current buttonAttributes are missing, try to use stored ones
if (
! this.buttonAttributes?.height &&
this.#storedButtonAttributes?.height
) {
this.buttonAttributes = { ...this.#storedButtonAttributes };
}
const button = document.createElement( 'apple-pay-button' );
button.id = 'apple-' + this.wrapperId;
button.setAttribute( 'buttonstyle', color );
button.setAttribute( 'type', type );
button.setAttribute( 'locale', language );
button.style.display = 'block';
button.addEventListener( 'click', ( evt ) => {
evt.preventDefault();
this.onButtonClick();

View file

@ -3,65 +3,83 @@ import ApplePayButton from './ApplepayButton';
import ContextHandlerFactory from './Context/ContextHandlerFactory';
class ApplePayManager {
#namespace = '';
#buttonConfig = null;
#ppcpConfig = null;
#applePayConfig = null;
#contextHandler = null;
#transactionInfo = null;
#buttons = [];
constructor( namespace, buttonConfig, ppcpConfig, buttonAttributes = {} ) {
this.namespace = namespace;
this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig;
this.buttonAttributes = buttonAttributes;
this.applePayConfig = null;
this.transactionInfo = null;
this.contextHandler = null;
constructor( namespace, buttonConfig, ppcpConfig ) {
this.#namespace = namespace;
this.#buttonConfig = buttonConfig;
this.#ppcpConfig = ppcpConfig;
this.buttons = [];
this.onContextBootstrap = this.onContextBootstrap.bind( this );
buttonModuleWatcher.watchContextBootstrap( this.onContextBootstrap );
}
buttonModuleWatcher.watchContextBootstrap( async ( bootstrap ) => {
this.contextHandler = ContextHandlerFactory.create(
bootstrap.context,
buttonConfig,
ppcpConfig,
bootstrap.handler
);
async onContextBootstrap( bootstrap ) {
this.#contextHandler = ContextHandlerFactory.create(
bootstrap.context,
this.#buttonConfig,
this.#ppcpConfig,
bootstrap.handler
);
const button = ApplePayButton.createButton(
bootstrap.context,
bootstrap.handler,
buttonConfig,
ppcpConfig,
this.contextHandler,
this.buttonAttributes
);
const button = ApplePayButton.createButton(
bootstrap.context,
bootstrap.handler,
this.#buttonConfig,
this.#ppcpConfig,
this.#contextHandler
);
this.buttons.push( button );
const initButton = () => {
button.configure(
this.applePayConfig,
this.transactionInfo,
this.buttonAttributes
);
button.init();
};
this.#buttons.push( button );
// Initialize button only if applePayConfig and transactionInfo are already fetched.
if ( this.applePayConfig && this.transactionInfo ) {
initButton();
} else {
// Ensure ApplePayConfig is loaded before proceeding.
await this.init();
// Ensure ApplePayConfig is loaded before proceeding.
await this.init();
button.configure( this.#applePayConfig, this.#transactionInfo );
button.init();
if ( this.applePayConfig && this.transactionInfo ) {
initButton();
}
}
} );
}
async init() {
try {
if ( ! this.#applePayConfig ) {
this.#applePayConfig = await window[ this.#namespace ]
if ( ! this.applePayConfig ) {
// Gets ApplePay configuration of the PayPal merchant.
this.applePayConfig = await window[ this.namespace ]
.Applepay()
.config();
if ( ! this.#applePayConfig ) {
console.error( 'No ApplePayConfig received during init' );
}
}
if ( ! this.#transactionInfo ) {
this.#transactionInfo = await this.fetchTransactionInfo();
if ( ! this.transactionInfo ) {
this.transactionInfo = await this.fetchTransactionInfo();
}
if ( ! this.#applePayConfig ) {
console.error( 'No transactionInfo found during init' );
if ( ! this.applePayConfig ) {
console.error( 'No ApplePayConfig received during init' );
} else if ( ! this.transactionInfo ) {
console.error( 'No transactionInfo found during init' );
} else {
for ( const button of this.buttons ) {
button.configure(
this.applePayConfig,
this.transactionInfo,
this.buttonAttributes
);
button.init();
}
}
} catch ( error ) {
@ -71,10 +89,10 @@ class ApplePayManager {
async fetchTransactionInfo() {
try {
if ( ! this.#contextHandler ) {
if ( ! this.contextHandler ) {
throw new Error( 'ContextHandler is not initialized' );
}
return await this.#contextHandler.transactionInfo();
return await this.contextHandler.transactionInfo();
} catch ( error ) {
console.error( 'Error fetching transaction info:', error );
throw error;
@ -82,7 +100,7 @@ class ApplePayManager {
}
reinit() {
for ( const button of this.#buttons ) {
for ( const button of this.buttons ) {
button.reinit();
}
}

View file

@ -4,11 +4,13 @@ const ApplePayManagerBlockEditor = ( {
namespace,
buttonConfig,
ppcpConfig,
buttonAttributes,
} ) => (
<ApplepayButton
namespace={ namespace }
buttonConfig={ buttonConfig }
ppcpConfig={ ppcpConfig }
buttonAttributes={ buttonAttributes }
/>
);

View file

@ -4,7 +4,12 @@ import usePayPalScript from '../hooks/usePayPalScript';
import useApplepayScript from '../hooks/useApplepayScript';
import useApplepayConfig from '../hooks/useApplepayConfig';
const ApplepayButton = ( { namespace, buttonConfig, ppcpConfig } ) => {
const ApplepayButton = ( {
namespace,
buttonConfig,
ppcpConfig,
buttonAttributes,
} ) => {
const [ buttonHtml, setButtonHtml ] = useState( '' );
const [ buttonElement, setButtonElement ] = useState( null );
const [ componentFrame, setComponentFrame ] = useState( null );
@ -31,19 +36,42 @@ const ApplepayButton = ( { namespace, buttonConfig, ppcpConfig } ) => {
namespace,
buttonConfig,
ppcpConfig,
applepayConfig
applepayConfig,
buttonAttributes
);
useEffect( () => {
if ( applepayButton ) {
setButtonHtml( applepayButton.outerHTML );
if ( ! applepayButton || ! buttonElement ) {
return;
}
}, [ applepayButton ] );
setButtonHtml( applepayButton.outerHTML );
// Add timeout to ensure button is displayed after render
setTimeout( () => {
const button = buttonElement.querySelector( 'apple-pay-button' );
if ( button ) {
button.style.display = 'block';
}
}, 100 ); // Add a small delay to ensure DOM is ready
}, [ applepayButton, buttonElement ] );
return (
<div
ref={ setButtonElement }
dangerouslySetInnerHTML={ { __html: buttonHtml } }
style={ {
height: buttonAttributes?.height
? `${ buttonAttributes.height }px`
: '48px',
'--apple-pay-button-height': buttonAttributes?.height
? `${ buttonAttributes.height }px`
: '48px',
borderRadius: buttonAttributes?.borderRadius
? `${ buttonAttributes.borderRadius }px`
: undefined,
overflow: 'hidden',
} }
/>
);
};

View file

@ -19,7 +19,7 @@ if ( typeof window.PayPalCommerceGateway === 'undefined' ) {
window.PayPalCommerceGateway = ppcpConfig;
}
const ApplePayComponent = ( { isEditing } ) => {
const ApplePayComponent = ( { isEditing, buttonAttributes } ) => {
const [ paypalLoaded, setPaypalLoaded ] = useState( false );
const [ applePayLoaded, setApplePayLoaded ] = useState( false );
const wrapperRef = useRef( null );
@ -57,8 +57,13 @@ const ApplePayComponent = ( { isEditing } ) => {
buttonConfig.reactWrapper = wrapperRef.current;
new ManagerClass( namespace, buttonConfig, ppcpConfig );
}, [ paypalLoaded, applePayLoaded, isEditing ] );
new ManagerClass(
namespace,
buttonConfig,
ppcpConfig,
buttonAttributes
);
}, [ paypalLoaded, applePayLoaded, isEditing, buttonAttributes ] );
if ( isEditing ) {
return (
@ -66,6 +71,7 @@ const ApplePayComponent = ( { isEditing } ) => {
namespace={ namespace }
buttonConfig={ buttonConfig }
ppcpConfig={ ppcpConfig }
buttonAttributes={ buttonAttributes }
/>
);
}
@ -102,5 +108,6 @@ registerExpressPaymentMethod( {
canMakePayment: () => buttonData.enabled,
supports: {
features,
style: [ 'height', 'borderRadius' ],
},
} );

View file

@ -17,10 +17,19 @@ $fast-transition-duration: 0.5s;
}
// 1. AXO Block Radio Label
#ppcp-axo-block-radio-label {
@include flex-space-between;
.wc-block-checkout__payment-method label[for="radio-control-wc-payment-method-options-ppcp-axo-gateway"] {
padding-right: .875em;
}
#radio-control-wc-payment-method-options-ppcp-axo-gateway__label {
display: flex;
align-items: center;
width: 100%;
padding-right: 1em;
.wc-block-components-payment-method-icons {
margin: 0;
}
}
// 2. AXO Block Card
@ -70,15 +79,16 @@ $fast-transition-duration: 0.5s;
}
&__edit {
background-color: transparent;
flex-grow: 1;
margin-left: auto;
text-align: right;
border: 0;
color: inherit;
cursor: pointer;
display: block;
font-family: inherit;
margin: 0 0 0 auto;
font-size: 0.875em;
font-weight: normal;
color: inherit;
background-color: transparent;
cursor: pointer;
&:hover {
text-decoration: underline;

View file

@ -1,15 +1,28 @@
import { createElement } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { STORE_NAME } from '../../stores/axoStore';
/**
* Renders a button to change the selected card in the checkout process.
*
* @param {Object} props
* @param {Function} props.onChangeButtonClick - Callback function to handle the click event.
* @return {JSX.Element} The rendered button as an anchor tag.
* @return {JSX.Element|null} The rendered button as an anchor tag, or null if conditions aren't met.
*/
const CardChangeButton = ( { onChangeButtonClick } ) =>
createElement(
const CardChangeButton = () => {
const { isGuest, cardDetails, cardChangeHandler } = useSelect(
( select ) => ( {
isGuest: select( STORE_NAME ).getIsGuest(),
cardDetails: select( STORE_NAME ).getCardDetails(),
cardChangeHandler: select( STORE_NAME ).getCardChangeHandler(),
} ),
[]
);
if ( isGuest || ! cardDetails || ! cardChangeHandler ) {
return null;
}
return createElement(
'a',
{
className:
@ -19,10 +32,11 @@ const CardChangeButton = ( { onChangeButtonClick } ) =>
// Prevent default anchor behavior
event.preventDefault();
// Call the provided click handler
onChangeButtonClick();
cardChangeHandler();
},
},
__( 'Choose a different card', 'woocommerce-paypal-payments' )
);
};
export default CardChangeButton;

View file

@ -1,51 +0,0 @@
import { createElement, createRoot, useEffect } from '@wordpress/element';
import CardChangeButton from './CardChangeButton';
/**
* Manages the insertion and removal of the CardChangeButton in the DOM.
*
* @param {Object} props
* @param {Function} props.onChangeButtonClick - Callback function for when the card change button is clicked.
* @return {null} This component doesn't render any visible elements directly.
*/
const CardChangeButtonManager = ( { onChangeButtonClick } ) => {
useEffect( () => {
const radioLabelElement = document.getElementById(
'ppcp-axo-block-radio-label'
);
if ( radioLabelElement ) {
// Check if the change button doesn't already exist
if (
! radioLabelElement.querySelector(
'.wc-block-checkout-axo-block-card__edit'
)
) {
// Create a new container for the button
const buttonContainer = document.createElement( 'div' );
radioLabelElement.appendChild( buttonContainer );
// Create a React root and render the CardChangeButton
const root = createRoot( buttonContainer );
root.render(
createElement( CardChangeButton, { onChangeButtonClick } )
);
}
}
// Cleanup function to remove the button when the component unmounts
return () => {
const button = document.querySelector(
'.wc-block-checkout-axo-block-card__edit'
);
if ( button && button.parentNode ) {
button.parentNode.remove();
}
};
}, [ onChangeButtonClick ] );
// This component doesn't render anything directly
return null;
};
export default CardChangeButtonManager;

View file

@ -1,4 +1,2 @@
export { default as Card } from './Card';
export { default as CardChangeButton } from './CardChangeButton';
export { default as CardChangeButtonManager } from './CardChangeButtonManager';
export { injectCardChangeButton, removeCardChangeButton } from './utils';

View file

@ -1,32 +0,0 @@
import { createElement, createRoot } from '@wordpress/element';
import CardChangeButtonManager from './CardChangeButtonManager';
/**
* Injects a card change button into the DOM.
*
* @param {Function} onChangeButtonClick - Callback function for when the card change button is clicked.
*/
export const injectCardChangeButton = ( onChangeButtonClick ) => {
// Create a container for the button
const container = document.createElement( 'div' );
document.body.appendChild( container );
// Render the CardChangeButtonManager in the new container
createRoot( container ).render(
createElement( CardChangeButtonManager, { onChangeButtonClick } )
);
};
/**
* Removes the card change button from the DOM if it exists.
*/
export const removeCardChangeButton = () => {
const button = document.querySelector(
'.wc-block-checkout-axo-block-card__edit'
);
// Remove the button's parent node if it exists
if ( button && button.parentNode ) {
button.parentNode.remove();
}
};

View file

@ -0,0 +1,24 @@
import CardChangeButton from './../Card/CardChangeButton';
/**
* TitleLabel component for displaying a payment method title with icons and a change card button.
*
* @param {Object} props - Component props
* @param {Object} props.components - Object containing WooCommerce components
* @param {Object} props.config - Configuration object for the payment method
* @return {JSX.Element} WordPress element
*/
const TitleLabel = ( { components, config } ) => {
const axoConfig = window.wc_ppcp_axo;
const { PaymentMethodIcons } = components;
return (
<>
<span dangerouslySetInnerHTML={ { __html: config.title } } />
<PaymentMethodIcons icons={ axoConfig?.card_icons } />
<CardChangeButton />
</>
);
};
export default TitleLabel;

View file

@ -0,0 +1 @@
export { default as TitleLabel } from './TitleLabel';

View file

@ -1,7 +1,6 @@
import { log } from '../../../../ppcp-axo/resources/js/Helper/Debug';
import { populateWooFields } from '../helpers/fieldHelpers';
import { injectShippingChangeButton } from '../components/Shipping';
import { injectCardChangeButton } from '../components/Card';
import { setIsGuest, setIsEmailLookupCompleted } from '../stores/axoStore';
/**
@ -16,7 +15,6 @@ import { setIsGuest, setIsEmailLookupCompleted } from '../stores/axoStore';
* @param {Function} setWooShippingAddress - Function to update WooCommerce shipping address.
* @param {Function} setWooBillingAddress - Function to update WooCommerce billing address.
* @param {Function} onChangeShippingAddressClick - Handler for shipping address change.
* @param {Function} onChangeCardButtonClick - Handler for card change.
* @return {Function} The email lookup handler function.
*/
export const createEmailLookupHandler = (
@ -28,8 +26,7 @@ export const createEmailLookupHandler = (
wooBillingAddress,
setWooShippingAddress,
setWooBillingAddress,
onChangeShippingAddressClick,
onChangeCardButtonClick
onChangeShippingAddressClick
) => {
return async ( email ) => {
try {
@ -102,9 +99,8 @@ export const createEmailLookupHandler = (
setWooBillingAddress
);
// Inject change buttons for shipping and card
// Inject the change button for shipping
injectShippingChangeButton( onChangeShippingAddressClick );
injectCardChangeButton( onChangeCardButtonClick );
} else {
log( 'Authentication failed or did not succeed', 'warn' );
}

View file

@ -3,7 +3,6 @@ import { useDispatch } from '@wordpress/data';
import { log } from '../../../../ppcp-axo/resources/js/Helper/Debug';
import { STORE_NAME } from '../stores/axoStore';
import { removeShippingChangeButton } from '../components/Shipping';
import { removeCardChangeButton } from '../components/Card';
import { removeWatermark } from '../components/Watermark';
import {
removeEmailFunctionality,
@ -50,7 +49,6 @@ const useAxoCleanup = () => {
// Remove AXO UI elements
removeShippingChangeButton();
removeCardChangeButton();
removeWatermark();
// Remove email functionality if it was set up

View file

@ -35,6 +35,7 @@ const useAxoSetup = (
setIsAxoScriptLoaded,
setShippingAddress,
setCardDetails,
setCardChangeHandler,
} = useDispatch( STORE_NAME );
// Check if PayPal script has loaded
@ -73,6 +74,7 @@ const useAxoSetup = (
if ( paypalLoaded && fastlaneSdk ) {
setIsAxoScriptLoaded( true );
setIsAxoActive( true );
setCardChangeHandler( onChangeCardButtonClick );
// Create and set up email lookup handler
const emailLookupHandler = createEmailLookupHandler(
@ -84,8 +86,7 @@ const useAxoSetup = (
wooBillingAddress,
setWooShippingAddress,
setWooBillingAddress,
onChangeShippingAddressClick,
onChangeCardButtonClick
onChangeShippingAddressClick
);
setupEmailFunctionality( emailLookupHandler );
}

View file

@ -0,0 +1,36 @@
import { useMemo } from '@wordpress/element';
const DEFAULT_ALLOWED_CARDS = [ 'VISA', 'MASTERCARD', 'AMEX', 'DISCOVER' ];
/**
* Custom hook to determine the allowed card options based on configuration.
*
* @param {Object} axoConfig - The AXO configuration object.
* @return {Array} The final list of allowed card options.
*/
const useCardOptions = ( axoConfig ) => {
const merchantCountry = axoConfig.merchant_country || 'US';
return useMemo( () => {
const allowedCards = new Set(
axoConfig.allowed_cards?.[ merchantCountry ] ||
DEFAULT_ALLOWED_CARDS
);
// Create a Set of disabled cards, converting each to uppercase
const disabledCards = new Set(
( axoConfig.disable_cards || [] ).map( ( card ) =>
card.toUpperCase()
)
);
// Filter out disabled cards from the allowed cards
const finalCardOptions = [ ...allowedCards ].filter(
( card ) => ! disabledCards.has( card )
);
return finalCardOptions;
}, [ axoConfig.allowed_cards, axoConfig.disable_cards, merchantCountry ] );
};
export default useCardOptions;

View file

@ -3,6 +3,7 @@ import { useSelect } from '@wordpress/data';
import Fastlane from '../../../../ppcp-axo/resources/js/Connection/Fastlane';
import { log } from '../../../../ppcp-axo/resources/js/Helper/Debug';
import { useDeleteEmptyKeys } from './useDeleteEmptyKeys';
import useCardOptions from './useCardOptions';
import useAllowedLocations from './useAllowedLocations';
import { STORE_NAME } from '../stores/axoStore';
@ -27,6 +28,8 @@ const useFastlaneSdk = ( namespace, axoConfig, ppcpConfig ) => {
[]
);
const cardOptions = useCardOptions( axoConfig );
const styleOptions = useMemo( () => {
return deleteEmptyKeys( configRef.current.axoConfig.style_options );
}, [ deleteEmptyKeys ] );
@ -51,10 +54,13 @@ const useFastlaneSdk = ( namespace, axoConfig, ppcpConfig ) => {
window.localStorage.setItem( 'axoEnv', 'sandbox' );
}
// Connect to Fastlane with locale and style options
// Connect to Fastlane with locale, style options, and allowed card brands
await fastlane.connect( {
locale: configRef.current.ppcpConfig.locale,
styles: styleOptions,
cardOptions: {
allowedBrands: cardOptions,
},
shippingAddressOptions: {
allowedLocations,
},
@ -77,6 +83,7 @@ const useFastlaneSdk = ( namespace, axoConfig, ppcpConfig ) => {
styleOptions,
isPayPalLoaded,
namespace,
cardOptions,
allowedLocations,
] );

View file

@ -13,6 +13,7 @@ import usePayPalCommerceGateway from './hooks/usePayPalCommerceGateway';
// Components
import { Payment } from './components/Payment/Payment';
import { TitleLabel } from './components/TitleLabel';
const gatewayHandle = 'ppcp-axo-gateway';
const namespace = 'ppcpBlocksPaypalAxo';
@ -89,12 +90,7 @@ const Axo = ( props ) => {
registerPaymentMethod( {
name: initialConfig.id,
label: (
<div
id="ppcp-axo-block-radio-label"
dangerouslySetInnerHTML={ { __html: initialConfig.title } }
/>
),
label: <TitleLabel config={ initialConfig } />,
content: <Axo />,
edit: createElement( initialConfig.title ),
ariaLabel: initialConfig.title,

View file

@ -0,0 +1,259 @@
import { registerPlugin } from '@wordpress/plugins';
import { useEffect, useCallback, useState, useRef } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { PAYMENT_STORE_KEY } from '@woocommerce/block-data';
import PayPalInsights from '../../../../ppcp-axo/resources/js/Insights/PayPalInsights';
import { STORE_NAME } from '../stores/axoStore';
import usePayPalCommerceGateway from '../hooks/usePayPalCommerceGateway';
const GATEWAY_HANDLE = 'ppcp-axo-gateway';
const useEventTracking = () => {
const [ triggeredEvents, setTriggeredEvents ] = useState( {
initialized: false,
jsLoaded: false,
beginCheckout: false,
emailSubmitted: false,
} );
const currentPaymentMethod = useRef( null );
const setEventTriggered = useCallback( ( eventName, value = true ) => {
setTriggeredEvents( ( prev ) => ( {
...prev,
[ eventName ]: value,
} ) );
}, [] );
const isEventTriggered = useCallback(
( eventName ) => triggeredEvents[ eventName ],
[ triggeredEvents ]
);
const setCurrentPaymentMethod = useCallback( ( methodName ) => {
currentPaymentMethod.current = methodName;
}, [] );
const getCurrentPaymentMethod = useCallback(
() => currentPaymentMethod.current,
[]
);
return {
setEventTriggered,
isEventTriggered,
setCurrentPaymentMethod,
getCurrentPaymentMethod,
};
};
const waitForPayPalInsight = () => {
return new Promise( ( resolve, reject ) => {
// If already loaded, resolve immediately
if ( window.paypalInsight ) {
resolve( window.paypalInsight );
return;
}
// Set a reasonable timeout
const timeoutId = setTimeout( () => {
observer.disconnect();
reject( new Error( 'PayPal Insights script load timeout' ) );
}, 10000 );
// Create MutationObserver to watch for script initialization
const observer = new MutationObserver( () => {
if ( window.paypalInsight ) {
observer.disconnect();
clearTimeout( timeoutId );
resolve( window.paypalInsight );
}
} );
// Start observing
observer.observe( document, {
childList: true,
subtree: true,
} );
} );
};
const usePayPalInsightsInit = ( axoConfig, ppcpConfig, eventTracking ) => {
const { setEventTriggered, isEventTriggered } = eventTracking;
const initialized = useRef( false );
useEffect( () => {
if (
! axoConfig?.insights?.enabled ||
! axoConfig?.insights?.client_id ||
! axoConfig?.insights?.session_id ||
initialized.current ||
isEventTriggered( 'initialized' )
) {
return;
}
const initializePayPalInsights = async () => {
try {
await waitForPayPalInsight();
if ( initialized.current ) {
return;
}
// Track JS load first
PayPalInsights.trackJsLoad();
setEventTriggered( 'jsLoaded' );
PayPalInsights.config( axoConfig.insights.client_id, {
debug: axoConfig?.wp_debug === '1',
} );
PayPalInsights.setSessionId( axoConfig.insights.session_id );
initialized.current = true;
setEventTriggered( 'initialized' );
if (
isEventTriggered( 'jsLoaded' ) &&
! isEventTriggered( 'beginCheckout' )
) {
PayPalInsights.trackBeginCheckout( {
amount: axoConfig.insights.amount,
page_type: 'checkout',
user_data: {
country: 'US',
is_store_member: false,
},
} );
setEventTriggered( 'beginCheckout' );
}
} catch ( error ) {
console.error(
'PayPal Insights initialization failed:',
error
);
}
};
initializePayPalInsights();
return () => {
initialized.current = false;
};
}, [ axoConfig, ppcpConfig, setEventTriggered, isEventTriggered ] );
};
const usePaymentMethodTracking = ( axoConfig, eventTracking ) => {
const { setCurrentPaymentMethod } = eventTracking;
const lastPaymentMethod = useRef( null );
const isInitialMount = useRef( true );
const activePaymentMethod = useSelect( ( select ) => {
return select( PAYMENT_STORE_KEY )?.getActivePaymentMethod();
}, [] );
const handlePaymentMethodChange = useCallback(
async ( paymentMethod ) => {
// Skip if no payment method or same as last one
if (
! paymentMethod ||
paymentMethod === lastPaymentMethod.current
) {
return;
}
try {
await waitForPayPalInsight();
// Only track if it's not the initial mount, and we have a previous payment method
if ( ! isInitialMount.current && lastPaymentMethod.current ) {
PayPalInsights.trackSelectPaymentMethod( {
payment_method_selected:
axoConfig?.insights?.payment_method_selected_map[
paymentMethod
] || 'other',
page_type: 'checkout',
} );
}
lastPaymentMethod.current = paymentMethod;
setCurrentPaymentMethod( paymentMethod );
} catch ( error ) {
console.error( 'Failed to track payment method:', error );
}
},
[
axoConfig?.insights?.payment_method_selected_map,
setCurrentPaymentMethod,
]
);
useEffect( () => {
if ( activePaymentMethod ) {
if ( isInitialMount.current ) {
// Just set the initial payment method without tracking
lastPaymentMethod.current = activePaymentMethod;
setCurrentPaymentMethod( activePaymentMethod );
isInitialMount.current = false;
} else {
handlePaymentMethodChange( activePaymentMethod );
}
}
}, [
activePaymentMethod,
handlePaymentMethodChange,
setCurrentPaymentMethod,
] );
useEffect( () => {
return () => {
lastPaymentMethod.current = null;
isInitialMount.current = true;
};
}, [] );
};
const PayPalInsightsLoader = () => {
const eventTracking = useEventTracking();
const { setEventTriggered, isEventTriggered } = eventTracking;
const initialConfig =
window?.wc?.wcSettings?.getSetting( `${ GATEWAY_HANDLE }_data` ) || {};
const { ppcpConfig } = usePayPalCommerceGateway( initialConfig );
const axoConfig = window?.wc_ppcp_axo;
const { isEmailSubmitted } = useSelect( ( select ) => {
const storeSelect = select( STORE_NAME );
return {
isEmailSubmitted: storeSelect?.getIsEmailSubmitted?.() ?? false,
};
}, [] );
usePayPalInsightsInit( axoConfig, ppcpConfig, eventTracking );
usePaymentMethodTracking( axoConfig, eventTracking );
useEffect( () => {
const trackEmail = async () => {
if ( isEmailSubmitted && ! isEventTriggered( 'emailSubmitted' ) ) {
try {
await waitForPayPalInsight();
PayPalInsights.trackSubmitCheckoutEmail();
setEventTriggered( 'emailSubmitted' );
} catch ( error ) {
console.error( 'Failed to track email submission:', error );
}
}
};
trackEmail();
}, [ isEmailSubmitted, setEventTriggered, isEventTriggered ] );
return null;
};
registerPlugin( 'wc-ppcp-paypal-insights', {
render: PayPalInsightsLoader,
scope: 'woocommerce-checkout',
} );
export default PayPalInsightsLoader;

View file

@ -1,4 +1,4 @@
import { createReduxStore, register, dispatch } from '@wordpress/data';
import { createReduxStore, register, dispatch, select } from '@wordpress/data';
export const STORE_NAME = 'woocommerce-paypal-payments/axo-block';
@ -12,6 +12,7 @@ const DEFAULT_STATE = {
shippingAddress: null,
cardDetails: null,
phoneNumber: '',
cardChangeHandler: null,
};
// Action creators for updating the store state
@ -52,6 +53,10 @@ const actions = {
type: 'SET_PHONE_NUMBER',
payload: phoneNumber,
} ),
setCardChangeHandler: ( cardChangeHandler ) => ( {
type: 'SET_CARD_CHANGE_HANDLER',
payload: cardChangeHandler,
} ),
};
/**
@ -81,6 +86,8 @@ const reducer = ( state = DEFAULT_STATE, action ) => {
return { ...state, cardDetails: action.payload };
case 'SET_PHONE_NUMBER':
return { ...state, phoneNumber: action.payload };
case 'SET_CARD_CHANGE_HANDLER':
return { ...state, cardChangeHandler: action.payload };
default:
return state;
}
@ -97,16 +104,19 @@ const selectors = {
getShippingAddress: ( state ) => state.shippingAddress,
getCardDetails: ( state ) => state.cardDetails,
getPhoneNumber: ( state ) => state.phoneNumber,
getCardChangeHandler: ( state ) => state.cardChangeHandler,
};
// Create and register the Redux store for the AXO block
const store = createReduxStore( STORE_NAME, {
reducer,
actions,
selectors,
} );
if ( ! select( STORE_NAME ) ) {
const store = createReduxStore( STORE_NAME, {
reducer,
actions,
selectors,
} );
register( store );
register( store );
}
// Action dispatchers
@ -163,3 +173,12 @@ export const setCardDetails = ( cardDetails ) => {
export const setPhoneNumber = ( phoneNumber ) => {
dispatch( STORE_NAME ).setPhoneNumber( phoneNumber );
};
/**
* Action dispatcher to update the card change handler in the store.
*
* @param {Function} cardChangeHandler - The card change handler function.
*/
export const setCardChangeHandler = ( cardChangeHandler ) => {
dispatch( STORE_NAME ).setCardChangeHandler( cardChangeHandler );
};

View file

@ -33,11 +33,13 @@ return array(
$container->get( 'axoblock.url' ),
$container->get( 'ppcp.asset-version' ),
$container->get( 'axo.gateway' ),
fn() : SmartButtonInterface => $container->get( 'button.smart-button' ),
fn(): SmartButtonInterface => $container->get( 'button.smart-button' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'wcgateway.configuration.dcc' ),
$container->get( 'onboarding.environment' ),
$container->get( 'wcgateway.url' ),
$container->get( 'axo.payment_method_selected_map' ),
$container->get( 'axo.supported-country-card-type-matrix' ),
$container->get( 'axo.shipping-wc-enabled-locations' )
);
},

View file

@ -133,6 +133,15 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule
wp_enqueue_style( 'wc-ppcp-axo-block' );
}
);
// Enqueue the PayPal Insights script.
add_action(
'wp_enqueue_scripts',
function () use ( $c ) {
$this->enqueue_paypal_insights_script( $c );
}
);
return true;
}
@ -166,4 +175,37 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule
return $localized_script_data;
}
/**
* Enqueues PayPal Insights analytics script for the Checkout block.
*
* @param ContainerInterface $c The service container.
* @return void
*/
private function enqueue_paypal_insights_script( ContainerInterface $c ): void {
if ( ! has_block( 'woocommerce/checkout' ) || WC()->cart->is_empty() ) {
return;
}
$module_url = $c->get( 'axoblock.url' );
$asset_version = $c->get( 'ppcp.asset-version' );
wp_register_script(
'wc-ppcp-paypal-insights',
untrailingslashit( $module_url ) . '/assets/js/PayPalInsightsLoader.js',
array( 'wp-plugins', 'wp-data', 'wp-element', 'wc-blocks-registry' ),
$asset_version,
true
);
wp_localize_script(
'wc-ppcp-paypal-insights',
'ppcpPayPalInsightsData',
array(
'isAxoEnabled' => true,
)
);
wp_enqueue_script( 'wc-ppcp-paypal-insights' );
}
}

View file

@ -72,6 +72,13 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType {
*/
private $environment;
/**
* Mapping of payment methods to the PayPal Insights 'payment_method_selected' types.
*
* @var array
*/
private array $payment_method_selected_map;
/**
* The WcGateway module URL.
*
@ -79,6 +86,13 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType {
*/
private $wcgateway_module_url;
/**
* The supported country card type matrix.
*
* @var array
*/
private $supported_country_card_type_matrix;
/**
* The list of WooCommerce enabled shipping locations.
*
@ -89,40 +103,44 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType {
/**
* AdvancedCardPaymentMethod constructor.
*
* @param string $module_url The URL of this module.
* @param string $version The assets version.
* @param WC_Payment_Gateway $gateway Credit card gateway.
* @param SmartButtonInterface|callable $smart_button The smart button script loading
* handler.
* @param Settings $settings The settings.
* @param DCCGatewayConfiguration $dcc_configuration The DCC gateway settings.
* @param Environment $environment The environment object.
* @param string $module_url The URL of this module.
* @param string $version The assets version.
* @param WC_Payment_Gateway $gateway Credit card gateway.
* @param SmartButtonInterface|callable $smart_button The smart button script loading handler.
* @param Settings $settings The settings.
* @param DCCGatewayConfiguration $dcc_configuration The DCC gateway settings.
* @param Environment $environment The environment object.
* @param string $wcgateway_module_url The WcGateway module URL.
* @param array $payment_method_selected_map Mapping of payment methods to the PayPal Insights 'payment_method_selected' types.
* @param array $supported_country_card_type_matrix The supported country card type matrix for Axo.
* @param array $enabled_shipping_locations The list of WooCommerce enabled shipping locations.
*/
public function __construct(
string $module_url,
string $version,
WC_Payment_Gateway $gateway,
$smart_button,
Settings $settings,
DCCGatewayConfiguration $dcc_configuration,
Environment $environment,
string $wcgateway_module_url,
array $enabled_shipping_locations
string $module_url,
string $version,
WC_Payment_Gateway $gateway,
$smart_button,
Settings $settings,
DCCGatewayConfiguration $dcc_configuration,
Environment $environment,
string $wcgateway_module_url,
array $payment_method_selected_map,
array $supported_country_card_type_matrix,
array $enabled_shipping_locations
) {
$this->name = AxoGateway::ID;
$this->module_url = $module_url;
$this->version = $version;
$this->gateway = $gateway;
$this->smart_button = $smart_button;
$this->settings = $settings;
$this->dcc_configuration = $dcc_configuration;
$this->environment = $environment;
$this->wcgateway_module_url = $wcgateway_module_url;
$this->enabled_shipping_locations = $enabled_shipping_locations;
$this->name = AxoGateway::ID;
$this->module_url = $module_url;
$this->version = $version;
$this->gateway = $gateway;
$this->smart_button = $smart_button;
$this->settings = $settings;
$this->dcc_configuration = $dcc_configuration;
$this->environment = $environment;
$this->wcgateway_module_url = $wcgateway_module_url;
$this->payment_method_selected_map = $payment_method_selected_map;
$this->supported_country_card_type_matrix = $supported_country_card_type_matrix;
$this->enabled_shipping_locations = $enabled_shipping_locations;
}
/**
* {@inheritDoc}
*/
@ -203,19 +221,22 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType {
'email' => 'render',
),
'insights' => array(
'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG,
'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ),
'session_id' =>
'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG,
'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ),
'session_id' =>
( WC()->session && method_exists( WC()->session, 'get_customer_unique_id' ) )
? substr( md5( WC()->session->get_customer_unique_id() ), 0, 16 )
: '',
'amount' => array(
'amount' => array(
'currency_code' => get_woocommerce_currency(),
'value' => ( WC()->cart && method_exists( WC()->cart, 'get_total' ) )
? WC()->cart->get_total( 'numeric' )
: null, // Set to null if WC()->cart is null or get_total doesn't exist.
: null,
),
'payment_method_selected_map' => $this->payment_method_selected_map,
),
'allowed_cards' => $this->supported_country_card_type_matrix,
'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(),
'enabled_shipping_locations' => $this->enabled_shipping_locations,
'style_options' => array(
'root' => array(
@ -253,6 +274,8 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType {
),
'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '',
'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
'card_icons' => $this->settings->has( 'card_icons' ) ? (array) $this->settings->get( 'card_icons' ) : array(),
'merchant_country' => WC()->countries->get_base_country(),
);
}
}

View file

@ -9,7 +9,10 @@ module.exports = {
target: 'web',
plugins: [ new DependencyExtractionWebpackPlugin() ],
entry: {
'index': path.resolve( './resources/js/index.js' ),
index: path.resolve( './resources/js/index.js' ),
PayPalInsightsLoader: path.resolve(
'./resources/js/plugins/PayPalInsightsLoader.js'
),
gateway: path.resolve( './resources/css/gateway.scss' ),
},
output: {

View file

@ -85,6 +85,8 @@ class AxoManager {
},
};
this.cardOptions = this.getCardOptions();
this.enabledShippingLocations =
this.axoConfig.enabled_shipping_locations;
@ -119,7 +121,7 @@ class AxoManager {
this.axoConfig?.insights?.session_id
) {
PayPalInsights.config( this.axoConfig?.insights?.client_id, {
debug: true,
debug: axoConfig?.wp_debug === '1',
} );
PayPalInsights.setSessionId( this.axoConfig?.insights?.session_id );
PayPalInsights.trackJsLoad();
@ -162,19 +164,25 @@ class AxoManager {
}
registerEventHandlers() {
// Payment method change tracking with duplicate prevention
let lastSelectedPaymentMethod = document.querySelector(
'input[name=payment_method]:checked'
)?.value;
this.$( document ).on(
'change',
'input[name=payment_method]',
( ev ) => {
const map = {
'ppcp-axo-gateway': 'card',
'ppcp-gateway': 'paypal',
};
PayPalInsights.trackSelectPaymentMethod( {
payment_method_selected: map[ ev.target.value ] || 'other',
page_type: 'checkout',
} );
if ( lastSelectedPaymentMethod !== ev.target.value ) {
PayPalInsights.trackSelectPaymentMethod( {
payment_method_selected:
this.axoConfig?.insights
?.payment_method_selected_map[
ev.target.value
] || 'other',
page_type: 'checkout',
} );
lastSelectedPaymentMethod = ev.target.value;
}
}
);
@ -664,6 +672,9 @@ class AxoManager {
await this.fastlane.connect( {
locale: this.locale,
styles: this.styles,
cardOptions: {
allowedBrands: this.cardOptions,
},
shippingAddressOptions: {
allowedLocations: this.enabledShippingLocations,
},
@ -1161,16 +1172,6 @@ class AxoManager {
this.el.axoNonceInput.get().value = nonce;
PayPalInsights.trackEndCheckout( {
amount: this.axoConfig?.insights?.amount,
page_type: 'checkout',
payment_method_selected: 'card',
user_data: {
country: 'US',
is_store_member: false,
},
} );
if ( data ) {
// Ryan flow.
const form = document.querySelector( 'form.woocommerce-checkout' );
@ -1251,6 +1252,31 @@ class AxoManager {
return this.axoConfig?.widgets?.email === 'use_widget';
}
getCardOptions() {
const DEFAULT_ALLOWED_CARDS = [
'VISA',
'MASTERCARD',
'AMEX',
'DISCOVER',
];
const merchantCountry = this.axoConfig.merchant_country || 'US';
const allowedCards = new Set(
this.axoConfig.allowed_cards?.[ merchantCountry ] ||
DEFAULT_ALLOWED_CARDS
);
const disabledCards = new Set(
( this.axoConfig.disable_cards || [] ).map( ( card ) =>
card.toUpperCase()
)
);
return [ ...allowedCards ].filter(
( card ) => ! disabledCards.has( card )
);
}
deleteKeysWithEmptyString = ( obj ) => {
for ( const key of Object.keys( obj ) ) {
if ( obj[ key ] === '' ) {

View file

@ -0,0 +1,99 @@
import PayPalInsights from '../../../../ppcp-axo/resources/js/Insights/PayPalInsights';
class EndCheckoutTracker {
constructor() {
this.initialize();
}
async initialize() {
const axoConfig = window.wc_ppcp_axo_insights_data || {};
if (
axoConfig?.enabled === '1' &&
axoConfig?.client_id &&
axoConfig?.session_id &&
axoConfig?.orderTotal &&
axoConfig?.orderCurrency
) {
try {
await this.waitForPayPalInsight();
PayPalInsights.config( axoConfig?.client_id, {
debug: axoConfig?.wp_debug === '1',
} );
PayPalInsights.setSessionId( axoConfig.session_id );
PayPalInsights.trackJsLoad();
const trackingData = {
amount: {
currency_code: axoConfig?.orderCurrency,
value: axoConfig?.orderTotal,
},
page_type: 'checkout',
payment_method_selected:
axoConfig?.payment_method_selected_map[
axoConfig?.paymentMethod
] || 'other',
user_data: {
country: 'US',
is_store_member: false,
},
order_id: axoConfig?.orderId,
order_key: axoConfig?.orderKey,
};
PayPalInsights.trackEndCheckout( trackingData );
} catch ( error ) {
console.error(
'EndCheckoutTracker: Error during tracking:',
error
);
console.error( 'PayPalInsights object:', window.paypalInsight );
}
} else {
console.warn(
'EndCheckoutTracker: Missing required configuration',
{
enabled: axoConfig?.enabled,
hasClientId: !! axoConfig?.client_id,
hasSessionId: !! axoConfig?.session_id,
hasOrderTotal: !! axoConfig?.orderTotal,
hasOrderCurrency: !! axoConfig?.orderCurrency,
}
);
}
}
waitForPayPalInsight() {
return new Promise( ( resolve, reject ) => {
// If already loaded, resolve immediately
if ( window.paypalInsight ) {
resolve( window.paypalInsight );
return;
}
const timeoutId = setTimeout( () => {
observer.disconnect();
reject( new Error( 'PayPal Insights script load timeout' ) );
}, 10000 );
// Create MutationObserver to watch for script initialization
const observer = new MutationObserver( () => {
if ( window.paypalInsight ) {
observer.disconnect();
clearTimeout( timeoutId );
resolve( window.paypalInsight );
}
} );
observer.observe( document, {
childList: true,
subtree: true,
} );
} );
}
}
document.addEventListener( 'DOMContentLoaded', () => {
new EndCheckoutTracker();
} );

View file

@ -18,6 +18,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration;
use WooCommerce\PayPalCommerce\ApiClient\Helper\CurrencyGetter;
return array(
@ -64,10 +65,12 @@ return array(
$container->get( 'session.handler' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'onboarding.environment' ),
$container->get( 'axo.insights' ),
$container->get( 'wcgateway.settings.status' ),
$container->get( 'api.shop.currency.getter' ),
$container->get( 'woocommerce.logger.woocommerce' ),
$container->get( 'wcgateway.url' ),
$container->get( 'axo.supported-country-card-type-matrix' ),
$container->get( 'axo.shipping-wc-enabled-locations' )
);
},
@ -90,6 +93,55 @@ return array(
);
},
// Data needed for the PayPal Insights.
'axo.insights' => static function ( ContainerInterface $container ): array {
$settings = $container->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$currency = $container->get( 'api.shop.currency.getter' );
assert( $currency instanceof CurrencyGetter );
$session_id = '';
if ( isset( WC()->session ) && method_exists( WC()->session, 'get_customer_unique_id' ) ) {
$session_id = substr(
md5( WC()->session->get_customer_unique_id() ),
0,
16
);
}
return array(
'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG,
'client_id' => ( $settings->has( 'client_id' ) ? $settings->get( 'client_id' ) : null ),
'session_id' => $session_id,
'amount' => array(
'currency_code' => $currency->get(),
),
'payment_method_selected_map' => $container->get( 'axo.payment_method_selected_map' ),
'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
);
},
// The mapping of payment methods to the PayPal Insights 'payment_method_selected' types.
'axo.payment_method_selected_map' => static function ( ContainerInterface $container ): array {
return array(
'ppcp-axo-gateway' => 'card',
'ppcp-credit-card-gateway' => 'card',
'ppcp-gateway' => 'paypal',
'ppcp-googlepay' => 'google_pay',
'ppcp-applepay' => 'apple_pay',
'ppcp-multibanco' => 'other',
'ppcp-trustly' => 'other',
'ppcp-p24' => 'other',
'ppcp-mybank' => 'other',
'ppcp-ideal' => 'other',
'ppcp-eps' => 'other',
'ppcp-blik' => 'other',
'ppcp-bancontact' => 'other',
'ppcp-card-button-gateway' => 'card',
);
},
/**
* The matrix which countries and currency combinations can be used for AXO.
*/
@ -111,7 +163,31 @@ return array(
)
);
},
/**
* The matrix which countries and card type combinations can be used for AXO.
*/
'axo.supported-country-card-type-matrix' => static function ( ContainerInterface $container ) : array {
/**
* Returns which countries and card type combinations can be used for AXO.
*/
return apply_filters(
'woocommerce_paypal_payments_axo_supported_country_card_type_matrix',
array(
'US' => array(
'VISA',
'MASTERCARD',
'AMEX',
'DISCOVER',
),
'CA' => array(
'VISA',
'MASTERCARD',
'AMEX',
'DISCOVER',
),
)
);
},
'axo.settings-conflict-notice' => static function ( ContainerInterface $container ) : string {
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );

View file

@ -29,28 +29,35 @@ class AxoManager {
*
* @var string
*/
private $module_url;
private string $module_url;
/**
* The assets version.
*
* @var string
*/
private $version;
private string $version;
/**
* The settings.
*
* @var Settings
*/
private $settings;
private Settings $settings;
/**
* The environment object.
*
* @var Environment
*/
private $environment;
private Environment $environment;
/**
* Data needed for the PayPal Insights.
*
* @var array
*/
private array $insights_data;
/**
* The Settings status helper.
@ -71,22 +78,27 @@ class AxoManager {
*
* @var LoggerInterface
*/
private $logger;
private LoggerInterface $logger;
/**
* Session handler.
*
* @var SessionHandler
*/
private $session_handler;
private SessionHandler $session_handler;
/**
* The WcGateway module URL.
*
* @var string
*/
private $wcgateway_module_url;
private string $wcgateway_module_url;
/**
* The supported country card type matrix.
*
* @var array
*/
private array $supported_country_card_type_matrix;
/**
* The list of WooCommerce enabled shipping locations.
*
@ -102,10 +114,12 @@ class AxoManager {
* @param SessionHandler $session_handler The Session handler.
* @param Settings $settings The Settings.
* @param Environment $environment The environment object.
* @param array $insights_data Data needed for the PayPal Insights.
* @param SettingsStatus $settings_status The Settings status helper.
* @param CurrencyGetter $currency The getter of the 3-letter currency code of the shop.
* @param LoggerInterface $logger The logger.
* @param string $wcgateway_module_url The WcGateway module URL.
* @param array $supported_country_card_type_matrix The supported country card type matrix for Axo.
* @param array $enabled_shipping_locations The list of WooCommerce enabled shipping locations.
*/
public function __construct(
@ -114,23 +128,27 @@ class AxoManager {
SessionHandler $session_handler,
Settings $settings,
Environment $environment,
array $insights_data,
SettingsStatus $settings_status,
CurrencyGetter $currency,
LoggerInterface $logger,
string $wcgateway_module_url,
array $supported_country_card_type_matrix,
array $enabled_shipping_locations
) {
$this->module_url = $module_url;
$this->version = $version;
$this->session_handler = $session_handler;
$this->settings = $settings;
$this->environment = $environment;
$this->settings_status = $settings_status;
$this->currency = $currency;
$this->logger = $logger;
$this->wcgateway_module_url = $wcgateway_module_url;
$this->enabled_shipping_locations = $enabled_shipping_locations;
$this->module_url = $module_url;
$this->version = $version;
$this->session_handler = $session_handler;
$this->settings = $settings;
$this->environment = $environment;
$this->insights_data = $insights_data;
$this->settings_status = $settings_status;
$this->currency = $currency;
$this->logger = $logger;
$this->wcgateway_module_url = $wcgateway_module_url;
$this->enabled_shipping_locations = $enabled_shipping_locations;
$this->supported_country_card_type_matrix = $supported_country_card_type_matrix;
}
/**
@ -171,7 +189,7 @@ class AxoManager {
*
* @return array
*/
private function script_data() {
private function script_data(): array {
return array(
'environment' => array(
'is_sandbox' => $this->environment->current_environment() === 'sandbox',
@ -179,20 +197,12 @@ class AxoManager {
'widgets' => array(
'email' => 'render',
),
'insights' => array(
'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG,
'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ),
'session_id' =>
substr(
method_exists( WC()->session, 'get_customer_unique_id' ) ? md5( WC()->session->get_customer_unique_id() ) : '',
0,
16
),
'amount' => array(
'currency_code' => $this->currency->get(),
'value' => WC()->cart->get_total( 'numeric' ),
),
),
// The amount is not available when setting the insights data, so we need to merge it here.
'insights' => ( function( array $data ): array {
$data['amount']['value'] = WC()->cart->get_total( 'numeric' );
return $data; } )( $this->insights_data ),
'allowed_cards' => $this->supported_country_card_type_matrix,
'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(),
'enabled_shipping_locations' => $this->enabled_shipping_locations,
'style_options' => array(
'root' => array(
@ -231,6 +241,7 @@ class AxoManager {
'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '',
'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
'billing_email_button_text' => __( 'Continue', 'woocommerce-paypal-payments' ),
'merchant_country' => WC()->countries->get_base_country(),
);
}

View file

@ -320,6 +320,15 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
$endpoint->handle_request();
}
);
// Enqueue the PayPal Insights script.
add_action(
'wp_enqueue_scripts',
function () use ( $c ) {
$this->enqueue_paypal_insights_script_on_order_received( $c );
}
);
return true;
}
@ -377,11 +386,15 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
$dcc_configuration = $c->get( 'wcgateway.configuration.dcc' );
assert( $dcc_configuration instanceof DCCGatewayConfiguration );
$subscription_helper = $c->get( 'wc-subscriptions.helper' );
assert( $subscription_helper instanceof SubscriptionHelper );
return ! is_user_logged_in()
&& CartCheckoutDetector::has_classic_checkout()
&& $dcc_configuration->use_fastlane()
&& ! $this->is_excluded_endpoint()
&& is_checkout();
&& is_checkout()
&& ! $subscription_helper->cart_contains_subscription();
}
/**
@ -431,8 +444,8 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
* @return bool
*/
private function is_excluded_endpoint(): bool {
// Exclude the Order Pay endpoint.
return is_wc_endpoint_url( 'order-pay' );
// Exclude the Order Pay and Order Received endpoints.
return is_wc_endpoint_url( 'order-pay' ) || is_wc_endpoint_url( 'order-received' );
}
/**
@ -453,4 +466,57 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
$axo_enabled ? 'enabled' : 'disabled'
);
}
/**
* Enqueues PayPal Insights on the Order Received endpoint.
*
* @param ContainerInterface $c The service container.
* @return void
*/
private function enqueue_paypal_insights_script_on_order_received( ContainerInterface $c ): void {
global $wp;
if ( ! isset( $wp->query_vars['order-received'] ) ) {
return;
}
$order_id = absint( $wp->query_vars['order-received'] );
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
if ( ! $order || ! $order instanceof \WC_Order ) {
return;
}
$module_url = $c->get( 'axo.url' );
$asset_version = $c->get( 'ppcp.asset-version' );
$insights_data = $c->get( 'axo.insights' );
wp_register_script(
'wc-ppcp-paypal-insights-end-checkout',
untrailingslashit( $module_url ) . '/assets/js/TrackEndCheckout.js',
array( 'wp-plugins', 'wp-data', 'wp-element', 'wc-blocks-registry' ),
$asset_version,
true
);
wp_localize_script(
'wc-ppcp-paypal-insights-end-checkout',
'wc_ppcp_axo_insights_data',
array_merge(
$insights_data,
array(
'orderId' => $order_id,
'orderTotal' => (string) $order->get_total(),
'orderCurrency' => (string) $order->get_currency(),
'paymentMethod' => (string) $order->get_payment_method(),
'orderKey' => (string) $order->get_order_key(),
)
)
);
wp_enqueue_script( 'wc-ppcp-paypal-insights-end-checkout' );
}
}

View file

@ -1,39 +1,44 @@
const path = require('path');
const path = require( 'path' );
const isProduction = process.env.NODE_ENV === 'production';
const DependencyExtractionWebpackPlugin = require( '@woocommerce/dependency-extraction-webpack-plugin' );
module.exports = {
devtool: isProduction ? 'source-map' : 'eval-source-map',
mode: isProduction ? 'production' : 'development',
target: 'web',
plugins: [ new DependencyExtractionWebpackPlugin() ],
entry: {
'boot': path.resolve('./resources/js/boot.js'),
'styles': path.resolve('./resources/css/styles.scss')
},
output: {
path: path.resolve(__dirname, 'assets/'),
filename: 'js/[name].js',
},
module: {
rules: [{
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
{
loader: 'file-loader',
options: {
name: 'css/[name].css',
}
},
{loader:'sass-loader'}
]
}]
}
devtool: isProduction ? 'source-map' : 'eval-source-map',
mode: isProduction ? 'production' : 'development',
target: 'web',
plugins: [ new DependencyExtractionWebpackPlugin() ],
entry: {
boot: path.resolve( './resources/js/boot.js' ),
styles: path.resolve( './resources/css/styles.scss' ),
TrackEndCheckout: path.resolve(
'./resources/js/Insights/EndCheckoutTracker.js'
),
},
output: {
path: path.resolve( __dirname, 'assets/' ),
filename: 'js/[name].js',
},
module: {
rules: [
{
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
{
loader: 'file-loader',
options: {
name: 'css/[name].css',
},
},
{ loader: 'sass-loader' },
],
},
],
},
};

View file

@ -3,7 +3,10 @@ import { useEffect, useState } from '@wordpress/element';
import {
PayPalScriptProvider,
PayPalCardFieldsProvider,
PayPalCardFieldsForm,
PayPalNameField,
PayPalNumberField,
PayPalExpiryField,
PayPalCVVField,
} from '@paypal/react-paypal-js';
import { CheckoutHandler } from './checkout-handler';
@ -14,6 +17,7 @@ import {
onApproveSavePayment,
} from '../card-fields-config';
import { cartHasSubscriptionProducts } from '../Helper/Subscription';
import { __ } from '@wordpress/i18n';
export function CardFields( {
config,
@ -92,7 +96,16 @@ export function CardFields( {
console.error( err );
} }
>
<PayPalCardFieldsForm />
<PayPalNameField placeholder={ __( 'Cardholder Name (optional)', 'woocommerce-paypal-payments' ) }/>
<PayPalNumberField placeholder={ __( 'Card number', 'woocommerce-paypal-payments' ) }/>
<div style={ { display: "flex", width: "100%" } }>
<div style={ { width: "100%" } }>
<PayPalExpiryField placeholder={ __( 'MM / YY', 'woocommerce-paypal-payments' ) }/>
</div>
<div style={ { width: "100%" } }>
<PayPalCVVField placeholder={ __( 'CVV', 'woocommerce-paypal-payments' ) }/>
</div>
</div>
<CheckoutHandler
getCardFieldsForm={ getCardFieldsForm }
getSavePayment={ getSavePayment }

View file

@ -1,30 +1,50 @@
import {registerPaymentMethod} from '@woocommerce/blocks-registry';
import {CardFields} from './Components/card-fields';
import { registerPaymentMethod } from '@woocommerce/blocks-registry';
import { CardFields } from './Components/card-fields';
const config = wc.wcSettings.getSetting('ppcp-credit-card-gateway_data');
const config = wc.wcSettings.getSetting( 'ppcp-credit-card-gateway_data' );
const isUserLoggedIn = config?.scriptData?.is_user_logged_in;
const axoConfig = wc.wcSettings.getSetting( 'ppcp-axo-gateway_data' );
const axoEnabled = axoConfig !== false;
const Label = ({components, config}) => {
const {PaymentMethodIcons} = components;
return <>
<span dangerouslySetInnerHTML={{__html: config.title}}/>
<PaymentMethodIcons
icons={ config.card_icons }
align="right"
/>
</>
}
const Label = ( { components } ) => {
const { PaymentMethodIcons } = components;
return (
<>
<span dangerouslySetInnerHTML={ { __html: config?.title } } />
<PaymentMethodIcons icons={ config?.card_icons } align="right" />
</>
);
};
registerPaymentMethod({
name: config.id,
label: <Label config={config}/>,
content: <CardFields config={config}/>,
edit: <CardFields config={config}/>,
ariaLabel: config.title,
canMakePayment: () => {
return true;
},
supports: {
showSavedCards: true,
features: config.supports,
},
});
registerPaymentMethod( {
name: config?.id,
label: <Label />,
content: <CardFields config={ config } />,
edit: <CardFields config={ config } />,
ariaLabel: config?.title,
canMakePayment: ( cartData ) => {
const cartItems = cartData?.cart?.cartItems || [];
// Check if any item in the cart is a subscription
const hasSubscription = cartItems.some(
( item ) =>
item?.type === 'subscription' ||
item?.type === 'variable-subscription' ||
cartData?.paymentRequirements?.includes( 'subscriptions' )
);
// Show payment method if:
// 1. Axo is disabled, OR
// 2. User is logged in, OR
// 3. Axo is enabled AND cart has subscriptions
return !! (
! axoEnabled ||
isUserLoggedIn ||
( axoEnabled && hasSubscription )
);
},
supports: {
showSavedCards: true,
features: config?.supports,
},
} );

View file

@ -802,9 +802,24 @@ if ( block_enabled ) {
);
}
const PaypalLabel = ( { components, config } ) => {
const { PaymentMethodIcons } = components;
return (
<>
<span
dangerouslySetInnerHTML={ {
__html: config.title,
} }
/>
<PaymentMethodIcons icons={ config.icon } align="right" />
</>
);
};
registerPaymentMethod( {
name: config.id,
label: <div dangerouslySetInnerHTML={ { __html: config.title } } />,
label: <PaypalLabel config={ config } />,
content: descriptionElement,
edit: descriptionElement,
placeOrderButtonLabel: config.placeOrderButtonText,

View file

@ -110,6 +110,7 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType {
*/
public function get_payment_method_data() {
$script_data = $this->smart_button_instance()->script_data();
$script_data = array_merge( $script_data, array( 'is_user_logged_in' => is_user_logged_in() ) );
return array(
'id' => $this->name,

View file

@ -18,6 +18,7 @@ use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameI
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
/**
* Class BlocksModule
@ -73,10 +74,7 @@ class BlocksModule implements ServiceModule, ExtendingModule, ExecutableModule {
$settings = $c->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
// Include ACDC in the Block Checkout only in case Axo doesn't exist or is not available or the user is logged in.
if ( ( $settings->has( 'axo_enabled' ) && ! $settings->get( 'axo_enabled' ) ) || is_user_logged_in() ) {
$payment_method_registry->register( $c->get( 'blocks.advanced-card-method' ) );
}
$payment_method_registry->register( $c->get( 'blocks.advanced-card-method' ) );
}
);

View file

@ -262,6 +262,13 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
return array(
'id' => $this->gateway->id,
'title' => $this->gateway->title,
'icon' => array(
array(
'id' => 'paypal',
'alt' => 'PayPal',
'src' => $this->gateway->icon,
),
),
'description' => $this->gateway->description,
'smartButtonsEnabled' => $smart_buttons_enabled,
'placeOrderEnabled' => $place_order_enabled,

View file

@ -6,16 +6,16 @@ const processAxoConfig = ( config ) => {
const scriptOptions = {};
const sdkClientToken = config?.axo?.sdk_client_token;
const uuid = uuidv4().replace( /-/g, '' );
if ( sdkClientToken ) {
if ( sdkClientToken && config?.user?.is_logged !== true ) {
scriptOptions[ 'data-sdk-client-token' ] = sdkClientToken;
scriptOptions[ 'data-client-metadata-id' ] = uuid;
}
return scriptOptions;
};
const processUserIdToken = ( config, sdkClientToken ) => {
const processUserIdToken = ( config ) => {
const userIdToken = config?.save_payment_methods?.id_token;
return userIdToken && ! sdkClientToken
return userIdToken && config?.user?.is_logged === true
? { 'data-user-id-token': userIdToken }
: {};
};
@ -26,9 +26,6 @@ export const processConfig = ( config ) => {
scriptOptions = merge( scriptOptions, config.script_attributes );
}
const axoOptions = processAxoConfig( config );
const userIdTokenOptions = processUserIdToken(
config,
axoOptions[ 'data-sdk-client-token' ]
);
const userIdTokenOptions = processUserIdToken( config );
return merge.all( [ scriptOptions, axoOptions, userIdTokenOptions ] );
};

View file

@ -9,7 +9,7 @@ const scriptPromises = new Map();
const handleDataClientIdAttribute = async ( scriptOptions, config ) => {
if (
config.data_client_id?.set_attribute &&
config.vault_v3_enabled !== '1'
config.vault_v3_enabled !== true
) {
return new Promise( ( resolve, reject ) => {
dataClientIdAttributeHandler(

View file

@ -75,7 +75,7 @@ export const loadPaypalScript = ( config, onLoaded, onError = null ) => {
// Axo SDK options
const sdkClientToken = config?.axo?.sdk_client_token;
const uuid = uuidv4().replace( /-/g, '' );
if ( sdkClientToken ) {
if ( sdkClientToken && config?.user?.is_logged !== true ) {
scriptOptions[ 'data-sdk-client-token' ] = sdkClientToken;
scriptOptions[ 'data-client-metadata-id' ] = uuid;
}
@ -96,7 +96,7 @@ export const loadPaypalScript = ( config, onLoaded, onError = null ) => {
// Adds data-user-id-token to script options.
const userIdToken = config?.save_payment_methods?.id_token;
if ( userIdToken && ! sdkClientToken ) {
if ( userIdToken && config?.user?.is_logged === true ) {
scriptOptions[ 'data-user-id-token' ] = userIdToken;
}

View file

@ -172,6 +172,11 @@ export default class PaymentButton {
*/
#contextHandler;
/**
* Button attributes.
*/
#buttonAttributes;
/**
* Whether the current browser/website support the payment method.
*
@ -211,11 +216,12 @@ export default class PaymentButton {
/**
* Factory method to create a new PaymentButton while limiting a single instance per context.
*
* @param {string} context - Button context name.
* @param {unknown} externalHandler - Handler object.
* @param {Object} buttonConfig - Payment button specific configuration.
* @param {Object} ppcpConfig - Plugin wide configuration object.
* @param {unknown} contextHandler - Handler object.
* @param {string} context - Button context name.
* @param {unknown} externalHandler - Handler object.
* @param {Object} buttonConfig - Payment button specific configuration.
* @param {Object} ppcpConfig - Plugin wide configuration object.
* @param {unknown} contextHandler - Handler object.
* @param {Object} buttonAttributes - Button attributes.
* @return {PaymentButton} The button instance.
*/
static createButton(
@ -223,7 +229,8 @@ export default class PaymentButton {
externalHandler,
buttonConfig,
ppcpConfig,
contextHandler
contextHandler,
buttonAttributes
) {
const buttonInstances = getInstances();
const instanceKey = `${ this.methodId }.${ context }`;
@ -234,7 +241,8 @@ export default class PaymentButton {
externalHandler,
buttonConfig,
ppcpConfig,
contextHandler
contextHandler,
buttonAttributes
);
buttonInstances.set( instanceKey, button );
@ -278,18 +286,20 @@ export default class PaymentButton {
* to avoid multiple button instances handling the same context.
*
* @private
* @param {string} context - Button context name.
* @param {Object} externalHandler - Handler object.
* @param {Object} buttonConfig - Payment button specific configuration.
* @param {Object} ppcpConfig - Plugin wide configuration object.
* @param {Object} contextHandler - Handler object.
* @param {string} context - Button context name.
* @param {Object} externalHandler - Handler object.
* @param {Object} buttonConfig - Payment button specific configuration.
* @param {Object} ppcpConfig - Plugin wide configuration object.
* @param {Object} contextHandler - Handler object.
* @param {Object} buttonAttributes - Button attributes.
*/
constructor(
context,
externalHandler = null,
buttonConfig = {},
ppcpConfig = {},
contextHandler = null
contextHandler = null,
buttonAttributes = {}
) {
if ( this.methodId === PaymentButton.methodId ) {
throw new Error( 'Cannot initialize the PaymentButton base class' );
@ -307,6 +317,7 @@ export default class PaymentButton {
this.#ppcpConfig = ppcpConfig;
this.#externalHandler = externalHandler;
this.#contextHandler = contextHandler;
this.#buttonAttributes = buttonAttributes;
this.#logger = new ConsoleLogger( methodName, context );
@ -921,15 +932,20 @@ export default class PaymentButton {
const styleSelector = `style[data-hide-gateway="${ this.methodId }"]`;
const wrapperSelector = `#${ this.wrappers.Default }`;
const paymentMethodLi = document.querySelector(`.wc_payment_method.payment_method_${ this.methodId }`);
const paymentMethodLi = document.querySelector(
`.wc_payment_method.payment_method_${ this.methodId }`
);
document
.querySelectorAll( styleSelector )
.forEach( ( el ) => el.remove() );
if (paymentMethodLi.style.display === 'none' || paymentMethodLi.style.display === '') {
paymentMethodLi.style.display = 'block';
}
if (
paymentMethodLi.style.display === 'none' ||
paymentMethodLi.style.display === ''
) {
paymentMethodLi.style.display = 'block';
}
document
.querySelectorAll( wrapperSelector )
@ -1007,7 +1023,7 @@ export default class PaymentButton {
this.removeButton();
}
this.log( 'addButton', button );
this.log( 'insertButton', button );
this.#button = button;
wrapper.appendChild( this.#button );

View file

@ -1,7 +1,6 @@
/* Front end display */
.ppcp-button-apm .gpay-card-info-container-fill .gpay-card-info-container {
outline-offset: -1px;
border-radius: var(--apm-button-border-radius);
}
/* Admin preview */

View file

@ -1,12 +1,15 @@
import { useState, useEffect } from '@wordpress/element';
import { useState } from '@wordpress/element';
import useGooglepayApiToGenerateButton from '../hooks/useGooglepayApiToGenerateButton';
import usePayPalScript from '../hooks/usePayPalScript';
import useGooglepayScript from '../hooks/useGooglepayScript';
import useGooglepayConfig from '../hooks/useGooglepayConfig';
const GooglepayButton = ( { namespace, buttonConfig, ppcpConfig } ) => {
const [ buttonHtml, setButtonHtml ] = useState( '' );
const [ buttonElement, setButtonElement ] = useState( null );
const GooglepayButton = ( {
namespace,
buttonConfig,
ppcpConfig,
buttonAttributes,
} ) => {
const [ componentFrame, setComponentFrame ] = useState( null );
const isPayPalLoaded = usePayPalScript( namespace, ppcpConfig );
@ -18,35 +21,45 @@ const GooglepayButton = ( { namespace, buttonConfig, ppcpConfig } ) => {
const googlepayConfig = useGooglepayConfig( namespace, isGooglepayLoaded );
useEffect( () => {
if ( ! buttonElement ) {
return;
}
setComponentFrame( buttonElement.ownerDocument );
}, [ buttonElement ] );
const googlepayButton = useGooglepayApiToGenerateButton(
const { button, containerStyles } = useGooglepayApiToGenerateButton(
componentFrame,
namespace,
buttonConfig,
ppcpConfig,
googlepayConfig
googlepayConfig,
buttonAttributes
);
useEffect( () => {
if ( googlepayButton ) {
const hideLoader =
'<style>.block-editor-iframe__html .gpay-card-info-animated-progress-bar-container {display:none !important}</style>';
setButtonHtml( googlepayButton.outerHTML + hideLoader );
}
}, [ googlepayButton ] );
return (
<div
ref={ setButtonElement }
dangerouslySetInnerHTML={ { __html: buttonHtml } }
/>
<>
<div
id="express-payment-method-ppcp-googlepay"
style={ containerStyles }
ref={ ( node ) => {
if ( ! node ) {
return;
}
// Set component frame
setComponentFrame( node.ownerDocument );
// Handle button mounting
while ( node.firstChild ) {
node.removeChild( node.firstChild );
}
if ( button ) {
node.appendChild( button );
}
} }
/>
{ button && (
<style>
{ `.block-editor-iframe__html .gpay-card-info-animated-progress-bar-container {
display: none !important
}` }
</style>
) }
</>
);
};

View file

@ -1,19 +1,26 @@
import { useMemo } from '@wordpress/element';
import { combineStyles } from '../../../../../ppcp-button/resources/js/modules/Helper/PaymentButtonHelpers';
const useButtonStyles = ( buttonConfig, ppcpConfig ) => {
const useButtonStyles = ( buttonConfig, ppcpConfig, buttonAttributes ) => {
return useMemo( () => {
const styles = combineStyles(
ppcpConfig?.button || {},
buttonConfig?.button || {}
);
if ( styles.MiniCart && styles.MiniCart.type === 'buy' ) {
if ( buttonAttributes && styles.Default ) {
styles.Default.height =
buttonAttributes.height || styles.Default.height;
styles.Default.borderRadius =
buttonAttributes.borderRadius || styles.Default.borderRadius;
}
if ( styles.MiniCart?.type === 'buy' ) {
styles.MiniCart.type = 'pay';
}
return styles;
}, [ buttonConfig, ppcpConfig ] );
}, [ buttonConfig, ppcpConfig, buttonAttributes ] );
};
export default useButtonStyles;

View file

@ -6,10 +6,16 @@ const useGooglepayApiToGenerateButton = (
namespace,
buttonConfig,
ppcpConfig,
googlepayConfig
googlepayConfig,
buttonAttributes
) => {
const [ googlepayButton, setGooglepayButton ] = useState( null );
const buttonStyles = useButtonStyles( buttonConfig, ppcpConfig );
const buttonStyles = useButtonStyles(
buttonConfig,
ppcpConfig,
buttonAttributes
);
useEffect( () => {
if (
@ -35,14 +41,13 @@ const useGooglepayApiToGenerateButton = (
buttonType: buttonConfig.buttonType || 'pay',
buttonLocale: buttonConfig.buttonLocale || 'en',
buttonSizeMode: 'fill',
};
const button = paymentsClient.createButton( {
...googlePayButtonOptions,
buttonRadius: parseInt( buttonStyles?.Default?.borderRadius ),
onClick: ( event ) => {
event.preventDefault();
},
} );
};
const button = paymentsClient.createButton( googlePayButtonOptions );
setGooglepayButton( button );
@ -51,7 +56,15 @@ const useGooglepayApiToGenerateButton = (
};
}, [ namespace, buttonConfig, ppcpConfig, googlepayConfig, buttonStyles ] );
return googlepayButton;
// Return both the button and the styles needed for the container
return {
button: googlepayButton,
containerStyles: {
height: buttonStyles?.Default?.height
? `${ buttonStyles.Default.height }px`
: '',
},
};
};
export default useGooglepayApiToGenerateButton;

View file

@ -106,8 +106,40 @@ class GooglepayButton extends PaymentButton {
*/
#transactionInfo = null;
/**
* The currently visible payment button.
*
* @type {HTMLElement|null}
*/
#button = null;
/**
* The Google Pay configuration object.
*
* @type {Object|null}
*/
googlePayConfig = null;
/**
* The start time of the configuration process.
*
* @type {number}
*/
#configureStartTime = 0;
/**
* The maximum time to wait for buttonAttributes before proceeding with initialization.
* @type {number}
*/
#maxWaitTime = 1000;
/**
* The stored button attributes.
*
* @type {null}
*/
#storedButtonAttributes = null;
/**
* @inheritDoc
*/
@ -142,7 +174,8 @@ class GooglepayButton extends PaymentButton {
externalHandler,
buttonConfig,
ppcpConfig,
contextHandler
contextHandler,
buttonAttributes
) {
// Disable debug output in the browser console:
// buttonConfig.is_debug = false;
@ -152,7 +185,8 @@ class GooglepayButton extends PaymentButton {
externalHandler,
buttonConfig,
ppcpConfig,
contextHandler
contextHandler,
buttonAttributes
);
this.init = this.init.bind( this );
@ -240,29 +274,95 @@ class GooglepayButton extends PaymentButton {
() => ! this.contextHandler?.validateContext(),
`Invalid context handler.`
);
invalidIf(
() =>
this.buttonAttributes?.height &&
isNaN( parseInt( this.buttonAttributes.height ) ),
'Invalid height in buttonAttributes'
);
invalidIf(
() =>
this.buttonAttributes?.borderRadius &&
isNaN( parseInt( this.buttonAttributes.borderRadius ) ),
'Invalid borderRadius in buttonAttributes'
);
return true;
}
/**
* Configures the button instance. Must be called before the initial `init()`.
*
* @param {Object} apiConfig - API configuration.
* @param {Object} transactionInfo - Transaction details; required before "init" call.
* @param {Object} apiConfig - API configuration.
* @param {Object} transactionInfo - Transaction details; required before "init" call.
* @param {Object} buttonAttributes - Button attributes.
*/
configure( apiConfig, transactionInfo ) {
configure( apiConfig, transactionInfo, buttonAttributes = {} ) {
// Start timing on first configure call
if ( ! this.#configureStartTime ) {
this.#configureStartTime = Date.now();
}
// If valid buttonAttributes, store them
if ( buttonAttributes?.height && buttonAttributes?.borderRadius ) {
this.#storedButtonAttributes = { ...buttonAttributes };
}
// Use stored attributes if current ones are missing
const attributes = buttonAttributes?.height
? buttonAttributes
: this.#storedButtonAttributes;
// Check if we've exceeded wait time
const timeWaited = Date.now() - this.#configureStartTime;
if ( timeWaited > this.#maxWaitTime ) {
this.log(
'GooglePay: Timeout waiting for buttonAttributes - proceeding with initialization'
);
this.googlePayConfig = apiConfig;
this.#transactionInfo = transactionInfo;
this.buttonAttributes = attributes || buttonAttributes;
this.allowedPaymentMethods =
this.googlePayConfig.allowedPaymentMethods;
this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ];
this.init();
return;
}
// Block any initialization until we have valid buttonAttributes
if ( ! attributes?.height || ! attributes?.borderRadius ) {
setTimeout(
() =>
this.configure(
apiConfig,
transactionInfo,
buttonAttributes
),
100
);
return;
}
// Reset timer for future configure calls
this.#configureStartTime = 0;
this.googlePayConfig = apiConfig;
this.#transactionInfo = transactionInfo;
this.buttonAttributes = attributes;
this.allowedPaymentMethods = this.googlePayConfig.allowedPaymentMethods;
this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ];
this.init();
}
init() {
// Use `reinit()` to force a full refresh of an initialized button.
// Skip if already initialized
if ( this.isInitialized ) {
return;
}
// Stop, if configuration is invalid.
// Validate configuration
if ( ! this.validateConfiguration() ) {
return;
}
@ -270,16 +370,6 @@ class GooglepayButton extends PaymentButton {
super.init();
this.#paymentsClient = this.createPaymentsClient();
if ( ! this.isPresent ) {
this.log( 'Payment wrapper not found', this.wrapperId );
return;
}
if ( ! this.paymentsClient ) {
this.log( 'Could not initialize the payments client' );
return;
}
this.paymentsClient
.isReadyToPay(
this.buildReadyToPayRequest(
@ -357,30 +447,86 @@ class GooglepayButton extends PaymentButton {
}
/**
* Creates the payment button and calls `this.insertButton()` to make the button visible in the
* correct wrapper.
* Creates the payment button and calls `super.insertButton()` to make the button visible in the correct wrapper.
*/
addButton() {
if ( ! this.paymentsClient ) {
return;
}
// If current buttonAttributes are missing, try to use stored ones
if (
! this.buttonAttributes?.height &&
this.#storedButtonAttributes?.height
) {
this.buttonAttributes = { ...this.#storedButtonAttributes };
}
this.removeButton();
const baseCardPaymentMethod = this.baseCardPaymentMethod;
const { color, type, language } = this.style;
/**
* @see https://developers.google.com/pay/api/web/reference/client#createButton
*/
const button = this.paymentsClient.createButton( {
const buttonOptions = {
buttonColor: color || 'black',
buttonSizeMode: 'fill',
buttonLocale: language || 'en',
buttonType: type || 'pay',
buttonRadius: parseInt( this.buttonAttributes?.borderRadius, 10 ),
onClick: this.onButtonClick,
allowedPaymentMethods: [ baseCardPaymentMethod ],
buttonColor: color || 'black',
buttonType: type || 'pay',
buttonLocale: language || 'en',
buttonSizeMode: 'fill',
} );
};
this.insertButton( button );
const button = this.paymentsClient.createButton( buttonOptions );
this.#button = button;
super.insertButton( button );
this.applyWrapperStyles();
}
/**
* Applies CSS classes and inline styling to the payment button wrapper.
* Extends parent implementation to handle Google Pay specific styling.
*/
applyWrapperStyles() {
super.applyWrapperStyles();
const wrapper = this.wrapperElement;
if ( ! wrapper ) {
return;
}
// Try stored attributes if current ones are missing
const attributes = this.buttonAttributes?.height
? this.buttonAttributes
: this.#storedButtonAttributes;
if ( attributes?.height ) {
const height = parseInt( attributes.height, 10 );
if ( ! isNaN( height ) ) {
wrapper.style.height = `${ height }px`;
wrapper.style.minHeight = `${ height }px`;
}
}
}
/**
* Removes the payment button from the DOM.
*/
removeButton() {
if ( ! this.isPresent || ! this.#button ) {
return;
}
this.log( 'removeButton' );
try {
this.wrapperElement.removeChild( this.#button );
} catch ( Exception ) {
// Ignore this.
}
this.#button = null;
}
//------------------------

View file

@ -3,10 +3,11 @@ import GooglepayButton from './GooglepayButton';
import ContextHandlerFactory from './Context/ContextHandlerFactory';
class GooglepayManager {
constructor( namespace, buttonConfig, ppcpConfig ) {
constructor( namespace, buttonConfig, ppcpConfig, buttonAttributes = {} ) {
this.namespace = namespace;
this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig;
this.buttonAttributes = buttonAttributes;
this.googlePayConfig = null;
this.transactionInfo = null;
this.contextHandler = null;
@ -26,13 +27,18 @@ class GooglepayManager {
bootstrap.handler,
buttonConfig,
ppcpConfig,
this.contextHandler
this.contextHandler,
this.buttonAttributes
);
this.buttons.push( button );
const initButton = () => {
button.configure( this.googlePayConfig, this.transactionInfo );
button.configure(
this.googlePayConfig,
this.transactionInfo,
this.buttonAttributes
);
button.init();
};
@ -70,7 +76,8 @@ class GooglepayManager {
for ( const button of this.buttons ) {
button.configure(
this.googlePayConfig,
this.transactionInfo
this.transactionInfo,
this.buttonAttributes
);
button.init();
}

View file

@ -4,11 +4,13 @@ const GooglepayManagerBlockEditor = ( {
namespace,
buttonConfig,
ppcpConfig,
buttonAttributes,
} ) => (
<GooglepayButton
namespace={ namespace }
buttonConfig={ buttonConfig }
ppcpConfig={ ppcpConfig }
buttonAttributes={ buttonAttributes }
/>
);

View file

@ -20,7 +20,7 @@ if ( typeof window.PayPalCommerceGateway === 'undefined' ) {
window.PayPalCommerceGateway = ppcpConfig;
}
const GooglePayComponent = ( { isEditing } ) => {
const GooglePayComponent = ( { isEditing, buttonAttributes } ) => {
const [ paypalLoaded, setPaypalLoaded ] = useState( false );
const [ googlePayLoaded, setGooglePayLoaded ] = useState( false );
const [ manager, setManager ] = useState( null );
@ -48,11 +48,18 @@ const GooglePayComponent = ( { isEditing } ) => {
const newManager = new GooglepayManager(
namespace,
buttonConfig,
ppcpConfig
ppcpConfig,
buttonAttributes
);
setManager( newManager );
}
}, [ paypalLoaded, googlePayLoaded, isEditing, manager ] );
}, [
paypalLoaded,
googlePayLoaded,
isEditing,
manager,
buttonAttributes,
] );
if ( isEditing ) {
return (
@ -60,6 +67,7 @@ const GooglePayComponent = ( { isEditing } ) => {
namespace={ namespace }
buttonConfig={ buttonConfig }
ppcpConfig={ ppcpConfig }
buttonAttributes={ buttonAttributes }
/>
);
}
@ -89,5 +97,6 @@ registerExpressPaymentMethod( {
canMakePayment: () => buttonData.enabled,
supports: {
features,
style: [ 'height', 'borderRadius' ],
},
} );

View file

@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods;
use WC_Order;
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
@ -46,7 +47,7 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
$settings = $c->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
if ( ! $settings->has( 'allow_local_apm_gateways' ) || $settings->get( 'allow_local_apm_gateways' ) !== true ) {
if ( ! self::should_add_local_apm_gateways( $settings ) ) {
return true;
}
@ -58,6 +59,11 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
* @psalm-suppress MissingClosureParamType
*/
function ( $methods ) use ( $c ) {
$onboarding_state = $c->get( 'onboarding.state' );
if ( $onboarding_state->current_state() === State::STATE_START ) {
return $methods;
}
if ( ! is_array( $methods ) ) {
return $methods;
}
@ -185,18 +191,6 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
2
);
add_filter(
'woocommerce_paypal_payments_allowed_refund_payment_methods',
function( array $payment_methods ) use ( $c ): array {
$local_payment_methods = $c->get( 'ppcp-local-apms.payment-methods' );
foreach ( $local_payment_methods as $payment_method ) {
$payment_methods[] = $payment_method['id'];
}
return $payment_methods;
}
);
return true;
}
@ -216,4 +210,17 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
return false;
}
/**
* Check if the local APMs should be added to the available payment gateways.
*
* @param Settings $settings PayPal gateway settings.
* @return bool
*/
private function should_add_local_apm_gateways( Settings $settings ): bool {
return $settings->has( 'enabled' )
&& $settings->get( 'enabled' ) === true
&& $settings->has( 'allow_local_apm_gateways' )
&& $settings->get( 'allow_local_apm_gateways' ) === true;
}
}

View file

@ -13,6 +13,7 @@ use WooCommerce\PayPalCommerce\Onboarding\Endpoint\UpdateSignupLinksEndpoint;
use WooCommerce\PayPalCommerce\Onboarding\Assets\OnboardingAssets;
use WooCommerce\PayPalCommerce\Onboarding\Endpoint\LoginSellerEndpoint;
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingRenderer;
use WooCommerce\PayPalCommerce\Settings\SettingsModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
@ -44,26 +45,34 @@ class OnboardingModule implements ServiceModule, ExtendingModule, ExecutableModu
*/
public function run( ContainerInterface $c ): bool {
$asset_loader = $c->get( 'onboarding.assets' );
/**
* The OnboardingAssets.
*
* @var OnboardingAssets $asset_loader
*/
add_action(
'admin_enqueue_scripts',
array(
$asset_loader,
'register',
)
);
add_action(
'woocommerce_settings_checkout',
array(
$asset_loader,
'enqueue',
)
);
if ( ! apply_filters(
// phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
'woocommerce.feature-flags.woocommerce_paypal_payments.settings_enabled',
getenv( 'PCP_SETTINGS_ENABLED' ) === '1'
) || SettingsModule::should_use_the_old_ui()
) {
$asset_loader = $c->get( 'onboarding.assets' );
/**
* The OnboardingAssets.
*
* @var OnboardingAssets $asset_loader
*/
add_action(
'admin_enqueue_scripts',
array(
$asset_loader,
'register',
)
);
add_action(
'woocommerce_settings_checkout',
array(
$asset_loader,
'enqueue',
)
);
}
add_filter(
'woocommerce_form_field',

View file

@ -82,9 +82,8 @@ import {
renderFields( cardFields );
}
document
.querySelector( '#place_order' )
?.addEventListener( 'click', ( event ) => {
const placeOrderButton = document.querySelector( '#place_order' );
placeOrderButton?.addEventListener( 'click', ( event ) => {
const cardPaymentToken = document.querySelector(
'input[name="wc-ppcp-credit-card-gateway-payment-token"]:checked'
)?.value;
@ -95,11 +94,11 @@ import {
) {
return;
}
placeOrderButton.disabled = true;
event.preventDefault();
cardFields.submit().catch( ( error ) => {
console.error( error );
placeOrderButton.disabled = false;
} );
} );
} );

View file

@ -94,7 +94,9 @@ class CreatePaymentToken implements EndpointInterface {
)
);
$result = $this->payment_method_tokens_endpoint->create_payment_token( $payment_source );
$customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
$result = $this->payment_method_tokens_endpoint->create_payment_token( $payment_source, $customer_id );
if ( is_user_logged_in() && isset( $result->customer->id ) ) {
$current_user_id = get_current_user_id();

View file

@ -103,7 +103,9 @@ class CreateSetupToken implements EndpointInterface {
);
}
$result = $this->payment_method_tokens_endpoint->setup_tokens( $payment_source );
$customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
$result = $this->payment_method_tokens_endpoint->setup_tokens( $payment_source, $customer_id );
wp_send_json_success( $result );
return true;

View file

@ -0,0 +1,9 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.75 22.5C28.75 23.1904 28.1904 23.75 27.5 23.75L10 23.75C9.30964 23.75 8.75 23.1904 8.75 22.5L8.75 13.75C8.75 13.0596 9.30965 12.5 10 12.5L27.5 12.5C28.1904 12.5 28.75 13.0596 28.75 13.75V22.5Z" fill="#5BBBFC"/>
<path d="M1.25 7.5C1.25 6.80964 1.80964 6.25 2.5 6.25H17.5C18.1904 6.25 18.75 6.80964 18.75 7.5V17.5C18.75 18.1904 18.1904 18.75 17.5 18.75H2.5C1.80964 18.75 1.25 18.1904 1.25 17.5V7.5Z" fill="#DD7F57"/>
<path d="M18.75 12.5V17.5C18.75 18.1904 18.1904 18.75 17.5 18.75H8.75L8.75 13.75C8.75 13.0596 9.30965 12.5 10 12.5L18.75 12.5Z" fill="#FAF8F5"/>
<path d="M14.375 18.125C14.375 15.7088 16.3338 13.75 18.75 13.75C21.1662 13.75 23.125 15.7088 23.125 18.125C23.125 20.5412 21.1662 22.5 18.75 22.5C16.3338 22.5 14.375 20.5412 14.375 18.125Z" fill="#001C64"/>
<path d="M1.25 8.75H18.75V11.25H1.25V8.75Z" fill="#4F2825"/>
<path d="M7.5 16.4062C7.5 16.5788 7.36009 16.7188 7.1875 16.7188H2.8125C2.63991 16.7188 2.5 16.5788 2.5 16.4062V15.9375C2.5 15.7649 2.63991 15.625 2.8125 15.625H7.1875C7.36009 15.625 7.5 15.7649 7.5 15.9375V16.4062Z" fill="#4F2825"/>
<path d="M14.375 18.125C14.375 15.7088 16.3338 13.75 18.75 13.75V17.5C18.75 18.1904 18.1904 18.75 17.5 18.75H14.4193C14.3901 18.5459 14.375 18.3372 14.375 18.125Z" fill="#4F2825"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1,5 +1,10 @@
<svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="0.000488281" width="58" height="58" rx="3" fill="white"/>
<rect x="0.5" y="0.500488" width="57" height="57" rx="2.5" stroke="black" stroke-opacity="0.07"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.6557 23.0408C15.1747 23.6163 14.405 24.0702 13.6354 24.0053C13.5392 23.2272 13.916 22.4005 14.3569 21.8898C14.8379 21.2981 15.6797 20.8767 16.3611 20.8442C16.4413 21.6548 16.1286 22.4491 15.6557 23.0408ZM16.3527 24.1591C15.6747 24.1197 15.056 24.3653 14.5563 24.5637C14.2347 24.6914 13.9624 24.7995 13.7552 24.7995C13.5228 24.7995 13.2392 24.6856 12.9208 24.5577C12.5037 24.3901 12.0267 24.1986 11.5266 24.2078C10.3802 24.224 9.31398 24.8805 8.72875 25.9261C7.52624 28.0173 8.4161 31.1136 9.57853 32.8157C10.1477 33.6587 10.8291 34.5827 11.727 34.5503C12.122 34.5352 12.4062 34.4133 12.7002 34.2871C13.0388 34.1419 13.3905 33.991 13.9396 33.991C14.4697 33.991 14.8061 34.1379 15.1289 34.279C15.4359 34.4131 15.7307 34.5419 16.1683 34.534C17.0982 34.5178 17.6835 33.6911 18.2526 32.8481C18.8669 31.9434 19.1368 31.0604 19.1778 30.9264L19.1826 30.9109C19.1816 30.9099 19.174 30.9064 19.1607 30.9003L19.1607 30.9003L19.1606 30.9002C18.9553 30.8052 17.3859 30.0788 17.3708 28.1308C17.3557 26.4957 18.6156 25.6673 18.814 25.5369C18.826 25.529 18.8342 25.5236 18.8379 25.5208C18.0362 24.3212 16.7856 24.1916 16.3527 24.1591ZM22.7899 34.4452V21.8089H27.4797C29.9007 21.8089 31.5923 23.4948 31.5923 25.9589C31.5923 28.4229 29.8687 30.125 27.4155 30.125H24.7299V34.4452H22.7899ZM24.7298 23.4621H26.9665C28.65 23.4621 29.612 24.3699 29.612 25.9667C29.612 27.5634 28.65 28.4793 26.9585 28.4793H24.7298V23.4621ZM37.9971 32.9291C37.4841 33.9179 36.3537 34.542 35.1352 34.542C33.3314 34.542 32.0728 33.4559 32.0728 31.8186C32.0728 30.1976 33.2913 29.2654 35.544 29.1276L37.9651 28.9818V28.2847C37.9651 27.2553 37.2997 26.696 36.1132 26.696C35.1352 26.696 34.4217 27.2067 34.2774 27.9848H32.5297C32.5858 26.3475 34.109 25.156 36.1693 25.156C38.3899 25.156 39.833 26.3313 39.833 28.155V34.4448H38.0372V32.9291H37.9971ZM35.6573 33.0428C34.6232 33.0428 33.9658 32.5403 33.9658 31.7703C33.9658 30.976 34.5991 30.514 35.8096 30.441L37.9661 30.3032V31.0165C37.9661 32.1999 36.9721 33.0428 35.6573 33.0428ZM45.7894 34.9398C45.0118 37.1525 44.1219 37.882 42.23 37.882C42.0857 37.882 41.6047 37.8658 41.4924 37.8334V36.3177C41.6127 36.3339 41.9093 36.3501 42.0616 36.3501C42.9194 36.3501 43.4004 35.9854 43.697 35.037L43.8734 34.4778L40.5865 25.2781H42.6148L44.8996 32.7432H44.9396L47.2244 25.2781H49.1965L45.7894 34.9398Z" fill="black"/>
<svg width="31" height="30" viewBox="0 0 31 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5121_50953)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.81566 10.6056C5.46406 11.0263 4.90149 11.3581 4.33893 11.3107C4.26861 10.7419 4.54403 10.1376 4.86633 9.76431C5.21794 9.33179 5.83324 9.0237 6.33135 9C6.38995 9.59249 6.16141 10.1731 5.81566 10.6056ZM6.3249 11.4231C5.82929 11.3943 5.37707 11.5738 5.0118 11.7188C4.77674 11.8122 4.57768 11.8912 4.42624 11.8912C4.2563 11.8912 4.04903 11.8079 3.81631 11.7145C3.51137 11.592 3.16274 11.452 2.79715 11.4587C1.95916 11.4705 1.17977 11.9504 0.751983 12.7147C-0.127026 14.2434 0.523441 16.5067 1.37315 17.7509C1.78921 18.3671 2.28732 19.0425 2.94365 19.0188C3.23239 19.0078 3.4401 18.9187 3.65505 18.8265C3.90253 18.7203 4.15961 18.61 4.56102 18.61C4.94852 18.61 5.19436 18.7174 5.43035 18.8205C5.65474 18.9186 5.87023 19.0127 6.19012 19.007C6.86989 18.9951 7.29767 18.3908 7.71374 17.7746C8.16273 17.1133 8.36005 16.4678 8.38999 16.3699L8.39351 16.3585C8.39279 16.3578 8.38724 16.3552 8.37745 16.3507C8.22736 16.2812 7.08014 15.7502 7.06913 14.3263C7.05808 13.1311 7.97908 12.5256 8.12406 12.4303C8.13288 12.4245 8.13883 12.4205 8.14152 12.4185C7.55552 11.5416 6.64135 11.4468 6.3249 11.4231ZM11.0306 18.9418V9.70495H14.4587C16.2284 9.70495 17.4649 10.9373 17.4649 12.7385C17.4649 14.5396 16.205 15.7839 14.4118 15.7839H12.4487V18.9418H11.0306ZM12.4487 10.9138H14.0837C15.3143 10.9138 16.0175 11.5774 16.0175 12.7446C16.0175 13.9118 15.3143 14.5813 14.0778 14.5813H12.4487V10.9138ZM22.1475 17.834C21.7724 18.5569 20.9462 19.0131 20.0554 19.0131C18.7369 19.0131 17.8169 18.2192 17.8169 17.0223C17.8169 15.8374 18.7076 15.156 20.3543 15.0553L22.124 14.9486V14.4391C22.124 13.6866 21.6377 13.2778 20.7704 13.2778C20.0554 13.2778 19.5339 13.6511 19.4284 14.2199H18.1509C18.1919 13.0231 19.3054 12.1521 20.8114 12.1521C22.4346 12.1521 23.4894 13.0112 23.4894 14.3443V18.942H22.1768V17.834H22.1475ZM20.4359 17.917C19.68 17.917 19.1995 17.5497 19.1995 16.9868C19.1995 16.4062 19.6624 16.0685 20.5473 16.0151L22.1236 15.9144V16.4358C22.1236 17.3008 21.397 17.917 20.4359 17.917ZM27.8427 19.3032C27.2743 20.9206 26.6238 21.4539 25.2409 21.4539C25.1354 21.4539 24.7838 21.442 24.7017 21.4183V20.3104C24.7896 20.3222 25.0065 20.3341 25.1178 20.3341C25.7448 20.3341 26.0964 20.0675 26.3133 19.3742L26.4422 18.9654L24.0396 12.2407H25.5222L27.1923 17.6975H27.2216L28.8917 12.2407H30.3333L27.8427 19.3032Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_5121_50953">
<rect width="30" height="30" fill="white" transform="translate(0.333496)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before After
Before After

View file

@ -1,9 +1,14 @@
<svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="0.000732422" width="58" height="58" rx="3" fill="white"/>
<rect x="0.5" y="0.500732" width="57" height="57" rx="2.5" stroke="black" stroke-opacity="0.07"/>
<path d="M44.3201 29.4307H40.9228C40.5449 29.4307 40.2375 29.1296 40.2375 28.7527C40.2375 28.3758 40.5449 28.044 40.9228 28.044H46.0949V25.4927H40.9228C39.1176 25.4927 37.6488 26.9644 37.6488 28.7651C37.6488 30.5658 39.1176 32.0375 40.9228 32.0375H44.2739C44.6518 32.0375 44.9592 32.3373 44.9592 32.7142C44.9592 33.0911 44.6517 33.3686 44.2739 33.3686H37.0878C36.4777 34.5334 35.8845 35.5317 34.6802 36.0309H44.3201C46.0949 36.0054 47.5468 34.5033 47.5468 32.7185C47.5468 30.9339 46.0949 29.4562 44.3201 29.4307Z" fill="#71706F"/>
<path d="M31.2969 25.4927C28.4175 25.4927 26.0723 27.8519 26.0723 30.7507V30.8616V41.6883H28.6791V36.0309H31.2914C34.1709 36.0309 36.5078 33.6404 36.5078 30.7415C36.5077 27.8427 34.1763 25.4927 31.2969 25.4927ZM31.2969 33.3686H28.6791V30.7415C28.6791 29.2735 29.8479 28.0791 31.2969 28.0791C32.7459 28.0791 33.9246 29.2735 33.9246 30.7415C33.9246 32.2096 32.7459 33.3686 31.2969 33.3686Z" fill="#71706F"/>
<path d="M17.8193 36.0308C15.3545 36.0308 13.2822 34.2744 12.7215 31.9798C12.7215 31.9798 12.5595 31.2212 12.5595 30.7216C12.5595 30.2221 12.7128 29.4556 12.7128 29.4556C13.2776 27.1667 15.3473 25.4623 17.8081 25.4623C20.7023 25.4623 23.0772 27.815 23.0772 30.7091V31.9819H15.3879C15.842 32.8693 16.7603 33.3685 17.8193 33.3685H24.7316L24.7411 25.8631C24.7411 24.7441 23.8257 23.8286 22.7067 23.8286H12.9095C11.7906 23.8286 10.875 24.7164 10.875 25.8354V35.6325C10.875 36.7515 11.7906 37.6947 12.9095 37.6947H22.7067C23.7109 37.6947 24.5485 36.9737 24.7104 36.0308H17.8193Z" fill="#C8036F"/>
<path d="M17.8079 27.9526C16.753 27.9526 15.8356 28.5985 15.3796 29.4304H20.2363C19.7804 28.5985 18.863 27.9526 17.8079 27.9526Z" fill="#C8036F"/>
<path d="M22.2452 20.6427C22.2452 18.2516 20.2581 16.3132 17.8068 16.3132C15.3961 16.3132 13.4355 18.1882 13.3711 20.5245C13.3701 20.5344 13.3709 20.544 13.3709 20.5542V21.8552C13.3709 22.0109 13.4977 22.1647 13.6572 22.1647H15.2901C15.4496 22.1647 15.5895 22.0109 15.5895 21.8552V20.6427C15.5895 19.4487 16.584 18.4772 17.8081 18.4772C19.0322 18.4772 20.0267 19.4487 20.0267 20.6427V21.8552C20.0267 22.0109 20.1561 22.1647 20.3156 22.1647H21.9486C22.1081 22.1647 22.2452 22.0109 22.2452 21.8552V20.6427Z" fill="#C8036F"/>
<svg width="31" height="30" viewBox="0 0 31 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5121_51087)">
<path d="M27.3615 15.7886H24.5822C24.273 15.7886 24.0216 15.541 24.0216 15.231C24.0216 14.921 24.2731 14.6482 24.5822 14.6482H28.8136V12.5498H24.5822C23.1053 12.5498 21.9037 13.7602 21.9037 15.2412C21.9037 16.7222 23.1053 17.9326 24.5822 17.9326H27.3238C27.6329 17.9326 27.8845 18.1792 27.8845 18.4891C27.8845 18.7991 27.6329 19.0274 27.3238 19.0274H21.4448C20.9456 19.9853 20.4603 20.8064 19.4751 21.217H27.3615C28.8136 21.1959 30.0013 19.9606 30.0013 18.4927C30.0013 17.0249 28.8136 15.8096 27.3615 15.7886Z" fill="#71706F"/>
<path d="M16.7064 12.5498C14.3508 12.5498 12.4321 14.4901 12.4321 16.8743V16.9655V25.8698H14.5648V21.217H16.7019C19.0576 21.217 20.9695 19.2508 20.9695 16.8667C20.9695 14.4825 19.0621 12.5498 16.7064 12.5498ZM16.7064 19.0274H14.5648V16.8667C14.5648 15.6593 15.521 14.677 16.7064 14.677C17.8918 14.677 18.8562 15.6593 18.8562 16.8667C18.8562 18.0741 17.8918 19.0274 16.7064 19.0274Z" fill="#71706F"/>
<path d="M5.68167 21.2166C3.66517 21.2166 1.96984 19.772 1.51109 17.8849C1.51109 17.8849 1.37855 17.2609 1.37855 16.85C1.37855 16.4392 1.50402 15.8088 1.50402 15.8088C1.96608 13.9263 3.65932 12.5245 5.67251 12.5245C8.04026 12.5245 9.98316 14.4595 9.98316 16.8397V17.8865H3.69249C4.06398 18.6164 4.81527 19.027 5.68167 19.027H11.3367L11.3444 12.8542C11.3444 11.9339 10.5955 11.1809 9.68005 11.1809H1.66496C0.749507 11.1809 0.000488281 11.911 0.000488281 12.8314V20.889C0.000488281 21.8093 0.749507 22.5851 1.66496 22.5851H9.68005C10.5016 22.5851 11.1869 21.992 11.3193 21.2166H5.68167Z" fill="#C8036F"/>
<path d="M5.67044 14.5728C4.80739 14.5728 4.05683 15.1039 3.68384 15.7882H7.65713C7.28414 15.1039 6.53362 14.5728 5.67044 14.5728Z" fill="#C8036F"/>
<path d="M9.30189 8.56078C9.30189 6.59421 7.67616 5 5.67078 5C3.69861 5 2.09462 6.54207 2.04194 8.46358C2.04108 8.47169 2.04176 8.47959 2.04176 8.48794V9.55801C2.04176 9.68601 2.14544 9.81255 2.27594 9.81255H3.61185C3.74235 9.81255 3.85679 9.68601 3.85679 9.55801V8.56078C3.85679 7.57875 4.67038 6.77978 5.67182 6.77978C6.67327 6.77978 7.48686 7.57875 7.48686 8.56078V9.55801C7.48686 9.68601 7.59272 9.81255 7.72322 9.81255H9.05917C9.18967 9.81255 9.30189 9.68601 9.30189 9.55801V8.56078Z" fill="#C8036F"/>
</g>
<defs>
<clipPath id="clip0_5121_51087">
<rect width="30" height="30" fill="white" transform="translate(0.333496)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

View file

@ -1,11 +1,9 @@
<svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="0.000488281" width="58" height="58" rx="3" fill="white"/>
<rect x="0.5" y="0.500488" width="57" height="57" rx="2.5" stroke="black" stroke-opacity="0.07"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.1926 34.1615V29.1674V29.1665H30.8942C32.0081 29.1671 32.9442 28.8171 33.7025 28.1165C34.4689 27.4515 34.8974 26.4954 34.8756 25.4989C34.8908 24.5079 34.4631 23.5589 33.7025 22.896C32.9508 22.1916 31.9398 21.8072 30.8942 21.8283H26.5574V34.1615H28.1926ZM28.1929 27.6524V23.3471V23.3461H30.9351C31.5463 23.3295 32.1355 23.5678 32.5531 24.0005C32.9732 24.3963 33.2102 24.9399 33.2102 25.5076C33.2102 26.0753 32.9732 26.6189 32.5531 27.0147C32.1304 27.4383 31.543 27.6698 30.9351 27.6524H28.1929Z" fill="#5F6368"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M41.5056 26.387C40.8061 25.7612 39.8521 25.4482 38.6434 25.4482C37.0908 25.4482 35.9184 25.9993 35.1262 27.1015L36.5664 27.9802C37.0986 27.2337 37.8214 26.8604 38.7348 26.8604C39.3172 26.8538 39.8808 27.06 40.3132 27.438C40.7443 27.7834 40.9923 28.2983 40.9886 28.8403V29.2024C40.3602 28.858 39.5606 28.6858 38.5896 28.6858C37.4521 28.6871 36.5427 28.9456 35.8616 29.4612C35.1804 29.9769 34.8398 30.6713 34.8398 31.5445C34.825 32.3396 35.1809 33.0988 35.8087 33.611C36.4547 34.1621 37.2574 34.4376 38.2169 34.4376C39.3409 34.4376 40.2414 33.9554 40.9185 32.991H40.9896V34.1621H42.5537V28.9613C42.5544 27.871 42.205 27.0129 41.5056 26.387ZM37.0703 32.6125C36.7278 32.3732 36.5257 31.9883 36.528 31.5792C36.528 31.1196 36.7504 30.7368 37.1983 30.4219C37.6418 30.1116 38.1957 29.9565 38.8599 29.9565C39.772 29.9565 40.4829 30.1533 40.9928 30.5469C40.9928 31.2121 40.7219 31.7917 40.1803 32.2857C39.6924 32.7585 39.0309 33.0246 38.3409 33.0258C37.8809 33.0341 37.432 32.8881 37.0703 32.6125Z" fill="#5F6368"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M51.5326 25.724L46.0724 37.882H44.3844L46.4107 33.628L42.8203 25.724H44.5977L47.1927 31.7858H47.2283L49.7522 25.724H51.5326Z" fill="#5F6368"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.5843 28.0914C21.5849 27.6086 21.5427 27.1266 21.4583 26.6508H14.562V29.3796H18.5119C18.3486 30.2602 17.821 31.038 17.0514 31.5327V33.304H19.4087C20.789 32.071 21.5843 30.2475 21.5843 28.0914Z" fill="#4285F4"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.5625 35.0146C16.5359 35.0146 18.1976 34.3868 19.4092 33.3043L17.0519 31.533C16.3958 31.964 15.5508 32.21 14.5625 32.21C12.6551 32.21 11.0362 30.9642 10.4572 29.2854H8.02881V31.1108C9.2701 33.504 11.7981 35.0145 14.5625 35.0146Z" fill="#34A853"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.4571 29.2863C10.1511 28.4066 10.1511 27.4539 10.4571 26.5742V24.7488H8.02868C6.99044 26.7505 6.99044 29.11 8.02868 31.1117L10.4571 29.2863Z" fill="#FBBC04"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.5625 23.6493C15.6054 23.6328 16.6131 24.0145 17.3678 24.7121L19.4549 22.6898C18.1315 21.4854 16.3781 20.8242 14.5625 20.8447C11.7981 20.8448 9.2701 22.3553 8.02881 24.7485L10.4572 26.5739C11.0362 24.8951 12.6551 23.6493 14.5625 23.6493Z" fill="#EA4335"/>
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.1889 18.2509V14.8676V14.8669H16.0191C16.7737 14.8674 17.4079 14.6303 17.9217 14.1556C18.4409 13.7051 18.7312 13.0574 18.7164 12.3823C18.7267 11.7109 18.4369 11.068 17.9217 10.6189C17.4124 10.1416 16.7275 9.88125 16.0191 9.89558H13.0811V18.2509H14.1889ZM14.1884 13.841V10.9243V10.9236H16.0462C16.4603 10.9123 16.8594 11.0738 17.1423 11.3669C17.4269 11.6351 17.5875 12.0033 17.5875 12.3879C17.5875 12.7726 17.4269 13.1408 17.1423 13.409C16.856 13.696 16.458 13.8528 16.0462 13.841H14.1884Z" fill="#5F6368"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.2076 12.9839C22.7337 12.5599 22.0874 12.3479 21.2686 12.3479C20.2167 12.3479 19.4224 12.7212 18.8857 13.4679L19.8614 14.0632C20.222 13.5575 20.7117 13.3046 21.3305 13.3046C21.725 13.3001 22.1068 13.4398 22.3997 13.6959C22.6918 13.9299 22.8599 14.2787 22.8573 14.6459V14.8912C22.4316 14.6579 21.8899 14.5412 21.2321 14.5412C20.4614 14.5421 19.8454 14.7172 19.3839 15.0666C18.9224 15.4159 18.6917 15.8864 18.6917 16.4779C18.6817 17.0166 18.9228 17.5309 19.3481 17.8779C19.7857 18.2513 20.3296 18.4379 20.9796 18.4379C21.741 18.4379 22.3511 18.1113 22.8098 17.4579H22.858V18.2513H23.9176V14.7279C23.9181 13.9892 23.6814 13.4079 23.2076 12.9839ZM20.2024 17.2012C19.9703 17.0391 19.8335 16.7784 19.835 16.5012C19.835 16.1899 19.9857 15.9305 20.2891 15.7172C20.5896 15.507 20.9648 15.4019 21.4148 15.4019C22.0327 15.4019 22.5143 15.5352 22.8598 15.8019C22.8598 16.2525 22.6763 16.6452 22.3093 16.9799C21.9788 17.3002 21.5307 17.4804 21.0632 17.4812C20.7515 17.4868 20.4474 17.3879 20.2024 17.2012Z" fill="#5F6368"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M30 12.5344L26.3009 20.7711H25.1573L26.53 17.8891L24.0977 12.5344H25.3018L27.0598 16.6411H27.0839L28.7938 12.5344H30Z" fill="#5F6368"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.71146 14.1384C9.71188 13.8113 9.68333 13.4847 9.62614 13.1624H4.9541V15.011H7.63003C7.51938 15.6077 7.16195 16.1346 6.64058 16.4697V17.6697H8.2376C9.1727 16.8344 9.71146 15.599 9.71146 14.1384Z" fill="#4285F4"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.95374 18.8293C6.29067 18.8293 7.41636 18.404 8.23724 17.6707L6.64021 16.4707C6.19571 16.7627 5.62323 16.9293 4.95374 16.9293C3.66153 16.9293 2.56474 16.0853 2.17253 14.948H0.527344V16.1847C1.36828 17.806 3.08095 18.8293 4.95374 18.8293Z" fill="#34A853"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.17272 14.9478C1.96538 14.3518 1.96538 13.7064 2.17272 13.1105V11.8738H0.527529C-0.175843 13.2299 -0.175843 14.8284 0.527529 16.1845L2.17272 14.9478Z" fill="#FBBC04"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.95374 11.1291C5.66024 11.1179 6.34292 11.3765 6.8542 11.8491L8.2682 10.4791C7.37158 9.66313 6.18375 9.21516 4.95374 9.22907C3.08095 9.22916 1.36828 10.2524 0.527344 11.8738L2.17253 13.1104C2.56474 11.9731 3.66153 11.1291 4.95374 11.1291Z" fill="#EA4335"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before After
Before After

View file

@ -1,20 +1,18 @@
<svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="0.000488281" width="58" height="58" rx="3" fill="white"/>
<rect x="0.5" y="0.500488" width="57" height="57" rx="2.5" stroke="black" stroke-opacity="0.07"/>
<g clip-path="url(#clip0_3277_11784)">
<path d="M9 13.5955V43.6249C9 45.0623 10.1761 46.2384 11.6135 46.2384H29.5554C43.1196 46.2384 49 38.6461 49 28.571C49 18.5481 43.1196 10.9819 29.5554 10.9819H11.6135C10.1761 10.9819 9 12.158 9 13.5955Z" fill="white"/>
<path d="M21.0078 18.3528V40.5417H30.6648C39.4332 40.5417 43.2358 35.589 43.2358 28.5848C43.2358 21.8811 39.4332 16.6802 30.6648 16.6802H22.6805C21.7527 16.6802 21.0078 17.4381 21.0078 18.3528Z" fill="#CC0066"/>
<path d="M29.5561 43.8352H13.9533C12.5682 43.8352 11.4443 42.7113 11.4443 41.3262V15.8835C11.4443 14.4983 12.5682 13.3745 13.9533 13.3745H29.5561C44.3617 13.3745 46.5701 22.9008 46.5701 28.5722C46.5701 38.4121 40.5198 43.8352 29.5561 43.8352ZM13.9533 14.2108C13.0255 14.2108 12.2807 14.9557 12.2807 15.8835V41.3262C12.2807 42.254 13.0255 42.9988 13.9533 42.9988H29.5561C39.984 42.9988 45.7338 37.8763 45.7338 28.5722C45.7338 16.0795 35.5933 14.2108 29.5561 14.2108H13.9533Z" fill="black"/>
<path d="M24.4023 25.5966C24.7421 25.5966 25.0557 25.6488 25.3562 25.7534C25.6568 25.8579 25.9051 26.0278 26.1272 26.2369C26.3363 26.459 26.5062 26.7334 26.6369 27.0471C26.7545 27.3738 26.8198 27.7527 26.8198 28.197C26.8198 28.5891 26.7676 28.9419 26.6761 29.2686C26.5715 29.5953 26.4278 29.8827 26.2318 30.118C26.0358 30.3532 25.7875 30.5361 25.4869 30.6799C25.1864 30.8105 24.8335 30.889 24.4284 30.889H22.1416V25.5835H24.4023V25.5966ZM24.3239 29.9219C24.4938 29.9219 24.6506 29.8958 24.8205 29.8435C24.9773 29.7913 25.121 29.6998 25.2386 29.5691C25.3562 29.4384 25.4608 29.2816 25.5392 29.0726C25.6176 28.8635 25.6568 28.6283 25.6568 28.3277C25.6568 28.0663 25.6307 27.8181 25.5784 27.609C25.5261 27.3999 25.4346 27.2039 25.317 27.0601C25.1994 26.9164 25.0426 26.7857 24.8466 26.7073C24.6506 26.6289 24.4154 26.5897 24.1279 26.5897H23.2916V29.935H24.3239V29.9219Z" fill="white"/>
<path d="M31.5357 25.5966V26.5766H28.7392V27.7135H31.3135V28.6152H28.7392V29.9089H31.601V30.889H27.5762V25.5835H31.5357V25.5966Z" fill="white"/>
<path d="M35.5369 25.5952L37.5232 30.9007H36.3079L35.9028 29.7246H33.9165L33.4984 30.9007H32.3223L34.3216 25.5952H35.5369ZM35.6022 28.8491L34.9358 26.902H34.9227L34.2301 28.8491H35.6022Z" fill="white"/>
<path d="M39.4189 25.5952V29.9206H42.0063V30.9007H38.2559V25.5952H39.4189Z" fill="white"/>
<path d="M16.6458 30.6925C17.9954 30.6925 19.0894 29.5984 19.0894 28.2488C19.0894 26.8992 17.9954 25.8052 16.6458 25.8052C15.2962 25.8052 14.2021 26.8992 14.2021 28.2488C14.2021 29.5984 15.2962 30.6925 16.6458 30.6925Z" fill="black"/>
<path d="M18.4872 40.5476C16.4356 40.5476 14.7891 38.8881 14.7891 36.8495V33.9616C14.7891 32.9423 15.6123 32.106 16.6447 32.106C17.6639 32.106 18.5003 32.9292 18.5003 33.9616V40.5476H18.4872Z" fill="black"/>
<svg width="31" height="30" viewBox="0 0 31 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5121_51053)">
<path d="M0.666748 3.73895V26.261C0.666748 27.3391 1.54881 28.2211 2.62689 28.2211H16.0833C26.2564 28.2211 30.6667 22.5269 30.6667 14.9706C30.6667 7.45343 26.2564 1.77881 16.0833 1.77881H2.62689C1.54881 1.77881 0.666748 2.66087 0.666748 3.73895Z" fill="white"/>
<path d="M9.67358 7.30649V23.9481H16.9163C23.4926 23.9481 26.3446 20.2336 26.3446 14.9805C26.3446 9.95269 23.4926 6.052 16.9163 6.052H10.9281C10.2322 6.052 9.67358 6.62044 9.67358 7.30649Z" fill="#CC0066"/>
<path d="M16.0833 26.4278H4.38125C3.34237 26.4278 2.49951 25.5849 2.49951 24.546V5.46401C2.49951 4.42514 3.34237 3.58228 4.38125 3.58228H16.0833C27.1875 3.58228 28.8438 10.727 28.8438 14.9805C28.8438 22.3605 24.3061 26.4278 16.0833 26.4278ZM4.38125 4.20952C3.6854 4.20952 3.12676 4.76816 3.12676 5.46401V24.546C3.12676 25.2419 3.6854 25.8005 4.38125 25.8005H16.0833C23.9043 25.8005 28.2166 21.9586 28.2166 14.9805C28.2166 5.61102 20.6112 4.20952 16.0833 4.20952H4.38125Z" fill="black"/>
<path d="M12.2219 12.7359C12.4767 12.7359 12.7119 12.7751 12.9373 12.8535C13.1628 12.9319 13.349 13.0593 13.5156 13.2161C13.6724 13.3827 13.7998 13.5885 13.8978 13.8238C13.986 14.0688 14.035 14.353 14.035 14.6862C14.035 14.9802 13.9958 15.2449 13.9272 15.4899C13.8488 15.7349 13.741 15.9505 13.594 16.1269C13.447 16.3033 13.2608 16.4405 13.0354 16.5484C12.8099 16.6464 12.5453 16.7052 12.2415 16.7052H10.5264V12.7261H12.2219V12.7359ZM12.1631 15.9799C12.2905 15.9799 12.4081 15.9603 12.5355 15.9211C12.6531 15.8819 12.7609 15.8133 12.8491 15.7153C12.9373 15.6173 13.0158 15.4997 13.0746 15.3429C13.1334 15.1861 13.1628 15.0096 13.1628 14.7842C13.1628 14.5882 13.1432 14.402 13.104 14.2452C13.0648 14.0884 12.9962 13.9414 12.9079 13.8336C12.8197 13.7257 12.7021 13.6277 12.5551 13.5689C12.4081 13.5101 12.2317 13.4807 12.0161 13.4807H11.3888V15.9897H12.1631V15.9799Z" fill="white"/>
<path d="M17.5731 12.7359V13.4709H15.4758V14.3236H17.4065V14.9998H15.4758V15.9701H17.6221V16.7052H14.6035V12.7261H17.5731V12.7359Z" fill="white"/>
<path d="M20.5721 12.7358L22.0618 16.7149H21.1504L20.8465 15.8329H19.3568L19.0432 16.7149H18.1611L19.6606 12.7358H20.5721ZM20.6211 15.1762L20.1213 13.7159H20.1115L19.592 15.1762H20.6211Z" fill="white"/>
<path d="M23.4826 12.7358V15.9799H25.4231V16.7149H22.6104V12.7358H23.4826Z" fill="white"/>
<path d="M6.39987 16.5583C7.41206 16.5583 8.23261 15.7378 8.23261 14.7256C8.23261 13.7134 7.41206 12.8928 6.39987 12.8928C5.38768 12.8928 4.56714 13.7134 4.56714 14.7256C4.56714 15.7378 5.38768 16.5583 6.39987 16.5583Z" fill="black"/>
<path d="M7.78215 23.948C6.24344 23.948 5.00854 22.7033 5.00854 21.1744V19.0084C5.00854 18.2439 5.62599 17.6167 6.40025 17.6167C7.16471 17.6167 7.79195 18.2341 7.79195 19.0084V23.948H7.78215Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_3277_11784">
<rect width="40" height="35.2565" fill="white" transform="translate(9 11.0005)"/>
<clipPath id="clip0_5121_51053">
<rect width="30" height="26.4423" fill="white" transform="translate(0.666748 1.77881)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before After
Before After

View file

@ -0,0 +1,15 @@
<svg width="64" height="63" viewBox="0 0 64 63" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="64" height="62.8571" rx="3" fill="white"/>
<rect x="0.5" y="0.5" width="63" height="61.8571" rx="2.5" stroke="black" stroke-opacity="0.07"/>
<g clip-path="url(#clip0_5121_51714)">
<path d="M43.2889 20.9441C43.2889 25.7526 38.8793 31.4358 32.2162 31.4358H25.7984L25.4869 33.4385L23.9858 43.0928H16L20.8014 12.0068H33.7358C38.0885 12.0068 41.5175 14.4552 42.774 17.8576C43.1296 18.821 43.3109 19.8611 43.2896 20.9441H43.2889Z" fill="#002991"/>
<path d="M47.8945 29.8816C47.0127 35.2732 42.4076 39.2071 36.969 39.2071H32.5103L30.6486 50.8642H22.7119L23.9855 43.0931L25.4867 33.4388L25.7981 31.436H32.2159C38.879 31.436 43.2886 25.7529 43.2886 20.9443C43.29 20.9443 43.2915 20.9458 43.2929 20.9465C46.5725 22.6477 48.4812 26.0945 47.8938 29.8816H47.8945Z" fill="#60CDFF"/>
<path d="M43.2881 20.9436C43.2881 20.9436 43.2909 20.9451 43.2924 20.9458C43.2909 20.8634 43.2902 20.8584 43.2881 20.9436Z" fill="black"/>
<path d="M43.2903 20.9428C41.9186 20.2143 40.2524 19.7773 38.4398 19.7773H27.6124L25.7998 31.4344H32.2176C38.8807 31.4344 43.2903 25.7513 43.2903 20.9428Z" fill="#008CFF"/>
</g>
<defs>
<clipPath id="clip0_5121_51714">
<rect width="32" height="38.8571" fill="white" transform="translate(16 12)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1,15 +1,13 @@
<svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="0.000183105" width="58" height="58" rx="3" fill="white"/>
<rect x="0.5" y="0.500183" width="57" height="57" rx="2.5" stroke="black" stroke-opacity="0.07"/>
<g clip-path="url(#clip0_3277_11734)">
<path d="M40.2889 18.9445C40.2889 23.7531 35.8793 29.4362 29.2162 29.4362H22.7984L22.4869 31.439L20.9858 41.0933H13L17.8014 10.0073H30.7358C35.0885 10.0073 38.5175 12.4556 39.774 15.858C40.1296 16.8214 40.3109 17.8615 40.2896 18.9445H40.2889Z" fill="#002991"/>
<path d="M44.8945 27.882C44.0127 33.2736 39.4076 37.2076 33.969 37.2076H29.5103L27.6486 48.8646H19.7119L20.9855 41.0935L22.4867 31.4392L22.7981 29.4364H29.2159C35.879 29.4364 40.2886 23.7533 40.2886 18.9448C40.29 18.9448 40.2915 18.9462 40.2929 18.9469C43.5725 20.6481 45.4812 24.095 44.8938 27.882H44.8945Z" fill="#60CDFF"/>
<path d="M40.2881 18.9441C40.2881 18.9441 40.2909 18.9455 40.2924 18.9462C40.2909 18.8638 40.2902 18.8588 40.2881 18.9441Z" fill="black"/>
<path d="M40.2903 18.9432C38.9186 18.2147 37.2524 17.7778 35.4398 17.7778H24.6124L22.7998 29.4348H29.2176C35.8807 29.4348 40.2903 23.7517 40.2903 18.9432Z" fill="#008CFF"/>
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5121_50796)">
<path d="M22.0767 8.52009C22.0767 11.4901 19.3125 15.0002 15.1356 15.0002H11.1125L10.9172 16.2372L9.97623 22.2002H4.97021L7.98006 3H16.0882C18.8168 3 20.9663 4.51219 21.7539 6.61369C21.9768 7.20875 22.0905 7.85114 22.0771 8.52009H22.0767Z" fill="#002991"/>
<path d="M24.9631 14.0401C24.4103 17.3702 21.5235 19.8 18.1143 19.8H15.3193L14.1523 26.9999H9.177L9.97538 22.2001L10.9164 16.2372L11.1116 15.0002H15.1347C19.3116 15.0002 22.0758 11.49 22.0758 8.52002C22.0767 8.52002 22.0776 8.5209 22.0785 8.52135C24.1344 9.5721 25.3309 11.701 24.9626 14.0401H24.9631Z" fill="#60CDFF"/>
<path d="M22.0769 8.52011C22.0769 8.52011 22.0787 8.521 22.0796 8.52144C22.0787 8.47056 22.0782 8.46746 22.0769 8.52011Z" fill="black"/>
<path d="M22.076 8.52011C21.2161 8.07017 20.1717 7.80029 19.0354 7.80029H12.2481L11.1118 15.0002H15.1349C19.3118 15.0002 22.076 11.4901 22.076 8.52011Z" fill="#008CFF"/>
</g>
<defs>
<clipPath id="clip0_3277_11734">
<rect width="32" height="38.8571" fill="white" transform="translate(13 10.0003)"/>
<clipPath id="clip0_5121_50796">
<rect width="20.0597" height="24" fill="white" transform="translate(4.97021 3)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 568 B

After

Width:  |  Height:  |  Size: 32 KiB

Before After
Before After

View file

@ -1,3 +1,4 @@
<svg width="38" height="24" viewBox="0 0 38 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="38" height="24" rx="2" fill="white"/>
<rect x="0.5" y="0.5" width="37" height="23" rx="1.5" stroke="black" stroke-opacity="0.07"/>

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Before After
Before After

View file

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.5001 4.5H12.5001V6H16.9394L10.9697 11.9697L12.0304 13.0303L18.0001 7.06066V11.5H19.5001V4.5Z" fill="white"/>
<path d="M6.5 5.5C5.39543 5.5 4.5 6.39543 4.5 7.5V17.5C4.5 18.6046 5.39543 19.5 6.5 19.5H16.5C17.6046 19.5 18.5 18.6046 18.5 17.5V14.5H17V17.5C17 17.7761 16.7761 18 16.5 18H6.5C6.22386 18 6 17.7761 6 17.5V7.5C6 7.22386 6.22386 7 6.5 7H9.5V5.5H6.5Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 489 B

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

View file

@ -0,0 +1,6 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 10.6667C8 9.19391 9.19391 8 10.6667 8H45.3333C46.8061 8 48 9.19391 48 10.6667V45.3333C48 46.8061 46.8061 48 45.3333 48H10.6667C9.19391 48 8 46.8061 8 45.3333V10.6667Z" fill="#5BBBFC"/>
<path d="M16 18.6667C16 17.1939 17.1939 16 18.6667 16H53.3333C54.8061 16 56 17.1939 56 18.6667V53.3333C56 54.8061 54.8061 56 53.3333 56H18.6667C17.1939 56 16 54.8061 16 53.3333V18.6667Z" fill="#FFD140"/>
<path d="M48 16V45.3333C48 46.8061 46.8061 48 45.3333 48H16V18.6667C16 17.1939 17.1939 16 18.6667 16H48Z" fill="#FAF8F5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M35.5851 24.5273V22.5185C35.586 22.3632 35.5558 22.2092 35.4963 22.0655C35.4367 21.9217 35.349 21.791 35.2382 21.6809C35.1273 21.5707 34.9955 21.4833 34.8504 21.4237C34.7052 21.364 34.5496 21.3333 34.3924 21.3333H29.6078C29.4506 21.3333 29.2949 21.364 29.1498 21.4237C29.0046 21.4833 28.8728 21.5707 28.762 21.6809C28.6512 21.791 28.5634 21.9217 28.5039 22.0655C28.4444 22.2092 28.4142 22.3632 28.4151 22.5185V24.5273C28.0087 24.7187 27.6188 24.9427 27.2494 25.1969L25.5145 24.2126C25.2446 24.0621 24.9261 24.0213 24.6263 24.0988C24.3265 24.1762 24.069 24.366 23.9083 24.6277L21.5161 28.719C21.3606 28.9913 21.3197 29.3131 21.4021 29.6149C21.4845 29.9168 21.6836 30.1746 21.9566 30.3327L23.6644 31.3237C23.6644 31.5447 23.6644 31.7656 23.6644 31.9933C23.6644 32.221 23.6644 32.4419 23.6644 32.6629L21.9295 33.6472C21.6565 33.8054 21.4574 34.0631 21.375 34.365C21.2926 34.6669 21.3335 34.9886 21.489 35.2609L23.9083 39.3589C23.9869 39.4932 24.0915 39.6108 24.2162 39.7051C24.3409 39.7994 24.4832 39.8684 24.6349 39.9082C24.7867 39.948 24.9449 39.9578 25.1005 39.9371C25.256 39.9164 25.406 39.8655 25.5416 39.7874L27.2765 38.8031C27.6459 39.0573 28.0358 39.2813 28.4422 39.4727V41.4815C28.4413 41.6368 28.4715 41.7908 28.531 41.9345C28.5905 42.0783 28.6783 42.209 28.7891 42.3191C28.9 42.4293 29.0317 42.5167 29.1769 42.5763C29.3221 42.636 29.4777 42.6667 29.6349 42.6667H34.4195C34.5767 42.6667 34.7323 42.636 34.8775 42.5763C35.0227 42.5167 35.1544 42.4293 35.2653 42.3191C35.3761 42.209 35.4639 42.0783 35.5234 41.9345C35.5829 41.7908 35.6131 41.6368 35.6122 41.4815V39.4727C36.0186 39.2813 36.4085 39.0573 36.7779 38.8031L38.5128 39.7874C38.7843 39.9367 39.104 39.9755 39.404 39.8955C39.7039 39.8155 39.9605 39.6229 40.1189 39.3589L42.5044 35.2676C42.5835 35.1336 42.635 34.9855 42.656 34.8317C42.6769 34.678 42.667 34.5217 42.6267 34.3718C42.5864 34.2219 42.5166 34.0813 42.4212 33.9581C42.3257 33.8349 42.2066 33.7315 42.0707 33.6539L40.3358 32.6629C40.3358 32.4419 40.3358 32.2143 40.3358 31.9933C40.3358 31.7723 40.3358 31.5447 40.3358 31.3237L42.0707 30.3394C42.2066 30.2618 42.3257 30.1584 42.4212 30.0352C42.5166 29.912 42.5864 29.7714 42.6267 29.6215C42.667 29.4716 42.6769 29.3153 42.656 29.1616C42.635 29.0078 42.5835 28.8597 42.5044 28.7257L40.1189 24.6277C40.0404 24.4934 39.9358 24.3758 39.8111 24.2815C39.6864 24.1872 39.5441 24.1182 39.3924 24.0784C39.2406 24.0386 39.0824 24.0288 38.9268 24.0495C38.7712 24.0702 38.6213 24.1211 38.4857 24.1992L36.7847 25.1902C36.4042 24.9367 36.0029 24.7149 35.5851 24.5273ZM37.1235 28.9533C36.5931 28.0797 35.8427 27.3566 34.9454 26.8546C34.0482 26.3526 33.0346 26.0887 32.0035 26.0887C30.9723 26.0887 29.9588 26.3526 29.0615 26.8546C28.1643 27.3566 27.4139 28.0797 26.8835 28.9533L26.7615 29.1542C26.2744 30.0267 26.0189 31.0069 26.0189 32.0033C26.0189 32.9998 26.2744 33.98 26.7615 34.8525L26.8699 35.04C27.4012 35.9135 28.1523 36.6364 29.0501 37.1382C29.9479 37.6401 30.9618 37.9038 31.9933 37.9038C33.0248 37.9038 34.0388 37.6401 34.9366 37.1382C35.8344 36.6364 36.5854 35.9135 37.1167 35.04L37.2252 34.8525C37.7098 33.978 37.9638 32.997 37.9638 32C37.9638 31.003 37.7098 30.022 37.2252 29.1475L37.1235 28.9533Z" fill="#001C64"/>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -0,0 +1,6 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 10.6669C8 9.19415 9.19391 8.00024 10.6667 8.00024H45.3333C46.8061 8.00024 48 9.19415 48 10.6669V45.3336C48 46.8063 46.8061 48.0002 45.3333 48.0002H10.6667C9.19391 48.0002 8 46.8063 8 45.3336V10.6669Z" fill="#C6C6C6"/>
<path d="M16 18.6669C16 17.1942 17.1939 16.0002 18.6667 16.0002H53.3333C54.8061 16.0002 56 17.1942 56 18.6669V53.3336C56 54.8063 54.8061 56.0002 53.3333 56.0002H18.6667C17.1939 56.0002 16 54.8063 16 53.3336V18.6669Z" fill="#5BBBFC"/>
<path d="M48 16.0002V45.3336C48 46.8063 46.8061 48.0002 45.3333 48.0002H16V18.6669C16 17.1942 17.1939 16.0002 18.6667 16.0002H48Z" fill="#FAF8F5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M35.2518 24.1945V22.1857C35.2527 22.0304 35.2225 21.8765 35.163 21.7327C35.1034 21.5889 35.0157 21.4582 34.9048 21.3481C34.794 21.238 34.6622 21.1506 34.5171 21.0909C34.3719 21.0313 34.2162 21.0006 34.059 21.0006H29.2745C29.1173 21.0006 28.9616 21.0313 28.8165 21.0909C28.6713 21.1506 28.5395 21.238 28.4287 21.3481C28.3178 21.4582 28.2301 21.5889 28.1706 21.7327C28.111 21.8765 28.0808 22.0304 28.0817 22.1857V24.1945C27.6753 24.386 27.2855 24.6099 26.9161 24.8641L25.1812 23.8798C24.9113 23.7293 24.5928 23.6885 24.293 23.766C23.9932 23.8435 23.7357 24.0332 23.575 24.295L21.1827 28.3862C21.0273 28.6585 20.9863 28.9803 21.0687 29.2821C21.1511 29.584 21.3503 29.8418 21.6232 29.9999L23.331 30.9909C23.331 31.2119 23.331 31.4329 23.331 31.6605C23.331 31.8882 23.331 32.1092 23.331 32.3301L21.5961 33.3144C21.3232 33.4726 21.124 33.7304 21.0416 34.0322C20.9592 34.3341 21.0002 34.6559 21.1556 34.9282L23.575 39.0261C23.6536 39.1604 23.7582 39.2781 23.8829 39.3723C24.0076 39.4666 24.1498 39.5356 24.3016 39.5754C24.4533 39.6152 24.6115 39.6251 24.7671 39.6043C24.9227 39.5836 25.0726 39.5327 25.2083 39.4546L26.9432 38.4703C27.3126 38.7246 27.7024 38.9485 28.1088 39.1399V41.1487C28.1079 41.304 28.1381 41.458 28.1977 41.6018C28.2572 41.7455 28.3449 41.8762 28.4558 41.9864C28.5666 42.0965 28.6984 42.1839 28.8436 42.2436C28.9887 42.3032 29.1444 42.3339 29.3016 42.3339H34.0861C34.2434 42.3339 34.399 42.3032 34.5442 42.2436C34.6893 42.1839 34.8211 42.0965 34.932 41.9864C35.0428 41.8762 35.1305 41.7455 35.1901 41.6018C35.2496 41.458 35.2798 41.304 35.2789 41.1487V39.1399C35.6853 38.9485 36.0751 38.7246 36.4445 38.4703L38.1795 39.4546C38.4509 39.604 38.7707 39.6428 39.0706 39.5627C39.3706 39.4827 39.6271 39.2902 39.7856 39.0261L42.1711 34.9349C42.2502 34.8008 42.3017 34.6527 42.3226 34.499C42.3436 34.3452 42.3337 34.1889 42.2934 34.039C42.2531 33.8891 42.1832 33.7485 42.0878 33.6253C41.9924 33.5021 41.8733 33.3987 41.7374 33.3211L40.0025 32.3301C40.0025 32.1092 40.0025 31.8815 40.0025 31.6605C40.0025 31.4396 40.0025 31.2119 40.0025 30.9909L41.7374 30.0066C41.8733 29.929 41.9924 29.8256 42.0878 29.7025C42.1832 29.5793 42.2531 29.4387 42.2934 29.2888C42.3337 29.1388 42.3436 28.9825 42.3226 28.8288C42.3017 28.6751 42.2502 28.5269 42.1711 28.3929L39.7856 24.295C39.707 24.1607 39.6024 24.043 39.4778 23.9487C39.3531 23.8545 39.2108 23.7854 39.059 23.7456C38.9073 23.7058 38.7491 23.696 38.5935 23.7167C38.4379 23.7375 38.288 23.7883 38.1523 23.8664L36.4513 24.8574C36.0708 24.6039 35.6696 24.3822 35.2518 24.1945ZM36.7902 28.6206C36.2598 27.7469 35.5094 27.0238 34.6121 26.5218C33.7148 26.0198 32.7013 25.756 31.6701 25.756C30.639 25.756 29.6255 26.0198 28.7282 26.5218C27.8309 27.0238 27.0805 27.7469 26.5501 28.6206L26.4281 28.8214C25.9411 29.694 25.6856 30.6741 25.6856 31.6706C25.6856 32.667 25.9411 33.6472 26.4281 34.5197L26.5366 34.7072C27.0679 35.5808 27.8189 36.3036 28.7167 36.8055C29.6145 37.3073 30.6285 37.5711 31.66 37.5711C32.6915 37.5711 33.7054 37.3073 34.6032 36.8055C35.501 36.3036 36.2521 35.5808 36.7834 34.7072L36.8918 34.5197C37.3765 33.6452 37.6305 32.6642 37.6305 31.6672C37.6305 30.6702 37.3765 29.6892 36.8918 28.8148L36.7902 28.6206Z" fill="#001C64"/>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -0,0 +1,3 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.9557 4.836C10.9945 4.6025 11.1149 4.39034 11.2954 4.23726C11.476 4.08418 11.705 4.00011 11.9417 4H13.2477C13.4846 3.99987 13.7138 4.08384 13.8945 4.23694C14.0753 4.39004 14.1958 4.60233 14.2347 4.836L14.4787 6.302C15.2657 6.562 15.9817 6.981 16.5867 7.52L17.9797 6.998C18.2013 6.91523 18.4451 6.91372 18.6677 6.99373C18.8904 7.07374 19.0774 7.23009 19.1957 7.435L19.8487 8.565C19.9673 8.77005 20.0094 9.01056 19.9672 9.2437C19.9251 9.47683 19.8016 9.68744 19.6187 9.838L18.4707 10.782C18.6364 11.5852 18.6364 12.4138 18.4707 13.217L19.6197 14.163C19.8023 14.3135 19.9257 14.5239 19.9678 14.7568C20.0099 14.9898 19.9681 15.2301 19.8497 15.435L19.1967 16.565C19.0784 16.7699 18.8914 16.9263 18.6687 17.0063C18.4461 17.0863 18.2023 17.0848 17.9807 17.002L16.5867 16.48C15.9817 17.02 15.2667 17.438 14.4787 17.698L14.2347 19.164C14.1958 19.3977 14.0753 19.61 13.8945 19.7631C13.7138 19.9162 13.4846 20.0001 13.2477 20H11.9417C11.705 19.9999 11.476 19.9158 11.2954 19.7627C11.1149 19.6097 10.9945 19.3975 10.9557 19.164L10.7117 17.698C9.9331 17.4405 9.21564 17.0259 8.60367 16.48L7.20967 17.002C6.98803 17.0851 6.74409 17.0869 6.52125 17.0071C6.2984 16.9273 6.11111 16.771 5.99267 16.566L5.33967 15.435C5.22128 15.2301 5.17941 14.9898 5.22153 14.7568C5.26364 14.5239 5.387 14.3135 5.56967 14.163L6.71867 13.217C6.55297 12.4138 6.55297 11.5852 6.71867 10.782L5.57067 9.838C5.388 9.68749 5.26464 9.47707 5.22253 9.24416C5.18041 9.01125 5.22228 8.77095 5.34067 8.566L5.99367 7.435C6.11195 7.22986 6.29918 7.07335 6.52204 6.99333C6.7449 6.91331 6.98892 6.91496 7.21067 6.998L8.60367 7.52C9.21562 6.97407 9.93308 6.55952 10.7117 6.302L10.9557 4.836ZM15.5957 12C15.5957 12.394 15.5181 12.7841 15.3673 13.1481C15.2165 13.512 14.9956 13.8427 14.717 14.1213C14.4384 14.3999 14.1077 14.6209 13.7437 14.7716C13.3797 14.9224 12.9896 15 12.5957 15C12.2017 15 11.8116 14.9224 11.4476 14.7716C11.0836 14.6209 10.7529 14.3999 10.4744 14.1213C10.1958 13.8427 9.9748 13.512 9.82403 13.1481C9.67327 12.7841 9.59567 12.394 9.59567 12C9.59567 11.2044 9.91174 10.4413 10.4744 9.87868C11.037 9.31607 11.8 9 12.5957 9C13.3913 9 14.1544 9.31607 14.717 9.87868C15.2796 10.4413 15.5957 11.2044 15.5957 12Z" fill="#505050"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -13,6 +13,7 @@
"@wordpress/scripts": "^30.3.0"
},
"dependencies": {
"@woocommerce/settings": "^1.0.0"
"@woocommerce/settings": "^1.0.0",
"react-select": "^5.8.3"
}
}

View file

@ -40,10 +40,17 @@
font-style: italic;
}
@mixin primaryFont{
font-family: "PayPalPro", sans-serif;
}
* {
box-sizing: border-box;
}
& {
font-family: "PayPalPro", sans-serif;
-webkit-font-smoothing: antialiased;
box-sizing: border-box;
color: $color-gray-700;
}

View file

@ -7,7 +7,7 @@
}
}
@mixin hide-input-field(){
@mixin hide-input-field() {
width: 100%;
height: 100%;
position: absolute;
@ -18,7 +18,7 @@
border-radius: 0;
}
@mixin fake-input-field($border-radius:0){
@mixin fake-input-field($border-radius:0) {
width: 20px;
height: 20px;
border: 1px solid $color-gray-600;
@ -26,3 +26,20 @@
pointer-events: none;
border-radius: $border-radius;
}
@mixin small-button {
border-radius: 2px;
padding: 6px 12px;
@include font(13, 20, 400);
}
@mixin vertical-layout-event-gap($gap:0) {
display: flex;
flex-direction: column;
gap: $gap;
}
@mixin horizontal-layout-even-gap($gap:0) {
display: flex;
gap: $gap;
}

View file

@ -6,11 +6,14 @@ $color-gray-900: #1E1E1E;
$color-gray-800: #2F2F2F;
$color-gray-700: #757575;
$color-gray-600: #949494;
$color-gray-500: #2c2c2c;
$color-gray-500: #BBBBBB;
$color-gray-400: #CCCCCC;
$color-gray-300: #EBEBEB;
$color-gray-200: #E0E0E0;
$color-gray: #646970;
$color-text-tertiary: #505050;
$color-text-text: #070707;
$color-border:#AEAEAE;
$shadow-card: 0 3px 6px 0 rgba(0, 0, 0, 0.15);
$shadow-selection-box: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
@ -18,7 +21,7 @@ $color-gradient-dark: #001435;
$gradient-header: linear-gradient(87.03deg, #003087 -0.49%, #001E51 29.22%, $color-gradient-dark 100%);
$max-width-onboarding: 1024px;
$max-width-onboarding-content: 662px;
$max-width-onboarding-content: 500px;
$max-width-settings: 938px;
#ppcp-settings-container {

View file

@ -5,29 +5,30 @@ button.components-button, a.components-button {
}
&:disabled {
background-color: $color-gray-500;
color: $color-white;
}
border-radius: 2px;
padding: 14px 17px;
height: auto;
}
&.is-primary {
@include font(13, 20, 400);
color:$color-white;
&:not(:disabled) {
background-color: $color-blueberry;
}
}
&.is-secondary:not(:disabled) {
border-color:$color-blueberry;
background-color:$color-white;
color:$color-blueberry;
&:hover{
background-color:$color-white;
background:none;
border-color: $color-blueberry;
background-color: $color-white;
color: $color-blueberry;
&:hover {
background-color: $color-white;
background: none;
}
}

View file

@ -3,12 +3,20 @@
&__radio-value {
@include hide-input-field;
&:checked {
+ .ppcp-r__radio-presentation {
background: $color-white;
width: 20px;
height: 20px;
border: 6px solid $color-blueberry;
&:checked + .ppcp-r__radio-presentation {
position: relative;
&::before {
content: '';
width: 12px;
height: 12px;
border-radius: 12px;
background-color: $color-blueberry;
display: block;
position: absolute;
transform: translate(-50%, -50%);
left: 50%;
top: 50%;
}
}
}
@ -40,4 +48,71 @@
&__checkbox-presentation {
@include fake-input-field(2px);
}
&__radio-wrapper {
display: flex;
gap: 18px;
align-items: center;
position: relative;
label {
@include font(14, 20, 400);
color: $color-gray-800;
}
}
&__radio-description {
@include font(14, 20, 400);
margin: 0;
color: $color-gray-800;
}
&__radio-content-additional {
padding-left: 38px;
padding-top: 18px;
}
}
.components-base-control {
&__label {
color: $color-gray-900;
@include font(13, 16, 600);
text-transform: none;
}
&__input {
border: 1px solid $color-gray-700;
border-radius: 2px;
box-shadow: none;
&:focus {
border-color: $color-blueberry;
}
}
}
input[type='text'] {
padding: 7px 11px;
@include font(14, 20, 400);
@include primaryFont;
border-radius: 2px;
}
select {
padding: 7px 27px 7px 11px;
@include font(14, 20, 400);
}
.components-form-toggle.is-checked > .components-form-toggle__track {
background-color: $color-blueberry;
}
.ppcp-r-vertical-text-control {
.components-base-control__field {
display: flex;
flex-direction: column;
gap: 0;
margin: 0;
}
}

View file

@ -1,5 +1,5 @@
.ppcp-r-onboarding-header{
margin: 0 0 32px 0;
margin: 0 0 24px 0;
&__logo {
text-align: center;
@ -29,7 +29,7 @@
&__description {
@include font(14, 22, 400);
margin: 0;
margin: 0 20%;
text-align: center;
}
}

View file

@ -1,48 +1,75 @@
.ppcp-r-payment-method-item-list {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.ppcp-r-payment-method-item {
display: flex;
align-items: center;
align-items: flex-start;
width: calc(100% / 3 - 32px / 3);
border: 1px solid $color-gray-300;
padding: 16px;
border-radius: 8px;
min-height: 200px;
&:not(:last-child) {
padding-bottom: 32px;
@media screen and (max-width: 767px) {
width: calc(50% - 8px);
}
&:not(:first-child) {
border-top: 1px solid $color-gray-400;
padding-top: 32px;
@media screen and (max-width: 480px) {
width: 100%;
}
&__checkbox-wrap {
position: relative;
&__wrap {
display: flex;
flex-direction: column;
height: 100%;
}
&__title-wrap {
display: flex;
align-items: center;
.ppcp-r__checkbox {
margin-right: 24px;
}
}
&__icon-wrap {
margin-right: 18px;
margin: 0 0 8px 0;
gap: 12px;
}
&__content {
padding-right: 24px;
p {
margin: 0;
color: $color-text-tertiary;
@include font(13, 20, 400);
}
margin: 0 0 12px 0;
}
&__title {
@include font(16, 20, 600);
@include font(13, 20, 500);
color: $color-black;
margin: 0 0 8px 0;
display: block;
}
button {
margin-left: auto;
@include font(13, 20, 400);
padding: 6px 12px !important;
&__settings-button {
line-height: 0;
transition: 0.2s ease-out transform;
transform: rotate(0deg);
zoom: 1.005;
&:hover {
transform: rotate(45deg);
cursor: pointer;
}
}
button.is-secondary {
@include small-button;
}
&__footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: auto;
}
}

View file

@ -3,18 +3,6 @@
@import './button';
@import './fields';
&__container {
max-width: 100%;
width: 400px;
&--small {
padding: 0 50px;
@media screen and (max-width: 480px){
padding:0;
}
}
}
.components-modal {
&__header {
height: 52px;
@ -26,8 +14,23 @@
}
&__content {
margin-top: 60px;
padding:0 24px 28px 24px;
}
}
&__container {
width: 352px;
max-width: 100%;
}
.has-size-small {
.ppcp-r-modal__container {
max-width: 252px;
}
.components-modal__content {
margin-top: 48px;
padding: 0 50px 48px 50px;
}
}
@ -37,23 +40,30 @@
align-items: center;
gap: 8px;
border-bottom: 1px solid $color-gray-500;
padding-bottom: 12px;
margin-bottom: 24px;
padding-bottom: 18px;
margin-bottom: 18px;
}
&__title {
@include font(16, 20, 600);
@include font(14, 20, 600);
color: $color-black;
}
&__content {
width: 400px;
max-width: 100%;
padding: 0;
}
&__inverted-toggle-control {
.components-form-toggle {
order: 1;
.components-toggle-control {
gap: 8px;
margin: 0;
label {
@include font(13, 20, 400);
color: $color-text-text;
}
.components-form-toggle__track {
border-color: $color-border;
}
}
@ -63,22 +73,15 @@
gap: 8px;
&--save {
margin-top: 24px;
margin-top: -4px;
align-items: flex-end;
}
input[type='text'] {
padding: 7px 11px;
@include font(14, 20, 400);
margin: 0;
border-color: $color-gray-700;
}
label {
@include font(14, 16, 400);
color: $color-gray-900;
}
}
&__field-rows {
@ -88,17 +91,28 @@
&--acdc {
gap: 18px;
.ppcp-r-modal__field-row--save {
margin-top: 2px;
}
}
}
.ppcp-r-modal__field-row--save button.is-primary {
border-radius: 2px;
@include small-button;
width: 100%;
justify-content: center;
padding: 6px 12px;
@include font(13, 20, 400);
}
.ppcp-r__radio-content {
label {
@include font(13, 20, 400);
}
}
&__content-title {
@include font(14, 20, 600);
@include font(13, 20, 600);
color: $color-black;
display: block;
margin: 0 0 4px 0;
@ -106,18 +120,7 @@
&__description {
@include font(14, 20, 400);
margin: 0 0 24px 0;
color: $color-black;
}
&__field-rdb {
display: flex;
gap: 18px;
align-items: center;
position: relative;
label {
@include font(14, 20, 400);
color: $color-black;
}
margin: 0 0 12px 0;
}
}

View file

@ -8,14 +8,12 @@
.ppcp-r-select-box {
position: relative;
width: 100%;
border: 1px solid $color-gray-500;
border: 1px solid $color-gray-200;
outline: 1px solid transparent;
border-radius: 8px;
border-radius: 4px;
display: flex;
gap: 32px;
align-items: center;
box-sizing: border-box;
padding: 28px 16px 28px 32px;
gap: 16px;
padding: 24px 16px 24px 16px;
&.selected {
@ -59,20 +57,23 @@
&__content {
display: flex;
gap: 18px;
}
&__title {
@include font(16, 24, 600);
color: $color-blueberry;
@include font(14, 20, 700);
color: $color-black;
margin: 0 0 4px 0;
display: block;
}
&__description {
@include font(14, 20, 400);
color: $color-gray-800;
margin: 0 0 18px 0;
@include font(13, 20, 400);
color: $color-gray-700;
margin:0;
&:not(:last-child){
margin-block-end:18px;
}
}
&__radio-presentation {

View file

@ -9,7 +9,7 @@
margin-left: auto;
margin-right: auto;
padding: 0 16px 48px;
box-sizing: border-box;
box-sizing: content-box;
@media screen and (max-width: 480px) {
padding-bottom: 36px;
@ -17,21 +17,35 @@
}
&-settings-card {
background-color: $color-white;
padding: 48px;
border-radius: 8px;
box-shadow: 0 2px 4px 0 #0000001A;
@media screen and (min-width: 960px) {
display: flex;
gap: 48px;
}
@media screen and (max-width: 480px) {
padding: 24px;
}
&__header {
display: flex;
align-items: center;
gap: 18px;
padding-bottom: 18px;
border-bottom: 2px solid $color-gray-700;
margin-bottom: 32px;
@media screen and (min-width: 960px) {
width: 280px;
flex-shrink: 0;
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
}
&__content {
@media screen and (min-width: 960px) {
flex: 1;
}
}
&__title {

View file

@ -10,7 +10,7 @@
box-shadow: 0 -1px 0 0 $color-gray-400 inset;
margin-bottom: 48px;
gap: 12px;
overflow: auto;
.components-button {
padding: 16px 20px;

View file

@ -1,17 +0,0 @@
.components-base-control {
&__label {
color: $color-gray-900;
@include font(13, 16, 600);
margin: 0 0 8px 0;
text-transform: none;
}
&__input {
border: 1px solid $color-gray-700;
border-radius: 2px;
box-shadow: none;
&:focus{
border-color:$color-blueberry;
}
}
}

View file

@ -1,7 +1,7 @@
body:has(.ppcp-r-container--onboarding) {
background-color: #fff !important;
.notice, .nav-tab-wrapper.woo-nav-tab-wrapper, .woocommerce-layout, .wrap.woocommerce form > h2 {
.notice, .nav-tab-wrapper.woo-nav-tab-wrapper, .woocommerce-layout, .wrap.woocommerce form > h2, #screen-meta-links {
display: none !important;
}
}

View file

@ -0,0 +1,7 @@
body:has(.ppcp-r-container--settings) {
background-color: #fff !important;
.notice, .nav-tab-wrapper.woo-nav-tab-wrapper, .woocommerce-layout, .wrap.woocommerce form > h2, #screen-meta-links {
display: none !important;
}
}

View file

@ -0,0 +1,8 @@
.ppcp-r-tabs.settings,
.ppcp-r-container--settings {
--max-container-width: var(--max-width-settings);
.ppcp-r-inner-container {
max-width: var(--max-container-width);
}
}

View file

@ -8,8 +8,7 @@
display: flex;
align-items: center;
gap: 4px;
color: $color-gray-700;
@include font(14, 20, 400);
@include font(13, 20, 400);
margin: 0;
&::before {
@ -29,7 +28,7 @@
.ppcp-r-select-box__additional-content {
a {
@include font(12, 20, 400);
@include font(13, 20, 500);
color: $color-blueberry;
}
}

View file

@ -32,8 +32,7 @@
margin: 0 0 24px 0;
}
.ppcp-r-toggle-block__toggled-content > button{
padding: 6px 12px;
@include font(13, 20, 500);
@include small-button;
color: $color-white;
border: none;
}

View file

@ -1,4 +1,4 @@
.ppcp-r-tab-dashboard-todo {
.ppcp-r-tab-overview-todo {
margin: 0 0 48px 0;
}
@ -86,18 +86,13 @@
margin: 0 0 8px 0;
strong {
@include font(14, 20, 700);
@include font(14, 24, 700);
color: $color-black;
}
}
&__show-all-data {
margin-left: 12px;
&:hover {
cursor: pointer;
opacity: 0.8;
}
}
&__status-label {
@ -113,21 +108,37 @@
gap: 12px;
}
&__status-toggle--toggled{
.ppcp-r-connection-status__show-all-data{
transform:rotate(180deg);
}
}
&__status-row {
display: flex;
align-items: center;
*{
user-select: none;
}
strong {
@include font(14, 20, 600);
@include font(14, 24, 600);
color: $color-gray-800;
margin-right: 12px;
white-space: nowrap;
}
span {
@include font(14, 20, 400);
span:not(.ppcp-r-connection-status__status-toggle) {
@include font(14, 24, 400);
color: $color-gray-800;
}
.ppcp-r-connection-status__status-toggle{
line-height: 0;
}
&--first{
&:hover{
cursor: pointer;
}
}
}
@media screen and (max-width: 767px) {

View file

@ -0,0 +1,312 @@
// Global settings styles
.ppcp-r-settings {
@include vertical-layout-event-gap(48px);
}
.ppcp-r-settings-card__content {
> .ppcp-r-settings-block {
&:not(:last-child) {
border-bottom: 1.5px solid $color-gray-700;
}
}
}
.ppcp-r-settings-block {
.ppcp-r-settings-block__header {
display: flex;
gap: 48px;
&-inner {
display: flex;
flex-direction: column;
gap: 4px;
}
}
&__action {
margin-left: auto;
}
&--primary {
> .ppcp-r-settings-block__header {
.ppcp-r-settings-block__title {
@include font(16, 20, 700);
color: $color-black;
}
}
}
&--secondary {
> .ppcp-r-settings-block__header {
.ppcp-r-settings-block__title {
@include font(16, 20, 600);
color: $color-gray-800;
}
}
}
&--tertiary {
padding-bottom: 0;
margin-bottom: 24px;
> .ppcp-r-settings-block__header {
align-items: center;
.ppcp-r-settings-block__title {
color: $color-gray-800;
@include font(14, 20, 400);
}
}
}
.ppcp-r-settings-block__description {
margin: 0;
@include font(14, 20, 400);
color: $color-gray-800;
a {
color: $color-blueberry;
}
strong {
color: $color-gray-800;
}
}
// Types
&--toggle-content {
&.ppcp-r-settings-block--content-visible {
.ppcp-r-settings-block__toggle-content {
transform: rotate(180deg);
}
}
.ppcp-r-settings-block__header {
user-select: none;
&:hover {
cursor: pointer;
}
}
}
&--sandbox-connected {
.ppcp-r-settings-block__content {
margin-top: 24px;
}
button.is-secondary {
@include small-button;
}
.ppcp-r-connection-status__data {
margin-bottom: 20px;
}
}
&--expert-rdb{
@include vertical-layout-event-gap(24px);
}
&--connect-sandbox {
button.components-button {
@include small-button;
}
.ppcp-r__radio-content-additional {
.ppcp-r-vertical-text-control {
width: 100%;
}
@include vertical-layout-event-gap(24px);
align-items: flex-start;
input[type='text'] {
width: 100%;
}
}
}
&--troubleshooting {
> .ppcp-r-settings-block__content > *:not(:last-child) {
padding-bottom: 32px;
margin-bottom: 32px;
border-bottom: 1px solid $color-gray-500;
}
}
&--settings{
> .ppcp-r-settings-block__content > *:not(:last-child){
padding-bottom: 32px;
margin-bottom: 32px;
border-bottom: 1px solid $color-gray-500;
}
}
// Fields
input[type='text'] {
border-color: $color-gray-700;
width: 282px;
max-width: 100%;
color: $color-gray-800;
}
input[type='text'] {
&::placeholder {
color: $color-gray-700;
}
}
.ppcp-r {
&__radio-wrapper {
align-items: flex-start;
gap: 12px;
}
&__radio-content {
display: flex;
flex-direction: column;
gap: 4px;
label {
font-weight: 600;
}
}
&__radio-content-additional {
padding-left: 32px;
}
}
// MultiSelect control
.ppcp-r {
&__control {
border-radius: 2px;
border-color: $color-gray-700;
width: 282px;
min-height: auto;
padding: 0;
}
&__input-container {
padding: 0;
margin: 0;
}
&__value-container {
padding: 0 0 0 7px;
}
&__indicator {
padding: 5px;
}
&__indicator-separator {
display: none;
}
&__value-container--has-value {
.ppcp-r__single-value {
color: $color-gray-800;
}
}
&__placeholde, &__single-value {
@include font(13, 20, 400);
}
&__option {
&--is-selected {
background-color: $color-gray-200;
}
}
}
}
// Special settings styles
// Hooks table
.ppcp-r-table {
&__hooks-url {
width: 70%;
padding-right: 20%;
text-align: left;
vertical-align: top;
}
&__hooks-events {
vertical-align: top;
text-align: left;
width: 40%;
span {
display: block;
}
}
td.ppcp-r-table__hooks-url, td.ppcp-r-table__hooks-events {
padding-top: 12px;
color: $color-gray-800;
@include font(14, 20, 400);
span {
color: inherit;
@include font(14, 20, 400);
}
}
th.ppcp-r-table__hooks-url, th.ppcp-r-table__hooks-events {
@include font(14, 20, 700);
color: $color-gray-800;
border-bottom: 1px solid $color-gray-600;
padding-bottom: 4px;
}
}
// Common settings have 48px margin&padding bottom between blocks
.ppcp-r-settings-card--common-settings .ppcp-r-settings-card__content {
> .ppcp-r-settings-block {
&:not(:last-child) {
padding-bottom: 48px;
margin-bottom: 48px;
}
}
}
// Expert settings have 32px margin&padding bottom between blocks
.ppcp-r-settings-card--expert-settings .ppcp-r-settings-card__content {
> .ppcp-r-settings-block {
&:not(:last-child) {
padding-bottom: 32px;
margin-bottom: 32px;
}
}
}
// Order intent block has 32px gap and no lines in between
// Save payment methods block has 32px gap and no lines in between
.ppcp-r-settings-block {
&--order-intent, &--save-payment-methods {
@include vertical-layout-event-gap(32px);
> .ppcp-r-settings-block__content {
@include vertical-layout-event-gap(32px);
}
}
}
// Most primary settings block in the expert settings have 32px space after description
.ppcp-r-settings-block--toggle-content {
.ppcp-r-settings-block__content {
margin-top: 32px;
}
}
// Common settings have actions aligned top with the text, Expert settings have actions alligned middle with the text
.ppcp-r-settings-card--expert-settings {
.ppcp-r-settings-block__header {
align-items: center;
}
}

View file

@ -6,7 +6,6 @@
@import './components/reusable-components/onboarding-header';
@import './components/reusable-components/button';
@import './components/reusable-components/settings-toggle-block';
@import './components/reusable-components/text-control';
@import './components/reusable-components/separator';
@import './components/reusable-components/payment-method-icons';
@import "./components/reusable-components/payment-method-item";
@ -21,9 +20,12 @@
@import './components/reusable-components/spinner-overlay';
@import './components/reusable-components/welcome-docs';
@import './components/screens/onboarding';
@import './components/screens/dashboard/tab-dashboard';
@import './components/screens/dashboard/tab-payment-methods';
@import './components/screens/settings';
@import './components/screens/overview/tab-overview';
@import './components/screens/overview/tab-payment-methods';
@import './components/screens/overview/tab-settings';
}
@import './components/reusable-components/payment-method-modal';
@import './components/screens/onboarding-global';
@import './components/screens/settings-global';

View file

@ -0,0 +1,75 @@
import { __ } from '@wordpress/i18n';
import data from '../../utils/data';
import { useState } from '@wordpress/element';
const ConnectionInfo = ( { connectionStatusDataDefault } ) => {
const [ connectionData, setConnectionData ] = useState( {
...connectionStatusDataDefault,
} );
const showAllData = () => {
setConnectionData( {
...connectionData,
showAllData: ! connectionData.showAllData,
} );
};
const toggleStatusClassName = [ 'ppcp-r-connection-status__status-toggle' ];
if ( connectionData.showAllData ) {
toggleStatusClassName.push(
'ppcp-r-connection-status__status-toggle--toggled'
);
}
return (
<div className="ppcp-r-connection-status__data">
<div
className="ppcp-r-connection-status__status-row ppcp-r-connection-status__status-row--first"
onClick={ () => showAllData() }
>
<strong>
{ __( 'Email address:', 'woocommerce-paypal-payments' ) }
</strong>
<span>{ connectionData.email }</span>
<span className={ toggleStatusClassName.join( ' ' ) }>
{ data().getImage(
'icon-arrow-down.svg',
'ppcp-r-connection-status__show-all-data'
) }
</span>
</div>
{ connectionData.showAllData && (
<>
<div className="ppcp-r-connection-status__status-row">
<strong>
{ __(
'Merchant ID:',
'woocommerce-paypal-payments'
) }
</strong>
<span>{ connectionData.merchantId }</span>
</div>
<div className="ppcp-r-connection-status__status-row">
<strong>
{ __(
'Client ID:',
'woocommerce-paypal-payments'
) }
</strong>
<span>{ connectionData.clientId }</span>
</div>
</>
) }
</div>
);
};
export default ConnectionInfo;
export const connectionStatusDataDefault = {
connectionStatus: true,
showAllData: false,
email: 'bt_us@woocommerce.com',
merchantId: 'AT45V2DGMKLRY',
clientId: 'BAARTJLxtUNN4d2GMB6Eut3suMDYad72xQA-FntdIFuJ6FmFJITxAY8',
};

View file

@ -24,7 +24,7 @@ export const PayPalRdb = ( props ) => {
return (
<div className="ppcp-r__radio">
<input
id={ props?.id ? props.id : null }
id={ props?.id }
className="ppcp-r__radio-value"
type="radio"
checked={ props.value === props.currentValue }
@ -37,6 +37,40 @@ export const PayPalRdb = ( props ) => {
);
};
export const PayPalRdbWithContent = ( props ) => {
const className = [ 'ppcp-r__radio-wrapper' ];
if ( props?.className ) {
className.push( props.className );
}
return (
<div className="ppcp-r__radio-outer-wrapper">
<div className={ className }>
<PayPalRdb { ...props } />
<div className="ppcp-r__radio-content">
<label htmlFor={ props?.id }>{ props.label }</label>
{ props.description && (
<p
className="ppcp-r__radio-description"
dangerouslySetInnerHTML={ {
__html: props.description,
} }
/>
) }
</div>
</div>
{ props?.toggleAdditionalContent &&
props.children &&
props.value === props.currentValue && (
<div className="ppcp-r__radio-content-additional">
{ props.children }
</div>
) }
</div>
);
};
export const handleCheckboxState = ( checked, props ) => {
let newValue = null;
if ( checked ) {

Some files were not shown because too many files have changed in this diff Show more