Add order tab
[PREMIUM-224]
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
import { CustomerData } from '../../../../store';
|
||||
|
||||
export function CustomerCell({
|
||||
customer,
|
||||
}: {
|
||||
customer: CustomerData;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<a
|
||||
className="mailpoet-analytics-orders__customer"
|
||||
href={`?page=mailpoet-subscribers#/edit/${customer.id}`}
|
||||
>
|
||||
<img src={customer.avatar} alt={customer.last_name} />
|
||||
{`${customer.first_name} ${customer.last_name}`}
|
||||
</a>
|
||||
);
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
import { Tooltip } from '@wordpress/components';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { addQueryArgs } from '@wordpress/url';
|
||||
import { OrderData, storeName } from '../../../../store';
|
||||
import { MailPoet } from '../../../../../../../../mailpoet';
|
||||
|
||||
export function EmailCell({ order }: { order: OrderData }): JSX.Element {
|
||||
const { automation } = useSelect((s) => ({
|
||||
automation: s(storeName).getAutomation(),
|
||||
}));
|
||||
|
||||
return (
|
||||
<Tooltip text={__('View in automation', 'mailpoet')}>
|
||||
<a
|
||||
href={addQueryArgs(MailPoet.urls.automationEditor, {
|
||||
id: automation.id,
|
||||
})}
|
||||
>
|
||||
{`${order.email.subject}`}
|
||||
</a>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
import { Tooltip } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { OrderDetails } from '../../../../store';
|
||||
|
||||
export function OrderCell({ order }: { order: OrderDetails }): JSX.Element {
|
||||
return (
|
||||
<Tooltip text={__('Order details', 'mailpoet')}>
|
||||
<a href={`post.php?post=${order.id}&action=edit`}>{`${order.id}`}</a>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
export function OrderStatusCell({
|
||||
id,
|
||||
name,
|
||||
}: {
|
||||
id: string;
|
||||
name: string;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<span
|
||||
className={`mailpoet-analytics-order-status mailpoet-analytics-order-status-${id}`}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
);
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
import { ViewMoreList } from '@woocommerce/components/build';
|
||||
import { Fragment } from '@wordpress/element';
|
||||
import { OrderDetails } from '../../../../store';
|
||||
|
||||
export function ProductsCell({ order }: { order: OrderDetails }) {
|
||||
const items =
|
||||
order.products.length > 0
|
||||
? order.products.map((item) => (
|
||||
<Fragment key={`key-${item.id}`}>
|
||||
{item.name}
|
||||
<span className="quantity">({item.quantity}×)</span>
|
||||
</Fragment>
|
||||
))
|
||||
: [];
|
||||
|
||||
if (!items.length) {
|
||||
return <span>—</span>;
|
||||
}
|
||||
|
||||
const visibleItem = items.slice(0, 1);
|
||||
|
||||
return (
|
||||
<div className="mailpoet-automations-analytics-order-products">
|
||||
{visibleItem}
|
||||
{items.length > 1 && <ViewMoreList items={items} />}
|
||||
</div>
|
||||
);
|
||||
}
|
@@ -1,3 +1,122 @@
|
||||
import { dispatch, useSelect } from '@wordpress/data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { TableCard } from '@woocommerce/components/build';
|
||||
import { MailPoet } from '../../../../../../../mailpoet';
|
||||
import { OrderSection, storeName } from '../../../store';
|
||||
import { transformOrdersToRows } from './rows';
|
||||
import { calculateSummary } from './summary';
|
||||
import { Upgrade } from './upgrade';
|
||||
|
||||
const headers = [
|
||||
{
|
||||
key: 'created_at',
|
||||
isSortable: true,
|
||||
label: __('Date', 'mailpoet'),
|
||||
},
|
||||
{
|
||||
key: 'id',
|
||||
label: __('Order #', 'mailpoet'),
|
||||
},
|
||||
{
|
||||
key: 'last_name',
|
||||
isSortable: true,
|
||||
label: __('Customer', 'mailpoet'),
|
||||
},
|
||||
{
|
||||
key: 'items',
|
||||
label: __('Product(s)', 'mailpoet'),
|
||||
},
|
||||
{
|
||||
key: 'subject',
|
||||
isSortable: true,
|
||||
label: __('Email clicked', 'mailpoet'),
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
isSortable: true,
|
||||
label: __('Status', 'mailpoet'),
|
||||
},
|
||||
{
|
||||
key: 'revenue',
|
||||
isSortable: true,
|
||||
label: __('Revenue', 'mailpoet'),
|
||||
},
|
||||
];
|
||||
|
||||
export function Orders(): JSX.Element {
|
||||
return <p>Orders</p>;
|
||||
const { ordersSection } = useSelect((s) => ({
|
||||
ordersSection: s(storeName).getSection('orders') as OrderSection,
|
||||
}));
|
||||
|
||||
const orders =
|
||||
ordersSection.data !== undefined ? ordersSection.data.items : undefined;
|
||||
const rows = transformOrdersToRows(orders);
|
||||
const summary = calculateSummary(orders ?? []);
|
||||
|
||||
return (
|
||||
<div className="mailpoet-analytics-orders">
|
||||
{!MailPoet.premiumActive && (
|
||||
<Upgrade
|
||||
text={
|
||||
<span>
|
||||
<strong>{__("You're viewing sample data.", 'mailpoet')}</strong>
|
||||
|
||||
{__(
|
||||
'To use data from your email activity, upgrade to a premium plan.',
|
||||
'mailpoet',
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TableCard
|
||||
title=""
|
||||
caption=""
|
||||
onQueryChange={(type: string) => (param: unknown) => {
|
||||
let customQuery = {};
|
||||
if (type === 'paged') {
|
||||
customQuery = { page: param };
|
||||
} else if (type === 'per_page') {
|
||||
customQuery = {
|
||||
page: 1,
|
||||
limit: param,
|
||||
};
|
||||
} else if (type === 'sort') {
|
||||
customQuery = {
|
||||
page: 1,
|
||||
order_by: param,
|
||||
order:
|
||||
ordersSection.customQuery.order_by === param &&
|
||||
ordersSection.customQuery.order === 'asc'
|
||||
? 'desc'
|
||||
: 'asc',
|
||||
};
|
||||
}
|
||||
dispatch(storeName).updateSection({
|
||||
...ordersSection,
|
||||
customQuery: {
|
||||
...ordersSection.customQuery,
|
||||
...customQuery,
|
||||
},
|
||||
});
|
||||
}}
|
||||
query={{
|
||||
paged: ordersSection.customQuery.page,
|
||||
orderby: ordersSection.customQuery.order_by,
|
||||
order: ordersSection.customQuery.order,
|
||||
}}
|
||||
rows={rows}
|
||||
headers={headers}
|
||||
showMenu={false}
|
||||
rowsPerPage={ordersSection.customQuery.limit}
|
||||
onRowClick={() => {}}
|
||||
totalRows={
|
||||
ordersSection.data !== undefined ? ordersSection.data.results : 0
|
||||
}
|
||||
summary={summary}
|
||||
isLoading={orders === undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -0,0 +1,48 @@
|
||||
import { OrderData } from '../../../store';
|
||||
import { OrderCell } from './cells/order';
|
||||
import { CustomerCell } from './cells/customer';
|
||||
import { ProductsCell } from './cells/products';
|
||||
import { EmailCell } from './cells/email';
|
||||
import { OrderStatusCell } from './cells/order_status';
|
||||
import { formattedPrice } from '../../../formatter';
|
||||
import { MailPoet } from '../../../../../../../mailpoet';
|
||||
|
||||
export function transformOrdersToRows(orders: OrderData[] | undefined) {
|
||||
return orders === undefined
|
||||
? []
|
||||
: orders.map((order) => [
|
||||
{
|
||||
display: MailPoet.Date.format(new Date(order.date)),
|
||||
value: order.date,
|
||||
},
|
||||
{
|
||||
display: <OrderCell order={order.details} />,
|
||||
value: order.details.id,
|
||||
},
|
||||
{
|
||||
display: <CustomerCell customer={order.customer} />,
|
||||
value: order.customer.last_name,
|
||||
},
|
||||
{
|
||||
display: <ProductsCell order={order.details} />,
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
display: <EmailCell order={order} />,
|
||||
value: order.email.subject,
|
||||
},
|
||||
{
|
||||
display: (
|
||||
<OrderStatusCell
|
||||
id={order.details.status.id}
|
||||
name={order.details.status.name}
|
||||
/>
|
||||
),
|
||||
value: order.details.status.id,
|
||||
},
|
||||
{
|
||||
display: formattedPrice(order.details.total),
|
||||
value: order.details.total,
|
||||
},
|
||||
]);
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { OrderData } from '../../../store';
|
||||
import { formattedPrice } from '../../../formatter';
|
||||
|
||||
function calculateDaysPassed(data: OrderData[]): number {
|
||||
const dates = data
|
||||
.map((order) => {
|
||||
const date = new Date(order.date);
|
||||
return date.getTime();
|
||||
})
|
||||
.filter((value, index, self) => self.indexOf(value) === index);
|
||||
|
||||
const latestDate = Math.max.apply(null, dates);
|
||||
const earliestDate = Math.min.apply(null, dates);
|
||||
|
||||
return Math.max.apply(null, [
|
||||
1,
|
||||
Math.round((latestDate - earliestDate) / (1000 * 3600 * 24)),
|
||||
]) as number;
|
||||
}
|
||||
|
||||
export function calculateSummary(data: OrderData[]) {
|
||||
const subscribers = data
|
||||
.map((order) => order.customer.id)
|
||||
.filter((value, index, self) => self.indexOf(value) === index).length;
|
||||
|
||||
const products = data
|
||||
.map((order) => order.details.products.map((item) => item.id))
|
||||
.flat()
|
||||
.filter((value, index, self) => self.indexOf(value) === index).length;
|
||||
|
||||
const revenue = data
|
||||
.map((order) => order.details.total)
|
||||
.reduce((a, b) => a + b, 0);
|
||||
return [
|
||||
{
|
||||
label: __('days', 'mailpoet'),
|
||||
value: calculateDaysPassed(data),
|
||||
},
|
||||
{
|
||||
label: __('subscribers', 'mailpoet'),
|
||||
value: subscribers,
|
||||
},
|
||||
{
|
||||
label: __('products', 'mailpoet'),
|
||||
value: products,
|
||||
},
|
||||
{
|
||||
label: __('revenue', 'mailpoet'),
|
||||
value: formattedPrice(revenue),
|
||||
},
|
||||
];
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
import { Notice } from '@wordpress/components/build';
|
||||
import { Button } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { addQueryArgs } from '@wordpress/url';
|
||||
import { MailPoet } from '../../../../../../../mailpoet';
|
||||
|
||||
function getUpgradeLink(): string {
|
||||
const utmArgs = {
|
||||
utm_source: 'plugin',
|
||||
utm_medium: 'upsell_modal',
|
||||
utm_campaign: 'automation-analytics',
|
||||
};
|
||||
const url = MailPoet.hasValidApiKey
|
||||
? `https://account.mailpoet.com/orders/upgrade/${MailPoet.pluginPartialKey}`
|
||||
: `https://account.mailpoet.com/?s=${MailPoet.subscribersCount}&g=business&billing=monthly&email=${MailPoet.currentWpUserEmail}`;
|
||||
|
||||
return addQueryArgs(url, utmArgs);
|
||||
}
|
||||
|
||||
export function Upgrade({ text }: { text: string | JSX.Element }): JSX.Element {
|
||||
return (
|
||||
<Notice
|
||||
className="mailpoet-analytics-upgrade-banner"
|
||||
status="warning"
|
||||
isDismissible={false}
|
||||
>
|
||||
<span className="mailpoet-analytics-upgrade-banner__inner">
|
||||
{text}
|
||||
|
||||
<Button href={getUpgradeLink()} isPrimary>
|
||||
{__('Upgrade', 'mailpoet')}
|
||||
</Button>
|
||||
</span>
|
||||
</Notice>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user