2019-04-22 13:23:37 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2019-04-30 00:35:37 +00:00
|
|
|
import { __, sprintf } from '@wordpress/i18n';
|
2021-08-09 20:32:51 +00:00
|
|
|
import { useMemo } from '@wordpress/element';
|
2019-05-07 07:21:34 +00:00
|
|
|
import { compose } from '@wordpress/compose';
|
2020-06-10 23:49:27 +00:00
|
|
|
import { partial } from 'lodash';
|
2021-05-25 15:14:14 +00:00
|
|
|
import { Dropdown, Button } from '@wordpress/components';
|
2019-12-05 22:38:26 +00:00
|
|
|
import { applyFilters } from '@wordpress/hooks';
|
2021-05-25 15:14:14 +00:00
|
|
|
import { Icon, plusCircleFilled } from '@wordpress/icons';
|
2020-09-03 21:45:40 +00:00
|
|
|
import { withSelect } from '@wordpress/data';
|
2020-09-14 23:44:46 +00:00
|
|
|
import { H } from '@woocommerce/components';
|
|
|
|
import { SETTINGS_STORE_NAME, useUserPreferences } from '@woocommerce/data';
|
2020-07-20 22:17:28 +00:00
|
|
|
import { getQuery } from '@woocommerce/navigation';
|
2020-08-17 21:53:13 +00:00
|
|
|
import {
|
|
|
|
getCurrentDates,
|
|
|
|
getDateParamsFromQuery,
|
|
|
|
isoDateFormat,
|
|
|
|
} from '@woocommerce/date';
|
2020-08-20 04:59:52 +00:00
|
|
|
import { recordEvent } from '@woocommerce/tracks';
|
2023-02-28 16:55:49 +00:00
|
|
|
import {
|
|
|
|
CurrencyContext,
|
|
|
|
getFilteredCurrencyInstance,
|
|
|
|
} from '@woocommerce/currency';
|
2019-04-22 13:23:37 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import './style.scss';
|
2019-05-07 07:21:34 +00:00
|
|
|
import defaultSections from './default-sections';
|
2019-05-09 01:13:14 +00:00
|
|
|
import Section from './section';
|
2020-08-13 02:05:22 +00:00
|
|
|
import ReportFilters from '../analytics/components/report-filters';
|
2019-04-22 13:23:37 +00:00
|
|
|
|
2019-12-05 22:38:26 +00:00
|
|
|
const DASHBOARD_FILTERS_FILTER = 'woocommerce_admin_dashboard_filters';
|
2022-02-11 14:38:38 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {import('../analytics/report/index.js').filter} filter
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add Report filters to the dashboard. None are added by default.
|
|
|
|
*
|
|
|
|
* @filter woocommerce_admin_dashboard_filters
|
|
|
|
* @param {Array.<filter>} filters Report filters.
|
|
|
|
*/
|
2019-12-05 22:38:26 +00:00
|
|
|
const filters = applyFilters( DASHBOARD_FILTERS_FILTER, [] );
|
|
|
|
|
2020-06-10 16:46:46 +00:00
|
|
|
const mergeSectionsWithDefaults = ( prefSections ) => {
|
|
|
|
if ( ! prefSections || prefSections.length === 0 ) {
|
2021-08-09 20:32:51 +00:00
|
|
|
return defaultSections.reduce( ( sections, section ) => {
|
|
|
|
return [ ...sections, { ...section } ];
|
|
|
|
}, [] );
|
2019-05-07 07:21:34 +00:00
|
|
|
}
|
2020-06-10 16:46:46 +00:00
|
|
|
const defaultKeys = defaultSections.map( ( section ) => section.key );
|
|
|
|
const prefKeys = prefSections.map( ( section ) => section.key );
|
|
|
|
const keys = new Set( [ ...prefKeys, ...defaultKeys ] );
|
|
|
|
const sections = [];
|
|
|
|
|
|
|
|
keys.forEach( ( key ) => {
|
|
|
|
const defaultSection = defaultSections.find(
|
|
|
|
( section ) => section.key === key
|
|
|
|
);
|
|
|
|
if ( ! defaultSection ) {
|
|
|
|
return;
|
2019-05-07 07:21:34 +00:00
|
|
|
}
|
2020-06-10 16:46:46 +00:00
|
|
|
const prefSection = prefSections.find(
|
|
|
|
( section ) => section.key === key
|
|
|
|
);
|
2021-05-25 15:14:14 +00:00
|
|
|
// Not defined by a string anymore.
|
|
|
|
if ( prefSection ) {
|
|
|
|
delete prefSection.icon;
|
|
|
|
}
|
2019-05-07 07:21:34 +00:00
|
|
|
|
2020-06-10 16:46:46 +00:00
|
|
|
sections.push( {
|
|
|
|
...defaultSection,
|
|
|
|
...prefSection,
|
2019-05-07 07:21:34 +00:00
|
|
|
} );
|
2020-06-10 16:46:46 +00:00
|
|
|
} );
|
|
|
|
|
|
|
|
return sections;
|
2020-06-22 21:25:01 +00:00
|
|
|
};
|
2019-05-07 07:21:34 +00:00
|
|
|
|
2020-09-14 23:44:46 +00:00
|
|
|
const CustomizableDashboard = ( { defaultDateRange, path, query } ) => {
|
2020-06-22 21:25:01 +00:00
|
|
|
const { updateUserPreferences, ...userPrefs } = useUserPreferences();
|
2019-05-07 07:21:34 +00:00
|
|
|
|
2021-08-09 20:32:51 +00:00
|
|
|
const sections = useMemo(
|
|
|
|
() => mergeSectionsWithDefaults( userPrefs.dashboard_sections ),
|
|
|
|
[ userPrefs.dashboard_sections ]
|
|
|
|
);
|
2020-06-10 16:46:46 +00:00
|
|
|
|
|
|
|
const updateSections = ( newSections ) => {
|
|
|
|
updateUserPreferences( { dashboard_sections: newSections } );
|
2020-06-22 21:25:01 +00:00
|
|
|
};
|
2019-05-07 07:21:34 +00:00
|
|
|
|
2020-06-10 16:46:46 +00:00
|
|
|
const updateSection = ( updatedKey, newSettings ) => {
|
|
|
|
const newSections = sections.map( ( section ) => {
|
2021-05-25 15:14:14 +00:00
|
|
|
// Do not save section icon as it is a component.
|
|
|
|
delete section.icon;
|
2019-05-07 07:21:34 +00:00
|
|
|
if ( section.key === updatedKey ) {
|
|
|
|
return {
|
|
|
|
...section,
|
|
|
|
...newSettings,
|
|
|
|
};
|
|
|
|
}
|
2021-05-25 15:14:14 +00:00
|
|
|
|
2019-05-07 07:21:34 +00:00
|
|
|
return section;
|
|
|
|
} );
|
2020-06-10 16:46:46 +00:00
|
|
|
updateSections( newSections );
|
2020-06-22 21:25:01 +00:00
|
|
|
};
|
2019-05-07 07:21:34 +00:00
|
|
|
|
2020-06-10 16:46:46 +00:00
|
|
|
const onChangeHiddenBlocks = ( updatedKey ) => {
|
2020-02-14 02:23:21 +00:00
|
|
|
return ( updatedHiddenBlocks ) => {
|
2020-06-10 16:46:46 +00:00
|
|
|
updateSection( updatedKey, {
|
2020-02-14 02:23:21 +00:00
|
|
|
hiddenBlocks: updatedHiddenBlocks,
|
|
|
|
} );
|
2019-05-07 07:21:34 +00:00
|
|
|
};
|
2020-06-22 21:25:01 +00:00
|
|
|
};
|
2019-05-02 10:22:34 +00:00
|
|
|
|
2020-06-10 16:46:46 +00:00
|
|
|
const onSectionTitleUpdate = ( updatedKey ) => {
|
2020-02-14 02:23:21 +00:00
|
|
|
return ( updatedTitle ) => {
|
2019-07-10 23:02:37 +00:00
|
|
|
recordEvent( 'dash_section_rename', { key: updatedKey } );
|
2020-06-10 16:46:46 +00:00
|
|
|
updateSection( updatedKey, { title: updatedTitle } );
|
2019-05-02 10:22:34 +00:00
|
|
|
};
|
2020-06-22 21:25:01 +00:00
|
|
|
};
|
2019-05-02 10:22:34 +00:00
|
|
|
|
2020-06-10 16:46:46 +00:00
|
|
|
const toggleVisibility = ( key, onToggle ) => {
|
2019-04-30 00:35:37 +00:00
|
|
|
return () => {
|
|
|
|
if ( onToggle ) {
|
|
|
|
// Close the dropdown before setting state so an action is not performed on an unmounted component.
|
|
|
|
onToggle();
|
|
|
|
}
|
2019-05-07 07:21:34 +00:00
|
|
|
// When toggling visibility, place section at the end of the array.
|
2020-02-14 02:23:21 +00:00
|
|
|
const index = sections.findIndex( ( s ) => key === s.key );
|
2019-05-07 07:21:34 +00:00
|
|
|
const toggledSection = sections.splice( index, 1 ).shift();
|
|
|
|
toggledSection.isVisible = ! toggledSection.isVisible;
|
|
|
|
sections.push( toggledSection );
|
|
|
|
|
2019-07-10 22:52:15 +00:00
|
|
|
if ( toggledSection.isVisible ) {
|
|
|
|
recordEvent( 'dash_section_add', { key: toggledSection.key } );
|
|
|
|
} else {
|
2020-02-14 02:23:21 +00:00
|
|
|
recordEvent( 'dash_section_remove', {
|
|
|
|
key: toggledSection.key,
|
|
|
|
} );
|
2019-07-01 10:16:12 +00:00
|
|
|
}
|
|
|
|
|
2020-06-10 16:46:46 +00:00
|
|
|
updateSections( sections );
|
2019-04-30 00:35:37 +00:00
|
|
|
};
|
2020-06-22 21:25:01 +00:00
|
|
|
};
|
2019-04-30 00:35:37 +00:00
|
|
|
|
2020-06-10 16:46:46 +00:00
|
|
|
const onMove = ( index, change ) => {
|
2019-04-30 00:35:37 +00:00
|
|
|
const movedSection = sections.splice( index, 1 ).shift();
|
2019-05-09 01:13:14 +00:00
|
|
|
const newIndex = index + change;
|
|
|
|
|
|
|
|
// Figure out the index of the skipped section.
|
|
|
|
const nextJumpedSectionIndex = change < 0 ? newIndex : newIndex - 1;
|
|
|
|
|
|
|
|
if (
|
|
|
|
sections[ nextJumpedSectionIndex ].isVisible || // Is the skipped section visible?
|
|
|
|
index === 0 || // Will this be the first element?
|
|
|
|
index === sections.length - 1 // Will this be the last element?
|
|
|
|
) {
|
|
|
|
// Yes, lets insert.
|
|
|
|
sections.splice( newIndex, 0, movedSection );
|
2020-06-10 16:46:46 +00:00
|
|
|
updateSections( sections );
|
2019-07-10 22:42:02 +00:00
|
|
|
|
|
|
|
const eventProps = {
|
|
|
|
key: movedSection.key,
|
2020-02-14 02:23:21 +00:00
|
|
|
direction: change > 0 ? 'down' : 'up',
|
2019-07-10 22:42:02 +00:00
|
|
|
};
|
|
|
|
recordEvent( 'dash_section_order_change', eventProps );
|
2019-05-09 01:13:14 +00:00
|
|
|
} else {
|
|
|
|
// No, lets try the next one.
|
2020-06-10 16:46:46 +00:00
|
|
|
onMove( index, change + change );
|
2019-05-09 01:13:14 +00:00
|
|
|
}
|
2020-06-22 21:25:01 +00:00
|
|
|
};
|
2019-04-30 00:35:37 +00:00
|
|
|
|
2020-06-10 16:46:46 +00:00
|
|
|
const renderAddMore = () => {
|
2020-02-14 02:23:21 +00:00
|
|
|
const hiddenSections = sections.filter(
|
|
|
|
( section ) => section.isVisible === false
|
|
|
|
);
|
2019-04-30 00:35:37 +00:00
|
|
|
|
2020-02-14 02:23:21 +00:00
|
|
|
if ( hiddenSections.length === 0 ) {
|
2019-04-30 00:35:37 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Dropdown
|
|
|
|
position="top center"
|
|
|
|
className="woocommerce-dashboard-section__add-more"
|
|
|
|
renderToggle={ ( { onToggle, isOpen } ) => (
|
2020-05-29 02:32:37 +00:00
|
|
|
<Button
|
2019-04-30 00:35:37 +00:00
|
|
|
onClick={ onToggle }
|
2022-03-30 09:00:04 +00:00
|
|
|
title={ __( 'Add more sections', 'woocommerce' ) }
|
2019-04-30 00:35:37 +00:00
|
|
|
aria-expanded={ isOpen }
|
2020-05-29 02:32:37 +00:00
|
|
|
>
|
2021-05-25 15:14:14 +00:00
|
|
|
<Icon icon={ plusCircleFilled } />
|
2020-05-29 02:32:37 +00:00
|
|
|
</Button>
|
2019-04-30 00:35:37 +00:00
|
|
|
) }
|
|
|
|
renderContent={ ( { onToggle } ) => (
|
2021-08-09 20:32:51 +00:00
|
|
|
<>
|
2022-03-30 09:00:04 +00:00
|
|
|
<H>{ __( 'Dashboard Sections', 'woocommerce' ) }</H>
|
2019-04-30 00:35:37 +00:00
|
|
|
<div className="woocommerce-dashboard-section__add-more-choices">
|
2020-02-14 02:23:21 +00:00
|
|
|
{ hiddenSections.map( ( section ) => {
|
2019-04-30 00:35:37 +00:00
|
|
|
return (
|
|
|
|
<Button
|
|
|
|
key={ section.key }
|
2020-06-10 16:46:46 +00:00
|
|
|
onClick={ toggleVisibility(
|
2020-02-14 02:23:21 +00:00
|
|
|
section.key,
|
|
|
|
onToggle
|
|
|
|
) }
|
2019-04-30 00:35:37 +00:00
|
|
|
className="woocommerce-dashboard-section__add-more-btn"
|
2020-02-14 02:23:21 +00:00
|
|
|
title={ sprintf(
|
|
|
|
__(
|
|
|
|
'Add %s section',
|
2022-03-30 09:00:04 +00:00
|
|
|
'woocommerce'
|
2020-02-14 02:23:21 +00:00
|
|
|
),
|
|
|
|
section.title
|
|
|
|
) }
|
2019-04-30 00:35:37 +00:00
|
|
|
>
|
2020-02-14 02:23:21 +00:00
|
|
|
<Icon
|
2021-05-25 15:14:14 +00:00
|
|
|
className={ section.key + '__icon' }
|
2020-02-14 02:23:21 +00:00
|
|
|
icon={ section.icon }
|
|
|
|
size={ 30 }
|
|
|
|
/>
|
2019-04-30 00:35:37 +00:00
|
|
|
<span className="woocommerce-dashboard-section__add-more-btn-title">
|
|
|
|
{ section.title }
|
|
|
|
</span>
|
|
|
|
</Button>
|
|
|
|
);
|
|
|
|
} ) }
|
|
|
|
</div>
|
2021-08-09 20:32:51 +00:00
|
|
|
</>
|
2019-04-30 00:35:37 +00:00
|
|
|
) }
|
|
|
|
/>
|
|
|
|
);
|
2020-06-22 21:25:01 +00:00
|
|
|
};
|
2019-04-30 00:35:37 +00:00
|
|
|
|
2020-06-10 16:46:46 +00:00
|
|
|
const renderDashboardReports = () => {
|
2020-02-14 02:23:21 +00:00
|
|
|
const { period, compare, before, after } = getDateParamsFromQuery(
|
2020-03-25 03:20:17 +00:00
|
|
|
query,
|
|
|
|
defaultDateRange
|
2020-02-14 02:23:21 +00:00
|
|
|
);
|
2022-06-21 08:37:34 +00:00
|
|
|
const { primary: primaryDate, secondary: secondaryDate } =
|
|
|
|
getCurrentDates( query, defaultDateRange );
|
2019-11-26 19:39:40 +00:00
|
|
|
const dateQuery = {
|
|
|
|
period,
|
|
|
|
compare,
|
|
|
|
before,
|
|
|
|
after,
|
|
|
|
primaryDate,
|
|
|
|
secondaryDate,
|
|
|
|
};
|
2020-02-14 02:23:21 +00:00
|
|
|
const visibleSectionKeys = sections
|
|
|
|
.filter( ( section ) => section.isVisible )
|
|
|
|
.map( ( section ) => section.key );
|
|
|
|
|
2019-04-22 13:23:37 +00:00
|
|
|
return (
|
2021-08-09 20:32:51 +00:00
|
|
|
<>
|
2019-11-27 23:12:33 +00:00
|
|
|
<ReportFilters
|
|
|
|
report="dashboard"
|
|
|
|
query={ query }
|
|
|
|
path={ path }
|
|
|
|
dateQuery={ dateQuery }
|
|
|
|
isoDateFormat={ isoDateFormat }
|
2019-12-05 22:38:26 +00:00
|
|
|
filters={ filters }
|
2019-11-27 23:12:33 +00:00
|
|
|
/>
|
2019-05-09 01:13:14 +00:00
|
|
|
{ sections.map( ( section, index ) => {
|
|
|
|
if ( section.isVisible ) {
|
|
|
|
return (
|
|
|
|
<Section
|
|
|
|
component={ section.component }
|
|
|
|
hiddenBlocks={ section.hiddenBlocks }
|
|
|
|
key={ section.key }
|
2020-06-10 16:46:46 +00:00
|
|
|
onChangeHiddenBlocks={ onChangeHiddenBlocks(
|
2020-02-14 02:23:21 +00:00
|
|
|
section.key
|
|
|
|
) }
|
2020-06-10 16:46:46 +00:00
|
|
|
onTitleUpdate={ onSectionTitleUpdate(
|
2020-02-14 02:23:21 +00:00
|
|
|
section.key
|
|
|
|
) }
|
2019-05-09 01:13:14 +00:00
|
|
|
path={ path }
|
2022-01-19 02:15:33 +00:00
|
|
|
defaultDateRange={ defaultDateRange }
|
2019-05-09 01:13:14 +00:00
|
|
|
query={ query }
|
|
|
|
title={ section.title }
|
2020-06-10 16:46:46 +00:00
|
|
|
onMove={ partial( onMove, index ) }
|
2020-06-22 21:25:01 +00:00
|
|
|
onRemove={ toggleVisibility( section.key ) }
|
2020-02-14 02:23:21 +00:00
|
|
|
isFirst={
|
|
|
|
section.key === visibleSectionKeys[ 0 ]
|
|
|
|
}
|
|
|
|
isLast={
|
|
|
|
section.key ===
|
|
|
|
visibleSectionKeys[
|
|
|
|
visibleSectionKeys.length - 1
|
|
|
|
]
|
|
|
|
}
|
2020-07-20 22:17:28 +00:00
|
|
|
filters={ filters }
|
2019-05-09 01:13:14 +00:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return null;
|
2019-05-02 10:22:34 +00:00
|
|
|
} ) }
|
2020-06-10 16:46:46 +00:00
|
|
|
{ renderAddMore() }
|
2021-08-09 20:32:51 +00:00
|
|
|
</>
|
2019-04-22 13:23:37 +00:00
|
|
|
);
|
2020-06-22 21:25:01 +00:00
|
|
|
};
|
2020-03-23 20:47:12 +00:00
|
|
|
|
2020-06-10 16:46:46 +00:00
|
|
|
return (
|
2020-07-20 22:17:28 +00:00
|
|
|
<CurrencyContext.Provider
|
|
|
|
value={ getFilteredCurrencyInstance( getQuery() ) }
|
|
|
|
>
|
2020-09-14 23:44:46 +00:00
|
|
|
{ renderDashboardReports() }
|
2020-07-20 22:17:28 +00:00
|
|
|
</CurrencyContext.Provider>
|
2020-06-10 16:46:46 +00:00
|
|
|
);
|
2020-06-22 21:25:01 +00:00
|
|
|
};
|
2019-05-07 07:21:34 +00:00
|
|
|
|
|
|
|
export default compose(
|
2020-03-23 20:47:12 +00:00
|
|
|
withSelect( ( select ) => {
|
2020-03-25 03:20:17 +00:00
|
|
|
const { woocommerce_default_date_range: defaultDateRange } = select(
|
|
|
|
SETTINGS_STORE_NAME
|
|
|
|
).getSetting( 'wc_admin', 'wcAdminSettings' );
|
|
|
|
|
2022-01-19 02:15:33 +00:00
|
|
|
return {
|
2020-03-25 03:20:17 +00:00
|
|
|
defaultDateRange,
|
2019-05-07 07:21:34 +00:00
|
|
|
};
|
|
|
|
} )
|
|
|
|
)( CustomizableDashboard );
|