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

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 (83) hide show
  1. package/README.md +5 -0
  2. package/package.json +14 -14
  3. package/src/lib/components/console/catalog-organization-card.component.js +626 -446
  4. package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
  5. package/src/lib/components/console/payments-settings-card.component.js +1 -1
  6. package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
  7. package/src/lib/components/console/product-editor-dialog.component.js +1127 -846
  8. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  9. package/src/lib/components/console/site-member-purchases.component.d.ts +24 -0
  10. package/src/lib/components/console/site-member-purchases.component.js +339 -0
  11. package/src/lib/components/console/site-member-purchases.component.js.map +1 -0
  12. package/src/lib/components/member-signup.js +2 -2
  13. package/src/lib/components/member-signup.js.map +1 -1
  14. package/src/lib/components/newsletter-signup.js +2 -2
  15. package/src/lib/components/newsletter-signup.js.map +1 -1
  16. package/src/lib/constants/operator-alerts.js +5 -3
  17. package/src/lib/constants/operator-alerts.js.map +1 -1
  18. package/src/lib/model/commerce-io.js +1 -1
  19. package/src/lib/model/commerce-io.js.map +1 -1
  20. package/src/lib/model/commerce-record-lists.d.ts +31 -0
  21. package/src/lib/model/commerce-record-lists.js +45 -0
  22. package/src/lib/model/commerce-record-lists.js.map +1 -0
  23. package/src/lib/model/commerce-record-routes.d.ts +18 -0
  24. package/src/lib/model/commerce-record-routes.js +1 -1
  25. package/src/lib/model/commerce-record-routes.js.map +1 -1
  26. package/src/lib/model/commerce-search-sources.js +4 -2
  27. package/src/lib/model/commerce-search-sources.js.map +1 -1
  28. package/src/lib/model/commerce-topics.d.ts +20 -0
  29. package/src/lib/model/commerce-topics.js +33 -0
  30. package/src/lib/model/commerce-topics.js.map +1 -0
  31. package/src/lib/model/index.d.ts +1 -0
  32. package/src/lib/model/index.js +1 -0
  33. package/src/lib/model/index.js.map +1 -1
  34. package/src/lib/model/product-record.d.ts +32 -0
  35. package/src/lib/model/product-record.js +82 -0
  36. package/src/lib/model/product-record.js.map +1 -0
  37. package/src/lib/model/site-member-purchases.d.ts +48 -0
  38. package/src/lib/model/site-member-purchases.js +50 -0
  39. package/src/lib/model/site-member-purchases.js.map +1 -0
  40. package/src/lib/plugin.js +12 -0
  41. package/src/lib/plugin.js.map +1 -1
  42. package/src/lib/server/billing-webhook.js +410 -208
  43. package/src/lib/server/billing-webhook.js.map +1 -1
  44. package/src/lib/server/cart-checkout.js +1 -1
  45. package/src/lib/server/cart-checkout.js.map +1 -1
  46. package/src/lib/server/gift-cards.js +7 -3
  47. package/src/lib/server/gift-cards.js.map +1 -1
  48. package/src/lib/server/low-stock.js +3 -1
  49. package/src/lib/server/low-stock.js.map +1 -1
  50. package/src/lib/server/member-post.js +2 -2
  51. package/src/lib/server/member-post.js.map +1 -1
  52. package/src/lib/server/membership-admin-password.js +4 -1
  53. package/src/lib/server/membership-admin-password.js.map +1 -1
  54. package/src/lib/server/membership-register.js +13 -2
  55. package/src/lib/server/membership-register.js.map +1 -1
  56. package/src/lib/server/newsletter.js +32 -19
  57. package/src/lib/server/newsletter.js.map +1 -1
  58. package/src/lib/server/pos-order.js +18 -10
  59. package/src/lib/server/pos-order.js.map +1 -1
  60. package/src/lib/server/process-abandoned.js +4 -2
  61. package/src/lib/server/process-abandoned.js.map +1 -1
  62. package/src/lib/server/process-restock.js +7 -5
  63. package/src/lib/server/process-restock.js.map +1 -1
  64. package/src/lib/server/product-record-index.js +5 -37
  65. package/src/lib/server/product-record-index.js.map +1 -1
  66. package/src/lib/server/reconcile-stock.js +1 -1
  67. package/src/lib/server/reconcile-stock.js.map +1 -1
  68. package/src/lib/server/reserve-stock.js +6 -1
  69. package/src/lib/server/reserve-stock.js.map +1 -1
  70. package/src/lib/server/revenue-source.js +3 -3
  71. package/src/lib/server/revenue-source.js.map +1 -1
  72. package/src/lib/server/supplier-outbox.js +16 -1
  73. package/src/lib/server/supplier-outbox.js.map +1 -1
  74. package/src/lib/server/supplier-update.js +19 -9
  75. package/src/lib/server/supplier-update.js.map +1 -1
  76. package/src/lib/starter-templates.d.ts +19 -0
  77. package/src/lib/starter-templates.js +157 -0
  78. package/src/lib/starter-templates.js.map +1 -0
  79. package/src/lib/tenant-emails.js +54 -14
  80. package/src/lib/tenant-emails.js.map +1 -1
  81. package/src/lib/testing/stand-in-record-system.d.ts +11 -2
  82. package/src/lib/testing/stand-in-record-system.js +5 -3
  83. package/src/lib/testing/stand-in-record-system.js.map +1 -1
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * Lifetime purchase math for a site's visitor accounts (AGL-546). Pure —
18
+ * operates on raw `hosts/{hostId}/orders` doc shapes (both the v1
19
+ * `totals.totalCents` form and the legacy flat `amountCents` form) so a
20
+ * member's purchases can be totalled from the docs already loaded. The stale
21
+ * `siteMembers.purchaseCents` field this replaces was never written.
22
+ */ /** The slice of an order doc the lifetime total needs. */ /** Orders that never charged the customer don't count toward lifetime. */ const UNCHARGED_STATUSES = new Set([
23
+ 'pending',
24
+ 'cancelled'
25
+ ]);
26
+ /** Charged cents for one order: v1 total, else legacy amount. */ export function orderChargedCents(order) {
27
+ var _ref, _ref1;
28
+ var _order_totals;
29
+ return Number((_ref = (_ref1 = (_order_totals = order.totals) == null ? void 0 : _order_totals.totalCents) != null ? _ref1 : order.amountCents) != null ? _ref : 0) || 0;
30
+ }
31
+ /**
32
+ * Sum of what the member actually paid: charged totals minus refunds,
33
+ * skipping never-charged (pending/cancelled) orders. Each order clamps at
34
+ * zero so an over-recorded refund cannot drag the lifetime negative.
35
+ *
36
+ * `refundedCents` carries a lost chargeback as well as a refund (AGL-1787),
37
+ * and money reversed is money reversed whichever door it left by, so the
38
+ * whole figure is netted; only a LABEL splits the two
39
+ * (`splitOrderReversal`, AGL-1810).
40
+ */ export function computeLifetimePurchaseCents(orders) {
41
+ return orders.reduce((sum, order)=>{
42
+ var _order_status, _order_refundedCents;
43
+ if (UNCHARGED_STATUSES.has(String((_order_status = order.status) != null ? _order_status : ''))) return sum;
44
+ const charged = orderChargedCents(order);
45
+ const refunded = Math.max(0, Number((_order_refundedCents = order.refundedCents) != null ? _order_refundedCents : 0) || 0);
46
+ return sum + Math.max(0, charged - refunded);
47
+ }, 0);
48
+ }
49
+
50
+ //# sourceMappingURL=site-member-purchases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/site-member-purchases.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Lifetime purchase math for a site's visitor accounts (AGL-546). Pure —\n * operates on raw `hosts/{hostId}/orders` doc shapes (both the v1\n * `totals.totalCents` form and the legacy flat `amountCents` form) so a\n * member's purchases can be totalled from the docs already loaded. The stale\n * `siteMembers.purchaseCents` field this replaces was never written.\n */\n\n/** The slice of an order doc the lifetime total needs. */\nexport interface PurchaseTotalOrder {\n status?: string\n /** Legacy Commerce Starter flat total (cents). */\n amountCents?: number\n /** Orders v1 totals block. */\n totals?: { totalCents?: number }\n /** Cents refunded so far (partial or full), a lost chargeback included. */\n refundedCents?: number\n}\n\n/** Orders that never charged the customer don't count toward lifetime. */\nconst UNCHARGED_STATUSES = new Set(['pending', 'cancelled'])\n\n/** Charged cents for one order: v1 total, else legacy amount. */\nexport function orderChargedCents(order: PurchaseTotalOrder): number {\n return Number(order.totals?.totalCents ?? order.amountCents ?? 0) || 0\n}\n\n/**\n * Sum of what the member actually paid: charged totals minus refunds,\n * skipping never-charged (pending/cancelled) orders. Each order clamps at\n * zero so an over-recorded refund cannot drag the lifetime negative.\n *\n * `refundedCents` carries a lost chargeback as well as a refund (AGL-1787),\n * and money reversed is money reversed whichever door it left by, so the\n * whole figure is netted; only a LABEL splits the two\n * (`splitOrderReversal`, AGL-1810).\n */\nexport function computeLifetimePurchaseCents(\n orders: readonly PurchaseTotalOrder[],\n): number {\n return orders.reduce((sum, order) => {\n if (UNCHARGED_STATUSES.has(String(order.status ?? ''))) return sum\n const charged = orderChargedCents(order)\n const refunded = Math.max(0, Number(order.refundedCents ?? 0) || 0)\n return sum + Math.max(0, charged - refunded)\n }, 0)\n}\n"],"names":["UNCHARGED_STATUSES","Set","orderChargedCents","order","Number","totals","totalCents","amountCents","computeLifetimePurchaseCents","orders","reduce","sum","has","String","status","charged","refunded","Math","max","refundedCents"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;CAMC,GAED,wDAAwD,GAWxD,wEAAwE,GACxE,MAAMA,qBAAqB,IAAIC,IAAI;IAAC;IAAW;CAAY;AAE3D,+DAA+D,GAC/D,OAAO,SAASC,kBAAkBC,KAAyB;QAC3CA;QAAAA;IAAd,OAAOC,QAAOD,iBAAAA,gBAAAA,MAAME,MAAM,qBAAZF,cAAcG,UAAU,oBAAIH,MAAMI,WAAW,YAA7CJ,OAAiD,MAAM;AACvE;AAEA;;;;;;;;;CASC,GACD,OAAO,SAASK,6BACdC,MAAqC;IAErC,OAAOA,OAAOC,MAAM,CAAC,CAACC,KAAKR;YACSA,eAEEA;QAFpC,IAAIH,mBAAmBY,GAAG,CAACC,QAAOV,gBAAAA,MAAMW,MAAM,YAAZX,gBAAgB,MAAM,OAAOQ;QAC/D,MAAMI,UAAUb,kBAAkBC;QAClC,MAAMa,WAAWC,KAAKC,GAAG,CAAC,GAAGd,QAAOD,uBAAAA,MAAMgB,aAAa,YAAnBhB,uBAAuB,MAAM;QACjE,OAAOQ,MAAMM,KAAKC,GAAG,CAAC,GAAGH,UAAUC;IACrC,GAAG;AACL"}
package/src/lib/plugin.js CHANGED
@@ -19,9 +19,11 @@ import { lazy } from "react";
19
19
  const PosConsolePage = lazy(()=>import("./components/console/pos-page.component.js"));
20
20
  const CommerceGlanceCard = lazy(()=>import("./components/console/commerce-glance-card.component.js"));
21
21
  const NewestSiteUsersCard = lazy(()=>import("./components/console/newest-site-users-card.component.js"));
22
+ const SiteMemberPurchases = lazy(()=>import("./components/console/site-member-purchases.component.js"));
22
23
  import { COMMERCE_CONSOLE_SECTIONS } from "./components/commerce-console-sections.js";
23
24
  import { BUNDLE_ID } from "./constants/bundle-common.js";
24
25
  import { COMMERCE_PERMISSIONS } from "./model/plugin-permissions.js";
26
+ import { registerCommerceRecordLists } from "./model/commerce-record-lists.js";
25
27
  import { registerCommerceRecordRoutes } from "./model/commerce-record-routes.js";
26
28
  import { registerCommerceZones } from "./components/console/product-zones.js";
27
29
  import { COMMERCE_SEARCH_SOURCES } from "./model/commerce-search-sources.js";
@@ -39,6 +41,7 @@ import { COMMERCE_CONFIG_SCHEMA } from "./plugin-config.js";
39
41
  */ export function registerCommerceConsole() {
40
42
  // Where a product is read, for surfaces outside this plugin (AGL-3080).
41
43
  registerCommerceRecordRoutes();
44
+ registerCommerceRecordLists();
42
45
  // The zones its product editor, products hub and CSV import dialog host,
43
46
  // with the props each hands a widget (AGL-2916, AGL-3080).
44
47
  registerCommerceZones();
@@ -60,6 +63,15 @@ import { COMMERCE_CONFIG_SCHEMA } from "./plugin-config.js";
60
63
  widgetId: 'commerce-glance',
61
64
  title: 'Commerce',
62
65
  Component: CommerceGlanceCard
66
+ },
67
+ // What a site user bought — lifetime total, orders and subscriptions —
68
+ // in their drawer on the site's Users page: the orders and
69
+ // subscriptions are this plugin's.
70
+ {
71
+ slot: Aglyn.CONSOLE_WIDGET_SLOTS.siteMember,
72
+ widgetId: 'commerce-site-member-purchases',
73
+ title: 'Purchases',
74
+ Component: SiteMemberPurchases
63
75
  }
64
76
  ],
65
77
  navItems: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/plugins/commerce/src/lib/plugin.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiStorefrontOutline } from '@aglyn/shared-data-mdi'\nimport { lazy } from 'react'\nconst PosConsolePage = lazy(() => import('./components/console/pos-page.component'))\nconst CommerceGlanceCard = lazy(\n () => import('./components/console/commerce-glance-card.component'),\n)\nconst NewestSiteUsersCard = lazy(\n () => import('./components/console/newest-site-users-card.component'),\n)\nimport { COMMERCE_CONSOLE_SECTIONS } from './components/commerce-console-sections'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport { COMMERCE_PERMISSIONS } from './model/plugin-permissions'\nimport { registerCommerceRecordRoutes } from './model/commerce-record-routes'\nimport { registerCommerceZones } from './components/console/product-zones'\nimport { COMMERCE_SEARCH_SOURCES } from './model/commerce-search-sources'\nimport { COMMERCE_CONFIG_SCHEMA } from './plugin-config'\n\n/** Code-split: the Products console page only loads when opened. */\nconst CommerceConsolePage = lazy(() => import('./components/commerce-console-page'))\n\n/**\n * Console half (AGL-395): registers the Products nav item in the\n * ConsoleExtension registry so the commerce console surface is owned by the\n * plugin, like events and email. The page body still lives in the console\n * app (`/[hostId]/products`): its product editor depends on the app's media\n * browser, which transitively pulls in the org/session context, so a full\n * body relocation needs a plugin-consumable media-picker context first —\n * tracked as follow-up. The nav item omits a `Component`, so the shell's\n * nav renders the link and the existing named route serves the page.\n */\nexport function registerCommerceConsole(): void {\n // Where a product is read, for surfaces outside this plugin (AGL-3080).\n registerCommerceRecordRoutes()\n // The zones its product editor, products hub and CSV import dialog host,\n // with the props each hands a widget (AGL-2916, AGL-3080).\n registerCommerceZones()\n // Plugin-declared permissions (AGL-435): tier defaults ride every\n // resolved role set; custom roles override key-by-key.\n Aglyn.registerPluginPermissions(COMMERCE_PERMISSIONS)\n // Register discount ceiling (AGL-2161) — the generic settings card renders\n // it from this schema, so there is no bespoke UI to keep in sync.\n Aglyn.registerPluginConfigSchema(COMMERCE_CONFIG_SCHEMA)\n Aglyn.registerConsoleExtension({\n pluginId: BUNDLE_ID,\n displayName: 'Commerce',\n searchSources: COMMERCE_SEARCH_SOURCES,\n // Dashboard/analytics glance card (AGL-419): rendered through the\n // shell's 'commerceGlance' widget slot.\n widgets: [\n {\n slot: 'commerceGlance',\n widgetId: 'commerce-glance',\n title: 'Commerce',\n Component: CommerceGlanceCard,\n },\n ],\n navItems: [\n {\n label: 'Products',\n href: '/products',\n icon: { path: mdiStorefrontOutline.path },\n // Sections as ROUTES (AGL-2501): `/products/orders` and friends are\n // real URLs the shell resolves and gates, so the page mounts the one\n // being read instead of subscribing all six.\n sections: COMMERCE_CONSOLE_SECTIONS,\n header: {\n title: 'Products',\n icon: { path: mdiStorefrontOutline.path },\n // The overview, not `catalog`: this surface is the whole commerce\n // console — Catalog, Orders, Promotions and Reservations.\n docsTopic: 'commerce',\n },\n Component: CommerceConsolePage,\n },\n {\n // POS register (AGL-312/419): relocated from the app's /pos route —\n // the generic [pluginSlug] shell now serves it at the same URL.\n label: 'POS',\n href: '/pos',\n /*\n * WHO may open the register, declared on THIS nav item rather than on\n * the extension.\n *\n * A key here narrows one surface; a key on the extension would gate\n * every surface it registers, and Products is the sibling — a catalog\n * anyone who works on the site may open. The register is the one that\n * takes money and lists checked-in guests by name, so the two\n * genuinely have different answers and only this one is narrowed.\n *\n * `managePos` rather than a dotted catalog key because it is the key\n * the sale itself is refused by: `server/pos-order.ts` resolves the\n * member's permission map and returns 403 unless `managePos` holds.\n * The shell answers this declaration out of that same resolved map, so\n * the console offers a register exactly to the people the route will\n * accept a sale from — the gate and its server twin cannot disagree,\n * because they are reading one value.\n *\n * `pos-order.ts` stays the enforcement point regardless. This decides\n * whether the surface is built at all, which is the part a check\n * inside the page cannot do: by the time the page can refuse itself it\n * has mounted and its product, register, location and reservation\n * listeners are already open.\n */\n permission: 'managePos',\n icon: { path: mdiStorefrontOutline.path },\n header: { title: 'Point of Sale', docsTopic: 'pos' },\n Component: PosConsolePage,\n },\n ],\n })\n /*\n * The USER ACCOUNTS half, registered under its own id.\n *\n * `accounts` is a switch rather than a bundle: the member blocks and every\n * `membership/*` handler ship inside this package, which is why the id\n * carries no loader manifest entry of its own. A second extension is how a\n * card about visitor accounts is gated on the switch that decides whether\n * the site serves them — `listConsoleWidgets` filters by the workspace's\n * effective plugin ids, and `accounts` is default-OFF per site.\n *\n * A separate call rather than more widgets on the commerce extension: the\n * registry is keyed by `pluginId` and that key IS the gate, so folding this\n * card in above would have kept the dashboard defect it fixes — `Newest\n * site users` on a site whose /signin returns 404.\n */\n Aglyn.registerConsoleExtension({\n pluginId: Aglyn.ACCOUNTS_PLUGIN_ID,\n displayName: 'User Accounts',\n widgets: [\n {\n slot: Aglyn.CONSOLE_WIDGET_SLOTS.hostDashboard,\n widgetId: 'accounts-newest-site-users',\n title: 'Newest site users',\n Component: NewestSiteUsersCard,\n },\n ],\n })\n}\n\nexport * from './site'\n"],"names":["Aglyn","mdiStorefrontOutline","lazy","PosConsolePage","CommerceGlanceCard","NewestSiteUsersCard","COMMERCE_CONSOLE_SECTIONS","BUNDLE_ID","COMMERCE_PERMISSIONS","registerCommerceRecordRoutes","registerCommerceZones","COMMERCE_SEARCH_SOURCES","COMMERCE_CONFIG_SCHEMA","CommerceConsolePage","registerCommerceConsole","registerPluginPermissions","registerPluginConfigSchema","registerConsoleExtension","pluginId","displayName","searchSources","widgets","slot","widgetId","title","Component","navItems","label","href","icon","path","sections","header","docsTopic","permission","ACCOUNTS_PLUGIN_ID","CONSOLE_WIDGET_SLOTS","hostDashboard"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,oBAAoB,QAAQ,yBAAwB;AAC7D,SAASC,IAAI,QAAQ,QAAO;AAC5B,MAAMC,iBAAiBD,KAAK,IAAM,MAAM,CAAC;AACzC,MAAME,qBAAqBF,KACzB,IAAM,MAAM,CAAC;AAEf,MAAMG,sBAAsBH,KAC1B,IAAM,MAAM,CAAC;AAEf,SAASI,yBAAyB,QAAQ,4CAAwC;AAClF,SAASC,SAAS,QAAQ,+BAA2B;AACrD,SAASC,oBAAoB,QAAQ,gCAA4B;AACjE,SAASC,4BAA4B,QAAQ,oCAAgC;AAC7E,SAASC,qBAAqB,QAAQ,wCAAoC;AAC1E,SAASC,uBAAuB,QAAQ,qCAAiC;AACzE,SAASC,sBAAsB,QAAQ,qBAAiB;AAExD,kEAAkE,GAClE,MAAMC,sBAAsBX,KAAK,IAAM,MAAM,CAAC;AAE9C;;;;;;;;;CASC,GACD,OAAO,SAASY;IACd,wEAAwE;IACxEL;IACA,yEAAyE;IACzE,2DAA2D;IAC3DC;IACA,kEAAkE;IAClE,uDAAuD;IACvDV,MAAMe,yBAAyB,CAACP;IAChC,2EAA2E;IAC3E,kEAAkE;IAClER,MAAMgB,0BAA0B,CAACJ;IACjCZ,MAAMiB,wBAAwB,CAAC;QAC7BC,UAAUX;QACVY,aAAa;QACbC,eAAeT;QACf,kEAAkE;QAClE,wCAAwC;QACxCU,SAAS;YACP;gBACEC,MAAM;gBACNC,UAAU;gBACVC,OAAO;gBACPC,WAAWrB;YACb;SACD;QACDsB,UAAU;YACR;gBACEC,OAAO;gBACPC,MAAM;gBACNC,MAAM;oBAAEC,MAAM7B,qBAAqB6B,IAAI;gBAAC;gBACxC,oEAAoE;gBACpE,qEAAqE;gBACrE,6CAA6C;gBAC7CC,UAAUzB;gBACV0B,QAAQ;oBACNR,OAAO;oBACPK,MAAM;wBAAEC,MAAM7B,qBAAqB6B,IAAI;oBAAC;oBACxC,kEAAkE;oBAClE,0DAA0D;oBAC1DG,WAAW;gBACb;gBACAR,WAAWZ;YACb;YACA;gBACE,oEAAoE;gBACpE,gEAAgE;gBAChEc,OAAO;gBACPC,MAAM;gBACN;;;;;;;;;;;;;;;;;;;;;;;SAuBC,GACDM,YAAY;gBACZL,MAAM;oBAAEC,MAAM7B,qBAAqB6B,IAAI;gBAAC;gBACxCE,QAAQ;oBAAER,OAAO;oBAAiBS,WAAW;gBAAM;gBACnDR,WAAWtB;YACb;SACD;IACH;IACA;;;;;;;;;;;;;;GAcC,GACDH,MAAMiB,wBAAwB,CAAC;QAC7BC,UAAUlB,MAAMmC,kBAAkB;QAClChB,aAAa;QACbE,SAAS;YACP;gBACEC,MAAMtB,MAAMoC,oBAAoB,CAACC,aAAa;gBAC9Cd,UAAU;gBACVC,OAAO;gBACPC,WAAWpB;YACb;SACD;IACH;AACF;AAEA,cAAc,YAAQ"}
1
+ {"version":3,"sources":["../../../../../../libs/plugins/commerce/src/lib/plugin.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiStorefrontOutline } from '@aglyn/shared-data-mdi'\nimport { lazy } from 'react'\nconst PosConsolePage = lazy(() => import('./components/console/pos-page.component'))\nconst CommerceGlanceCard = lazy(\n () => import('./components/console/commerce-glance-card.component'),\n)\nconst NewestSiteUsersCard = lazy(\n () => import('./components/console/newest-site-users-card.component'),\n)\nconst SiteMemberPurchases = lazy(\n () => import('./components/console/site-member-purchases.component'),\n)\nimport { COMMERCE_CONSOLE_SECTIONS } from './components/commerce-console-sections'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport { COMMERCE_PERMISSIONS } from './model/plugin-permissions'\nimport { registerCommerceRecordLists } from './model/commerce-record-lists'\nimport { registerCommerceRecordRoutes } from './model/commerce-record-routes'\nimport { registerCommerceZones } from './components/console/product-zones'\nimport { COMMERCE_SEARCH_SOURCES } from './model/commerce-search-sources'\nimport { COMMERCE_CONFIG_SCHEMA } from './plugin-config'\n\n/** Code-split: the Products console page only loads when opened. */\nconst CommerceConsolePage = lazy(() => import('./components/commerce-console-page'))\n\n/**\n * Console half (AGL-395): registers the Products nav item in the\n * ConsoleExtension registry so the commerce console surface is owned by the\n * plugin, like events and email. The page body still lives in the console\n * app (`/[hostId]/products`): its product editor depends on the app's media\n * browser, which transitively pulls in the org/session context, so a full\n * body relocation needs a plugin-consumable media-picker context first —\n * tracked as follow-up. The nav item omits a `Component`, so the shell's\n * nav renders the link and the existing named route serves the page.\n */\nexport function registerCommerceConsole(): void {\n // Where a product is read, for surfaces outside this plugin (AGL-3080).\n registerCommerceRecordRoutes()\n registerCommerceRecordLists()\n // The zones its product editor, products hub and CSV import dialog host,\n // with the props each hands a widget (AGL-2916, AGL-3080).\n registerCommerceZones()\n // Plugin-declared permissions (AGL-435): tier defaults ride every\n // resolved role set; custom roles override key-by-key.\n Aglyn.registerPluginPermissions(COMMERCE_PERMISSIONS)\n // Register discount ceiling (AGL-2161) — the generic settings card renders\n // it from this schema, so there is no bespoke UI to keep in sync.\n Aglyn.registerPluginConfigSchema(COMMERCE_CONFIG_SCHEMA)\n Aglyn.registerConsoleExtension({\n pluginId: BUNDLE_ID,\n displayName: 'Commerce',\n searchSources: COMMERCE_SEARCH_SOURCES,\n // Dashboard/analytics glance card (AGL-419): rendered through the\n // shell's 'commerceGlance' widget slot.\n widgets: [\n {\n slot: 'commerceGlance',\n widgetId: 'commerce-glance',\n title: 'Commerce',\n Component: CommerceGlanceCard,\n },\n // What a site user bought — lifetime total, orders and subscriptions —\n // in their drawer on the site's Users page: the orders and\n // subscriptions are this plugin's.\n {\n slot: Aglyn.CONSOLE_WIDGET_SLOTS.siteMember,\n widgetId: 'commerce-site-member-purchases',\n title: 'Purchases',\n Component: SiteMemberPurchases,\n },\n ],\n navItems: [\n {\n label: 'Products',\n href: '/products',\n icon: { path: mdiStorefrontOutline.path },\n // Sections as ROUTES (AGL-2501): `/products/orders` and friends are\n // real URLs the shell resolves and gates, so the page mounts the one\n // being read instead of subscribing all six.\n sections: COMMERCE_CONSOLE_SECTIONS,\n header: {\n title: 'Products',\n icon: { path: mdiStorefrontOutline.path },\n // The overview, not `catalog`: this surface is the whole commerce\n // console — Catalog, Orders, Promotions and Reservations.\n docsTopic: 'commerce',\n },\n Component: CommerceConsolePage,\n },\n {\n // POS register (AGL-312/419): relocated from the app's /pos route —\n // the generic [pluginSlug] shell now serves it at the same URL.\n label: 'POS',\n href: '/pos',\n /*\n * WHO may open the register, declared on THIS nav item rather than on\n * the extension.\n *\n * A key here narrows one surface; a key on the extension would gate\n * every surface it registers, and Products is the sibling — a catalog\n * anyone who works on the site may open. The register is the one that\n * takes money and lists checked-in guests by name, so the two\n * genuinely have different answers and only this one is narrowed.\n *\n * `managePos` rather than a dotted catalog key because it is the key\n * the sale itself is refused by: `server/pos-order.ts` resolves the\n * member's permission map and returns 403 unless `managePos` holds.\n * The shell answers this declaration out of that same resolved map, so\n * the console offers a register exactly to the people the route will\n * accept a sale from — the gate and its server twin cannot disagree,\n * because they are reading one value.\n *\n * `pos-order.ts` stays the enforcement point regardless. This decides\n * whether the surface is built at all, which is the part a check\n * inside the page cannot do: by the time the page can refuse itself it\n * has mounted and its product, register, location and reservation\n * listeners are already open.\n */\n permission: 'managePos',\n icon: { path: mdiStorefrontOutline.path },\n header: { title: 'Point of Sale', docsTopic: 'pos' },\n Component: PosConsolePage,\n },\n ],\n })\n /*\n * The USER ACCOUNTS half, registered under its own id.\n *\n * `accounts` is a switch rather than a bundle: the member blocks and every\n * `membership/*` handler ship inside this package, which is why the id\n * carries no loader manifest entry of its own. A second extension is how a\n * card about visitor accounts is gated on the switch that decides whether\n * the site serves them — `listConsoleWidgets` filters by the workspace's\n * effective plugin ids, and `accounts` is default-OFF per site.\n *\n * A separate call rather than more widgets on the commerce extension: the\n * registry is keyed by `pluginId` and that key IS the gate, so folding this\n * card in above would have kept the dashboard defect it fixes — `Newest\n * site users` on a site whose /signin returns 404.\n */\n Aglyn.registerConsoleExtension({\n pluginId: Aglyn.ACCOUNTS_PLUGIN_ID,\n displayName: 'User Accounts',\n widgets: [\n {\n slot: Aglyn.CONSOLE_WIDGET_SLOTS.hostDashboard,\n widgetId: 'accounts-newest-site-users',\n title: 'Newest site users',\n Component: NewestSiteUsersCard,\n },\n ],\n })\n}\n\nexport * from './site'\n"],"names":["Aglyn","mdiStorefrontOutline","lazy","PosConsolePage","CommerceGlanceCard","NewestSiteUsersCard","SiteMemberPurchases","COMMERCE_CONSOLE_SECTIONS","BUNDLE_ID","COMMERCE_PERMISSIONS","registerCommerceRecordLists","registerCommerceRecordRoutes","registerCommerceZones","COMMERCE_SEARCH_SOURCES","COMMERCE_CONFIG_SCHEMA","CommerceConsolePage","registerCommerceConsole","registerPluginPermissions","registerPluginConfigSchema","registerConsoleExtension","pluginId","displayName","searchSources","widgets","slot","widgetId","title","Component","CONSOLE_WIDGET_SLOTS","siteMember","navItems","label","href","icon","path","sections","header","docsTopic","permission","ACCOUNTS_PLUGIN_ID","hostDashboard"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,oBAAoB,QAAQ,yBAAwB;AAC7D,SAASC,IAAI,QAAQ,QAAO;AAC5B,MAAMC,iBAAiBD,KAAK,IAAM,MAAM,CAAC;AACzC,MAAME,qBAAqBF,KACzB,IAAM,MAAM,CAAC;AAEf,MAAMG,sBAAsBH,KAC1B,IAAM,MAAM,CAAC;AAEf,MAAMI,sBAAsBJ,KAC1B,IAAM,MAAM,CAAC;AAEf,SAASK,yBAAyB,QAAQ,4CAAwC;AAClF,SAASC,SAAS,QAAQ,+BAA2B;AACrD,SAASC,oBAAoB,QAAQ,gCAA4B;AACjE,SAASC,2BAA2B,QAAQ,mCAA+B;AAC3E,SAASC,4BAA4B,QAAQ,oCAAgC;AAC7E,SAASC,qBAAqB,QAAQ,wCAAoC;AAC1E,SAASC,uBAAuB,QAAQ,qCAAiC;AACzE,SAASC,sBAAsB,QAAQ,qBAAiB;AAExD,kEAAkE,GAClE,MAAMC,sBAAsBb,KAAK,IAAM,MAAM,CAAC;AAE9C;;;;;;;;;CASC,GACD,OAAO,SAASc;IACd,wEAAwE;IACxEL;IACAD;IACA,yEAAyE;IACzE,2DAA2D;IAC3DE;IACA,kEAAkE;IAClE,uDAAuD;IACvDZ,MAAMiB,yBAAyB,CAACR;IAChC,2EAA2E;IAC3E,kEAAkE;IAClET,MAAMkB,0BAA0B,CAACJ;IACjCd,MAAMmB,wBAAwB,CAAC;QAC7BC,UAAUZ;QACVa,aAAa;QACbC,eAAeT;QACf,kEAAkE;QAClE,wCAAwC;QACxCU,SAAS;YACP;gBACEC,MAAM;gBACNC,UAAU;gBACVC,OAAO;gBACPC,WAAWvB;YACb;YACA,uEAAuE;YACvE,2DAA2D;YAC3D,mCAAmC;YACnC;gBACEoB,MAAMxB,MAAM4B,oBAAoB,CAACC,UAAU;gBAC3CJ,UAAU;gBACVC,OAAO;gBACPC,WAAWrB;YACb;SACD;QACDwB,UAAU;YACR;gBACEC,OAAO;gBACPC,MAAM;gBACNC,MAAM;oBAAEC,MAAMjC,qBAAqBiC,IAAI;gBAAC;gBACxC,oEAAoE;gBACpE,qEAAqE;gBACrE,6CAA6C;gBAC7CC,UAAU5B;gBACV6B,QAAQ;oBACNV,OAAO;oBACPO,MAAM;wBAAEC,MAAMjC,qBAAqBiC,IAAI;oBAAC;oBACxC,kEAAkE;oBAClE,0DAA0D;oBAC1DG,WAAW;gBACb;gBACAV,WAAWZ;YACb;YACA;gBACE,oEAAoE;gBACpE,gEAAgE;gBAChEgB,OAAO;gBACPC,MAAM;gBACN;;;;;;;;;;;;;;;;;;;;;;;SAuBC,GACDM,YAAY;gBACZL,MAAM;oBAAEC,MAAMjC,qBAAqBiC,IAAI;gBAAC;gBACxCE,QAAQ;oBAAEV,OAAO;oBAAiBW,WAAW;gBAAM;gBACnDV,WAAWxB;YACb;SACD;IACH;IACA;;;;;;;;;;;;;;GAcC,GACDH,MAAMmB,wBAAwB,CAAC;QAC7BC,UAAUpB,MAAMuC,kBAAkB;QAClClB,aAAa;QACbE,SAAS;YACP;gBACEC,MAAMxB,MAAM4B,oBAAoB,CAACY,aAAa;gBAC9Cf,UAAU;gBACVC,OAAO;gBACPC,WAAWtB;YACb;SACD;IACH;AACF;AAEA,cAAc,YAAQ"}