Add progress header bar to task list experiment
This commit is contained in:
parent
9056be919d
commit
c281ddb820
|
@ -33,6 +33,7 @@ const TYPES = {
|
|||
ACTION_TASK_ERROR: 'ACTION_TASK_ERROR',
|
||||
ACTION_TASK_REQUEST: 'ACTION_TASK_REQUEST',
|
||||
ACTION_TASK_SUCCESS: 'ACTION_TASK_SUCCESS',
|
||||
VISITED_TASK: 'VISITED_TASK',
|
||||
};
|
||||
|
||||
export default TYPES;
|
||||
|
|
|
@ -201,6 +201,13 @@ export function optimisticallyCompleteTaskRequest( taskId ) {
|
|||
};
|
||||
}
|
||||
|
||||
export function visitedTask( taskId ) {
|
||||
return {
|
||||
type: TYPES.VISITED_TASK,
|
||||
taskId,
|
||||
};
|
||||
}
|
||||
|
||||
export function setPaymentMethods( paymentMethods ) {
|
||||
return {
|
||||
type: TYPES.GET_PAYMENT_METHODS_SUCCESS,
|
||||
|
|
|
@ -380,6 +380,14 @@ const onboarding = (
|
|||
isComplete: true,
|
||||
} ),
|
||||
};
|
||||
case TYPES.VISITED_TASK:
|
||||
return {
|
||||
...state,
|
||||
taskLists: getUpdatedTaskLists( state.taskLists, {
|
||||
id: taskId,
|
||||
isVisited: true,
|
||||
} ),
|
||||
};
|
||||
case TYPES.ACTION_TASK_ERROR:
|
||||
return {
|
||||
...state,
|
||||
|
|
|
@ -6,8 +6,9 @@ export type TaskType = {
|
|||
isComplete: boolean;
|
||||
isDismissable: boolean;
|
||||
isDismissed: boolean;
|
||||
isVisible: boolean;
|
||||
isSnoozed: boolean;
|
||||
isVisible: boolean;
|
||||
isVisited: boolean;
|
||||
isSnoozable: boolean;
|
||||
snoozedUntil: number;
|
||||
time: string;
|
||||
|
@ -24,4 +25,5 @@ export type TaskListType = {
|
|||
tasks: TaskType[];
|
||||
title: string;
|
||||
eventPrefix: string;
|
||||
displayProgressHeader: boolean;
|
||||
};
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from './progress-header';
|
|
@ -0,0 +1,53 @@
|
|||
$progress-complete-color: #007cba;
|
||||
|
||||
.woocommerce-task-progress-header {
|
||||
position: relative;
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
p {
|
||||
color: $gray-700;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin-top: $gap-smaller;
|
||||
}
|
||||
|
||||
.woocommerce-task-progress-header__progress-bar {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 16px;
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
|
||||
// Firefox
|
||||
& {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
&::-moz-progress-bar {
|
||||
background-color: $progress-complete-color;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
// Chrome
|
||||
&::-webkit-progress-bar {
|
||||
background-color: #fff;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
&::-webkit-progress-value {
|
||||
background-color: $progress-complete-color;
|
||||
border-bottom-left-radius: 16px;
|
||||
border-top-left-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-card__menu {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { useMemo } from '@wordpress/element';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { ONBOARDING_STORE_NAME, TaskListType } from '@woocommerce/data';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './progress-header.scss';
|
||||
import { TaskListMenu } from '~/tasks/task-list-menu';
|
||||
|
||||
type ProgressHeaderProps = {
|
||||
taskListId: string;
|
||||
};
|
||||
|
||||
export const ProgressHeader: React.FC< ProgressHeaderProps > = ( {
|
||||
taskListId,
|
||||
} ) => {
|
||||
const { loading, tasksCount, completedCount, hasVisitedTasks } = useSelect(
|
||||
( select ) => {
|
||||
const isResolving = select( ONBOARDING_STORE_NAME ).isResolving(
|
||||
'getTaskList'
|
||||
);
|
||||
const taskList: TaskListType = select(
|
||||
ONBOARDING_STORE_NAME
|
||||
).getTaskList( taskListId );
|
||||
const nowTimestamp = Date.now();
|
||||
const visibleTasks = taskList?.tasks.filter(
|
||||
( task ) =>
|
||||
! task.isDismissed &&
|
||||
( ! task.isSnoozed || task.snoozedUntil < nowTimestamp )
|
||||
);
|
||||
|
||||
return {
|
||||
loading: isResolving,
|
||||
tasksCount: visibleTasks?.length,
|
||||
completedCount: visibleTasks?.filter(
|
||||
( task ) => task.isComplete
|
||||
).length,
|
||||
hasVisitedTasks:
|
||||
visibleTasks?.filter( ( task ) => task.isVisited ).length >
|
||||
0,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const progressTitle = useMemo( () => {
|
||||
if ( ! hasVisitedTasks || completedCount === tasksCount ) {
|
||||
const siteTitle = getSetting( 'siteTitle' );
|
||||
return siteTitle
|
||||
? sprintf(
|
||||
/* translators: %s = site title */
|
||||
__( 'Welcome to %s', 'woocommerce-admin' ),
|
||||
siteTitle
|
||||
)
|
||||
: __( 'Welcome', 'woocommerce-admin' );
|
||||
} else if ( completedCount > 0 && completedCount < 3 ) {
|
||||
return __( "Let's get you set up", 'woocommerce-admin' ) + ' 🚀';
|
||||
} else if ( completedCount > 2 && completedCount < 5 ) {
|
||||
return __( 'You are on the right track', 'woocommerce-admin' );
|
||||
}
|
||||
return __( 'Just a few tasks left', 'woocommerce-admin' );
|
||||
}, [ completedCount, hasVisitedTasks ] );
|
||||
|
||||
if ( loading || completedCount === tasksCount ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="woocommerce-task-progress-header">
|
||||
<TaskListMenu
|
||||
id={ taskListId }
|
||||
hideTaskListText={ __(
|
||||
'Hide setup list',
|
||||
'woocommerce-admin'
|
||||
) }
|
||||
/>
|
||||
<div className="woocommerce-task-progress-header__contents">
|
||||
<h1 className="woocommerce-task-progress-header__title">
|
||||
{ progressTitle }
|
||||
</h1>
|
||||
<p>
|
||||
{ sprintf(
|
||||
/* translators: 1: completed tasks, 2: total tasks */
|
||||
__(
|
||||
'Follow these steps to start selling quickly. %1$d out of %2$d complete.',
|
||||
'woocommerce-admin'
|
||||
),
|
||||
completedCount,
|
||||
tasksCount
|
||||
) }
|
||||
</p>
|
||||
<progress
|
||||
className="woocommerce-task-progress-header__progress-bar"
|
||||
max={ tasksCount }
|
||||
value={ completedCount || 0 }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -43,6 +43,7 @@ export const TaskListItem: React.FC< TaskListItemProps > = ( {
|
|||
snoozeTask,
|
||||
undoDismissTask,
|
||||
undoSnoozeTask,
|
||||
visitedTask,
|
||||
} = useDispatch( ONBOARDING_STORE_NAME );
|
||||
const userPreferences = useUserPreferences();
|
||||
|
||||
|
@ -106,6 +107,7 @@ export const TaskListItem: React.FC< TaskListItemProps > = ( {
|
|||
const trackedStartedTasks =
|
||||
userPreferences.task_list_tracked_started_tasks || {};
|
||||
|
||||
visitedTask( id );
|
||||
userPreferences.updateUserPreferences( {
|
||||
task_list_tracked_started_tasks: {
|
||||
...( trackedStartedTasks || {} ),
|
||||
|
|
|
@ -9,9 +9,13 @@ import { useDispatch } from '@wordpress/data';
|
|||
|
||||
export type TaskListMenuProps = {
|
||||
id: string;
|
||||
hideTaskListText?: string;
|
||||
};
|
||||
|
||||
export const TaskListMenu: React.FC< TaskListMenuProps > = ( { id } ) => {
|
||||
export const TaskListMenu: React.FC< TaskListMenuProps > = ( {
|
||||
id,
|
||||
hideTaskListText,
|
||||
} ) => {
|
||||
const { hideTaskList } = useDispatch( ONBOARDING_STORE_NAME );
|
||||
|
||||
return (
|
||||
|
@ -21,7 +25,8 @@ export const TaskListMenu: React.FC< TaskListMenuProps > = ( { id } ) => {
|
|||
renderContent={ () => (
|
||||
<div className="woocommerce-task-card__section-controls">
|
||||
<Button onClick={ () => hideTaskList( id ) }>
|
||||
{ __( 'Hide this', 'woocommerce-admin' ) }
|
||||
{ hideTaskListText ||
|
||||
__( 'Hide this', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
</div>
|
||||
) }
|
||||
|
|
|
@ -16,6 +16,7 @@ import { Text, List, CollapsibleList } from '@woocommerce/experimental';
|
|||
import { TaskListItem } from './task-list-item';
|
||||
import { TaskListMenu } from './task-list-menu';
|
||||
import './task-list.scss';
|
||||
import { ProgressHeader } from '~/task-lists/progress-header';
|
||||
|
||||
export type TaskListProps = TaskListType & {
|
||||
query: {
|
||||
|
@ -33,6 +34,7 @@ export const TaskList: React.FC< TaskListProps > = ( {
|
|||
title: listTitle,
|
||||
isCollapsible = false,
|
||||
isExpandable = false,
|
||||
displayProgressHeader = false,
|
||||
query,
|
||||
} ) => {
|
||||
const { profileItems } = useSelect( ( select ) => {
|
||||
|
@ -114,6 +116,9 @@ export const TaskList: React.FC< TaskListProps > = ( {
|
|||
id
|
||||
}
|
||||
>
|
||||
{ displayProgressHeader ? (
|
||||
<ProgressHeader taskListId={ id } />
|
||||
) : null }
|
||||
<Card
|
||||
size="large"
|
||||
className="woocommerce-task-card woocommerce-homescreen-card"
|
||||
|
|
|
@ -140,6 +140,7 @@ export const Tasks: React.FC< TasksProps > = ( { query } ) => {
|
|||
isToggleable,
|
||||
title,
|
||||
tasks,
|
||||
displayProgressHeader,
|
||||
} = taskList;
|
||||
|
||||
if ( ! isVisible ) {
|
||||
|
@ -163,6 +164,7 @@ export const Tasks: React.FC< TasksProps > = ( { query } ) => {
|
|||
tasks={ tasks }
|
||||
title={ title }
|
||||
twoColumns={ false }
|
||||
displayProgressHeader={ displayProgressHeader }
|
||||
/>
|
||||
</Suspense>
|
||||
{ isToggleable && (
|
||||
|
|
|
@ -24,6 +24,7 @@ import taskHeaders from './task-headers';
|
|||
import DismissModal from './dismiss-modal';
|
||||
import TaskListCompleted from './completed';
|
||||
import { TaskListProps } from '~/tasks/task-list';
|
||||
import { ProgressHeader } from '~/task-lists/progress-header';
|
||||
|
||||
export const TaskList: React.FC< TaskListProps > = ( {
|
||||
query,
|
||||
|
@ -33,6 +34,7 @@ export const TaskList: React.FC< TaskListProps > = ( {
|
|||
twoColumns,
|
||||
keepCompletedTaskList,
|
||||
isComplete,
|
||||
displayProgressHeader,
|
||||
} ) => {
|
||||
const { createNotice } = useDispatch( 'core/notices' );
|
||||
const { updateOptions, dismissTask, undoDismissTask } = useDispatch(
|
||||
|
@ -223,6 +225,9 @@ export const TaskList: React.FC< TaskListProps > = ( {
|
|||
hideTasks={ hideTasks }
|
||||
/>
|
||||
) }
|
||||
{ displayProgressHeader ? (
|
||||
<ProgressHeader taskListId={ id } />
|
||||
) : null }
|
||||
<div
|
||||
className={ classnames(
|
||||
`woocommerce-task-dashboard__container two-column-experiment woocommerce-task-list__${ id }`,
|
||||
|
@ -241,7 +246,7 @@ export const TaskList: React.FC< TaskListProps > = ( {
|
|||
headerData
|
||||
) }
|
||||
</div>
|
||||
{ renderMenu() }
|
||||
{ ! displayProgressHeader && renderMenu() }
|
||||
</div>
|
||||
<List animation="custom">
|
||||
{ visibleTasks.map( ( task, index ) => {
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminUser;
|
||||
|
||||
/**
|
||||
* Task class.
|
||||
*/
|
||||
|
@ -414,6 +416,18 @@ abstract class Task {
|
|||
return self::is_actioned();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the task has been visited.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_visited() {
|
||||
$user_id = get_current_user_id();
|
||||
$response = WCAdminUser::get_user_data_field( $user_id, 'task_list_tracked_started_tasks' );
|
||||
$tracked_tasks = $response ? json_decode( $response, true ) : array();
|
||||
|
||||
return isset( $tracked_tasks[ $this->get_id() ] ) && $tracked_tasks[ $this->get_id() ] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the task as JSON.
|
||||
|
@ -440,6 +454,7 @@ abstract class Task {
|
|||
'isDismissable' => $this->is_dismissable(),
|
||||
'isSnoozed' => $this->is_snoozed(),
|
||||
'isSnoozeable' => $this->is_snoozeable(),
|
||||
'isVisited' => $this->is_visited(),
|
||||
'snoozedUntil' => $this->get_snoozed_until(),
|
||||
'additionalData' => self::convert_object_to_camelcase( $this->get_additional_data() ),
|
||||
'eventPrefix' => $this->prefix_event( '' ),
|
||||
|
|
|
@ -47,6 +47,13 @@ class TaskList {
|
|||
*/
|
||||
public $hidden_id = '';
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $display_progress_header = false;
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
|
@ -96,25 +103,27 @@ class TaskList {
|
|||
*/
|
||||
public function __construct( $data = array() ) {
|
||||
$defaults = array(
|
||||
'id' => null,
|
||||
'hidden_id' => null,
|
||||
'title' => '',
|
||||
'tasks' => array(),
|
||||
'sort_by' => array(),
|
||||
'event_prefix' => null,
|
||||
'options' => array(),
|
||||
'visible' => true,
|
||||
'id' => null,
|
||||
'hidden_id' => null,
|
||||
'title' => '',
|
||||
'tasks' => array(),
|
||||
'sort_by' => array(),
|
||||
'event_prefix' => null,
|
||||
'options' => array(),
|
||||
'visible' => true,
|
||||
'display_progress_header' => false,
|
||||
);
|
||||
|
||||
$data = wp_parse_args( $data, $defaults );
|
||||
|
||||
$this->id = $data['id'];
|
||||
$this->hidden_id = $data['hidden_id'];
|
||||
$this->title = $data['title'];
|
||||
$this->sort_by = $data['sort_by'];
|
||||
$this->event_prefix = $data['event_prefix'];
|
||||
$this->options = $data['options'];
|
||||
$this->visible = $data['visible'];
|
||||
$this->id = $data['id'];
|
||||
$this->hidden_id = $data['hidden_id'];
|
||||
$this->title = $data['title'];
|
||||
$this->sort_by = $data['sort_by'];
|
||||
$this->event_prefix = $data['event_prefix'];
|
||||
$this->options = $data['options'];
|
||||
$this->visible = $data['visible'];
|
||||
$this->display_progress_header = $data['display_progress_header'];
|
||||
|
||||
foreach ( $data['tasks'] as $task_name ) {
|
||||
$class = 'Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\\' . $task_name;
|
||||
|
@ -342,18 +351,19 @@ class TaskList {
|
|||
public function get_json() {
|
||||
$this->possibly_track_completion();
|
||||
return array(
|
||||
'id' => $this->get_list_id(),
|
||||
'title' => $this->title,
|
||||
'isHidden' => $this->is_hidden(),
|
||||
'isVisible' => $this->is_visible(),
|
||||
'isComplete' => $this->is_complete(),
|
||||
'tasks' => array_map(
|
||||
'id' => $this->get_list_id(),
|
||||
'title' => $this->title,
|
||||
'isHidden' => $this->is_hidden(),
|
||||
'isVisible' => $this->is_visible(),
|
||||
'isComplete' => $this->is_complete(),
|
||||
'tasks' => array_map(
|
||||
function( $task ) {
|
||||
return $task->get_json();
|
||||
},
|
||||
$this->get_viewable_tasks()
|
||||
),
|
||||
'eventPrefix' => $this->prefix_event( '' ),
|
||||
'eventPrefix' => $this->prefix_event( '' ),
|
||||
'displayProgressHeader' => $this->display_progress_header,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,10 +98,10 @@ class TaskLists {
|
|||
|
||||
self::add_list(
|
||||
array(
|
||||
'id' => 'setup_experiment_1',
|
||||
'hidden_id' => 'setup',
|
||||
'title' => __( 'Get ready to start selling', 'woocommerce-admin' ),
|
||||
'tasks' => array(
|
||||
'id' => 'setup_experiment_1',
|
||||
'hidden_id' => 'setup',
|
||||
'title' => __( 'Get ready to start selling', 'woocommerce-admin' ),
|
||||
'tasks' => array(
|
||||
'StoreDetails',
|
||||
'Products',
|
||||
'WooCommercePayments',
|
||||
|
@ -111,11 +111,12 @@ class TaskLists {
|
|||
'Marketing',
|
||||
'Appearance',
|
||||
),
|
||||
'event_prefix' => 'tasklist_',
|
||||
'options' => array(
|
||||
'display_progress_header' => true,
|
||||
'event_prefix' => 'tasklist_',
|
||||
'options' => array(
|
||||
'use_completed_title' => true,
|
||||
),
|
||||
'visible' => Features::is_enabled( 'tasklist-setup-experiment-1' ),
|
||||
'visible' => Features::is_enabled( 'tasklist-setup-experiment-1' ),
|
||||
)
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in New Issue