@aglyn/plugins-commerce 1.0.0-beta.232 → 1.0.0-beta.233

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 (228) hide show
  1. package/package.json +13 -13
  2. package/src/lib/components/cart-extras.d.ts +6 -0
  3. package/src/lib/components/cart-extras.js +4 -1
  4. package/src/lib/components/cart-extras.js.map +1 -1
  5. package/src/lib/components/cart-fulfillment.d.ts +117 -0
  6. package/src/lib/components/cart-fulfillment.js +367 -0
  7. package/src/lib/components/cart-fulfillment.js.map +1 -0
  8. package/src/lib/components/cart.js +52 -10
  9. package/src/lib/components/cart.js.map +1 -1
  10. package/src/lib/components/commerce-console-page.js +42 -1
  11. package/src/lib/components/commerce-console-page.js.map +1 -1
  12. package/src/lib/components/console/host-orders-card.component.js +38 -10
  13. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  14. package/src/lib/components/console/local-delivery-settings-card.component.d.ts +37 -0
  15. package/src/lib/components/console/local-delivery-settings-card.component.js +542 -0
  16. package/src/lib/components/console/local-delivery-settings-card.component.js.map +1 -0
  17. package/src/lib/components/console/locations-card.component.js +177 -3
  18. package/src/lib/components/console/locations-card.component.js.map +1 -1
  19. package/src/lib/components/console/order-detail-dialog.component.js +10 -0
  20. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
  21. package/src/lib/components/console/order-local-fulfillment-panel.component.d.ts +44 -0
  22. package/src/lib/components/console/order-local-fulfillment-panel.component.js +265 -0
  23. package/src/lib/components/console/order-local-fulfillment-panel.component.js.map +1 -0
  24. package/src/lib/components/console/order-offline-notice.component.d.ts +12 -0
  25. package/src/lib/components/console/order-offline-notice.component.js +53 -0
  26. package/src/lib/components/console/order-offline-notice.component.js.map +1 -0
  27. package/src/lib/components/console/pickup-delivery-queue-card.component.d.ts +35 -0
  28. package/src/lib/components/console/pickup-delivery-queue-card.component.js +222 -0
  29. package/src/lib/components/console/pickup-delivery-queue-card.component.js.map +1 -0
  30. package/src/lib/components/console/pos/pos-api.d.ts +24 -1
  31. package/src/lib/components/console/pos/pos-api.js +13 -0
  32. package/src/lib/components/console/pos/pos-api.js.map +1 -1
  33. package/src/lib/components/console/pos/pos-item-dialog.component.d.ts +12 -0
  34. package/src/lib/components/console/pos/pos-item-dialog.component.js +10 -8
  35. package/src/lib/components/console/pos/pos-item-dialog.component.js.map +1 -1
  36. package/src/lib/components/console/pos/pos-kiosk-queue.component.d.ts +22 -0
  37. package/src/lib/components/console/pos/pos-kiosk-queue.component.js +168 -0
  38. package/src/lib/components/console/pos/pos-kiosk-queue.component.js.map +1 -0
  39. package/src/lib/components/console/pos/pos-tender-dialogs.component.d.ts +18 -0
  40. package/src/lib/components/console/pos/pos-tender-dialogs.component.js +116 -1
  41. package/src/lib/components/console/pos/pos-tender-dialogs.component.js.map +1 -1
  42. package/src/lib/components/console/pos/pos-tender-panel.component.d.ts +8 -0
  43. package/src/lib/components/console/pos/pos-tender-panel.component.js +77 -18
  44. package/src/lib/components/console/pos/pos-tender-panel.component.js.map +1 -1
  45. package/src/lib/components/console/pos/use-pos-display.d.ts +5 -2
  46. package/src/lib/components/console/pos/use-pos-display.js +3 -2
  47. package/src/lib/components/console/pos/use-pos-display.js.map +1 -1
  48. package/src/lib/components/console/pos-devices-card.component.js +2 -2
  49. package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
  50. package/src/lib/components/console/pos-display/pairing-screen.d.ts +5 -2
  51. package/src/lib/components/console/pos-display/pairing-screen.js +7 -5
  52. package/src/lib/components/console/pos-display/pairing-screen.js.map +1 -1
  53. package/src/lib/components/console/pos-display/pos-display-api.d.ts +7 -1
  54. package/src/lib/components/console/pos-display/pos-display-api.js +7 -2
  55. package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -1
  56. package/src/lib/components/console/pos-kiosk/kiosk-checkout.d.ts +41 -0
  57. package/src/lib/components/console/pos-kiosk/kiosk-checkout.js +275 -0
  58. package/src/lib/components/console/pos-kiosk/kiosk-checkout.js.map +1 -0
  59. package/src/lib/components/console/pos-kiosk/kiosk-menu.d.ts +32 -0
  60. package/src/lib/components/console/pos-kiosk/kiosk-menu.js +344 -0
  61. package/src/lib/components/console/pos-kiosk/kiosk-menu.js.map +1 -0
  62. package/src/lib/components/console/pos-kiosk/kiosk-staff.d.ts +12 -0
  63. package/src/lib/components/console/pos-kiosk/kiosk-staff.js +257 -0
  64. package/src/lib/components/console/pos-kiosk/kiosk-staff.js.map +1 -0
  65. package/src/lib/components/console/pos-kiosk/pos-kiosk-api.d.ts +114 -0
  66. package/src/lib/components/console/pos-kiosk/pos-kiosk-api.js +189 -0
  67. package/src/lib/components/console/pos-kiosk/pos-kiosk-api.js.map +1 -0
  68. package/src/lib/components/console/pos-kiosk/use-kiosk-idle.d.ts +16 -0
  69. package/src/lib/components/console/pos-kiosk/use-kiosk-idle.js +80 -0
  70. package/src/lib/components/console/pos-kiosk/use-kiosk-idle.js.map +1 -0
  71. package/src/lib/components/console/pos-kiosk-page.component.d.ts +28 -0
  72. package/src/lib/components/console/pos-kiosk-page.component.js +791 -0
  73. package/src/lib/components/console/pos-kiosk-page.component.js.map +1 -0
  74. package/src/lib/components/console/pos-offline/pos-offline-api.d.ts +44 -0
  75. package/src/lib/components/console/pos-offline/pos-offline-api.js +69 -0
  76. package/src/lib/components/console/pos-offline/pos-offline-api.js.map +1 -0
  77. package/src/lib/components/console/pos-offline/pos-offline-banner.component.d.ts +7 -0
  78. package/src/lib/components/console/pos-offline/pos-offline-banner.component.js +135 -0
  79. package/src/lib/components/console/pos-offline/pos-offline-banner.component.js.map +1 -0
  80. package/src/lib/components/console/pos-offline/pos-offline-checkout.component.d.ts +32 -0
  81. package/src/lib/components/console/pos-offline/pos-offline-checkout.component.js +287 -0
  82. package/src/lib/components/console/pos-offline/pos-offline-checkout.component.js.map +1 -0
  83. package/src/lib/components/console/pos-offline/pos-offline-store.d.ts +63 -0
  84. package/src/lib/components/console/pos-offline/pos-offline-store.js +145 -0
  85. package/src/lib/components/console/pos-offline/pos-offline-store.js.map +1 -0
  86. package/src/lib/components/console/pos-offline/register-offline.d.ts +24 -0
  87. package/src/lib/components/console/pos-offline/register-offline.js +24 -0
  88. package/src/lib/components/console/pos-offline/register-offline.js.map +1 -0
  89. package/src/lib/components/console/pos-offline/use-pos-offline.d.ts +85 -0
  90. package/src/lib/components/console/pos-offline/use-pos-offline.js +542 -0
  91. package/src/lib/components/console/pos-offline/use-pos-offline.js.map +1 -0
  92. package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js +72 -17
  93. package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js.map +1 -1
  94. package/src/lib/components/console/pos-page.component.js +174 -27
  95. package/src/lib/components/console/pos-page.component.js.map +1 -1
  96. package/src/lib/components/console/products-hub-card.component.js +35 -10
  97. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  98. package/src/lib/components/console/shipping-settings-card.component.js +1 -1
  99. package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
  100. package/src/lib/components/console/stock-movements-card.component.js +48 -16
  101. package/src/lib/components/console/stock-movements-card.component.js.map +1 -1
  102. package/src/lib/components/console/store-zones.d.ts +8 -1
  103. package/src/lib/components/console/store-zones.js +11 -1
  104. package/src/lib/components/console/store-zones.js.map +1 -1
  105. package/src/lib/components/order-status.js +66 -0
  106. package/src/lib/components/order-status.js.map +1 -1
  107. package/src/lib/components/product-detail.js +42 -15
  108. package/src/lib/components/product-detail.js.map +1 -1
  109. package/src/lib/constants/local-fulfillment-list-query.d.ts +48 -0
  110. package/src/lib/constants/local-fulfillment-list-query.js +84 -0
  111. package/src/lib/constants/local-fulfillment-list-query.js.map +1 -0
  112. package/src/lib/constants/orders-list-query.d.ts +7 -2
  113. package/src/lib/constants/orders-list-query.js +89 -8
  114. package/src/lib/constants/orders-list-query.js.map +1 -1
  115. package/src/lib/constants/pos-shift-list-query.d.ts +22 -0
  116. package/src/lib/constants/pos-shift-list-query.js +115 -0
  117. package/src/lib/constants/pos-shift-list-query.js.map +1 -0
  118. package/src/lib/constants/product-list-query.d.ts +6 -1
  119. package/src/lib/constants/product-list-query.js +77 -8
  120. package/src/lib/constants/product-list-query.js.map +1 -1
  121. package/src/lib/constants/stock-movement-query.d.ts +3 -2
  122. package/src/lib/constants/stock-movement-query.js +52 -8
  123. package/src/lib/constants/stock-movement-query.js.map +1 -1
  124. package/src/lib/model/buyer-notifications.d.ts +5 -3
  125. package/src/lib/model/buyer-notifications.js +25 -4
  126. package/src/lib/model/buyer-notifications.js.map +1 -1
  127. package/src/lib/model/commerce-orders.d.ts +59 -0
  128. package/src/lib/model/commerce-orders.js.map +1 -1
  129. package/src/lib/model/commerce-pos-kiosk.d.ts +175 -0
  130. package/src/lib/model/commerce-pos-kiosk.js +147 -0
  131. package/src/lib/model/commerce-pos-kiosk.js.map +1 -0
  132. package/src/lib/model/commerce-pos-offline.d.ts +234 -0
  133. package/src/lib/model/commerce-pos-offline.js +301 -0
  134. package/src/lib/model/commerce-pos-offline.js.map +1 -0
  135. package/src/lib/model/commerce-pos-ops.d.ts +16 -6
  136. package/src/lib/model/commerce-pos-ops.js +12 -4
  137. package/src/lib/model/commerce-pos-ops.js.map +1 -1
  138. package/src/lib/model/commerce-pos.d.ts +13 -1
  139. package/src/lib/model/commerce-pos.js +5 -4
  140. package/src/lib/model/commerce-pos.js.map +1 -1
  141. package/src/lib/model/commerce-stock-holds.d.ts +24 -3
  142. package/src/lib/model/commerce-stock-holds.js +26 -8
  143. package/src/lib/model/commerce-stock-holds.js.map +1 -1
  144. package/src/lib/model/commerce.d.ts +3 -0
  145. package/src/lib/model/commerce.js.map +1 -1
  146. package/src/lib/model/index.d.ts +4 -0
  147. package/src/lib/model/index.js +4 -0
  148. package/src/lib/model/index.js.map +1 -1
  149. package/src/lib/model/local-fulfillment-settings.d.ts +178 -0
  150. package/src/lib/model/local-fulfillment-settings.js +361 -0
  151. package/src/lib/model/local-fulfillment-settings.js.map +1 -0
  152. package/src/lib/model/order-list-fields.d.ts +10 -1
  153. package/src/lib/model/order-list-fields.js +1 -0
  154. package/src/lib/model/order-list-fields.js.map +1 -1
  155. package/src/lib/model/order-local-fulfillment.d.ts +131 -0
  156. package/src/lib/model/order-local-fulfillment.js +125 -0
  157. package/src/lib/model/order-local-fulfillment.js.map +1 -0
  158. package/src/lib/model/order-shipping-export.js +4 -0
  159. package/src/lib/model/order-shipping-export.js.map +1 -1
  160. package/src/lib/model/order-status-view.d.ts +20 -1
  161. package/src/lib/model/order-status-view.js +60 -6
  162. package/src/lib/model/order-status-view.js.map +1 -1
  163. package/src/lib/plugin-config.js +6 -2
  164. package/src/lib/plugin-config.js.map +1 -1
  165. package/src/lib/plugin.js +8 -0
  166. package/src/lib/plugin.js.map +1 -1
  167. package/src/lib/pos-kiosk-config.d.ts +26 -0
  168. package/src/lib/pos-kiosk-config.js +59 -0
  169. package/src/lib/pos-kiosk-config.js.map +1 -0
  170. package/src/lib/server/api-v1/openapi.js +8 -0
  171. package/src/lib/server/api-v1/openapi.js.map +1 -1
  172. package/src/lib/server/api-v1/order-view.d.ts +19 -0
  173. package/src/lib/server/api-v1/order-view.js +35 -0
  174. package/src/lib/server/api-v1/order-view.js.map +1 -1
  175. package/src/lib/server/billing-webhook.js +238 -87
  176. package/src/lib/server/billing-webhook.js.map +1 -1
  177. package/src/lib/server/cart-checkout.d.ts +2 -0
  178. package/src/lib/server/cart-checkout.js +173 -13
  179. package/src/lib/server/cart-checkout.js.map +1 -1
  180. package/src/lib/server/checkout-extras.js +18 -4
  181. package/src/lib/server/checkout-extras.js.map +1 -1
  182. package/src/lib/server/checkout.js +71 -14
  183. package/src/lib/server/checkout.js.map +1 -1
  184. package/src/lib/server/local-fulfillment-status.d.ts +86 -0
  185. package/src/lib/server/local-fulfillment-status.js +379 -0
  186. package/src/lib/server/local-fulfillment-status.js.map +1 -0
  187. package/src/lib/server/local-fulfillment.d.ts +190 -0
  188. package/src/lib/server/local-fulfillment.js +580 -0
  189. package/src/lib/server/local-fulfillment.js.map +1 -0
  190. package/src/lib/server/membership-register.js +3 -1
  191. package/src/lib/server/membership-register.js.map +1 -1
  192. package/src/lib/server/order-notifications.js +61 -0
  193. package/src/lib/server/order-notifications.js.map +1 -1
  194. package/src/lib/server/pos-display.d.ts +27 -1
  195. package/src/lib/server/pos-display.js +50 -9
  196. package/src/lib/server/pos-display.js.map +1 -1
  197. package/src/lib/server/pos-kiosk-principal.d.ts +29 -0
  198. package/src/lib/server/pos-kiosk-principal.js +42 -0
  199. package/src/lib/server/pos-kiosk-principal.js.map +1 -0
  200. package/src/lib/server/pos-kiosk.d.ts +54 -0
  201. package/src/lib/server/pos-kiosk.js +972 -0
  202. package/src/lib/server/pos-kiosk.js.map +1 -0
  203. package/src/lib/server/pos-offline-sync.d.ts +85 -0
  204. package/src/lib/server/pos-offline-sync.js +765 -0
  205. package/src/lib/server/pos-offline-sync.js.map +1 -0
  206. package/src/lib/server/pos-order.js +22 -4
  207. package/src/lib/server/pos-order.js.map +1 -1
  208. package/src/lib/server/pos-payment.d.ts +6 -0
  209. package/src/lib/server/pos-payment.js +225 -6
  210. package/src/lib/server/pos-payment.js.map +1 -1
  211. package/src/lib/server/pos-print.js +5 -0
  212. package/src/lib/server/pos-print.js.map +1 -1
  213. package/src/lib/server/pos-return.js +27 -0
  214. package/src/lib/server/pos-return.js.map +1 -1
  215. package/src/lib/server/pos-sale.d.ts +6 -0
  216. package/src/lib/server/pos-sale.js +2 -0
  217. package/src/lib/server/pos-sale.js.map +1 -1
  218. package/src/lib/server/pos-shift.js +8 -1
  219. package/src/lib/server/pos-shift.js.map +1 -1
  220. package/src/lib/server/refund.js +3 -0
  221. package/src/lib/server/refund.js.map +1 -1
  222. package/src/lib/server/stock-hold.d.ts +9 -1
  223. package/src/lib/server/stock-hold.js +15 -7
  224. package/src/lib/server/stock-hold.js.map +1 -1
  225. package/src/lib/server.js +16 -0
  226. package/src/lib/server.js.map +1 -1
  227. package/src/lib/tenant-emails.js +161 -0
  228. package/src/lib/tenant-emails.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/shipping-settings-card.component.tsx"],"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'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n AlertTitle,\n Button,\n Divider,\n FormControlLabel,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { doc, setDoc } from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n useFirestoreDoc,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface ShippingSettingsCardProps {\n hostId: string\n}\n\n/**\n * Shipping settings (AGL-288): zones (countries, '*' = rest of world)\n * and rates (flat / free-over / subtotal or weight tiers), stored on\n * `hosts/{hostId}/settings/store` under `shipping`. Cart checkout (AGL-1707)\n * and buy-now (AGL-1720) both read this document and declare the rates as\n * Stripe `shipping_options` (`planCheckoutShipping`) — until those landed\n * nothing read it, and everything saved here was charged to nobody. Buy-now\n * resolves only for physical one-time sales; draft orders and POS still\n * resolve no shipping at all.\n *\n * WHAT A ZONE LAYOUT COSTS THE SHOPPER (AGL-1721). A session's rates are\n * charged as picked and never re-checked against the address, so the checkout\n * can only offer rates that hold for every country it will ship to. Saving\n * zones whose rates DIFFER therefore makes the storefront ask the shopper\n * where the parcel is going before it can price one, and a destination no\n * zone covers is refused outright rather than shipped for free. One zone, or\n * one '*' zone, needs neither: the shopper is asked nothing.\n *\n * AND THE MERCHANT HAS TO BE ABLE TO SEE IT (AGL-1791). Both consequences\n * were invisible here: a merchant saved zones and the checkouts they refused\n * were refused with nothing on this page saying so, which is a support ticket\n * from someone who did nothing wrong. The coverage block reads\n * `summarizeShippingCoverage` off the same `current` object the save writes,\n * so it moves as the form is typed rather than after a round trip.\n *\n * It escalates to a warning ONLY for a merchant who priced some destinations\n * and not others. A merchant whose rates reach nowhere is not misconfigured —\n * that store charges no shipping and refuses nobody — and warning them would\n * nag every store selling downloads.\n */\nexport function ShippingSettingsCard(props: ShippingSettingsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const {\n data: store,\n status: storeStatus,\n /**\n * The settings doc this form is seeded from is unconfirmed by the server\n * (AGL-1358). `current` is `draft ?? store?.shipping ?? {}` and the save\n * writes `{shipping: current}`, so `merge: true` protects the doc's other\n * maps and nothing inside this one — `zones` and `rates` are arrays, and\n * a merge replaces an array wholesale. Against a cached read, adding one\n * rate restores that snapshot's whole price table, and a zone that\n * disappears with it means `resolveShippingRates` matches nothing, which\n * is a checkout offering no shipping option at all.\n */\n fromCache: storeFromCache,\n } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId, 'settings', 'store'),\n [firestore, hostId],\n )\n const [draft, setDraft] = useState<CommerceModel.ShippingSettings | null>(null)\n const current: CommerceModel.ShippingSettings = draft ?? store?.shipping ?? {}\n /*\n * Live carrier rates (AGL-3612) are offered only where a plugin quotes\n * carriers for this site — the server asks core's quoter seam. A rate\n * already saved as `carrier` stays editable either way, so a merchant can\n * still see and change what checkout falls back to.\n */\n const { data: user } = useUser()\n const [carrier, setCarrier] = useState<{\n available: boolean\n services: Array<{ serviceKey: string; label: string }>\n }>({ available: false, services: [] })\n useEffect(() => {\n let live = true\n authorizedFetch(user, `/api/commerce/shipping/carrier-rates?hostId=${encodeURIComponent(hostId)}`)\n .then((response) => (response.ok ? response.json() : null))\n .then((answer) => {\n if (live && answer) setCarrier({ available: Boolean(answer.available), services: answer.services ?? [] })\n })\n .catch(() => undefined)\n return () => {\n live = false\n }\n }, [hostId, user])\n const update = (patch: Partial<CommerceModel.ShippingSettings>) =>\n setDraft({ ...current, ...patch })\n\n const updateZone = (\n index: number,\n patch: Partial<CommerceModel.ShippingZone> | null,\n ) => {\n const zones = [...(current.zones ?? [])]\n if (patch === null) {\n const removed = zones.splice(index, 1)[0]\n update({\n zones,\n rates: (current.rates ?? []).filter(\n (rate) => rate.zoneId !== removed?.id,\n ),\n })\n return\n }\n zones[index] = {\n id: Aglyn.createResourceUid(),\n name: '',\n countries: [],\n ...zones[index],\n ...patch,\n }\n update({ zones })\n }\n\n const updateRate = (\n index: number,\n patch: Partial<CommerceModel.ShippingRate> | null,\n ) => {\n const rates = [...(current.rates ?? [])]\n if (patch === null) rates.splice(index, 1)\n else\n rates[index] = {\n id: Aglyn.createResourceUid(),\n zoneId: current.zones?.[0]?.id ?? '',\n name: '',\n kind: 'flat',\n ...rates[index],\n ...patch,\n }\n update({ rates })\n }\n\n const handleSave = useCallback(async () => {\n /**\n * Refuse a save whose seed the server never confirmed (AGL-1358).\n *\n * No create path to exempt: this is a FIXED document path, so the empty\n * `{}` that `store?.shipping ?? {}` yields when the cache has never seen\n * the map is not the harmless blank a fresh uid would be — it replaces\n * the real zones and rates with nothing.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'shipping settings',\n unreadable: storeStatus === 'error',\n fromCache: storeFromCache,\n },\n async () => {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'settings', 'store'),\n { shipping: current },\n { merge: true },\n )\n },\n )\n // Before `setDraft(null)`, so a refusal keeps every zone and rate that\n // was typed on screen.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n setDraft(null)\n enqueueSnackbar('Shipping settings saved', {\n variant: 'success',\n persist: false,\n })\n }, [current, firestore, hostId, enqueueSnackbar, storeStatus, storeFromCache])\n\n /**\n * Recomputed from `current` on every render, so the block answers for what\n * is typed rather than for what was last saved — a merchant deleting the\n * zone that covered France should see France go uncovered before they\n * commit it, not after.\n */\n const coverage = CommerceModel.summarizeShippingCoverage(current)\n const countryNames = (codes: readonly string[]) =>\n codes\n .map((code) => CommerceModel.CHECKOUT_SHIPPING_COUNTRY_NAMES[code] ?? code)\n .join(', ')\n /**\n * The one-click half of the fix: a zone claiming everywhere the merchant\n * has not named. It adds the ZONE only — a rate on it prices the parcel,\n * and choosing that price is the merchant's call, not ours. A rate row\n * defaulted here would post at whatever we picked, and a blank flat rate\n * resolves to free.\n */\n const addRestOfWorldZone = () =>\n updateZone(current.zones?.length ?? 0, {\n name: 'Rest of world',\n countries: ['*'],\n })\n\n const usd = (cents: number | undefined) =>\n cents == null ? '' : String(cents / 100)\n const cents = (raw: string) =>\n raw.trim() === '' ? undefined : Math.round(Number(raw) * 100)\n\n return (\n <CardDisplay\n header={'Shipping'}\n help={pluginDocsHelp('commerce', { anchor: '#shipping--taxes' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.5}>\n <Typography variant=\"subtitle2\">{'Zones'}</Typography>\n {(current.zones ?? []).map((zone, index) => (\n <Stack key={zone.id} direction=\"row\" spacing={1}>\n <TextField\n label=\"Zone name\"\n value={zone.name}\n onChange={(event) =>\n updateZone(index, { name: event.target.value })\n }\n size=\"small\"\n sx={{ width: 180 }}\n />\n <TextField\n label=\"Countries\"\n value={zone.countries.join(', ')}\n onChange={(event) =>\n updateZone(index, {\n countries: event.target.value\n .split(',')\n .map((code) => code.trim().toUpperCase())\n .filter(Boolean),\n })\n }\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"US, CA — or * for everywhere else\"\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() => updateZone(index, null)}\n >\n {'✕'}\n </Button>\n </Stack>\n ))}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => updateZone(current.zones?.length ?? 0, {})}\n >\n {'Add zone'}\n </Button>\n\n <Divider />\n <Typography variant=\"subtitle2\">{'Rates'}</Typography>\n {(current.rates ?? []).map((rate, index) => (\n <Stack key={rate.id} spacing={1}>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Zone\"\n value={rate.zoneId}\n onChange={(event) =>\n updateRate(index, { zoneId: event.target.value })\n }\n size=\"small\"\n select\n sx={{ width: 150 }}\n >\n {(current.zones ?? []).map((zone) => (\n <MenuItem key={zone.id} value={zone.id}>\n {zone.name || zone.countries.join(',')}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n label=\"Name\"\n value={rate.name}\n onChange={(event) =>\n updateRate(index, { name: event.target.value })\n }\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"Standard\"\n /**\n * A rate with no name is DROPPED at checkout, silently\n * (AGL-1791): Stripe requires a `display_name`, so\n * `resolveCheckoutShippingOptions` skips the row rather than\n * emit a session Stripe would reject. Saved blank it looks\n * like a priced rate on this page and is not one anywhere\n * else, which is the same invisibility the coverage block\n * below exists to end — said on the row that causes it,\n * because the block can only report the destination it costs.\n */\n error={!rate.name.trim()}\n helperText={\n rate.name.trim() ? undefined : 'Needed — checkout drops it'\n }\n />\n <TextField\n label=\"Type\"\n value={rate.kind}\n onChange={(event) =>\n updateRate(index, {\n kind: event.target.value as CommerceModel.ShippingRate['kind'],\n })\n }\n size=\"small\"\n select\n sx={{ width: 170 }}\n >\n <MenuItem value=\"flat\">{'Flat'}</MenuItem>\n <MenuItem value=\"free_over\">{'Free over subtotal'}</MenuItem>\n <MenuItem value=\"price_tiers\">{'Subtotal tiers'}</MenuItem>\n <MenuItem value=\"weight_tiers\">{'Weight tiers'}</MenuItem>\n {carrier.available || rate.kind === 'carrier' ? (\n <MenuItem value=\"carrier\">{'Carrier rates'}</MenuItem>\n ) : null}\n </TextField>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() => updateRate(index, null)}\n >\n {'✕'}\n </Button>\n </Stack>\n {rate.kind === 'carrier' ? (\n <CarrierRateFields\n rate={rate}\n services={carrier.services}\n available={carrier.available}\n fallbacks={(current.rates ?? []).filter(\n (other) =>\n other.id !== rate.id &&\n other.zoneId === rate.zoneId &&\n other.kind !== 'carrier',\n )}\n onChange={(patch) => updateRate(index, patch)}\n />\n ) : rate.kind === 'flat' || rate.kind === 'free_over' ? (\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Price ($)\"\n value={usd(rate.amountCents)}\n onChange={(event) =>\n updateRate(index, { amountCents: cents(event.target.value) })\n }\n size=\"small\"\n sx={{ width: 120 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n {rate.kind === 'free_over' ? (\n <TextField\n label=\"Free at subtotal ($)\"\n value={usd(rate.freeOverCents)}\n onChange={(event) =>\n updateRate(index, {\n freeOverCents: cents(event.target.value),\n })\n }\n size=\"small\"\n sx={{ width: 160 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n ) : null}\n </Stack>\n ) : (\n <Stack spacing={1} sx={{ pl: 2 }}>\n {(rate.tiers ?? []).map((tier, tierIndex) => (\n <Stack key={tierIndex} direction=\"row\" spacing={1}>\n <TextField\n label={\n rate.kind === 'weight_tiers'\n ? 'Up to (g)'\n : 'Up to subtotal ($)'\n }\n value={\n rate.kind === 'weight_tiers'\n ? tier.upTo\n : tier.upTo / 100\n }\n onChange={(event) => {\n const tiers = [...(rate.tiers ?? [])]\n const value = Number(event.target.value) || 0\n tiers[tierIndex] = {\n ...tier,\n upTo:\n rate.kind === 'weight_tiers'\n ? Math.round(value)\n : Math.round(value * 100),\n }\n updateRate(index, { tiers })\n }}\n size=\"small\"\n sx={{ width: 160 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Price ($)\"\n value={tier.amountCents / 100}\n onChange={(event) => {\n const tiers = [...(rate.tiers ?? [])]\n tiers[tierIndex] = {\n ...tier,\n amountCents: Math.round(\n (Number(event.target.value) || 0) * 100,\n ),\n }\n updateRate(index, { tiers })\n }}\n size=\"small\"\n sx={{ width: 120 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() =>\n updateRate(index, {\n tiers: (rate.tiers ?? []).filter(\n (_item, itemIndex) => itemIndex !== tierIndex,\n ),\n })\n }\n >\n {'✕'}\n </Button>\n </Stack>\n ))}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() =>\n updateRate(index, {\n tiers: [\n ...(rate.tiers ?? []),\n { upTo: 0, amountCents: 0 },\n ],\n })\n }\n >\n {'Add tier'}\n </Button>\n </Stack>\n )}\n </Stack>\n ))}\n <Button\n size=\"small\"\n disabled={(current.zones?.length ?? 0) === 0}\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => updateRate(current.rates?.length ?? 0, {})}\n >\n {'Add rate'}\n </Button>\n\n <Stack spacing={0}>\n <FormControlLabel\n control={\n <Switch\n size=\"small\"\n checked={Boolean(current.localPickup)}\n onChange={(event) =>\n update({ localPickup: event.target.checked })\n }\n />\n }\n label=\"Offer free local pickup\"\n />\n {/*\n What the toggle does NOT do, said on the toggle (AGL-2325). It\n used to silently switch off every \"we don't ship there\" refusal,\n because pickup resolved for every destination and checkout read a\n non-empty list as coverage. It no longer does — but a merchant\n reading one line of label still has to be told that turning\n collection on is not the same as adding a destination.\n */}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {\n 'A free collection choice at checkout. It does not widen where ' +\n 'you ship — a destination none of your rates reaches is still ' +\n 'turned away.'\n }\n </Typography>\n </Stack>\n\n <Divider />\n <Typography variant=\"subtitle2\">{'Coverage'}</Typography>\n {coverage.unserved.length > 0 ? (\n <Alert\n severity=\"warning\"\n action={\n coverage.hasRestOfWorldZone ? undefined : (\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={addRestOfWorldZone}\n >\n {'Add rest-of-world zone'}\n </Button>\n )\n }\n >\n <AlertTitle>\n {`Checkout turns away ${coverage.unserved.length} of the ${CommerceModel.CHECKOUT_SHIPPING_COUNTRIES.length} destinations it collects addresses for`}\n </AlertTitle>\n {`No rate reaches ${countryNames(coverage.unserved)}, so an order shipping there is refused rather than posted for free. `}\n {coverage.offersLocalPickup\n ? 'Local pickup does not cover them: it is a collection choice, not a rate, so it is offered where checkout completes and closes no gap here. '\n : ''}\n {coverage.hasRestOfWorldZone\n ? 'A rest-of-world zone is already saved, so what those destinations are missing is a rate on the zone that covers them.'\n : 'Give them a zone with a rate, or add a rest-of-world zone and price one on it.'}\n </Alert>\n ) : (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {coverage.shipsNowhere\n ? coverage.offersLocalPickup\n ? 'No rate reaches any destination, so local pickup is the only delivery choice checkout offers. Nobody is turned away and no shipping is charged.'\n : 'No rate reaches any destination, so checkout charges no shipping and turns nobody away.'\n : `Checkout can price shipping to every destination it collects an address for: ${countryNames(coverage.served)}.`}\n </Typography>\n )}\n {coverage.asksDestination ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {\n 'Rates differ by destination, so checkout asks the shopper where the parcel is going before it can price one.'\n }\n </Typography>\n ) : null}\n\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"small\"\n disabled={!draft}\n onClick={handleSave}\n sx={{ alignSelf: 'flex-start' }}\n >\n {'Save shipping settings'}\n </Button>\n </Stack>\n </CardDisplay>\n )\n}\nShippingSettingsCard.displayName = 'ShippingSettingsCard'\n\nexport default ShippingSettingsCard\n\ninterface CarrierRateFieldsProps {\n rate: CommerceModel.ShippingRate\n services: Array<{ serviceKey: string; label: string }>\n available: boolean\n /** The zone's other rates, any of which checkout may fall back to. */\n fallbacks: CommerceModel.ShippingRate[]\n onChange: (patch: Partial<CommerceModel.ShippingRate>) => void\n}\n\n/**\n * A carrier rate's options (AGL-3612): which services checkout offers, what\n * is added to each quote, and the zone's rate offered when no live quote is.\n */\nfunction CarrierRateFields(props: CarrierRateFieldsProps) {\n const { rate, services, available, fallbacks, onChange } = props\n const options = rate.carrier ?? {}\n const set = (patch: Partial<CommerceModel.CarrierRateSettings>) =>\n onChange({ carrier: { ...options, ...patch } })\n const picked = new Set(options.services ?? [])\n return (\n <Stack spacing={1} sx={{ pl: 2 }}>\n {!available ? (\n <Alert severity=\"info\">\n {'Live carrier rates are not available on this site right now, so checkout offers the fallback rate.'}\n </Alert>\n ) : null}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Shoppers see each carrier’s price for their address. Leave every service unticked to offer all of them.'}\n </Typography>\n <Stack direction=\"row\" sx={{ flexWrap: 'wrap', columnGap: 2 }}>\n {services.map((service) => (\n <FormControlLabel\n key={service.serviceKey}\n control={\n <Switch\n size=\"small\"\n checked={picked.has(service.serviceKey)}\n onChange={(event) =>\n set({\n services: event.target.checked\n ? [...picked, service.serviceKey]\n : [...picked].filter((key) => key !== service.serviceKey),\n })\n }\n />\n }\n label={service.label}\n />\n ))}\n </Stack>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Markup (%)\"\n value={options.markupPct ?? ''}\n onChange={(event) => {\n const pct = Number(event.target.value)\n set({ markupPct: Number.isFinite(pct) && pct > 0 ? Math.min(pct, 500) : 0 })\n }}\n size=\"small\"\n sx={{ width: 120 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Handling fee ($)\"\n value={options.handlingCents ? options.handlingCents / 100 : ''}\n onChange={(event) => {\n const dollars = Number(event.target.value)\n set({ handlingCents: Number.isFinite(dollars) && dollars > 0 ? Math.round(dollars * 100) : 0 })\n }}\n size=\"small\"\n sx={{ width: 150 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n select\n label=\"If no live rate\"\n value={options.fallbackRateId ?? ''}\n onChange={(event) => set({ fallbackRateId: event.target.value })}\n size=\"small\"\n sx={{ minWidth: 200 }}\n error={!options.fallbackRateId}\n helperText={\n options.fallbackRateId ? undefined : 'Pick a rate of this zone, or checkout can’t price without a quote'\n }\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {fallbacks.map((fallback) => (\n <MenuItem key={fallback.id} value={fallback.id}>\n {fallback.name || 'Unnamed rate'}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n </Stack>\n )\n}\n"],"names":["Aglyn","CommerceModel","CardDisplay","useSnackbar","Alert","AlertTitle","Button","Divider","FormControlLabel","MenuItem","Stack","Switch","TextField","Typography","authorizedFetch","doc","setDoc","useCallback","useEffect","useState","useFirestore","useUser","useFirestoreDoc","writeGuardedBySeed","pluginDocsHelp","ShippingSettingsCard","props","draft","current","hostId","firestore","enqueueSnackbar","data","store","status","storeStatus","fromCache","storeFromCache","setDraft","shipping","user","carrier","setCarrier","available","services","live","encodeURIComponent","then","response","ok","json","answer","Boolean","catch","undefined","update","patch","updateZone","index","zones","removed","splice","rates","filter","rate","zoneId","id","createResourceUid","name","countries","updateRate","kind","handleSave","verdict","subject","unreadable","merge","message","variant","persist","coverage","summarizeShippingCoverage","countryNames","codes","map","code","CHECKOUT_SHIPPING_COUNTRY_NAMES","join","addRestOfWorldZone","length","usd","cents","String","raw","trim","Math","round","Number","header","help","anchor","contentGutterX","contentGutterY","spacing","zone","direction","label","value","onChange","event","target","size","sx","width","split","toUpperCase","flex","placeholder","color","onClick","alignSelf","select","error","helperText","CarrierRateFields","fallbacks","other","amountCents","slotProps","htmlInput","inputMode","freeOverCents","pl","tiers","tier","tierIndex","upTo","_item","itemIndex","disabled","control","checked","localPickup","unserved","severity","action","hasRestOfWorldZone","CHECKOUT_SHIPPING_COUNTRIES","offersLocalPickup","shipsNowhere","served","asksDestination","displayName","options","set","picked","Set","flexWrap","columnGap","service","has","serviceKey","key","markupPct","pct","isFinite","min","handlingCents","dollars","fallbackRateId","minWidth","fallback"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/shipping-settings-card.component.tsx"],"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'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n AlertTitle,\n Button,\n Divider,\n FormControlLabel,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { doc, setDoc } from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n useFirestoreDoc,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface ShippingSettingsCardProps {\n hostId: string\n}\n\n/**\n * Shipping settings (AGL-288): zones (countries, '*' = rest of world)\n * and rates (flat / free-over / subtotal or weight tiers), stored on\n * `hosts/{hostId}/settings/store` under `shipping`. Cart checkout (AGL-1707)\n * and buy-now (AGL-1720) both read this document and declare the rates as\n * Stripe `shipping_options` (`planCheckoutShipping`) — until those landed\n * nothing read it, and everything saved here was charged to nobody. Buy-now\n * resolves only for physical one-time sales; draft orders and POS still\n * resolve no shipping at all.\n *\n * WHAT A ZONE LAYOUT COSTS THE SHOPPER (AGL-1721). A session's rates are\n * charged as picked and never re-checked against the address, so the checkout\n * can only offer rates that hold for every country it will ship to. Saving\n * zones whose rates DIFFER therefore makes the storefront ask the shopper\n * where the parcel is going before it can price one, and a destination no\n * zone covers is refused outright rather than shipped for free. One zone, or\n * one '*' zone, needs neither: the shopper is asked nothing.\n *\n * AND THE MERCHANT HAS TO BE ABLE TO SEE IT (AGL-1791). Both consequences\n * were invisible here: a merchant saved zones and the checkouts they refused\n * were refused with nothing on this page saying so, which is a support ticket\n * from someone who did nothing wrong. The coverage block reads\n * `summarizeShippingCoverage` off the same `current` object the save writes,\n * so it moves as the form is typed rather than after a round trip.\n *\n * It escalates to a warning ONLY for a merchant who priced some destinations\n * and not others. A merchant whose rates reach nowhere is not misconfigured —\n * that store charges no shipping and refuses nobody — and warning them would\n * nag every store selling downloads.\n */\nexport function ShippingSettingsCard(props: ShippingSettingsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const {\n data: store,\n status: storeStatus,\n /**\n * The settings doc this form is seeded from is unconfirmed by the server\n * (AGL-1358). `current` is `draft ?? store?.shipping ?? {}` and the save\n * writes `{shipping: current}`, so `merge: true` protects the doc's other\n * maps and nothing inside this one — `zones` and `rates` are arrays, and\n * a merge replaces an array wholesale. Against a cached read, adding one\n * rate restores that snapshot's whole price table, and a zone that\n * disappears with it means `resolveShippingRates` matches nothing, which\n * is a checkout offering no shipping option at all.\n */\n fromCache: storeFromCache,\n } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId, 'settings', 'store'),\n [firestore, hostId],\n )\n const [draft, setDraft] = useState<CommerceModel.ShippingSettings | null>(null)\n const current: CommerceModel.ShippingSettings = draft ?? store?.shipping ?? {}\n /*\n * Live carrier rates (AGL-3612) are offered only where a plugin quotes\n * carriers for this site — the server asks core's quoter seam. A rate\n * already saved as `carrier` stays editable either way, so a merchant can\n * still see and change what checkout falls back to.\n */\n const { data: user } = useUser()\n const [carrier, setCarrier] = useState<{\n available: boolean\n services: Array<{ serviceKey: string; label: string }>\n }>({ available: false, services: [] })\n useEffect(() => {\n let live = true\n authorizedFetch(user, `/api/commerce/shipping/carrier-rates?hostId=${encodeURIComponent(hostId)}`)\n .then((response) => (response.ok ? response.json() : null))\n .then((answer) => {\n if (live && answer) setCarrier({ available: Boolean(answer.available), services: answer.services ?? [] })\n })\n .catch(() => undefined)\n return () => {\n live = false\n }\n }, [hostId, user])\n const update = (patch: Partial<CommerceModel.ShippingSettings>) =>\n setDraft({ ...current, ...patch })\n\n const updateZone = (\n index: number,\n patch: Partial<CommerceModel.ShippingZone> | null,\n ) => {\n const zones = [...(current.zones ?? [])]\n if (patch === null) {\n const removed = zones.splice(index, 1)[0]\n update({\n zones,\n rates: (current.rates ?? []).filter(\n (rate) => rate.zoneId !== removed?.id,\n ),\n })\n return\n }\n zones[index] = {\n id: Aglyn.createResourceUid(),\n name: '',\n countries: [],\n ...zones[index],\n ...patch,\n }\n update({ zones })\n }\n\n const updateRate = (\n index: number,\n patch: Partial<CommerceModel.ShippingRate> | null,\n ) => {\n const rates = [...(current.rates ?? [])]\n if (patch === null) rates.splice(index, 1)\n else\n rates[index] = {\n id: Aglyn.createResourceUid(),\n zoneId: current.zones?.[0]?.id ?? '',\n name: '',\n kind: 'flat',\n ...rates[index],\n ...patch,\n }\n update({ rates })\n }\n\n const handleSave = useCallback(async () => {\n /**\n * Refuse a save whose seed the server never confirmed (AGL-1358).\n *\n * No create path to exempt: this is a FIXED document path, so the empty\n * `{}` that `store?.shipping ?? {}` yields when the cache has never seen\n * the map is not the harmless blank a fresh uid would be — it replaces\n * the real zones and rates with nothing.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'shipping settings',\n unreadable: storeStatus === 'error',\n fromCache: storeFromCache,\n },\n async () => {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'settings', 'store'),\n { shipping: current },\n { merge: true },\n )\n },\n )\n // Before `setDraft(null)`, so a refusal keeps every zone and rate that\n // was typed on screen.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n setDraft(null)\n enqueueSnackbar('Shipping settings saved', {\n variant: 'success',\n persist: false,\n })\n }, [current, firestore, hostId, enqueueSnackbar, storeStatus, storeFromCache])\n\n /**\n * Recomputed from `current` on every render, so the block answers for what\n * is typed rather than for what was last saved — a merchant deleting the\n * zone that covered France should see France go uncovered before they\n * commit it, not after.\n */\n const coverage = CommerceModel.summarizeShippingCoverage(current)\n const countryNames = (codes: readonly string[]) =>\n codes\n .map((code) => CommerceModel.CHECKOUT_SHIPPING_COUNTRY_NAMES[code] ?? code)\n .join(', ')\n /**\n * The one-click half of the fix: a zone claiming everywhere the merchant\n * has not named. It adds the ZONE only — a rate on it prices the parcel,\n * and choosing that price is the merchant's call, not ours. A rate row\n * defaulted here would post at whatever we picked, and a blank flat rate\n * resolves to free.\n */\n const addRestOfWorldZone = () =>\n updateZone(current.zones?.length ?? 0, {\n name: 'Rest of world',\n countries: ['*'],\n })\n\n const usd = (cents: number | undefined) =>\n cents == null ? '' : String(cents / 100)\n const cents = (raw: string) =>\n raw.trim() === '' ? undefined : Math.round(Number(raw) * 100)\n\n return (\n <CardDisplay\n header={'Shipping'}\n help={pluginDocsHelp('commerce', { anchor: '#shipping--taxes' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.5}>\n <Typography variant=\"subtitle2\">{'Zones'}</Typography>\n {(current.zones ?? []).map((zone, index) => (\n <Stack key={zone.id} direction=\"row\" spacing={1}>\n <TextField\n label=\"Zone name\"\n value={zone.name}\n onChange={(event) =>\n updateZone(index, { name: event.target.value })\n }\n size=\"small\"\n sx={{ width: 180 }}\n />\n <TextField\n label=\"Countries\"\n value={zone.countries.join(', ')}\n onChange={(event) =>\n updateZone(index, {\n countries: event.target.value\n .split(',')\n .map((code) => code.trim().toUpperCase())\n .filter(Boolean),\n })\n }\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"US, CA — or * for everywhere else\"\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() => updateZone(index, null)}\n >\n {'✕'}\n </Button>\n </Stack>\n ))}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => updateZone(current.zones?.length ?? 0, {})}\n >\n {'Add zone'}\n </Button>\n\n <Divider />\n <Typography variant=\"subtitle2\">{'Rates'}</Typography>\n {(current.rates ?? []).map((rate, index) => (\n <Stack key={rate.id} spacing={1}>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Zone\"\n value={rate.zoneId}\n onChange={(event) =>\n updateRate(index, { zoneId: event.target.value })\n }\n size=\"small\"\n select\n sx={{ width: 150 }}\n >\n {(current.zones ?? []).map((zone) => (\n <MenuItem key={zone.id} value={zone.id}>\n {zone.name || zone.countries.join(',')}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n label=\"Name\"\n value={rate.name}\n onChange={(event) =>\n updateRate(index, { name: event.target.value })\n }\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"Standard\"\n /**\n * A rate with no name is DROPPED at checkout, silently\n * (AGL-1791): Stripe requires a `display_name`, so\n * `resolveCheckoutShippingOptions` skips the row rather than\n * emit a session Stripe would reject. Saved blank it looks\n * like a priced rate on this page and is not one anywhere\n * else, which is the same invisibility the coverage block\n * below exists to end — said on the row that causes it,\n * because the block can only report the destination it costs.\n */\n error={!rate.name.trim()}\n helperText={\n rate.name.trim() ? undefined : 'Needed — checkout drops it'\n }\n />\n <TextField\n label=\"Type\"\n value={rate.kind}\n onChange={(event) =>\n updateRate(index, {\n kind: event.target.value as CommerceModel.ShippingRate['kind'],\n })\n }\n size=\"small\"\n select\n sx={{ width: 170 }}\n >\n <MenuItem value=\"flat\">{'Flat'}</MenuItem>\n <MenuItem value=\"free_over\">{'Free over subtotal'}</MenuItem>\n <MenuItem value=\"price_tiers\">{'Subtotal tiers'}</MenuItem>\n <MenuItem value=\"weight_tiers\">{'Weight tiers'}</MenuItem>\n {carrier.available || rate.kind === 'carrier' ? (\n <MenuItem value=\"carrier\">{'Carrier rates'}</MenuItem>\n ) : null}\n </TextField>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() => updateRate(index, null)}\n >\n {'✕'}\n </Button>\n </Stack>\n {rate.kind === 'carrier' ? (\n <CarrierRateFields\n rate={rate}\n services={carrier.services}\n available={carrier.available}\n fallbacks={(current.rates ?? []).filter(\n (other) =>\n other.id !== rate.id &&\n other.zoneId === rate.zoneId &&\n other.kind !== 'carrier',\n )}\n onChange={(patch) => updateRate(index, patch)}\n />\n ) : rate.kind === 'flat' || rate.kind === 'free_over' ? (\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Price ($)\"\n value={usd(rate.amountCents)}\n onChange={(event) =>\n updateRate(index, { amountCents: cents(event.target.value) })\n }\n size=\"small\"\n sx={{ width: 120 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n {rate.kind === 'free_over' ? (\n <TextField\n label=\"Free at subtotal ($)\"\n value={usd(rate.freeOverCents)}\n onChange={(event) =>\n updateRate(index, {\n freeOverCents: cents(event.target.value),\n })\n }\n size=\"small\"\n sx={{ width: 160 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n ) : null}\n </Stack>\n ) : (\n <Stack spacing={1} sx={{ pl: 2 }}>\n {(rate.tiers ?? []).map((tier, tierIndex) => (\n <Stack key={tierIndex} direction=\"row\" spacing={1}>\n <TextField\n label={\n rate.kind === 'weight_tiers'\n ? 'Up to (g)'\n : 'Up to subtotal ($)'\n }\n value={\n rate.kind === 'weight_tiers'\n ? tier.upTo\n : tier.upTo / 100\n }\n onChange={(event) => {\n const tiers = [...(rate.tiers ?? [])]\n const value = Number(event.target.value) || 0\n tiers[tierIndex] = {\n ...tier,\n upTo:\n rate.kind === 'weight_tiers'\n ? Math.round(value)\n : Math.round(value * 100),\n }\n updateRate(index, { tiers })\n }}\n size=\"small\"\n sx={{ width: 160 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Price ($)\"\n value={tier.amountCents / 100}\n onChange={(event) => {\n const tiers = [...(rate.tiers ?? [])]\n tiers[tierIndex] = {\n ...tier,\n amountCents: Math.round(\n (Number(event.target.value) || 0) * 100,\n ),\n }\n updateRate(index, { tiers })\n }}\n size=\"small\"\n sx={{ width: 120 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() =>\n updateRate(index, {\n tiers: (rate.tiers ?? []).filter(\n (_item, itemIndex) => itemIndex !== tierIndex,\n ),\n })\n }\n >\n {'✕'}\n </Button>\n </Stack>\n ))}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() =>\n updateRate(index, {\n tiers: [\n ...(rate.tiers ?? []),\n { upTo: 0, amountCents: 0 },\n ],\n })\n }\n >\n {'Add tier'}\n </Button>\n </Stack>\n )}\n </Stack>\n ))}\n <Button\n size=\"small\"\n disabled={(current.zones?.length ?? 0) === 0}\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => updateRate(current.rates?.length ?? 0, {})}\n >\n {'Add rate'}\n </Button>\n\n <Stack spacing={0}>\n <FormControlLabel\n control={\n <Switch\n size=\"small\"\n checked={Boolean(current.localPickup)}\n onChange={(event) =>\n update({ localPickup: event.target.checked })\n }\n />\n }\n label=\"Offer free local pickup\"\n />\n {/*\n What the toggle does NOT do, said on the toggle (AGL-2325). It\n used to silently switch off every \"we don't ship there\" refusal,\n because pickup resolved for every destination and checkout read a\n non-empty list as coverage. It no longer does — but a merchant\n reading one line of label still has to be told that turning\n collection on is not the same as adding a destination.\n */}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {\n 'A free collection choice at checkout. It does not widen where ' +\n 'you ship — a destination none of your rates reaches is still ' +\n 'turned away. To let buyers choose WHERE to collect, with hours ' +\n 'and a ready-for-pickup email, turn on pickup for a location ' +\n 'under Inventory locations; this choice then gives way to it.'\n }\n </Typography>\n </Stack>\n\n <Divider />\n <Typography variant=\"subtitle2\">{'Coverage'}</Typography>\n {coverage.unserved.length > 0 ? (\n <Alert\n severity=\"warning\"\n action={\n coverage.hasRestOfWorldZone ? undefined : (\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={addRestOfWorldZone}\n >\n {'Add rest-of-world zone'}\n </Button>\n )\n }\n >\n <AlertTitle>\n {`Checkout turns away ${coverage.unserved.length} of the ${CommerceModel.CHECKOUT_SHIPPING_COUNTRIES.length} destinations it collects addresses for`}\n </AlertTitle>\n {`No rate reaches ${countryNames(coverage.unserved)}, so an order shipping there is refused rather than posted for free. `}\n {coverage.offersLocalPickup\n ? 'Local pickup does not cover them: it is a collection choice, not a rate, so it is offered where checkout completes and closes no gap here. '\n : ''}\n {coverage.hasRestOfWorldZone\n ? 'A rest-of-world zone is already saved, so what those destinations are missing is a rate on the zone that covers them.'\n : 'Give them a zone with a rate, or add a rest-of-world zone and price one on it.'}\n </Alert>\n ) : (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {coverage.shipsNowhere\n ? coverage.offersLocalPickup\n ? 'No rate reaches any destination, so local pickup is the only delivery choice checkout offers. Nobody is turned away and no shipping is charged.'\n : 'No rate reaches any destination, so checkout charges no shipping and turns nobody away.'\n : `Checkout can price shipping to every destination it collects an address for: ${countryNames(coverage.served)}.`}\n </Typography>\n )}\n {coverage.asksDestination ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {\n 'Rates differ by destination, so checkout asks the shopper where the parcel is going before it can price one.'\n }\n </Typography>\n ) : null}\n\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"small\"\n disabled={!draft}\n onClick={handleSave}\n sx={{ alignSelf: 'flex-start' }}\n >\n {'Save shipping settings'}\n </Button>\n </Stack>\n </CardDisplay>\n )\n}\nShippingSettingsCard.displayName = 'ShippingSettingsCard'\n\nexport default ShippingSettingsCard\n\ninterface CarrierRateFieldsProps {\n rate: CommerceModel.ShippingRate\n services: Array<{ serviceKey: string; label: string }>\n available: boolean\n /** The zone's other rates, any of which checkout may fall back to. */\n fallbacks: CommerceModel.ShippingRate[]\n onChange: (patch: Partial<CommerceModel.ShippingRate>) => void\n}\n\n/**\n * A carrier rate's options (AGL-3612): which services checkout offers, what\n * is added to each quote, and the zone's rate offered when no live quote is.\n */\nfunction CarrierRateFields(props: CarrierRateFieldsProps) {\n const { rate, services, available, fallbacks, onChange } = props\n const options = rate.carrier ?? {}\n const set = (patch: Partial<CommerceModel.CarrierRateSettings>) =>\n onChange({ carrier: { ...options, ...patch } })\n const picked = new Set(options.services ?? [])\n return (\n <Stack spacing={1} sx={{ pl: 2 }}>\n {!available ? (\n <Alert severity=\"info\">\n {'Live carrier rates are not available on this site right now, so checkout offers the fallback rate.'}\n </Alert>\n ) : null}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Shoppers see each carrier’s price for their address. Leave every service unticked to offer all of them.'}\n </Typography>\n <Stack direction=\"row\" sx={{ flexWrap: 'wrap', columnGap: 2 }}>\n {services.map((service) => (\n <FormControlLabel\n key={service.serviceKey}\n control={\n <Switch\n size=\"small\"\n checked={picked.has(service.serviceKey)}\n onChange={(event) =>\n set({\n services: event.target.checked\n ? [...picked, service.serviceKey]\n : [...picked].filter((key) => key !== service.serviceKey),\n })\n }\n />\n }\n label={service.label}\n />\n ))}\n </Stack>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Markup (%)\"\n value={options.markupPct ?? ''}\n onChange={(event) => {\n const pct = Number(event.target.value)\n set({ markupPct: Number.isFinite(pct) && pct > 0 ? Math.min(pct, 500) : 0 })\n }}\n size=\"small\"\n sx={{ width: 120 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Handling fee ($)\"\n value={options.handlingCents ? options.handlingCents / 100 : ''}\n onChange={(event) => {\n const dollars = Number(event.target.value)\n set({ handlingCents: Number.isFinite(dollars) && dollars > 0 ? Math.round(dollars * 100) : 0 })\n }}\n size=\"small\"\n sx={{ width: 150 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n select\n label=\"If no live rate\"\n value={options.fallbackRateId ?? ''}\n onChange={(event) => set({ fallbackRateId: event.target.value })}\n size=\"small\"\n sx={{ minWidth: 200 }}\n error={!options.fallbackRateId}\n helperText={\n options.fallbackRateId ? undefined : 'Pick a rate of this zone, or checkout can’t price without a quote'\n }\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {fallbacks.map((fallback) => (\n <MenuItem key={fallback.id} value={fallback.id}>\n {fallback.name || 'Unnamed rate'}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n </Stack>\n )\n}\n"],"names":["Aglyn","CommerceModel","CardDisplay","useSnackbar","Alert","AlertTitle","Button","Divider","FormControlLabel","MenuItem","Stack","Switch","TextField","Typography","authorizedFetch","doc","setDoc","useCallback","useEffect","useState","useFirestore","useUser","useFirestoreDoc","writeGuardedBySeed","pluginDocsHelp","ShippingSettingsCard","props","draft","current","hostId","firestore","enqueueSnackbar","data","store","status","storeStatus","fromCache","storeFromCache","setDraft","shipping","user","carrier","setCarrier","available","services","live","encodeURIComponent","then","response","ok","json","answer","Boolean","catch","undefined","update","patch","updateZone","index","zones","removed","splice","rates","filter","rate","zoneId","id","createResourceUid","name","countries","updateRate","kind","handleSave","verdict","subject","unreadable","merge","message","variant","persist","coverage","summarizeShippingCoverage","countryNames","codes","map","code","CHECKOUT_SHIPPING_COUNTRY_NAMES","join","addRestOfWorldZone","length","usd","cents","String","raw","trim","Math","round","Number","header","help","anchor","contentGutterX","contentGutterY","spacing","zone","direction","label","value","onChange","event","target","size","sx","width","split","toUpperCase","flex","placeholder","color","onClick","alignSelf","select","error","helperText","CarrierRateFields","fallbacks","other","amountCents","slotProps","htmlInput","inputMode","freeOverCents","pl","tiers","tier","tierIndex","upTo","_item","itemIndex","disabled","control","checked","localPickup","unserved","severity","action","hasRestOfWorldZone","CHECKOUT_SHIPPING_COUNTRIES","offersLocalPickup","shipsNowhere","served","asksDestination","displayName","options","set","picked","Set","flexWrap","columnGap","service","has","serviceKey","key","markupPct","pct","isFinite","min","handlingCents","dollars","fallbackRateId","minWidth","fallback"],"mappings":"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"}
@@ -23,14 +23,15 @@ import { ListQueryNotices, listQueryRefusals } from "@aglyn/shared-ui-jsx/compon
23
23
  import { ListTable } from "@aglyn/shared-ui-jsx/components/list-table.component";
24
24
  import { hiddenFilterVisibility } from "@aglyn/shared-ui-jsx/const/list-filter";
25
25
  import { listFilterGridColumns } from "@aglyn/shared-ui-jsx/const/list-grid-filter";
26
+ import { useListColumnSort } from "@aglyn/shared-ui-jsx/hooks/use-list-column-sort";
26
27
  import { useListGridFilter } from "@aglyn/shared-ui-jsx/hooks/use-list-grid-filter";
27
28
  import { Stack, Typography } from "@mui/material";
28
29
  import { collection } from "firebase/firestore";
29
- import { useMemo } from "react";
30
+ import { useMemo, useState } from "react";
30
31
  import { ceilingedWindow, collectionCeiling, useFirestore, useFirestoreCollection } from "@aglyn/tenant-feature-instance";
31
32
  import { useListQuery } from "@aglyn/tenant-feature-instance/hooks/use-list-query";
32
33
  import { pluginDocsHelp } from "@aglyn/aglyn";
33
- import { STOCK_MOVEMENT_FILTER_FIELDS, STOCK_MOVEMENT_FILTER_HEADERS, STOCK_MOVEMENT_QUERY, STOCK_MOVEMENT_REASON_LABEL, STOCK_MOVEMENT_SELECT_FIELDS } from "../../constants/stock-movement-query.js";
34
+ import { STOCK_MOVEMENT_COLUMN_SORTS, STOCK_MOVEMENT_FILTER_FIELDS, STOCK_MOVEMENT_FILTER_HEADERS, STOCK_MOVEMENT_QUERY, STOCK_MOVEMENT_REASON_LABEL, STOCK_MOVEMENT_SELECT_FIELDS } from "../../constants/stock-movement-query.js";
34
35
  /**
35
36
  * How many products the card reads to NAME its rows and offer the Product
36
37
  * filter's choices. A picker's reach, not the ledger's: the ledger itself is
@@ -53,6 +54,26 @@ const REASON_OPTIONS = Object.entries(STOCK_MOVEMENT_REASON_LABEL).map(([value,
53
54
  'reason',
54
55
  'source'
55
56
  ];
57
+ /** What the Source column reads: the order, the location and who counted. */ const movementSource = (row)=>[
58
+ row.orderId ? `order ${row.orderId}` : null,
59
+ row.locationId ? `at ${row.locationId}` : null,
60
+ row.source ? `counted by ${row.source}` : null
61
+ ].filter(Boolean).join(' · ');
62
+ /**
63
+ * Product (a name read from the product) and Source (composed from three
64
+ * fields) sort the page on screen (AGL-3680); When, Change and Reason are the
65
+ * query's order (`STOCK_MOVEMENT_COLUMN_SORTS`).
66
+ */ const MOVEMENT_PAGE_SORTS = {
67
+ productId: (row)=>{
68
+ var _row_productName;
69
+ return (_row_productName = row.productName) != null ? _row_productName : row.productId;
70
+ },
71
+ source: (row)=>movementSource(row) || null
72
+ };
73
+ const MOVEMENT_PAGE_SORT_HEADERS = {
74
+ productId: 'Product',
75
+ source: 'Source'
76
+ };
56
77
  /**
57
78
  * Stock movements (AGL-2341) — the adjustment history that had no history
58
79
  * view.
@@ -136,11 +157,14 @@ const REASON_OPTIONS = Object.entries(STOCK_MOVEMENT_REASON_LABEL).map(([value,
136
157
  const gridFilter = useListGridFilter({
137
158
  selectFields: STOCK_MOVEMENT_SELECT_FIELDS
138
159
  });
160
+ // Every header sorts (AGL-3680); newest first until one is clicked.
161
+ const [askedSort, setAskedSort] = useState(STOCK_MOVEMENT_COLUMN_SORTS[0]);
139
162
  const { rows: movementRows, hasMore, page, setPage, pageSize, setPageSize, plan, status } = useListQuery({
140
163
  collection: collection(firestore, 'hosts', hostId, 'inventoryAdjustments'),
141
164
  declaration: STOCK_MOVEMENT_QUERY,
142
165
  request: {
143
- clauses: gridFilter.clauses
166
+ clauses: gridFilter.clauses,
167
+ sort: askedSort
144
168
  },
145
169
  deps: [
146
170
  firestore,
@@ -157,6 +181,16 @@ const REASON_OPTIONS = Object.entries(STOCK_MOVEMENT_REASON_LABEL).map(([value,
157
181
  movementRows,
158
182
  productNames
159
183
  ]);
184
+ const columnSort = useListColumnSort({
185
+ sorts: STOCK_MOVEMENT_COLUMN_SORTS,
186
+ defaultSort: STOCK_MOVEMENT_COLUMN_SORTS[0],
187
+ sort: askedSort,
188
+ onSortChange: setAskedSort,
189
+ orderBy: plan.orderBy,
190
+ rows: movements,
191
+ pageSorts: MOVEMENT_PAGE_SORTS,
192
+ headers: MOVEMENT_PAGE_SORT_HEADERS
193
+ });
160
194
  const columns = useMemo(()=>listFilterGridColumns([
161
195
  {
162
196
  field: 'atMs',
@@ -225,11 +259,7 @@ const REASON_OPTIONS = Object.entries(STOCK_MOVEMENT_REASON_LABEL).map(([value,
225
259
  renderCell: ({ row })=>/*#__PURE__*/ _jsx(Typography, {
226
260
  variant: "caption",
227
261
  color: "text.secondary",
228
- children: [
229
- row.orderId ? `order ${row.orderId}` : null,
230
- row.locationId ? `at ${row.locationId}` : null,
231
- row.source ? `counted by ${row.source}` : null
232
- ].filter(Boolean).join(' · ') || '—'
262
+ children: movementSource(row) || '—'
233
263
  })
234
264
  }
235
265
  ], STOCK_MOVEMENT_FILTER_FIELDS, filterOptions, STOCK_MOVEMENT_FILTER_HEADERS), [
@@ -238,7 +268,7 @@ const REASON_OPTIONS = Object.entries(STOCK_MOVEMENT_REASON_LABEL).map(([value,
238
268
  const filtering = gridFilter.clauses.length > 0;
239
269
  // The ledger is empty only when an UNFILTERED first page came back empty;
240
270
  // a filter that matches nothing keeps the grid, its chips and its panel.
241
- const empty = status === 'success' && !filtering && page === 0 && movements.length === 0;
271
+ const empty = status === 'success' && !filtering && askedSort === STOCK_MOVEMENT_COLUMN_SORTS[0] && page === 0 && movements.length === 0;
242
272
  return /*#__PURE__*/ _jsx(CardDisplay, {
243
273
  header: "Stock movements",
244
274
  help: movementsHelp,
@@ -265,21 +295,23 @@ const REASON_OPTIONS = Object.entries(STOCK_MOVEMENT_REASON_LABEL).map(([value,
265
295
  headers: STOCK_MOVEMENT_FILTER_HEADERS,
266
296
  options: filterOptions
267
297
  }),
268
- notices: plan.notices
298
+ notices: [
299
+ ...plan.notices,
300
+ ...columnSort.notices
301
+ ]
269
302
  }),
270
303
  /*#__PURE__*/ _jsx(ListTable, {
271
304
  "aria-label": "Stock movements",
272
- rows: movements,
305
+ rows: columnSort.rows,
273
306
  columns: columns,
274
307
  /*
275
- * The grid must NOT also filter or sort. The query answers
276
- * both, and the ledger has one order — newest first — which
277
- * the query already holds.
308
+ * The grid must NOT also filter or sort on its own. The query
309
+ * answers the filters, and every header order is the query's
310
+ * or the page's (`columnSort`).
278
311
  */ filterMode: "server",
279
312
  filterModel: gridFilter.filterModel,
280
313
  onFilterModelChange: gridFilter.onFilterModelChange,
281
- sortingMode: "server",
282
- disableColumnSorting: true,
314
+ columnSort: columnSort,
283
315
  // `ListPagination` below pages the query.
284
316
  hideFooter: true,
285
317
  noRowsLabel: "No stock movements match these filters",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/stock-movements-card.component.tsx"],"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'use client'\n\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport {\n ListQueryNotices,\n listQueryRefusals,\n} from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { hiddenFilterVisibility } from '@aglyn/shared-ui-jsx/const/list-filter'\nimport { listFilterGridColumns } from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport { Stack, Typography } from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { collection } from 'firebase/firestore'\nimport { useMemo } from 'react'\nimport {\n ceilingedWindow,\n collectionCeiling,\n useFirestore,\n useFirestoreCollection,\n} from '@aglyn/tenant-feature-instance'\nimport { useListQuery } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport {\n STOCK_MOVEMENT_FILTER_FIELDS,\n STOCK_MOVEMENT_FILTER_HEADERS,\n STOCK_MOVEMENT_QUERY,\n STOCK_MOVEMENT_REASON_LABEL,\n STOCK_MOVEMENT_SELECT_FIELDS,\n} from '../../constants/stock-movement-query'\n\nexport interface StockMovementsCardProps {\n hostId: string\n}\n\n/**\n * How many products the card reads to NAME its rows and offer the Product\n * filter's choices. A picker's reach, not the ledger's: the ledger itself is\n * paged by its query and reaches every movement whatever this holds.\n */\nconst PRODUCT_NAMES_CEILING = 500\n/** A site's locations, which the plan caps well under this. */\nconst LOCATION_CEILING = 25\n\nconst movementsHelp = pluginDocsHelp('commerce', {\n anchor: '#stock-movements',\n title: 'Stock movements',\n excerpt:\n 'Every change to a tracked count — sales, returns, cancellations and ' +\n 'hand adjustments — newest first, with the reason each one was made.',\n})\n\nconst REASON_OPTIONS = Object.entries(STOCK_MOVEMENT_REASON_LABEL).map(([value, label]) => ({\n value,\n label,\n}))\n\n/** The columns the grid draws; every other declared field is a hidden filter column. */\nconst VISIBLE_COLUMNS = ['atMs', 'productId', 'delta', 'reason', 'source']\n\ntype MovementRow = CommerceModel.InventoryAdjustment & { $id: string }\n\n/**\n * Stock movements (AGL-2341) — the adjustment history that had no history\n * view.\n *\n * `hosts/{hostId}/inventoryAdjustments` has three writers: the products\n * hub's \"Adjust stock\" dialog, `decrementVariantStock` (the sale, whether the\n * checkout webhook or the POS sold it), and the restock in `cancel-order.ts`.\n * The hub's own comment calls the collection \"adjustment history\".\n *\n * Its only reader was arithmetic: `cancel-order.ts` projects `appliedDelta`\n * off the `reason: 'sale'` rows to cap what a cancellation may put back. No\n * surface displayed a single one of them. A merchant whose count disagreed\n * with the shelf had no way to see what moved it, when, or why — and the one\n * number that reconciles the discrepancy was written on every adjustment and\n * unreachable.\n *\n * `delta` is what the merchant's history says moved; `appliedDelta` is what\n * the count could actually give up when the floor in `adjustVariantInventory`\n * absorbed part of it, which happens on a backorder product selling past\n * zero. They differ rarely and they differ importantly — three sold out of a\n * count of zero is exactly the state a merchant is trying to explain — so\n * both are shown when they disagree and only `delta` when they do not.\n *\n * ## Every filter is on the query (AGL-3321)\n *\n * The ledger used to read its hundred newest movements and filter those, so\n * \"Damaged\" searched a hundred rows and answered \"none\" about a ledger full\n * of older ones. Now the Filters panel's clauses are the query's `where`s —\n * see `STOCK_MOVEMENT_QUERY` for what each one costs — and the footer pages\n * the query's answer, so page three of \"Damaged\" is the third page of damage\n * the ledger holds, however far back it goes.\n */\nexport function StockMovementsCard(props: StockMovementsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n\n // Names, so a row reads as \"Desk lamp\" rather than a 20-character id, and\n // the Product filter's choices.\n const { data: productDocs } = useFirestoreCollection<any>(\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'products'),\n PRODUCT_NAMES_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { data: locationDocs } = useFirestoreCollection<any>(\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'locations'),\n LOCATION_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const products = useMemo(\n () => ceilingedWindow<any>(productDocs ?? undefined, PRODUCT_NAMES_CEILING).rows,\n [productDocs],\n )\n const productNames = useMemo(() => {\n const names = new Map<string, string>()\n for (const product of products) {\n names.set(product.$id, product.name ?? product.$id)\n }\n return names\n }, [products])\n\n const filterOptions = useMemo(\n () => ({\n productId: [...productNames]\n .map(([value, label]) => ({ value, label }))\n .sort((a, b) => a.label.localeCompare(b.label)),\n reason: REASON_OPTIONS,\n locationId: ceilingedWindow<any>(locationDocs ?? undefined, LOCATION_CEILING).rows.map(\n (location: any) => ({ value: location.$id, label: location.name ?? location.$id }),\n ),\n }),\n [productNames, locationDocs],\n )\n\n const gridFilter = useListGridFilter({ selectFields: STOCK_MOVEMENT_SELECT_FIELDS })\n const {\n rows: movementRows,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n plan,\n status,\n } = useListQuery<MovementRow>({\n collection: collection(firestore, 'hosts', hostId, 'inventoryAdjustments'),\n declaration: STOCK_MOVEMENT_QUERY,\n request: { clauses: gridFilter.clauses },\n deps: [firestore, hostId],\n idField: '$id',\n })\n const movements = useMemo(\n () =>\n movementRows.map((row) => ({\n ...row,\n productName: productNames.get(row.productId) ?? row.productId,\n })),\n [movementRows, productNames],\n )\n\n const columns = useMemo(\n () =>\n listFilterGridColumns(\n [\n {\n field: 'atMs',\n headerName: 'When',\n width: 190,\n renderCell: ({ row }: { row: MovementRow }) =>\n row.atMs ? new Date(Number(row.atMs)).toLocaleString() : '—',\n },\n {\n field: 'productId',\n headerName: 'Product',\n flex: 1,\n minWidth: 180,\n renderCell: ({ row }: { row: MovementRow & { productName: string } }) => (\n <span>\n {row.productName}\n {row.variantId ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {` · ${row.variantId}`}\n </Typography>\n ) : null}\n </span>\n ),\n },\n {\n field: 'delta',\n headerName: 'Change',\n type: 'number',\n width: 150,\n align: 'right',\n headerAlign: 'right',\n renderCell: ({ row }: { row: MovementRow }) => {\n const delta = Number(row.delta ?? 0)\n const applied = Number(row.appliedDelta ?? delta)\n return (\n <span>\n {/*\n * The sign is carried explicitly. \"3\" and \"-3\" are the same\n * width and opposite facts, and a merchant scanning a column\n * for the movement that broke their count reads the sign\n * before the number.\n */}\n <Typography\n variant=\"body2\"\n color={delta < 0 ? 'error.main' : 'success.main'}\n component=\"span\"\n >\n {delta > 0 ? `+${delta}` : String(delta)}\n </Typography>\n {applied !== delta ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {` (${applied > 0 ? `+${applied}` : applied} applied)`}\n </Typography>\n ) : null}\n </span>\n )\n },\n },\n {\n field: 'reason',\n headerName: 'Reason',\n width: 150,\n renderCell: ({ row }: { row: MovementRow }) =>\n STOCK_MOVEMENT_REASON_LABEL[row.reason] ?? row.reason,\n },\n {\n field: 'source',\n headerName: 'Source',\n flex: 1,\n minWidth: 160,\n renderCell: ({ row }: { row: MovementRow }) => (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {[\n row.orderId ? `order ${row.orderId}` : null,\n row.locationId ? `at ${row.locationId}` : null,\n row.source ? `counted by ${row.source}` : null,\n ]\n .filter(Boolean)\n .join(' · ') || '—'}\n </Typography>\n ),\n },\n ] as GridColDef[],\n STOCK_MOVEMENT_FILTER_FIELDS,\n filterOptions,\n STOCK_MOVEMENT_FILTER_HEADERS,\n ),\n [filterOptions],\n )\n\n const filtering = gridFilter.clauses.length > 0\n // The ledger is empty only when an UNFILTERED first page came back empty;\n // a filter that matches nothing keeps the grid, its chips and its panel.\n const empty = status === 'success' && !filtering && page === 0 && movements.length === 0\n\n return (\n <CardDisplay header=\"Stock movements\" help={movementsHelp} contentGutterX contentGutterY>\n <Stack spacing={2}>\n {empty ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No stock movements recorded yet. Sales, returns, cancellations ' +\n 'and hand adjustments all land here.'}\n </Typography>\n ) : (\n <>\n <ListFilterChips\n fields={STOCK_MOVEMENT_FILTER_FIELDS}\n headers={STOCK_MOVEMENT_FILTER_HEADERS}\n clauses={gridFilter.clauses}\n onChange={gridFilter.setClauses}\n options={filterOptions}\n />\n <ListQueryNotices\n refused={listQueryRefusals(plan.refused, {\n fields: STOCK_MOVEMENT_FILTER_FIELDS,\n headers: STOCK_MOVEMENT_FILTER_HEADERS,\n options: filterOptions,\n })}\n notices={plan.notices}\n />\n <ListTable\n aria-label=\"Stock movements\"\n rows={movements}\n columns={columns}\n /*\n * The grid must NOT also filter or sort. The query answers\n * both, and the ledger has one order — newest first — which\n * the query already holds.\n */\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n sortingMode=\"server\"\n disableColumnSorting\n // `ListPagination` below pages the query.\n hideFooter\n noRowsLabel=\"No stock movements match these filters\"\n initialState={{\n columns: {\n columnVisibilityModel: hiddenFilterVisibility(\n STOCK_MOVEMENT_FILTER_FIELDS,\n VISIBLE_COLUMNS,\n ),\n },\n }}\n />\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={movements.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </>\n )}\n </Stack>\n </CardDisplay>\n )\n}\nStockMovementsCard.displayName = 'StockMovementsCard'\n\nexport default StockMovementsCard\n"],"names":["CardDisplay","ListFilterChips","ListPagination","ListQueryNotices","listQueryRefusals","ListTable","hiddenFilterVisibility","listFilterGridColumns","useListGridFilter","Stack","Typography","collection","useMemo","ceilingedWindow","collectionCeiling","useFirestore","useFirestoreCollection","useListQuery","pluginDocsHelp","STOCK_MOVEMENT_FILTER_FIELDS","STOCK_MOVEMENT_FILTER_HEADERS","STOCK_MOVEMENT_QUERY","STOCK_MOVEMENT_REASON_LABEL","STOCK_MOVEMENT_SELECT_FIELDS","PRODUCT_NAMES_CEILING","LOCATION_CEILING","movementsHelp","anchor","title","excerpt","REASON_OPTIONS","Object","entries","map","value","label","VISIBLE_COLUMNS","StockMovementsCard","props","hostId","firestore","data","productDocs","idField","locationDocs","products","undefined","rows","productNames","names","Map","product","set","$id","name","filterOptions","productId","sort","a","b","localeCompare","reason","locationId","location","gridFilter","selectFields","movementRows","hasMore","page","setPage","pageSize","setPageSize","plan","status","declaration","request","clauses","deps","movements","row","productName","get","columns","field","headerName","width","renderCell","atMs","Date","Number","toLocaleString","flex","minWidth","span","variantId","variant","color","type","align","headerAlign","delta","applied","appliedDelta","component","String","orderId","source","filter","Boolean","join","filtering","length","empty","header","help","contentGutterX","contentGutterY","spacing","fields","headers","onChange","setClauses","options","refused","notices","aria-label","filterMode","filterModel","onFilterModelChange","sortingMode","disableColumnSorting","hideFooter","noRowsLabel","initialState","columnVisibilityModel","rowCount","onPageChange","onPageSizeChange","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/stock-movements-card.component.tsx"],"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'use client'\n\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport {\n ListQueryNotices,\n listQueryRefusals,\n} from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { hiddenFilterVisibility } from '@aglyn/shared-ui-jsx/const/list-filter'\nimport { listFilterGridColumns } from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport type { ListQuerySort } from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport { useListColumnSort } from '@aglyn/shared-ui-jsx/hooks/use-list-column-sort'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport { Stack, Typography } from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { collection } from 'firebase/firestore'\nimport { useMemo, useState } from 'react'\nimport {\n ceilingedWindow,\n collectionCeiling,\n useFirestore,\n useFirestoreCollection,\n} from '@aglyn/tenant-feature-instance'\nimport { useListQuery } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport {\n STOCK_MOVEMENT_COLUMN_SORTS,\n STOCK_MOVEMENT_FILTER_FIELDS,\n STOCK_MOVEMENT_FILTER_HEADERS,\n STOCK_MOVEMENT_QUERY,\n STOCK_MOVEMENT_REASON_LABEL,\n STOCK_MOVEMENT_SELECT_FIELDS,\n} from '../../constants/stock-movement-query'\n\nexport interface StockMovementsCardProps {\n hostId: string\n}\n\n/**\n * How many products the card reads to NAME its rows and offer the Product\n * filter's choices. A picker's reach, not the ledger's: the ledger itself is\n * paged by its query and reaches every movement whatever this holds.\n */\nconst PRODUCT_NAMES_CEILING = 500\n/** A site's locations, which the plan caps well under this. */\nconst LOCATION_CEILING = 25\n\nconst movementsHelp = pluginDocsHelp('commerce', {\n anchor: '#stock-movements',\n title: 'Stock movements',\n excerpt:\n 'Every change to a tracked count — sales, returns, cancellations and ' +\n 'hand adjustments — newest first, with the reason each one was made.',\n})\n\nconst REASON_OPTIONS = Object.entries(STOCK_MOVEMENT_REASON_LABEL).map(([value, label]) => ({\n value,\n label,\n}))\n\n/** The columns the grid draws; every other declared field is a hidden filter column. */\nconst VISIBLE_COLUMNS = ['atMs', 'productId', 'delta', 'reason', 'source']\n\ntype MovementRow = CommerceModel.InventoryAdjustment & { $id: string }\n\n/** What the Source column reads: the order, the location and who counted. */\nconst movementSource = (row: MovementRow): string =>\n [\n row.orderId ? `order ${row.orderId}` : null,\n row.locationId ? `at ${row.locationId}` : null,\n row.source ? `counted by ${row.source}` : null,\n ]\n .filter(Boolean)\n .join(' · ')\n\n/**\n * Product (a name read from the product) and Source (composed from three\n * fields) sort the page on screen (AGL-3680); When, Change and Reason are the\n * query's order (`STOCK_MOVEMENT_COLUMN_SORTS`).\n */\nconst MOVEMENT_PAGE_SORTS = {\n productId: (row: MovementRow & { productName?: string }) => row.productName ?? row.productId,\n source: (row: MovementRow) => movementSource(row) || null,\n}\nconst MOVEMENT_PAGE_SORT_HEADERS = { productId: 'Product', source: 'Source' }\n\n/**\n * Stock movements (AGL-2341) — the adjustment history that had no history\n * view.\n *\n * `hosts/{hostId}/inventoryAdjustments` has three writers: the products\n * hub's \"Adjust stock\" dialog, `decrementVariantStock` (the sale, whether the\n * checkout webhook or the POS sold it), and the restock in `cancel-order.ts`.\n * The hub's own comment calls the collection \"adjustment history\".\n *\n * Its only reader was arithmetic: `cancel-order.ts` projects `appliedDelta`\n * off the `reason: 'sale'` rows to cap what a cancellation may put back. No\n * surface displayed a single one of them. A merchant whose count disagreed\n * with the shelf had no way to see what moved it, when, or why — and the one\n * number that reconciles the discrepancy was written on every adjustment and\n * unreachable.\n *\n * `delta` is what the merchant's history says moved; `appliedDelta` is what\n * the count could actually give up when the floor in `adjustVariantInventory`\n * absorbed part of it, which happens on a backorder product selling past\n * zero. They differ rarely and they differ importantly — three sold out of a\n * count of zero is exactly the state a merchant is trying to explain — so\n * both are shown when they disagree and only `delta` when they do not.\n *\n * ## Every filter is on the query (AGL-3321)\n *\n * The ledger used to read its hundred newest movements and filter those, so\n * \"Damaged\" searched a hundred rows and answered \"none\" about a ledger full\n * of older ones. Now the Filters panel's clauses are the query's `where`s —\n * see `STOCK_MOVEMENT_QUERY` for what each one costs — and the footer pages\n * the query's answer, so page three of \"Damaged\" is the third page of damage\n * the ledger holds, however far back it goes.\n */\nexport function StockMovementsCard(props: StockMovementsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n\n // Names, so a row reads as \"Desk lamp\" rather than a 20-character id, and\n // the Product filter's choices.\n const { data: productDocs } = useFirestoreCollection<any>(\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'products'),\n PRODUCT_NAMES_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { data: locationDocs } = useFirestoreCollection<any>(\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'locations'),\n LOCATION_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const products = useMemo(\n () => ceilingedWindow<any>(productDocs ?? undefined, PRODUCT_NAMES_CEILING).rows,\n [productDocs],\n )\n const productNames = useMemo(() => {\n const names = new Map<string, string>()\n for (const product of products) {\n names.set(product.$id, product.name ?? product.$id)\n }\n return names\n }, [products])\n\n const filterOptions = useMemo(\n () => ({\n productId: [...productNames]\n .map(([value, label]) => ({ value, label }))\n .sort((a, b) => a.label.localeCompare(b.label)),\n reason: REASON_OPTIONS,\n locationId: ceilingedWindow<any>(locationDocs ?? undefined, LOCATION_CEILING).rows.map(\n (location: any) => ({ value: location.$id, label: location.name ?? location.$id }),\n ),\n }),\n [productNames, locationDocs],\n )\n\n const gridFilter = useListGridFilter({ selectFields: STOCK_MOVEMENT_SELECT_FIELDS })\n // Every header sorts (AGL-3680); newest first until one is clicked.\n const [askedSort, setAskedSort] = useState<ListQuerySort | null>(STOCK_MOVEMENT_COLUMN_SORTS[0])\n const {\n rows: movementRows,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n plan,\n status,\n } = useListQuery<MovementRow>({\n collection: collection(firestore, 'hosts', hostId, 'inventoryAdjustments'),\n declaration: STOCK_MOVEMENT_QUERY,\n request: { clauses: gridFilter.clauses, sort: askedSort },\n deps: [firestore, hostId],\n idField: '$id',\n })\n const movements = useMemo(\n () =>\n movementRows.map((row) => ({\n ...row,\n productName: productNames.get(row.productId) ?? row.productId,\n })),\n [movementRows, productNames],\n )\n const columnSort = useListColumnSort({\n sorts: STOCK_MOVEMENT_COLUMN_SORTS,\n defaultSort: STOCK_MOVEMENT_COLUMN_SORTS[0],\n sort: askedSort,\n onSortChange: setAskedSort,\n orderBy: plan.orderBy,\n rows: movements,\n pageSorts: MOVEMENT_PAGE_SORTS,\n headers: MOVEMENT_PAGE_SORT_HEADERS,\n })\n\n const columns = useMemo(\n () =>\n listFilterGridColumns(\n [\n {\n field: 'atMs',\n headerName: 'When',\n width: 190,\n renderCell: ({ row }: { row: MovementRow }) =>\n row.atMs ? new Date(Number(row.atMs)).toLocaleString() : '—',\n },\n {\n field: 'productId',\n headerName: 'Product',\n flex: 1,\n minWidth: 180,\n renderCell: ({ row }: { row: MovementRow & { productName: string } }) => (\n <span>\n {row.productName}\n {row.variantId ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {` · ${row.variantId}`}\n </Typography>\n ) : null}\n </span>\n ),\n },\n {\n field: 'delta',\n headerName: 'Change',\n type: 'number',\n width: 150,\n align: 'right',\n headerAlign: 'right',\n renderCell: ({ row }: { row: MovementRow }) => {\n const delta = Number(row.delta ?? 0)\n const applied = Number(row.appliedDelta ?? delta)\n return (\n <span>\n {/*\n * The sign is carried explicitly. \"3\" and \"-3\" are the same\n * width and opposite facts, and a merchant scanning a column\n * for the movement that broke their count reads the sign\n * before the number.\n */}\n <Typography\n variant=\"body2\"\n color={delta < 0 ? 'error.main' : 'success.main'}\n component=\"span\"\n >\n {delta > 0 ? `+${delta}` : String(delta)}\n </Typography>\n {applied !== delta ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {` (${applied > 0 ? `+${applied}` : applied} applied)`}\n </Typography>\n ) : null}\n </span>\n )\n },\n },\n {\n field: 'reason',\n headerName: 'Reason',\n width: 150,\n renderCell: ({ row }: { row: MovementRow }) =>\n STOCK_MOVEMENT_REASON_LABEL[row.reason] ?? row.reason,\n },\n {\n field: 'source',\n headerName: 'Source',\n flex: 1,\n minWidth: 160,\n renderCell: ({ row }: { row: MovementRow }) => (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {movementSource(row) || '—'}\n </Typography>\n ),\n },\n ] as GridColDef[],\n STOCK_MOVEMENT_FILTER_FIELDS,\n filterOptions,\n STOCK_MOVEMENT_FILTER_HEADERS,\n ),\n [filterOptions],\n )\n\n const filtering = gridFilter.clauses.length > 0\n // The ledger is empty only when an UNFILTERED first page came back empty;\n // a filter that matches nothing keeps the grid, its chips and its panel.\n const empty =\n status === 'success' &&\n !filtering &&\n askedSort === STOCK_MOVEMENT_COLUMN_SORTS[0] &&\n page === 0 &&\n movements.length === 0\n\n return (\n <CardDisplay header=\"Stock movements\" help={movementsHelp} contentGutterX contentGutterY>\n <Stack spacing={2}>\n {empty ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No stock movements recorded yet. Sales, returns, cancellations ' +\n 'and hand adjustments all land here.'}\n </Typography>\n ) : (\n <>\n <ListFilterChips\n fields={STOCK_MOVEMENT_FILTER_FIELDS}\n headers={STOCK_MOVEMENT_FILTER_HEADERS}\n clauses={gridFilter.clauses}\n onChange={gridFilter.setClauses}\n options={filterOptions}\n />\n <ListQueryNotices\n refused={listQueryRefusals(plan.refused, {\n fields: STOCK_MOVEMENT_FILTER_FIELDS,\n headers: STOCK_MOVEMENT_FILTER_HEADERS,\n options: filterOptions,\n })}\n notices={[...plan.notices, ...columnSort.notices]}\n />\n <ListTable\n aria-label=\"Stock movements\"\n rows={columnSort.rows}\n columns={columns}\n /*\n * The grid must NOT also filter or sort on its own. The query\n * answers the filters, and every header order is the query's\n * or the page's (`columnSort`).\n */\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n columnSort={columnSort}\n // `ListPagination` below pages the query.\n hideFooter\n noRowsLabel=\"No stock movements match these filters\"\n initialState={{\n columns: {\n columnVisibilityModel: hiddenFilterVisibility(\n STOCK_MOVEMENT_FILTER_FIELDS,\n VISIBLE_COLUMNS,\n ),\n },\n }}\n />\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={movements.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </>\n )}\n </Stack>\n </CardDisplay>\n )\n}\nStockMovementsCard.displayName = 'StockMovementsCard'\n\nexport default StockMovementsCard\n"],"names":["CardDisplay","ListFilterChips","ListPagination","ListQueryNotices","listQueryRefusals","ListTable","hiddenFilterVisibility","listFilterGridColumns","useListColumnSort","useListGridFilter","Stack","Typography","collection","useMemo","useState","ceilingedWindow","collectionCeiling","useFirestore","useFirestoreCollection","useListQuery","pluginDocsHelp","STOCK_MOVEMENT_COLUMN_SORTS","STOCK_MOVEMENT_FILTER_FIELDS","STOCK_MOVEMENT_FILTER_HEADERS","STOCK_MOVEMENT_QUERY","STOCK_MOVEMENT_REASON_LABEL","STOCK_MOVEMENT_SELECT_FIELDS","PRODUCT_NAMES_CEILING","LOCATION_CEILING","movementsHelp","anchor","title","excerpt","REASON_OPTIONS","Object","entries","map","value","label","VISIBLE_COLUMNS","movementSource","row","orderId","locationId","source","filter","Boolean","join","MOVEMENT_PAGE_SORTS","productId","productName","MOVEMENT_PAGE_SORT_HEADERS","StockMovementsCard","props","hostId","firestore","data","productDocs","idField","locationDocs","products","undefined","rows","productNames","names","Map","product","set","$id","name","filterOptions","sort","a","b","localeCompare","reason","location","gridFilter","selectFields","askedSort","setAskedSort","movementRows","hasMore","page","setPage","pageSize","setPageSize","plan","status","declaration","request","clauses","deps","movements","get","columnSort","sorts","defaultSort","onSortChange","orderBy","pageSorts","headers","columns","field","headerName","width","renderCell","atMs","Date","Number","toLocaleString","flex","minWidth","span","variantId","variant","color","type","align","headerAlign","delta","applied","appliedDelta","component","String","filtering","length","empty","header","help","contentGutterX","contentGutterY","spacing","fields","onChange","setClauses","options","refused","notices","aria-label","filterMode","filterModel","onFilterModelChange","hideFooter","noRowsLabel","initialState","columnVisibilityModel","rowCount","onPageChange","onPageSizeChange","displayName"],"mappings":"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"}
@@ -38,5 +38,12 @@ export interface ConsoleCommerceSettingsZoneProps {
38
38
  }
39
39
  export declare const ORDERS_BULK_ZONE: import("@aglyn/aglyn/plugin-manager/plugin-zones").PluginZone<ConsoleOrdersBulkZoneProps>;
40
40
  export declare const COMMERCE_SETTINGS_ZONE: import("@aglyn/aglyn/plugin-manager/plugin-zones").PluginZone<ConsoleCommerceSettingsZoneProps>;
41
- /** Declares both, from the console registrar. */
41
+ /** What `commercePromotions` hands a widget: the site (AGL-3640). */
42
+ export type ConsoleCommercePromotionsZoneProps = ConsoleCommerceSettingsZoneProps;
43
+ /**
44
+ * At the foot of the store's Promotions, beside its discounts and gift
45
+ * cards (AGL-3640): a rewards program is the first widget.
46
+ */
47
+ export declare const COMMERCE_PROMOTIONS_ZONE: import("@aglyn/aglyn/plugin-manager/plugin-zones").PluginZone<ConsoleCommerceSettingsZoneProps>;
48
+ /** Declares them, from the console registrar. */
42
49
  export declare function registerCommerceStoreZones(): void;
@@ -17,7 +17,11 @@
17
17
  import { BUNDLE_ID } from "../../constants/bundle-common.js";
18
18
  export const ORDERS_BULK_ZONE = definePluginZone('ordersBulk');
19
19
  export const COMMERCE_SETTINGS_ZONE = definePluginZone('commerceSettings');
20
- /** Declares both, from the console registrar. */ export function registerCommerceStoreZones() {
20
+ /**
21
+ * At the foot of the store's Promotions, beside its discounts and gift
22
+ * cards (AGL-3640): a rewards program is the first widget.
23
+ */ export const COMMERCE_PROMOTIONS_ZONE = definePluginZone('commercePromotions');
24
+ /** Declares them, from the console registrar. */ export function registerCommerceStoreZones() {
21
25
  const owner = {
22
26
  pluginId: BUNDLE_ID
23
27
  };
@@ -28,6 +32,12 @@ export const COMMERCE_SETTINGS_ZONE = definePluginZone('commerceSettings');
28
32
  layout: 'bare',
29
33
  description: 'Beside the orders list’s bulk actions, while orders are ticked. A widget here acts on the ticked orders through its own routes and writes no order itself.'
30
34
  }, owner);
35
+ registerPluginZone({
36
+ zone: COMMERCE_PROMOTIONS_ZONE,
37
+ label: 'Store promotions',
38
+ surface: 'console',
39
+ description: 'At the foot of the store’s Promotions, beside its discounts and gift cards. A widget here keeps its own programs and balances through its own routes; it writes none of the store’s.'
40
+ }, owner);
31
41
  registerPluginZone({
32
42
  zone: COMMERCE_SETTINGS_ZONE,
33
43
  label: 'Store settings',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/store-zones.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 {\n definePluginZone,\n registerPluginZone,\n} from '@aglyn/aglyn/plugin-manager/plugin-zones'\nimport { BUNDLE_ID } from '../../constants/bundle-common'\n\n/**\n * Two more zones the store's console hosts (AGL-3612): `ordersBulk`, beside\n * the orders list's bulk actions, and `commerceSettings`, at the foot of the\n * store's Settings. A shipping plugin's batch labels and its label settings\n * are the first widgets; another plugin restates these props rather than\n * importing this package.\n */\n\n/** What `ordersBulk` hands a widget: the orders ticked in the list. */\nexport interface ConsoleOrdersBulkZoneProps {\n hostId: string\n /** The org the page names; `undefined` where the host does not know it. */\n orgId: string | undefined\n /** The ticked orders' ids, in the list's order. */\n selectedOrderIds: readonly string[]\n /** The store's name, for anything printed. */\n storeName: string\n}\n\n/** What `commerceSettings` hands a widget: the site. */\nexport interface ConsoleCommerceSettingsZoneProps {\n hostId: string\n orgId: string | undefined\n}\n\nexport const ORDERS_BULK_ZONE = definePluginZone<ConsoleOrdersBulkZoneProps>('ordersBulk')\n\nexport const COMMERCE_SETTINGS_ZONE =\n definePluginZone<ConsoleCommerceSettingsZoneProps>('commerceSettings')\n\n/** Declares both, from the console registrar. */\nexport function registerCommerceStoreZones(): void {\n const owner = { pluginId: BUNDLE_ID }\n registerPluginZone(\n {\n zone: ORDERS_BULK_ZONE,\n label: 'Orders bulk actions',\n surface: 'console',\n layout: 'bare',\n description:\n 'Beside the orders list’s bulk actions, while orders are ticked. A widget here acts on the ticked orders through its own routes and writes no order itself.',\n },\n owner,\n )\n registerPluginZone(\n {\n zone: COMMERCE_SETTINGS_ZONE,\n label: 'Store settings',\n surface: 'console',\n description:\n 'At the foot of the store’s Settings. A widget here keeps settings of its own beside the store’s; it writes none of the store’s.',\n },\n owner,\n )\n}\n"],"names":["definePluginZone","registerPluginZone","BUNDLE_ID","ORDERS_BULK_ZONE","COMMERCE_SETTINGS_ZONE","registerCommerceStoreZones","owner","pluginId","zone","label","surface","layout","description"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,gBAAgB,EAChBC,kBAAkB,QACb,2CAA0C;AACjD,SAASC,SAAS,QAAQ,mCAA+B;AA2BzD,OAAO,MAAMC,mBAAmBH,iBAA6C,cAAa;AAE1F,OAAO,MAAMI,yBACXJ,iBAAmD,oBAAmB;AAExE,+CAA+C,GAC/C,OAAO,SAASK;IACd,MAAMC,QAAQ;QAAEC,UAAUL;IAAU;IACpCD,mBACE;QACEO,MAAML;QACNM,OAAO;QACPC,SAAS;QACTC,QAAQ;QACRC,aACE;IACJ,GACAN;IAEFL,mBACE;QACEO,MAAMJ;QACNK,OAAO;QACPC,SAAS;QACTE,aACE;IACJ,GACAN;AAEJ"}
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/store-zones.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 {\n definePluginZone,\n registerPluginZone,\n} from '@aglyn/aglyn/plugin-manager/plugin-zones'\nimport { BUNDLE_ID } from '../../constants/bundle-common'\n\n/**\n * Two more zones the store's console hosts (AGL-3612): `ordersBulk`, beside\n * the orders list's bulk actions, and `commerceSettings`, at the foot of the\n * store's Settings. A shipping plugin's batch labels and its label settings\n * are the first widgets; another plugin restates these props rather than\n * importing this package.\n */\n\n/** What `ordersBulk` hands a widget: the orders ticked in the list. */\nexport interface ConsoleOrdersBulkZoneProps {\n hostId: string\n /** The org the page names; `undefined` where the host does not know it. */\n orgId: string | undefined\n /** The ticked orders' ids, in the list's order. */\n selectedOrderIds: readonly string[]\n /** The store's name, for anything printed. */\n storeName: string\n}\n\n/** What `commerceSettings` hands a widget: the site. */\nexport interface ConsoleCommerceSettingsZoneProps {\n hostId: string\n orgId: string | undefined\n}\n\nexport const ORDERS_BULK_ZONE = definePluginZone<ConsoleOrdersBulkZoneProps>('ordersBulk')\n\nexport const COMMERCE_SETTINGS_ZONE =\n definePluginZone<ConsoleCommerceSettingsZoneProps>('commerceSettings')\n\n/** What `commercePromotions` hands a widget: the site (AGL-3640). */\nexport type ConsoleCommercePromotionsZoneProps = ConsoleCommerceSettingsZoneProps\n\n/**\n * At the foot of the store's Promotions, beside its discounts and gift\n * cards (AGL-3640): a rewards program is the first widget.\n */\nexport const COMMERCE_PROMOTIONS_ZONE =\n definePluginZone<ConsoleCommercePromotionsZoneProps>('commercePromotions')\n\n/** Declares them, from the console registrar. */\nexport function registerCommerceStoreZones(): void {\n const owner = { pluginId: BUNDLE_ID }\n registerPluginZone(\n {\n zone: ORDERS_BULK_ZONE,\n label: 'Orders bulk actions',\n surface: 'console',\n layout: 'bare',\n description:\n 'Beside the orders list’s bulk actions, while orders are ticked. A widget here acts on the ticked orders through its own routes and writes no order itself.',\n },\n owner,\n )\n registerPluginZone(\n {\n zone: COMMERCE_PROMOTIONS_ZONE,\n label: 'Store promotions',\n surface: 'console',\n description:\n 'At the foot of the store’s Promotions, beside its discounts and gift cards. A widget here keeps its own programs and balances through its own routes; it writes none of the store’s.',\n },\n owner,\n )\n registerPluginZone(\n {\n zone: COMMERCE_SETTINGS_ZONE,\n label: 'Store settings',\n surface: 'console',\n description:\n 'At the foot of the store’s Settings. A widget here keeps settings of its own beside the store’s; it writes none of the store’s.',\n },\n owner,\n )\n}\n"],"names":["definePluginZone","registerPluginZone","BUNDLE_ID","ORDERS_BULK_ZONE","COMMERCE_SETTINGS_ZONE","COMMERCE_PROMOTIONS_ZONE","registerCommerceStoreZones","owner","pluginId","zone","label","surface","layout","description"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,gBAAgB,EAChBC,kBAAkB,QACb,2CAA0C;AACjD,SAASC,SAAS,QAAQ,mCAA+B;AA2BzD,OAAO,MAAMC,mBAAmBH,iBAA6C,cAAa;AAE1F,OAAO,MAAMI,yBACXJ,iBAAmD,oBAAmB;AAKxE;;;CAGC,GACD,OAAO,MAAMK,2BACXL,iBAAqD,sBAAqB;AAE5E,+CAA+C,GAC/C,OAAO,SAASM;IACd,MAAMC,QAAQ;QAAEC,UAAUN;IAAU;IACpCD,mBACE;QACEQ,MAAMN;QACNO,OAAO;QACPC,SAAS;QACTC,QAAQ;QACRC,aACE;IACJ,GACAN;IAEFN,mBACE;QACEQ,MAAMJ;QACNK,OAAO;QACPC,SAAS;QACTE,aACE;IACJ,GACAN;IAEFN,mBACE;QACEQ,MAAML;QACNM,OAAO;QACPC,SAAS;QACTE,aACE;IACJ,GACAN;AAEJ"}
@@ -240,6 +240,72 @@ function dateLabel(atMs) {
240
240
  })
241
241
  }, step.key))
242
242
  }),
243
+ view.pickup ? /*#__PURE__*/ _jsx(Paper, {
244
+ variant: "outlined",
245
+ sx: {
246
+ p: 2
247
+ },
248
+ children: /*#__PURE__*/ _jsxs(Stack, {
249
+ spacing: 0.5,
250
+ children: [
251
+ /*#__PURE__*/ _jsx(Typography, {
252
+ variant: "h6",
253
+ component: "h2",
254
+ children: view.pickup.status === 'picked_up' ? 'Picked up' : 'Pickup'
255
+ }),
256
+ /*#__PURE__*/ _jsx(Typography, {
257
+ variant: "body2",
258
+ children: [
259
+ view.pickup.locationName,
260
+ view.pickup.address
261
+ ].filter(Boolean).join(', ')
262
+ }),
263
+ view.pickup.status === 'preparing' ? /*#__PURE__*/ _jsx(Typography, {
264
+ variant: "body2",
265
+ color: "text.secondary",
266
+ children: 'We’re preparing your order and will email you when it’s ready.'
267
+ }) : null,
268
+ view.pickup.hours && view.pickup.status !== 'picked_up' ? /*#__PURE__*/ _jsx(Typography, {
269
+ variant: "body2",
270
+ color: "text.secondary",
271
+ sx: {
272
+ whiteSpace: 'pre-line'
273
+ },
274
+ children: view.pickup.hours
275
+ }) : null,
276
+ view.pickup.instructions && view.pickup.status !== 'picked_up' ? /*#__PURE__*/ _jsx(Typography, {
277
+ variant: "body2",
278
+ color: "text.secondary",
279
+ children: view.pickup.instructions
280
+ }) : null
281
+ ]
282
+ })
283
+ }) : null,
284
+ view.localDelivery ? /*#__PURE__*/ _jsx(Paper, {
285
+ variant: "outlined",
286
+ sx: {
287
+ p: 2
288
+ },
289
+ children: /*#__PURE__*/ _jsxs(Stack, {
290
+ spacing: 0.5,
291
+ children: [
292
+ /*#__PURE__*/ _jsx(Typography, {
293
+ variant: "h6",
294
+ component: "h2",
295
+ children: 'Local delivery'
296
+ }),
297
+ /*#__PURE__*/ _jsx(Typography, {
298
+ variant: "body2",
299
+ children: view.localDelivery.statusLabel
300
+ }),
301
+ view.localDelivery.windowLabel ? /*#__PURE__*/ _jsx(Typography, {
302
+ variant: "body2",
303
+ color: "text.secondary",
304
+ children: view.localDelivery.windowLabel
305
+ }) : null
306
+ ]
307
+ })
308
+ }) : null,
243
309
  view.shipments.length ? /*#__PURE__*/ _jsxs(Stack, {
244
310
  spacing: 1.5,
245
311
  children: [