@aglyn/plugins-commerce 1.0.0-beta.218 → 1.0.0-beta.219

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.
Files changed (95) hide show
  1. package/README.md +1 -0
  2. package/package.json +13 -13
  3. package/src/index.d.ts +1 -0
  4. package/src/index.js +1 -19
  5. package/src/index.js.map +1 -1
  6. package/src/lib/components/cart.js +2 -1
  7. package/src/lib/components/cart.js.map +1 -1
  8. package/src/lib/components/console/catalog-organization-card.component.js +44 -18
  9. package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
  10. package/src/lib/components/console/host-orders-card.component.js +2 -1
  11. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  12. package/src/lib/components/console/member-posts-card.component.js +1 -1
  13. package/src/lib/components/console/member-posts-card.component.js.map +1 -1
  14. package/src/lib/components/console/order-detail-dialog.component.js +8 -7
  15. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
  16. package/src/lib/components/console/payments-settings-card.component.js +1 -1
  17. package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
  18. package/src/lib/components/console/product-editor-dialog.component.js +114 -63
  19. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  20. package/src/lib/components/console/product-zones.d.ts +184 -0
  21. package/src/lib/components/console/product-zones.js +54 -0
  22. package/src/lib/components/console/product-zones.js.map +1 -0
  23. package/src/lib/components/console/products-hub-card.component.js +3 -2
  24. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  25. package/src/lib/components/console/products-hub-zone.component.d.ts +1 -1
  26. package/src/lib/components/console/products-hub-zone.component.js +2 -1
  27. package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
  28. package/src/lib/components/console/store-settings-card.component.js +2 -2
  29. package/src/lib/components/console/store-settings-card.component.js.map +1 -1
  30. package/src/lib/components/console/storefront-tax-summary-card.component.js +1 -1
  31. package/src/lib/components/console/storefront-tax-summary-card.component.js.map +1 -1
  32. package/src/lib/components/product-detail.js +2 -1
  33. package/src/lib/components/product-detail.js.map +1 -1
  34. package/src/lib/components/product-grid.js +1 -1
  35. package/src/lib/components/product-grid.js.map +1 -1
  36. package/src/lib/constants/storefront-payments.d.ts +33 -0
  37. package/src/lib/constants/storefront-payments.js +35 -0
  38. package/src/lib/constants/storefront-payments.js.map +1 -0
  39. package/src/lib/declarations.server.d.ts +3 -1
  40. package/src/lib/declarations.server.js +19 -1
  41. package/src/lib/declarations.server.js.map +1 -1
  42. package/src/lib/model/commerce-record-routes.d.ts +27 -0
  43. package/src/lib/model/commerce-record-routes.js +72 -0
  44. package/src/lib/model/commerce-record-routes.js.map +1 -0
  45. package/src/lib/model/commerce-search-sources.d.ts +21 -0
  46. package/src/lib/model/commerce-search-sources.js +40 -0
  47. package/src/lib/model/commerce-search-sources.js.map +1 -0
  48. package/src/lib/model/storefront-tax-summary.d.ts +121 -0
  49. package/src/lib/model/storefront-tax-summary.js +148 -0
  50. package/src/lib/model/storefront-tax-summary.js.map +1 -0
  51. package/src/lib/plan-entitlements.d.ts +35 -0
  52. package/src/lib/plan-entitlements.js +54 -0
  53. package/src/lib/plan-entitlements.js.map +1 -0
  54. package/src/lib/plugin.js +9 -0
  55. package/src/lib/plugin.js.map +1 -1
  56. package/src/lib/server/billing-webhook.js +135 -168
  57. package/src/lib/server/billing-webhook.js.map +1 -1
  58. package/src/lib/server/cart-checkout.js +2 -2
  59. package/src/lib/server/cart-checkout.js.map +1 -1
  60. package/src/lib/server/checkout.js +2 -2
  61. package/src/lib/server/checkout.js.map +1 -1
  62. package/src/lib/server/connect.js +5 -5
  63. package/src/lib/server/connect.js.map +1 -1
  64. package/src/lib/server/draft-order.js +2 -2
  65. package/src/lib/server/draft-order.js.map +1 -1
  66. package/src/lib/server/membership-content.js +1 -1
  67. package/src/lib/server/membership-content.js.map +1 -1
  68. package/src/lib/server/pos-order.js +2 -2
  69. package/src/lib/server/pos-order.js.map +1 -1
  70. package/src/lib/server/product-record-index.d.ts +19 -0
  71. package/src/lib/server/product-record-index.js +115 -0
  72. package/src/lib/server/product-record-index.js.map +1 -0
  73. package/src/lib/server/reserve.js +2 -2
  74. package/src/lib/server/reserve.js.map +1 -1
  75. package/src/lib/server/revenue-source.d.ts +130 -0
  76. package/src/lib/server/revenue-source.js +286 -0
  77. package/src/lib/server/revenue-source.js.map +1 -0
  78. package/src/lib/server/tax-profile.d.ts +8 -5
  79. package/src/lib/server/tax-profile.js +22 -5
  80. package/src/lib/server/tax-profile.js.map +1 -1
  81. package/src/lib/server/tax-return-source.d.ts +125 -0
  82. package/src/lib/server/tax-return-source.js +524 -0
  83. package/src/lib/server/tax-return-source.js.map +1 -0
  84. package/src/lib/server/tax-summary.d.ts +92 -0
  85. package/src/lib/server/tax-summary.js +258 -0
  86. package/src/lib/server/tax-summary.js.map +1 -0
  87. package/src/lib/server.d.ts +1 -0
  88. package/src/lib/server.js +23 -0
  89. package/src/lib/server.js.map +1 -1
  90. package/src/lib/tenant-emails.d.ts +33 -0
  91. package/src/lib/tenant-emails.js +511 -0
  92. package/src/lib/tenant-emails.js.map +1 -0
  93. package/src/lib/testing/stand-in-crm-record-routes.d.ts +25 -0
  94. package/src/lib/testing/stand-in-crm-record-routes.js +46 -0
  95. package/src/lib/testing/stand-in-crm-record-routes.js.map +1 -0
@@ -23,7 +23,7 @@ import { Timestamp } from "@aglyn/shared-util-timestamp";
23
23
  import { Alert, Autocomplete, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Divider, IconButton, MenuItem, Stack, TableBody, TableCell, TableHead, TableRow, TextField, Typography } from "@mui/material";
24
24
  import { collection, doc, getDoc } from "firebase/firestore";
25
25
  import { productCollectionFields } from "./smart-collections.js";
26
- import { useCallback, useMemo, useState } from "react";
26
+ import { memo, useCallback, useMemo, useRef, useState } from "react";
27
27
  import { ceilingedWindow, collectionCeiling, useFirestore, useFirestoreCollection, useHostResourceApi, writeGuardedBySeed, useUser } from "@aglyn/tenant-feature-instance";
28
28
  import { writeSiteWideChange } from "@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change";
29
29
  import { useMediaPicker } from "@aglyn/aglyn";
@@ -31,6 +31,7 @@ import { useConsoleWidgetSlot } from "@aglyn/aglyn/app-utils/console-widget-slot
31
31
  import { seoListingFieldCount, seoListingFieldTooLong } from "@aglyn/aglyn/app-utils/seo-listing-fields";
32
32
  import { ScrollTable } from "@aglyn/shared-ui-jsx/components/scroll-table.component";
33
33
  import { EntitlementUpsell, useCommerceEntitlement } from "./entitlement-gate.component.js";
34
+ import { PRODUCT_EDITOR_ZONE } from "./product-zones.js";
34
35
  import { MembersVideosField, PaidDownloadAddButton, PaidMediaProtection } from "./paid-media.js";
35
36
  /**
36
37
  * What each picker in this dialog will offer.
@@ -72,6 +73,49 @@ function comboLabel(options) {
72
73
  const values = Object.values(options != null ? options : {});
73
74
  return values.length ? values.join(' / ') : 'Default';
74
75
  }
76
+ /** Stand-in for a list the product does not hold yet, the same one each render. */ const NO_ITEMS = [];
77
+ const FILL_ROW = {
78
+ flex: 1
79
+ };
80
+ const categoryLabel = (category)=>category.name;
81
+ const recordLabel = (item)=>{
82
+ var _item_name;
83
+ return (_item_name = item.name) != null ? _item_name : item.$id;
84
+ };
85
+ const sameRecord = (option, value)=>option.$id === value.$id;
86
+ /**
87
+ * One of the editor's chip lists — tags, categories, an option's values,
88
+ * related products — MEMOIZED, and every prop it takes is stable while its
89
+ * own list is (AGL-3423).
90
+ *
91
+ * A multiple Autocomplete hands its input a new chip array as
92
+ * `startAdornment` on every render, and MUI's `InputBase` copies that into
93
+ * its `FormControl` from a passive effect: a state update left pending after
94
+ * every commit the field takes part in. React 19 counts each such commit as a
95
+ * nested update, and keystrokes delivered back to back commit one after
96
+ * another with nothing to clear the count, so the fifty-first throws
97
+ * "Maximum update depth exceeded" (#185). Every field in this editor writes
98
+ * one draft, so every keystroke in any of them re-rendered all four lists;
99
+ * kept out of those renders, a list commits only when it changes.
100
+ */ const ProductChipsField = /*#__PURE__*/ memo(function ProductChipsField(props) {
101
+ const { index = 0, onChange } = props;
102
+ return /*#__PURE__*/ _jsx(Autocomplete, {
103
+ multiple: true,
104
+ freeSolo: props.freeSolo,
105
+ options: props.options,
106
+ value: props.value,
107
+ getOptionLabel: props.getOptionLabel,
108
+ isOptionEqualToValue: props.isOptionEqualToValue,
109
+ onChange: (_event, next)=>onChange(next, index),
110
+ renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
111
+ label: props.label,
112
+ size: "small",
113
+ placeholder: props.placeholder,
114
+ helperText: props.helperText
115
+ })),
116
+ sx: props.fill ? FILL_ROW : undefined
117
+ });
118
+ });
75
119
  /**
76
120
  * Products hub editor (AGL-279): the full catalog editor — basics,
77
121
  * media, tags, options → variants matrix, per-variant pricing/stock, and
@@ -161,6 +205,42 @@ function comboLabel(options) {
161
205
  // Lazy-init per open; parent remounts via `key` on product change.
162
206
  const current = (_ref = draft != null ? draft : lifted) != null ? _ref : blankProduct();
163
207
  const update = (patch)=>setDraft(_extends({}, current, patch));
208
+ /*
209
+ * `update` for the memoized chip lists: the same write from the last
210
+ * rendered draft, from a callback that keeps its identity across renders.
211
+ */ const latest = useRef(current);
212
+ latest.current = current;
213
+ const updateLatest = useCallback((patch)=>setDraft(_extends({}, latest.current, patch)), []);
214
+ const setTags = useCallback((tags)=>updateLatest({
215
+ tags: tags.map((tag)=>String(tag).trim()).filter(Boolean)
216
+ }), [
217
+ updateLatest
218
+ ]);
219
+ const setCategories = useCallback((picked)=>updateLatest({
220
+ categoryIds: picked.map((category)=>category.$id)
221
+ }), [
222
+ updateLatest
223
+ ]);
224
+ const setRelatedProducts = useCallback((picked)=>updateLatest({
225
+ relatedProductIds: picked.map((item)=>item.$id)
226
+ }), [
227
+ updateLatest
228
+ ]);
229
+ const categoryIds = current.categoryIds;
230
+ const pickedCategories = useMemo(()=>categories.rows.filter((category)=>(categoryIds != null ? categoryIds : []).includes(category.$id)), [
231
+ categories.rows,
232
+ categoryIds
233
+ ]);
234
+ const relatedProductIds = current.relatedProductIds;
235
+ const productId = product == null ? void 0 : product.$id;
236
+ const relatedOptions = useMemo(()=>relatedProducts.rows.filter((item)=>!item.deletedAt && item.$id !== productId), [
237
+ relatedProducts.rows,
238
+ productId
239
+ ]);
240
+ const pickedRelated = useMemo(()=>relatedProducts.rows.filter((item)=>(relatedProductIds != null ? relatedProductIds : []).includes(item.$id)), [
241
+ relatedProducts.rows,
242
+ relatedProductIds
243
+ ]);
164
244
  /** The shell's zone renderer (AGL-2910); `null` outside the console shell. */ const WidgetSlot = useConsoleWidgetSlot();
165
245
  /**
166
246
  * A listing zone widget's proposal, staged into the draft like typing.
@@ -303,6 +383,11 @@ function comboLabel(options) {
303
383
  }, [
304
384
  current
305
385
  ]);
386
+ const latestOptionsChange = useRef(handleOptionsChange);
387
+ latestOptionsChange.current = handleOptionsChange;
388
+ const setOptionValues = useCallback((values, index)=>latestOptionsChange.current(index, {
389
+ values: values.map((value)=>String(value).trim()).filter(Boolean)
390
+ }), []);
306
391
  const handleVariantField = (index, field, raw)=>{
307
392
  const variants = [
308
393
  ...current.variants
@@ -633,39 +718,24 @@ function comboLabel(options) {
633
718
  multiline: true,
634
719
  minRows: 2
635
720
  }),
636
- /*#__PURE__*/ _jsx(Autocomplete, {
637
- multiple: true,
721
+ /*#__PURE__*/ _jsx(ProductChipsField, {
638
722
  freeSolo: true,
639
- options: [],
640
- value: (_current_tags1 = current.tags) != null ? _current_tags1 : [],
641
- onChange: (_event, tags)=>update({
642
- tags: tags.map((tag)=>String(tag).trim()).filter(Boolean)
643
- }),
644
- renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
645
- label: "Tags",
646
- size: "small",
647
- placeholder: "Type and press Enter"
648
- }))
723
+ label: "Tags",
724
+ placeholder: "Type and press Enter",
725
+ options: NO_ITEMS,
726
+ value: (_current_tags1 = current.tags) != null ? _current_tags1 : NO_ITEMS,
727
+ onChange: setTags
649
728
  }),
650
- categories.rows.length > 0 ? /*#__PURE__*/ _jsx(Autocomplete, {
651
- multiple: true,
729
+ categories.rows.length > 0 ? /*#__PURE__*/ _jsx(ProductChipsField, {
730
+ label: "Categories",
652
731
  options: categories.rows,
653
- getOptionLabel: (category)=>category.name,
654
- isOptionEqualToValue: (option, value)=>option.$id === value.$id,
655
- value: categories.rows.filter((category)=>{
656
- var _current_categoryIds;
657
- return ((_current_categoryIds = current.categoryIds) != null ? _current_categoryIds : []).includes(category.$id);
658
- }),
659
- onChange: (_event, picked)=>update({
660
- categoryIds: picked.map((category)=>category.$id)
661
- }),
662
- renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
663
- label: "Categories",
664
- size: "small"
665
- }))
732
+ getOptionLabel: categoryLabel,
733
+ isOptionEqualToValue: sameRecord,
734
+ value: pickedCategories,
735
+ onChange: setCategories
666
736
  }) : null,
667
737
  WidgetSlot ? /*#__PURE__*/ _jsx(WidgetSlot, {
668
- slot: "productEditor",
738
+ slot: PRODUCT_EDITOR_ZONE.id,
669
739
  hostId: hostId,
670
740
  orgId: undefined,
671
741
  product: zoneProduct,
@@ -761,22 +831,15 @@ function comboLabel(options) {
761
831
  },
762
832
  placeholder: "Size"
763
833
  }),
764
- /*#__PURE__*/ _jsx(Autocomplete, {
765
- multiple: true,
834
+ /*#__PURE__*/ _jsx(ProductChipsField, {
766
835
  freeSolo: true,
767
- options: [],
836
+ fill: true,
837
+ label: "Values",
838
+ placeholder: "S, M, L…",
839
+ options: NO_ITEMS,
768
840
  value: option.values,
769
- onChange: (_event, values)=>handleOptionsChange(index, {
770
- values: values.map((value)=>String(value).trim()).filter(Boolean)
771
- }),
772
- renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
773
- label: "Values",
774
- size: "small",
775
- placeholder: "S, M, L…"
776
- })),
777
- sx: {
778
- flex: 1
779
- }
841
+ index: index,
842
+ onChange: setOptionValues
780
843
  }),
781
844
  /*#__PURE__*/ _jsx(Button, {
782
845
  size: "small",
@@ -1283,26 +1346,14 @@ function comboLabel(options) {
1283
1346
  })
1284
1347
  ]
1285
1348
  }) : null,
1286
- /*#__PURE__*/ _jsx(Autocomplete, {
1287
- multiple: true,
1288
- options: relatedProducts.rows.filter((item)=>!item.deletedAt && item.$id !== (product == null ? void 0 : product.$id)),
1289
- getOptionLabel: (item)=>{
1290
- var _item_name;
1291
- return (_item_name = item.name) != null ? _item_name : item.$id;
1292
- },
1293
- isOptionEqualToValue: (option, value)=>option.$id === value.$id,
1294
- value: relatedProducts.rows.filter((item)=>{
1295
- var _current_relatedProductIds;
1296
- return ((_current_relatedProductIds = current.relatedProductIds) != null ? _current_relatedProductIds : []).includes(item.$id);
1297
- }),
1298
- onChange: (_event, picked)=>update({
1299
- relatedProductIds: picked.map((item)=>item.$id)
1300
- }),
1301
- renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
1302
- label: "Related products (upsells)",
1303
- size: "small",
1304
- helperText: "Shown by the Related products block; blank falls back to frequently-bought-together"
1305
- }))
1349
+ /*#__PURE__*/ _jsx(ProductChipsField, {
1350
+ label: "Related products (upsells)",
1351
+ helperText: "Shown by the Related products block; blank falls back to frequently-bought-together",
1352
+ options: relatedOptions,
1353
+ getOptionLabel: recordLabel,
1354
+ isOptionEqualToValue: sameRecord,
1355
+ value: pickedRelated,
1356
+ onChange: setRelatedProducts
1306
1357
  }),
1307
1358
  /*#__PURE__*/ _jsx(Divider, {
1308
1359
  textAlign: "left",