Add order tab

[PREMIUM-224]
This commit is contained in:
David Remer
2023-06-22 10:28:01 +03:00
committed by Aschepikov
parent 844fa421c8
commit 1e5217f3ee
9 changed files with 352 additions and 1 deletions

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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}&nbsp;
<span className="quantity">({item.quantity}&times;)</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>
);
}

View File

@@ -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>
&nbsp;
{__(
'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>
);
}

View File

@@ -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,
},
]);
}

View File

@@ -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),
},
];
}

View File

@@ -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>
);
}