@aglyn/plugins-commerce 1.0.0-beta.233 → 1.0.0-beta.235
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.
- package/package.json +13 -13
- package/src/lib/components/cart.js +3 -2
- package/src/lib/components/cart.js.map +1 -1
- package/src/lib/components/console/commerce-analytics-card.component.js +3 -6
- package/src/lib/components/console/commerce-analytics-card.component.js.map +1 -1
- package/src/lib/components/console/commerce-glance-card.component.js +9 -3
- package/src/lib/components/console/commerce-glance-card.component.js.map +1 -1
- package/src/lib/components/console/customer-notifications-card.component.js +3 -1
- package/src/lib/components/console/customer-notifications-card.component.js.map +1 -1
- package/src/lib/components/console/discounts-card.component.js +2 -3
- package/src/lib/components/console/discounts-card.component.js.map +1 -1
- package/src/lib/components/console/host-coupons-card.component.js +3 -4
- package/src/lib/components/console/host-coupons-card.component.js.map +1 -1
- package/src/lib/components/console/local-delivery-settings-card.component.js +2 -2
- package/src/lib/components/console/local-delivery-settings-card.component.js.map +1 -1
- package/src/lib/components/console/locations-card.component.js +1 -1
- package/src/lib/components/console/locations-card.component.js.map +1 -1
- package/src/lib/components/console/member-posts-card.component.js +2 -2
- package/src/lib/components/console/member-posts-card.component.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +35 -30
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/order-fulfillment-panel.component.d.ts +11 -1
- package/src/lib/components/console/order-fulfillment-panel.component.js +75 -4
- package/src/lib/components/console/order-fulfillment-panel.component.js.map +1 -1
- package/src/lib/components/console/order-zones.d.ts +45 -1
- package/src/lib/components/console/order-zones.js +8 -0
- package/src/lib/components/console/order-zones.js.map +1 -1
- package/src/lib/components/console/pickup-delivery-queue-card.component.js +15 -1
- package/src/lib/components/console/pickup-delivery-queue-card.component.js.map +1 -1
- package/src/lib/components/console/pos-page.component.js +3 -2
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/product-editor-dialog.component.d.ts +2 -0
- package/src/lib/components/console/product-editor-dialog.component.js +30 -12
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +13 -4
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/products-hub-zone.component.d.ts +2 -1
- package/src/lib/components/console/products-hub-zone.component.js +1 -1
- package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
- package/src/lib/components/console/reviews-moderation-card.component.js +1 -2
- package/src/lib/components/console/reviews-moderation-card.component.js.map +1 -1
- package/src/lib/components/console/shipping-settings-card.component.js +2 -2
- package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
- package/src/lib/components/console/shippingeasy-card.component.js +1 -1
- package/src/lib/components/console/shippingeasy-card.component.js.map +1 -1
- package/src/lib/components/console/shipstation-card.component.js +1 -2
- package/src/lib/components/console/shipstation-card.component.js.map +1 -1
- package/src/lib/components/console/store-settings-card.component.js +3 -1
- package/src/lib/components/console/store-settings-card.component.js.map +1 -1
- package/src/lib/components/console/tax-settings-card.component.js +4 -1
- package/src/lib/components/console/tax-settings-card.component.js.map +1 -1
- package/src/lib/components/order-status.js +24 -1
- package/src/lib/components/order-status.js.map +1 -1
- package/src/lib/components/product-detail.js +30 -16
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/components/product-grid.d.ts +7 -0
- package/src/lib/components/product-grid.js +50 -7
- package/src/lib/components/product-grid.js.map +1 -1
- package/src/lib/components/related-products.js +1 -1
- package/src/lib/components/related-products.js.map +1 -1
- package/src/lib/components/wishlist.js +1 -1
- package/src/lib/components/wishlist.js.map +1 -1
- package/src/lib/declarations.server.js +11 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/model/commerce-orders.d.ts +8 -0
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce.d.ts +21 -0
- package/src/lib/model/commerce.js +39 -1
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/index.d.ts +1 -0
- package/src/lib/model/index.js +1 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/order-annotations.d.ts +54 -0
- package/src/lib/model/order-annotations.js +76 -0
- package/src/lib/model/order-annotations.js.map +1 -0
- package/src/lib/model/order-events.d.ts +2 -0
- package/src/lib/model/order-events.js.map +1 -1
- package/src/lib/model/order-fulfillment.d.ts +29 -0
- package/src/lib/model/order-fulfillment.js +63 -0
- package/src/lib/model/order-fulfillment.js.map +1 -1
- package/src/lib/model/order-local-fulfillment.d.ts +36 -0
- package/src/lib/model/order-local-fulfillment.js +19 -1
- package/src/lib/model/order-local-fulfillment.js.map +1 -1
- package/src/lib/model/order-status-view.d.ts +11 -0
- package/src/lib/model/order-status-view.js +20 -3
- package/src/lib/model/order-status-view.js.map +1 -1
- package/src/lib/model/product-write.js +8 -4
- package/src/lib/model/product-write.js.map +1 -1
- package/src/lib/server/api-v1/openapi.js +1 -0
- package/src/lib/server/api-v1/openapi.js.map +1 -1
- package/src/lib/server/api-v1/order-view.d.ts +1 -0
- package/src/lib/server/api-v1/order-view.js +10 -0
- package/src/lib/server/api-v1/order-view.js.map +1 -1
- package/src/lib/server/cart-checkout.js +9 -1
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/catalog.d.ts +6 -0
- package/src/lib/server/catalog.js +3 -1
- package/src/lib/server/catalog.js.map +1 -1
- package/src/lib/server/checkout.js +11 -1
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/fulfill-order.d.ts +12 -0
- package/src/lib/server/fulfill-order.js +13 -0
- package/src/lib/server/fulfill-order.js.map +1 -1
- package/src/lib/server/local-delivery-records.d.ts +30 -0
- package/src/lib/server/local-delivery-records.js +284 -0
- package/src/lib/server/local-delivery-records.js.map +1 -0
- package/src/lib/server/order-annotations.d.ts +28 -0
- package/src/lib/server/order-annotations.js +143 -0
- package/src/lib/server/order-annotations.js.map +1 -0
- package/src/lib/server/order-events.js +2 -1
- package/src/lib/server/order-events.js.map +1 -1
- package/src/lib/server/order-notifications.js +39 -8
- package/src/lib/server/order-notifications.js.map +1 -1
- package/src/lib/server/pos-payment.js +9 -3
- package/src/lib/server/pos-payment.js.map +1 -1
- package/src/lib/server/product-catalog.js +6 -2
- package/src/lib/server/product-catalog.js.map +1 -1
- package/src/lib/server/product-drafts.d.ts +36 -9
- package/src/lib/server/product-drafts.js +24 -5
- package/src/lib/server/product-drafts.js.map +1 -1
- package/src/lib/server/product.d.ts +6 -1
- package/src/lib/server/product.js +1 -1
- package/src/lib/server/product.js.map +1 -1
- package/src/lib/server/products-write.d.ts +29 -0
- package/src/lib/server/products-write.js +7 -7
- package/src/lib/server/products-write.js.map +1 -1
- package/src/lib/server/shipment-records.js +2 -0
- package/src/lib/server/shipment-records.js.map +1 -1
- package/src/lib/server/site-page-resolver.d.ts +13 -0
- package/src/lib/server/site-page-resolver.js +138 -1
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server.js +5 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/utils/use-storefront-purchase-event.js +8 -2
- package/src/lib/utils/use-storefront-purchase-event.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos-page.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 type { ConsolePluginPageProps } from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport {\n PRODUCT_LIST_BASE,\n PRODUCT_LIST_QUERY,\n} from '../../constants/product-list-query'\nimport { NextPageTitle } from '@aglyn/shared-ui-next/contexts/next-page-title-provider'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport type { ListFilterRequest } from '@aglyn/shared-ui-jsx/const/list-filter'\nimport { planListQuery } from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport { useViewportFill } from '@aglyn/shared-ui-jsx/hooks/use-viewport-fill'\nimport { nameSearchNormalizers } from '@aglyn/aglyn/app-utils/name-search'\nimport {\n Badge,\n Box,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Drawer,\n MenuItem,\n Stack,\n TextField,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n useMediaQuery,\n useTheme,\n} from '@mui/material'\nimport {\n collection,\n getDocs,\n limit,\n query,\n where,\n} from 'firebase/firestore'\nimport { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport { useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport { listQueryConstraints } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { useOrgPlan } from '@aglyn/tenant-feature-instance'\nimport {\n newAttemptKey,\n openPosSale,\n posRegisterContext,\n posTender,\n PosRequestError,\n usd,\n type PosRegisterContext,\n type PosSaleSummary,\n} from './pos/pos-api'\nimport { PosCartPanel, type RegisterLine } from './pos/pos-cart-panel.component'\nimport { ScanAdornment } from '../../barcode/scan-button.component'\nimport { useScannerWedge } from '../../barcode/scanner-wedge'\nimport {\n PosCustomerLookup,\n PosLastReceipt,\n PosOperationsBar,\n usePosCashier,\n usePosOpsSettings,\n type PosSelectedCustomer,\n} from './pos-ops/register-ops'\nimport { POS_QUICK_KEYS, POS_TOUCH_PX, PosProductGrid } from './pos/pos-product-grid.component'\nimport { PosItemDialog, posItemNeedsChoice, type PosItemChoice } from './pos/pos-item-dialog.component'\nimport { PosReceiptPanel } from './pos/pos-receipt-panel.component'\nimport { PosTenderPanel } from './pos/pos-tender-panel.component'\nimport { PosKioskQueue } from './pos/pos-kiosk-queue.component'\nimport { posDisplayTipCents, usePosDisplay } from './pos/use-pos-display'\nimport {\n type ConsoleWidgetSlotRenderer,\n useConsoleWidgetSlot,\n} from '@aglyn/aglyn/app-utils/console-widget-slot-context'\nimport { POS_ORDERS_ZONE } from './pos-zones'\nimport { PosOfflineBanner, PosOfflineCheckout, usePosOffline } from './pos-offline/register-offline'\n\n/** The till sells active products only; every read of the catalog asks it. */\nconst SELLABLE: ListFilterRequest = { field: 'status', op: 'equals', value: 'active' }\n\n/**\n * How many products the till's grid shows. The grid is a picker, not the\n * catalog: typing narrows it on the query, so a name reaches every product.\n */\nconst POS_GRID_CEILING = 500\n\n/**\n * The products hub's query (AGL-3321), narrowed to what the till may sell:\n * live products (`deletedAt == null`, the list's scope), `status == active`,\n * and any typed word as the search on `nameTokens` — or, for a scan, the\n * whole scanned code as a SKU or barcode clause. Every shape is one the hub's\n * composites already serve.\n */\nexport function posProductPlan(options: {\n search?: string\n code?: { field: 'barcodes' | 'skus'; value: string }\n /** A category chip (AGL-3607): served by the storefront's own composite. */\n categoryId?: string\n /** The Quick keys chip (AGL-3607): products marked `posQuickKey`. */\n quickKeys?: boolean\n}) {\n const typed = options.search?.trim()\n const plan = planListQuery(\n POS_GRID_QUERY,\n {\n base: PRODUCT_LIST_BASE,\n clauses: [\n SELLABLE,\n ...(options.code\n ? [{ field: options.code.field, op: 'contains', value: options.code.value }]\n : []),\n ...(options.categoryId\n ? [{ field: 'categoryIds', op: 'contains', value: options.categoryId }]\n : []),\n ],\n // A chip and a typed word are both array clauses, and Firestore takes\n // one: with a chip on, the word narrows by name prefix instead.\n search: typed && !options.categoryId ? [typed] : [],\n },\n nameSearchNormalizers,\n )\n // The Quick keys chip (AGL-3607) is a boolean equality the list planner\n // does not model, added beside the scope and status it composes with —\n // `deletedAt ==, status ==, posQuickKey ==, nameLower ASC` is declared in\n // the index file. A typed word searches the whole catalog instead: a\n // cashier who types is looking past the quick keys.\n if (options.quickKeys && !typed && !options.code) {\n return {\n ...plan,\n filters: [...plan.filters, { path: 'posQuickKey', op: '==', value: true }],\n } as typeof plan\n }\n return plan\n}\n\n/**\n * The grid's query: the products hub's, plus the category a chip picks\n * (AGL-3607). `categoryIds CONTAINS, nameLower ASC` is a composite the\n * storefront catalog already declares.\n */\nexport const POS_GRID_QUERY = {\n ...PRODUCT_LIST_QUERY,\n fields: [\n ...PRODUCT_LIST_QUERY.fields,\n {\n column: 'categoryIds',\n kind: 'exact' as const,\n path: 'categoryIds',\n tokensPath: 'categoryIds',\n operators: ['contains'],\n },\n ],\n} as typeof PRODUCT_LIST_QUERY\n\n/**\n * POS register (AGL-312, AGL-3607): a tablet-first sale surface — the\n * product grid with search, scan and category chips, the basket, and a\n * tender panel that takes one or more payments against an open sale (cash,\n * card reader, a native Tap to Pay bridge, a typed card, the QR link, gift\n * cards and room charges) until the balance is zero. A paired customer\n * display mirrors the basket and takes the tip and the receipt (AGL-3608).\n *\n * On a wide screen (900px and up) the grid and the register sit side by\n * side; below that the register is a bottom sheet behind a sticky total bar.\n */\n/*\n * `managePos` is NOT read here. The nav item in `plugin.ts` declares it and\n * the shell refuses the route before this component is constructed, so a\n * reader without the key never reaches this file and there is no unpermitted\n * state for it to render — the same arrangement the entitlement gate has.\n *\n * Re-adding a check off the `permissions` prop would be a second answer to a\n * settled question, and a laxer one: a prop absent because the map has not\n * landed reads as permitted here, while the shell holds the route on that\n * same condition rather than guessing. The server routes remain the\n * enforcement point for every sale and every payment.\n */\n/** Where a register device remembers the category chip it was left on. */\nconst POS_CATEGORY_KEY = 'aglyn.pos.category'\n\n/** The basket line an item-sheet choice makes: label and estimate from the product. */\nexport function registerLineFor(\n product: any,\n choice: PosItemChoice,\n): RegisterLine {\n const { variant, modifiers, quantity } = choice\n const resolved = CommerceModel.resolveLineModifiers(product, modifiers)\n const picked = resolved.ok ? resolved.modifiers : []\n const label = CommerceModel.lineLabelWithModifiers(\n Object.keys(variant.options ?? {}).length\n ? Object.values(variant.options ?? {}).join(' / ')\n : undefined,\n picked,\n )\n return {\n productId: product.$id,\n ...(variant.id !== 'default' ? { variantId: variant.id } : {}),\n name: product.name,\n ...(label ? { variantLabel: label } : {}),\n unitAmountCents:\n Math.round(Number(variant.priceUsd) * 100) + (resolved.ok ? resolved.extraCents : 0),\n quantity,\n ...(modifiers.length ? { modifiers } : {}),\n }\n}\n\n/** Same product, variant and choices: the lines that merge into one. */\nfunction lineKey(line: Pick<RegisterLine, 'productId' | 'variantId' | 'modifiers'>): string {\n return `${line.productId}:${line.variantId ?? ''}:${CommerceModel.modifierSelectionKey(line.modifiers)}`\n}\n\n/**\n * The register's zone (AGL-3644), drawn again only when what it is handed\n * changes: the till redraws on every tap, and the shell's renderer is not\n * memoized (the order dialog's reasoning).\n */\nconst PosOrdersZone = memo(function PosOrdersZone(props: {\n renderer: ConsoleWidgetSlotRenderer\n hostId: string\n registerId: string | null\n}) {\n const { renderer: Renderer, hostId, registerId } = props\n return <Renderer slot={POS_ORDERS_ZONE.id} hostId={hostId} orgId={undefined} registerId={registerId} />\n})\n\nexport function PosConsolePage({ hostId }: ConsolePluginPageProps) {\n const WidgetSlot = useConsoleWidgetSlot()\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const theme = useTheme()\n const wide = useMediaQuery(theme.breakpoints.up('md'))\n\n const [search, setSearch] = useState('')\n // The chip a register opens on is remembered per device: a counter that\n // rings from its quick keys reopens on them.\n const [categoryId, setCategoryIdState] = useState(() => {\n try {\n return window.localStorage.getItem(POS_CATEGORY_KEY) ?? ''\n } catch {\n return ''\n }\n })\n const setCategoryId = useCallback((id: string) => {\n setCategoryIdState(id)\n try {\n window.localStorage.setItem(POS_CATEGORY_KEY, id)\n } catch {\n // Storage blocked: the chip still works for this visit.\n }\n }, [])\n const gridPlan = useMemo(\n () =>\n categoryId === POS_QUICK_KEYS\n ? posProductPlan({ search, quickKeys: true })\n : posProductPlan({ search, categoryId }),\n [search, categoryId],\n )\n const { data: productDocs } = useFirestoreCollection<any>(\n /*\n * The till's grid, narrowed by the QUERY rather than by the rows it\n * happened to fetch (AGL-2501, AGL-2292, AGL-3321): the first five\n * hundred SELLABLE products by name, and a typed name reaches the whole\n * catalog. The scan does not come through here — see `handleSearchEnter`.\n */\n () =>\n query(\n collection(firestore, 'hosts', hostId, 'products'),\n ...listQueryConstraints(gridPlan),\n limit(POS_GRID_CEILING),\n ),\n [firestore, hostId, gridPlan],\n { idField: '$id' },\n )\n const { data: categoryDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'productCategories'), limit(30)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { data: locationDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'locations'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { data: registerDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'registers'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { org, ready: planReady } = useOrgPlan(hostId)\n const registers = [...(registerDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n // Only registers within the plan cap can transact (AGL-482), and not until\n // the org doc has arrived (AGL-1064). Per SITE (AGL-1775).\n const registerCap = Aglyn.checkHostRegisterQuota(org, hostId, registers.length).limit\n const withinCap = CommerceModel.registersWithinCap(registers, registerCap)\n const usableRegisters = planReady ? registers.filter((r: any) => withinCap.has(r.$id)) : []\n /*\n * The stays a sale can be charged to, asked for BY STATUS (AGL-3321).\n */\n const { data: reservationDocs } = useFirestoreCollection<any>(\n () =>\n query(\n collection(firestore, 'hosts', hostId, 'reservations'),\n where('status', '==', 'checked_in'),\n limit(100),\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const openStays = reservationDocs ?? []\n\n const [lines, setLines] = useState<RegisterLine[]>([])\n const [discountPct, setDiscountPct] = useState(0)\n // The customer the lookup attached (AGL-3609); their email is the sale's.\n const [customer, setCustomer] = useState<PosSelectedCustomer | null>(null)\n const customerEmail = customer?.email ?? ''\n const [locationId, setLocationId] = useState('')\n const [registerId, setRegisterId] = useState('')\n useEffect(() => {\n if (registerId || usableRegisters.length === 0) return\n const first = usableRegisters[0]\n setRegisterId(first.$id)\n if (first.locationId) setLocationId(first.locationId)\n }, [usableRegisters, registerId])\n const registerName = String(\n usableRegisters.find((register: any) => register.$id === registerId)?.name ?? '',\n )\n // Who is ringing (AGL-3609): a PIN-switched cashier, and the idle lock.\n const opsSettings = usePosOpsSettings(hostId)\n const cashier = usePosCashier({\n hostId,\n registerId,\n autoLockMinutes: opsSettings.autoLockMinutes,\n })\n // The last completed sale, whose receipt can still be printed (AGL-3609).\n const [lastReceipt, setLastReceipt] = useState<{ orderId: string } | null>(null)\n\n const [context, setContext] = useState<PosRegisterContext | null>(null)\n // Keyed on the uid, not the user object: a session refresh hands back a\n // new object for the same person, and re-reading the context on every one\n // would re-render the register in a loop.\n const userRef = useRef(user)\n userRef.current = user\n const uid = user?.uid ?? ''\n useEffect(() => {\n const signedIn = userRef.current\n if (!signedIn) return\n let active = true\n posRegisterContext(signedIn, hostId)\n .then((answer) => {\n // Only a whole answer: a register that half-knows its tenders would\n // offer one the server then refuses.\n if (active && answer?.settings && answer?.terminal) setContext(answer)\n })\n .catch(() => undefined)\n return () => {\n active = false\n }\n }, [uid, hostId])\n\n /** The open sale, once the basket has been priced and payment started. */\n const [sale, setSale] = useState<PosSaleSummary | null>(null)\n const [saleLines, setSaleLines] = useState<RegisterLine[]>([])\n const [opening, setOpening] = useState(false)\n const [sheetOpen, setSheetOpen] = useState(false)\n const [pairing, setPairing] = useState<{\n code: string\n expiresAtMs: number\n /** A customer display, or a self-service kiosk (AGL-3623). */\n mode: CommerceModel.PosDeviceMode\n } | null>(null)\n const display = usePosDisplay(user, hostId, registerId)\n\n /**\n * One key per basket (AGL-1691): minted on the first Charge and retired\n * whenever the basket changes, so a retried Charge finds the same sale and\n * ringing the same coffee twice is two sales.\n */\n const attemptKey = useRef('')\n const fill = useViewportFill({ min: 480 })\n useEffect(() => {\n attemptKey.current = ''\n }, [lines, discountPct])\n\n const products = useMemo(\n () =>\n [...(productDocs ?? [])].map((product: any) => ({\n ...CommerceModel.liftLegacyProduct(product),\n $id: product.$id,\n })),\n [productDocs],\n )\n /*\n * THE OFFLINE REGISTER (AGL-3625): the catalog and the site's offline rules\n * kept on this device, the queue of cash sales rung without a connection,\n * and the sync that records them once it returns.\n */\n const loadCatalog = useCallback(\n async () =>\n (\n await getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'products'),\n ...listQueryConstraints(posProductPlan({})),\n limit(POS_GRID_CEILING),\n ),\n )\n ).docs.map((snapshot) => ({ ...(snapshot.data() as Record<string, unknown>), $id: snapshot.id })),\n [firestore, hostId],\n )\n const liveRegister = usableRegisters.find((register: any) => register.$id === registerId)\n const offline = usePosOffline({\n hostId,\n user,\n registerId,\n openShiftId: liveRegister ? (liveRegister.openShiftId ?? null) : undefined,\n locationId,\n ...(cashier.assertion ? { cashierAssertion: cashier.assertion } : {}),\n liveProducts: products,\n loadCatalog,\n })\n const [offlineCheckout, setOfflineCheckout] = useState(false)\n /** Open sales left unpaid when the connection dropped, voided once it returns. */\n const strandedSales = useRef<string[]>([])\n useEffect(() => {\n if (offline.offline || !user || !strandedSales.current.length) return\n const orderIds = strandedSales.current.splice(0)\n for (const orderId of orderIds) {\n void posTender(user, hostId, orderId, 'void').catch(() => undefined)\n }\n }, [offline.offline, user, hostId])\n const sellProducts = useMemo(\n () => (offline.offline && offline.ready ? offline.gridProducts(search, categoryId, POS_QUICK_KEYS) : products),\n [offline, search, categoryId, products],\n )\n const productsById = useMemo(\n () => new Map<string, any>(sellProducts.map((product: any) => [product.$id, product] as const)),\n [sellProducts],\n )\n /** AGL-2357: warns, never blocks. */\n const shortfalls = useMemo(\n () =>\n lines.map((line) => {\n const product: any = productsById.get(line.productId)\n if (!product) return null\n return CommerceModel.stockShortfall(\n product,\n line.variantId ?? product.variants?.[0]?.id,\n line.quantity,\n )\n }),\n [lines, productsById],\n )\n\n const itemsCents = lines.reduce((sum, line) => sum + line.unitAmountCents * line.quantity, 0)\n const discountCents = Math.round((itemsCents * discountPct) / 100)\n const estimateCents = itemsCents - discountCents\n const units = lines.reduce((sum, line) => sum + line.quantity, 0)\n\n const notify = useCallback(\n (message: string, variant: 'success' | 'error' | 'warning' | 'info') => {\n enqueueSnackbar(message, { variant, persist: false, allowDuplicate: true })\n },\n [enqueueSnackbar],\n )\n\n /** A pairing code for a customer display or a self-service kiosk (AGL-3623). */\n const showPairingCode = useCallback(\n async (mode: CommerceModel.PosDeviceMode) => {\n try {\n setPairing({ ...(await display.pairingCode(mode)), mode })\n } catch (error) {\n notify(error instanceof PosRequestError ? error.message : 'Could not make a code', 'error')\n }\n },\n [display, notify],\n )\n\n /**\n * Adds a line, or more of an identical one: the same product, variant and\n * modifiers merge, so two oat lattes are one line of two and an oat latte\n * beside a whole-milk one stays two lines.\n */\n const addProduct = useCallback(\n (product: any, variant?: any, modifiers: CommerceModel.ModifierSelection[] = [], quantity = 1) => {\n const next = registerLineFor(product, {\n variant: variant ?? product.variants[0],\n modifiers,\n quantity,\n })\n setLines((prev) => {\n const existing = prev.find((line) => lineKey(line) === lineKey(next))\n if (existing) {\n return prev.map((line) =>\n line === existing\n ? { ...line, quantity: Math.min(99, line.quantity + next.quantity) }\n : line,\n )\n }\n return [...prev, next]\n })\n },\n [],\n )\n\n /** The item sheet: a product to add, or a basket line to change. */\n const [itemSheet, setItemSheet] = useState<{\n product: any\n editIndex?: number\n initial?: { variantId?: string; modifiers?: CommerceModel.ModifierSelection[]; quantity: number }\n } | null>(null)\n // Every product rung up this sale, so a line can be reopened after the\n // grid has moved on to another category or search.\n const productCache = useRef(new Map<string, any>())\n const tapProduct = useCallback(\n (product: any, variant?: any) => {\n productCache.current.set(product.$id, product)\n if (posItemNeedsChoice(product) && !(variant && CommerceModel.productModifierGroups(product).length === 0)) {\n setItemSheet({\n product,\n ...(variant ? { initial: { variantId: variant.id, quantity: 1 } } : {}),\n })\n return\n }\n addProduct(product, variant)\n },\n [addProduct],\n )\n const basketCounts = useMemo(() => {\n const counts = new Map<string, number>()\n for (const line of lines) counts.set(line.productId, (counts.get(line.productId) ?? 0) + line.quantity)\n return counts\n }, [lines])\n\n /**\n * The barcode wedge: a LOOKUP against the whole catalog (AGL-2501),\n * barcode first, and a miss says so.\n */\n const lookupCode = useCallback(async (scanned: string) => {\n const needle = scanned.trim().toLowerCase()\n if (!needle || sale) return\n if (offline.offline) {\n const found = offline.findByCode(needle)\n if (!found) return void notify(`No product matches “${scanned.trim()}”`, 'warning')\n tapProduct(found.product, found.variant)\n setSearch('')\n return\n }\n const lookup = async (field: 'barcodes' | 'skus') => {\n const found = await getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'products'),\n ...listQueryConstraints(posProductPlan({ code: { field, value: needle } })),\n limit(1),\n ),\n )\n return found.docs[0]\n }\n let hit: Awaited<ReturnType<typeof lookup>>\n try {\n hit = (await lookup('barcodes')) ?? (await lookup('skus'))\n } catch (error) {\n console.error(error)\n offline.reportNetworkFailure()\n return void notify('Could not reach the catalog — try again', 'warning')\n }\n if (!hit) return void notify(`No product matches “${scanned.trim()}”`, 'warning')\n const product = { ...CommerceModel.liftLegacyProduct(hit.data() as any), $id: hit.id }\n const variant =\n product.variants.find(\n (item: any) =>\n item.barcode?.trim().toLowerCase() === needle || item.sku?.trim().toLowerCase() === needle,\n ) ?? product.variants[0]\n // A scanned variant is the variant; a product with modifiers still asks\n // for them, with that variant already picked.\n tapProduct(product, variant)\n setSearch('')\n }, [sale, firestore, hostId, tapProduct, notify, offline])\n const handleSearchEnter = useCallback(() => lookupCode(search), [lookupCode, search])\n // A scanner fired while focus is on a product or a button (AGL-3619), and\n // the camera; neither while a sale is taking payment or an item is open.\n useScannerWedge((code) => void lookupCode(code), !sale && !itemSheet)\n const scanAdornment = useMemo(\n () => <ScanAdornment label=\"Scan a barcode with the camera\" onScan={(code) => void lookupCode(code)} />,\n [lookupCode],\n )\n\n /** Prices the basket on the server and opens the sale for payment. */\n // A ref, not `opening`: two taps inside one React batch both read the\n // pre-click state, and only a ref is written before the second reads it\n // (the AGL-1682 lesson).\n const chargeInFlight = useRef(false)\n const charge = useCallback(async () => {\n if (chargeInFlight.current || lines.length === 0 || !user) return\n if (!registerId) return void notify('Select a register before taking payment', 'warning')\n // Offline, the basket rings as a cash sale kept on this register (AGL-3625).\n if (offline.offline) {\n if (!offline.ready) return void notify(offline.unavailableReason ?? 'Offline selling is unavailable', 'warning')\n setOfflineCheckout(true)\n return\n }\n if (!attemptKey.current) attemptKey.current = newAttemptKey()\n chargeInFlight.current = true\n setOpening(true)\n try {\n const opened = await openPosSale(user, attemptKey.current, {\n hostId,\n registerId,\n lines,\n discountPct,\n ...(customerEmail ? { customerEmail } : {}),\n ...(customer ? { customer } : {}),\n ...(cashier.assertion ? { cashierAssertion: cashier.assertion } : {}),\n ...(locationId ? { locationId } : {}),\n })\n setSaleLines(lines)\n setSale({\n orderId: opened.orderId,\n status: 'pending',\n totalCents: opened.totals.totalCents,\n paidCents: 0,\n dueCents: opened.totals.totalCents,\n tenderableCents: opened.totals.totalCents,\n tipCents: 0,\n payments: [],\n })\n setSheetOpen(true)\n } catch (error) {\n if (error instanceof PosRequestError) {\n notify(error.message, 'error')\n } else {\n // The request never reached the server: the register is offline.\n offline.reportNetworkFailure()\n notify('The register lost its connection. Charge again to take cash offline.', 'warning')\n }\n } finally {\n chargeInFlight.current = false\n setOpening(false)\n }\n }, [lines, user, registerId, hostId, discountPct, customerEmail, customer, cashier.assertion, locationId, notify, offline])\n\n const resetSale = useCallback(() => {\n if (sale?.status === 'paid') setLastReceipt({ orderId: sale.orderId })\n setSale(null)\n setSaleLines([])\n setLines([])\n setDiscountPct(0)\n setCustomer(null)\n setSheetOpen(false)\n }, [sale])\n\n // The customer display mirrors the basket while nothing else is on it.\n const mirrored = sale ? saleLines : lines\n // Whether the display was last left showing a basket. A paid sale leaves it\n // on the thank-you the server wrote, which an empty new basket must not\n // cut short with the idle screen; the next item added takes it over.\n // True at first, so a register that opens empty clears whatever a previous\n // session left on the screen.\n const displayShowsBasket = useRef(true)\n // The tip picked for the payment about to be taken; the ledger has it only\n // once that payment is recorded. Gone with the sale.\n const [pendingTipCents, setPendingTipCents] = useState(0)\n useEffect(() => {\n if (!sale || sale.status === 'paid') setPendingTipCents(0)\n }, [sale])\n useEffect(() => {\n if (sale?.status === 'paid') displayShowsBasket.current = false\n }, [sale?.status])\n useEffect(() => {\n if (!display.connected || display.asking) return\n if (sale?.payments.some((payment) => payment.status === 'pending' && payment.method === 'card_present')) {\n return\n }\n if (sale?.status === 'paid') return\n if (!mirrored.length && !displayShowsBasket.current) return\n const timer = setTimeout(() => {\n displayShowsBasket.current = mirrored.length > 0\n void display.show(\n mirrored.length\n ? {\n mode: 'cart',\n cart: {\n lines: mirrored.map((line) => ({\n name: line.name,\n ...(line.variantLabel ? { variantLabel: line.variantLabel } : {}),\n quantity: line.quantity,\n amountCents: line.unitAmountCents * line.quantity,\n })),\n itemsCents,\n discountCents,\n taxCents: sale ? Math.max(0, sale.totalCents - estimateCents) : 0,\n totalCents: sale ? sale.totalCents : estimateCents,\n ...(sale\n ? {\n paidCents: sale.paidCents,\n dueCents: sale.dueCents,\n tipCents: posDisplayTipCents(sale, pendingTipCents),\n }\n : {}),\n },\n }\n : { mode: 'idle' },\n )\n }, 400)\n return () => clearTimeout(timer)\n }, [display, mirrored, sale, itemsCents, discountCents, estimateCents, pendingTipCents])\n\n const registerPicker = !planReady ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Checking your plan…'}\n </Typography>\n ) : usableRegisters.length === 0 ? (\n <Typography variant=\"body2\" color=\"warning.main\">\n {registers.length > 0\n ? 'Your registers exceed your plan — remove extras or upgrade in Billing to take payments.'\n : 'No POS register yet. Add one under Commerce → Settings → POS registers before taking payments.'}\n </Typography>\n ) : usableRegisters.length > 1 ? (\n <TextField\n label=\"Register\"\n value={registerId}\n onChange={(event) => setRegisterId(event.target.value)}\n size=\"small\"\n select\n disabled={Boolean(sale)}\n >\n {usableRegisters.map((register: any) => (\n <MenuItem key={register.$id} value={register.$id}>\n {register.name}\n </MenuItem>\n ))}\n </TextField>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {usableRegisters[0]?.name}\n </Typography>\n )\n\n /**\n * On a narrow screen the register's bar owns the bottom edge, so it moves\n * the console's floating launchers clear of itself through the shell's\n * `--aglyn-dock-inset-bottom` seam; a launcher left in the corner sat on\n * top of Charge.\n */\n const bottomBarRef = useRef<HTMLDivElement | null>(null)\n useEffect(() => {\n const node = bottomBarRef.current\n if (wide || !node) return undefined\n const root = document.documentElement\n const property = '--aglyn-dock-inset-bottom'\n const publish = () => {\n root.style.setProperty(property, `${Math.round(node.getBoundingClientRect().height) + 20}px`)\n }\n publish()\n const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(publish)\n observer?.observe(node)\n return () => {\n observer?.disconnect()\n root.style.removeProperty(property)\n }\n }, [wide])\n\n const registerPanel = (\n <Stack spacing={1.5} sx={{ minHeight: 0, flex: 1 }}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"h6\" sx={{ flex: 1 }}>\n {'Register'}\n </Typography>\n {registerId ? (\n <Button\n size=\"small\"\n onClick={() => void showPairingCode('display')}\n >\n {display.connected ? 'Display connected' : 'Pair display'}\n </Button>\n ) : null}\n </Stack>\n {registerPicker}\n {(locationDocs?.length ?? 0) > 1 ? (\n <TextField\n label=\"Location\"\n value={locationId}\n onChange={(event) => setLocationId(event.target.value)}\n size=\"small\"\n select\n disabled={Boolean(sale)}\n >\n {(locationDocs ?? []).map((location: any) => (\n <MenuItem key={location.$id} value={location.$id}>\n {location.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n {registerId ? (\n <PosOperationsBar\n hostId={hostId}\n registerId={registerId}\n {...(registerName ? { registerName } : {})}\n cashier={cashier}\n onExchange={(returned) =>\n setCustomer(\n returned.email || returned.name\n ? { kind: 'none', id: '', name: returned.name ?? '', email: returned.email, phone: null }\n : null,\n )\n }\n />\n ) : null}\n <PosCartPanel\n lines={sale ? saleLines : lines}\n shortfalls={sale ? [] : shortfalls}\n onQuantity={(index, quantity) =>\n setLines((prev) =>\n quantity <= 0\n ? prev.filter((_line, at) => at !== index)\n : prev.map((line, at) => (at === index ? { ...line, quantity: Math.min(99, quantity) } : line)),\n )\n }\n onRemove={(index) => setLines((prev) => prev.filter((_line, at) => at !== index))}\n onEdit={(index) => {\n const line = lines[index]\n const product = line\n ? (productCache.current.get(line.productId) ?? productsById.get(line.productId))\n : undefined\n if (!line || !product) return\n setItemSheet({\n product,\n editIndex: index,\n initial: {\n ...(line.variantId ? { variantId: line.variantId } : {}),\n ...(line.modifiers ? { modifiers: line.modifiers } : {}),\n quantity: line.quantity,\n },\n })\n }}\n discountPct={discountPct}\n onDiscountPct={setDiscountPct}\n customer={\n sale ? null : <PosCustomerLookup hostId={hostId} value={customer} onChange={setCustomer} />\n }\n locked={Boolean(sale)}\n />\n {sale && user ? (\n sale.status === 'paid' ? (\n <PosReceiptPanel\n user={user}\n hostId={hostId}\n sale={sale}\n context={context}\n display={display}\n onNewSale={resetSale}\n registerId={registerId}\n {...(registerName ? { registerName } : {})}\n {...(cashier.cashier ? { cashierName: cashier.cashier.name } : {})}\n notify={notify}\n />\n ) : (\n <PosTenderPanel\n user={user}\n hostId={hostId}\n registerId={registerId}\n sale={sale}\n context={context}\n stays={openStays}\n display={display}\n onSale={setSale}\n onVoided={() => {\n setSale(null)\n setSaleLines([])\n }}\n onTipChange={setPendingTipCents}\n {...(cashier.assertion ? { cashierAssertion: cashier.assertion } : {})}\n notify={notify}\n offline={offline.offline}\n {...(offline.ready\n ? {\n onSellOffline: () => {\n // The server's open sale took no payment; it is voided once\n // the connection returns, and the basket rings offline.\n strandedSales.current.push(sale.orderId)\n setSale(null)\n setSaleLines([])\n setOfflineCheckout(true)\n },\n }\n : {})}\n />\n )\n ) : (\n <>\n <Box sx={{ display: 'flex', justifyContent: 'space-between' }}>\n <Typography variant=\"h6\">{'Due'}</Typography>\n <Typography variant=\"h6\">{usd(estimateCents)}</Typography>\n </Box>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Tax is added when you charge.'}\n </Typography>\n <Button\n variant=\"contained\"\n size=\"large\"\n disabled={opening || lines.length === 0 || !registerId}\n onClick={() => void charge()}\n sx={{ minHeight: 56 }}\n >\n {opening\n ? 'Pricing…'\n : offline.offline\n ? `Cash ${usd(offline.totalsFor(lines, discountPct)?.totalCents ?? estimateCents)} (offline)`\n : `Charge ${usd(estimateCents)}`}\n </Button>\n {lastReceipt ? (\n <PosLastReceipt\n hostId={hostId}\n orderId={lastReceipt.orderId}\n {...(registerName ? { registerName } : {})}\n {...(cashier.cashier ? { cashierName: cashier.cashier.name } : {})}\n />\n ) : null}\n </>\n )}\n </Stack>\n )\n\n return (\n <>\n <NextPageTitle screen={'POS'} />\n <Box\n ref={fill.ref}\n sx={{\n display: 'flex',\n // From under the console's header, nav and page title to the\n // bottom of the window, not a whole window tall below them.\n height: fill.height,\n overflow: 'hidden',\n }}\n >\n <Box sx={{ flex: 1, p: 2, overflowY: 'auto', pb: wide ? 2 : 12 }}>\n <Box sx={{ mb: 2, '&:empty': { display: 'none' } }}>\n <PosOfflineBanner offline={offline} />\n </Box>\n {/* \"Pay at counter\" orders from this register's kiosks (AGL-3623). */}\n {registerId ? (\n <PosKioskQueue\n user={user}\n hostId={hostId}\n registerId={registerId}\n disabled={Boolean(sale)}\n onTake={(next, nextLines) => {\n setSaleLines(nextLines)\n setSale(next)\n setSheetOpen(true)\n }}\n notify={notify}\n />\n ) : null}\n {/* Orders other channels send to the counter (AGL-3644). */}\n {WidgetSlot ? <PosOrdersZone renderer={WidgetSlot} hostId={hostId} registerId={registerId || null} /> : null}\n <PosProductGrid\n search={search}\n onSearch={setSearch}\n onSearchEnter={() => void handleSearchEnter()}\n scanAdornment={scanAdornment}\n categories={(categoryDocs ?? []).map((category: any) => ({\n $id: category.$id,\n name: String(category.name ?? 'Category'),\n }))}\n categoryId={categoryId}\n onCategory={setCategoryId}\n notices={gridPlan.notices}\n hostId={hostId}\n products={sellProducts}\n basketCounts={basketCounts}\n onTap={(product) => {\n if (sale) return void notify('Finish or void the open sale first', 'info')\n tapProduct(product)\n }}\n />\n </Box>\n {wide ? (\n <Box\n sx={{\n width: { md: 400, lg: 440 },\n borderLeft: 1,\n borderColor: 'divider',\n display: 'flex',\n flexDirection: 'column',\n p: 2,\n overflowY: 'auto',\n }}\n >\n {registerPanel}\n </Box>\n ) : null}\n </Box>\n {!wide ? (\n <>\n <Box\n ref={bottomBarRef}\n sx={{\n position: 'fixed',\n left: 0,\n right: 0,\n bottom: 0,\n p: 1.5,\n bgcolor: 'background.paper',\n borderTop: 1,\n borderColor: 'divider',\n display: 'flex',\n gap: 1,\n alignItems: 'center',\n zIndex: theme.zIndex.appBar,\n }}\n >\n <Badge badgeContent={units} color=\"primary\">\n <Button\n variant=\"outlined\"\n onClick={() => setSheetOpen(true)}\n sx={{ minHeight: POS_TOUCH_PX }}\n >\n {sale ? 'Payment' : 'Cart'}\n </Button>\n </Badge>\n <Typography variant=\"h6\" sx={{ flex: 1, textAlign: 'right' }}>\n {usd(sale ? sale.dueCents : estimateCents)}\n </Typography>\n {!sale ? (\n <Button\n variant=\"contained\"\n disabled={opening || lines.length === 0 || !registerId}\n onClick={() => void charge()}\n sx={{ minHeight: POS_TOUCH_PX }}\n >\n {'Charge'}\n </Button>\n ) : null}\n </Box>\n <Drawer\n anchor=\"bottom\"\n open={sheetOpen}\n onClose={() => setSheetOpen(false)}\n slotProps={{ paper: { sx: { maxHeight: '90dvh', p: 2, borderTopLeftRadius: 16, borderTopRightRadius: 16 } } }}\n >\n {registerPanel}\n </Drawer>\n </>\n ) : null}\n <PosItemDialog\n product={itemSheet?.product ?? null}\n initial={itemSheet?.initial}\n onClose={() => setItemSheet(null)}\n onConfirm={(choice) => {\n const sheet = itemSheet\n setItemSheet(null)\n if (!sheet) return\n if (sheet.editIndex === undefined) {\n addProduct(sheet.product, choice.variant, choice.modifiers, choice.quantity)\n return\n }\n const at = sheet.editIndex\n setLines((prev) =>\n prev.map((line, index) => (index === at ? registerLineFor(sheet.product, choice) : line)),\n )\n }}\n {...(itemSheet?.editIndex !== undefined\n ? {\n onRemove: () => {\n const at = itemSheet.editIndex\n setItemSheet(null)\n setLines((prev) => prev.filter((_line, index) => index !== at))\n },\n }\n : {})}\n />\n <PosOfflineCheckout\n open={offlineCheckout}\n offline={offline}\n hostId={hostId}\n lines={lines}\n discountPct={discountPct}\n customer={customer}\n {...(registerName ? { registerName } : {})}\n {...(cashier.cashier ? { cashierName: cashier.cashier.name } : {})}\n onClose={() => setOfflineCheckout(false)}\n onRung={() => undefined}\n onDone={() => {\n setOfflineCheckout(false)\n setLines([])\n setDiscountPct(0)\n setCustomer(null)\n setSheetOpen(false)\n }}\n />\n <Dialog open={Boolean(pairing)} onClose={() => setPairing(null)} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{pairing?.mode === 'kiosk' ? 'Pair a self-service kiosk' : 'Pair a customer display'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <ToggleButtonGroup\n exclusive\n fullWidth\n size=\"small\"\n value={pairing?.mode ?? 'display'}\n onChange={(_event, mode: CommerceModel.PosDeviceMode | null) => {\n if (mode && mode !== pairing?.mode) void showPairingCode(mode)\n }}\n sx={{ mt: 1 }}\n >\n <ToggleButton value=\"display\">{'Customer display'}</ToggleButton>\n <ToggleButton value=\"kiosk\">{'Self-service kiosk'}</ToggleButton>\n </ToggleButtonGroup>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {(pairing?.mode === 'kiosk'\n ? 'On the tablet customers order from, open this address and enter the code. '\n : 'On the tablet facing your customer, open this address and enter the code. ') +\n 'The code works once and expires in 10 minutes.'}\n </Typography>\n <Typography variant=\"body1\" sx={{ wordBreak: 'break-all' }}>\n {`${typeof window === 'undefined' ? '' : window.location.origin}/kiosk/commerce/${\n pairing?.mode === 'kiosk' ? 'pos-kiosk' : 'pos-display'\n }`}\n </Typography>\n <Typography variant=\"h3\" component=\"p\" sx={{ textAlign: 'center', letterSpacing: 8 }}>\n {pairing?.code}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setPairing(null)}>{'Done'}</Button>\n </DialogActions>\n </Dialog>\n </>\n )\n}\nPosConsolePage.displayName = 'PosConsolePage'\n\nexport default PosConsolePage\n"],"names":["Aglyn","CommerceModel","PRODUCT_LIST_BASE","PRODUCT_LIST_QUERY","NextPageTitle","useSnackbar","planListQuery","useViewportFill","nameSearchNormalizers","Badge","Box","Button","Dialog","DialogActions","DialogContent","DialogTitle","Drawer","MenuItem","Stack","TextField","ToggleButton","ToggleButtonGroup","Typography","useMediaQuery","useTheme","collection","getDocs","limit","query","where","memo","useCallback","useEffect","useMemo","useRef","useState","useFirestore","useUser","useFirestoreCollection","listQueryConstraints","useOrgPlan","newAttemptKey","openPosSale","posRegisterContext","posTender","PosRequestError","usd","PosCartPanel","ScanAdornment","useScannerWedge","PosCustomerLookup","PosLastReceipt","PosOperationsBar","usePosCashier","usePosOpsSettings","POS_QUICK_KEYS","POS_TOUCH_PX","PosProductGrid","PosItemDialog","posItemNeedsChoice","PosReceiptPanel","PosTenderPanel","PosKioskQueue","posDisplayTipCents","usePosDisplay","useConsoleWidgetSlot","POS_ORDERS_ZONE","PosOfflineBanner","PosOfflineCheckout","usePosOffline","SELLABLE","field","op","value","POS_GRID_CEILING","posProductPlan","options","typed","search","trim","plan","POS_GRID_QUERY","base","clauses","code","categoryId","quickKeys","filters","path","fields","column","kind","tokensPath","operators","POS_CATEGORY_KEY","registerLineFor","product","choice","variant","modifiers","quantity","resolved","resolveLineModifiers","picked","ok","label","lineLabelWithModifiers","Object","keys","length","values","join","undefined","productId","$id","id","variantId","name","variantLabel","unitAmountCents","Math","round","Number","priceUsd","extraCents","lineKey","line","modifierSelectionKey","PosOrdersZone","props","renderer","Renderer","hostId","registerId","slot","orgId","PosConsolePage","liveRegister","usableRegisters","offline","WidgetSlot","firestore","data","user","enqueueSnackbar","theme","wide","breakpoints","up","setSearch","setCategoryIdState","window","localStorage","getItem","setCategoryId","setItem","gridPlan","productDocs","idField","categoryDocs","locationDocs","registerDocs","org","ready","planReady","registers","sort","a","b","String","localeCompare","registerCap","checkHostRegisterQuota","withinCap","registersWithinCap","filter","r","has","reservationDocs","openStays","lines","setLines","discountPct","setDiscountPct","customer","setCustomer","customerEmail","email","locationId","setLocationId","setRegisterId","first","registerName","find","register","opsSettings","cashier","autoLockMinutes","lastReceipt","setLastReceipt","context","setContext","userRef","current","uid","signedIn","active","then","answer","settings","terminal","catch","sale","setSale","saleLines","setSaleLines","opening","setOpening","sheetOpen","setSheetOpen","pairing","setPairing","display","attemptKey","fill","min","products","map","liftLegacyProduct","loadCatalog","docs","snapshot","openShiftId","assertion","cashierAssertion","liveProducts","offlineCheckout","setOfflineCheckout","strandedSales","orderIds","splice","orderId","sellProducts","gridProducts","productsById","Map","shortfalls","get","stockShortfall","variants","itemsCents","reduce","sum","discountCents","estimateCents","units","notify","message","persist","allowDuplicate","showPairingCode","mode","pairingCode","error","addProduct","next","prev","existing","itemSheet","setItemSheet","productCache","tapProduct","set","productModifierGroups","initial","basketCounts","counts","lookupCode","scanned","needle","toLowerCase","found","findByCode","lookup","hit","console","reportNetworkFailure","item","barcode","sku","handleSearchEnter","scanAdornment","onScan","chargeInFlight","charge","unavailableReason","opened","status","totalCents","totals","paidCents","dueCents","tenderableCents","tipCents","payments","resetSale","mirrored","displayShowsBasket","pendingTipCents","setPendingTipCents","connected","asking","some","payment","method","timer","setTimeout","show","cart","amountCents","taxCents","max","clearTimeout","registerPicker","color","onChange","event","target","size","select","disabled","Boolean","bottomBarRef","node","root","document","documentElement","property","publish","style","setProperty","getBoundingClientRect","height","observer","ResizeObserver","observe","disconnect","removeProperty","registerPanel","spacing","sx","minHeight","flex","direction","alignItems","onClick","location","onExchange","returned","phone","onQuantity","index","_line","at","onRemove","onEdit","editIndex","onDiscountPct","locked","onNewSale","cashierName","stays","onSale","onVoided","onTipChange","onSellOffline","push","justifyContent","totalsFor","screen","ref","overflow","p","overflowY","pb","mb","onTake","nextLines","onSearch","onSearchEnter","categories","category","onCategory","notices","onTap","width","md","lg","borderLeft","borderColor","flexDirection","position","left","right","bottom","bgcolor","borderTop","gap","zIndex","appBar","badgeContent","textAlign","anchor","open","onClose","slotProps","paper","maxHeight","borderTopLeftRadius","borderTopRightRadius","onConfirm","sheet","onRung","onDone","maxWidth","fullWidth","exclusive","_event","mt","wordBreak","origin","component","letterSpacing","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos-page.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 type { ConsolePluginPageProps } from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport {\n PRODUCT_LIST_BASE,\n PRODUCT_LIST_QUERY,\n} from '../../constants/product-list-query'\nimport { NextPageTitle } from '@aglyn/shared-ui-next/contexts/next-page-title-provider'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport type { ListFilterRequest } from '@aglyn/shared-ui-jsx/const/list-filter'\nimport { planListQuery } from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport { useViewportFill } from '@aglyn/shared-ui-jsx/hooks/use-viewport-fill'\nimport { nameSearchNormalizers } from '@aglyn/aglyn/app-utils/name-search'\nimport {\n Badge,\n Box,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Drawer,\n MenuItem,\n Stack,\n TextField,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n useMediaQuery,\n useTheme,\n} from '@mui/material'\nimport {\n collection,\n getDocs,\n limit,\n query,\n where,\n} from 'firebase/firestore'\nimport { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport { useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport { listQueryConstraints } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { useOrgPlan } from '@aglyn/tenant-feature-instance'\nimport {\n newAttemptKey,\n openPosSale,\n posRegisterContext,\n posTender,\n PosRequestError,\n usd,\n type PosRegisterContext,\n type PosSaleSummary,\n} from './pos/pos-api'\nimport { PosCartPanel, type RegisterLine } from './pos/pos-cart-panel.component'\nimport { ScanAdornment } from '../../barcode/scan-button.component'\nimport { useScannerWedge } from '../../barcode/scanner-wedge'\nimport {\n PosCustomerLookup,\n PosLastReceipt,\n PosOperationsBar,\n usePosCashier,\n usePosOpsSettings,\n type PosSelectedCustomer,\n} from './pos-ops/register-ops'\nimport { POS_QUICK_KEYS, POS_TOUCH_PX, PosProductGrid } from './pos/pos-product-grid.component'\nimport { PosItemDialog, posItemNeedsChoice, type PosItemChoice } from './pos/pos-item-dialog.component'\nimport { PosReceiptPanel } from './pos/pos-receipt-panel.component'\nimport { PosTenderPanel } from './pos/pos-tender-panel.component'\nimport { PosKioskQueue } from './pos/pos-kiosk-queue.component'\nimport { posDisplayTipCents, usePosDisplay } from './pos/use-pos-display'\nimport {\n type ConsoleWidgetSlotRenderer,\n useConsoleWidgetSlot,\n} from '@aglyn/aglyn/app-utils/console-widget-slot-context'\nimport { POS_ORDERS_ZONE } from './pos-zones'\nimport { PosOfflineBanner, PosOfflineCheckout, usePosOffline } from './pos-offline/register-offline'\n\n/** The till sells active products only; every read of the catalog asks it. */\nconst SELLABLE: ListFilterRequest = { field: 'status', op: 'equals', value: 'active' }\n\n/**\n * How many products the till's grid shows. The grid is a picker, not the\n * catalog: typing narrows it on the query, so a name reaches every product.\n */\nconst POS_GRID_CEILING = 500\n\n/**\n * The products hub's query (AGL-3321), narrowed to what the till may sell:\n * live products (`deletedAt == null`, the list's scope), `status == active`,\n * and any typed word as the search on `nameTokens` — or, for a scan, the\n * whole scanned code as a SKU or barcode clause. Every shape is one the hub's\n * composites already serve.\n */\nexport function posProductPlan(options: {\n search?: string\n code?: { field: 'barcodes' | 'skus'; value: string }\n /** A category chip (AGL-3607): served by the storefront's own composite. */\n categoryId?: string\n /** The Quick keys chip (AGL-3607): products marked `posQuickKey`. */\n quickKeys?: boolean\n}) {\n const typed = options.search?.trim()\n const plan = planListQuery(\n POS_GRID_QUERY,\n {\n base: PRODUCT_LIST_BASE,\n clauses: [\n SELLABLE,\n ...(options.code\n ? [{ field: options.code.field, op: 'contains', value: options.code.value }]\n : []),\n ...(options.categoryId\n ? [{ field: 'categoryIds', op: 'contains', value: options.categoryId }]\n : []),\n ],\n // A chip and a typed word are both array clauses, and Firestore takes\n // one: with a chip on, the word narrows by name prefix instead.\n search: typed && !options.categoryId ? [typed] : [],\n },\n nameSearchNormalizers,\n )\n // The Quick keys chip (AGL-3607) is a boolean equality the list planner\n // does not model, added beside the scope and status it composes with —\n // `deletedAt ==, status ==, posQuickKey ==, nameLower ASC` is declared in\n // the index file. A typed word searches the whole catalog instead: a\n // cashier who types is looking past the quick keys.\n if (options.quickKeys && !typed && !options.code) {\n return {\n ...plan,\n filters: [...plan.filters, { path: 'posQuickKey', op: '==', value: true }],\n } as typeof plan\n }\n return plan\n}\n\n/**\n * The grid's query: the products hub's, plus the category a chip picks\n * (AGL-3607). `categoryIds CONTAINS, nameLower ASC` is a composite the\n * storefront catalog already declares.\n */\nexport const POS_GRID_QUERY = {\n ...PRODUCT_LIST_QUERY,\n fields: [\n ...PRODUCT_LIST_QUERY.fields,\n {\n column: 'categoryIds',\n kind: 'exact' as const,\n path: 'categoryIds',\n tokensPath: 'categoryIds',\n operators: ['contains'],\n },\n ],\n} as typeof PRODUCT_LIST_QUERY\n\n/**\n * POS register (AGL-312, AGL-3607): a tablet-first sale surface — the\n * product grid with search, scan and category chips, the basket, and a\n * tender panel that takes one or more payments against an open sale (cash,\n * card reader, a native Tap to Pay bridge, a typed card, the QR link, gift\n * cards and room charges) until the balance is zero. A paired customer\n * display mirrors the basket and takes the tip and the receipt (AGL-3608).\n *\n * On a wide screen (900px and up) the grid and the register sit side by\n * side; below that the register is a bottom sheet behind a sticky total bar.\n */\n/*\n * `managePos` is NOT read here. The nav item in `plugin.ts` declares it and\n * the shell refuses the route before this component is constructed, so a\n * reader without the key never reaches this file and there is no unpermitted\n * state for it to render — the same arrangement the entitlement gate has.\n *\n * Re-adding a check off the `permissions` prop would be a second answer to a\n * settled question, and a laxer one: a prop absent because the map has not\n * landed reads as permitted here, while the shell holds the route on that\n * same condition rather than guessing. The server routes remain the\n * enforcement point for every sale and every payment.\n */\n/** Where a register device remembers the category chip it was left on. */\nconst POS_CATEGORY_KEY = 'aglyn.pos.category'\n\n/** The basket line an item-sheet choice makes: label and estimate from the product. */\nexport function registerLineFor(\n product: any,\n choice: PosItemChoice,\n): RegisterLine {\n const { variant, modifiers, quantity } = choice\n const resolved = CommerceModel.resolveLineModifiers(product, modifiers)\n const picked = resolved.ok ? resolved.modifiers : []\n const label = CommerceModel.lineLabelWithModifiers(\n Object.keys(variant.options ?? {}).length\n ? Object.values(variant.options ?? {}).join(' / ')\n : undefined,\n picked,\n )\n return {\n productId: product.$id,\n ...(variant.id !== 'default' ? { variantId: variant.id } : {}),\n name: product.name,\n ...(label ? { variantLabel: label } : {}),\n unitAmountCents:\n Math.round(Number(variant.priceUsd) * 100) + (resolved.ok ? resolved.extraCents : 0),\n quantity,\n ...(modifiers.length ? { modifiers } : {}),\n }\n}\n\n/** Same product, variant and choices: the lines that merge into one. */\nfunction lineKey(line: Pick<RegisterLine, 'productId' | 'variantId' | 'modifiers'>): string {\n return `${line.productId}:${line.variantId ?? ''}:${CommerceModel.modifierSelectionKey(line.modifiers)}`\n}\n\n/**\n * The register's zone (AGL-3644), drawn again only when what it is handed\n * changes: the till redraws on every tap, and the shell's renderer is not\n * memoized (the order dialog's reasoning).\n */\nconst PosOrdersZone = memo(function PosOrdersZone(props: {\n renderer: ConsoleWidgetSlotRenderer\n hostId: string\n registerId: string | null\n}) {\n const { renderer: Renderer, hostId, registerId } = props\n return <Renderer slot={POS_ORDERS_ZONE.id} hostId={hostId} orgId={undefined} registerId={registerId} />\n})\n\nexport function PosConsolePage({ hostId }: ConsolePluginPageProps) {\n const WidgetSlot = useConsoleWidgetSlot()\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const theme = useTheme()\n const wide = useMediaQuery(theme.breakpoints.up('md'))\n\n const [search, setSearch] = useState('')\n // The chip a register opens on is remembered per device: a counter that\n // rings from its quick keys reopens on them.\n const [categoryId, setCategoryIdState] = useState(() => {\n try {\n return window.localStorage.getItem(POS_CATEGORY_KEY) ?? ''\n } catch {\n return ''\n }\n })\n const setCategoryId = useCallback((id: string) => {\n setCategoryIdState(id)\n try {\n window.localStorage.setItem(POS_CATEGORY_KEY, id)\n } catch {\n // Storage blocked: the chip still works for this visit.\n }\n }, [])\n const gridPlan = useMemo(\n () =>\n categoryId === POS_QUICK_KEYS\n ? posProductPlan({ search, quickKeys: true })\n : posProductPlan({ search, categoryId }),\n [search, categoryId],\n )\n const { data: productDocs } = useFirestoreCollection<any>(\n /*\n * The till's grid, narrowed by the QUERY rather than by the rows it\n * happened to fetch (AGL-2501, AGL-2292, AGL-3321): the first five\n * hundred SELLABLE products by name, and a typed name reaches the whole\n * catalog. The scan does not come through here — see `handleSearchEnter`.\n */\n () =>\n query(\n collection(firestore, 'hosts', hostId, 'products'),\n ...listQueryConstraints(gridPlan),\n limit(POS_GRID_CEILING),\n ),\n [firestore, hostId, gridPlan],\n { idField: '$id' },\n )\n const { data: categoryDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'productCategories'), limit(30)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { data: locationDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'locations'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { data: registerDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'registers'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { org, ready: planReady } = useOrgPlan(hostId)\n const registers = [...(registerDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n // Only registers within the plan cap can transact (AGL-482), and not until\n // the org doc has arrived (AGL-1064). Per SITE (AGL-1775).\n const registerCap = Aglyn.checkHostRegisterQuota(org, hostId, registers.length).limit\n const withinCap = CommerceModel.registersWithinCap(registers, registerCap)\n const usableRegisters = planReady ? registers.filter((r: any) => withinCap.has(r.$id)) : []\n /*\n * The stays a sale can be charged to, asked for BY STATUS (AGL-3321).\n */\n const { data: reservationDocs } = useFirestoreCollection<any>(\n () =>\n query(\n collection(firestore, 'hosts', hostId, 'reservations'),\n where('status', '==', 'checked_in'),\n limit(100),\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const openStays = reservationDocs ?? []\n\n const [lines, setLines] = useState<RegisterLine[]>([])\n const [discountPct, setDiscountPct] = useState(0)\n // The customer the lookup attached (AGL-3609); their email is the sale's.\n const [customer, setCustomer] = useState<PosSelectedCustomer | null>(null)\n const customerEmail = customer?.email ?? ''\n const [locationId, setLocationId] = useState('')\n const [registerId, setRegisterId] = useState('')\n useEffect(() => {\n if (registerId || usableRegisters.length === 0) return\n const first = usableRegisters[0]\n setRegisterId(first.$id)\n if (first.locationId) setLocationId(first.locationId)\n }, [usableRegisters, registerId])\n const registerName = String(\n usableRegisters.find((register: any) => register.$id === registerId)?.name ?? '',\n )\n // Who is ringing (AGL-3609): a PIN-switched cashier, and the idle lock.\n const opsSettings = usePosOpsSettings(hostId)\n const cashier = usePosCashier({\n hostId,\n registerId,\n autoLockMinutes: opsSettings.autoLockMinutes,\n })\n // The last completed sale, whose receipt can still be printed (AGL-3609).\n const [lastReceipt, setLastReceipt] = useState<{ orderId: string } | null>(null)\n\n const [context, setContext] = useState<PosRegisterContext | null>(null)\n // Keyed on the uid, not the user object: a session refresh hands back a\n // new object for the same person, and re-reading the context on every one\n // would re-render the register in a loop.\n const userRef = useRef(user)\n userRef.current = user\n const uid = user?.uid ?? ''\n useEffect(() => {\n const signedIn = userRef.current\n if (!signedIn) return\n let active = true\n posRegisterContext(signedIn, hostId)\n .then((answer) => {\n // Only a whole answer: a register that half-knows its tenders would\n // offer one the server then refuses.\n if (active && answer?.settings && answer?.terminal) setContext(answer)\n })\n .catch(() => undefined)\n return () => {\n active = false\n }\n }, [uid, hostId])\n\n /** The open sale, once the basket has been priced and payment started. */\n const [sale, setSale] = useState<PosSaleSummary | null>(null)\n const [saleLines, setSaleLines] = useState<RegisterLine[]>([])\n const [opening, setOpening] = useState(false)\n const [sheetOpen, setSheetOpen] = useState(false)\n const [pairing, setPairing] = useState<{\n code: string\n expiresAtMs: number\n /** A customer display, or a self-service kiosk (AGL-3623). */\n mode: CommerceModel.PosDeviceMode\n } | null>(null)\n const display = usePosDisplay(user, hostId, registerId)\n\n /**\n * One key per basket (AGL-1691): minted on the first Charge and retired\n * whenever the basket changes, so a retried Charge finds the same sale and\n * ringing the same coffee twice is two sales.\n */\n const attemptKey = useRef('')\n const fill = useViewportFill({ min: 480 })\n useEffect(() => {\n attemptKey.current = ''\n }, [lines, discountPct])\n\n const products = useMemo(\n () =>\n [...(productDocs ?? [])].map((product: any) => ({\n ...CommerceModel.liftLegacyProduct(product),\n $id: product.$id,\n })),\n [productDocs],\n )\n /*\n * THE OFFLINE REGISTER (AGL-3625): the catalog and the site's offline rules\n * kept on this device, the queue of cash sales rung without a connection,\n * and the sync that records them once it returns.\n */\n const loadCatalog = useCallback(\n async () =>\n (\n await getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'products'),\n ...listQueryConstraints(posProductPlan({})),\n limit(POS_GRID_CEILING),\n ),\n )\n ).docs.map((snapshot) => ({ ...(snapshot.data() as Record<string, unknown>), $id: snapshot.id })),\n [firestore, hostId],\n )\n const liveRegister = usableRegisters.find((register: any) => register.$id === registerId)\n const offline = usePosOffline({\n hostId,\n user,\n registerId,\n openShiftId: liveRegister ? (liveRegister.openShiftId ?? null) : undefined,\n locationId,\n ...(cashier.assertion ? { cashierAssertion: cashier.assertion } : {}),\n liveProducts: products,\n loadCatalog,\n })\n const [offlineCheckout, setOfflineCheckout] = useState(false)\n /** Open sales left unpaid when the connection dropped, voided once it returns. */\n const strandedSales = useRef<string[]>([])\n useEffect(() => {\n if (offline.offline || !user || !strandedSales.current.length) return\n const orderIds = strandedSales.current.splice(0)\n for (const orderId of orderIds) {\n void posTender(user, hostId, orderId, 'void').catch(() => undefined)\n }\n }, [offline.offline, user, hostId])\n const sellProducts = useMemo(\n () => (offline.offline && offline.ready ? offline.gridProducts(search, categoryId, POS_QUICK_KEYS) : products),\n [offline, search, categoryId, products],\n )\n const productsById = useMemo(\n () => new Map<string, any>(sellProducts.map((product: any) => [product.$id, product] as const)),\n [sellProducts],\n )\n /** AGL-2357: warns, never blocks. */\n const shortfalls = useMemo(\n () =>\n lines.map((line) => {\n const product: any = productsById.get(line.productId)\n if (!product) return null\n return CommerceModel.stockShortfall(\n product,\n line.variantId ?? product.variants?.[0]?.id,\n line.quantity,\n )\n }),\n [lines, productsById],\n )\n\n const itemsCents = lines.reduce((sum, line) => sum + line.unitAmountCents * line.quantity, 0)\n const discountCents = Math.round((itemsCents * discountPct) / 100)\n const estimateCents = itemsCents - discountCents\n const units = lines.reduce((sum, line) => sum + line.quantity, 0)\n\n const notify = useCallback(\n (message: string, variant: 'success' | 'error' | 'warning' | 'info') => {\n enqueueSnackbar(message, { variant, persist: false, allowDuplicate: true })\n },\n [enqueueSnackbar],\n )\n\n /** A pairing code for a customer display or a self-service kiosk (AGL-3623). */\n const showPairingCode = useCallback(\n async (mode: CommerceModel.PosDeviceMode) => {\n try {\n setPairing({ ...(await display.pairingCode(mode)), mode })\n } catch (error) {\n notify(error instanceof PosRequestError ? error.message : 'Could not make a code', 'error')\n }\n },\n [display, notify],\n )\n\n /**\n * Adds a line, or more of an identical one: the same product, variant and\n * modifiers merge, so two oat lattes are one line of two and an oat latte\n * beside a whole-milk one stays two lines.\n */\n const addProduct = useCallback(\n (product: any, variant?: any, modifiers: CommerceModel.ModifierSelection[] = [], quantity = 1) => {\n const next = registerLineFor(product, {\n variant: variant ?? product.variants[0],\n modifiers,\n quantity,\n })\n setLines((prev) => {\n const existing = prev.find((line) => lineKey(line) === lineKey(next))\n if (existing) {\n return prev.map((line) =>\n line === existing\n ? { ...line, quantity: Math.min(99, line.quantity + next.quantity) }\n : line,\n )\n }\n return [...prev, next]\n })\n },\n [],\n )\n\n /** The item sheet: a product to add, or a basket line to change. */\n const [itemSheet, setItemSheet] = useState<{\n product: any\n editIndex?: number\n initial?: { variantId?: string; modifiers?: CommerceModel.ModifierSelection[]; quantity: number }\n } | null>(null)\n // Every product rung up this sale, so a line can be reopened after the\n // grid has moved on to another category or search.\n const productCache = useRef(new Map<string, any>())\n const tapProduct = useCallback(\n (product: any, variant?: any) => {\n productCache.current.set(product.$id, product)\n if (posItemNeedsChoice(product) && !(variant && CommerceModel.productModifierGroups(product).length === 0)) {\n setItemSheet({\n product,\n ...(variant ? { initial: { variantId: variant.id, quantity: 1 } } : {}),\n })\n return\n }\n addProduct(product, variant)\n },\n [addProduct],\n )\n const basketCounts = useMemo(() => {\n const counts = new Map<string, number>()\n for (const line of lines) counts.set(line.productId, (counts.get(line.productId) ?? 0) + line.quantity)\n return counts\n }, [lines])\n\n /**\n * The barcode wedge: a LOOKUP against the whole catalog (AGL-2501),\n * barcode first, and a miss says so.\n */\n const lookupCode = useCallback(async (scanned: string) => {\n const needle = scanned.trim().toLowerCase()\n if (!needle || sale) return\n if (offline.offline) {\n const found = offline.findByCode(needle)\n if (!found) return void notify(`No product matches “${scanned.trim()}”`, 'warning')\n tapProduct(found.product, found.variant)\n setSearch('')\n return\n }\n const lookup = async (field: 'barcodes' | 'skus') => {\n const found = await getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'products'),\n ...listQueryConstraints(posProductPlan({ code: { field, value: needle } })),\n limit(1),\n ),\n )\n return found.docs[0]\n }\n let hit: Awaited<ReturnType<typeof lookup>>\n try {\n hit = (await lookup('barcodes')) ?? (await lookup('skus'))\n } catch (error) {\n console.error(error)\n offline.reportNetworkFailure()\n return void notify('Could not reach the catalog — try again', 'warning')\n }\n if (!hit) return void notify(`No product matches “${scanned.trim()}”`, 'warning')\n const product = { ...CommerceModel.liftLegacyProduct(hit.data() as any), $id: hit.id }\n const variant =\n product.variants.find(\n (item: any) =>\n item.barcode?.trim().toLowerCase() === needle || item.sku?.trim().toLowerCase() === needle,\n ) ?? product.variants[0]\n // A scanned variant is the variant; a product with modifiers still asks\n // for them, with that variant already picked.\n tapProduct(product, variant)\n setSearch('')\n }, [sale, firestore, hostId, tapProduct, notify, offline])\n const handleSearchEnter = useCallback(() => lookupCode(search), [lookupCode, search])\n // A scanner fired while focus is on a product or a button (AGL-3619), and\n // the camera; neither while a sale is taking payment or an item is open.\n useScannerWedge((code) => void lookupCode(code), !sale && !itemSheet)\n const scanAdornment = useMemo(\n () => <ScanAdornment label=\"Scan a barcode with the camera\" onScan={(code) => void lookupCode(code)} />,\n [lookupCode],\n )\n\n /** Prices the basket on the server and opens the sale for payment. */\n // A ref, not `opening`: two taps inside one React batch both read the\n // pre-click state, and only a ref is written before the second reads it\n // (the AGL-1682 lesson).\n const chargeInFlight = useRef(false)\n const charge = useCallback(async () => {\n if (chargeInFlight.current || lines.length === 0 || !user) return\n if (!registerId) return void notify('Select a register before taking payment', 'warning')\n // Offline, the basket rings as a cash sale kept on this register (AGL-3625).\n if (offline.offline) {\n if (!offline.ready) return void notify(offline.unavailableReason ?? 'Offline selling is unavailable', 'warning')\n setOfflineCheckout(true)\n return\n }\n if (!attemptKey.current) attemptKey.current = newAttemptKey()\n chargeInFlight.current = true\n setOpening(true)\n try {\n const opened = await openPosSale(user, attemptKey.current, {\n hostId,\n registerId,\n lines,\n discountPct,\n ...(customerEmail ? { customerEmail } : {}),\n ...(customer ? { customer } : {}),\n ...(cashier.assertion ? { cashierAssertion: cashier.assertion } : {}),\n ...(locationId ? { locationId } : {}),\n })\n setSaleLines(lines)\n setSale({\n orderId: opened.orderId,\n status: 'pending',\n totalCents: opened.totals.totalCents,\n paidCents: 0,\n dueCents: opened.totals.totalCents,\n tenderableCents: opened.totals.totalCents,\n tipCents: 0,\n payments: [],\n })\n setSheetOpen(true)\n } catch (error) {\n if (error instanceof PosRequestError) {\n notify(error.message, 'error')\n } else {\n // The request never reached the server: the register is offline.\n offline.reportNetworkFailure()\n notify('The register lost its connection. Charge again to take cash offline.', 'warning')\n }\n } finally {\n chargeInFlight.current = false\n setOpening(false)\n }\n }, [lines, user, registerId, hostId, discountPct, customerEmail, customer, cashier.assertion, locationId, notify, offline])\n\n const resetSale = useCallback(() => {\n if (sale?.status === 'paid') setLastReceipt({ orderId: sale.orderId })\n setSale(null)\n setSaleLines([])\n setLines([])\n setDiscountPct(0)\n setCustomer(null)\n setSheetOpen(false)\n }, [sale])\n\n // The customer display mirrors the basket while nothing else is on it.\n const mirrored = sale ? saleLines : lines\n // Whether the display was last left showing a basket. A paid sale leaves it\n // on the thank-you the server wrote, which an empty new basket must not\n // cut short with the idle screen; the next item added takes it over.\n // True at first, so a register that opens empty clears whatever a previous\n // session left on the screen.\n const displayShowsBasket = useRef(true)\n // The tip picked for the payment about to be taken; the ledger has it only\n // once that payment is recorded. Gone with the sale.\n const [pendingTipCents, setPendingTipCents] = useState(0)\n useEffect(() => {\n if (!sale || sale.status === 'paid') setPendingTipCents(0)\n }, [sale])\n useEffect(() => {\n if (sale?.status === 'paid') displayShowsBasket.current = false\n }, [sale?.status])\n useEffect(() => {\n if (!display.connected || display.asking) return\n if (sale?.payments.some((payment) => payment.status === 'pending' && payment.method === 'card_present')) {\n return\n }\n if (sale?.status === 'paid') return\n if (!mirrored.length && !displayShowsBasket.current) return\n const timer = setTimeout(() => {\n displayShowsBasket.current = mirrored.length > 0\n void display.show(\n mirrored.length\n ? {\n mode: 'cart',\n cart: {\n lines: mirrored.map((line) => ({\n name: line.name,\n ...(line.variantLabel ? { variantLabel: line.variantLabel } : {}),\n quantity: line.quantity,\n amountCents: line.unitAmountCents * line.quantity,\n })),\n itemsCents,\n discountCents,\n taxCents: sale ? Math.max(0, sale.totalCents - estimateCents) : 0,\n totalCents: sale ? sale.totalCents : estimateCents,\n ...(sale\n ? {\n paidCents: sale.paidCents,\n dueCents: sale.dueCents,\n tipCents: posDisplayTipCents(sale, pendingTipCents),\n }\n : {}),\n },\n }\n : { mode: 'idle' },\n )\n }, 400)\n return () => clearTimeout(timer)\n }, [display, mirrored, sale, itemsCents, discountCents, estimateCents, pendingTipCents])\n\n const registerPicker = !planReady ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Checking your plan…'}\n </Typography>\n ) : usableRegisters.length === 0 ? (\n <Typography variant=\"body2\" color=\"warning.main\">\n {registers.length > 0\n ? 'Your registers exceed your plan — remove extras or upgrade in Billing to take payments.'\n : 'No POS register yet. Add one under Commerce → Settings → POS registers before taking payments.'}\n </Typography>\n ) : usableRegisters.length > 1 ? (\n <TextField\n label=\"Register\"\n value={registerId}\n onChange={(event) => setRegisterId(event.target.value)}\n size=\"small\"\n select\n disabled={Boolean(sale)}\n >\n {usableRegisters.map((register: any) => (\n <MenuItem key={register.$id} value={register.$id}>\n {register.name}\n </MenuItem>\n ))}\n </TextField>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {usableRegisters[0]?.name}\n </Typography>\n )\n\n /**\n * On a narrow screen the register's bar owns the bottom edge, so it moves\n * the console's floating launchers clear of itself through the shell's\n * `--aglyn-dock-inset-bottom` seam; a launcher left in the corner sat on\n * top of Charge.\n */\n const bottomBarRef = useRef<HTMLDivElement | null>(null)\n useEffect(() => {\n const node = bottomBarRef.current\n if (wide || !node) return undefined\n const root = document.documentElement\n const property = '--aglyn-dock-inset-bottom'\n const publish = () => {\n root.style.setProperty(property, `${Math.round(node.getBoundingClientRect().height) + 20}px`)\n }\n publish()\n const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(publish)\n observer?.observe(node)\n return () => {\n observer?.disconnect()\n root.style.removeProperty(property)\n }\n }, [wide])\n\n const registerPanel = (\n <Stack spacing={1.5} sx={{ minHeight: 0, flex: 1 }}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"h6\" sx={{ flex: 1 }}>\n {'Register'}\n </Typography>\n {registerId ? (\n <Button\n size=\"small\"\n onClick={() => void showPairingCode('display')}\n >\n {display.connected ? 'Display connected' : 'Pair display'}\n </Button>\n ) : null}\n </Stack>\n {registerPicker}\n {(locationDocs?.length ?? 0) > 1 ? (\n <TextField\n label=\"Location\"\n value={locationId}\n onChange={(event) => setLocationId(event.target.value)}\n size=\"small\"\n select\n disabled={Boolean(sale)}\n >\n {(locationDocs ?? []).map((location: any) => (\n <MenuItem key={location.$id} value={location.$id}>\n {location.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n {registerId ? (\n <PosOperationsBar\n hostId={hostId}\n registerId={registerId}\n {...(registerName ? { registerName } : {})}\n cashier={cashier}\n onExchange={(returned) =>\n setCustomer(\n returned.email || returned.name\n ? { kind: 'none', id: '', name: returned.name ?? '', email: returned.email, phone: null }\n : null,\n )\n }\n />\n ) : null}\n <PosCartPanel\n lines={sale ? saleLines : lines}\n shortfalls={sale ? [] : shortfalls}\n onQuantity={(index, quantity) =>\n setLines((prev) =>\n quantity <= 0\n ? prev.filter((_line, at) => at !== index)\n : prev.map((line, at) => (at === index ? { ...line, quantity: Math.min(99, quantity) } : line)),\n )\n }\n onRemove={(index) => setLines((prev) => prev.filter((_line, at) => at !== index))}\n onEdit={(index) => {\n const line = lines[index]\n const product = line\n ? (productCache.current.get(line.productId) ?? productsById.get(line.productId))\n : undefined\n if (!line || !product) return\n setItemSheet({\n product,\n editIndex: index,\n initial: {\n ...(line.variantId ? { variantId: line.variantId } : {}),\n ...(line.modifiers ? { modifiers: line.modifiers } : {}),\n quantity: line.quantity,\n },\n })\n }}\n discountPct={discountPct}\n onDiscountPct={setDiscountPct}\n customer={\n sale ? null : <PosCustomerLookup hostId={hostId} value={customer} onChange={setCustomer} />\n }\n locked={Boolean(sale)}\n />\n {sale && user ? (\n sale.status === 'paid' ? (\n <PosReceiptPanel\n user={user}\n hostId={hostId}\n sale={sale}\n context={context}\n display={display}\n onNewSale={resetSale}\n registerId={registerId}\n {...(registerName ? { registerName } : {})}\n {...(cashier.cashier ? { cashierName: cashier.cashier.name } : {})}\n notify={notify}\n />\n ) : (\n <PosTenderPanel\n user={user}\n hostId={hostId}\n registerId={registerId}\n sale={sale}\n context={context}\n stays={openStays}\n display={display}\n onSale={setSale}\n onVoided={() => {\n setSale(null)\n setSaleLines([])\n }}\n onTipChange={setPendingTipCents}\n {...(cashier.assertion ? { cashierAssertion: cashier.assertion } : {})}\n notify={notify}\n offline={offline.offline}\n {...(offline.ready\n ? {\n onSellOffline: () => {\n // The server's open sale took no payment; it is voided once\n // the connection returns, and the basket rings offline.\n strandedSales.current.push(sale.orderId)\n setSale(null)\n setSaleLines([])\n setOfflineCheckout(true)\n },\n }\n : {})}\n />\n )\n ) : (\n <>\n <Box sx={{ display: 'flex', justifyContent: 'space-between' }}>\n <Typography variant=\"h6\">{'Due'}</Typography>\n <Typography variant=\"h6\">{usd(estimateCents)}</Typography>\n </Box>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Tax is added when you charge.'}\n </Typography>\n <Button\n variant=\"contained\"\n size=\"large\"\n disabled={opening || lines.length === 0 || !registerId}\n onClick={() => void charge()}\n sx={{ minHeight: 56 }}\n >\n {opening\n ? 'Pricing…'\n : offline.offline\n ? `Cash ${usd(offline.totalsFor(lines, discountPct)?.totalCents ?? estimateCents)} (offline)`\n : `Charge ${usd(estimateCents)}`}\n </Button>\n {lastReceipt ? (\n <PosLastReceipt\n hostId={hostId}\n orderId={lastReceipt.orderId}\n {...(registerName ? { registerName } : {})}\n {...(cashier.cashier ? { cashierName: cashier.cashier.name } : {})}\n />\n ) : null}\n </>\n )}\n </Stack>\n )\n\n return (\n <>\n <NextPageTitle screen={'POS'} />\n <Box\n ref={fill.ref}\n sx={{\n display: 'flex',\n // From under the console's header, nav and page title down to\n // where the page's padding and footer begin, so the whole page fits\n // the window (AGL-3679, AGL-3710).\n height: fill.height,\n overflow: 'hidden',\n }}\n >\n <Box sx={{ flex: 1, p: 2, overflowY: 'auto', pb: wide ? 2 : 12 }}>\n <Box sx={{ mb: 2, '&:empty': { display: 'none' } }}>\n <PosOfflineBanner offline={offline} />\n </Box>\n {/* \"Pay at counter\" orders from this register's kiosks (AGL-3623). */}\n {registerId ? (\n <PosKioskQueue\n user={user}\n hostId={hostId}\n registerId={registerId}\n disabled={Boolean(sale)}\n onTake={(next, nextLines) => {\n setSaleLines(nextLines)\n setSale(next)\n setSheetOpen(true)\n }}\n notify={notify}\n />\n ) : null}\n {/* Orders other channels send to the counter (AGL-3644). */}\n {WidgetSlot ? <PosOrdersZone renderer={WidgetSlot} hostId={hostId} registerId={registerId || null} /> : null}\n <PosProductGrid\n search={search}\n onSearch={setSearch}\n onSearchEnter={() => void handleSearchEnter()}\n scanAdornment={scanAdornment}\n categories={(categoryDocs ?? []).map((category: any) => ({\n $id: category.$id,\n name: String(category.name ?? 'Category'),\n }))}\n categoryId={categoryId}\n onCategory={setCategoryId}\n notices={gridPlan.notices}\n hostId={hostId}\n products={sellProducts}\n basketCounts={basketCounts}\n onTap={(product) => {\n if (sale) return void notify('Finish or void the open sale first', 'info')\n tapProduct(product)\n }}\n />\n </Box>\n {wide ? (\n <Box\n sx={{\n width: { md: 400, lg: 440 },\n borderLeft: 1,\n borderColor: 'divider',\n display: 'flex',\n flexDirection: 'column',\n p: 2,\n overflowY: 'auto',\n }}\n >\n {registerPanel}\n </Box>\n ) : null}\n </Box>\n {!wide ? (\n <>\n <Box\n ref={bottomBarRef}\n sx={{\n position: 'fixed',\n left: 0,\n right: 0,\n bottom: 0,\n p: 1.5,\n bgcolor: 'background.paper',\n borderTop: 1,\n borderColor: 'divider',\n display: 'flex',\n gap: 1,\n alignItems: 'center',\n zIndex: theme.zIndex.appBar,\n }}\n >\n <Badge badgeContent={units} color=\"primary\">\n <Button\n variant=\"outlined\"\n onClick={() => setSheetOpen(true)}\n sx={{ minHeight: POS_TOUCH_PX }}\n >\n {sale ? 'Payment' : 'Cart'}\n </Button>\n </Badge>\n <Typography variant=\"h6\" sx={{ flex: 1, textAlign: 'right' }}>\n {usd(sale ? sale.dueCents : estimateCents)}\n </Typography>\n {!sale ? (\n <Button\n variant=\"contained\"\n disabled={opening || lines.length === 0 || !registerId}\n onClick={() => void charge()}\n sx={{ minHeight: POS_TOUCH_PX }}\n >\n {'Charge'}\n </Button>\n ) : null}\n </Box>\n <Drawer\n anchor=\"bottom\"\n open={sheetOpen}\n onClose={() => setSheetOpen(false)}\n slotProps={{ paper: { sx: { maxHeight: '90dvh', p: 2, borderTopLeftRadius: 16, borderTopRightRadius: 16 } } }}\n >\n {registerPanel}\n </Drawer>\n </>\n ) : null}\n <PosItemDialog\n product={itemSheet?.product ?? null}\n initial={itemSheet?.initial}\n onClose={() => setItemSheet(null)}\n onConfirm={(choice) => {\n const sheet = itemSheet\n setItemSheet(null)\n if (!sheet) return\n if (sheet.editIndex === undefined) {\n addProduct(sheet.product, choice.variant, choice.modifiers, choice.quantity)\n return\n }\n const at = sheet.editIndex\n setLines((prev) =>\n prev.map((line, index) => (index === at ? registerLineFor(sheet.product, choice) : line)),\n )\n }}\n {...(itemSheet?.editIndex !== undefined\n ? {\n onRemove: () => {\n const at = itemSheet.editIndex\n setItemSheet(null)\n setLines((prev) => prev.filter((_line, index) => index !== at))\n },\n }\n : {})}\n />\n <PosOfflineCheckout\n open={offlineCheckout}\n offline={offline}\n hostId={hostId}\n lines={lines}\n discountPct={discountPct}\n customer={customer}\n {...(registerName ? { registerName } : {})}\n {...(cashier.cashier ? { cashierName: cashier.cashier.name } : {})}\n onClose={() => setOfflineCheckout(false)}\n onRung={() => undefined}\n onDone={() => {\n setOfflineCheckout(false)\n setLines([])\n setDiscountPct(0)\n setCustomer(null)\n setSheetOpen(false)\n }}\n />\n <Dialog open={Boolean(pairing)} onClose={() => setPairing(null)} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{pairing?.mode === 'kiosk' ? 'Pair a self-service kiosk' : 'Pair a customer display'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <ToggleButtonGroup\n exclusive\n fullWidth\n size=\"small\"\n value={pairing?.mode ?? 'display'}\n onChange={(_event, mode: CommerceModel.PosDeviceMode | null) => {\n if (mode && mode !== pairing?.mode) void showPairingCode(mode)\n }}\n sx={{ mt: 1 }}\n >\n <ToggleButton value=\"display\">{'Customer display'}</ToggleButton>\n <ToggleButton value=\"kiosk\">{'Self-service kiosk'}</ToggleButton>\n </ToggleButtonGroup>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {(pairing?.mode === 'kiosk'\n ? 'On the tablet customers order from, open this address and enter the code. '\n : 'On the tablet facing your customer, open this address and enter the code. ') +\n 'The code works once and expires in 10 minutes.'}\n </Typography>\n <Typography variant=\"body1\" sx={{ wordBreak: 'break-all' }}>\n {`${typeof window === 'undefined' ? '' : window.location.origin}/kiosk/commerce/${\n pairing?.mode === 'kiosk' ? 'pos-kiosk' : 'pos-display'\n }`}\n </Typography>\n <Typography variant=\"h3\" component=\"p\" sx={{ textAlign: 'center', letterSpacing: 8 }}>\n {pairing?.code}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setPairing(null)}>{'Done'}</Button>\n </DialogActions>\n </Dialog>\n </>\n )\n}\nPosConsolePage.displayName = 'PosConsolePage'\n\nexport default PosConsolePage\n"],"names":["Aglyn","CommerceModel","PRODUCT_LIST_BASE","PRODUCT_LIST_QUERY","NextPageTitle","useSnackbar","planListQuery","useViewportFill","nameSearchNormalizers","Badge","Box","Button","Dialog","DialogActions","DialogContent","DialogTitle","Drawer","MenuItem","Stack","TextField","ToggleButton","ToggleButtonGroup","Typography","useMediaQuery","useTheme","collection","getDocs","limit","query","where","memo","useCallback","useEffect","useMemo","useRef","useState","useFirestore","useUser","useFirestoreCollection","listQueryConstraints","useOrgPlan","newAttemptKey","openPosSale","posRegisterContext","posTender","PosRequestError","usd","PosCartPanel","ScanAdornment","useScannerWedge","PosCustomerLookup","PosLastReceipt","PosOperationsBar","usePosCashier","usePosOpsSettings","POS_QUICK_KEYS","POS_TOUCH_PX","PosProductGrid","PosItemDialog","posItemNeedsChoice","PosReceiptPanel","PosTenderPanel","PosKioskQueue","posDisplayTipCents","usePosDisplay","useConsoleWidgetSlot","POS_ORDERS_ZONE","PosOfflineBanner","PosOfflineCheckout","usePosOffline","SELLABLE","field","op","value","POS_GRID_CEILING","posProductPlan","options","typed","search","trim","plan","POS_GRID_QUERY","base","clauses","code","categoryId","quickKeys","filters","path","fields","column","kind","tokensPath","operators","POS_CATEGORY_KEY","registerLineFor","product","choice","variant","modifiers","quantity","resolved","resolveLineModifiers","picked","ok","label","lineLabelWithModifiers","Object","keys","length","values","join","undefined","productId","$id","id","variantId","name","variantLabel","unitAmountCents","Math","round","Number","priceUsd","extraCents","lineKey","line","modifierSelectionKey","PosOrdersZone","props","renderer","Renderer","hostId","registerId","slot","orgId","PosConsolePage","liveRegister","usableRegisters","offline","WidgetSlot","firestore","data","user","enqueueSnackbar","theme","wide","breakpoints","up","setSearch","setCategoryIdState","window","localStorage","getItem","setCategoryId","setItem","gridPlan","productDocs","idField","categoryDocs","locationDocs","registerDocs","org","ready","planReady","registers","sort","a","b","String","localeCompare","registerCap","checkHostRegisterQuota","withinCap","registersWithinCap","filter","r","has","reservationDocs","openStays","lines","setLines","discountPct","setDiscountPct","customer","setCustomer","customerEmail","email","locationId","setLocationId","setRegisterId","first","registerName","find","register","opsSettings","cashier","autoLockMinutes","lastReceipt","setLastReceipt","context","setContext","userRef","current","uid","signedIn","active","then","answer","settings","terminal","catch","sale","setSale","saleLines","setSaleLines","opening","setOpening","sheetOpen","setSheetOpen","pairing","setPairing","display","attemptKey","fill","min","products","map","liftLegacyProduct","loadCatalog","docs","snapshot","openShiftId","assertion","cashierAssertion","liveProducts","offlineCheckout","setOfflineCheckout","strandedSales","orderIds","splice","orderId","sellProducts","gridProducts","productsById","Map","shortfalls","get","stockShortfall","variants","itemsCents","reduce","sum","discountCents","estimateCents","units","notify","message","persist","allowDuplicate","showPairingCode","mode","pairingCode","error","addProduct","next","prev","existing","itemSheet","setItemSheet","productCache","tapProduct","set","productModifierGroups","initial","basketCounts","counts","lookupCode","scanned","needle","toLowerCase","found","findByCode","lookup","hit","console","reportNetworkFailure","item","barcode","sku","handleSearchEnter","scanAdornment","onScan","chargeInFlight","charge","unavailableReason","opened","status","totalCents","totals","paidCents","dueCents","tenderableCents","tipCents","payments","resetSale","mirrored","displayShowsBasket","pendingTipCents","setPendingTipCents","connected","asking","some","payment","method","timer","setTimeout","show","cart","amountCents","taxCents","max","clearTimeout","registerPicker","color","onChange","event","target","size","select","disabled","Boolean","bottomBarRef","node","root","document","documentElement","property","publish","style","setProperty","getBoundingClientRect","height","observer","ResizeObserver","observe","disconnect","removeProperty","registerPanel","spacing","sx","minHeight","flex","direction","alignItems","onClick","location","onExchange","returned","phone","onQuantity","index","_line","at","onRemove","onEdit","editIndex","onDiscountPct","locked","onNewSale","cashierName","stays","onSale","onVoided","onTipChange","onSellOffline","push","justifyContent","totalsFor","screen","ref","overflow","p","overflowY","pb","mb","onTake","nextLines","onSearch","onSearchEnter","categories","category","onCategory","notices","onTap","width","md","lg","borderLeft","borderColor","flexDirection","position","left","right","bottom","bgcolor","borderTop","gap","zIndex","appBar","badgeContent","textAlign","anchor","open","onClose","slotProps","paper","maxHeight","borderTopLeftRadius","borderTopRightRadius","onConfirm","sheet","onRung","onDone","maxWidth","fullWidth","exclusive","_event","mt","wordBreak","origin","component","letterSpacing","displayName"],"mappings":"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"}
|
|
@@ -18,6 +18,8 @@ export interface ProductEditorDialogProps {
|
|
|
18
18
|
open: boolean;
|
|
19
19
|
onClose: () => void;
|
|
20
20
|
}
|
|
21
|
+
/** What the editor says while a variant has no price (AGL-3676): savable, not sellable. */
|
|
22
|
+
export declare const PRODUCT_PRICE_EMPTY_NOTICE = "Visitors will see \u201CPrice coming soon\u201D and can\u2019t buy this yet. Set a price to start selling it.";
|
|
21
23
|
/**
|
|
22
24
|
* Products hub editor (AGL-279): the full catalog editor — basics,
|
|
23
25
|
* media, tags, options → variants matrix, per-variant pricing/stock, and
|
|
@@ -55,7 +55,10 @@ const SUPPLIER_CEILING = 50;
|
|
|
55
55
|
'title',
|
|
56
56
|
'description'
|
|
57
57
|
];
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* A product nobody has saved yet, as the editor starts one: at the default
|
|
60
|
+
* price (AGL-3676), for the owner to change or clear.
|
|
61
|
+
*/ function blankProduct() {
|
|
59
62
|
return {
|
|
60
63
|
name: '',
|
|
61
64
|
slug: '',
|
|
@@ -64,11 +67,12 @@ const SUPPLIER_CEILING = 50;
|
|
|
64
67
|
variants: [
|
|
65
68
|
{
|
|
66
69
|
id: 'default',
|
|
67
|
-
priceUsd:
|
|
70
|
+
priceUsd: CommerceModel.COMMERCE_DEFAULT_PRICE_USD
|
|
68
71
|
}
|
|
69
72
|
]
|
|
70
73
|
};
|
|
71
74
|
}
|
|
75
|
+
/** What the editor says while a variant has no price (AGL-3676): savable, not sellable. */ export const PRODUCT_PRICE_EMPTY_NOTICE = 'Visitors will see “Price coming soon” and can’t buy this yet. Set a price to start selling it.';
|
|
72
76
|
/** Stable key for matching variants across matrix regenerations. */ function comboKey(options) {
|
|
73
77
|
return Object.entries(options != null ? options : {}).sort(([a], [b])=>a.localeCompare(b)).map(([name, value])=>`${name}:${value}`).join('|');
|
|
74
78
|
}
|
|
@@ -557,9 +561,11 @@ const ProductVariantRow = /*#__PURE__*/ memo(function ProductVariantRow(props) {
|
|
|
557
561
|
onChange: set.priceUsd,
|
|
558
562
|
size: "small",
|
|
559
563
|
sx: PRICE_CELL,
|
|
560
|
-
// An empty price is marked (AGL-2916):
|
|
561
|
-
// product
|
|
562
|
-
|
|
564
|
+
// An empty price is marked (AGL-2916), gently (AGL-3676): the
|
|
565
|
+
// product saves without one, and the storefront says "Price
|
|
566
|
+
// coming soon" and sells it nowhere.
|
|
567
|
+
color: CommerceModel.variantHasPrice(variant) ? undefined : 'warning',
|
|
568
|
+
focused: CommerceModel.variantHasPrice(variant) ? undefined : true,
|
|
563
569
|
placeholder: "Set",
|
|
564
570
|
slotProps: DECIMAL_INPUT
|
|
565
571
|
})
|
|
@@ -1383,14 +1389,20 @@ const MemoPaidDownloadAddButton = /*#__PURE__*/ memo(PaidDownloadAddButton);
|
|
|
1383
1389
|
]));
|
|
1384
1390
|
const fallback = at.variants[0];
|
|
1385
1391
|
const variants = CommerceModel.expandVariantMatrix(options).map((combo, comboIndex)=>{
|
|
1386
|
-
var _ref
|
|
1392
|
+
var _ref;
|
|
1387
1393
|
const existing = previous.get(comboKey(combo));
|
|
1388
|
-
return existing != null ? existing :
|
|
1394
|
+
return existing != null ? existing : // An unpriced variant is stored with no `priceUsd` (AGL-3676),
|
|
1395
|
+
// as a proposed product's are; the type names the priced shape.
|
|
1396
|
+
_extends({
|
|
1389
1397
|
id: `v${Date.now().toString(36)}${comboIndex}`,
|
|
1390
|
-
options: combo
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
}
|
|
1398
|
+
options: combo
|
|
1399
|
+
}, fallback ? CommerceModel.variantHasPrice(fallback) ? {
|
|
1400
|
+
priceUsd: fallback.priceUsd
|
|
1401
|
+
} : {} : {
|
|
1402
|
+
priceUsd: CommerceModel.COMMERCE_DEFAULT_PRICE_USD
|
|
1403
|
+
}, {
|
|
1404
|
+
inventory: (_ref = fallback == null ? void 0 : fallback.inventory) != null ? _ref : null
|
|
1405
|
+
});
|
|
1394
1406
|
});
|
|
1395
1407
|
return {
|
|
1396
1408
|
options,
|
|
@@ -1417,7 +1429,9 @@ const MemoPaidDownloadAddButton = /*#__PURE__*/ memo(PaidDownloadAddButton);
|
|
|
1417
1429
|
variants[index] = _extends({}, variants[index], {
|
|
1418
1430
|
[field]: value
|
|
1419
1431
|
});
|
|
1420
|
-
|
|
1432
|
+
// A cleared price is no price (AGL-3676), never an `undefined` the
|
|
1433
|
+
// client-direct `setDoc` refuses.
|
|
1434
|
+
if (value === undefined && (field === 'priceUsd' || field === 'compareAtPriceUsd' || field === 'weightGrams')) {
|
|
1421
1435
|
delete variants[index][field];
|
|
1422
1436
|
}
|
|
1423
1437
|
return {
|
|
@@ -1618,6 +1632,10 @@ const MemoPaidDownloadAddButton = /*#__PURE__*/ memo(PaidDownloadAddButton);
|
|
|
1618
1632
|
stockApplies: stockApplies,
|
|
1619
1633
|
onField: handleVariantField
|
|
1620
1634
|
}),
|
|
1635
|
+
CommerceModel.productPriceMissing(current) ? /*#__PURE__*/ _jsx(Alert, {
|
|
1636
|
+
severity: "warning",
|
|
1637
|
+
children: PRODUCT_PRICE_EMPTY_NOTICE
|
|
1638
|
+
}) : null,
|
|
1621
1639
|
/*#__PURE__*/ _jsx(Typography, {
|
|
1622
1640
|
variant: "caption",
|
|
1623
1641
|
color: "text.secondary",
|