diff --git a/plugins/woocommerce-admin/client/dashboard/profile-wizard/index.js b/plugins/woocommerce-admin/client/dashboard/profile-wizard/index.js index 80ad8f93c03..b102d040841 100644 --- a/plugins/woocommerce-admin/client/dashboard/profile-wizard/index.js +++ b/plugins/woocommerce-admin/client/dashboard/profile-wizard/index.js @@ -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; diff --git a/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/theme/index.js b/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/theme/index.js new file mode 100644 index 00000000000..ab6dc614eef --- /dev/null +++ b/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/theme/index.js @@ -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 ( + + { image && ( + + ) } + + { title } + + { this.getPriceValue( price ) <= 0 + ? __( 'Free', 'woocommerce-admin' ) + : sprintf( __( '%s per year', 'woocommerce-admin' ), decodeEntities( price ) ) } + + + this.onChoose( slug ) }> + { __( 'Choose', 'woocommerce-admin' ) } + + this.openDemo( slug ) }> + { __( 'Live Demo', 'woocommerce-admin' ) } + + + + + ); + } + + 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 ( + + + { __( 'Choose a theme', 'woocommerce-admin' ) } + + + { __( 'Your theme determines how your store appears to customers', 'woocommerce-admin' ) } + + + { () => ( + + { themes.map( theme => this.renderTheme( theme ) ) } + + ) } + + + ); + } +} + +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 ); diff --git a/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/theme/style.scss b/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/theme/style.scss new file mode 100644 index 00000000000..f883baa715d --- /dev/null +++ b/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/theme/style.scss @@ -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; + } + } + } +} diff --git a/plugins/woocommerce-admin/client/dashboard/profile-wizard/style.scss b/plugins/woocommerce-admin/client/dashboard/profile-wizard/style.scss index 411b0869c65..847a46f0632 100644 --- a/plugins/woocommerce-admin/client/dashboard/profile-wizard/style.scss +++ b/plugins/woocommerce-admin/client/dashboard/profile-wizard/style.scss @@ -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; diff --git a/plugins/woocommerce-admin/includes/features/onboarding/class-wc-admin-onboarding.php b/plugins/woocommerce-admin/includes/features/onboarding/class-wc-admin-onboarding.php index e0ce3c7face..1f2aea55f01 100644 --- a/plugins/woocommerce-admin/includes/features/onboarding/class-wc-admin-onboarding.php +++ b/plugins/woocommerce-admin/includes/features/onboarding/class-wc-admin-onboarding.php @@ -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;
+ { this.getPriceValue( price ) <= 0 + ? __( 'Free', 'woocommerce-admin' ) + : sprintf( __( '%s per year', 'woocommerce-admin' ), decodeEntities( price ) ) } +