@aglyn/plugins-commerce 1.0.0-beta.250 → 1.0.0-beta.252
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/commerce-console-page.d.ts +1 -1
- package/src/lib/components/commerce-console-page.js +139 -175
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/commerce-console-sections.d.ts +30 -2
- package/src/lib/components/commerce-console-sections.js +82 -7
- package/src/lib/components/commerce-console-sections.js.map +1 -1
- package/src/lib/components/console/discounts-card.component.js +36 -3
- package/src/lib/components/console/discounts-card.component.js.map +1 -1
- package/src/lib/components/console/host-coupons-card.component.js +1 -1
- package/src/lib/components/console/host-coupons-card.component.js.map +1 -1
- package/src/lib/components/console/pos/pos-pairing.component.d.ts +59 -0
- package/src/lib/components/console/pos/pos-pairing.component.js +174 -0
- package/src/lib/components/console/pos/pos-pairing.component.js.map +1 -0
- package/src/lib/components/console/pos/use-pos-display.d.ts +0 -8
- package/src/lib/components/console/pos/use-pos-display.js +2 -16
- package/src/lib/components/console/pos/use-pos-display.js.map +1 -1
- package/src/lib/components/console/pos-devices-card.component.d.ts +12 -1
- package/src/lib/components/console/pos-devices-card.component.js +132 -59
- package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
- package/src/lib/components/console/pos-page.component.d.ts +1 -1
- package/src/lib/components/console/pos-page.component.js +37 -86
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/registers-card.component.js +13 -0
- package/src/lib/components/console/registers-card.component.js.map +1 -1
- package/src/lib/components/console/shipping-settings-card.component.js +6 -11
- package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
- package/src/lib/components/console/store-zones.d.ts +15 -0
- package/src/lib/components/console/store-zones.js +36 -4
- package/src/lib/components/console/store-zones.js.map +1 -1
- package/src/lib/components/console/suppliers-card.component.js +2 -2
- package/src/lib/components/console/suppliers-card.component.js.map +1 -1
- package/src/lib/model/commerce-record-routes.js +10 -3
- package/src/lib/model/commerce-record-routes.js.map +1 -1
- package/src/lib/model/commerce-shipping.d.ts +16 -0
- package/src/lib/model/commerce-shipping.js +21 -0
- package/src/lib/model/commerce-shipping.js.map +1 -1
- package/src/lib/model/commerce-tax-decision.js +2 -2
- package/src/lib/model/commerce-tax-decision.js.map +1 -1
- package/src/lib/plugin.js +4 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/billing-webhook.js +13 -0
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/order-webhooks.js +1 -1
- package/src/lib/server/order-webhooks.js.map +1 -1
- package/src/lib/server/shippingeasy.js +1 -1
- package/src/lib/server/shippingeasy.js.map +1 -1
- package/src/lib/server/store-setup-ai-capability.js +10 -2
- package/src/lib/server/store-setup-ai-capability.js.map +1 -1
- package/src/lib/server/supplier-routing.d.ts +86 -0
- package/src/lib/server/supplier-routing.js +175 -0
- package/src/lib/server/supplier-routing.js.map +1 -0
|
@@ -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 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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"}
|
|
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 Drawer,\n MenuItem,\n Stack,\n TextField,\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 { posRegistersSettingsHref, usePosPairing } from './pos/pos-pairing.component'\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, basePath }: 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 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 /**\n * A pairing code for a customer display or a self-service kiosk (AGL-3623),\n * through the same flow the POS devices card offers (AGL-3712).\n */\n const onPairingError = useCallback((message: string) => notify(message, 'error'), [notify])\n const pairingFlow = usePosPairing({ user, hostId, onError: onPairingError })\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 registersHref = posRegistersSettingsHref(basePath)\n const registerPicker = !planReady ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Checking your plan…'}\n </Typography>\n ) : usableRegisters.length === 0 ? (\n registers.length > 0 ? (\n <Typography variant=\"body2\" color=\"warning.main\">\n {'Your registers exceed your plan — remove extras or upgrade in Billing to take payments.'}\n </Typography>\n ) : (\n // A way there, not a path to follow by hand (AGL-3712).\n <Stack spacing={1} sx={{ alignItems: 'flex-start' }}>\n <Typography variant=\"body2\" color=\"warning.main\">\n {'No POS register yet. Add one before taking payments.'}\n </Typography>\n {registersHref ? (\n <Button size=\"small\" variant=\"outlined\" href={registersHref}>\n {'Add a register'}\n </Button>\n ) : null}\n </Stack>\n )\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 pairingFlow.open(registerId, '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 {pairingFlow.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","Drawer","MenuItem","Stack","TextField","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","posRegistersSettingsHref","usePosPairing","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","basePath","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","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","onPairingError","pairingFlow","onError","addProduct","next","prev","existing","itemSheet","setItemSheet","productCache","tapProduct","set","productModifierGroups","initial","basketCounts","counts","lookupCode","scanned","needle","toLowerCase","found","findByCode","lookup","hit","error","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","mode","cart","amountCents","taxCents","max","clearTimeout","registersHref","registerPicker","color","spacing","sx","alignItems","size","href","onChange","event","target","select","disabled","Boolean","bottomBarRef","node","root","document","documentElement","property","publish","style","setProperty","getBoundingClientRect","height","observer","ResizeObserver","observe","disconnect","removeProperty","registerPanel","minHeight","flex","direction","onClick","open","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","onClose","slotProps","paper","maxHeight","borderTopLeftRadius","borderTopRightRadius","onConfirm","sheet","onRung","onDone","dialog","displayName"],"mappings":"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"}
|
|
@@ -25,6 +25,7 @@ import { collection, getCountFromServer, limit, query } from "firebase/firestore
|
|
|
25
25
|
import { useCallback, useEffect, useState } from "react";
|
|
26
26
|
import { useFirestore, useFirestoreCollection, useHostResourceApi, useOrgPlan, useUser } from "@aglyn/tenant-feature-instance";
|
|
27
27
|
import { posReadersCall } from "./pos/pos-api.js";
|
|
28
|
+
import { POS_REGISTERS_ANCHOR } from "./pos/pos-pairing.component.js";
|
|
28
29
|
import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
29
30
|
/**
|
|
30
31
|
* POS registers (AGL-472): named registers under `hosts/{hostId}/registers`,
|
|
@@ -199,11 +200,23 @@ import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
|
199
200
|
hostId,
|
|
200
201
|
enqueueSnackbar
|
|
201
202
|
]);
|
|
203
|
+
/*
|
|
204
|
+
* Arrived by "Add a register" (AGL-3712): the register page and the POS
|
|
205
|
+
* devices card link here by anchor, and this card renders after the page
|
|
206
|
+
* has already tried to scroll to it, so it brings itself into view.
|
|
207
|
+
*/ useEffect(()=>{
|
|
208
|
+
var _document_getElementById_scrollIntoView, _document_getElementById;
|
|
209
|
+
if (typeof window === 'undefined' || window.location.hash !== `#${POS_REGISTERS_ANCHOR}`) return;
|
|
210
|
+
(_document_getElementById = document.getElementById(POS_REGISTERS_ANCHOR)) == null ? void 0 : (_document_getElementById_scrollIntoView = _document_getElementById.scrollIntoView) == null ? void 0 : _document_getElementById_scrollIntoView.call(_document_getElementById, {
|
|
211
|
+
block: 'start'
|
|
212
|
+
});
|
|
213
|
+
}, []);
|
|
202
214
|
const locationName = (id)=>{
|
|
203
215
|
var _locations_find;
|
|
204
216
|
return (_locations_find = locations.find((location)=>location.$id === id)) == null ? void 0 : _locations_find.name;
|
|
205
217
|
};
|
|
206
218
|
return /*#__PURE__*/ _jsx(CardDisplay, {
|
|
219
|
+
id: POS_REGISTERS_ANCHOR,
|
|
207
220
|
header: 'POS registers',
|
|
208
221
|
help: pluginDocsHelp('pos', {
|
|
209
222
|
anchor: '#registers'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/registers-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Button, Chip, MenuItem, Stack, TextField, Typography } from '@mui/material'\nimport {\n collection,\n getCountFromServer,\n limit,\n query,\n} from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useHostResourceApi,\n useOrgPlan,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { posReadersCall } from './pos/pos-api'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface RegistersCardProps {\n hostId: string\n}\n\n/**\n * POS registers (AGL-472): named registers under `hosts/{hostId}/registers`,\n * capped by the plan's `posRegisters` quota (Pro 1, Business 2, Advanced 5)\n * PLUS the register seats the org has allocated to this site out of the\n * purchased $89/mo pool (AGL-1775). Creation rides the quota-enforcing\n * resources API so the cap is authoritative; every POS sale stamps its\n * register so takings are attributable.\n *\n * The add-on used to raise the org-level entitlement, which every site\n * inherited — one purchase, `hostLimit` registers. It is a pool now, and the\n * message below tells an owner which of the two things to do about an\n * exhausted one: assign a seat they already own, or buy another.\n */\nexport function RegistersCard(props: RegistersCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const createHostResource = useHostResourceApi()\n const { org, ready: planReady } = useOrgPlan(hostId)\n const { data: registerDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'registers'), limit(25)),\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 registers = [...(registerDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n const locations = locationDocs ?? []\n /**\n * The register HEAD-COUNT is a server aggregate, not the length of the\n * capped listener (AGL-1738, the ninth instance of AGL-1716's AGL-1706\n * shape — filed as latent, made real when the clamp was declined).\n *\n * The listener is `limit(25)` and the stock `posRegisters` bands top out\n * at agency's 20 — under the window — but the $89/mo register add-on is\n * sold flat up to `POS_REGISTERS_ADDON_MAX` (50) on any plan carrying\n * `pos`, so an agency org's effective limit reaches 70. From the 26th\n * register the listener saturates and the card understated usage in the\n * flattering direction while `api/hosts/resources`, which counts the\n * collection, refused the add.\n *\n * Re-read after an add AND a delete: registers are hard-deleted here\n * (like locations, unlike the soft-deleting cards), so a removal really\n * does free a slot on both sides, and a one-shot that missed it would\n * keep refusing.\n */\n const [registerCountEpoch, setRegisterCountEpoch] = useState(0)\n const [serverRegisterCount, setServerRegisterCount] = useState<number | null>(\n null,\n )\n useEffect(() => {\n let active = true\n void getCountFromServer(collection(firestore, 'hosts', hostId, 'registers'))\n .then((snapshot) => {\n if (active) setServerRegisterCount(snapshot.data().count)\n })\n .catch(() => {\n // Falls back to the loaded rows — a LOWER bound and the prior\n // behaviour, never 0, which reads as \"no registers used\" on a site\n // that is over its cap.\n })\n return () => {\n active = false\n }\n }, [firestore, hostId, registerCountEpoch])\n const registerCount = serverRegisterCount ?? registers.length\n // Per SITE (AGL-1775), not the org-level value: the plan's cap plus this\n // host's allocated seats. The allocation rides the SAME org doc `useOrgPlan`\n // already returns, so there is no second read to be loading or to fail —\n // and an unallocated (or not-yet-arrived) org resolves to the plan cap,\n // never to the pool.\n const quota = Aglyn.checkHostRegisterQuota(org, hostId, registerCount)\n const registerPool = Aglyn.resolveRegisterSeatPool(org)\n // Registers beyond the plan cap (e.g. after a downgrade) can't transact —\n // pos-order.ts blocks them by creation rank (AGL-482); mirror that here.\n //\n // Only once the org doc has arrived (AGL-1064). An absent org resolves to\n // the free tier's `posRegisters: 0`, which would badge EVERY register\n // \"Over plan limit\" for the first render or two of every mount.\n const withinCap = planReady\n ? CommerceModel.registersWithinCap(registers, quota.limit)\n : new Set<string>(registers.map((register: any) => register.$id))\n const [name, setName] = useState('')\n const [locationId, setLocationId] = useState('')\n\n const handleAdd = useCallback(async () => {\n if (!name.trim()) return\n // The button is disabled until the plan is known; this guards the case\n // where a click and the org doc race (AGL-1064).\n if (!planReady) return\n if (!quota.allowed) {\n // Three different answers, because the fix differs (AGL-1775). An\n // exhausted POOL is a purchase; an UNASSIGNED seat the org already owns\n // is a click, and telling that owner to buy another would sell them a\n // register they are already paying for.\n return void enqueueSnackbar(\n quota.limit === 0\n ? 'POS registers require the Pro plan or above — see Billing'\n : registerPool.available > 0\n ? `This site can run ${quota.limit} register${\n quota.limit === 1 ? '' : 's'\n } — you have ${registerPool.available} unassigned register seat${\n registerPool.available === 1 ? '' : 's'\n }; assign one to this site in Billing → Add-ons`\n : `This site can run ${quota.limit} register${\n quota.limit === 1 ? '' : 's'\n } and every purchased seat is assigned — add another from ` +\n 'Billing → Add-ons ($89/mo each)',\n { variant: 'info', persist: false },\n )\n }\n try {\n // Creation rides the quota-enforcing resources API (AGL-472/473).\n await createHostResource({\n hostId,\n resource: 'register',\n data: {\n name: name.trim().slice(0, 80),\n ...(locationId ? { locationId } : {}),\n } satisfies CommerceModel.PosRegister,\n })\n setName('')\n setLocationId('')\n setRegisterCountEpoch((epoch) => epoch + 1)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not add register', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [\n name,\n locationId,\n planReady,\n quota,\n hostId,\n createHostResource,\n enqueueSnackbar,\n ])\n\n const handleDelete = useCallback(\n (register: any) => async () => {\n const confirmed = await confirm({\n title: 'Remove this register?',\n description:\n `\"${register.name}\" stops being available at checkout. Past ` +\n 'sales keep their register tag.',\n confirmationText: 'Remove',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n // On the server (AGL-3617): the rules have refused a client delete of a\n // register since AGL-1775, so this button failed for every merchant.\n // The route also signs out the register's customer displays.\n try {\n await posReadersCall(user, { action: 'remove-register', hostId, registerId: register.$id })\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not remove the register', {\n variant: 'warning',\n persist: false,\n })\n return\n }\n // A HARD delete, so the slot really is freed on both sides — the\n // one-shot aggregate has to be told (AGL-1716/AGL-1738).\n setRegisterCountEpoch((epoch) => epoch + 1)\n },\n [confirm, user, hostId, enqueueSnackbar],\n )\n\n const locationName = (id: string) =>\n locations.find((location: any) => location.$id === id)?.name\n\n return (\n <CardDisplay\n header={'POS registers'}\n help={pluginDocsHelp('pos', { anchor: '#registers' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n {registers.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Name each till or device that takes in-person payments. Every ' +\n 'POS sale is tagged with its register for end-of-day takings.'}\n </Typography>\n ) : (\n registers.map((register: any) => {\n const overCap = !withinCap.has(register.$id)\n return (\n <Stack\n key={register.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', opacity: overCap ? 0.6 : 1 }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {register.name}\n {register.locationId && locationName(register.locationId) ? (\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">\n {` · ${locationName(register.locationId)}`}\n </Typography>\n ) : null}\n </Typography>\n {overCap ? (\n <Chip\n label=\"Over plan limit\"\n size=\"small\"\n color=\"warning\"\n variant=\"outlined\"\n />\n ) : null}\n <Button size=\"small\" color=\"error\" onClick={handleDelete(register)}>\n {'Remove'}\n </Button>\n </Stack>\n )\n })\n )}\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"New register\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"Front counter\"\n />\n {locations.length > 1 ? (\n <TextField\n select\n label=\"Location\"\n value={locationId}\n onChange={(event) => setLocationId(event.target.value)}\n size=\"small\"\n sx={{ minWidth: 140 }}\n >\n <MenuItem value=\"\">{'Any'}</MenuItem>\n {locations.map((location: any) => (\n <MenuItem key={location.$id} value={location.$id}>\n {location.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n <Button\n size=\"small\"\n disabled={!name.trim() || !planReady}\n onClick={handleAdd}\n >\n {'Add'}\n </Button>\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {/* The site's registers, not this card's rows (AGL-1738) — the\n readout and the gate must agree, and the gate now counts. */}\n {planReady\n ? `${registerCount}/${\n quota.limit === Aglyn.UNLIMITED ? '∞' : quota.limit\n } registers on your plan`\n : `${registerCount} register${\n registerCount === 1 ? '' : 's'\n } · checking your plan…`}\n </Typography>\n </Stack>\n </CardDisplay>\n )\n}\nRegistersCard.displayName = 'RegistersCard'\n\nexport default RegistersCard\n"],"names":["Aglyn","CommerceModel","CardDisplay","useConfirmationContext","useSnackbar","Button","Chip","MenuItem","Stack","TextField","Typography","collection","getCountFromServer","limit","query","useCallback","useEffect","useState","useFirestore","useFirestoreCollection","useHostResourceApi","useOrgPlan","useUser","posReadersCall","pluginDocsHelp","RegistersCard","props","hostId","firestore","data","user","enqueueSnackbar","confirm","createHostResource","org","ready","planReady","registerDocs","idField","locationDocs","registers","sort","a","b","String","name","localeCompare","locations","registerCountEpoch","setRegisterCountEpoch","serverRegisterCount","setServerRegisterCount","active","then","snapshot","count","catch","registerCount","length","quota","checkHostRegisterQuota","registerPool","resolveRegisterSeatPool","withinCap","registersWithinCap","Set","map","register","$id","setName","locationId","setLocationId","handleAdd","trim","allowed","available","variant","persist","resource","slice","epoch","error","message","handleDelete","confirmed","title","description","confirmationText","confirmationButtonProps","color","action","registerId","locationName","id","find","location","header","help","anchor","contentGutterX","contentGutterY","spacing","overCap","has","direction","sx","alignItems","opacity","flex","noWrap","component","label","size","onClick","value","onChange","event","target","placeholder","select","minWidth","disabled","UNLIMITED","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,YAAYA,WAAW,eAAc;AACrC,YAAYC,mBAAmB,uBAAa;AAC5C,SAASC,WAAW,EAAEC,sBAAsB,QAAQ,uBAAsB;AAC1E,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SAASC,MAAM,EAAEC,IAAI,EAAEC,QAAQ,EAAEC,KAAK,EAAEC,SAAS,EAAEC,UAAU,QAAQ,gBAAe;AACpF,SACEC,UAAU,EACVC,kBAAkB,EAClBC,KAAK,EACLC,KAAK,QACA,qBAAoB;AAC3B,SAASC,WAAW,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AACxD,SACEC,YAAY,EACZC,sBAAsB,EACtBC,kBAAkB,EAClBC,UAAU,EACVC,OAAO,QACF,iCAAgC;AACvC,SAASC,cAAc,QAAQ,mBAAe;AAC9C,SAASC,cAAc,QAAQ,eAAc;AAM7C;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC,cAAcC,KAAyB;IACrD,MAAM,EAAEC,MAAM,EAAE,GAAGD;IACnB,MAAME,YAAYV;IAClB,MAAM,EAAEW,MAAMC,IAAI,EAAE,GAAGR;IACvB,MAAM,EAAES,eAAe,EAAE,GAAG3B;IAC5B,MAAM,EAAE4B,OAAO,EAAE,GAAG7B;IACpB,MAAM8B,qBAAqBb;IAC3B,MAAM,EAAEc,GAAG,EAAEC,OAAOC,SAAS,EAAE,GAAGf,WAAWM;IAC7C,MAAM,EAAEE,MAAMQ,YAAY,EAAE,GAAGlB,uBAC7B,IAAML,MAAMH,WAAWiB,WAAW,SAASD,QAAQ,cAAcd,MAAM,MACvE;QAACe;QAAWD;KAAO,EACnB;QAAEW,SAAS;IAAM;IAEnB,MAAM,EAAET,MAAMU,YAAY,EAAE,GAAGpB,uBAC7B,IAAML,MAAMH,WAAWiB,WAAW,SAASD,QAAQ,cAAcd,MAAM,MACvE;QAACe;QAAWD;KAAO,EACnB;QAAEW,SAAS;IAAM;IAEnB,MAAME,YAAY;WAAKH,uBAAAA,eAAgB,EAAE;KAAE,CAACI,IAAI,CAAC,CAACC,GAAQC;YACjDD,SAAmCC;eAA1CC,QAAOF,UAAAA,EAAEG,IAAI,YAANH,UAAU,IAAII,aAAa,CAACF,QAAOD,UAAAA,EAAEE,IAAI,YAANF,UAAU;;IAEtD,MAAMI,YAAYR,uBAAAA,eAAgB,EAAE;IACpC;;;;;;;;;;;;;;;;;GAiBC,GACD,MAAM,CAACS,oBAAoBC,sBAAsB,GAAGhC,SAAS;IAC7D,MAAM,CAACiC,qBAAqBC,uBAAuB,GAAGlC,SACpD;IAEFD,UAAU;QACR,IAAIoC,SAAS;QACb,KAAKxC,mBAAmBD,WAAWiB,WAAW,SAASD,QAAQ,cAC5D0B,IAAI,CAAC,CAACC;YACL,IAAIF,QAAQD,uBAAuBG,SAASzB,IAAI,GAAG0B,KAAK;QAC1D,GACCC,KAAK,CAAC;QACL,8DAA8D;QAC9D,mEAAmE;QACnE,wBAAwB;QAC1B;QACF,OAAO;YACLJ,SAAS;QACX;IACF,GAAG;QAACxB;QAAWD;QAAQqB;KAAmB;IAC1C,MAAMS,gBAAgBP,8BAAAA,sBAAuBV,UAAUkB,MAAM;IAC7D,yEAAyE;IACzE,6EAA6E;IAC7E,yEAAyE;IACzE,wEAAwE;IACxE,qBAAqB;IACrB,MAAMC,QAAQ3D,MAAM4D,sBAAsB,CAAC1B,KAAKP,QAAQ8B;IACxD,MAAMI,eAAe7D,MAAM8D,uBAAuB,CAAC5B;IACnD,0EAA0E;IAC1E,yEAAyE;IACzE,EAAE;IACF,0EAA0E;IAC1E,sEAAsE;IACtE,gEAAgE;IAChE,MAAM6B,YAAY3B,YACdnC,cAAc+D,kBAAkB,CAACxB,WAAWmB,MAAM9C,KAAK,IACvD,IAAIoD,IAAYzB,UAAU0B,GAAG,CAAC,CAACC,WAAkBA,SAASC,GAAG;IACjE,MAAM,CAACvB,MAAMwB,QAAQ,GAAGpD,SAAS;IACjC,MAAM,CAACqD,YAAYC,cAAc,GAAGtD,SAAS;IAE7C,MAAMuD,YAAYzD,YAAY;QAC5B,IAAI,CAAC8B,KAAK4B,IAAI,IAAI;QAClB,uEAAuE;QACvE,iDAAiD;QACjD,IAAI,CAACrC,WAAW;QAChB,IAAI,CAACuB,MAAMe,OAAO,EAAE;YAClB,kEAAkE;YAClE,wEAAwE;YACxE,sEAAsE;YACtE,wCAAwC;YACxC,OAAO,KAAK3C,gBACV4B,MAAM9C,KAAK,KAAK,IACZ,8DACAgD,aAAac,SAAS,GAAG,IACvB,CAAC,kBAAkB,EAAEhB,MAAM9C,KAAK,CAAC,SAAS,EACxC8C,MAAM9C,KAAK,KAAK,IAAI,KAAK,IAC1B,YAAY,EAAEgD,aAAac,SAAS,CAAC,yBAAyB,EAC7Dd,aAAac,SAAS,KAAK,IAAI,KAAK,IACrC,8CAA8C,CAAC,GAChD,CAAC,kBAAkB,EAAEhB,MAAM9C,KAAK,CAAC,SAAS,EACxC8C,MAAM9C,KAAK,KAAK,IAAI,KAAK,IAC1B,yDAAyD,CAAC,GAC3D,mCACN;gBAAE+D,SAAS;gBAAQC,SAAS;YAAM;QAEtC;QACA,IAAI;YACF,kEAAkE;YAClE,MAAM5C,mBAAmB;gBACvBN;gBACAmD,UAAU;gBACVjD,MAAM;oBACJgB,MAAMA,KAAK4B,IAAI,GAAGM,KAAK,CAAC,GAAG;mBACvBT,aAAa;oBAAEA;gBAAW,IAAI,CAAC;YAEvC;YACAD,QAAQ;YACRE,cAAc;YACdtB,sBAAsB,CAAC+B,QAAUA,QAAQ;QAC3C,EAAE,OAAOC,OAAY;;YACnBlD,wBAAgBkD,yBAAAA,MAAOC,OAAO,mBAAI,0BAA0B;gBAC1DN,SAAS;gBACTC,SAAS;YACX;QACF;IACF,GAAG;QACDhC;QACAyB;QACAlC;QACAuB;QACAhC;QACAM;QACAF;KACD;IAED,MAAMoD,eAAepE,YACnB,CAACoD,WAAkB;YACjB,MAAMiB,YAAY,MAAMpD,QAAQ;gBAC9BqD,OAAO;gBACPC,aACE,CAAC,CAAC,EAAEnB,SAAStB,IAAI,CAAC,0CAA0C,CAAC,GAC7D;gBACF0C,kBAAkB;gBAClBC,yBAAyB;oBAAEC,OAAO;gBAAQ;YAC5C,GACGpC,IAAI,CAAC,IAAM,MACXG,KAAK,CAAC,IAAM;YACf,IAAI,CAAC4B,WAAW;YAChB,wEAAwE;YACxE,qEAAqE;YACrE,6DAA6D;YAC7D,IAAI;gBACF,MAAM7D,eAAeO,MAAM;oBAAE4D,QAAQ;oBAAmB/D;oBAAQgE,YAAYxB,SAASC,GAAG;gBAAC;YAC3F,EAAE,OAAOa,OAAY;;gBACnBlD,wBAAgBkD,yBAAAA,MAAOC,OAAO,mBAAI,iCAAiC;oBACjEN,SAAS;oBACTC,SAAS;gBACX;gBACA;YACF;YACA,iEAAiE;YACjE,yDAAyD;YACzD5B,sBAAsB,CAAC+B,QAAUA,QAAQ;QAC3C,GACA;QAAChD;QAASF;QAAMH;QAAQI;KAAgB;IAG1C,MAAM6D,eAAe,CAACC;YACpB9C;gBAAAA,kBAAAA,UAAU+C,IAAI,CAAC,CAACC,WAAkBA,SAAS3B,GAAG,KAAKyB,wBAAnD9C,gBAAwDF,IAAI;;IAE9D,qBACE,KAAC3C;QACC8F,QAAQ;QACRC,MAAMzE,eAAe,OAAO;YAAE0E,QAAQ;QAAa;QACnDC,cAAc;QACdC,cAAc;kBAEd,cAAA,MAAC5F;YAAM6F,SAAS;;gBACb7D,UAAUkB,MAAM,KAAK,kBACpB,KAAChD;oBAAWkE,SAAQ;oBAAQa,OAAM;8BAC/B,mEACC;qBAGJjD,UAAU0B,GAAG,CAAC,CAACC;oBACb,MAAMmC,UAAU,CAACvC,UAAUwC,GAAG,CAACpC,SAASC,GAAG;oBAC3C,qBACE,MAAC5D;wBAECgG,WAAU;wBACVH,SAAS;wBACTI,IAAI;4BAAEC,YAAY;4BAAUC,SAASL,UAAU,MAAM;wBAAE;;0CAEvD,MAAC5F;gCAAWkE,SAAQ;gCAAQ6B,IAAI;oCAAEG,MAAM;gCAAE;gCAAGC,MAAM;;oCAChD1C,SAAStB,IAAI;oCACbsB,SAASG,UAAU,IAAIsB,aAAazB,SAASG,UAAU,kBACtD,KAAC5D;wCAAWoG,WAAU;wCAAOlC,SAAQ;wCAAUa,OAAM;kDAClD,CAAC,GAAG,EAAEG,aAAazB,SAASG,UAAU,GAAG;yCAE1C;;;4BAELgC,wBACC,KAAChG;gCACCyG,OAAM;gCACNC,MAAK;gCACLvB,OAAM;gCACNb,SAAQ;iCAER;0CACJ,KAACvE;gCAAO2G,MAAK;gCAAQvB,OAAM;gCAAQwB,SAAS9B,aAAahB;0CACtD;;;uBAtBEA,SAASC,GAAG;gBA0BvB;8BAEF,MAAC5D;oBAAMgG,WAAU;oBAAMH,SAAS;;sCAC9B,KAAC5F;4BACCsG,OAAM;4BACNG,OAAOrE;4BACPsE,UAAU,CAACC,QAAU/C,QAAQ+C,MAAMC,MAAM,CAACH,KAAK;4BAC/CF,MAAK;4BACLP,IAAI;gCAAEG,MAAM;4BAAE;4BACdU,aAAY;;wBAEbvE,UAAUW,MAAM,GAAG,kBAClB,MAACjD;4BACC8G,MAAM;4BACNR,OAAM;4BACNG,OAAO5C;4BACP6C,UAAU,CAACC,QAAU7C,cAAc6C,MAAMC,MAAM,CAACH,KAAK;4BACrDF,MAAK;4BACLP,IAAI;gCAAEe,UAAU;4BAAI;;8CAEpB,KAACjH;oCAAS2G,OAAM;8CAAI;;gCACnBnE,UAAUmB,GAAG,CAAC,CAAC6B,yBACd,KAACxF;wCAA4B2G,OAAOnB,SAAS3B,GAAG;kDAC7C2B,SAASlD,IAAI;uCADDkD,SAAS3B,GAAG;;6BAK7B;sCACJ,KAAC/D;4BACC2G,MAAK;4BACLS,UAAU,CAAC5E,KAAK4B,IAAI,MAAM,CAACrC;4BAC3B6E,SAASzC;sCAER;;;;8BAGL,KAAC9D;oBAAWkE,SAAQ;oBAAUa,OAAM;8BAGjCrD,YACG,GAAGqB,cAAc,CAAC,EAChBE,MAAM9C,KAAK,KAAKb,MAAM0H,SAAS,GAAG,MAAM/D,MAAM9C,KAAK,CACpD,uBAAuB,CAAC,GACzB,GAAG4C,cAAc,SAAS,EACxBA,kBAAkB,IAAI,KAAK,IAC5B,sBAAsB,CAAC;;;;;AAKtC;AACAhC,cAAckG,WAAW,GAAG;AAE5B,eAAelG,cAAa"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/registers-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Button, Chip, MenuItem, Stack, TextField, Typography } from '@mui/material'\nimport {\n collection,\n getCountFromServer,\n limit,\n query,\n} from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useHostResourceApi,\n useOrgPlan,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { posReadersCall } from './pos/pos-api'\nimport { POS_REGISTERS_ANCHOR } from './pos/pos-pairing.component'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface RegistersCardProps {\n hostId: string\n}\n\n/**\n * POS registers (AGL-472): named registers under `hosts/{hostId}/registers`,\n * capped by the plan's `posRegisters` quota (Pro 1, Business 2, Advanced 5)\n * PLUS the register seats the org has allocated to this site out of the\n * purchased $89/mo pool (AGL-1775). Creation rides the quota-enforcing\n * resources API so the cap is authoritative; every POS sale stamps its\n * register so takings are attributable.\n *\n * The add-on used to raise the org-level entitlement, which every site\n * inherited — one purchase, `hostLimit` registers. It is a pool now, and the\n * message below tells an owner which of the two things to do about an\n * exhausted one: assign a seat they already own, or buy another.\n */\nexport function RegistersCard(props: RegistersCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const createHostResource = useHostResourceApi()\n const { org, ready: planReady } = useOrgPlan(hostId)\n const { data: registerDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'registers'), limit(25)),\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 registers = [...(registerDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n const locations = locationDocs ?? []\n /**\n * The register HEAD-COUNT is a server aggregate, not the length of the\n * capped listener (AGL-1738, the ninth instance of AGL-1716's AGL-1706\n * shape — filed as latent, made real when the clamp was declined).\n *\n * The listener is `limit(25)` and the stock `posRegisters` bands top out\n * at agency's 20 — under the window — but the $89/mo register add-on is\n * sold flat up to `POS_REGISTERS_ADDON_MAX` (50) on any plan carrying\n * `pos`, so an agency org's effective limit reaches 70. From the 26th\n * register the listener saturates and the card understated usage in the\n * flattering direction while `api/hosts/resources`, which counts the\n * collection, refused the add.\n *\n * Re-read after an add AND a delete: registers are hard-deleted here\n * (like locations, unlike the soft-deleting cards), so a removal really\n * does free a slot on both sides, and a one-shot that missed it would\n * keep refusing.\n */\n const [registerCountEpoch, setRegisterCountEpoch] = useState(0)\n const [serverRegisterCount, setServerRegisterCount] = useState<number | null>(\n null,\n )\n useEffect(() => {\n let active = true\n void getCountFromServer(collection(firestore, 'hosts', hostId, 'registers'))\n .then((snapshot) => {\n if (active) setServerRegisterCount(snapshot.data().count)\n })\n .catch(() => {\n // Falls back to the loaded rows — a LOWER bound and the prior\n // behaviour, never 0, which reads as \"no registers used\" on a site\n // that is over its cap.\n })\n return () => {\n active = false\n }\n }, [firestore, hostId, registerCountEpoch])\n const registerCount = serverRegisterCount ?? registers.length\n // Per SITE (AGL-1775), not the org-level value: the plan's cap plus this\n // host's allocated seats. The allocation rides the SAME org doc `useOrgPlan`\n // already returns, so there is no second read to be loading or to fail —\n // and an unallocated (or not-yet-arrived) org resolves to the plan cap,\n // never to the pool.\n const quota = Aglyn.checkHostRegisterQuota(org, hostId, registerCount)\n const registerPool = Aglyn.resolveRegisterSeatPool(org)\n // Registers beyond the plan cap (e.g. after a downgrade) can't transact —\n // pos-order.ts blocks them by creation rank (AGL-482); mirror that here.\n //\n // Only once the org doc has arrived (AGL-1064). An absent org resolves to\n // the free tier's `posRegisters: 0`, which would badge EVERY register\n // \"Over plan limit\" for the first render or two of every mount.\n const withinCap = planReady\n ? CommerceModel.registersWithinCap(registers, quota.limit)\n : new Set<string>(registers.map((register: any) => register.$id))\n const [name, setName] = useState('')\n const [locationId, setLocationId] = useState('')\n\n const handleAdd = useCallback(async () => {\n if (!name.trim()) return\n // The button is disabled until the plan is known; this guards the case\n // where a click and the org doc race (AGL-1064).\n if (!planReady) return\n if (!quota.allowed) {\n // Three different answers, because the fix differs (AGL-1775). An\n // exhausted POOL is a purchase; an UNASSIGNED seat the org already owns\n // is a click, and telling that owner to buy another would sell them a\n // register they are already paying for.\n return void enqueueSnackbar(\n quota.limit === 0\n ? 'POS registers require the Pro plan or above — see Billing'\n : registerPool.available > 0\n ? `This site can run ${quota.limit} register${\n quota.limit === 1 ? '' : 's'\n } — you have ${registerPool.available} unassigned register seat${\n registerPool.available === 1 ? '' : 's'\n }; assign one to this site in Billing → Add-ons`\n : `This site can run ${quota.limit} register${\n quota.limit === 1 ? '' : 's'\n } and every purchased seat is assigned — add another from ` +\n 'Billing → Add-ons ($89/mo each)',\n { variant: 'info', persist: false },\n )\n }\n try {\n // Creation rides the quota-enforcing resources API (AGL-472/473).\n await createHostResource({\n hostId,\n resource: 'register',\n data: {\n name: name.trim().slice(0, 80),\n ...(locationId ? { locationId } : {}),\n } satisfies CommerceModel.PosRegister,\n })\n setName('')\n setLocationId('')\n setRegisterCountEpoch((epoch) => epoch + 1)\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not add register', {\n variant: 'warning',\n persist: false,\n })\n }\n }, [\n name,\n locationId,\n planReady,\n quota,\n hostId,\n createHostResource,\n enqueueSnackbar,\n ])\n\n const handleDelete = useCallback(\n (register: any) => async () => {\n const confirmed = await confirm({\n title: 'Remove this register?',\n description:\n `\"${register.name}\" stops being available at checkout. Past ` +\n 'sales keep their register tag.',\n confirmationText: 'Remove',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n // On the server (AGL-3617): the rules have refused a client delete of a\n // register since AGL-1775, so this button failed for every merchant.\n // The route also signs out the register's customer displays.\n try {\n await posReadersCall(user, { action: 'remove-register', hostId, registerId: register.$id })\n } catch (error: any) {\n enqueueSnackbar(error?.message ?? 'Could not remove the register', {\n variant: 'warning',\n persist: false,\n })\n return\n }\n // A HARD delete, so the slot really is freed on both sides — the\n // one-shot aggregate has to be told (AGL-1716/AGL-1738).\n setRegisterCountEpoch((epoch) => epoch + 1)\n },\n [confirm, user, hostId, enqueueSnackbar],\n )\n\n /*\n * Arrived by \"Add a register\" (AGL-3712): the register page and the POS\n * devices card link here by anchor, and this card renders after the page\n * has already tried to scroll to it, so it brings itself into view.\n */\n useEffect(() => {\n if (typeof window === 'undefined' || window.location.hash !== `#${POS_REGISTERS_ANCHOR}`) return\n document.getElementById(POS_REGISTERS_ANCHOR)?.scrollIntoView?.({ block: 'start' })\n }, [])\n\n const locationName = (id: string) =>\n locations.find((location: any) => location.$id === id)?.name\n\n return (\n <CardDisplay\n id={POS_REGISTERS_ANCHOR}\n header={'POS registers'}\n help={pluginDocsHelp('pos', { anchor: '#registers' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n {registers.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Name each till or device that takes in-person payments. Every ' +\n 'POS sale is tagged with its register for end-of-day takings.'}\n </Typography>\n ) : (\n registers.map((register: any) => {\n const overCap = !withinCap.has(register.$id)\n return (\n <Stack\n key={register.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', opacity: overCap ? 0.6 : 1 }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {register.name}\n {register.locationId && locationName(register.locationId) ? (\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">\n {` · ${locationName(register.locationId)}`}\n </Typography>\n ) : null}\n </Typography>\n {overCap ? (\n <Chip\n label=\"Over plan limit\"\n size=\"small\"\n color=\"warning\"\n variant=\"outlined\"\n />\n ) : null}\n <Button size=\"small\" color=\"error\" onClick={handleDelete(register)}>\n {'Remove'}\n </Button>\n </Stack>\n )\n })\n )}\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"New register\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"Front counter\"\n />\n {locations.length > 1 ? (\n <TextField\n select\n label=\"Location\"\n value={locationId}\n onChange={(event) => setLocationId(event.target.value)}\n size=\"small\"\n sx={{ minWidth: 140 }}\n >\n <MenuItem value=\"\">{'Any'}</MenuItem>\n {locations.map((location: any) => (\n <MenuItem key={location.$id} value={location.$id}>\n {location.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n <Button\n size=\"small\"\n disabled={!name.trim() || !planReady}\n onClick={handleAdd}\n >\n {'Add'}\n </Button>\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {/* The site's registers, not this card's rows (AGL-1738) — the\n readout and the gate must agree, and the gate now counts. */}\n {planReady\n ? `${registerCount}/${\n quota.limit === Aglyn.UNLIMITED ? '∞' : quota.limit\n } registers on your plan`\n : `${registerCount} register${\n registerCount === 1 ? '' : 's'\n } · checking your plan…`}\n </Typography>\n </Stack>\n </CardDisplay>\n )\n}\nRegistersCard.displayName = 'RegistersCard'\n\nexport default RegistersCard\n"],"names":["Aglyn","CommerceModel","CardDisplay","useConfirmationContext","useSnackbar","Button","Chip","MenuItem","Stack","TextField","Typography","collection","getCountFromServer","limit","query","useCallback","useEffect","useState","useFirestore","useFirestoreCollection","useHostResourceApi","useOrgPlan","useUser","posReadersCall","POS_REGISTERS_ANCHOR","pluginDocsHelp","RegistersCard","props","hostId","firestore","data","user","enqueueSnackbar","confirm","createHostResource","org","ready","planReady","registerDocs","idField","locationDocs","registers","sort","a","b","String","name","localeCompare","locations","registerCountEpoch","setRegisterCountEpoch","serverRegisterCount","setServerRegisterCount","active","then","snapshot","count","catch","registerCount","length","quota","checkHostRegisterQuota","registerPool","resolveRegisterSeatPool","withinCap","registersWithinCap","Set","map","register","$id","setName","locationId","setLocationId","handleAdd","trim","allowed","available","variant","persist","resource","slice","epoch","error","message","handleDelete","confirmed","title","description","confirmationText","confirmationButtonProps","color","action","registerId","document","window","location","hash","getElementById","scrollIntoView","block","locationName","id","find","header","help","anchor","contentGutterX","contentGutterY","spacing","overCap","has","direction","sx","alignItems","opacity","flex","noWrap","component","label","size","onClick","value","onChange","event","target","placeholder","select","minWidth","disabled","UNLIMITED","displayName"],"mappings":"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"}
|
|
@@ -193,10 +193,7 @@ import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
|
193
193
|
* zone that covered France should see France go uncovered before they
|
|
194
194
|
* commit it, not after.
|
|
195
195
|
*/ const coverage = CommerceModel.summarizeShippingCoverage(current);
|
|
196
|
-
const
|
|
197
|
-
var _CommerceModel_CHECKOUT_SHIPPING_COUNTRY_NAMES_code;
|
|
198
|
-
return (_CommerceModel_CHECKOUT_SHIPPING_COUNTRY_NAMES_code = CommerceModel.CHECKOUT_SHIPPING_COUNTRY_NAMES[code]) != null ? _CommerceModel_CHECKOUT_SHIPPING_COUNTRY_NAMES_code : code;
|
|
199
|
-
}).join(', ');
|
|
196
|
+
const words = CommerceModel.describeShippingCoverage(coverage);
|
|
200
197
|
/**
|
|
201
198
|
* The one-click half of the fix: a zone claiming everywhere the merchant
|
|
202
199
|
* has not named. It adds the ZONE only — a rate on it prices the parcel,
|
|
@@ -560,21 +557,19 @@ import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
|
560
557
|
}),
|
|
561
558
|
children: [
|
|
562
559
|
/*#__PURE__*/ _jsx(AlertTitle, {
|
|
563
|
-
children:
|
|
560
|
+
children: words.headline
|
|
564
561
|
}),
|
|
565
|
-
|
|
566
|
-
coverage.offersLocalPickup ? 'Local pickup does not cover them: it is a collection choice, not a rate, so it is offered where checkout completes and closes no gap here. ' : '',
|
|
567
|
-
coverage.hasRestOfWorldZone ? 'A rest-of-world zone is already saved, so what those destinations are missing is a rate on the zone that covers them.' : 'Give them a zone with a rate, or add a rest-of-world zone and price one on it.'
|
|
562
|
+
words.detail
|
|
568
563
|
]
|
|
569
564
|
}) : /*#__PURE__*/ _jsx(Typography, {
|
|
570
565
|
variant: "caption",
|
|
571
566
|
color: "text.secondary",
|
|
572
|
-
children:
|
|
567
|
+
children: words.note
|
|
573
568
|
}),
|
|
574
|
-
|
|
569
|
+
words.asks ? /*#__PURE__*/ _jsx(Typography, {
|
|
575
570
|
variant: "caption",
|
|
576
571
|
color: "text.secondary",
|
|
577
|
-
children:
|
|
572
|
+
children: words.asks
|
|
578
573
|
}) : null,
|
|
579
574
|
/*#__PURE__*/ _jsx(Button, {
|
|
580
575
|
variant: "contained",
|