diff --git a/packages/js/components/changelog/add-onclick-plugins b/packages/js/components/changelog/add-onclick-plugins new file mode 100644 index 00000000000..9be31ae01f2 --- /dev/null +++ b/packages/js/components/changelog/add-onclick-plugins @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Add optional onclick to Plugins component diff --git a/packages/js/components/src/plugins/index.tsx b/packages/js/components/src/plugins/index.tsx index fc3971f0346..df2561ea28f 100644 --- a/packages/js/components/src/plugins/index.tsx +++ b/packages/js/components/src/plugins/index.tsx @@ -19,6 +19,7 @@ type PluginsProps = { response: InstallPluginsResponse ) => void; onError: ( errors: unknown, response: InstallPluginsResponse ) => void; + onClick?: () => void; onSkip?: () => void; skipText?: string; autoInstall?: boolean; @@ -37,6 +38,7 @@ export const Plugins = ( { onAbort, onComplete, onError = () => null, + onClick = () => null, pluginSlugs = [ 'woocommerce-services' ], onSkip, installText = __( 'Install & enable', 'woocommerce' ), @@ -159,6 +161,7 @@ export const Plugins = ( { } disabled={ isRequesting && hasBeenClicked } onClick={ () => { + onClick(); setHasBeenClicked( true ); installAndActivate(); } } diff --git a/packages/js/data/changelog/add-stripe-tax-to task b/packages/js/data/changelog/add-stripe-tax-to task new file mode 100644 index 00000000000..44d5916dff9 --- /dev/null +++ b/packages/js/data/changelog/add-stripe-tax-to task @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Add stripe tax status to task type diff --git a/packages/js/data/src/onboarding/types.ts b/packages/js/data/src/onboarding/types.ts index 1e62f7036ca..a39fa627e63 100644 --- a/packages/js/data/src/onboarding/types.ts +++ b/packages/js/data/src/onboarding/types.ts @@ -29,8 +29,10 @@ export type TaskType = { badge?: string; additionalData?: { woocommerceTaxCountries?: string[]; + stripeTaxCountries?: string[]; taxJarActivated?: boolean; avalaraActivated?: boolean; + stripeTaxActivated?: boolean; woocommerceTaxActivated?: boolean; woocommerceShippingActivated?: boolean; }; diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/avalara/card.tsx b/plugins/woocommerce-admin/client/task-lists/fills/tax/avalara/card.tsx deleted file mode 100644 index d88b1c114aa..00000000000 --- a/plugins/woocommerce-admin/client/task-lists/fills/tax/avalara/card.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/** - * External dependencies - */ -import { __ } from '@wordpress/i18n'; -import { getAdminLink } from '@woocommerce/settings'; -import interpolateComponents from '@automattic/interpolate-components'; -import { recordEvent } from '@woocommerce/tracks'; - -/** - * Internal dependencies - */ -import { PartnerCard } from '../components/partner-card'; -import { TaxChildProps } from '../utils'; -import logo from './logo.png'; - -export const Card: React.FC< TaxChildProps > = ( { task } ) => { - const { additionalData: { avalaraActivated } = {} } = task; - - return ( - , - }, - } ), - __( - 'Cross-border and multi-channel compliance', - 'woocommerce' - ), - __( 'Automate filing & remittance', 'woocommerce' ), - __( - 'Return-ready, jurisdiction-level reporting.', - 'woocommerce' - ), - ] } - terms={ '' } - actionText={ - avalaraActivated - ? __( 'Continue setup', 'woocommerce' ) - : __( 'Download', 'woocommerce' ) - } - 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/' - ).toString(), - '_blank' - ); - } } - /> - ); -}; diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/avalara/logo.png b/plugins/woocommerce-admin/client/task-lists/fills/tax/avalara/logo.png deleted file mode 100644 index 8430603eb9f..00000000000 Binary files a/plugins/woocommerce-admin/client/task-lists/fills/tax/avalara/logo.png and /dev/null differ diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.scss b/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.scss index 7ad3feef6b7..9bcdb291555 100644 --- a/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.scss +++ b/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.scss @@ -41,7 +41,7 @@ .woocommerce-tax-partner-card__terms { color: $gray-600; - font-size: 9px; + font-size: 12px; margin-bottom: $gap-smaller; } diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.tsx b/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.tsx index 30e2e1e5eb7..cef2b27b108 100644 --- a/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.tsx +++ b/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.tsx @@ -15,7 +15,8 @@ export const PartnerCard: React.FC< { description: string; benefits: ( string | JSX.Element )[]; terms: string | JSX.Element; - actionText: string; + children?: React.ReactNode; + actionText?: string; onClick: () => void; isBusy?: boolean; } > = ( { @@ -27,6 +28,7 @@ export const PartnerCard: React.FC< { actionText, onClick, isBusy, + children, } ) => { return (
@@ -59,14 +61,18 @@ export const PartnerCard: React.FC< {
{ terms }
- + { children ? ( + children + ) : ( + + ) }
); diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/index.tsx b/plugins/woocommerce-admin/client/task-lists/fills/tax/index.tsx index c4c95cdb95e..f98fd06a168 100644 --- a/plugins/woocommerce-admin/client/task-lists/fills/tax/index.tsx +++ b/plugins/woocommerce-admin/client/task-lists/fills/tax/index.tsx @@ -17,6 +17,7 @@ import { useEffect, useState, createElement, + useMemo, } from '@wordpress/element'; import { WooOnboardingTask } from '@woocommerce/onboarding'; @@ -25,6 +26,7 @@ import { WooOnboardingTask } from '@woocommerce/onboarding'; */ import { redirectToTaxSettings } from './utils'; import { Card as WooCommerceTaxCard } from './woocommerce-tax/card'; +import { Card as StripeTaxCard } from './stripe-tax/card'; import { createNoticesFromResponse } from '../../../lib/notices'; import { getCountryCode } from '~/dashboard/utils'; import { ManualConfiguration } from './manual-configuration'; @@ -150,20 +152,21 @@ export const Tax: React.FC< TaxProps > = ( { onComplete, query, task } ) => { } ); }, [ updateOptions ] ); - const getVisiblePartners = () => { + const partners = useMemo( () => { const countryCode = getCountryCode( generalSettings?.woocommerce_default_country ) || ''; const { additionalData: { woocommerceTaxCountries = [], + stripeTaxCountries = [], taxJarActivated, woocommerceTaxActivated, woocommerceShippingActivated, } = {}, } = task; - const partners = [ + const allPartners = [ { id: 'woocommerce-tax', card: WooCommerceTaxCard, @@ -174,31 +177,35 @@ export const Tax: React.FC< TaxProps > = ( { onComplete, query, task } ) => { ! woocommerceShippingActivated && woocommerceTaxCountries.includes( countryCode ), }, + { + id: 'stripe-tax', + card: StripeTaxCard, + + isVisible: stripeTaxCountries.includes( countryCode ), + }, ]; - return partners.filter( ( partner ) => partner.isVisible ); - }; - - const partners = getVisiblePartners(); + return allPartners.filter( ( partner ) => partner.isVisible ); + // eslint-disable-next-line react-hooks/exhaustive-deps -- the partner list shouldn't be changing in the middle of interaction. for some reason the country is becoming null in a re-render and causing unexpected behaviour + }, [] ); + const { auto } = query; useEffect( () => { - const { auto } = query; - if ( auto === 'true' ) { onAutomate(); - return; } + }, [ auto, onAutomate ] ); + useEffect( () => { if ( query.partner ) { return; } - recordEvent( 'tasklist_tax_view_options', { options: partners.map( ( partner ) => partner.id ), } ); - }, [ onAutomate, partners, query ] ); + }, [ partners, query.partner ] ); - const getCurrentPartner = () => { + const currentPartner = useMemo( () => { if ( ! query.partner ) { return null; } @@ -206,7 +213,7 @@ export const Tax: React.FC< TaxProps > = ( { onComplete, query, task } ) => { return ( partners.find( ( partner ) => partner.id === query.partner ) || null ); - }; + }, [ partners, query.partner ] ); const childProps = { isPending, @@ -220,8 +227,6 @@ export const Tax: React.FC< TaxProps > = ( { onComplete, query, task } ) => { return ; } - const currentPartner = getCurrentPartner(); - if ( ! partners.length ) { return ( diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/stripe-tax/card.tsx b/plugins/woocommerce-admin/client/task-lists/fills/tax/stripe-tax/card.tsx new file mode 100644 index 00000000000..3795a7dee60 --- /dev/null +++ b/plugins/woocommerce-admin/client/task-lists/fills/tax/stripe-tax/card.tsx @@ -0,0 +1,107 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { getAdminLink } from '@woocommerce/settings'; +import { recordEvent } from '@woocommerce/tracks'; +import { Plugins } from '@woocommerce/components'; +import { dispatch, useDispatch } from '@wordpress/data'; +import { SETTINGS_STORE_NAME } from '@woocommerce/data'; +import { Button } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { PartnerCard } from '../components/partner-card'; +import { TaxChildProps } from '../utils'; +import StripeTaxLogo from './stripe-tax-logo.svg'; +import { createNoticesFromResponse } from '~/lib/notices'; + +const STRIPE_TAX_PLUGIN_SLUG = 'stripe-tax-for-woocommerce'; + +const redirectToStripeTaxSettings = () => { + window.location.href = getAdminLink( + '/admin.php?page=wc-settings&tab=stripe_tax_for_woocommerce' + ); +}; + +export const Card: React.FC< TaxChildProps > = ( { + task: { + additionalData: { stripeTaxActivated } = { + stripeTaxActivated: false, + }, + }, +} ) => { + const { createSuccessNotice } = useDispatch( 'core/notices' ); + + return ( + {} } + > + { stripeTaxActivated ? ( + + ) : ( + { + recordEvent( 'tasklist_tax_select_option', { + selected_option: STRIPE_TAX_PLUGIN_SLUG, + } ); + } } + onComplete={ () => { + recordEvent( 'tasklist_tax_install_plugin_success', { + selected_option: STRIPE_TAX_PLUGIN_SLUG, + } ); + const { updateAndPersistSettingsForGroup } = + dispatch( SETTINGS_STORE_NAME ); + updateAndPersistSettingsForGroup( 'general', { + general: { + woocommerce_calc_taxes: 'yes', // Stripe tax requires tax calculation to be enabled so let's do it here to save the user from doing it manually + }, + } ).then( () => { + createSuccessNotice( + __( + "Stripe Tax for Woocommerce has been successfully installed. Let's configure it now.", + 'woocommerce' + ) + ); + redirectToStripeTaxSettings(); + } ); + } } + onError={ ( errors, response ) => { + recordEvent( 'tasklist_tax_install_plugin_error', { + selected_option: STRIPE_TAX_PLUGIN_SLUG, + errors, + } ); + createNoticesFromResponse( response ); + } } + installButtonVariant="secondary" + pluginSlugs={ [ STRIPE_TAX_PLUGIN_SLUG ] } + /> + ) } + + ); +}; diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/stripe-tax/stripe-tax-logo.svg b/plugins/woocommerce-admin/client/task-lists/fills/tax/stripe-tax/stripe-tax-logo.svg new file mode 100644 index 00000000000..d290cefc63f --- /dev/null +++ b/plugins/woocommerce-admin/client/task-lists/fills/tax/stripe-tax/stripe-tax-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugins/woocommerce/changelog/add-stripe-tax-onboarding-tax-task b/plugins/woocommerce/changelog/add-stripe-tax-onboarding-tax-task new file mode 100644 index 00000000000..7de97421d64 --- /dev/null +++ b/plugins/woocommerce/changelog/add-stripe-tax-onboarding-tax-task @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Added Stripe tax in onboarding tax task diff --git a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Tax.php b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Tax.php index ee8f9ce99be..ba33d9b2362 100644 --- a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Tax.php +++ b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Tax.php @@ -129,9 +129,11 @@ class Tax extends Task { return array( 'avalara_activated' => PluginsHelper::is_plugin_active( 'woocommerce-avatax' ), 'tax_jar_activated' => class_exists( 'WC_Taxjar' ), + 'stripe_tax_activated' => PluginsHelper::is_plugin_active( 'stripe-tax-for-woocommerce' ), 'woocommerce_tax_activated' => PluginsHelper::is_plugin_active( 'woocommerce-tax' ), 'woocommerce_shipping_activated' => PluginsHelper::is_plugin_active( 'woocommerce-shipping' ), 'woocommerce_tax_countries' => self::get_automated_support_countries(), + 'stripe_tax_countries' => self::get_stripe_tax_support_countries(), ); } @@ -162,4 +164,54 @@ class Tax extends Task { return $tax_supported_countries; } + + /** + * Get an array of countries that support Stripe tax. + * + * @return array + */ + private static function get_stripe_tax_support_countries() { + // https://docs.stripe.com/tax/supported-countries#supported-countries accurate as of 2024-08-26. + // countries with remote sales not included. + return array( + 'AU', + 'AT', + 'BE', + 'BG', + 'CA', + 'HR', + 'CY', + 'CZ', + 'DK', + 'EE', + 'FI', + 'FR', + 'DE', + 'GR', + 'HK', + 'HU', + 'IE', + 'IT', + 'JP', + 'LV', + 'LT', + 'LU', + 'MT', + 'NL', + 'NZ', + 'NO', + 'PL', + 'PT', + 'RO', + 'SG', + 'SK', + 'SI', + 'ES', + 'SE', + 'CH', + 'AE', + 'GB', + 'US', + ); + } }