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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/package.json +13 -13
  2. package/src/lib/components/cart-extras.d.ts +6 -0
  3. package/src/lib/components/cart-extras.js +4 -1
  4. package/src/lib/components/cart-extras.js.map +1 -1
  5. package/src/lib/components/cart-fulfillment.d.ts +117 -0
  6. package/src/lib/components/cart-fulfillment.js +367 -0
  7. package/src/lib/components/cart-fulfillment.js.map +1 -0
  8. package/src/lib/components/cart.js +52 -10
  9. package/src/lib/components/cart.js.map +1 -1
  10. package/src/lib/components/commerce-console-page.js +42 -1
  11. package/src/lib/components/commerce-console-page.js.map +1 -1
  12. package/src/lib/components/console/host-orders-card.component.js +38 -10
  13. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  14. package/src/lib/components/console/local-delivery-settings-card.component.d.ts +37 -0
  15. package/src/lib/components/console/local-delivery-settings-card.component.js +542 -0
  16. package/src/lib/components/console/local-delivery-settings-card.component.js.map +1 -0
  17. package/src/lib/components/console/locations-card.component.js +177 -3
  18. package/src/lib/components/console/locations-card.component.js.map +1 -1
  19. package/src/lib/components/console/order-detail-dialog.component.js +10 -0
  20. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
  21. package/src/lib/components/console/order-local-fulfillment-panel.component.d.ts +44 -0
  22. package/src/lib/components/console/order-local-fulfillment-panel.component.js +265 -0
  23. package/src/lib/components/console/order-local-fulfillment-panel.component.js.map +1 -0
  24. package/src/lib/components/console/order-offline-notice.component.d.ts +12 -0
  25. package/src/lib/components/console/order-offline-notice.component.js +53 -0
  26. package/src/lib/components/console/order-offline-notice.component.js.map +1 -0
  27. package/src/lib/components/console/pickup-delivery-queue-card.component.d.ts +35 -0
  28. package/src/lib/components/console/pickup-delivery-queue-card.component.js +222 -0
  29. package/src/lib/components/console/pickup-delivery-queue-card.component.js.map +1 -0
  30. package/src/lib/components/console/pos/pos-api.d.ts +24 -1
  31. package/src/lib/components/console/pos/pos-api.js +13 -0
  32. package/src/lib/components/console/pos/pos-api.js.map +1 -1
  33. package/src/lib/components/console/pos/pos-item-dialog.component.d.ts +12 -0
  34. package/src/lib/components/console/pos/pos-item-dialog.component.js +10 -8
  35. package/src/lib/components/console/pos/pos-item-dialog.component.js.map +1 -1
  36. package/src/lib/components/console/pos/pos-kiosk-queue.component.d.ts +22 -0
  37. package/src/lib/components/console/pos/pos-kiosk-queue.component.js +168 -0
  38. package/src/lib/components/console/pos/pos-kiosk-queue.component.js.map +1 -0
  39. package/src/lib/components/console/pos/pos-tender-dialogs.component.d.ts +18 -0
  40. package/src/lib/components/console/pos/pos-tender-dialogs.component.js +116 -1
  41. package/src/lib/components/console/pos/pos-tender-dialogs.component.js.map +1 -1
  42. package/src/lib/components/console/pos/pos-tender-panel.component.d.ts +8 -0
  43. package/src/lib/components/console/pos/pos-tender-panel.component.js +77 -18
  44. package/src/lib/components/console/pos/pos-tender-panel.component.js.map +1 -1
  45. package/src/lib/components/console/pos/use-pos-display.d.ts +5 -2
  46. package/src/lib/components/console/pos/use-pos-display.js +3 -2
  47. package/src/lib/components/console/pos/use-pos-display.js.map +1 -1
  48. package/src/lib/components/console/pos-devices-card.component.js +2 -2
  49. package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
  50. package/src/lib/components/console/pos-display/pairing-screen.d.ts +5 -2
  51. package/src/lib/components/console/pos-display/pairing-screen.js +7 -5
  52. package/src/lib/components/console/pos-display/pairing-screen.js.map +1 -1
  53. package/src/lib/components/console/pos-display/pos-display-api.d.ts +7 -1
  54. package/src/lib/components/console/pos-display/pos-display-api.js +7 -2
  55. package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -1
  56. package/src/lib/components/console/pos-kiosk/kiosk-checkout.d.ts +41 -0
  57. package/src/lib/components/console/pos-kiosk/kiosk-checkout.js +275 -0
  58. package/src/lib/components/console/pos-kiosk/kiosk-checkout.js.map +1 -0
  59. package/src/lib/components/console/pos-kiosk/kiosk-menu.d.ts +32 -0
  60. package/src/lib/components/console/pos-kiosk/kiosk-menu.js +344 -0
  61. package/src/lib/components/console/pos-kiosk/kiosk-menu.js.map +1 -0
  62. package/src/lib/components/console/pos-kiosk/kiosk-staff.d.ts +12 -0
  63. package/src/lib/components/console/pos-kiosk/kiosk-staff.js +257 -0
  64. package/src/lib/components/console/pos-kiosk/kiosk-staff.js.map +1 -0
  65. package/src/lib/components/console/pos-kiosk/pos-kiosk-api.d.ts +114 -0
  66. package/src/lib/components/console/pos-kiosk/pos-kiosk-api.js +189 -0
  67. package/src/lib/components/console/pos-kiosk/pos-kiosk-api.js.map +1 -0
  68. package/src/lib/components/console/pos-kiosk/use-kiosk-idle.d.ts +16 -0
  69. package/src/lib/components/console/pos-kiosk/use-kiosk-idle.js +80 -0
  70. package/src/lib/components/console/pos-kiosk/use-kiosk-idle.js.map +1 -0
  71. package/src/lib/components/console/pos-kiosk-page.component.d.ts +28 -0
  72. package/src/lib/components/console/pos-kiosk-page.component.js +791 -0
  73. package/src/lib/components/console/pos-kiosk-page.component.js.map +1 -0
  74. package/src/lib/components/console/pos-offline/pos-offline-api.d.ts +44 -0
  75. package/src/lib/components/console/pos-offline/pos-offline-api.js +69 -0
  76. package/src/lib/components/console/pos-offline/pos-offline-api.js.map +1 -0
  77. package/src/lib/components/console/pos-offline/pos-offline-banner.component.d.ts +7 -0
  78. package/src/lib/components/console/pos-offline/pos-offline-banner.component.js +135 -0
  79. package/src/lib/components/console/pos-offline/pos-offline-banner.component.js.map +1 -0
  80. package/src/lib/components/console/pos-offline/pos-offline-checkout.component.d.ts +32 -0
  81. package/src/lib/components/console/pos-offline/pos-offline-checkout.component.js +287 -0
  82. package/src/lib/components/console/pos-offline/pos-offline-checkout.component.js.map +1 -0
  83. package/src/lib/components/console/pos-offline/pos-offline-store.d.ts +63 -0
  84. package/src/lib/components/console/pos-offline/pos-offline-store.js +145 -0
  85. package/src/lib/components/console/pos-offline/pos-offline-store.js.map +1 -0
  86. package/src/lib/components/console/pos-offline/register-offline.d.ts +24 -0
  87. package/src/lib/components/console/pos-offline/register-offline.js +24 -0
  88. package/src/lib/components/console/pos-offline/register-offline.js.map +1 -0
  89. package/src/lib/components/console/pos-offline/use-pos-offline.d.ts +85 -0
  90. package/src/lib/components/console/pos-offline/use-pos-offline.js +542 -0
  91. package/src/lib/components/console/pos-offline/use-pos-offline.js.map +1 -0
  92. package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js +72 -17
  93. package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js.map +1 -1
  94. package/src/lib/components/console/pos-page.component.js +174 -27
  95. package/src/lib/components/console/pos-page.component.js.map +1 -1
  96. package/src/lib/components/console/products-hub-card.component.js +35 -10
  97. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  98. package/src/lib/components/console/shipping-settings-card.component.js +1 -1
  99. package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
  100. package/src/lib/components/console/stock-movements-card.component.js +48 -16
  101. package/src/lib/components/console/stock-movements-card.component.js.map +1 -1
  102. package/src/lib/components/console/store-zones.d.ts +8 -1
  103. package/src/lib/components/console/store-zones.js +11 -1
  104. package/src/lib/components/console/store-zones.js.map +1 -1
  105. package/src/lib/components/order-status.js +66 -0
  106. package/src/lib/components/order-status.js.map +1 -1
  107. package/src/lib/components/product-detail.js +42 -15
  108. package/src/lib/components/product-detail.js.map +1 -1
  109. package/src/lib/constants/local-fulfillment-list-query.d.ts +48 -0
  110. package/src/lib/constants/local-fulfillment-list-query.js +84 -0
  111. package/src/lib/constants/local-fulfillment-list-query.js.map +1 -0
  112. package/src/lib/constants/orders-list-query.d.ts +7 -2
  113. package/src/lib/constants/orders-list-query.js +89 -8
  114. package/src/lib/constants/orders-list-query.js.map +1 -1
  115. package/src/lib/constants/pos-shift-list-query.d.ts +22 -0
  116. package/src/lib/constants/pos-shift-list-query.js +115 -0
  117. package/src/lib/constants/pos-shift-list-query.js.map +1 -0
  118. package/src/lib/constants/product-list-query.d.ts +6 -1
  119. package/src/lib/constants/product-list-query.js +77 -8
  120. package/src/lib/constants/product-list-query.js.map +1 -1
  121. package/src/lib/constants/stock-movement-query.d.ts +3 -2
  122. package/src/lib/constants/stock-movement-query.js +52 -8
  123. package/src/lib/constants/stock-movement-query.js.map +1 -1
  124. package/src/lib/model/buyer-notifications.d.ts +5 -3
  125. package/src/lib/model/buyer-notifications.js +25 -4
  126. package/src/lib/model/buyer-notifications.js.map +1 -1
  127. package/src/lib/model/commerce-orders.d.ts +59 -0
  128. package/src/lib/model/commerce-orders.js.map +1 -1
  129. package/src/lib/model/commerce-pos-kiosk.d.ts +175 -0
  130. package/src/lib/model/commerce-pos-kiosk.js +147 -0
  131. package/src/lib/model/commerce-pos-kiosk.js.map +1 -0
  132. package/src/lib/model/commerce-pos-offline.d.ts +234 -0
  133. package/src/lib/model/commerce-pos-offline.js +301 -0
  134. package/src/lib/model/commerce-pos-offline.js.map +1 -0
  135. package/src/lib/model/commerce-pos-ops.d.ts +16 -6
  136. package/src/lib/model/commerce-pos-ops.js +12 -4
  137. package/src/lib/model/commerce-pos-ops.js.map +1 -1
  138. package/src/lib/model/commerce-pos.d.ts +13 -1
  139. package/src/lib/model/commerce-pos.js +5 -4
  140. package/src/lib/model/commerce-pos.js.map +1 -1
  141. package/src/lib/model/commerce-stock-holds.d.ts +24 -3
  142. package/src/lib/model/commerce-stock-holds.js +26 -8
  143. package/src/lib/model/commerce-stock-holds.js.map +1 -1
  144. package/src/lib/model/commerce.d.ts +3 -0
  145. package/src/lib/model/commerce.js.map +1 -1
  146. package/src/lib/model/index.d.ts +4 -0
  147. package/src/lib/model/index.js +4 -0
  148. package/src/lib/model/index.js.map +1 -1
  149. package/src/lib/model/local-fulfillment-settings.d.ts +178 -0
  150. package/src/lib/model/local-fulfillment-settings.js +361 -0
  151. package/src/lib/model/local-fulfillment-settings.js.map +1 -0
  152. package/src/lib/model/order-list-fields.d.ts +10 -1
  153. package/src/lib/model/order-list-fields.js +1 -0
  154. package/src/lib/model/order-list-fields.js.map +1 -1
  155. package/src/lib/model/order-local-fulfillment.d.ts +131 -0
  156. package/src/lib/model/order-local-fulfillment.js +125 -0
  157. package/src/lib/model/order-local-fulfillment.js.map +1 -0
  158. package/src/lib/model/order-shipping-export.js +4 -0
  159. package/src/lib/model/order-shipping-export.js.map +1 -1
  160. package/src/lib/model/order-status-view.d.ts +20 -1
  161. package/src/lib/model/order-status-view.js +60 -6
  162. package/src/lib/model/order-status-view.js.map +1 -1
  163. package/src/lib/plugin-config.js +6 -2
  164. package/src/lib/plugin-config.js.map +1 -1
  165. package/src/lib/plugin.js +8 -0
  166. package/src/lib/plugin.js.map +1 -1
  167. package/src/lib/pos-kiosk-config.d.ts +26 -0
  168. package/src/lib/pos-kiosk-config.js +59 -0
  169. package/src/lib/pos-kiosk-config.js.map +1 -0
  170. package/src/lib/server/api-v1/openapi.js +8 -0
  171. package/src/lib/server/api-v1/openapi.js.map +1 -1
  172. package/src/lib/server/api-v1/order-view.d.ts +19 -0
  173. package/src/lib/server/api-v1/order-view.js +35 -0
  174. package/src/lib/server/api-v1/order-view.js.map +1 -1
  175. package/src/lib/server/billing-webhook.js +238 -87
  176. package/src/lib/server/billing-webhook.js.map +1 -1
  177. package/src/lib/server/cart-checkout.d.ts +2 -0
  178. package/src/lib/server/cart-checkout.js +173 -13
  179. package/src/lib/server/cart-checkout.js.map +1 -1
  180. package/src/lib/server/checkout-extras.js +18 -4
  181. package/src/lib/server/checkout-extras.js.map +1 -1
  182. package/src/lib/server/checkout.js +71 -14
  183. package/src/lib/server/checkout.js.map +1 -1
  184. package/src/lib/server/local-fulfillment-status.d.ts +86 -0
  185. package/src/lib/server/local-fulfillment-status.js +379 -0
  186. package/src/lib/server/local-fulfillment-status.js.map +1 -0
  187. package/src/lib/server/local-fulfillment.d.ts +190 -0
  188. package/src/lib/server/local-fulfillment.js +580 -0
  189. package/src/lib/server/local-fulfillment.js.map +1 -0
  190. package/src/lib/server/membership-register.js +3 -1
  191. package/src/lib/server/membership-register.js.map +1 -1
  192. package/src/lib/server/order-notifications.js +61 -0
  193. package/src/lib/server/order-notifications.js.map +1 -1
  194. package/src/lib/server/pos-display.d.ts +27 -1
  195. package/src/lib/server/pos-display.js +50 -9
  196. package/src/lib/server/pos-display.js.map +1 -1
  197. package/src/lib/server/pos-kiosk-principal.d.ts +29 -0
  198. package/src/lib/server/pos-kiosk-principal.js +42 -0
  199. package/src/lib/server/pos-kiosk-principal.js.map +1 -0
  200. package/src/lib/server/pos-kiosk.d.ts +54 -0
  201. package/src/lib/server/pos-kiosk.js +972 -0
  202. package/src/lib/server/pos-kiosk.js.map +1 -0
  203. package/src/lib/server/pos-offline-sync.d.ts +85 -0
  204. package/src/lib/server/pos-offline-sync.js +765 -0
  205. package/src/lib/server/pos-offline-sync.js.map +1 -0
  206. package/src/lib/server/pos-order.js +22 -4
  207. package/src/lib/server/pos-order.js.map +1 -1
  208. package/src/lib/server/pos-payment.d.ts +6 -0
  209. package/src/lib/server/pos-payment.js +225 -6
  210. package/src/lib/server/pos-payment.js.map +1 -1
  211. package/src/lib/server/pos-print.js +5 -0
  212. package/src/lib/server/pos-print.js.map +1 -1
  213. package/src/lib/server/pos-return.js +27 -0
  214. package/src/lib/server/pos-return.js.map +1 -1
  215. package/src/lib/server/pos-sale.d.ts +6 -0
  216. package/src/lib/server/pos-sale.js +2 -0
  217. package/src/lib/server/pos-sale.js.map +1 -1
  218. package/src/lib/server/pos-shift.js +8 -1
  219. package/src/lib/server/pos-shift.js.map +1 -1
  220. package/src/lib/server/refund.js +3 -0
  221. package/src/lib/server/refund.js.map +1 -1
  222. package/src/lib/server/stock-hold.d.ts +9 -1
  223. package/src/lib/server/stock-hold.js +15 -7
  224. package/src/lib/server/stock-hold.js.map +1 -1
  225. package/src/lib/server.js +16 -0
  226. package/src/lib/server.js.map +1 -1
  227. package/src/lib/tenant-emails.js +161 -0
  228. package/src/lib/tenant-emails.js.map +1 -1
@@ -24,14 +24,14 @@ import { useSnackbar } from "@aglyn/shared-ui-snackstack";
24
24
  import { planListQuery } from "@aglyn/shared-ui-jsx/const/list-query-plan";
25
25
  import { useViewportFill } from "@aglyn/shared-ui-jsx/hooks/use-viewport-fill";
26
26
  import { nameSearchNormalizers } from "@aglyn/aglyn/app-utils/name-search";
27
- import { Badge, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Drawer, MenuItem, Stack, TextField, Typography, useMediaQuery, useTheme } from "@mui/material";
27
+ import { Badge, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Drawer, MenuItem, Stack, TextField, ToggleButton, ToggleButtonGroup, Typography, useMediaQuery, useTheme } from "@mui/material";
28
28
  import { collection, getDocs, limit, query, where } from "firebase/firestore";
29
29
  import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
30
30
  import { useFirestore, useUser } from "@aglyn/tenant-feature-instance";
31
31
  import { useFirestoreCollection } from "@aglyn/tenant-feature-instance";
32
32
  import { listQueryConstraints } from "@aglyn/tenant-feature-instance/hooks/use-list-query";
33
33
  import { useOrgPlan } from "@aglyn/tenant-feature-instance";
34
- import { newAttemptKey, openPosSale, posRegisterContext, PosRequestError, usd } from "./pos/pos-api.js";
34
+ import { newAttemptKey, openPosSale, posRegisterContext, posTender, PosRequestError, usd } from "./pos/pos-api.js";
35
35
  import { PosCartPanel } from "./pos/pos-cart-panel.component.js";
36
36
  import { ScanAdornment } from "../../barcode/scan-button.component.js";
37
37
  import { useScannerWedge } from "../../barcode/scanner-wedge.js";
@@ -40,9 +40,11 @@ import { POS_QUICK_KEYS, POS_TOUCH_PX, PosProductGrid } from "./pos/pos-product-
40
40
  import { PosItemDialog, posItemNeedsChoice } from "./pos/pos-item-dialog.component.js";
41
41
  import { PosReceiptPanel } from "./pos/pos-receipt-panel.component.js";
42
42
  import { PosTenderPanel } from "./pos/pos-tender-panel.component.js";
43
+ import { PosKioskQueue } from "./pos/pos-kiosk-queue.component.js";
43
44
  import { posDisplayTipCents, usePosDisplay } from "./pos/use-pos-display.js";
44
45
  import { useConsoleWidgetSlot } from "@aglyn/aglyn/app-utils/console-widget-slot-context";
45
46
  import { POS_ORDERS_ZONE } from "./pos-zones.js";
47
+ import { PosOfflineBanner, PosOfflineCheckout, usePosOffline } from "./pos-offline/register-offline.js";
46
48
  /** The till sells active products only; every read of the catalog asks it. */ const SELLABLE = {
47
49
  field: 'status',
48
50
  op: 'equals',
@@ -184,8 +186,8 @@ import { POS_ORDERS_ZONE } from "./pos-zones.js";
184
186
  });
185
187
  });
186
188
  export function PosConsolePage({ hostId }) {
187
- var _ref, _ref1, _ref2, _ref3, _ref4;
188
- var _usableRegisters_find, _usableRegisters_;
189
+ var _ref, _ref1, _ref2, _liveRegister_openShiftId, _ref3, _ref4, _ref5, _ref6;
190
+ var _usableRegisters_find, _usableRegisters_, _offline_totalsFor;
189
191
  const WidgetSlot = useConsoleWidgetSlot();
190
192
  const firestore = useFirestore();
191
193
  const { data: user } = useUser();
@@ -348,11 +350,53 @@ export function PosConsolePage({ hostId }) {
348
350
  })), [
349
351
  productDocs
350
352
  ]);
351
- const productsById = useMemo(()=>new Map(products.map((product)=>[
353
+ /*
354
+ * THE OFFLINE REGISTER (AGL-3625): the catalog and the site's offline rules
355
+ * kept on this device, the queue of cash sales rung without a connection,
356
+ * and the sync that records them once it returns.
357
+ */ const loadCatalog = useCallback(async ()=>(await getDocs(query(collection(firestore, 'hosts', hostId, 'products'), ...listQueryConstraints(posProductPlan({})), limit(POS_GRID_CEILING)))).docs.map((snapshot)=>_extends({}, snapshot.data(), {
358
+ $id: snapshot.id
359
+ })), [
360
+ firestore,
361
+ hostId
362
+ ]);
363
+ const liveRegister = usableRegisters.find((register)=>register.$id === registerId);
364
+ const offline = usePosOffline(_extends({
365
+ hostId,
366
+ user,
367
+ registerId,
368
+ openShiftId: liveRegister ? (_liveRegister_openShiftId = liveRegister.openShiftId) != null ? _liveRegister_openShiftId : null : undefined,
369
+ locationId
370
+ }, cashier.assertion ? {
371
+ cashierAssertion: cashier.assertion
372
+ } : {}, {
373
+ liveProducts: products,
374
+ loadCatalog
375
+ }));
376
+ const [offlineCheckout, setOfflineCheckout] = useState(false);
377
+ /** Open sales left unpaid when the connection dropped, voided once it returns. */ const strandedSales = useRef([]);
378
+ useEffect(()=>{
379
+ if (offline.offline || !user || !strandedSales.current.length) return;
380
+ const orderIds = strandedSales.current.splice(0);
381
+ for (const orderId of orderIds){
382
+ void posTender(user, hostId, orderId, 'void').catch(()=>undefined);
383
+ }
384
+ }, [
385
+ offline.offline,
386
+ user,
387
+ hostId
388
+ ]);
389
+ const sellProducts = useMemo(()=>offline.offline && offline.ready ? offline.gridProducts(search, categoryId, POS_QUICK_KEYS) : products, [
390
+ offline,
391
+ search,
392
+ categoryId,
393
+ products
394
+ ]);
395
+ const productsById = useMemo(()=>new Map(sellProducts.map((product)=>[
352
396
  product.$id,
353
397
  product
354
398
  ])), [
355
- products
399
+ sellProducts
356
400
  ]);
357
401
  /** AGL-2357: warns, never blocks. */ const shortfalls = useMemo(()=>lines.map((line)=>{
358
402
  var _line_variantId;
@@ -377,6 +421,18 @@ export function PosConsolePage({ hostId }) {
377
421
  }, [
378
422
  enqueueSnackbar
379
423
  ]);
424
+ /** A pairing code for a customer display or a self-service kiosk (AGL-3623). */ const showPairingCode = useCallback(async (mode)=>{
425
+ try {
426
+ setPairing(_extends({}, await display.pairingCode(mode), {
427
+ mode
428
+ }));
429
+ } catch (error) {
430
+ notify(error instanceof PosRequestError ? error.message : 'Could not make a code', 'error');
431
+ }
432
+ }, [
433
+ display,
434
+ notify
435
+ ]);
380
436
  /**
381
437
  * Adds a line, or more of an identical one: the same product, variant and
382
438
  * modifiers merge, so two oat lattes are one line of two and an oat latte
@@ -436,6 +492,13 @@ export function PosConsolePage({ hostId }) {
436
492
  var _product_variants_find;
437
493
  const needle = scanned.trim().toLowerCase();
438
494
  if (!needle || sale) return;
495
+ if (offline.offline) {
496
+ const found = offline.findByCode(needle);
497
+ if (!found) return void notify(`No product matches “${scanned.trim()}”`, 'warning');
498
+ tapProduct(found.product, found.variant);
499
+ setSearch('');
500
+ return;
501
+ }
439
502
  const lookup = async (field)=>{
440
503
  const found = await getDocs(query(collection(firestore, 'hosts', hostId, 'products'), ...listQueryConstraints(posProductPlan({
441
504
  code: {
@@ -451,6 +514,7 @@ export function PosConsolePage({ hostId }) {
451
514
  hit = (_ref = await lookup('barcodes')) != null ? _ref : await lookup('skus');
452
515
  } catch (error) {
453
516
  console.error(error);
517
+ offline.reportNetworkFailure();
454
518
  return void notify('Could not reach the catalog — try again', 'warning');
455
519
  }
456
520
  if (!hit) return void notify(`No product matches “${scanned.trim()}”`, 'warning');
@@ -470,7 +534,8 @@ export function PosConsolePage({ hostId }) {
470
534
  firestore,
471
535
  hostId,
472
536
  tapProduct,
473
- notify
537
+ notify,
538
+ offline
474
539
  ]);
475
540
  const handleSearchEnter = useCallback(()=>lookupCode(search), [
476
541
  lookupCode,
@@ -492,6 +557,13 @@ export function PosConsolePage({ hostId }) {
492
557
  const charge = useCallback(async ()=>{
493
558
  if (chargeInFlight.current || lines.length === 0 || !user) return;
494
559
  if (!registerId) return void notify('Select a register before taking payment', 'warning');
560
+ // Offline, the basket rings as a cash sale kept on this register (AGL-3625).
561
+ if (offline.offline) {
562
+ var _offline_unavailableReason;
563
+ if (!offline.ready) return void notify((_offline_unavailableReason = offline.unavailableReason) != null ? _offline_unavailableReason : 'Offline selling is unavailable', 'warning');
564
+ setOfflineCheckout(true);
565
+ return;
566
+ }
495
567
  if (!attemptKey.current) attemptKey.current = newAttemptKey();
496
568
  chargeInFlight.current = true;
497
569
  setOpening(true);
@@ -523,7 +595,13 @@ export function PosConsolePage({ hostId }) {
523
595
  });
524
596
  setSheetOpen(true);
525
597
  } catch (error) {
526
- notify(error instanceof PosRequestError ? error.message : 'Sale failed', 'error');
598
+ if (error instanceof PosRequestError) {
599
+ notify(error.message, 'error');
600
+ } else {
601
+ // The request never reached the server: the register is offline.
602
+ offline.reportNetworkFailure();
603
+ notify('The register lost its connection. Charge again to take cash offline.', 'warning');
604
+ }
527
605
  } finally{
528
606
  chargeInFlight.current = false;
529
607
  setOpening(false);
@@ -538,7 +616,8 @@ export function PosConsolePage({ hostId }) {
538
616
  customer,
539
617
  cashier.assertion,
540
618
  locationId,
541
- notify
619
+ notify,
620
+ offline
542
621
  ]);
543
622
  const resetSale = useCallback(()=>{
544
623
  if ((sale == null ? void 0 : sale.status) === 'paid') setLastReceipt({
@@ -688,13 +767,7 @@ export function PosConsolePage({ hostId }) {
688
767
  }),
689
768
  registerId ? /*#__PURE__*/ _jsx(Button, {
690
769
  size: "small",
691
- onClick: async ()=>{
692
- try {
693
- setPairing(await display.pairingCode());
694
- } catch (error) {
695
- notify(error instanceof PosRequestError ? error.message : 'Could not make a code', 'error');
696
- }
697
- },
770
+ onClick: ()=>void showPairingCode('display'),
698
771
  children: display.connected ? 'Display connected' : 'Pair display'
699
772
  }) : null
700
773
  ]
@@ -794,8 +867,18 @@ export function PosConsolePage({ hostId }) {
794
867
  }, cashier.assertion ? {
795
868
  cashierAssertion: cashier.assertion
796
869
  } : {}, {
797
- notify: notify
798
- })) : /*#__PURE__*/ _jsxs(_Fragment, {
870
+ notify: notify,
871
+ offline: offline.offline
872
+ }, offline.ready ? {
873
+ onSellOffline: ()=>{
874
+ // The server's open sale took no payment; it is voided once
875
+ // the connection returns, and the basket rings offline.
876
+ strandedSales.current.push(sale.orderId);
877
+ setSale(null);
878
+ setSaleLines([]);
879
+ setOfflineCheckout(true);
880
+ }
881
+ } : {})) : /*#__PURE__*/ _jsxs(_Fragment, {
799
882
  children: [
800
883
  /*#__PURE__*/ _jsxs(Box, {
801
884
  sx: {
@@ -826,7 +909,7 @@ export function PosConsolePage({ hostId }) {
826
909
  sx: {
827
910
  minHeight: 56
828
911
  },
829
- children: opening ? 'Pricing…' : `Charge ${usd(estimateCents)}`
912
+ children: opening ? 'Pricing…' : offline.offline ? `Cash ${usd((_ref4 = (_offline_totalsFor = offline.totalsFor(lines, discountPct)) == null ? void 0 : _offline_totalsFor.totalCents) != null ? _ref4 : estimateCents)} (offline)` : `Charge ${usd(estimateCents)}`
830
913
  }),
831
914
  lastReceipt ? /*#__PURE__*/ _jsx(PosLastReceipt, _extends({
832
915
  hostId: hostId,
@@ -863,6 +946,29 @@ export function PosConsolePage({ hostId }) {
863
946
  pb: wide ? 2 : 12
864
947
  },
865
948
  children: [
949
+ /*#__PURE__*/ _jsx(Box, {
950
+ sx: {
951
+ mb: 2,
952
+ '&:empty': {
953
+ display: 'none'
954
+ }
955
+ },
956
+ children: /*#__PURE__*/ _jsx(PosOfflineBanner, {
957
+ offline: offline
958
+ })
959
+ }),
960
+ registerId ? /*#__PURE__*/ _jsx(PosKioskQueue, {
961
+ user: user,
962
+ hostId: hostId,
963
+ registerId: registerId,
964
+ disabled: Boolean(sale),
965
+ onTake: (next, nextLines)=>{
966
+ setSaleLines(nextLines);
967
+ setSale(next);
968
+ setSheetOpen(true);
969
+ },
970
+ notify: notify
971
+ }) : null,
866
972
  WidgetSlot ? /*#__PURE__*/ _jsx(PosOrdersZone, {
867
973
  renderer: WidgetSlot,
868
974
  hostId: hostId,
@@ -884,7 +990,7 @@ export function PosConsolePage({ hostId }) {
884
990
  onCategory: setCategoryId,
885
991
  notices: gridPlan.notices,
886
992
  hostId: hostId,
887
- products: products,
993
+ products: sellProducts,
888
994
  basketCounts: basketCounts,
889
995
  onTap: (product)=>{
890
996
  if (sale) return void notify('Finish or void the open sale first', 'info');
@@ -979,7 +1085,7 @@ export function PosConsolePage({ hostId }) {
979
1085
  ]
980
1086
  }) : null,
981
1087
  /*#__PURE__*/ _jsx(PosItemDialog, _extends({
982
- product: (_ref4 = itemSheet == null ? void 0 : itemSheet.product) != null ? _ref4 : null,
1088
+ product: (_ref5 = itemSheet == null ? void 0 : itemSheet.product) != null ? _ref5 : null,
983
1089
  initial: itemSheet == null ? void 0 : itemSheet.initial,
984
1090
  onClose: ()=>setItemSheet(null),
985
1091
  onConfirm: (choice)=>{
@@ -1000,6 +1106,28 @@ export function PosConsolePage({ hostId }) {
1000
1106
  setLines((prev)=>prev.filter((_line, index)=>index !== at));
1001
1107
  }
1002
1108
  } : {})),
1109
+ /*#__PURE__*/ _jsx(PosOfflineCheckout, _extends({
1110
+ open: offlineCheckout,
1111
+ offline: offline,
1112
+ hostId: hostId,
1113
+ lines: lines,
1114
+ discountPct: discountPct,
1115
+ customer: customer
1116
+ }, registerName ? {
1117
+ registerName
1118
+ } : {}, cashier.cashier ? {
1119
+ cashierName: cashier.cashier.name
1120
+ } : {}, {
1121
+ onClose: ()=>setOfflineCheckout(false),
1122
+ onRung: ()=>undefined,
1123
+ onDone: ()=>{
1124
+ setOfflineCheckout(false);
1125
+ setLines([]);
1126
+ setDiscountPct(0);
1127
+ setCustomer(null);
1128
+ setSheetOpen(false);
1129
+ }
1130
+ })),
1003
1131
  /*#__PURE__*/ _jsxs(Dialog, {
1004
1132
  open: Boolean(pairing),
1005
1133
  onClose: ()=>setPairing(null),
@@ -1007,7 +1135,7 @@ export function PosConsolePage({ hostId }) {
1007
1135
  fullWidth: true,
1008
1136
  children: [
1009
1137
  /*#__PURE__*/ _jsx(DialogTitle, {
1010
- children: 'Pair a customer display'
1138
+ children: (pairing == null ? void 0 : pairing.mode) === 'kiosk' ? 'Pair a self-service kiosk' : 'Pair a customer display'
1011
1139
  }),
1012
1140
  /*#__PURE__*/ _jsxs(DialogContent, {
1013
1141
  sx: {
@@ -1016,20 +1144,39 @@ export function PosConsolePage({ hostId }) {
1016
1144
  gap: 2
1017
1145
  },
1018
1146
  children: [
1019
- /*#__PURE__*/ _jsx(Typography, {
1020
- variant: "body2",
1021
- color: "text.secondary",
1147
+ /*#__PURE__*/ _jsxs(ToggleButtonGroup, {
1148
+ exclusive: true,
1149
+ fullWidth: true,
1150
+ size: "small",
1151
+ value: (_ref6 = pairing == null ? void 0 : pairing.mode) != null ? _ref6 : 'display',
1152
+ onChange: (_event, mode)=>{
1153
+ if (mode && mode !== (pairing == null ? void 0 : pairing.mode)) void showPairingCode(mode);
1154
+ },
1022
1155
  sx: {
1023
1156
  mt: 1
1024
1157
  },
1025
- children: 'On the tablet facing your customer, open this address and enter the code. ' + 'The code works once and expires in 10 minutes.'
1158
+ children: [
1159
+ /*#__PURE__*/ _jsx(ToggleButton, {
1160
+ value: "display",
1161
+ children: 'Customer display'
1162
+ }),
1163
+ /*#__PURE__*/ _jsx(ToggleButton, {
1164
+ value: "kiosk",
1165
+ children: 'Self-service kiosk'
1166
+ })
1167
+ ]
1168
+ }),
1169
+ /*#__PURE__*/ _jsx(Typography, {
1170
+ variant: "body2",
1171
+ color: "text.secondary",
1172
+ children: ((pairing == null ? void 0 : pairing.mode) === 'kiosk' ? 'On the tablet customers order from, open this address and enter the code. ' : 'On the tablet facing your customer, open this address and enter the code. ') + 'The code works once and expires in 10 minutes.'
1026
1173
  }),
1027
1174
  /*#__PURE__*/ _jsx(Typography, {
1028
1175
  variant: "body1",
1029
1176
  sx: {
1030
1177
  wordBreak: 'break-all'
1031
1178
  },
1032
- children: `${typeof window === 'undefined' ? '' : window.location.origin}/kiosk/commerce/pos-display`
1179
+ children: `${typeof window === 'undefined' ? '' : window.location.origin}/kiosk/commerce/${(pairing == null ? void 0 : pairing.mode) === 'kiosk' ? 'pos-kiosk' : 'pos-display'}`
1033
1180
  }),
1034
1181
  /*#__PURE__*/ _jsx(Typography, {
1035
1182
  variant: "h3",