@aglyn/plugins-commerce 1.0.0-beta.189 → 1.0.0-beta.191
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +14 -13
- package/src/lib/components/console/host-orders-card.component.js +333 -337
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +235 -215
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/stock-movements-card.component.js +166 -154
- package/src/lib/components/console/stock-movements-card.component.js.map +1 -1
- package/src/lib/server/member-post.js +3 -2
- package/src/lib/server/member-post.js.map +1 -1
- package/src/lib/server/process-abandoned.js +9 -8
- package/src/lib/server/process-abandoned.js.map +1 -1
- package/src/lib/server/process-restock.js +9 -8
- package/src/lib/server/process-restock.js.map +1 -1
|
@@ -18,10 +18,14 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
18
18
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
19
|
import * as CommerceModel from "../../model/index.js";
|
|
20
20
|
import { CardDisplay } from "@aglyn/shared-ui-jsx";
|
|
21
|
-
import
|
|
21
|
+
import ListFilterChips from "@aglyn/shared-ui-jsx/components/list-filter-chips.component";
|
|
22
|
+
import { ListTable } from "@aglyn/shared-ui-jsx/components/list-table.component";
|
|
23
|
+
import { hiddenFilterVisibility } from "@aglyn/shared-ui-jsx/const/list-filter";
|
|
24
|
+
import { inMemoryListField, upsertListFilterClause } from "@aglyn/shared-ui-jsx/const/list-grid-filter";
|
|
25
|
+
import { useListRowsFilter } from "@aglyn/shared-ui-jsx/hooks/use-list-rows-filter";
|
|
22
26
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
23
27
|
import { checkEntitlement, ORDERS_CUSTOMER_PARAM, ORDERS_ORDER_PARAM, pluginDocsHelp } from "@aglyn/aglyn";
|
|
24
|
-
import { Alert, AlertTitle, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, MenuItem, Stack,
|
|
28
|
+
import { Alert, AlertTitle, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, MenuItem, Stack, TextField, Tooltip, Typography } from "@mui/material";
|
|
25
29
|
import { collection, doc, getDoc, limit, orderBy, query } from "firebase/firestore";
|
|
26
30
|
import { useSearchParams } from "next/navigation";
|
|
27
31
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
@@ -33,13 +37,83 @@ import OrderDetailDialog, { DISPUTE_COLOR } from "./order-detail-dialog.componen
|
|
|
33
37
|
/**
|
|
34
38
|
* How far back the table, its filters and its export reach.
|
|
35
39
|
*
|
|
36
|
-
* This window cannot be paged the way a plain list can.
|
|
37
|
-
* CSV export all run over what was read, so a page of ten
|
|
38
|
-
* every one of them — a status filter would search a
|
|
39
|
-
* report confidently on what it found. Bounding the read and saying where the
|
|
40
|
+
* This window cannot be paged the way a plain list can. The Filters panel,
|
|
41
|
+
* the search and the CSV export all run over what was read, so a page of ten
|
|
42
|
+
* would quietly narrow every one of them — a status filter would search a
|
|
43
|
+
* tenth of the orders and report confidently on what it found. Bounding the read and saying where the
|
|
40
44
|
* bound falls keeps the filters honest about their own scope.
|
|
41
45
|
*/ const ORDERS_WINDOW = 200;
|
|
42
46
|
/** A name map for the filter menu; it renders no product rows of its own. */ const PRODUCT_NAME_WINDOW = 100;
|
|
47
|
+
/*
|
|
48
|
+
* What the orders grid's Filters panel offers. The card holds its whole
|
|
49
|
+
* window, so the panel and the search answer over every order it read.
|
|
50
|
+
*
|
|
51
|
+
* Status and Channel are the order's own enumerations; Disputes is apart
|
|
52
|
+
* from Status on purpose, because the two are orthogonal: an OPEN dispute
|
|
53
|
+
* sits on an order that is still `paid`, and a lost one on `refunded`
|
|
54
|
+
* beside every ordinary refund. Product is the ids of every line item, so
|
|
55
|
+
* a cart, POS or draft order is found by any product it holds. The date is
|
|
56
|
+
* the field the query orders by, which every order writer stamps.
|
|
57
|
+
*/ const ORDER_FILTER_FIELDS = [
|
|
58
|
+
inMemoryListField('orderLabel', 'text'),
|
|
59
|
+
inMemoryListField('customerEmail', 'text'),
|
|
60
|
+
inMemoryListField('channelKey', 'select'),
|
|
61
|
+
inMemoryListField('statusKey', 'select'),
|
|
62
|
+
inMemoryListField('createdAtMs', 'date'),
|
|
63
|
+
_extends({}, inMemoryListField('productIds', 'select'), {
|
|
64
|
+
tokensPath: 'productIds',
|
|
65
|
+
verbatimTokens: true
|
|
66
|
+
}),
|
|
67
|
+
inMemoryListField('disputeKey', 'select')
|
|
68
|
+
];
|
|
69
|
+
const ORDER_FILTER_HEADERS = {
|
|
70
|
+
orderLabel: 'Order',
|
|
71
|
+
customerEmail: 'Customer',
|
|
72
|
+
channelKey: 'Channel',
|
|
73
|
+
statusKey: 'Status',
|
|
74
|
+
createdAtMs: 'Date',
|
|
75
|
+
productIds: 'Product',
|
|
76
|
+
disputeKey: 'Disputes'
|
|
77
|
+
};
|
|
78
|
+
const ORDER_STATUS_OPTIONS = Object.keys(CommerceModel.ORDER_STATUS_LABELS).map((status)=>({
|
|
79
|
+
value: status,
|
|
80
|
+
label: CommerceModel.ORDER_STATUS_LABELS[status]
|
|
81
|
+
}));
|
|
82
|
+
const ORDER_CHANNEL_OPTIONS = Object.keys(CommerceModel.ORDER_CHANNEL_LABELS).map((channel)=>({
|
|
83
|
+
value: channel,
|
|
84
|
+
label: CommerceModel.ORDER_CHANNEL_LABELS[channel]
|
|
85
|
+
}));
|
|
86
|
+
/*
|
|
87
|
+
* `lost` is the badge's tone, not the raw dispute status: a dispute that was
|
|
88
|
+
* WON also closes with money untouched, and listing it as charged back would
|
|
89
|
+
* tell a merchant they lost a case they won.
|
|
90
|
+
*/ const ORDER_DISPUTE_OPTIONS = [
|
|
91
|
+
{
|
|
92
|
+
value: 'open',
|
|
93
|
+
label: 'Open dispute'
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
value: 'lost',
|
|
97
|
+
label: 'Charged back'
|
|
98
|
+
}
|
|
99
|
+
];
|
|
100
|
+
/** What the quick search reads on an order row. */ const ORDER_SEARCH_FIELDS = [
|
|
101
|
+
'orderLabel',
|
|
102
|
+
'customerEmail',
|
|
103
|
+
'itemName'
|
|
104
|
+
];
|
|
105
|
+
/** The filter-only columns never show. */ const ORDER_HIDDEN_COLUMNS = hiddenFilterVisibility(ORDER_FILTER_FIELDS, [
|
|
106
|
+
'orderLabel',
|
|
107
|
+
'customerEmail',
|
|
108
|
+
'channelKey',
|
|
109
|
+
'statusKey',
|
|
110
|
+
'createdAtMs'
|
|
111
|
+
]);
|
|
112
|
+
/** The clause the dispute banner's "Show them" sets. */ const OPEN_DISPUTE_CLAUSE = {
|
|
113
|
+
field: 'disputeKey',
|
|
114
|
+
op: 'equals',
|
|
115
|
+
value: 'open'
|
|
116
|
+
};
|
|
43
117
|
/**
|
|
44
118
|
* Orders console (AGL-287): filterable list over webhook-written order
|
|
45
119
|
* docs with a detail dialog (timeline, fulfill, refund, cancel, notes,
|
|
@@ -97,28 +171,23 @@ import OrderDetailDialog, { DISPUTE_COLOR } from "./order-detail-dialog.componen
|
|
|
97
171
|
// The query returns the window already newest-first; re-sorting it here
|
|
98
172
|
// would only restate the order it arrived in.
|
|
99
173
|
const orders = orderWindow.rows;
|
|
100
|
-
// Filters + CSV export (AGL-96) over the loaded window.
|
|
101
|
-
const [productFilter, setProductFilter] = useState('');
|
|
102
|
-
const [dateFilter, setDateFilter] = useState('');
|
|
103
|
-
const [statusFilter, setStatusFilter] = useState('');
|
|
104
|
-
const [channelFilter, setChannelFilter] = useState('');
|
|
105
|
-
/**
|
|
106
|
-
* Disputes filter (AGL-1796), separate from Status on purpose: the two are
|
|
107
|
-
* orthogonal. An OPEN dispute sits on an order that is still `paid`, and a
|
|
108
|
-
* lost one sits on `refunded` beside every ordinary refund — so folding
|
|
109
|
-
* either into the Status select would make that control lie about what it
|
|
110
|
-
* selects.
|
|
111
|
-
*/ const [disputeFilter, setDisputeFilter] = useState('');
|
|
112
174
|
/*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
175
|
+
* The grid's clauses (AGL-96, AGL-3317), held here so two things outside
|
|
176
|
+
* the grid can set one: the CRM's contact page links with the buyer's
|
|
177
|
+
* address to answer "what has this customer ordered" (AGL-2622), seeded as
|
|
178
|
+
* a Customer clause matched as a substring so a domain finds every buyer at
|
|
179
|
+
* a company; and the dispute banner's "Show them".
|
|
118
180
|
*/ const searchParams = useSearchParams();
|
|
119
|
-
const [
|
|
120
|
-
var
|
|
121
|
-
|
|
181
|
+
const [clauses, setClauses] = useState(()=>{
|
|
182
|
+
var _searchParams_get;
|
|
183
|
+
const customer = searchParams == null ? void 0 : (_searchParams_get = searchParams.get(ORDERS_CUSTOMER_PARAM)) == null ? void 0 : _searchParams_get.trim();
|
|
184
|
+
return customer ? [
|
|
185
|
+
{
|
|
186
|
+
field: 'customerEmail',
|
|
187
|
+
op: 'contains',
|
|
188
|
+
value: customer
|
|
189
|
+
}
|
|
190
|
+
] : [];
|
|
122
191
|
});
|
|
123
192
|
const [selectedId, setSelectedId] = useState(null);
|
|
124
193
|
/*
|
|
@@ -157,51 +226,64 @@ import OrderDetailDialog, { DISPUTE_COLOR } from "./order-detail-dialog.componen
|
|
|
157
226
|
const [draft, setDraft] = useState(null);
|
|
158
227
|
const { data: user } = useUser();
|
|
159
228
|
const { enqueueSnackbar } = useSnackbar();
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
229
|
+
/**
|
|
230
|
+
* Each order with the values the grid shows and filters by. `liftLegacyOrder`
|
|
231
|
+
* names a status and channel on a row that predates them, and Product
|
|
232
|
+
* reads the line items first (AGL-1747): the flat `productId` is written
|
|
233
|
+
* only by the two buy-now Stripe paths.
|
|
234
|
+
*/ const orderRows = useMemo(()=>orders.map((order)=>{
|
|
235
|
+
var _lifted_lineItems, _ref, _ref1, _ref2, _lifted_channel;
|
|
236
|
+
var _lifted_lineItems_, _lifted_lineItems1;
|
|
165
237
|
const lifted = CommerceModel.liftLegacyOrder(order);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
// would tell a merchant they lost a case they won.
|
|
185
|
-
if (disputeFilter === 'lost' && ((_CommerceModel_describeOrderDispute = CommerceModel.describeOrderDispute(lifted)) == null ? void 0 : _CommerceModel_describeOrderDispute.tone) !== 'lost') {
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
// The same field the query orders by. `createdAt` is not universal
|
|
189
|
-
// across order writers, and a row missing it read as epoch zero — which
|
|
190
|
-
// both date filters excluded outright.
|
|
191
|
-
const created = ((_order_createdAtMs = order.createdAtMs) != null ? _order_createdAtMs : 0) / 1000;
|
|
192
|
-
if (dateFilter === '7d' && now - created > 7 * 86400) return false;
|
|
193
|
-
if (dateFilter === '30d' && now - created > 30 * 86400) return false;
|
|
194
|
-
return true;
|
|
195
|
-
});
|
|
196
|
-
}, [
|
|
238
|
+
const dispute = CommerceModel.describeOrderDispute(lifted);
|
|
239
|
+
const productIds = [
|
|
240
|
+
...new Set([
|
|
241
|
+
...((_lifted_lineItems = lifted.lineItems) != null ? _lifted_lineItems : []).map((line)=>line.productId),
|
|
242
|
+
order.productId
|
|
243
|
+
].filter((id)=>typeof id === 'string' && id !== ''))
|
|
244
|
+
];
|
|
245
|
+
return _extends({}, order, {
|
|
246
|
+
orderLabel: CommerceModel.formatOrderNumber(lifted, order.$id),
|
|
247
|
+
itemName: (_ref = (_ref1 = (_ref2 = (_lifted_lineItems1 = lifted.lineItems) == null ? void 0 : (_lifted_lineItems_ = _lifted_lineItems1[0]) == null ? void 0 : _lifted_lineItems_.name) != null ? _ref2 : productNames[order.productId]) != null ? _ref1 : order.productId) != null ? _ref : '',
|
|
248
|
+
statusKey: lifted.status,
|
|
249
|
+
channelKey: (_lifted_channel = lifted.channel) != null ? _lifted_channel : 'online',
|
|
250
|
+
netCents: CommerceModel.orderNetCents(lifted),
|
|
251
|
+
disputeBadge: dispute,
|
|
252
|
+
productIds,
|
|
253
|
+
disputeKey: CommerceModel.orderHasOpenDispute(lifted) ? 'open' : (dispute == null ? void 0 : dispute.tone) === 'lost' ? 'lost' : ''
|
|
254
|
+
});
|
|
255
|
+
}), [
|
|
197
256
|
orders,
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
257
|
+
productNames
|
|
258
|
+
]);
|
|
259
|
+
const productOptions = useMemo(()=>productWindow.rows.map((product)=>{
|
|
260
|
+
var _product_name;
|
|
261
|
+
return {
|
|
262
|
+
value: String(product.$id),
|
|
263
|
+
label: String((_product_name = product.name) != null ? _product_name : product.$id)
|
|
264
|
+
};
|
|
265
|
+
}), [
|
|
266
|
+
productWindow
|
|
267
|
+
]);
|
|
268
|
+
const filterOptions = useMemo(()=>({
|
|
269
|
+
statusKey: ORDER_STATUS_OPTIONS,
|
|
270
|
+
channelKey: ORDER_CHANNEL_OPTIONS,
|
|
271
|
+
productIds: productOptions,
|
|
272
|
+
disputeKey: ORDER_DISPUTE_OPTIONS
|
|
273
|
+
}), [
|
|
274
|
+
productOptions
|
|
204
275
|
]);
|
|
276
|
+
const listFilter = useListRowsFilter({
|
|
277
|
+
rows: orderRows,
|
|
278
|
+
fields: ORDER_FILTER_FIELDS,
|
|
279
|
+
options: filterOptions,
|
|
280
|
+
headers: ORDER_FILTER_HEADERS,
|
|
281
|
+
search: ORDER_SEARCH_FIELDS,
|
|
282
|
+
clauses,
|
|
283
|
+
onChange: setClauses
|
|
284
|
+
});
|
|
285
|
+
const visibleOrders = listFilter.rows;
|
|
286
|
+
const showingOpenDisputes = clauses.some((clause)=>clause.field === OPEN_DISPUTE_CLAUSE.field && clause.op === OPEN_DISPUTE_CLAUSE.op && clause.value === OPEN_DISPUTE_CLAUSE.value);
|
|
205
287
|
/**
|
|
206
288
|
* Raised over EVERY loaded order, not the visible ones (AGL-1796). A
|
|
207
289
|
* merchant filtered to "delivered" still has a deadline running on a `paid`
|
|
@@ -340,6 +422,168 @@ import OrderDetailDialog, { DISPUTE_COLOR } from "./order-detail-dialog.componen
|
|
|
340
422
|
/** What the draft dialog can actually offer. */ const selectableProducts = useMemo(()=>productWindow.rows.filter((product)=>!product.deletedAt), [
|
|
341
423
|
productWindow
|
|
342
424
|
]);
|
|
425
|
+
/*
|
|
426
|
+
* The six columns `/product/commerce` advertises, in the order the mockup
|
|
427
|
+
* shows them — every fact scannable on its own, Channel included.
|
|
428
|
+
*/ const orderColumns = useMemo(()=>[
|
|
429
|
+
{
|
|
430
|
+
field: 'orderLabel',
|
|
431
|
+
headerName: 'Order',
|
|
432
|
+
flex: 1,
|
|
433
|
+
minWidth: 160,
|
|
434
|
+
renderCell: ({ row })=>{
|
|
435
|
+
var _ref;
|
|
436
|
+
var _row_lineItems;
|
|
437
|
+
const extraItems = Math.max(0, ((_ref = (_row_lineItems = row.lineItems) == null ? void 0 : _row_lineItems.length) != null ? _ref : 0) - 1);
|
|
438
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
439
|
+
sx: {
|
|
440
|
+
minWidth: 0
|
|
441
|
+
},
|
|
442
|
+
children: [
|
|
443
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
444
|
+
variant: "body2",
|
|
445
|
+
noWrap: true,
|
|
446
|
+
children: row.orderLabel
|
|
447
|
+
}),
|
|
448
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
449
|
+
variant: "caption",
|
|
450
|
+
color: "text.secondary",
|
|
451
|
+
noWrap: true,
|
|
452
|
+
children: extraItems ? `${row.itemName} +${extraItems} more` : row.itemName
|
|
453
|
+
})
|
|
454
|
+
]
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
field: 'customerEmail',
|
|
460
|
+
headerName: 'Customer',
|
|
461
|
+
flex: 1,
|
|
462
|
+
minWidth: 180,
|
|
463
|
+
renderCell: ({ row })=>/*#__PURE__*/ _jsx(Typography, {
|
|
464
|
+
variant: "body2",
|
|
465
|
+
noWrap: true,
|
|
466
|
+
children: row.customerEmail || '—'
|
|
467
|
+
})
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
field: 'channelKey',
|
|
471
|
+
headerName: 'Channel',
|
|
472
|
+
width: 120,
|
|
473
|
+
renderCell: ({ row })=>/*#__PURE__*/ _jsx(Typography, {
|
|
474
|
+
variant: "body2",
|
|
475
|
+
children: CommerceModel.orderChannelLabel(row.channelKey)
|
|
476
|
+
})
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
field: 'netCents',
|
|
480
|
+
headerName: 'Total',
|
|
481
|
+
type: 'number',
|
|
482
|
+
width: 150,
|
|
483
|
+
align: 'right',
|
|
484
|
+
headerAlign: 'right',
|
|
485
|
+
renderCell: ({ row })=>{
|
|
486
|
+
var _ref, _ref1;
|
|
487
|
+
var _row_totals;
|
|
488
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
489
|
+
sx: {
|
|
490
|
+
alignItems: 'flex-end',
|
|
491
|
+
minWidth: 0,
|
|
492
|
+
width: 1
|
|
493
|
+
},
|
|
494
|
+
children: [
|
|
495
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
496
|
+
variant: "body2",
|
|
497
|
+
children: `$${(row.netCents / 100).toFixed(2)}`
|
|
498
|
+
}),
|
|
499
|
+
row.refundedCents ? /*#__PURE__*/ _jsx(Typography, {
|
|
500
|
+
variant: "caption",
|
|
501
|
+
color: "text.secondary",
|
|
502
|
+
noWrap: true,
|
|
503
|
+
children: `$${(((_ref = (_ref1 = (_row_totals = row.totals) == null ? void 0 : _row_totals.totalCents) != null ? _ref1 : row.amountCents) != null ? _ref : 0) / 100).toFixed(2)} less refunds`
|
|
504
|
+
}) : null
|
|
505
|
+
]
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
field: 'statusKey',
|
|
511
|
+
headerName: 'Status',
|
|
512
|
+
width: 230,
|
|
513
|
+
renderCell: ({ row })=>{
|
|
514
|
+
var _CommerceModel_ORDER_STATUS_LABELS_row_statusKey, _CommerceModel_ORDER_STATUS_COLOR_row_statusKey;
|
|
515
|
+
// A lost chargeback leaves `status: 'refunded'` (AGL-1787), so the
|
|
516
|
+
// status pill cannot tell the two apart either — which is why the
|
|
517
|
+
// dispute chip sits beside it rather than being folded into it.
|
|
518
|
+
const dispute = row.disputeBadge;
|
|
519
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
520
|
+
sx: {
|
|
521
|
+
minWidth: 0
|
|
522
|
+
},
|
|
523
|
+
children: [
|
|
524
|
+
/*#__PURE__*/ _jsxs(Stack, {
|
|
525
|
+
direction: "row",
|
|
526
|
+
spacing: 0.5,
|
|
527
|
+
sx: {
|
|
528
|
+
alignItems: 'center'
|
|
529
|
+
},
|
|
530
|
+
children: [
|
|
531
|
+
/*#__PURE__*/ _jsx(Chip, {
|
|
532
|
+
label: (_CommerceModel_ORDER_STATUS_LABELS_row_statusKey = CommerceModel.ORDER_STATUS_LABELS[row.statusKey]) != null ? _CommerceModel_ORDER_STATUS_LABELS_row_statusKey : row.statusKey,
|
|
533
|
+
size: "small",
|
|
534
|
+
color: (_CommerceModel_ORDER_STATUS_COLOR_row_statusKey = CommerceModel.ORDER_STATUS_COLOR[row.statusKey]) != null ? _CommerceModel_ORDER_STATUS_COLOR_row_statusKey : 'default',
|
|
535
|
+
variant: "outlined"
|
|
536
|
+
}),
|
|
537
|
+
dispute ? /*#__PURE__*/ _jsx(Tooltip, {
|
|
538
|
+
title: dispute.detail,
|
|
539
|
+
children: /*#__PURE__*/ _jsx(Chip, {
|
|
540
|
+
label: dispute.label,
|
|
541
|
+
size: "small",
|
|
542
|
+
color: DISPUTE_COLOR[dispute.tone],
|
|
543
|
+
variant: "filled"
|
|
544
|
+
})
|
|
545
|
+
}) : null
|
|
546
|
+
]
|
|
547
|
+
}),
|
|
548
|
+
(dispute == null ? void 0 : dispute.evidenceDaysLeft) !== undefined ? /*#__PURE__*/ _jsx(Typography, {
|
|
549
|
+
variant: "caption",
|
|
550
|
+
component: "div",
|
|
551
|
+
color: dispute.evidenceDaysLeft < 0 ? 'error' : 'warning.main',
|
|
552
|
+
children: dispute.evidenceDaysLeft < 0 ? 'Evidence deadline passed' : `Evidence due in ${dispute.evidenceDaysLeft} day${dispute.evidenceDaysLeft === 1 ? '' : 's'}`
|
|
553
|
+
}) : null
|
|
554
|
+
]
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
field: 'createdAtMs',
|
|
560
|
+
headerName: 'Date',
|
|
561
|
+
width: 130,
|
|
562
|
+
renderCell: ({ row })=>{
|
|
563
|
+
var _ref;
|
|
564
|
+
var _row_createdAt_toDate, _row_createdAt;
|
|
565
|
+
const createdAt = (_ref = (_row_createdAt = row.createdAt) == null ? void 0 : (_row_createdAt_toDate = _row_createdAt.toDate) == null ? void 0 : _row_createdAt_toDate.call(_row_createdAt)) != null ? _ref : row.createdAtMs ? new Date(row.createdAtMs) : null;
|
|
566
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
567
|
+
sx: {
|
|
568
|
+
minWidth: 0
|
|
569
|
+
},
|
|
570
|
+
children: [
|
|
571
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
572
|
+
variant: "body2",
|
|
573
|
+
noWrap: true,
|
|
574
|
+
children: createdAt ? createdAt.toLocaleDateString() : '—'
|
|
575
|
+
}),
|
|
576
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
577
|
+
variant: "caption",
|
|
578
|
+
color: "text.secondary",
|
|
579
|
+
noWrap: true,
|
|
580
|
+
children: createdAt ? createdAt.toLocaleTimeString() : ''
|
|
581
|
+
})
|
|
582
|
+
]
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
], []);
|
|
343
587
|
return /*#__PURE__*/ _jsxs(CardDisplay, {
|
|
344
588
|
header: 'Orders',
|
|
345
589
|
help: pluginDocsHelp('commerce', {
|
|
@@ -416,10 +660,10 @@ import OrderDetailDialog, { DISPUTE_COLOR } from "./order-detail-dialog.componen
|
|
|
416
660
|
}) : null,
|
|
417
661
|
openDisputes.count > 0 ? /*#__PURE__*/ _jsxs(Alert, {
|
|
418
662
|
severity: openDisputes.overdue ? 'error' : 'warning',
|
|
419
|
-
action:
|
|
663
|
+
action: showingOpenDisputes ? undefined : /*#__PURE__*/ _jsx(Button, {
|
|
420
664
|
size: "small",
|
|
421
665
|
color: "inherit",
|
|
422
|
-
onClick: ()=>
|
|
666
|
+
onClick: ()=>setClauses(upsertListFilterClause(clauses, OPEN_DISPUTE_CLAUSE.field, OPEN_DISPUTE_CLAUSE)),
|
|
423
667
|
children: 'Show them'
|
|
424
668
|
}),
|
|
425
669
|
children: [
|
|
@@ -433,130 +677,10 @@ import OrderDetailDialog, { DISPUTE_COLOR } from "./order-detail-dialog.componen
|
|
|
433
677
|
direction: "row",
|
|
434
678
|
spacing: 1,
|
|
435
679
|
sx: {
|
|
436
|
-
alignItems: 'center'
|
|
680
|
+
alignItems: 'center',
|
|
681
|
+
justifyContent: 'flex-end'
|
|
437
682
|
},
|
|
438
683
|
children: [
|
|
439
|
-
/*#__PURE__*/ _jsxs(TextField, {
|
|
440
|
-
select: true,
|
|
441
|
-
size: "small",
|
|
442
|
-
label: "Product",
|
|
443
|
-
value: productFilter,
|
|
444
|
-
onChange: (event)=>setProductFilter(event.target.value),
|
|
445
|
-
sx: {
|
|
446
|
-
minWidth: 160
|
|
447
|
-
},
|
|
448
|
-
children: [
|
|
449
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
450
|
-
value: "",
|
|
451
|
-
children: 'All products'
|
|
452
|
-
}),
|
|
453
|
-
productWindow.rows.map((product)=>{
|
|
454
|
-
var _product_name;
|
|
455
|
-
return /*#__PURE__*/ _jsx(MenuItem, {
|
|
456
|
-
value: product.$id,
|
|
457
|
-
children: (_product_name = product.name) != null ? _product_name : product.$id
|
|
458
|
-
}, product.$id);
|
|
459
|
-
})
|
|
460
|
-
]
|
|
461
|
-
}),
|
|
462
|
-
/*#__PURE__*/ _jsxs(TextField, {
|
|
463
|
-
select: true,
|
|
464
|
-
size: "small",
|
|
465
|
-
label: "Period",
|
|
466
|
-
value: dateFilter,
|
|
467
|
-
onChange: (event)=>setDateFilter(event.target.value),
|
|
468
|
-
sx: {
|
|
469
|
-
minWidth: 130
|
|
470
|
-
},
|
|
471
|
-
children: [
|
|
472
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
473
|
-
value: "",
|
|
474
|
-
children: 'All time'
|
|
475
|
-
}),
|
|
476
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
477
|
-
value: "7d",
|
|
478
|
-
children: 'Last 7 days'
|
|
479
|
-
}),
|
|
480
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
481
|
-
value: "30d",
|
|
482
|
-
children: 'Last 30 days'
|
|
483
|
-
})
|
|
484
|
-
]
|
|
485
|
-
}),
|
|
486
|
-
/*#__PURE__*/ _jsxs(TextField, {
|
|
487
|
-
select: true,
|
|
488
|
-
size: "small",
|
|
489
|
-
label: "Status",
|
|
490
|
-
value: statusFilter,
|
|
491
|
-
onChange: (event)=>setStatusFilter(event.target.value),
|
|
492
|
-
sx: {
|
|
493
|
-
minWidth: 130
|
|
494
|
-
},
|
|
495
|
-
children: [
|
|
496
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
497
|
-
value: "",
|
|
498
|
-
children: 'All statuses'
|
|
499
|
-
}),
|
|
500
|
-
Object.keys(CommerceModel.ORDER_STATUS_LABELS).map((status)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
501
|
-
value: status,
|
|
502
|
-
children: CommerceModel.ORDER_STATUS_LABELS[status]
|
|
503
|
-
}, status))
|
|
504
|
-
]
|
|
505
|
-
}),
|
|
506
|
-
/*#__PURE__*/ _jsxs(TextField, {
|
|
507
|
-
select: true,
|
|
508
|
-
size: "small",
|
|
509
|
-
label: "Channel",
|
|
510
|
-
value: channelFilter,
|
|
511
|
-
onChange: (event)=>setChannelFilter(event.target.value),
|
|
512
|
-
sx: {
|
|
513
|
-
minWidth: 120
|
|
514
|
-
},
|
|
515
|
-
children: [
|
|
516
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
517
|
-
value: "",
|
|
518
|
-
children: 'All channels'
|
|
519
|
-
}),
|
|
520
|
-
Object.keys(CommerceModel.ORDER_CHANNEL_LABELS).map((channel)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
521
|
-
value: channel,
|
|
522
|
-
children: CommerceModel.ORDER_CHANNEL_LABELS[channel]
|
|
523
|
-
}, channel))
|
|
524
|
-
]
|
|
525
|
-
}),
|
|
526
|
-
/*#__PURE__*/ _jsx(TextField, {
|
|
527
|
-
size: "small",
|
|
528
|
-
label: "Customer",
|
|
529
|
-
placeholder: "Email",
|
|
530
|
-
value: customerFilter,
|
|
531
|
-
onChange: (event)=>setCustomerFilter(event.target.value),
|
|
532
|
-
sx: {
|
|
533
|
-
minWidth: 180
|
|
534
|
-
}
|
|
535
|
-
}),
|
|
536
|
-
/*#__PURE__*/ _jsxs(TextField, {
|
|
537
|
-
select: true,
|
|
538
|
-
size: "small",
|
|
539
|
-
label: "Disputes",
|
|
540
|
-
value: disputeFilter,
|
|
541
|
-
onChange: (event)=>setDisputeFilter(event.target.value),
|
|
542
|
-
sx: {
|
|
543
|
-
minWidth: 140
|
|
544
|
-
},
|
|
545
|
-
children: [
|
|
546
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
547
|
-
value: "",
|
|
548
|
-
children: 'All orders'
|
|
549
|
-
}),
|
|
550
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
551
|
-
value: "open",
|
|
552
|
-
children: 'Open dispute'
|
|
553
|
-
}),
|
|
554
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
555
|
-
value: "lost",
|
|
556
|
-
children: 'Charged back'
|
|
557
|
-
})
|
|
558
|
-
]
|
|
559
|
-
}),
|
|
560
684
|
/*#__PURE__*/ _jsx(Button, {
|
|
561
685
|
size: "small",
|
|
562
686
|
onClick: handleExportCsv,
|
|
@@ -571,153 +695,25 @@ import OrderDetailDialog, { DISPUTE_COLOR } from "./order-detail-dialog.componen
|
|
|
571
695
|
})
|
|
572
696
|
]
|
|
573
697
|
}),
|
|
574
|
-
/*#__PURE__*/
|
|
575
|
-
|
|
698
|
+
/*#__PURE__*/ _jsx(ListFilterChips, _extends({}, listFilter.chipsProps)),
|
|
699
|
+
/*#__PURE__*/ _jsx(ListTable, _extends({
|
|
576
700
|
"aria-label": "Orders",
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
children: 'Channel'
|
|
589
|
-
}),
|
|
590
|
-
/*#__PURE__*/ _jsx(TableCell, {
|
|
591
|
-
align: "right",
|
|
592
|
-
children: 'Total'
|
|
593
|
-
}),
|
|
594
|
-
/*#__PURE__*/ _jsx(TableCell, {
|
|
595
|
-
children: 'Status'
|
|
596
|
-
}),
|
|
597
|
-
/*#__PURE__*/ _jsx(TableCell, {
|
|
598
|
-
children: 'Date'
|
|
599
|
-
})
|
|
600
|
-
]
|
|
601
|
-
})
|
|
602
|
-
}),
|
|
603
|
-
/*#__PURE__*/ _jsx(TableBody, {
|
|
604
|
-
children: visibleOrders.map((order)=>{
|
|
605
|
-
var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _CommerceModel_ORDER_STATUS_LABELS_lifted_status, _CommerceModel_ORDER_STATUS_COLOR_lifted_status;
|
|
606
|
-
var _order_createdAt_toDate, _order_createdAt, _lifted_lineItems_, _lifted_lineItems, _lifted_lineItems1, _lifted_totals;
|
|
607
|
-
const lifted = CommerceModel.liftLegacyOrder(order);
|
|
608
|
-
// A lost chargeback leaves `status: 'refunded'` (AGL-1787),
|
|
609
|
-
// so the status pill on this row cannot tell the two apart
|
|
610
|
-
// either — which is why the dispute chip sits beside it
|
|
611
|
-
// rather than being folded into the status.
|
|
612
|
-
const dispute = CommerceModel.describeOrderDispute(lifted);
|
|
613
|
-
const createdAt = (_order_createdAt = order.createdAt) == null ? void 0 : (_order_createdAt_toDate = _order_createdAt.toDate) == null ? void 0 : _order_createdAt_toDate.call(_order_createdAt);
|
|
614
|
-
const itemName = (_ref = (_ref1 = (_ref2 = (_lifted_lineItems = lifted.lineItems) == null ? void 0 : (_lifted_lineItems_ = _lifted_lineItems[0]) == null ? void 0 : _lifted_lineItems_.name) != null ? _ref2 : productNames[order.productId]) != null ? _ref1 : order.productId) != null ? _ref : '';
|
|
615
|
-
const extraItems = Math.max(0, ((_ref3 = (_lifted_lineItems1 = lifted.lineItems) == null ? void 0 : _lifted_lineItems1.length) != null ? _ref3 : 0) - 1);
|
|
616
|
-
return /*#__PURE__*/ _jsxs(TableRow, {
|
|
617
|
-
hover: true,
|
|
618
|
-
onClick: ()=>setSelectedId(order.$id),
|
|
619
|
-
sx: {
|
|
620
|
-
cursor: 'pointer'
|
|
621
|
-
},
|
|
622
|
-
children: [
|
|
623
|
-
/*#__PURE__*/ _jsxs(TableCell, {
|
|
624
|
-
children: [
|
|
625
|
-
/*#__PURE__*/ _jsx(Typography, {
|
|
626
|
-
variant: "body2",
|
|
627
|
-
children: CommerceModel.formatOrderNumber(lifted, order.$id)
|
|
628
|
-
}),
|
|
629
|
-
/*#__PURE__*/ _jsx(Typography, {
|
|
630
|
-
variant: "caption",
|
|
631
|
-
color: "text.secondary",
|
|
632
|
-
children: extraItems ? `${itemName} +${extraItems} more` : itemName
|
|
633
|
-
})
|
|
634
|
-
]
|
|
635
|
-
}),
|
|
636
|
-
/*#__PURE__*/ _jsx(TableCell, {
|
|
637
|
-
children: /*#__PURE__*/ _jsx(Typography, {
|
|
638
|
-
variant: "body2",
|
|
639
|
-
noWrap: true,
|
|
640
|
-
children: order.customerEmail || '—'
|
|
641
|
-
})
|
|
642
|
-
}),
|
|
643
|
-
/*#__PURE__*/ _jsx(TableCell, {
|
|
644
|
-
children: /*#__PURE__*/ _jsx(Typography, {
|
|
645
|
-
variant: "body2",
|
|
646
|
-
children: CommerceModel.orderChannelLabel(lifted.channel)
|
|
647
|
-
})
|
|
648
|
-
}),
|
|
649
|
-
/*#__PURE__*/ _jsxs(TableCell, {
|
|
650
|
-
align: "right",
|
|
651
|
-
children: [
|
|
652
|
-
/*#__PURE__*/ _jsx(Typography, {
|
|
653
|
-
variant: "body2",
|
|
654
|
-
children: `$${(CommerceModel.orderNetCents(lifted) / 100).toFixed(2)}`
|
|
655
|
-
}),
|
|
656
|
-
order.refundedCents ? /*#__PURE__*/ _jsx(Typography, {
|
|
657
|
-
variant: "caption",
|
|
658
|
-
color: "text.secondary",
|
|
659
|
-
children: `$${(((_ref4 = (_ref5 = (_lifted_totals = lifted.totals) == null ? void 0 : _lifted_totals.totalCents) != null ? _ref5 : order.amountCents) != null ? _ref4 : 0) / 100).toFixed(2)} less refunds`
|
|
660
|
-
}) : null
|
|
661
|
-
]
|
|
662
|
-
}),
|
|
663
|
-
/*#__PURE__*/ _jsxs(TableCell, {
|
|
664
|
-
children: [
|
|
665
|
-
/*#__PURE__*/ _jsxs(Stack, {
|
|
666
|
-
direction: "row",
|
|
667
|
-
spacing: 0.5,
|
|
668
|
-
sx: {
|
|
669
|
-
alignItems: 'center'
|
|
670
|
-
},
|
|
671
|
-
children: [
|
|
672
|
-
/*#__PURE__*/ _jsx(Chip, {
|
|
673
|
-
label: (_CommerceModel_ORDER_STATUS_LABELS_lifted_status = CommerceModel.ORDER_STATUS_LABELS[lifted.status]) != null ? _CommerceModel_ORDER_STATUS_LABELS_lifted_status : lifted.status,
|
|
674
|
-
size: "small",
|
|
675
|
-
color: (_CommerceModel_ORDER_STATUS_COLOR_lifted_status = CommerceModel.ORDER_STATUS_COLOR[lifted.status]) != null ? _CommerceModel_ORDER_STATUS_COLOR_lifted_status : 'default',
|
|
676
|
-
variant: "outlined"
|
|
677
|
-
}),
|
|
678
|
-
dispute ? /*#__PURE__*/ _jsx(Tooltip, {
|
|
679
|
-
title: dispute.detail,
|
|
680
|
-
children: /*#__PURE__*/ _jsx(Chip, {
|
|
681
|
-
label: dispute.label,
|
|
682
|
-
size: "small",
|
|
683
|
-
color: DISPUTE_COLOR[dispute.tone],
|
|
684
|
-
variant: "filled"
|
|
685
|
-
})
|
|
686
|
-
}) : null
|
|
687
|
-
]
|
|
688
|
-
}),
|
|
689
|
-
(dispute == null ? void 0 : dispute.evidenceDaysLeft) !== undefined ? /*#__PURE__*/ _jsx(Typography, {
|
|
690
|
-
variant: "caption",
|
|
691
|
-
component: "div",
|
|
692
|
-
color: dispute.evidenceDaysLeft < 0 ? 'error' : 'warning.main',
|
|
693
|
-
children: dispute.evidenceDaysLeft < 0 ? 'Evidence deadline passed' : `Evidence due in ${dispute.evidenceDaysLeft} day${dispute.evidenceDaysLeft === 1 ? '' : 's'}`
|
|
694
|
-
}) : null
|
|
695
|
-
]
|
|
696
|
-
}),
|
|
697
|
-
/*#__PURE__*/ _jsxs(TableCell, {
|
|
698
|
-
children: [
|
|
699
|
-
/*#__PURE__*/ _jsx(Typography, {
|
|
700
|
-
variant: "body2",
|
|
701
|
-
noWrap: true,
|
|
702
|
-
children: createdAt ? createdAt.toLocaleDateString() : '—'
|
|
703
|
-
}),
|
|
704
|
-
/*#__PURE__*/ _jsx(Typography, {
|
|
705
|
-
variant: "caption",
|
|
706
|
-
color: "text.secondary",
|
|
707
|
-
children: createdAt ? createdAt.toLocaleTimeString() : ''
|
|
708
|
-
})
|
|
709
|
-
]
|
|
710
|
-
})
|
|
711
|
-
]
|
|
712
|
-
}, order.$id);
|
|
713
|
-
})
|
|
714
|
-
})
|
|
715
|
-
]
|
|
716
|
-
}),
|
|
701
|
+
rows: visibleOrders,
|
|
702
|
+
columns: listFilter.filterColumns(orderColumns),
|
|
703
|
+
onOpen: (id)=>setSelectedId(id)
|
|
704
|
+
}, listFilter.gridProps, {
|
|
705
|
+
initialState: {
|
|
706
|
+
columns: {
|
|
707
|
+
columnVisibilityModel: ORDER_HIDDEN_COLUMNS
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
noRowsLabel: "No orders match these filters"
|
|
711
|
+
})),
|
|
717
712
|
orderWindow.truncated ? /*
|
|
718
|
-
* Where the filters and the export stop.
|
|
719
|
-
* read, so a store past this window would otherwise see
|
|
720
|
-
* filter return nothing and read it as having no such
|
|
713
|
+
* Where the filters, the search and the export stop. All run over
|
|
714
|
+
* what was read, so a store past this window would otherwise see
|
|
715
|
+
* a status filter return nothing and read it as having no such
|
|
716
|
+
* orders.
|
|
721
717
|
*/ /*#__PURE__*/ _jsx(Typography, {
|
|
722
718
|
variant: "caption",
|
|
723
719
|
color: "text.secondary",
|