423 lines
9.6 KiB
JavaScript
423 lines
9.6 KiB
JavaScript
/**
|
|
* External dependencies
|
|
*/
|
|
import { __, _n, sprintf } from '@wordpress/i18n';
|
|
import { useEffect, useRef, useState } from '@wordpress/element';
|
|
import { Button, Card, CardBody, CardHeader } from '@wordpress/components';
|
|
import { useSelect, useDispatch } from '@wordpress/data';
|
|
import { EllipsisMenu, Badge } from '@woocommerce/components';
|
|
import { updateQueryString } from '@woocommerce/navigation';
|
|
import { OPTIONS_STORE_NAME, ONBOARDING_STORE_NAME } from '@woocommerce/data';
|
|
import { recordEvent } from '@woocommerce/tracks';
|
|
import {
|
|
Text,
|
|
List,
|
|
CollapsibleList,
|
|
TaskItem,
|
|
} from '@woocommerce/experimental';
|
|
|
|
/**
|
|
* Internal dependencies
|
|
*/
|
|
import './task-list.scss';
|
|
|
|
const DAY_IN_MS = 24 * 60 * 60 * 1000;
|
|
|
|
export const TaskList = ( {
|
|
query,
|
|
name,
|
|
eventName,
|
|
isComplete,
|
|
dismissedTasks,
|
|
remindMeLaterTasks,
|
|
tasks,
|
|
trackedCompletedTasks: totalTrackedCompletedTasks,
|
|
title: listTitle,
|
|
collapsible = false,
|
|
onComplete,
|
|
onHide,
|
|
expandingItems = false,
|
|
} ) => {
|
|
const { createNotice } = useDispatch( 'core/notices' );
|
|
const { updateOptions } = useDispatch( OPTIONS_STORE_NAME );
|
|
const { profileItems } = useSelect( ( select ) => {
|
|
const { getProfileItems } = select( ONBOARDING_STORE_NAME );
|
|
|
|
return {
|
|
profileItems: getProfileItems(),
|
|
};
|
|
} );
|
|
|
|
const prevQueryRef = useRef( query );
|
|
useEffect( () => {
|
|
recordTaskListView();
|
|
}, [] );
|
|
|
|
useEffect( () => {
|
|
const { task: prevTask } = prevQueryRef.current;
|
|
const { task } = query;
|
|
|
|
if ( prevTask !== task ) {
|
|
window.document.documentElement.scrollTop = 0;
|
|
prevQueryRef.current = query;
|
|
}
|
|
|
|
possiblyCompleteTaskList();
|
|
possiblyTrackCompletedTasks();
|
|
}, [ query ] );
|
|
|
|
const nowTimestamp = Date.now();
|
|
const visibleTasks = tasks.filter(
|
|
( task ) =>
|
|
task.visible &&
|
|
! dismissedTasks.includes( task.key ) &&
|
|
( ! remindMeLaterTasks[ task.key ] ||
|
|
remindMeLaterTasks[ task.key ] < nowTimestamp )
|
|
);
|
|
|
|
const completedTaskKeys = visibleTasks
|
|
.filter( ( task ) => task.completed )
|
|
.map( ( task ) => task.key );
|
|
|
|
const incompleteTasks = tasks.filter(
|
|
( task ) =>
|
|
task.visible &&
|
|
! task.completed &&
|
|
! dismissedTasks.includes( task.key )
|
|
);
|
|
|
|
const [ currentTask, setCurrentTask ] = useState(
|
|
incompleteTasks[ 0 ]?.key
|
|
);
|
|
|
|
const possiblyCompleteTaskList = () => {
|
|
const taskListVariableName = `woocommerce_${ name }_complete`;
|
|
const taskListToComplete = isComplete
|
|
? { [ taskListVariableName ]: 'no' }
|
|
: { [ taskListVariableName ]: 'yes' };
|
|
|
|
if (
|
|
( ! incompleteTasks.length && ! isComplete ) ||
|
|
( incompleteTasks.length && isComplete )
|
|
) {
|
|
updateOptions( {
|
|
...taskListToComplete,
|
|
} );
|
|
|
|
if ( typeof onComplete === 'function' ) {
|
|
onComplete();
|
|
}
|
|
}
|
|
};
|
|
|
|
const getTrackedIncompletedTasks = (
|
|
partialCompletedTasks,
|
|
allTrackedTask
|
|
) => {
|
|
return visibleTasks
|
|
.filter(
|
|
( task ) =>
|
|
allTrackedTask.includes( task.key ) &&
|
|
! partialCompletedTasks.includes( task.key )
|
|
)
|
|
.map( ( task ) => task.key );
|
|
};
|
|
|
|
const possiblyTrackCompletedTasks = () => {
|
|
const trackedCompletedTasks = getTrackedCompletedTasks(
|
|
completedTaskKeys,
|
|
totalTrackedCompletedTasks
|
|
);
|
|
|
|
const trackedIncompleteTasks = getTrackedIncompletedTasks(
|
|
trackedCompletedTasks,
|
|
totalTrackedCompletedTasks
|
|
);
|
|
|
|
if (
|
|
shouldUpdateCompletedTasks(
|
|
trackedCompletedTasks,
|
|
trackedIncompleteTasks,
|
|
completedTaskKeys
|
|
)
|
|
) {
|
|
updateOptions( {
|
|
woocommerce_task_list_tracked_completed_tasks: getTasksForUpdate(
|
|
completedTaskKeys,
|
|
totalTrackedCompletedTasks,
|
|
trackedIncompleteTasks
|
|
),
|
|
} );
|
|
}
|
|
};
|
|
|
|
const dismissTask = ( { key, onDismiss } ) => {
|
|
createNotice( 'success', __( 'Task dismissed' ), {
|
|
actions: [
|
|
{
|
|
label: __( 'Undo', 'woocommerce-admin' ),
|
|
onClick: () => undoDismissTask( key ),
|
|
},
|
|
],
|
|
} );
|
|
|
|
recordEvent( `${ eventName }_dismiss_task`, { task_name: key } );
|
|
|
|
updateOptions( {
|
|
woocommerce_task_list_dismissed_tasks: [ ...dismissedTasks, key ],
|
|
} );
|
|
if ( onDismiss ) {
|
|
onDismiss();
|
|
}
|
|
};
|
|
|
|
const undoDismissTask = ( key ) => {
|
|
const updatedDismissedTasks = dismissedTasks.filter(
|
|
( task ) => task !== key
|
|
);
|
|
|
|
updateOptions( {
|
|
woocommerce_task_list_dismissed_tasks: updatedDismissedTasks,
|
|
} );
|
|
recordEvent( `${ eventName }_undo_dismiss_task`, {
|
|
task_name: key,
|
|
} );
|
|
};
|
|
|
|
const remindTaskLater = ( { key, onDismiss } ) => {
|
|
createNotice(
|
|
'success',
|
|
__( 'Task postponed until tomorrow', 'woocommerce-admin' ),
|
|
{
|
|
actions: [
|
|
{
|
|
label: __( 'Undo', 'woocommerce-admin' ),
|
|
onClick: () => undoRemindTaskLater( key ),
|
|
},
|
|
],
|
|
}
|
|
);
|
|
recordEvent( `${ eventName }_remindmelater_task`, {
|
|
task_name: key,
|
|
} );
|
|
|
|
const dismissTime = Date.now() + DAY_IN_MS;
|
|
updateOptions( {
|
|
woocommerce_task_list_remind_me_later_tasks: {
|
|
...remindMeLaterTasks,
|
|
[ key ]: dismissTime,
|
|
},
|
|
} );
|
|
if ( onDismiss ) {
|
|
onDismiss();
|
|
}
|
|
};
|
|
|
|
const undoRemindTaskLater = ( key ) => {
|
|
const {
|
|
// eslint-disable-next-line no-unused-vars
|
|
[ key ]: oldValue,
|
|
...updatedRemindMeLaterTasks
|
|
} = remindMeLaterTasks;
|
|
|
|
updateOptions( {
|
|
woocommerce_task_list_remind_me_later_tasks: updatedRemindMeLaterTasks,
|
|
} );
|
|
recordEvent( `${ eventName }_undo_remindmelater_task`, {
|
|
task_name: key,
|
|
} );
|
|
};
|
|
|
|
const recordTaskListView = () => {
|
|
if ( query.task ) {
|
|
return;
|
|
}
|
|
|
|
recordEvent( `${ eventName }_view`, {
|
|
number_tasks: visibleTasks.length,
|
|
store_connected: profileItems.wccom_connected,
|
|
} );
|
|
};
|
|
|
|
const hideTaskCard = ( action ) => {
|
|
const updateOptionsParams = {
|
|
[ `woocommerce_${ name }_hidden` ]: 'yes',
|
|
};
|
|
|
|
recordEvent( `${ eventName }_completed`, {
|
|
action,
|
|
completed_task_count: completedTaskKeys.length,
|
|
incomplete_task_count: incompleteTasks.length,
|
|
} );
|
|
updateOptions( {
|
|
...updateOptionsParams,
|
|
} );
|
|
|
|
if ( typeof onHide === 'function' ) {
|
|
onHide();
|
|
}
|
|
};
|
|
|
|
const renderMenu = () => {
|
|
return (
|
|
<div className="woocommerce-card__menu woocommerce-card__header-item">
|
|
<EllipsisMenu
|
|
label={ __( 'Task List Options', 'woocommerce-admin' ) }
|
|
renderContent={ () => (
|
|
<div className="woocommerce-task-card__section-controls">
|
|
<Button
|
|
onClick={ () => hideTaskCard( 'remove_card' ) }
|
|
>
|
|
{ __( 'Hide this', 'woocommerce-admin' ) }
|
|
</Button>
|
|
</div>
|
|
) }
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const listTasks = visibleTasks.map( ( task ) => {
|
|
if ( ! task.onClick ) {
|
|
task.onClick = ( e ) => {
|
|
recordEvent( `${ eventName }_click`, {
|
|
task_name: task.key,
|
|
} );
|
|
if ( e.target.nodeName === 'A' ) {
|
|
// This is a nested link, so don't activate this task.
|
|
return false;
|
|
}
|
|
|
|
updateQueryString( { task: task.key } );
|
|
};
|
|
}
|
|
|
|
return task;
|
|
} );
|
|
|
|
if ( ! listTasks.length ) {
|
|
return <div className="woocommerce-task-dashboard__container"></div>;
|
|
}
|
|
|
|
const expandLabel = sprintf(
|
|
/* translators: %i = number of hidden tasks */
|
|
_n(
|
|
'Show %i more task.',
|
|
'Show %i more tasks.',
|
|
listTasks.length - 2,
|
|
'woocommerce-admin'
|
|
),
|
|
listTasks.length - 2
|
|
);
|
|
const collapseLabel = __( 'Show less', 'woocommerce-admin' );
|
|
const ListComp = collapsible ? CollapsibleList : List;
|
|
|
|
const listProps = collapsible
|
|
? {
|
|
collapseLabel,
|
|
expandLabel,
|
|
show: 2,
|
|
onCollapse: () => recordEvent( `${ eventName }_collapse` ),
|
|
onExpand: () => recordEvent( `${ eventName }_expand` ),
|
|
}
|
|
: {};
|
|
|
|
return (
|
|
<>
|
|
<div className="woocommerce-task-dashboard__container">
|
|
<Card
|
|
size="large"
|
|
className="woocommerce-task-card woocommerce-homescreen-card"
|
|
>
|
|
<CardHeader size="medium">
|
|
<div className="wooocommerce-task-card__header">
|
|
<Text
|
|
size="20"
|
|
lineHeight="28px"
|
|
variant="title.small"
|
|
>
|
|
{ listTitle }
|
|
</Text>
|
|
<Badge count={ incompleteTasks.length } />
|
|
</div>
|
|
{ renderMenu() }
|
|
</CardHeader>
|
|
<CardBody>
|
|
<ListComp animation="custom" { ...listProps }>
|
|
{ listTasks.map( ( task ) => (
|
|
<TaskItem
|
|
key={ task.key }
|
|
title={ task.title }
|
|
completed={ task.completed }
|
|
content={ task.content }
|
|
onClick={
|
|
! expandingItems || task.completed
|
|
? task.onClick
|
|
: () => setCurrentTask( task.key )
|
|
}
|
|
expandable={ expandingItems }
|
|
expanded={
|
|
expandingItems &&
|
|
currentTask === task.key
|
|
}
|
|
onDismiss={
|
|
task.isDismissable
|
|
? () => dismissTask( task )
|
|
: undefined
|
|
}
|
|
remindMeLater={
|
|
task.allowRemindMeLater
|
|
? () => remindTaskLater( task )
|
|
: undefined
|
|
}
|
|
time={ task.time }
|
|
level={ task.level }
|
|
action={ task.onClick }
|
|
actionLabel={ task.action }
|
|
additionalInfo={ task.additionalInfo }
|
|
/>
|
|
) ) }
|
|
</ListComp>
|
|
</CardBody>
|
|
</Card>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
function shouldUpdateCompletedTasks( tasks, untrackedTasks, completedTasks ) {
|
|
if ( untrackedTasks.length > 0 ) {
|
|
return true;
|
|
}
|
|
if ( completedTasks.length === 0 ) {
|
|
return false;
|
|
}
|
|
return ! completedTasks.every(
|
|
( taskName ) => tasks.indexOf( taskName ) >= 0
|
|
);
|
|
}
|
|
|
|
function getTrackedCompletedTasks( completedTasks, trackedTasks ) {
|
|
if ( ! trackedTasks ) {
|
|
return [];
|
|
}
|
|
return completedTasks.filter( ( taskName ) =>
|
|
trackedTasks.includes( taskName )
|
|
);
|
|
}
|
|
|
|
function getTasksForUpdate(
|
|
completedTaskKeys,
|
|
totalTrackedCompletedTasks,
|
|
trackedIncompleteTasks
|
|
) {
|
|
const mergedLists = [
|
|
...new Set( [ ...completedTaskKeys, ...totalTrackedCompletedTasks ] ),
|
|
];
|
|
return mergedLists.filter(
|
|
( taskName ) => ! trackedIncompleteTasks.includes( taskName )
|
|
);
|
|
}
|
|
|
|
export default TaskList;
|