@aglyn/plugins-commerce 1.0.0-beta.220 → 1.0.0-beta.222

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 (130) hide show
  1. package/README.md +4 -2
  2. package/package.json +14 -13
  3. package/src/lib/components/console/catalog-organization-card.component.js +9 -0
  4. package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
  5. package/src/lib/components/console/discounts-card.component.js +8 -0
  6. package/src/lib/components/console/discounts-card.component.js.map +1 -1
  7. package/src/lib/components/console/gift-cards-card.component.js +8 -0
  8. package/src/lib/components/console/gift-cards-card.component.js.map +1 -1
  9. package/src/lib/components/console/host-coupons-card.component.js +8 -0
  10. package/src/lib/components/console/host-coupons-card.component.js.map +1 -1
  11. package/src/lib/components/console/host-orders-card.component.js +34 -70
  12. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  13. package/src/lib/components/console/product-zones.d.ts +7 -6
  14. package/src/lib/components/console/product-zones.js +5 -4
  15. package/src/lib/components/console/product-zones.js.map +1 -1
  16. package/src/lib/components/console/products-hub-card.component.js +86 -284
  17. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  18. package/src/lib/declarations.console-server.d.ts +38 -0
  19. package/src/lib/declarations.console-server.js +67 -0
  20. package/src/lib/declarations.console-server.js.map +1 -0
  21. package/src/lib/model/commerce-gift-cards.d.ts +26 -0
  22. package/src/lib/model/commerce-gift-cards.js +42 -0
  23. package/src/lib/model/commerce-gift-cards.js.map +1 -1
  24. package/src/lib/model/commerce-orders.d.ts +0 -57
  25. package/src/lib/model/commerce-orders.js +0 -80
  26. package/src/lib/model/commerce-orders.js.map +1 -1
  27. package/src/lib/model/index.d.ts +0 -1
  28. package/src/lib/model/index.js +0 -1
  29. package/src/lib/model/index.js.map +1 -1
  30. package/src/lib/plugin.js +5 -0
  31. package/src/lib/plugin.js.map +1 -1
  32. package/src/lib/server/api-v1/openapi.d.ts +27 -0
  33. package/src/lib/server/api-v1/openapi.js +219 -0
  34. package/src/lib/server/api-v1/openapi.js.map +1 -0
  35. package/src/lib/server/api-v1/orders-and-products.d.ts +19 -0
  36. package/src/lib/server/api-v1/orders-and-products.js +419 -0
  37. package/src/lib/server/api-v1/orders-and-products.js.map +1 -0
  38. package/src/lib/server/billing-webhook.js +83 -56
  39. package/src/lib/server/billing-webhook.js.map +1 -1
  40. package/src/lib/server/download.js +4 -1
  41. package/src/lib/server/download.js.map +1 -1
  42. package/src/lib/server/fulfill-order.d.ts +52 -11
  43. package/src/lib/server/fulfill-order.js +6 -9
  44. package/src/lib/server/fulfill-order.js.map +1 -1
  45. package/src/lib/server/gift-card-issue.d.ts +57 -0
  46. package/src/lib/server/gift-card-issue.js +155 -0
  47. package/src/lib/server/gift-card-issue.js.map +1 -0
  48. package/src/lib/server/gift-cards.js +25 -84
  49. package/src/lib/server/gift-cards.js.map +1 -1
  50. package/src/lib/server/membership-register.js +9 -8
  51. package/src/lib/server/membership-register.js.map +1 -1
  52. package/src/lib/server/newsletter.js +7 -5
  53. package/src/lib/server/newsletter.js.map +1 -1
  54. package/src/lib/server/paid-media-delivery.d.ts +21 -0
  55. package/src/lib/server/paid-media-delivery.js +23 -5
  56. package/src/lib/server/paid-media-delivery.js.map +1 -1
  57. package/src/lib/server/paid-media-serve-count.d.ts +28 -0
  58. package/src/lib/server/paid-media-serve-count.js +46 -0
  59. package/src/lib/server/paid-media-serve-count.js.map +1 -0
  60. package/src/lib/server/person-eraser.d.ts +45 -0
  61. package/src/lib/server/person-eraser.js +57 -0
  62. package/src/lib/server/person-eraser.js.map +1 -0
  63. package/src/lib/server/pos-order.js +17 -9
  64. package/src/lib/server/pos-order.js.map +1 -1
  65. package/src/lib/server/refund.js +15 -15
  66. package/src/lib/server/refund.js.map +1 -1
  67. package/src/lib/server/restock-flag.d.ts +2 -2
  68. package/src/lib/server/restock-flag.js +1 -1
  69. package/src/lib/server/restock-flag.js.map +1 -1
  70. package/src/lib/server/stream.js +4 -1
  71. package/src/lib/server/stream.js.map +1 -1
  72. package/src/lib/server.js +5 -21
  73. package/src/lib/server.js.map +1 -1
  74. package/src/lib/testing/stand-in-person-records.d.ts +43 -0
  75. package/src/lib/testing/stand-in-person-records.js +63 -0
  76. package/src/lib/testing/stand-in-person-records.js.map +1 -0
  77. package/src/lib/testing/stand-in-record-system.d.ts +11 -2
  78. package/src/lib/testing/stand-in-record-system.js +5 -3
  79. package/src/lib/testing/stand-in-record-system.js.map +1 -1
  80. package/src/lib/transfer/gift-card-confirm-step.component.d.ts +22 -0
  81. package/src/lib/transfer/gift-card-confirm-step.component.js +259 -0
  82. package/src/lib/transfer/gift-card-confirm-step.component.js.map +1 -0
  83. package/src/lib/transfer/gift-cards-plan.d.ts +29 -0
  84. package/src/lib/transfer/gift-cards-plan.js +40 -0
  85. package/src/lib/transfer/gift-cards-plan.js.map +1 -0
  86. package/src/lib/transfer/gift-cards.server.d.ts +34 -0
  87. package/src/lib/transfer/gift-cards.server.js +296 -0
  88. package/src/lib/transfer/gift-cards.server.js.map +1 -0
  89. package/src/lib/transfer/list-filter.d.ts +29 -0
  90. package/src/lib/transfer/list-filter.js +112 -0
  91. package/src/lib/transfer/list-filter.js.map +1 -0
  92. package/src/lib/transfer/product-import-results.d.ts +29 -0
  93. package/src/lib/transfer/product-import-results.js +49 -0
  94. package/src/lib/transfer/product-import-results.js.map +1 -0
  95. package/src/lib/transfer/product-import-step.component.d.ts +30 -0
  96. package/src/lib/transfer/product-import-step.component.js +99 -0
  97. package/src/lib/transfer/product-import-step.component.js.map +1 -0
  98. package/src/lib/transfer/product-transfer.d.ts +211 -0
  99. package/src/lib/transfer/product-transfer.js +1594 -0
  100. package/src/lib/transfer/product-transfer.js.map +1 -0
  101. package/src/lib/transfer/products.server.d.ts +44 -0
  102. package/src/lib/transfer/products.server.js +550 -0
  103. package/src/lib/transfer/products.server.js.map +1 -0
  104. package/src/lib/transfer/records-transfer.d.ts +182 -0
  105. package/src/lib/transfer/records-transfer.js +1153 -0
  106. package/src/lib/transfer/records-transfer.js.map +1 -0
  107. package/src/lib/transfer/records.server.d.ts +68 -0
  108. package/src/lib/transfer/records.server.js +455 -0
  109. package/src/lib/transfer/records.server.js.map +1 -0
  110. package/src/lib/transfer/register-transfer-resources.d.ts +17 -0
  111. package/src/lib/transfer/register-transfer-resources.js +171 -0
  112. package/src/lib/transfer/register-transfer-resources.js.map +1 -0
  113. package/src/lib/transfer/register-transfer-ui.d.ts +23 -0
  114. package/src/lib/transfer/register-transfer-ui.js +78 -0
  115. package/src/lib/transfer/register-transfer-ui.js.map +1 -0
  116. package/src/lib/transfer/server-common.d.ts +76 -0
  117. package/src/lib/transfer/server-common.js +165 -0
  118. package/src/lib/transfer/server-common.js.map +1 -0
  119. package/src/lib/transfer/transfer-header-actions.component.d.ts +39 -0
  120. package/src/lib/transfer/transfer-header-actions.component.js +79 -0
  121. package/src/lib/transfer/transfer-header-actions.component.js.map +1 -0
  122. package/src/lib/transfer/transfer-keys.d.ts +31 -0
  123. package/src/lib/transfer/transfer-keys.js +56 -0
  124. package/src/lib/transfer/transfer-keys.js.map +1 -0
  125. package/src/lib/model/commerce-io.d.ts +0 -36
  126. package/src/lib/model/commerce-io.js +0 -245
  127. package/src/lib/model/commerce-io.js.map +0 -1
  128. package/src/lib/server/contact-refund.d.ts +0 -117
  129. package/src/lib/server/contact-refund.js +0 -196
  130. package/src/lib/server/contact-refund.js.map +0 -1
@@ -17,6 +17,7 @@
17
17
  import { _ as _extends } from "@swc/helpers/_/_extends";
18
18
  import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
19
19
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
20
+ import { TransferResumeImport } from "@aglyn/aglyn/app-utils/transfer-resume-import";
20
21
  import * as Aglyn from "@aglyn/aglyn";
21
22
  import * as CommerceModel from "../../model/index.js";
22
23
  import { PRODUCT_LIST_BASE, PRODUCT_LIST_FIELDS, PRODUCT_LIST_HEADERS, PRODUCT_LIST_OPTIONS, PRODUCT_LIST_QUERY, PRODUCT_LIST_SELECT_FIELDS, productListRequestClauses } from "../../constants/product-list-query.js";
@@ -32,30 +33,21 @@ import { useListGridFilter } from "@aglyn/shared-ui-jsx/hooks/use-list-grid-filt
32
33
  import { useSnackbar } from "@aglyn/shared-ui-snackstack";
33
34
  import { Timestamp } from "@aglyn/shared-util-timestamp";
34
35
  import { Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, MenuItem, Stack, TextField, Typography } from "@mui/material";
35
- import { addDoc, collection, doc, documentId, getCountFromServer, getDocs, limit, orderBy, query, startAfter, updateDoc, where } from "firebase/firestore";
36
+ import { addDoc, collection, doc, documentId, getCountFromServer, limit, orderBy, query, updateDoc, where } from "firebase/firestore";
36
37
  import { useCallback, useEffect, useMemo, useState } from "react";
37
38
  import { useFirestore, useUser } from "@aglyn/tenant-feature-instance";
38
39
  import { writeSiteWideChange } from "@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change";
39
40
  import { useFirestoreCollection, writeGuardedBySeed } from "@aglyn/tenant-feature-instance";
40
- import { listQueryConstraints, useListQuery } from "@aglyn/tenant-feature-instance/hooks/use-list-query";
41
+ import { useListQuery } from "@aglyn/tenant-feature-instance/hooks/use-list-query";
41
42
  import { useHostResourceApi } from "@aglyn/tenant-feature-instance";
42
43
  import { useOrgPlan } from "@aglyn/tenant-feature-instance";
43
44
  import ProductEditorDialog from "./product-editor-dialog.component.js";
44
45
  import { productSlugLedger } from "./product-slugs.js";
45
- import { productCollectionFields, readSmartCollections } from "./smart-collections.js";
46
+ import { productCollectionFields } from "./smart-collections.js";
46
47
  import ProductsHubZone from "./products-hub-zone.component.js";
47
48
  import { pluginDocsHelp } from "@aglyn/aglyn";
48
- import { useConsoleWidgetSlot } from "@aglyn/aglyn/app-utils/console-widget-slot-context";
49
- import { PRODUCT_IMPORT_ZONE } from "./product-zones.js";
50
- /** Products read per request of the CSV export's walk. */ const EXPORT_PAGE = 500;
51
- /**
52
- * The most products one CSV export writes (AGL-3321).
53
- *
54
- * The export walks the table's own query page by page, so it writes what the
55
- * filters and search match across the whole catalog rather than the rows on
56
- * screen. It stops here because the file is built in the browser, and says
57
- * so when it does.
58
- */ const EXPORT_CEILING = 10000;
49
+ import { commerceListFilter } from "../../transfer/list-filter.js";
50
+ import { COMMERCE_PRODUCTS_TRANSFER } from "../../transfer/transfer-keys.js";
59
51
  /**
60
52
  * How many of ONE product's license keys the dialog reads.
61
53
  *
@@ -83,8 +75,7 @@ const STATUS_COLOR = {
83
75
  * gated here on create/duplicate/import (AGL-471); server-side
84
76
  * enforcement of the client-write path rides AGL-473.
85
77
  */ export function ProductsHubCard(props) {
86
- var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _ref10, _ref11, _ref12;
87
- var _importing_parsed, _importing_parsed1, _importing_parsed2;
78
+ var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _ref10, _ref11;
88
79
  const { hostId } = props;
89
80
  const firestore = useFirestore();
90
81
  const { data: user } = useUser();
@@ -105,16 +96,14 @@ const STATUS_COLOR = {
105
96
  const [editing, setEditing] = useState(null);
106
97
  const [creating, setCreating] = useState(false);
107
98
  const [adjusting, setAdjusting] = useState(null);
108
- const [importing, setImporting] = useState(null);
109
99
  const [keysFor, setKeysFor] = useState(null);
110
100
  const [keysText, setKeysText] = useState('');
111
101
  /**
112
- * What the import zone set for the import in the dialog, and what the last
113
- * import created with those options (AGL-2916), which the hub's zone hands
114
- * to its widgets.
115
- */ const [importOptions, setImportOptions] = useState({});
116
- const [lastImport, setLastImport] = useState(null);
117
- const WidgetSlot = useConsoleWidgetSlot();
102
+ * What the last import created, with the options its After import step
103
+ * set (AGL-2916, AGL-3531), which the hub's zone hands to its widgets.
104
+ */ const [lastImport, setLastImport] = useState(null);
105
+ // The console's import wizard and export dialog; none outside the console.
106
+ const transfer = Aglyn.useTransferLauncher();
118
107
  const { rows: productDocs, data: productWindow, hasMore, page, setPage, pageSize, setPageSize, plan, status: productsStatus, /**
119
108
  * The product rows the stock dialog is seeded from are unconfirmed by
120
109
  * the server (AGL-1358). A stock adjustment does not write the delta —
@@ -416,148 +405,55 @@ const STATUS_COLOR = {
416
405
  enqueueSnackbar
417
406
  ]);
418
407
  /*
419
- * CSV import/export (AGL-282): Shopify-dialect columns, dry-run first.
408
+ * Import and export (AGL-3531) run on the console's transfer wizard and
409
+ * dialog, on the `commerce.products` resource: a product matched by
410
+ * handle, SKU or ID is updated, every step is a choice, and the export
411
+ * writes what the table's query matches — its filters and search — on the
412
+ * server, a row per variant, with a Shopify preset.
420
413
  *
421
- * The export writes what the table's filters and search MATCH, across the
422
- * whole catalog (AGL-3321): it walks the table's own query — the same plan,
423
- * so the same scope, predicates and order — a page of `EXPORT_PAGE` at a
424
- * time from a cursor, rather than writing the rows on screen. A walk that
425
- * reaches `EXPORT_CEILING` stops, asks once whether anything is left, and
426
- * says so when something is.
427
- */ const [exporting, setExporting] = useState(false);
428
- const handleExport = useCallback(async ()=>{
429
- setExporting(true);
430
- try {
431
- const productsRef = collection(firestore, 'hosts', hostId, 'products');
432
- const constraints = listQueryConstraints(plan);
433
- const exported = [];
434
- let cursor = null;
435
- let more = false;
436
- for(;;){
437
- const room = EXPORT_CEILING - exported.length;
438
- const snapshot = await getDocs(query(productsRef, ...constraints, ...cursor ? [
439
- startAfter(cursor)
440
- ] : [], limit(room > 0 ? Math.min(EXPORT_PAGE, room) : 1)));
441
- if (room <= 0) {
442
- more = !snapshot.empty;
443
- break;
444
- }
445
- for (const product of snapshot.docs){
446
- exported.push(_extends({}, CommerceModel.liftLegacyProduct(product.data()), {
447
- $id: product.id
448
- }));
449
- }
450
- if (snapshot.docs.length < Math.min(EXPORT_PAGE, room)) break;
451
- cursor = snapshot.docs[snapshot.docs.length - 1];
452
- }
453
- const csv = CommerceModel.productsToCsv(exported);
454
- const blob = new Blob([
455
- csv
456
- ], {
457
- type: 'text/csv'
458
- });
459
- const anchor = document.createElement('a');
460
- anchor.href = URL.createObjectURL(blob);
461
- anchor.download = `products-${hostId}.csv`;
462
- anchor.click();
463
- URL.revokeObjectURL(anchor.href);
464
- if (more) {
465
- enqueueSnackbar(`Exported the first ${EXPORT_CEILING.toLocaleString()} matching ` + 'products — the most one file holds. Filter the list to export the rest.', {
466
- variant: 'info',
467
- persist: false
468
- });
414
+ * When the wizard closes on its results, the products the import created
415
+ * reach the hub's zone with the options its After import step set.
416
+ */ const openImport = useCallback(()=>{
417
+ transfer == null ? void 0 : transfer.openImport({
418
+ resource: COMMERCE_PRODUCTS_TRANSFER,
419
+ scope: 'host',
420
+ hostId,
421
+ onFinished: ()=>{
422
+ setProductCountEpoch((epoch)=>epoch + 1);
423
+ void (async ()=>{
424
+ const { takeProductImport, createdProductIds } = await import("../../transfer/product-import-results.js");
425
+ const finished = takeProductImport(hostId);
426
+ if (!finished) return;
427
+ const productIds = await createdProductIds(user, finished);
428
+ setLastImport({
429
+ key: `${finished.jobId}-${productIds.length}`,
430
+ productIds,
431
+ options: finished.options
432
+ });
433
+ })();
469
434
  }
470
- } catch (error) {
471
- var _ref;
472
- enqueueSnackbar((_ref = error == null ? void 0 : error.message) != null ? _ref : 'Could not export the products', {
473
- variant: 'warning',
474
- persist: false
475
- });
476
- } finally{
477
- setExporting(false);
478
- }
435
+ });
479
436
  }, [
480
- firestore,
437
+ transfer,
481
438
  hostId,
482
- plan,
483
- enqueueSnackbar
439
+ user
484
440
  ]);
485
- const handleImportApply = useCallback(async ()=>{
486
- const parsed = importing == null ? void 0 : importing.parsed;
487
- if (!parsed || parsed.products.length === 0) return;
488
- // Batch-aware cap (AGL-471): the whole import must fit the plan.
489
- // Not startable until the plan is known (AGL-1064) — the old `org ?`
490
- // guard let an import begin against an unknown cap and leaned on the
491
- // API rejecting it partway through, leaving a half-imported catalog.
492
- if (!planReady) return;
493
- const batchQuota = Aglyn.checkQuota(org, 'productsPerHost', productCount + parsed.products.length - 1);
494
- if (!batchQuota.allowed) {
495
- return void enqueueSnackbar(`This import needs ${parsed.products.length} product slots — your ` + `plan allows ${batchQuota.limit}. See Billing to upgrade.`, {
496
- variant: 'info',
497
- persist: false
498
- });
499
- }
500
- /*
501
- * The duplicate-slug check asks the STORE (AGL-3321): every slug the file
502
- * names, by `slug in […]`, and any suffixed one only when it is needed.
503
- * It asked the table's rows, which held five hundred products and now
504
- * hold one page, so a slug past them was handed out a second time.
505
- */ const slugs = productSlugLedger(firestore, hostId);
506
- const created = [];
507
- try {
508
- await slugs.ask(parsed.products.map((product)=>product.slug));
509
- // One read of the host's smart collections for the whole file.
510
- const smartCollections = await readSmartCollections(firestore, hostId);
511
- // Each create rides the quota-enforcing API (AGL-473); the batch cap
512
- // above short-circuits before we start, so this loop stays bounded.
513
- for (const product of parsed.products){
514
- var _ref, _ref1;
515
- var _product_variants_, _product_mediaUrls;
516
- const slug = await slugs.claim(product.slug);
517
- const { id } = await createHostResource({
518
- hostId,
519
- resource: 'product',
520
- data: _extends({}, product, CommerceModel.productSearchFields(product), CommerceModel.productStockFields(product), {
521
- // Its smart collections (AGL-3321).
522
- collectionIds: CommerceModel.productCollectionIds(product, smartCollections),
523
- slug,
524
- priceUsd: (_ref = (_product_variants_ = product.variants[0]) == null ? void 0 : _product_variants_.priceUsd) != null ? _ref : 0,
525
- imageUrl: (_ref1 = (_product_mediaUrls = product.mediaUrls) == null ? void 0 : _product_mediaUrls[0]) != null ? _ref1 : null,
526
- createdAtMs: Date.now(),
527
- updatedAtMs: Date.now()
528
- })
529
- });
530
- created.push(id);
441
+ const openExport = useCallback(()=>{
442
+ transfer == null ? void 0 : transfer.openExport(_extends({
443
+ resource: COMMERCE_PRODUCTS_TRANSFER,
444
+ scope: 'host',
445
+ hostId
446
+ }, filtering ? {
447
+ filter: {
448
+ label: 'the products the list’s filters and search find',
449
+ value: commerceListFilter(plan)
531
450
  }
532
- } catch (error) {
533
- var _ref2;
534
- return void enqueueSnackbar((_ref2 = error == null ? void 0 : error.message) != null ? _ref2 : 'Import failed', {
535
- variant: 'warning',
536
- persist: false
537
- });
538
- }
539
- setLastImport({
540
- key: `${Date.now().toString(36)}-${created.length}`,
541
- productIds: created,
542
- options: importOptions
543
- });
544
- setImportOptions({});
545
- setImporting(null);
546
- setProductCountEpoch((epoch)=>epoch + 1);
547
- enqueueSnackbar(`Imported ${parsed.products.length} products`, {
548
- variant: 'success',
549
- persist: false
550
- });
451
+ } : {}));
551
452
  }, [
552
- importing,
553
- firestore,
453
+ transfer,
554
454
  hostId,
555
- createHostResource,
556
- enqueueSnackbar,
557
- org,
558
- planReady,
559
- productCount,
560
- importOptions
455
+ filtering,
456
+ plan
561
457
  ]);
562
458
  const handleAdjustSave = useCallback(async ()=>{
563
459
  if (!adjusting) return;
@@ -630,9 +526,6 @@ const STATUS_COLOR = {
630
526
  productCount
631
527
  ]);
632
528
  const onCatalogCreated = useCallback(()=>setProductCountEpoch((epoch)=>epoch + 1), []);
633
- const setImportOption = useCallback((key, on)=>setImportOptions((current)=>_extends({}, current, {
634
- [key]: on
635
- })), []);
636
529
  const formatPrice = (product)=>{
637
530
  const [min, max] = CommerceModel.productPriceRange(product);
638
531
  return min === max ? `$${min}` : `$${min}–$${max}`;
@@ -844,25 +737,35 @@ const STATUS_COLOR = {
844
737
  },
845
738
  children: 'Add product'
846
739
  }),
847
- /*#__PURE__*/ _jsx(Button, {
740
+ /*#__PURE__*/ _jsx(TransferResumeImport, {
741
+ target: {
742
+ resource: COMMERCE_PRODUCTS_TRANSFER,
743
+ scope: 'host',
744
+ hostId
745
+ },
746
+ onFinished: ()=>setProductCountEpoch((epoch)=>epoch + 1)
747
+ }),
748
+ (transfer == null ? void 0 : transfer.can('import', {
749
+ resource: COMMERCE_PRODUCTS_TRANSFER,
750
+ scope: 'host',
751
+ hostId
752
+ })) ? /*#__PURE__*/ _jsx(Button, {
848
753
  size: "small",
849
754
  disabled: !planReady,
850
- onClick: ()=>{
851
- setImportOptions({});
852
- setImporting({
853
- text: '',
854
- parsed: null
855
- });
856
- },
755
+ onClick: openImport,
857
756
  children: 'Import'
858
- }),
859
- /*#__PURE__*/ _jsx(Button, {
757
+ }) : null,
758
+ (transfer == null ? void 0 : transfer.can('export', {
759
+ resource: COMMERCE_PRODUCTS_TRANSFER,
760
+ scope: 'host',
761
+ hostId
762
+ })) ? /*#__PURE__*/ _jsx(Button, {
860
763
  size: "small",
861
- // Nothing matches: there is nothing for the walk to write.
862
- disabled: exporting || products.length === 0 && page === 0,
863
- onClick: handleExport,
864
- children: exporting ? 'Exporting…' : 'Export'
865
- })
764
+ // Nothing matches: there is nothing for the export to write.
765
+ disabled: products.length === 0 && page === 0,
766
+ onClick: openExport,
767
+ children: 'Export'
768
+ }) : null
866
769
  ]
867
770
  }),
868
771
  /*#__PURE__*/ _jsx(QuotaReadoutComponent, {
@@ -1037,107 +940,6 @@ const STATUS_COLOR = {
1037
940
  })
1038
941
  ]
1039
942
  }),
1040
- /*#__PURE__*/ _jsxs(Dialog, {
1041
- open: Boolean(importing),
1042
- onClose: ()=>setImporting(null),
1043
- maxWidth: "sm",
1044
- fullWidth: true,
1045
- children: [
1046
- /*#__PURE__*/ _jsx(DialogTitle, {
1047
- children: 'Import products (CSV)'
1048
- }),
1049
- /*#__PURE__*/ _jsxs(DialogContent, {
1050
- sx: {
1051
- display: 'flex',
1052
- flexDirection: 'column',
1053
- gap: 2
1054
- },
1055
- children: [
1056
- /*#__PURE__*/ _jsx(Typography, {
1057
- variant: "body2",
1058
- color: "text.secondary",
1059
- sx: {
1060
- mt: 1
1061
- },
1062
- children: 'Shopify-compatible columns (Handle, Title, Option/Variant ' + 'columns, Image Src). Paste the file contents or choose a file.'
1063
- }),
1064
- /*#__PURE__*/ _jsxs(Button, {
1065
- component: "label",
1066
- size: "small",
1067
- sx: {
1068
- alignSelf: 'flex-start'
1069
- },
1070
- children: [
1071
- 'Choose file',
1072
- /*#__PURE__*/ _jsx("input", {
1073
- type: "file",
1074
- accept: ".csv,text/csv",
1075
- hidden: true,
1076
- onChange: async (event)=>{
1077
- var _event_target_files;
1078
- const file = (_event_target_files = event.target.files) == null ? void 0 : _event_target_files[0];
1079
- if (!file) return;
1080
- const text = await file.text();
1081
- setImporting({
1082
- text,
1083
- parsed: CommerceModel.parseProductsCsv(text)
1084
- });
1085
- }
1086
- })
1087
- ]
1088
- }),
1089
- /*#__PURE__*/ _jsx(TextField, {
1090
- label: "CSV",
1091
- value: (_ref4 = importing == null ? void 0 : importing.text) != null ? _ref4 : '',
1092
- onChange: (event)=>setImporting({
1093
- text: event.target.value,
1094
- parsed: event.target.value.trim() ? CommerceModel.parseProductsCsv(event.target.value) : null
1095
- }),
1096
- size: "small",
1097
- multiline: true,
1098
- minRows: 5,
1099
- maxRows: 10
1100
- }),
1101
- (importing == null ? void 0 : importing.parsed) ? /*#__PURE__*/ _jsxs(_Fragment, {
1102
- children: [
1103
- /*#__PURE__*/ _jsx(Typography, {
1104
- variant: "body2",
1105
- children: `Ready to import ${importing.parsed.products.length} products` + (importing.parsed.errors.length ? ` — ${importing.parsed.errors.length} rows skipped:` : '')
1106
- }),
1107
- importing.parsed.errors.slice(0, 5).map((error)=>/*#__PURE__*/ _jsx(Typography, {
1108
- variant: "caption",
1109
- color: "warning.main",
1110
- children: error
1111
- }, error))
1112
- ]
1113
- }) : null,
1114
- WidgetSlot && (importing == null ? void 0 : (_importing_parsed = importing.parsed) == null ? void 0 : _importing_parsed.products.length) ? /*#__PURE__*/ _jsx(WidgetSlot, {
1115
- slot: PRODUCT_IMPORT_ZONE.id,
1116
- hostId: hostId,
1117
- orgId: undefined,
1118
- count: importing.parsed.products.length,
1119
- options: importOptions,
1120
- setOption: setImportOption
1121
- }) : null
1122
- ]
1123
- }),
1124
- /*#__PURE__*/ _jsxs(DialogActions, {
1125
- children: [
1126
- /*#__PURE__*/ _jsx(Button, {
1127
- onClick: ()=>setImporting(null),
1128
- children: 'Cancel'
1129
- }),
1130
- /*#__PURE__*/ _jsx(Button, {
1131
- variant: "contained",
1132
- color: "primary",
1133
- disabled: !(importing == null ? void 0 : (_importing_parsed1 = importing.parsed) == null ? void 0 : _importing_parsed1.products.length),
1134
- onClick: handleImportApply,
1135
- children: `Import${(importing == null ? void 0 : (_importing_parsed2 = importing.parsed) == null ? void 0 : _importing_parsed2.products.length) ? ` ${importing.parsed.products.length}` : ''}`
1136
- })
1137
- ]
1138
- })
1139
- ]
1140
- }),
1141
943
  /*#__PURE__*/ _jsxs(Dialog, {
1142
944
  open: Boolean(adjusting),
1143
945
  onClose: ()=>setAdjusting(null),
@@ -1145,7 +947,7 @@ const STATUS_COLOR = {
1145
947
  fullWidth: true,
1146
948
  children: [
1147
949
  /*#__PURE__*/ _jsx(DialogTitle, {
1148
- children: `Adjust stock — ${(_ref5 = adjusting == null ? void 0 : adjusting.product.name) != null ? _ref5 : ''}`
950
+ children: `Adjust stock — ${(_ref4 = adjusting == null ? void 0 : adjusting.product.name) != null ? _ref4 : ''}`
1149
951
  }),
1150
952
  /*#__PURE__*/ _jsxs(DialogContent, {
1151
953
  sx: {
@@ -1156,7 +958,7 @@ const STATUS_COLOR = {
1156
958
  children: [
1157
959
  /*#__PURE__*/ _jsx(TextField, {
1158
960
  label: "Variant",
1159
- value: (_ref6 = adjusting == null ? void 0 : adjusting.variantId) != null ? _ref6 : '',
961
+ value: (_ref5 = adjusting == null ? void 0 : adjusting.variantId) != null ? _ref5 : '',
1160
962
  onChange: (event)=>setAdjusting((prev)=>prev ? _extends({}, prev, {
1161
963
  variantId: event.target.value
1162
964
  }) : prev),
@@ -1165,7 +967,7 @@ const STATUS_COLOR = {
1165
967
  sx: {
1166
968
  mt: 1
1167
969
  },
1168
- children: ((_ref7 = adjusting == null ? void 0 : adjusting.product.variants) != null ? _ref7 : []).filter((variant)=>variant.inventory != null).map((variant)=>{
970
+ children: ((_ref6 = adjusting == null ? void 0 : adjusting.product.variants) != null ? _ref6 : []).filter((variant)=>variant.inventory != null).map((variant)=>{
1169
971
  var _variant_options;
1170
972
  return /*#__PURE__*/ _jsx(MenuItem, {
1171
973
  value: variant.id,
@@ -1173,9 +975,9 @@ const STATUS_COLOR = {
1173
975
  }, variant.id);
1174
976
  })
1175
977
  }),
1176
- ((_ref8 = locationDocs == null ? void 0 : locationDocs.length) != null ? _ref8 : 0) > 1 ? /*#__PURE__*/ _jsx(TextField, {
978
+ ((_ref7 = locationDocs == null ? void 0 : locationDocs.length) != null ? _ref7 : 0) > 1 ? /*#__PURE__*/ _jsx(TextField, {
1177
979
  label: "Location",
1178
- value: (_ref9 = adjusting == null ? void 0 : adjusting.locationId) != null ? _ref9 : '',
980
+ value: (_ref8 = adjusting == null ? void 0 : adjusting.locationId) != null ? _ref8 : '',
1179
981
  onChange: (event)=>setAdjusting((prev)=>prev ? _extends({}, prev, {
1180
982
  locationId: event.target.value
1181
983
  }) : prev),
@@ -1189,7 +991,7 @@ const STATUS_COLOR = {
1189
991
  /*#__PURE__*/ _jsx(TextField, {
1190
992
  label: "Change",
1191
993
  placeholder: "+10 or -3",
1192
- value: (_ref10 = adjusting == null ? void 0 : adjusting.delta) != null ? _ref10 : '',
994
+ value: (_ref9 = adjusting == null ? void 0 : adjusting.delta) != null ? _ref9 : '',
1193
995
  onChange: (event)=>setAdjusting((prev)=>prev ? _extends({}, prev, {
1194
996
  delta: event.target.value.replace(/[^0-9+-]/g, '')
1195
997
  }) : prev),
@@ -1197,7 +999,7 @@ const STATUS_COLOR = {
1197
999
  }),
1198
1000
  /*#__PURE__*/ _jsxs(TextField, {
1199
1001
  label: "Reason",
1200
- value: (_ref11 = adjusting == null ? void 0 : adjusting.reason) != null ? _ref11 : 'restock',
1002
+ value: (_ref10 = adjusting == null ? void 0 : adjusting.reason) != null ? _ref10 : 'restock',
1201
1003
  onChange: (event)=>setAdjusting((prev)=>prev ? _extends({}, prev, {
1202
1004
  reason: event.target.value
1203
1005
  }) : prev),
@@ -1258,7 +1060,7 @@ const STATUS_COLOR = {
1258
1060
  // keeps the cap off the dialog's shoulders (AGL-1716).
1259
1061
  setProductCountEpoch((epoch)=>epoch + 1);
1260
1062
  }
1261
- }, (_ref12 = editing == null ? void 0 : editing.$id) != null ? _ref12 : creating ? 'new' : 'closed')
1063
+ }, (_ref11 = editing == null ? void 0 : editing.$id) != null ? _ref11 : creating ? 'new' : 'closed')
1262
1064
  ]
1263
1065
  });
1264
1066
  }