2018-06-28 13:52:45 +00:00
|
|
|
/** @format */
|
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2018-06-29 15:20:08 +00:00
|
|
|
import { __ } from '@wordpress/i18n';
|
|
|
|
import classnames from 'classnames';
|
2018-07-06 12:40:05 +00:00
|
|
|
import clickOutside from 'react-click-outside';
|
|
|
|
import { Component } from '@wordpress/element';
|
2018-06-29 15:20:08 +00:00
|
|
|
import Gridicon from 'gridicons';
|
2018-07-13 20:28:01 +00:00
|
|
|
import { IconButton, NavigableMenu } from '@wordpress/components';
|
|
|
|
import { partial, uniqueId, find } from 'lodash';
|
2018-06-28 13:52:45 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
2018-06-29 15:20:08 +00:00
|
|
|
import './style.scss';
|
2018-07-09 15:46:31 +00:00
|
|
|
import ActivityPanelToggleBubble from './toggle-bubble';
|
2019-03-22 08:48:20 +00:00
|
|
|
import { DEFAULT_ACTIONABLE_STATUSES } from 'wc-api/constants';
|
2018-09-21 15:19:05 +00:00
|
|
|
import { H, Section } from '@woocommerce/components';
|
2018-10-12 19:20:48 +00:00
|
|
|
import InboxPanel from './panels/inbox';
|
2018-07-16 18:37:25 +00:00
|
|
|
import OrdersPanel from './panels/orders';
|
|
|
|
import StockPanel from './panels/stock';
|
|
|
|
import ReviewsPanel from './panels/reviews';
|
2019-03-19 10:57:11 +00:00
|
|
|
import withSelect from 'wc-api/with-select';
|
2018-06-29 15:20:08 +00:00
|
|
|
import WordPressNotices from './wordpress-notices';
|
2018-06-28 13:52:45 +00:00
|
|
|
|
|
|
|
class ActivityPanel extends Component {
|
|
|
|
constructor() {
|
|
|
|
super( ...arguments );
|
|
|
|
this.togglePanel = this.togglePanel.bind( this );
|
2018-06-29 15:20:08 +00:00
|
|
|
this.toggleMobile = this.toggleMobile.bind( this );
|
|
|
|
this.renderTab = this.renderTab.bind( this );
|
2018-07-18 15:20:00 +00:00
|
|
|
this.updateNoticeFlag = this.updateNoticeFlag.bind( this );
|
2018-06-28 13:52:45 +00:00
|
|
|
this.state = {
|
2018-06-29 15:20:08 +00:00
|
|
|
isPanelOpen: false,
|
|
|
|
mobileOpen: false,
|
2018-06-28 13:52:45 +00:00
|
|
|
currentTab: '',
|
2018-07-18 15:20:00 +00:00
|
|
|
hasWordPressNotices: false,
|
2018-06-28 13:52:45 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
togglePanel( tabName ) {
|
2018-06-29 15:20:08 +00:00
|
|
|
// The WordPress Notices tab is handled differently, since they are displayed inline, so the panel should be closed,
|
|
|
|
// Close behavior of the expanded notices is based on current tab.
|
|
|
|
if ( 'wpnotices' === tabName ) {
|
2018-06-28 13:52:45 +00:00
|
|
|
this.setState( state => ( {
|
2018-06-29 15:20:08 +00:00
|
|
|
currentTab: 'wpnotices' === state.currentTab ? '' : tabName,
|
|
|
|
mobileOpen: 'wpnotices' !== state.currentTab,
|
|
|
|
isPanelOpen: false,
|
2018-06-28 13:52:45 +00:00
|
|
|
} ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-29 15:20:08 +00:00
|
|
|
this.setState( state => {
|
|
|
|
if (
|
|
|
|
tabName === state.currentTab ||
|
|
|
|
'' === state.currentTab ||
|
|
|
|
'wpnotices' === state.currentTab
|
|
|
|
) {
|
|
|
|
return {
|
|
|
|
isPanelOpen: ! state.isPanelOpen,
|
|
|
|
currentTab: state.isPanelOpen ? '' : tabName,
|
|
|
|
mobileOpen: ! state.isPanelOpen,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return { currentTab: tabName };
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
// On smaller screen, the panel buttons are hidden behind a toggle.
|
|
|
|
toggleMobile() {
|
2018-09-05 18:52:48 +00:00
|
|
|
const tabs = this.getTabs();
|
2018-06-29 15:20:08 +00:00
|
|
|
this.setState( state => ( {
|
|
|
|
mobileOpen: ! state.mobileOpen,
|
2018-09-05 18:52:48 +00:00
|
|
|
currentTab: state.mobileOpen ? '' : tabs[ 0 ].name,
|
2018-06-29 15:20:08 +00:00
|
|
|
isPanelOpen: ! state.mobileOpen,
|
|
|
|
} ) );
|
2018-06-28 13:52:45 +00:00
|
|
|
}
|
|
|
|
|
2018-07-06 12:40:05 +00:00
|
|
|
handleClickOutside() {
|
|
|
|
const { isPanelOpen, currentTab } = this.state;
|
|
|
|
|
|
|
|
if ( isPanelOpen ) {
|
|
|
|
this.togglePanel( currentTab );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-18 15:20:00 +00:00
|
|
|
updateNoticeFlag( noticeCount ) {
|
|
|
|
this.setState( {
|
|
|
|
hasWordPressNotices: noticeCount > 0,
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
2019-02-06 06:41:53 +00:00
|
|
|
// @todo Pull in dynamic unread status/count
|
2018-06-28 13:52:45 +00:00
|
|
|
getTabs() {
|
2019-03-21 11:35:46 +00:00
|
|
|
const { unreadNotes, unreadOrders, unreadReviews } = this.props;
|
2018-06-28 13:52:45 +00:00
|
|
|
return [
|
2018-10-12 19:20:48 +00:00
|
|
|
{
|
|
|
|
name: 'inbox',
|
2019-03-13 17:14:02 +00:00
|
|
|
title: __( 'Inbox', 'woocommerce-admin' ),
|
2018-10-12 19:20:48 +00:00
|
|
|
icon: <Gridicon icon="mail" />,
|
2019-03-21 02:54:36 +00:00
|
|
|
unread: unreadNotes,
|
2018-10-12 19:20:48 +00:00
|
|
|
},
|
2018-06-28 13:52:45 +00:00
|
|
|
{
|
2018-06-29 15:20:08 +00:00
|
|
|
name: 'orders',
|
2019-03-13 17:14:02 +00:00
|
|
|
title: __( 'Orders', 'woocommerce-admin' ),
|
2018-06-29 15:20:08 +00:00
|
|
|
icon: <Gridicon icon="pages" />,
|
2019-03-19 10:57:11 +00:00
|
|
|
unread: unreadOrders,
|
2018-06-28 13:52:45 +00:00
|
|
|
},
|
2019-03-21 05:42:07 +00:00
|
|
|
'yes' === wcSettings.manageStock
|
|
|
|
? {
|
|
|
|
name: 'stock',
|
|
|
|
title: __( 'Stock', 'woocommerce-admin' ),
|
|
|
|
icon: <Gridicon icon="clipboard" />,
|
|
|
|
unread: false,
|
|
|
|
}
|
|
|
|
: null,
|
2019-03-20 10:24:12 +00:00
|
|
|
'yes' === wcSettings.reviewsEnabled
|
|
|
|
? {
|
|
|
|
name: 'reviews',
|
|
|
|
title: __( 'Reviews', 'woocommerce-admin' ),
|
|
|
|
icon: <Gridicon icon="star" />,
|
2019-03-21 11:35:46 +00:00
|
|
|
unread: unreadReviews,
|
2019-03-20 10:24:12 +00:00
|
|
|
}
|
|
|
|
: null,
|
|
|
|
].filter( Boolean );
|
2018-06-28 13:52:45 +00:00
|
|
|
}
|
|
|
|
|
2018-07-06 12:40:05 +00:00
|
|
|
getPanelContent( tab ) {
|
|
|
|
switch ( tab ) {
|
2018-10-12 19:20:48 +00:00
|
|
|
case 'inbox':
|
|
|
|
return <InboxPanel />;
|
2018-06-28 13:52:45 +00:00
|
|
|
case 'orders':
|
2019-03-19 10:57:11 +00:00
|
|
|
const { unreadOrders } = this.props;
|
|
|
|
return <OrdersPanel isEmpty={ unreadOrders === false } />;
|
2018-07-09 15:46:31 +00:00
|
|
|
case 'stock':
|
|
|
|
return <StockPanel />;
|
|
|
|
case 'reviews':
|
2019-03-21 11:35:46 +00:00
|
|
|
const { numberOfReviews } = this.props;
|
|
|
|
return <ReviewsPanel numberOfReviews={ numberOfReviews } />;
|
2018-06-28 13:52:45 +00:00
|
|
|
default:
|
2018-07-09 15:46:31 +00:00
|
|
|
return null;
|
2018-06-28 13:52:45 +00:00
|
|
|
}
|
2018-07-06 12:40:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
renderPanel() {
|
|
|
|
const { isPanelOpen, currentTab } = this.state;
|
2018-06-29 15:20:08 +00:00
|
|
|
|
2018-07-13 20:28:01 +00:00
|
|
|
const tab = find( this.getTabs(), { name: currentTab } );
|
|
|
|
if ( ! tab ) {
|
2018-07-17 17:07:36 +00:00
|
|
|
return <div className="woocommerce-layout__activity-panel-wrapper" />;
|
2018-07-13 20:28:01 +00:00
|
|
|
}
|
|
|
|
|
2018-07-17 17:07:36 +00:00
|
|
|
const classNames = classnames( 'woocommerce-layout__activity-panel-wrapper', {
|
|
|
|
'is-open': isPanelOpen,
|
|
|
|
} );
|
|
|
|
|
2018-06-29 15:20:08 +00:00
|
|
|
return (
|
2018-07-13 20:28:01 +00:00
|
|
|
<div className={ classNames } tabIndex={ 0 } role="tabpanel" aria-label={ tab.title }>
|
2018-07-06 12:40:05 +00:00
|
|
|
{ ( isPanelOpen && (
|
|
|
|
<div
|
|
|
|
className="woocommerce-layout__activity-panel-content"
|
|
|
|
key={ 'activity-panel-' + currentTab }
|
|
|
|
id={ 'activity-panel-' + currentTab }
|
|
|
|
>
|
|
|
|
{ this.getPanelContent( currentTab ) }
|
|
|
|
</div>
|
|
|
|
) ) ||
|
|
|
|
null }
|
2018-07-13 19:31:58 +00:00
|
|
|
</div>
|
2018-06-29 15:20:08 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-07-13 20:28:01 +00:00
|
|
|
renderTab( tab, i ) {
|
|
|
|
const { currentTab, isPanelOpen } = this.state;
|
2018-06-29 15:20:08 +00:00
|
|
|
const className = classnames( 'woocommerce-layout__activity-panel-tab', {
|
|
|
|
'is-active': tab.name === currentTab,
|
2018-07-06 12:40:05 +00:00
|
|
|
'has-unread': tab.unread,
|
2018-06-29 15:20:08 +00:00
|
|
|
} );
|
|
|
|
|
2018-07-13 20:28:01 +00:00
|
|
|
const selected = tab.name === currentTab;
|
|
|
|
let tabIndex = -1;
|
|
|
|
|
2018-09-05 18:52:48 +00:00
|
|
|
// Only make this item tabbable if it is the currently selected item, or the panel is closed and the item is the first item
|
2018-07-13 20:28:01 +00:00
|
|
|
// If wpnotices is currently selected, tabindex below should be -1 and <WordPressNotices /> will become the tabbed element.
|
|
|
|
if ( selected || ( ! isPanelOpen && i === 0 && 'wpnotices' !== currentTab ) ) {
|
|
|
|
tabIndex = null;
|
|
|
|
}
|
|
|
|
|
2018-06-29 15:20:08 +00:00
|
|
|
return (
|
|
|
|
<IconButton
|
2018-07-13 20:28:01 +00:00
|
|
|
role="tab"
|
2018-06-29 15:20:08 +00:00
|
|
|
className={ className }
|
2018-07-13 20:28:01 +00:00
|
|
|
tabIndex={ tabIndex }
|
|
|
|
aria-selected={ selected }
|
|
|
|
aria-controls={ 'activity-panel-' + tab.name }
|
|
|
|
key={ 'activity-panel-tab-' + tab.name }
|
|
|
|
id={ 'activity-panel-tab-' + tab.name }
|
2018-06-29 15:20:08 +00:00
|
|
|
onClick={ partial( this.togglePanel, tab.name ) }
|
|
|
|
icon={ tab.icon }
|
|
|
|
>
|
2018-07-18 15:20:00 +00:00
|
|
|
{ tab.title }{' '}
|
|
|
|
{ tab.unread && (
|
2019-03-13 17:14:02 +00:00
|
|
|
<span className="screen-reader-text">
|
|
|
|
{ __( 'unread activity', 'woocommerce-admin' ) }
|
|
|
|
</span>
|
2018-07-18 15:20:00 +00:00
|
|
|
) }
|
2018-06-29 15:20:08 +00:00
|
|
|
</IconButton>
|
|
|
|
);
|
2018-06-28 13:52:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const tabs = this.getTabs();
|
2018-07-18 15:20:00 +00:00
|
|
|
const { currentTab, mobileOpen, hasWordPressNotices } = this.state;
|
2018-07-13 19:31:58 +00:00
|
|
|
const headerId = uniqueId( 'activity-panel-header_' );
|
2018-06-29 15:20:08 +00:00
|
|
|
const panelClasses = classnames( 'woocommerce-layout__activity-panel', {
|
|
|
|
'is-mobile-open': this.state.mobileOpen,
|
|
|
|
} );
|
2018-06-28 13:52:45 +00:00
|
|
|
|
2018-07-18 15:20:00 +00:00
|
|
|
const hasUnread = hasWordPressNotices || tabs.some( tab => tab.unread );
|
|
|
|
const viewLabel = hasUnread
|
2019-03-13 17:14:02 +00:00
|
|
|
? __( 'View Activity Panel, you have unread activity', 'woocommerce-admin' )
|
|
|
|
: __( 'View Activity Panel', 'woocommerce-admin' );
|
2018-07-18 15:20:00 +00:00
|
|
|
|
2018-06-28 13:52:45 +00:00
|
|
|
return (
|
2018-07-13 19:31:58 +00:00
|
|
|
<div>
|
|
|
|
<H id={ headerId } className="screen-reader-text">
|
2019-03-13 17:14:02 +00:00
|
|
|
{ __( 'Store Activity', 'woocommerce-admin' ) }
|
2018-07-13 19:31:58 +00:00
|
|
|
</H>
|
|
|
|
<Section component="aside" id="woocommerce-activity-panel" aria-labelledby={ headerId }>
|
|
|
|
<IconButton
|
|
|
|
onClick={ this.toggleMobile }
|
2018-07-18 15:20:00 +00:00
|
|
|
icon={
|
|
|
|
mobileOpen ? (
|
|
|
|
<Gridicon icon="cross-small" />
|
|
|
|
) : (
|
|
|
|
<ActivityPanelToggleBubble hasUnread={ hasUnread } />
|
|
|
|
)
|
2018-07-13 19:31:58 +00:00
|
|
|
}
|
2019-03-13 17:14:02 +00:00
|
|
|
label={ mobileOpen ? __( 'Close Activity Panel', 'woocommerce-admin' ) : viewLabel }
|
2018-07-13 19:31:58 +00:00
|
|
|
aria-expanded={ mobileOpen }
|
|
|
|
tooltip={ false }
|
|
|
|
className="woocommerce-layout__activity-panel-mobile-toggle"
|
|
|
|
/>
|
|
|
|
<div className={ panelClasses }>
|
2018-07-13 20:28:01 +00:00
|
|
|
<NavigableMenu
|
|
|
|
role="tablist"
|
|
|
|
orientation="horizontal"
|
|
|
|
className="woocommerce-layout__activity-panel-tabs"
|
|
|
|
>
|
2018-07-13 19:31:58 +00:00
|
|
|
{ tabs && tabs.map( this.renderTab ) }
|
|
|
|
<WordPressNotices
|
|
|
|
showNotices={ 'wpnotices' === currentTab }
|
|
|
|
togglePanel={ this.togglePanel }
|
2018-07-18 15:20:00 +00:00
|
|
|
onCountUpdate={ this.updateNoticeFlag }
|
2018-07-13 19:31:58 +00:00
|
|
|
/>
|
2018-07-13 20:28:01 +00:00
|
|
|
</NavigableMenu>
|
2018-07-13 19:31:58 +00:00
|
|
|
{ this.renderPanel() }
|
2018-06-29 15:20:08 +00:00
|
|
|
</div>
|
2018-07-13 19:31:58 +00:00
|
|
|
</Section>
|
2018-07-06 12:40:05 +00:00
|
|
|
</div>
|
2018-06-28 13:52:45 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-19 10:57:11 +00:00
|
|
|
export default withSelect( select => {
|
2019-03-21 02:54:36 +00:00
|
|
|
const {
|
|
|
|
getCurrentUserData,
|
|
|
|
getNotes,
|
|
|
|
getNotesError,
|
|
|
|
getReportItems,
|
|
|
|
getReportItemsError,
|
|
|
|
isGetNotesRequesting,
|
|
|
|
isReportItemsRequesting,
|
2019-03-21 11:35:46 +00:00
|
|
|
getReviews,
|
|
|
|
getReviewsTotalCount,
|
|
|
|
getReviewsError,
|
|
|
|
isGetReviewsRequesting,
|
2019-03-21 02:54:36 +00:00
|
|
|
} = select( 'wc-api' );
|
2019-03-21 11:35:46 +00:00
|
|
|
const userData = getCurrentUserData();
|
2019-03-22 08:48:20 +00:00
|
|
|
const orderStatuses =
|
|
|
|
wcSettings.wcAdminSettings.woocommerce_actionable_order_statuses || DEFAULT_ACTIONABLE_STATUSES;
|
2019-03-21 02:54:36 +00:00
|
|
|
|
|
|
|
const notesQuery = {
|
|
|
|
page: 1,
|
|
|
|
per_page: 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
const latestNote = getNotes( notesQuery );
|
|
|
|
const unreadNotes =
|
|
|
|
! Boolean( getNotesError( notesQuery ) ) &&
|
|
|
|
! isGetNotesRequesting( notesQuery ) &&
|
|
|
|
latestNote[ 0 ] &&
|
|
|
|
new Date( latestNote[ 0 ].date_created_gmt ).getTime() >
|
|
|
|
userData.activity_panel_inbox_last_read;
|
2019-03-19 10:57:11 +00:00
|
|
|
|
2019-03-21 11:35:46 +00:00
|
|
|
let unreadOrders = null;
|
2019-03-19 10:57:11 +00:00
|
|
|
if ( ! orderStatuses.length ) {
|
2019-03-21 11:35:46 +00:00
|
|
|
unreadOrders = false;
|
|
|
|
} else {
|
|
|
|
const ordersQuery = {
|
|
|
|
page: 1,
|
|
|
|
per_page: 0,
|
|
|
|
status_is: orderStatuses,
|
|
|
|
};
|
2019-03-19 10:57:11 +00:00
|
|
|
|
2019-03-21 11:35:46 +00:00
|
|
|
const totalOrders = getReportItems( 'orders', ordersQuery ).totalResults;
|
|
|
|
const isOrdersError = Boolean( getReportItemsError( 'orders', ordersQuery ) );
|
|
|
|
const isOrdersRequesting = isReportItemsRequesting( 'orders', ordersQuery );
|
2019-03-19 10:57:11 +00:00
|
|
|
|
2019-03-21 11:35:46 +00:00
|
|
|
if ( ! isOrdersError && ! isOrdersRequesting ) {
|
|
|
|
if ( totalOrders > 0 ) {
|
|
|
|
unreadOrders = true;
|
|
|
|
} else {
|
|
|
|
unreadOrders = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-19 10:57:11 +00:00
|
|
|
|
2019-03-21 11:35:46 +00:00
|
|
|
let numberOfReviews = null;
|
|
|
|
let unreadReviews = false;
|
|
|
|
if ( 'yes' === wcSettings.reviewsEnabled ) {
|
|
|
|
const reviewsQuery = {
|
|
|
|
order: 'desc',
|
|
|
|
orderby: 'date_gmt',
|
|
|
|
page: 1,
|
|
|
|
per_page: 1,
|
|
|
|
};
|
|
|
|
const reviews = getReviews( reviewsQuery );
|
|
|
|
const totalReviews = getReviewsTotalCount( reviewsQuery );
|
|
|
|
const isReviewsError = Boolean( getReviewsError( reviewsQuery ) );
|
|
|
|
const isReviewsRequesting = isGetReviewsRequesting( reviewsQuery );
|
2019-03-19 10:57:11 +00:00
|
|
|
|
2019-03-21 11:35:46 +00:00
|
|
|
if ( ! isReviewsError && ! isReviewsRequesting ) {
|
|
|
|
numberOfReviews = totalReviews;
|
|
|
|
unreadReviews =
|
|
|
|
reviews.length &&
|
|
|
|
reviews[ 0 ].date_created_gmt &&
|
|
|
|
new Date( reviews[ 0 ].date_created_gmt + 'Z' ).getTime() >
|
|
|
|
userData.activity_panel_reviews_last_read;
|
2019-03-19 10:57:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-21 11:35:46 +00:00
|
|
|
return { unreadNotes, unreadOrders, unreadReviews, numberOfReviews };
|
2019-03-19 10:57:11 +00:00
|
|
|
} )( clickOutside( ActivityPanel ) );
|