@aglyn/plugins-commerce 1.0.0-beta.285 → 1.0.0-beta.288

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 (430) hide show
  1. package/README.md +1 -1
  2. package/package.json +13 -13
  3. package/src/lib/components/cart.js +25 -12
  4. package/src/lib/components/cart.js.map +1 -1
  5. package/src/lib/components/commerce-console-page.js +70 -13
  6. package/src/lib/components/commerce-console-page.js.map +1 -1
  7. package/src/lib/components/console/order-detail-dialog.component.js +2 -1
  8. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
  9. package/src/lib/components/console/order-webhooks-card.component.js +1 -1
  10. package/src/lib/components/console/order-webhooks-card.component.js.map +1 -1
  11. package/src/lib/components/console/payment-methods-card.component.js +1 -1
  12. package/src/lib/components/console/payment-methods-card.component.js.map +1 -1
  13. package/src/lib/components/console/pos/pos-cart-panel.component.d.ts +5 -0
  14. package/src/lib/components/console/pos/pos-cart-panel.component.js +18 -12
  15. package/src/lib/components/console/pos/pos-cart-panel.component.js.map +1 -1
  16. package/src/lib/components/console/pos/pos-hardware-api.d.ts +76 -5
  17. package/src/lib/components/console/pos/pos-hardware-api.js +53 -11
  18. package/src/lib/components/console/pos/pos-hardware-api.js.map +1 -1
  19. package/src/lib/components/console/pos/pos-hardware-buyer.component.d.ts +18 -0
  20. package/src/lib/components/console/pos/pos-hardware-buyer.component.js +629 -0
  21. package/src/lib/components/console/pos/pos-hardware-buyer.component.js.map +1 -0
  22. package/src/lib/components/console/pos/pos-hardware-sale-api.d.ts +162 -0
  23. package/src/lib/components/console/pos/pos-hardware-sale-api.js +122 -0
  24. package/src/lib/components/console/pos/pos-hardware-sale-api.js.map +1 -0
  25. package/src/lib/components/console/pos/pos-hardware-sale-terms.component.d.ts +39 -0
  26. package/src/lib/components/console/pos/pos-hardware-sale-terms.component.js +251 -0
  27. package/src/lib/components/console/pos/pos-hardware-sale-terms.component.js.map +1 -0
  28. package/src/lib/components/console/pos/use-pos-display.js +50 -0
  29. package/src/lib/components/console/pos/use-pos-display.js.map +1 -1
  30. package/src/lib/components/console/pos-cellular-control.component.d.ts +41 -0
  31. package/src/lib/components/console/pos-cellular-control.component.js +211 -0
  32. package/src/lib/components/console/pos-cellular-control.component.js.map +1 -0
  33. package/src/lib/components/console/pos-devices-card.component.d.ts +9 -1
  34. package/src/lib/components/console/pos-devices-card.component.js +47 -15
  35. package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
  36. package/src/lib/components/console/pos-display/pos-display-api.d.ts +6 -0
  37. package/src/lib/components/console/pos-display/pos-display-api.js +8 -1
  38. package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -1
  39. package/src/lib/components/console/pos-display-page.component.d.ts +13 -0
  40. package/src/lib/components/console/pos-display-page.component.js +44 -2
  41. package/src/lib/components/console/pos-display-page.component.js.map +1 -1
  42. package/src/lib/components/console/pos-hardware-card.component.d.ts +12 -4
  43. package/src/lib/components/console/pos-hardware-card.component.js +157 -36
  44. package/src/lib/components/console/pos-hardware-card.component.js.map +1 -1
  45. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js +360 -80
  46. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js.map +1 -1
  47. package/src/lib/components/console/pos-hardware-terms-page.component.d.ts +34 -0
  48. package/src/lib/components/console/pos-hardware-terms-page.component.js +257 -0
  49. package/src/lib/components/console/pos-hardware-terms-page.component.js.map +1 -0
  50. package/src/lib/components/console/pos-hardware-terms-sign-page.component.d.ts +27 -0
  51. package/src/lib/components/console/pos-hardware-terms-sign-page.component.js +104 -0
  52. package/src/lib/components/console/pos-hardware-terms-sign-page.component.js.map +1 -0
  53. package/src/lib/components/console/pos-hardware-waiting-card.component.d.ts +24 -0
  54. package/src/lib/components/console/pos-hardware-waiting-card.component.js +205 -0
  55. package/src/lib/components/console/pos-hardware-waiting-card.component.js.map +1 -0
  56. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js +297 -201
  57. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js.map +1 -1
  58. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.d.ts +3 -0
  59. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +260 -92
  60. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -1
  61. package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +9 -0
  62. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +15 -6
  63. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -1
  64. package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.d.ts +25 -0
  65. package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.js +136 -0
  66. package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.js.map +1 -0
  67. package/src/lib/components/console/pos-ops/pos-shift-panel.component.d.ts +6 -0
  68. package/src/lib/components/console/pos-ops/pos-shift-panel.component.js +48 -31
  69. package/src/lib/components/console/pos-ops/pos-shift-panel.component.js.map +1 -1
  70. package/src/lib/components/console/pos-page.component.d.ts +1 -1
  71. package/src/lib/components/console/pos-page.component.js +63 -12
  72. package/src/lib/components/console/pos-page.component.js.map +1 -1
  73. package/src/lib/components/console/pos-reader-splash-setting.component.js +1 -1
  74. package/src/lib/components/console/pos-reader-splash-setting.component.js.map +1 -1
  75. package/src/lib/components/console/product-check-in-fields.component.d.ts +4 -2
  76. package/src/lib/components/console/product-check-in-fields.component.js +60 -14
  77. package/src/lib/components/console/product-check-in-fields.component.js.map +1 -1
  78. package/src/lib/components/console/product-editor-dialog.component.js +44 -1
  79. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  80. package/src/lib/components/console/product-hardware-sku-field.component.d.ts +19 -0
  81. package/src/lib/components/console/product-hardware-sku-field.component.js +92 -0
  82. package/src/lib/components/console/product-hardware-sku-field.component.js.map +1 -0
  83. package/src/lib/components/console/product-kiosk-offer-field.component.d.ts +22 -0
  84. package/src/lib/components/console/product-kiosk-offer-field.component.js +94 -0
  85. package/src/lib/components/console/product-kiosk-offer-field.component.js.map +1 -0
  86. package/src/lib/components/console/product-register-fields.component.d.ts +4 -0
  87. package/src/lib/components/console/product-register-fields.component.js +31 -2
  88. package/src/lib/components/console/product-register-fields.component.js.map +1 -1
  89. package/src/lib/components/console/products-hub-card.component.js +11 -2
  90. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  91. package/src/lib/components/console/register-hardware-card.component.d.ts +11 -0
  92. package/src/lib/components/console/register-hardware-card.component.js +56 -27
  93. package/src/lib/components/console/register-hardware-card.component.js.map +1 -1
  94. package/src/lib/components/console/reservation-guests-dialog.component.d.ts +28 -0
  95. package/src/lib/components/console/reservation-guests-dialog.component.js +189 -0
  96. package/src/lib/components/console/reservation-guests-dialog.component.js.map +1 -0
  97. package/src/lib/components/console/reservations-card.component.d.ts +9 -0
  98. package/src/lib/components/console/reservations-card.component.js +158 -23
  99. package/src/lib/components/console/reservations-card.component.js.map +1 -1
  100. package/src/lib/components/console/shippingeasy-card.component.js +1 -1
  101. package/src/lib/components/console/shippingeasy-card.component.js.map +1 -1
  102. package/src/lib/components/console/shipstation-card.component.d.ts +1 -1
  103. package/src/lib/components/console/shipstation-card.component.js +3 -3
  104. package/src/lib/components/console/shipstation-card.component.js.map +1 -1
  105. package/src/lib/components/product-detail.d.ts +4 -0
  106. package/src/lib/components/product-detail.js +40 -5
  107. package/src/lib/components/product-detail.js.map +1 -1
  108. package/src/lib/components/product-reviews.js +28 -11
  109. package/src/lib/components/product-reviews.js.map +1 -1
  110. package/src/lib/components/reservation-widget.js +28 -5
  111. package/src/lib/components/reservation-widget.js.map +1 -1
  112. package/src/lib/components/use-day-picker.d.ts +25 -0
  113. package/src/lib/components/use-day-picker.js +57 -0
  114. package/src/lib/components/use-day-picker.js.map +1 -0
  115. package/src/lib/constants/operator-alerts.d.ts +55 -0
  116. package/src/lib/constants/operator-alerts.js +156 -1
  117. package/src/lib/constants/operator-alerts.js.map +1 -1
  118. package/src/lib/declarations.console-server.js +54 -1
  119. package/src/lib/declarations.console-server.js.map +1 -1
  120. package/src/lib/declarations.d.ts +4 -2
  121. package/src/lib/declarations.js +15 -4
  122. package/src/lib/declarations.js.map +1 -1
  123. package/src/lib/declarations.server.js +9 -0
  124. package/src/lib/declarations.server.js.map +1 -1
  125. package/src/lib/model/commerce-cart.d.ts +9 -1
  126. package/src/lib/model/commerce-cart.js +4 -3
  127. package/src/lib/model/commerce-cart.js.map +1 -1
  128. package/src/lib/model/commerce-check-in-product.d.ts +26 -0
  129. package/src/lib/model/commerce-check-in-product.js +64 -2
  130. package/src/lib/model/commerce-check-in-product.js.map +1 -1
  131. package/src/lib/model/commerce-check-in-record.d.ts +127 -0
  132. package/src/lib/model/commerce-check-in-record.js +36 -0
  133. package/src/lib/model/commerce-check-in-record.js.map +1 -0
  134. package/src/lib/model/commerce-check-in.d.ts +91 -87
  135. package/src/lib/model/commerce-check-in.js +186 -62
  136. package/src/lib/model/commerce-check-in.js.map +1 -1
  137. package/src/lib/model/commerce-kiosk-offer.d.ts +31 -0
  138. package/src/lib/model/commerce-kiosk-offer.js +35 -0
  139. package/src/lib/model/commerce-kiosk-offer.js.map +1 -0
  140. package/src/lib/model/commerce-orders.d.ts +76 -0
  141. package/src/lib/model/commerce-orders.js +6 -0
  142. package/src/lib/model/commerce-orders.js.map +1 -1
  143. package/src/lib/model/commerce-pos-day-report.d.ts +54 -1
  144. package/src/lib/model/commerce-pos-day-report.js +39 -2
  145. package/src/lib/model/commerce-pos-day-report.js.map +1 -1
  146. package/src/lib/model/commerce-pos-offline.d.ts +1 -1
  147. package/src/lib/model/commerce-pos-offline.js +6 -1
  148. package/src/lib/model/commerce-pos-offline.js.map +1 -1
  149. package/src/lib/model/commerce-pos-ops.js +3 -2
  150. package/src/lib/model/commerce-pos-ops.js.map +1 -1
  151. package/src/lib/model/commerce-pos.d.ts +36 -3
  152. package/src/lib/model/commerce-pos.js +14 -6
  153. package/src/lib/model/commerce-pos.js.map +1 -1
  154. package/src/lib/model/commerce-receipt.js +3 -2
  155. package/src/lib/model/commerce-receipt.js.map +1 -1
  156. package/src/lib/model/commerce-reservations.d.ts +69 -4
  157. package/src/lib/model/commerce-reservations.js +57 -5
  158. package/src/lib/model/commerce-reservations.js.map +1 -1
  159. package/src/lib/model/commerce.d.ts +66 -0
  160. package/src/lib/model/commerce.js +15 -0
  161. package/src/lib/model/commerce.js.map +1 -1
  162. package/src/lib/model/order-list-fields.d.ts +11 -0
  163. package/src/lib/model/order-list-fields.js +17 -1
  164. package/src/lib/model/order-list-fields.js.map +1 -1
  165. package/src/lib/model/own-store-tax-summary.d.ts +152 -0
  166. package/src/lib/model/own-store-tax-summary.js +288 -0
  167. package/src/lib/model/own-store-tax-summary.js.map +1 -0
  168. package/src/lib/model/plugin-permissions.d.ts +74 -0
  169. package/src/lib/model/plugin-permissions.js +224 -7
  170. package/src/lib/model/plugin-permissions.js.map +1 -1
  171. package/src/lib/model/pos-cellular.d.ts +223 -0
  172. package/src/lib/model/pos-cellular.js +293 -0
  173. package/src/lib/model/pos-cellular.js.map +1 -0
  174. package/src/lib/model/pos-hardware-in-person.d.ts +98 -0
  175. package/src/lib/model/pos-hardware-in-person.js +224 -0
  176. package/src/lib/model/pos-hardware-in-person.js.map +1 -0
  177. package/src/lib/model/pos-hardware-tax-summary.d.ts +166 -0
  178. package/src/lib/model/pos-hardware-tax-summary.js +225 -0
  179. package/src/lib/model/pos-hardware-tax-summary.js.map +1 -0
  180. package/src/lib/model/pos-hardware-terms.d.ts +74 -0
  181. package/src/lib/model/pos-hardware-terms.js +115 -0
  182. package/src/lib/model/pos-hardware-terms.js.map +1 -0
  183. package/src/lib/model/pos-hardware.d.ts +349 -7
  184. package/src/lib/model/pos-hardware.js +291 -20
  185. package/src/lib/model/pos-hardware.js.map +1 -1
  186. package/src/lib/model/product-write.d.ts +10 -0
  187. package/src/lib/model/product-write.js +4 -2
  188. package/src/lib/model/product-write.js.map +1 -1
  189. package/src/lib/model/workspace-site-permissions.d.ts +26 -0
  190. package/src/lib/model/workspace-site-permissions.js +81 -0
  191. package/src/lib/model/workspace-site-permissions.js.map +1 -0
  192. package/src/lib/plugin.js +16 -0
  193. package/src/lib/plugin.js.map +1 -1
  194. package/src/lib/server/billing-webhook.js +25 -1
  195. package/src/lib/server/billing-webhook.js.map +1 -1
  196. package/src/lib/server/cancel-order.js +7 -5
  197. package/src/lib/server/cancel-order.js.map +1 -1
  198. package/src/lib/server/cart-checkout.js +20 -8
  199. package/src/lib/server/cart-checkout.js.map +1 -1
  200. package/src/lib/server/cart.js +29 -0
  201. package/src/lib/server/cart.js.map +1 -1
  202. package/src/lib/server/checkout.js +42 -13
  203. package/src/lib/server/checkout.js.map +1 -1
  204. package/src/lib/server/collection-membership.d.ts +4 -0
  205. package/src/lib/server/collection-membership.js +5 -10
  206. package/src/lib/server/collection-membership.js.map +1 -1
  207. package/src/lib/server/commerce-site-keys.d.ts +48 -0
  208. package/src/lib/server/commerce-site-keys.js +61 -0
  209. package/src/lib/server/commerce-site-keys.js.map +1 -0
  210. package/src/lib/server/draft-order.js +8 -5
  211. package/src/lib/server/draft-order.js.map +1 -1
  212. package/src/lib/server/fulfill-order.js +7 -5
  213. package/src/lib/server/fulfill-order.js.map +1 -1
  214. package/src/lib/server/gift-cards.js +8 -5
  215. package/src/lib/server/gift-cards.js.map +1 -1
  216. package/src/lib/server/local-fulfillment-status.js +9 -6
  217. package/src/lib/server/local-fulfillment-status.js.map +1 -1
  218. package/src/lib/server/member-post.js +6 -5
  219. package/src/lib/server/member-post.js.map +1 -1
  220. package/src/lib/server/membership-admin-password.d.ts +4 -2
  221. package/src/lib/server/membership-admin-password.js +10 -8
  222. package/src/lib/server/membership-admin-password.js.map +1 -1
  223. package/src/lib/server/membership-admin-remove.d.ts +7 -5
  224. package/src/lib/server/membership-admin-remove.js +9 -9
  225. package/src/lib/server/membership-admin-remove.js.map +1 -1
  226. package/src/lib/server/order-notifications.js +5 -2
  227. package/src/lib/server/order-notifications.js.map +1 -1
  228. package/src/lib/server/order-print.js +7 -5
  229. package/src/lib/server/order-print.js.map +1 -1
  230. package/src/lib/server/order-receipt-send.js +8 -5
  231. package/src/lib/server/order-receipt-send.js.map +1 -1
  232. package/src/lib/server/order-webhooks.js +8 -8
  233. package/src/lib/server/order-webhooks.js.map +1 -1
  234. package/src/lib/server/orders-shipping-prepare.js +9 -6
  235. package/src/lib/server/orders-shipping-prepare.js.map +1 -1
  236. package/src/lib/server/own-store-tax-return-source.d.ts +47 -0
  237. package/src/lib/server/own-store-tax-return-source.js +312 -0
  238. package/src/lib/server/own-store-tax-return-source.js.map +1 -0
  239. package/src/lib/server/payment-methods.js +13 -9
  240. package/src/lib/server/payment-methods.js.map +1 -1
  241. package/src/lib/server/pos-auth.d.ts +37 -6
  242. package/src/lib/server/pos-auth.js +40 -15
  243. package/src/lib/server/pos-auth.js.map +1 -1
  244. package/src/lib/server/pos-cellular-billing.d.ts +81 -0
  245. package/src/lib/server/pos-cellular-billing.js +711 -0
  246. package/src/lib/server/pos-cellular-billing.js.map +1 -0
  247. package/src/lib/server/pos-cellular-notify.d.ts +47 -0
  248. package/src/lib/server/pos-cellular-notify.js +150 -0
  249. package/src/lib/server/pos-cellular-notify.js.map +1 -0
  250. package/src/lib/server/pos-cellular.d.ts +88 -0
  251. package/src/lib/server/pos-cellular.js +339 -0
  252. package/src/lib/server/pos-cellular.js.map +1 -0
  253. package/src/lib/server/pos-check-in.d.ts +5 -1
  254. package/src/lib/server/pos-check-in.js +296 -28
  255. package/src/lib/server/pos-check-in.js.map +1 -1
  256. package/src/lib/server/pos-day-report.js +29 -3
  257. package/src/lib/server/pos-day-report.js.map +1 -1
  258. package/src/lib/server/pos-display.d.ts +16 -0
  259. package/src/lib/server/pos-display.js +129 -2
  260. package/src/lib/server/pos-display.js.map +1 -1
  261. package/src/lib/server/pos-hardware-admin.d.ts +10 -1
  262. package/src/lib/server/pos-hardware-admin.js +250 -84
  263. package/src/lib/server/pos-hardware-admin.js.map +1 -1
  264. package/src/lib/server/pos-hardware-attach.d.ts +105 -0
  265. package/src/lib/server/pos-hardware-attach.js +443 -0
  266. package/src/lib/server/pos-hardware-attach.js.map +1 -0
  267. package/src/lib/server/pos-hardware-buyer-lookup.d.ts +58 -0
  268. package/src/lib/server/pos-hardware-buyer-lookup.js +180 -0
  269. package/src/lib/server/pos-hardware-buyer-lookup.js.map +1 -0
  270. package/src/lib/server/pos-hardware-buyer-record.d.ts +29 -0
  271. package/src/lib/server/pos-hardware-buyer-record.js +86 -0
  272. package/src/lib/server/pos-hardware-buyer-record.js.map +1 -0
  273. package/src/lib/server/pos-hardware-cancel.d.ts +58 -0
  274. package/src/lib/server/pos-hardware-cancel.js +147 -0
  275. package/src/lib/server/pos-hardware-cancel.js.map +1 -0
  276. package/src/lib/server/pos-hardware-closure-notices.d.ts +27 -0
  277. package/src/lib/server/pos-hardware-closure-notices.js +85 -0
  278. package/src/lib/server/pos-hardware-closure-notices.js.map +1 -0
  279. package/src/lib/server/pos-hardware-closure.d.ts +60 -0
  280. package/src/lib/server/pos-hardware-closure.js +153 -0
  281. package/src/lib/server/pos-hardware-closure.js.map +1 -0
  282. package/src/lib/server/pos-hardware-in-person.d.ts +61 -0
  283. package/src/lib/server/pos-hardware-in-person.js +236 -0
  284. package/src/lib/server/pos-hardware-in-person.js.map +1 -0
  285. package/src/lib/server/pos-hardware-notify.d.ts +40 -2
  286. package/src/lib/server/pos-hardware-notify.js +195 -14
  287. package/src/lib/server/pos-hardware-notify.js.map +1 -1
  288. package/src/lib/server/pos-hardware-readers.d.ts +115 -0
  289. package/src/lib/server/pos-hardware-readers.js +430 -0
  290. package/src/lib/server/pos-hardware-readers.js.map +1 -0
  291. package/src/lib/server/pos-hardware-returns.d.ts +116 -0
  292. package/src/lib/server/pos-hardware-returns.js +502 -0
  293. package/src/lib/server/pos-hardware-returns.js.map +1 -0
  294. package/src/lib/server/pos-hardware-sale-fields.d.ts +28 -0
  295. package/src/lib/server/pos-hardware-sale-fields.js +44 -0
  296. package/src/lib/server/pos-hardware-sale-fields.js.map +1 -0
  297. package/src/lib/server/pos-hardware-sale.d.ts +54 -0
  298. package/src/lib/server/pos-hardware-sale.js +369 -0
  299. package/src/lib/server/pos-hardware-sale.js.map +1 -0
  300. package/src/lib/server/pos-hardware-tax-return-source.d.ts +99 -0
  301. package/src/lib/server/pos-hardware-tax-return-source.js +462 -0
  302. package/src/lib/server/pos-hardware-tax-return-source.js.map +1 -0
  303. package/src/lib/server/pos-hardware-terms.d.ts +64 -0
  304. package/src/lib/server/pos-hardware-terms.js +154 -0
  305. package/src/lib/server/pos-hardware-terms.js.map +1 -0
  306. package/src/lib/server/pos-hardware-user-eraser.d.ts +3 -0
  307. package/src/lib/server/pos-hardware-user-eraser.js +31 -7
  308. package/src/lib/server/pos-hardware-user-eraser.js.map +1 -1
  309. package/src/lib/server/pos-hardware-webhook.d.ts +5 -0
  310. package/src/lib/server/pos-hardware-webhook.js +51 -6
  311. package/src/lib/server/pos-hardware-webhook.js.map +1 -1
  312. package/src/lib/server/pos-hardware.d.ts +74 -1
  313. package/src/lib/server/pos-hardware.js +175 -10
  314. package/src/lib/server/pos-hardware.js.map +1 -1
  315. package/src/lib/server/pos-kiosk.d.ts +2 -0
  316. package/src/lib/server/pos-kiosk.js +17 -12
  317. package/src/lib/server/pos-kiosk.js.map +1 -1
  318. package/src/lib/server/pos-offline-card-order.d.ts +64 -0
  319. package/src/lib/server/pos-offline-card-order.js +102 -0
  320. package/src/lib/server/pos-offline-card-order.js.map +1 -0
  321. package/src/lib/server/pos-offline-card-reconcile.d.ts +67 -1
  322. package/src/lib/server/pos-offline-card-reconcile.js +215 -18
  323. package/src/lib/server/pos-offline-card-reconcile.js.map +1 -1
  324. package/src/lib/server/pos-offline-sync.d.ts +39 -0
  325. package/src/lib/server/pos-offline-sync.js +190 -55
  326. package/src/lib/server/pos-offline-sync.js.map +1 -1
  327. package/src/lib/server/pos-ops-gate.d.ts +28 -9
  328. package/src/lib/server/pos-ops-gate.js +50 -19
  329. package/src/lib/server/pos-ops-gate.js.map +1 -1
  330. package/src/lib/server/pos-ops-routes.js +24 -0
  331. package/src/lib/server/pos-ops-routes.js.map +1 -1
  332. package/src/lib/server/pos-order-refund-ids.d.ts +28 -0
  333. package/src/lib/server/pos-order-refund-ids.js +31 -0
  334. package/src/lib/server/pos-order-refund-ids.js.map +1 -0
  335. package/src/lib/server/pos-order.js +52 -49
  336. package/src/lib/server/pos-order.js.map +1 -1
  337. package/src/lib/server/pos-payment.js +1 -1
  338. package/src/lib/server/pos-payment.js.map +1 -1
  339. package/src/lib/server/pos-reader-splash-admin.js +30 -32
  340. package/src/lib/server/pos-reader-splash-admin.js.map +1 -1
  341. package/src/lib/server/pos-reader-splash-images-route.d.ts +17 -0
  342. package/src/lib/server/pos-reader-splash-images-route.js +113 -0
  343. package/src/lib/server/pos-reader-splash-images-route.js.map +1 -0
  344. package/src/lib/server/pos-reader-splash-images.d.ts +51 -0
  345. package/src/lib/server/pos-reader-splash-images.js +123 -0
  346. package/src/lib/server/pos-reader-splash-images.js.map +1 -0
  347. package/src/lib/server/pos-reader-splash.d.ts +68 -1
  348. package/src/lib/server/pos-reader-splash.js +182 -11
  349. package/src/lib/server/pos-reader-splash.js.map +1 -1
  350. package/src/lib/server/pos-readers.d.ts +29 -0
  351. package/src/lib/server/pos-readers.js +134 -35
  352. package/src/lib/server/pos-readers.js.map +1 -1
  353. package/src/lib/server/pos-refund-authority.d.ts +3 -1
  354. package/src/lib/server/pos-refund-authority.js +3 -2
  355. package/src/lib/server/pos-refund-authority.js.map +1 -1
  356. package/src/lib/server/pos-return.d.ts +10 -0
  357. package/src/lib/server/pos-return.js +29 -7
  358. package/src/lib/server/pos-return.js.map +1 -1
  359. package/src/lib/server/pos-sale.js +6 -1
  360. package/src/lib/server/pos-sale.js.map +1 -1
  361. package/src/lib/server/pos-shift.js +11 -0
  362. package/src/lib/server/pos-shift.js.map +1 -1
  363. package/src/lib/server/pos-staff-pin.js +29 -24
  364. package/src/lib/server/pos-staff-pin.js.map +1 -1
  365. package/src/lib/server/pos-terminal.d.ts +14 -4
  366. package/src/lib/server/pos-terminal.js +34 -38
  367. package/src/lib/server/pos-terminal.js.map +1 -1
  368. package/src/lib/server/printers.d.ts +7 -3
  369. package/src/lib/server/printers.js +18 -12
  370. package/src/lib/server/printers.js.map +1 -1
  371. package/src/lib/server/product-drafts.d.ts +11 -1
  372. package/src/lib/server/product-drafts.js +12 -8
  373. package/src/lib/server/product-drafts.js.map +1 -1
  374. package/src/lib/server/product.d.ts +15 -1
  375. package/src/lib/server/product.js +16 -3
  376. package/src/lib/server/product.js.map +1 -1
  377. package/src/lib/server/products-write.d.ts +26 -2
  378. package/src/lib/server/products-write.js +19 -7
  379. package/src/lib/server/products-write.js.map +1 -1
  380. package/src/lib/server/refund.js +55 -42
  381. package/src/lib/server/refund.js.map +1 -1
  382. package/src/lib/server/reservation-availability-query.d.ts +8 -0
  383. package/src/lib/server/reservation-availability-query.js +11 -1
  384. package/src/lib/server/reservation-availability-query.js.map +1 -1
  385. package/src/lib/server/reservation-availability.d.ts +1 -1
  386. package/src/lib/server/reservation-availability.js +7 -2
  387. package/src/lib/server/reservation-availability.js.map +1 -1
  388. package/src/lib/server/reservation-guests.d.ts +32 -0
  389. package/src/lib/server/reservation-guests.js +285 -0
  390. package/src/lib/server/reservation-guests.js.map +1 -0
  391. package/src/lib/server/reservation-walk-in.d.ts +11 -1
  392. package/src/lib/server/reservation-walk-in.js +56 -12
  393. package/src/lib/server/reservation-walk-in.js.map +1 -1
  394. package/src/lib/server/reserve.js +18 -5
  395. package/src/lib/server/reserve.js.map +1 -1
  396. package/src/lib/server/returns.js +9 -6
  397. package/src/lib/server/returns.js.map +1 -1
  398. package/src/lib/server/reviews.d.ts +22 -2
  399. package/src/lib/server/reviews.js +88 -20
  400. package/src/lib/server/reviews.js.map +1 -1
  401. package/src/lib/server/shipping-connectors.js +10 -11
  402. package/src/lib/server/shipping-connectors.js.map +1 -1
  403. package/src/lib/server/site-page-resolver.js +20 -3
  404. package/src/lib/server/site-page-resolver.js.map +1 -1
  405. package/src/lib/server/store-day.d.ts +33 -0
  406. package/src/lib/server/store-day.js +46 -0
  407. package/src/lib/server/store-day.js.map +1 -0
  408. package/src/lib/server/store-setup-ai-writer.d.ts +2 -1
  409. package/src/lib/server/store-setup-ai-writer.js +5 -6
  410. package/src/lib/server/store-setup-ai-writer.js.map +1 -1
  411. package/src/lib/server/storefront-tax.d.ts +13 -0
  412. package/src/lib/server/storefront-tax.js +8 -1
  413. package/src/lib/server/storefront-tax.js.map +1 -1
  414. package/src/lib/server/tax-return-source.d.ts +32 -3
  415. package/src/lib/server/tax-return-source.js +99 -20
  416. package/src/lib/server/tax-return-source.js.map +1 -1
  417. package/src/lib/server.js +100 -2
  418. package/src/lib/server.js.map +1 -1
  419. package/src/lib/testing/fake-firestore.js +58 -16
  420. package/src/lib/testing/fake-firestore.js.map +1 -1
  421. package/src/lib/testing/pos-ops-memory-firestore.js +18 -4
  422. package/src/lib/testing/pos-ops-memory-firestore.js.map +1 -1
  423. package/src/lib/testing/site-register-permissions.d.ts +29 -0
  424. package/src/lib/testing/site-register-permissions.js +38 -0
  425. package/src/lib/testing/site-register-permissions.js.map +1 -0
  426. package/src/lib/transfer/tracking.server.js +8 -5
  427. package/src/lib/transfer/tracking.server.js.map +1 -1
  428. package/src/lib/utils/add-to-cart.d.ts +2 -0
  429. package/src/lib/utils/add-to-cart.js +6 -3
  430. package/src/lib/utils/add-to-cart.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/products-hub-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 { TransferResumeImport } from '@aglyn/aglyn/app-utils/transfer-resume-import'\nimport { useConsoleWidgetSlot } from '@aglyn/aglyn/app-utils/console-widget-slot-context'\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport {\n PRODUCT_LIST_BASE,\n PRODUCT_LIST_COLUMN_SORTS,\n PRODUCT_LIST_FIELDS,\n PRODUCT_LIST_HEADERS,\n PRODUCT_LIST_OPTIONS,\n PRODUCT_LIST_QUERY,\n PRODUCT_LIST_SELECT_FIELDS,\n productListRequestClauses,\n} from '../../constants/product-list-query'\nimport { CardDisplay, useConfirmationContext } 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 {\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport QuotaReadoutComponent from '@aglyn/shared-ui-jsx/components/quota-readout.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 { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Timestamp } from '@aglyn/shared-util-timestamp'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport {\n addDoc,\n collection,\n doc,\n documentId,\n limit,\n orderBy,\n query,\n updateDoc,\n where,\n} from 'firebase/firestore'\nimport { getLiveCountFromServer } from '@aglyn/aglyn/app-utils/live-document-count.client'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport {\n useFirestoreCollection,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { useListQuery } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { useHostResourceApi } from '@aglyn/tenant-feature-instance'\nimport { useOrgPlan } from '@aglyn/tenant-feature-instance'\nimport { ProductLabelsDialog } from './product-labels-dialog.component'\nimport ProductEditorDialog from './product-editor-dialog.component'\nimport { productSlugLedger } from './product-slugs'\nimport { productCollectionFields } from './smart-collections'\nimport ProductsHubZone from './products-hub-zone.component'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { PRODUCTS_CREATE_ZONE, type ConsoleProductsHubZoneProps } from './product-zones'\nimport { commerceListFilter } from '../../transfer/list-filter'\nimport { COMMERCE_PRODUCTS_TRANSFER } from '../../transfer/transfer-keys'\n\n/**\n * How many of ONE product's license keys the dialog reads.\n *\n * Per product, not per store: the query carries the `productId` equality, so\n * this bounds a single pool rather than the site's whole key collection.\n */\nconst KEY_POOL_CEILING = 500\n\nexport interface ProductsHubCardProps {\n hostId: string\n}\n\ntype ProductRow = CommerceModel.HostProduct & { $id: string }\n\n/**\n * Stock and Variants are read from inside `variants`, which no query can\n * order by, so they sort the page on screen (AGL-3680); Product, Status, Type\n * and Price are the query's order (`PRODUCT_LIST_COLUMN_SORTS`).\n */\nconst PRODUCT_PAGE_SORTS = {\n stock: (row: ProductRow) => CommerceModel.productInventory(row),\n variants: (row: ProductRow) => row.variants.length,\n}\nconst PRODUCT_PAGE_SORT_HEADERS = { stock: 'Stock', variants: 'Variants' }\n\nconst STATUS_COLOR: Record<string, 'default' | 'success' | 'warning'> = {\n active: 'success',\n draft: 'warning',\n archived: 'default',\n}\n\n/** The grid's own columns; every other declared field is a hidden filter column. */\nconst PRODUCT_VISIBLE_COLUMNS = ['name', 'status', 'type', 'priceUsd', 'stock', 'variants']\n\n/**\n * Products hub v1 (AGL-279): the catalog manager replacing the Commerce\n * Starter card — a table over `hosts/{hostId}/products` whose search and\n * filters are its Firestore query (AGL-3321), full editor dialog, duplicate, archive/activate, soft delete (past\n * order rows keep resolving). Product cap (`productsPerHost`, AGL-278)\n * gated here on create/duplicate/import (AGL-471); server-side\n * enforcement of the client-write path rides AGL-473.\n */\nexport function ProductsHubCard(props: ProductsHubCardProps) {\n const { hostId } = props\n /** The shell's zone renderer, for the `productsCreate` zone (AGL-3596). */\n const CreateZone = useConsoleWidgetSlot()\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n // Product cap (AGL-471): per-plan `productsPerHost`, same pattern as\n // locations. Console-side gate; server enforcement rides AGL-473.\n const createHostResource = useHostResourceApi()\n const { org, ready: planReady } = useOrgPlan(hostId)\n const { confirm } = useConfirmationContext()\n /*\n * The grid's Filters panel and quick search, bound to the clauses the\n * query below serves (AGL-3321). Every clause and the search word go on\n * that query; see `constants/product-list-query.ts` for what is offered.\n */\n const gridFilter = useListGridFilter({\n selectFields: PRODUCT_LIST_SELECT_FIELDS,\n })\n const filtering =\n gridFilter.clauses.length > 0 ||\n gridFilter.searchWords.some((word) => word.trim())\n // Every header sorts (AGL-3680); A to Z until one is clicked.\n const [askedSort, setAskedSort] = useState<ListQuerySort | null>(\n PRODUCT_LIST_COLUMN_SORTS[0],\n )\n const [editing, setEditing] = useState<ProductRow | null>(null)\n const [creating, setCreating] = useState(false)\n const [adjusting, setAdjusting] = useState<{\n product: ProductRow\n variantId: string\n delta: string\n reason: CommerceModel.InventoryAdjustmentReason\n locationId: string\n } | null>(null)\n const [keysFor, setKeysFor] = useState<ProductRow | null>(null)\n // Product labels for a label printer (AGL-3619).\n const [labelsFor, setLabelsFor] = useState<ProductRow | null>(null)\n const [keysText, setKeysText] = useState('')\n /**\n * What the last import created, with the options its After import step\n * set (AGL-2916, AGL-3531), which the hub's zone hands to its widgets.\n */\n const [lastImport, setLastImport] =\n useState<ConsoleProductsHubZoneProps['lastImport']>(null)\n // The console's import wizard and export dialog; none outside the console.\n const transfer = Aglyn.useTransferLauncher()\n\n const {\n rows: productDocs,\n data: productWindow,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n plan,\n status: productsStatus,\n /**\n * The product rows the stock dialog is seeded from are unconfirmed by\n * the server (AGL-1358). A stock adjustment does not write the delta —\n * it recomputes the WHOLE `variants` array from the seeded product and\n * replaces it, so a cached seed silently reverts every sale, return and\n * adjustment the server has recorded since that snapshot, on every\n * variant and every location.\n */\n fromCache: productsFromCache,\n } = useListQuery<any>({\n /*\n * PAGED BY THE QUERY, and every filter ON it (AGL-3321).\n *\n * The table read a five-hundred-product window and put ONE predicate on\n * it — the search or the Status clause — then matched Status beside a\n * search, dropped soft-deleted products and sorted by name over the rows\n * it held, and paged that. Past five hundred products, a match past the\n * window answered \"no products match\", and a page came back short by\n * however many deleted products it had held.\n *\n * Now the plan puts the search word and every clause on one query under\n * the `deletedAt == null` scope, ordered by `nameLower`, and each page is\n * a page of that query — one page plus a probe row, widened as the reader\n * pages forward. Nothing is matched over the rows afterwards; a clause\n * the query cannot hold is refused by name (`ListQueryNotices` below).\n */\n collection: collection(firestore, 'hosts', hostId, 'products'),\n declaration: PRODUCT_LIST_QUERY,\n request: {\n clauses: productListRequestClauses(gridFilter.clauses),\n search: gridFilter.searchWords,\n base: PRODUCT_LIST_BASE,\n sort: askedSort,\n },\n deps: [firestore, hostId],\n idField: '$id',\n /*\n * The scope and the filters read MUTABLE fields — a delete sets\n * `deletedAt`, Archive moves `status`, a rename moves the tokens — so a\n * product can leave this query mid-session, and the SDK can cache that\n * departure as a tombstone at the product's own path (AGL-1196). A\n * disappearance is confirmed against the server before it is believed.\n */\n confirmDisappearances: true,\n })\n /*\n * License key pool (AGL-308) for the open dialog's product — asked FOR that\n * product, and ordered (AGL-2501).\n *\n * This read `limit(500)` over the site's whole `licenseKeys` collection with\n * no `orderBy`, then filtered by `productId` in the browser. Firestore\n * answers an unordered limit in DOCUMENT-ID order, so on a store past five\n * hundred keys the window was an arbitrary five hundred taken across every\n * product — and the \"N available\" line below counted what happened to be in\n * it. A product whose keys all hashed high showed a pool of zero while the\n * storefront went on delivering them.\n *\n * The equality moves into the query, so the window is this product's keys\n * rather than the store's. `orderBy(documentId())` orders on the document\n * NAME, which cannot be absent — every candidate FIELD here (`createdAtMs`,\n * `assignedAtMs`, `revokedAtMs`) is either optional by design or absent on\n * exactly the keys the counts are about. An equality plus an ordering on\n * `__name__` is served by the automatic single-field index, so this adds no\n * composite for anyone to deploy.\n */\n const { data: keyDocs } = useFirestoreCollection<any>(\n () =>\n keysFor\n ? query(\n collection(firestore, 'hosts', hostId, 'licenseKeys'),\n where('productId', '==', keysFor.$id),\n orderBy(documentId()),\n // One past the ceiling, so a pool larger than the window is a\n // fact rather than a guess from `length === 500`.\n limit(KEY_POOL_CEILING + 1),\n )\n : null,\n [firestore, hostId, keysFor?.$id],\n { idField: '$id' },\n )\n /** The key pool is larger than the window, so the counts below are partial. */\n const keyPoolTruncated = (keyDocs?.length ?? 0) > KEY_POOL_CEILING\n // Locations (AGL-286): the stock dialog buckets deltas when they exist.\n const { data: locationDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'locations'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n /*\n * The PAGE on screen, lifted from legacy shapes. It is what the table\n * draws, the reserved-stock clock watches and the zone hands its widgets,\n * and nothing narrows it: the query already answered every filter, and\n * soft-deleted products are outside its scope.\n */\n const products = useMemo<ProductRow[]>(\n () =>\n (productDocs ?? []).map((product: any) => ({\n ...CommerceModel.liftLegacyProduct(product),\n $id: product.$id,\n })),\n [productDocs],\n )\n // Listed but not yet priced (AGL-3676), among the products on screen.\n const comingSoon = useMemo(\n () =>\n products.filter(\n (product) => product.status === 'active' && CommerceModel.productPriceMissing(product),\n ),\n [products],\n )\n const columnSort = useListColumnSort<ProductRow>({\n sorts: PRODUCT_LIST_COLUMN_SORTS,\n defaultSort: PRODUCT_LIST_COLUMN_SORTS[0],\n sort: askedSort,\n onSortChange: setAskedSort,\n orderBy: plan.orderBy,\n rows: products,\n pageSorts: PRODUCT_PAGE_SORTS,\n headers: PRODUCT_PAGE_SORT_HEADERS,\n })\n\n /**\n * A CLOCK, because a hold lapses without anybody writing anything\n * (AGL-2356).\n *\n * The Firestore listener re-renders on document changes, and a reservation\n * expiring is not one — `expiresAtMs` simply passes. Without a tick the\n * \"reserved\" caption below would keep naming a hold that lapsed twenty\n * minutes ago, which is worse than not showing it at all: the merchant would\n * be told stock is spoken for while the storefront happily sells it.\n *\n * Only runs while something is actually held, so an ordinary catalog costs\n * no timer. A NEW hold arrives as a document change, which re-renders, which\n * re-arms this. It watches the page on screen, which is every row whose\n * caption it keeps true.\n */\n const [nowMs, setNowMs] = useState(() => Date.now())\n const anyHeld = useMemo(\n () =>\n products.some(\n (product) => CommerceModel.heldProductUnits(product, nowMs) > 0,\n ),\n [products, nowMs],\n )\n useEffect(() => {\n if (!anyHeld) return undefined\n const timer = setInterval(() => setNowMs(Date.now()), 60_000)\n return () => clearInterval(timer)\n }, [anyHeld])\n\n /**\n * The catalog HEAD-COUNT is a server aggregate, not the length of the\n * capped listener (AGL-1716, the AGL-1706 shape).\n *\n * The listener is `limit(500)` — correctly; a 25,000-product catalog does\n * not belong in a table. What it must not do is answer \"how many products\n * does this site have\", and it did: the length saturated at 500 and was\n * handed to `checkQuota(org, 'productsPerHost', …)` and to the batch check\n * the CSV importer runs. The bands are 2,500 / 10,000 / 25,000 above the\n * window, so on Pro and up the check compared 500 against thousands and\n * could never refuse — the card offered headroom and `api/hosts/resources`\n * then refused the create, which is the AGL-1716 shape exactly.\n *\n * It counts LIVE products, as `api/hosts/resources` and every product\n * writer count them (AGL-3918): a deleted product frees its slot, an\n * archived or draft one keeps it. Every document less the tombstones\n * (`getLiveCountFromServer`), so the card asks the enforcing route's\n * question in the enforcing route's terms.\n *\n * The table pages its own query (AGL-3321) and answers a different\n * question — which products match — so it never stands in for this. A\n * one-shot goes stale where a listener refreshed for free, so the count is\n * re-read after any mutation that moves it.\n *\n * No counting RULE moves: `checkQuota` is untouched and `report-usage`\n * meters contacts, storage and API requests — never the catalog.\n */\n const [productCountEpoch, setProductCountEpoch] = useState(0)\n const [serverProductCount, setServerProductCount] = useState<number | null>(\n null,\n )\n useEffect(() => {\n let active = true\n void getLiveCountFromServer(collection(firestore, 'hosts', hostId, 'products'))\n .then((count) => {\n if (active) setServerProductCount(count)\n })\n .catch(() => {\n // Falls back to the live-row count below — a LOWER bound, and this\n // card's prior behaviour. Deliberately not 0: `checkQuota` answers\n // from whatever it is handed, and 0 used is a confident wrong\n // number in the flattering direction.\n })\n return () => {\n active = false\n }\n }, [firestore, hostId, productCountEpoch])\n // The fallback, pending or denied: the live products the table's query has\n // read, probe row included. Each is a product the site has, so it can only\n // UNDERSTATE, never overstate, and nothing it gates fires on a count larger\n // than the truth (AGL-471).\n const loadedProductCount = productWindow?.length ?? 0\n const productCount = serverProductCount ?? loadedProductCount\n // Gate only once the org doc has loaded: an unresolved org reads as the\n // free tier's 0-product cap, which swallowed every Add/Duplicate click.\n // The resources API (AGL-473) stays the authoritative cap on create.\n //\n // `planReady` rather than `org` truthiness (AGL-1064): a host with no\n // owning org never produces one, and waiting on the value alone would\n // hold this open forever. The old fallback allowed UNLIMITED during the\n // window — safe only because the API re-checks; the controls now disable\n // instead, which does not lean on that backstop.\n // NULL means \"not known yet\", which a boolean cannot say — the old\n // fallback object claimed UNLIMITED and every consumer believed it.\n const productQuota = useMemo(\n () =>\n planReady\n ? Aglyn.checkQuota(org, 'productsPerHost', productCount)\n : null,\n [org, planReady, productCount],\n )\n\n const handleDuplicate = useCallback(\n (product: ProductRow) => async () => {\n // Plan unknown — the control is disabled; this guards the race.\n if (!productQuota) return\n if (!productQuota.allowed) {\n return void enqueueSnackbar(\n `Your plan includes ${productQuota.limit} products — upgrade for more`,\n { variant: 'info', persist: false },\n )\n }\n try {\n /*\n * The copy's slug is asked of the store, so a second Duplicate of\n * the same product does not mint a second `-copy` (AGL-3321), and\n * its search keys are the COPY's: spreading the source's would sort\n * and find it by the source's name.\n */\n const slug = await productSlugLedger(firestore, hostId).claim(\n CommerceModel.productDuplicateSlugBase(product.slug, product.name),\n )\n // Duplicate is a create — rides the quota-enforcing API (AGL-473).\n // The source's SKU and barcode keys are not copied: the copy's own\n // are derived (`productDuplicateFields`), and a copy of a product\n // with none must have none.\n await createHostResource({\n hostId,\n resource: 'product',\n data: {\n ...CommerceModel.productDuplicateFields(product, slug, Date.now()),\n // The COPY's smart collections: its name differs from the source's.\n ...(await productCollectionFields(firestore, hostId, {\n ...product,\n name: CommerceModel.productDuplicateName(product.name),\n })),\n },\n })\n // A create moves the count and the aggregate is a one-shot — the\n // listener refreshes the ROWS for free, the count has to be asked\n // again or the cap drifts stale for the rest of the session.\n setProductCountEpoch((epoch) => epoch + 1)\n enqueueSnackbar('Product duplicated as draft', {\n variant: 'success',\n persist: false,\n })\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not duplicate product', {\n variant: 'warning',\n persist: false,\n })\n }\n },\n [firestore, hostId, createHostResource, enqueueSnackbar, productQuota],\n )\n\n const handleStatus = useCallback(\n (product: ProductRow, status: CommerceModel.ProductStatus) => async () => {\n // A product with a variant nobody has priced may go live (AGL-3676):\n // the storefront says \"Price coming soon\" and sells it nowhere until\n // the editor has a price for each. The owner is told, not stopped.\n if (status === 'active' && CommerceModel.productPriceMissing(product)) {\n enqueueSnackbar(\n `${product.name} has no price yet: visitors will see “Price coming soon” and can’t buy it.`,\n { variant: 'info', persist: false },\n )\n }\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(doc(firestore, 'hosts', hostId, 'products', product.$id), {\n status,\n updatedAtMs: Date.now(),\n updatedAt: Timestamp.now(),\n }),\n })\n },\n [firestore, hostId, user, enqueueSnackbar],\n )\n\n const handleDelete = useCallback(\n (product: ProductRow) => async () => {\n const confirmed = await confirm({\n title: 'Delete this product?',\n description:\n `\"${product.name}\" stops being purchasable; blocks referencing ` +\n 'it show a checkout error until repointed.',\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(doc(firestore, 'hosts', hostId, 'products', product.$id), {\n deletedAt: Timestamp.now(),\n }),\n })\n // Deleting frees the slot (AGL-3918), so the head-count is re-read.\n setProductCountEpoch((epoch) => epoch + 1)\n enqueueSnackbar('Product deleted', { variant: 'success', persist: false })\n },\n [confirm, firestore, hostId, user, enqueueSnackbar],\n )\n\n /*\n * Import and export (AGL-3531) run on the console's transfer wizard and\n * dialog, on the `commerce.products` resource: a product matched by\n * handle, SKU or ID is updated, every step is a choice, and the export\n * writes what the table's query matches — its filters and search — on the\n * server, a row per variant, with a Shopify preset.\n *\n * When the wizard closes on its results, the products the import created\n * reach the hub's zone with the options its After import step set.\n */\n const openImport = useCallback(() => {\n transfer?.openImport({\n resource: COMMERCE_PRODUCTS_TRANSFER,\n scope: 'host',\n hostId,\n onFinished: () => {\n setProductCountEpoch((epoch) => epoch + 1)\n void (async () => {\n const { takeProductImport, createdProductIds } = await import(\n '../../transfer/product-import-results'\n )\n const finished = takeProductImport(hostId)\n if (!finished) return\n const productIds = await createdProductIds(user, finished)\n setLastImport({\n key: `${finished.jobId}-${productIds.length}`,\n productIds,\n options: finished.options,\n })\n })()\n },\n })\n }, [transfer, hostId, user])\n const openExport = useCallback(() => {\n transfer?.openExport({\n resource: COMMERCE_PRODUCTS_TRANSFER,\n scope: 'host',\n hostId,\n ...(filtering\n ? {\n filter: {\n label: 'the products the list’s filters and search find',\n value: commerceListFilter(plan),\n },\n }\n : {}),\n })\n }, [transfer, hostId, filtering, plan])\n\n const handleAdjustSave = useCallback(async () => {\n if (!adjusting) return\n // The update and the ledger row, as the native stock route writes them (AGL-3652).\n const write = CommerceModel.stockAdjustmentWrite(\n adjusting.product,\n adjusting.product.$id,\n {\n variantId: adjusting.variantId,\n delta: Number(adjusting.delta),\n reason: adjusting.reason,\n locationId: adjusting.locationId || undefined,\n },\n Date.now(),\n )\n if (!write) return\n /**\n * Refuse the adjustment when the seed is unconfirmed (AGL-1358).\n *\n * `adjustVariantInventory` reads counts off the seeded product and\n * returns a whole new `variants` array, which is then written over the\n * stored one — so this is a full replace of live stock, not a delta, and\n * `merge` could not help. The refusal also has to cover the ADJUSTMENT\n * LOG, which is why the guard wraps both writes: a logged adjustment\n * whose stock write never happened is worse than neither, because the\n * history then disagrees with the count it is supposed to explain.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'stock',\n unreadable: productsStatus === 'error',\n fromCache: productsFromCache,\n },\n async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(\n doc(firestore, 'hosts', hostId, 'products', adjusting.product.$id),\n // The total and the In stock verdict move with the count (AGL-3321).\n write.update,\n ),\n })\n // Adjustment history (AGL-281): the same log the sale webhook writes.\n await addDoc(\n collection(firestore, 'hosts', hostId, 'inventoryAdjustments'),\n write.ledger,\n )\n },\n )\n // Keep the dialog open with the typed delta rather than failing silently.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n setAdjusting(null)\n enqueueSnackbar('Stock adjusted', { variant: 'success', persist: false })\n }, [\n user,\n adjusting,\n firestore,\n hostId,\n enqueueSnackbar,\n productsFromCache,\n productsStatus,\n ])\n\n /** The hub's allowance check for a batch of `count` new products, for its zone. */\n const roomFor = useCallback(\n (count: number) =>\n planReady\n ? Aglyn.checkQuota(org, 'productsPerHost', productCount + count - 1)\n : null,\n [org, planReady, productCount],\n )\n const onCatalogCreated = useCallback(\n () => setProductCountEpoch((epoch) => epoch + 1),\n [],\n )\n\n const formatPrice = (product: ProductRow) => {\n const [min, max] = CommerceModel.productPriceRange(product)\n return min === max ? `$${min}` : `$${min}–$${max}`\n }\n const formatStock = (product: ProductRow) => {\n const total = CommerceModel.productInventory(product)\n if (total == null) return '—'\n return total > 0 ? String(total) : 'Sold out'\n }\n /**\n * RESERVED UNITS, NAMED (AGL-2356).\n *\n * Checkout now takes a hold on the units a live session is about to buy, and\n * that hold deliberately does NOT move `inventory` — the shelf count means\n * units on the shelf, and half the product reads it that way. The\n * consequence a merchant meets is that the storefront can refuse a sale of\n * the third unit while this column says `3`, and without this caption the\n * number is right and the behaviour looks broken.\n *\n * The house style of `stock-movements-card.component.tsx`: a trailing\n * `caption` in `text.secondary`, rendered ONLY when there is something to\n * say. Nothing is held on the overwhelming majority of products, so the\n * column reads exactly as it does today.\n */\n const formatHeld = (product: ProductRow) => {\n const held = CommerceModel.heldProductUnits(product, nowMs)\n return held > 0 ? ` (${held} reserved)` : ''\n }\n\n const productColumns = listFilterGridColumns(\n [\n {\n field: 'name',\n headerName: 'Product',\n flex: 1,\n minWidth: 200,\n renderCell: ({ row }: { row: ProductRow }) => (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {row.name}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`/${row.slug} · id: ${row.$id}`}\n </Typography>\n </Stack>\n ),\n },\n {\n field: 'status',\n headerName: 'Status',\n width: 230,\n renderCell: ({ row }: { row: ProductRow }) => (\n <Stack direction=\"row\" spacing={0.5} sx={{ alignItems: 'center', height: '100%' }}>\n <Chip\n label={row.status}\n size=\"small\"\n color={STATUS_COLOR[row.status] ?? 'default'}\n variant=\"outlined\"\n />\n {/* Sold at the register, kiosk and POS apps, never online (AGL-4060). */}\n {CommerceModel.productSellsOnline(row) ? null : (\n <Chip label=\"In person only\" size=\"small\" variant=\"outlined\" />\n )}\n </Stack>\n ),\n },\n { field: 'type', headerName: 'Type', width: 110 },\n {\n field: 'priceUsd',\n headerName: 'Price',\n width: 130,\n renderCell: ({ row }: { row: ProductRow }) =>\n CommerceModel.productPriceMissing(row) ? (\n <Chip label=\"Set a price\" size=\"small\" color=\"warning\" variant=\"outlined\" />\n ) : (\n formatPrice(row)\n ),\n },\n {\n field: 'stock',\n headerName: 'Stock',\n width: 150,\n renderCell: ({ row }: { row: ProductRow }) => (\n <span>\n {formatStock(row)}\n {formatHeld(row) ? (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"span\">\n {formatHeld(row)}\n </Typography>\n ) : null}\n </span>\n ),\n },\n {\n field: 'variants',\n headerName: 'Variants',\n width: 100,\n valueGetter: (_value: unknown, row: ProductRow) => row.variants.length,\n },\n listActionsColumn(\n (product: ProductRow) => (\n <Stack\n direction=\"row\"\n spacing={0.5}\n sx={{ justifyContent: 'flex-end', whiteSpace: 'nowrap' }}\n >\n <Button size=\"small\" onClick={() => setEditing(product)}>\n {'Edit'}\n </Button>\n <Button size=\"small\" onClick={handleDuplicate(product)}>\n {'Duplicate'}\n </Button>\n {product.type === 'physical' ? (\n <Button size=\"small\" onClick={() => setLabelsFor(product)}>\n {'Labels'}\n </Button>\n ) : null}\n {product.type === 'digital' ? (\n <Button size=\"small\" onClick={() => setKeysFor(product)}>\n {'Keys'}\n </Button>\n ) : null}\n {CommerceModel.productInventory(product) != null ? (\n <Button\n size=\"small\"\n onClick={() =>\n setAdjusting({\n product,\n variantId:\n product.variants.find(\n (variant) => variant.inventory != null,\n )?.id ?? product.variants[0].id,\n delta: '',\n reason: 'restock',\n locationId:\n (locationDocs ?? []).find(\n (location: any) => location.isDefault,\n )?.$id ??\n (locationDocs ?? [])[0]?.$id ??\n '',\n })\n }\n >\n {'Stock'}\n </Button>\n ) : null}\n <Button\n size=\"small\"\n onClick={handleStatus(\n product,\n product.status === 'archived' ? 'active' : 'archived',\n )}\n >\n {product.status === 'archived' ? 'Activate' : 'Archive'}\n </Button>\n <Button size=\"small\" color=\"error\" onClick={handleDelete(product)}>\n {'Delete'}\n </Button>\n </Stack>\n ),\n { width: 470 },\n ),\n ] as GridColDef[],\n PRODUCT_LIST_FIELDS,\n PRODUCT_LIST_OPTIONS,\n PRODUCT_LIST_HEADERS,\n )\n\n /** Add product: the quota first, then the create drawer. */\n const startAddProduct = () => {\n if (!productQuota) return\n if (!productQuota.allowed) {\n return void enqueueSnackbar(\n `Your plan includes ${productQuota.limit} products — ` + 'upgrade for more',\n { variant: 'info', persist: false },\n )\n }\n setCreating(true)\n }\n\n /*\n * Other ways to start a product (AGL-3596): the `productsCreate` zone this\n * plugin declares, drawn through the shell's gated slot beside Add product\n * and again in the empty catalog. `null` outside the console shell. The\n * org is not this page's to know, so a widget reads it from the site.\n */\n const createZone = CreateZone ? (\n <CreateZone slot={PRODUCTS_CREATE_ZONE.id} hostId={hostId} orgId={undefined} />\n ) : null\n\n return (\n <CardDisplay\n /*\n * The header count is the SITE'S catalog, not the rows in hand.\n *\n * It was `products.length`, which is the filtered, ceilinged, now-paged\n * view — so it read 500 on a 25,000-product catalog and would read 10\n * once the table paged. `productCount` is the same server aggregate the\n * quota gate uses. Under a filter there is no honest number to put here:\n * the aggregate counts the whole catalog and the view counts matches, so\n * neither describes what the reader is looking at, and the footer's own\n * count line answers it instead.\n */\n header={\n filtering\n ? 'Products'\n : `Products${productCount ? ` (${productCount})` : ''}`\n }\n help={pluginDocsHelp('commerce', { anchor: '#products-hub' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1}>\n <Box sx={{ flex: 1 }} />\n {createZone}\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"small\"\n disabled={!productQuota}\n onClick={startAddProduct}\n >\n {'Add product'}\n </Button>\n {/* Each only for whom the route takes it (AGL-3554); an import\n left unfinished, reopened where it stopped (AGL-3549). */}\n <TransferResumeImport\n target={{ resource: COMMERCE_PRODUCTS_TRANSFER, scope: 'host', hostId }}\n onFinished={() => setProductCountEpoch((epoch) => epoch + 1)}\n />\n {transfer?.can('import', { resource: COMMERCE_PRODUCTS_TRANSFER, scope: 'host', hostId }) ? (\n <Button size=\"small\" disabled={!planReady} onClick={openImport}>\n {'Import'}\n </Button>\n ) : null}\n {transfer?.can('export', { resource: COMMERCE_PRODUCTS_TRANSFER, scope: 'host', hostId }) ? (\n <Button\n size=\"small\"\n // Nothing matches: there is nothing for the export to write.\n disabled={products.length === 0 && page === 0}\n onClick={openExport}\n >\n {'Export'}\n </Button>\n ) : null}\n </Stack>\n {/* The cap, standing rather than only on refusal (AGL-2113). The\n count is `productCount` — the site's products, the same number\n the gate above counts — not `products.length`, which is one page\n of the table's query (AGL-1716). */}\n {/* Products listed before they have a price (AGL-3676) — a guided\n start's — show \"Price coming soon\" on the store and sell nowhere\n until each is priced: the one step left before the store sells. */}\n {comingSoon.length ? (\n <Alert severity=\"info\">\n {`Set prices to start selling: ${comingSoon.length === 1 ? `${comingSoon[0].name} shows` : `${comingSoon.length} products show`} “Price coming soon” on your store until you set ${comingSoon.length === 1 ? 'its price' : 'their prices'}. Open each one and give every variant a price.`}\n </Alert>\n ) : null}\n <QuotaReadoutComponent\n ready={productQuota !== null}\n used={productCount}\n limit={productQuota?.limit ?? 0}\n noun=\"product\"\n />\n {/* The zone's widgets are handed the PAGE on screen — the products\n the reader has found and is looking at. Every write the zone makes\n asks the store for what it must not duplicate, never these rows. */}\n <ProductsHubZone\n hostId={hostId}\n products={products}\n roomFor={roomFor}\n lastImport={lastImport}\n onCreated={onCatalogCreated}\n />\n <ListFilterChips\n fields={PRODUCT_LIST_FIELDS}\n headers={PRODUCT_LIST_HEADERS}\n clauses={gridFilter.clauses}\n onChange={gridFilter.setClauses}\n options={PRODUCT_LIST_OPTIONS}\n />\n <ListQueryNotices\n refused={listQueryRefusals(plan.refused, {\n fields: PRODUCT_LIST_FIELDS,\n headers: PRODUCT_LIST_HEADERS,\n options: PRODUCT_LIST_OPTIONS,\n })}\n notices={[...plan.notices, ...columnSort.notices]}\n />\n <ListTable\n aria-label=\"Products\"\n rows={columnSort.rows}\n columns={productColumns}\n loading={productsStatus === 'loading'}\n // One page of the query, turned by the footer below: the grid\n // neither slices, filters nor sorts it.\n hideFooter\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n // Every header order is the query's, or the page's (`columnSort`).\n columnSort={columnSort}\n initialState={{\n columns: {\n columnVisibilityModel: hiddenFilterVisibility(\n PRODUCT_LIST_FIELDS,\n PRODUCT_VISIBLE_COLUMNS,\n ),\n },\n }}\n noRowsLabel={\n filtering ? 'No products match these filters' : 'No products yet'\n }\n noRowsDescription={\n filtering\n ? undefined\n : 'Build your catalog: add a product, then drop commerce ' +\n 'blocks on any page in Besigner.'\n }\n noRowsAction={\n filtering ? undefined : (\n <Stack direction=\"row\" spacing={1}>\n {createZone}\n <Button variant=\"contained\" disabled={!productQuota} onClick={startAddProduct}>\n {'Add your first product'}\n </Button>\n </Stack>\n )\n }\n />\n {products.length === 0 && page === 0 && productsStatus !== 'loading' ? null : (\n /* The count line is the PAGE's. `hasMore` is a fact from the probe\n row; the site's total is `productCount` above, which counts the\n whole catalog rather than the matches, so it is not this. */\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={products.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n )}\n </Stack>\n <ProductLabelsDialog product={labelsFor} onClose={() => setLabelsFor(null)} />\n <Dialog\n open={Boolean(keysFor)}\n onClose={() => setKeysFor(null)}\n maxWidth=\"sm\"\n fullWidth\n >\n <DialogTitle>{`License keys — ${keysFor?.name ?? ''}`}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n {(() => {\n // The query already asked for this product's keys, so no second\n // filter here: narrowing a window that was already narrowed is how\n // a reader comes to believe the count covers the pool.\n const productKeys = (keyDocs ?? []).slice(0, KEY_POOL_CEILING)\n const available = productKeys.filter(\n (key: any) => !key.assignedAtMs,\n )\n // RETIRED KEYS ARE COUNTED SEPARATELY (AGL-2454). A refund retires\n // the key rather than returning it to the pool — the buyer already\n // holds the string, so reissuing it would give two people one\n // working key — and without this line the merchant simply watches\n // \"available\" fall with nothing to explain where the key went.\n const retired = productKeys.filter((key: any) => key.revokedAtMs)\n const assigned =\n productKeys.length - available.length - retired.length\n return (\n <>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ mt: 1 }}>\n {`${available.length} available · ${assigned} assigned` +\n (retired.length\n ? ` · ${retired.length} retired (refunded or revoked — not reissued)`\n : '') +\n (keyPoolTruncated\n ? `, of the ${KEY_POOL_CEILING} keys read`\n : '') +\n '. Keys deliver automatically on purchase (receipt + account).'}\n </Typography>\n {available.slice(0, 8).map((key: any) => (\n <Stack\n key={key.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"caption\" sx={{ flex: 1, fontFamily: 'monospace' }} noWrap>\n {key.key}\n </Typography>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() =>\n updateDoc(\n doc(firestore, 'hosts', hostId, 'licenseKeys', key.$id),\n { revokedAtMs: Date.now(), assignedAtMs: Date.now() },\n )\n }\n >\n {'Revoke'}\n </Button>\n </Stack>\n ))}\n <TextField\n label=\"Add keys (one per line)\"\n value={keysText}\n onChange={(event) => setKeysText(event.target.value)}\n size=\"small\"\n multiline\n minRows={3}\n />\n </>\n )\n })()}\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setKeysFor(null)}>{'Close'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!keysText.trim()}\n onClick={async () => {\n const keys = keysText\n .split('\\n')\n .map((key) => key.trim())\n .filter(Boolean)\n .slice(0, 200)\n for (const key of keys) {\n await addDoc(\n collection(firestore, 'hosts', hostId, 'licenseKeys'),\n {\n productId: keysFor!.$id,\n key,\n assignedAtMs: null,\n createdAtMs: Date.now(),\n },\n )\n }\n setKeysText('')\n enqueueSnackbar(`Added ${keys.length} keys`, {\n variant: 'success',\n persist: false,\n })\n }}\n >\n {'Add keys'}\n </Button>\n </DialogActions>\n </Dialog>\n <Dialog\n open={Boolean(adjusting)}\n onClose={() => setAdjusting(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>{`Adjust stock — ${adjusting?.product.name ?? ''}`}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <TextField\n label=\"Variant\"\n value={adjusting?.variantId ?? ''}\n onChange={(event) =>\n setAdjusting((prev) =>\n prev ? { ...prev, variantId: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n sx={{ mt: 1 }}\n >\n {(adjusting?.product.variants ?? [])\n .filter((variant) => variant.inventory != null)\n .map((variant) => (\n <MenuItem key={variant.id} value={variant.id}>\n {`${Object.values(variant.options ?? {}).join(' / ') || 'Default'} — ${variant.inventory} in stock${\n CommerceModel.heldVariantUnits(\n adjusting?.product,\n variant.id,\n nowMs,\n ) > 0\n ? `, ${CommerceModel.heldVariantUnits(\n adjusting?.product,\n variant.id,\n nowMs,\n )} reserved in checkout`\n : ''\n }`}\n </MenuItem>\n ))}\n </TextField>\n {(locationDocs?.length ?? 0) > 1 ? (\n <TextField\n label=\"Location\"\n value={adjusting?.locationId ?? ''}\n onChange={(event) =>\n setAdjusting((prev) =>\n prev ? { ...prev, locationId: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n >\n {(locationDocs ?? []).map((location: any) => (\n <MenuItem key={location.$id} value={location.$id}>\n {location.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n <TextField\n label=\"Change\"\n placeholder=\"+10 or -3\"\n value={adjusting?.delta ?? ''}\n onChange={(event) =>\n setAdjusting((prev) =>\n prev\n ? {\n ...prev,\n delta: event.target.value.replace(/[^0-9+-]/g, ''),\n }\n : prev,\n )\n }\n size=\"small\"\n />\n <TextField\n label=\"Reason\"\n value={adjusting?.reason ?? 'restock'}\n onChange={(event) =>\n setAdjusting((prev) =>\n prev\n ? {\n ...prev,\n reason: event.target\n .value as CommerceModel.InventoryAdjustmentReason,\n }\n : prev,\n )\n }\n size=\"small\"\n select\n >\n <MenuItem value=\"restock\">{'Restock'}</MenuItem>\n <MenuItem value=\"correction\">{'Correction'}</MenuItem>\n <MenuItem value=\"damage\">{'Damaged'}</MenuItem>\n <MenuItem value=\"refund\">{'Refund return'}</MenuItem>\n </TextField>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setAdjusting(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!Math.round(Number(adjusting?.delta))}\n onClick={handleAdjustSave}\n >\n {'Apply'}\n </Button>\n </DialogActions>\n </Dialog>\n <ProductEditorDialog\n key={editing?.$id ?? (creating ? 'new' : 'closed')}\n hostId={hostId}\n product={editing}\n // The editor is seeded from a row of THIS listener and replaces the\n // whole document, so the freshness verdict belongs here (AGL-1358) —\n // the dialog has no listener of its own to ask.\n seedFromCache={productsFromCache}\n seedUnreadable={productsStatus === 'error'}\n open={creating || editing !== null}\n onClose={() => {\n setEditing(null)\n setCreating(false)\n // The dialog reports no verdict, so a create and a cancel look\n // the same from here. Re-reading on both is one aggregate and\n // keeps the cap off the dialog's shoulders (AGL-1716).\n setProductCountEpoch((epoch) => epoch + 1)\n }}\n />\n </CardDisplay>\n )\n}\nProductsHubCard.displayName = 'ProductsHubCard'\n\nexport default ProductsHubCard\n"],"names":["TransferResumeImport","useConsoleWidgetSlot","Aglyn","CommerceModel","PRODUCT_LIST_BASE","PRODUCT_LIST_COLUMN_SORTS","PRODUCT_LIST_FIELDS","PRODUCT_LIST_HEADERS","PRODUCT_LIST_OPTIONS","PRODUCT_LIST_QUERY","PRODUCT_LIST_SELECT_FIELDS","productListRequestClauses","CardDisplay","useConfirmationContext","ListFilterChips","ListPagination","ListQueryNotices","listQueryRefusals","ListTable","listActionsColumn","QuotaReadoutComponent","hiddenFilterVisibility","listFilterGridColumns","useListColumnSort","useListGridFilter","useSnackbar","Timestamp","Alert","Box","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","addDoc","collection","doc","documentId","limit","orderBy","query","updateDoc","where","getLiveCountFromServer","useCallback","useEffect","useMemo","useState","useFirestore","useUser","writeSiteWideChange","useFirestoreCollection","writeGuardedBySeed","useListQuery","useHostResourceApi","useOrgPlan","ProductLabelsDialog","ProductEditorDialog","productSlugLedger","productCollectionFields","ProductsHubZone","pluginDocsHelp","PRODUCTS_CREATE_ZONE","commerceListFilter","COMMERCE_PRODUCTS_TRANSFER","KEY_POOL_CEILING","PRODUCT_PAGE_SORTS","stock","row","productInventory","variants","length","PRODUCT_PAGE_SORT_HEADERS","STATUS_COLOR","active","draft","archived","PRODUCT_VISIBLE_COLUMNS","ProductsHubCard","props","hostId","CreateZone","firestore","data","user","enqueueSnackbar","createHostResource","org","ready","planReady","confirm","gridFilter","selectFields","filtering","clauses","searchWords","some","word","trim","askedSort","setAskedSort","editing","setEditing","creating","setCreating","adjusting","setAdjusting","keysFor","setKeysFor","labelsFor","setLabelsFor","keysText","setKeysText","lastImport","setLastImport","transfer","useTransferLauncher","rows","productDocs","productWindow","hasMore","page","setPage","pageSize","setPageSize","plan","status","productsStatus","fromCache","productsFromCache","declaration","request","search","base","sort","deps","idField","confirmDisappearances","keyDocs","$id","keyPoolTruncated","locationDocs","products","map","product","liftLegacyProduct","comingSoon","filter","productPriceMissing","columnSort","sorts","defaultSort","onSortChange","pageSorts","headers","nowMs","setNowMs","Date","now","anyHeld","heldProductUnits","undefined","timer","setInterval","clearInterval","productCountEpoch","setProductCountEpoch","serverProductCount","setServerProductCount","then","count","catch","loadedProductCount","productCount","productQuota","checkQuota","handleDuplicate","allowed","variant","persist","slug","claim","productDuplicateSlugBase","name","resource","productDuplicateFields","productDuplicateName","epoch","error","message","handleStatus","write","batch","update","updatedAtMs","updatedAt","handleDelete","confirmed","title","description","confirmationText","confirmationButtonProps","color","deletedAt","openImport","scope","onFinished","takeProductImport","createdProductIds","finished","productIds","key","jobId","options","openExport","label","value","handleAdjustSave","stockAdjustmentWrite","variantId","delta","Number","reason","locationId","verdict","subject","unreadable","ledger","ok","roomFor","onCatalogCreated","formatPrice","min","max","productPriceRange","formatStock","total","String","formatHeld","held","productColumns","field","headerName","flex","minWidth","renderCell","sx","noWrap","width","direction","spacing","alignItems","height","size","productSellsOnline","span","component","valueGetter","_value","justifyContent","whiteSpace","onClick","type","find","inventory","id","location","isDefault","startAddProduct","createZone","slot","orgId","header","help","anchor","contentGutterX","contentGutterY","xs","sm","disabled","target","can","severity","used","noun","onCreated","fields","onChange","setClauses","refused","notices","aria-label","columns","loading","hideFooter","filterMode","filterModel","onFilterModelChange","quickFilter","initialState","columnVisibilityModel","noRowsLabel","noRowsDescription","noRowsAction","rowCount","onPageChange","onPageSizeChange","onClose","open","Boolean","maxWidth","fullWidth","display","flexDirection","gap","productKeys","slice","available","assignedAtMs","retired","revokedAtMs","assigned","mt","fontFamily","event","multiline","minRows","keys","split","productId","createdAtMs","prev","select","Object","values","join","heldVariantUnits","placeholder","replace","Math","round","seedFromCache","seedUnreadable","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/products-hub-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 { TransferResumeImport } from '@aglyn/aglyn/app-utils/transfer-resume-import'\nimport { useConsoleWidgetSlot } from '@aglyn/aglyn/app-utils/console-widget-slot-context'\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport {\n PRODUCT_LIST_BASE,\n PRODUCT_LIST_COLUMN_SORTS,\n PRODUCT_LIST_FIELDS,\n PRODUCT_LIST_HEADERS,\n PRODUCT_LIST_OPTIONS,\n PRODUCT_LIST_QUERY,\n PRODUCT_LIST_SELECT_FIELDS,\n productListRequestClauses,\n} from '../../constants/product-list-query'\nimport { CardDisplay, useConfirmationContext } 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 {\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport QuotaReadoutComponent from '@aglyn/shared-ui-jsx/components/quota-readout.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 { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Timestamp } from '@aglyn/shared-util-timestamp'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport {\n addDoc,\n collection,\n doc,\n documentId,\n limit,\n orderBy,\n query,\n updateDoc,\n where,\n} from 'firebase/firestore'\nimport { getLiveCountFromServer } from '@aglyn/aglyn/app-utils/live-document-count.client'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport {\n useFirestoreCollection,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { useListQuery } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { useHostResourceApi } from '@aglyn/tenant-feature-instance'\nimport { useOrgPlan } from '@aglyn/tenant-feature-instance'\nimport { ProductLabelsDialog } from './product-labels-dialog.component'\nimport ProductEditorDialog from './product-editor-dialog.component'\nimport { productSlugLedger } from './product-slugs'\nimport { productCollectionFields } from './smart-collections'\nimport ProductsHubZone from './products-hub-zone.component'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { PRODUCTS_CREATE_ZONE, type ConsoleProductsHubZoneProps } from './product-zones'\nimport { commerceListFilter } from '../../transfer/list-filter'\nimport { COMMERCE_PRODUCTS_TRANSFER } from '../../transfer/transfer-keys'\n\n/**\n * How many of ONE product's license keys the dialog reads.\n *\n * Per product, not per store: the query carries the `productId` equality, so\n * this bounds a single pool rather than the site's whole key collection.\n */\nconst KEY_POOL_CEILING = 500\n\nexport interface ProductsHubCardProps {\n hostId: string\n}\n\ntype ProductRow = CommerceModel.HostProduct & { $id: string }\n\n/**\n * Stock and Variants are read from inside `variants`, which no query can\n * order by, so they sort the page on screen (AGL-3680); Product, Status, Type\n * and Price are the query's order (`PRODUCT_LIST_COLUMN_SORTS`).\n */\nconst PRODUCT_PAGE_SORTS = {\n stock: (row: ProductRow) => CommerceModel.productInventory(row),\n variants: (row: ProductRow) => row.variants.length,\n}\nconst PRODUCT_PAGE_SORT_HEADERS = { stock: 'Stock', variants: 'Variants' }\n\nconst STATUS_COLOR: Record<string, 'default' | 'success' | 'warning'> = {\n active: 'success',\n draft: 'warning',\n archived: 'default',\n}\n\n/** The grid's own columns; every other declared field is a hidden filter column. */\nconst PRODUCT_VISIBLE_COLUMNS = ['name', 'status', 'type', 'priceUsd', 'stock', 'variants']\n\n/**\n * Products hub v1 (AGL-279): the catalog manager replacing the Commerce\n * Starter card — a table over `hosts/{hostId}/products` whose search and\n * filters are its Firestore query (AGL-3321), full editor dialog, duplicate, archive/activate, soft delete (past\n * order rows keep resolving). Product cap (`productsPerHost`, AGL-278)\n * gated here on create/duplicate/import (AGL-471); server-side\n * enforcement of the client-write path rides AGL-473.\n */\nexport function ProductsHubCard(props: ProductsHubCardProps) {\n const { hostId } = props\n /** The shell's zone renderer, for the `productsCreate` zone (AGL-3596). */\n const CreateZone = useConsoleWidgetSlot()\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n // Product cap (AGL-471): per-plan `productsPerHost`, same pattern as\n // locations. Console-side gate; server enforcement rides AGL-473.\n const createHostResource = useHostResourceApi()\n const { org, ready: planReady } = useOrgPlan(hostId)\n const { confirm } = useConfirmationContext()\n /*\n * The grid's Filters panel and quick search, bound to the clauses the\n * query below serves (AGL-3321). Every clause and the search word go on\n * that query; see `constants/product-list-query.ts` for what is offered.\n */\n const gridFilter = useListGridFilter({\n selectFields: PRODUCT_LIST_SELECT_FIELDS,\n })\n const filtering =\n gridFilter.clauses.length > 0 ||\n gridFilter.searchWords.some((word) => word.trim())\n // Every header sorts (AGL-3680); A to Z until one is clicked.\n const [askedSort, setAskedSort] = useState<ListQuerySort | null>(\n PRODUCT_LIST_COLUMN_SORTS[0],\n )\n const [editing, setEditing] = useState<ProductRow | null>(null)\n const [creating, setCreating] = useState(false)\n const [adjusting, setAdjusting] = useState<{\n product: ProductRow\n variantId: string\n delta: string\n reason: CommerceModel.InventoryAdjustmentReason\n locationId: string\n } | null>(null)\n const [keysFor, setKeysFor] = useState<ProductRow | null>(null)\n // Product labels for a label printer (AGL-3619).\n const [labelsFor, setLabelsFor] = useState<ProductRow | null>(null)\n const [keysText, setKeysText] = useState('')\n /**\n * What the last import created, with the options its After import step\n * set (AGL-2916, AGL-3531), which the hub's zone hands to its widgets.\n */\n const [lastImport, setLastImport] =\n useState<ConsoleProductsHubZoneProps['lastImport']>(null)\n // The console's import wizard and export dialog; none outside the console.\n const transfer = Aglyn.useTransferLauncher()\n\n const {\n rows: productDocs,\n data: productWindow,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n plan,\n status: productsStatus,\n /**\n * The product rows the stock dialog is seeded from are unconfirmed by\n * the server (AGL-1358). A stock adjustment does not write the delta —\n * it recomputes the WHOLE `variants` array from the seeded product and\n * replaces it, so a cached seed silently reverts every sale, return and\n * adjustment the server has recorded since that snapshot, on every\n * variant and every location.\n */\n fromCache: productsFromCache,\n } = useListQuery<any>({\n /*\n * PAGED BY THE QUERY, and every filter ON it (AGL-3321).\n *\n * The table read a five-hundred-product window and put ONE predicate on\n * it — the search or the Status clause — then matched Status beside a\n * search, dropped soft-deleted products and sorted by name over the rows\n * it held, and paged that. Past five hundred products, a match past the\n * window answered \"no products match\", and a page came back short by\n * however many deleted products it had held.\n *\n * Now the plan puts the search word and every clause on one query under\n * the `deletedAt == null` scope, ordered by `nameLower`, and each page is\n * a page of that query — one page plus a probe row, widened as the reader\n * pages forward. Nothing is matched over the rows afterwards; a clause\n * the query cannot hold is refused by name (`ListQueryNotices` below).\n */\n collection: collection(firestore, 'hosts', hostId, 'products'),\n declaration: PRODUCT_LIST_QUERY,\n request: {\n clauses: productListRequestClauses(gridFilter.clauses),\n search: gridFilter.searchWords,\n base: PRODUCT_LIST_BASE,\n sort: askedSort,\n },\n deps: [firestore, hostId],\n idField: '$id',\n /*\n * The scope and the filters read MUTABLE fields — a delete sets\n * `deletedAt`, Archive moves `status`, a rename moves the tokens — so a\n * product can leave this query mid-session, and the SDK can cache that\n * departure as a tombstone at the product's own path (AGL-1196). A\n * disappearance is confirmed against the server before it is believed.\n */\n confirmDisappearances: true,\n })\n /*\n * License key pool (AGL-308) for the open dialog's product — asked FOR that\n * product, and ordered (AGL-2501).\n *\n * This read `limit(500)` over the site's whole `licenseKeys` collection with\n * no `orderBy`, then filtered by `productId` in the browser. Firestore\n * answers an unordered limit in DOCUMENT-ID order, so on a store past five\n * hundred keys the window was an arbitrary five hundred taken across every\n * product — and the \"N available\" line below counted what happened to be in\n * it. A product whose keys all hashed high showed a pool of zero while the\n * storefront went on delivering them.\n *\n * The equality moves into the query, so the window is this product's keys\n * rather than the store's. `orderBy(documentId())` orders on the document\n * NAME, which cannot be absent — every candidate FIELD here (`createdAtMs`,\n * `assignedAtMs`, `revokedAtMs`) is either optional by design or absent on\n * exactly the keys the counts are about. An equality plus an ordering on\n * `__name__` is served by the automatic single-field index, so this adds no\n * composite for anyone to deploy.\n */\n const { data: keyDocs } = useFirestoreCollection<any>(\n () =>\n keysFor\n ? query(\n collection(firestore, 'hosts', hostId, 'licenseKeys'),\n where('productId', '==', keysFor.$id),\n orderBy(documentId()),\n // One past the ceiling, so a pool larger than the window is a\n // fact rather than a guess from `length === 500`.\n limit(KEY_POOL_CEILING + 1),\n )\n : null,\n [firestore, hostId, keysFor?.$id],\n { idField: '$id' },\n )\n /** The key pool is larger than the window, so the counts below are partial. */\n const keyPoolTruncated = (keyDocs?.length ?? 0) > KEY_POOL_CEILING\n // Locations (AGL-286): the stock dialog buckets deltas when they exist.\n const { data: locationDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'locations'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n /*\n * The PAGE on screen, lifted from legacy shapes. It is what the table\n * draws, the reserved-stock clock watches and the zone hands its widgets,\n * and nothing narrows it: the query already answered every filter, and\n * soft-deleted products are outside its scope.\n */\n const products = useMemo<ProductRow[]>(\n () =>\n (productDocs ?? []).map((product: any) => ({\n ...CommerceModel.liftLegacyProduct(product),\n $id: product.$id,\n })),\n [productDocs],\n )\n // Listed but not yet priced (AGL-3676), among the products on screen.\n const comingSoon = useMemo(\n () =>\n products.filter(\n (product) => product.status === 'active' && CommerceModel.productPriceMissing(product),\n ),\n [products],\n )\n const columnSort = useListColumnSort<ProductRow>({\n sorts: PRODUCT_LIST_COLUMN_SORTS,\n defaultSort: PRODUCT_LIST_COLUMN_SORTS[0],\n sort: askedSort,\n onSortChange: setAskedSort,\n orderBy: plan.orderBy,\n rows: products,\n pageSorts: PRODUCT_PAGE_SORTS,\n headers: PRODUCT_PAGE_SORT_HEADERS,\n })\n\n /**\n * A CLOCK, because a hold lapses without anybody writing anything\n * (AGL-2356).\n *\n * The Firestore listener re-renders on document changes, and a reservation\n * expiring is not one — `expiresAtMs` simply passes. Without a tick the\n * \"reserved\" caption below would keep naming a hold that lapsed twenty\n * minutes ago, which is worse than not showing it at all: the merchant would\n * be told stock is spoken for while the storefront happily sells it.\n *\n * Only runs while something is actually held, so an ordinary catalog costs\n * no timer. A NEW hold arrives as a document change, which re-renders, which\n * re-arms this. It watches the page on screen, which is every row whose\n * caption it keeps true.\n */\n const [nowMs, setNowMs] = useState(() => Date.now())\n const anyHeld = useMemo(\n () =>\n products.some(\n (product) => CommerceModel.heldProductUnits(product, nowMs) > 0,\n ),\n [products, nowMs],\n )\n useEffect(() => {\n if (!anyHeld) return undefined\n const timer = setInterval(() => setNowMs(Date.now()), 60_000)\n return () => clearInterval(timer)\n }, [anyHeld])\n\n /**\n * The catalog HEAD-COUNT is a server aggregate, not the length of the\n * capped listener (AGL-1716, the AGL-1706 shape).\n *\n * The listener is `limit(500)` — correctly; a 25,000-product catalog does\n * not belong in a table. What it must not do is answer \"how many products\n * does this site have\", and it did: the length saturated at 500 and was\n * handed to `checkQuota(org, 'productsPerHost', …)` and to the batch check\n * the CSV importer runs. The bands are 2,500 / 10,000 / 25,000 above the\n * window, so on Pro and up the check compared 500 against thousands and\n * could never refuse — the card offered headroom and `api/hosts/resources`\n * then refused the create, which is the AGL-1716 shape exactly.\n *\n * It counts LIVE products, as `api/hosts/resources` and every product\n * writer count them (AGL-3918): a deleted product frees its slot, an\n * archived or draft one keeps it. Every document less the tombstones\n * (`getLiveCountFromServer`), so the card asks the enforcing route's\n * question in the enforcing route's terms.\n *\n * The table pages its own query (AGL-3321) and answers a different\n * question — which products match — so it never stands in for this. A\n * one-shot goes stale where a listener refreshed for free, so the count is\n * re-read after any mutation that moves it.\n *\n * No counting RULE moves: `checkQuota` is untouched and `report-usage`\n * meters contacts, storage and API requests — never the catalog.\n */\n const [productCountEpoch, setProductCountEpoch] = useState(0)\n const [serverProductCount, setServerProductCount] = useState<number | null>(\n null,\n )\n useEffect(() => {\n let active = true\n void getLiveCountFromServer(collection(firestore, 'hosts', hostId, 'products'))\n .then((count) => {\n if (active) setServerProductCount(count)\n })\n .catch(() => {\n // Falls back to the live-row count below — a LOWER bound, and this\n // card's prior behaviour. Deliberately not 0: `checkQuota` answers\n // from whatever it is handed, and 0 used is a confident wrong\n // number in the flattering direction.\n })\n return () => {\n active = false\n }\n }, [firestore, hostId, productCountEpoch])\n // The fallback, pending or denied: the live products the table's query has\n // read, probe row included. Each is a product the site has, so it can only\n // UNDERSTATE, never overstate, and nothing it gates fires on a count larger\n // than the truth (AGL-471).\n const loadedProductCount = productWindow?.length ?? 0\n const productCount = serverProductCount ?? loadedProductCount\n // Gate only once the org doc has loaded: an unresolved org reads as the\n // free tier's 0-product cap, which swallowed every Add/Duplicate click.\n // The resources API (AGL-473) stays the authoritative cap on create.\n //\n // `planReady` rather than `org` truthiness (AGL-1064): a host with no\n // owning org never produces one, and waiting on the value alone would\n // hold this open forever. The old fallback allowed UNLIMITED during the\n // window — safe only because the API re-checks; the controls now disable\n // instead, which does not lean on that backstop.\n // NULL means \"not known yet\", which a boolean cannot say — the old\n // fallback object claimed UNLIMITED and every consumer believed it.\n const productQuota = useMemo(\n () =>\n planReady\n ? Aglyn.checkQuota(org, 'productsPerHost', productCount)\n : null,\n [org, planReady, productCount],\n )\n\n const handleDuplicate = useCallback(\n (product: ProductRow) => async () => {\n // Plan unknown — the control is disabled; this guards the race.\n if (!productQuota) return\n if (!productQuota.allowed) {\n return void enqueueSnackbar(\n `Your plan includes ${productQuota.limit} products — upgrade for more`,\n { variant: 'info', persist: false },\n )\n }\n try {\n /*\n * The copy's slug is asked of the store, so a second Duplicate of\n * the same product does not mint a second `-copy` (AGL-3321), and\n * its search keys are the COPY's: spreading the source's would sort\n * and find it by the source's name.\n */\n const slug = await productSlugLedger(firestore, hostId).claim(\n CommerceModel.productDuplicateSlugBase(product.slug, product.name),\n )\n // Duplicate is a create — rides the quota-enforcing API (AGL-473).\n // The source's SKU and barcode keys are not copied: the copy's own\n // are derived (`productDuplicateFields`), and a copy of a product\n // with none must have none.\n await createHostResource({\n hostId,\n resource: 'product',\n data: {\n ...CommerceModel.productDuplicateFields(product, slug, Date.now()),\n // The COPY's smart collections: its name differs from the source's.\n ...(await productCollectionFields(firestore, hostId, {\n ...product,\n name: CommerceModel.productDuplicateName(product.name),\n })),\n },\n })\n // A create moves the count and the aggregate is a one-shot — the\n // listener refreshes the ROWS for free, the count has to be asked\n // again or the cap drifts stale for the rest of the session.\n setProductCountEpoch((epoch) => epoch + 1)\n enqueueSnackbar('Product duplicated as draft', {\n variant: 'success',\n persist: false,\n })\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not duplicate product', {\n variant: 'warning',\n persist: false,\n })\n }\n },\n [firestore, hostId, createHostResource, enqueueSnackbar, productQuota],\n )\n\n const handleStatus = useCallback(\n (product: ProductRow, status: CommerceModel.ProductStatus) => async () => {\n // A product with a variant nobody has priced may go live (AGL-3676):\n // the storefront says \"Price coming soon\" and sells it nowhere until\n // the editor has a price for each. The owner is told, not stopped.\n if (status === 'active' && CommerceModel.productPriceMissing(product)) {\n enqueueSnackbar(\n `${product.name} has no price yet: visitors will see “Price coming soon” and can’t buy it.`,\n { variant: 'info', persist: false },\n )\n }\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(doc(firestore, 'hosts', hostId, 'products', product.$id), {\n status,\n updatedAtMs: Date.now(),\n updatedAt: Timestamp.now(),\n }),\n })\n },\n [firestore, hostId, user, enqueueSnackbar],\n )\n\n const handleDelete = useCallback(\n (product: ProductRow) => async () => {\n const confirmed = await confirm({\n title: 'Delete this product?',\n description:\n `\"${product.name}\" stops being purchasable; blocks referencing ` +\n 'it show a checkout error until repointed.',\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(doc(firestore, 'hosts', hostId, 'products', product.$id), {\n deletedAt: Timestamp.now(),\n }),\n })\n // Deleting frees the slot (AGL-3918), so the head-count is re-read.\n setProductCountEpoch((epoch) => epoch + 1)\n enqueueSnackbar('Product deleted', { variant: 'success', persist: false })\n },\n [confirm, firestore, hostId, user, enqueueSnackbar],\n )\n\n /*\n * Import and export (AGL-3531) run on the console's transfer wizard and\n * dialog, on the `commerce.products` resource: a product matched by\n * handle, SKU or ID is updated, every step is a choice, and the export\n * writes what the table's query matches — its filters and search — on the\n * server, a row per variant, with a Shopify preset.\n *\n * When the wizard closes on its results, the products the import created\n * reach the hub's zone with the options its After import step set.\n */\n const openImport = useCallback(() => {\n transfer?.openImport({\n resource: COMMERCE_PRODUCTS_TRANSFER,\n scope: 'host',\n hostId,\n onFinished: () => {\n setProductCountEpoch((epoch) => epoch + 1)\n void (async () => {\n const { takeProductImport, createdProductIds } = await import(\n '../../transfer/product-import-results'\n )\n const finished = takeProductImport(hostId)\n if (!finished) return\n const productIds = await createdProductIds(user, finished)\n setLastImport({\n key: `${finished.jobId}-${productIds.length}`,\n productIds,\n options: finished.options,\n })\n })()\n },\n })\n }, [transfer, hostId, user])\n const openExport = useCallback(() => {\n transfer?.openExport({\n resource: COMMERCE_PRODUCTS_TRANSFER,\n scope: 'host',\n hostId,\n ...(filtering\n ? {\n filter: {\n label: 'the products the list’s filters and search find',\n value: commerceListFilter(plan),\n },\n }\n : {}),\n })\n }, [transfer, hostId, filtering, plan])\n\n const handleAdjustSave = useCallback(async () => {\n if (!adjusting) return\n // The update and the ledger row, as the native stock route writes them (AGL-3652).\n const write = CommerceModel.stockAdjustmentWrite(\n adjusting.product,\n adjusting.product.$id,\n {\n variantId: adjusting.variantId,\n delta: Number(adjusting.delta),\n reason: adjusting.reason,\n locationId: adjusting.locationId || undefined,\n },\n Date.now(),\n )\n if (!write) return\n /**\n * Refuse the adjustment when the seed is unconfirmed (AGL-1358).\n *\n * `adjustVariantInventory` reads counts off the seeded product and\n * returns a whole new `variants` array, which is then written over the\n * stored one — so this is a full replace of live stock, not a delta, and\n * `merge` could not help. The refusal also has to cover the ADJUSTMENT\n * LOG, which is why the guard wraps both writes: a logged adjustment\n * whose stock write never happened is worse than neither, because the\n * history then disagrees with the count it is supposed to explain.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'stock',\n unreadable: productsStatus === 'error',\n fromCache: productsFromCache,\n },\n async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(\n doc(firestore, 'hosts', hostId, 'products', adjusting.product.$id),\n // The total and the In stock verdict move with the count (AGL-3321).\n write.update,\n ),\n })\n // Adjustment history (AGL-281): the same log the sale webhook writes.\n await addDoc(\n collection(firestore, 'hosts', hostId, 'inventoryAdjustments'),\n write.ledger,\n )\n },\n )\n // Keep the dialog open with the typed delta rather than failing silently.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n setAdjusting(null)\n enqueueSnackbar('Stock adjusted', { variant: 'success', persist: false })\n }, [\n user,\n adjusting,\n firestore,\n hostId,\n enqueueSnackbar,\n productsFromCache,\n productsStatus,\n ])\n\n /** The hub's allowance check for a batch of `count` new products, for its zone. */\n const roomFor = useCallback(\n (count: number) =>\n planReady\n ? Aglyn.checkQuota(org, 'productsPerHost', productCount + count - 1)\n : null,\n [org, planReady, productCount],\n )\n const onCatalogCreated = useCallback(\n () => setProductCountEpoch((epoch) => epoch + 1),\n [],\n )\n\n const formatPrice = (product: ProductRow) => {\n const [min, max] = CommerceModel.productPriceRange(product)\n return min === max ? `$${min}` : `$${min}–$${max}`\n }\n const formatStock = (product: ProductRow) => {\n const total = CommerceModel.productInventory(product)\n if (total == null) return '—'\n return total > 0 ? String(total) : 'Sold out'\n }\n /**\n * RESERVED UNITS, NAMED (AGL-2356).\n *\n * Checkout now takes a hold on the units a live session is about to buy, and\n * that hold deliberately does NOT move `inventory` — the shelf count means\n * units on the shelf, and half the product reads it that way. The\n * consequence a merchant meets is that the storefront can refuse a sale of\n * the third unit while this column says `3`, and without this caption the\n * number is right and the behaviour looks broken.\n *\n * The house style of `stock-movements-card.component.tsx`: a trailing\n * `caption` in `text.secondary`, rendered ONLY when there is something to\n * say. Nothing is held on the overwhelming majority of products, so the\n * column reads exactly as it does today.\n */\n const formatHeld = (product: ProductRow) => {\n const held = CommerceModel.heldProductUnits(product, nowMs)\n return held > 0 ? ` (${held} reserved)` : ''\n }\n\n const productColumns = listFilterGridColumns(\n [\n {\n field: 'name',\n headerName: 'Product',\n flex: 1,\n minWidth: 200,\n renderCell: ({ row }: { row: ProductRow }) => (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {row.name}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`/${row.slug} · id: ${row.$id}`}\n </Typography>\n </Stack>\n ),\n },\n {\n field: 'status',\n headerName: 'Status',\n width: 230,\n renderCell: ({ row }: { row: ProductRow }) => (\n <Stack direction=\"row\" spacing={0.5} sx={{ alignItems: 'center', height: '100%' }}>\n <Chip\n label={row.status}\n size=\"small\"\n color={STATUS_COLOR[row.status] ?? 'default'}\n variant=\"outlined\"\n />\n {/* Sold at the register, kiosk and POS apps, never online (AGL-4060). */}\n {CommerceModel.productSellsOnline(row) ? null : (\n <Chip label=\"In person only\" size=\"small\" variant=\"outlined\" />\n )}\n {/* A sample a guided start made, to replace with the merchant's own (AGL-4065). */}\n {CommerceModel.productIsSample(row) ? (\n <Tooltip title={CommerceModel.PRODUCT_SAMPLE_NOTE}>\n <Chip label=\"Sample\" size=\"small\" color=\"info\" variant=\"outlined\" />\n </Tooltip>\n ) : null}\n </Stack>\n ),\n },\n { field: 'type', headerName: 'Type', width: 110 },\n {\n field: 'priceUsd',\n headerName: 'Price',\n width: 130,\n renderCell: ({ row }: { row: ProductRow }) =>\n CommerceModel.productPriceMissing(row) ? (\n <Chip label=\"Set a price\" size=\"small\" color=\"warning\" variant=\"outlined\" />\n ) : (\n formatPrice(row)\n ),\n },\n {\n field: 'stock',\n headerName: 'Stock',\n width: 150,\n renderCell: ({ row }: { row: ProductRow }) => (\n <span>\n {formatStock(row)}\n {formatHeld(row) ? (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"span\">\n {formatHeld(row)}\n </Typography>\n ) : null}\n </span>\n ),\n },\n {\n field: 'variants',\n headerName: 'Variants',\n width: 100,\n valueGetter: (_value: unknown, row: ProductRow) => row.variants.length,\n },\n listActionsColumn(\n (product: ProductRow) => (\n <Stack\n direction=\"row\"\n spacing={0.5}\n sx={{ justifyContent: 'flex-end', whiteSpace: 'nowrap' }}\n >\n <Button size=\"small\" onClick={() => setEditing(product)}>\n {'Edit'}\n </Button>\n <Button size=\"small\" onClick={handleDuplicate(product)}>\n {'Duplicate'}\n </Button>\n {product.type === 'physical' ? (\n <Button size=\"small\" onClick={() => setLabelsFor(product)}>\n {'Labels'}\n </Button>\n ) : null}\n {product.type === 'digital' ? (\n <Button size=\"small\" onClick={() => setKeysFor(product)}>\n {'Keys'}\n </Button>\n ) : null}\n {CommerceModel.productInventory(product) != null ? (\n <Button\n size=\"small\"\n onClick={() =>\n setAdjusting({\n product,\n variantId:\n product.variants.find(\n (variant) => variant.inventory != null,\n )?.id ?? product.variants[0].id,\n delta: '',\n reason: 'restock',\n locationId:\n (locationDocs ?? []).find(\n (location: any) => location.isDefault,\n )?.$id ??\n (locationDocs ?? [])[0]?.$id ??\n '',\n })\n }\n >\n {'Stock'}\n </Button>\n ) : null}\n <Button\n size=\"small\"\n onClick={handleStatus(\n product,\n product.status === 'archived' ? 'active' : 'archived',\n )}\n >\n {product.status === 'archived' ? 'Activate' : 'Archive'}\n </Button>\n <Button size=\"small\" color=\"error\" onClick={handleDelete(product)}>\n {'Delete'}\n </Button>\n </Stack>\n ),\n { width: 470 },\n ),\n ] as GridColDef[],\n PRODUCT_LIST_FIELDS,\n PRODUCT_LIST_OPTIONS,\n PRODUCT_LIST_HEADERS,\n )\n\n /** Add product: the quota first, then the create drawer. */\n const startAddProduct = () => {\n if (!productQuota) return\n if (!productQuota.allowed) {\n return void enqueueSnackbar(\n `Your plan includes ${productQuota.limit} products — ` + 'upgrade for more',\n { variant: 'info', persist: false },\n )\n }\n setCreating(true)\n }\n\n /*\n * Other ways to start a product (AGL-3596): the `productsCreate` zone this\n * plugin declares, drawn through the shell's gated slot beside Add product\n * and again in the empty catalog. `null` outside the console shell. The\n * org is not this page's to know, so a widget reads it from the site.\n */\n const createZone = CreateZone ? (\n <CreateZone slot={PRODUCTS_CREATE_ZONE.id} hostId={hostId} orgId={undefined} />\n ) : null\n\n return (\n <CardDisplay\n /*\n * The header count is the SITE'S catalog, not the rows in hand.\n *\n * It was `products.length`, which is the filtered, ceilinged, now-paged\n * view — so it read 500 on a 25,000-product catalog and would read 10\n * once the table paged. `productCount` is the same server aggregate the\n * quota gate uses. Under a filter there is no honest number to put here:\n * the aggregate counts the whole catalog and the view counts matches, so\n * neither describes what the reader is looking at, and the footer's own\n * count line answers it instead.\n */\n header={\n filtering\n ? 'Products'\n : `Products${productCount ? ` (${productCount})` : ''}`\n }\n help={pluginDocsHelp('commerce', { anchor: '#products-hub' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1}>\n <Box sx={{ flex: 1 }} />\n {createZone}\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"small\"\n disabled={!productQuota}\n onClick={startAddProduct}\n >\n {'Add product'}\n </Button>\n {/* Each only for whom the route takes it (AGL-3554); an import\n left unfinished, reopened where it stopped (AGL-3549). */}\n <TransferResumeImport\n target={{ resource: COMMERCE_PRODUCTS_TRANSFER, scope: 'host', hostId }}\n onFinished={() => setProductCountEpoch((epoch) => epoch + 1)}\n />\n {transfer?.can('import', { resource: COMMERCE_PRODUCTS_TRANSFER, scope: 'host', hostId }) ? (\n <Button size=\"small\" disabled={!planReady} onClick={openImport}>\n {'Import'}\n </Button>\n ) : null}\n {transfer?.can('export', { resource: COMMERCE_PRODUCTS_TRANSFER, scope: 'host', hostId }) ? (\n <Button\n size=\"small\"\n // Nothing matches: there is nothing for the export to write.\n disabled={products.length === 0 && page === 0}\n onClick={openExport}\n >\n {'Export'}\n </Button>\n ) : null}\n </Stack>\n {/* The cap, standing rather than only on refusal (AGL-2113). The\n count is `productCount` — the site's products, the same number\n the gate above counts — not `products.length`, which is one page\n of the table's query (AGL-1716). */}\n {/* Products listed before they have a price (AGL-3676) — a guided\n start's — show \"Price coming soon\" on the store and sell nowhere\n until each is priced: the one step left before the store sells. */}\n {comingSoon.length ? (\n <Alert severity=\"info\">\n {`Set prices to start selling: ${comingSoon.length === 1 ? `${comingSoon[0].name} shows` : `${comingSoon.length} products show`} “Price coming soon” on your store until you set ${comingSoon.length === 1 ? 'its price' : 'their prices'}. Open each one and give every variant a price.`}\n </Alert>\n ) : null}\n <QuotaReadoutComponent\n ready={productQuota !== null}\n used={productCount}\n limit={productQuota?.limit ?? 0}\n noun=\"product\"\n />\n {/* The zone's widgets are handed the PAGE on screen — the products\n the reader has found and is looking at. Every write the zone makes\n asks the store for what it must not duplicate, never these rows. */}\n <ProductsHubZone\n hostId={hostId}\n products={products}\n roomFor={roomFor}\n lastImport={lastImport}\n onCreated={onCatalogCreated}\n />\n <ListFilterChips\n fields={PRODUCT_LIST_FIELDS}\n headers={PRODUCT_LIST_HEADERS}\n clauses={gridFilter.clauses}\n onChange={gridFilter.setClauses}\n options={PRODUCT_LIST_OPTIONS}\n />\n <ListQueryNotices\n refused={listQueryRefusals(plan.refused, {\n fields: PRODUCT_LIST_FIELDS,\n headers: PRODUCT_LIST_HEADERS,\n options: PRODUCT_LIST_OPTIONS,\n })}\n notices={[...plan.notices, ...columnSort.notices]}\n />\n <ListTable\n aria-label=\"Products\"\n rows={columnSort.rows}\n columns={productColumns}\n loading={productsStatus === 'loading'}\n // One page of the query, turned by the footer below: the grid\n // neither slices, filters nor sorts it.\n hideFooter\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n // Every header order is the query's, or the page's (`columnSort`).\n columnSort={columnSort}\n initialState={{\n columns: {\n columnVisibilityModel: hiddenFilterVisibility(\n PRODUCT_LIST_FIELDS,\n PRODUCT_VISIBLE_COLUMNS,\n ),\n },\n }}\n noRowsLabel={\n filtering ? 'No products match these filters' : 'No products yet'\n }\n noRowsDescription={\n filtering\n ? undefined\n : 'Build your catalog: add a product, then drop commerce ' +\n 'blocks on any page in Besigner.'\n }\n noRowsAction={\n filtering ? undefined : (\n <Stack direction=\"row\" spacing={1}>\n {createZone}\n <Button variant=\"contained\" disabled={!productQuota} onClick={startAddProduct}>\n {'Add your first product'}\n </Button>\n </Stack>\n )\n }\n />\n {products.length === 0 && page === 0 && productsStatus !== 'loading' ? null : (\n /* The count line is the PAGE's. `hasMore` is a fact from the probe\n row; the site's total is `productCount` above, which counts the\n whole catalog rather than the matches, so it is not this. */\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={products.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n )}\n </Stack>\n <ProductLabelsDialog product={labelsFor} onClose={() => setLabelsFor(null)} />\n <Dialog\n open={Boolean(keysFor)}\n onClose={() => setKeysFor(null)}\n maxWidth=\"sm\"\n fullWidth\n >\n <DialogTitle>{`License keys — ${keysFor?.name ?? ''}`}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n {(() => {\n // The query already asked for this product's keys, so no second\n // filter here: narrowing a window that was already narrowed is how\n // a reader comes to believe the count covers the pool.\n const productKeys = (keyDocs ?? []).slice(0, KEY_POOL_CEILING)\n const available = productKeys.filter(\n (key: any) => !key.assignedAtMs,\n )\n // RETIRED KEYS ARE COUNTED SEPARATELY (AGL-2454). A refund retires\n // the key rather than returning it to the pool — the buyer already\n // holds the string, so reissuing it would give two people one\n // working key — and without this line the merchant simply watches\n // \"available\" fall with nothing to explain where the key went.\n const retired = productKeys.filter((key: any) => key.revokedAtMs)\n const assigned =\n productKeys.length - available.length - retired.length\n return (\n <>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ mt: 1 }}>\n {`${available.length} available · ${assigned} assigned` +\n (retired.length\n ? ` · ${retired.length} retired (refunded or revoked — not reissued)`\n : '') +\n (keyPoolTruncated\n ? `, of the ${KEY_POOL_CEILING} keys read`\n : '') +\n '. Keys deliver automatically on purchase (receipt + account).'}\n </Typography>\n {available.slice(0, 8).map((key: any) => (\n <Stack\n key={key.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"caption\" sx={{ flex: 1, fontFamily: 'monospace' }} noWrap>\n {key.key}\n </Typography>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() =>\n updateDoc(\n doc(firestore, 'hosts', hostId, 'licenseKeys', key.$id),\n { revokedAtMs: Date.now(), assignedAtMs: Date.now() },\n )\n }\n >\n {'Revoke'}\n </Button>\n </Stack>\n ))}\n <TextField\n label=\"Add keys (one per line)\"\n value={keysText}\n onChange={(event) => setKeysText(event.target.value)}\n size=\"small\"\n multiline\n minRows={3}\n />\n </>\n )\n })()}\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setKeysFor(null)}>{'Close'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!keysText.trim()}\n onClick={async () => {\n const keys = keysText\n .split('\\n')\n .map((key) => key.trim())\n .filter(Boolean)\n .slice(0, 200)\n for (const key of keys) {\n await addDoc(\n collection(firestore, 'hosts', hostId, 'licenseKeys'),\n {\n productId: keysFor!.$id,\n key,\n assignedAtMs: null,\n createdAtMs: Date.now(),\n },\n )\n }\n setKeysText('')\n enqueueSnackbar(`Added ${keys.length} keys`, {\n variant: 'success',\n persist: false,\n })\n }}\n >\n {'Add keys'}\n </Button>\n </DialogActions>\n </Dialog>\n <Dialog\n open={Boolean(adjusting)}\n onClose={() => setAdjusting(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>{`Adjust stock — ${adjusting?.product.name ?? ''}`}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <TextField\n label=\"Variant\"\n value={adjusting?.variantId ?? ''}\n onChange={(event) =>\n setAdjusting((prev) =>\n prev ? { ...prev, variantId: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n sx={{ mt: 1 }}\n >\n {(adjusting?.product.variants ?? [])\n .filter((variant) => variant.inventory != null)\n .map((variant) => (\n <MenuItem key={variant.id} value={variant.id}>\n {`${Object.values(variant.options ?? {}).join(' / ') || 'Default'} — ${variant.inventory} in stock${\n CommerceModel.heldVariantUnits(\n adjusting?.product,\n variant.id,\n nowMs,\n ) > 0\n ? `, ${CommerceModel.heldVariantUnits(\n adjusting?.product,\n variant.id,\n nowMs,\n )} reserved in checkout`\n : ''\n }`}\n </MenuItem>\n ))}\n </TextField>\n {(locationDocs?.length ?? 0) > 1 ? (\n <TextField\n label=\"Location\"\n value={adjusting?.locationId ?? ''}\n onChange={(event) =>\n setAdjusting((prev) =>\n prev ? { ...prev, locationId: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n >\n {(locationDocs ?? []).map((location: any) => (\n <MenuItem key={location.$id} value={location.$id}>\n {location.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n <TextField\n label=\"Change\"\n placeholder=\"+10 or -3\"\n value={adjusting?.delta ?? ''}\n onChange={(event) =>\n setAdjusting((prev) =>\n prev\n ? {\n ...prev,\n delta: event.target.value.replace(/[^0-9+-]/g, ''),\n }\n : prev,\n )\n }\n size=\"small\"\n />\n <TextField\n label=\"Reason\"\n value={adjusting?.reason ?? 'restock'}\n onChange={(event) =>\n setAdjusting((prev) =>\n prev\n ? {\n ...prev,\n reason: event.target\n .value as CommerceModel.InventoryAdjustmentReason,\n }\n : prev,\n )\n }\n size=\"small\"\n select\n >\n <MenuItem value=\"restock\">{'Restock'}</MenuItem>\n <MenuItem value=\"correction\">{'Correction'}</MenuItem>\n <MenuItem value=\"damage\">{'Damaged'}</MenuItem>\n <MenuItem value=\"refund\">{'Refund return'}</MenuItem>\n </TextField>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setAdjusting(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!Math.round(Number(adjusting?.delta))}\n onClick={handleAdjustSave}\n >\n {'Apply'}\n </Button>\n </DialogActions>\n </Dialog>\n <ProductEditorDialog\n key={editing?.$id ?? (creating ? 'new' : 'closed')}\n hostId={hostId}\n product={editing}\n // The editor is seeded from a row of THIS listener and replaces the\n // whole document, so the freshness verdict belongs here (AGL-1358) —\n // the dialog has no listener of its own to ask.\n seedFromCache={productsFromCache}\n seedUnreadable={productsStatus === 'error'}\n open={creating || editing !== null}\n onClose={() => {\n setEditing(null)\n setCreating(false)\n // The dialog reports no verdict, so a create and a cancel look\n // the same from here. Re-reading on both is one aggregate and\n // keeps the cap off the dialog's shoulders (AGL-1716).\n setProductCountEpoch((epoch) => epoch + 1)\n }}\n />\n </CardDisplay>\n )\n}\nProductsHubCard.displayName = 'ProductsHubCard'\n\nexport default ProductsHubCard\n"],"names":["TransferResumeImport","useConsoleWidgetSlot","Aglyn","CommerceModel","PRODUCT_LIST_BASE","PRODUCT_LIST_COLUMN_SORTS","PRODUCT_LIST_FIELDS","PRODUCT_LIST_HEADERS","PRODUCT_LIST_OPTIONS","PRODUCT_LIST_QUERY","PRODUCT_LIST_SELECT_FIELDS","productListRequestClauses","CardDisplay","useConfirmationContext","ListFilterChips","ListPagination","ListQueryNotices","listQueryRefusals","ListTable","listActionsColumn","QuotaReadoutComponent","hiddenFilterVisibility","listFilterGridColumns","useListColumnSort","useListGridFilter","useSnackbar","Timestamp","Alert","Box","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Tooltip","Typography","addDoc","collection","doc","documentId","limit","orderBy","query","updateDoc","where","getLiveCountFromServer","useCallback","useEffect","useMemo","useState","useFirestore","useUser","writeSiteWideChange","useFirestoreCollection","writeGuardedBySeed","useListQuery","useHostResourceApi","useOrgPlan","ProductLabelsDialog","ProductEditorDialog","productSlugLedger","productCollectionFields","ProductsHubZone","pluginDocsHelp","PRODUCTS_CREATE_ZONE","commerceListFilter","COMMERCE_PRODUCTS_TRANSFER","KEY_POOL_CEILING","PRODUCT_PAGE_SORTS","stock","row","productInventory","variants","length","PRODUCT_PAGE_SORT_HEADERS","STATUS_COLOR","active","draft","archived","PRODUCT_VISIBLE_COLUMNS","ProductsHubCard","props","hostId","CreateZone","firestore","data","user","enqueueSnackbar","createHostResource","org","ready","planReady","confirm","gridFilter","selectFields","filtering","clauses","searchWords","some","word","trim","askedSort","setAskedSort","editing","setEditing","creating","setCreating","adjusting","setAdjusting","keysFor","setKeysFor","labelsFor","setLabelsFor","keysText","setKeysText","lastImport","setLastImport","transfer","useTransferLauncher","rows","productDocs","productWindow","hasMore","page","setPage","pageSize","setPageSize","plan","status","productsStatus","fromCache","productsFromCache","declaration","request","search","base","sort","deps","idField","confirmDisappearances","keyDocs","$id","keyPoolTruncated","locationDocs","products","map","product","liftLegacyProduct","comingSoon","filter","productPriceMissing","columnSort","sorts","defaultSort","onSortChange","pageSorts","headers","nowMs","setNowMs","Date","now","anyHeld","heldProductUnits","undefined","timer","setInterval","clearInterval","productCountEpoch","setProductCountEpoch","serverProductCount","setServerProductCount","then","count","catch","loadedProductCount","productCount","productQuota","checkQuota","handleDuplicate","allowed","variant","persist","slug","claim","productDuplicateSlugBase","name","resource","productDuplicateFields","productDuplicateName","epoch","error","message","handleStatus","write","batch","update","updatedAtMs","updatedAt","handleDelete","confirmed","title","description","confirmationText","confirmationButtonProps","color","deletedAt","openImport","scope","onFinished","takeProductImport","createdProductIds","finished","productIds","key","jobId","options","openExport","label","value","handleAdjustSave","stockAdjustmentWrite","variantId","delta","Number","reason","locationId","verdict","subject","unreadable","ledger","ok","roomFor","onCatalogCreated","formatPrice","min","max","productPriceRange","formatStock","total","String","formatHeld","held","productColumns","field","headerName","flex","minWidth","renderCell","sx","noWrap","width","direction","spacing","alignItems","height","size","productSellsOnline","productIsSample","PRODUCT_SAMPLE_NOTE","span","component","valueGetter","_value","justifyContent","whiteSpace","onClick","type","find","inventory","id","location","isDefault","startAddProduct","createZone","slot","orgId","header","help","anchor","contentGutterX","contentGutterY","xs","sm","disabled","target","can","severity","used","noun","onCreated","fields","onChange","setClauses","refused","notices","aria-label","columns","loading","hideFooter","filterMode","filterModel","onFilterModelChange","quickFilter","initialState","columnVisibilityModel","noRowsLabel","noRowsDescription","noRowsAction","rowCount","onPageChange","onPageSizeChange","onClose","open","Boolean","maxWidth","fullWidth","display","flexDirection","gap","productKeys","slice","available","assignedAtMs","retired","revokedAtMs","assigned","mt","fontFamily","event","multiline","minRows","keys","split","productId","createdAtMs","prev","select","Object","values","join","heldVariantUnits","placeholder","replace","Math","round","seedFromCache","seedUnreadable","displayName"],"mappings":"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"}
@@ -3,10 +3,20 @@ import * as CommerceModel from '../../model';
3
3
  export declare function seenAgo(atMs: number | undefined, nowMs: number): string;
4
4
  /** Where each brand's poll URL goes, step by step. */
5
5
  export declare function printerSetupSteps(brand: CommerceModel.PrinterBrand): string[];
6
+ /**
7
+ * Why the viewer may not do a printer action (AGL-4047), by the register key
8
+ * it needs: setting printers up (`posDevices`) and opening the drawer without
9
+ * a sale (`posCashDrawer`). Absent: allowed.
10
+ */
11
+ export interface RegisterHardwareBlocked {
12
+ devices?: string;
13
+ drawer?: string;
14
+ }
6
15
  export interface RegisterHardwareCardProps {
7
16
  hostId: string;
8
17
  registerId: string;
9
18
  registerName: string;
19
+ blocked?: RegisterHardwareBlocked;
10
20
  }
11
21
  /**
12
22
  * The register's other devices, read-only: the Stripe Terminal card readers
@@ -29,5 +39,6 @@ export declare namespace RegisterHardwareCard {
29
39
  /** One Hardware card per register on the site. */
30
40
  export declare function RegisterHardwareCards(props: {
31
41
  hostId: string;
42
+ blocked?: RegisterHardwareBlocked;
32
43
  }): import("react").JSX.Element;
33
44
  export default RegisterHardwareCards;
@@ -19,6 +19,7 @@ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_wi
19
19
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
20
20
  import * as CommerceModel from "../../model/index.js";
21
21
  import { CardDisplay, useConfirmationContext } from "@aglyn/shared-ui-jsx";
22
+ import { BlockedControl } from "@aglyn/shared-ui-jsx/components/blocked-control.component";
22
23
  import { useSnackbar } from "@aglyn/shared-ui-snackstack";
23
24
  import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
24
25
  import { pluginDocsHelp } from "@aglyn/aglyn";
@@ -285,8 +286,10 @@ function PollUrlPanel(props) {
285
286
  });
286
287
  }
287
288
  function PrinterRowView(props) {
288
- var _printer_status;
289
+ var _props_blocked, _props_blocked1, _printer_status;
289
290
  const { printer, now, call, onSettings } = props;
291
+ const devices = (_props_blocked = props.blocked) == null ? void 0 : _props_blocked.devices;
292
+ const drawer = (_props_blocked1 = props.blocked) == null ? void 0 : _props_blocked1.drawer;
290
293
  const { enqueueSnackbar } = useSnackbar();
291
294
  const { confirm } = useConfirmationContext();
292
295
  const state = CommerceModel.printerDisplayState(printer.status, now);
@@ -352,26 +355,42 @@ function PrinterRowView(props) {
352
355
  color: STATE_COLOR[state],
353
356
  label: label
354
357
  }),
355
- /*#__PURE__*/ _jsx(Button, {
356
- size: "small",
357
- onClick: ()=>send('test'),
358
- children: 'Test print'
358
+ /*#__PURE__*/ _jsx(BlockedControl, {
359
+ blocked: Boolean(devices),
360
+ reason: devices != null ? devices : '',
361
+ children: /*#__PURE__*/ _jsx(Button, {
362
+ size: "small",
363
+ onClick: ()=>send('test'),
364
+ children: 'Test print'
365
+ })
359
366
  }),
360
- printer.kickDrawer ? /*#__PURE__*/ _jsx(Button, {
361
- size: "small",
362
- onClick: ()=>send('drawer'),
363
- children: 'Open drawer'
367
+ printer.kickDrawer ? /*#__PURE__*/ _jsx(BlockedControl, {
368
+ blocked: Boolean(drawer),
369
+ reason: drawer != null ? drawer : '',
370
+ children: /*#__PURE__*/ _jsx(Button, {
371
+ size: "small",
372
+ onClick: ()=>send('drawer'),
373
+ children: 'Open drawer'
374
+ })
364
375
  }) : null,
365
- /*#__PURE__*/ _jsx(Button, {
366
- size: "small",
367
- onClick: ()=>onSettings(printer),
368
- children: 'Settings'
376
+ /*#__PURE__*/ _jsx(BlockedControl, {
377
+ blocked: Boolean(devices),
378
+ reason: devices != null ? devices : '',
379
+ children: /*#__PURE__*/ _jsx(Button, {
380
+ size: "small",
381
+ onClick: ()=>onSettings(printer),
382
+ children: 'Settings'
383
+ })
369
384
  }),
370
- /*#__PURE__*/ _jsx(Button, {
371
- size: "small",
372
- color: "error",
373
- onClick: remove,
374
- children: 'Remove'
385
+ /*#__PURE__*/ _jsx(BlockedControl, {
386
+ blocked: Boolean(devices),
387
+ reason: devices != null ? devices : '',
388
+ children: /*#__PURE__*/ _jsx(Button, {
389
+ size: "small",
390
+ color: "error",
391
+ onClick: remove,
392
+ children: 'Remove'
393
+ })
375
394
  })
376
395
  ]
377
396
  }),
@@ -519,7 +538,9 @@ function PrinterRowView(props) {
519
538
  RegisterOtherDevices.displayName = 'RegisterOtherDevices';
520
539
  export function RegisterHardwareCard(props) {
521
540
  var _ref;
541
+ var _props_blocked;
522
542
  const { hostId, registerId, registerName } = props;
543
+ const devicesBlocked = (_props_blocked = props.blocked) == null ? void 0 : _props_blocked.devices;
523
544
  const firestore = useFirestore();
524
545
  const call = usePrintersApi(hostId);
525
546
  const now = useNow(15000);
@@ -617,11 +638,15 @@ export function RegisterHardwareCard(props) {
617
638
  anchor: '#receipt-printers'
618
639
  }),
619
640
  HeaderProps: {
620
- action: /*#__PURE__*/ _jsx(Button, {
621
- size: "small",
622
- onClick: openAdd,
623
- disabled: printers.length >= 4,
624
- children: 'Add printer'
641
+ action: /*#__PURE__*/ _jsx(BlockedControl, {
642
+ blocked: Boolean(devicesBlocked),
643
+ reason: devicesBlocked != null ? devicesBlocked : '',
644
+ children: /*#__PURE__*/ _jsx(Button, {
645
+ size: "small",
646
+ onClick: openAdd,
647
+ disabled: printers.length >= 4,
648
+ children: 'Add printer'
649
+ })
625
650
  })
626
651
  },
627
652
  contentGutterX: true,
@@ -638,12 +663,14 @@ export function RegisterHardwareCard(props) {
638
663
  variant: "body2",
639
664
  color: "text.secondary",
640
665
  children: 'Pair a Star CloudPRNT or Epson Server Direct Print printer to print receipts from this register ' + 'on any device, with no driver, and to open its cash drawer on cash sales.'
641
- }) : printers.map((printer)=>/*#__PURE__*/ _jsx(PrinterRowView, {
666
+ }) : printers.map((printer)=>/*#__PURE__*/ _jsx(PrinterRowView, _extends({
642
667
  printer: printer,
643
668
  now: now,
644
669
  call: call,
645
670
  onSettings: openSettings
646
- }, printer.$id)),
671
+ }, props.blocked ? {
672
+ blocked: props.blocked
673
+ } : {}), printer.$id)),
647
674
  jobs.length ? /*#__PURE__*/ _jsxs(_Fragment, {
648
675
  children: [
649
676
  /*#__PURE__*/ _jsx(Divider, {}),
@@ -814,11 +841,13 @@ RegisterHardwareCard.displayName = 'RegisterHardwareCard';
814
841
  if (!registers.length) return null;
815
842
  return /*#__PURE__*/ _jsx(Stack, {
816
843
  spacing: 3,
817
- children: registers.map((register)=>/*#__PURE__*/ _jsx(RegisterHardwareCard, {
844
+ children: registers.map((register)=>/*#__PURE__*/ _jsx(RegisterHardwareCard, _extends({
818
845
  hostId: hostId,
819
846
  registerId: register.$id,
820
847
  registerName: register.name || 'Register'
821
- }, register.$id))
848
+ }, props.blocked ? {
849
+ blocked: props.blocked
850
+ } : {}), register.$id))
822
851
  });
823
852
  }
824
853
  export default RegisterHardwareCards;