🔀 Merge branch 'PCP-3793’

# Conflicts:
#	modules/ppcp-settings/resources/js/App.js
This commit is contained in:
Philipp Stracker 2024-10-23 13:29:00 +02:00
commit 180df1001e
No known key found for this signature in database
46 changed files with 957 additions and 35 deletions

View file

@ -0,0 +1,12 @@
* {
font-family: "Inter", sans-serif;
-webkit-font-smoothing: antialiased;
}
a:not(.button) {
color: $color-blueberry;
}
.components-form-toggle.is-checked > .components-form-toggle__track {
background-color: $color-blueberry;
}

View file

@ -0,0 +1,8 @@
@mixin font($font-size, $line-height, $font-weight, $letter-spacing: false) {
font-size: $font-size + px;
line-height: calc($line-height / $font-size);
font-weight: $font-weight;
@if $letter-spacing {
letter-spacing: $letter-spacing;
}
}

View file

@ -0,0 +1,13 @@
$color-white: #fff;
$color-blue: #1D35B4;
$color-blueberry: #3858E9;
$color-gray-900: #1E1E1E;
$color-gray-800: #2F2F2F;
$color-gray-700: #757575;
$color-gray-600: #949494;
$color-gray-500: #BBBBBB;
$color-gray-200: #E0E0E0;
$color-gray: #646970;
$shadow-card: 0 3px 6px 0 rgba(0, 0, 0, 0.15);
$gradient-header: linear-gradient(87.03deg, #003087 -0.49%, #001E51 29.22%, #001435 100%);

View file

@ -0,0 +1,22 @@
button.components-button {
&.is-primary, &.is-secondary {
background-color: $color-blueberry;
border-radius: 2px;
padding: 14px 17px;
height: auto;
&:hover {
background: $gradient-header;
}
}
&.is-primary {
@include font(13, 16, 500);
}
&.is-secondary {
padding: 6px 12px;
@include font(13, 20, 500);
color: $color-white;
border: none;
}
}

View file

@ -0,0 +1,52 @@
.ppcp-r-onboarding-header{
margin: 0 0 32px 0;
&__gradient {
background: $gradient-header;
width: 100%;
height: 112px;
position: relative;
margin-bottom: 55px;
}
&__logo-wrapper {
width: 110px;
height: 110px;
background-color: $color-white;
border-radius: 110px;
position: absolute;
left: calc(50% - 55px);
bottom: -55px;
img {
width: 56px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
&__content {
max-width: 458px;
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
padding:0 16px;
}
&__title {
@include font(20, 28, 700);
color: $color-blue;
margin: 0 0 4px 0;
text-align: center;
}
&__description {
color: $color-gray-800;
@include font(14, 20, 400);
margin: 0;
text-align: center;
}
}

View file

@ -0,0 +1,6 @@
.ppcp-r-payment-method-icons {
display: flex;
gap: 8px;
justify-content: center;
flex-wrap: wrap;
}

View file

@ -0,0 +1,18 @@
.ppcp-r-separator{
display: flex;
align-items: center;
&__line {
height: 1px;
background-color: $color-gray-600;
display: block;
width: 100%;
}
&__text {
color: $color-gray;
@include font(12, 24, 500, 0.8px);
text-transform: uppercase;
padding: 0 23px;
}
}

View file

@ -0,0 +1,30 @@
.ppcp-r-toggle-block {
&__wrapper {
display: flex;
width: 100%;
gap: 12px;
}
&__switch {
.components-base-control__field > div {
display: block;
}
}
&__content-label {
@include font(14, 20, 600);
display: block;
margin: 0 0 4px 0;
color: $color-gray-900;
}
&__content-description {
@include font(13, 18, 400);
color: $color-gray-700;
margin: 0;
}
&__toggled-content {
margin-top: 24px;
}
}

View file

@ -0,0 +1,21 @@
.ppcp-r {
&-inner-container {
width: 652px;
max-width: 100%;
margin: 0 auto;
padding:0 16px;
box-sizing: border-box;
}
&-container {
box-shadow: $shadow-card;
max-width: 1024px;
margin: 0 auto;
}
&-card {
box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.15);
background-color: $color-white;
}
}

View file

@ -0,0 +1,17 @@
.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

@ -0,0 +1,83 @@
.ppcp-r-page-welcome {
.ppcp-r-inner-container {
padding-bottom: 72px;
@media screen and (max-width: 480px) {
padding-bottom: 36px;
}
}
.ppcp-r-welcome-features {
margin: 0 0 32px 0;
}
.ppcp-r-payment-method-icons {
margin: 0 0 12px 0;
}
.ppcp-r-button-activate-paypal {
display: block;
margin: 0 auto 32px auto;
}
.ppcp-r-page-welcome-or-separator {
margin: 0 0 32px 0;
}
.ppcp-r-page-welcome-mode-separator {
margin: 32px 0;
.ppcp-r-separator__line {
background-color: $color-gray-500;
}
}
.components-base-control__field {
margin: 0 0 24px 0;
}
}
.ppcp-r-welcome-features {
display: flex;
justify-content: center;
padding: 8px;
&__col {
display: flex;
flex-direction: column;
gap: 4px;
> span {
@include font(11, 16, 600);
text-transform: uppercase;
color: $color-gray-800;
}
> p {
margin: 0;
@include font(13, 16, 400);
color: $color-gray-700;
}
&:not(:last-child) {
padding-right: 18px;
border-right: 1px solid $color-gray-200;
margin-right: 18px;
}
}
@media screen and (max-width: 480px) {
flex-wrap: wrap;
row-gap: 8px;
&__col {
width: 100%;
text-align: center;
&:not(:last-child) {
border-bottom: 1px solid $color-gray-200;
border-right: 0;
padding-right: 0;
padding-bottom: 8px;
}
}
}
}

View file

@ -1 +1,14 @@
.red {color:red;}
@import 'variables';
@import 'mixins';
#ppcp-settings-container {
@import './global';
@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/settings-wrapper';
@import './components/screens/onboarding/step-welcome';
}

View file

@ -1,40 +1,9 @@
import * as Store from './data';
const StoreTest = () => {
const { isSaving, onboardingStep, setOnboardingStep } =
Store.useOnboardingDetails();
return (
<div>
<hr />
<div>Onboarding Step: { onboardingStep }</div>
<div>{ isSaving ? 'Saving...' : 'Not Saving' }</div>
<div>
<button
type={ 'button' }
onClick={ () => setOnboardingStep( onboardingStep - 1 ) }
disabled={ onboardingStep < 1 }
>
Prev
</button>
<button
type={ 'button' }
onClick={ () => setOnboardingStep( onboardingStep + 1 ) }
disabled={ onboardingStep > 3 }
>
Next
</button>
</div>
</div>
);
};
import Onboarding from './components/screens/onboarding/onboarding.js';
export function App() {
return (
<div className="red">
App
<StoreTest />
<div>
<Onboarding />
</div>
);
}

View file

@ -0,0 +1,5 @@
const Container = ( props ) => {
return <div className="ppcp-r-container">{ props.children }</div>;
};
export default Container;

View file

@ -0,0 +1,25 @@
import data from '../../utils/data';
const OnboardingHeader = ( props ) => {
return (
<section className="ppcp-r-onboarding-header">
<div className="ppcp-r-onboarding-header__gradient">
<div className="ppcp-r-onboarding-header__logo-wrapper">
{ data().getImage( 'logo-paypal.svg' ) }
</div>
</div>
<div className="ppcp-r-onboarding-header__content">
<h1 className="ppcp-r-onboarding-header__title">
{ props.title }
</h1>
{ props.description && (
<p className="ppcp-r-onboarding-header__description">
{ props.description }
</p>
) }
</div>
</section>
);
};
export default OnboardingHeader;

View file

@ -0,0 +1,15 @@
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' );
}
return <></>;
};
export default PaymentMethodIcon;

View file

@ -0,0 +1,21 @@
import PaymentMethodIcon from './payment-method-icon';
const PaymentMethodIcons = ( props ) => {
return (
<div className="ppcp-r-payment-method-icons">
<PaymentMethodIcon type="paypal" icons={ props.icons } />
<PaymentMethodIcon type="venmo" icons={ props.icons } />
<PaymentMethodIcon type="visa" icons={ props.icons } />
<PaymentMethodIcon type="mastercard" icons={ props.icons } />
<PaymentMethodIcon type="amex" icons={ props.icons } />
<PaymentMethodIcon type="discover" icons={ props.icons } />
<PaymentMethodIcon type="apple-pay" icons={ props.icons } />
<PaymentMethodIcon type="google-pay" icons={ props.icons } />
<PaymentMethodIcon type="sepa" icons={ props.icons } />
<PaymentMethodIcon type="ideal" icons={ props.icons } />
<PaymentMethodIcon type="bancontact" icons={ props.icons } />
</div>
);
};
export default PaymentMethodIcons;

View file

@ -0,0 +1,26 @@
const Separator = ( props ) => {
let separatorClass = 'ppcp-r-separator';
if ( props?.className ) {
separatorClass += ' ' + props.className;
}
if ( props.text ) {
return (
<div className={ separatorClass }>
<span className="ppcp-r-separator__line ppcp-r-separator__line--before"></span>
<span className="ppcp-r-separator__text">{ props.text }</span>
<span className="ppcp-r-separator__line ppcp-r-separator__line--after"></span>
</div>
);
}
return (
<div className={ separatorClass }>
<span className="ppcp-r-separator__line ppcp-r-separator__line--before"></span>
</div>
);
};
export default Separator;

View file

@ -0,0 +1,43 @@
import { useState } from '@wordpress/element';
import { ToggleControl } from '@wordpress/components';
const SettingsToggleBlock = ( props ) => {
const [ isToggled, setToggled ] = useState( false );
return (
<div className="ppcp-r-toggle-block">
<div className="ppcp-r-toggle-block__wrapper">
<div className="ppcp-r-toggle-block__content">
{ props?.label && (
<span className="ppcp-r-toggle-block__content-label">
{ props.label }
</span>
) }
{ props?.description && (
<p
className="ppcp-r-toggle-block__content-description"
dangerouslySetInnerHTML={ {
__html: props.description,
} }
></p>
) }
</div>
<div className="ppcp-r-toggle-block__switch">
<ToggleControl
checked={ isToggled }
onChange={ ( newValue ) => {
setToggled( newValue );
} }
/>
</div>
</div>
{ props.children && isToggled && (
<div className="ppcp-r-toggle-block__toggled-content">
{ props.children }
</div>
) }
</div>
);
};
export default SettingsToggleBlock;

View file

@ -0,0 +1,14 @@
import Container from '../../reusable-components/container';
import StepWelcome from './step-welcome.js';
const Onboarding = () => {
return (
<Container>
<div className="ppcp-r-card">
<StepWelcome />
</div>
</Container>
);
};
export default Onboarding;

View file

@ -0,0 +1,130 @@
import OnboardingHeader from '../../reusable-components/onboarding-header.js';
import { __, sprintf } from '@wordpress/i18n';
import { Button, TextControl } from '@wordpress/components';
import PaymentMethodIcons from '../../reusable-components/payment-method-icons';
import SettingsToggleBlock from '../../reusable-components/settings-toggle-block';
import Separator from '../../reusable-components/separator';
const StepWelcome = () => {
return (
<div className="ppcp-r-page-welcome">
<OnboardingHeader
title={ __(
'Welcome to PayPal Payments',
'woocommerce-paypal-payments'
) }
description={ __(
'Your all-in-one checkout solution with PayPal, Venmo, Pay Later, all major credit/debit cards, Apple Pay, Google Pay, and more.',
'woocommerce-paypal-payments'
) }
/>
<div className="ppcp-r-inner-container">
<PaymentMethodIcons icons="all" />
<WelcomeFeatures />
<Button
className="ppcp-r-button-activate-paypal"
variant="primary"
>
{ __(
'Activate PayPal Payments',
'woocommerce-paypal-payments'
) }
</Button>
<Separator
className="ppcp-r-page-welcome-or-separator"
text={ __( 'or', 'woocommerce-paypal-payments' ) }
/>
<WelcomeForm />
</div>
</div>
);
};
const WelcomeFeatures = () => {
return (
<div className="ppcp-r-welcome-features">
<div className="ppcp-r-welcome-features__col">
<span>{ __( 'Deposits', 'woocommerce-paypal-payments' ) }</span>
<p>{ __( 'Instant', 'woocommerce-paypal-payments' ) }</p>
</div>
<div className="ppcp-r-welcome-features__col">
<span>
{ __( 'Payment Capture', 'woocommerce-paypal-payments' ) }
</span>
<p>
{ __(
'Authorize only or Capture',
'woocommerce-paypal-payments'
) }
</p>
</div>
<div className="ppcp-r-welcome-features__col">
<span>
{ __(
'Recurring payments',
'woocommerce-paypal-payments'
) }
</span>
<p>{ __( 'Supported', 'woocommerce-paypal-payments' ) }</p>
</div>
</div>
);
};
const WelcomeForm = () => {
const advancedUsersDescription = sprintf(
// translators: %s: Link to PayPal REST application guide
__(
'For advanced users: Connect a custom PayPal REST app for full control over your integration. For more information on creating a PayPal REST application, <a href="%s">click here</a>.',
'woocommerce-paypal-payments'
),
'#'
);
return (
<>
<SettingsToggleBlock
label={ __(
'Enable Sandbox Mode',
'woocommerce-paypal-payments'
) }
description={ __(
'Activate Sandbox mode to safely test PayPal with sample data. Once your store is ready to go live, you can easily switch to your production account.',
'woocommerce-paypal-payments'
) }
>
<Button variant="secondary">
{ __( 'Connect Account', 'woocommerce-paypal-payments' ) }
</Button>
</SettingsToggleBlock>
<Separator className="ppcp-r-page-welcome-mode-separator" />
<SettingsToggleBlock
label={ __(
'Manually Connect - TODO missing link',
'woocommerce-paypal-payments'
) }
description={ advancedUsersDescription }
>
<TextControl
label={ __(
'Sandbox Client ID',
'woocommerce-paypal-payments'
) }
></TextControl>
<TextControl
label={ __(
'Sandbox Secret Key',
'woocommerce-paypal-payments'
) }
type="password"
></TextControl>
<Button variant="secondary">
{ __( 'Connect Account', 'woocommerce-paypal-payments' ) }
</Button>
</SettingsToggleBlock>
</>
);
};
export default StepWelcome;

View file

@ -0,0 +1,18 @@
const data = () => {
return {
...global.ppcpSettings,
getImage( imageName, className = '' ) {
const pathToImages = global.ppcpSettings.assets.imagesUrl;
return (
<img
className={ className }
alt=""
src={ pathToImages + imageName }
/>
);
},
};
};
export default data;