@aglyn/plugins-commerce 1.0.0-beta.189 → 1.0.0-beta.190
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
|
@@ -21,13 +21,16 @@ import * as Aglyn from "@aglyn/aglyn";
|
|
|
21
21
|
import * as CommerceModel from "../../model/index.js";
|
|
22
22
|
import { PRODUCT_LIST_FILTER_FIELDS } from "../../constants/product-filters.js";
|
|
23
23
|
import { CardDisplay, useConfirmationContext } from "@aglyn/shared-ui-jsx";
|
|
24
|
+
import ListFilterChips from "@aglyn/shared-ui-jsx/components/list-filter-chips.component";
|
|
24
25
|
import { ListPagination } from "@aglyn/shared-ui-jsx/components/list-pagination.component";
|
|
26
|
+
import { ListTable, listActionsColumn } from "@aglyn/shared-ui-jsx/components/list-table.component";
|
|
25
27
|
import QuotaReadoutComponent from "@aglyn/shared-ui-jsx/components/quota-readout.component";
|
|
28
|
+
import { filterListRows, inMemoryListField, listFilterGridColumns } from "@aglyn/shared-ui-jsx/const/list-grid-filter";
|
|
26
29
|
import { TABLE_PAGE_SIZE_DEFAULT } from "@aglyn/shared-ui-jsx/const/table-pagination";
|
|
27
|
-
import {
|
|
30
|
+
import { useListGridFilter } from "@aglyn/shared-ui-jsx/hooks/use-list-grid-filter";
|
|
28
31
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
29
32
|
import { Timestamp } from "@aglyn/shared-util-timestamp";
|
|
30
|
-
import { Alert, Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, MenuItem, Stack,
|
|
33
|
+
import { Alert, Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, MenuItem, Stack, TextField, Typography } from "@mui/material";
|
|
31
34
|
import { addDoc, collection, doc, documentId, getCountFromServer, limit, orderBy, query, updateDoc, where } from "firebase/firestore";
|
|
32
35
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
33
36
|
import { useFirestore } from "@aglyn/tenant-feature-instance";
|
|
@@ -57,6 +60,33 @@ const STATUS_COLOR = {
|
|
|
57
60
|
draft: 'warning',
|
|
58
61
|
archived: 'default'
|
|
59
62
|
};
|
|
63
|
+
/*
|
|
64
|
+
* What the products grid's Filters panel offers: Status, a select. The
|
|
65
|
+
* query serves it whenever no search is typed; under a search the query is
|
|
66
|
+
* the search, and Status narrows its matches over the rows the card holds.
|
|
67
|
+
*/ const PRODUCT_GRID_FILTER_FIELDS = [
|
|
68
|
+
inMemoryListField('status', 'select')
|
|
69
|
+
];
|
|
70
|
+
const PRODUCT_GRID_FILTER_HEADERS = {
|
|
71
|
+
status: 'Status'
|
|
72
|
+
};
|
|
73
|
+
const PRODUCT_GRID_FILTER_OPTIONS = {
|
|
74
|
+
status: [
|
|
75
|
+
{
|
|
76
|
+
value: 'active',
|
|
77
|
+
label: 'Active'
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
value: 'draft',
|
|
81
|
+
label: 'Draft'
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
value: 'archived',
|
|
85
|
+
label: 'Archived'
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
};
|
|
89
|
+
const PRODUCT_GRID_SELECT_FIELDS = Object.keys(PRODUCT_GRID_FILTER_OPTIONS);
|
|
60
90
|
/**
|
|
61
91
|
* Products hub v1 (AGL-279): the catalog manager replacing the Commerce
|
|
62
92
|
* Starter card — search + status filter over `hosts/{hostId}/products`,
|
|
@@ -65,7 +95,7 @@ const STATUS_COLOR = {
|
|
|
65
95
|
* gated here on create/duplicate/import (AGL-471); server-side
|
|
66
96
|
* enforcement of the client-write path rides AGL-473.
|
|
67
97
|
*/ export function ProductsHubCard(props) {
|
|
68
|
-
var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _ref10, _ref11, _ref12;
|
|
98
|
+
var _filterClauses_find, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _ref10, _ref11, _ref12;
|
|
69
99
|
var _importing_parsed, _importing_parsed1, _importing_parsed2;
|
|
70
100
|
const { hostId } = props;
|
|
71
101
|
const firestore = useFirestore();
|
|
@@ -75,9 +105,27 @@ const STATUS_COLOR = {
|
|
|
75
105
|
const createHostResource = useHostResourceApi();
|
|
76
106
|
const { org, ready: planReady } = useOrgPlan(hostId);
|
|
77
107
|
const { confirm } = useConfirmationContext();
|
|
78
|
-
const [search, setSearch] = useState('');
|
|
79
108
|
/*
|
|
80
|
-
*
|
|
109
|
+
* The grid's quick search IS the served search: its words go to the query,
|
|
110
|
+
* never matched again over the rows (see the query below).
|
|
111
|
+
*/ const [searchWords, setSearchWords] = useState([]);
|
|
112
|
+
const search = searchWords.join(' ').trim();
|
|
113
|
+
const gridFilter = useListGridFilter({
|
|
114
|
+
selectFields: PRODUCT_GRID_SELECT_FIELDS,
|
|
115
|
+
search: {
|
|
116
|
+
words: searchWords,
|
|
117
|
+
onChange: setSearchWords
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
const filterClauses = gridFilter.clauses;
|
|
121
|
+
/*
|
|
122
|
+
* The Status clause the query can serve — only while no search is typed,
|
|
123
|
+
* because the query carries one predicate.
|
|
124
|
+
*/ const servedStatus = search ? null : (_filterClauses_find = filterClauses.find((clause)=>clause.field === 'status' && listFilterConstraints(PRODUCT_LIST_FILTER_FIELDS, clause) !== null)) != null ? _filterClauses_find : null;
|
|
125
|
+
/*
|
|
126
|
+
* WHICH field the search box searches (AGL-2501). A SCOPE of the search,
|
|
127
|
+
* not a filter: it picks which served query the search runs, so it stays
|
|
128
|
+
* beside the grid rather than in its panel.
|
|
81
129
|
*
|
|
82
130
|
* The box used to compare four fields at once — name, slug, tag, SKU — over
|
|
83
131
|
* the rows the listener had already fetched. Server-side that is not one
|
|
@@ -90,7 +138,6 @@ const STATUS_COLOR = {
|
|
|
90
138
|
* the case the old box got wrong — and a merchant looking for a SKU knows
|
|
91
139
|
* they are looking for a SKU.
|
|
92
140
|
*/ const [searchField, setSearchField] = useState('name');
|
|
93
|
-
const [statusFilter, setStatusFilter] = useState('all');
|
|
94
141
|
const [editing, setEditing] = useState(null);
|
|
95
142
|
const [creating, setCreating] = useState(false);
|
|
96
143
|
const [adjusting, setAdjusting] = useState(null);
|
|
@@ -144,15 +191,11 @@ const STATUS_COLOR = {
|
|
|
144
191
|
* ordering does move to `nameLower`, and there it is safe: a document
|
|
145
192
|
* without the key has no `nameTokens` either, so the `array-contains`
|
|
146
193
|
* has already excluded it.
|
|
147
|
-
*/ const constraints = listFilterConstraints(PRODUCT_LIST_FILTER_FIELDS, search
|
|
194
|
+
*/ const constraints = listFilterConstraints(PRODUCT_LIST_FILTER_FIELDS, search ? {
|
|
148
195
|
field: searchField,
|
|
149
196
|
op: 'contains',
|
|
150
|
-
value: search
|
|
151
|
-
} :
|
|
152
|
-
field: 'status',
|
|
153
|
-
op: 'equals',
|
|
154
|
-
value: statusFilter
|
|
155
|
-
} : null);
|
|
197
|
+
value: search
|
|
198
|
+
} : servedStatus);
|
|
156
199
|
return query(collection(firestore, 'hosts', hostId, 'products'), ...constraints != null ? constraints : [
|
|
157
200
|
orderBy(documentId())
|
|
158
201
|
], /*
|
|
@@ -166,7 +209,8 @@ const STATUS_COLOR = {
|
|
|
166
209
|
hostId,
|
|
167
210
|
search,
|
|
168
211
|
searchField,
|
|
169
|
-
|
|
212
|
+
servedStatus == null ? void 0 : servedStatus.op,
|
|
213
|
+
servedStatus == null ? void 0 : servedStatus.value
|
|
170
214
|
], {
|
|
171
215
|
idField: '$id'
|
|
172
216
|
});
|
|
@@ -216,20 +260,25 @@ const STATUS_COLOR = {
|
|
|
216
260
|
* its first word and then dropped again here by a whole-string compare
|
|
217
261
|
* that the matching row never satisfies: rows found, then hidden.
|
|
218
262
|
*
|
|
219
|
-
* The
|
|
220
|
-
* is also active — that is the one combination the single
|
|
221
|
-
* cannot express, and here it runs over rows the server
|
|
222
|
-
*
|
|
263
|
+
* The panel's clauses are matched here, and Status only earns its keep
|
|
264
|
+
* when a search is also active — that is the one combination the single
|
|
265
|
+
* predicate above cannot express, and here it runs over rows the server
|
|
266
|
+
* already matched across the whole catalog rather than over an arbitrary
|
|
267
|
+
* window. No search words are passed: the query answered them.
|
|
223
268
|
*
|
|
224
269
|
* Soft-deleted products are still dropped here rather than in the query:
|
|
225
270
|
* `deletedAt` is absent on a live product, and Firestore cannot ask for
|
|
226
271
|
* documents that LACK a field.
|
|
227
|
-
*/
|
|
272
|
+
*/ const live = (productDocs != null ? productDocs : []).slice(0, CATALOG_CEILING).filter((product)=>!product.deletedAt).map((product)=>_extends({}, CommerceModel.liftLegacyProduct(product), {
|
|
228
273
|
$id: product.$id
|
|
229
|
-
}))
|
|
274
|
+
}));
|
|
275
|
+
return filterListRows(live, PRODUCT_GRID_FILTER_FIELDS, filterClauses, {
|
|
276
|
+
paths: [],
|
|
277
|
+
words: []
|
|
278
|
+
}).sort((a, b)=>a.name.localeCompare(b.name));
|
|
230
279
|
}, [
|
|
231
280
|
productDocs,
|
|
232
|
-
|
|
281
|
+
filterClauses
|
|
233
282
|
]);
|
|
234
283
|
/*==========================================
|
|
235
284
|
* THE TABLE PAGES, and the READ deliberately does not (AGL-2501).
|
|
@@ -258,7 +307,7 @@ const STATUS_COLOR = {
|
|
|
258
307
|
useEffect(()=>setPage(0), [
|
|
259
308
|
search,
|
|
260
309
|
searchField,
|
|
261
|
-
|
|
310
|
+
filterClauses
|
|
262
311
|
]);
|
|
263
312
|
const visibleProducts = useMemo(()=>products.slice(page * pageSize, page * pageSize + pageSize), [
|
|
264
313
|
products,
|
|
@@ -631,6 +680,140 @@ const STATUS_COLOR = {
|
|
|
631
680
|
const held = CommerceModel.heldProductUnits(product, nowMs);
|
|
632
681
|
return held > 0 ? ` (${held} reserved)` : '';
|
|
633
682
|
};
|
|
683
|
+
const productColumns = listFilterGridColumns([
|
|
684
|
+
{
|
|
685
|
+
field: 'name',
|
|
686
|
+
headerName: 'Product',
|
|
687
|
+
flex: 1,
|
|
688
|
+
minWidth: 200,
|
|
689
|
+
renderCell: ({ row })=>/*#__PURE__*/ _jsxs(Stack, {
|
|
690
|
+
sx: {
|
|
691
|
+
minWidth: 0
|
|
692
|
+
},
|
|
693
|
+
children: [
|
|
694
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
695
|
+
variant: "body2",
|
|
696
|
+
noWrap: true,
|
|
697
|
+
children: row.name
|
|
698
|
+
}),
|
|
699
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
700
|
+
variant: "caption",
|
|
701
|
+
color: "text.secondary",
|
|
702
|
+
noWrap: true,
|
|
703
|
+
children: `/${row.slug} · id: ${row.$id}`
|
|
704
|
+
})
|
|
705
|
+
]
|
|
706
|
+
})
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
field: 'status',
|
|
710
|
+
headerName: 'Status',
|
|
711
|
+
width: 120,
|
|
712
|
+
renderCell: ({ row })=>{
|
|
713
|
+
var _STATUS_COLOR_row_status;
|
|
714
|
+
return /*#__PURE__*/ _jsx(Chip, {
|
|
715
|
+
label: row.status,
|
|
716
|
+
size: "small",
|
|
717
|
+
color: (_STATUS_COLOR_row_status = STATUS_COLOR[row.status]) != null ? _STATUS_COLOR_row_status : 'default',
|
|
718
|
+
variant: "outlined"
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
field: 'type',
|
|
724
|
+
headerName: 'Type',
|
|
725
|
+
width: 110
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
field: 'priceUsd',
|
|
729
|
+
headerName: 'Price',
|
|
730
|
+
width: 130,
|
|
731
|
+
sortable: false,
|
|
732
|
+
renderCell: ({ row })=>CommerceModel.productPriceMissing(row) ? /*#__PURE__*/ _jsx(Chip, {
|
|
733
|
+
label: "Set a price",
|
|
734
|
+
size: "small",
|
|
735
|
+
color: "warning",
|
|
736
|
+
variant: "outlined"
|
|
737
|
+
}) : formatPrice(row)
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
field: 'stock',
|
|
741
|
+
headerName: 'Stock',
|
|
742
|
+
width: 150,
|
|
743
|
+
sortable: false,
|
|
744
|
+
renderCell: ({ row })=>/*#__PURE__*/ _jsxs("span", {
|
|
745
|
+
children: [
|
|
746
|
+
formatStock(row),
|
|
747
|
+
formatHeld(row) ? /*#__PURE__*/ _jsx(Typography, {
|
|
748
|
+
variant: "caption",
|
|
749
|
+
color: "text.secondary",
|
|
750
|
+
component: "span",
|
|
751
|
+
children: formatHeld(row)
|
|
752
|
+
}) : null
|
|
753
|
+
]
|
|
754
|
+
})
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
field: 'variants',
|
|
758
|
+
headerName: 'Variants',
|
|
759
|
+
width: 100,
|
|
760
|
+
sortable: false,
|
|
761
|
+
valueGetter: (_value, row)=>row.variants.length
|
|
762
|
+
},
|
|
763
|
+
listActionsColumn((product)=>/*#__PURE__*/ _jsxs(Stack, {
|
|
764
|
+
direction: "row",
|
|
765
|
+
spacing: 0.5,
|
|
766
|
+
sx: {
|
|
767
|
+
justifyContent: 'flex-end',
|
|
768
|
+
whiteSpace: 'nowrap'
|
|
769
|
+
},
|
|
770
|
+
children: [
|
|
771
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
772
|
+
size: "small",
|
|
773
|
+
onClick: ()=>setEditing(product),
|
|
774
|
+
children: 'Edit'
|
|
775
|
+
}),
|
|
776
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
777
|
+
size: "small",
|
|
778
|
+
onClick: handleDuplicate(product),
|
|
779
|
+
children: 'Duplicate'
|
|
780
|
+
}),
|
|
781
|
+
product.type === 'digital' ? /*#__PURE__*/ _jsx(Button, {
|
|
782
|
+
size: "small",
|
|
783
|
+
onClick: ()=>setKeysFor(product),
|
|
784
|
+
children: 'Keys'
|
|
785
|
+
}) : null,
|
|
786
|
+
CommerceModel.productInventory(product) != null ? /*#__PURE__*/ _jsx(Button, {
|
|
787
|
+
size: "small",
|
|
788
|
+
onClick: ()=>{
|
|
789
|
+
var _ref, _ref1, _ref2;
|
|
790
|
+
var _product_variants_find, _find, _;
|
|
791
|
+
return setAdjusting({
|
|
792
|
+
product,
|
|
793
|
+
variantId: (_ref = (_product_variants_find = product.variants.find((variant)=>variant.inventory != null)) == null ? void 0 : _product_variants_find.id) != null ? _ref : product.variants[0].id,
|
|
794
|
+
delta: '',
|
|
795
|
+
reason: 'restock',
|
|
796
|
+
locationId: (_ref1 = (_ref2 = (_find = (locationDocs != null ? locationDocs : []).find((location)=>location.isDefault)) == null ? void 0 : _find.$id) != null ? _ref2 : (_ = (locationDocs != null ? locationDocs : [])[0]) == null ? void 0 : _.$id) != null ? _ref1 : ''
|
|
797
|
+
});
|
|
798
|
+
},
|
|
799
|
+
children: 'Stock'
|
|
800
|
+
}) : null,
|
|
801
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
802
|
+
size: "small",
|
|
803
|
+
onClick: handleStatus(product, product.status === 'archived' ? 'active' : 'archived'),
|
|
804
|
+
children: product.status === 'archived' ? 'Activate' : 'Archive'
|
|
805
|
+
}),
|
|
806
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
807
|
+
size: "small",
|
|
808
|
+
color: "error",
|
|
809
|
+
onClick: handleDelete(product),
|
|
810
|
+
children: 'Delete'
|
|
811
|
+
})
|
|
812
|
+
]
|
|
813
|
+
}), {
|
|
814
|
+
width: 470
|
|
815
|
+
})
|
|
816
|
+
], PRODUCT_GRID_FILTER_FIELDS, PRODUCT_GRID_FILTER_OPTIONS, PRODUCT_GRID_FILTER_HEADERS);
|
|
634
817
|
return /*#__PURE__*/ _jsxs(CardDisplay, {
|
|
635
818
|
/*
|
|
636
819
|
* The header count is the SITE'S catalog, not the rows in hand.
|
|
@@ -642,7 +825,7 @@ const STATUS_COLOR = {
|
|
|
642
825
|
* the aggregate counts the whole catalog and the view counts matches, so
|
|
643
826
|
* neither describes what the reader is looking at, and the footer's own
|
|
644
827
|
* count line answers it instead.
|
|
645
|
-
*/ header: search ||
|
|
828
|
+
*/ header: search || filterClauses.length ? 'Products' : `Products${productCount ? ` (${productCount})` : ''}`,
|
|
646
829
|
help: pluginDocsHelp('commerce', {
|
|
647
830
|
anchor: '#products-hub'
|
|
648
831
|
}),
|
|
@@ -659,24 +842,14 @@ const STATUS_COLOR = {
|
|
|
659
842
|
},
|
|
660
843
|
spacing: 1,
|
|
661
844
|
children: [
|
|
662
|
-
/*#__PURE__*/ _jsx(TextField, {
|
|
663
|
-
label: "Search",
|
|
664
|
-
placeholder: searchField === 'name' ? 'Product name' : searchField === 'skus' ? 'Whole SKU' : 'Whole barcode',
|
|
665
|
-
value: search,
|
|
666
|
-
onChange: (event)=>setSearch(event.target.value),
|
|
667
|
-
size: "small",
|
|
668
|
-
sx: {
|
|
669
|
-
flex: 1
|
|
670
|
-
}
|
|
671
|
-
}),
|
|
672
845
|
/*#__PURE__*/ _jsxs(TextField, {
|
|
673
|
-
label: "
|
|
846
|
+
label: "Search in",
|
|
674
847
|
value: searchField,
|
|
675
848
|
onChange: (event)=>setSearchField(event.target.value),
|
|
676
849
|
size: "small",
|
|
677
850
|
select: true,
|
|
678
851
|
sx: {
|
|
679
|
-
minWidth:
|
|
852
|
+
minWidth: 170
|
|
680
853
|
},
|
|
681
854
|
children: [
|
|
682
855
|
/*#__PURE__*/ _jsx(MenuItem, {
|
|
@@ -685,41 +858,18 @@ const STATUS_COLOR = {
|
|
|
685
858
|
}),
|
|
686
859
|
/*#__PURE__*/ _jsx(MenuItem, {
|
|
687
860
|
value: "skus",
|
|
688
|
-
children: 'SKU'
|
|
861
|
+
children: 'SKU (whole)'
|
|
689
862
|
}),
|
|
690
863
|
/*#__PURE__*/ _jsx(MenuItem, {
|
|
691
864
|
value: "barcodes",
|
|
692
|
-
children: 'Barcode'
|
|
865
|
+
children: 'Barcode (whole)'
|
|
693
866
|
})
|
|
694
867
|
]
|
|
695
868
|
}),
|
|
696
|
-
/*#__PURE__*/
|
|
697
|
-
label: "Status",
|
|
698
|
-
value: statusFilter,
|
|
699
|
-
onChange: (event)=>setStatusFilter(event.target.value),
|
|
700
|
-
size: "small",
|
|
701
|
-
select: true,
|
|
869
|
+
/*#__PURE__*/ _jsx(Box, {
|
|
702
870
|
sx: {
|
|
703
|
-
|
|
704
|
-
}
|
|
705
|
-
children: [
|
|
706
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
707
|
-
value: "all",
|
|
708
|
-
children: 'All'
|
|
709
|
-
}),
|
|
710
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
711
|
-
value: "active",
|
|
712
|
-
children: 'Active'
|
|
713
|
-
}),
|
|
714
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
715
|
-
value: "draft",
|
|
716
|
-
children: 'Draft'
|
|
717
|
-
}),
|
|
718
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
719
|
-
value: "archived",
|
|
720
|
-
children: 'Archived'
|
|
721
|
-
})
|
|
722
|
-
]
|
|
871
|
+
flex: 1
|
|
872
|
+
}
|
|
723
873
|
}),
|
|
724
874
|
/*#__PURE__*/ _jsx(Button, {
|
|
725
875
|
variant: "contained",
|
|
@@ -771,157 +921,27 @@ const STATUS_COLOR = {
|
|
|
771
921
|
lastImport: lastImport,
|
|
772
922
|
onCreated: onCatalogCreated
|
|
773
923
|
}),
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
children: 'Price'
|
|
796
|
-
}),
|
|
797
|
-
/*#__PURE__*/ _jsx(TableCell, {
|
|
798
|
-
children: 'Stock'
|
|
799
|
-
}),
|
|
800
|
-
/*#__PURE__*/ _jsx(TableCell, {
|
|
801
|
-
children: 'Variants'
|
|
802
|
-
}),
|
|
803
|
-
/*#__PURE__*/ _jsx(TableCell, {
|
|
804
|
-
align: "right",
|
|
805
|
-
children: 'Actions'
|
|
806
|
-
})
|
|
807
|
-
]
|
|
808
|
-
})
|
|
809
|
-
}),
|
|
810
|
-
/*#__PURE__*/ _jsx(TableBody, {
|
|
811
|
-
children: visibleProducts.map((product)=>{
|
|
812
|
-
var _STATUS_COLOR_product_status;
|
|
813
|
-
return /*#__PURE__*/ _jsxs(TableRow, {
|
|
814
|
-
hover: true,
|
|
815
|
-
children: [
|
|
816
|
-
/*#__PURE__*/ _jsxs(TableCell, {
|
|
817
|
-
sx: {
|
|
818
|
-
maxWidth: 260
|
|
819
|
-
},
|
|
820
|
-
children: [
|
|
821
|
-
/*#__PURE__*/ _jsx(Typography, {
|
|
822
|
-
variant: "body2",
|
|
823
|
-
noWrap: true,
|
|
824
|
-
children: product.name
|
|
825
|
-
}),
|
|
826
|
-
/*#__PURE__*/ _jsx(Typography, {
|
|
827
|
-
variant: "caption",
|
|
828
|
-
color: "text.secondary",
|
|
829
|
-
noWrap: true,
|
|
830
|
-
sx: {
|
|
831
|
-
display: 'block'
|
|
832
|
-
},
|
|
833
|
-
children: `/${product.slug} · id: ${product.$id}`
|
|
834
|
-
})
|
|
835
|
-
]
|
|
836
|
-
}),
|
|
837
|
-
/*#__PURE__*/ _jsx(TableCell, {
|
|
838
|
-
children: /*#__PURE__*/ _jsx(Chip, {
|
|
839
|
-
label: product.status,
|
|
840
|
-
size: "small",
|
|
841
|
-
color: (_STATUS_COLOR_product_status = STATUS_COLOR[product.status]) != null ? _STATUS_COLOR_product_status : 'default',
|
|
842
|
-
variant: "outlined"
|
|
843
|
-
})
|
|
844
|
-
}),
|
|
845
|
-
/*#__PURE__*/ _jsx(TableCell, {
|
|
846
|
-
children: product.type
|
|
847
|
-
}),
|
|
848
|
-
/*#__PURE__*/ _jsx(TableCell, {
|
|
849
|
-
children: CommerceModel.productPriceMissing(product) ? /*#__PURE__*/ _jsx(Chip, {
|
|
850
|
-
label: "Set a price",
|
|
851
|
-
size: "small",
|
|
852
|
-
color: "warning",
|
|
853
|
-
variant: "outlined"
|
|
854
|
-
}) : formatPrice(product)
|
|
855
|
-
}),
|
|
856
|
-
/*#__PURE__*/ _jsxs(TableCell, {
|
|
857
|
-
children: [
|
|
858
|
-
formatStock(product),
|
|
859
|
-
formatHeld(product) ? /*#__PURE__*/ _jsx(Typography, {
|
|
860
|
-
variant: "caption",
|
|
861
|
-
color: "text.secondary",
|
|
862
|
-
component: "span",
|
|
863
|
-
children: formatHeld(product)
|
|
864
|
-
}) : null
|
|
865
|
-
]
|
|
866
|
-
}),
|
|
867
|
-
/*#__PURE__*/ _jsx(TableCell, {
|
|
868
|
-
children: product.variants.length
|
|
869
|
-
}),
|
|
870
|
-
/*#__PURE__*/ _jsxs(TableCell, {
|
|
871
|
-
align: "right",
|
|
872
|
-
sx: {
|
|
873
|
-
whiteSpace: 'nowrap'
|
|
874
|
-
},
|
|
875
|
-
children: [
|
|
876
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
877
|
-
size: "small",
|
|
878
|
-
onClick: ()=>setEditing(product),
|
|
879
|
-
children: 'Edit'
|
|
880
|
-
}),
|
|
881
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
882
|
-
size: "small",
|
|
883
|
-
onClick: handleDuplicate(product),
|
|
884
|
-
children: 'Duplicate'
|
|
885
|
-
}),
|
|
886
|
-
product.type === 'digital' ? /*#__PURE__*/ _jsx(Button, {
|
|
887
|
-
size: "small",
|
|
888
|
-
onClick: ()=>setKeysFor(product),
|
|
889
|
-
children: 'Keys'
|
|
890
|
-
}) : null,
|
|
891
|
-
CommerceModel.productInventory(product) != null ? /*#__PURE__*/ _jsx(Button, {
|
|
892
|
-
size: "small",
|
|
893
|
-
onClick: ()=>{
|
|
894
|
-
var _ref, _ref1, _ref2;
|
|
895
|
-
var _product_variants_find, _find, _;
|
|
896
|
-
return setAdjusting({
|
|
897
|
-
product,
|
|
898
|
-
variantId: (_ref = (_product_variants_find = product.variants.find((variant)=>variant.inventory != null)) == null ? void 0 : _product_variants_find.id) != null ? _ref : product.variants[0].id,
|
|
899
|
-
delta: '',
|
|
900
|
-
reason: 'restock',
|
|
901
|
-
locationId: (_ref1 = (_ref2 = (_find = (locationDocs != null ? locationDocs : []).find((location)=>location.isDefault)) == null ? void 0 : _find.$id) != null ? _ref2 : (_ = (locationDocs != null ? locationDocs : [])[0]) == null ? void 0 : _.$id) != null ? _ref1 : ''
|
|
902
|
-
});
|
|
903
|
-
},
|
|
904
|
-
children: 'Stock'
|
|
905
|
-
}) : null,
|
|
906
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
907
|
-
size: "small",
|
|
908
|
-
onClick: handleStatus(product, product.status === 'archived' ? 'active' : 'archived'),
|
|
909
|
-
children: product.status === 'archived' ? 'Activate' : 'Archive'
|
|
910
|
-
}),
|
|
911
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
912
|
-
size: "small",
|
|
913
|
-
color: "error",
|
|
914
|
-
onClick: handleDelete(product),
|
|
915
|
-
children: 'Delete'
|
|
916
|
-
})
|
|
917
|
-
]
|
|
918
|
-
})
|
|
919
|
-
]
|
|
920
|
-
}, product.$id);
|
|
921
|
-
})
|
|
922
|
-
})
|
|
923
|
-
]
|
|
924
|
-
})
|
|
924
|
+
/*#__PURE__*/ _jsx(ListFilterChips, {
|
|
925
|
+
fields: PRODUCT_GRID_FILTER_FIELDS,
|
|
926
|
+
headers: PRODUCT_GRID_FILTER_HEADERS,
|
|
927
|
+
clauses: filterClauses,
|
|
928
|
+
onChange: gridFilter.setClauses,
|
|
929
|
+
options: PRODUCT_GRID_FILTER_OPTIONS,
|
|
930
|
+
servedField: servedStatus ? 'status' : null
|
|
931
|
+
}),
|
|
932
|
+
/*#__PURE__*/ _jsx(ListTable, {
|
|
933
|
+
"aria-label": "Products",
|
|
934
|
+
rows: visibleProducts,
|
|
935
|
+
columns: productColumns,
|
|
936
|
+
filterMode: "server",
|
|
937
|
+
filterModel: gridFilter.filterModel,
|
|
938
|
+
onFilterModelChange: gridFilter.onFilterModelChange,
|
|
939
|
+
// The search box drives the served query above.
|
|
940
|
+
quickFilter: true,
|
|
941
|
+
// `ListPagination` below pages what the card holds.
|
|
942
|
+
hideFooter: true,
|
|
943
|
+
noRowsLabel: search || filterClauses.length ? 'No products match these filters' : 'No products yet',
|
|
944
|
+
noRowsDescription: search || filterClauses.length ? undefined : 'Build your catalog: add a product, then drop commerce ' + 'blocks on any screen in Besigner.'
|
|
925
945
|
}),
|
|
926
946
|
products.length === 0 ? null : /*#__PURE__*/ _jsx(ListPagination, {
|
|
927
947
|
page: page,
|
|
@@ -936,7 +956,7 @@ const STATUS_COLOR = {
|
|
|
936
956
|
}),
|
|
937
957
|
catalogTruncated ? /*#__PURE__*/ _jsx(Alert, {
|
|
938
958
|
severity: "info",
|
|
939
|
-
children: `This table holds ${CATALOG_CEILING} products at a time and this ` + 'catalog is larger. Search
|
|
959
|
+
children: `This table holds ${CATALOG_CEILING} products at a time and this ` + 'catalog is larger. Search, and Status on its own, reach every ' + 'product; the CSV export covers what the table holds.'
|
|
940
960
|
}) : null
|
|
941
961
|
]
|
|
942
962
|
}),
|