mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
Merge trunk
This commit is contained in:
commit
7b30568dd2
70 changed files with 1172 additions and 814 deletions
|
@ -9,7 +9,12 @@ const ControlButton = ( {
|
|||
buttonLabel,
|
||||
} ) => (
|
||||
<Action>
|
||||
<Button isBusy={ isBusy } variant={ type } onClick={ onClick }>
|
||||
<Button
|
||||
className="small-button"
|
||||
isBusy={ isBusy }
|
||||
variant={ type }
|
||||
onClick={ onClick }
|
||||
>
|
||||
{ buttonLabel }
|
||||
</Button>
|
||||
</Action>
|
||||
|
|
|
@ -10,6 +10,7 @@ const ControlTextInput = ( {
|
|||
} ) => (
|
||||
<Action>
|
||||
<TextControl
|
||||
__nextHasNoMarginBottom={ true }
|
||||
className="ppcp-r-vertical-text-control"
|
||||
placeholder={ placeholder }
|
||||
value={ value }
|
||||
|
|
|
@ -24,6 +24,7 @@ const Checkbox = ( {
|
|||
|
||||
return (
|
||||
<CheckboxControl
|
||||
__nextHasNoMarginBottom={ true }
|
||||
label={ label }
|
||||
value={ value }
|
||||
checked={ checked }
|
||||
|
|
|
@ -1,15 +1,39 @@
|
|||
import { PayPalCheckbox } from './index';
|
||||
import { useCallback } from '@wordpress/element';
|
||||
|
||||
const CheckboxGroup = ( { options, value, onChange } ) => {
|
||||
const handleChange = ( key, checked ) => {
|
||||
const getNewValue = () => {
|
||||
if ( checked ) {
|
||||
return [ ...value, key ];
|
||||
}
|
||||
return value.filter( ( val ) => val !== key );
|
||||
};
|
||||
const CheckboxGroup = ( { name, options, value, onChange } ) => {
|
||||
const handleChange = useCallback(
|
||||
( key, checked ) => {
|
||||
const getNewValue = () => {
|
||||
if ( 'boolean' === typeof value ) {
|
||||
return checked;
|
||||
}
|
||||
|
||||
onChange( getNewValue() );
|
||||
if ( checked ) {
|
||||
return [ ...value, key ];
|
||||
}
|
||||
return value.filter( ( val ) => val !== key );
|
||||
};
|
||||
|
||||
onChange( getNewValue() );
|
||||
},
|
||||
[ onChange, value ]
|
||||
);
|
||||
|
||||
const isItemChecked = ( checked, itemValue ) => {
|
||||
if ( typeof checked === 'boolean' ) {
|
||||
return checked;
|
||||
}
|
||||
|
||||
if ( Array.isArray( value ) ) {
|
||||
return value.includes( itemValue );
|
||||
}
|
||||
|
||||
if ( typeof value === 'boolean' ) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return value === itemValue;
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -21,16 +45,14 @@ const CheckboxGroup = ( { options, value, onChange } ) => {
|
|||
checked,
|
||||
disabled,
|
||||
description,
|
||||
tooltip,
|
||||
} ) => (
|
||||
<PayPalCheckbox
|
||||
key={ itemValue }
|
||||
key={ name + itemValue }
|
||||
value={ itemValue }
|
||||
label={ label }
|
||||
checked={ checked }
|
||||
checked={ isItemChecked( checked, itemValue ) }
|
||||
disabled={ disabled }
|
||||
description={ description }
|
||||
tooltip={ tooltip }
|
||||
changeCallback={ handleChange }
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -49,10 +49,12 @@ const OptionItem = ( {
|
|||
} ) => {
|
||||
const boxClassName = classNames( 'ppcp-r-select-box', {
|
||||
'ppcp--selected': isSelected,
|
||||
'ppcp--multiselect': isMulti,
|
||||
} );
|
||||
|
||||
return (
|
||||
<div className={ boxClassName }>
|
||||
// eslint-disable-next-line jsx-a11y/label-has-associated-control -- label has a nested input control.
|
||||
<label className={ boxClassName }>
|
||||
<InputField
|
||||
value={ itemValue }
|
||||
isRadio={ ! isMulti }
|
||||
|
@ -60,22 +62,16 @@ const OptionItem = ( {
|
|||
isSelected={ isSelected }
|
||||
/>
|
||||
|
||||
<div className="ppcp-r-select-box__content">
|
||||
<div className="ppcp-r-select-box__content-inner">
|
||||
<span className="ppcp-r-select-box__title">
|
||||
{ itemTitle }
|
||||
</span>
|
||||
<p className="ppcp-r-select-box__description">
|
||||
{ itemDescription }
|
||||
</p>
|
||||
<div className="ppcp--box-content">
|
||||
<div className="ppcp--box-content-inner">
|
||||
<span className="ppcp--box-title">{ itemTitle }</span>
|
||||
<p className="ppcp--box-description">{ itemDescription }</p>
|
||||
{ children && (
|
||||
<div className="ppcp-r-select-box__additional-content">
|
||||
{ children }
|
||||
</div>
|
||||
<div className="ppcp--box-details">{ children }</div>
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
/**
|
||||
* Temporary component, until the experimental HStack block editor component is stable.
|
||||
*
|
||||
* @see https://wordpress.github.io/gutenberg/?path=/docs/components-experimental-hstack--docs
|
||||
* @file
|
||||
*/
|
||||
import classNames from 'classnames';
|
||||
|
||||
const HStack = ( { className, spacing = 3, children } ) => {
|
||||
const wrapperClass = classNames(
|
||||
'components-flex components-h-stack',
|
||||
className
|
||||
);
|
||||
|
||||
const styles = {
|
||||
gap: `calc(${ 4 * spacing }px)`,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={ wrapperClass } style={ styles }>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HStack;
|
|
@ -1,15 +1,20 @@
|
|||
import { Icon } from '@wordpress/components';
|
||||
|
||||
import data from '../../utils/data';
|
||||
|
||||
const PaymentMethodIcon = ( props ) => {
|
||||
if (
|
||||
( Array.isArray( props.icons ) &&
|
||||
props.icons.includes( props.type ) ) ||
|
||||
props.icons === 'all'
|
||||
) {
|
||||
return data().getImage( 'icon-button-' + props.type + '.svg' );
|
||||
const PaymentMethodIcon = ( { icons, type } ) => {
|
||||
const validIcon = Array.isArray( icons ) && icons.includes( type );
|
||||
|
||||
if ( validIcon || icons === 'all' ) {
|
||||
return (
|
||||
<Icon
|
||||
icon={ data().getImage( 'icon-button-' + type + '.svg' ) }
|
||||
className="ppcp--method-icon"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <></>;
|
||||
return null;
|
||||
};
|
||||
|
||||
export default PaymentMethodIcon;
|
||||
|
|
|
@ -15,22 +15,6 @@ const SettingsBlock = ( {
|
|||
'ppcp--horizontal': horizontalLayout,
|
||||
} );
|
||||
|
||||
const BlockTitle = ( { blockTitle, blockSuffix, blockDescription } ) => {
|
||||
if ( ! blockTitle && ! blockDescription ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Header>
|
||||
<Title>
|
||||
{ blockTitle }
|
||||
<TitleExtra>{ blockSuffix }</TitleExtra>
|
||||
</Title>
|
||||
<Description>{ blockDescription }</Description>
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={ blockClassName }>
|
||||
<BlockTitle
|
||||
|
@ -45,3 +29,19 @@ const SettingsBlock = ( {
|
|||
};
|
||||
|
||||
export default SettingsBlock;
|
||||
|
||||
const BlockTitle = ( { blockTitle, blockSuffix, blockDescription } ) => {
|
||||
if ( ! blockTitle && ! blockDescription ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Header>
|
||||
<Title>
|
||||
{ blockTitle }
|
||||
<TitleExtra>{ blockSuffix }</TitleExtra>
|
||||
</Title>
|
||||
<Description>{ blockDescription }</Description>
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -12,7 +12,7 @@ const FeatureSettingsBlock = ( { title, description, ...props } ) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<span className="ppcp-r-feature-item__notes">
|
||||
<span className="ppcp--item-notes">
|
||||
{ notes.map( ( note, index ) => (
|
||||
<span key={ index }>{ note }</span>
|
||||
) ) }
|
||||
|
@ -20,30 +20,39 @@ const FeatureSettingsBlock = ( { title, description, ...props } ) => {
|
|||
);
|
||||
};
|
||||
|
||||
const renderButton = ( button ) => {
|
||||
const buttonElement = (
|
||||
<Button
|
||||
className={ button.class ? button.class : '' }
|
||||
key={ button.text }
|
||||
isBusy={ props.actionProps?.isBusy }
|
||||
variant={ button.type }
|
||||
onClick={ button.onClick }
|
||||
>
|
||||
{ button.text }
|
||||
</Button>
|
||||
);
|
||||
const FeatureButton = ( {
|
||||
className,
|
||||
variant,
|
||||
text,
|
||||
isBusy,
|
||||
url,
|
||||
urls,
|
||||
onClick,
|
||||
} ) => {
|
||||
const buttonProps = {
|
||||
className,
|
||||
isBusy,
|
||||
variant,
|
||||
};
|
||||
|
||||
// If there's a URL (either direct or in urls object), wrap in anchor tag
|
||||
if ( button.url || button.urls ) {
|
||||
const href = button.urls ? button.urls.live : button.url;
|
||||
return (
|
||||
<a href={ href } key={ button.text }>
|
||||
{ buttonElement }
|
||||
</a>
|
||||
);
|
||||
if ( url || urls ) {
|
||||
buttonProps.href = urls ? urls.live : url;
|
||||
buttonProps.target = '_blank';
|
||||
}
|
||||
if ( ! buttonProps.href ) {
|
||||
buttonProps.onClick = onClick;
|
||||
}
|
||||
|
||||
return buttonElement;
|
||||
return <Button { ...buttonProps }>{ text }</Button>;
|
||||
};
|
||||
|
||||
const renderDescription = () => {
|
||||
return (
|
||||
<span
|
||||
className="ppcp-r-feature-item__description ppcp-r-settings-block__feature__description"
|
||||
dangerouslySetInnerHTML={ { __html: description } }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -56,13 +65,33 @@ const FeatureSettingsBlock = ( { title, description, ...props } ) => {
|
|||
) }
|
||||
</Title>
|
||||
<Description className="ppcp-r-settings-block__feature__description">
|
||||
{ description }
|
||||
{ renderDescription() }
|
||||
{ printNotes() }
|
||||
</Description>
|
||||
</Header>
|
||||
<Action>
|
||||
<div className="ppcp-r-feature-item__buttons">
|
||||
{ props.actionProps?.buttons.map( renderButton ) }
|
||||
<div className="ppcp--action-buttons">
|
||||
{ props.actionProps?.buttons.map(
|
||||
( {
|
||||
class: className,
|
||||
type,
|
||||
text,
|
||||
url,
|
||||
urls,
|
||||
onClick,
|
||||
} ) => (
|
||||
<FeatureButton
|
||||
key={ text }
|
||||
className={ className }
|
||||
variant={ type }
|
||||
text={ text }
|
||||
isBusy={ props.actionProps.isBusy }
|
||||
url={ url }
|
||||
urls={ urls }
|
||||
onClick={ onClick }
|
||||
/>
|
||||
)
|
||||
) }
|
||||
</div>
|
||||
</Action>
|
||||
</SettingsBlock>
|
||||
|
|
|
@ -2,7 +2,6 @@ import { ToggleControl } from '@wordpress/components';
|
|||
|
||||
import SettingsBlock from '../SettingsBlock';
|
||||
import PaymentMethodIcon from '../PaymentMethodIcon';
|
||||
import data from '../../../utils/data';
|
||||
|
||||
const PaymentMethodItemBlock = ( {
|
||||
paymentMethod,
|
||||
|
@ -11,35 +10,35 @@ const PaymentMethodItemBlock = ( {
|
|||
isSelected,
|
||||
} ) => {
|
||||
return (
|
||||
<SettingsBlock className="ppcp-r-settings-block__payment-methods__item">
|
||||
<div className="ppcp-r-settings-block__payment-methods__item__inner">
|
||||
<div className="ppcp-r-settings-block__payment-methods__item__title-wrapper">
|
||||
{ paymentMethod?.icon && (
|
||||
<PaymentMethodIcon
|
||||
icons={ [ paymentMethod.icon ] }
|
||||
type={ paymentMethod.icon }
|
||||
/>
|
||||
) }
|
||||
<span className="ppcp-r-settings-block__payment-methods__item__title">
|
||||
<SettingsBlock className="ppcp--method-item" separatorAndGap={ false }>
|
||||
<div className="ppcp--method-inner">
|
||||
<div className="ppcp--method-title-wrapper">
|
||||
{ paymentMethod?.icon && (
|
||||
<PaymentMethodIcon
|
||||
icons={ [ paymentMethod.icon ] }
|
||||
type={ paymentMethod.icon }
|
||||
/>
|
||||
) }
|
||||
<span className="ppcp--method-title">
|
||||
{ paymentMethod.itemTitle }
|
||||
</span>
|
||||
</div>
|
||||
<p className="ppcp-r-settings-block__payment-methods__item__description">
|
||||
<p className="ppcp--method-description">
|
||||
{ paymentMethod.itemDescription }
|
||||
</p>
|
||||
<div className="ppcp-r-settings-block__payment-methods__item__footer">
|
||||
<div className="ppcp--method-footer">
|
||||
<ToggleControl
|
||||
__nextHasNoMarginBottom={ true }
|
||||
checked={ isSelected }
|
||||
onChange={ onSelect }
|
||||
/>
|
||||
{ paymentMethod?.fields && onTriggerModal && (
|
||||
<div
|
||||
className="ppcp-r-settings-block__payment-methods__item__settings"
|
||||
<Button
|
||||
className="ppcp--method-settings"
|
||||
onClick={ onTriggerModal }
|
||||
>
|
||||
{ data().getImage( 'icon-settings.svg' ) }
|
||||
</div>
|
||||
<Icon icon={ cog } />
|
||||
</Button>
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,42 +1,38 @@
|
|||
import SettingsBlock from '../SettingsBlock';
|
||||
import PaymentMethodItemBlock from './PaymentMethodItemBlock';
|
||||
import { usePaymentMethods } from '../../../data/payment/hooks';
|
||||
import { PaymentHooks } from '../../../data';
|
||||
|
||||
const PaymentMethodsBlock = ( {
|
||||
paymentMethods,
|
||||
className = '',
|
||||
onTriggerModal,
|
||||
} ) => {
|
||||
const { setPersistent } = usePaymentMethods();
|
||||
// TODO: This is not a reusable component, as it's connected to the Redux store.
|
||||
const PaymentMethodsBlock = ( { paymentMethods = [], onTriggerModal } ) => {
|
||||
const { changePaymentSettings } = PaymentHooks.useStore();
|
||||
|
||||
if ( ! paymentMethods?.length ) {
|
||||
const handleSelect = ( methodId, isSelected ) =>
|
||||
changePaymentSettings( methodId, {
|
||||
enabled: isSelected,
|
||||
} );
|
||||
|
||||
if ( ! paymentMethods.length ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleSelect = ( paymentMethod, isSelected ) => {
|
||||
setPersistent( paymentMethod.id, {
|
||||
...paymentMethod,
|
||||
enabled: isSelected,
|
||||
} );
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingsBlock
|
||||
className={ `ppcp-r-settings-block__payment-methods ${ className }` }
|
||||
>
|
||||
{ paymentMethods.map( ( paymentMethod ) => (
|
||||
<PaymentMethodItemBlock
|
||||
key={ paymentMethod.id }
|
||||
paymentMethod={ paymentMethod }
|
||||
isSelected={ paymentMethod.enabled }
|
||||
onSelect={ ( checked ) =>
|
||||
handleSelect( paymentMethod, checked )
|
||||
}
|
||||
onTriggerModal={ () =>
|
||||
onTriggerModal?.( paymentMethod.id )
|
||||
}
|
||||
/>
|
||||
) ) }
|
||||
<SettingsBlock className="ppcp--grid ppcp-r-settings-block__payment-methods">
|
||||
{ paymentMethods
|
||||
// Remove empty/invalid payment method entries.
|
||||
.filter( ( m ) => m.id )
|
||||
.map( ( paymentMethod ) => (
|
||||
<PaymentMethodItemBlock
|
||||
key={ paymentMethod.id }
|
||||
paymentMethod={ paymentMethod }
|
||||
isSelected={ paymentMethod.enabled }
|
||||
onSelect={ ( checked ) =>
|
||||
handleSelect( paymentMethod.id, checked )
|
||||
}
|
||||
onTriggerModal={ () =>
|
||||
onTriggerModal?.( paymentMethod.id )
|
||||
}
|
||||
/>
|
||||
) ) }
|
||||
</SettingsBlock>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,55 +1,47 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
import { Content, ContentWrapper } from './Elements';
|
||||
import { Content } from './Elements';
|
||||
|
||||
const SettingsCard = ( {
|
||||
id,
|
||||
className: extraClassName,
|
||||
className,
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
contentItems,
|
||||
contentContainer = true,
|
||||
} ) => {
|
||||
const className = classNames( 'ppcp-r-settings-card', extraClassName );
|
||||
|
||||
const renderContent = () => {
|
||||
// If contentItems array is provided, wrap each item in Content component
|
||||
if ( contentItems ) {
|
||||
return (
|
||||
<ContentWrapper>
|
||||
{ contentItems.map( ( item ) => (
|
||||
<Content key={ item.key } id={ item.key }>
|
||||
{ item }
|
||||
</Content>
|
||||
) ) }
|
||||
</ContentWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise handle regular children with contentContainer prop
|
||||
if ( contentContainer ) {
|
||||
return <Content>{ children }</Content>;
|
||||
}
|
||||
|
||||
return children;
|
||||
const cardClassNames = classNames( 'ppcp-r-settings-card', className );
|
||||
const cardProps = {
|
||||
className: cardClassNames,
|
||||
id,
|
||||
};
|
||||
|
||||
return (
|
||||
<div id={ id } className={ className }>
|
||||
<div { ...cardProps }>
|
||||
<div className="ppcp-r-settings-card__header">
|
||||
<div className="ppcp-r-settings-card__content-inner">
|
||||
<span className="ppcp-r-settings-card__title">
|
||||
{ title }
|
||||
</span>
|
||||
<p className="ppcp-r-settings-card__description">
|
||||
<div className="ppcp-r-settings-card__description">
|
||||
{ description }
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{ renderContent() }
|
||||
|
||||
<InnerContent showCards={ contentContainer }>
|
||||
{ children }
|
||||
</InnerContent>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsCard;
|
||||
|
||||
const InnerContent = ( { showCards, children } ) => {
|
||||
if ( showCards ) {
|
||||
return <Content>{ children }</Content>;
|
||||
}
|
||||
|
||||
return children;
|
||||
};
|
||||
|
|
|
@ -9,9 +9,7 @@ const SpinnerOverlay = ( { message = null } ) => {
|
|||
return (
|
||||
<div className="ppcp-r-spinner-overlay">
|
||||
{ message && (
|
||||
<span className="ppcp-r-spinner-overlay__message">
|
||||
{ message }
|
||||
</span>
|
||||
<span className="ppcp--spinner-message">{ message }</span>
|
||||
) }
|
||||
<Spinner />
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Temporary component, until the experimental VStack/HStack block editor component is stable.
|
||||
*
|
||||
* @see https://wordpress.github.io/gutenberg/?path=/docs/components-experimental-hstack--docs
|
||||
* @see https://wordpress.github.io/gutenberg/?path=/docs/components-experimental-vstack--docs
|
||||
* @file
|
||||
*/
|
||||
import classNames from 'classnames';
|
||||
|
||||
const Stack = ( { type, className, spacing, children } ) => {
|
||||
const wrapperClass = classNames(
|
||||
'components-flex',
|
||||
`components-${ type }-stack`,
|
||||
className
|
||||
);
|
||||
|
||||
const styles = {
|
||||
gap: `calc(${ 4 * spacing }px)`,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={ wrapperClass } style={ styles }>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const HStack = ( { className, spacing = 3, children } ) => {
|
||||
return (
|
||||
<Stack type="h" className={ className } spacing={ spacing }>
|
||||
{ children }
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const VStack = ( { className, spacing = 3, children } ) => {
|
||||
return (
|
||||
<Stack type="v" className={ className } spacing={ spacing }>
|
||||
{ children }
|
||||
</Stack>
|
||||
);
|
||||
};
|
|
@ -1,134 +0,0 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import SettingsCard from '../../ReusableComponents/SettingsCard';
|
||||
import { PaymentMethodsBlock } from '../../ReusableComponents/SettingsBlocks';
|
||||
import { PaymentHooks } from '../../../data';
|
||||
import { useActiveModal } from '../../../data/common/hooks';
|
||||
import Modal from './TabSettingsElements/Blocks/Modal';
|
||||
import { usePaymentMethods } from '../../../data/payment/hooks';
|
||||
|
||||
const TabPaymentMethods = () => {
|
||||
const { paymentMethodsPayPalCheckout } =
|
||||
PaymentHooks.usePaymentMethodsPayPalCheckout();
|
||||
const { paymentMethodsOnlineCardPayments } =
|
||||
PaymentHooks.usePaymentMethodsOnlineCardPayments();
|
||||
const { paymentMethodsAlternative } =
|
||||
PaymentHooks.usePaymentMethodsAlternative();
|
||||
|
||||
const { setPersistent } = usePaymentMethods();
|
||||
|
||||
const { activeModal, setActiveModal } = useActiveModal();
|
||||
|
||||
const getActiveMethod = () => {
|
||||
if ( ! activeModal ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const allMethods = [
|
||||
...paymentMethodsPayPalCheckout,
|
||||
...paymentMethodsOnlineCardPayments,
|
||||
...paymentMethodsAlternative,
|
||||
];
|
||||
|
||||
return allMethods.find( ( method ) => method.id === activeModal );
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ppcp-r-payment-methods">
|
||||
<SettingsCard
|
||||
id="ppcp-paypal-checkout-card"
|
||||
title={ __( 'PayPal Checkout', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Select your preferred checkout option with PayPal for easy payment processing.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
icon="icon-checkout-standard.svg"
|
||||
contentContainer={ false }
|
||||
>
|
||||
<PaymentMethodsBlock
|
||||
paymentMethods={ paymentMethodsPayPalCheckout }
|
||||
onTriggerModal={ setActiveModal }
|
||||
/>
|
||||
</SettingsCard>
|
||||
<SettingsCard
|
||||
id="ppcp-card-payments-card"
|
||||
title={ __(
|
||||
'Online Card Payments',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Select your preferred card payment options for efficient payment processing.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
icon="icon-checkout-online-methods.svg"
|
||||
contentContainer={ false }
|
||||
>
|
||||
<PaymentMethodsBlock
|
||||
paymentMethods={ paymentMethodsOnlineCardPayments }
|
||||
onTriggerModal={ setActiveModal }
|
||||
/>
|
||||
</SettingsCard>
|
||||
<SettingsCard
|
||||
id="ppcp-alternative-payments-card"
|
||||
title={ __(
|
||||
'Alternative Payment Methods',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'With alternative payment methods, customers across the globe can pay with their bank accounts and other local payment methods.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
icon="icon-checkout-alternative-methods.svg"
|
||||
contentContainer={ false }
|
||||
>
|
||||
<PaymentMethodsBlock
|
||||
paymentMethods={ paymentMethodsAlternative }
|
||||
onTriggerModal={ setActiveModal }
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
{ activeModal && (
|
||||
<Modal
|
||||
method={ getActiveMethod() }
|
||||
setModalIsVisible={ () => setActiveModal( null ) }
|
||||
onSave={ ( methodId, settings ) => {
|
||||
setPersistent( methodId, {
|
||||
...getActiveMethod(),
|
||||
title: settings.checkoutPageTitle,
|
||||
description: settings.checkoutPageDescription,
|
||||
} );
|
||||
|
||||
if ( 'paypalShowLogo' in settings ) {
|
||||
setPersistent(
|
||||
'paypalShowLogo',
|
||||
settings.paypalShowLogo
|
||||
);
|
||||
}
|
||||
if ( 'threeDSecure' in settings ) {
|
||||
setPersistent(
|
||||
'threeDSecure',
|
||||
settings.threeDSecure
|
||||
);
|
||||
}
|
||||
if ( 'fastlaneCardholderName' in settings ) {
|
||||
setPersistent(
|
||||
'fastlaneCardholderName',
|
||||
settings.fastlaneCardholderName
|
||||
);
|
||||
}
|
||||
if ( 'fastlaneDisplayWatermark' in settings ) {
|
||||
setPersistent(
|
||||
'fastlaneDisplayWatermark',
|
||||
settings.fastlaneDisplayWatermark
|
||||
);
|
||||
}
|
||||
|
||||
setActiveModal( null );
|
||||
} }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TabPaymentMethods;
|
|
@ -1,4 +1,5 @@
|
|||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { Button } from '@wordpress/components';
|
||||
|
||||
import Container from '../ReusableComponents/Container';
|
||||
import SettingsCard from '../ReusableComponents/SettingsCard';
|
||||
|
@ -9,7 +10,7 @@ const SendOnlyMessage = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<SettingsNavigation />
|
||||
<SettingsNavigation canSave={ false } />
|
||||
<Container page="settings">
|
||||
<SettingsCard
|
||||
title={ __(
|
||||
|
@ -45,6 +46,19 @@ const SendOnlyMessage = () => {
|
|||
),
|
||||
} }
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Button
|
||||
href={ settingsPageUrl }
|
||||
variant="primary"
|
||||
className="small-button"
|
||||
>
|
||||
{ __(
|
||||
'Go to WooCommerce settings',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</Button>
|
||||
</div>
|
||||
</SettingsCard>
|
||||
</Container>
|
||||
</>
|
||||
|
|
|
@ -2,21 +2,20 @@ import { Button } from '@wordpress/components';
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import TopNavigation from '../../../ReusableComponents/TopNavigation';
|
||||
import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper';
|
||||
import { useSaveSettings } from '../../../../hooks/useSaveSettings';
|
||||
|
||||
const SettingsNavigation = () => {
|
||||
const SettingsNavigation = ( { canSave = true } ) => {
|
||||
const { persistAll } = useSaveSettings();
|
||||
|
||||
const title = __( 'PayPal Payments', 'woocommerce-paypal-payments' );
|
||||
|
||||
return (
|
||||
<TopNavigation title={ title } exitOnTitleClick={ true }>
|
||||
<BusyStateWrapper>
|
||||
{ canSave && (
|
||||
<Button variant="primary" onClick={ persistAll }>
|
||||
{ __( 'Save', 'woocommerce-paypal-payments' ) }
|
||||
</Button>
|
||||
</BusyStateWrapper>
|
||||
) }
|
||||
</TopNavigation>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import { TAB_IDS, selectTab } from '../../../../../utils/tabSelector';
|
||||
import { payLaterMessaging } from './pay-later-messaging';
|
||||
|
||||
const Features = {
|
||||
getFeatures: ( setActiveModal ) => [
|
||||
export const getFeatures = ( setActiveModal ) => {
|
||||
const storeCountry = ppcpSettings?.storeCountry;
|
||||
const features = [
|
||||
{
|
||||
id: 'save_paypal_and_venmo',
|
||||
title: __( 'Save PayPal and Venmo', 'woocommerce-paypal-payments' ),
|
||||
|
@ -39,7 +41,10 @@ const Features = {
|
|||
{
|
||||
type: 'tertiary',
|
||||
text: __( 'Learn more', 'woocommerce-paypal-payments' ),
|
||||
url: 'https://developer.paypal.com/studio/checkout/standard',
|
||||
urls: {
|
||||
sandbox: '#',
|
||||
live: '#',
|
||||
},
|
||||
class: 'small-button',
|
||||
},
|
||||
],
|
||||
|
@ -230,7 +235,16 @@ const Features = {
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
];
|
||||
|
||||
const countryData = payLaterMessaging[ storeCountry ] || {};
|
||||
|
||||
// Add "Pay Later Messaging" to the feature list, if it's available.
|
||||
if (
|
||||
!! window.ppcpSettings?.isPayLaterConfiguratorAvailable &&
|
||||
countryData
|
||||
) {
|
||||
features.push( {
|
||||
id: 'pay_later_messaging',
|
||||
title: __( 'Pay Later Messaging', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
|
@ -269,8 +283,8 @@ const Features = {
|
|||
class: 'small-button',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
} );
|
||||
}
|
||||
|
||||
export default Features;
|
||||
return features;
|
||||
};
|
|
@ -0,0 +1,106 @@
|
|||
import { __, sprintf } from '@wordpress/i18n';
|
||||
|
||||
export const payLaterMessaging = {
|
||||
US: {
|
||||
description: sprintf(
|
||||
__(
|
||||
'Your customers can already buy now and pay later with PayPal — add messaging to your site to let them know. PayPal’s Pay Later helps boost merchants\' conversion rates and increases cart sizes by 39%%.¹ You get paid in full up front. <a target="_blank" href="%s">More about Pay Later</a>',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'https://www.paypal.com/us/business/accept-payments/checkout/installments'
|
||||
),
|
||||
notes: [
|
||||
__( '¹PayPal Q2 Earnings-2021.', 'woocommerce-paypal-payments' ),
|
||||
],
|
||||
},
|
||||
GB: {
|
||||
description: sprintf(
|
||||
__(
|
||||
'Your customers can already buy now and pay later with PayPal — add messaging to your site to let them know. Pay in 3 gets a 216%% higher Average Order Value than a standard PayPal transaction.¹ There’s <strong>no extra cost</strong> and you get paid up front. <a target="_blank" href="%s">More about Pay in 3</a>',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'https://www.paypal.com/uk/business/accept-payments/checkout/installments'
|
||||
),
|
||||
notes: [
|
||||
__(
|
||||
'Based on PayPal internal data from Q1 2022, results include Pay in 3 (UK).',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
],
|
||||
},
|
||||
FR: {
|
||||
description: sprintf(
|
||||
__(
|
||||
'Your customers can already buy now and pay later with PayPal — add messaging to your site to let them know. Pay in 4x gets a 65%% higher Average Order Value than a standard PayPal transaction.¹ <strong>There\'s no extra cost on top of your PayPal Checkout rate</strong>, and you get paid up front. <a target="_blank" href="%s">More about Pay in 4x</a>',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'https://www.paypal.com/fr/business/accept-payments/checkout/installments'
|
||||
),
|
||||
notes: [
|
||||
__(
|
||||
'Internal Data Analysis of 1124 SMB across integrated partners and non-integrated partners, November 2022. SMB internally defined as up to 100,000€ in annual estimated ecommerce online payment volume.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
],
|
||||
},
|
||||
AU: {
|
||||
description: sprintf(
|
||||
__(
|
||||
'Your customers can already buy now and pay later with PayPal — add messaging to your site to let them know. Pay in 4 gets more than a 100%% higher Average Order Value than a standard PayPal transaction.¹ There’s <strong>no extra cost</strong> and you get paid up front. <a target="_blank" href="%s">More about Pay in 4</a>',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'https://www.paypal.com/au/business/accept-payments/checkout/installments'
|
||||
),
|
||||
notes: [
|
||||
__(
|
||||
'Based on PayPal internal data from Q1 2022, results include Pay in 4 (AU). Consumer eligibility applies.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
],
|
||||
},
|
||||
IT: {
|
||||
description: sprintf(
|
||||
__(
|
||||
'Your customers can already buy now and pay later with PayPal — add messaging to your site to let them know. Pay in 3 installments gets about a 275%% higher Average Order Value than a standard PayPal transaction.¹ <strong>There\'s no extra cost on top of your PayPal Checkout rate</strong>, and you get paid up front. <a target="_blank" href="%s">More about Pay in 3 installments</a>',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'https://www.paypal.com/it/business/accept-payments/checkout/installments'
|
||||
),
|
||||
notes: [
|
||||
__(
|
||||
'Based on PayPal internal data from Q1 2022, results include Pay in 3 installments (IT).',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
],
|
||||
},
|
||||
ES: {
|
||||
description: sprintf(
|
||||
__(
|
||||
'Your customers can already buy now and pay later with PayPal — add messaging to your site to let them know. Pay in 3 installments gets about a 275%% higher Average Order Value than a standard PayPal transaction.¹ <strong>There\'s no extra cost on top of your PayPal Checkout rate</strong>, and you get paid up front. <a target="_blank" href="%s">More about Pay in 3 installments</a>',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'https://www.paypal.com/es/business/accept-payments/checkout/installments'
|
||||
),
|
||||
notes: [
|
||||
__(
|
||||
'Based on PayPal internal data from Q1 2022, results include Pay in 3 installments (ES).',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
],
|
||||
},
|
||||
DE: {
|
||||
description: sprintf(
|
||||
__(
|
||||
'Your customers can already buy now and pay later with PayPal — add messaging to your site to let them know. When you offer your customers Pay Later options, 57%% will be more likely to buy from you again.¹ <strong>There\'s no extra cost</strong> and you get paid up front. <a target="_blank" href="%s">More about Pay Later</a>',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'https://www.paypal.com/de/business/accept-payments/checkout/installments'
|
||||
),
|
||||
notes: [
|
||||
__(
|
||||
'Average order value in 2020 with PayPal installments compared to total PayPal sales.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
],
|
||||
},
|
||||
};
|
|
@ -6,20 +6,18 @@ import {
|
|||
RadioControl,
|
||||
} from '@wordpress/components';
|
||||
import { useState } from '@wordpress/element';
|
||||
|
||||
import PaymentMethodModal from '../../../../ReusableComponents/PaymentMethodModal';
|
||||
import {
|
||||
usePaymentMethods,
|
||||
usePaymentMethodsModal,
|
||||
} from '../../../../../data/payment/hooks';
|
||||
import { PaymentHooks } from '../../../../../data';
|
||||
|
||||
const Modal = ( { method, setModalIsVisible, onSave } ) => {
|
||||
const { paymentMethods } = usePaymentMethods();
|
||||
const { paymentMethods } = PaymentHooks.usePaymentMethods();
|
||||
const {
|
||||
paypalShowLogo,
|
||||
threeDSecure,
|
||||
fastlaneCardholderName,
|
||||
fastlaneDisplayWatermark,
|
||||
} = usePaymentMethodsModal();
|
||||
} = PaymentHooks.usePaymentMethodsModal();
|
||||
|
||||
const [ settings, setSettings ] = useState( () => {
|
||||
if ( ! method?.id ) {
|
||||
|
@ -68,6 +66,7 @@ const Modal = ( { method, setModalIsVisible, onSave } ) => {
|
|||
return (
|
||||
<div className="ppcp-r-modal__field-row">
|
||||
<TextControl
|
||||
__nextHasNoMarginBottom={ true }
|
||||
className="ppcp-r-vertical-text-control"
|
||||
label={ field.label }
|
||||
value={ settings[ key ] }
|
|
@ -4,9 +4,9 @@ import { Button } from '@wordpress/components';
|
|||
import {
|
||||
ControlTextInput,
|
||||
ControlRadioGroup,
|
||||
} from '../../../../ReusableComponents/Controls';
|
||||
import Accordion from '../../../../ReusableComponents/AccordionSection';
|
||||
import SettingsBlock from '../../../../ReusableComponents/SettingsBlock';
|
||||
} from '../../../../../ReusableComponents/Controls';
|
||||
import Accordion from '../../../../../ReusableComponents/AccordionSection';
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
|
||||
const ConnectionDetails = ( { settings, updateFormValue } ) => {
|
||||
const isSandbox = settings.sandboxConnected;
|
|
@ -4,7 +4,7 @@ import { CommonHooks } from '../../../../../../data';
|
|||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
import { Title } from '../../../../../ReusableComponents/Elements';
|
||||
|
||||
const HooksTableBlock = () => {
|
||||
const HooksListBlock = () => {
|
||||
const { webhooks } = CommonHooks.useWebhooks();
|
||||
const { url, events } = webhooks;
|
||||
|
||||
|
@ -13,7 +13,7 @@ const HooksTableBlock = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<SettingsBlock separatorAndGap={ false }>
|
||||
<SettingsBlock separatorAndGap={ false } className="ppcp--webhooks">
|
||||
<WebhookUrl url={ url } />
|
||||
<WebhookEvents events={ events } />
|
||||
</SettingsBlock>
|
||||
|
@ -37,7 +37,7 @@ const WebhookEvents = ( { events } ) => {
|
|||
<Title>
|
||||
{ __( 'Subscribed Events', 'woocommerce-paypal-payments' ) }
|
||||
</Title>
|
||||
<ul>
|
||||
<ul className="ppcp--webhook-list">
|
||||
{ events.map( ( event, index ) => (
|
||||
<li key={ index }>{ event }</li>
|
||||
) ) }
|
||||
|
@ -46,4 +46,4 @@ const WebhookEvents = ( { events } ) => {
|
|||
);
|
||||
};
|
||||
|
||||
export default HooksTableBlock;
|
||||
export default HooksListBlock;
|
|
@ -1,9 +1,9 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import Accordion from '../../../../ReusableComponents/AccordionSection';
|
||||
import SettingsBlock from '../../../../ReusableComponents/SettingsBlock';
|
||||
import { ControlSelect } from '../../../../ReusableComponents/Controls';
|
||||
import { SettingsHooks } from '../../../../../data';
|
||||
import Accordion from '../../../../../ReusableComponents/AccordionSection';
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
import { ControlSelect } from '../../../../../ReusableComponents/Controls';
|
||||
import { SettingsHooks } from '../../../../../../data';
|
||||
|
||||
const OtherSettings = () => {
|
||||
const { disabledCards, setDisabledCards } = SettingsHooks.useSettings();
|
|
@ -5,10 +5,10 @@ import {
|
|||
ControlToggleButton,
|
||||
ControlTextInput,
|
||||
ControlSelect,
|
||||
} from '../../../../ReusableComponents/Controls';
|
||||
import SettingsBlock from '../../../../ReusableComponents/SettingsBlock';
|
||||
import Accordion from '../../../../ReusableComponents/AccordionSection';
|
||||
import { SettingsHooks } from '../../../../../data';
|
||||
} from '../../../../../ReusableComponents/Controls';
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
import Accordion from '../../../../../ReusableComponents/AccordionSection';
|
||||
import { SettingsHooks } from '../../../../../../data';
|
||||
|
||||
const PaypalSettings = () => {
|
||||
const {
|
|
@ -59,6 +59,7 @@ const ResubscribeBlock = () => {
|
|||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
horizontalLayout={ true }
|
||||
className="ppcp--webhook-resubscribe"
|
||||
>
|
||||
<ControlButton
|
||||
type={ 'secondary' }
|
|
@ -116,6 +116,7 @@ const SimulationBlock = () => {
|
|||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
horizontalLayout={ true }
|
||||
className="ppcp--webhook-simulation"
|
||||
>
|
||||
<ControlButton
|
||||
type={ 'secondary' }
|
|
@ -6,7 +6,7 @@ import Accordion from '../../../../../ReusableComponents/AccordionSection';
|
|||
|
||||
import SimulationBlock from './SimulationBlock';
|
||||
import ResubscribeBlock from './ResubscribeBlock';
|
||||
import HooksTableBlock from './HooksTableBlock';
|
||||
import HooksListBlock from './HooksListBlock';
|
||||
import { SettingsHooks } from '../../../../../../data';
|
||||
|
||||
const Troubleshooting = () => {
|
||||
|
@ -43,7 +43,7 @@ const Troubleshooting = () => {
|
|||
'https://woocommerce.com/document/woocommerce-paypal-payments/#webhook-status'
|
||||
) }
|
||||
>
|
||||
<HooksTableBlock />
|
||||
<HooksListBlock />
|
||||
<ResubscribeBlock />
|
||||
<SimulationBlock />
|
||||
</SettingsBlock>
|
|
@ -4,10 +4,10 @@ import {
|
|||
Content,
|
||||
ContentWrapper,
|
||||
} from '../../../../ReusableComponents/Elements';
|
||||
import ConnectionDetails from '../../../Overview/TabSettingsElements/Blocks/ConnectionDetails';
|
||||
import Troubleshooting from '../../../Overview/TabSettingsElements/Blocks/Troubleshooting/Troubleshooting';
|
||||
import PaypalSettings from '../../../Overview/TabSettingsElements/Blocks/PaypalSettings';
|
||||
import OtherSettings from '../../../Overview/TabSettingsElements/Blocks/OtherSettings';
|
||||
import ConnectionDetails from './Blocks/ConnectionDetails';
|
||||
import Troubleshooting from './Blocks/Troubleshooting';
|
||||
import PaypalSettings from './Blocks/PaypalSettings';
|
||||
import OtherSettings from './Blocks/OtherSettings';
|
||||
|
||||
const ExpertSettings = () => {
|
||||
const settings = {}; // dummy object
|
||||
|
|
|
@ -49,7 +49,7 @@ const LocationSelector = ( { location, setLocation } ) => {
|
|||
) }
|
||||
</SelectStylingSection>
|
||||
<CheckboxStylingSection
|
||||
className="location-activation"
|
||||
name="location-activation"
|
||||
separatorAndGap={ false }
|
||||
options={ [ activateCheckbox ] }
|
||||
value={ isActive }
|
||||
|
|
|
@ -9,8 +9,8 @@ const SectionPaymentMethods = ( { location } ) => {
|
|||
|
||||
return (
|
||||
<CheckboxStylingSection
|
||||
name="payment-methods"
|
||||
title={ __( 'Payment Methods', 'woocommerce-paypal-payments' ) }
|
||||
className="payment-methods"
|
||||
options={ choices }
|
||||
value={ paymentMethods }
|
||||
onChange={ setPaymentMethods }
|
||||
|
|
|
@ -21,7 +21,7 @@ const SectionTagline = ( { location } ) => {
|
|||
|
||||
return (
|
||||
<CheckboxStylingSection
|
||||
className="tagline"
|
||||
name="tagline"
|
||||
separatorAndGap={ false }
|
||||
options={ [ checkbox ] }
|
||||
value={ tagline }
|
||||
|
|
|
@ -20,7 +20,7 @@ const StylingSection = ( {
|
|||
separatorAndGap={ separatorAndGap }
|
||||
>
|
||||
<Header>
|
||||
<Title altStyle={ true } big={ bigTitle }>
|
||||
<Title noCaps={ true } big={ bigTitle }>
|
||||
{ title }
|
||||
</Title>
|
||||
<Description>{ description }</Description>
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
import { CheckboxGroup } from '../../../../../ReusableComponents/Fields';
|
||||
import HStack from '../../../../../ReusableComponents/HStack';
|
||||
import { VStack } from '../../../../../ReusableComponents/Stack';
|
||||
import StylingSection from './StylingSection';
|
||||
|
||||
const StylingSectionWithCheckboxes = ( {
|
||||
title,
|
||||
name,
|
||||
className = '',
|
||||
description = '',
|
||||
separatorAndGap = true,
|
||||
|
@ -14,7 +15,14 @@ const StylingSectionWithCheckboxes = ( {
|
|||
onChange,
|
||||
children,
|
||||
} ) => {
|
||||
className = classNames( 'ppcp--has-checkboxes', className );
|
||||
className = classNames( 'ppcp--has-checkboxes', name, className );
|
||||
|
||||
if ( ! name ) {
|
||||
console.error(
|
||||
'Checkbox sections need a unique name! No name given to:',
|
||||
title
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<StylingSection
|
||||
|
@ -23,13 +31,14 @@ const StylingSectionWithCheckboxes = ( {
|
|||
description={ description }
|
||||
separatorAndGap={ separatorAndGap }
|
||||
>
|
||||
<HStack spacing={ 6 }>
|
||||
<VStack spacing={ 6 }>
|
||||
<CheckboxGroup
|
||||
name={ name }
|
||||
options={ options }
|
||||
value={ value }
|
||||
onChange={ onChange }
|
||||
/>
|
||||
</HStack>
|
||||
</VStack>
|
||||
|
||||
{ children }
|
||||
</StylingSection>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { RadioControl } from '@wordpress/components';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import HStack from '../../../../../ReusableComponents/HStack';
|
||||
import { HStack } from '../../../../../ReusableComponents/Stack';
|
||||
import StylingSection from './StylingSection';
|
||||
|
||||
const StylingSectionWithRadiobuttons = ( {
|
||||
|
|
|
@ -9,11 +9,12 @@ import {
|
|||
TodoSettingsBlock,
|
||||
FeatureSettingsBlock,
|
||||
} from '../../../ReusableComponents/SettingsBlocks';
|
||||
import { Content, ContentWrapper } from '../../../ReusableComponents/Elements';
|
||||
import SettingsCard from '../../../ReusableComponents/SettingsCard';
|
||||
import { TITLE_BADGE_POSITIVE } from '../../../ReusableComponents/TitleBadge';
|
||||
import { useMerchantInfo } from '../../../../data/common/hooks';
|
||||
import { STORE_NAME } from '../../../../data/common';
|
||||
import Features from '../Components/Overview/Features';
|
||||
import { getFeatures } from '../Components/Overview/features-config';
|
||||
import { todosData } from '../todo-items';
|
||||
|
||||
import {
|
||||
|
@ -22,8 +23,34 @@ import {
|
|||
} from '../../../ReusableComponents/Icons';
|
||||
|
||||
const TabOverview = () => {
|
||||
const [ isRefreshing, setIsRefreshing ] = useState( false );
|
||||
return (
|
||||
<div className="ppcp-r-tab-overview">
|
||||
{ todosData.length > 0 && (
|
||||
<SettingsCard
|
||||
className="ppcp-r-tab-overview-todo"
|
||||
title={ __(
|
||||
'Things to do next',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Complete these tasks to keep your store updated with the latest products and services.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
>
|
||||
<TodoSettingsBlock todosData={ todosData } />
|
||||
</SettingsCard>
|
||||
) }
|
||||
|
||||
<OverviewFeatures />
|
||||
<OverviewHelp />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TabOverview;
|
||||
|
||||
const OverviewFeatures = () => {
|
||||
const [ isRefreshing, setIsRefreshing ] = useState( false );
|
||||
const { merchant, features: merchantFeatures } = useMerchantInfo();
|
||||
const { refreshFeatureStatuses, setActiveModal } =
|
||||
useDispatch( STORE_NAME );
|
||||
|
@ -32,7 +59,7 @@ const TabOverview = () => {
|
|||
|
||||
// Get the features data with access to setActiveModal
|
||||
const featuresData = useMemo(
|
||||
() => Features.getFeatures( setActiveModal ),
|
||||
() => getFeatures( setActiveModal ),
|
||||
[ setActiveModal ]
|
||||
);
|
||||
|
||||
|
@ -49,6 +76,7 @@ const TabOverview = () => {
|
|||
|
||||
const refreshHandler = async () => {
|
||||
setIsRefreshing( true );
|
||||
|
||||
try {
|
||||
const result = await refreshFeatureStatuses();
|
||||
if ( result && ! result.success ) {
|
||||
|
@ -79,7 +107,6 @@ const TabOverview = () => {
|
|||
icon: NOTIFICATION_SUCCESS,
|
||||
}
|
||||
);
|
||||
console.log( 'Features refreshed successfully.' );
|
||||
}
|
||||
} finally {
|
||||
setIsRefreshing( false );
|
||||
|
@ -87,111 +114,132 @@ const TabOverview = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="ppcp-r-tab-overview">
|
||||
{ todosData.length > 0 && (
|
||||
<SettingsCard
|
||||
className="ppcp-r-tab-overview-todo"
|
||||
title={ __(
|
||||
'Things to do next',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Complete these tasks to keep your store updated with the latest products and services.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
>
|
||||
<TodoSettingsBlock todosData={ todosData } />
|
||||
</SettingsCard>
|
||||
) }
|
||||
<SettingsCard
|
||||
className="ppcp-r-tab-overview-features"
|
||||
title={ __( 'Features', 'woocommerce-paypal-payments' ) }
|
||||
description={
|
||||
<OverviewFeatureDescription
|
||||
refreshHandler={ refreshHandler }
|
||||
isRefreshing={ isRefreshing }
|
||||
/>
|
||||
}
|
||||
contentContainer={ false }
|
||||
>
|
||||
<ContentWrapper>
|
||||
{ features.map( ( { id, ...feature } ) => (
|
||||
<OverviewFeatureItem
|
||||
key={ id }
|
||||
isBusy={ isRefreshing }
|
||||
isSandbox={ merchant.isSandbox }
|
||||
title={ feature.title }
|
||||
description={ feature.description }
|
||||
buttons={ feature.buttons }
|
||||
enabled={ feature.enabled }
|
||||
notes={ feature.notes }
|
||||
/>
|
||||
) ) }
|
||||
</ContentWrapper>
|
||||
</SettingsCard>
|
||||
);
|
||||
};
|
||||
|
||||
<SettingsCard
|
||||
className="ppcp-r-tab-overview-features"
|
||||
title={ __( 'Features', 'woocommerce-paypal-payments' ) }
|
||||
description={
|
||||
<>
|
||||
<p>
|
||||
{ __(
|
||||
'Enable additional features and capabilities on your WooCommerce store.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</p>
|
||||
<p>
|
||||
{ __(
|
||||
'Click Refresh to update your current features after making changes.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</p>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
onClick={ refreshHandler }
|
||||
disabled={ isRefreshing }
|
||||
>
|
||||
<Icon icon={ reusableBlock } size={ 18 } />
|
||||
{ isRefreshing
|
||||
? __(
|
||||
'Refreshing…',
|
||||
'woocommerce-paypal-payments'
|
||||
)
|
||||
: __(
|
||||
'Refresh',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
contentItems={ features.map( ( feature ) => {
|
||||
return (
|
||||
<FeatureSettingsBlock
|
||||
key={ feature.id }
|
||||
title={ feature.title }
|
||||
description={ feature.description }
|
||||
actionProps={ {
|
||||
buttons: feature.buttons
|
||||
.filter(
|
||||
( button ) =>
|
||||
! button.showWhen || // Learn more buttons
|
||||
( feature.enabled &&
|
||||
button.showWhen ===
|
||||
'enabled' ) ||
|
||||
( ! feature.enabled &&
|
||||
button.showWhen === 'disabled' )
|
||||
)
|
||||
.map( ( button ) => ( {
|
||||
...button,
|
||||
url: button.urls
|
||||
? merchant?.isSandbox
|
||||
? button.urls.sandbox
|
||||
: button.urls.live
|
||||
: button.url,
|
||||
} ) ),
|
||||
isBusy: isRefreshing,
|
||||
enabled: feature.enabled,
|
||||
notes: feature.notes,
|
||||
badge: feature.enabled
|
||||
? {
|
||||
text: __(
|
||||
'Active',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
type: TITLE_BADGE_POSITIVE,
|
||||
}
|
||||
: undefined,
|
||||
} }
|
||||
/>
|
||||
);
|
||||
} ) }
|
||||
const OverviewFeatureItem = ( {
|
||||
isBusy,
|
||||
isSandbox,
|
||||
title,
|
||||
description,
|
||||
buttons,
|
||||
enabled,
|
||||
notes,
|
||||
} ) => {
|
||||
const getButtonUrl = ( button ) => {
|
||||
if ( button.urls ) {
|
||||
return isSandbox ? button.urls.sandbox : button.urls.live;
|
||||
}
|
||||
|
||||
return button.url;
|
||||
};
|
||||
|
||||
const visibleButtons = buttons.filter(
|
||||
( button ) =>
|
||||
! button.showWhen || // Learn more buttons
|
||||
( enabled && button.showWhen === 'enabled' ) ||
|
||||
( ! enabled && button.showWhen === 'disabled' )
|
||||
);
|
||||
|
||||
const actionProps = {
|
||||
isBusy,
|
||||
enabled,
|
||||
notes,
|
||||
buttons: visibleButtons.map( ( button ) => ( {
|
||||
...button,
|
||||
url: getButtonUrl( button ),
|
||||
} ) ),
|
||||
};
|
||||
|
||||
if ( enabled ) {
|
||||
actionProps.badge = {
|
||||
text: __( 'Active', 'woocommerce-paypal-payments' ),
|
||||
type: TITLE_BADGE_POSITIVE,
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Content>
|
||||
<FeatureSettingsBlock
|
||||
title={ title }
|
||||
description={ description }
|
||||
actionProps={ actionProps }
|
||||
/>
|
||||
</Content>
|
||||
);
|
||||
};
|
||||
|
||||
<SettingsCard
|
||||
className="ppcp-r-tab-overview-help"
|
||||
title={ __( 'Help Center', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Access detailed guides and responsive support to streamline setup and enhance your experience.',
|
||||
const OverviewFeatureDescription = ( { refreshHandler, isRefreshing } ) => {
|
||||
const buttonLabel = isRefreshing
|
||||
? __( 'Refreshing…', 'woocommerce-paypal-payments' )
|
||||
: __( 'Refresh', 'woocommerce-paypal-payments' );
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
{ __(
|
||||
'Enable additional features and capabilities on your WooCommerce store.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
contentItems={ [
|
||||
</p>
|
||||
<p>
|
||||
{ __(
|
||||
'Click Refresh to update your current features after making changes.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</p>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
onClick={ refreshHandler }
|
||||
disabled={ isRefreshing }
|
||||
>
|
||||
<Icon icon={ reusableBlock } size={ 18 } />
|
||||
{ buttonLabel }
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const OverviewHelp = () => {
|
||||
return (
|
||||
<SettingsCard
|
||||
className="ppcp-r-tab-overview-help"
|
||||
title={ __( 'Help Center', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Access detailed guides and responsive support to streamline setup and enhance your experience.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
contentContainer={ false }
|
||||
>
|
||||
<ContentWrapper>
|
||||
<Content>
|
||||
<FeatureSettingsBlock
|
||||
key="documentation"
|
||||
title={ __(
|
||||
'Documentation',
|
||||
'woocommerce-paypal-payments'
|
||||
|
@ -212,9 +260,11 @@ const TabOverview = () => {
|
|||
},
|
||||
],
|
||||
} }
|
||||
/>,
|
||||
/>
|
||||
</Content>
|
||||
|
||||
<Content>
|
||||
<FeatureSettingsBlock
|
||||
key="support"
|
||||
title={ __( 'Support', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Need help? Access troubleshooting tips or contact our support team for personalized assistance.',
|
||||
|
@ -232,11 +282,9 @@ const TabOverview = () => {
|
|||
},
|
||||
],
|
||||
} }
|
||||
/>,
|
||||
] }
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
</Content>
|
||||
</ContentWrapper>
|
||||
</SettingsCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default TabOverview;
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import { useCallback } from '@wordpress/element';
|
||||
|
||||
import SettingsCard from '../../../ReusableComponents/SettingsCard';
|
||||
import { PaymentMethodsBlock } from '../../../ReusableComponents/SettingsBlocks';
|
||||
import { PaymentHooks } from '../../../../data';
|
||||
import { useActiveModal } from '../../../../data/common/hooks';
|
||||
import Modal from '../Components/Payment/Modal';
|
||||
|
||||
const TabPaymentMethods = () => {
|
||||
const methods = PaymentHooks.usePaymentMethods();
|
||||
const { setPersistent, changePaymentSettings } = PaymentHooks.useStore();
|
||||
const { activeModal, setActiveModal } = useActiveModal();
|
||||
|
||||
const getActiveMethod = () => {
|
||||
if ( ! activeModal ) {
|
||||
return null;
|
||||
}
|
||||
return methods.all.find( ( method ) => method.id === activeModal );
|
||||
};
|
||||
|
||||
const handleSave = useCallback(
|
||||
( methodId, settings ) => {
|
||||
changePaymentSettings( methodId, {
|
||||
title: settings.checkoutPageTitle,
|
||||
description: settings.checkoutPageDescription,
|
||||
} );
|
||||
|
||||
const persistentSettings = [
|
||||
'paypalShowLogo',
|
||||
'threeDSecure',
|
||||
'fastlaneCardholderName',
|
||||
'fastlaneDisplayWatermark',
|
||||
];
|
||||
|
||||
persistentSettings.forEach( ( setting ) => {
|
||||
if ( setting in settings ) {
|
||||
// TODO: Create a dedicated setter for those values.
|
||||
setPersistent( setting, settings[ setting ] );
|
||||
}
|
||||
} );
|
||||
|
||||
setActiveModal( null );
|
||||
},
|
||||
[ changePaymentSettings, setActiveModal, setPersistent ]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="ppcp-r-payment-methods">
|
||||
<PaymentMethodCard
|
||||
id="ppcp-paypal-checkout-card"
|
||||
title={ __( 'PayPal Checkout', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Select your preferred checkout option with PayPal for easy payment processing.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
icon="icon-checkout-standard.svg"
|
||||
methods={ methods.paypal }
|
||||
onTriggerModal={ setActiveModal }
|
||||
/>
|
||||
<PaymentMethodCard
|
||||
id="ppcp-card-payments-card"
|
||||
title={ __(
|
||||
'Online Card Payments',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Select your preferred card payment options for efficient payment processing.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
icon="icon-checkout-online-methods.svg"
|
||||
methods={ methods.cardPayment }
|
||||
onTriggerModal={ setActiveModal }
|
||||
/>
|
||||
<PaymentMethodCard
|
||||
id="ppcp-alternative-payments-card"
|
||||
title={ __(
|
||||
'Alternative Payment Methods',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'With alternative payment methods, customers across the globe can pay with their bank accounts and other local payment methods.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
icon="icon-checkout-alternative-methods.svg"
|
||||
methods={ methods.apm }
|
||||
onTriggerModal={ setActiveModal }
|
||||
/>
|
||||
|
||||
{ activeModal && (
|
||||
<Modal
|
||||
method={ getActiveMethod() }
|
||||
setModalIsVisible={ () => setActiveModal( null ) }
|
||||
onSave={ handleSave }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TabPaymentMethods;
|
||||
|
||||
const PaymentMethodCard = ( {
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
icon,
|
||||
methods,
|
||||
onTriggerModal,
|
||||
} ) => (
|
||||
<SettingsCard
|
||||
id={ id }
|
||||
title={ title }
|
||||
description={ description }
|
||||
icon={ icon }
|
||||
contentContainer={ false }
|
||||
>
|
||||
<PaymentMethodsBlock
|
||||
paymentMethods={ methods }
|
||||
onTriggerModal={ onTriggerModal }
|
||||
/>
|
||||
</SettingsCard>
|
||||
);
|
|
@ -1,7 +1,7 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import TabOverview from './TabOverview';
|
||||
import TabPaymentMethods from '../../Overview/TabPaymentMethods';
|
||||
import TabPaymentMethods from './TabPaymentMethods';
|
||||
import TabSettings from './TabSettings';
|
||||
import TabStyling from './TabStyling';
|
||||
import TabPayLaterMessaging from '../../Overview/TabPayLaterMessaging';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue