Merge pull request woocommerce/woocommerce-admin#192 from woocommerce/add/filter-picker
Components: Add filter picker
This commit is contained in:
commit
e354bc264c
|
@ -9,18 +9,13 @@ import { Component, Fragment } from '@wordpress/element';
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import Header from 'layout/header';
|
import Header from 'layout/header';
|
||||||
import DatePicker from 'components/date-picker';
|
|
||||||
import DropdownButton from 'components/dropdown-button';
|
|
||||||
|
|
||||||
export default class extends Component {
|
export default class extends Component {
|
||||||
render() {
|
render() {
|
||||||
const { query, path } = this.props;
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Header sections={ [ __( 'Analytics', 'wc-admin' ) ] } />
|
<Header sections={ [ __( 'Analytics', 'wc-admin' ) ] } />
|
||||||
<DatePicker query={ query } path={ path } />
|
<p>Overview Section</p>
|
||||||
<p>Example single line button - default width 100% of container</p>
|
|
||||||
<DropdownButton labels={ [ 'All Products Sold' ] } />
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import PropTypes from 'prop-types';
|
||||||
*/
|
*/
|
||||||
import ExampleReport from './example';
|
import ExampleReport from './example';
|
||||||
import RevenueReport from './revenue';
|
import RevenueReport from './revenue';
|
||||||
|
import ProductsReport from './products';
|
||||||
|
|
||||||
class Report extends Component {
|
class Report extends Component {
|
||||||
render() {
|
render() {
|
||||||
|
@ -17,6 +18,8 @@ class Report extends Component {
|
||||||
switch ( params.report ) {
|
switch ( params.report ) {
|
||||||
case 'revenue':
|
case 'revenue':
|
||||||
return <RevenueReport { ...this.props } />;
|
return <RevenueReport { ...this.props } />;
|
||||||
|
case 'products':
|
||||||
|
return <ProductsReport { ...this.props } />;
|
||||||
default:
|
default:
|
||||||
return <ExampleReport />;
|
return <ExampleReport />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
|
||||||
|
export const filters = [
|
||||||
|
{ label: __( 'All Products', 'wc-admin' ), value: 'all' },
|
||||||
|
{
|
||||||
|
label: __( 'Single Product', 'wc-admin' ),
|
||||||
|
value: 'single',
|
||||||
|
subFilters: [
|
||||||
|
{
|
||||||
|
label: __( 'Single Product', 'wc-admin' ),
|
||||||
|
component: 'Search',
|
||||||
|
value: 'single_search',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ label: __( 'Top Products by Items Sold', 'wc-admin' ), value: 'top_items' },
|
||||||
|
{ label: __( 'Top Products by Gross Sales', 'wc-admin' ), value: 'top_sales' },
|
||||||
|
{ label: __( 'Comparison', 'wc-admin' ), value: 'compare' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const filterPaths = {
|
||||||
|
all: [],
|
||||||
|
single: [],
|
||||||
|
single_search: [ 'single' ],
|
||||||
|
top_items: [],
|
||||||
|
top_sales: [],
|
||||||
|
compare: [],
|
||||||
|
};
|
|
@ -0,0 +1,54 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import Header from 'layout/header';
|
||||||
|
import DatePicker from 'components/date-picker';
|
||||||
|
import FilterPicker from 'components/filter-picker';
|
||||||
|
import { filters, filterPaths } from './constants';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
export default class extends Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.getQueryParamValue = this.getQueryParamValue.bind( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
getQueryParamValue() {
|
||||||
|
const { query } = this.props;
|
||||||
|
return query.product || 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { query, path } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Header
|
||||||
|
sections={ [
|
||||||
|
[ '/analytics', __( 'Analytics', 'wc-admin' ) ],
|
||||||
|
__( 'Products', 'wc-admin' ),
|
||||||
|
] }
|
||||||
|
/>
|
||||||
|
<div className="woocommerce-products__pickers">
|
||||||
|
<DatePicker query={ query } path={ path } />
|
||||||
|
<FilterPicker
|
||||||
|
query={ query }
|
||||||
|
path={ path }
|
||||||
|
filters={ filters }
|
||||||
|
filterPaths={ filterPaths }
|
||||||
|
queryParam="product"
|
||||||
|
getQueryParamValue={ this.getQueryParamValue }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
.woocommerce-products__pickers {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include breakpoint( '<1100px' ) {
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
Date Picker (Work in Progress)
|
Date Picker
|
||||||
===
|
===
|
||||||
|
|
||||||
Select a range of dates or single dates
|
Select a range of dates or single dates
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { Component, Fragment } from '@wordpress/element';
|
import { Component } from '@wordpress/element';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Dropdown } from '@wordpress/components';
|
import { Dropdown } from '@wordpress/components';
|
||||||
import { stringify as stringifyQueryObject } from 'qs';
|
import { stringify as stringifyQueryObject } from 'qs';
|
||||||
|
@ -76,10 +76,9 @@ class DatePicker extends Component {
|
||||||
render() {
|
render() {
|
||||||
const { period, compare, after, before } = this.state;
|
const { period, compare, after, before } = this.state;
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<div className="woocommerce-date-picker">
|
||||||
<p>{ __( 'Date Range', 'wc-admin' ) }:</p>
|
<p>{ __( 'Date Range', 'wc-admin' ) }:</p>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className="woocommerce-date-picker"
|
|
||||||
contentClassName="woocommerce-date-picker__content"
|
contentClassName="woocommerce-date-picker__content"
|
||||||
position="bottom"
|
position="bottom"
|
||||||
expandOnMobile
|
expandOnMobile
|
||||||
|
@ -103,7 +102,7 @@ class DatePicker extends Component {
|
||||||
/>
|
/>
|
||||||
) }
|
) }
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
Filter Picker
|
||||||
|
===
|
||||||
|
|
||||||
|
Modify a url query parameter via a dropdown selection of configurable options
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import FilterPicker from 'components/filter-picker';
|
||||||
|
|
||||||
|
const renderFilterPicker = ( { path, query } ) {
|
||||||
|
const filters = [
|
||||||
|
{ label: 'Breakfast', value: 'breakfast' },
|
||||||
|
{ label: 'Lunch', value: 'lunch', subFilters: [
|
||||||
|
{ label: 'Meat', value: 'meat' },
|
||||||
|
{ label: 'Vegan', value: 'vegan' },
|
||||||
|
{ label: 'Pescatarian', value: 'fish', subFilters: [
|
||||||
|
{ label: 'Snapper', value: 'snapper' },
|
||||||
|
{ label: 'Cod', value: 'cod' },
|
||||||
|
] },
|
||||||
|
// Specify a custom component to render (Work in Progress)
|
||||||
|
{ label: 'Other', value: 'other_fish', component: 'OtherFish' },
|
||||||
|
] },
|
||||||
|
{ label: 'Dinner', value: 'dinner' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const filterPaths = {
|
||||||
|
breakfast: [],
|
||||||
|
lunch: [],
|
||||||
|
dinner: [],
|
||||||
|
meat: [ 'lunch' ],
|
||||||
|
vegan: [ 'lunch' ],
|
||||||
|
fish: [ 'lunch' ],
|
||||||
|
snapper: [ 'lunch', 'fish' ],
|
||||||
|
cod: [ 'lunch', 'fish' ],
|
||||||
|
other_fish: [ 'lunch', 'fish' ],
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryParam = 'meal';
|
||||||
|
|
||||||
|
const getQueryParamValue = () => {
|
||||||
|
const { query } = this.props;
|
||||||
|
return return query[ queryParam ] || 'breakfast';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FilterPicker
|
||||||
|
query={ query }
|
||||||
|
path={ path }
|
||||||
|
filters={ filters }
|
||||||
|
filterPaths={ filterPaths }
|
||||||
|
queryParam={ queryParam }
|
||||||
|
getQueryParamValue={ getQueryParamValue }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
* `query` (required): The query string represented in object form
|
||||||
|
* `path` (required): Parameter supplied by React-Router
|
||||||
|
* `filters` (required): An array of filters and subFilters to construct the menu
|
||||||
|
* `filterPaths` (required): A map of representing the structure of the tree. Required for faster lookups than searches
|
||||||
|
* `queryParam` (required): The query parameter to update
|
||||||
|
* `getQueryParamValue` (required): A function used to obtain the current value represented in the url
|
|
@ -0,0 +1,187 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Component, Fragment, createRef } from '@wordpress/element';
|
||||||
|
import { Dropdown, Button, Dashicon } from '@wordpress/components';
|
||||||
|
import { stringify as stringifyQueryObject } from 'qs';
|
||||||
|
import { omit, find, partial } from 'lodash';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import DropdownButton from 'components/dropdown-button';
|
||||||
|
import Link from 'components/link';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
class FilterPicker extends Component {
|
||||||
|
constructor( props ) {
|
||||||
|
super( props );
|
||||||
|
|
||||||
|
const { filterPaths, getQueryParamValue } = props;
|
||||||
|
this.state = {
|
||||||
|
nav: filterPaths[ getQueryParamValue() ],
|
||||||
|
};
|
||||||
|
this.listRef = createRef();
|
||||||
|
|
||||||
|
this.getSelectionPath = this.getSelectionPath.bind( this );
|
||||||
|
this.getOtherQueries = this.getOtherQueries.bind( this );
|
||||||
|
this.getSelectedFilter = this.getSelectedFilter.bind( this );
|
||||||
|
this.selectSubFilters = this.selectSubFilters.bind( this );
|
||||||
|
this.getVisibleFilters = this.getVisibleFilters.bind( this );
|
||||||
|
this.goBack = this.goBack.bind( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
getOtherQueries( query ) {
|
||||||
|
const { queryParam } = this.props;
|
||||||
|
return omit( query, queryParam );
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectionPath( filter ) {
|
||||||
|
const { path, query, queryParam } = this.props;
|
||||||
|
const otherQueries = this.getOtherQueries( query );
|
||||||
|
const data = {
|
||||||
|
[ queryParam ]: filter.value,
|
||||||
|
};
|
||||||
|
const queryString = stringifyQueryObject( Object.assign( otherQueries, data ) );
|
||||||
|
return `${ path }?${ queryString }`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedFilter() {
|
||||||
|
const { filters, getQueryParamValue, filterPaths } = this.props;
|
||||||
|
const value = getQueryParamValue();
|
||||||
|
const filterPath = filterPaths[ value ];
|
||||||
|
const visibleFilters = this.getVisibleFilters( filters, [ ...filterPath ] );
|
||||||
|
return find( visibleFilters, filter => filter.value === value );
|
||||||
|
}
|
||||||
|
|
||||||
|
getLabels( selectedFilter ) {
|
||||||
|
// @TODO: handle single product secondary labels
|
||||||
|
return selectedFilter ? [ selectedFilter.label ] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
selectSubFilters( value ) {
|
||||||
|
const nav = [ ...this.state.nav ];
|
||||||
|
nav.push( value );
|
||||||
|
this.setState( { nav } );
|
||||||
|
this.focusFirstFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
getVisibleFilters( filters, nav ) {
|
||||||
|
if ( nav.length === 0 ) {
|
||||||
|
return filters;
|
||||||
|
}
|
||||||
|
const value = nav.shift();
|
||||||
|
const nextFilters = find( filters, filter => value === filter.value );
|
||||||
|
return this.getVisibleFilters( nextFilters && nextFilters.subFilters, nav );
|
||||||
|
}
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
const nav = [ ...this.state.nav ];
|
||||||
|
nav.pop();
|
||||||
|
this.setState( { nav } );
|
||||||
|
this.focusFirstFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
focusFirstFilter() {
|
||||||
|
setTimeout( () => {
|
||||||
|
const list = this.listRef.current;
|
||||||
|
if ( list.children.length && list.children[ 0 ].children.length ) {
|
||||||
|
list.children[ 0 ].children[ 0 ].focus();
|
||||||
|
}
|
||||||
|
}, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
renderButton( filter, onClose ) {
|
||||||
|
if ( filter.subFilters ) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
className="woocommerce-filter-picker__content-list-item-btn"
|
||||||
|
onClick={ partial( this.selectSubFilters, filter.value ) }
|
||||||
|
>
|
||||||
|
{ filter.label }
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( filter.component ) {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Button
|
||||||
|
className="woocommerce-filter-picker__content-list-item-btn has-parent-nav"
|
||||||
|
onClick={ this.goBack }
|
||||||
|
>
|
||||||
|
<Dashicon icon="arrow-left-alt2" />
|
||||||
|
{ filter.label }
|
||||||
|
</Button>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
style={ { width: '100%', margin: '0' } }
|
||||||
|
placeholder="Search Placeholder"
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
className="woocommerce-filter-picker__content-list-item-btn components-button"
|
||||||
|
to={ this.getSelectionPath( filter ) }
|
||||||
|
onClick={ onClose }
|
||||||
|
>
|
||||||
|
{ filter.label }
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { filters } = this.props;
|
||||||
|
const visibleFilters = this.getVisibleFilters( filters, [ ...this.state.nav ] );
|
||||||
|
const selectedFilter = this.getSelectedFilter();
|
||||||
|
return (
|
||||||
|
<div className="woocommerce-filter-picker">
|
||||||
|
<p>{ __( 'Show', 'wc-admin' ) }:</p>
|
||||||
|
<Dropdown
|
||||||
|
contentClassName="woocommerce-filter-picker__content"
|
||||||
|
position="bottom"
|
||||||
|
expandOnMobile
|
||||||
|
headerTitle={ __( 'filter report to show:', 'wc-admin' ) }
|
||||||
|
renderToggle={ ( { isOpen, onToggle } ) => (
|
||||||
|
<DropdownButton
|
||||||
|
onClick={ onToggle }
|
||||||
|
isOpen={ isOpen }
|
||||||
|
labels={ this.getLabels( selectedFilter ) }
|
||||||
|
/>
|
||||||
|
) }
|
||||||
|
renderContent={ ( { onClose } ) => (
|
||||||
|
<ul className="woocommerce-filter-picker__content-list" ref={ this.listRef }>
|
||||||
|
{ visibleFilters.map( filter => (
|
||||||
|
<li
|
||||||
|
className={ classnames( 'woocommerce-filter-picker__content-list-item', {
|
||||||
|
'is-selected': selectedFilter.value === filter.value,
|
||||||
|
} ) }
|
||||||
|
>
|
||||||
|
{ this.renderButton( filter, onClose ) }
|
||||||
|
</li>
|
||||||
|
) ) }
|
||||||
|
</ul>
|
||||||
|
) }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FilterPicker.propTypes = {
|
||||||
|
path: PropTypes.string.isRequired,
|
||||||
|
query: PropTypes.object.isRequired,
|
||||||
|
filters: PropTypes.array.isRequired,
|
||||||
|
filterPaths: PropTypes.object.isRequired,
|
||||||
|
queryParam: PropTypes.string.isRequired,
|
||||||
|
getQueryParamValue: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FilterPicker;
|
|
@ -0,0 +1,101 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
.woocommerce-filter-picker {
|
||||||
|
width: 33.3%;
|
||||||
|
|
||||||
|
@include breakpoint( '<1100px' ) {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include breakpoint( '<782px' ) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-filter-picker__content {
|
||||||
|
.components-popover__content {
|
||||||
|
width: 320px;
|
||||||
|
border: 1px solid $core-grey-light-700;
|
||||||
|
background-color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-mobile {
|
||||||
|
.components-popover__content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-popover__header-title {
|
||||||
|
@include font-size( 12 );
|
||||||
|
font-weight: 100;
|
||||||
|
text-transform: uppercase;
|
||||||
|
text-align: center;
|
||||||
|
color: $core-grey-dark-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-filter-picker__content-list
|
||||||
|
.woocommerce-filter-picker__content-list-item:last-child {
|
||||||
|
border-bottom: 1px solid $core-grey-light-700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-filter-picker__content-list {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.woocommerce-filter-picker__content-list-item {
|
||||||
|
border-bottom: 1px solid $core-grey-light-700;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-selected {
|
||||||
|
.woocommerce-filter-picker__content-list-item-btn {
|
||||||
|
background-color: $white;
|
||||||
|
|
||||||
|
&.components-button:not(:disabled):not([aria-disabled='true']):focus {
|
||||||
|
background-color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background-color: $woocommerce;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 1em;
|
||||||
|
transform: translate(50%, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-filter-picker__content-list-item-btn {
|
||||||
|
padding: 1em 1em 1em 3em;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
background-color: $core-grey-light-100;
|
||||||
|
color: $core-grey-dark-500;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $core-grey-light-200;
|
||||||
|
color: $core-grey-dark-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.components-button:not(:disabled):not([aria-disabled='true']):focus {
|
||||||
|
background-color: $core-grey-light-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashicon {
|
||||||
|
position: absolute;
|
||||||
|
left: 1em;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,6 +74,15 @@ function wc_admin_register_pages(){
|
||||||
'wc-admin#/analytics/revenue',
|
'wc-admin#/analytics/revenue',
|
||||||
'wc_admin_page'
|
'wc_admin_page'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
add_submenu_page(
|
||||||
|
'wc-admin#/analytics',
|
||||||
|
__( 'Products', 'wc-admin' ),
|
||||||
|
__( 'Products', 'wc-admin' ),
|
||||||
|
'manage_options',
|
||||||
|
'wc-admin#/analytics/products',
|
||||||
|
'wc_admin_page'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
add_action( 'admin_menu', 'wc_admin_register_pages' );
|
add_action( 'admin_menu', 'wc_admin_register_pages' );
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue