@aglyn/plugins-commerce 1.0.0-beta.229 → 1.0.0-beta.231

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 (590) hide show
  1. package/package.json +13 -13
  2. package/src/index.d.ts +0 -1
  3. package/src/index.js +0 -1
  4. package/src/index.js.map +1 -1
  5. package/src/lib/barcode/barcode-decode.d.ts +71 -0
  6. package/src/lib/barcode/barcode-decode.js +498 -0
  7. package/src/lib/barcode/barcode-decode.js.map +1 -0
  8. package/src/lib/barcode/barcode-encode.d.ts +44 -0
  9. package/src/lib/barcode/barcode-encode.js +138 -0
  10. package/src/lib/barcode/barcode-encode.js.map +1 -0
  11. package/src/lib/barcode/barcode-scanner.component.d.ts +23 -0
  12. package/src/lib/barcode/barcode-scanner.component.js +260 -0
  13. package/src/lib/barcode/barcode-scanner.component.js.map +1 -0
  14. package/src/lib/barcode/scan-button.component.d.ts +10 -0
  15. package/src/lib/barcode/scan-button.component.js +62 -0
  16. package/src/lib/barcode/scan-button.component.js.map +1 -0
  17. package/src/lib/barcode/scanner-wedge.d.ts +35 -0
  18. package/src/lib/barcode/scanner-wedge.js +108 -0
  19. package/src/lib/barcode/scanner-wedge.js.map +1 -0
  20. package/src/lib/components/account.js +10 -1
  21. package/src/lib/components/account.js.map +1 -1
  22. package/src/lib/components/cart-extras.d.ts +45 -0
  23. package/src/lib/components/cart-extras.js +127 -0
  24. package/src/lib/components/cart-extras.js.map +1 -0
  25. package/src/lib/components/cart.js +82 -15
  26. package/src/lib/components/cart.js.map +1 -1
  27. package/src/lib/components/checkout-return-notice.d.ts +26 -0
  28. package/src/lib/components/checkout-return-notice.js +48 -0
  29. package/src/lib/components/checkout-return-notice.js.map +1 -0
  30. package/src/lib/components/commerce-console-page.js +124 -0
  31. package/src/lib/components/commerce-console-page.js.map +1 -1
  32. package/src/lib/components/commerce-console-sections.d.ts +1 -1
  33. package/src/lib/components/commerce-console-sections.js +4 -0
  34. package/src/lib/components/commerce-console-sections.js.map +1 -1
  35. package/src/lib/components/console/bulk-fulfill.d.ts +44 -0
  36. package/src/lib/components/console/bulk-fulfill.js +84 -0
  37. package/src/lib/components/console/bulk-fulfill.js.map +1 -0
  38. package/src/lib/components/console/customer-notifications-card.component.d.ts +19 -0
  39. package/src/lib/components/console/customer-notifications-card.component.js +149 -0
  40. package/src/lib/components/console/customer-notifications-card.component.js.map +1 -0
  41. package/src/lib/components/console/host-orders-card.component.js +84 -5
  42. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  43. package/src/lib/components/console/locations-card.component.js +164 -57
  44. package/src/lib/components/console/locations-card.component.js.map +1 -1
  45. package/src/lib/components/console/order-detail-dialog.component.js +209 -61
  46. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
  47. package/src/lib/components/console/order-fulfillment-panel.component.d.ts +48 -0
  48. package/src/lib/components/console/order-fulfillment-panel.component.js +542 -0
  49. package/src/lib/components/console/order-fulfillment-panel.component.js.map +1 -0
  50. package/src/lib/components/console/order-invoice.component.d.ts +33 -0
  51. package/src/lib/components/console/order-invoice.component.js +81 -0
  52. package/src/lib/components/console/order-invoice.component.js.map +1 -0
  53. package/src/lib/components/console/order-receipt-resend.component.d.ts +21 -0
  54. package/src/lib/components/console/order-receipt-resend.component.js +203 -0
  55. package/src/lib/components/console/order-receipt-resend.component.js.map +1 -0
  56. package/src/lib/components/console/order-returns.component.d.ts +36 -0
  57. package/src/lib/components/console/order-returns.component.js +142 -0
  58. package/src/lib/components/console/order-returns.component.js.map +1 -0
  59. package/src/lib/components/console/order-shipping-actions.component.d.ts +54 -0
  60. package/src/lib/components/console/order-shipping-actions.component.js +149 -0
  61. package/src/lib/components/console/order-shipping-actions.component.js.map +1 -0
  62. package/src/lib/components/console/order-webhooks-card.component.d.ts +50 -0
  63. package/src/lib/components/console/order-webhooks-card.component.js +623 -0
  64. package/src/lib/components/console/order-webhooks-card.component.js.map +1 -0
  65. package/src/lib/components/console/order-zones.d.ts +155 -0
  66. package/src/lib/components/console/order-zones.js +40 -0
  67. package/src/lib/components/console/order-zones.js.map +1 -0
  68. package/src/lib/components/console/payment-methods-card.component.d.ts +67 -0
  69. package/src/lib/components/console/payment-methods-card.component.js +395 -0
  70. package/src/lib/components/console/payment-methods-card.component.js.map +1 -0
  71. package/src/lib/components/console/pos/pos-api.d.ts +124 -0
  72. package/src/lib/components/console/pos/pos-api.js +101 -0
  73. package/src/lib/components/console/pos/pos-api.js.map +1 -0
  74. package/src/lib/components/console/pos/pos-cart-panel.component.d.ts +36 -0
  75. package/src/lib/components/console/pos/pos-cart-panel.component.js +160 -0
  76. package/src/lib/components/console/pos/pos-cart-panel.component.js.map +1 -0
  77. package/src/lib/components/console/pos/pos-item-dialog.component.d.ts +39 -0
  78. package/src/lib/components/console/pos/pos-item-dialog.component.js +352 -0
  79. package/src/lib/components/console/pos/pos-item-dialog.component.js.map +1 -0
  80. package/src/lib/components/console/pos/pos-product-grid.component.d.ts +47 -0
  81. package/src/lib/components/console/pos/pos-product-grid.component.js +272 -0
  82. package/src/lib/components/console/pos/pos-product-grid.component.js.map +1 -0
  83. package/src/lib/components/console/pos/pos-receipt-panel.component.d.ts +27 -0
  84. package/src/lib/components/console/pos/pos-receipt-panel.component.js +282 -0
  85. package/src/lib/components/console/pos/pos-receipt-panel.component.js.map +1 -0
  86. package/src/lib/components/console/pos/pos-tender-dialogs.component.d.ts +53 -0
  87. package/src/lib/components/console/pos/pos-tender-dialogs.component.js +445 -0
  88. package/src/lib/components/console/pos/pos-tender-dialogs.component.js.map +1 -0
  89. package/src/lib/components/console/pos/pos-tender-panel.component.d.ts +31 -0
  90. package/src/lib/components/console/pos/pos-tender-panel.component.js +640 -0
  91. package/src/lib/components/console/pos/pos-tender-panel.component.js.map +1 -0
  92. package/src/lib/components/console/pos/use-pos-display.d.ts +43 -0
  93. package/src/lib/components/console/pos/use-pos-display.js +161 -0
  94. package/src/lib/components/console/pos/use-pos-display.js.map +1 -0
  95. package/src/lib/components/console/pos-devices-card.component.d.ts +14 -0
  96. package/src/lib/components/console/pos-devices-card.component.js +546 -0
  97. package/src/lib/components/console/pos-devices-card.component.js.map +1 -0
  98. package/src/lib/components/console/pos-display/brand-screen.d.ts +33 -0
  99. package/src/lib/components/console/pos-display/brand-screen.js +85 -0
  100. package/src/lib/components/console/pos-display/brand-screen.js.map +1 -0
  101. package/src/lib/components/console/pos-display/cart-screen.d.ts +29 -0
  102. package/src/lib/components/console/pos-display/cart-screen.js +166 -0
  103. package/src/lib/components/console/pos-display/cart-screen.js.map +1 -0
  104. package/src/lib/components/console/pos-display/pairing-screen.d.ts +24 -0
  105. package/src/lib/components/console/pos-display/pairing-screen.js +127 -0
  106. package/src/lib/components/console/pos-display/pairing-screen.js.map +1 -0
  107. package/src/lib/components/console/pos-display/pos-display-api.d.ts +90 -0
  108. package/src/lib/components/console/pos-display/pos-display-api.js +140 -0
  109. package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -0
  110. package/src/lib/components/console/pos-display/receipt-screen.d.ts +35 -0
  111. package/src/lib/components/console/pos-display/receipt-screen.js +200 -0
  112. package/src/lib/components/console/pos-display/receipt-screen.js.map +1 -0
  113. package/src/lib/components/console/pos-display/tip-screen.d.ts +36 -0
  114. package/src/lib/components/console/pos-display/tip-screen.js +211 -0
  115. package/src/lib/components/console/pos-display/tip-screen.js.map +1 -0
  116. package/src/lib/components/console/pos-display-page.component.d.ts +16 -0
  117. package/src/lib/components/console/pos-display-page.component.js +407 -0
  118. package/src/lib/components/console/pos-display-page.component.js.map +1 -0
  119. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.d.ts +40 -0
  120. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js +289 -0
  121. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js.map +1 -0
  122. package/src/lib/components/console/pos-ops/pos-last-receipt.component.d.ts +34 -0
  123. package/src/lib/components/console/pos-ops/pos-last-receipt.component.js +61 -0
  124. package/src/lib/components/console/pos-ops/pos-last-receipt.component.js.map +1 -0
  125. package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +38 -0
  126. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +129 -0
  127. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -0
  128. package/src/lib/components/console/pos-ops/pos-ops-api.d.ts +37 -0
  129. package/src/lib/components/console/pos-ops/pos-ops-api.js +60 -0
  130. package/src/lib/components/console/pos-ops/pos-ops-api.js.map +1 -0
  131. package/src/lib/components/console/pos-ops/pos-pin-pad.component.d.ts +48 -0
  132. package/src/lib/components/console/pos-ops/pos-pin-pad.component.js +220 -0
  133. package/src/lib/components/console/pos-ops/pos-pin-pad.component.js.map +1 -0
  134. package/src/lib/components/console/pos-ops/pos-receipt-actions.component.d.ts +63 -0
  135. package/src/lib/components/console/pos-ops/pos-receipt-actions.component.js +186 -0
  136. package/src/lib/components/console/pos-ops/pos-receipt-actions.component.js.map +1 -0
  137. package/src/lib/components/console/pos-ops/pos-receipt.d.ts +39 -0
  138. package/src/lib/components/console/pos-ops/pos-receipt.js +182 -0
  139. package/src/lib/components/console/pos-ops/pos-receipt.js.map +1 -0
  140. package/src/lib/components/console/pos-ops/pos-return-dialog.component.d.ts +40 -0
  141. package/src/lib/components/console/pos-ops/pos-return-dialog.component.js +465 -0
  142. package/src/lib/components/console/pos-ops/pos-return-dialog.component.js.map +1 -0
  143. package/src/lib/components/console/pos-ops/pos-shift-history-card.component.d.ts +29 -0
  144. package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js +253 -0
  145. package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js.map +1 -0
  146. package/src/lib/components/console/pos-ops/pos-shift-panel.component.d.ts +34 -0
  147. package/src/lib/components/console/pos-ops/pos-shift-panel.component.js +494 -0
  148. package/src/lib/components/console/pos-ops/pos-shift-panel.component.js.map +1 -0
  149. package/src/lib/components/console/pos-ops/pos-shift-report.component.d.ts +37 -0
  150. package/src/lib/components/console/pos-ops/pos-shift-report.component.js +102 -0
  151. package/src/lib/components/console/pos-ops/pos-shift-report.component.js.map +1 -0
  152. package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.d.ts +30 -0
  153. package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.js +277 -0
  154. package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.js.map +1 -0
  155. package/src/lib/components/console/pos-ops/register-ops.d.ts +28 -0
  156. package/src/lib/components/console/pos-ops/register-ops.js +28 -0
  157. package/src/lib/components/console/pos-ops/register-ops.js.map +1 -0
  158. package/src/lib/components/console/pos-ops/use-pos-cashier.d.ts +46 -0
  159. package/src/lib/components/console/pos-ops/use-pos-cashier.js +128 -0
  160. package/src/lib/components/console/pos-ops/use-pos-cashier.js.map +1 -0
  161. package/src/lib/components/console/pos-ops/use-pos-sale-receipt.d.ts +36 -0
  162. package/src/lib/components/console/pos-ops/use-pos-sale-receipt.js +64 -0
  163. package/src/lib/components/console/pos-ops/use-pos-sale-receipt.js.map +1 -0
  164. package/src/lib/components/console/pos-page.component.d.ts +13 -5
  165. package/src/lib/components/console/pos-page.component.js +729 -657
  166. package/src/lib/components/console/pos-page.component.js.map +1 -1
  167. package/src/lib/components/console/product-editor-dialog.component.js +51 -7
  168. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  169. package/src/lib/components/console/product-labels-dialog.component.d.ts +13 -0
  170. package/src/lib/components/console/product-labels-dialog.component.js +197 -0
  171. package/src/lib/components/console/product-labels-dialog.component.js.map +1 -0
  172. package/src/lib/components/console/product-register-fields.component.d.ts +19 -0
  173. package/src/lib/components/console/product-register-fields.component.js +241 -0
  174. package/src/lib/components/console/product-register-fields.component.js.map +1 -0
  175. package/src/lib/components/console/product-zones.d.ts +58 -2
  176. package/src/lib/components/console/product-zones.js +10 -2
  177. package/src/lib/components/console/product-zones.js.map +1 -1
  178. package/src/lib/components/console/products-hub-card.component.js +51 -11
  179. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  180. package/src/lib/components/console/register-hardware-card.component.d.ts +33 -0
  181. package/src/lib/components/console/register-hardware-card.component.js +826 -0
  182. package/src/lib/components/console/register-hardware-card.component.js.map +1 -0
  183. package/src/lib/components/console/registers-card.component.js +24 -5
  184. package/src/lib/components/console/registers-card.component.js.map +1 -1
  185. package/src/lib/components/console/return-actions.d.ts +38 -0
  186. package/src/lib/components/console/return-actions.js +76 -0
  187. package/src/lib/components/console/return-actions.js.map +1 -0
  188. package/src/lib/components/console/return-detail-dialog.component.d.ts +34 -0
  189. package/src/lib/components/console/return-detail-dialog.component.js +718 -0
  190. package/src/lib/components/console/return-detail-dialog.component.js.map +1 -0
  191. package/src/lib/components/console/return-settings-card.component.d.ts +34 -0
  192. package/src/lib/components/console/return-settings-card.component.js +195 -0
  193. package/src/lib/components/console/return-settings-card.component.js.map +1 -0
  194. package/src/lib/components/console/return-zones.d.ts +87 -0
  195. package/src/lib/components/console/return-zones.js +31 -0
  196. package/src/lib/components/console/return-zones.js.map +1 -0
  197. package/src/lib/components/console/returns-card.component.d.ts +45 -0
  198. package/src/lib/components/console/returns-card.component.js +159 -0
  199. package/src/lib/components/console/returns-card.component.js.map +1 -0
  200. package/src/lib/components/console/shipping-settings-card.component.js +161 -5
  201. package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
  202. package/src/lib/components/console/shippingeasy-card.component.d.ts +59 -0
  203. package/src/lib/components/console/shippingeasy-card.component.js +378 -0
  204. package/src/lib/components/console/shippingeasy-card.component.js.map +1 -0
  205. package/src/lib/components/console/shipstation-card.component.d.ts +35 -0
  206. package/src/lib/components/console/shipstation-card.component.js +345 -0
  207. package/src/lib/components/console/shipstation-card.component.js.map +1 -0
  208. package/src/lib/components/console/start-return-dialog.component.d.ts +40 -0
  209. package/src/lib/components/console/start-return-dialog.component.js +243 -0
  210. package/src/lib/components/console/start-return-dialog.component.js.map +1 -0
  211. package/src/lib/components/console/stock-movements-card.component.js +2 -1
  212. package/src/lib/components/console/stock-movements-card.component.js.map +1 -1
  213. package/src/lib/components/console/store-settings-card.component.js +2 -66
  214. package/src/lib/components/console/store-settings-card.component.js.map +1 -1
  215. package/src/lib/components/console/store-zones.d.ts +42 -0
  216. package/src/lib/components/console/store-zones.js +39 -0
  217. package/src/lib/components/console/store-zones.js.map +1 -0
  218. package/src/lib/components/order-status.d.ts +36 -0
  219. package/src/lib/components/order-status.js +502 -0
  220. package/src/lib/components/order-status.js.map +1 -0
  221. package/src/lib/components/product-detail.js +44 -2
  222. package/src/lib/components/product-detail.js.map +1 -1
  223. package/src/lib/components/return-request.d.ts +32 -0
  224. package/src/lib/components/return-request.js +576 -0
  225. package/src/lib/components/return-request.js.map +1 -0
  226. package/src/lib/components/storefront-payment-appearance.d.ts +32 -0
  227. package/src/lib/components/storefront-payment-appearance.js +114 -0
  228. package/src/lib/components/storefront-payment-appearance.js.map +1 -0
  229. package/src/lib/components/storefront-payment-element.d.ts +57 -1
  230. package/src/lib/components/storefront-payment-element.js +332 -33
  231. package/src/lib/components/storefront-payment-element.js.map +1 -1
  232. package/src/lib/constants/order-status.d.ts +22 -0
  233. package/src/lib/constants/order-status.js +22 -0
  234. package/src/lib/constants/order-status.js.map +1 -0
  235. package/src/lib/constants/return-request.d.ts +27 -0
  236. package/src/lib/constants/return-request.js +32 -0
  237. package/src/lib/constants/return-request.js.map +1 -0
  238. package/src/lib/constants/stock-movement-query.js +2 -1
  239. package/src/lib/constants/stock-movement-query.js.map +1 -1
  240. package/src/lib/declarations.console-server.js +9 -0
  241. package/src/lib/declarations.console-server.js.map +1 -1
  242. package/src/lib/declarations.server.d.ts +2 -1
  243. package/src/lib/declarations.server.js +47 -1
  244. package/src/lib/declarations.server.js.map +1 -1
  245. package/src/lib/model/buyer-notifications.d.ts +59 -0
  246. package/src/lib/model/buyer-notifications.js +97 -0
  247. package/src/lib/model/buyer-notifications.js.map +1 -0
  248. package/src/lib/model/commerce-orders.d.ts +127 -1
  249. package/src/lib/model/commerce-orders.js +24 -15
  250. package/src/lib/model/commerce-orders.js.map +1 -1
  251. package/src/lib/model/commerce-payment-methods.d.ts +146 -0
  252. package/src/lib/model/commerce-payment-methods.js +247 -0
  253. package/src/lib/model/commerce-payment-methods.js.map +1 -0
  254. package/src/lib/model/commerce-pos-ops.d.ts +437 -0
  255. package/src/lib/model/commerce-pos-ops.js +721 -0
  256. package/src/lib/model/commerce-pos-ops.js.map +1 -0
  257. package/src/lib/model/commerce-pos.d.ts +263 -0
  258. package/src/lib/model/commerce-pos.js +375 -0
  259. package/src/lib/model/commerce-pos.js.map +1 -0
  260. package/src/lib/model/commerce-printers.d.ts +211 -0
  261. package/src/lib/model/commerce-printers.js +181 -0
  262. package/src/lib/model/commerce-printers.js.map +1 -0
  263. package/src/lib/model/commerce-receipt.d.ts +97 -0
  264. package/src/lib/model/commerce-receipt.js +180 -0
  265. package/src/lib/model/commerce-receipt.js.map +1 -0
  266. package/src/lib/model/commerce-returns.d.ts +207 -0
  267. package/src/lib/model/commerce-returns.js +300 -0
  268. package/src/lib/model/commerce-returns.js.map +1 -0
  269. package/src/lib/model/commerce-shipping.d.ts +44 -1
  270. package/src/lib/model/commerce-shipping.js +53 -9
  271. package/src/lib/model/commerce-shipping.js.map +1 -1
  272. package/src/lib/model/commerce-tax-engine.d.ts +90 -0
  273. package/src/lib/model/commerce-tax-engine.js +129 -0
  274. package/src/lib/model/commerce-tax-engine.js.map +1 -0
  275. package/src/lib/model/commerce.d.ts +88 -2
  276. package/src/lib/model/commerce.js +89 -1
  277. package/src/lib/model/commerce.js.map +1 -1
  278. package/src/lib/model/index.d.ts +15 -0
  279. package/src/lib/model/index.js +15 -0
  280. package/src/lib/model/index.js.map +1 -1
  281. package/src/lib/model/order-events.d.ts +154 -0
  282. package/src/lib/model/order-events.js +153 -0
  283. package/src/lib/model/order-events.js.map +1 -0
  284. package/src/lib/model/order-fulfillment.d.ts +117 -0
  285. package/src/lib/model/order-fulfillment.js +217 -0
  286. package/src/lib/model/order-fulfillment.js.map +1 -0
  287. package/src/lib/model/order-history.d.ts +58 -0
  288. package/src/lib/model/order-history.js +22 -0
  289. package/src/lib/model/order-history.js.map +1 -0
  290. package/src/lib/model/order-list-fields.d.ts +9 -1
  291. package/src/lib/model/order-list-fields.js +7 -1
  292. package/src/lib/model/order-list-fields.js.map +1 -1
  293. package/src/lib/model/order-shipping-export.d.ts +91 -0
  294. package/src/lib/model/order-shipping-export.js +192 -0
  295. package/src/lib/model/order-shipping-export.js.map +1 -0
  296. package/src/lib/model/order-status-view.d.ts +103 -0
  297. package/src/lib/model/order-status-view.js +156 -0
  298. package/src/lib/model/order-status-view.js.map +1 -0
  299. package/src/lib/model/order-webhooks.d.ts +142 -0
  300. package/src/lib/model/order-webhooks.js +91 -0
  301. package/src/lib/model/order-webhooks.js.map +1 -0
  302. package/src/lib/model/product-channel.d.ts +56 -0
  303. package/src/lib/model/product-channel.js +86 -0
  304. package/src/lib/model/product-channel.js.map +1 -0
  305. package/src/lib/model/product-modifiers.d.ts +80 -0
  306. package/src/lib/model/product-modifiers.js +140 -0
  307. package/src/lib/model/product-modifiers.js.map +1 -0
  308. package/src/lib/model/shippingeasy.d.ts +94 -0
  309. package/src/lib/model/shippingeasy.js +267 -0
  310. package/src/lib/model/shippingeasy.js.map +1 -0
  311. package/src/lib/model/shipstation.d.ts +125 -0
  312. package/src/lib/model/shipstation.js +399 -0
  313. package/src/lib/model/shipstation.js.map +1 -0
  314. package/src/lib/model/smart-collection-reads.d.ts +32 -0
  315. package/src/lib/model/smart-collection-reads.js +63 -0
  316. package/src/lib/model/smart-collection-reads.js.map +1 -0
  317. package/src/lib/model/tracking-url.d.ts +74 -0
  318. package/src/lib/model/tracking-url.js +159 -0
  319. package/src/lib/model/tracking-url.js.map +1 -0
  320. package/src/lib/plugin-config.d.ts +13 -0
  321. package/src/lib/plugin-config.js +77 -5
  322. package/src/lib/plugin-config.js.map +1 -1
  323. package/src/lib/plugin.js +23 -0
  324. package/src/lib/plugin.js.map +1 -1
  325. package/src/lib/pos-ops-config.d.ts +39 -0
  326. package/src/lib/pos-ops-config.js +88 -0
  327. package/src/lib/pos-ops-config.js.map +1 -0
  328. package/src/lib/printing/print-document.d.ts +106 -0
  329. package/src/lib/printing/print-document.js +464 -0
  330. package/src/lib/printing/print-document.js.map +1 -0
  331. package/src/lib/printing/product-labels.d.ts +58 -0
  332. package/src/lib/printing/product-labels.js +116 -0
  333. package/src/lib/printing/product-labels.js.map +1 -0
  334. package/src/lib/printing/render-epson.d.ts +57 -0
  335. package/src/lib/printing/render-epson.js +135 -0
  336. package/src/lib/printing/render-epson.js.map +1 -0
  337. package/src/lib/printing/render-star.d.ts +45 -0
  338. package/src/lib/printing/render-star.js +100 -0
  339. package/src/lib/printing/render-star.js.map +1 -0
  340. package/src/lib/printing/render-text.d.ts +25 -0
  341. package/src/lib/printing/render-text.js +53 -0
  342. package/src/lib/printing/render-text.js.map +1 -0
  343. package/src/lib/server/api-v1/openapi.js +27 -4
  344. package/src/lib/server/api-v1/openapi.js.map +1 -1
  345. package/src/lib/server/api-v1/order-view.d.ts +67 -0
  346. package/src/lib/server/api-v1/order-view.js +120 -0
  347. package/src/lib/server/api-v1/order-view.js.map +1 -0
  348. package/src/lib/server/api-v1/orders-and-products.js +58 -65
  349. package/src/lib/server/api-v1/orders-and-products.js.map +1 -1
  350. package/src/lib/server/billing-webhook.js +135 -28
  351. package/src/lib/server/billing-webhook.js.map +1 -1
  352. package/src/lib/server/cancel-order.js +29 -0
  353. package/src/lib/server/cancel-order.js.map +1 -1
  354. package/src/lib/server/carrier-rates-availability.d.ts +26 -0
  355. package/src/lib/server/carrier-rates-availability.js +76 -0
  356. package/src/lib/server/carrier-rates-availability.js.map +1 -0
  357. package/src/lib/server/carrier-shipping.d.ts +84 -0
  358. package/src/lib/server/carrier-shipping.js +135 -0
  359. package/src/lib/server/carrier-shipping.js.map +1 -0
  360. package/src/lib/server/cart-checkout.d.ts +2 -0
  361. package/src/lib/server/cart-checkout.js +226 -22
  362. package/src/lib/server/cart-checkout.js.map +1 -1
  363. package/src/lib/server/checkout-extras.d.ts +64 -0
  364. package/src/lib/server/checkout-extras.js +157 -0
  365. package/src/lib/server/checkout-extras.js.map +1 -0
  366. package/src/lib/server/{feed.d.ts → checkout-status.d.ts} +2 -7
  367. package/src/lib/server/checkout-status.js +111 -0
  368. package/src/lib/server/checkout-status.js.map +1 -0
  369. package/src/lib/server/checkout.js +86 -9
  370. package/src/lib/server/checkout.js.map +1 -1
  371. package/src/lib/server/draft-order.js +34 -5
  372. package/src/lib/server/draft-order.js.map +1 -1
  373. package/src/lib/server/fulfill-order.d.ts +127 -10
  374. package/src/lib/server/fulfill-order.js +451 -49
  375. package/src/lib/server/fulfill-order.js.map +1 -1
  376. package/src/lib/server/legacy-feed.d.ts +28 -0
  377. package/src/lib/server/legacy-feed.js +52 -0
  378. package/src/lib/server/legacy-feed.js.map +1 -0
  379. package/src/lib/server/native-checkout.d.ts +34 -15
  380. package/src/lib/server/native-checkout.js +32 -17
  381. package/src/lib/server/native-checkout.js.map +1 -1
  382. package/src/lib/server/order-event-host-emit.d.ts +23 -0
  383. package/src/lib/server/order-event-host-emit.js +30 -0
  384. package/src/lib/server/order-event-host-emit.js.map +1 -0
  385. package/src/lib/server/order-event-triggers.d.ts +37 -0
  386. package/src/lib/server/order-event-triggers.js +119 -0
  387. package/src/lib/server/order-event-triggers.js.map +1 -0
  388. package/src/lib/server/order-events.d.ts +62 -0
  389. package/src/lib/server/order-events.js +109 -0
  390. package/src/lib/server/order-events.js.map +1 -0
  391. package/src/lib/server/order-history.d.ts +23 -0
  392. package/src/lib/server/order-history.js +65 -0
  393. package/src/lib/server/order-history.js.map +1 -0
  394. package/src/lib/server/order-notifications.d.ts +185 -0
  395. package/src/lib/server/order-notifications.js +567 -0
  396. package/src/lib/server/order-notifications.js.map +1 -0
  397. package/src/lib/server/order-receipt-send.d.ts +39 -0
  398. package/src/lib/server/order-receipt-send.js +151 -0
  399. package/src/lib/server/order-receipt-send.js.map +1 -0
  400. package/src/lib/server/order-status-token.d.ts +31 -0
  401. package/src/lib/server/order-status-token.js +75 -0
  402. package/src/lib/server/order-status-token.js.map +1 -0
  403. package/src/lib/server/order-status.d.ts +58 -0
  404. package/src/lib/server/order-status.js +141 -0
  405. package/src/lib/server/order-status.js.map +1 -0
  406. package/src/lib/server/order-webhooks.d.ts +108 -0
  407. package/src/lib/server/order-webhooks.js +622 -0
  408. package/src/lib/server/order-webhooks.js.map +1 -0
  409. package/src/lib/server/orders-shipping-prepare.d.ts +26 -0
  410. package/src/lib/server/orders-shipping-prepare.js +121 -0
  411. package/src/lib/server/orders-shipping-prepare.js.map +1 -0
  412. package/src/lib/server/payment-method-domains.d.ts +209 -0
  413. package/src/lib/server/payment-method-domains.js +444 -0
  414. package/src/lib/server/payment-method-domains.js.map +1 -0
  415. package/src/lib/server/payment-methods.d.ts +77 -0
  416. package/src/lib/server/payment-methods.js +284 -0
  417. package/src/lib/server/payment-methods.js.map +1 -0
  418. package/src/lib/server/pos-auth.d.ts +50 -0
  419. package/src/lib/server/pos-auth.js +121 -0
  420. package/src/lib/server/pos-auth.js.map +1 -0
  421. package/src/lib/server/pos-customer.d.ts +49 -0
  422. package/src/lib/server/pos-customer.js +257 -0
  423. package/src/lib/server/pos-customer.js.map +1 -0
  424. package/src/lib/server/pos-display.d.ts +57 -0
  425. package/src/lib/server/pos-display.js +419 -0
  426. package/src/lib/server/pos-display.js.map +1 -0
  427. package/src/lib/server/pos-fee-month.d.ts +34 -0
  428. package/src/lib/server/pos-fee-month.js +36 -0
  429. package/src/lib/server/pos-fee-month.js.map +1 -0
  430. package/src/lib/server/pos-ops-gate.d.ts +161 -0
  431. package/src/lib/server/pos-ops-gate.js +295 -0
  432. package/src/lib/server/pos-ops-gate.js.map +1 -0
  433. package/src/lib/server/pos-ops-routes.d.ts +22 -0
  434. package/src/lib/server/pos-ops-routes.js +36 -0
  435. package/src/lib/server/pos-ops-routes.js.map +1 -0
  436. package/src/lib/server/pos-order.d.ts +1 -18
  437. package/src/lib/server/pos-order.js +222 -42
  438. package/src/lib/server/pos-order.js.map +1 -1
  439. package/src/lib/server/pos-payment.d.ts +39 -0
  440. package/src/lib/server/pos-payment.js +922 -0
  441. package/src/lib/server/pos-payment.js.map +1 -0
  442. package/src/lib/server/pos-print.d.ts +82 -0
  443. package/src/lib/server/pos-print.js +193 -0
  444. package/src/lib/server/pos-print.js.map +1 -0
  445. package/src/lib/server/pos-readers.d.ts +27 -0
  446. package/src/lib/server/pos-readers.js +276 -0
  447. package/src/lib/server/pos-readers.js.map +1 -0
  448. package/src/lib/server/pos-refund-authority.d.ts +60 -0
  449. package/src/lib/server/pos-refund-authority.js +72 -0
  450. package/src/lib/server/pos-refund-authority.js.map +1 -0
  451. package/src/lib/server/pos-return.d.ts +58 -0
  452. package/src/lib/server/pos-return.js +891 -0
  453. package/src/lib/server/pos-return.js.map +1 -0
  454. package/src/lib/server/pos-sale-stamp.d.ts +66 -0
  455. package/src/lib/server/pos-sale-stamp.js +84 -0
  456. package/src/lib/server/pos-sale-stamp.js.map +1 -0
  457. package/src/lib/server/pos-sale.d.ts +170 -0
  458. package/src/lib/server/pos-sale.js +461 -0
  459. package/src/lib/server/pos-sale.js.map +1 -0
  460. package/src/lib/server/pos-shift.d.ts +45 -0
  461. package/src/lib/server/pos-shift.js +484 -0
  462. package/src/lib/server/pos-shift.js.map +1 -0
  463. package/src/lib/server/pos-staff-pin.d.ts +26 -0
  464. package/src/lib/server/pos-staff-pin.js +339 -0
  465. package/src/lib/server/pos-staff-pin.js.map +1 -0
  466. package/src/lib/server/pos-stripe.d.ts +91 -0
  467. package/src/lib/server/pos-stripe.js +129 -0
  468. package/src/lib/server/pos-stripe.js.map +1 -0
  469. package/src/lib/server/pos-terminal-connection-token.d.ts +48 -0
  470. package/src/lib/server/pos-terminal-connection-token.js +166 -0
  471. package/src/lib/server/pos-terminal-connection-token.js.map +1 -0
  472. package/src/lib/server/pos-terminal.d.ts +218 -0
  473. package/src/lib/server/pos-terminal.js +1014 -0
  474. package/src/lib/server/pos-terminal.js.map +1 -0
  475. package/src/lib/server/print-queue.d.ts +124 -0
  476. package/src/lib/server/print-queue.js +301 -0
  477. package/src/lib/server/print-queue.js.map +1 -0
  478. package/src/lib/server/printer-poll.d.ts +75 -0
  479. package/src/lib/server/printer-poll.js +325 -0
  480. package/src/lib/server/printer-poll.js.map +1 -0
  481. package/src/lib/server/printer-secret.d.ts +39 -0
  482. package/src/lib/server/printer-secret.js +56 -0
  483. package/src/lib/server/printer-secret.js.map +1 -0
  484. package/src/lib/server/printers.d.ts +114 -0
  485. package/src/lib/server/printers.js +546 -0
  486. package/src/lib/server/printers.js.map +1 -0
  487. package/src/lib/server/product-ai-capability.d.ts +42 -0
  488. package/src/lib/server/product-ai-capability.js +159 -0
  489. package/src/lib/server/product-ai-capability.js.map +1 -0
  490. package/src/lib/server/product-catalog.d.ts +42 -0
  491. package/src/lib/server/product-catalog.js +305 -0
  492. package/src/lib/server/product-catalog.js.map +1 -0
  493. package/src/lib/server/product-drafts.d.ts +119 -0
  494. package/src/lib/server/product-drafts.js +384 -0
  495. package/src/lib/server/product-drafts.js.map +1 -0
  496. package/src/lib/server/refund.js +110 -45
  497. package/src/lib/server/refund.js.map +1 -1
  498. package/src/lib/server/reserve.js +2 -0
  499. package/src/lib/server/reserve.js.map +1 -1
  500. package/src/lib/server/return-status-action.d.ts +37 -0
  501. package/src/lib/server/return-status-action.js +57 -0
  502. package/src/lib/server/return-status-action.js.map +1 -0
  503. package/src/lib/server/returns.d.ts +119 -0
  504. package/src/lib/server/returns.js +938 -0
  505. package/src/lib/server/returns.js.map +1 -0
  506. package/src/lib/server/shipment-records.d.ts +36 -0
  507. package/src/lib/server/shipment-records.js +289 -0
  508. package/src/lib/server/shipment-records.js.map +1 -0
  509. package/src/lib/server/shipping-connector-gates.d.ts +40 -0
  510. package/src/lib/server/shipping-connector-gates.js +97 -0
  511. package/src/lib/server/shipping-connector-gates.js.map +1 -0
  512. package/src/lib/server/shipping-connectors.d.ts +29 -0
  513. package/src/lib/server/shipping-connectors.js +236 -0
  514. package/src/lib/server/shipping-connectors.js.map +1 -0
  515. package/src/lib/server/shippingeasy.d.ts +238 -0
  516. package/src/lib/server/shippingeasy.js +787 -0
  517. package/src/lib/server/shippingeasy.js.map +1 -0
  518. package/src/lib/server/shipstation.d.ts +129 -0
  519. package/src/lib/server/shipstation.js +483 -0
  520. package/src/lib/server/shipstation.js.map +1 -0
  521. package/src/lib/server/site-page-resolver.d.ts +5 -0
  522. package/src/lib/server/site-page-resolver.js +156 -1
  523. package/src/lib/server/site-page-resolver.js.map +1 -1
  524. package/src/lib/server/stock-levels.d.ts +21 -0
  525. package/src/lib/server/stock-levels.js +263 -0
  526. package/src/lib/server/stock-levels.js.map +1 -0
  527. package/src/lib/server/stripe-refund.d.ts +47 -0
  528. package/src/lib/server/stripe-refund.js +87 -0
  529. package/src/lib/server/stripe-refund.js.map +1 -0
  530. package/src/lib/server/supplier-update.js +46 -5
  531. package/src/lib/server/supplier-update.js.map +1 -1
  532. package/src/lib/server/tax-engine-quote.d.ts +47 -0
  533. package/src/lib/server/tax-engine-quote.js +105 -0
  534. package/src/lib/server/tax-engine-quote.js.map +1 -0
  535. package/src/lib/server.js +146 -2
  536. package/src/lib/server.js.map +1 -1
  537. package/src/lib/site.js +12 -0
  538. package/src/lib/site.js.map +1 -1
  539. package/src/lib/subprocessors.d.ts +26 -0
  540. package/src/lib/subprocessors.js +50 -0
  541. package/src/lib/subprocessors.js.map +1 -0
  542. package/src/lib/tenant-emails.js +461 -3
  543. package/src/lib/tenant-emails.js.map +1 -1
  544. package/src/lib/testing/fake-firestore.d.ts +45 -0
  545. package/src/lib/testing/fake-firestore.js +189 -0
  546. package/src/lib/testing/fake-firestore.js.map +1 -0
  547. package/src/lib/testing/memory-firestore.d.ts +123 -0
  548. package/src/lib/testing/memory-firestore.js +274 -0
  549. package/src/lib/testing/memory-firestore.js.map +1 -0
  550. package/src/lib/testing/pos-ops-harness.d.ts +50 -0
  551. package/src/lib/testing/pos-ops-harness.js +184 -0
  552. package/src/lib/testing/pos-ops-harness.js.map +1 -0
  553. package/src/lib/testing/pos-ops-memory-firestore.d.ts +55 -0
  554. package/src/lib/testing/pos-ops-memory-firestore.js +376 -0
  555. package/src/lib/testing/pos-ops-memory-firestore.js.map +1 -0
  556. package/src/lib/transfer/records-transfer.d.ts +11 -1
  557. package/src/lib/transfer/records-transfer.js +127 -5
  558. package/src/lib/transfer/records-transfer.js.map +1 -1
  559. package/src/lib/transfer/records.server.d.ts +9 -2
  560. package/src/lib/transfer/records.server.js +58 -7
  561. package/src/lib/transfer/records.server.js.map +1 -1
  562. package/src/lib/transfer/register-transfer-resources.js +40 -1
  563. package/src/lib/transfer/register-transfer-resources.js.map +1 -1
  564. package/src/lib/transfer/register-transfer-ui.js +4 -1
  565. package/src/lib/transfer/register-transfer-ui.js.map +1 -1
  566. package/src/lib/transfer/shipping-presets.d.ts +23 -0
  567. package/src/lib/transfer/shipping-presets.js +156 -0
  568. package/src/lib/transfer/shipping-presets.js.map +1 -0
  569. package/src/lib/transfer/tracking-transfer.d.ts +101 -0
  570. package/src/lib/transfer/tracking-transfer.js +546 -0
  571. package/src/lib/transfer/tracking-transfer.js.map +1 -0
  572. package/src/lib/transfer/tracking.server.d.ts +37 -0
  573. package/src/lib/transfer/tracking.server.js +393 -0
  574. package/src/lib/transfer/tracking.server.js.map +1 -0
  575. package/src/lib/transfer/transfer-keys.d.ts +2 -1
  576. package/src/lib/transfer/transfer-keys.js +6 -2
  577. package/src/lib/transfer/transfer-keys.js.map +1 -1
  578. package/src/lib/utils/code128.d.ts +37 -0
  579. package/src/lib/utils/code128.js +199 -0
  580. package/src/lib/utils/code128.js.map +1 -0
  581. package/src/lib/utils/order-invoice.d.ts +55 -0
  582. package/src/lib/utils/order-invoice.js +127 -0
  583. package/src/lib/utils/order-invoice.js.map +1 -0
  584. package/src/lib/utils/use-checkout-return-notice.d.ts +34 -0
  585. package/src/lib/utils/use-checkout-return-notice.js +78 -0
  586. package/src/lib/utils/use-checkout-return-notice.js.map +1 -0
  587. package/src/lib/utils/use-storefront-purchase-event.js +4 -0
  588. package/src/lib/utils/use-storefront-purchase-event.js.map +1 -1
  589. package/src/lib/server/feed.js +0 -69
  590. package/src/lib/server/feed.js.map +0 -1
@@ -19,22 +19,27 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
19
19
  import * as Aglyn from "@aglyn/aglyn";
20
20
  import * as CommerceModel from "../../model/index.js";
21
21
  import { PRODUCT_LIST_BASE, PRODUCT_LIST_QUERY } from "../../constants/product-list-query.js";
22
- import { escapeHtml } from "../../utils/escape-html.js";
23
22
  import { NextPageTitle } from "@aglyn/shared-ui-next/contexts/next-page-title-provider";
24
23
  import { useSnackbar } from "@aglyn/shared-ui-snackstack";
25
- import { ListQueryNotices } from "@aglyn/shared-ui-jsx/components/list-query-notices.component";
26
24
  import { planListQuery } from "@aglyn/shared-ui-jsx/const/list-query-plan";
27
25
  import { nameSearchNormalizers } from "@aglyn/aglyn/app-utils/name-search";
28
- import { Alert, Box, Button, Card, CardActionArea, Chip, Dialog, DialogActions, DialogContent, DialogTitle, MenuItem, Stack, TextField, Typography } from "@mui/material";
29
- import { QRCodeSVG } from "qrcode.react";
26
+ import { Badge, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Drawer, MenuItem, Stack, TextField, Typography, useMediaQuery, useTheme } from "@mui/material";
30
27
  import { collection, getDocs, limit, query, where } from "firebase/firestore";
31
28
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
32
29
  import { useFirestore, useUser } from "@aglyn/tenant-feature-instance";
33
30
  import { useFirestoreCollection } from "@aglyn/tenant-feature-instance";
34
31
  import { listQueryConstraints } from "@aglyn/tenant-feature-instance/hooks/use-list-query";
35
32
  import { useOrgPlan } from "@aglyn/tenant-feature-instance";
36
- import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
37
- const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
33
+ import { newAttemptKey, openPosSale, posRegisterContext, PosRequestError, usd } from "./pos/pos-api.js";
34
+ import { PosCartPanel } from "./pos/pos-cart-panel.component.js";
35
+ import { ScanAdornment } from "../../barcode/scan-button.component.js";
36
+ import { useScannerWedge } from "../../barcode/scanner-wedge.js";
37
+ import { PosCustomerLookup, PosLastReceipt, PosOperationsBar, usePosCashier, usePosOpsSettings } from "./pos-ops/register-ops.js";
38
+ import { POS_QUICK_KEYS, POS_TOUCH_PX, PosProductGrid } from "./pos/pos-product-grid.component.js";
39
+ import { PosItemDialog, posItemNeedsChoice } from "./pos/pos-item-dialog.component.js";
40
+ import { PosReceiptPanel } from "./pos/pos-receipt-panel.component.js";
41
+ import { PosTenderPanel } from "./pos/pos-tender-panel.component.js";
42
+ import { posDisplayTipCents, usePosDisplay } from "./pos/use-pos-display.js";
38
43
  /** The till sells active products only; every read of the catalog asks it. */ const SELLABLE = {
39
44
  field: 'status',
40
45
  op: 'equals',
@@ -53,7 +58,7 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
53
58
  */ export function posProductPlan(options) {
54
59
  var _options_search;
55
60
  const typed = (_options_search = options.search) == null ? void 0 : _options_search.trim();
56
- return planListQuery(PRODUCT_LIST_QUERY, {
61
+ const plan = planListQuery(POS_GRID_QUERY, {
57
62
  base: PRODUCT_LIST_BASE,
58
63
  clauses: [
59
64
  SELLABLE,
@@ -63,19 +68,68 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
63
68
  op: 'contains',
64
69
  value: options.code.value
65
70
  }
71
+ ] : [],
72
+ ...options.categoryId ? [
73
+ {
74
+ field: 'categoryIds',
75
+ op: 'contains',
76
+ value: options.categoryId
77
+ }
66
78
  ] : []
67
79
  ],
68
- search: typed ? [
80
+ // A chip and a typed word are both array clauses, and Firestore takes
81
+ // one: with a chip on, the word narrows by name prefix instead.
82
+ search: typed && !options.categoryId ? [
69
83
  typed
70
84
  ] : []
71
85
  }, nameSearchNormalizers);
86
+ // The Quick keys chip (AGL-3607) is a boolean equality the list planner
87
+ // does not model, added beside the scope and status it composes with —
88
+ // `deletedAt ==, status ==, posQuickKey ==, nameLower ASC` is declared in
89
+ // the index file. A typed word searches the whole catalog instead: a
90
+ // cashier who types is looking past the quick keys.
91
+ if (options.quickKeys && !typed && !options.code) {
92
+ return _extends({}, plan, {
93
+ filters: [
94
+ ...plan.filters,
95
+ {
96
+ path: 'posQuickKey',
97
+ op: '==',
98
+ value: true
99
+ }
100
+ ]
101
+ });
102
+ }
103
+ return plan;
72
104
  }
73
105
  /**
74
- * POS register (AGL-312): touch-first full-screen sale surface —
75
- * product grid with search/barcode (keyboard-wedge scanners type into
76
- * the search box and press Enter), a register cart with a whole-sale
77
- * discount, and cash / QR-card / reservation-folio settlement through
78
- * the server-priced pos-order API. Receipts print via the browser.
106
+ * The grid's query: the products hub's, plus the category a chip picks
107
+ * (AGL-3607). `categoryIds CONTAINS, nameLower ASC` is a composite the
108
+ * storefront catalog already declares.
109
+ */ export const POS_GRID_QUERY = _extends({}, PRODUCT_LIST_QUERY, {
110
+ fields: [
111
+ ...PRODUCT_LIST_QUERY.fields,
112
+ {
113
+ column: 'categoryIds',
114
+ kind: 'exact',
115
+ path: 'categoryIds',
116
+ tokensPath: 'categoryIds',
117
+ operators: [
118
+ 'contains'
119
+ ]
120
+ }
121
+ ]
122
+ });
123
+ /**
124
+ * POS register (AGL-312, AGL-3607): a tablet-first sale surface — the
125
+ * product grid with search, scan and category chips, the basket, and a
126
+ * tender panel that takes one or more payments against an open sale (cash,
127
+ * card reader, a native Tap to Pay bridge, a typed card, the QR link, gift
128
+ * cards and room charges) until the balance is zero. A paired customer
129
+ * display mirrors the basket and takes the tip and the receipt (AGL-3608).
130
+ *
131
+ * On a wide screen (900px and up) the grid and the register sit side by
132
+ * side; below that the register is a bottom sheet behind a sticky total bar.
79
133
  */ /*
80
134
  * `managePos` is NOT read here. The nav item in `plugin.ts` declares it and
81
135
  * the shell refuses the route before this component is constructed, so a
@@ -85,31 +139,76 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
85
139
  * Re-adding a check off the `permissions` prop would be a second answer to a
86
140
  * settled question, and a laxer one: a prop absent because the map has not
87
141
  * landed reads as permitted here, while the shell holds the route on that
88
- * same condition rather than guessing. `server/pos-order.ts` remains the
89
- * enforcement point for the sale itself.
90
- */ export function PosConsolePage({ hostId }) {
91
- var _ref;
92
- var _usableRegisters_;
142
+ * same condition rather than guessing. The server routes remain the
143
+ * enforcement point for every sale and every payment.
144
+ */ /** Where a register device remembers the category chip it was left on. */ const POS_CATEGORY_KEY = 'aglyn.pos.category';
145
+ /** The basket line an item-sheet choice makes: label and estimate from the product. */ export function registerLineFor(product, choice) {
146
+ var _variant_options, _variant_options1;
147
+ const { variant, modifiers, quantity } = choice;
148
+ const resolved = CommerceModel.resolveLineModifiers(product, modifiers);
149
+ const picked = resolved.ok ? resolved.modifiers : [];
150
+ const label = CommerceModel.lineLabelWithModifiers(Object.keys((_variant_options = variant.options) != null ? _variant_options : {}).length ? Object.values((_variant_options1 = variant.options) != null ? _variant_options1 : {}).join(' / ') : undefined, picked);
151
+ return _extends({
152
+ productId: product.$id
153
+ }, variant.id !== 'default' ? {
154
+ variantId: variant.id
155
+ } : {}, {
156
+ name: product.name
157
+ }, label ? {
158
+ variantLabel: label
159
+ } : {}, {
160
+ unitAmountCents: Math.round(Number(variant.priceUsd) * 100) + (resolved.ok ? resolved.extraCents : 0),
161
+ quantity
162
+ }, modifiers.length ? {
163
+ modifiers
164
+ } : {});
165
+ }
166
+ /** Same product, variant and choices: the lines that merge into one. */ function lineKey(line) {
167
+ var _line_variantId;
168
+ return `${line.productId}:${(_line_variantId = line.variantId) != null ? _line_variantId : ''}:${CommerceModel.modifierSelectionKey(line.modifiers)}`;
169
+ }
170
+ export function PosConsolePage({ hostId }) {
171
+ var _ref, _ref1, _ref2, _ref3, _ref4;
172
+ var _usableRegisters_find, _usableRegisters_;
93
173
  const firestore = useFirestore();
94
174
  const { data: user } = useUser();
95
175
  const { enqueueSnackbar } = useSnackbar();
176
+ const theme = useTheme();
177
+ const wide = useMediaQuery(theme.breakpoints.up('md'));
96
178
  const [search, setSearch] = useState('');
97
- const gridPlan = useMemo(()=>posProductPlan({
98
- search
179
+ // The chip a register opens on is remembered per device: a counter that
180
+ // rings from its quick keys reopens on them.
181
+ const [categoryId, setCategoryIdState] = useState(()=>{
182
+ try {
183
+ var _window_localStorage_getItem;
184
+ return (_window_localStorage_getItem = window.localStorage.getItem(POS_CATEGORY_KEY)) != null ? _window_localStorage_getItem : '';
185
+ } catch (unused) {
186
+ return '';
187
+ }
188
+ });
189
+ const setCategoryId = useCallback((id)=>{
190
+ setCategoryIdState(id);
191
+ try {
192
+ window.localStorage.setItem(POS_CATEGORY_KEY, id);
193
+ } catch (unused) {
194
+ // Storage blocked: the chip still works for this visit.
195
+ }
196
+ }, []);
197
+ const gridPlan = useMemo(()=>categoryId === POS_QUICK_KEYS ? posProductPlan({
198
+ search,
199
+ quickKeys: true
200
+ }) : posProductPlan({
201
+ search,
202
+ categoryId
99
203
  }), [
100
- search
204
+ search,
205
+ categoryId
101
206
  ]);
102
207
  const { data: productDocs } = useFirestoreCollection(/*
103
208
  * The till's grid, narrowed by the QUERY rather than by the rows it
104
- * happened to fetch (AGL-2501, AGL-2292, AGL-3321).
105
- *
106
- * `limit(500)` with no `orderBy` was document-id order over
107
- * `createResourceUid()` — an arbitrary five hundred, filtered afterwards
108
- * for status, deletion and the typed search. All three are the query's
109
- * now (`posProductPlan`), so the window is the first five hundred
110
- * SELLABLE products by name and a typed name reaches the whole catalog.
111
- * The scan does not come through here at all — see `handleSearchEnter`,
112
- * which is a lookup rather than a filter.
209
+ * happened to fetch (AGL-2501, AGL-2292, AGL-3321): the first five
210
+ * hundred SELLABLE products by name, and a typed name reaches the whole
211
+ * catalog. The scan does not come through here — see `handleSearchEnter`.
113
212
  */ ()=>query(collection(firestore, 'hosts', hostId, 'products'), ...listQueryConstraints(gridPlan), limit(POS_GRID_CEILING)), [
114
213
  firestore,
115
214
  hostId,
@@ -117,6 +216,12 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
117
216
  ], {
118
217
  idField: '$id'
119
218
  });
219
+ const { data: categoryDocs } = useFirestoreCollection(()=>query(collection(firestore, 'hosts', hostId, 'productCategories'), limit(30)), [
220
+ firestore,
221
+ hostId
222
+ ], {
223
+ idField: '$id'
224
+ });
120
225
  const { data: locationDocs } = useFirestoreCollection(()=>query(collection(firestore, 'hosts', hostId, 'locations'), limit(25)), [
121
226
  firestore,
122
227
  hostId
@@ -136,24 +241,13 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
136
241
  var _a_name, _b_name;
137
242
  return String((_a_name = a.name) != null ? _a_name : '').localeCompare(String((_b_name = b.name) != null ? _b_name : ''));
138
243
  });
139
- // Only registers within the plan cap can transact (pos-order.ts enforces
140
- // this at sale time by creation rank, AGL-482) — offer only those.
141
- //
142
- // Not until the org doc has arrived (AGL-1064): an absent org resolves to
143
- // the free tier's `posRegisters: 0`, which empties this list and tells a
144
- // paying seller their registers exceed a plan nobody has read yet.
145
- // Per SITE (AGL-1775): the plan's cap plus the register seats the org has
146
- // allocated to this host out of the purchased pool. `checkQuota` on the
147
- // org-level `posRegisters` no longer carries the pool and would hide
148
- // registers this site is paying for.
244
+ // Only registers within the plan cap can transact (AGL-482), and not until
245
+ // the org doc has arrived (AGL-1064). Per SITE (AGL-1775).
149
246
  const registerCap = Aglyn.checkHostRegisterQuota(org, hostId, registers.length).limit;
150
247
  const withinCap = CommerceModel.registersWithinCap(registers, registerCap);
151
248
  const usableRegisters = planReady ? registers.filter((r)=>withinCap.has(r.$id)) : [];
152
249
  /*
153
- * The stays a sale can be charged to, asked for BY STATUS (AGL-3321). This
154
- * read the first hundred reservations in document-id order and kept the
155
- * checked-in ones, so a guest in the house was missing from the folio
156
- * picker whenever a hundred other bookings sorted before theirs.
250
+ * The stays a sale can be charged to, asked for BY STATUS (AGL-3321).
157
251
  */ const { data: reservationDocs } = useFirestoreCollection(()=>query(collection(firestore, 'hosts', hostId, 'reservations'), where('status', '==', 'checked_in'), limit(100)), [
158
252
  firestore,
159
253
  hostId
@@ -163,13 +257,11 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
163
257
  const openStays = reservationDocs != null ? reservationDocs : [];
164
258
  const [lines, setLines] = useState([]);
165
259
  const [discountPct, setDiscountPct] = useState(0);
166
- const [customerEmail, setCustomerEmail] = useState('');
260
+ // The customer the lookup attached (AGL-3609); their email is the sale's.
261
+ const [customer, setCustomer] = useState(null);
262
+ const customerEmail = (_ref = customer == null ? void 0 : customer.email) != null ? _ref : '';
167
263
  const [locationId, setLocationId] = useState('');
168
- // Register (AGL-472): a sale must run through a named register so the
169
- // `posRegisters` cap is meaningful and takings are attributable.
170
264
  const [registerId, setRegisterId] = useState('');
171
- // Default to the first register once they load; if that register pins a
172
- // location, adopt it (the cashier can still override below).
173
265
  useEffect(()=>{
174
266
  if (registerId || usableRegisters.length === 0) return;
175
267
  const first = usableRegisters[0];
@@ -179,88 +271,70 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
179
271
  usableRegisters,
180
272
  registerId
181
273
  ]);
182
- // `paying` routes the settlement DIALOGS — nothing else. It is deliberately
183
- // not the tender `settle` acts on (AGL-1682): it used to be both, and the
184
- // Card button had to `setPaying('link')` and call `settle()` in one handler,
185
- // where `settle` still closed over the pre-click `null` and returned at its
186
- // own guard. Card has no dialog of its own — the QR dialog is gated on
187
- // `cardUrl` — so the card path sets nothing here at all.
188
- const [paying, setPaying] = useState(null);
189
- const [cashReceived, setCashReceived] = useState('');
190
- const [folioReservation, setFolioReservation] = useState('');
191
- const [cardUrl, setCardUrl] = useState('');
192
- const [busy, setBusy] = useState(false);
193
- // Re-entrancy guard for `settle`, deliberately a ref and not `busy`. `busy`
194
- // is for RENDERING (it disables the settle buttons); a second tap arriving
195
- // before React has re-rendered would read the pre-click `false` out of the
196
- // handler's closure exactly the way `paying` was read above. The ref is
197
- // written and read synchronously, so it holds whatever the scheduler does.
198
- // It is no longer the last line of defence — the server dedupes on the
199
- // attempt key below (AGL-1691) — but it is still the cheap one, and it stops
200
- // the redundant round trip rather than merely making it harmless.
201
- const inFlight = useRef(false);
274
+ const registerName = String((_ref1 = (_usableRegisters_find = usableRegisters.find((register)=>register.$id === registerId)) == null ? void 0 : _usableRegisters_find.name) != null ? _ref1 : '');
275
+ // Who is ringing (AGL-3609): a PIN-switched cashier, and the idle lock.
276
+ const opsSettings = usePosOpsSettings(hostId);
277
+ const cashier = usePosCashier({
278
+ hostId,
279
+ registerId,
280
+ autoLockMinutes: opsSettings.autoLockMinutes
281
+ });
282
+ // The last completed sale, whose receipt can still be printed (AGL-3609).
283
+ const [lastReceipt, setLastReceipt] = useState(null);
284
+ const [context, setContext] = useState(null);
285
+ // Keyed on the uid, not the user object: a session refresh hands back a
286
+ // new object for the same person, and re-reading the context on every one
287
+ // would re-render the register in a loop.
288
+ const userRef = useRef(user);
289
+ userRef.current = user;
290
+ const uid = (_ref2 = user == null ? void 0 : user.uid) != null ? _ref2 : '';
291
+ useEffect(()=>{
292
+ const signedIn = userRef.current;
293
+ if (!signedIn) return;
294
+ let active = true;
295
+ posRegisterContext(signedIn, hostId).then((answer)=>{
296
+ // Only a whole answer: a register that half-knows its tenders would
297
+ // offer one the server then refuses.
298
+ if (active && (answer == null ? void 0 : answer.settings) && (answer == null ? void 0 : answer.terminal)) setContext(answer);
299
+ }).catch(()=>undefined);
300
+ return ()=>{
301
+ active = false;
302
+ };
303
+ }, [
304
+ uid,
305
+ hostId
306
+ ]);
307
+ /** The open sale, once the basket has been priced and payment started. */ const [sale, setSale] = useState(null);
308
+ const [saleLines, setSaleLines] = useState([]);
309
+ const [opening, setOpening] = useState(false);
310
+ const [sheetOpen, setSheetOpen] = useState(false);
311
+ const [pairing, setPairing] = useState(null);
312
+ const display = usePosDisplay(user, hostId, registerId);
202
313
  /**
203
- * Idempotency key for ONE settlement attempt (AGL-1691).
204
- *
205
- * The guards above are all client-side and none of them survives a reload, a
206
- * lost response, or a retry. `/api/commerce/pos-order` now dedupes on this
207
- * key, but only if the key is right, and the key is the whole design:
208
- *
209
- * - Minted lazily on the first settle of a basket, NOT per `settle()` call.
210
- * Per-call would defeat the point — two taps would mint two keys and the
211
- * server would see two distinct sales.
212
- * - Retired whenever the register's contents change (the effect below),
213
- * which covers the sale completing and clearing the lines. So a cashier
214
- * ringing the same coffee twice gets a NEW key and a real second order —
215
- * de-duplicating that would be a worse bug than the one being fixed.
314
+ * One key per basket (AGL-1691): minted on the first Charge and retired
315
+ * whenever the basket changes, so a retried Charge finds the same sale and
316
+ * ringing the same coffee twice is two sales.
216
317
  */ const attemptKey = useRef('');
217
318
  useEffect(()=>{
218
- // A different basket is a different attempt. Also fires when `settle`
219
- // clears the lines, which is what retires a spent key.
220
319
  attemptKey.current = '';
221
320
  }, [
222
321
  lines,
223
322
  discountPct
224
323
  ]);
225
- const [lastReceipt, setLastReceipt] = useState(null);
226
- const products = useMemo(()=>// Status, deletion and the search are all the query's (`posProductPlan`).
227
- [
324
+ const products = useMemo(()=>[
228
325
  ...productDocs != null ? productDocs : []
229
326
  ].map((product)=>_extends({}, CommerceModel.liftLegacyProduct(product), {
230
327
  $id: product.$id
231
328
  })), [
232
329
  productDocs
233
330
  ]);
234
- /*
235
- * The grid is what the query returned. Re-filtering it by the same text
236
- * would narrow it AGAIN and more strictly: the server matches a word prefix
237
- * ("cof" finds "Coffee"), the old compare wanted the whole typed string as a
238
- * substring, so "flat white" was sent as "flat" and then hidden again by a
239
- * row that never contained "flat white". Rows found, then dropped.
240
- */ const visible = products;
241
331
  const productsById = useMemo(()=>new Map(products.map((product)=>[
242
332
  product.$id,
243
333
  product
244
334
  ])), [
245
335
  products
246
336
  ]);
247
- /**
248
- * WHAT THE COUNT SAYS, at the line the cashier is looking at (AGL-2357).
249
- *
250
- * The register ignored `oversellPolicy` entirely — `pos-order.ts` had no
251
- * `canPurchase` call — so a merchant who chose "deny" in the product editor
252
- * silently got "backorder" at the counter. That silence is the defect.
253
- *
254
- * WARNS, NEVER BLOCKS (the decision on AGL-2357). Nothing here touches
255
- * `disabled` on the settle buttons, and nothing gates `settle`: a till is the
256
- * wrong place for a stale number to stop a real sale, because the cashier is
257
- * holding the goods. Honouring the policy behind a manager override is the
258
- * post-launch shape (AGL-2372).
259
- *
260
- * Indexed alongside `lines` rather than keyed, because the line list is
261
- * rendered by index and two lines of the same product/variant cannot exist —
262
- * `addProduct` merges them into one quantity.
263
- */ const shortfalls = useMemo(()=>lines.map((line)=>{
337
+ /** AGL-2357: warns, never blocks. */ const shortfalls = useMemo(()=>lines.map((line)=>{
264
338
  var _line_variantId;
265
339
  var _product_variants_, _product_variants;
266
340
  const product = productsById.get(line.productId);
@@ -272,60 +346,76 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
272
346
  ]);
273
347
  const itemsCents = lines.reduce((sum, line)=>sum + line.unitAmountCents * line.quantity, 0);
274
348
  const discountCents = Math.round(itemsCents * discountPct / 100);
275
- const dueCents = itemsCents - discountCents;
276
- const addProduct = useCallback((product, variant)=>{
277
- const pick = variant != null ? variant : product.variants[0];
349
+ const estimateCents = itemsCents - discountCents;
350
+ const units = lines.reduce((sum, line)=>sum + line.quantity, 0);
351
+ const notify = useCallback((message, variant)=>{
352
+ enqueueSnackbar(message, {
353
+ variant,
354
+ persist: false,
355
+ allowDuplicate: true
356
+ });
357
+ }, [
358
+ enqueueSnackbar
359
+ ]);
360
+ /**
361
+ * Adds a line, or more of an identical one: the same product, variant and
362
+ * modifiers merge, so two oat lattes are one line of two and an oat latte
363
+ * beside a whole-milk one stays two lines.
364
+ */ const addProduct = useCallback((product, variant, modifiers = [], quantity = 1)=>{
365
+ const next = registerLineFor(product, {
366
+ variant: variant != null ? variant : product.variants[0],
367
+ modifiers,
368
+ quantity
369
+ });
278
370
  setLines((prev)=>{
279
- var _pick_options, _pick_options1;
280
- const key = `${product.$id}:${pick.id}`;
281
- const existing = prev.find((line)=>{
282
- var _line_variantId;
283
- return `${line.productId}:${(_line_variantId = line.variantId) != null ? _line_variantId : pick.id}` === key;
284
- });
371
+ const existing = prev.find((line)=>lineKey(line) === lineKey(next));
285
372
  if (existing) {
286
373
  return prev.map((line)=>line === existing ? _extends({}, line, {
287
- quantity: line.quantity + 1
374
+ quantity: Math.min(99, line.quantity + next.quantity)
288
375
  }) : line);
289
376
  }
290
377
  return [
291
378
  ...prev,
292
- _extends({
293
- productId: product.$id
294
- }, pick.id !== 'default' ? {
295
- variantId: pick.id
296
- } : {}, {
297
- name: product.name
298
- }, Object.keys((_pick_options = pick.options) != null ? _pick_options : {}).length ? {
299
- variantLabel: Object.values((_pick_options1 = pick.options) != null ? _pick_options1 : {}).join(' / ')
300
- } : {}, {
301
- unitAmountCents: Math.round(Number(pick.priceUsd) * 100),
302
- quantity: 1
303
- })
379
+ next
304
380
  ];
305
381
  });
306
382
  }, []);
383
+ /** The item sheet: a product to add, or a basket line to change. */ const [itemSheet, setItemSheet] = useState(null);
384
+ // Every product rung up this sale, so a line can be reopened after the
385
+ // grid has moved on to another category or search.
386
+ const productCache = useRef(new Map());
387
+ const tapProduct = useCallback((product, variant)=>{
388
+ productCache.current.set(product.$id, product);
389
+ if (posItemNeedsChoice(product) && !(variant && CommerceModel.productModifierGroups(product).length === 0)) {
390
+ setItemSheet(_extends({
391
+ product
392
+ }, variant ? {
393
+ initial: {
394
+ variantId: variant.id,
395
+ quantity: 1
396
+ }
397
+ } : {}));
398
+ return;
399
+ }
400
+ addProduct(product, variant);
401
+ }, [
402
+ addProduct
403
+ ]);
404
+ const basketCounts = useMemo(()=>{
405
+ var _counts_get;
406
+ const counts = new Map();
407
+ for (const line of lines)counts.set(line.productId, ((_counts_get = counts.get(line.productId)) != null ? _counts_get : 0) + line.quantity);
408
+ return counts;
409
+ }, [
410
+ lines
411
+ ]);
307
412
  /**
308
- * The barcode wedge: a scanner types the code and presses Enter.
309
- *
310
- * A LOOKUP against the whole catalog, not a scan of the rows on screen
311
- * (AGL-2501). It used to walk `products` — the grid's `limit(500)` window —
312
- * so a shop whose catalog was larger than that had items whose barcode
313
- * simply did nothing when scanned. At a till, holding the goods, with a
314
- * customer waiting.
315
- *
316
- * `barcodes` and `skus` are top-level arrays the write path flattens out of
317
- * `variants`, because Firestore cannot query a field inside an array of
318
- * objects. Barcode is tried first: it is what the scanner produced, and a
319
- * SKU that happens to equal another product's barcode should not win over
320
- * the code actually scanned.
321
- *
322
- * ⚠️ A MISS NOW SAYS SO. The old loop returned silently, so an unknown code
323
- * and a code outside the window were indistinguishable from a scanner that
324
- * had not fired — the cashier's only signal was that nothing happened.
325
- */ const handleSearchEnter = useCallback(async ()=>{
413
+ * The barcode wedge: a LOOKUP against the whole catalog (AGL-2501),
414
+ * barcode first, and a miss says so.
415
+ */ const lookupCode = useCallback(async (scanned)=>{
326
416
  var _product_variants_find;
327
- const needle = search.trim().toLowerCase();
328
- if (!needle) return;
417
+ const needle = scanned.trim().toLowerCase();
418
+ if (!needle || sale) return;
329
419
  const lookup = async (field)=>{
330
420
  const found = await getDocs(query(collection(firestore, 'hosts', hostId, 'products'), ...listQueryConstraints(posProductPlan({
331
421
  code: {
@@ -341,17 +431,9 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
341
431
  hit = (_ref = await lookup('barcodes')) != null ? _ref : await lookup('skus');
342
432
  } catch (error) {
343
433
  console.error(error);
344
- return void enqueueSnackbar('Could not reach the catalog — try again', {
345
- variant: 'warning',
346
- persist: false
347
- });
348
- }
349
- if (!hit) {
350
- return void enqueueSnackbar(`No product matches “${search.trim()}”`, {
351
- variant: 'warning',
352
- persist: false
353
- });
434
+ return void notify('Could not reach the catalog — try again', 'warning');
354
435
  }
436
+ if (!hit) return void notify(`No product matches “${scanned.trim()}”`, 'warning');
355
437
  const product = _extends({}, CommerceModel.liftLegacyProduct(hit.data()), {
356
438
  $id: hit.id
357
439
  });
@@ -359,140 +441,385 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
359
441
  var _item_barcode, _item_sku;
360
442
  return ((_item_barcode = item.barcode) == null ? void 0 : _item_barcode.trim().toLowerCase()) === needle || ((_item_sku = item.sku) == null ? void 0 : _item_sku.trim().toLowerCase()) === needle;
361
443
  })) != null ? _product_variants_find : product.variants[0];
362
- addProduct(product, variant);
444
+ // A scanned variant is the variant; a product with modifiers still asks
445
+ // for them, with that variant already picked.
446
+ tapProduct(product, variant);
363
447
  setSearch('');
364
448
  }, [
365
- search,
449
+ sale,
366
450
  firestore,
367
451
  hostId,
368
- addProduct,
369
- enqueueSnackbar
452
+ tapProduct,
453
+ notify
370
454
  ]);
371
- /**
372
- * Take payment. The tender is an ARGUMENT, never read back out of state
373
- * (AGL-1682) — every caller already knows which button was pressed, and the
374
- * one caller that had to announce it through `setPaying` first could not
375
- * then observe its own write.
376
- */ const settle = useCallback(async (tender)=>{
377
- if (inFlight.current || lines.length === 0) return;
378
- if (!registerId) {
379
- return void enqueueSnackbar('Select a register before taking payment', {
380
- variant: 'warning',
381
- persist: false
382
- });
383
- }
384
- inFlight.current = true;
385
- setBusy(true);
386
- // Reuse the key across retries of this basket; mint one if this is the
387
- // first attempt (AGL-1691). `randomUUID` needs a secure context, which the
388
- // console always is, but fall back rather than throw on a settle.
389
- if (!attemptKey.current) {
390
- var _ref;
391
- var _globalThis_crypto_randomUUID, _globalThis_crypto;
392
- attemptKey.current = (_ref = (_globalThis_crypto = globalThis.crypto) == null ? void 0 : (_globalThis_crypto_randomUUID = _globalThis_crypto.randomUUID) == null ? void 0 : _globalThis_crypto_randomUUID.call(_globalThis_crypto)) != null ? _ref : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
393
- }
455
+ const handleSearchEnter = useCallback(()=>lookupCode(search), [
456
+ lookupCode,
457
+ search
458
+ ]);
459
+ // A scanner fired while focus is on a product or a button (AGL-3619), and
460
+ // the camera; neither while a sale is taking payment or an item is open.
461
+ useScannerWedge((code)=>void lookupCode(code), !sale && !itemSheet);
462
+ const scanAdornment = useMemo(()=>/*#__PURE__*/ _jsx(ScanAdornment, {
463
+ label: "Scan a barcode with the camera",
464
+ onScan: (code)=>void lookupCode(code)
465
+ }), [
466
+ lookupCode
467
+ ]);
468
+ /** Prices the basket on the server and opens the sale for payment. */ // A ref, not `opening`: two taps inside one React batch both read the
469
+ // pre-click state, and only a ref is written before the second reads it
470
+ // (the AGL-1682 lesson).
471
+ const chargeInFlight = useRef(false);
472
+ const charge = useCallback(async ()=>{
473
+ if (chargeInFlight.current || lines.length === 0 || !user) return;
474
+ if (!registerId) return void notify('Select a register before taking payment', 'warning');
475
+ if (!attemptKey.current) attemptKey.current = newAttemptKey();
476
+ chargeInFlight.current = true;
477
+ setOpening(true);
394
478
  try {
395
- var _ref1, _payload_changeCents;
396
- var _payload_totals;
397
- const response = await authorizedFetch(user, '/api/commerce/pos-order', {
398
- method: 'POST',
399
- headers: {
400
- 'Content-Type': 'application/json',
401
- 'Idempotency-Key': attemptKey.current
402
- },
403
- body: JSON.stringify({
404
- hostId,
405
- lines,
406
- discountPct,
407
- payment: tender,
408
- registerId,
409
- customerEmail: customerEmail || undefined,
410
- locationId: locationId || undefined,
411
- cashReceivedCents: Math.round(Number(cashReceived) * 100) || 0,
412
- reservationId: tender === 'folio' ? folioReservation : undefined
413
- })
414
- });
415
- const payload = await response.json();
416
- if (!response.ok) {
417
- var _ref2;
418
- return void enqueueSnackbar((_ref2 = payload == null ? void 0 : payload.error) != null ? _ref2 : 'Sale failed', {
419
- variant: 'error',
420
- allowDuplicate: true
421
- });
422
- }
423
- if (tender === 'link' && payload.url) {
424
- setCardUrl(payload.url);
425
- return;
426
- }
427
- setLastReceipt({
479
+ const opened = await openPosSale(user, attemptKey.current, _extends({
480
+ hostId,
481
+ registerId,
428
482
  lines,
429
- totalCents: (_ref1 = (_payload_totals = payload.totals) == null ? void 0 : _payload_totals.totalCents) != null ? _ref1 : dueCents,
430
- changeCents: (_payload_changeCents = payload.changeCents) != null ? _payload_changeCents : 0
431
- });
432
- setLines([]);
433
- setDiscountPct(0);
434
- setCustomerEmail('');
435
- setCashReceived('');
436
- setPaying(null);
437
- enqueueSnackbar(payload.changeCents > 0 ? `Paid — change ${usd(payload.changeCents)}` : 'Paid', {
438
- variant: 'success',
439
- persist: false
483
+ discountPct
484
+ }, customerEmail ? {
485
+ customerEmail
486
+ } : {}, customer ? {
487
+ customer
488
+ } : {}, cashier.assertion ? {
489
+ cashierAssertion: cashier.assertion
490
+ } : {}, locationId ? {
491
+ locationId
492
+ } : {}));
493
+ setSaleLines(lines);
494
+ setSale({
495
+ orderId: opened.orderId,
496
+ status: 'pending',
497
+ totalCents: opened.totals.totalCents,
498
+ paidCents: 0,
499
+ dueCents: opened.totals.totalCents,
500
+ tenderableCents: opened.totals.totalCents,
501
+ tipCents: 0,
502
+ payments: []
440
503
  });
504
+ setSheetOpen(true);
505
+ } catch (error) {
506
+ notify(error instanceof PosRequestError ? error.message : 'Sale failed', 'error');
441
507
  } finally{
442
- inFlight.current = false;
443
- setBusy(false);
508
+ chargeInFlight.current = false;
509
+ setOpening(false);
444
510
  }
445
511
  }, [
446
512
  lines,
447
- registerId,
448
513
  user,
514
+ registerId,
449
515
  hostId,
450
516
  discountPct,
451
517
  customerEmail,
518
+ customer,
519
+ cashier.assertion,
452
520
  locationId,
453
- cashReceived,
454
- folioReservation,
455
- dueCents,
456
- enqueueSnackbar
521
+ notify
457
522
  ]);
458
- /**
459
- * Close the card QR dialog. Clears the cart, because by the time this dialog
460
- * exists the order is already on the server awaiting the webhook — leaving
461
- * the lines behind meant a backdrop click or Escape dropped the cashier back
462
- * on a full register that would mint a SECOND pending order and a SECOND
463
- * Checkout session for the same basket on the next tap (AGL-1682). `Done`
464
- * always did this; `onClose` did not, and only `onClose` can fire by
465
- * accident.
466
- */ const closeCardDialog = useCallback(()=>{
467
- setCardUrl('');
523
+ const resetSale = useCallback(()=>{
524
+ if ((sale == null ? void 0 : sale.status) === 'paid') setLastReceipt({
525
+ orderId: sale.orderId
526
+ });
527
+ setSale(null);
528
+ setSaleLines([]);
468
529
  setLines([]);
469
- setPaying(null);
470
- }, []);
471
- const printReceipt = useCallback(()=>{
472
- if (!lastReceipt) return;
473
- const win = window.open('', '_blank', 'width=320,height=600');
474
- if (!win) return;
475
- // ESCAPED (AGL-2283), the same construction as the packing slip: a
476
- // `document.write` into an `about:blank` popup that inherits the console's
477
- // origin. These names are merchant-authored rather than shopper-typed, so
478
- // the reach is a merchant's own session — still not a reason to build
479
- // markup out of unescaped product text.
480
- win.document.write(`<pre style="font-family:monospace;font-size:12px">` + lastReceipt.lines.map((line)=>`${escapeHtml(line.quantity)}x ${escapeHtml(line.name)}${line.variantLabel ? ` (${escapeHtml(line.variantLabel)})` : ''}` + ` ${usd(line.unitAmountCents * line.quantity)}`).join('\n') + `\n\nTOTAL ${usd(lastReceipt.totalCents)}` + (lastReceipt.changeCents ? `\nCHANGE ${usd(lastReceipt.changeCents)}` : '') + `\n${new Date().toLocaleString()}` + `</pre>`);
481
- win.document.close();
482
- // Print from HERE, not from a `<script>` written into the receipt
483
- // (AGL-523). `window.open('')` yields an about:blank document that
484
- // INHERITS the opener's CSP, so an injected inline script has no nonce and
485
- // `strict-dynamic` means `'self'` will not save it either. Under the
486
- // enforcing policy that script is blocked, the receipt window opens, and
487
- // the print dialog never appears — a silent break of the one action the
488
- // window exists for.
489
- //
490
- // This call is in the opener, whose script Next has already nonced.
491
- win.focus();
492
- win.print();
530
+ setDiscountPct(0);
531
+ setCustomer(null);
532
+ setSheetOpen(false);
493
533
  }, [
494
- lastReceipt
534
+ sale
495
535
  ]);
536
+ // The customer display mirrors the basket while nothing else is on it.
537
+ const mirrored = sale ? saleLines : lines;
538
+ // Whether the display was last left showing a basket. A paid sale leaves it
539
+ // on the thank-you the server wrote, which an empty new basket must not
540
+ // cut short with the idle screen; the next item added takes it over.
541
+ // True at first, so a register that opens empty clears whatever a previous
542
+ // session left on the screen.
543
+ const displayShowsBasket = useRef(true);
544
+ // The tip picked for the payment about to be taken; the ledger has it only
545
+ // once that payment is recorded. Gone with the sale.
546
+ const [pendingTipCents, setPendingTipCents] = useState(0);
547
+ useEffect(()=>{
548
+ if (!sale || sale.status === 'paid') setPendingTipCents(0);
549
+ }, [
550
+ sale
551
+ ]);
552
+ useEffect(()=>{
553
+ if ((sale == null ? void 0 : sale.status) === 'paid') displayShowsBasket.current = false;
554
+ }, [
555
+ sale == null ? void 0 : sale.status
556
+ ]);
557
+ useEffect(()=>{
558
+ if (!display.connected || display.asking) return;
559
+ if (sale == null ? void 0 : sale.payments.some((payment)=>payment.status === 'pending' && payment.method === 'card_present')) {
560
+ return;
561
+ }
562
+ if ((sale == null ? void 0 : sale.status) === 'paid') return;
563
+ if (!mirrored.length && !displayShowsBasket.current) return;
564
+ const timer = setTimeout(()=>{
565
+ displayShowsBasket.current = mirrored.length > 0;
566
+ void display.show(mirrored.length ? {
567
+ mode: 'cart',
568
+ cart: _extends({
569
+ lines: mirrored.map((line)=>_extends({
570
+ name: line.name
571
+ }, line.variantLabel ? {
572
+ variantLabel: line.variantLabel
573
+ } : {}, {
574
+ quantity: line.quantity,
575
+ amountCents: line.unitAmountCents * line.quantity
576
+ })),
577
+ itemsCents,
578
+ discountCents,
579
+ taxCents: sale ? Math.max(0, sale.totalCents - estimateCents) : 0,
580
+ totalCents: sale ? sale.totalCents : estimateCents
581
+ }, sale ? {
582
+ paidCents: sale.paidCents,
583
+ dueCents: sale.dueCents,
584
+ tipCents: posDisplayTipCents(sale, pendingTipCents)
585
+ } : {})
586
+ } : {
587
+ mode: 'idle'
588
+ });
589
+ }, 400);
590
+ return ()=>clearTimeout(timer);
591
+ }, [
592
+ display,
593
+ mirrored,
594
+ sale,
595
+ itemsCents,
596
+ discountCents,
597
+ estimateCents,
598
+ pendingTipCents
599
+ ]);
600
+ const registerPicker = !planReady ? /*#__PURE__*/ _jsx(Typography, {
601
+ variant: "body2",
602
+ color: "text.secondary",
603
+ children: 'Checking your plan…'
604
+ }) : usableRegisters.length === 0 ? /*#__PURE__*/ _jsx(Typography, {
605
+ variant: "body2",
606
+ color: "warning.main",
607
+ children: registers.length > 0 ? 'Your registers exceed your plan — remove extras or upgrade in Billing to take payments.' : 'No POS register yet. Add one under Commerce → Settings → POS registers before taking payments.'
608
+ }) : usableRegisters.length > 1 ? /*#__PURE__*/ _jsx(TextField, {
609
+ label: "Register",
610
+ value: registerId,
611
+ onChange: (event)=>setRegisterId(event.target.value),
612
+ size: "small",
613
+ select: true,
614
+ disabled: Boolean(sale),
615
+ children: usableRegisters.map((register)=>/*#__PURE__*/ _jsx(MenuItem, {
616
+ value: register.$id,
617
+ children: register.name
618
+ }, register.$id))
619
+ }) : /*#__PURE__*/ _jsx(Typography, {
620
+ variant: "body2",
621
+ color: "text.secondary",
622
+ children: (_usableRegisters_ = usableRegisters[0]) == null ? void 0 : _usableRegisters_.name
623
+ });
624
+ /**
625
+ * On a narrow screen the register's bar owns the bottom edge, so it moves
626
+ * the console's floating launchers clear of itself through the shell's
627
+ * `--aglyn-dock-inset-bottom` seam; a launcher left in the corner sat on
628
+ * top of Charge.
629
+ */ const bottomBarRef = useRef(null);
630
+ useEffect(()=>{
631
+ const node = bottomBarRef.current;
632
+ if (wide || !node) return undefined;
633
+ const root = document.documentElement;
634
+ const property = '--aglyn-dock-inset-bottom';
635
+ const publish = ()=>{
636
+ root.style.setProperty(property, `${Math.round(node.getBoundingClientRect().height) + 20}px`);
637
+ };
638
+ publish();
639
+ const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(publish);
640
+ observer == null ? void 0 : observer.observe(node);
641
+ return ()=>{
642
+ observer == null ? void 0 : observer.disconnect();
643
+ root.style.removeProperty(property);
644
+ };
645
+ }, [
646
+ wide
647
+ ]);
648
+ const registerPanel = /*#__PURE__*/ _jsxs(Stack, {
649
+ spacing: 1.5,
650
+ sx: {
651
+ minHeight: 0,
652
+ flex: 1
653
+ },
654
+ children: [
655
+ /*#__PURE__*/ _jsxs(Stack, {
656
+ direction: "row",
657
+ spacing: 1,
658
+ sx: {
659
+ alignItems: 'center'
660
+ },
661
+ children: [
662
+ /*#__PURE__*/ _jsx(Typography, {
663
+ variant: "h6",
664
+ sx: {
665
+ flex: 1
666
+ },
667
+ children: 'Register'
668
+ }),
669
+ registerId ? /*#__PURE__*/ _jsx(Button, {
670
+ size: "small",
671
+ onClick: async ()=>{
672
+ try {
673
+ setPairing(await display.pairingCode());
674
+ } catch (error) {
675
+ notify(error instanceof PosRequestError ? error.message : 'Could not make a code', 'error');
676
+ }
677
+ },
678
+ children: display.connected ? 'Display connected' : 'Pair display'
679
+ }) : null
680
+ ]
681
+ }),
682
+ registerPicker,
683
+ ((_ref3 = locationDocs == null ? void 0 : locationDocs.length) != null ? _ref3 : 0) > 1 ? /*#__PURE__*/ _jsx(TextField, {
684
+ label: "Location",
685
+ value: locationId,
686
+ onChange: (event)=>setLocationId(event.target.value),
687
+ size: "small",
688
+ select: true,
689
+ disabled: Boolean(sale),
690
+ children: (locationDocs != null ? locationDocs : []).map((location)=>/*#__PURE__*/ _jsx(MenuItem, {
691
+ value: location.$id,
692
+ children: location.name
693
+ }, location.$id))
694
+ }) : null,
695
+ registerId ? /*#__PURE__*/ _jsx(PosOperationsBar, _extends({
696
+ hostId: hostId,
697
+ registerId: registerId
698
+ }, registerName ? {
699
+ registerName
700
+ } : {}, {
701
+ cashier: cashier,
702
+ onExchange: (returned)=>{
703
+ var _returned_name;
704
+ return setCustomer(returned.email || returned.name ? {
705
+ kind: 'none',
706
+ id: '',
707
+ name: (_returned_name = returned.name) != null ? _returned_name : '',
708
+ email: returned.email,
709
+ phone: null
710
+ } : null);
711
+ }
712
+ })) : null,
713
+ /*#__PURE__*/ _jsx(PosCartPanel, {
714
+ lines: sale ? saleLines : lines,
715
+ shortfalls: sale ? [] : shortfalls,
716
+ onQuantity: (index, quantity)=>setLines((prev)=>quantity <= 0 ? prev.filter((_line, at)=>at !== index) : prev.map((line, at)=>at === index ? _extends({}, line, {
717
+ quantity: Math.min(99, quantity)
718
+ }) : line)),
719
+ onRemove: (index)=>setLines((prev)=>prev.filter((_line, at)=>at !== index)),
720
+ onEdit: (index)=>{
721
+ var _productCache_current_get;
722
+ const line = lines[index];
723
+ const product = line ? (_productCache_current_get = productCache.current.get(line.productId)) != null ? _productCache_current_get : productsById.get(line.productId) : undefined;
724
+ if (!line || !product) return;
725
+ setItemSheet({
726
+ product,
727
+ editIndex: index,
728
+ initial: _extends({}, line.variantId ? {
729
+ variantId: line.variantId
730
+ } : {}, line.modifiers ? {
731
+ modifiers: line.modifiers
732
+ } : {}, {
733
+ quantity: line.quantity
734
+ })
735
+ });
736
+ },
737
+ discountPct: discountPct,
738
+ onDiscountPct: setDiscountPct,
739
+ customer: sale ? null : /*#__PURE__*/ _jsx(PosCustomerLookup, {
740
+ hostId: hostId,
741
+ value: customer,
742
+ onChange: setCustomer
743
+ }),
744
+ locked: Boolean(sale)
745
+ }),
746
+ sale && user ? sale.status === 'paid' ? /*#__PURE__*/ _jsx(PosReceiptPanel, _extends({
747
+ user: user,
748
+ hostId: hostId,
749
+ sale: sale,
750
+ context: context,
751
+ display: display,
752
+ onNewSale: resetSale,
753
+ registerId: registerId
754
+ }, registerName ? {
755
+ registerName
756
+ } : {}, cashier.cashier ? {
757
+ cashierName: cashier.cashier.name
758
+ } : {}, {
759
+ notify: notify
760
+ })) : /*#__PURE__*/ _jsx(PosTenderPanel, _extends({
761
+ user: user,
762
+ hostId: hostId,
763
+ registerId: registerId,
764
+ sale: sale,
765
+ context: context,
766
+ stays: openStays,
767
+ display: display,
768
+ onSale: setSale,
769
+ onVoided: ()=>{
770
+ setSale(null);
771
+ setSaleLines([]);
772
+ },
773
+ onTipChange: setPendingTipCents
774
+ }, cashier.assertion ? {
775
+ cashierAssertion: cashier.assertion
776
+ } : {}, {
777
+ notify: notify
778
+ })) : /*#__PURE__*/ _jsxs(_Fragment, {
779
+ children: [
780
+ /*#__PURE__*/ _jsxs(Box, {
781
+ sx: {
782
+ display: 'flex',
783
+ justifyContent: 'space-between'
784
+ },
785
+ children: [
786
+ /*#__PURE__*/ _jsx(Typography, {
787
+ variant: "h6",
788
+ children: 'Due'
789
+ }),
790
+ /*#__PURE__*/ _jsx(Typography, {
791
+ variant: "h6",
792
+ children: usd(estimateCents)
793
+ })
794
+ ]
795
+ }),
796
+ /*#__PURE__*/ _jsx(Typography, {
797
+ variant: "caption",
798
+ color: "text.secondary",
799
+ children: 'Tax is added when you charge.'
800
+ }),
801
+ /*#__PURE__*/ _jsx(Button, {
802
+ variant: "contained",
803
+ size: "large",
804
+ disabled: opening || lines.length === 0 || !registerId,
805
+ onClick: ()=>void charge(),
806
+ sx: {
807
+ minHeight: 56
808
+ },
809
+ children: opening ? 'Pricing…' : `Charge ${usd(estimateCents)}`
810
+ }),
811
+ lastReceipt ? /*#__PURE__*/ _jsx(PosLastReceipt, _extends({
812
+ hostId: hostId,
813
+ orderId: lastReceipt.orderId
814
+ }, registerName ? {
815
+ registerName
816
+ } : {}, cashier.cashier ? {
817
+ cashierName: cashier.cashier.name
818
+ } : {})) : null
819
+ ]
820
+ })
821
+ ]
822
+ });
496
823
  return /*#__PURE__*/ _jsxs(_Fragment, {
497
824
  children: [
498
825
  /*#__PURE__*/ _jsx(NextPageTitle, {
@@ -501,407 +828,156 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
501
828
  /*#__PURE__*/ _jsxs(Box, {
502
829
  sx: {
503
830
  display: 'flex',
504
- height: '100vh',
831
+ height: '100dvh',
505
832
  overflow: 'hidden'
506
833
  },
507
834
  children: [
508
- /*#__PURE__*/ _jsxs(Box, {
835
+ /*#__PURE__*/ _jsx(Box, {
509
836
  sx: {
510
837
  flex: 1,
511
838
  p: 2,
512
- overflowY: 'auto'
839
+ overflowY: 'auto',
840
+ pb: wide ? 2 : 12
513
841
  },
514
- children: [
515
- /*#__PURE__*/ _jsx(TextField, {
516
- placeholder: "Search or scan barcode…",
517
- value: search,
518
- onChange: (event)=>setSearch(event.target.value),
519
- onKeyDown: (event)=>{
520
- if (event.key === 'Enter') void handleSearchEnter();
521
- },
522
- size: "small",
523
- fullWidth: true,
524
- autoFocus: true,
525
- sx: {
526
- mb: 2
527
- }
842
+ children: /*#__PURE__*/ _jsx(PosProductGrid, {
843
+ search: search,
844
+ onSearch: setSearch,
845
+ onSearchEnter: ()=>void handleSearchEnter(),
846
+ scanAdornment: scanAdornment,
847
+ categories: (categoryDocs != null ? categoryDocs : []).map((category)=>{
848
+ var _category_name;
849
+ return {
850
+ $id: category.$id,
851
+ name: String((_category_name = category.name) != null ? _category_name : 'Category')
852
+ };
528
853
  }),
529
- /*#__PURE__*/ _jsx(Box, {
530
- sx: {
531
- mb: gridPlan.notices.length ? 2 : 0
532
- },
533
- children: /*#__PURE__*/ _jsx(ListQueryNotices, {
534
- refused: [],
535
- notices: gridPlan.notices
536
- })
537
- }),
538
- /*#__PURE__*/ _jsx(Box, {
539
- sx: {
540
- display: 'grid',
541
- gap: 1.5,
542
- gridTemplateColumns: 'repeat(auto-fill, minmax(140px, 1fr))'
543
- },
544
- children: visible.map((product)=>{
545
- var _ref;
546
- var _product_variants_;
547
- return /*#__PURE__*/ _jsxs(Card, {
548
- variant: "outlined",
549
- children: [
550
- /*#__PURE__*/ _jsxs(CardActionArea, {
551
- onClick: ()=>addProduct(product),
552
- sx: {
553
- p: 1.5,
554
- minHeight: 88
555
- },
556
- children: [
557
- /*#__PURE__*/ _jsx(Typography, {
558
- variant: "body2",
559
- sx: {
560
- fontWeight: 600
561
- },
562
- noWrap: true,
563
- children: product.name
564
- }),
565
- /*#__PURE__*/ _jsxs(Typography, {
566
- variant: "caption",
567
- color: "text.secondary",
568
- children: [
569
- `$${(_ref = (_product_variants_ = product.variants[0]) == null ? void 0 : _product_variants_.priceUsd) != null ? _ref : 0}`,
570
- product.variants.length > 1 ? ` · ${product.variants.length} variants` : ''
571
- ]
572
- })
573
- ]
574
- }),
575
- product.variants.length > 1 ? /*#__PURE__*/ _jsx(Box, {
576
- sx: {
577
- display: 'flex',
578
- flexWrap: 'wrap',
579
- gap: 0.5,
580
- p: 0.5
581
- },
582
- children: product.variants.slice(0, 6).map((variant)=>{
583
- var _variant_options;
584
- return /*#__PURE__*/ _jsx(Chip, {
585
- label: Object.values((_variant_options = variant.options) != null ? _variant_options : {}).join('/') || 'Default',
586
- size: "small",
587
- onClick: ()=>addProduct(product, variant)
588
- }, variant.id);
589
- })
590
- }) : null
591
- ]
592
- }, product.$id);
593
- })
594
- })
595
- ]
854
+ categoryId: categoryId,
855
+ onCategory: setCategoryId,
856
+ notices: gridPlan.notices,
857
+ hostId: hostId,
858
+ products: products,
859
+ basketCounts: basketCounts,
860
+ onTap: (product)=>{
861
+ if (sale) return void notify('Finish or void the open sale first', 'info');
862
+ tapProduct(product);
863
+ }
864
+ })
596
865
  }),
597
- /*#__PURE__*/ _jsxs(Box, {
866
+ wide ? /*#__PURE__*/ _jsx(Box, {
598
867
  sx: {
599
- width: 380,
868
+ width: {
869
+ md: 400,
870
+ lg: 440
871
+ },
600
872
  borderLeft: 1,
601
873
  borderColor: 'divider',
602
874
  display: 'flex',
603
875
  flexDirection: 'column',
604
876
  p: 2,
605
- gap: 1
877
+ overflowY: 'auto'
878
+ },
879
+ children: registerPanel
880
+ }) : null
881
+ ]
882
+ }),
883
+ !wide ? /*#__PURE__*/ _jsxs(_Fragment, {
884
+ children: [
885
+ /*#__PURE__*/ _jsxs(Box, {
886
+ ref: bottomBarRef,
887
+ sx: {
888
+ position: 'fixed',
889
+ left: 0,
890
+ right: 0,
891
+ bottom: 0,
892
+ p: 1.5,
893
+ bgcolor: 'background.paper',
894
+ borderTop: 1,
895
+ borderColor: 'divider',
896
+ display: 'flex',
897
+ gap: 1,
898
+ alignItems: 'center',
899
+ zIndex: theme.zIndex.appBar
606
900
  },
607
901
  children: [
902
+ /*#__PURE__*/ _jsx(Badge, {
903
+ badgeContent: units,
904
+ color: "primary",
905
+ children: /*#__PURE__*/ _jsx(Button, {
906
+ variant: "outlined",
907
+ onClick: ()=>setSheetOpen(true),
908
+ sx: {
909
+ minHeight: POS_TOUCH_PX
910
+ },
911
+ children: sale ? 'Payment' : 'Cart'
912
+ })
913
+ }),
608
914
  /*#__PURE__*/ _jsx(Typography, {
609
915
  variant: "h6",
610
- children: 'Register'
611
- }),
612
- !planReady ? /*#__PURE__*/ _jsx(Typography, {
613
- variant: "body2",
614
- color: "text.secondary",
615
- children: 'Checking your plan…'
616
- }) : usableRegisters.length === 0 ? /*#__PURE__*/ _jsx(Typography, {
617
- variant: "body2",
618
- color: "warning.main",
619
- children: registers.length > 0 ? 'Your registers exceed your plan — remove extras or ' + 'upgrade in Billing to take payments.' : 'No POS register yet. Add one under Commerce → Settings → ' + 'POS registers before taking payments.'
620
- }) : usableRegisters.length > 1 ? /*#__PURE__*/ _jsx(TextField, {
621
- label: "Register",
622
- value: registerId,
623
- onChange: (event)=>setRegisterId(event.target.value),
624
- size: "small",
625
- select: true,
626
- children: usableRegisters.map((register)=>/*#__PURE__*/ _jsx(MenuItem, {
627
- value: register.$id,
628
- children: register.name
629
- }, register.$id))
630
- }) : /*#__PURE__*/ _jsx(Typography, {
631
- variant: "body2",
632
- color: "text.secondary",
633
- children: (_usableRegisters_ = usableRegisters[0]) == null ? void 0 : _usableRegisters_.name
634
- }),
635
- ((_ref = locationDocs == null ? void 0 : locationDocs.length) != null ? _ref : 0) > 1 ? /*#__PURE__*/ _jsx(TextField, {
636
- label: "Location",
637
- value: locationId,
638
- onChange: (event)=>setLocationId(event.target.value),
639
- size: "small",
640
- select: true,
641
- children: (locationDocs != null ? locationDocs : []).map((location)=>/*#__PURE__*/ _jsx(MenuItem, {
642
- value: location.$id,
643
- children: location.name
644
- }, location.$id))
645
- }) : null,
646
- /*#__PURE__*/ _jsx(Box, {
647
916
  sx: {
648
917
  flex: 1,
649
- overflowY: 'auto'
918
+ textAlign: 'right'
650
919
  },
651
- children: lines.length === 0 ? /*#__PURE__*/ _jsx(Typography, {
652
- variant: "body2",
653
- color: "text.secondary",
654
- children: 'Tap products to add them.'
655
- }) : lines.map((line, index)=>{
656
- var _shortfalls_index;
657
- return /*#__PURE__*/ _jsxs(Box, {
658
- sx: {
659
- py: 0.5
660
- },
661
- children: [
662
- /*#__PURE__*/ _jsxs(Stack, {
663
- direction: "row",
664
- spacing: 1,
665
- sx: {
666
- alignItems: 'center'
667
- },
668
- children: [
669
- /*#__PURE__*/ _jsxs(Typography, {
670
- variant: "body2",
671
- sx: {
672
- flex: 1
673
- },
674
- noWrap: true,
675
- children: [
676
- `${line.quantity}× ${line.name}`,
677
- line.variantLabel ? ` (${line.variantLabel})` : ''
678
- ]
679
- }),
680
- /*#__PURE__*/ _jsx(Typography, {
681
- variant: "body2",
682
- children: usd(line.unitAmountCents * line.quantity)
683
- }),
684
- /*#__PURE__*/ _jsx(Button, {
685
- size: "small",
686
- color: "error",
687
- onClick: ()=>setLines((prev)=>prev.filter((_item, itemIndex)=>itemIndex !== index)),
688
- children: '✕'
689
- })
690
- ]
691
- }),
692
- shortfalls[index] ? // AGL-2357: said, not enforced. The cashier reads it and
693
- // rings the sale through — the settle buttons below are
694
- // untouched by this.
695
- /*#__PURE__*/ _jsx(Typography, {
696
- variant: "caption",
697
- color: "warning.main",
698
- children: `Only ${(_shortfalls_index = shortfalls[index]) == null ? void 0 : _shortfalls_index.available} in stock — selling ${line.quantity}`
699
- }) : null
700
- ]
701
- }, index);
702
- })
703
- }),
704
- /*#__PURE__*/ _jsxs(Stack, {
705
- direction: "row",
706
- spacing: 1,
707
- children: [
708
- /*#__PURE__*/ _jsx(TextField, {
709
- label: "Discount %",
710
- value: discountPct || '',
711
- onChange: (event)=>setDiscountPct(Math.min(100, Math.max(0, Number(event.target.value) || 0))),
712
- size: "small",
713
- sx: {
714
- width: 110
715
- },
716
- slotProps: {
717
- htmlInput: {
718
- inputMode: 'numeric'
719
- }
720
- }
721
- }),
722
- /*#__PURE__*/ _jsx(TextField, {
723
- label: "Customer email",
724
- value: customerEmail,
725
- onChange: (event)=>setCustomerEmail(event.target.value),
726
- size: "small",
727
- sx: {
728
- flex: 1
729
- }
730
- })
731
- ]
920
+ children: usd(sale ? sale.dueCents : estimateCents)
732
921
  }),
733
- /*#__PURE__*/ _jsxs(Box, {
922
+ !sale ? /*#__PURE__*/ _jsx(Button, {
923
+ variant: "contained",
924
+ disabled: opening || lines.length === 0 || !registerId,
925
+ onClick: ()=>void charge(),
734
926
  sx: {
735
- display: 'flex',
736
- justifyContent: 'space-between'
927
+ minHeight: POS_TOUCH_PX
737
928
  },
738
- children: [
739
- /*#__PURE__*/ _jsx(Typography, {
740
- variant: "h6",
741
- children: 'Due'
742
- }),
743
- /*#__PURE__*/ _jsx(Typography, {
744
- variant: "h6",
745
- children: usd(dueCents)
746
- })
747
- ]
748
- }),
749
- /*#__PURE__*/ _jsxs(Stack, {
750
- direction: "row",
751
- spacing: 1,
752
- children: [
753
- /*#__PURE__*/ _jsx(Button, {
754
- variant: "contained",
755
- color: "primary",
756
- disabled: lines.length === 0,
757
- onClick: ()=>setPaying('cash'),
758
- sx: {
759
- flex: 1
760
- },
761
- children: 'Cash'
762
- }),
763
- /*#__PURE__*/ _jsx(Button, {
764
- variant: "contained",
765
- disabled: busy || lines.length === 0,
766
- onClick: ()=>void settle('link'),
767
- sx: {
768
- flex: 1
769
- },
770
- children: 'Card (QR)'
771
- }),
772
- /*#__PURE__*/ _jsx(Button, {
773
- variant: "outlined",
774
- disabled: lines.length === 0 || openStays.length === 0,
775
- onClick: ()=>setPaying('folio'),
776
- sx: {
777
- flex: 1
778
- },
779
- children: 'Room'
780
- })
781
- ]
782
- }),
783
- lastReceipt ? /*#__PURE__*/ _jsx(Button, {
784
- size: "small",
785
- onClick: printReceipt,
786
- children: 'Print last receipt'
929
+ children: 'Charge'
787
930
  }) : null
788
931
  ]
789
- })
790
- ]
791
- }),
792
- /*#__PURE__*/ _jsxs(Dialog, {
793
- open: paying === 'cash',
794
- onClose: ()=>setPaying(null),
795
- maxWidth: "xs",
796
- fullWidth: true,
797
- children: [
798
- /*#__PURE__*/ _jsx(DialogTitle, {
799
- children: `Cash — due ${usd(dueCents)}`
800
932
  }),
801
- /*#__PURE__*/ _jsxs(DialogContent, {
802
- sx: {
803
- display: 'flex',
804
- flexDirection: 'column',
805
- gap: 2
806
- },
807
- children: [
808
- /*#__PURE__*/ _jsx(TextField, {
809
- label: "Cash received ($)",
810
- value: cashReceived,
811
- onChange: (event)=>setCashReceived(event.target.value.replace(/[^0-9.]/g, '')),
812
- size: "small",
813
- autoFocus: true,
933
+ /*#__PURE__*/ _jsx(Drawer, {
934
+ anchor: "bottom",
935
+ open: sheetOpen,
936
+ onClose: ()=>setSheetOpen(false),
937
+ slotProps: {
938
+ paper: {
814
939
  sx: {
815
- mt: 1
816
- },
817
- slotProps: {
818
- htmlInput: {
819
- inputMode: 'decimal'
820
- }
940
+ maxHeight: '90dvh',
941
+ p: 2,
942
+ borderTopLeftRadius: 16,
943
+ borderTopRightRadius: 16
821
944
  }
822
- }),
823
- Number(cashReceived) * 100 >= dueCents && cashReceived ? /*#__PURE__*/ _jsx(Alert, {
824
- severity: "success",
825
- children: `Change: ${usd(Math.round(Number(cashReceived) * 100) - dueCents)}`
826
- }) : null
827
- ]
828
- }),
829
- /*#__PURE__*/ _jsxs(DialogActions, {
830
- children: [
831
- /*#__PURE__*/ _jsx(Button, {
832
- onClick: ()=>setPaying(null),
833
- children: 'Cancel'
834
- }),
835
- /*#__PURE__*/ _jsx(Button, {
836
- variant: "contained",
837
- color: "primary",
838
- disabled: busy || Math.round(Number(cashReceived) * 100) < dueCents,
839
- // `onClick={settle}` would hand MUI's click event to `tender`.
840
- onClick: ()=>void settle('cash'),
841
- children: 'Complete sale'
842
- })
843
- ]
844
- })
845
- ]
846
- }),
847
- /*#__PURE__*/ _jsxs(Dialog, {
848
- open: paying === 'folio',
849
- onClose: ()=>setPaying(null),
850
- maxWidth: "xs",
851
- fullWidth: true,
852
- children: [
853
- /*#__PURE__*/ _jsx(DialogTitle, {
854
- children: `Charge to room — ${usd(dueCents)}`
855
- }),
856
- /*#__PURE__*/ _jsx(DialogContent, {
857
- sx: {
858
- display: 'flex',
859
- flexDirection: 'column',
860
- gap: 2
945
+ }
861
946
  },
862
- children: /*#__PURE__*/ _jsx(TextField, {
863
- label: "Checked-in stay",
864
- value: folioReservation,
865
- onChange: (event)=>setFolioReservation(event.target.value),
866
- size: "small",
867
- select: true,
868
- sx: {
869
- mt: 1
870
- },
871
- children: openStays.map((stay)=>{
872
- var _ref, _stay_guestName;
873
- return /*#__PURE__*/ _jsx(MenuItem, {
874
- value: stay.$id,
875
- children: (_ref = (_stay_guestName = stay.guestName) != null ? _stay_guestName : stay.guestEmail) != null ? _ref : stay.$id
876
- }, stay.$id);
877
- })
878
- })
879
- }),
880
- /*#__PURE__*/ _jsxs(DialogActions, {
881
- children: [
882
- /*#__PURE__*/ _jsx(Button, {
883
- onClick: ()=>setPaying(null),
884
- children: 'Cancel'
885
- }),
886
- /*#__PURE__*/ _jsx(Button, {
887
- variant: "contained",
888
- color: "primary",
889
- disabled: busy || !folioReservation,
890
- onClick: ()=>void settle('folio'),
891
- children: 'Charge folio'
892
- })
893
- ]
947
+ children: registerPanel
894
948
  })
895
949
  ]
896
- }),
950
+ }) : null,
951
+ /*#__PURE__*/ _jsx(PosItemDialog, _extends({
952
+ product: (_ref4 = itemSheet == null ? void 0 : itemSheet.product) != null ? _ref4 : null,
953
+ initial: itemSheet == null ? void 0 : itemSheet.initial,
954
+ onClose: ()=>setItemSheet(null),
955
+ onConfirm: (choice)=>{
956
+ const sheet = itemSheet;
957
+ setItemSheet(null);
958
+ if (!sheet) return;
959
+ if (sheet.editIndex === undefined) {
960
+ addProduct(sheet.product, choice.variant, choice.modifiers, choice.quantity);
961
+ return;
962
+ }
963
+ const at = sheet.editIndex;
964
+ setLines((prev)=>prev.map((line, index)=>index === at ? registerLineFor(sheet.product, choice) : line));
965
+ }
966
+ }, (itemSheet == null ? void 0 : itemSheet.editIndex) !== undefined ? {
967
+ onRemove: ()=>{
968
+ const at = itemSheet.editIndex;
969
+ setItemSheet(null);
970
+ setLines((prev)=>prev.filter((_line, index)=>index !== at));
971
+ }
972
+ } : {})),
897
973
  /*#__PURE__*/ _jsxs(Dialog, {
898
- open: Boolean(cardUrl),
899
- onClose: closeCardDialog,
974
+ open: Boolean(pairing),
975
+ onClose: ()=>setPairing(null),
900
976
  maxWidth: "xs",
901
977
  fullWidth: true,
902
978
  children: [
903
979
  /*#__PURE__*/ _jsx(DialogTitle, {
904
- children: 'Customer pays by card'
980
+ children: 'Pair a customer display'
905
981
  }),
906
982
  /*#__PURE__*/ _jsxs(DialogContent, {
907
983
  sx: {
@@ -916,33 +992,29 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
916
992
  sx: {
917
993
  mt: 1
918
994
  },
919
- children: 'Show this QR (or open the link on the customer display); the ' + 'order completes automatically once paid. Stripe Terminal ' + 'readers can replace this step later.'
995
+ children: 'On the tablet facing your customer, open this address and enter the code. ' + 'The code works once and expires in 10 minutes.'
920
996
  }),
921
- /*#__PURE__*/ _jsx(Box, {
997
+ /*#__PURE__*/ _jsx(Typography, {
998
+ variant: "body1",
922
999
  sx: {
923
- display: 'flex',
924
- justifyContent: 'center'
1000
+ wordBreak: 'break-all'
925
1001
  },
926
- children: /*#__PURE__*/ _jsx(QRCodeSVG, {
927
- value: cardUrl,
928
- size: 256,
929
- level: "L",
930
- marginSize: 4,
931
- title: "Payment QR",
932
- role: "img"
933
- })
1002
+ children: `${typeof window === 'undefined' ? '' : window.location.origin}/kiosk/commerce/pos-display`
934
1003
  }),
935
- /*#__PURE__*/ _jsx(Button, {
936
- size: "small",
937
- href: cardUrl,
938
- target: "_blank",
939
- children: 'Open payment page'
1004
+ /*#__PURE__*/ _jsx(Typography, {
1005
+ variant: "h3",
1006
+ component: "p",
1007
+ sx: {
1008
+ textAlign: 'center',
1009
+ letterSpacing: 8
1010
+ },
1011
+ children: pairing == null ? void 0 : pairing.code
940
1012
  })
941
1013
  ]
942
1014
  }),
943
1015
  /*#__PURE__*/ _jsx(DialogActions, {
944
1016
  children: /*#__PURE__*/ _jsx(Button, {
945
- onClick: closeCardDialog,
1017
+ onClick: ()=>setPairing(null),
946
1018
  children: 'Done'
947
1019
  })
948
1020
  })