Onboarding: Add theme step and theme browser (https://github.com/woocommerce/woocommerce-admin/pull/2493)
* Add theme data to wcSettings onboarding data * Add theme step to onboarding profiler * Use free in place of price if theme is <= 0 * Add theme paid/free filter tabs
This commit is contained in:
parent
22fb9d357b
commit
0de2f70392
|
@ -23,6 +23,7 @@ import ProductTypes from './steps/product-types';
|
|||
import ProfileWizardHeader from './header';
|
||||
import Start from './steps/start';
|
||||
import StoreDetails from './steps/store-details';
|
||||
import Theme from './steps/theme';
|
||||
import withSelect from 'wc-api/with-select';
|
||||
import './style.scss';
|
||||
|
||||
|
@ -58,7 +59,7 @@ const getSteps = () => {
|
|||
} );
|
||||
steps.push( {
|
||||
key: 'theme',
|
||||
container: Fragment,
|
||||
container: Theme,
|
||||
label: __( 'Theme', 'woocommerce-admin' ),
|
||||
} );
|
||||
return steps;
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { Button } from 'newspack-components';
|
||||
import { Component, Fragment } from '@wordpress/element';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
import { TabPanel } from '@wordpress/components';
|
||||
import { withDispatch } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
*/
|
||||
import { Card, H } from '@woocommerce/components';
|
||||
|
||||
/**
|
||||
* Internal depdencies
|
||||
*/
|
||||
import withSelect from 'wc-api/with-select';
|
||||
import './style.scss';
|
||||
|
||||
class Theme extends Component {
|
||||
constructor() {
|
||||
super( ...arguments );
|
||||
|
||||
this.state = {
|
||||
activeTab: 'all',
|
||||
};
|
||||
|
||||
this.onChoose = this.onChoose.bind( this );
|
||||
this.onSelectTab = this.onSelectTab.bind( this );
|
||||
this.openDemo = this.openDemo.bind( this );
|
||||
}
|
||||
|
||||
async onChoose( theme ) {
|
||||
const { addNotice, goToNextStep, isError, updateProfileItems } = this.props;
|
||||
|
||||
await updateProfileItems( { theme } );
|
||||
|
||||
if ( ! isError ) {
|
||||
// @todo This should send profile information to woocommerce.com.
|
||||
goToNextStep();
|
||||
} else {
|
||||
addNotice( {
|
||||
status: 'error',
|
||||
message: __( 'There was a problem selecting your store theme.', 'woocommerce-admin' ),
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
openDemo() {
|
||||
// @todo This should open a theme demo preview.
|
||||
}
|
||||
|
||||
renderTheme( theme ) {
|
||||
const { image, price, slug, title } = theme;
|
||||
|
||||
return (
|
||||
<Card className="woocommerce-profile-wizard__theme" key={ theme.slug }>
|
||||
{ image && (
|
||||
<img alt={ title } src={ image } className="woocommerce-profile-wizard__theme-image" />
|
||||
) }
|
||||
<div className="woocommerce-profile-wizard__theme-details">
|
||||
<H className="woocommerce-profile-wizard__theme-name">{ title }</H>
|
||||
<p className="woocommerce-profile-wizard__theme-price">
|
||||
{ this.getPriceValue( price ) <= 0
|
||||
? __( 'Free', 'woocommerce-admin' )
|
||||
: sprintf( __( '%s per year', 'woocommerce-admin' ), decodeEntities( price ) ) }
|
||||
</p>
|
||||
<div className="woocommerce-profile-wizard__theme-actions">
|
||||
<Button isPrimary onClick={ () => this.onChoose( slug ) }>
|
||||
{ __( 'Choose', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
<Button isDefault onClick={ () => this.openDemo( slug ) }>
|
||||
{ __( 'Live Demo', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
onSelectTab( tab ) {
|
||||
this.setState( { activeTab: tab } );
|
||||
}
|
||||
|
||||
getPriceValue( string ) {
|
||||
return Number( decodeEntities( string ).replace( /[^0-9.-]+/g, '' ) );
|
||||
}
|
||||
|
||||
getThemes() {
|
||||
const { themes } = wcSettings.onboarding;
|
||||
const { activeTab } = this.state;
|
||||
|
||||
switch ( activeTab ) {
|
||||
case 'paid':
|
||||
return themes.filter( theme => this.getPriceValue( theme.price ) > 0 );
|
||||
case 'free':
|
||||
return themes.filter( theme => this.getPriceValue( theme.price ) <= 0 );
|
||||
case 'all':
|
||||
default:
|
||||
return themes;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const themes = this.getThemes();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<H className="woocommerce-profile-wizard__header-title">
|
||||
{ __( 'Choose a theme', 'woocommerce-admin' ) }
|
||||
</H>
|
||||
<H className="woocommerce-profile-wizard__header-subtitle">
|
||||
{ __( 'Your theme determines how your store appears to customers', 'woocommerce-admin' ) }
|
||||
</H>
|
||||
<TabPanel
|
||||
className="woocommerce-profile-wizard__themes-tab-panel"
|
||||
activeClass="is-active"
|
||||
onSelect={ this.onSelectTab }
|
||||
tabs={ [
|
||||
{
|
||||
name: 'all',
|
||||
title: __( 'All themes', 'woocommerce-admin' ),
|
||||
},
|
||||
{
|
||||
name: 'paid',
|
||||
title: __( 'Paid themes', 'woocommerce-admin' ),
|
||||
},
|
||||
{
|
||||
name: 'free',
|
||||
title: __( 'Free themes', 'woocommerce-admin' ),
|
||||
},
|
||||
] }
|
||||
>
|
||||
{ () => (
|
||||
<div className="woocommerce-profile-wizard__themes">
|
||||
{ themes.map( theme => this.renderTheme( theme ) ) }
|
||||
</div>
|
||||
) }
|
||||
</TabPanel>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSelect( select => {
|
||||
const { getProfileItemsError } = select( 'wc-api' );
|
||||
|
||||
const isError = Boolean( getProfileItemsError() );
|
||||
|
||||
return { isError };
|
||||
} ),
|
||||
withDispatch( dispatch => {
|
||||
const { addNotice, updateProfileItems } = dispatch( 'wc-api' );
|
||||
|
||||
return {
|
||||
addNotice,
|
||||
updateProfileItems,
|
||||
};
|
||||
} )
|
||||
)( Theme );
|
|
@ -0,0 +1,106 @@
|
|||
/** @format */
|
||||
.woocommerce-profile-wizard__body .woocommerce-profile-wizard__container {
|
||||
> .woocommerce-profile-wizard__themes-tab-panel {
|
||||
margin-bottom: $gap-large;
|
||||
|
||||
@include breakpoint( '>782px' ) {
|
||||
max-width: 810px;
|
||||
|
||||
.woocommerce-profile-wizard__themes {
|
||||
display: grid;
|
||||
grid-gap: $gap-large;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__themes-tab-panel .components-tab-panel__tabs {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: $white;
|
||||
box-shadow: $muriel-box-shadow-1dp;
|
||||
border-radius: 3px;
|
||||
margin-top: $gap-large;
|
||||
margin-bottom: $gap-large;
|
||||
|
||||
button {
|
||||
border: 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
color: $muriel-gray-500;
|
||||
display: flex;
|
||||
background: transparent;
|
||||
height: 48px;
|
||||
width: 100%;
|
||||
@include font-size(14);
|
||||
font-weight: 500;
|
||||
outline: none;
|
||||
padding: 0 $gap-large;
|
||||
|
||||
&.is-active {
|
||||
border-bottom: 1px solid $muriel-hot-pink-500;
|
||||
color: $muriel-hot-pink-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__body .woocommerce-profile-wizard__theme.woocommerce-card {
|
||||
overflow: hidden;
|
||||
|
||||
@include breakpoint( '>782px' ) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.woocommerce-card__body {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__theme-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__theme-name {
|
||||
margin-top: 0;
|
||||
margin-bottom: $gap-smaller;
|
||||
@include font-size(24);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__theme-details {
|
||||
padding: $gap;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__theme-price {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__theme-learn-more {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__theme-actions {
|
||||
margin-top: auto;
|
||||
|
||||
button.components-button {
|
||||
display: block;
|
||||
float: left;
|
||||
min-width: 106px;
|
||||
height: 40px;
|
||||
margin-right: $gap-smaller;
|
||||
box-shadow: none;
|
||||
|
||||
&.is-default {
|
||||
border-color: $muriel-hot-pink-500;
|
||||
color: $muriel-hot-pink-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -98,7 +98,12 @@
|
|||
margin-top: $gap-larger;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 476px;
|
||||
|
||||
> * {
|
||||
max-width: 476px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
@include breakpoint( '<782px' ) {
|
||||
padding-left: $gap;
|
||||
|
|
|
@ -74,7 +74,6 @@ class WC_Admin_Onboarding {
|
|||
/**
|
||||
* Get a list of allowed product types for the onboarding wizard.
|
||||
*
|
||||
* @todo Prices for products should be pulled dynamically.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_allowed_product_types() {
|
||||
|
@ -110,6 +109,26 @@ class WC_Admin_Onboarding {
|
|||
return apply_filters( 'woocommerce_admin_onboarding_product_types', $product_types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of themes for the onboarding wizard.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_themes() {
|
||||
$theme_data_transient_name = 'wc_onboarding_themes';
|
||||
$theme_data = get_transient( $theme_data_transient_name );
|
||||
if ( false === $theme_data ) {
|
||||
// @todo This should be replaced with the real wccom URL once
|
||||
// https://github.com/Automattic/woocommerce.com/pull/6035 is merged and deployed.
|
||||
$theme_data = wp_remote_get( 'http://woocommerce.test/wp-json/wccom-extensions/1.0/search?category=themes' );
|
||||
set_transient( $theme_data_transient_name, $theme_data, DAY_IN_SECONDS );
|
||||
}
|
||||
|
||||
$theme_data = json_decode( $theme_data['body'] );
|
||||
|
||||
return apply_filters( 'woocommerce_admin_onboarding_themes', $theme_data->products );
|
||||
}
|
||||
|
||||
/**
|
||||
* Append dynamic product data from API.
|
||||
*
|
||||
|
@ -160,6 +179,7 @@ class WC_Admin_Onboarding {
|
|||
// Only fetch if the onboarding wizard is incomplete.
|
||||
if ( $this->should_show_profiler() ) {
|
||||
$settings['onboarding']['productTypes'] = self::get_allowed_product_types();
|
||||
$settings['onboarding']['themes'] = self::get_themes();
|
||||
}
|
||||
|
||||
return $settings;
|
||||
|
|
Loading…
Reference in New Issue