Onboarding: Add shipping rate base task (https://github.com/woocommerce/woocommerce-admin/pull/2760)
This commit is contained in:
parent
dd9948aa1a
commit
f4d7936b17
|
@ -0,0 +1,114 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { decodeEntities } from '@wordpress/html-entities';
|
||||||
|
import { SelectControl, TextControl } from 'newspack-components';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form validation.
|
||||||
|
*
|
||||||
|
* @param {Object} values Keyed values of all fields in the form.
|
||||||
|
* @return {Object} Key value of fields and error messages, { myField: 'This field is required' }
|
||||||
|
*/
|
||||||
|
export function validateStoreAddress( values ) {
|
||||||
|
const errors = {};
|
||||||
|
|
||||||
|
if ( ! values.addressLine1.length ) {
|
||||||
|
errors.addressLine1 = __( 'Please add an address', 'woocommerce-admin' );
|
||||||
|
}
|
||||||
|
if ( ! values.countryState.length ) {
|
||||||
|
errors.countryState = __( 'Please select a country and state', 'woocommerce-admin' );
|
||||||
|
}
|
||||||
|
if ( ! values.city.length ) {
|
||||||
|
errors.city = __( 'Please add a city', 'woocommerce-admin' );
|
||||||
|
}
|
||||||
|
if ( ! values.postCode.length ) {
|
||||||
|
errors.postCode = __( 'Please add a post code', 'woocommerce-admin' );
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all country and state combinations used for select dropdowns.
|
||||||
|
*
|
||||||
|
* @return {Object} Select options, { value: 'US:GA', label: 'United States - Georgia' }
|
||||||
|
*/
|
||||||
|
export function getCountryStateOptions() {
|
||||||
|
const countries = ( wcSettings.dataEndpoints && wcSettings.dataEndpoints.countries ) || [];
|
||||||
|
|
||||||
|
const countryStateOptions = countries.reduce( ( acc, country ) => {
|
||||||
|
if ( ! country.states.length ) {
|
||||||
|
acc.push( {
|
||||||
|
value: country.code,
|
||||||
|
label: decodeEntities( country.name ),
|
||||||
|
} );
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const countryStates = country.states.map( state => {
|
||||||
|
return {
|
||||||
|
value: country.code + ':' + state.code,
|
||||||
|
label: decodeEntities( country.name ) + ' -- ' + decodeEntities( state.name ),
|
||||||
|
};
|
||||||
|
} );
|
||||||
|
|
||||||
|
acc.push( ...countryStates );
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, [] );
|
||||||
|
|
||||||
|
countryStateOptions.unshift( { value: '', label: '' } );
|
||||||
|
|
||||||
|
return countryStateOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store address fields.
|
||||||
|
*
|
||||||
|
* @param {Object} props Props for input components.
|
||||||
|
* @return {Object} -
|
||||||
|
*/
|
||||||
|
export function StoreAddress( props ) {
|
||||||
|
const { getInputProps } = props;
|
||||||
|
const countryStateOptions = useMemo( () => getCountryStateOptions(), [] );
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="woocommerce-store-address-fields">
|
||||||
|
<TextControl
|
||||||
|
label={ __( 'Address line 1', 'woocommerce-admin' ) }
|
||||||
|
required
|
||||||
|
{ ...getInputProps( 'addressLine1' ) }
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextControl
|
||||||
|
label={ __( 'Address line 2 (optional)', 'woocommerce-admin' ) }
|
||||||
|
required
|
||||||
|
{ ...getInputProps( 'addressLine2' ) }
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SelectControl
|
||||||
|
label={ __( 'Country / State', 'woocommerce-admin' ) }
|
||||||
|
required
|
||||||
|
options={ countryStateOptions }
|
||||||
|
{ ...getInputProps( 'countryState' ) }
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextControl
|
||||||
|
label={ __( 'City', 'woocommerce-admin' ) }
|
||||||
|
required
|
||||||
|
{ ...getInputProps( 'city' ) }
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextControl
|
||||||
|
label={ __( 'Post code', 'woocommerce-admin' ) }
|
||||||
|
required
|
||||||
|
{ ...getInputProps( 'postCode' ) }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -73,12 +73,14 @@ class ProfileWizard extends Component {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
document.documentElement.classList.remove( 'wp-toolbar' );
|
document.documentElement.classList.remove( 'wp-toolbar' );
|
||||||
|
document.body.classList.add( 'woocommerce-onboarding' );
|
||||||
document.body.classList.add( 'woocommerce-profile-wizard__body' );
|
document.body.classList.add( 'woocommerce-profile-wizard__body' );
|
||||||
document.body.classList.add( 'woocommerce-admin-full-screen' );
|
document.body.classList.add( 'woocommerce-admin-full-screen' );
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
document.documentElement.classList.add( 'wp-toolbar' );
|
document.documentElement.classList.add( 'wp-toolbar' );
|
||||||
|
document.body.classList.remove( 'woocommerce-onboarding' );
|
||||||
document.body.classList.remove( 'woocommerce-profile-wizard__body' );
|
document.body.classList.remove( 'woocommerce-profile-wizard__body' );
|
||||||
document.body.classList.remove( 'woocommerce-admin-full-screen' );
|
document.body.classList.remove( 'woocommerce-admin-full-screen' );
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,7 +188,6 @@ class BusinessDetails extends Component {
|
||||||
<div className="woocommerce-profile-wizard__benefit-toggle">
|
<div className="woocommerce-profile-wizard__benefit-toggle">
|
||||||
<FormToggle
|
<FormToggle
|
||||||
checked={ values[ benefit.slug ] }
|
checked={ values[ benefit.slug ] }
|
||||||
className="woocommerce-profile-wizard__toggle"
|
|
||||||
{ ...getInputProps( benefit.slug ) }
|
{ ...getInputProps( benefit.slug ) }
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -181,7 +181,6 @@ class Start extends Component {
|
||||||
onChange={ this.onTrackingChange }
|
onChange={ this.onTrackingChange }
|
||||||
onClick={ e => e.stopPropagation() }
|
onClick={ e => e.stopPropagation() }
|
||||||
tabIndex="-1"
|
tabIndex="-1"
|
||||||
className="woocommerce-profile-wizard__toggle"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,9 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Button, SelectControl, TextControl, CheckboxControl } from 'newspack-components';
|
import { Button, CheckboxControl } from 'newspack-components';
|
||||||
import { Component, Fragment } from '@wordpress/element';
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
import { compose } from '@wordpress/compose';
|
import { compose } from '@wordpress/compose';
|
||||||
import { decodeEntities } from '@wordpress/html-entities';
|
|
||||||
import { withDispatch } from '@wordpress/data';
|
import { withDispatch } from '@wordpress/data';
|
||||||
import { recordEvent } from 'lib/tracks';
|
import { recordEvent } from 'lib/tracks';
|
||||||
|
|
||||||
|
@ -15,15 +14,15 @@ import { recordEvent } from 'lib/tracks';
|
||||||
*/
|
*/
|
||||||
import { H, Card, Form } from '@woocommerce/components';
|
import { H, Card, Form } from '@woocommerce/components';
|
||||||
import withSelect from 'wc-api/with-select';
|
import withSelect from 'wc-api/with-select';
|
||||||
|
import {
|
||||||
|
StoreAddress,
|
||||||
|
validateStoreAddress,
|
||||||
|
} from '../../components/settings/general/store-address';
|
||||||
|
|
||||||
class StoreDetails extends Component {
|
class StoreDetails extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super( ...arguments );
|
super( ...arguments );
|
||||||
|
|
||||||
this.state = {
|
|
||||||
countryStateOptions: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
this.initialValues = {
|
this.initialValues = {
|
||||||
addressLine1: '',
|
addressLine1: '',
|
||||||
addressLine2: '',
|
addressLine2: '',
|
||||||
|
@ -36,30 +35,6 @@ class StoreDetails extends Component {
|
||||||
this.onContinue = this.onContinue.bind( this );
|
this.onContinue = this.onContinue.bind( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
const countryStateOptions = this.getCountryStateOptions();
|
|
||||||
this.setState( { countryStateOptions } );
|
|
||||||
}
|
|
||||||
|
|
||||||
validate( values ) {
|
|
||||||
const errors = {};
|
|
||||||
|
|
||||||
if ( ! values.addressLine1.length ) {
|
|
||||||
errors.addressLine1 = __( 'Please add an address', 'woocommerce-admin' );
|
|
||||||
}
|
|
||||||
if ( ! values.countryState.length ) {
|
|
||||||
errors.countryState = __( 'Please select a country and state', 'woocommerce-admin' );
|
|
||||||
}
|
|
||||||
if ( ! values.city.length ) {
|
|
||||||
errors.city = __( 'Please add a city', 'woocommerce-admin' );
|
|
||||||
}
|
|
||||||
if ( ! values.postCode.length ) {
|
|
||||||
errors.postCode = __( 'Please add a post code', 'woocommerce-admin' );
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
async onContinue( values ) {
|
async onContinue( values ) {
|
||||||
const {
|
const {
|
||||||
createNotice,
|
createNotice,
|
||||||
|
@ -97,39 +72,7 @@ class StoreDetails extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getCountryStateOptions() {
|
|
||||||
const countries = ( wcSettings.dataEndpoints && wcSettings.dataEndpoints.countries ) || [];
|
|
||||||
|
|
||||||
const countryStateOptions = countries.reduce( ( acc, country ) => {
|
|
||||||
if ( ! country.states.length ) {
|
|
||||||
acc.push( {
|
|
||||||
value: country.code,
|
|
||||||
label: decodeEntities( country.name ),
|
|
||||||
} );
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
const countryStates = country.states.map( state => {
|
|
||||||
return {
|
|
||||||
value: country.code + ':' + state.code,
|
|
||||||
label: decodeEntities( country.name ) + ' -- ' + decodeEntities( state.name ),
|
|
||||||
};
|
|
||||||
} );
|
|
||||||
|
|
||||||
acc.push( ...countryStates );
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, [] );
|
|
||||||
|
|
||||||
countryStateOptions.unshift( { value: '', label: '' } );
|
|
||||||
|
|
||||||
return countryStateOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { countryStateOptions } = this.state;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<H className="woocommerce-profile-wizard__header-title">
|
<H className="woocommerce-profile-wizard__header-title">
|
||||||
|
@ -146,41 +89,11 @@ class StoreDetails extends Component {
|
||||||
<Form
|
<Form
|
||||||
initialValues={ this.initialValues }
|
initialValues={ this.initialValues }
|
||||||
onSubmitCallback={ this.onContinue }
|
onSubmitCallback={ this.onContinue }
|
||||||
validate={ this.validate }
|
validate={ validateStoreAddress }
|
||||||
>
|
>
|
||||||
{ ( { getInputProps, handleSubmit } ) => (
|
{ ( { getInputProps, handleSubmit } ) => (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<TextControl
|
<StoreAddress getInputProps={ getInputProps } />
|
||||||
label={ __( 'Address line 1', 'woocommerce-admin' ) }
|
|
||||||
required
|
|
||||||
{ ...getInputProps( 'addressLine1' ) }
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextControl
|
|
||||||
label={ __( 'Address line 2 (optional)', 'woocommerce-admin' ) }
|
|
||||||
required
|
|
||||||
{ ...getInputProps( 'addressLine2' ) }
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SelectControl
|
|
||||||
label={ __( 'Country / State', 'woocommerce-admin' ) }
|
|
||||||
required
|
|
||||||
options={ countryStateOptions }
|
|
||||||
{ ...getInputProps( 'countryState' ) }
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextControl
|
|
||||||
label={ __( 'City', 'woocommerce-admin' ) }
|
|
||||||
required
|
|
||||||
{ ...getInputProps( 'city' ) }
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextControl
|
|
||||||
label={ __( 'Post code', 'woocommerce-admin' ) }
|
|
||||||
required
|
|
||||||
{ ...getInputProps( 'postCode' ) }
|
|
||||||
/>
|
|
||||||
|
|
||||||
<CheckboxControl
|
<CheckboxControl
|
||||||
label={ __( "I'm setting up a store for a client", 'woocommerce-admin' ) }
|
label={ __( "I'm setting up a store for a client", 'woocommerce-admin' ) }
|
||||||
{ ...getInputProps( 'isClient' ) }
|
{ ...getInputProps( 'isClient' ) }
|
||||||
|
|
|
@ -211,7 +211,7 @@
|
||||||
|
|
||||||
.woocommerce-theme-preview__theme-name {
|
.woocommerce-theme-preview__theme-name {
|
||||||
padding-left: $gap;
|
padding-left: $gap;
|
||||||
color: #1a1a1a;
|
color: $muriel-gray-900;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
|
@ -36,28 +36,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-stepper .woocommerce-stepper__step {
|
|
||||||
.woocommerce-stepper__step-label {
|
|
||||||
color: $muriel-gray-800;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-active,
|
|
||||||
&.is-complete {
|
|
||||||
.woocommerce-stepper__step-icon {
|
|
||||||
background: $muriel-hot-purple-600;
|
|
||||||
color: $muriel-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.woocommerce-stepper__step-label {
|
|
||||||
color: $muriel-gray-900;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.woocommerce-spinner {
|
|
||||||
background: $muriel-hot-purple-600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.woocommerce-profile-wizard__header {
|
.woocommerce-profile-wizard__header {
|
||||||
height: 56px;
|
height: 56px;
|
||||||
border-bottom: 1px solid $muriel-gray-50;
|
border-bottom: 1px solid $muriel-gray-50;
|
||||||
|
@ -137,26 +115,6 @@
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Muriel style overrides */
|
|
||||||
.muriel-component {
|
|
||||||
margin-top: $gap;
|
|
||||||
margin-bottom: $gap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.components-base-control.has-error {
|
|
||||||
margin-bottom: $gap * 2 !important;
|
|
||||||
border-color: $muriel-red-500;
|
|
||||||
|
|
||||||
.components-base-control__help {
|
|
||||||
top: 100%;
|
|
||||||
position: absolute;
|
|
||||||
margin-top: $gap-smallest;
|
|
||||||
font-size: 12px;
|
|
||||||
font-style: normal;
|
|
||||||
color: $muriel-red-500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.woocommerce-profile-wizard__error {
|
.woocommerce-profile-wizard__error {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: $gap;
|
margin-top: $gap;
|
||||||
|
@ -165,88 +123,6 @@
|
||||||
color: $muriel-red-500;
|
color: $muriel-red-500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.muriel-input-text {
|
|
||||||
&.empty {
|
|
||||||
.components-base-control__label {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
top: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type='text']:focus {
|
|
||||||
box-shadow: none;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.muriel-input-text label,
|
|
||||||
.muriel-select label {
|
|
||||||
display: initial;
|
|
||||||
width: auto;
|
|
||||||
right: auto;
|
|
||||||
left: $gap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.muriel-select select {
|
|
||||||
left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.muriel-select.empty label {
|
|
||||||
top: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.muriel-select::after {
|
|
||||||
margin-top: -$gap-smallest;
|
|
||||||
}
|
|
||||||
|
|
||||||
.muriel-input-text,
|
|
||||||
.muriel-select {
|
|
||||||
&.with-value,
|
|
||||||
&.active {
|
|
||||||
label {
|
|
||||||
top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input,
|
|
||||||
select {
|
|
||||||
top: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.muriel-input-text.active,
|
|
||||||
.muriel-select.active {
|
|
||||||
box-shadow: 0 0 0 2px $muriel-hot-purple-600;
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.muriel-checkbox label.components-checkbox-control__label {
|
|
||||||
margin-left: $gap-smaller;
|
|
||||||
}
|
|
||||||
|
|
||||||
.muriel-checkbox input[type='checkbox'] {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.muriel-checkbox input[type='checkbox']:checked::before {
|
|
||||||
font-size: 18px;
|
|
||||||
margin-left: -3px;
|
|
||||||
margin-top: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.muriel-button.is-button {
|
|
||||||
height: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.muriel-checkbox input:checked {
|
|
||||||
background-color: $muriel-hot-purple-600;
|
|
||||||
border-color: $muriel-hot-purple-600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.woocommerce-profile-wizard__benefit {
|
.woocommerce-profile-wizard__benefit {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
@ -294,42 +170,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-profile-wizard__toggle {
|
|
||||||
label {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.components-base-control {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.components-form-toggle {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.components-form-toggle .components-form-toggle__track {
|
|
||||||
width: 36px;
|
|
||||||
max-width: 36px;
|
|
||||||
height: 18px;
|
|
||||||
max-height: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.components-base-control__field {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.muriel-checkbox label.components-checkbox-control__label {
|
|
||||||
margin-left: $gap-large;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.components-form-toggle.is-checked {
|
|
||||||
.components-form-toggle__track {
|
|
||||||
background-color: $muriel-hot-purple-600;
|
|
||||||
border-color: $muriel-hot-purple-600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.woocommerce-profile-wizard__tracking {
|
.woocommerce-profile-wizard__tracking {
|
||||||
.components-form-toggle {
|
.components-form-toggle {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -75,28 +75,163 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-dashboard__body {
|
.woocommerce-onboarding {
|
||||||
background: $muriel-gray-0;
|
.woocommerce-stepper .woocommerce-stepper__step {
|
||||||
color: $muriel-gray-600;
|
.woocommerce-stepper__step-label {
|
||||||
font-family: $default-font;
|
color: $muriel-gray-800;
|
||||||
|
}
|
||||||
|
|
||||||
#wpbody-content {
|
&.is-active,
|
||||||
min-height: 100vh;
|
&.is-complete {
|
||||||
|
.woocommerce-stepper__step-icon {
|
||||||
|
background: $muriel-hot-purple-600;
|
||||||
|
color: $muriel-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-stepper__step-label {
|
||||||
|
color: $muriel-gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-spinner {
|
||||||
|
background: $muriel-hot-purple-600;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide wp-admin and WooCommerce elements when the dashboard body class is present */
|
/* Muriel style overrides */
|
||||||
#adminmenumain,
|
.muriel-component {
|
||||||
.woocommerce-layout__header,
|
margin-top: $gap;
|
||||||
.update-nag,
|
margin-bottom: $gap;
|
||||||
.woocommerce-store-alerts,
|
|
||||||
.woocommerce-message,
|
|
||||||
.notice,
|
|
||||||
.error,
|
|
||||||
.updated {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#wpcontent {
|
.components-base-control.has-error {
|
||||||
margin-left: 0;
|
margin-bottom: $gap * 2 !important;
|
||||||
|
border-color: $muriel-red-500;
|
||||||
|
|
||||||
|
.components-base-control__help {
|
||||||
|
top: 100%;
|
||||||
|
position: absolute;
|
||||||
|
margin-top: $gap-smallest;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
color: $muriel-red-500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-input-text {
|
||||||
|
&.empty {
|
||||||
|
.components-base-control__label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
top: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='text']:focus {
|
||||||
|
box-shadow: none;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-input-text label,
|
||||||
|
.muriel-select label {
|
||||||
|
display: initial;
|
||||||
|
width: auto;
|
||||||
|
right: auto;
|
||||||
|
left: $gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-select select {
|
||||||
|
left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-select.empty label {
|
||||||
|
top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-select::after {
|
||||||
|
margin-top: -$gap-smallest;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-input-text,
|
||||||
|
.muriel-select {
|
||||||
|
&.with-value,
|
||||||
|
&.active {
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select {
|
||||||
|
top: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-input-text.active,
|
||||||
|
.muriel-select.active {
|
||||||
|
box-shadow: 0 0 0 2px $muriel-hot-purple-600;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-checkbox label.components-checkbox-control__label {
|
||||||
|
margin-left: $gap-smaller;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-checkbox input[type='checkbox'] {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-checkbox input[type='checkbox']:checked::before {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-left: -3px;
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-button.is-button {
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-checkbox input[type='checkbox']:checked {
|
||||||
|
background-color: $muriel-hot-purple-600;
|
||||||
|
border-color: $muriel-hot-purple-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-form-toggle {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-base-control {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-form-toggle__track {
|
||||||
|
width: 36px;
|
||||||
|
max-width: 36px;
|
||||||
|
height: 18px;
|
||||||
|
max-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-base-control__field {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-checkbox label.components-checkbox-control__label {
|
||||||
|
margin-left: $gap-large;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-checked {
|
||||||
|
.components-form-toggle__track {
|
||||||
|
background-color: $muriel-hot-purple-600;
|
||||||
|
border-color: $muriel-hot-purple-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,19 +19,22 @@ import { updateQueryString } from '@woocommerce/navigation';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
import Connect from './tasks/connect';
|
import Connect from './tasks/connect';
|
||||||
import Products from './tasks/products';
|
import Products from './tasks/products';
|
||||||
|
import Shipping from './tasks/shipping';
|
||||||
import withSelect from 'wc-api/with-select';
|
import withSelect from 'wc-api/with-select';
|
||||||
|
|
||||||
class TaskDashboard extends Component {
|
class TaskDashboard extends Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
document.body.classList.add( 'woocommerce-onboarding' );
|
||||||
document.body.classList.add( 'woocommerce-task-dashboard__body' );
|
document.body.classList.add( 'woocommerce-task-dashboard__body' );
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
document.body.classList.remove( 'woocommerce-onboarding' );
|
||||||
document.body.classList.remove( 'woocommerce-task-dashboard__body' );
|
document.body.classList.remove( 'woocommerce-task-dashboard__body' );
|
||||||
}
|
}
|
||||||
|
|
||||||
getTasks() {
|
getTasks() {
|
||||||
const { tasks } = wcSettings.onboarding;
|
const { shippingZonesCount, tasks } = wcSettings.onboarding;
|
||||||
const { profileItems, query } = this.props;
|
const { profileItems, query } = this.props;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
@ -82,9 +85,16 @@ class TaskDashboard extends Component {
|
||||||
'Configure some basic shipping rates to get started',
|
'Configure some basic shipping rates to get started',
|
||||||
'wooocommerce-admin'
|
'wooocommerce-admin'
|
||||||
),
|
),
|
||||||
before: <i className="material-icons-outlined">local_shipping</i>,
|
before:
|
||||||
|
shippingZonesCount > 0 ? (
|
||||||
|
<i className="material-icons-outlined">check_circle</i>
|
||||||
|
) : (
|
||||||
|
<i className="material-icons-outlined">local_shipping</i>
|
||||||
|
),
|
||||||
after: <i className="material-icons-outlined">chevron_right</i>,
|
after: <i className="material-icons-outlined">chevron_right</i>,
|
||||||
onClick: noop,
|
onClick: () => updateQueryString( { task: 'shipping' } ),
|
||||||
|
container: <Shipping />,
|
||||||
|
className: shippingZonesCount > 0 ? 'is-complete' : null,
|
||||||
visible: true,
|
visible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -136,6 +146,7 @@ class TaskDashboard extends Component {
|
||||||
currentTask.container
|
currentTask.container
|
||||||
) : (
|
) : (
|
||||||
<Card
|
<Card
|
||||||
|
className="woocommerce-task-card"
|
||||||
title={ __( 'Set up your store and start selling', 'woocommerce-admin' ) }
|
title={ __( 'Set up your store and start selling', 'woocommerce-admin' ) }
|
||||||
description={ __(
|
description={ __(
|
||||||
'Below you’ll find a list of the most important steps to get your store up and running.',
|
'Below you’ll find a list of the most important steps to get your store up and running.',
|
||||||
|
|
|
@ -5,11 +5,22 @@
|
||||||
color: $muriel-gray-500;
|
color: $muriel-gray-500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-card__body {
|
.woocommerce-task-card .woocommerce-card__body {
|
||||||
border-top: 1px solid $muriel-gray-50;
|
border-top: 1px solid $muriel-gray-50;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.woocommerce-card.is-narrow {
|
||||||
|
max-width: 680px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muriel-button.is-button {
|
||||||
|
height: 40px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.woocommerce-list {
|
.woocommerce-list {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
@ -53,3 +64,74 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.woocommerce-shipping-rates {
|
||||||
|
margin-bottom: $gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-shipping-rate {
|
||||||
|
display: flex;
|
||||||
|
padding-top: $gap-small;
|
||||||
|
padding-bottom: $gap-small;
|
||||||
|
|
||||||
|
.woocommerce-shipping-rate__main {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-shipping-rate__icon {
|
||||||
|
padding-top: $gap;
|
||||||
|
margin-right: $gap-large;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-shipping-rate__name {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
padding-top: $gap;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 22px;
|
||||||
|
color: $muriel-gray-900;
|
||||||
|
margin-bottom: $gap-small;
|
||||||
|
border-top: 1px solid $new-muriel-gray-5;
|
||||||
|
|
||||||
|
.components-form-toggle {
|
||||||
|
margin-left: auto;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-shipping-rate__control-wrapper {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.muriel-input-text input {
|
||||||
|
left: $gap * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-base-control {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-shipping-rate__control-prefix,
|
||||||
|
.woocommerce-shipping-rate__control-suffix {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
left: $gap;
|
||||||
|
top: 26px;
|
||||||
|
z-index: 1;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
color: $new-muriel-gray-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-value {
|
||||||
|
.woocommerce-shipping-rate__control-prefix,
|
||||||
|
.woocommerce-shipping-rate__control-suffix {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-shipping-rate__control-suffix {
|
||||||
|
left: auto;
|
||||||
|
right: $gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ export default class Products extends Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Card>
|
<Card className="woocommerce-task-card">
|
||||||
<List items={ subTasks } />
|
<List items={ subTasks } />
|
||||||
</Card>
|
</Card>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import apiFetch from '@wordpress/api-fetch';
|
||||||
|
import { Component } from '@wordpress/element';
|
||||||
|
import { compose } from '@wordpress/compose';
|
||||||
|
import { filter } from 'lodash';
|
||||||
|
import { withDispatch } from '@wordpress/data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce dependencies
|
||||||
|
*/
|
||||||
|
import { Card, Stepper } from '@woocommerce/components';
|
||||||
|
import { getHistory, getNewPath } from '@woocommerce/navigation';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import StoreLocation from './location';
|
||||||
|
import ShippingRates from './rates';
|
||||||
|
import withSelect from 'wc-api/with-select';
|
||||||
|
|
||||||
|
class Shipping extends Component {
|
||||||
|
constructor() {
|
||||||
|
super( ...arguments );
|
||||||
|
|
||||||
|
this.initialState = {
|
||||||
|
isPending: false,
|
||||||
|
step: 'store_location',
|
||||||
|
shippingZones: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
this.state = this.initialState;
|
||||||
|
|
||||||
|
this.completeStep = this.completeStep.bind( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.setState( this.initialState );
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchShippingZones() {
|
||||||
|
this.setState( { isPending: true } );
|
||||||
|
const { countryCode, countryName } = this.props;
|
||||||
|
|
||||||
|
// @todo The following fetches for shipping information should be moved into
|
||||||
|
// the wc-api to make these methods and states more readily available.
|
||||||
|
const shippingZones = [];
|
||||||
|
const zones = await apiFetch( { path: '/wc/v3/shipping/zones' } );
|
||||||
|
let hasCountryZone = false;
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
zones.map( async zone => {
|
||||||
|
// "Rest of the world zone"
|
||||||
|
if ( 0 === zone.id ) {
|
||||||
|
zone.methods = await apiFetch( { path: `/wc/v3/shipping/zones/${ zone.id }/methods` } );
|
||||||
|
zone.name = __( 'Rest of the world', 'woocommerce-admin' );
|
||||||
|
zone.toggleEnabled = true;
|
||||||
|
shippingZones.push( zone );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return any zone with a single location matching the country zone.
|
||||||
|
zone.locations = await apiFetch( { path: `/wc/v3/shipping/zones/${ zone.id }/locations` } );
|
||||||
|
const countryLocation = zone.locations.find( location => countryCode === location.code );
|
||||||
|
if ( countryLocation ) {
|
||||||
|
zone.methods = await apiFetch( { path: `/wc/v3/shipping/zones/${ zone.id }/methods` } );
|
||||||
|
shippingZones.push( zone );
|
||||||
|
hasCountryZone = true;
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create the default store country zone if it doesn't exist.
|
||||||
|
if ( ! hasCountryZone ) {
|
||||||
|
const zone = await apiFetch( {
|
||||||
|
method: 'POST',
|
||||||
|
path: '/wc/v3/shipping/zones',
|
||||||
|
data: { name: countryName },
|
||||||
|
} );
|
||||||
|
zone.locations = await apiFetch( {
|
||||||
|
method: 'POST',
|
||||||
|
path: `/wc/v3/shipping/zones/${ zone.id }/locations`,
|
||||||
|
data: [ { code: countryCode, type: 'country' } ],
|
||||||
|
} );
|
||||||
|
shippingZones.push( zone );
|
||||||
|
}
|
||||||
|
|
||||||
|
shippingZones.reverse();
|
||||||
|
|
||||||
|
this.setState( { isPending: false, shippingZones } );
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate( prevProps, prevState ) {
|
||||||
|
const { countryCode, settings } = this.props;
|
||||||
|
const {
|
||||||
|
woocommerce_store_address,
|
||||||
|
woocommerce_default_country,
|
||||||
|
woocommerce_store_postcode,
|
||||||
|
} = settings;
|
||||||
|
const { step } = this.state;
|
||||||
|
|
||||||
|
if (
|
||||||
|
'store_location' === step &&
|
||||||
|
woocommerce_store_address &&
|
||||||
|
woocommerce_default_country &&
|
||||||
|
woocommerce_store_postcode
|
||||||
|
) {
|
||||||
|
this.completeStep();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
'rates' === step &&
|
||||||
|
( prevProps.countryCode !== countryCode || 'rates' !== prevState.step )
|
||||||
|
) {
|
||||||
|
this.fetchShippingZones();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
completeStep() {
|
||||||
|
const { step } = this.state;
|
||||||
|
const steps = this.getSteps();
|
||||||
|
const currentStepIndex = steps.findIndex( s => s.key === step );
|
||||||
|
const nextStep = steps[ currentStepIndex + 1 ];
|
||||||
|
|
||||||
|
if ( nextStep ) {
|
||||||
|
this.setState( { step: nextStep.key } );
|
||||||
|
} else {
|
||||||
|
getHistory().push( getNewPath( {}, '/', {} ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSteps() {
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
key: 'store_location',
|
||||||
|
label: __( 'Set store location', 'woocommerce-admin' ),
|
||||||
|
description: __( 'The address from which your business operates', 'woocommerce-admin' ),
|
||||||
|
content: <StoreLocation completeStep={ this.completeStep } { ...this.props } />,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'rates',
|
||||||
|
label: __( 'Set shipping costs', 'woocommerce-admin' ),
|
||||||
|
description: __(
|
||||||
|
'Define how much customers pay to ship to different destinations',
|
||||||
|
'woocommerce-admin'
|
||||||
|
),
|
||||||
|
content: (
|
||||||
|
<ShippingRates
|
||||||
|
shippingZones={ this.state.shippingZones }
|
||||||
|
completeStep={ this.completeStep }
|
||||||
|
{ ...this.props }
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return filter( steps, step => step.visible );
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isPending, step } = this.state;
|
||||||
|
const { isSettingsRequesting } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="woocommerce-task-shipping">
|
||||||
|
<Card className="is-narrow">
|
||||||
|
<Stepper
|
||||||
|
isPending={ isPending || isSettingsRequesting }
|
||||||
|
isVertical
|
||||||
|
currentStep={ step }
|
||||||
|
steps={ this.getSteps() }
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withSelect( select => {
|
||||||
|
const { getSettings, getSettingsError, isGetSettingsRequesting } = select( 'wc-api' );
|
||||||
|
|
||||||
|
const settings = getSettings( 'general' );
|
||||||
|
const isSettingsError = Boolean( getSettingsError( 'general' ) );
|
||||||
|
const isSettingsRequesting = isGetSettingsRequesting( 'general' );
|
||||||
|
|
||||||
|
const countryCode = settings.woocommerce_default_country
|
||||||
|
? settings.woocommerce_default_country.split( ':' )[ 0 ]
|
||||||
|
: null;
|
||||||
|
const countries = ( wcSettings.dataEndpoints && wcSettings.dataEndpoints.countries ) || [];
|
||||||
|
const country = countryCode ? countries.find( c => c.code === countryCode ) : null;
|
||||||
|
const countryName = country ? country.name : null;
|
||||||
|
|
||||||
|
return { countryCode, countryName, isSettingsError, isSettingsRequesting, settings };
|
||||||
|
} ),
|
||||||
|
withDispatch( dispatch => {
|
||||||
|
const { createNotice } = dispatch( 'core/notices' );
|
||||||
|
const { updateSettings } = dispatch( 'wc-api' );
|
||||||
|
|
||||||
|
return {
|
||||||
|
createNotice,
|
||||||
|
updateSettings,
|
||||||
|
};
|
||||||
|
} )
|
||||||
|
)( Shipping );
|
|
@ -0,0 +1,95 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Button } from 'newspack-components';
|
||||||
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce dependencies
|
||||||
|
*/
|
||||||
|
import { Form } from '@woocommerce/components';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
StoreAddress,
|
||||||
|
validateStoreAddress,
|
||||||
|
} from 'dashboard/components/settings/general/store-address';
|
||||||
|
|
||||||
|
export default class StoreLocation extends Component {
|
||||||
|
constructor() {
|
||||||
|
super( ...arguments );
|
||||||
|
this.onSubmit = this.onSubmit.bind( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
async onSubmit( values ) {
|
||||||
|
const { completeStep, createNotice, isSettingsError, updateSettings } = this.props;
|
||||||
|
|
||||||
|
await updateSettings( {
|
||||||
|
general: {
|
||||||
|
woocommerce_store_address: values.addressLine1,
|
||||||
|
woocommerce_store_address_2: values.addressLine2,
|
||||||
|
woocommerce_default_country: values.countryState,
|
||||||
|
woocommerce_store_city: values.city,
|
||||||
|
woocommerce_store_postcode: values.postCode,
|
||||||
|
},
|
||||||
|
} );
|
||||||
|
|
||||||
|
if ( ! isSettingsError ) {
|
||||||
|
completeStep();
|
||||||
|
} else {
|
||||||
|
createNotice(
|
||||||
|
'error',
|
||||||
|
__( 'There was a problem saving your store location.', 'woocommerce-admin' )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getInitialValues() {
|
||||||
|
const { settings } = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
woocommerce_store_address,
|
||||||
|
woocommerce_store_address_2,
|
||||||
|
woocommerce_store_city,
|
||||||
|
woocommerce_default_country,
|
||||||
|
woocommerce_store_postcode,
|
||||||
|
} = settings;
|
||||||
|
|
||||||
|
return {
|
||||||
|
addressLine1: woocommerce_store_address || '',
|
||||||
|
addressLine2: woocommerce_store_address_2 || '',
|
||||||
|
city: woocommerce_store_city || '',
|
||||||
|
countryState: woocommerce_default_country || '',
|
||||||
|
postCode: woocommerce_store_postcode || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isSettingsRequesting } = this.props;
|
||||||
|
|
||||||
|
if ( isSettingsRequesting ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
initialValues={ this.getInitialValues() }
|
||||||
|
onSubmitCallback={ this.onSubmit }
|
||||||
|
validate={ validateStoreAddress }
|
||||||
|
>
|
||||||
|
{ ( { getInputProps, handleSubmit } ) => (
|
||||||
|
<Fragment>
|
||||||
|
<StoreAddress getInputProps={ getInputProps } />
|
||||||
|
<Button isPrimary onClick={ handleSubmit }>
|
||||||
|
{ __( 'Continue', 'woocommerce-admin' ) }
|
||||||
|
</Button>
|
||||||
|
</Fragment>
|
||||||
|
) }
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,244 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import apiFetch from '@wordpress/api-fetch';
|
||||||
|
import { Button, TextControl } from 'newspack-components';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
|
import { FormToggle } from '@wordpress/components';
|
||||||
|
import { get } from 'lodash';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce dependencies
|
||||||
|
*/
|
||||||
|
import { Flag, Form } from '@woocommerce/components';
|
||||||
|
import { getCurrencyFormatString } from '@woocommerce/currency';
|
||||||
|
|
||||||
|
class ShippingRates extends Component {
|
||||||
|
constructor() {
|
||||||
|
super( ...arguments );
|
||||||
|
|
||||||
|
this.updateShippingZones = this.updateShippingZones.bind( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateShippingZones( values ) {
|
||||||
|
const { createNotice, shippingZones } = this.props;
|
||||||
|
|
||||||
|
shippingZones.map( zone => {
|
||||||
|
const flatRateMethods = zone.methods
|
||||||
|
? zone.methods.filter( method => 'flat_rate' === method.method_id )
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if ( zone.toggleEnabled && ! values[ `${ zone.id }_enabled` ] ) {
|
||||||
|
// Disable any flat rate methods that exist if toggled off.
|
||||||
|
if ( flatRateMethods.length ) {
|
||||||
|
flatRateMethods.map( method => {
|
||||||
|
apiFetch( {
|
||||||
|
method: 'POST',
|
||||||
|
path: `/wc/v3/shipping/zones/${ zone.id }/methods/${ method.instance_id }`,
|
||||||
|
data: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( flatRateMethods.length ) {
|
||||||
|
// Update the existing method.
|
||||||
|
apiFetch( {
|
||||||
|
method: 'POST',
|
||||||
|
path: `/wc/v3/shipping/zones/${ zone.id }/methods/${ flatRateMethods[ 0 ].instance_id }`,
|
||||||
|
data: {
|
||||||
|
enabled: true,
|
||||||
|
settings: { cost: values[ `${ zone.id }_rate` ] },
|
||||||
|
},
|
||||||
|
} );
|
||||||
|
} else {
|
||||||
|
// Add new method if one doesn't exist.
|
||||||
|
apiFetch( {
|
||||||
|
method: 'POST',
|
||||||
|
path: `/wc/v3/shipping/zones/${ zone.id }/methods`,
|
||||||
|
data: {
|
||||||
|
method_id: 'flat_rate',
|
||||||
|
settings: { cost: values[ `${ zone.id }_rate` ] },
|
||||||
|
},
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// @todo This is a workaround to force the task to mark as complete.
|
||||||
|
// This should probably be updated to use wc-api so we can fetch shipping methods.
|
||||||
|
wcSettings.onboarding.shippingZonesCount = 1;
|
||||||
|
|
||||||
|
createNotice( 'success', __( 'Your shipping rates have been updated.', 'woocommerce-admin' ) );
|
||||||
|
|
||||||
|
this.props.completeStep();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderInputPrefix() {
|
||||||
|
const symbolPosition = get( wcSettings, [ 'currency', 'position' ] );
|
||||||
|
if ( 0 === symbolPosition.indexOf( 'right' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="woocommerce-shipping-rate__control-prefix">
|
||||||
|
{ get( wcSettings, [ 'currency', 'symbol' ], '$' ) }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderInputSuffix( rate ) {
|
||||||
|
const symbolPosition = get( wcSettings, [ 'currency', 'position' ] );
|
||||||
|
if ( 0 === symbolPosition.indexOf( 'right' ) ) {
|
||||||
|
return (
|
||||||
|
<span className="woocommerce-shipping-rate__control-suffix">
|
||||||
|
{ get( wcSettings, [ 'currency', 'symbol' ], '$' ) }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseFloat( rate ) === parseFloat( 0 ) ? (
|
||||||
|
<span className="woocommerce-shipping-rate__control-suffix">
|
||||||
|
{ __( 'Free shipping', 'woocommerce-admin' ) }
|
||||||
|
</span>
|
||||||
|
) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInitialValues() {
|
||||||
|
const values = {};
|
||||||
|
|
||||||
|
this.props.shippingZones.forEach( zone => {
|
||||||
|
const flatRateMethods =
|
||||||
|
zone.methods && zone.methods.length
|
||||||
|
? zone.methods.filter( method => 'flat_rate' === method.method_id )
|
||||||
|
: [];
|
||||||
|
const rate = flatRateMethods.length
|
||||||
|
? flatRateMethods[ 0 ].settings.cost.value
|
||||||
|
: getCurrencyFormatString( 0 );
|
||||||
|
values[ `${ zone.id }_rate` ] = rate;
|
||||||
|
|
||||||
|
if ( flatRateMethods.length && flatRateMethods[ 0 ].enabled ) {
|
||||||
|
values[ `${ zone.id }_enabled` ] = true;
|
||||||
|
} else {
|
||||||
|
values[ `${ zone.id }_enabled` ] = false;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
validate( values ) {
|
||||||
|
const errors = {};
|
||||||
|
|
||||||
|
const rates = Object.keys( values ).filter( field => field.endsWith( '_rate' ) );
|
||||||
|
|
||||||
|
rates.forEach( rate => {
|
||||||
|
if ( values[ rate ] < 0 ) {
|
||||||
|
errors[ rate ] = __( 'Shipping rates can not be negative numbers.', 'woocommerce-admin' );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { shippingZones } = this.props;
|
||||||
|
|
||||||
|
if ( ! shippingZones.length ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
initialValues={ this.getInitialValues() }
|
||||||
|
onSubmitCallback={ this.updateShippingZones }
|
||||||
|
validate={ this.validate }
|
||||||
|
>
|
||||||
|
{ ( { getInputProps, handleSubmit, setTouched, setValue, values } ) => {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<div className="woocommerce-shipping-rates">
|
||||||
|
{ shippingZones.map( zone => (
|
||||||
|
<div className="woocommerce-shipping-rate" key={ zone.id }>
|
||||||
|
<div className="woocommerce-shipping-rate__icon">
|
||||||
|
{ zone.locations ? (
|
||||||
|
zone.locations.map( location => (
|
||||||
|
<Flag size={ 24 } code={ location.code } key={ location.code } />
|
||||||
|
) )
|
||||||
|
) : (
|
||||||
|
// Icon used for zones without locations or "Rest of the world".
|
||||||
|
<i className="material-icons-outlined">public</i>
|
||||||
|
) }
|
||||||
|
</div>
|
||||||
|
<div className="woocommerce-shipping-rate__main">
|
||||||
|
<div className="woocommerce-shipping-rate__name">
|
||||||
|
{ zone.name }
|
||||||
|
{ zone.toggleEnabled && (
|
||||||
|
<FormToggle { ...getInputProps( `${ zone.id }_enabled` ) } />
|
||||||
|
) }
|
||||||
|
</div>
|
||||||
|
{ ( ! zone.toggleEnabled || values[ `${ zone.id }_enabled` ] ) && (
|
||||||
|
<div
|
||||||
|
className={ classnames( 'woocommerce-shipping-rate__control-wrapper', {
|
||||||
|
'has-value': values[ `${ zone.id }_rate` ],
|
||||||
|
} ) }
|
||||||
|
>
|
||||||
|
{ this.renderInputPrefix() }
|
||||||
|
<TextControl
|
||||||
|
label={ __( 'Shipping cost', 'woocommerce-admin' ) }
|
||||||
|
required
|
||||||
|
{ ...getInputProps( `${ zone.id }_rate` ) }
|
||||||
|
onBlur={ () => {
|
||||||
|
setTouched( `${ zone.id }_rate` );
|
||||||
|
setValue(
|
||||||
|
`${ zone.id }_rate`,
|
||||||
|
getCurrencyFormatString( values[ `${ zone.id }_rate` ] )
|
||||||
|
);
|
||||||
|
} }
|
||||||
|
/>
|
||||||
|
{ this.renderInputSuffix( values[ `${ zone.id }_rate` ] ) }
|
||||||
|
</div>
|
||||||
|
) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) ) }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button isPrimary onClick={ handleSubmit }>
|
||||||
|
{ __( 'Complete task', 'woocommerce-admin' ) }
|
||||||
|
</Button>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
} }
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShippingRates.propTypes = {
|
||||||
|
/**
|
||||||
|
* Function used to mark the step complete.
|
||||||
|
*/
|
||||||
|
completeStep: PropTypes.func.isRequired,
|
||||||
|
/**
|
||||||
|
* Function to create a transient notice in the store.
|
||||||
|
*/
|
||||||
|
createNotice: PropTypes.func.isRequired,
|
||||||
|
/**
|
||||||
|
* Array of shipping zones returned from the WC REST API with added
|
||||||
|
* `methods` and `locations` properties appended from separate API calls.
|
||||||
|
*/
|
||||||
|
shippingZones: PropTypes.array,
|
||||||
|
};
|
||||||
|
|
||||||
|
ShippingRates.defaultProps = {
|
||||||
|
shippingZones: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ShippingRates;
|
|
@ -82,4 +82,7 @@ $muriel-box-shadow-1dp: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0
|
||||||
0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||||
$muriel-box-shadow-8dp: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14),
|
$muriel-box-shadow-8dp: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14),
|
||||||
0 3px 14px 2px rgba(0, 0, 0, 0.12);
|
0 3px 14px 2px rgba(0, 0, 0, 0.12);
|
||||||
$muriel-primary-500: #005fb7;
|
// @todo These can be removed once color-studio is updated to >= 2.0.0.
|
||||||
|
$new-muriel-gray-50: #676a74;
|
||||||
|
$new-muriel-gray-5: #e3dfe2;
|
||||||
|
$new-muriel-primary-500: #005fb7;
|
||||||
|
|
|
@ -80,7 +80,8 @@ function settingToSettingsResource( resourceName, data ) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const resources = {};
|
// Override lastReceived time for group when batch updating.
|
||||||
|
const resources = { [ resourceName ]: { lastReceived: Date.now() } };
|
||||||
data.update.forEach(
|
data.update.forEach(
|
||||||
setting =>
|
setting =>
|
||||||
( resources[ getResourceName( resourceName, setting.id ) ] = { data: setting.value } )
|
( resources[ getResourceName( resourceName, setting.id ) ] = { data: setting.value } )
|
||||||
|
|
|
@ -20,6 +20,7 @@ class Form extends Component {
|
||||||
|
|
||||||
this.getInputProps = this.getInputProps.bind( this );
|
this.getInputProps = this.getInputProps.bind( this );
|
||||||
this.handleSubmit = this.handleSubmit.bind( this );
|
this.handleSubmit = this.handleSubmit.bind( this );
|
||||||
|
this.setTouched = this.setTouched.bind( this );
|
||||||
this.setValue = this.setValue.bind( this );
|
this.setValue = this.setValue.bind( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +45,12 @@ class Form extends Component {
|
||||||
} ), this.validate );
|
} ), this.validate );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTouched( name, touched = true ) {
|
||||||
|
this.setState( prevState => ( {
|
||||||
|
touched: { ...prevState.touched, [ name ]: touched },
|
||||||
|
} ) );
|
||||||
|
}
|
||||||
|
|
||||||
handleChange( name, value ) {
|
handleChange( name, value ) {
|
||||||
const { values } = this.state;
|
const { values } = this.state;
|
||||||
|
|
||||||
|
@ -60,9 +67,7 @@ class Form extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBlur( name ) {
|
handleBlur( name ) {
|
||||||
this.setState( prevState => ( {
|
this.setTouched( name );
|
||||||
touched: { ...prevState.touched, [ name ]: true },
|
|
||||||
} ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleSubmit() {
|
async handleSubmit() {
|
||||||
|
@ -97,6 +102,7 @@ class Form extends Component {
|
||||||
values,
|
values,
|
||||||
errors,
|
errors,
|
||||||
touched,
|
touched,
|
||||||
|
setTouched: this.setTouched,
|
||||||
setValue: this.setValue,
|
setValue: this.setValue,
|
||||||
handleSubmit: this.handleSubmit,
|
handleSubmit: this.handleSubmit,
|
||||||
getInputProps: this.getInputProps,
|
getInputProps: this.getInputProps,
|
||||||
|
@ -137,8 +143,9 @@ Form.propTypes = {
|
||||||
Form.defaultProps = {
|
Form.defaultProps = {
|
||||||
errors: {},
|
errors: {},
|
||||||
initialValues: {},
|
initialValues: {},
|
||||||
touched: {},
|
|
||||||
onSubmitCallback: noop,
|
onSubmitCallback: noop,
|
||||||
|
touched: {},
|
||||||
|
validate: noop,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Form;
|
export default Form;
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
padding: $gap;
|
padding: $gap;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: inset 0 0 0 1px $muriel-primary-500, inset 0 0 0 2px #fff;
|
box-shadow: inset 0 0 0 1px $new-muriel-primary-500, inset 0 0 0 2px #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
color: #1a1a1a;
|
color: $muriel-gray-900;
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-list__item-description {
|
.woocommerce-list__item-description {
|
||||||
|
|
|
@ -13,6 +13,10 @@
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
.woocommerce-stepper__step-text {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.woocommerce-stepper__step-label {
|
.woocommerce-stepper__step-label {
|
||||||
color: $muriel-gray-900;
|
color: $muriel-gray-900;
|
||||||
line-height: $step-icon-size;
|
line-height: $step-icon-size;
|
||||||
|
@ -112,10 +116,11 @@
|
||||||
.woocommerce-stepper__steps {
|
.woocommerce-stepper__steps {
|
||||||
align-items: initial;
|
align-items: initial;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-stepper__step {
|
.woocommerce-stepper__step {
|
||||||
min-height: 90px;
|
padding-bottom: $gap-larger;
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-stepper__step::after {
|
.woocommerce-stepper__step::after {
|
||||||
|
@ -128,7 +133,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-stepper__step:last-child {
|
.woocommerce-stepper__step:last-child {
|
||||||
min-height: auto;
|
padding-bottom: $gap-smaller;
|
||||||
&::after {
|
&::after {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ module.exports = {
|
||||||
outlines: '#007cba',
|
outlines: '#007cba',
|
||||||
},
|
},
|
||||||
themes: {
|
themes: {
|
||||||
'woocommerce-profile-wizard__body': {
|
'woocommerce-onboarding': {
|
||||||
primary: '#d52c82',
|
primary: '#d52c82',
|
||||||
secondary: '#d52c82',
|
secondary: '#d52c82',
|
||||||
toggle: '#d52c82',
|
toggle: '#d52c82',
|
||||||
|
|
|
@ -71,7 +71,8 @@ class OnboardingTasks {
|
||||||
set_transient( self::TASKS_TRANSIENT, $tasks, DAY_IN_SECONDS );
|
set_transient( self::TASKS_TRANSIENT, $tasks, DAY_IN_SECONDS );
|
||||||
}
|
}
|
||||||
|
|
||||||
$settings['onboarding']['tasks'] = $tasks;
|
$settings['onboarding']['tasks'] = $tasks;
|
||||||
|
$settings['onboarding']['shippingZonesCount'] = count( \WC_Shipping_Zones::get_zones() );
|
||||||
|
|
||||||
return $settings;
|
return $settings;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue