Add Avalara to tax task (https://github.com/woocommerce/woocommerce-admin/pull/7874)
* Move woocommerce tax flow to subdirectory * Add partner cards * Use png for logo files * Add partner card other actions * Add partner card styling * Add in mobile styles * Interpolate links and html elements * Redirect to tax configuration if Avalara not supported * Mark task complete if Alavara is installed * Record events when task partners are shown or selected * Add changelog entry * Update task card flow based on visible partners * Skip plugin step if all plugins installed * Move reusable components into subdirectory * Record available partner options * Fix type reference * Wrap callback functions in useCallback to avoid rerenders * Handle PR feedback * Add key to partner card container * Add testing instructions
This commit is contained in:
parent
675cf379c1
commit
abc47adc95
|
@ -1,5 +1,46 @@
|
|||
# Testing instructions
|
||||
|
||||
## 2.9.0
|
||||
|
||||
### Add Avalara to tax task #7874
|
||||
|
||||
**Avalara supported, WooCommerce Tax supported**
|
||||
|
||||
1. Select an Avalara and WC Tax supported country (e.g., `US`) for your store's country
|
||||
2. Visit the tax task.
|
||||
3. Make sure your shown the "WooCommerce Tax" and "Avalara" options in the task list
|
||||
|
||||
**Avalara supported, WooCommerce Tax not supported**
|
||||
|
||||
1. Install the TaxJar plugin so that WC Tax is not supported and set your country to an Avalara supported country
|
||||
2. Visit the task tax.
|
||||
3. Make sure you are shown only the partner card of Avalara
|
||||
|
||||
**Avalara not supported, WooCommerce Tax not supported**
|
||||
|
||||
1. Set your store country to one not supported by Avalara or WC Tax (e.g., New Caledonia)
|
||||
2. Visit the task tax.
|
||||
3. Make sure you are immediately shown the manual set up flow with the "Configure" button
|
||||
|
||||
**Partner actions**
|
||||
1. Visit the task tax with an Avalara supported country
|
||||
2. Click Avalara and check that a new tab opens with the WCCOM plugin page
|
||||
3. Back on the task tax, click on WooCommerce Tax
|
||||
4. Make sure you are dropped into the old configuration flow (which should be identical to the old flow)
|
||||
|
||||
**Events**
|
||||
1. Enter `localStorage.setItem( 'debug', 'wc-admin:*' );` in your browser's console
|
||||
2. Set your store's country to an Avalara supported country
|
||||
3. Note the `wcadmin_tasklist_tax_view_options` event occurs
|
||||
4. Click on each of the partner action buttons
|
||||
5. Make sure that `wcadmin_tasklist_tax_select_option` is recorded with the respective `selected_option` partner key.
|
||||
|
||||
**Completion**
|
||||
1. Create a fresh site without ever having set any taxes
|
||||
2. Note the task is incomplete
|
||||
3. Install the WC Avalara plugin
|
||||
4. Check that the task is now marked complete
|
||||
|
||||
## 2.8.0
|
||||
|
||||
### Store Profiler and Product task - include Subscriptions #7734
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: Add
|
||||
|
||||
Add Avalara to tax task #7874
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { getAdminLink } from '@woocommerce/wc-admin-settings';
|
||||
import interpolateComponents from 'interpolate-components';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { PartnerCard } from '../components/partner-card';
|
||||
import logo from './logo.png';
|
||||
|
||||
export const Card = ( { isPending, tasksStatus } ) => {
|
||||
const { avalaraActivated } = tasksStatus;
|
||||
|
||||
return (
|
||||
<PartnerCard
|
||||
name={ __( 'Avalara', 'woocommerce-admin' ) }
|
||||
isPending={ isPending }
|
||||
logo={ logo }
|
||||
description={ __(
|
||||
'Powerful all-in-one tax tool',
|
||||
'woocommerce-admin'
|
||||
) }
|
||||
benefits={ [
|
||||
__( 'Real-time sales tax calculation', 'woocommerce-admin' ),
|
||||
interpolateComponents( {
|
||||
mixedString: __(
|
||||
'{{strong}}Multi{{/strong}}-economic nexus compliance',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
components: {
|
||||
strong: <strong />,
|
||||
},
|
||||
} ),
|
||||
__(
|
||||
'Cross-border and multi-channel compliance',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
__( 'Automate filing & remittance', 'woocommerce-admin' ),
|
||||
__(
|
||||
'Return-ready, jurisdiction-level reporting.',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
] }
|
||||
terms={ __(
|
||||
'30-day free trial. No credit card needed.',
|
||||
'woocommerce-admin'
|
||||
) }
|
||||
actionText={
|
||||
avalaraActivated
|
||||
? __( 'Continue setup', 'woocommerce-admin' )
|
||||
: __( 'Enable & set up', 'woocommerce-admin' )
|
||||
}
|
||||
onClick={ () => {
|
||||
recordEvent( 'tasklist_tax_select_option', {
|
||||
selected_option: 'avalara',
|
||||
} );
|
||||
|
||||
if ( avalaraActivated ) {
|
||||
window.location.href = getAdminLink(
|
||||
'/admin.php?page=wc-settings&tab=tax§ion=avatax'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
window.open(
|
||||
new URL(
|
||||
'https://woocommerce.com/products/woocommerce-avatax/'
|
||||
),
|
||||
'_blank'
|
||||
);
|
||||
} }
|
||||
/>
|
||||
);
|
||||
};
|
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
|
@ -0,0 +1,17 @@
|
|||
export const Bullet: React.FC = () => {
|
||||
return (
|
||||
<svg
|
||||
width="13"
|
||||
height="10"
|
||||
viewBox="0 0 13 10"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12.1883 1.1814L4.7091 8.66062L1.48438 5.4359"
|
||||
stroke="#4AB866"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
.woocommerce-tax-partner-card {
|
||||
border: 1px solid $gray-300;
|
||||
border-radius: 3px;
|
||||
margin: 0 $gap-smaller $gap $gap-smaller;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-tax-partner-card__logo {
|
||||
margin-bottom: $gap-smaller;
|
||||
img {
|
||||
max-height: 35px;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-tax-partner-card__description {
|
||||
color: $gray-700;
|
||||
}
|
||||
|
||||
.woocommerce-tax-partner-card__benefits {
|
||||
color: $gray-900;
|
||||
list-style: none;
|
||||
li {
|
||||
margin-bottom: $gap-smaller;
|
||||
display: flex;
|
||||
|
||||
svg {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-tax-partner-card__action {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.woocommerce-tax-partner-card__terms {
|
||||
color: $gray-600;
|
||||
font-size: 9px;
|
||||
margin-bottom: $gap-smaller;
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Button } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Bullet } from './bullet';
|
||||
import './partner-card.scss';
|
||||
|
||||
export const PartnerCard: React.FC< {
|
||||
name: string;
|
||||
logo: string;
|
||||
description: string;
|
||||
benefits: string[];
|
||||
terms: string;
|
||||
actionText: string;
|
||||
onClick: () => void;
|
||||
isPending: boolean;
|
||||
} > = ( {
|
||||
name,
|
||||
logo,
|
||||
description,
|
||||
benefits,
|
||||
terms,
|
||||
actionText,
|
||||
onClick,
|
||||
isPending,
|
||||
} ) => {
|
||||
return (
|
||||
<div className="woocommerce-tax-partner-card">
|
||||
<div className="woocommerce-tax-partner-card__logo">
|
||||
<img src={ logo } alt={ name } />
|
||||
</div>
|
||||
|
||||
<div className="woocommerce-tax-partner-card__description">
|
||||
{ description }
|
||||
</div>
|
||||
<ul className="woocommerce-tax-partner-card__benefits">
|
||||
{ benefits.map( ( benefit, i ) => {
|
||||
return (
|
||||
<li
|
||||
className="woocommerce-tax-partner-card__benefit"
|
||||
key={ i }
|
||||
>
|
||||
<span className="woocommerce-tax-partner-card__benefit-bullet">
|
||||
<Bullet />
|
||||
</span>
|
||||
<span className="woocommerce-tax-partner-card__benefit-text">
|
||||
{ benefit }
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
} ) }
|
||||
</ul>
|
||||
|
||||
<div className="woocommerce-tax-partner-card__action">
|
||||
<div className="woocommerce-tax-partner-card__terms">
|
||||
{ terms }
|
||||
</div>
|
||||
<Button
|
||||
isSecondary
|
||||
onClick={ onClick }
|
||||
isBusy={ isPending }
|
||||
disabled={ isPending }
|
||||
>
|
||||
{ actionText }
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,69 @@
|
|||
.woocommerce-tax-partners__partners {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
|
||||
&.woocommerce-tax-partners__partners-count-1 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@include breakpoint( '<782px' ) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-tax-partners__partners-count-1 .woocommerce-tax-partners__partners {
|
||||
grid-template-columns: 1fr;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.woocommerce-tax-partners {
|
||||
.components-card__body.is-size-medium {
|
||||
padding: $gap-larger;
|
||||
}
|
||||
|
||||
.components-card__header {
|
||||
line-height: 28px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-tax-partners__other-actions {
|
||||
text-align: center;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
|
||||
@include breakpoint( '<782px' ) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-top: $gap;
|
||||
margin-right: $gap-smallest;
|
||||
|
||||
button.is-tertiary {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '•';
|
||||
color: #bbb;
|
||||
margin-left: 4px;
|
||||
|
||||
@include breakpoint( '<782px' ) {
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
&::after {
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Button, Card, CardBody, CardHeader } from '@wordpress/components';
|
||||
import { Children } from '@wordpress/element';
|
||||
import classnames from 'classnames';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { TaxChildProps } from '../utils';
|
||||
import './partners.scss';
|
||||
|
||||
export const Partners: React.FC< TaxChildProps > = ( {
|
||||
children,
|
||||
isPending,
|
||||
onManual,
|
||||
onDisable,
|
||||
} ) => {
|
||||
const classes = classnames(
|
||||
'woocommerce-task-card',
|
||||
'woocommerce-tax-partners',
|
||||
`woocommerce-tax-partners__partners-count-${ Children.count(
|
||||
children
|
||||
) }`
|
||||
);
|
||||
return (
|
||||
<Card className={ classes }>
|
||||
<CardHeader>
|
||||
{ __( 'Choose a tax partner', 'woocommerce-admin' ) }
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="woocommerce-tax-partners__partners">
|
||||
{ children }
|
||||
</div>
|
||||
<ul className="woocommerce-tax-partners__other-actions">
|
||||
<li>
|
||||
<Button
|
||||
isTertiary
|
||||
disabled={ isPending }
|
||||
isBusy={ isPending }
|
||||
onClick={ () => {
|
||||
onManual();
|
||||
} }
|
||||
>
|
||||
{ __(
|
||||
'Set up taxes manually',
|
||||
'woocommerce-admin'
|
||||
) }
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
<Button
|
||||
isTertiary
|
||||
disabled={ isPending }
|
||||
isBusy={ isPending }
|
||||
onClick={ () => {
|
||||
onDisable();
|
||||
} }
|
||||
>
|
||||
{ __(
|
||||
"I don't charge sales tax",
|
||||
'woocommerce-admin'
|
||||
) }
|
||||
</Button>
|
||||
</li>
|
||||
</ul>
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
};
|
|
@ -10,25 +10,26 @@ import { useSelect, useDispatch } from '@wordpress/data';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ConfigurationStepProps } from '.';
|
||||
import { getCountryCode } from '../../../../dashboard/utils';
|
||||
import { hasCompleteAddress, SettingsSelector } from '../utils';
|
||||
import { default as StoreLocationForm } from '../../steps/location';
|
||||
import { getCountryCode } from '~/dashboard/utils';
|
||||
import { hasCompleteAddress, SettingsSelector, TaxChildProps } from '../utils';
|
||||
import { default as StoreLocationForm } from '~/tasks/fills/steps/location';
|
||||
|
||||
export const StoreLocation: React.FC< ConfigurationStepProps > = ( {
|
||||
isResolving,
|
||||
nextStep,
|
||||
} ) => {
|
||||
export const StoreLocation: React.FC< {
|
||||
nextStep: () => void;
|
||||
} > = ( { nextStep } ) => {
|
||||
const { updateAndPersistSettingsForGroup } = useDispatch(
|
||||
SETTINGS_STORE_NAME
|
||||
);
|
||||
const { generalSettings } = useSelect( ( select ) => {
|
||||
const { getSettings } = select(
|
||||
const { generalSettings, isResolving } = useSelect( ( select ) => {
|
||||
const { getSettings, hasFinishedResolution } = select(
|
||||
SETTINGS_STORE_NAME
|
||||
) as SettingsSelector;
|
||||
|
||||
return {
|
||||
generalSettings: getSettings( 'general' )?.general,
|
||||
isResolving: ! hasFinishedResolution( 'getSettings', [
|
||||
'general',
|
||||
] ),
|
||||
};
|
||||
} );
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { difference, filter } from 'lodash';
|
||||
import { useEffect, useState } from '@wordpress/element';
|
||||
import { Stepper } from '@woocommerce/components';
|
||||
import {
|
||||
OPTIONS_STORE_NAME,
|
||||
PLUGINS_STORE_NAME,
|
||||
SETTINGS_STORE_NAME,
|
||||
} from '@woocommerce/data';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { AUTOMATION_PLUGINS, SettingsSelector } from '../utils';
|
||||
import { Connect } from './connect';
|
||||
import { ManualConfiguration } from './manual-configuration';
|
||||
import { Plugins } from './plugins';
|
||||
import { StoreLocation } from './store-location';
|
||||
|
||||
export type ConfigurationStepperProps = {
|
||||
isPending: boolean;
|
||||
onDisable: () => void;
|
||||
onAutomate: () => void;
|
||||
onManual: () => void;
|
||||
supportsAutomatedTaxes: boolean;
|
||||
};
|
||||
|
||||
export type ConfigurationStepProps = {
|
||||
isPending: boolean;
|
||||
isResolving: boolean;
|
||||
nextStep: () => void;
|
||||
onDisable: () => void;
|
||||
onAutomate: () => void;
|
||||
onManual: () => void;
|
||||
pluginsToActivate: string[];
|
||||
};
|
||||
|
||||
export const ConfigurationStepper: React.FC< ConfigurationStepperProps > = ( {
|
||||
isPending,
|
||||
onDisable,
|
||||
onAutomate,
|
||||
onManual,
|
||||
supportsAutomatedTaxes,
|
||||
} ) => {
|
||||
const [ pluginsToActivate, setPluginsToActivate ] = useState( [] );
|
||||
const {
|
||||
activePlugins,
|
||||
isJetpackConnected,
|
||||
isResolving,
|
||||
tosAccepted,
|
||||
} = useSelect( ( select ) => {
|
||||
const { getSettings } = select(
|
||||
SETTINGS_STORE_NAME
|
||||
) as SettingsSelector;
|
||||
const { getOption, hasFinishedResolution } = select(
|
||||
OPTIONS_STORE_NAME
|
||||
) as SettingsSelector;
|
||||
const { getActivePlugins } = select( PLUGINS_STORE_NAME );
|
||||
|
||||
return {
|
||||
activePlugins: getActivePlugins(),
|
||||
generalSettings: getSettings( 'general' )?.general,
|
||||
isJetpackConnected: select(
|
||||
PLUGINS_STORE_NAME
|
||||
).isJetpackConnected(),
|
||||
isResolving:
|
||||
! hasFinishedResolution( 'getOption', [
|
||||
'woocommerce_setup_jetpack_opted_in',
|
||||
] ) ||
|
||||
! hasFinishedResolution( 'getOption', [
|
||||
'wc_connect_options',
|
||||
] ),
|
||||
tosAccepted:
|
||||
getOption( 'wc_connect_options' )?.tos_accepted ||
|
||||
getOption( 'woocommerce_setup_jetpack_opted_in' ) === '1',
|
||||
};
|
||||
} );
|
||||
const [ stepIndex, setStepIndex ] = useState( 0 );
|
||||
|
||||
useEffect( () => {
|
||||
const remainingPlugins = difference(
|
||||
AUTOMATION_PLUGINS,
|
||||
activePlugins
|
||||
);
|
||||
if ( remainingPlugins.length <= pluginsToActivate.length ) {
|
||||
return;
|
||||
}
|
||||
setPluginsToActivate( remainingPlugins );
|
||||
}, [ activePlugins ] );
|
||||
|
||||
const nextStep = () => {
|
||||
setStepIndex( stepIndex + 1 );
|
||||
};
|
||||
|
||||
const stepProps = {
|
||||
isPending,
|
||||
isResolving,
|
||||
onAutomate,
|
||||
onDisable,
|
||||
nextStep,
|
||||
onManual,
|
||||
pluginsToActivate,
|
||||
};
|
||||
|
||||
const getVisibleSteps = () => {
|
||||
const allSteps = [
|
||||
{
|
||||
key: 'store_location',
|
||||
label: __( 'Set store location', 'woocommerce-admin' ),
|
||||
description: __(
|
||||
'The address from which your business operates',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
content: <StoreLocation { ...stepProps } />,
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
key: 'plugins',
|
||||
label: pluginsToActivate.includes( 'woocommerce-services' )
|
||||
? __(
|
||||
'Install Jetpack and WooCommerce Tax',
|
||||
'woocommerce-admin'
|
||||
)
|
||||
: __( 'Install Jetpack', 'woocommerce-admin' ),
|
||||
description: __(
|
||||
'Jetpack and WooCommerce Tax allow you to automate sales tax calculations',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
content: <Plugins { ...stepProps } />,
|
||||
visible:
|
||||
! isResolving &&
|
||||
( pluginsToActivate.length || ! tosAccepted ) &&
|
||||
supportsAutomatedTaxes,
|
||||
},
|
||||
{
|
||||
key: 'connect',
|
||||
label: __( 'Connect your store', 'woocommerce-admin' ),
|
||||
description: __(
|
||||
'Connect your store to WordPress.com to enable automated sales tax calculations',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
content: <Connect { ...stepProps } />,
|
||||
visible:
|
||||
! isResolving &&
|
||||
! isJetpackConnected &&
|
||||
supportsAutomatedTaxes,
|
||||
},
|
||||
{
|
||||
key: 'manual_configuration',
|
||||
label: __( 'Configure tax rates', 'woocommerce-admin' ),
|
||||
description: __(
|
||||
'Head over to the tax rate settings screen to configure your tax rates',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
content: <ManualConfiguration { ...stepProps } />,
|
||||
visible: ! supportsAutomatedTaxes,
|
||||
},
|
||||
];
|
||||
|
||||
return filter( allSteps, ( step ) => step.visible );
|
||||
};
|
||||
|
||||
const steps = getVisibleSteps();
|
||||
|
||||
const step = steps[ stepIndex ];
|
||||
|
||||
return (
|
||||
<Stepper
|
||||
isPending={ isResolving }
|
||||
isVertical={ true }
|
||||
currentStep={ step.key }
|
||||
steps={ steps }
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -2,36 +2,48 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Card, CardBody } from '@wordpress/components';
|
||||
import { difference } from 'lodash';
|
||||
import { Card, CardBody, Spinner } from '@wordpress/components';
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
import { Spinner } from '@woocommerce/components';
|
||||
import { getAdminLink } from '@woocommerce/wc-admin-settings';
|
||||
import {
|
||||
ONBOARDING_STORE_NAME,
|
||||
OPTIONS_STORE_NAME,
|
||||
PLUGINS_STORE_NAME,
|
||||
SETTINGS_STORE_NAME,
|
||||
} from '@woocommerce/data';
|
||||
import { queueRecordEvent } from '@woocommerce/tracks';
|
||||
import { queueRecordEvent, recordEvent } from '@woocommerce/tracks';
|
||||
import { registerPlugin } from '@wordpress/plugins';
|
||||
import { useEffect, useState } from '@wordpress/element';
|
||||
import { updateQueryString } from '@woocommerce/navigation';
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useState,
|
||||
createElement,
|
||||
} from '@wordpress/element';
|
||||
import { WooOnboardingTask } from '@woocommerce/onboarding';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
AUTOMATION_PLUGINS,
|
||||
hasCompleteAddress,
|
||||
redirectToTaxSettings,
|
||||
SettingsSelector,
|
||||
supportsAvalara,
|
||||
} from './utils';
|
||||
import { AutomatedTaxes } from './automated-taxes';
|
||||
import { ConfigurationStepper } from './configuration-stepper';
|
||||
import { Card as AvalaraCard } from './avalara/card';
|
||||
import { Card as WooCommerceTaxCard } from './woocommerce-tax/card';
|
||||
import { createNoticesFromResponse } from '../../../lib/notices';
|
||||
import { getCountryCode } from '../../../dashboard/utils';
|
||||
import './tax.scss';
|
||||
import { getCountryCode } from '~/dashboard/utils';
|
||||
import { ManualConfiguration } from './manual-configuration';
|
||||
import { Partners } from './components/partners';
|
||||
import { WooCommerceTax } from './woocommerce-tax';
|
||||
|
||||
const TaskCard = ( { children } ) => {
|
||||
return (
|
||||
<Card className="woocommerce-task-card">
|
||||
<CardBody>{ children }</CardBody>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
const Tax = ( { onComplete, query } ) => {
|
||||
const [ isPending, setIsPending ] = useState( false );
|
||||
|
@ -42,64 +54,28 @@ const Tax = ( { onComplete, query } ) => {
|
|||
);
|
||||
const {
|
||||
generalSettings,
|
||||
isJetpackConnected,
|
||||
isResolving,
|
||||
pluginsToActivate,
|
||||
tasksStatus,
|
||||
taxSettings,
|
||||
} = useSelect( ( select ) => {
|
||||
const { getSettings } = select(
|
||||
const { getSettings, hasFinishedResolution } = select(
|
||||
SETTINGS_STORE_NAME
|
||||
) as SettingsSelector;
|
||||
const { getActivePlugins } = select( PLUGINS_STORE_NAME );
|
||||
const activePlugins = getActivePlugins();
|
||||
|
||||
return {
|
||||
generalSettings: getSettings( 'general' ).general,
|
||||
isJetpackConnected: select(
|
||||
PLUGINS_STORE_NAME
|
||||
).isJetpackConnected(),
|
||||
isResolving:
|
||||
! select( PLUGINS_STORE_NAME ).hasFinishedResolution(
|
||||
'isJetpackConnected'
|
||||
) ||
|
||||
! select(
|
||||
SETTINGS_STORE_NAME
|
||||
).hasFinishedResolution( 'getSettings', [ 'general' ] ) ||
|
||||
! hasFinishedResolution( 'getSettings', [ 'general' ] ) ||
|
||||
! select( ONBOARDING_STORE_NAME ).hasFinishedResolution(
|
||||
'getTasksStatus'
|
||||
),
|
||||
pluginsToActivate: difference( AUTOMATION_PLUGINS, activePlugins ),
|
||||
// @Todo this should be removed as soon as https://github.com/woocommerce/woocommerce-admin/pull/7841 is merged.
|
||||
tasksStatus: select( ONBOARDING_STORE_NAME ).getTasksStatus(),
|
||||
taxSettings: getSettings( 'tax' ).tax || {},
|
||||
};
|
||||
} );
|
||||
|
||||
const supportsAutomatedTaxes = () => {
|
||||
const {
|
||||
automatedTaxSupportedCountries = [],
|
||||
taxJarActivated,
|
||||
} = tasksStatus;
|
||||
|
||||
return (
|
||||
! taxJarActivated && // WCS integration doesn't work with the official TaxJar plugin.
|
||||
automatedTaxSupportedCountries.includes(
|
||||
getCountryCode( generalSettings?.woocommerce_default_country )
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const canAutomateTaxes = () => {
|
||||
return (
|
||||
hasCompleteAddress( generalSettings ) &&
|
||||
! pluginsToActivate.length &&
|
||||
isJetpackConnected &&
|
||||
supportsAutomatedTaxes()
|
||||
);
|
||||
};
|
||||
|
||||
const onManual = async () => {
|
||||
const onManual = useCallback( async () => {
|
||||
setIsPending( true );
|
||||
if ( generalSettings.woocommerce_calc_taxes !== 'yes' ) {
|
||||
updateAndPersistSettingsForGroup( 'tax', {
|
||||
|
@ -122,9 +98,9 @@ const Tax = ( { onComplete, query } ) => {
|
|||
} else {
|
||||
redirectToTaxSettings();
|
||||
}
|
||||
};
|
||||
}, [] );
|
||||
|
||||
const onAutomate = () => {
|
||||
const onAutomate = useCallback( () => {
|
||||
setIsPending( true );
|
||||
updateAndPersistSettingsForGroup( 'tax', {
|
||||
tax: {
|
||||
|
@ -147,9 +123,9 @@ const Tax = ( { onComplete, query } ) => {
|
|||
)
|
||||
);
|
||||
onComplete();
|
||||
};
|
||||
}, [] );
|
||||
|
||||
const onDisable = () => {
|
||||
const onDisable = useCallback( () => {
|
||||
setIsPending( true );
|
||||
queueRecordEvent( 'tasklist_tax_connect_store', {
|
||||
connect: false,
|
||||
|
@ -162,40 +138,121 @@ const Tax = ( { onComplete, query } ) => {
|
|||
} ).then( () => {
|
||||
window.location.href = getAdminLink( 'admin.php?page=wc-admin' );
|
||||
} );
|
||||
}, [] );
|
||||
|
||||
const getVisiblePartners = () => {
|
||||
const countryCode = getCountryCode(
|
||||
generalSettings?.woocommerce_default_country
|
||||
);
|
||||
const {
|
||||
automatedTaxSupportedCountries = [],
|
||||
taxJarActivated,
|
||||
} = tasksStatus;
|
||||
|
||||
const partners = [
|
||||
{
|
||||
id: 'woocommerce-tax',
|
||||
card: WooCommerceTaxCard,
|
||||
component: WooCommerceTax,
|
||||
isVisible:
|
||||
! taxJarActivated && // WCS integration doesn't work with the official TaxJar plugin.
|
||||
automatedTaxSupportedCountries.includes(
|
||||
getCountryCode(
|
||||
generalSettings?.woocommerce_default_country
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'avalara',
|
||||
card: AvalaraCard,
|
||||
component: null,
|
||||
isVisible: supportsAvalara( countryCode ),
|
||||
},
|
||||
];
|
||||
|
||||
return partners.filter( ( partner ) => partner.isVisible );
|
||||
};
|
||||
|
||||
const partners = getVisiblePartners();
|
||||
|
||||
useEffect( () => {
|
||||
const { auto } = query;
|
||||
|
||||
if ( auto === 'true' ) {
|
||||
onAutomate();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( query.partner ) {
|
||||
return;
|
||||
}
|
||||
|
||||
recordEvent( 'tasklist_tax_view_options', {
|
||||
options: partners.map( ( partner ) => partner.id ),
|
||||
} );
|
||||
}, [] );
|
||||
|
||||
if ( isResolving ) {
|
||||
return <Spinner />;
|
||||
const getCurrentPartner = () => {
|
||||
if ( ! query.partner ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
partners.find( ( partner ) => partner.id === query.partner ) || null
|
||||
);
|
||||
};
|
||||
|
||||
useEffect( () => {
|
||||
if ( partners.length > 1 || query.partner ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( partners.length === 1 && partners[ 0 ].component ) {
|
||||
updateQueryString( {
|
||||
partner: partners[ 0 ].id,
|
||||
} );
|
||||
}
|
||||
}, [ partners ] );
|
||||
|
||||
const childProps = {
|
||||
isPending,
|
||||
onAutomate,
|
||||
onManual,
|
||||
onDisable,
|
||||
supportsAutomatedTaxes: supportsAutomatedTaxes(),
|
||||
tasksStatus,
|
||||
};
|
||||
|
||||
if ( isResolving ) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
const currentPartner = getCurrentPartner();
|
||||
|
||||
if ( ! partners.length ) {
|
||||
return (
|
||||
<div className="woocommerce-task-tax">
|
||||
<Card className="woocommerce-task-card">
|
||||
<CardBody>
|
||||
{ canAutomateTaxes() ? (
|
||||
<AutomatedTaxes { ...childProps } />
|
||||
) : (
|
||||
<ConfigurationStepper { ...childProps } />
|
||||
<TaskCard>
|
||||
<ManualConfiguration { ...childProps } />
|
||||
</TaskCard>
|
||||
);
|
||||
}
|
||||
|
||||
if ( currentPartner ) {
|
||||
return (
|
||||
<TaskCard>
|
||||
{ createElement( currentPartner.component, childProps ) }
|
||||
</TaskCard>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Partners>
|
||||
{ partners.map( ( partner ) =>
|
||||
createElement( partner.card, {
|
||||
key: partner.id,
|
||||
...childProps,
|
||||
} )
|
||||
) }
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</Partners>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -12,10 +12,9 @@ import { useSelect } from '@wordpress/data';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ConfigurationStepProps } from '.';
|
||||
import { SettingsSelector } from '../utils';
|
||||
import { SettingsSelector, TaxChildProps } from '../utils';
|
||||
|
||||
export const ManualConfiguration: React.FC< ConfigurationStepProps > = ( {
|
||||
export const Configure: React.FC< TaxChildProps > = ( {
|
||||
isPending,
|
||||
onManual,
|
||||
} ) => {
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { filter } from 'lodash';
|
||||
import { useState } from '@wordpress/element';
|
||||
import { Stepper } from '@woocommerce/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Configure } from './configure';
|
||||
import { StoreLocation } from '../components/store-location';
|
||||
|
||||
export type ManualConfigurationProps = {
|
||||
isPending: boolean;
|
||||
onDisable: () => void;
|
||||
onAutomate: () => void;
|
||||
onManual: () => void;
|
||||
};
|
||||
|
||||
export const ManualConfiguration: React.FC< ManualConfigurationProps > = ( {
|
||||
isPending,
|
||||
onDisable,
|
||||
onAutomate,
|
||||
onManual,
|
||||
} ) => {
|
||||
const [ stepIndex, setStepIndex ] = useState( 0 );
|
||||
|
||||
const nextStep = () => {
|
||||
setStepIndex( stepIndex + 1 );
|
||||
};
|
||||
|
||||
const stepProps = {
|
||||
isPending,
|
||||
onAutomate,
|
||||
onDisable,
|
||||
nextStep,
|
||||
onManual,
|
||||
};
|
||||
|
||||
const steps = [
|
||||
{
|
||||
key: 'store_location',
|
||||
label: __( 'Set store location', 'woocommerce-admin' ),
|
||||
description: __(
|
||||
'The address from which your business operates',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
content: <StoreLocation { ...stepProps } />,
|
||||
},
|
||||
{
|
||||
key: 'manual_configuration',
|
||||
label: __( 'Configure tax rates', 'woocommerce-admin' ),
|
||||
description: __(
|
||||
'Head over to the tax rate settings screen to configure your tax rates',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
content: <Configure { ...stepProps } />,
|
||||
},
|
||||
];
|
||||
|
||||
const step = steps[ stepIndex ];
|
||||
|
||||
return (
|
||||
<Stepper isVertical={ true } currentStep={ step.key } steps={ steps } />
|
||||
);
|
||||
};
|
|
@ -13,8 +13,15 @@ export const AUTOMATION_PLUGINS = [ 'jetpack', 'woocommerce-services' ];
|
|||
* Check if a store has a complete address given general settings.
|
||||
*
|
||||
* @param {Object} generalSettings General settings.
|
||||
* @param {Object} generalSettings.woocommerce_store_address Store address.
|
||||
* @param {Object} generalSettings.woocommerce_default_country Store default country.
|
||||
* @param {Object} generalSettings.woocommerce_store_postcode Store postal code.
|
||||
*/
|
||||
export const hasCompleteAddress = ( generalSettings ): boolean => {
|
||||
export const hasCompleteAddress = ( generalSettings: {
|
||||
woocommerce_store_address?: string;
|
||||
woocommerce_default_country?: string;
|
||||
woocommerce_store_postcode?: string;
|
||||
} ): boolean => {
|
||||
const {
|
||||
woocommerce_store_address: storeAddress,
|
||||
woocommerce_default_country: defaultCountry,
|
||||
|
@ -47,3 +54,246 @@ export type SettingsSelector = WPDataSelectors & {
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Types for child tax components.
|
||||
*/
|
||||
export type TaxChildProps = {
|
||||
isPending: boolean;
|
||||
onAutomate: () => void;
|
||||
onManual: () => void;
|
||||
onDisable: () => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a given country is supported by Avalara.
|
||||
*
|
||||
* @param {string} countryCode Country code.
|
||||
* @return {boolean} If the country is supported.
|
||||
*/
|
||||
export const supportsAvalara = ( countryCode: string ): boolean => {
|
||||
const countries = [
|
||||
'AF',
|
||||
'AL',
|
||||
'DZ',
|
||||
'AD',
|
||||
'AO',
|
||||
'AI',
|
||||
'AG',
|
||||
'AR',
|
||||
'AM',
|
||||
'AW',
|
||||
'AU',
|
||||
'AT',
|
||||
'AZ',
|
||||
'BS',
|
||||
'BH',
|
||||
'BD',
|
||||
'BB',
|
||||
'BY',
|
||||
'BE',
|
||||
'BZ',
|
||||
'BJ',
|
||||
'BM',
|
||||
'BO',
|
||||
'BA',
|
||||
'BW',
|
||||
'BR',
|
||||
'BN',
|
||||
'BG',
|
||||
'BF',
|
||||
'BI',
|
||||
'KH',
|
||||
'CM',
|
||||
'CA',
|
||||
'IC',
|
||||
'CV',
|
||||
'KY',
|
||||
'CF',
|
||||
'TD',
|
||||
'CL',
|
||||
'CN',
|
||||
'CC',
|
||||
'CO',
|
||||
'KM',
|
||||
'CD',
|
||||
'CK',
|
||||
'CR',
|
||||
'CI',
|
||||
'HR',
|
||||
'CU',
|
||||
'CW',
|
||||
'CY',
|
||||
'CZ',
|
||||
'DK',
|
||||
'DJ',
|
||||
'DM',
|
||||
'DO',
|
||||
'EC',
|
||||
'EG',
|
||||
'SV',
|
||||
'GQ',
|
||||
'ER',
|
||||
'EE',
|
||||
'ET',
|
||||
'FK',
|
||||
'FO',
|
||||
'FJ',
|
||||
'FI',
|
||||
'FR',
|
||||
'PF',
|
||||
'TF',
|
||||
'GA',
|
||||
'GM',
|
||||
'GE',
|
||||
'DE',
|
||||
'GH',
|
||||
'GI',
|
||||
'GR',
|
||||
'GL',
|
||||
'GD',
|
||||
'GP',
|
||||
'GT',
|
||||
'GG',
|
||||
'GN',
|
||||
'GW',
|
||||
'GY',
|
||||
'HT',
|
||||
'HN',
|
||||
'HK',
|
||||
'HU',
|
||||
'IS',
|
||||
'IN',
|
||||
'ID',
|
||||
'IR',
|
||||
'IQ',
|
||||
'IE',
|
||||
'IL',
|
||||
'IT',
|
||||
'JM',
|
||||
'JP',
|
||||
'JE',
|
||||
'JO',
|
||||
'KZ',
|
||||
'KE',
|
||||
'KI',
|
||||
'KP',
|
||||
'KV',
|
||||
'KW',
|
||||
'KG',
|
||||
'LA',
|
||||
'LV',
|
||||
'LB',
|
||||
'LS',
|
||||
'LR',
|
||||
'LY',
|
||||
'LI',
|
||||
'LT',
|
||||
'LU',
|
||||
'MO',
|
||||
'MK',
|
||||
'MG',
|
||||
'MW',
|
||||
'MY',
|
||||
'MV',
|
||||
'ML',
|
||||
'MT',
|
||||
'MQ',
|
||||
'MR',
|
||||
'MU',
|
||||
'MX',
|
||||
'MD',
|
||||
'MC',
|
||||
'MN',
|
||||
'ME',
|
||||
'MS',
|
||||
'MA',
|
||||
'MZ',
|
||||
'MM',
|
||||
'NA',
|
||||
'NR',
|
||||
'NP',
|
||||
'NL',
|
||||
'NZ',
|
||||
'NI',
|
||||
'NE',
|
||||
'NG',
|
||||
'NU',
|
||||
'NF',
|
||||
'NO',
|
||||
'OM',
|
||||
'PK',
|
||||
'PS',
|
||||
'PA',
|
||||
'PG',
|
||||
'PY',
|
||||
'PE',
|
||||
'PH',
|
||||
'PL',
|
||||
'PT',
|
||||
'QA',
|
||||
'KR',
|
||||
'RE',
|
||||
'RO',
|
||||
'RU',
|
||||
'RW',
|
||||
'SH',
|
||||
'KN',
|
||||
'LC',
|
||||
'MF',
|
||||
'VC',
|
||||
'WS',
|
||||
'SM',
|
||||
'ST',
|
||||
'SA',
|
||||
'SN',
|
||||
'RS',
|
||||
'SC',
|
||||
'SL',
|
||||
'SG',
|
||||
'SX',
|
||||
'SK',
|
||||
'SI',
|
||||
'SB',
|
||||
'SO',
|
||||
'ZA',
|
||||
'SD',
|
||||
'ES',
|
||||
'LK',
|
||||
'SD',
|
||||
'SR',
|
||||
'SZ',
|
||||
'SE',
|
||||
'CH',
|
||||
'SY',
|
||||
'TW',
|
||||
'TJ',
|
||||
'TZ',
|
||||
'TH',
|
||||
'TL',
|
||||
'TG',
|
||||
'TO',
|
||||
'TT',
|
||||
'TN',
|
||||
'TR',
|
||||
'TM',
|
||||
'TC',
|
||||
'TV',
|
||||
'UG',
|
||||
'UA',
|
||||
'AE',
|
||||
'GB',
|
||||
'US',
|
||||
'UY',
|
||||
'UZ',
|
||||
'VU',
|
||||
'VE',
|
||||
'VN',
|
||||
'VG',
|
||||
'YE',
|
||||
'ZM',
|
||||
'ZW',
|
||||
];
|
||||
|
||||
return countries.includes( countryCode );
|
||||
};
|
||||
|
|
|
@ -7,7 +7,12 @@ import interpolateComponents from 'interpolate-components';
|
|||
import { H } from '@woocommerce/components';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
|
||||
export const AutomatedTaxes = ( {
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { SetupStepProps } from './setup';
|
||||
|
||||
export const AutomatedTaxes: React.FC< SetupStepProps > = ( {
|
||||
isPending,
|
||||
onAutomate,
|
||||
onManual,
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import interpolateComponents from 'interpolate-components';
|
||||
import { Link } from '@woocommerce/components';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import { updateQueryString } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { PartnerCard } from '../components/partner-card';
|
||||
import logo from './logo.png';
|
||||
import { TaxChildProps } from '../utils';
|
||||
|
||||
export const Card: React.FC< TaxChildProps > = ( { isPending } ) => {
|
||||
return (
|
||||
<PartnerCard
|
||||
name={ __( 'WooCommerce Tax', 'woocommerce-admin' ) }
|
||||
isPending={ isPending }
|
||||
logo={ logo }
|
||||
description={ __( 'Best for new stores', 'woocommerce-admin' ) }
|
||||
benefits={ [
|
||||
__( 'Real-time sales tax calculation', 'woocommerce-admin' ),
|
||||
interpolateComponents( {
|
||||
mixedString: __(
|
||||
'{{strong}}Single{{/strong}} economic nexus compliance',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
components: {
|
||||
strong: <strong />,
|
||||
},
|
||||
} ),
|
||||
interpolateComponents( {
|
||||
mixedString: __(
|
||||
'Powered by {{link}}Jetpack{{/link}}',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
components: {
|
||||
link: (
|
||||
<Link
|
||||
type="external"
|
||||
href="https://woocommerce.com/products/jetpack/?utm_medium=product"
|
||||
target="_blank"
|
||||
/>
|
||||
),
|
||||
},
|
||||
} ),
|
||||
// eslint-disable-next-line @wordpress/i18n-translator-comments
|
||||
__( '100% free', 'woocommerce-admin' ),
|
||||
] }
|
||||
terms={ interpolateComponents( {
|
||||
mixedString: __(
|
||||
'By installing WooCommerce Tax and Jetpack you agree to the {{link}}Terms of Service{{/link}}.',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
components: {
|
||||
link: (
|
||||
<Link
|
||||
href={ 'https://wordpress.com/tos/' }
|
||||
target="_blank"
|
||||
type="external"
|
||||
/>
|
||||
),
|
||||
},
|
||||
} ) }
|
||||
actionText={ __( 'Continue setup', 'woocommerce-admin' ) }
|
||||
onClick={ () => {
|
||||
recordEvent( 'tasklist_tax_select_option', {
|
||||
selected_option: 'woocommerce-tax',
|
||||
} );
|
||||
updateQueryString( {
|
||||
partner: 'woocommerce-tax',
|
||||
} );
|
||||
} }
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -7,10 +7,10 @@ import { recordEvent, queueRecordEvent } from '@woocommerce/tracks';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { default as ConnectForm } from '../../../../dashboard/components/connect';
|
||||
import { ConfigurationStepProps } from '.';
|
||||
import { default as ConnectForm } from '~/dashboard/components/connect';
|
||||
import { SetupStepProps } from './setup';
|
||||
|
||||
export const Connect: React.FC< ConfigurationStepProps > = ( {
|
||||
export const Connect: React.FC< SetupStepProps > = ( {
|
||||
onDisable,
|
||||
onManual,
|
||||
} ) => {
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { difference } from 'lodash';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { Spinner } from '@woocommerce/components';
|
||||
import { PLUGINS_STORE_NAME, SETTINGS_STORE_NAME } from '@woocommerce/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
AUTOMATION_PLUGINS,
|
||||
hasCompleteAddress,
|
||||
SettingsSelector,
|
||||
TaxChildProps,
|
||||
} from '../utils';
|
||||
import { AutomatedTaxes } from './automated-taxes';
|
||||
import { Setup } from './setup';
|
||||
|
||||
export const WooCommerceTax: React.FC< TaxChildProps > = ( {
|
||||
isPending,
|
||||
onAutomate,
|
||||
onManual,
|
||||
onDisable,
|
||||
} ) => {
|
||||
const {
|
||||
generalSettings,
|
||||
isJetpackConnected,
|
||||
isResolving,
|
||||
pluginsToActivate,
|
||||
} = useSelect( ( select ) => {
|
||||
const { getSettings } = select(
|
||||
SETTINGS_STORE_NAME
|
||||
) as SettingsSelector;
|
||||
const { getActivePlugins, hasFinishedResolution } = select(
|
||||
PLUGINS_STORE_NAME
|
||||
);
|
||||
const activePlugins = getActivePlugins();
|
||||
|
||||
return {
|
||||
generalSettings: getSettings( 'general' ).general,
|
||||
isJetpackConnected: select(
|
||||
PLUGINS_STORE_NAME
|
||||
).isJetpackConnected(),
|
||||
isResolving:
|
||||
! hasFinishedResolution( 'isJetpackConnected' ) ||
|
||||
! select(
|
||||
SETTINGS_STORE_NAME
|
||||
).hasFinishedResolution( 'getSettings', [ 'general' ] ) ||
|
||||
! hasFinishedResolution( 'getActivePlugins' ),
|
||||
pluginsToActivate: difference( AUTOMATION_PLUGINS, activePlugins ),
|
||||
};
|
||||
} );
|
||||
|
||||
const canAutomateTaxes = () => {
|
||||
return (
|
||||
hasCompleteAddress( generalSettings ) &&
|
||||
! pluginsToActivate.length &&
|
||||
isJetpackConnected
|
||||
);
|
||||
};
|
||||
|
||||
if ( isResolving ) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
const childProps = {
|
||||
isPending,
|
||||
onAutomate,
|
||||
onManual,
|
||||
onDisable,
|
||||
};
|
||||
|
||||
if ( canAutomateTaxes() ) {
|
||||
return <AutomatedTaxes { ...childProps } />;
|
||||
}
|
||||
|
||||
return <Setup { ...childProps } />;
|
||||
};
|
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
|
@ -8,33 +8,49 @@ import { OPTIONS_STORE_NAME, PLUGINS_STORE_NAME } from '@woocommerce/data';
|
|||
import { recordEvent, queueRecordEvent } from '@woocommerce/tracks';
|
||||
import { Text } from '@woocommerce/experimental';
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
import { useEffect } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { createNoticesFromResponse } from '../../../../lib/notices';
|
||||
import { ConfigurationStepProps } from '.';
|
||||
import { createNoticesFromResponse } from '~/lib/notices';
|
||||
import { SetupStepProps } from './setup';
|
||||
import { SettingsSelector } from '../utils';
|
||||
|
||||
export const Plugins: React.FC< ConfigurationStepProps > = ( {
|
||||
export const Plugins: React.FC< SetupStepProps > = ( {
|
||||
nextStep,
|
||||
onDisable,
|
||||
onManual,
|
||||
pluginsToActivate,
|
||||
} ) => {
|
||||
const { updateOptions } = useDispatch( OPTIONS_STORE_NAME );
|
||||
const { tosAccepted } = useSelect( ( select ) => {
|
||||
const { getOption } = select( OPTIONS_STORE_NAME ) as SettingsSelector;
|
||||
const { getActivePlugins } = select( PLUGINS_STORE_NAME );
|
||||
const { isResolving, tosAccepted } = useSelect( ( select ) => {
|
||||
const { getOption, hasFinishedResolution } = select(
|
||||
OPTIONS_STORE_NAME
|
||||
) as SettingsSelector;
|
||||
|
||||
return {
|
||||
activePlugins: getActivePlugins(),
|
||||
isResolving:
|
||||
! hasFinishedResolution( 'getOption', [
|
||||
'woocommerce_setup_jetpack_opted_in',
|
||||
] ) ||
|
||||
! hasFinishedResolution( 'getOption', [
|
||||
'wc_connect_options',
|
||||
] ),
|
||||
tosAccepted:
|
||||
getOption( 'wc_connect_options' )?.tos_accepted ||
|
||||
getOption( 'woocommerce_setup_jetpack_opted_in' ) === '1',
|
||||
};
|
||||
} );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! tosAccepted || pluginsToActivate.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
nextStep();
|
||||
}, [ isResolving ] );
|
||||
|
||||
const agreementText = pluginsToActivate.includes( 'woocommerce-services' )
|
||||
? __(
|
||||
'By installing Jetpack and WooCommerce Tax you agree to the {{link}}Terms of Service{{/link}}.',
|
||||
|
@ -45,6 +61,10 @@ export const Plugins: React.FC< ConfigurationStepProps > = ( {
|
|||
'woocommerce-admin'
|
||||
);
|
||||
|
||||
if ( isResolving ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<PluginInstaller
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { difference } from 'lodash';
|
||||
import { useEffect, useState } from '@wordpress/element';
|
||||
import { Stepper } from '@woocommerce/components';
|
||||
import {
|
||||
OPTIONS_STORE_NAME,
|
||||
PLUGINS_STORE_NAME,
|
||||
SETTINGS_STORE_NAME,
|
||||
} from '@woocommerce/data';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { AUTOMATION_PLUGINS, SettingsSelector } from '../utils';
|
||||
import { Connect } from './connect';
|
||||
import { Plugins } from './plugins';
|
||||
import { StoreLocation } from '../components/store-location';
|
||||
import './setup.scss';
|
||||
|
||||
export type SetupProps = {
|
||||
isPending: boolean;
|
||||
onDisable: () => void;
|
||||
onAutomate: () => void;
|
||||
onManual: () => void;
|
||||
};
|
||||
|
||||
export type SetupStepProps = {
|
||||
isPending: boolean;
|
||||
isResolving: boolean;
|
||||
nextStep: () => void;
|
||||
onDisable: () => void;
|
||||
onAutomate: () => void;
|
||||
onManual: () => void;
|
||||
pluginsToActivate: string[];
|
||||
};
|
||||
|
||||
export const Setup: React.FC< SetupProps > = ( {
|
||||
isPending,
|
||||
onDisable,
|
||||
onAutomate,
|
||||
onManual,
|
||||
} ) => {
|
||||
const [ pluginsToActivate, setPluginsToActivate ] = useState( [] );
|
||||
const { activePlugins, isResolving } = useSelect( ( select ) => {
|
||||
const { getSettings } = select(
|
||||
SETTINGS_STORE_NAME
|
||||
) as SettingsSelector;
|
||||
const { hasFinishedResolution } = select(
|
||||
OPTIONS_STORE_NAME
|
||||
) as SettingsSelector;
|
||||
const { getActivePlugins } = select( PLUGINS_STORE_NAME );
|
||||
|
||||
return {
|
||||
activePlugins: getActivePlugins(),
|
||||
generalSettings: getSettings( 'general' )?.general,
|
||||
isResolving:
|
||||
! hasFinishedResolution( 'getOption', [
|
||||
'woocommerce_setup_jetpack_opted_in',
|
||||
] ) ||
|
||||
! hasFinishedResolution( 'getOption', [
|
||||
'wc_connect_options',
|
||||
] ),
|
||||
};
|
||||
} );
|
||||
const [ stepIndex, setStepIndex ] = useState( 0 );
|
||||
|
||||
useEffect( () => {
|
||||
const remainingPlugins = difference(
|
||||
AUTOMATION_PLUGINS,
|
||||
activePlugins
|
||||
);
|
||||
if ( remainingPlugins.length <= pluginsToActivate.length ) {
|
||||
return;
|
||||
}
|
||||
setPluginsToActivate( remainingPlugins );
|
||||
}, [ activePlugins ] );
|
||||
|
||||
const nextStep = () => {
|
||||
setStepIndex( stepIndex + 1 );
|
||||
};
|
||||
|
||||
const stepProps = {
|
||||
isPending,
|
||||
isResolving,
|
||||
onAutomate,
|
||||
onDisable,
|
||||
nextStep,
|
||||
onManual,
|
||||
pluginsToActivate,
|
||||
};
|
||||
|
||||
const steps = [
|
||||
{
|
||||
key: 'store_location',
|
||||
label: __( 'Set store location', 'woocommerce-admin' ),
|
||||
description: __(
|
||||
'The address from which your business operates',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
content: <StoreLocation { ...stepProps } />,
|
||||
},
|
||||
{
|
||||
key: 'plugins',
|
||||
label: pluginsToActivate.includes( 'woocommerce-services' )
|
||||
? __(
|
||||
'Install Jetpack and WooCommerce Tax',
|
||||
'woocommerce-admin'
|
||||
)
|
||||
: __( 'Install Jetpack', 'woocommerce-admin' ),
|
||||
description: __(
|
||||
'Jetpack and WooCommerce Tax allow you to automate sales tax calculations',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
content: <Plugins { ...stepProps } />,
|
||||
},
|
||||
{
|
||||
key: 'connect',
|
||||
label: __( 'Connect your store', 'woocommerce-admin' ),
|
||||
description: __(
|
||||
'Connect your store to WordPress.com to enable automated sales tax calculations',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
content: <Connect { ...stepProps } />,
|
||||
},
|
||||
];
|
||||
|
||||
const step = steps[ stepIndex ];
|
||||
|
||||
return (
|
||||
<Stepper
|
||||
isPending={ isResolving }
|
||||
isVertical={ true }
|
||||
currentStep={ step.key }
|
||||
steps={ steps }
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@woocommerce/admin-library",
|
||||
"version": "2.7.0-dev",
|
||||
"version": "2.9.0-dev",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -10907,6 +10907,7 @@
|
|||
"@wordpress/i18n": "3.17.0",
|
||||
"@wordpress/url": "2.21.0",
|
||||
"md5": "^2.3.0",
|
||||
"qs": "6.9.6",
|
||||
"rememo": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -12,6 +12,7 @@ use Automattic\WooCommerce\Admin\Features\OnboardingTasks\DeprecatedOptions;
|
|||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\Appearance;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\Products;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\Tax;
|
||||
use Automattic\WooCommerce\Admin\PluginsHelper;
|
||||
|
||||
/**
|
||||
* Contains the logic for completing onboarding tasks.
|
||||
|
@ -72,6 +73,7 @@ class Init {
|
|||
$settings['hasProducts'] = Products::has_products();
|
||||
$settings['stylesheet'] = get_option( 'stylesheet' );
|
||||
$settings['taxJarActivated'] = class_exists( 'WC_Taxjar' );
|
||||
$settings['avalaraActivated'] = PluginsHelper::is_plugin_active( 'woocommerce-avatax' );
|
||||
$settings['themeMods'] = get_theme_mods();
|
||||
|
||||
return $settings;
|
||||
|
|
|
@ -6,6 +6,7 @@ use Automattic\WooCommerce\Admin\API\Reports\Taxes\Stats\DataStore as TaxDataSto
|
|||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use Automattic\WooCommerce\Admin\Loader;
|
||||
use Automattic\WooCommerce\Admin\PluginsHelper;
|
||||
|
||||
/**
|
||||
* Tax Task
|
||||
|
@ -69,7 +70,8 @@ class Tax {
|
|||
: __( "Let's go", 'woocommerce-admin' ),
|
||||
'is_complete' => get_option( 'wc_connect_taxes_enabled' ) ||
|
||||
count( TaxDataStore::get_taxes( array() ) ) > 0 ||
|
||||
false !== get_option( 'woocommerce_no_sales_tax' ),
|
||||
false !== get_option( 'woocommerce_no_sales_tax' ) ||
|
||||
PluginsHelper::is_plugin_active( 'woocommerce-avatax' ),
|
||||
'is_visible' => true,
|
||||
'time' => __( '1 minute', 'woocommerce-admin' ),
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue