/** * External dependencies */ import { __, _n, sprintf } from '@wordpress/i18n'; import { useEffect, useRef, useState } from '@wordpress/element'; import { Button, Card, 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 (
(
) } />
); }; 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
; } 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 ( <>
{ listTitle }
{ renderMenu() }
{ listTasks.map( ( task ) => ( 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 } /> ) ) }
); }; 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;