Merge pull request #32302 from woocommerce/feature/32164_new_task_list_version_2

Add sectioned task list component
This commit is contained in:
louwie17 2022-04-13 13:56:27 -03:00 committed by GitHub
commit 212198b3c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 899 additions and 50 deletions

View File

@ -34,6 +34,7 @@ export { withPluginsHydration } from './plugins/with-plugins-hydration';
export { ONBOARDING_STORE_NAME } from './onboarding';
export { withOnboardingHydration } from './onboarding/with-onboarding-hydration';
export { getVisibleTasks } from './onboarding/utils';
export type { TaskType, TaskListType } from './onboarding/types';
export { USER_STORE_NAME } from './user';

View File

@ -9,12 +9,22 @@ export type TaskType = {
isSnoozed: boolean;
isVisible: boolean;
isSnoozable: boolean;
isDisabled: boolean;
snoozedUntil: number;
time: string;
title: string;
isVisited: boolean;
};
export type TaskListSection = {
id: string;
title: string;
description: string;
image: string;
tasks: string[];
isComplete: boolean;
};
export type TaskListType = {
id: string;
isCollapsible?: boolean;
@ -25,4 +35,5 @@ export type TaskListType = {
title: string;
eventPrefix: string;
displayProgressHeader: boolean;
sections?: TaskListSection[];
};

View File

@ -0,0 +1,16 @@
/**
* Internal dependencies
*/
import { TaskType } from './types';
/**
* Filters tasks to only visible tasks, taking in account snoozed tasks.
*/
export function getVisibleTasks( tasks: TaskType[] ) {
const nowTimestamp = Date.now();
return tasks.filter(
( task ) =>
! task.isDismissed &&
( ! task.isSnoozed || task.snoozedUntil < nowTimestamp )
);
}

View File

@ -1,5 +1,7 @@
# Unreleased
- Update TaskList types.
# 3.0.1
- Add missing dependency.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: Add
Add support for sections in our TaskList class and create a new sectional task list component. #32302

View File

@ -1,3 +1,4 @@
@import 'node_modules/@wordpress/base-styles/colors.native';
// By using CSS variables, we can switch the spacing rhythm using a single media query.
:root {
--large-gap: 40px;
@ -37,6 +38,14 @@
max-width: 100%;
line-height: 1;
}
.components-panel__body > .components-panel__body-title,
.woocommerce-experimental-list__item,
.woocommerce-inbox-message {
&:hover {
background: $gray-0;
}
}
}
body.woocommerce-page {

View File

@ -4,7 +4,11 @@
import { __, sprintf } from '@wordpress/i18n';
import { useMemo } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { ONBOARDING_STORE_NAME, TaskListType } from '@woocommerce/data';
import {
getVisibleTasks,
ONBOARDING_STORE_NAME,
TaskListType,
} from '@woocommerce/data';
import { getSetting } from '@woocommerce/settings';
/**
@ -20,37 +24,38 @@ type ProgressHeaderProps = {
export const ProgressHeader: React.FC< ProgressHeaderProps > = ( {
taskListId,
} ) => {
const { loading, tasksCount, completedCount, hasVisitedTasks } = useSelect(
( select ) => {
const taskList: TaskListType = select(
ONBOARDING_STORE_NAME
).getTaskList( taskListId );
const finishedResolution = select(
ONBOARDING_STORE_NAME
).hasFinishedResolution( 'getTaskList', [ taskListId ] );
const nowTimestamp = Date.now();
const visibleTasks = taskList?.tasks.filter(
( task ) =>
! task.isDismissed &&
( ! task.isSnoozed || task.snoozedUntil < nowTimestamp )
);
const {
loading,
tasksCount,
completedCount,
hasVisitedTasks,
disabledCompletedCount,
} = useSelect( ( select ) => {
const taskList: TaskListType = select(
ONBOARDING_STORE_NAME
).getTaskList( taskListId );
const finishedResolution = select(
ONBOARDING_STORE_NAME
).hasFinishedResolution( 'getTaskList', [ taskListId ] );
const visibleTasks = getVisibleTasks( taskList?.tasks );
return {
loading: ! finishedResolution,
tasksCount: visibleTasks?.length,
completedCount: visibleTasks?.filter(
( task ) => task.isComplete
).length,
hasVisitedTasks:
visibleTasks?.filter( ( task ) => task.isVisited ).length >
0,
};
}
);
return {
loading: ! finishedResolution,
tasksCount: visibleTasks?.length,
completedCount: visibleTasks?.filter( ( task ) => task.isComplete )
.length,
disabledCompletedCount: visibleTasks?.filter(
( task ) => task.isComplete && task.isDisabled
).length,
hasVisitedTasks:
visibleTasks?.filter( ( task ) => task.isVisited ).length > 0,
};
} );
const progressTitle = useMemo( () => {
if (
( ! hasVisitedTasks && completedCount < 2 ) ||
( ! hasVisitedTasks &&
completedCount < 2 + disabledCompletedCount ) ||
completedCount === tasksCount
) {
const siteTitle = getSetting( 'siteTitle' );

View File

@ -6,7 +6,11 @@ import { useEffect, useRef, useState } from '@wordpress/element';
import { Card, CardHeader } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { Badge } from '@woocommerce/components';
import { ONBOARDING_STORE_NAME, TaskListType } from '@woocommerce/data';
import {
getVisibleTasks,
ONBOARDING_STORE_NAME,
TaskListType,
} from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import { Text, List, CollapsibleList } from '@woocommerce/experimental';
@ -45,12 +49,7 @@ export const TaskList: React.FC< TaskListProps > = ( {
};
} );
const prevQueryRef = useRef( query );
const nowTimestamp = Date.now();
const visibleTasks = tasks.filter(
( task ) =>
! task.isDismissed &&
( ! task.isSnoozed || task.snoozedUntil < nowTimestamp )
);
const visibleTasks = getVisibleTasks( tasks );
const incompleteTasks = tasks.filter(
( task ) => ! task.isComplete && ! task.isDismissed

View File

@ -19,6 +19,7 @@ import { TasksPlaceholder, TasksPlaceholderProps } from './placeholder';
import './tasks.scss';
import { TaskListProps, TaskList } from './task-list';
import { TaskList as TwoColumnTaskList } from '../two-column-tasks/task-list';
import { SectionedTaskList } from '../two-column-tasks/sectioned-task-list';
import TwoColumnTaskListPlaceholder from '../two-column-tasks/placeholder';
import '../two-column-tasks/style.scss';
import { getAdminSetting } from '~/utils/admin-settings';
@ -31,6 +32,8 @@ function getTaskListComponent( taskListId: string ): React.FC< TaskListProps > {
switch ( taskListId ) {
case 'setup_experiment_1':
return TwoColumnTaskList;
case 'setup_experiment_2':
return SectionedTaskList;
default:
return TaskList;
}

View File

@ -0,0 +1,26 @@
.woocommerce-task-section-header__container {
display: flex;
.woocommerce-task-header__illustration {
max-width: 150px;
width: 34%;
margin-left: auto;
margin-right: 7%;
display: flex;
align-items: center;
.illustration-background {
max-width: 100%;
}
}
.woocommerce-task-header__contents p {
font-size: 16px;
}
.woocommerce-task-header__contents h1 {
font-size: 20px;
line-height: 28px;
padding: 0;
}
}

View File

@ -0,0 +1,30 @@
/**
* Internal dependencies
*/
import './section-header.scss';
type Props = {
title: string;
description: string;
image: string;
};
const SectionHeader: React.FC< Props > = ( { title, description, image } ) => {
return (
<div className="woocommerce-task-header__contents-container woocommerce-task-section-header__container">
<div className="woocommerce-task-header__contents">
<h1>{ title }</h1>
<p>{ description }</p>
</div>
<div className="woocommerce-task-header__illustration">
<img
src={ image }
alt={ title }
className="illustration-background"
/>
</div>
</div>
);
};
export default SectionHeader;

View File

@ -0,0 +1,53 @@
/**
* External dependencies
*/
import { Badge } from '@woocommerce/components';
import { TaskListSection, TaskType } from '@woocommerce/data';
import { Icon, check } from '@wordpress/icons';
import { Text } from '@woocommerce/experimental';
/**
* Internal dependencies
*/
import SectionHeader from './headers/section-header';
type SectionPanelTitleProps = {
section: TaskListSection;
active: boolean;
tasks: TaskType[];
};
export const SectionPanelTitle: React.FC< SectionPanelTitleProps > = ( {
section,
active,
tasks,
} ) => {
if ( active ) {
return (
<div className="wooocommerce-task-card__header-container">
<div className="wooocommerce-task-card__header">
<SectionHeader { ...section } />
</div>
</div>
);
}
const uncompletedTasksCount = tasks.filter(
( task ) => ! task.isComplete && section.tasks.includes( task.id )
).length;
const isComplete = section.isComplete || uncompletedTasksCount === 0;
return (
<>
<Text variant="title.small" size="20" lineHeight="28px">
{ section.title }
</Text>
{ ! isComplete && <Badge count={ uncompletedTasksCount } /> }
{ isComplete && (
<div className="woocommerce-task__icon">
<Icon icon={ check } />
</div>
) }
</>
);
};

View File

@ -0,0 +1,92 @@
.woocommerce-sectioned-task-list {
.components-panel {
width: 100%;
background: transparent;
border: 0;
}
.components-panel__body {
padding-bottom: 0;
margin-bottom: $gap-smaller;
background: #fff;
border: 1px solid $gray-200;
&.is-opened {
padding-bottom: 0;
}
.components-panel__body-title {
margin-bottom: 0;
border-bottom: 1px solid #e0e0e0;
&:hover {
border-bottom: 1px solid #e0e0e0;
}
> .components-button {
font-size: 20px;
font-weight: 400;
padding-top: 20px;
padding-bottom: 20px;
}
.components-panel__arrow {
right: $gap-large;
}
}
.wooocommerce-task-card__header-container {
width: 100%;
border-bottom: none;
}
.components-panel__body-toggle {
box-shadow: none;
padding-left: $gap-large;
}
&.is-opened .components-panel__body-toggle {
width: 100%;
padding: 0;
.components-panel__arrow {
top: 32px;
}
}
.woocommerce-experimental-list {
width: calc(100% + 32px);
margin: 0 -16px;
}
}
ul li.woocommerce-task-list__item {
padding-top: $gap;
padding-bottom: $gap;
&.is-disabled {
pointer-events: none;
}
&:not(.is-complete) .woocommerce-task-list__item-before .woocommerce-task__icon {
border-color: $gray-300;
}
}
.woocommerce-task-list__item.is-complete .woocommerce-task__icon {
background-color: $alert-green;
}
.components-panel__body-title {
.woocommerce-badge {
width: 28px;
height: 28px;
}
.woocommerce-task__icon {
margin-left: $gap;
background-color: $alert-green;
border-radius: 50%;
width: 24px;
height: 24px;
svg {
fill: #fff;
position: relative;
}
}
}
}

View File

@ -0,0 +1,252 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useEffect, useRef, useState } from '@wordpress/element';
import { Panel, PanelBody, PanelRow } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { updateQueryString } from '@woocommerce/navigation';
import {
OPTIONS_STORE_NAME,
ONBOARDING_STORE_NAME,
TaskType,
getVisibleTasks,
} from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import { List, TaskItem } from '@woocommerce/experimental';
import classnames from 'classnames';
/**
* Internal dependencies
*/
import '../tasks/task-list.scss';
import './sectioned-task-list.scss';
import TaskListCompleted from './completed';
import { TaskListProps } from '~/tasks/task-list';
import { ProgressHeader } from '~/task-lists/progress-header';
import { SectionPanelTitle } from './section-panel-title';
type PanelBodyProps = Omit< PanelBody.Props, 'title' | 'onToggle' > & {
title: string | React.ReactNode | undefined;
onToggle?: ( isOpen: boolean ) => void;
};
const PanelBodyWithUpdatedType = PanelBody as React.ComponentType< PanelBodyProps >;
export const SectionedTaskList: React.FC< TaskListProps > = ( {
query,
id,
eventName,
tasks,
keepCompletedTaskList,
isComplete,
sections,
displayProgressHeader,
} ) => {
const { createNotice } = useDispatch( 'core/notices' );
const { updateOptions, dismissTask, undoDismissTask } = useDispatch(
OPTIONS_STORE_NAME
);
const { profileItems } = useSelect( ( select ) => {
const { getProfileItems } = select( ONBOARDING_STORE_NAME );
return {
profileItems: getProfileItems(),
};
} );
const { hideTaskList } = useDispatch( ONBOARDING_STORE_NAME );
const [ openPanel, setOpenPanel ] = useState< string | null >(
sections?.find( ( section ) => ! section.isComplete )?.id || null
);
const prevQueryRef = useRef( query );
const visibleTasks = getVisibleTasks( tasks );
const recordTaskListView = () => {
if ( query.task ) {
return;
}
recordEvent( `${ eventName }_view`, {
number_tasks: visibleTasks.length,
store_connected: profileItems.wccom_connected,
} );
};
useEffect( () => {
recordTaskListView();
}, [] );
useEffect( () => {
const { task: prevTask } = prevQueryRef.current;
const { task } = query;
if ( prevTask !== task ) {
window.document.documentElement.scrollTop = 0;
prevQueryRef.current = query;
}
}, [ query ] );
const onDismissTask = ( taskId: string ) => {
dismissTask( taskId );
createNotice( 'success', __( 'Task dismissed' ), {
actions: [
{
label: __( 'Undo', 'woocommerce-admin' ),
onClick: () => undoDismissTask( taskId ),
},
],
} );
};
const hideTasks = () => {
hideTaskList( id );
};
const keepTasks = () => {
const updateOptionsParams = {
woocommerce_task_list_keep_completed: 'yes',
};
updateOptions( {
...updateOptionsParams,
} );
};
let selectedHeaderCard = visibleTasks.find(
( listTask ) => listTask.isComplete === false
);
// If nothing is selected, default to the last task since everything is completed.
if ( ! selectedHeaderCard ) {
selectedHeaderCard = visibleTasks[ visibleTasks.length - 1 ];
}
const trackClick = ( task: TaskType ) => {
recordEvent( `${ eventName }_click`, {
task_name: task.id,
} );
};
const goToTask = ( task: TaskType ) => {
trackClick( task );
updateQueryString( { task: task.id } );
};
const onTaskSelected = ( task: TaskType ) => {
goToTask( task );
};
const getSectionTasks = ( sectionTaskIds: string[] ) => {
return visibleTasks.filter( ( task ) =>
sectionTaskIds.includes( task.id )
);
};
if ( ! visibleTasks.length ) {
return <div className="woocommerce-task-dashboard__container"></div>;
}
if ( isComplete && ! keepCompletedTaskList ) {
return (
<>
<TaskListCompleted
hideTasks={ hideTasks }
keepTasks={ keepTasks }
twoColumns={ false }
/>
</>
);
}
return (
<>
{ displayProgressHeader ? (
<ProgressHeader taskListId={ id } />
) : null }
<div
className={ classnames(
`woocommerce-task-dashboard__container woocommerce-sectioned-task-list two-column-experiment woocommerce-task-list__${ id }`
) }
>
<Panel>
{ ( sections || [] ).map( ( section ) => (
<PanelBodyWithUpdatedType
key={ section.id }
title={
<SectionPanelTitle
section={ section }
tasks={ tasks }
active={ openPanel === section.id }
/>
}
opened={ openPanel === section.id }
onToggle={ ( isOpen: boolean ) => {
if ( ! isOpen && openPanel === section.id ) {
setOpenPanel( null );
} else {
setOpenPanel( section.id );
}
} }
initialOpen={ false }
>
<PanelRow>
<List animation="custom">
{ getSectionTasks( section.tasks ).map(
( task ) => {
const className = classnames(
'woocommerce-task-list__item',
{
'is-complete':
task.isComplete,
'is-disabled':
task.isDisabled,
}
);
return (
<TaskItem
key={ task.id }
className={ className }
title={ task.title }
completed={
task.isComplete
}
expanded={
! task.isComplete
}
content={ task.content }
onClick={ () => {
if (
! task.isDisabled
) {
onTaskSelected(
task
);
}
} }
onDismiss={
task.isDismissable
? () =>
onDismissTask(
task.id
)
: undefined
}
action={ () => {} }
actionLabel={
task.actionLabel
}
/>
);
}
) }
</List>
</PanelRow>
</PanelBodyWithUpdatedType>
) ) }
</Panel>
</div>
</>
);
};
export default SectionedTaskList;

View File

@ -103,7 +103,7 @@
margin: 0 auto;
justify-content: space-between;
ul li.complete .woocommerce-task-list__item-title {
ul li.is-complete .woocommerce-task-list__item-title {
font-weight: 600;
color: $gray-600;
}
@ -178,10 +178,13 @@
height: 100%;
}
}
.woocommerce-task-list__item:not(.complete) .woocommerce-task__icon {
.woocommerce-task-list__item:not(.is-complete) .woocommerce-task__icon {
border: 1px solid var(--wp-admin-theme-color);
background: transparent;
}
.woocommerce-task-list__item.is-complete:not(.complete) .woocommerce-task__icon {
border: none;
}
.woocommerce-task-list__item-before {
display: block;
@ -203,7 +206,7 @@
}
@for $i from 1 through 10 {
.woocommerce-task-list__item:not(.complete).index-#{$i} .woocommerce-task__icon::after {
.woocommerce-task-list__item:not(.is-complete).index-#{$i} .woocommerce-task__icon::after {
content: '#{$i}';
@extend .numbered-circle;
color: var(--wp-admin-theme-color);

View File

@ -16,6 +16,7 @@ import {
ONBOARDING_STORE_NAME,
TaskType,
useUserPreferences,
getVisibleTasks,
} from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import { List, TaskItem } from '@woocommerce/experimental';
@ -63,12 +64,7 @@ export const TaskList: React.FC< TaskListProps > = ( {
const prevQueryRef = useRef( query );
const nowTimestamp = Date.now();
const visibleTasks = tasks.filter(
( task ) =>
! task.isDismissed &&
( ! task.isSnoozed || task.snoozedUntil < nowTimestamp )
);
const visibleTasks = getVisibleTasks( tasks );
const recordTaskListView = () => {
if ( query.task ) {
@ -295,7 +291,7 @@ export const TaskList: React.FC< TaskListProps > = ( {
const className = classnames(
'woocommerce-task-list__item index-' + index,
{
complete: task.isComplete,
'is-complete': task.isComplete,
'is-active': task.id === activeTaskId,
}
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -0,0 +1,4 @@
Significance: minor
Type: enhancement
Add new sectioned task list component. #32302

View File

@ -21,6 +21,7 @@
"transient-notices": true,
"wc-pay-promotion": true,
"wc-pay-welcome-page": true,
"tasklist-setup-experiment-1": false
"tasklist-setup-experiment-1": false,
"tasklist-setup-experiment-2": false
}
}

View File

@ -21,6 +21,7 @@
"transient-notices": true,
"wc-pay-promotion": true,
"wc-pay-welcome-page": true,
"tasklist-setup-experiment-1": false
"tasklist-setup-experiment-1": false,
"tasklist-setup-experiment-2": false
}
}

View File

@ -21,6 +21,7 @@
"transient-notices": true,
"wc-pay-promotion": true,
"wc-pay-welcome-page": true,
"tasklist-setup-experiment-1": false
"tasklist-setup-experiment-1": false,
"tasklist-setup-experiment-2": false
}
}

View File

@ -403,6 +403,15 @@ abstract class Task {
return true;
}
/**
* Check if task is disabled.
*
* @return bool
*/
public function is_disabled() {
return false;
}
/**
* Check if the task is complete.
*
@ -451,6 +460,7 @@ abstract class Task {
'isSnoozed' => $this->is_snoozed(),
'isSnoozeable' => $this->is_snoozeable(),
'isVisited' => $this->is_visited(),
'isDisabled' => $this->is_disabled(),
'snoozedUntil' => $this->get_snoozed_until(),
'additionalData' => self::convert_object_to_camelcase( $this->get_additional_data() ),
'eventPrefix' => $this->prefix_event( '' ),

View File

@ -96,6 +96,20 @@ class TaskList {
*/
public $options = array();
/**
* Array of TaskListSection.
*
* @var array
*/
private $sections = array();
/**
* Key value map of task class and id used for sections.
*
* @var array
*/
public $task_class_id_map = array();
/**
* Constructor
*
@ -112,6 +126,7 @@ class TaskList {
'options' => array(),
'visible' => true,
'display_progress_header' => false,
'sections' => array(),
);
$data = wp_parse_args( $data, $defaults );
@ -132,6 +147,12 @@ class TaskList {
}
$this->possibly_remove_reminder_bar();
$this->sections = array_map(
function( $section ) {
return new TaskListSection( $section, $this );
},
$data['sections']
);
}
/**
@ -243,7 +264,9 @@ class TaskList {
return;
}
$this->tasks[] = $task;
$task_class_name = substr( get_class( $task ), strrpos( get_class( $task ), '\\' ) + 1 );
$this->task_class_id_map[ $task_class_name ] = $task->get_id();
$this->tasks[] = $task;
}
/**
@ -279,6 +302,15 @@ class TaskList {
);
}
/**
* Get task list sections.
*
* @return array
*/
public function get_sections() {
return $this->sections;
}
/**
* Track list completion of viewable tasks.
*/
@ -377,6 +409,12 @@ class TaskList {
'eventPrefix' => $this->prefix_event( '' ),
'displayProgressHeader' => $this->display_progress_header,
'keepCompletedTaskList' => $this->get_keep_completed_task_list(),
'sections' => array_map(
function( $section ) {
return $section->get_json();
},
$this->sections
),
);
}
}

View File

@ -0,0 +1,122 @@
<?php
/**
* Handles storage and retrieval of a task list section
*/
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
/**
* Task List section class.
*/
class TaskListSection {
/**
* Title.
*
* @var string
*/
public $id = '';
/**
* Title.
*
* @var string
*/
public $title = '';
/**
* Description.
*
* @var string
*/
public $description = '';
/**
* Image.
*
* @var string
*/
public $image = '';
/**
* Tasks.
*
* @var array
*/
public $task_names = array();
/**
* Parent task list.
*
* @var TaskList
*/
protected $task_list;
/**
* Constructor
*
* @param array $data Task list data.
* @param TaskList|null $task_list Parent task list.
*/
public function __construct( $data = array(), $task_list = null ) {
$defaults = array(
'id' => '',
'title' => '',
'description' => '',
'image' => '',
'tasks' => array(),
);
$data = wp_parse_args( $data, $defaults );
$this->task_list = $task_list;
$this->id = $data['id'];
$this->title = $data['title'];
$this->description = $data['description'];
$this->image = $data['image'];
$this->task_names = $data['task_names'];
}
/**
* Returns if section is complete.
*
* @return boolean;
*/
private function is_complete() {
$complete = true;
foreach ( $this->task_names as $task_name ) {
if ( null !== $this->task_list && isset( $this->task_list->task_class_id_map[ $task_name ] ) ) {
$task = $this->task_list->get_task( $this->task_list->task_class_id_map[ $task_name ] );
if ( $task->can_view() && ! $task->is_complete() ) {
$complete = false;
break;
}
}
}
return $complete;
}
/**
* Get the list for use in JSON.
*
* @return array
*/
public function get_json() {
return array(
'id' => $this->id,
'title' => $this->title,
'description' => $this->description,
'image' => $this->image,
'tasks' => array_map(
function( $task_name ) {
if ( null !== $this->task_list && isset( $this->task_list->task_class_id_map[ $task_name ] ) ) {
return $this->task_list->task_class_id_map[ $task_name ];
}
return '';
},
$this->task_names
),
'isComplete' => $this->is_complete(),
);
}
}

View File

@ -93,7 +93,7 @@ class TaskLists {
'Appearance',
),
'event_prefix' => 'tasklist_',
'visible' => ! Features::is_enabled( 'tasklist-setup-experiment-1' ),
'visible' => ! Features::is_enabled( 'tasklist-setup-experiment-1' ) && ! Features::is_enabled( 'tasklist-setup-experiment-2' ),
)
);
@ -121,6 +121,63 @@ class TaskLists {
)
);
self::add_list(
array(
'id' => 'setup_experiment_2',
'hidden_id' => 'setup',
'title' => __( 'Get ready to start selling', 'woocommerce' ),
'tasks' => array(
'StoreCreation',
'StoreDetails',
'Products',
'WooCommercePayments',
'Payments',
'Tax',
'Shipping',
'Marketing',
'Appearance',
),
'event_prefix' => 'tasklist_',
'visible' => Features::is_enabled( 'tasklist-setup-experiment-2' ),
'options' => array(
'use_completed_title' => true,
),
'display_progress_header' => true,
'sections' => array(
array(
'id' => 'basics',
'title' => __( 'Cover the basics', 'woocommerce' ),
'description' => __( 'Make sure youve got everything you need to start selling—from business details to products.', 'woocommerce' ),
'image' => plugins_url(
'/assets/images/task_list/basics-section-illustration.png',
WC_ADMIN_PLUGIN_FILE
),
'task_names' => array( 'StoreCreation', 'StoreDetails', 'Products', 'Payments', 'WooCommercePayments' ),
),
array(
'id' => 'sales',
'title' => __( 'Get ready to sell', 'woocommerce' ),
'description' => __( 'Easily set up the backbone of your stores operations and get ready to accept first orders.', 'woocommerce' ),
'image' => plugins_url(
'/assets/images/task_list/sales-section-illustration.png',
WC_ADMIN_PLUGIN_FILE
),
'task_names' => array( 'Shipping', 'Tax' ),
),
array(
'id' => 'expand',
'title' => __( 'Customize & expand', 'woocommerce' ),
'description' => __( 'Personalize your stores design and grow your business by enabling new sales channels.', 'woocommerce' ),
'image' => plugins_url(
'/assets/images/task_list/expand-section-illustration.png',
WC_ADMIN_PLUGIN_FILE
),
'task_names' => array( 'Appearance', 'Marketing' ),
),
),
)
);
self::add_list(
array(
'id' => 'extended',

View File

@ -39,6 +39,9 @@ class Appearance extends Task {
* @return string
*/
public function get_title() {
if ( count( $this->task_list->get_sections() ) > 0 && ! $this->is_complete() ) {
return __( 'Make your store stand out with unique design', 'woocommerce' );
}
if ( true === $this->get_parent_option( 'use_completed_title' ) ) {
if ( $this->is_complete() ) {
return __( 'You personalized your store', 'woocommerce' );
@ -54,6 +57,9 @@ class Appearance extends Task {
* @return string
*/
public function get_content() {
if ( count( $this->task_list->get_sections() ) > 0 ) {
return __( 'Upload your logo to adapt the store to your brands personality.', 'woocommerce' );
}
return __(
'Add your logo, create a homepage, and start designing your store.',
'woocommerce'

View File

@ -25,6 +25,9 @@ class Marketing extends Task {
* @return string
*/
public function get_title() {
if ( count( $this->task_list->get_sections() ) > 0 && ! $this->is_complete() ) {
return __( 'Grow your business with marketing tools', 'woocommerce' );
}
if ( true === $this->get_parent_option( 'use_completed_title' ) ) {
if ( $this->is_complete() ) {
return __( 'You added sales channels', 'woocommerce' );
@ -40,6 +43,9 @@ class Marketing extends Task {
* @return string
*/
public function get_content() {
if ( count( $this->task_list->get_sections() ) > 0 ) {
return __( 'Promote your store in other sales channels, like email, Google, and Facebook.', 'woocommerce' );
}
return __(
'Add recommended marketing tools to reach new customers and grow your business',
'woocommerce'

View File

@ -25,6 +25,9 @@ class Payments extends Task {
* @return string
*/
public function get_title() {
if ( count( $this->task_list->get_sections() ) > 0 && ! $this->is_complete() ) {
return __( 'Add a way to get paid', 'woocommerce' );
}
if ( true === $this->get_parent_option( 'use_completed_title' ) ) {
if ( $this->is_complete() ) {
return __( 'You set up payments', 'woocommerce' );
@ -40,6 +43,9 @@ class Payments extends Task {
* @return string
*/
public function get_content() {
if ( count( $this->task_list->get_sections() ) > 0 ) {
return __( 'Let your customers pay the way they like.', 'woocommerce' );
}
return __(
'Choose payment providers and enable payment methods at checkout.',
'woocommerce'

View File

@ -36,6 +36,9 @@ class Products extends Task {
* @return string
*/
public function get_title() {
if ( count( $this->task_list->get_sections() ) > 0 && ! $this->is_complete() ) {
return __( 'Create or upload your first products', 'woocommerce' );
}
if ( true === $this->get_parent_option( 'use_completed_title' ) ) {
if ( $this->is_complete() ) {
return __( 'You added products', 'woocommerce' );
@ -51,6 +54,9 @@ class Products extends Task {
* @return string
*/
public function get_content() {
if ( count( $this->task_list->get_sections() ) > 0 ) {
return __( 'Add products to sell and build your catalog.', 'woocommerce' );
}
return __(
'Start by adding the first product to your store. You can add your products manually, via CSV, or import them from another service.',
'woocommerce'

View File

@ -24,6 +24,9 @@ class Shipping extends Task {
* @return string
*/
public function get_title() {
if ( count( $this->task_list->get_sections() ) > 0 && ! $this->is_complete() ) {
return __( 'Select how to ship your products', 'woocommerce' );
}
if ( true === $this->get_parent_option( 'use_completed_title' ) ) {
if ( $this->is_complete() ) {
return __( 'You added shipping costs', 'woocommerce' );
@ -39,6 +42,9 @@ class Shipping extends Task {
* @return string
*/
public function get_content() {
if ( count( $this->task_list->get_sections() ) > 0 ) {
return __( 'Set delivery costs and enable extra features, like shipping label printing.', 'woocommerce' );
}
return __(
"Set your store location and where you'll ship to.",
'woocommerce'

View File

@ -0,0 +1,77 @@
<?php
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
use Automattic\WooCommerce\Admin\Features\Onboarding;
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
/**
* Store Details Task
*/
class StoreCreation extends Task {
/**
* ID.
*
* @return string
*/
public function get_id() {
return 'store_creation';
}
/**
* Title.
*
* @return string
*/
public function get_title() {
/* translators: Store name */
return sprintf( __( 'You created %s', 'woocommerce' ), get_bloginfo( 'name' ) );
}
/**
* Content.
*
* @return string
*/
public function get_content() {
return '';
}
/**
* Time.
*
* @return string
*/
public function get_time() {
return '';
}
/**
* Time.
*
* @return string
*/
public function get_action_url() {
return '';
}
/**
* Task completion.
*
* @return bool
*/
public function is_complete() {
return true;
}
/**
* Check if task is disabled.
*
* @return bool
*/
public function is_disabled() {
return true;
}
}

View File

@ -65,6 +65,9 @@ class Tax extends Task {
* @return string
*/
public function get_title() {
if ( count( $this->task_list->get_sections() ) > 0 && ! $this->is_complete() ) {
return __( 'Get taxes out of your mind', 'woocommerce' );
}
if ( true === $this->get_parent_option( 'use_completed_title' ) ) {
if ( $this->is_complete() ) {
return __( 'You added tax rates', 'woocommerce' );
@ -80,6 +83,9 @@ class Tax extends Task {
* @return string
*/
public function get_content() {
if ( count( $this->task_list->get_sections() ) > 0 ) {
return __( 'Have sales tax calculated automatically, or add the rates manually.', 'woocommerce' );
}
return self::can_use_automated_taxes()
? __(
'Good news! WooCommerce Services and Jetpack can automate your sales tax calculations for you.',