Update shipping partner suggestions to use data from API (#38457)

* Added shipping partner suggestions data handling in JS

* Add woocommerce services real info

* Add dynamic layout components, remove unused JS, added images to proper asset paths

* Changelog

* Update changelog and removed unused variable

* Remove console.log

* Lint fix

* Add shipping methods module in woocommerce/data

* Update data usage

* Revert "Added shipping partner suggestions data handling in JS"

This reverts commit 6a87ef2658.

* Lint fix
This commit is contained in:
Ilyas Foo 2023-05-30 15:56:32 +08:00 committed by GitHub
parent ab18828e84
commit edf95bf3f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 419 additions and 366 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Update shipping partner suggestions to use data from API, fix default partners data, replaced hardcoded JS components and added relevant types

View File

@ -16,6 +16,7 @@ export { NAVIGATION_STORE_NAME } from './navigation';
export { OPTIONS_STORE_NAME } from './options';
export { ITEMS_STORE_NAME } from './items';
export { PAYMENT_GATEWAYS_STORE_NAME } from './payment-gateways';
export { SHIPPING_METHODS_STORE_NAME } from './shipping-methods';
export { PRODUCTS_STORE_NAME } from './products';
export { ORDERS_STORE_NAME } from './orders';
export { EXPERIMENTAL_PRODUCT_ATTRIBUTES_STORE_NAME } from './product-attributes';
@ -28,6 +29,7 @@ export { EXPERIMENTAL_PRODUCT_VARIATIONS_STORE_NAME } from './product-variations
export { EXPERIMENTAL_PRODUCT_FORM_STORE_NAME } from './product-form';
export { EXPERIMENTAL_TAX_CLASSES_STORE_NAME } from './tax-classes';
export { PaymentGateway } from './payment-gateways/types';
export { ShippingMethod } from './shipping-methods/types';
// Export hooks
export { withSettingsHydration } from './settings/with-settings-hydration';
@ -121,6 +123,7 @@ import type { REPORTS_STORE_NAME } from './reports';
import type { ITEMS_STORE_NAME } from './items';
import type { COUNTRIES_STORE_NAME } from './countries';
import type { PAYMENT_GATEWAYS_STORE_NAME } from './payment-gateways';
import type { SHIPPING_METHODS_STORE_NAME } from './shipping-methods';
import type { PRODUCTS_STORE_NAME } from './products';
import type { ORDERS_STORE_NAME } from './orders';
import type { EXPERIMENTAL_PRODUCT_ATTRIBUTES_STORE_NAME } from './product-attributes';
@ -146,6 +149,7 @@ export type WCDataStoreName =
| typeof ITEMS_STORE_NAME
| typeof COUNTRIES_STORE_NAME
| typeof PAYMENT_GATEWAYS_STORE_NAME
| typeof SHIPPING_METHODS_STORE_NAME
| typeof PRODUCTS_STORE_NAME
| typeof ORDERS_STORE_NAME
| typeof EXPERIMENTAL_PRODUCT_ATTRIBUTES_STORE_NAME
@ -163,6 +167,7 @@ export type WCDataStoreName =
*/
import { WPDataSelectors } from './types';
import { PaymentSelectors } from './payment-gateways/selectors';
import { ShippingMethodsSelectors } from './shipping-methods/selectors';
import { PluginSelectors } from './plugins/selectors';
import { OnboardingSelectors } from './onboarding/selectors';
import { OptionsSelectors } from './options/types';
@ -190,6 +195,8 @@ export type WCSelectorType< T > = T extends typeof REVIEWS_STORE_NAME
? OnboardingSelectors
: T extends typeof PAYMENT_GATEWAYS_STORE_NAME
? PaymentSelectors
: T extends typeof SHIPPING_METHODS_STORE_NAME
? ShippingMethodsSelectors
: T extends typeof USER_STORE_NAME
? WPDataSelectors
: T extends typeof OPTIONS_STORE_NAME

View File

@ -0,0 +1,5 @@
export enum ACTION_TYPES {
GET_SHIPPING_METHODS_REQUEST = 'GET_SHIPPING_METHODS_REQUEST',
GET_SHIPPING_METHODS_SUCCESS = 'GET_SHIPPING_METHODS_SUCCESS',
GET_SHIPPING_METHODS_ERROR = 'GET_SHIPPING_METHODS_ERROR',
}

View File

@ -0,0 +1,30 @@
/**
* Internal dependencies
*/
import { ACTION_TYPES } from './action-types';
import { ShippingMethod } from './types';
export function getShippingMethodsRequest() {
return {
type: ACTION_TYPES.GET_SHIPPING_METHODS_REQUEST as const,
};
}
export function getShippingMethodsSuccess( shippingMethods: ShippingMethod[] ) {
return {
type: ACTION_TYPES.GET_SHIPPING_METHODS_SUCCESS as const,
shippingMethods,
};
}
export function getShippingMethodsError( error: unknown ) {
return {
type: ACTION_TYPES.GET_SHIPPING_METHODS_ERROR as const,
error,
};
}
export type Actions =
| ReturnType< typeof getShippingMethodsRequest >
| ReturnType< typeof getShippingMethodsSuccess >
| ReturnType< typeof getShippingMethodsError >;

View File

@ -0,0 +1 @@
export const STORE_KEY = 'wc/shipping-methods';

View File

@ -0,0 +1,25 @@
/**
* External dependencies
*/
import { createReduxStore, register } from '@wordpress/data';
import { controls } from '@wordpress/data-controls';
/**
* Internal dependencies
*/
import * as actions from './actions';
import * as resolvers from './resolvers';
import * as selectors from './selectors';
import reducer from './reducer';
import { STORE_KEY } from './constants';
export const SHIPPING_METHODS_STORE_NAME = STORE_KEY;
export const store = createReduxStore( SHIPPING_METHODS_STORE_NAME, {
reducer,
selectors,
resolvers,
controls,
actions,
} );
register( store );

View File

@ -0,0 +1,54 @@
/**
* External dependencies
*/
import { Reducer } from 'redux';
/**
* Internal dependencies
*/
import { ACTION_TYPES } from './action-types';
import { ShippingMethod } from './types';
import { Actions } from './actions';
export type ShippingMethodsState = {
shippingMethods: ShippingMethod[];
isUpdating: boolean;
errors: Record< string, unknown >;
};
const reducer: Reducer< ShippingMethodsState, Actions > = (
state = {
shippingMethods: [],
isUpdating: false,
errors: {},
},
payload
) => {
if ( payload && 'type' in payload ) {
switch ( payload.type ) {
case ACTION_TYPES.GET_SHIPPING_METHODS_REQUEST:
return {
...state,
isUpdating: true,
};
case ACTION_TYPES.GET_SHIPPING_METHODS_SUCCESS:
return {
...state,
shippingMethods: payload.shippingMethods,
isUpdating: false,
};
case ACTION_TYPES.GET_SHIPPING_METHODS_ERROR:
return {
...state,
isUpdating: false,
errors: {
...state.errors,
getShippingMethods: payload.error,
},
};
}
}
return state;
};
export default reducer;

View File

@ -0,0 +1,33 @@
/**
* External dependencies
*/
import { apiFetch } from '@wordpress/data-controls';
/**
* Internal dependencies
*/
import {
getShippingMethodsSuccess,
getShippingMethodsRequest,
getShippingMethodsError,
} from './actions';
import { ShippingMethod } from './types';
import { WC_ADMIN_NAMESPACE } from '../constants';
export function* getShippingMethods( forceDefaultSuggestions = false ) {
let path = WC_ADMIN_NAMESPACE + '/shipping-partner-suggestions';
if ( forceDefaultSuggestions ) {
path += '?force_default_suggestions=true';
}
yield getShippingMethodsRequest();
try {
const results: ShippingMethod[] = yield apiFetch( {
path,
method: 'GET',
} );
yield getShippingMethodsSuccess( results );
} catch ( error ) {
yield getShippingMethodsError( error );
}
}

View File

@ -0,0 +1,21 @@
/**
* Internal dependencies
*/
import { ShippingMethodsState } from './reducer';
import { ShippingMethod } from './types';
import { WPDataSelector, WPDataSelectors } from '../types';
export const getShippingMethods = (
state: ShippingMethodsState
): ShippingMethod[] => {
return state.shippingMethods || [];
};
export function isShippingMethodsUpdating(
state: ShippingMethodsState
): boolean {
return state.isUpdating || false;
}
export type ShippingMethodsSelectors = {
getShippingMethods: WPDataSelector< typeof getShippingMethods >;
} & WPDataSelectors;

View File

@ -0,0 +1,49 @@
/**
* Internal dependencies
*/
import { ShippingMethod } from '../types';
export const shippingMethodsStub: ShippingMethod[] = [
{
name: 'Dummy shipping method',
slug: 'dummy-shipping-method',
description: 'Dummy shipping method description',
learn_more_link: 'https://example.com',
is_visible: true,
available_layouts: [ 'row' ],
layout_row: {
image: 'https://example.com/image.png',
features: [
{
icon: 'https://example.com/icon.png',
title: 'Feature title',
description: 'Feature description',
},
{
icon: 'https://example.com/icon.png',
title: 'Feature title 2',
description: 'Feature description 2',
},
],
},
},
{
name: 'Dummy shipping method 2',
slug: 'dummy-shipping-method-2',
description: 'Dummy shipping method description',
learn_more_link: 'https://example.com',
is_visible: true,
available_layouts: [ 'column' ],
layout_column: {
image: 'https://example.com/image.png',
features: [
{
icon: 'https://example.com/icon.png',
title: 'Feature title',
description: 'Feature description',
},
],
},
dependencies: [ 'jetpack' ],
},
];

View File

@ -0,0 +1,57 @@
/**
* @jest-environment node
*/
/**
* Internal dependencies
*/
import reducer, { ShippingMethodsState } from '../reducer';
import { ACTION_TYPES } from '../action-types';
import { shippingMethodsStub } from '../test-helpers/stub';
import { Actions } from '../actions';
const defaultState: ShippingMethodsState = {
shippingMethods: [],
isUpdating: false,
errors: {},
};
const restApiError = {
code: 'error code',
message: 'error message',
};
describe( 'Shipping methods reducer', () => {
it( 'should return a default state', () => {
const state = reducer( undefined, {} as Actions );
expect( state ).toEqual( defaultState );
expect( state ).not.toBe( defaultState );
} );
it( 'should handle GET_SHIPPING_METHODS_REQUEST', () => {
const state = reducer( defaultState, {
type: ACTION_TYPES.GET_SHIPPING_METHODS_REQUEST,
} );
expect( state.isUpdating ).toBe( true );
} );
it( 'should handle GET_SHIPPING_METHODS_ERROR', () => {
const state = reducer( defaultState, {
type: ACTION_TYPES.GET_SHIPPING_METHODS_ERROR,
error: restApiError,
} );
expect( state.errors.getShippingMethods ).toBe( restApiError );
} );
it( 'should handle GET_SHIPPING_METHODS_SUCCESS', () => {
const state = reducer( defaultState, {
type: ACTION_TYPES.GET_SHIPPING_METHODS_SUCCESS,
shippingMethods: shippingMethodsStub,
} );
expect( state.shippingMethods ).toHaveLength( 2 );
expect( state.shippingMethods ).toBe( shippingMethodsStub );
} );
} );

View File

@ -0,0 +1,25 @@
// Types to describe shipping method object.
type Feature = {
icon: string;
title?: string;
description: string;
};
type Layout = {
image: string;
features: Feature[];
};
type LayoutType = 'row' | 'column';
export type ShippingMethod = {
name: string;
slug: string;
description: string;
learn_more_link: string;
is_visible: boolean;
available_layouts: LayoutType[];
layout_column?: Layout;
layout_row?: Layout;
dependencies?: string[];
};

View File

@ -36,6 +36,7 @@
.plugins-install__plugin-banner-image {
display: flex;
width: 150px;
img {
width: 100%;

View File

@ -17,6 +17,7 @@ import {
ONBOARDING_STORE_NAME,
PLUGINS_STORE_NAME,
COUNTRIES_STORE_NAME,
SHIPPING_METHODS_STORE_NAME,
} from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import { registerPlugin } from '@wordpress/plugins';
@ -31,9 +32,12 @@ import Connect from '../../../dashboard/components/connect';
import { getCountryCode } from '../../../dashboard/utils';
import StoreLocation from '../steps/location';
import ShippingRates from './rates';
import { getShippingProviders } from './shipping-providers/shipping-providers';
import { createNoticesFromResponse } from '../../../lib/notices';
import './shipping.scss';
import {
ShippingLayoutColumn,
ShippingLayoutRow,
} from './shipping-providers/partners';
export class Shipping extends Component {
constructor( props ) {
@ -55,6 +59,7 @@ export class Shipping extends Component {
window.wcAdminFeatures[ 'shipping-smart-defaults' ];
this.storeLocationCompleted = false;
this.shippingPartners = props.shippingPartners;
}
componentDidMount() {
@ -194,8 +199,10 @@ export class Shipping extends Component {
settings,
task,
updateAndPersistSettingsForGroup,
shippingPartners,
} = this.props;
const pluginsToPromote = getShippingProviders( this.props.countryCode );
const pluginsToPromote = shippingPartners;
const pluginsToActivate = pluginsToPromote.map( ( pluginToPromote ) => {
return pluginToPromote.slug;
} );
@ -426,7 +433,7 @@ export class Shipping extends Component {
pluginsToPromote.length === 1
? getSinglePluginDescription(
pluginsToPromote[ 0 ].name,
pluginsToPromote[ 0 ].url
pluginsToPromote[ 0 ].learn_more_link
)
: __(
'Save time and money by printing your shipping labels right from your computer with one of these shipping solutions.',
@ -436,24 +443,28 @@ export class Shipping extends Component {
content: (
<>
{ pluginsToPromote.length === 1 ? (
pluginsToPromote[ 0 ][
'single-partner-layout'
]()
<ShippingLayoutColumn
shippingMethod={ pluginsToPromote[ 0 ] }
/>
) : (
<div className="woocommerce-task-shipping-recommendation_plugins-install-container">
{ pluginsToPromote.map(
( pluginToPromote ) => {
( shippingMethod ) => {
const pluginsForPartner = [
pluginToPromote?.slug,
pluginToPromote?.dependencies,
shippingMethod?.slug,
...( shippingMethod?.dependencies ??
[] ),
].filter(
( element ) =>
element !== undefined
); // remove undefineds
return pluginToPromote[
'dual-partner-layout'
]( {
children: (
return (
<ShippingLayoutRow
shippingMethod={
shippingMethod
}
key={ shippingMethod.name }
>
<div className="woocommerce-task-shipping-recommendations_plugins-buttons">
<Plugins
onComplete={ (
@ -486,13 +497,13 @@ export class Shipping extends Component {
'woocommerce'
) }
learnMoreLink={
pluginToPromote.url
shippingMethod.learn_more_link
}
onLearnMore={ () => {
recordEvent(
'tasklist_shipping_label_printing_learn_more',
{
plugin: pluginToPromote.slug,
plugin: shippingMethod.slug,
}
);
} }
@ -504,8 +515,8 @@ export class Shipping extends Component {
}
/>
</div>
),
} );
</ShippingLayoutRow>
);
}
) }
</div>
@ -513,7 +524,10 @@ export class Shipping extends Component {
{ pluginsToPromote.length === 1 &&
pluginsToPromote[ 0 ].slug === undefined && ( // if it doesn't have a slug we just show a download button
<a
href={ pluginsToPromote[ 0 ].url }
href={
pluginsToPromote[ 0 ]
.learn_more_link
}
target="_blank"
rel="noreferrer"
>
@ -665,6 +679,10 @@ const ShippingWrapper = compose(
settings.woocommerce_default_country
);
const shippingPartners = select(
SHIPPING_METHODS_STORE_NAME
).getShippingMethods( true );
const country = countryCode ? getCountry( countryCode ) : null;
const countryName = country ? country.name : null;
const activePlugins = getActivePlugins();
@ -676,6 +694,7 @@ const ShippingWrapper = compose(
settings,
activePlugins,
isJetpackConnected: isJetpackConnected(),
shippingPartners,
};
} ),
withDispatch( ( dispatch ) => {

View File

@ -1,232 +1,56 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { ShippingMethod } from '@woocommerce/data';
/**
* Internal dependencies
*/
import { WCSBanner } from '../../experimental-shipping-recommendation/components/wcs-banner';
import { SinglePartnerFeatures } from '../single-partner-features';
import { PluginBanner } from '../../experimental-shipping-recommendation/components/plugin-banner';
import ShipStationImage from './assets/shipstation.svg';
import SendcloudImage from './assets/sendcloud-banner-side.svg';
import PacklinkImage from './assets/packlink-banner-side.svg';
import EnviaImage from './assets/envia-banner-side.svg';
import SkydropxImage from './assets/skydropx-banner-side.svg';
import CheckIcon from './assets/check.svg';
import SendCloudColumnImage from './assets/sendcloud-column-logo.svg';
import PacklinkColumnImage from './assets/packlink-column-logo.svg';
import ShipStationColumnImage from './assets/shipstation-column-logo.svg';
/**
* Banner layout for Envia
* Renders a column layout for the shipping method. Used for rendering a single shipping partner.
*
* @param {Object} props Component props
* @param {Object} props.shippingMethod Shipping method object
* @return {JSX.Element} React node
*/
export const EnviaSinglePartner = () => {
export const ShippingLayoutColumn = ( {
shippingMethod,
}: {
shippingMethod: ShippingMethod;
} ) => {
return (
<PluginBanner
features={ SinglePartnerFeatures }
logo={ { image: EnviaImage } }
features={ shippingMethod.layout_column?.features || [] }
logo={ { image: shippingMethod.layout_column?.image || '' } }
/>
);
};
/**
* Banner layout for Envia
* Renders a row layout for the shipping method. Used for rendering two shipping partners side by side.
*
* @param {Object} props Component props
* @param {Object} props.shippingMethod Shipping method object
* @param {Array} props.children Children to render inside the layout
* @return {JSX.Element} React node
*/
export const SkydropxSinglePartner = () => {
return (
<PluginBanner
features={ SinglePartnerFeatures }
logo={ { image: SkydropxImage } }
/>
);
};
/**
* Banner layout for Sendcloud
*/
export const SendcloudSinglePartner = () => {
return (
<PluginBanner
features={ SinglePartnerFeatures }
logo={ { image: SendcloudImage } }
/>
);
};
/**
* Narrow Column Layout for Sendcloud
*/
export const SendcloudDualPartner = ( {
export const ShippingLayoutRow = ( {
shippingMethod,
children,
}: {
shippingMethod: ShippingMethod;
children: React.ReactNode;
} ) => {
const features = [
{
icon: CheckIcon,
description: __( 'Print labels from 80+ carriers', 'woocommerce' ),
},
{
icon: CheckIcon,
description: __(
'Process orders in just a few clicks',
'woocommerce'
),
},
{
icon: CheckIcon,
description: __( 'Customize checkout options', 'woocommerce' ),
},
{
icon: CheckIcon,
description: __( 'Self-service tracking & returns', 'woocommerce' ),
},
{
icon: CheckIcon,
description: __( 'Start with a free plan', 'woocommerce' ),
},
];
return (
<PluginBanner
layout="dual"
features={ features }
description={ __( 'All-in-one shipping tool:', 'woocommerce' ) }
logo={ { image: SendCloudColumnImage } }
features={ shippingMethod.layout_row?.features || [] }
logo={ { image: shippingMethod.layout_row?.image || '' } }
description={ shippingMethod.description }
>
{ children }
</PluginBanner>
);
};
/**
* Banner layout for Packlink
*/
export const PacklinkSinglePartner = () => {
return (
<PluginBanner
features={ SinglePartnerFeatures }
logo={ { image: PacklinkImage } }
/>
);
};
/**
* Narrow Column Layout for Packlink
*/
export const PacklinkDualPartner = ( {
children,
}: {
children: React.ReactNode;
} ) => {
const features = [
{
icon: CheckIcon,
description: __(
'Automated, real-time order import',
'woocommerce'
),
},
{
icon: CheckIcon,
description: __(
'Direct access to leading carriers',
'woocommerce'
),
},
{
icon: CheckIcon,
description: __(
'Access competitive shipping prices',
'woocommerce'
),
},
{
icon: CheckIcon,
description: __( 'Quickly bulk print labels', 'woocommerce' ),
},
{
icon: CheckIcon,
description: __( 'Free shipping platform', 'woocommerce' ),
},
];
return (
<PluginBanner
layout="dual"
features={ features }
logo={ { image: PacklinkColumnImage } }
description={ __(
'Optimize your full shipping process:',
'woocommerce'
) }
>
{ children }
</PluginBanner>
);
};
/**
* Banner layout for ShipStation
*/
export const ShipStationSinglePartner = () => {
return (
<PluginBanner
features={ SinglePartnerFeatures }
logo={ { image: ShipStationImage } }
/>
);
};
/**
* Narrow Column Layout for ShipStation
*/
export const ShipStationDualPartner = ( {
children,
}: {
children: React.ReactNode;
} ) => {
const features = [
{
icon: CheckIcon,
description: __(
'Print labels from Royal Mail, Parcel Force, DPD, and many more',
'woocommerce'
),
},
{
icon: CheckIcon,
description: __(
'Shop for the best rates, in real-time',
'woocommerce'
),
},
{
icon: CheckIcon,
description: __( 'Connect selling channels easily', 'woocommerce' ),
},
{
icon: CheckIcon,
description: __( 'Advance automated workflows', 'woocommerce' ),
},
{
icon: CheckIcon,
description: __( '30-days free trial', 'woocommerce' ),
},
];
return (
<PluginBanner
layout="dual"
features={ features }
logo={ { image: ShipStationColumnImage } }
description={ __(
'Powerful yet easy-to-use solution:',
'woocommerce'
) }
>
{ children }
</PluginBanner>
);
};
export const WooCommerceShippingSinglePartner = () => {
return <WCSBanner />;
};

View File

@ -1,142 +0,0 @@
/**
* Internal dependencies
*/
import {
EnviaSinglePartner,
SkydropxSinglePartner,
SendcloudSinglePartner,
PacklinkSinglePartner,
ShipStationSinglePartner,
PacklinkDualPartner,
SendcloudDualPartner,
ShipStationDualPartner,
WooCommerceShippingSinglePartner,
} from './partners';
// Order is respected, left to right in the UI
const shippingProviders = {
AU: {
shipping_providers: [ 'ShipStation' ] as const,
},
MX: {
// using name instead of directly referencing the component because we want to send this over the API in the near future
shipping_providers: [ 'Skydropx' ] as const,
},
CA: {
shipping_providers: [ 'ShipStation' ] as const,
},
CO: {
shipping_providers: [ 'Skydropx' ] as const,
},
CL: {
shipping_providers: [ 'Envia' ] as const,
},
AR: {
shipping_providers: [ 'Envia' ] as const,
},
PE: {
shipping_providers: [ 'Envia' ] as const,
},
BR: {
shipping_providers: [ 'Envia' ] as const,
},
UY: {
shipping_providers: [ 'Envia' ] as const,
},
GT: {
shipping_providers: [ 'Envia' ] as const,
},
NL: {
shipping_providers: [ 'Sendcloud' ] as const,
},
AT: {
shipping_providers: [ 'Sendcloud' ] as const,
},
BE: {
shipping_providers: [ 'Sendcloud' ] as const,
},
FR: {
shipping_providers: [ 'Sendcloud', 'Packlink' ] as const,
},
DE: {
shipping_providers: [ 'Sendcloud', 'Packlink' ] as const,
},
ES: {
shipping_providers: [ 'Packlink', 'Sendcloud' ] as const,
},
IT: {
shipping_providers: [ 'Packlink', 'Sendcloud' ] as const,
},
GB: {
shipping_providers: [ 'Sendcloud', 'ShipStation' ] as const,
},
US: {
shipping_providers: [ 'WooCommerceShipping' ] as const,
},
};
const providers = {
Envia: {
name: 'Envia' as const,
url: 'https://woocommerce.com/products/envia-shipping-and-fulfillment/', // no plugin yet so we link to the product page
'single-partner-layout': EnviaSinglePartner,
},
Skydropx: {
name: 'Skydropx' as const,
slug: 'skydropx-cotizador-y-envios', // https://wordpress.org/plugins/skydropx-cotizador-y-envios/
url: 'https://wordpress.org/plugins/skydropx-cotizador-y-envios/',
'single-partner-layout': SkydropxSinglePartner,
},
Sendcloud: {
name: 'Sendcloud' as const,
slug: 'sendcloud-shipping', // https://wordpress.org/plugins/sendcloud-shipping/
url: 'https://wordpress.org/plugins/sendcloud-shipping/',
'single-partner-layout': SendcloudSinglePartner,
'dual-partner-layout': SendcloudDualPartner,
},
Packlink: {
name: 'Packlink' as const,
slug: 'packlink-pro-shipping', // https://wordpress.org/plugins/packlink-pro-shipping/
url: 'https://wordpress.org/plugins/packlink-pro-shipping/',
'single-partner-layout': PacklinkSinglePartner,
'dual-partner-layout': PacklinkDualPartner,
},
ShipStation: {
name: 'ShipStation' as const,
slug: 'woocommerce-shipstation-integration', // https://wordpress.org/plugins/woocommerce-shipstation-integration/
url: 'https://wordpress.org/plugins/woocommerce-shipstation-integration/',
'single-partner-layout': ShipStationSinglePartner,
'dual-partner-layout': ShipStationDualPartner,
},
WooCommerceShipping: {
name: 'WooCommerce Shipping' as const,
slug: 'woocommerce-services', // https://wordpress.org/plugins/woocommerce-services/
url: 'https://woocommerce.com/products/shipping',
dependencies: 'jetpack',
'single-partner-layout': WooCommerceShippingSinglePartner,
},
} as const;
/**
* Gets the components for the shipping providers for a given country
*
* @param countryCode One of the countries above
* @return Either a single partner component or an array containing two partner components
*/
export const getShippingProviders = (
countryCode: keyof typeof shippingProviders
) => {
const countryProviders =
shippingProviders[ countryCode ]?.shipping_providers;
if ( ! countryProviders ) {
return [];
}
return countryProviders.length === 1
? [ providers[ countryProviders[ 0 ] ] ]
: [
providers[ countryProviders[ 0 ] ],
providers[ countryProviders[ 1 ] ],
];
};

View File

@ -0,0 +1,10 @@
<svg width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.645508" width="28" height="28" rx="4" fill="#F6F7F7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.9787 8.66671V16.6667L15.9787 20.6667H10.6453C9.90868 20.6667 9.31201 20.07 9.31201 19.3334V8.66671C9.31201 7.93004 9.90868 7.33337 10.6453 7.33337H18.6453C19.382 7.33337 19.9787 7.93004 19.9787 8.66671ZM11.9787 11.3334H17.312V10H11.9787V11.3334ZM11.9787 12.6667H17.312V14H11.9787V12.6667ZM18.6453 8.66671V15.3334H15.9787C15.242 15.3334 14.6453 15.93 14.6453 16.6667V19.3334H10.6453V8.66671H18.6453Z" fill="black"/>
<mask id="mask0_887_116920" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="9" y="7" width="11" height="14">
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.9787 8.66671V16.6667L15.9787 20.6667H10.6453C9.90868 20.6667 9.31201 20.07 9.31201 19.3334V8.66671C9.31201 7.93004 9.90868 7.33337 10.6453 7.33337H18.6453C19.382 7.33337 19.9787 7.93004 19.9787 8.66671ZM11.9787 11.3334H17.312V10H11.9787V11.3334ZM11.9787 12.6667H17.312V14H11.9787V12.6667ZM18.6453 8.66671V15.3334H15.9787C15.242 15.3334 14.6453 15.93 14.6453 16.6667V19.3334H10.6453V8.66671H18.6453Z" fill="white"/>
</mask>
<g mask="url(#mask0_887_116920)">
<rect x="6.64551" y="6" width="16" height="16" fill="#8C8F94"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,9 @@
<svg width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.645508" width="28" height="28" rx="4" fill="#F6F7F7"/>
<mask id="mask0_887_116914" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="7" y="8" width="15" height="13">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.3123 17.3333H19.3123V19.3333C19.3123 20.07 18.7157 20.6667 17.979 20.6667H11.3123C10.5757 20.6667 9.979 20.07 9.979 19.3333V17.3333H7.979V12C7.979 11.2633 8.57567 10.6667 9.31234 10.6667H9.979V9.33333C9.979 8.59667 10.5757 8 11.3123 8H17.979C18.7157 8 19.3123 8.59667 19.3123 9.33333V10.6667H19.979C20.7157 10.6667 21.3123 11.2633 21.3123 12V17.3333ZM12.6457 16.6667H16.6457V18H12.6457V16.6667ZM17.979 10.6667H11.3123V9.33333H17.979V10.6667ZM17.979 15.3333H11.3123V19.3333H17.979V15.3333ZM18.979 12C19.531 12 19.979 12.448 19.979 13C19.979 13.552 19.531 14 18.979 14C18.427 14 17.979 13.552 17.979 13C17.979 12.448 18.427 12 18.979 12Z" fill="white"/>
</mask>
<g mask="url(#mask0_887_116914)">
<rect x="6.64551" y="6" width="16" height="16" fill="#8C8F94"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Fix DefaultShippingPartners data mismatch

View File

@ -124,7 +124,7 @@ class PaymentGatewaySuggestions extends \WC_REST_Data_Controller {
$schema = array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'payment-gateway-suggestions',
'type' => 'array',
'type' => 'object',
'properties' => array(
'content' => array(
'description' => __( 'Suggestion description.', 'woocommerce' ),

View File

@ -209,12 +209,29 @@ class DefaultShippingPartners {
'available_layouts' => array( 'row', 'column' ),
),
array(
'title' => 'WooCommerce Shipping',
'name' => 'WooCommerce Shipping',
'slug' => 'woocommerce-services',
'description' => __( 'Save time and money by printing your shipping labels right from your computer with WooCommerce Shipping. Try WooCommerce Shipping for free.', 'woocommerce' ),
'dependencies' => array( 'jetpack' ),
'layout_column' => array(
'image' => $asset_base_url . 'wcs-column.svg',
'features' => $column_layout_features,
'features' => array(
array(
'icon' => $asset_base_url . 'printer.svg',
'title' => __( 'Buy postage when you need it', 'woocommerce' ),
'description' => __( 'No need to wonder where that stampbook went.', 'woocommerce' ),
),
array(
'icon' => $asset_base_url . 'paper.svg',
'title' => __( 'Print at home', 'woocommerce' ),
'description' => __( 'Pick up an order, then just pay, print, package and post.', 'woocommerce' ),
),
array(
'icon' => $asset_base_url . 'discount.svg',
'title' => __( 'Discounted rates', 'woocommerce' ),
'description' => __( 'Access discounted shipping rates with DHL and USPS.', 'woocommerce' ),
),
),
),
'learn_more_link' => 'https://woocommerce.com/products/shipping/',
'is_visible' => array(