Implement product view

This commit is contained in:
inpsyde-maticluznar 2024-10-24 13:54:50 +02:00
parent 1ced06b24e
commit fc51e7f1a3
No known key found for this signature in database
GPG key ID: D005973F231309F6
18 changed files with 444 additions and 52 deletions

View file

@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="20" height="20" rx="1" fill="#3858E9"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.941 6.64853L8.94251 14.7159L5.1789 11.9174L5.92476 10.9144L8.68527 12.9669L13.9379 5.90267L14.941 6.64853Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 332 B

View file

@ -0,0 +1,10 @@
<svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.66675 21.75H47.1251C47.8857 21.75 48.602 22.1081 49.0584 22.7167L55.1001 30.7722C55.4138 31.1905 55.5834 31.6993 55.5834 32.2222V43.5C55.5834 44.8347 54.5014 45.9167 53.1667 45.9167H9.66675C8.33206 45.9167 7.25008 44.8347 7.25008 43.5V24.1667C7.25008 22.832 8.33206 21.75 9.66675 21.75Z" fill="#DD7F57"/>
<path d="M2.41675 9.66667C2.41675 8.33198 3.49873 7.25 4.83341 7.25H26.5834C27.9181 7.25 29.0001 8.33198 29.0001 9.66667V29C29.0001 30.3347 27.9181 31.4167 26.5834 31.4167H4.83341C3.49873 31.4167 2.41675 30.3347 2.41675 29V9.66667Z" fill="#5BBBFC"/>
<path d="M9.66675 12.0833C8.33206 12.0833 7.25008 13.1653 7.25008 14.5V33.8333C7.25008 35.168 8.33206 36.25 9.66675 36.25H38.6667C40.0014 36.25 41.0834 35.168 41.0834 33.8333V14.5C41.0834 13.1653 40.0014 12.0833 38.6667 12.0833H9.66675Z" fill="#4F2825"/>
<path d="M16.9167 50.75C19.5861 50.75 21.7501 48.586 21.7501 45.9167C21.7501 43.2473 19.5861 41.0833 16.9167 41.0833C14.2474 41.0833 12.0834 43.2473 12.0834 45.9167C12.0834 48.586 14.2474 50.75 16.9167 50.75Z" fill="#4F2825"/>
<path d="M50.7501 45.9167C50.7501 48.586 48.5861 50.75 45.9167 50.75C43.2474 50.75 41.0834 48.586 41.0834 45.9167C41.0834 43.2473 43.2474 41.0833 45.9167 41.0833C48.5861 41.0833 50.7501 43.2473 50.7501 45.9167Z" fill="#4F2825"/>
<path d="M50.1459 32.474C50.4796 32.474 50.7501 32.2035 50.7501 31.8698V30.9635C50.7501 30.6299 50.4796 30.3594 50.1459 30.3594H44.1042C43.7706 30.3594 43.5001 30.6299 43.5001 30.9635V31.8698C43.5001 32.2035 43.7706 32.474 44.1042 32.474H50.1459Z" fill="#4F2825"/>
<path d="M29.0001 12.0833V29C29.0001 30.3347 27.9181 31.4167 26.5834 31.4167H7.25008V14.5C7.25008 13.1653 8.33206 12.0833 9.66675 12.0833H29.0001Z" fill="#FAF8F5"/>
<path d="M20.104 22.7004H12.3855C12.0518 22.7004 11.7813 22.4299 11.7813 22.0962V21.19C11.7813 20.8563 12.0518 20.5858 12.3855 20.5858H20.1038L17.5027 17.9847C17.2667 17.7488 17.2667 17.3662 17.5027 17.1303L18.1435 16.4895C18.3794 16.2535 18.762 16.2535 18.9979 16.4895L23.7244 21.216C23.9604 21.4519 23.9604 21.8345 23.7244 22.0704L18.9979 26.7969C18.762 27.0329 18.3794 27.0329 18.1435 26.7969L17.5027 26.1561C17.2667 25.9202 17.2667 25.5376 17.5027 25.3017L20.104 22.7004Z" fill="#001C64"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,6 @@
<svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M38.6666 16.9167H14.4999C13.1652 16.9167 12.0833 17.9986 12.0833 19.3333V54.5628C12.0833 55.022 12.5754 55.3134 12.978 55.0925L25.9186 47.9932C26.0955 47.8962 26.3091 47.8938 26.4881 47.987L40.2002 55.1237C40.6024 55.333 41.0833 55.0412 41.0833 54.5878V19.3333C41.0833 17.9986 40.0013 16.9167 38.6666 16.9167Z" fill="#5BBBFC"/>
<path d="M43.4999 7.25H19.3333C17.9986 7.25 16.9166 8.33198 16.9166 9.66667V44.8961C16.9166 45.3554 17.4087 45.6467 17.8113 45.4258L30.7519 38.3265C30.9288 38.2295 31.1425 38.2272 31.3214 38.3203L45.0335 45.457C45.4357 45.6664 45.9166 45.3745 45.9166 44.9211V9.66667C45.9166 8.33198 44.8346 7.25 43.4999 7.25Z" fill="#6FC400"/>
<path d="M41.0833 43.4011V19.3333C41.0833 17.9986 40.0013 16.9167 38.6666 16.9167H16.9166V44.8961C16.9166 45.3554 17.4087 45.6467 17.8113 45.4258L30.7519 38.3265C30.9288 38.2295 31.1425 38.2272 31.3214 38.3203L41.0833 43.4011Z" fill="white"/>
<path d="M27.7916 33.747C27.7916 34.0807 28.0621 34.3512 28.3958 34.3512H29.6041C29.9378 34.3512 30.2083 34.0807 30.2083 33.747V28.3958H35.0416C35.3753 28.3958 35.6458 28.1253 35.6458 27.7917V26.5833C35.6458 26.2497 35.3753 25.9792 35.0416 25.9792H30.2083V21.1458C30.2083 20.8122 29.9378 20.5417 29.6041 20.5417H28.3958C28.0621 20.5417 27.7916 20.8122 27.7916 21.1458V25.9792H22.9583C22.6246 25.9792 22.3541 26.2497 22.3541 26.5833V27.7917C22.3541 28.1253 22.6246 28.3958 22.9583 28.3958H27.7916V33.747Z" fill="#001C64"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,8 @@
<svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.25008 19.3333C7.25008 17.9986 8.33206 16.9167 9.66675 16.9167H48.3334C49.6681 16.9167 50.7501 17.9986 50.7501 19.3333V44.7083H7.25008V19.3333Z" fill="#D8A6FF"/>
<path d="M12.0834 9.66667C12.0834 8.33198 13.1654 7.25 14.5001 7.25H43.5001C44.8348 7.25 45.9167 8.33198 45.9167 9.66667V24.1667C45.9167 25.5014 44.8348 26.5833 43.5001 26.5833H14.5001C13.1654 26.5833 12.0834 25.5014 12.0834 24.1667V9.66667Z" fill="#5BBBFC"/>
<path d="M2.41675 44.1042C2.41675 43.7705 2.68724 43.5 3.02091 43.5H54.9792C55.3129 43.5 55.5834 43.7705 55.5834 44.1042V45.9167C55.5834 47.2514 54.5014 48.3333 53.1667 48.3333H4.83342C3.49873 48.3333 2.41675 47.2514 2.41675 45.9167V44.1042Z" fill="#7252CC"/>
<path d="M45.9167 16.9167V24.1667C45.9167 25.5014 44.8348 26.5833 43.5001 26.5833H14.5001C13.1654 26.5833 12.0834 25.5014 12.0834 24.1667V16.9167H45.9167Z" fill="#FAF8F5"/>
<path d="M21.7501 44.7083C21.7501 44.041 22.2911 43.5 22.9584 43.5H35.0417C35.7091 43.5 36.2501 44.041 36.2501 44.7083C36.2501 45.3757 35.7091 45.9167 35.0417 45.9167H22.9584C22.2911 45.9167 21.7501 45.3757 21.7501 44.7083Z" fill="#FAF8F5"/>
<path d="M36.2501 16.9167C36.2501 20.9207 33.0041 24.1667 29.0001 24.1667C24.996 24.1667 21.7501 20.9207 21.7501 16.9167C21.7501 12.9126 24.996 9.66667 29.0001 9.66667C33.0041 9.66667 36.2501 12.9126 36.2501 16.9167Z" fill="#001C64"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -6,3 +6,23 @@
letter-spacing: $letter-spacing;
}
}
@mixin hide-input-field(){
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
margin: 0;
opacity: 0;
border-radius: 0;
}
@mixin fake-input-field($border-radius:0){
width: 20px;
height: 20px;
border: 1px solid $color-gray-600;
display: block;
pointer-events: none;
border-radius: $border-radius;
}

View file

@ -1,13 +1,23 @@
button.components-button {
&.is-primary, &.is-secondary {
background-color: $color-blueberry;
&:not(:disabled) {
background-color: $color-blueberry;
&:hover {
background: $gradient-header;
}
}
&:disabled {
background-color: $color-gray-500;
color: $color-white;
}
border-radius: 2px;
padding: 14px 17px;
height: auto;
&:hover {
background: $gradient-header;
}
}
&.is-primary {

View file

@ -9,7 +9,7 @@
position: relative;
width: 100%;
border: 1px solid $color-gray-500;
outline:1px solid transparent;
outline: 1px solid transparent;
border-radius: 8px;
display: flex;
gap: 32px;
@ -25,14 +25,8 @@
}
&__radio-value {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
margin: 0;
opacity: 0;
border-radius: 0;
@include hide-input-field;
&:checked {
+ .ppcp-r-select-box__radio-presentation {
background: $color-white;
@ -43,6 +37,26 @@
}
}
&__checkbox-value {
@include hide-input-field;
&:not(:checked) + .ppcp-r-select-box__checkbox-presentation img {
display: none;
}
&:checked {
+ .ppcp-r-select-box__checkbox-presentation {
width: 20px;
height: 20px;
border: none;
img {
border-radius: 2px;
}
}
}
}
&__content {
display: flex;
gap: 18px;
@ -62,11 +76,26 @@
}
&__radio-presentation {
width: 20px;
height: 20px;
border: 1px solid $color-gray-600;
border-radius: 20px;
display: block;
pointer-events: none;
@include fake-input-field(20px);
}
&__checkbox-presentation {
@include fake-input-field(2px);
}
@media screen and (max-width: 480px) {
gap: 16px;
padding: 18px 16px;
&__description {
margin: 0 0 8px 0;
}
&__content {
gap: 12px;
}
}
@media screen and (max-width: 380px) {
&__content > img {
max-width: 32px;
}
}
}

View file

@ -0,0 +1,44 @@
.ppcp-r-services {
display: flex;
flex-wrap: wrap;
gap: 4px 18px;
margin: 0;
li {
display: flex;
align-items: center;
gap: 4px;
color: $color-gray-700;
@include font(14, 20, 400);
margin: 0;
&::before {
content: "";
}
}
}
.ppcp-r-page-products {
.ppcp-r-inner-container {
width: 622px;
padding-bottom: 48px;
@media screen and (max-width: 480px) {
padding-bottom: 24px;
}
}
.ppcp-r-payment-method-icons {
justify-content: flex-start;
}
.ppcp-r-select-box-wrapper {
margin: 0 0 48px 0;
}
.ppcp-r-select-box__additional-content {
a {
@include font(12, 20, 400);
color: $color-blueberry;
}
}
}

View file

@ -14,4 +14,5 @@
@import './components/reusable-components/navigation';
@import './components/screens/onboarding/step-welcome';
@import './components/screens/onboarding/step-business';
@import './components/screens/onboarding/step-products';
}

View file

@ -1,9 +1,8 @@
import Onboarding from './Components/Screens/Onboarding/Onboarding.js';
import Settings from './Components/Screens/Settings';
export function App() {
return (
<div>
<Onboarding />
<Settings />
</div>
);
}

View file

@ -1,18 +1,29 @@
import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
const Navigation = ( { setStep, currentStep } ) => {
const Navigation = ( {
setStep,
currentStep,
stepperOrder,
canProceeedCallback = () => true,
} ) => {
const setNextStep = ( nextStep ) => {
let newStep = currentStep + nextStep;
if ( newStep > stepperOrder.length - 1 ) {
newStep = currentStep;
}
setStep( newStep );
};
return (
<div className="ppcp-r-navigation">
<Button
variant="tertiary"
onClick={ () => setStep( currentStep - 1 ) }
>
<Button variant="tertiary" onClick={ () => setNextStep( -1 ) }>
{ __( 'Back', 'woocommerce-paypal-payments' ) }
</Button>
<Button
variant="primary"
onClick={ () => setStep( currentStep + 1 ) }
disabled={ ! canProceeedCallback() }
onClick={ () => setNextStep( 1 ) }
>
{ __( 'Next', 'woocommerce-paypal-payments' ) }
</Button>

View file

@ -1,23 +1,61 @@
import data from '../../utils/data';
const SelectBox = ( props ) => {
const handleCheckboxState = ( checked ) => {
let newValue = null;
if ( checked ) {
newValue = [ ...props.currentValue, props.value ];
props.changeCallback( newValue );
} else {
newValue = props.currentValue.filter(
( value ) => value !== props.value
);
}
props.changeCallback( newValue );
};
let boxClassName = 'ppcp-r-select-box';
if ( props.value === props.currentValue ) {
if (
props.value === props.currentValue ||
( Array.isArray( props.currentValue ) &&
props.currentValue.includes( props.value ) )
) {
boxClassName += ' selected';
}
return (
<div className={ boxClassName }>
<div className="ppcp-r-select-box__radio">
<input
className="ppcp-r-select-box__radio-value"
type="radio"
name={ props.name }
value={ props.value }
onChange={ () => props.changeCallback( props.value ) }
/>
<span className="ppcp-r-select-box__radio-presentation"></span>
</div>
{ props.type === 'radio' && (
<div className="ppcp-r-select-box__radio">
<input
className="ppcp-r-select-box__radio-value"
type="radio"
checked={ props.value === props.currentValue }
name={ props.name }
value={ props.value }
onChange={ () => props.changeCallback( props.value ) }
/>
<span className="ppcp-r-select-box__radio-presentation"></span>
</div>
) }
{ props.type === 'checkbox' && (
<div className="ppcp-r-select-box__checkbox">
<input
className="ppcp-r-select-box__checkbox-value"
type="checkbox"
checked={ props.currentValue.includes( props.value ) }
name={ props.name }
value={ props.value }
onChange={ ( e ) =>
handleCheckboxState( e.target.checked )
}
/>
<span className="ppcp-r-select-box__checkbox-presentation">
{ data().getImage( 'icon-checkbox.svg' ) }
</span>
</div>
) }
<div className="ppcp-r-select-box__content">
{ data().getImage( props.icon ) }
<div className="ppcp-r-select-box__content-inner">

View file

@ -0,0 +1,45 @@
import { TabPanel } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
const onSelect = ( tabName ) => {
console.log( 'Selecting tab', tabName );
};
const TabNavigation = () => {
return (
<TabPanel
className="my-tab-panel"
activeClass="active-tab"
onSelect={ onSelect }
tabs={ [
{
name: 'dashboard',
title: __( 'Dashboard', 'woocommerce-paypal-payments' ),
className: 'ppcp-r-tab-dashboard',
},
{
name: 'payment-methods',
title: __(
'Payment Methods',
'woocommerce-paypal-payments'
),
className: 'ppcp-r-tab-payment-methods',
},
{
name: 'settings',
title: __( 'Settings', 'woocommerce-paypal-payments' ),
className: 'ppcp-r-tab-settings',
},
{
name: 'styling',
title: __( 'Styling', 'woocommerce-paypal-payments' ),
className: 'ppcp-r-tab-styling',
},
] }
>
{ ( tab ) => <p>{ tab.title }</p> }
</TabPanel>
);
};
export default TabNavigation;

View file

@ -0,0 +1,11 @@
import TabNavigation from '../../ReusableComponents/TabNavigation';
const Dashboard = () => {
return (
<div>
<TabNavigation />
</div>
);
};
export default Dashboard;

View file

@ -1,7 +1,9 @@
import Container from '../../ReusableComponents/Container.js';
import StepWelcome from './StepWelcome.js';
import StepBusiness from './StepBusiness';
import StepBusiness from './StepBusiness.js';
import StepProducts from './StepProducts.js';
import { useState } from '@wordpress/element';
import Dashboard from '../Dashboard/Dashboard';
const Onboarding = () => {
const [ step, setStep ] = useState( 0 );
@ -16,18 +18,26 @@ const Onboarding = () => {
};
const Stepper = ( { currentStep, setStep } ) => {
const stepperOrder = {
0: StepWelcome,
1: StepBusiness,
const stepperOrder = [ StepWelcome, StepBusiness, StepProducts ];
const renderSteps = () => {
return stepperOrder.map( ( Step, index ) => {
return (
<div
key={ index }
style={ index !== currentStep ? { display: 'none' } : {} }
>
<Step
setStep={ setStep }
currentStep={ currentStep }
stepperOrder={ stepperOrder }
/>
</div>
);
} );
};
const Component = stepperOrder[ currentStep ];
return (
<>
<Component setStep={ setStep } currentStep={ currentStep } />
</>
);
return <>{ renderSteps() }</>;
};
export default Onboarding;

View file

@ -6,7 +6,7 @@ import PaymentMethodIcons from '../../ReusableComponents/PaymentMethodIcons';
import { useState } from '@wordpress/element';
import Navigation from '../../ReusableComponents/Navigation';
const StepBusiness = ( { setStep, currentStep } ) => {
const StepBusiness = ( { setStep, currentStep, stepperOrder } ) => {
const [ businessCategory, setBusinessCategory ] = useState( null );
const BUSINESS_RADIO_GROUP_NAME = 'business';
const CASUAL_SELLER_CHECKBOX_VALUE = 'casual_seller';
@ -40,6 +40,7 @@ const StepBusiness = ( { setStep, currentStep } ) => {
businessCategory ===
{ CASUAL_SELLER_CHECKBOX_VALUE }
}
type="radio"
>
<PaymentMethodIcons
icons={ [
@ -69,6 +70,7 @@ const StepBusiness = ( { setStep, currentStep } ) => {
checked={
businessCategory === { BUSINESS_CHECKBOX_VALUE }
}
type="radio"
>
<PaymentMethodIcons
icons={ [
@ -85,7 +87,12 @@ const StepBusiness = ( { setStep, currentStep } ) => {
/>
</SelectBox>
</SelectBoxWrapper>
<Navigation setStep={ setStep } currentStep={ currentStep } />
<Navigation
setStep={ setStep }
currentStep={ currentStep }
stepperOrder={ stepperOrder }
canProceeedCallback={ () => businessCategory !== null }
/>
</div>
</div>
);

View file

@ -0,0 +1,128 @@
import OnboardingHeader from '../../ReusableComponents/OnboardingHeader';
import Navigation from '../../ReusableComponents/Navigation';
import { __ } from '@wordpress/i18n';
import SelectBox from '../../ReusableComponents/SelectBox';
import SelectBoxWrapper from '../../ReusableComponents/SelectBoxWrapper';
import { useState } from '@wordpress/element';
const StepProducts = ( { setStep, currentStep, stepperOrder } ) => {
const [ products, setProducts ] = useState( [] );
const PRODUCTS_CHECKBOX_GROUP_NAME = 'products';
const VIRTUAL_CHECKBOX_VALUE = 'virtual';
const PHYSICAL_CHECKBOX_VALUE = 'physical';
const SUBSCRIPTIONS_CHECKBOX_VALUE = 'subscriptions';
return (
<div className="ppcp-r-page-products">
<OnboardingHeader
title={ __(
'Tell Us About the Products You Sell',
'woocommerce-paypal-payments'
) }
/>
<div className="ppcp-r-inner-container">
<SelectBoxWrapper>
<SelectBox
title={ __( 'Virtual', 'woocommerce-paypal-payments' ) }
description={ __(
'Digital items or services that dont require shipping.',
'woocommerce-paypal-payments'
) }
icon="icon-product-virtual.svg"
name={ PRODUCTS_CHECKBOX_GROUP_NAME }
value={ VIRTUAL_CHECKBOX_VALUE }
changeCallback={ setProducts }
currentValue={ products }
type="checkbox"
>
<ul className="ppcp-r-services">
<li>
{ __(
'Services',
'woocommerce-paypal-payments'
) }
</li>
<li>
{ __(
'Downloadable',
'woocommerce-paypal-payments'
) }
</li>
<li>
{ __(
'Bookings',
'woocommerce-paypal-payments'
) }
</li>
<li>
{ __(
'Deposits',
'woocommerce-paypal-payments'
) }
</li>
</ul>
</SelectBox>
<SelectBox
title={ __(
'Physical Goods',
'woocommerce-paypal-payments'
) }
description={ __(
'Items that need to be shipped.',
'woocommerce-paypal-payments'
) }
icon="icon-product-physical.svg"
name={ PRODUCTS_CHECKBOX_GROUP_NAME }
value={ PHYSICAL_CHECKBOX_VALUE }
changeCallback={ setProducts }
currentValue={ products }
type="checkbox"
>
<ul className="ppcp-r-services">
<li>
{ __( 'Goods', 'woocommerce-paypal-payments' ) }
</li>
<li>
{ __(
'Deliveries',
'woocommerce-paypal-payments'
) }
</li>
</ul>
</SelectBox>
<SelectBox
title={ __(
'Subscriptions',
'woocommerce-paypal-payments'
) }
description={ __(
'Recurring payments for physical goods or services.',
'woocommerce-paypal-payments'
) }
icon="icon-product-subscription.svg"
name={ PRODUCTS_CHECKBOX_GROUP_NAME }
value={ SUBSCRIPTIONS_CHECKBOX_VALUE }
changeCallback={ setProducts }
currentValue={ products }
type="checkbox"
>
<a href="#">
{ __(
'WooCommerce Subscriptions - TODO missing link',
'woocommerce-paypal-payments'
) }
</a>
</SelectBox>
</SelectBoxWrapper>
<Navigation
setStep={ setStep }
currentStep={ currentStep }
stepperOrder={ stepperOrder }
canProceeedCallback={ () => products.length > 0 }
/>
</div>
</div>
);
};
export default StepProducts;

View file

@ -0,0 +1,11 @@
import Onboarding from './Onboarding/Onboarding';
import { useState } from '@wordpress/element';
import Dashboard from './Dashboard/Dashboard';
const Settings = () => {
const [ onboarded, setOnboarded ] = useState( true );
return <>{ onboarded ? <Onboarding /> : <Dashboard /> }</>;
};
export default Settings;