Merge Commit
This commit is contained in:
commit
0a562e6e37
|
@ -5,16 +5,13 @@
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Component, Fragment } from '@wordpress/element';
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
import { compose } from '@wordpress/compose';
|
import { compose } from '@wordpress/compose';
|
||||||
import { Button } from '@wordpress/components';
|
import { withSelect } from '@wordpress/data';
|
||||||
import { withSelect, withDispatch } from '@wordpress/data';
|
import { map } from 'lodash';
|
||||||
import moment from 'moment';
|
|
||||||
import { map, partial } from 'lodash';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
Card,
|
|
||||||
ReportFilters,
|
ReportFilters,
|
||||||
SummaryList,
|
SummaryList,
|
||||||
SummaryListPlaceholder,
|
SummaryListPlaceholder,
|
||||||
|
@ -26,6 +23,7 @@ import { getNewPath } from 'lib/nav-utils';
|
||||||
import { getReportChartData } from 'store/reports/utils';
|
import { getReportChartData } from 'store/reports/utils';
|
||||||
import { getCurrentDates, getDateParamsFromQuery, getIntervalForQuery } from 'lib/date';
|
import { getCurrentDates, getDateParamsFromQuery, getIntervalForQuery } from 'lib/date';
|
||||||
import { MAX_PER_PAGE } from 'store/constants';
|
import { MAX_PER_PAGE } from 'store/constants';
|
||||||
|
import OrdersReportTable from './table';
|
||||||
|
|
||||||
class OrdersReport extends Component {
|
class OrdersReport extends Component {
|
||||||
constructor( props ) {
|
constructor( props ) {
|
||||||
|
@ -35,8 +33,6 @@ class OrdersReport extends Component {
|
||||||
primaryTotals: null,
|
primaryTotals: null,
|
||||||
secondaryTotals: null,
|
secondaryTotals: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.toggleStatus = this.toggleStatus.bind( this );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCharts() {
|
getCharts() {
|
||||||
|
@ -76,14 +72,6 @@ class OrdersReport extends Component {
|
||||||
return charts[ 0 ];
|
return charts[ 0 ];
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleStatus( order ) {
|
|
||||||
const { requestUpdateOrder } = this.props;
|
|
||||||
const updatedOrder = { ...order };
|
|
||||||
const status = updatedOrder.status === 'completed' ? 'processing' : 'completed';
|
|
||||||
updatedOrder.status = status;
|
|
||||||
requestUpdateOrder( updatedOrder );
|
|
||||||
}
|
|
||||||
|
|
||||||
renderChartSummaryNumbers() {
|
renderChartSummaryNumbers() {
|
||||||
const selectedChart = this.getSelectedChart();
|
const selectedChart = this.getSelectedChart();
|
||||||
const charts = this.getCharts();
|
const charts = this.getCharts();
|
||||||
|
@ -109,7 +97,6 @@ class OrdersReport extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ( type ) {
|
switch ( type ) {
|
||||||
// TODO: implement other format handlers
|
|
||||||
case 'currency':
|
case 'currency':
|
||||||
value = formatCurrency( value );
|
value = formatCurrency( value );
|
||||||
secondaryValue = secondaryValue && formatCurrency( secondaryValue );
|
secondaryValue = secondaryValue && formatCurrency( secondaryValue );
|
||||||
|
@ -144,7 +131,8 @@ class OrdersReport extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { orders, orderIds, query, path } = this.props;
|
const { isRequesting, orders, path, query } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ReportFilters
|
<ReportFilters
|
||||||
|
@ -154,39 +142,7 @@ class OrdersReport extends Component {
|
||||||
advancedConfig={ advancedFilterConfig }
|
advancedConfig={ advancedFilterConfig }
|
||||||
/>
|
/>
|
||||||
{ this.renderChartSummaryNumbers() }
|
{ this.renderChartSummaryNumbers() }
|
||||||
<p>Below is a temporary example</p>
|
<OrdersReportTable isRequesting={ isRequesting } orders={ orders } query={ query } />
|
||||||
<Card title="Orders">
|
|
||||||
<table style={ { width: '100%' } }>
|
|
||||||
<thead>
|
|
||||||
<tr style={ { textAlign: 'left' } }>
|
|
||||||
<th>Id</th>
|
|
||||||
<th>Date</th>
|
|
||||||
<th>Total</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Action</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{ orderIds &&
|
|
||||||
orderIds.map( id => {
|
|
||||||
const order = orders[ id ];
|
|
||||||
return (
|
|
||||||
<tr key={ id }>
|
|
||||||
<td>{ id }</td>
|
|
||||||
<td>{ moment( order.date_created ).format( 'LL' ) }</td>
|
|
||||||
<td>{ order.total }</td>
|
|
||||||
<td>{ order.status }</td>
|
|
||||||
<td>
|
|
||||||
<Button isPrimary onClick={ partial( this.toggleStatus, order ) }>
|
|
||||||
Toggle Status
|
|
||||||
</Button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
} ) }
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</Card>
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -194,7 +150,9 @@ class OrdersReport extends Component {
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
withSelect( ( select, props ) => {
|
withSelect( ( select, props ) => {
|
||||||
const { getOrders, getOrderIds } = select( 'wc-admin' );
|
const { getOrders } = select( 'wc-admin' );
|
||||||
|
const orders = getOrders();
|
||||||
|
const isRequesting = select( 'core/data' ).isResolving( 'wc-admin', 'getOrders' );
|
||||||
const { query } = props;
|
const { query } = props;
|
||||||
const interval = getIntervalForQuery( query );
|
const interval = getIntervalForQuery( query );
|
||||||
const datesFromQuery = getCurrentDates( query );
|
const datesFromQuery = getCurrentDates( query );
|
||||||
|
@ -224,17 +182,10 @@ export default compose(
|
||||||
select
|
select
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
orders: getOrders(),
|
isRequesting,
|
||||||
orderIds: getOrderIds(),
|
orders,
|
||||||
primaryData,
|
primaryData,
|
||||||
secondaryData,
|
secondaryData,
|
||||||
};
|
};
|
||||||
} ),
|
|
||||||
withDispatch( dispatch => {
|
|
||||||
return {
|
|
||||||
requestUpdateOrder: function( updatedOrder ) {
|
|
||||||
dispatch( 'wc-admin' ).requestUpdateOrder( updatedOrder );
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} )
|
} )
|
||||||
)( OrdersReport );
|
)( OrdersReport );
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
|
import { format as formatDate } from '@wordpress/date';
|
||||||
|
import { map, orderBy } from 'lodash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { Card, OrderStatus, TableCard, TablePlaceholder } from '@woocommerce/components';
|
||||||
|
import { downloadCSVFile, generateCSVDataFromTable, generateCSVFileName } from 'lib/csv';
|
||||||
|
import { formatCurrency, getCurrencyFormatDecimal } from 'lib/currency';
|
||||||
|
import { getIntervalForQuery, getDateFormatsForInterval } from 'lib/date';
|
||||||
|
import { getAdminLink, onQueryChange } from 'lib/nav-utils';
|
||||||
|
|
||||||
|
export default class OrdersReportTable extends Component {
|
||||||
|
constructor( props ) {
|
||||||
|
super( props );
|
||||||
|
}
|
||||||
|
|
||||||
|
onDownload( headers, rows, query ) {
|
||||||
|
// @TODO The current implementation only downloads the contents displayed in the table.
|
||||||
|
// Another solution is required when the data set is larger (see #311).
|
||||||
|
return () => {
|
||||||
|
downloadCSVFile(
|
||||||
|
generateCSVFileName( 'orders', query ),
|
||||||
|
generateCSVDataFromTable( headers, rows )
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeadersContent() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: __( 'Date', 'wc-admin' ),
|
||||||
|
key: 'date_created',
|
||||||
|
required: true,
|
||||||
|
defaultSort: true,
|
||||||
|
isLeftAligned: true,
|
||||||
|
isSortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __( 'Order #', 'wc-admin' ),
|
||||||
|
key: 'id',
|
||||||
|
required: true,
|
||||||
|
isLeftAligned: true,
|
||||||
|
isSortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __( 'Status', 'wc-admin' ),
|
||||||
|
key: 'status',
|
||||||
|
required: false,
|
||||||
|
isSortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __( 'Customer', 'wc-admin' ),
|
||||||
|
key: 'customer_id',
|
||||||
|
required: false,
|
||||||
|
isSortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __( 'Product(s)', 'wc-admin' ),
|
||||||
|
key: 'products',
|
||||||
|
required: false,
|
||||||
|
isSortable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __( 'Items Sold', 'wc-admin' ),
|
||||||
|
key: 'items_sold',
|
||||||
|
required: false,
|
||||||
|
isSortable: true,
|
||||||
|
isNumeric: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __( 'Coupon(s)', 'wc-admin' ),
|
||||||
|
key: 'coupons',
|
||||||
|
required: false,
|
||||||
|
isSortable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __( 'N. Revenue', 'wc-admin' ),
|
||||||
|
key: 'net_revenue',
|
||||||
|
required: true,
|
||||||
|
isSortable: true,
|
||||||
|
isNumeric: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
formatTableData( data ) {
|
||||||
|
return map( data, row => {
|
||||||
|
const {
|
||||||
|
date_created,
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
customer_id,
|
||||||
|
line_items,
|
||||||
|
coupon_lines,
|
||||||
|
currency,
|
||||||
|
total,
|
||||||
|
total_tax,
|
||||||
|
shipping_total,
|
||||||
|
discount_total,
|
||||||
|
} = row;
|
||||||
|
|
||||||
|
return {
|
||||||
|
date_created,
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
customer_id,
|
||||||
|
line_items,
|
||||||
|
items_sold: line_items.reduce( ( acc, item ) => item.quantity + acc, 0 ),
|
||||||
|
coupon_lines,
|
||||||
|
currency,
|
||||||
|
net_revenue: getCurrencyFormatDecimal(
|
||||||
|
total - total_tax - shipping_total - discount_total
|
||||||
|
),
|
||||||
|
};
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
getRowsContent( tableData ) {
|
||||||
|
const { query } = this.props;
|
||||||
|
const currentInterval = getIntervalForQuery( query );
|
||||||
|
const { tableFormat } = getDateFormatsForInterval( currentInterval );
|
||||||
|
|
||||||
|
return map( tableData, row => {
|
||||||
|
const {
|
||||||
|
date_created,
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
customer_id,
|
||||||
|
line_items,
|
||||||
|
items_sold,
|
||||||
|
coupon_lines,
|
||||||
|
currency,
|
||||||
|
net_revenue,
|
||||||
|
} = row;
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
display: formatDate( tableFormat, date_created ),
|
||||||
|
value: date_created,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: <a href={ getAdminLink( 'post.php?post=' + id + '&action=edit' ) }>{ id }</a>,
|
||||||
|
value: id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: <OrderStatus order={ { status } } />,
|
||||||
|
value: status,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// @TODO This should display customer type (new/returning) once it's
|
||||||
|
// implemented in the API.
|
||||||
|
display: customer_id,
|
||||||
|
value: customer_id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: this.renderList(
|
||||||
|
line_items.map( item => ( {
|
||||||
|
href: getAdminLink( 'post.php?post=' + item.product_id + '&action=edit' ),
|
||||||
|
label: item.name,
|
||||||
|
} ) )
|
||||||
|
),
|
||||||
|
value: line_items
|
||||||
|
.map( item => item.name )
|
||||||
|
.join()
|
||||||
|
.toLowerCase(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: items_sold,
|
||||||
|
value: items_sold,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: this.renderList(
|
||||||
|
coupon_lines.map( coupon => ( {
|
||||||
|
// @TODO It should link to the coupons report.
|
||||||
|
href: getAdminLink( 'edit.php?s=' + coupon.code + '&post_type=shop_coupon' ),
|
||||||
|
label: coupon.code,
|
||||||
|
} ) )
|
||||||
|
),
|
||||||
|
value: coupon_lines
|
||||||
|
.map( item => item.code )
|
||||||
|
.join()
|
||||||
|
.toLowerCase(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: formatCurrency( net_revenue, currency ),
|
||||||
|
value: net_revenue,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
renderList( items ) {
|
||||||
|
// @TODO Use ViewMore component if there are many items.
|
||||||
|
return items.map( ( item, i ) => (
|
||||||
|
<Fragment key={ i }>
|
||||||
|
{ i > 0 ? ', ' : null }
|
||||||
|
<a className={ items.length > 1 ? 'is-inline' : null } href={ item.href }>
|
||||||
|
{ item.label }
|
||||||
|
</a>
|
||||||
|
</Fragment>
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPlaceholderTable() {
|
||||||
|
const headers = this.getHeadersContent();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
title={ __( 'Orders', 'wc-admin' ) }
|
||||||
|
className="woocommerce-analytics__table-placeholder"
|
||||||
|
>
|
||||||
|
<TablePlaceholder caption={ __( 'Orders last week', 'wc-admin' ) } headers={ headers } />
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTable() {
|
||||||
|
const { orders, query } = this.props;
|
||||||
|
|
||||||
|
const page = parseInt( query.page ) || 1;
|
||||||
|
const rowsPerPage = parseInt( query.per_page ) || 25;
|
||||||
|
const rows = this.getRowsContent(
|
||||||
|
orderBy(
|
||||||
|
this.formatTableData( orders ),
|
||||||
|
query.orderby || 'date_created',
|
||||||
|
query.order || 'asc'
|
||||||
|
).slice( ( page - 1 ) * rowsPerPage, page * rowsPerPage )
|
||||||
|
);
|
||||||
|
|
||||||
|
const headers = this.getHeadersContent();
|
||||||
|
|
||||||
|
const tableQuery = {
|
||||||
|
...query,
|
||||||
|
orderby: query.orderby || 'date_created',
|
||||||
|
order: query.order || 'asc',
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableCard
|
||||||
|
title={ __( 'Orders last week', 'wc-admin' ) }
|
||||||
|
rows={ rows }
|
||||||
|
totalRows={ Object.keys( orders ).length }
|
||||||
|
rowsPerPage={ rowsPerPage }
|
||||||
|
headers={ headers }
|
||||||
|
onClickDownload={ this.onDownload( headers, rows, tableQuery ) }
|
||||||
|
onQueryChange={ onQueryChange }
|
||||||
|
query={ tableQuery }
|
||||||
|
summary={ null }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isRequesting } = this.props;
|
||||||
|
|
||||||
|
return isRequesting ? this.renderPlaceholderTable() : this.renderTable();
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ export default class extends Component {
|
||||||
label: __( 'Product Title', 'wc-admin' ),
|
label: __( 'Product Title', 'wc-admin' ),
|
||||||
key: 'name',
|
key: 'name',
|
||||||
required: true,
|
required: true,
|
||||||
|
isLeftAligned: true,
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -93,6 +93,7 @@ export class RevenueReport extends Component {
|
||||||
key: 'date',
|
key: 'date',
|
||||||
required: true,
|
required: true,
|
||||||
defaultSort: true,
|
defaultSort: true,
|
||||||
|
isLeftAligned: true,
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -100,6 +101,7 @@ export class RevenueReport extends Component {
|
||||||
key: 'orders_count',
|
key: 'orders_count',
|
||||||
required: false,
|
required: false,
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
|
isNumeric: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: __( 'Gross Revenue', 'wc-admin' ),
|
label: __( 'Gross Revenue', 'wc-admin' ),
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: 2px solid lightseagreen;
|
box-shadow: inset 0 -1px 0 $button-focus-inner, 0 0 0 2px $button-focus-outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,9 +58,11 @@
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: block;
|
&:not(.is-inline) {
|
||||||
margin: ($gap*-1) ($gap-large*-1);
|
display: block;
|
||||||
padding: $gap $gap-large;
|
margin: ($gap*-1) ($gap-large*-1);
|
||||||
|
padding: $gap $gap-large;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
|
@ -80,21 +82,28 @@
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-numeric {
|
&:not(.is-left-aligned) {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
||||||
|
.rtl & {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.is-placeholder {
|
&.is-numeric .is-placeholder {
|
||||||
max-width: 40px;
|
max-width: 40px;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-table__header,
|
|
||||||
th.woocommerce-table__item {
|
th.woocommerce-table__item {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-table__header {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
@ -106,6 +115,11 @@ th.woocommerce-table__item {
|
||||||
|
|
||||||
& + .woocommerce-table__header {
|
& + .woocommerce-table__header {
|
||||||
border-left: 1px solid $core-grey-light-700;
|
border-left: 1px solid $core-grey-light-700;
|
||||||
|
|
||||||
|
.rtl & {
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 1px solid $core-grey-light-700;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.components-button.is-button {
|
.components-button.is-button {
|
||||||
|
@ -118,6 +132,10 @@ th.woocommerce-table__item {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
|
|
||||||
|
.rtl & {
|
||||||
|
padding: $gap-smaller 0 $gap-smaller $gap-large;
|
||||||
|
}
|
||||||
|
|
||||||
// @todo Add interactive styles
|
// @todo Add interactive styles
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
|
|
|
@ -120,10 +120,11 @@ class Table extends Component {
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
{ headers.map( ( header, i ) => {
|
{ headers.map( ( header, i ) => {
|
||||||
const { isSortable, isNumeric, key, label } = header;
|
const { isLeftAligned, isSortable, isNumeric, key, label } = header;
|
||||||
const labelId = `header-${ instanceId } -${ i }`;
|
const labelId = `header-${ instanceId } -${ i }`;
|
||||||
const thProps = {
|
const thProps = {
|
||||||
className: classnames( 'woocommerce-table__header', {
|
className: classnames( 'woocommerce-table__header', {
|
||||||
|
'is-left-aligned': isLeftAligned,
|
||||||
'is-sortable': isSortable,
|
'is-sortable': isSortable,
|
||||||
'is-sorted': sortedBy === key,
|
'is-sorted': sortedBy === key,
|
||||||
'is-numeric': isNumeric,
|
'is-numeric': isNumeric,
|
||||||
|
@ -173,10 +174,11 @@ class Table extends Component {
|
||||||
{ rows.map( ( row, i ) => (
|
{ rows.map( ( row, i ) => (
|
||||||
<tr key={ i }>
|
<tr key={ i }>
|
||||||
{ row.map( ( cell, j ) => {
|
{ row.map( ( cell, j ) => {
|
||||||
const { isNumeric } = headers[ j ];
|
const { isLeftAligned, isNumeric } = headers[ j ];
|
||||||
const isHeader = rowHeader === j;
|
const isHeader = rowHeader === j;
|
||||||
const Cell = isHeader ? 'th' : 'td';
|
const Cell = isHeader ? 'th' : 'td';
|
||||||
const cellClasses = classnames( 'woocommerce-table__item', {
|
const cellClasses = classnames( 'woocommerce-table__item', {
|
||||||
|
'is-left-aligned': isLeftAligned,
|
||||||
'is-numeric': isNumeric,
|
'is-numeric': isNumeric,
|
||||||
} );
|
} );
|
||||||
return (
|
return (
|
||||||
|
@ -217,6 +219,10 @@ Table.propTypes = {
|
||||||
* Boolean, true if this column is the default for sorting. Only one column should have this set.
|
* Boolean, true if this column is the default for sorting. Only one column should have this set.
|
||||||
*/
|
*/
|
||||||
defaultSort: PropTypes.bool,
|
defaultSort: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Boolean, true if this column should be aligned to the left.
|
||||||
|
*/
|
||||||
|
isLeftAligned: PropTypes.bool,
|
||||||
/**
|
/**
|
||||||
* Boolean, true if this column is a number value.
|
* Boolean, true if this column is a number value.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -25,6 +25,7 @@ export class TopSellingProducts extends Component {
|
||||||
label: __( 'Product', 'wc-admin' ),
|
label: __( 'Product', 'wc-admin' ),
|
||||||
key: 'product',
|
key: 'product',
|
||||||
required: true,
|
required: true,
|
||||||
|
isLeftAligned: true,
|
||||||
isSortable: false,
|
isSortable: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/** @format */
|
/** @format */
|
||||||
|
|
||||||
export default `Date,Orders,Gross Revenue,Refunds,Coupons,Taxes,Shipping,Net Revenue
|
export default `Date,Orders,Description,Gross Revenue,Refunds,Coupons,Taxes,Shipping,Net Revenue
|
||||||
2018-04-29T00:00:00,30,200,19,19,100,19,200`;
|
2018-04-29T00:00:00,30,lorem ipsum,200,19,19,100,19,200`;
|
||||||
|
|
|
@ -9,6 +9,10 @@ export default [
|
||||||
label: 'Orders',
|
label: 'Orders',
|
||||||
key: 'orders_count',
|
key: 'orders_count',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Description',
|
||||||
|
key: 'description',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Gross Revenue',
|
label: 'Gross Revenue',
|
||||||
key: 'gross_revenue',
|
key: 'gross_revenue',
|
||||||
|
|
|
@ -10,6 +10,10 @@ export default [
|
||||||
display: 'Product 30',
|
display: 'Product 30',
|
||||||
value: '30',
|
value: '30',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
display: 'Lorem, ipsum',
|
||||||
|
value: 'lorem, ipsum',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
display: '€200.00',
|
display: '€200.00',
|
||||||
value: 200,
|
value: 200,
|
||||||
|
|
|
@ -11,7 +11,11 @@ function getCSVHeaders( headers ) {
|
||||||
|
|
||||||
function getCSVRows( rows ) {
|
function getCSVRows( rows ) {
|
||||||
return Array.isArray( rows )
|
return Array.isArray( rows )
|
||||||
? rows.map( row => row.map( rowItem => rowItem.value ).join( ',' ) ).join( '\n' )
|
? rows
|
||||||
|
.map( row =>
|
||||||
|
row.map( rowItem => rowItem.value.toString().replace( /,/g, '' ) ).join( ',' )
|
||||||
|
)
|
||||||
|
.join( '\n' )
|
||||||
: [];
|
: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/** @format */
|
/** @format */
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { union } from 'lodash';
|
|
||||||
|
|
||||||
const DEFAULT_STATE = {
|
const DEFAULT_STATE = {
|
||||||
orders: {},
|
orders: {},
|
||||||
|
@ -13,7 +9,6 @@ export default function ordersReducer( state = DEFAULT_STATE, action ) {
|
||||||
switch ( action.type ) {
|
switch ( action.type ) {
|
||||||
case 'SET_ORDERS':
|
case 'SET_ORDERS':
|
||||||
const { orders } = action;
|
const { orders } = action;
|
||||||
const ids = orders.map( order => order.id );
|
|
||||||
const ordersMap = orders.reduce( ( map, order ) => {
|
const ordersMap = orders.reduce( ( map, order ) => {
|
||||||
map[ order.id ] = order;
|
map[ order.id ] = order;
|
||||||
return map;
|
return map;
|
||||||
|
@ -21,7 +16,6 @@ export default function ordersReducer( state = DEFAULT_STATE, action ) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
orders: Object.assign( {}, state.orders, ordersMap ),
|
orders: Object.assign( {}, state.orders, ordersMap ),
|
||||||
ids: union( state.ids, ids ),
|
|
||||||
};
|
};
|
||||||
case 'UPDATE_ORDER':
|
case 'UPDATE_ORDER':
|
||||||
const updatedOrders = { ...state.orders };
|
const updatedOrders = { ...state.orders };
|
||||||
|
|
|
@ -4,7 +4,4 @@ export default {
|
||||||
getOrders( state ) {
|
getOrders( state ) {
|
||||||
return state.orders.orders;
|
return state.orders.orders;
|
||||||
},
|
},
|
||||||
getOrderIds( state ) {
|
|
||||||
return state.orders.ids;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -46,6 +46,8 @@ $woocommerce: $woocommerce-500;
|
||||||
|
|
||||||
// Gutenberg
|
// Gutenberg
|
||||||
$button-hover: #fafafa;
|
$button-hover: #fafafa;
|
||||||
|
$button-focus-inner: #00435d;
|
||||||
|
$button-focus-outer: #bfe7f3;
|
||||||
|
|
||||||
// wp-admin
|
// wp-admin
|
||||||
$wp-admin-background: #f1f1f1;
|
$wp-admin-background: #f1f1f1;
|
||||||
|
|
Loading…
Reference in New Issue