@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
@@ -21,8 +21,9 @@ import * as CommerceModel from "../../model/index.js";
21
21
  import { CardDisplay, useConfirmationContext } from "@aglyn/shared-ui-jsx";
22
22
  import QuotaReadoutComponent from "@aglyn/shared-ui-jsx/components/quota-readout.component";
23
23
  import { useSnackbar } from "@aglyn/shared-ui-snackstack";
24
- import { Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, Stack, TextField, Typography } from "@mui/material";
25
- import { collection, deleteDoc, doc, getCountFromServer, limit, query, setDoc } from "firebase/firestore";
24
+ import { Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, FormControlLabel, MenuItem, Stack, Switch, TextField, Typography } from "@mui/material";
25
+ import { invalidOpeningHoursLines } from "@aglyn/aglyn/app-utils/local-business";
26
+ import { collection, deleteDoc, doc, getCountFromServer, limit, query, setDoc, updateDoc } from "firebase/firestore";
26
27
  import { useCallback, useEffect, useState } from "react";
27
28
  import { useFirestore } from "@aglyn/tenant-feature-instance";
28
29
  import { useFirestoreCollection } from "@aglyn/tenant-feature-instance";
@@ -35,7 +36,8 @@ import { pluginDocsHelp } from "@aglyn/aglyn";
35
36
  * quota (AGL-278). Variant stock splits per location in the products
36
37
  * hub; POS registers sell from their location (AGL-312).
37
38
  */ export function LocationsCard(props) {
38
- var _ref;
39
+ var _pickupDraft_hours, _ref, _ref1, _pickupDraft_hours1, _pickupDraft_instructions, _pickupDraft_readyWithinMinutes;
40
+ var _pickupFor_postalAddress;
39
41
  const { hostId } = props;
40
42
  const firestore = useFirestore();
41
43
  const { enqueueSnackbar } = useSnackbar();
@@ -219,6 +221,40 @@ import { pluginDocsHelp } from "@aglyn/aglyn";
219
221
  size: "small"
220
222
  });
221
223
  };
224
+ /*
225
+ * Pickup at a location (AGL-3624): whether buyers may collect orders here,
226
+ * its hours and what to do on arrival. Checkout offers every location that
227
+ * has it on, and routes the order and its stock there.
228
+ */ const [pickupFor, setPickupFor] = useState(null);
229
+ const [pickupDraft, setPickupDraft] = useState({});
230
+ const openPickup = useCallback((location)=>()=>{
231
+ setPickupFor(location);
232
+ setPickupDraft(CommerceModel.normalizePickupSettings(location.pickup));
233
+ }, []);
234
+ const badHourLines = invalidOpeningHoursLines((_pickupDraft_hours = pickupDraft.hours) != null ? _pickupDraft_hours : '');
235
+ const handleSavePickup = useCallback(async ()=>{
236
+ if (!pickupFor) return;
237
+ try {
238
+ // `updateDoc` replaces the map whole, so a cleared field is cleared;
239
+ // a merge-set would deep-merge it back.
240
+ await updateDoc(doc(firestore, 'hosts', hostId, 'locations', pickupFor.$id), {
241
+ pickup: CommerceModel.normalizePickupSettings(pickupDraft)
242
+ });
243
+ setPickupFor(null);
244
+ } catch (error) {
245
+ var _ref;
246
+ enqueueSnackbar((_ref = error == null ? void 0 : error.message) != null ? _ref : 'Could not save pickup', {
247
+ variant: 'warning',
248
+ persist: false
249
+ });
250
+ }
251
+ }, [
252
+ enqueueSnackbar,
253
+ firestore,
254
+ hostId,
255
+ pickupDraft,
256
+ pickupFor
257
+ ]);
222
258
  return /*#__PURE__*/ _jsxs(CardDisplay, {
223
259
  header: 'Inventory locations',
224
260
  help: pluginDocsHelp('commerce', {
@@ -251,6 +287,17 @@ import { pluginDocsHelp } from "@aglyn/aglyn";
251
287
  noWrap: true,
252
288
  children: location.name
253
289
  }),
290
+ CommerceModel.normalizePickupSettings(location.pickup).enabled ? /*#__PURE__*/ _jsx(Chip, {
291
+ label: "Pickup",
292
+ size: "small",
293
+ color: "primary",
294
+ variant: "outlined"
295
+ }) : null,
296
+ /*#__PURE__*/ _jsx(Button, {
297
+ size: "small",
298
+ onClick: openPickup(location),
299
+ children: 'Pickup'
300
+ }),
254
301
  /*#__PURE__*/ _jsx(Button, {
255
302
  size: "small",
256
303
  onClick: openAddress(location),
@@ -353,6 +400,133 @@ import { pluginDocsHelp } from "@aglyn/aglyn";
353
400
  ]
354
401
  })
355
402
  ]
403
+ }),
404
+ /*#__PURE__*/ _jsxs(Dialog, {
405
+ open: Boolean(pickupFor),
406
+ onClose: ()=>setPickupFor(null),
407
+ fullWidth: true,
408
+ maxWidth: "sm",
409
+ children: [
410
+ /*#__PURE__*/ _jsx(DialogTitle, {
411
+ children: `Pickup at ${(_ref1 = pickupFor == null ? void 0 : pickupFor.name) != null ? _ref1 : 'this location'}`
412
+ }),
413
+ /*#__PURE__*/ _jsx(DialogContent, {
414
+ children: /*#__PURE__*/ _jsxs(Stack, {
415
+ spacing: 1.5,
416
+ sx: {
417
+ pt: 1
418
+ },
419
+ children: [
420
+ /*#__PURE__*/ _jsx(FormControlLabel, {
421
+ control: /*#__PURE__*/ _jsx(Switch, {
422
+ checked: Boolean(pickupDraft.enabled),
423
+ onChange: (event)=>setPickupDraft((prior)=>_extends({}, prior, {
424
+ enabled: event.target.checked
425
+ }))
426
+ }),
427
+ label: "Buyers can pick up orders here"
428
+ }),
429
+ pickupDraft.enabled && !(pickupFor == null ? void 0 : (_pickupFor_postalAddress = pickupFor.postalAddress) == null ? void 0 : _pickupFor_postalAddress.line1) && !(pickupFor == null ? void 0 : pickupFor.address) ? /*#__PURE__*/ _jsx(Typography, {
430
+ variant: "caption",
431
+ color: "warning.main",
432
+ children: 'Add this location’s address so buyers know where to go.'
433
+ }) : null,
434
+ /*#__PURE__*/ _jsx(TextField, {
435
+ label: "Pickup hours",
436
+ value: (_pickupDraft_hours1 = pickupDraft.hours) != null ? _pickupDraft_hours1 : '',
437
+ onChange: (event)=>setPickupDraft((prior)=>_extends({}, prior, {
438
+ hours: event.target.value
439
+ })),
440
+ multiline: true,
441
+ minRows: 2,
442
+ size: "small",
443
+ placeholder: 'Mo-Fr 09:00-17:00\nSa 10:00-14:00',
444
+ error: badHourLines.length > 0,
445
+ helperText: badHourLines.length ? `Line ${badHourLines.join(', ')} does not read as days and times, like Mo-Fr 09:00-17:00.` : 'One line per set of days, like Mo-Fr 09:00-17:00.'
446
+ }),
447
+ /*#__PURE__*/ _jsx(TextField, {
448
+ label: "Arrival instructions",
449
+ value: (_pickupDraft_instructions = pickupDraft.instructions) != null ? _pickupDraft_instructions : '',
450
+ onChange: (event)=>setPickupDraft((prior)=>_extends({}, prior, {
451
+ instructions: event.target.value
452
+ })),
453
+ multiline: true,
454
+ minRows: 2,
455
+ size: "small",
456
+ placeholder: "Ring the bell at the side door and give your order number.",
457
+ slotProps: {
458
+ htmlInput: {
459
+ maxLength: CommerceModel.PICKUP_INSTRUCTIONS_MAX
460
+ }
461
+ }
462
+ }),
463
+ /*#__PURE__*/ _jsxs(TextField, {
464
+ select: true,
465
+ label: "Usually ready in",
466
+ value: String((_pickupDraft_readyWithinMinutes = pickupDraft.readyWithinMinutes) != null ? _pickupDraft_readyWithinMinutes : ''),
467
+ onChange: (event)=>setPickupDraft((prior)=>_extends({}, prior, {
468
+ readyWithinMinutes: event.target.value ? Number(event.target.value) : undefined
469
+ })),
470
+ size: "small",
471
+ children: [
472
+ /*#__PURE__*/ _jsx(MenuItem, {
473
+ value: "",
474
+ children: 'Don’t say'
475
+ }),
476
+ [
477
+ [
478
+ 30,
479
+ '30 minutes'
480
+ ],
481
+ [
482
+ 60,
483
+ '1 hour'
484
+ ],
485
+ [
486
+ 120,
487
+ '2 hours'
488
+ ],
489
+ [
490
+ 240,
491
+ '4 hours'
492
+ ],
493
+ [
494
+ 1440,
495
+ '1 day'
496
+ ],
497
+ [
498
+ 2880,
499
+ '2 days'
500
+ ]
501
+ ].map(([minutes, label])=>/*#__PURE__*/ _jsx(MenuItem, {
502
+ value: String(minutes),
503
+ children: label
504
+ }, minutes))
505
+ ]
506
+ }),
507
+ /*#__PURE__*/ _jsx(Typography, {
508
+ variant: "caption",
509
+ color: "text.secondary",
510
+ children: 'Buyers choose this location at checkout. The order’s stock comes off this location, ' + 'and the buyer is emailed when you mark the order ready.'
511
+ })
512
+ ]
513
+ })
514
+ }),
515
+ /*#__PURE__*/ _jsxs(DialogActions, {
516
+ children: [
517
+ /*#__PURE__*/ _jsx(Button, {
518
+ onClick: ()=>setPickupFor(null),
519
+ children: 'Cancel'
520
+ }),
521
+ /*#__PURE__*/ _jsx(Button, {
522
+ variant: "contained",
523
+ disabled: badHourLines.length > 0,
524
+ onClick: handleSavePickup,
525
+ children: 'Save'
526
+ })
527
+ ]
528
+ })
529
+ ]
356
530
  })
357
531
  ]
358
532
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/locations-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport QuotaReadoutComponent from '@aglyn/shared-ui-jsx/components/quota-readout.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport {\n collection,\n deleteDoc,\n doc,\n getCountFromServer,\n limit,\n query,\n setDoc,\n} from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport { useHostResourceApi } from '@aglyn/tenant-feature-instance'\nimport { useOrgPlan } from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface LocationsCardProps {\n hostId: string\n}\n\n/**\n * Inventory locations (AGL-286): named stock buckets under\n * `hosts/{hostId}/locations`, capped by the plan's `inventoryLocations`\n * quota (AGL-278). Variant stock splits per location in the products\n * hub; POS registers sell from their location (AGL-312).\n */\nexport function LocationsCard(props: LocationsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const createHostResource = useHostResourceApi()\n const { org, ready: planReady } = useOrgPlan(hostId)\n const { data: locationDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'locations'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const locations = [...(locationDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n /**\n * The location HEAD-COUNT is a server aggregate, not the length of the\n * capped listener (AGL-1716, the AGL-1706 shape).\n *\n * The smallest margin of the eight, and real for exactly one plan: the\n * listener is `limit(25)` and `inventoryLocations` runs 1 / 1 / 2 / 4 / 6 /\n * 10 / 50, so only Agency's 50 sits above the window. An agency with more\n * than 25 stock buckets read its cap as satisfiable, and\n * `api/hosts/resources` — which counts the collection — refused the add.\n *\n * Re-read after an add AND a delete: locations are hard-deleted here, so\n * unlike the soft-deleting cards a removal really does free a slot on both\n * sides, and a one-shot that missed it would keep refusing.\n *\n * `locations.length === 0` below still decides the FIRST location's\n * `isDefault` flag off the loaded rows, which is the right question for it\n * — that one is about what this card can see, not about the site's total.\n */\n const [locationCountEpoch, setLocationCountEpoch] = useState(0)\n const [serverLocationCount, setServerLocationCount] = useState<number | null>(\n null,\n )\n useEffect(() => {\n let active = true\n void getCountFromServer(collection(firestore, 'hosts', hostId, 'locations'))\n .then((snapshot) => {\n if (active) setServerLocationCount(snapshot.data().count)\n })\n .catch(() => {\n // Falls back to the loaded rows — a LOWER bound and the prior\n // behaviour, never 0, which reads as \"no locations used\" on a site\n // that is over its band.\n })\n return () => {\n active = false\n }\n }, [firestore, hostId, locationCountEpoch])\n const locationCount = serverLocationCount ?? locations.length\n const quota = Aglyn.checkQuota(org, 'inventoryLocations', locationCount)\n const [name, setName] = useState('')\n\n const handleAdd = useCallback(async () => {\n if (!name.trim()) return\n // Held until the org doc lands (AGL-1064): an absent org resolves to\n // the free tier's `inventoryLocations: 1`, so a site that already has\n // its default location would be refused with an upgrade prompt it does\n // not need. The button is disabled too; this guards the race.\n if (!planReady) return\n if (!quota.allowed) {\n return void enqueueSnackbar(\n `Your plan includes ${quota.limit} locations — upgrade for more`,\n { variant: 'info', persist: false },\n )\n }\n try {\n // Creation rides the quota-enforcing resources API (AGL-473).\n await createHostResource({\n hostId,\n resource: 'location',\n data: {\n name: name.trim().slice(0, 80),\n ...(locations.length === 0 ? { isDefault: true } : {}),\n } satisfies CommerceModel.InventoryLocation,\n })\n setName('')\n setLocationCountEpoch((epoch) => epoch + 1)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not add location', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [\n name,\n planReady,\n quota,\n locations.length,\n hostId,\n createHostResource,\n enqueueSnackbar,\n ])\n\n const handleDelete = useCallback(\n (location: any) => async () => {\n const confirmed = await confirm({\n title: 'Remove this location?',\n description:\n `Stock bucketed under \"${location.name}\" folds back into the ` +\n 'flat totals; adjust counts afterward if needed.',\n confirmationText: 'Remove',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await deleteDoc(doc(firestore, 'hosts', hostId, 'locations', location.$id))\n // A HARD delete, so the slot really is freed on both sides — the\n // one-shot aggregate has to be told (AGL-1716).\n setLocationCountEpoch((epoch) => epoch + 1)\n },\n [confirm, firestore, hostId],\n )\n\n const handleMakeDefault = useCallback(\n (location: any) => async () => {\n for (const other of locations) {\n if (other.isDefault && other.$id !== location.$id) {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'locations', other.$id),\n { isDefault: false },\n { merge: true },\n )\n }\n }\n await setDoc(\n doc(firestore, 'hosts', hostId, 'locations', location.$id),\n { isDefault: true },\n { merge: true },\n )\n },\n [locations, firestore, hostId],\n )\n\n /*\n * A location's postal address (AGL-3612): where parcels can leave from,\n * for anything that ships. Optional, written beside the free-text name.\n */\n const [addressFor, setAddressFor] = useState<any | null>(null)\n const [addressDraft, setAddressDraft] = useState<CommerceModel.PostalAddress>({})\n const openAddress = useCallback(\n (location: any) => () => {\n setAddressFor(location)\n setAddressDraft(CommerceModel.normalizePostalAddress(location.postalAddress) ?? {})\n },\n [],\n )\n const handleSaveAddress = useCallback(async () => {\n if (!addressFor) return\n try {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'locations', addressFor.$id),\n { postalAddress: CommerceModel.normalizePostalAddress(addressDraft) ?? {} },\n { merge: true },\n )\n setAddressFor(null)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not save the address', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [addressDraft, addressFor, enqueueSnackbar, firestore, hostId])\n const addressField = (field: keyof CommerceModel.PostalAddress, label: string) => (\n <TextField\n label={label}\n value={addressDraft[field] ?? ''}\n onChange={(event) =>\n setAddressDraft((prior) => ({\n ...prior,\n [field]: field === 'country' ? event.target.value.toUpperCase().slice(0, 2) : event.target.value,\n }))\n }\n size=\"small\"\n />\n )\n\n return (\n <CardDisplay\n header={'Inventory locations'}\n help={pluginDocsHelp('commerce', { anchor: '#inventory' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n {locations.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Track stock per warehouse or storefront. Without locations, ' +\n 'every variant has a single stock count.'}\n </Typography>\n ) : (\n locations.map((location: any) => (\n <Stack\n key={location.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {location.name}\n </Typography>\n <Button size=\"small\" onClick={openAddress(location)}>\n {location.postalAddress?.line1 ? 'Edit address' : 'Add address'}\n </Button>\n {location.isDefault ? (\n <Chip label=\"Default\" size=\"small\" variant=\"outlined\" />\n ) : (\n <Button size=\"small\" onClick={handleMakeDefault(location)}>\n {'Make default'}\n </Button>\n )}\n <Button size=\"small\" color=\"error\" onClick={handleDelete(location)}>\n {'Remove'}\n </Button>\n </Stack>\n ))\n )}\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"New location\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"Main warehouse\"\n />\n <Button\n size=\"small\"\n disabled={!name.trim() || !planReady}\n onClick={handleAdd}\n >\n {'Add'}\n </Button>\n </Stack>\n {/* The site's locations, not this card's rows (AGL-1716) — the\n readout and the gate must agree, and the gate now counts.\n The wording moved to the shared component in AGL-2113 so the five\n sibling cards that were missing it render the identical string\n rather than five near-misses; the output here is unchanged. */}\n <QuotaReadoutComponent\n ready={planReady}\n used={locationCount}\n limit={quota.limit}\n noun=\"location\"\n />\n </Stack>\n <Dialog open={Boolean(addressFor)} onClose={() => setAddressFor(null)} fullWidth maxWidth=\"sm\">\n <DialogTitle>{`Address of ${addressFor?.name ?? 'this location'}`}</DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n {addressField('line1', 'Street address')}\n {addressField('line2', 'Apartment, suite (optional)')}\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5}>\n {addressField('city', 'City')}\n {addressField('state', 'State or region')}\n {addressField('postalCode', 'Postal code')}\n {addressField('country', 'Country (two letters)')}\n </Stack>\n {addressField('phone', 'Phone')}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setAddressFor(null)}>{'Cancel'}</Button>\n <Button variant=\"contained\" onClick={handleSaveAddress}>\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nLocationsCard.displayName = 'LocationsCard'\n\nexport default LocationsCard\n"],"names":["Aglyn","CommerceModel","CardDisplay","useConfirmationContext","QuotaReadoutComponent","useSnackbar","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","TextField","Typography","collection","deleteDoc","doc","getCountFromServer","limit","query","setDoc","useCallback","useEffect","useState","useFirestore","useFirestoreCollection","useHostResourceApi","useOrgPlan","pluginDocsHelp","LocationsCard","props","hostId","firestore","enqueueSnackbar","confirm","createHostResource","org","ready","planReady","data","locationDocs","idField","locations","sort","a","b","String","name","localeCompare","locationCountEpoch","setLocationCountEpoch","serverLocationCount","setServerLocationCount","active","then","snapshot","count","catch","locationCount","length","quota","checkQuota","setName","handleAdd","trim","allowed","variant","persist","resource","slice","isDefault","epoch","error","message","handleDelete","location","confirmed","title","description","confirmationText","confirmationButtonProps","color","$id","handleMakeDefault","other","merge","addressFor","setAddressFor","addressDraft","setAddressDraft","openAddress","normalizePostalAddress","postalAddress","handleSaveAddress","addressField","field","label","value","onChange","event","prior","target","toUpperCase","size","header","help","anchor","contentGutterX","contentGutterY","spacing","map","direction","sx","alignItems","flex","noWrap","onClick","line1","placeholder","disabled","used","noun","open","Boolean","onClose","fullWidth","maxWidth","pt","xs","sm","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/locations-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport QuotaReadoutComponent from '@aglyn/shared-ui-jsx/components/quota-readout.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControlLabel,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { invalidOpeningHoursLines } from '@aglyn/aglyn/app-utils/local-business'\nimport {\n collection,\n deleteDoc,\n doc,\n getCountFromServer,\n limit,\n query,\n setDoc,\n updateDoc,\n} from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport { useHostResourceApi } from '@aglyn/tenant-feature-instance'\nimport { useOrgPlan } from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface LocationsCardProps {\n hostId: string\n}\n\n/**\n * Inventory locations (AGL-286): named stock buckets under\n * `hosts/{hostId}/locations`, capped by the plan's `inventoryLocations`\n * quota (AGL-278). Variant stock splits per location in the products\n * hub; POS registers sell from their location (AGL-312).\n */\nexport function LocationsCard(props: LocationsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const createHostResource = useHostResourceApi()\n const { org, ready: planReady } = useOrgPlan(hostId)\n const { data: locationDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'locations'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const locations = [...(locationDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n /**\n * The location HEAD-COUNT is a server aggregate, not the length of the\n * capped listener (AGL-1716, the AGL-1706 shape).\n *\n * The smallest margin of the eight, and real for exactly one plan: the\n * listener is `limit(25)` and `inventoryLocations` runs 1 / 1 / 2 / 4 / 6 /\n * 10 / 50, so only Agency's 50 sits above the window. An agency with more\n * than 25 stock buckets read its cap as satisfiable, and\n * `api/hosts/resources` — which counts the collection — refused the add.\n *\n * Re-read after an add AND a delete: locations are hard-deleted here, so\n * unlike the soft-deleting cards a removal really does free a slot on both\n * sides, and a one-shot that missed it would keep refusing.\n *\n * `locations.length === 0` below still decides the FIRST location's\n * `isDefault` flag off the loaded rows, which is the right question for it\n * — that one is about what this card can see, not about the site's total.\n */\n const [locationCountEpoch, setLocationCountEpoch] = useState(0)\n const [serverLocationCount, setServerLocationCount] = useState<number | null>(\n null,\n )\n useEffect(() => {\n let active = true\n void getCountFromServer(collection(firestore, 'hosts', hostId, 'locations'))\n .then((snapshot) => {\n if (active) setServerLocationCount(snapshot.data().count)\n })\n .catch(() => {\n // Falls back to the loaded rows — a LOWER bound and the prior\n // behaviour, never 0, which reads as \"no locations used\" on a site\n // that is over its band.\n })\n return () => {\n active = false\n }\n }, [firestore, hostId, locationCountEpoch])\n const locationCount = serverLocationCount ?? locations.length\n const quota = Aglyn.checkQuota(org, 'inventoryLocations', locationCount)\n const [name, setName] = useState('')\n\n const handleAdd = useCallback(async () => {\n if (!name.trim()) return\n // Held until the org doc lands (AGL-1064): an absent org resolves to\n // the free tier's `inventoryLocations: 1`, so a site that already has\n // its default location would be refused with an upgrade prompt it does\n // not need. The button is disabled too; this guards the race.\n if (!planReady) return\n if (!quota.allowed) {\n return void enqueueSnackbar(\n `Your plan includes ${quota.limit} locations — upgrade for more`,\n { variant: 'info', persist: false },\n )\n }\n try {\n // Creation rides the quota-enforcing resources API (AGL-473).\n await createHostResource({\n hostId,\n resource: 'location',\n data: {\n name: name.trim().slice(0, 80),\n ...(locations.length === 0 ? { isDefault: true } : {}),\n } satisfies CommerceModel.InventoryLocation,\n })\n setName('')\n setLocationCountEpoch((epoch) => epoch + 1)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not add location', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [\n name,\n planReady,\n quota,\n locations.length,\n hostId,\n createHostResource,\n enqueueSnackbar,\n ])\n\n const handleDelete = useCallback(\n (location: any) => async () => {\n const confirmed = await confirm({\n title: 'Remove this location?',\n description:\n `Stock bucketed under \"${location.name}\" folds back into the ` +\n 'flat totals; adjust counts afterward if needed.',\n confirmationText: 'Remove',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await deleteDoc(doc(firestore, 'hosts', hostId, 'locations', location.$id))\n // A HARD delete, so the slot really is freed on both sides — the\n // one-shot aggregate has to be told (AGL-1716).\n setLocationCountEpoch((epoch) => epoch + 1)\n },\n [confirm, firestore, hostId],\n )\n\n const handleMakeDefault = useCallback(\n (location: any) => async () => {\n for (const other of locations) {\n if (other.isDefault && other.$id !== location.$id) {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'locations', other.$id),\n { isDefault: false },\n { merge: true },\n )\n }\n }\n await setDoc(\n doc(firestore, 'hosts', hostId, 'locations', location.$id),\n { isDefault: true },\n { merge: true },\n )\n },\n [locations, firestore, hostId],\n )\n\n /*\n * A location's postal address (AGL-3612): where parcels can leave from,\n * for anything that ships. Optional, written beside the free-text name.\n */\n const [addressFor, setAddressFor] = useState<any | null>(null)\n const [addressDraft, setAddressDraft] = useState<CommerceModel.PostalAddress>({})\n const openAddress = useCallback(\n (location: any) => () => {\n setAddressFor(location)\n setAddressDraft(CommerceModel.normalizePostalAddress(location.postalAddress) ?? {})\n },\n [],\n )\n const handleSaveAddress = useCallback(async () => {\n if (!addressFor) return\n try {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'locations', addressFor.$id),\n { postalAddress: CommerceModel.normalizePostalAddress(addressDraft) ?? {} },\n { merge: true },\n )\n setAddressFor(null)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not save the address', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [addressDraft, addressFor, enqueueSnackbar, firestore, hostId])\n const addressField = (field: keyof CommerceModel.PostalAddress, label: string) => (\n <TextField\n label={label}\n value={addressDraft[field] ?? ''}\n onChange={(event) =>\n setAddressDraft((prior) => ({\n ...prior,\n [field]: field === 'country' ? event.target.value.toUpperCase().slice(0, 2) : event.target.value,\n }))\n }\n size=\"small\"\n />\n )\n\n /*\n * Pickup at a location (AGL-3624): whether buyers may collect orders here,\n * its hours and what to do on arrival. Checkout offers every location that\n * has it on, and routes the order and its stock there.\n */\n const [pickupFor, setPickupFor] = useState<any | null>(null)\n const [pickupDraft, setPickupDraft] = useState<CommerceModel.PickupLocationSettings>({})\n const openPickup = useCallback(\n (location: any) => () => {\n setPickupFor(location)\n setPickupDraft(CommerceModel.normalizePickupSettings(location.pickup))\n },\n [],\n )\n const badHourLines = invalidOpeningHoursLines(pickupDraft.hours ?? '')\n const handleSavePickup = useCallback(async () => {\n if (!pickupFor) return\n try {\n // `updateDoc` replaces the map whole, so a cleared field is cleared;\n // a merge-set would deep-merge it back.\n await updateDoc(doc(firestore, 'hosts', hostId, 'locations', pickupFor.$id), {\n pickup: CommerceModel.normalizePickupSettings(pickupDraft),\n })\n setPickupFor(null)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not save pickup', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [enqueueSnackbar, firestore, hostId, pickupDraft, pickupFor])\n\n return (\n <CardDisplay\n header={'Inventory locations'}\n help={pluginDocsHelp('commerce', { anchor: '#inventory' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n {locations.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Track stock per warehouse or storefront. Without locations, ' +\n 'every variant has a single stock count.'}\n </Typography>\n ) : (\n locations.map((location: any) => (\n <Stack\n key={location.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {location.name}\n </Typography>\n {CommerceModel.normalizePickupSettings(location.pickup).enabled ? (\n <Chip label=\"Pickup\" size=\"small\" color=\"primary\" variant=\"outlined\" />\n ) : null}\n <Button size=\"small\" onClick={openPickup(location)}>\n {'Pickup'}\n </Button>\n <Button size=\"small\" onClick={openAddress(location)}>\n {location.postalAddress?.line1 ? 'Edit address' : 'Add address'}\n </Button>\n {location.isDefault ? (\n <Chip label=\"Default\" size=\"small\" variant=\"outlined\" />\n ) : (\n <Button size=\"small\" onClick={handleMakeDefault(location)}>\n {'Make default'}\n </Button>\n )}\n <Button size=\"small\" color=\"error\" onClick={handleDelete(location)}>\n {'Remove'}\n </Button>\n </Stack>\n ))\n )}\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"New location\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"Main warehouse\"\n />\n <Button\n size=\"small\"\n disabled={!name.trim() || !planReady}\n onClick={handleAdd}\n >\n {'Add'}\n </Button>\n </Stack>\n {/* The site's locations, not this card's rows (AGL-1716) — the\n readout and the gate must agree, and the gate now counts.\n The wording moved to the shared component in AGL-2113 so the five\n sibling cards that were missing it render the identical string\n rather than five near-misses; the output here is unchanged. */}\n <QuotaReadoutComponent\n ready={planReady}\n used={locationCount}\n limit={quota.limit}\n noun=\"location\"\n />\n </Stack>\n <Dialog open={Boolean(addressFor)} onClose={() => setAddressFor(null)} fullWidth maxWidth=\"sm\">\n <DialogTitle>{`Address of ${addressFor?.name ?? 'this location'}`}</DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n {addressField('line1', 'Street address')}\n {addressField('line2', 'Apartment, suite (optional)')}\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5}>\n {addressField('city', 'City')}\n {addressField('state', 'State or region')}\n {addressField('postalCode', 'Postal code')}\n {addressField('country', 'Country (two letters)')}\n </Stack>\n {addressField('phone', 'Phone')}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setAddressFor(null)}>{'Cancel'}</Button>\n <Button variant=\"contained\" onClick={handleSaveAddress}>\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n <Dialog open={Boolean(pickupFor)} onClose={() => setPickupFor(null)} fullWidth maxWidth=\"sm\">\n <DialogTitle>{`Pickup at ${pickupFor?.name ?? 'this location'}`}</DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n <FormControlLabel\n control={\n <Switch\n checked={Boolean(pickupDraft.enabled)}\n onChange={(event) =>\n setPickupDraft((prior) => ({ ...prior, enabled: event.target.checked }))\n }\n />\n }\n label=\"Buyers can pick up orders here\"\n />\n {pickupDraft.enabled && !pickupFor?.postalAddress?.line1 && !pickupFor?.address ? (\n <Typography variant=\"caption\" color=\"warning.main\">\n {'Add this location’s address so buyers know where to go.'}\n </Typography>\n ) : null}\n <TextField\n label=\"Pickup hours\"\n value={pickupDraft.hours ?? ''}\n onChange={(event) => setPickupDraft((prior) => ({ ...prior, hours: event.target.value }))}\n multiline\n minRows={2}\n size=\"small\"\n placeholder={'Mo-Fr 09:00-17:00\\nSa 10:00-14:00'}\n error={badHourLines.length > 0}\n helperText={\n badHourLines.length\n ? `Line ${badHourLines.join(', ')} does not read as days and times, like Mo-Fr 09:00-17:00.`\n : 'One line per set of days, like Mo-Fr 09:00-17:00.'\n }\n />\n <TextField\n label=\"Arrival instructions\"\n value={pickupDraft.instructions ?? ''}\n onChange={(event) =>\n setPickupDraft((prior) => ({ ...prior, instructions: event.target.value }))\n }\n multiline\n minRows={2}\n size=\"small\"\n placeholder=\"Ring the bell at the side door and give your order number.\"\n slotProps={{ htmlInput: { maxLength: CommerceModel.PICKUP_INSTRUCTIONS_MAX } }}\n />\n <TextField\n select\n label=\"Usually ready in\"\n value={String(pickupDraft.readyWithinMinutes ?? '')}\n onChange={(event) =>\n setPickupDraft((prior) => ({\n ...prior,\n readyWithinMinutes: event.target.value ? Number(event.target.value) : undefined,\n }))\n }\n size=\"small\"\n >\n <MenuItem value=\"\">{'Don’t say'}</MenuItem>\n {[\n [30, '30 minutes'],\n [60, '1 hour'],\n [120, '2 hours'],\n [240, '4 hours'],\n [1440, '1 day'],\n [2880, '2 days'],\n ].map(([minutes, label]) => (\n <MenuItem key={minutes} value={String(minutes)}>\n {label}\n </MenuItem>\n ))}\n </TextField>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Buyers choose this location at checkout. The order’s stock comes off this location, ' +\n 'and the buyer is emailed when you mark the order ready.'}\n </Typography>\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setPickupFor(null)}>{'Cancel'}</Button>\n <Button variant=\"contained\" disabled={badHourLines.length > 0} onClick={handleSavePickup}>\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nLocationsCard.displayName = 'LocationsCard'\n\nexport default LocationsCard\n"],"names":["Aglyn","CommerceModel","CardDisplay","useConfirmationContext","QuotaReadoutComponent","useSnackbar","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","FormControlLabel","MenuItem","Stack","Switch","TextField","Typography","invalidOpeningHoursLines","collection","deleteDoc","doc","getCountFromServer","limit","query","setDoc","updateDoc","useCallback","useEffect","useState","useFirestore","useFirestoreCollection","useHostResourceApi","useOrgPlan","pluginDocsHelp","LocationsCard","props","pickupDraft","pickupFor","hostId","firestore","enqueueSnackbar","confirm","createHostResource","org","ready","planReady","data","locationDocs","idField","locations","sort","a","b","String","name","localeCompare","locationCountEpoch","setLocationCountEpoch","serverLocationCount","setServerLocationCount","active","then","snapshot","count","catch","locationCount","length","quota","checkQuota","setName","handleAdd","trim","allowed","variant","persist","resource","slice","isDefault","epoch","error","message","handleDelete","location","confirmed","title","description","confirmationText","confirmationButtonProps","color","$id","handleMakeDefault","other","merge","addressFor","setAddressFor","addressDraft","setAddressDraft","openAddress","normalizePostalAddress","postalAddress","handleSaveAddress","addressField","field","label","value","onChange","event","prior","target","toUpperCase","size","setPickupFor","setPickupDraft","openPickup","normalizePickupSettings","pickup","badHourLines","hours","handleSavePickup","header","help","anchor","contentGutterX","contentGutterY","spacing","map","direction","sx","alignItems","flex","noWrap","enabled","onClick","line1","placeholder","disabled","used","noun","open","Boolean","onClose","fullWidth","maxWidth","pt","xs","sm","control","checked","address","multiline","minRows","helperText","join","instructions","slotProps","htmlInput","maxLength","PICKUP_INSTRUCTIONS_MAX","select","readyWithinMinutes","Number","undefined","minutes","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,YAAYA,WAAW,eAAc;AACrC,YAAYC,mBAAmB,uBAAa;AAC5C,SAASC,WAAW,EAAEC,sBAAsB,QAAQ,uBAAsB;AAC1E,OAAOC,2BAA2B,0DAAyD;AAC3F,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SACEC,MAAM,EACNC,IAAI,EACJC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,WAAW,EACXC,gBAAgB,EAChBC,QAAQ,EACRC,KAAK,EACLC,MAAM,EACNC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SAASC,wBAAwB,QAAQ,wCAAuC;AAChF,SACEC,UAAU,EACVC,SAAS,EACTC,GAAG,EACHC,kBAAkB,EAClBC,KAAK,EACLC,KAAK,EACLC,MAAM,EACNC,SAAS,QACJ,qBAAoB;AAC3B,SAASC,WAAW,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AACxD,SAASC,YAAY,QAAQ,iCAAgC;AAC7D,SAASC,sBAAsB,QAAQ,iCAAgC;AACvE,SAASC,kBAAkB,QAAQ,iCAAgC;AACnE,SAASC,UAAU,QAAQ,iCAAgC;AAC3D,SAASC,cAAc,QAAQ,eAAc;AAM7C;;;;;CAKC,GACD,OAAO,SAASC,cAAcC,KAAyB;QAmMPC,iCAyI3BA,qBAeAA,2BAaOA;QAnCSC;IApUnC,MAAM,EAAEC,MAAM,EAAE,GAAGH;IACnB,MAAMI,YAAYV;IAClB,MAAM,EAAEW,eAAe,EAAE,GAAGpC;IAC5B,MAAM,EAAEqC,OAAO,EAAE,GAAGvC;IACpB,MAAMwC,qBAAqBX;IAC3B,MAAM,EAAEY,GAAG,EAAEC,OAAOC,SAAS,EAAE,GAAGb,WAAWM;IAC7C,MAAM,EAAEQ,MAAMC,YAAY,EAAE,GAAGjB,uBAC7B,IAAMP,MAAML,WAAWqB,WAAW,SAASD,QAAQ,cAAchB,MAAM,MACvE;QAACiB;QAAWD;KAAO,EACnB;QAAEU,SAAS;IAAM;IAEnB,MAAMC,YAAY;WAAKF,uBAAAA,eAAgB,EAAE;KAAE,CAACG,IAAI,CAAC,CAACC,GAAQC;YACjDD,SAAmCC;eAA1CC,QAAOF,UAAAA,EAAEG,IAAI,YAANH,UAAU,IAAII,aAAa,CAACF,QAAOD,UAAAA,EAAEE,IAAI,YAANF,UAAU;;IAEtD;;;;;;;;;;;;;;;;;GAiBC,GACD,MAAM,CAACI,oBAAoBC,sBAAsB,GAAG7B,SAAS;IAC7D,MAAM,CAAC8B,qBAAqBC,uBAAuB,GAAG/B,SACpD;IAEFD,UAAU;QACR,IAAIiC,SAAS;QACb,KAAKvC,mBAAmBH,WAAWqB,WAAW,SAASD,QAAQ,cAC5DuB,IAAI,CAAC,CAACC;YACL,IAAIF,QAAQD,uBAAuBG,SAAShB,IAAI,GAAGiB,KAAK;QAC1D,GACCC,KAAK,CAAC;QACL,8DAA8D;QAC9D,mEAAmE;QACnE,yBAAyB;QAC3B;QACF,OAAO;YACLJ,SAAS;QACX;IACF,GAAG;QAACrB;QAAWD;QAAQkB;KAAmB;IAC1C,MAAMS,gBAAgBP,8BAAAA,sBAAuBT,UAAUiB,MAAM;IAC7D,MAAMC,QAAQpE,MAAMqE,UAAU,CAACzB,KAAK,sBAAsBsB;IAC1D,MAAM,CAACX,MAAMe,QAAQ,GAAGzC,SAAS;IAEjC,MAAM0C,YAAY5C,YAAY;QAC5B,IAAI,CAAC4B,KAAKiB,IAAI,IAAI;QAClB,qEAAqE;QACrE,sEAAsE;QACtE,uEAAuE;QACvE,8DAA8D;QAC9D,IAAI,CAAC1B,WAAW;QAChB,IAAI,CAACsB,MAAMK,OAAO,EAAE;YAClB,OAAO,KAAKhC,gBACV,CAAC,mBAAmB,EAAE2B,MAAM7C,KAAK,CAAC,6BAA6B,CAAC,EAChE;gBAAEmD,SAAS;gBAAQC,SAAS;YAAM;QAEtC;QACA,IAAI;YACF,8DAA8D;YAC9D,MAAMhC,mBAAmB;gBACvBJ;gBACAqC,UAAU;gBACV7B,MAAM;oBACJQ,MAAMA,KAAKiB,IAAI,GAAGK,KAAK,CAAC,GAAG;mBACvB3B,UAAUiB,MAAM,KAAK,IAAI;oBAAEW,WAAW;gBAAK,IAAI,CAAC;YAExD;YACAR,QAAQ;YACRZ,sBAAsB,CAACqB,QAAUA,QAAQ;QAC3C,EAAE,OAAOC,OAAY;;YACnBvC,wBAAgBuC,yBAAAA,MAAOC,OAAO,mBAAI,0BAA0B;gBAC1DP,SAAS;gBACTC,SAAS;YACX;QACF;IACF,GAAG;QACDpB;QACAT;QACAsB;QACAlB,UAAUiB,MAAM;QAChB5B;QACAI;QACAF;KACD;IAED,MAAMyC,eAAevD,YACnB,CAACwD,WAAkB;YACjB,MAAMC,YAAY,MAAM1C,QAAQ;gBAC9B2C,OAAO;gBACPC,aACE,CAAC,sBAAsB,EAAEH,SAAS5B,IAAI,CAAC,sBAAsB,CAAC,GAC9D;gBACFgC,kBAAkB;gBAClBC,yBAAyB;oBAAEC,OAAO;gBAAQ;YAC5C,GACG3B,IAAI,CAAC,IAAM,MACXG,KAAK,CAAC,IAAM;YACf,IAAI,CAACmB,WAAW;YAChB,MAAMhE,UAAUC,IAAImB,WAAW,SAASD,QAAQ,aAAa4C,SAASO,GAAG;YACzE,iEAAiE;YACjE,gDAAgD;YAChDhC,sBAAsB,CAACqB,QAAUA,QAAQ;QAC3C,GACA;QAACrC;QAASF;QAAWD;KAAO;IAG9B,MAAMoD,oBAAoBhE,YACxB,CAACwD,WAAkB;YACjB,KAAK,MAAMS,SAAS1C,UAAW;gBAC7B,IAAI0C,MAAMd,SAAS,IAAIc,MAAMF,GAAG,KAAKP,SAASO,GAAG,EAAE;oBACjD,MAAMjE,OACJJ,IAAImB,WAAW,SAASD,QAAQ,aAAaqD,MAAMF,GAAG,GACtD;wBAAEZ,WAAW;oBAAM,GACnB;wBAAEe,OAAO;oBAAK;gBAElB;YACF;YACA,MAAMpE,OACJJ,IAAImB,WAAW,SAASD,QAAQ,aAAa4C,SAASO,GAAG,GACzD;gBAAEZ,WAAW;YAAK,GAClB;gBAAEe,OAAO;YAAK;QAElB,GACA;QAAC3C;QAAWV;QAAWD;KAAO;IAGhC;;;GAGC,GACD,MAAM,CAACuD,YAAYC,cAAc,GAAGlE,SAAqB;IACzD,MAAM,CAACmE,cAAcC,gBAAgB,GAAGpE,SAAsC,CAAC;IAC/E,MAAMqE,cAAcvE,YAClB,CAACwD,WAAkB;gBAEDlF;YADhB8F,cAAcZ;YACdc,iBAAgBhG,wCAAAA,cAAckG,sBAAsB,CAAChB,SAASiB,aAAa,aAA3DnG,wCAAgE,CAAC;QACnF,GACA,EAAE;IAEJ,MAAMoG,oBAAoB1E,YAAY;QACpC,IAAI,CAACmE,YAAY;QACjB,IAAI;gBAGiB7F;YAFnB,MAAMwB,OACJJ,IAAImB,WAAW,SAASD,QAAQ,aAAauD,WAAWJ,GAAG,GAC3D;gBAAEU,aAAa,GAAEnG,wCAAAA,cAAckG,sBAAsB,CAACH,yBAArC/F,wCAAsD,CAAC;YAAE,GAC1E;gBAAE4F,OAAO;YAAK;YAEhBE,cAAc;QAChB,EAAE,OAAOf,OAAY;;YACnBvC,wBAAgBuC,yBAAAA,MAAOC,OAAO,mBAAI,8BAA8B;gBAC9DP,SAAS;gBACTC,SAAS;YACX;QACF;IACF,GAAG;QAACqB;QAAcF;QAAYrD;QAAiBD;QAAWD;KAAO;IACjE,MAAM+D,eAAe,CAACC,OAA0CC;YAGrDR;6BAFT,KAAChF;YACCwF,OAAOA;YACPC,KAAK,GAAET,sBAAAA,YAAY,CAACO,MAAM,YAAnBP,sBAAuB;YAC9BU,UAAU,CAACC,QACTV,gBAAgB,CAACW,QAAW,aACvBA;wBACH,CAACL,MAAM,EAAEA,UAAU,YAAYI,MAAME,MAAM,CAACJ,KAAK,CAACK,WAAW,GAAGjC,KAAK,CAAC,GAAG,KAAK8B,MAAME,MAAM,CAACJ,KAAK;;YAGpGM,MAAK;;;IAIT;;;;GAIC,GACD,MAAM,CAACzE,WAAW0E,aAAa,GAAGnF,SAAqB;IACvD,MAAM,CAACQ,aAAa4E,eAAe,GAAGpF,SAA+C,CAAC;IACtF,MAAMqF,aAAavF,YACjB,CAACwD,WAAkB;YACjB6B,aAAa7B;YACb8B,eAAehH,cAAckH,uBAAuB,CAAChC,SAASiC,MAAM;QACtE,GACA,EAAE;IAEJ,MAAMC,eAAenG,0BAAyBmB,qBAAAA,YAAYiF,KAAK,YAAjBjF,qBAAqB;IACnE,MAAMkF,mBAAmB5F,YAAY;QACnC,IAAI,CAACW,WAAW;QAChB,IAAI;YACF,qEAAqE;YACrE,wCAAwC;YACxC,MAAMZ,UAAUL,IAAImB,WAAW,SAASD,QAAQ,aAAaD,UAAUoD,GAAG,GAAG;gBAC3E0B,QAAQnH,cAAckH,uBAAuB,CAAC9E;YAChD;YACA2E,aAAa;QACf,EAAE,OAAOhC,OAAY;;YACnBvC,wBAAgBuC,yBAAAA,MAAOC,OAAO,mBAAI,yBAAyB;gBACzDP,SAAS;gBACTC,SAAS;YACX;QACF;IACF,GAAG;QAAClC;QAAiBD;QAAWD;QAAQF;QAAaC;KAAU;IAE/D,qBACE,MAACpC;QACCsH,QAAQ;QACRC,MAAMvF,eAAe,YAAY;YAAEwF,QAAQ;QAAa;QACxDC,cAAc;QACdC,cAAc;;0BAEd,MAAC9G;gBAAM+G,SAAS;;oBACb3E,UAAUiB,MAAM,KAAK,kBACpB,KAAClD;wBAAWyD,SAAQ;wBAAQe,OAAM;kCAC/B,iEACC;yBAGJvC,UAAU4E,GAAG,CAAC,CAAC3C;4BAiBRA;6CAhBL,MAACrE;4BAECiH,WAAU;4BACVF,SAAS;4BACTG,IAAI;gCAAEC,YAAY;4BAAS;;8CAE3B,KAAChH;oCAAWyD,SAAQ;oCAAQsD,IAAI;wCAAEE,MAAM;oCAAE;oCAAGC,MAAM;8CAChDhD,SAAS5B,IAAI;;gCAEftD,cAAckH,uBAAuB,CAAChC,SAASiC,MAAM,EAAEgB,OAAO,iBAC7D,KAAC7H;oCAAKiG,OAAM;oCAASO,MAAK;oCAAQtB,OAAM;oCAAUf,SAAQ;qCACxD;8CACJ,KAACpE;oCAAOyG,MAAK;oCAAQsB,SAASnB,WAAW/B;8CACtC;;8CAEH,KAAC7E;oCAAOyG,MAAK;oCAAQsB,SAASnC,YAAYf;8CACvCA,EAAAA,0BAAAA,SAASiB,aAAa,qBAAtBjB,wBAAwBmD,KAAK,IAAG,iBAAiB;;gCAEnDnD,SAASL,SAAS,iBACjB,KAACvE;oCAAKiG,OAAM;oCAAUO,MAAK;oCAAQrC,SAAQ;mDAE3C,KAACpE;oCAAOyG,MAAK;oCAAQsB,SAAS1C,kBAAkBR;8CAC7C;;8CAGL,KAAC7E;oCAAOyG,MAAK;oCAAQtB,OAAM;oCAAQ4C,SAASnD,aAAaC;8CACtD;;;2BAzBEA,SAASO,GAAG;;kCA8BvB,MAAC5E;wBAAMiH,WAAU;wBAAMF,SAAS;;0CAC9B,KAAC7G;gCACCwF,OAAM;gCACNC,OAAOlD;gCACPmD,UAAU,CAACC,QAAUrC,QAAQqC,MAAME,MAAM,CAACJ,KAAK;gCAC/CM,MAAK;gCACLiB,IAAI;oCAAEE,MAAM;gCAAE;gCACdK,aAAY;;0CAEd,KAACjI;gCACCyG,MAAK;gCACLyB,UAAU,CAACjF,KAAKiB,IAAI,MAAM,CAAC1B;gCAC3BuF,SAAS9D;0CAER;;;;kCAQL,KAACnE;wBACCyC,OAAOC;wBACP2F,MAAMvE;wBACN3C,OAAO6C,MAAM7C,KAAK;wBAClBmH,MAAK;;;;0BAGT,MAAClI;gBAAOmI,MAAMC,QAAQ9C;gBAAa+C,SAAS,IAAM9C,cAAc;gBAAO+C,SAAS;gBAACC,UAAS;;kCACxF,KAACpI;kCAAa,CAAC,WAAW,UAAEmF,8BAAAA,WAAYvC,IAAI,mBAAI,iBAAiB;;kCACjE,KAAC7C;kCACC,cAAA,MAACI;4BAAM+G,SAAS;4BAAKG,IAAI;gCAAEgB,IAAI;4BAAE;;gCAC9B1C,aAAa,SAAS;gCACtBA,aAAa,SAAS;8CACvB,MAACxF;oCAAMiH,WAAW;wCAAEkB,IAAI;wCAAUC,IAAI;oCAAM;oCAAGrB,SAAS;;wCACrDvB,aAAa,QAAQ;wCACrBA,aAAa,SAAS;wCACtBA,aAAa,cAAc;wCAC3BA,aAAa,WAAW;;;gCAE1BA,aAAa,SAAS;;;;kCAG3B,MAAC7F;;0CACC,KAACH;gCAAO+H,SAAS,IAAMtC,cAAc;0CAAQ;;0CAC7C,KAACzF;gCAAOoE,SAAQ;gCAAY2D,SAAShC;0CAClC;;;;;;0BAIP,MAAC7F;gBAAOmI,MAAMC,QAAQtG;gBAAYuG,SAAS,IAAM7B,aAAa;gBAAO8B,SAAS;gBAACC,UAAS;;kCACtF,KAACpI;kCAAa,CAAC,UAAU,WAAE2B,6BAAAA,UAAWiB,IAAI,oBAAI,iBAAiB;;kCAC/D,KAAC7C;kCACC,cAAA,MAACI;4BAAM+G,SAAS;4BAAKG,IAAI;gCAAEgB,IAAI;4BAAE;;8CAC/B,KAACpI;oCACCuI,uBACE,KAACpI;wCACCqI,SAASR,QAAQvG,YAAY+F,OAAO;wCACpC1B,UAAU,CAACC,QACTM,eAAe,CAACL,QAAW,aAAKA;oDAAOwB,SAASzB,MAAME,MAAM,CAACuC,OAAO;;;oCAI1E5C,OAAM;;gCAEPnE,YAAY+F,OAAO,IAAI,EAAC9F,8BAAAA,2BAAAA,UAAW8D,aAAa,qBAAxB9D,yBAA0BgG,KAAK,KAAI,EAAChG,6BAAAA,UAAW+G,OAAO,kBAC7E,KAACpI;oCAAWyD,SAAQ;oCAAUe,OAAM;8CACjC;qCAED;8CACJ,KAACzE;oCACCwF,OAAM;oCACNC,KAAK,GAAEpE,sBAAAA,YAAYiF,KAAK,YAAjBjF,sBAAqB;oCAC5BqE,UAAU,CAACC,QAAUM,eAAe,CAACL,QAAW,aAAKA;gDAAOU,OAAOX,MAAME,MAAM,CAACJ,KAAK;;oCACrF6C,SAAS;oCACTC,SAAS;oCACTxC,MAAK;oCACLwB,aAAa;oCACbvD,OAAOqC,aAAalD,MAAM,GAAG;oCAC7BqF,YACEnC,aAAalD,MAAM,GACf,CAAC,KAAK,EAAEkD,aAAaoC,IAAI,CAAC,MAAM,yDAAyD,CAAC,GAC1F;;8CAGR,KAACzI;oCACCwF,OAAM;oCACNC,KAAK,GAAEpE,4BAAAA,YAAYqH,YAAY,YAAxBrH,4BAA4B;oCACnCqE,UAAU,CAACC,QACTM,eAAe,CAACL,QAAW,aAAKA;gDAAO8C,cAAc/C,MAAME,MAAM,CAACJ,KAAK;;oCAEzE6C,SAAS;oCACTC,SAAS;oCACTxC,MAAK;oCACLwB,aAAY;oCACZoB,WAAW;wCAAEC,WAAW;4CAAEC,WAAW5J,cAAc6J,uBAAuB;wCAAC;oCAAE;;8CAE/E,MAAC9I;oCACC+I,MAAM;oCACNvD,OAAM;oCACNC,OAAOnD,QAAOjB,kCAAAA,YAAY2H,kBAAkB,YAA9B3H,kCAAkC;oCAChDqE,UAAU,CAACC,QACTM,eAAe,CAACL,QAAW,aACtBA;gDACHoD,oBAAoBrD,MAAME,MAAM,CAACJ,KAAK,GAAGwD,OAAOtD,MAAME,MAAM,CAACJ,KAAK,IAAIyD;;oCAG1EnD,MAAK;;sDAEL,KAAClG;4CAAS4F,OAAM;sDAAI;;wCACnB;4CACC;gDAAC;gDAAI;6CAAa;4CAClB;gDAAC;gDAAI;6CAAS;4CACd;gDAAC;gDAAK;6CAAU;4CAChB;gDAAC;gDAAK;6CAAU;4CAChB;gDAAC;gDAAM;6CAAQ;4CACf;gDAAC;gDAAM;6CAAS;yCACjB,CAACqB,GAAG,CAAC,CAAC,CAACqC,SAAS3D,MAAM,iBACrB,KAAC3F;gDAAuB4F,OAAOnD,OAAO6G;0DACnC3D;+CADY2D;;;8CAKnB,KAAClJ;oCAAWyD,SAAQ;oCAAUe,OAAM;8CACjC,yFACC;;;;;kCAIR,MAAChF;;0CACC,KAACH;gCAAO+H,SAAS,IAAMrB,aAAa;0CAAQ;;0CAC5C,KAAC1G;gCAAOoE,SAAQ;gCAAY8D,UAAUnB,aAAalD,MAAM,GAAG;gCAAGkE,SAASd;0CACrE;;;;;;;;AAMb;AACApF,cAAciI,WAAW,GAAG;AAE5B,eAAejI,cAAa"}
@@ -33,6 +33,7 @@ import { doc, runTransaction, updateDoc } from "firebase/firestore";
33
33
  import { useParams } from "next/navigation";
34
34
  import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
35
35
  import { useFirestore, useUser } from "@aglyn/tenant-feature-instance";
36
+ import OrderLocalFulfillmentPanel from "./order-local-fulfillment-panel.component.js";
36
37
  import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
37
38
  import { useConsoleWidgetSlot } from "@aglyn/aglyn/app-utils/console-widget-slot-context";
38
39
  import { consoleOrderZoneOrder, FulfillItemsPanel, FulfillmentList, toZoneFulfillment } from "./order-fulfillment-panel.component.js";
@@ -41,6 +42,7 @@ import { OrderInvoiceButton } from "./order-invoice.component.js";
41
42
  import { OrderReceiptResend } from "./order-receipt-resend.component.js";
42
43
  import { OrderReturns } from "./order-returns.component.js";
43
44
  import { PosReceiptActions } from "./pos-ops/pos-receipt-actions.component.js";
45
+ import { OrderOfflineNotice } from "./order-offline-notice.component.js";
44
46
  const STATUS_COLOR = {
45
47
  pending: 'warning',
46
48
  paid: 'info',
@@ -970,6 +972,14 @@ const usd = (cents)=>`$${((cents != null ? cents : 0) / 100).toFixed(2)}`;
970
972
  })
971
973
  ]
972
974
  }) : null,
975
+ /*#__PURE__*/ _jsx(OrderOfflineNotice, {
976
+ order: order
977
+ }),
978
+ orderId ? /*#__PURE__*/ _jsx(OrderLocalFulfillmentPanel, {
979
+ hostId: hostId,
980
+ orderId: orderId,
981
+ order: order
982
+ }) : null,
973
983
  /*#__PURE__*/ _jsx(Divider, {}),
974
984
  /*#__PURE__*/ _jsx(Typography, {
975
985
  variant: "subtitle2",