@aglyn/plugins-commerce 1.0.0-beta.198 → 1.0.0-beta.200

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/package.json +13 -13
  2. package/src/lib/components/console/catalog-organization-card.component.js +48 -3
  3. package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
  4. package/src/lib/components/console/gift-cards-card.component.d.ts +9 -0
  5. package/src/lib/components/console/gift-cards-card.component.js +53 -28
  6. package/src/lib/components/console/gift-cards-card.component.js.map +1 -1
  7. package/src/lib/components/console/host-orders-card.component.d.ts +26 -0
  8. package/src/lib/components/console/host-orders-card.component.js +264 -194
  9. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  10. package/src/lib/components/console/pos-page.component.d.ts +14 -0
  11. package/src/lib/components/console/pos-page.component.js +86 -45
  12. package/src/lib/components/console/pos-page.component.js.map +1 -1
  13. package/src/lib/components/console/product-editor-dialog.component.js +30 -6
  14. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  15. package/src/lib/components/console/product-slugs.d.ts +43 -0
  16. package/src/lib/components/console/product-slugs.js +60 -0
  17. package/src/lib/components/console/product-slugs.js.map +1 -0
  18. package/src/lib/components/console/products-hub-card.component.d.ts +2 -2
  19. package/src/lib/components/console/products-hub-card.component.js +229 -282
  20. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  21. package/src/lib/components/console/products-hub-zone.component.d.ts +10 -5
  22. package/src/lib/components/console/products-hub-zone.component.js +32 -9
  23. package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
  24. package/src/lib/components/console/recovery-queue-card.component.d.ts +37 -5
  25. package/src/lib/components/console/recovery-queue-card.component.js +107 -86
  26. package/src/lib/components/console/recovery-queue-card.component.js.map +1 -1
  27. package/src/lib/components/console/reservations-card.component.js +15 -3
  28. package/src/lib/components/console/reservations-card.component.js.map +1 -1
  29. package/src/lib/components/console/smart-collections.d.ts +32 -0
  30. package/src/lib/components/console/smart-collections.js +57 -0
  31. package/src/lib/components/console/smart-collections.js.map +1 -0
  32. package/src/lib/components/console/stock-movements-card.component.d.ts +13 -4
  33. package/src/lib/components/console/stock-movements-card.component.js +142 -180
  34. package/src/lib/components/console/stock-movements-card.component.js.map +1 -1
  35. package/src/lib/components/product-grid.d.ts +5 -0
  36. package/src/lib/components/product-grid.js +95 -34
  37. package/src/lib/components/product-grid.js.map +1 -1
  38. package/src/lib/constants/orders-list-query.d.ts +40 -0
  39. package/src/lib/constants/orders-list-query.js +205 -0
  40. package/src/lib/constants/orders-list-query.js.map +1 -0
  41. package/src/lib/constants/product-list-query.d.ts +42 -0
  42. package/src/lib/constants/product-list-query.js +217 -0
  43. package/src/lib/constants/product-list-query.js.map +1 -0
  44. package/src/lib/constants/stock-movement-query.d.ts +27 -0
  45. package/src/lib/constants/stock-movement-query.js +110 -0
  46. package/src/lib/constants/stock-movement-query.js.map +1 -0
  47. package/src/lib/constants/storefront-catalog-query.d.ts +103 -0
  48. package/src/lib/constants/storefront-catalog-query.js +312 -0
  49. package/src/lib/constants/storefront-catalog-query.js.map +1 -0
  50. package/src/lib/model/checkout-recovery.d.ts +37 -0
  51. package/src/lib/model/checkout-recovery.js +44 -0
  52. package/src/lib/model/checkout-recovery.js.map +1 -0
  53. package/src/lib/model/commerce.d.ts +80 -0
  54. package/src/lib/model/commerce.js +67 -6
  55. package/src/lib/model/commerce.js.map +1 -1
  56. package/src/lib/model/gift-card-search.d.ts +24 -0
  57. package/src/lib/model/gift-card-search.js +48 -0
  58. package/src/lib/model/gift-card-search.js.map +1 -0
  59. package/src/lib/model/index.d.ts +1 -0
  60. package/src/lib/model/index.js +1 -0
  61. package/src/lib/model/index.js.map +1 -1
  62. package/src/lib/model/order-list-fields.d.ts +80 -0
  63. package/src/lib/model/order-list-fields.js +112 -0
  64. package/src/lib/model/order-list-fields.js.map +1 -0
  65. package/src/lib/server/billing-webhook.js +108 -89
  66. package/src/lib/server/billing-webhook.js.map +1 -1
  67. package/src/lib/server/cancel-order.js +6 -6
  68. package/src/lib/server/cancel-order.js.map +1 -1
  69. package/src/lib/server/cart-checkout.js +6 -0
  70. package/src/lib/server/cart-checkout.js.map +1 -1
  71. package/src/lib/server/catalog.d.ts +46 -30
  72. package/src/lib/server/catalog.js +492 -170
  73. package/src/lib/server/catalog.js.map +1 -1
  74. package/src/lib/server/collection-membership.d.ts +35 -0
  75. package/src/lib/server/collection-membership.js +172 -0
  76. package/src/lib/server/collection-membership.js.map +1 -0
  77. package/src/lib/server/draft-order.js +3 -2
  78. package/src/lib/server/draft-order.js.map +1 -1
  79. package/src/lib/server/gift-cards.js +3 -0
  80. package/src/lib/server/gift-cards.js.map +1 -1
  81. package/src/lib/server/member-name-search.d.ts +17 -0
  82. package/src/lib/server/member-name-search.js +23 -1
  83. package/src/lib/server/member-name-search.js.map +1 -1
  84. package/src/lib/server/membership-account.js +7 -3
  85. package/src/lib/server/membership-account.js.map +1 -1
  86. package/src/lib/server/membership-register.js +3 -1
  87. package/src/lib/server/membership-register.js.map +1 -1
  88. package/src/lib/server/pos-order.js +6 -4
  89. package/src/lib/server/pos-order.js.map +1 -1
  90. package/src/lib/server/process-abandoned.js +14 -5
  91. package/src/lib/server/process-abandoned.js.map +1 -1
  92. package/src/lib/server/reserve-stock.js +3 -4
  93. package/src/lib/server/reserve-stock.js.map +1 -1
  94. package/src/lib/server/site-page-enricher.js +3 -3
  95. package/src/lib/server/site-page-enricher.js.map +1 -1
  96. package/src/lib/server.js +4 -0
  97. package/src/lib/server.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/host-orders-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 CommerceModel from '../../model'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { hiddenFilterVisibility } from '@aglyn/shared-ui-jsx/const/list-filter'\nimport {\n inMemoryListField,\n type ListFilterClause,\n upsertListFilterClause,\n} from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { useListRowsFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-rows-filter'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n checkEntitlement,\n ORDERS_CUSTOMER_PARAM,\n ORDERS_ORDER_PARAM,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport {\n Alert,\n AlertTitle,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { collection, doc, getDoc, limit, orderBy, query } from 'firebase/firestore'\nimport { useSearchParams } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n ceilingedWindow,\n collectionCeiling,\n useFirestore,\n useOrgPlan,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\n\nimport CommerceStatTile from './commerce-stat-tile.component'\nimport OrderDetailDialog, {\n DISPUTE_COLOR,\n} from './order-detail-dialog.component'\n\n/**\n * How far back the table, its filters and its export reach.\n *\n * This window cannot be paged the way a plain list can. The Filters panel,\n * the search and the CSV export all run over what was read, so a page of ten\n * would quietly narrow every one of them — a status filter would search a\n * tenth of the orders and report confidently on what it found. Bounding the read and saying where the\n * bound falls keeps the filters honest about their own scope.\n */\nconst ORDERS_WINDOW = 200\n\n/** A name map for the filter menu; it renders no product rows of its own. */\nconst PRODUCT_NAME_WINDOW = 100\n\n/*\n * What the orders grid's Filters panel offers. The card holds its whole\n * window, so the panel and the search answer over every order it read.\n *\n * Status and Channel are the order's own enumerations; Disputes is apart\n * from Status on purpose, because the two are orthogonal: an OPEN dispute\n * sits on an order that is still `paid`, and a lost one on `refunded`\n * beside every ordinary refund. Product is the ids of every line item, so\n * a cart, POS or draft order is found by any product it holds. The date is\n * the field the query orders by, which every order writer stamps.\n */\nconst ORDER_FILTER_FIELDS = [\n inMemoryListField('orderLabel', 'text'),\n inMemoryListField('customerEmail', 'text'),\n inMemoryListField('channelKey', 'select'),\n inMemoryListField('statusKey', 'select'),\n inMemoryListField('createdAtMs', 'date'),\n { ...inMemoryListField('productIds', 'select'), tokensPath: 'productIds', verbatimTokens: true },\n inMemoryListField('disputeKey', 'select'),\n]\nconst ORDER_FILTER_HEADERS: Readonly<Record<string, string>> = {\n orderLabel: 'Order',\n customerEmail: 'Customer',\n channelKey: 'Channel',\n statusKey: 'Status',\n createdAtMs: 'Date',\n productIds: 'Product',\n disputeKey: 'Disputes',\n}\nconst ORDER_STATUS_OPTIONS = (\n Object.keys(CommerceModel.ORDER_STATUS_LABELS) as (keyof typeof CommerceModel.ORDER_STATUS_LABELS)[]\n).map((status) => ({ value: status, label: CommerceModel.ORDER_STATUS_LABELS[status] }))\nconst ORDER_CHANNEL_OPTIONS = (\n Object.keys(CommerceModel.ORDER_CHANNEL_LABELS) as (keyof typeof CommerceModel.ORDER_CHANNEL_LABELS)[]\n).map((channel) => ({ value: channel, label: CommerceModel.ORDER_CHANNEL_LABELS[channel] }))\n/*\n * `lost` is the badge's tone, not the raw dispute status: a dispute that was\n * WON also closes with money untouched, and listing it as charged back would\n * tell a merchant they lost a case they won.\n */\nconst ORDER_DISPUTE_OPTIONS = [\n { value: 'open', label: 'Open dispute' },\n { value: 'lost', label: 'Charged back' },\n]\n/** What the quick search reads on an order row. */\nconst ORDER_SEARCH_FIELDS = ['orderLabel', 'customerEmail', 'itemName'] as const\n/** The filter-only columns never show. */\nconst ORDER_HIDDEN_COLUMNS = hiddenFilterVisibility(ORDER_FILTER_FIELDS, [\n 'orderLabel',\n 'customerEmail',\n 'channelKey',\n 'statusKey',\n 'createdAtMs',\n])\n\n/** The clause the dispute banner's \"Show them\" sets. */\nconst OPEN_DISPUTE_CLAUSE: ListFilterClause = {\n field: 'disputeKey',\n op: 'equals',\n value: 'open',\n}\n\nexport interface HostOrdersCardProps {\n hostId: string\n}\n\n/**\n * Orders console (AGL-287): filterable list over webhook-written order\n * docs with a detail dialog (timeline, fulfill, refund, cancel, notes,\n * packing slip) and draft orders that send the buyer a payment link.\n */\nexport function HostOrdersCard(props: HostOrdersCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n /**\n * The most recent orders, in the order the table shows them.\n *\n * `createdAtMs` is the field every order writer in the plugin stamps and the\n * one `reconcile-stock` already walks the collection by. Ordering on it is\n * what makes this window the RECENT orders: an unordered cap is answered in\n * document-id order, and orders are keyed by generated ids, so it returns a\n * pseudo-random sample that a client sort then dresses as newest-first.\n */\n const { data: orderDocs } = useFirestoreCollection<any>(\n () =>\n query(\n collection(firestore, 'hosts', hostId, 'orders'),\n orderBy('createdAtMs', 'desc'),\n limit(ORDERS_WINDOW + 1),\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { data: productDocs } = useFirestoreCollection<any>(\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'products'),\n PRODUCT_NAME_WINDOW,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const orderWindow = useMemo(\n () => ceilingedWindow<any>(orderDocs ?? undefined, ORDERS_WINDOW),\n [orderDocs],\n )\n const productWindow = useMemo(\n () => ceilingedWindow<any>(productDocs ?? undefined, PRODUCT_NAME_WINDOW),\n [productDocs],\n )\n const productNames = useMemo(() => {\n const map: Record<string, string> = {}\n for (const product of productWindow.rows) {\n map[product.$id] = product.name ?? product.$id\n }\n return map\n }, [productWindow])\n\n /**\n * The money tiles are the `commerceAnalytics` surface (AGL-1938,\n * AGL-2056), and this is the third place they appear. Rendering them\n * ungated here would undo both of those passes and hand a Starter org\n * the Pro figures one tab away from the upgrade prompt that refuses\n * them. The TABLE below is not gated — a list of your own orders is not\n * a paid feature.\n */\n const { org, ready: orgReady } = useOrgPlan(hostId)\n const showStats =\n orgReady && checkEntitlement(org as never, 'commerceAnalytics')\n\n // The query returns the window already newest-first; re-sorting it here\n // would only restate the order it arrived in.\n const orders = orderWindow.rows\n\n /*\n * The grid's clauses (AGL-96, AGL-3317), held here so two things outside\n * the grid can set one: the CRM's contact page links with the buyer's\n * address to answer \"what has this customer ordered\" (AGL-2622), seeded as\n * a Customer clause matched as a substring so a domain finds every buyer at\n * a company; and the dispute banner's \"Show them\".\n */\n const searchParams = useSearchParams()\n const [clauses, setClauses] = useState<ListFilterClause[]>(() => {\n const customer = searchParams?.get(ORDERS_CUSTOMER_PARAM)?.trim()\n return customer\n ? [{ field: 'customerEmail', op: 'contains', value: customer }]\n : []\n })\n const [selectedId, setSelectedId] = useState<string | null>(null)\n /*\n * An order named in the URL opens in its dialog on arrival (AGL-2622). A\n * contact's timeline names the order that made the person a customer,\n * and the address it links to is this list with `?order={id}`. The\n * window is the newest two hundred, and an order from last year is not\n * in it, so the document is read once by id and held beside the window\n * for the dialog; an order that IS in the window is found there first\n * and the read is skipped. Once per id — the ref keeps a re-render from\n * reopening a dialog the merchant has since closed — and the landing read\n * is judged against the ref rather than an effect cleanup, because the\n * `setSelectedId` above re-renders before the read lands and a cleanup\n * keyed on any dep would cancel the answer to the question just asked.\n */\n const seededOrderId = searchParams?.get(ORDERS_ORDER_PARAM) ?? null\n const [seededOrder, setSeededOrder] = useState<any | null>(null)\n const seededOpened = useRef<string | null>(null)\n useEffect(() => {\n if (!seededOrderId || seededOpened.current === seededOrderId) return\n if (orderDocs === undefined) return\n seededOpened.current = seededOrderId\n setSelectedId(seededOrderId)\n if (orderDocs.some((order: any) => order.$id === seededOrderId)) return\n void getDoc(doc(firestore, 'hosts', hostId, 'orders', seededOrderId))\n .then((snapshot) => {\n if (seededOpened.current !== snapshot.id || !snapshot.exists()) return\n setSeededOrder({ ...snapshot.data(), $id: snapshot.id })\n })\n .catch(() => undefined)\n }, [seededOrderId, orderDocs, firestore, hostId])\n const [draft, setDraft] = useState<{\n productId: string\n variantId: string\n quantity: string\n email: string\n /** Destination the merchant declared, once they have been asked. */\n shipTo?: string\n /**\n * Revealed only when the server refuses for want of a destination\n * (AGL-1792). A merchant whose rates are the same everywhere — or who\n * configured none — is never asked and never sees this field.\n */\n shipCountries?: string[]\n busy?: boolean\n } | null>(null)\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n /**\n * Each order with the values the grid shows and filters by. `liftLegacyOrder`\n * names a status and channel on a row that predates them, and Product\n * reads the line items first (AGL-1747): the flat `productId` is written\n * only by the two buy-now Stripe paths.\n */\n const orderRows = useMemo(\n () =>\n orders.map((order: any) => {\n const lifted = CommerceModel.liftLegacyOrder(order)\n const dispute = CommerceModel.describeOrderDispute(lifted)\n const productIds = [\n ...new Set(\n [\n ...(lifted.lineItems ?? []).map((line) => line.productId),\n order.productId,\n ].filter((id): id is string => typeof id === 'string' && id !== ''),\n ),\n ]\n return {\n ...order,\n orderLabel: CommerceModel.formatOrderNumber(lifted, order.$id),\n itemName:\n lifted.lineItems?.[0]?.name ??\n productNames[order.productId] ??\n order.productId ??\n '',\n statusKey: lifted.status,\n channelKey: lifted.channel ?? 'online',\n netCents: CommerceModel.orderNetCents(lifted),\n disputeBadge: dispute,\n productIds,\n disputeKey: CommerceModel.orderHasOpenDispute(lifted)\n ? 'open'\n : dispute?.tone === 'lost'\n ? 'lost'\n : '',\n }\n }),\n [orders, productNames],\n )\n const productOptions = useMemo(\n () =>\n productWindow.rows.map((product: any) => ({\n value: String(product.$id),\n label: String(product.name ?? product.$id),\n })),\n [productWindow],\n )\n const filterOptions = useMemo(\n () => ({\n statusKey: ORDER_STATUS_OPTIONS,\n channelKey: ORDER_CHANNEL_OPTIONS,\n productIds: productOptions,\n disputeKey: ORDER_DISPUTE_OPTIONS,\n }),\n [productOptions],\n )\n const listFilter = useListRowsFilter({\n rows: orderRows,\n fields: ORDER_FILTER_FIELDS,\n options: filterOptions,\n headers: ORDER_FILTER_HEADERS,\n search: ORDER_SEARCH_FIELDS,\n clauses,\n onChange: setClauses,\n })\n const visibleOrders = listFilter.rows\n const showingOpenDisputes = clauses.some(\n (clause) =>\n clause.field === OPEN_DISPUTE_CLAUSE.field &&\n clause.op === OPEN_DISPUTE_CLAUSE.op &&\n clause.value === OPEN_DISPUTE_CLAUSE.value,\n )\n\n /**\n * Raised over EVERY loaded order, not the visible ones (AGL-1796). A\n * merchant filtered to \"delivered\" still has a deadline running on a `paid`\n * order, and the evidence window is days long.\n */\n const openDisputes = useMemo(\n () =>\n CommerceModel.summariseOpenDisputes(\n orders.map((order: any) => CommerceModel.liftLegacyOrder(order)),\n ),\n [orders],\n )\n /**\n * 30-day money summary with the prior 30 days behind it (AGL-2136). Over\n * the LOADED window, like every other figure on this card — the query is\n * `limit(200)`, so a store past 200 orders in 60 days is summarising a\n * slice. That is the same bound the analytics card has always had, and\n * making this one silently different would be worse than sharing it.\n */\n const summary = useMemo(\n () => CommerceModel.summarizeOrderWindow(orders, { nowMs: Date.now() }),\n [orders],\n )\n\n const handleExportCsv = useCallback(() => {\n // The rows themselves are built by the pure model helper (AGL-1747) so the\n // column-by-column arithmetic is unit-testable without a Firestore mock.\n const csv = CommerceModel.buildOrdersCsv(visibleOrders, productNames)\n const url = URL.createObjectURL(new Blob([csv], { type: 'text/csv' }))\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = 'orders.csv'\n anchor.click()\n URL.revokeObjectURL(url)\n }, [visibleOrders, productNames])\n\n /**\n * Idempotency key for ONE draft attempt (AGL-1697).\n *\n * Minted lazily on the first create click, NOT per call — two clicks (or a\n * click retried after a lost response) must present ONE key so the server\n * replays the original payment link instead of minting a second order.\n * Retired whenever a content field changes: an edited draft is a different\n * order, and replaying the old one against it would hand the merchant a\n * link priced for fields they no longer see. Answering the shipping-country\n * ask also retires it, which is safe — that refusal is served before the\n * server takes the claim, so the first key was never spent.\n */\n const draftAttemptKey = useRef('')\n useEffect(() => {\n draftAttemptKey.current = ''\n }, [\n draft?.productId,\n draft?.variantId,\n draft?.quantity,\n draft?.email,\n draft?.shipTo,\n ])\n\n const handleDraftCreate = useCallback(async () => {\n if (!draft?.productId) return\n setDraft((prev) => (prev ? { ...prev, busy: true } : prev))\n try {\n if (!draftAttemptKey.current) {\n draftAttemptKey.current =\n globalThis.crypto?.randomUUID?.() ??\n `${Date.now()}-${Math.random().toString(36).slice(2)}`\n }\n const response = await authorizedFetch(\n user,\n '/api/commerce/draft-order',\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n // Stable across a retry of THIS attempt (AGL-1697), so a\n // double-click cannot mint two live payment links.\n 'Idempotency-Key': draftAttemptKey.current,\n },\n body: JSON.stringify({\n hostId,\n productId: draft.productId,\n variantId: draft.variantId || undefined,\n quantity: Number(draft.quantity) || 1,\n email: draft.email || undefined,\n // A request, never an instruction: the server resolves the\n // rates for this country AND restricts the payment link's\n // collectable addresses to it, so declaring one cannot buy a\n // cheaper zone's rate than the address the buyer then enters\n // (AGL-1721).\n ...(draft.shipTo ? { shippingCountry: draft.shipTo } : {}),\n }),\n },\n )\n const payload = await response.json()\n if (!response.ok) {\n // The merchant's rates differ by destination, so the server will not\n // price this order until it knows one (AGL-1792). Reveal the field and\n // let them answer; a store whose rates are the same everywhere, or\n // which configured none, never sends this and never shows it.\n if (payload?.needsShippingCountry) {\n setDraft((prev) =>\n prev\n ? {\n ...prev,\n shipCountries: (\n payload.shippingCountries as string[] | undefined\n )?.length\n ? (payload.shippingCountries as string[])\n : [...CommerceModel.CHECKOUT_SHIPPING_COUNTRIES],\n }\n : prev,\n )\n }\n return void enqueueSnackbar(payload?.error ?? 'Draft order failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n await navigator.clipboard.writeText(payload.url).catch(() => undefined)\n enqueueSnackbar('Draft created — payment link copied', {\n variant: 'success',\n persist: false,\n })\n setDraft(null)\n } finally {\n setDraft((prev) => (prev ? { ...prev, busy: false } : prev))\n }\n }, [draft, user, hostId, enqueueSnackbar])\n\n const selectedOrder =\n (orderDocs ?? []).find((order: any) => order.$id === selectedId) ??\n (seededOrder && seededOrder.$id === selectedId ? seededOrder : null)\n\n /**\n * Shared by the two triggers (AGL-1805) so the empty state and the toolbar\n * cannot drift into opening the dialog with different starting fields.\n */\n const openDraft = useCallback(\n () => setDraft({ productId: '', variantId: '', quantity: '1', email: '' }),\n [],\n )\n\n /** What the draft dialog can actually offer. */\n const selectableProducts = useMemo(\n () => productWindow.rows.filter((product: any) => !product.deletedAt),\n [productWindow],\n )\n\n /*\n * The six columns `/product/commerce` advertises, in the order the mockup\n * shows them — every fact scannable on its own, Channel included.\n */\n const orderColumns = useMemo<GridColDef[]>(\n () => [\n {\n field: 'orderLabel',\n headerName: 'Order',\n flex: 1,\n minWidth: 160,\n renderCell: ({ row }: any) => {\n const extraItems = Math.max(0, (row.lineItems?.length ?? 0) - 1)\n return (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {row.orderLabel}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {extraItems ? `${row.itemName} +${extraItems} more` : row.itemName}\n </Typography>\n </Stack>\n )\n },\n },\n {\n field: 'customerEmail',\n headerName: 'Customer',\n flex: 1,\n minWidth: 180,\n renderCell: ({ row }: any) => (\n <Typography variant=\"body2\" noWrap>\n {row.customerEmail || '—'}\n </Typography>\n ),\n },\n {\n field: 'channelKey',\n headerName: 'Channel',\n width: 120,\n renderCell: ({ row }: any) => (\n <Typography variant=\"body2\">\n {CommerceModel.orderChannelLabel(row.channelKey)}\n </Typography>\n ),\n },\n {\n field: 'netCents',\n headerName: 'Total',\n type: 'number',\n width: 150,\n align: 'right',\n headerAlign: 'right',\n renderCell: ({ row }: any) => (\n <Stack sx={{ alignItems: 'flex-end', minWidth: 0, width: 1 }}>\n <Typography variant=\"body2\">\n {`$${(row.netCents / 100).toFixed(2)}`}\n </Typography>\n {row.refundedCents ? (\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`$${((row.totals?.totalCents ?? row.amountCents ?? 0) / 100).toFixed(2)} less refunds`}\n </Typography>\n ) : null}\n </Stack>\n ),\n },\n {\n field: 'statusKey',\n headerName: 'Status',\n width: 230,\n renderCell: ({ row }: any) => {\n // A lost chargeback leaves `status: 'refunded'` (AGL-1787), so the\n // status pill cannot tell the two apart either — which is why the\n // dispute chip sits beside it rather than being folded into it.\n const dispute: ReturnType<typeof CommerceModel.describeOrderDispute> =\n row.disputeBadge\n return (\n <Stack sx={{ minWidth: 0 }}>\n <Stack direction=\"row\" spacing={0.5} sx={{ alignItems: 'center' }}>\n <Chip\n label={\n CommerceModel.ORDER_STATUS_LABELS[\n row.statusKey as CommerceModel.OrderStatus\n ] ?? row.statusKey\n }\n size=\"small\"\n color={\n CommerceModel.ORDER_STATUS_COLOR[\n row.statusKey as CommerceModel.OrderStatus\n ] ?? 'default'\n }\n variant=\"outlined\"\n />\n {dispute ? (\n <Tooltip title={dispute.detail}>\n <Chip\n label={dispute.label}\n size=\"small\"\n color={DISPUTE_COLOR[dispute.tone]}\n variant=\"filled\"\n />\n </Tooltip>\n ) : null}\n </Stack>\n {/*\n The deadline on the row itself, while the case is open — the\n one fact on this screen that expires.\n */}\n {dispute?.evidenceDaysLeft !== undefined ? (\n <Typography\n variant=\"caption\"\n component=\"div\"\n color={dispute.evidenceDaysLeft < 0 ? 'error' : 'warning.main'}\n >\n {dispute.evidenceDaysLeft < 0\n ? 'Evidence deadline passed'\n : `Evidence due in ${dispute.evidenceDaysLeft} day${\n dispute.evidenceDaysLeft === 1 ? '' : 's'\n }`}\n </Typography>\n ) : null}\n </Stack>\n )\n },\n },\n {\n field: 'createdAtMs',\n headerName: 'Date',\n width: 130,\n renderCell: ({ row }: any) => {\n const createdAt =\n row.createdAt?.toDate?.() ??\n (row.createdAtMs ? new Date(row.createdAtMs) : null)\n return (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {createdAt ? createdAt.toLocaleDateString() : '—'}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {createdAt ? createdAt.toLocaleTimeString() : ''}\n </Typography>\n </Stack>\n )\n },\n },\n ],\n [],\n )\n\n return (\n <CardDisplay\n header={'Orders'}\n help={pluginDocsHelp('commerce', {\n anchor: '#orders-screen',\n excerpt:\n 'Every order with its channel, status and net total. A refunded ' +\n 'order shows what is left, with the gross beneath it.',\n })}\n contentGutterX\n contentGutterY\n >\n {orders.length === 0 ? (\n /*\n * An invitation, not a report (AGL-1805). The \"Draft order\" button\n * used to live in the other arm of this ternary, so the one state a\n * draft order exists for — no sales yet, invoice the customer you\n * already have — was the only state that could not reach it, and\n * every other route into the dialog needs an order to exist first.\n *\n * The filters stay behind deliberately: they belong to a list with\n * rows in it. Export CSV likewise — a header-only file is not\n * something a merchant on day one is looking for.\n */\n <Stack spacing={1} sx={{ alignItems: 'flex-start' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\n 'No orders yet. Storefront sales, POS sales and draft orders ' +\n 'all appear here as they come in.'\n }\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\n 'Selling to someone directly? Draft their order and send them ' +\n 'a payment link.'\n }\n </Typography>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={openDraft}\n >\n {'Draft order'}\n </Button>\n </Stack>\n ) : (\n <Stack spacing={1}>\n {showStats ? (\n <Stack\n direction=\"row\"\n spacing={3}\n sx={{ flexWrap: 'wrap', rowGap: 1 }}\n >\n <CommerceStatTile\n label=\"Revenue · 30d\"\n value={`$${(summary.revenueCents / 100).toFixed(2)}`}\n deltaPct={summary.revenueDeltaPct}\n deltaCaption=\"vs the previous 30 days\"\n />\n <CommerceStatTile\n label=\"Orders · 30d\"\n value={String(summary.orders)}\n deltaPct={summary.ordersDeltaPct}\n deltaCaption=\"vs the previous 30 days\"\n />\n <CommerceStatTile\n label=\"Avg order value\"\n value={`$${(summary.aovCents / 100).toFixed(2)}`}\n deltaPct={summary.aovDeltaPct}\n deltaCaption=\"vs the previous 30 days\"\n />\n </Stack>\n ) : null}\n {openDisputes.count > 0 ? (\n <Alert\n severity={openDisputes.overdue ? 'error' : 'warning'}\n action={\n showingOpenDisputes ? undefined : (\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={() =>\n setClauses(\n upsertListFilterClause(\n clauses,\n OPEN_DISPUTE_CLAUSE.field,\n OPEN_DISPUTE_CLAUSE,\n ),\n )\n }\n >\n {'Show them'}\n </Button>\n )\n }\n >\n <AlertTitle>\n {openDisputes.count === 1\n ? 'A shopper has disputed a charge with their bank'\n : `${openDisputes.count} charges are disputed with the shopper’s bank`}\n </AlertTitle>\n {openDisputes.soonestDaysLeft === undefined\n ? 'Answer in the Stripe dashboard while the case is open — an unanswered dispute is decided for the shopper.'\n : openDisputes.soonestDaysLeft < 0\n ? 'The evidence deadline has passed on at least one of these. Stripe decides an unanswered dispute for the shopper.'\n : `Evidence is due to Stripe in ${openDisputes.soonestDaysLeft} day${\n openDisputes.soonestDaysLeft === 1 ? '' : 's'\n } on the tightest of these. An unanswered dispute is decided for the shopper.`}\n </Alert>\n ) : null}\n {/*\n The buttons stay beside the grid: Export CSV writes the model's\n own order columns (`buildOrdersCsv`) for the rows the filters\n leave, which the grid's own export cannot.\n */}\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', justifyContent: 'flex-end' }}\n >\n <Button size=\"small\" onClick={handleExportCsv}>\n {'Export CSV'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={openDraft}\n >\n {'Draft order'}\n </Button>\n </Stack>\n <ListFilterChips {...listFilter.chipsProps} />\n <ListTable\n aria-label=\"Orders\"\n rows={visibleOrders}\n columns={listFilter.filterColumns(orderColumns)}\n onOpen={(id) => setSelectedId(id)}\n {...listFilter.gridProps}\n initialState={{\n columns: { columnVisibilityModel: ORDER_HIDDEN_COLUMNS },\n }}\n noRowsLabel=\"No orders match these filters\"\n />\n {orderWindow.truncated ? (\n /*\n * Where the filters, the search and the export stop. All run over\n * what was read, so a store past this window would otherwise see\n * a status filter return nothing and read it as having no such\n * orders.\n */\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Showing the ${ORDERS_WINDOW} most recent orders. Filters and Export CSV cover these.`}\n </Typography>\n ) : null}\n </Stack>\n )}\n {selectedOrder ? (\n <OrderDetailDialog\n hostId={hostId}\n order={selectedOrder}\n onClose={() => setSelectedId(null)}\n />\n ) : null}\n <Dialog\n open={Boolean(draft)}\n onClose={() => setDraft(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>{'Draft order'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <TextField\n label=\"Product\"\n value={draft?.productId ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev\n ? { ...prev, productId: event.target.value, variantId: '' }\n : prev,\n )\n }\n size=\"small\"\n select\n sx={{ mt: 1 }}\n /*\n * The second dead end behind the first (AGL-1805): a store with\n * no orders may have no products either, and a draft order is\n * composed from one — so \"Create & copy link\" stays disabled. Say\n * why, rather than opening an empty menu onto nothing.\n */\n helperText={\n selectableProducts.length === 0\n ? 'Add a product to this store first — a draft order is built from one.'\n : undefined\n }\n >\n {selectableProducts.map((product: any) => (\n <MenuItem key={product.$id} value={product.$id}>\n {product.name ?? product.$id}\n </MenuItem>\n ))}\n </TextField>\n {(() => {\n const product = productWindow.rows.find(\n (item: any) => item.$id === draft?.productId,\n )\n const variants = product\n ? CommerceModel.liftLegacyProduct(product).variants\n : []\n return variants.length > 1 ? (\n <TextField\n label=\"Variant\"\n value={draft?.variantId ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, variantId: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n >\n {variants.map((variant) => (\n <MenuItem key={variant.id} value={variant.id}>\n {`${Object.values(variant.options ?? {}).join(' / ') || 'Default'} — $${variant.priceUsd}`}\n </MenuItem>\n ))}\n </TextField>\n ) : null\n })()}\n <TextField\n label=\"Quantity\"\n value={draft?.quantity ?? '1'}\n onChange={(event) =>\n setDraft((prev) =>\n prev\n ? {\n ...prev,\n quantity: event.target.value.replace(/[^0-9]/g, ''),\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'numeric' } }}\n />\n <TextField\n label=\"Buyer email (optional)\"\n value={draft?.email ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, email: event.target.value } : prev,\n )\n }\n size=\"small\"\n />\n {draft?.shipCountries?.length ? (\n <TextField\n label=\"Ships to\"\n value={draft?.shipTo ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, shipTo: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n helperText={\n 'This store’s shipping rates differ by destination, so the ' +\n 'payment link has to be priced for one.'\n }\n >\n {draft.shipCountries.map((code) => (\n <MenuItem key={code} value={code}>\n {CommerceModel.CHECKOUT_SHIPPING_COUNTRY_NAMES[code] ?? code}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setDraft(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n !draft?.productId ||\n draft?.busy ||\n // Once asked, the answer is required: retrying without one is\n // refused again, so the button would only look broken.\n Boolean(draft?.shipCountries?.length && !draft?.shipTo)\n }\n onClick={handleDraftCreate}\n >\n {'Create & copy link'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nHostOrdersCard.displayName = 'HostOrdersCard'\n\nexport default HostOrdersCard\n"],"names":["CommerceModel","CardDisplay","ListFilterChips","ListTable","hiddenFilterVisibility","inMemoryListField","upsertListFilterClause","useListRowsFilter","useSnackbar","checkEntitlement","ORDERS_CUSTOMER_PARAM","ORDERS_ORDER_PARAM","pluginDocsHelp","Alert","AlertTitle","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Tooltip","Typography","collection","doc","getDoc","limit","orderBy","query","useSearchParams","useCallback","useEffect","useMemo","useRef","useState","ceilingedWindow","collectionCeiling","useFirestore","useOrgPlan","useUser","useFirestoreCollection","authorizedFetch","CommerceStatTile","OrderDetailDialog","DISPUTE_COLOR","ORDERS_WINDOW","PRODUCT_NAME_WINDOW","ORDER_FILTER_FIELDS","tokensPath","verbatimTokens","ORDER_FILTER_HEADERS","orderLabel","customerEmail","channelKey","statusKey","createdAtMs","productIds","disputeKey","ORDER_STATUS_OPTIONS","Object","keys","ORDER_STATUS_LABELS","map","status","value","label","ORDER_CHANNEL_OPTIONS","ORDER_CHANNEL_LABELS","channel","ORDER_DISPUTE_OPTIONS","ORDER_SEARCH_FIELDS","ORDER_HIDDEN_COLUMNS","OPEN_DISPUTE_CLAUSE","field","op","HostOrdersCard","props","draft","hostId","firestore","data","orderDocs","idField","productDocs","orderWindow","undefined","productWindow","productNames","product","rows","$id","name","org","ready","orgReady","showStats","orders","searchParams","clauses","setClauses","customer","get","trim","selectedId","setSelectedId","seededOrderId","seededOrder","setSeededOrder","seededOpened","current","some","order","then","snapshot","id","exists","catch","setDraft","user","enqueueSnackbar","orderRows","lifted","liftLegacyOrder","dispute","describeOrderDispute","Set","lineItems","line","productId","filter","formatOrderNumber","itemName","netCents","orderNetCents","disputeBadge","orderHasOpenDispute","tone","productOptions","String","filterOptions","listFilter","fields","options","headers","search","onChange","visibleOrders","showingOpenDisputes","clause","openDisputes","summariseOpenDisputes","summary","summarizeOrderWindow","nowMs","Date","now","handleExportCsv","csv","buildOrdersCsv","url","URL","createObjectURL","Blob","type","anchor","document","createElement","href","download","click","revokeObjectURL","draftAttemptKey","variantId","quantity","email","shipTo","handleDraftCreate","prev","busy","globalThis","crypto","randomUUID","Math","random","toString","slice","response","method","body","JSON","stringify","Number","shippingCountry","payload","json","ok","needsShippingCountry","shipCountries","shippingCountries","length","CHECKOUT_SHIPPING_COUNTRIES","error","variant","allowDuplicate","navigator","clipboard","writeText","persist","selectedOrder","find","openDraft","selectableProducts","deletedAt","orderColumns","headerName","flex","minWidth","renderCell","row","extraItems","max","sx","noWrap","color","width","orderChannelLabel","align","headerAlign","alignItems","toFixed","refundedCents","totals","totalCents","amountCents","direction","spacing","size","ORDER_STATUS_COLOR","title","detail","evidenceDaysLeft","component","createdAt","toDate","toLocaleDateString","toLocaleTimeString","header","help","excerpt","contentGutterX","contentGutterY","onClick","flexWrap","rowGap","revenueCents","deltaPct","revenueDeltaPct","deltaCaption","ordersDeltaPct","aovCents","aovDeltaPct","count","severity","overdue","action","soonestDaysLeft","justifyContent","chipsProps","aria-label","columns","filterColumns","onOpen","gridProps","initialState","columnVisibilityModel","noRowsLabel","truncated","onClose","open","Boolean","maxWidth","fullWidth","display","flexDirection","gap","event","target","select","mt","helperText","item","variants","liftLegacyProduct","values","join","priceUsd","replace","slotProps","htmlInput","inputMode","code","CHECKOUT_SHIPPING_COUNTRY_NAMES","disabled","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/host-orders-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\n'use client'\n\nimport * as CommerceModel from '../../model'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport {\n ListQueryNotices,\n listQueryRefusals,\n} from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { hiddenFilterVisibility } from '@aglyn/shared-ui-jsx/const/list-filter'\nimport {\n type ListFilterClause,\n listFilterGridColumns,\n upsertListFilterClause,\n} from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n checkEntitlement,\n ORDERS_CUSTOMER_PARAM,\n ORDERS_ORDER_PARAM,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport {\n Alert,\n AlertTitle,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport {\n collection,\n doc,\n getDoc,\n getDocs,\n limit,\n orderBy,\n query,\n type QueryDocumentSnapshot,\n startAfter,\n where,\n} from 'firebase/firestore'\nimport { useSearchParams } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n ceilingedWindow,\n collectionCeiling,\n useFirestore,\n useOrgPlan,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport {\n listQueryConstraints,\n useListQuery,\n} from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\n\nimport {\n OPEN_DISPUTE_CLAUSE,\n ORDER_CHANNEL_OPTIONS,\n ORDER_DISPUTE_OPTIONS,\n ORDER_LIST_FIELDS,\n ORDER_LIST_HEADERS,\n ORDER_LIST_QUERY,\n ORDER_LIST_SELECT_FIELDS,\n ORDER_STATUS_OPTIONS,\n ordersCustomerClause,\n} from '../../constants/orders-list-query'\nimport CommerceStatTile from './commerce-stat-tile.component'\nimport OrderDetailDialog, {\n DISPUTE_COLOR,\n} from './order-detail-dialog.component'\n\n/**\n * The products the card reads to NAME a legacy row and the CSV, to offer the\n * Product filter's choices and to fill the draft dialog's picker. A picker's\n * reach, not the list's: the orders themselves are paged by their query and\n * the Product filter asks it for any product id, named here or not.\n */\nconst PRODUCT_NAME_WINDOW = 100\n\n/**\n * How many open disputes the banner reads. Its own query, `disputeKey ==\n * 'open'`, so a deadline is raised whatever the list is filtered to and\n * however deep in the store the order sits; a store past this many open\n * cases is told \"more than\".\n */\nconst OPEN_DISPUTE_CEILING = 50\n\n/**\n * The money tiles read the last sixty days (thirty, and the thirty before it)\n * on their own bounded query. They are analytics over a period, not a filter\n * over the list, and share the analytics card's bound.\n */\nconst STATS_WINDOW_MS = 60 * 24 * 60 * 60 * 1000\nconst STATS_ORDER_CEILING = 500\n\n/**\n * Export CSV walks the list's own query, page by page, up to this many\n * orders, and says so when the matches run past it.\n */\nconst ORDERS_EXPORT_CEILING = 5000\nconst ORDERS_EXPORT_PAGE = 500\n\n/** The columns the grid draws; every other declared field is a hidden filter column. */\nconst ORDER_VISIBLE_COLUMNS = [\n 'orderLabel',\n 'customerEmail',\n 'channelKey',\n 'statusKey',\n 'createdAtMs',\n]\nconst ORDER_HIDDEN_COLUMNS = hiddenFilterVisibility(\n ORDER_LIST_FIELDS,\n ORDER_VISIBLE_COLUMNS,\n)\n\nexport interface HostOrdersCardProps {\n hostId: string\n}\n\n/**\n * Orders console (AGL-287): filterable list over webhook-written order\n * docs with a detail dialog (timeline, fulfill, refund, cancel, notes,\n * packing slip) and draft orders that send the buyer a payment link.\n *\n * ## Every filter and the search are on the query (AGL-3321)\n *\n * The list used to read the newest two hundred orders and match its Filters\n * panel and search over them, so a Status filter on a busy store answered\n * \"none\" about every order past the two hundredth. Now each clause and the\n * search word are predicates on ONE Firestore query (`ORDER_LIST_QUERY`),\n * over the fields every writer stamps (`orderListFields`), and the footer\n * pages that query's answer. A combination one query cannot hold is refused\n * by name above the grid and never applied to some rows only.\n */\nexport function HostOrdersCard(props: HostOrdersCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const ordersRef = useMemo(\n () => collection(firestore, 'hosts', hostId, 'orders'),\n [firestore, hostId],\n )\n const { data: productDocs } = useFirestoreCollection<any>(\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'products'),\n PRODUCT_NAME_WINDOW,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const productWindow = useMemo(\n () => ceilingedWindow<any>(productDocs ?? undefined, PRODUCT_NAME_WINDOW),\n [productDocs],\n )\n const productNames = useMemo(() => {\n const map: Record<string, string> = {}\n for (const product of productWindow.rows) {\n map[product.$id] = product.name ?? product.$id\n }\n return map\n }, [productWindow])\n\n /**\n * The money tiles are the `commerceAnalytics` surface (AGL-1938,\n * AGL-2056), and this is the third place they appear. Rendering them\n * ungated here would undo both of those passes and hand a Starter org\n * the Pro figures one tab away from the upgrade prompt that refuses\n * them. The TABLE below is not gated — a list of your own orders is not\n * a paid feature.\n */\n const { org, ready: orgReady } = useOrgPlan(hostId)\n const showStats =\n orgReady && checkEntitlement(org as never, 'commerceAnalytics')\n\n /*\n * The grid's clauses (AGL-96, AGL-3317), held here so two things outside\n * the grid can set one: the CRM's contact page links with the buyer's\n * address to answer \"what has this customer ordered\" (AGL-2622) — a whole\n * address is that buyer, a domain every buyer at a company\n * (`ordersCustomerClause`) — and the dispute banner's \"Show them\".\n */\n const searchParams = useSearchParams()\n const [clauses, setClauses] = useState<ListFilterClause[]>(() => {\n const customer = ordersCustomerClause(\n searchParams?.get(ORDERS_CUSTOMER_PARAM) ?? '',\n )\n return customer ? [customer] : []\n })\n const gridFilter = useListGridFilter({\n clauses,\n onChange: setClauses,\n selectFields: ORDER_LIST_SELECT_FIELDS,\n })\n const filtering =\n clauses.length > 0 || gridFilter.searchWords.some((word) => word.trim())\n\n /*\n * The open disputes, on their OWN query (AGL-1796). Raised whatever the\n * list is filtered to — a merchant filtered to \"delivered\" still has a\n * deadline running on a `paid` order, and the evidence window is days —\n * and however far back the order is. Asked before the list so the list's\n * plan is the one a spec reads last.\n */\n const {\n rows: disputedRows,\n hasMore: moreDisputes,\n } = useListQuery<any>({\n collection: ordersRef,\n declaration: ORDER_LIST_QUERY,\n request: { clauses: [OPEN_DISPUTE_CLAUSE] },\n deps: [firestore, hostId],\n idField: '$id',\n pageSize: OPEN_DISPUTE_CEILING,\n // A dispute that closes leaves this query; confirm it before believing it.\n confirmDisappearances: true,\n })\n\n /*\n * THE LIST: every clause and the search word on one query, newest first,\n * paged by the footer. A status, a dispute or a paid draft's address can\n * move an order out of the filtered query mid-session, so a disappearance\n * is confirmed against the server before it is believed (AGL-1196).\n */\n const {\n rows: orders,\n data: loadedOrders,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n plan,\n status: listStatus,\n } = useListQuery<any>({\n collection: ordersRef,\n declaration: ORDER_LIST_QUERY,\n request: { clauses, search: gridFilter.searchWords },\n deps: [firestore, hostId],\n idField: '$id',\n confirmDisappearances: true,\n })\n\n const [selectedId, setSelectedId] = useState<string | null>(null)\n /*\n * An order named in the URL opens in its dialog on arrival (AGL-2622). A\n * contact's timeline names the order that made the person a customer,\n * and the address it links to is this list with `?order={id}`. The list\n * shows one page, and an order from last year is not on it, so the\n * document is read once by id and held beside the page for the dialog; an\n * order that IS loaded is found there first and the read is skipped. Once\n * per id — the ref keeps a re-render from reopening a dialog the merchant\n * has since closed — and the landing read is judged against the ref\n * rather than an effect cleanup, because the `setSelectedId` above\n * re-renders before the read lands and a cleanup keyed on any dep would\n * cancel the answer to the question just asked.\n */\n const seededOrderId = searchParams?.get(ORDERS_ORDER_PARAM) ?? null\n const [seededOrder, setSeededOrder] = useState<any | null>(null)\n const seededOpened = useRef<string | null>(null)\n useEffect(() => {\n if (!seededOrderId || seededOpened.current === seededOrderId) return\n if (listStatus === 'loading') return\n seededOpened.current = seededOrderId\n setSelectedId(seededOrderId)\n if (loadedOrders.some((order: any) => order.$id === seededOrderId)) return\n void getDoc(doc(firestore, 'hosts', hostId, 'orders', seededOrderId))\n .then((snapshot) => {\n if (seededOpened.current !== snapshot.id || !snapshot.exists()) return\n setSeededOrder({ ...snapshot.data(), $id: snapshot.id })\n })\n .catch(() => undefined)\n }, [seededOrderId, loadedOrders, listStatus, firestore, hostId])\n const [draft, setDraft] = useState<{\n productId: string\n variantId: string\n quantity: string\n email: string\n /** Destination the merchant declared, once they have been asked. */\n shipTo?: string\n /**\n * Revealed only when the server refuses for want of a destination\n * (AGL-1792). A merchant whose rates are the same everywhere — or who\n * configured none — is never asked and never sees this field.\n */\n shipCountries?: string[]\n busy?: boolean\n } | null>(null)\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n /**\n * Each order on the page with the values the grid DRAWS. What it filters\n * by is on the query; this is presentation only. `liftLegacyOrder` names\n * a status and channel on a row that predates them, and the item name\n * reads the line items first (AGL-1747): the flat `productId` is written\n * only by the two buy-now Stripe paths.\n */\n const orderRows = useMemo(\n () =>\n orders.map((order: any) => {\n const lifted = CommerceModel.liftLegacyOrder(order)\n return {\n ...order,\n orderLabel: CommerceModel.formatOrderNumber(lifted, order.$id),\n itemName:\n lifted.lineItems?.[0]?.name ??\n productNames[order.productId] ??\n order.productId ??\n '',\n statusKey: lifted.status,\n channelKey: lifted.channel ?? 'online',\n netCents: CommerceModel.orderNetCents(lifted),\n disputeBadge: CommerceModel.describeOrderDispute(lifted),\n }\n }),\n [orders, productNames],\n )\n const productOptions = useMemo(\n () =>\n productWindow.rows.map((product: any) => ({\n value: String(product.$id),\n label: String(product.name ?? product.$id),\n })),\n [productWindow],\n )\n const filterOptions = useMemo(\n () => ({\n statusKey: ORDER_STATUS_OPTIONS,\n channelKey: ORDER_CHANNEL_OPTIONS,\n productIds: productOptions,\n disputeKey: ORDER_DISPUTE_OPTIONS,\n }),\n [productOptions],\n )\n const refused = useMemo(\n () =>\n listQueryRefusals(plan.refused, {\n fields: ORDER_LIST_FIELDS,\n headers: ORDER_LIST_HEADERS,\n options: filterOptions,\n }),\n [plan, filterOptions],\n )\n const showingOpenDisputes = clauses.some(\n (clause) =>\n clause.field === OPEN_DISPUTE_CLAUSE.field &&\n clause.op === OPEN_DISPUTE_CLAUSE.op &&\n clause.value === OPEN_DISPUTE_CLAUSE.value,\n )\n\n const openDisputes = useMemo(\n () =>\n CommerceModel.summariseOpenDisputes(\n disputedRows.map((order: any) => CommerceModel.liftLegacyOrder(order)),\n ),\n [disputedRows],\n )\n\n /*\n * 30-day money summary with the prior 30 days behind it (AGL-2136).\n * ANALYTICS, NOT A FILTER: the tiles describe a period, not the list, so\n * they keep their own bounded read — sixty days by `createdAtMs`, the\n * newest `STATS_ORDER_CEILING` of them, read only when the tiles show.\n * Range and order are the same field, so it needs no composite.\n */\n const statsSince = useMemo(() => Date.now() - STATS_WINDOW_MS, [])\n const { data: statsDocs } = useFirestoreCollection<any>(\n () =>\n showStats\n ? query(\n ordersRef,\n where('createdAtMs', '>=', statsSince),\n orderBy('createdAtMs', 'desc'),\n limit(STATS_ORDER_CEILING + 1),\n )\n : null,\n [firestore, hostId, showStats, statsSince],\n { idField: '$id' },\n )\n const statsWindow = useMemo(\n () => ceilingedWindow<any>(statsDocs ?? undefined, STATS_ORDER_CEILING),\n [statsDocs],\n )\n const summary = useMemo(\n () =>\n CommerceModel.summarizeOrderWindow(statsWindow.rows, { nowMs: Date.now() }),\n [statsWindow],\n )\n\n /**\n * Export CSV writes what the QUERY matches — every clause and the search\n * the grid is showing — not the page on screen. It walks the list's own\n * plan in pages of `ORDERS_EXPORT_PAGE` up to `ORDERS_EXPORT_CEILING`, and\n * says so when the matches run past it.\n */\n const [exporting, setExporting] = useState(false)\n const handleExportCsv = useCallback(async () => {\n setExporting(true)\n try {\n const matched: any[] = []\n let cursor: QueryDocumentSnapshot | null = null\n let more = true\n while (more && matched.length < ORDERS_EXPORT_CEILING) {\n const take = Math.min(\n ORDERS_EXPORT_PAGE,\n ORDERS_EXPORT_CEILING - matched.length,\n )\n // One past the page: whether more match is a fact, not a guess.\n const snapshot = await getDocs(\n query(\n ordersRef,\n ...listQueryConstraints(plan),\n ...(cursor ? [startAfter(cursor)] : []),\n limit(take + 1),\n ),\n )\n const docs = snapshot.docs.slice(0, take)\n for (const entry of docs) matched.push({ ...entry.data(), $id: entry.id })\n more = snapshot.docs.length > take\n cursor = docs[docs.length - 1] ?? null\n if (!cursor) more = false\n }\n // The rows themselves are built by the pure model helper (AGL-1747) so\n // the column-by-column arithmetic is unit-testable without Firestore.\n const csv = CommerceModel.buildOrdersCsv(matched, productNames)\n const url = URL.createObjectURL(new Blob([csv], { type: 'text/csv' }))\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = 'orders.csv'\n anchor.click()\n URL.revokeObjectURL(url)\n if (more) {\n enqueueSnackbar(\n `Exported the ${ORDERS_EXPORT_CEILING.toLocaleString('en-US')} newest matching orders. ` +\n 'Narrow the filters to export the rest.',\n { variant: 'warning', allowDuplicate: true },\n )\n }\n } catch (error) {\n console.error('Orders export failed', error)\n enqueueSnackbar('Export failed — try again', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setExporting(false)\n }\n }, [ordersRef, plan, productNames, enqueueSnackbar])\n\n /**\n * Idempotency key for ONE draft attempt (AGL-1697).\n *\n * Minted lazily on the first create click, NOT per call — two clicks (or a\n * click retried after a lost response) must present ONE key so the server\n * replays the original payment link instead of minting a second order.\n * Retired whenever a content field changes: an edited draft is a different\n * order, and replaying the old one against it would hand the merchant a\n * link priced for fields they no longer see. Answering the shipping-country\n * ask also retires it, which is safe — that refusal is served before the\n * server takes the claim, so the first key was never spent.\n */\n const draftAttemptKey = useRef('')\n useEffect(() => {\n draftAttemptKey.current = ''\n }, [\n draft?.productId,\n draft?.variantId,\n draft?.quantity,\n draft?.email,\n draft?.shipTo,\n ])\n\n const handleDraftCreate = useCallback(async () => {\n if (!draft?.productId) return\n setDraft((prev) => (prev ? { ...prev, busy: true } : prev))\n try {\n if (!draftAttemptKey.current) {\n draftAttemptKey.current =\n globalThis.crypto?.randomUUID?.() ??\n `${Date.now()}-${Math.random().toString(36).slice(2)}`\n }\n const response = await authorizedFetch(\n user,\n '/api/commerce/draft-order',\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n // Stable across a retry of THIS attempt (AGL-1697), so a\n // double-click cannot mint two live payment links.\n 'Idempotency-Key': draftAttemptKey.current,\n },\n body: JSON.stringify({\n hostId,\n productId: draft.productId,\n variantId: draft.variantId || undefined,\n quantity: Number(draft.quantity) || 1,\n email: draft.email || undefined,\n // A request, never an instruction: the server resolves the\n // rates for this country AND restricts the payment link's\n // collectable addresses to it, so declaring one cannot buy a\n // cheaper zone's rate than the address the buyer then enters\n // (AGL-1721).\n ...(draft.shipTo ? { shippingCountry: draft.shipTo } : {}),\n }),\n },\n )\n const payload = await response.json()\n if (!response.ok) {\n // The merchant's rates differ by destination, so the server will not\n // price this order until it knows one (AGL-1792). Reveal the field and\n // let them answer; a store whose rates are the same everywhere, or\n // which configured none, never sends this and never shows it.\n if (payload?.needsShippingCountry) {\n setDraft((prev) =>\n prev\n ? {\n ...prev,\n shipCountries: (\n payload.shippingCountries as string[] | undefined\n )?.length\n ? (payload.shippingCountries as string[])\n : [...CommerceModel.CHECKOUT_SHIPPING_COUNTRIES],\n }\n : prev,\n )\n }\n return void enqueueSnackbar(payload?.error ?? 'Draft order failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n await navigator.clipboard.writeText(payload.url).catch(() => undefined)\n enqueueSnackbar('Draft created — payment link copied', {\n variant: 'success',\n persist: false,\n })\n setDraft(null)\n } finally {\n setDraft((prev) => (prev ? { ...prev, busy: false } : prev))\n }\n }, [draft, user, hostId, enqueueSnackbar])\n\n const selectedOrder =\n loadedOrders.find((order: any) => order.$id === selectedId) ??\n (seededOrder && seededOrder.$id === selectedId ? seededOrder : null)\n\n /**\n * Shared by the two triggers (AGL-1805) so the empty state and the toolbar\n * cannot drift into opening the dialog with different starting fields.\n */\n const openDraft = useCallback(\n () => setDraft({ productId: '', variantId: '', quantity: '1', email: '' }),\n [],\n )\n\n /** What the draft dialog can actually offer. */\n const selectableProducts = useMemo(\n () => productWindow.rows.filter((product: any) => !product.deletedAt),\n [productWindow],\n )\n\n /*\n * The six columns `/product/commerce` advertises, in the order the mockup\n * shows them — every fact scannable on its own, Channel included.\n */\n const orderColumns = useMemo<GridColDef[]>(\n () => [\n {\n field: 'orderLabel',\n headerName: 'Order',\n flex: 1,\n minWidth: 160,\n renderCell: ({ row }: any) => {\n const extraItems = Math.max(0, (row.lineItems?.length ?? 0) - 1)\n return (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {row.orderLabel}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {extraItems ? `${row.itemName} +${extraItems} more` : row.itemName}\n </Typography>\n </Stack>\n )\n },\n },\n {\n field: 'customerEmail',\n headerName: 'Customer',\n flex: 1,\n minWidth: 180,\n renderCell: ({ row }: any) => (\n <Typography variant=\"body2\" noWrap>\n {row.customerEmail || '—'}\n </Typography>\n ),\n },\n {\n field: 'channelKey',\n headerName: 'Channel',\n width: 120,\n renderCell: ({ row }: any) => (\n <Typography variant=\"body2\">\n {CommerceModel.orderChannelLabel(row.channelKey)}\n </Typography>\n ),\n },\n {\n field: 'netCents',\n headerName: 'Total',\n type: 'number',\n width: 150,\n align: 'right',\n headerAlign: 'right',\n renderCell: ({ row }: any) => (\n <Stack sx={{ alignItems: 'flex-end', minWidth: 0, width: 1 }}>\n <Typography variant=\"body2\">\n {`$${(row.netCents / 100).toFixed(2)}`}\n </Typography>\n {row.refundedCents ? (\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`$${((row.totals?.totalCents ?? row.amountCents ?? 0) / 100).toFixed(2)} less refunds`}\n </Typography>\n ) : null}\n </Stack>\n ),\n },\n {\n field: 'statusKey',\n headerName: 'Status',\n width: 230,\n renderCell: ({ row }: any) => {\n // A lost chargeback leaves `status: 'refunded'` (AGL-1787), so the\n // status pill cannot tell the two apart either — which is why the\n // dispute chip sits beside it rather than being folded into it.\n const dispute: ReturnType<typeof CommerceModel.describeOrderDispute> =\n row.disputeBadge\n return (\n <Stack sx={{ minWidth: 0 }}>\n <Stack direction=\"row\" spacing={0.5} sx={{ alignItems: 'center' }}>\n <Chip\n label={\n CommerceModel.ORDER_STATUS_LABELS[\n row.statusKey as CommerceModel.OrderStatus\n ] ?? row.statusKey\n }\n size=\"small\"\n color={\n CommerceModel.ORDER_STATUS_COLOR[\n row.statusKey as CommerceModel.OrderStatus\n ] ?? 'default'\n }\n variant=\"outlined\"\n />\n {dispute ? (\n <Tooltip title={dispute.detail}>\n <Chip\n label={dispute.label}\n size=\"small\"\n color={DISPUTE_COLOR[dispute.tone]}\n variant=\"filled\"\n />\n </Tooltip>\n ) : null}\n </Stack>\n {/*\n The deadline on the row itself, while the case is open — the\n one fact on this screen that expires.\n */}\n {dispute?.evidenceDaysLeft !== undefined ? (\n <Typography\n variant=\"caption\"\n component=\"div\"\n color={dispute.evidenceDaysLeft < 0 ? 'error' : 'warning.main'}\n >\n {dispute.evidenceDaysLeft < 0\n ? 'Evidence deadline passed'\n : `Evidence due in ${dispute.evidenceDaysLeft} day${\n dispute.evidenceDaysLeft === 1 ? '' : 's'\n }`}\n </Typography>\n ) : null}\n </Stack>\n )\n },\n },\n {\n field: 'createdAtMs',\n headerName: 'Date',\n width: 130,\n renderCell: ({ row }: any) => {\n const createdAt =\n row.createdAt?.toDate?.() ??\n (row.createdAtMs ? new Date(row.createdAtMs) : null)\n return (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {createdAt ? createdAt.toLocaleDateString() : '—'}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {createdAt ? createdAt.toLocaleTimeString() : ''}\n </Typography>\n </Stack>\n )\n },\n },\n ],\n [],\n )\n\n\n const columns = useMemo(\n () =>\n listFilterGridColumns(\n orderColumns,\n ORDER_LIST_FIELDS,\n filterOptions,\n ORDER_LIST_HEADERS,\n ),\n [orderColumns, filterOptions],\n )\n\n /*\n * The store is empty only when an UNFILTERED first page came back empty; a\n * filter or search that matches nothing keeps the grid, its chips and its\n * panel, so the reader can take the filter back off.\n */\n const empty =\n listStatus === 'success' && !filtering && page === 0 && orders.length === 0\n\n return (\n <CardDisplay\n header={'Orders'}\n help={pluginDocsHelp('commerce', {\n anchor: '#orders-screen',\n excerpt:\n 'Every order with its channel, status and net total. A refunded ' +\n 'order shows what is left, with the gross beneath it.',\n })}\n contentGutterX\n contentGutterY\n >\n {empty ? (\n /*\n * An invitation, not a report (AGL-1805). The \"Draft order\" button\n * used to live in the other arm of this ternary, so the one state a\n * draft order exists for — no sales yet, invoice the customer you\n * already have — was the only state that could not reach it, and\n * every other route into the dialog needs an order to exist first.\n *\n * The filters stay behind deliberately: they belong to a list with\n * rows in it. Export CSV likewise — a header-only file is not\n * something a merchant on day one is looking for.\n */\n <Stack spacing={1} sx={{ alignItems: 'flex-start' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\n 'No orders yet. Storefront sales, POS sales and draft orders ' +\n 'all appear here as they come in.'\n }\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\n 'Selling to someone directly? Draft their order and send them ' +\n 'a payment link.'\n }\n </Typography>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={openDraft}\n >\n {'Draft order'}\n </Button>\n </Stack>\n ) : (\n <Stack spacing={1}>\n {showStats ? (\n <Stack\n direction=\"row\"\n spacing={3}\n sx={{ flexWrap: 'wrap', rowGap: 1 }}\n >\n <CommerceStatTile\n label=\"Revenue · 30d\"\n value={`$${(summary.revenueCents / 100).toFixed(2)}`}\n deltaPct={summary.revenueDeltaPct}\n deltaCaption=\"vs the previous 30 days\"\n />\n <CommerceStatTile\n label=\"Orders · 30d\"\n value={String(summary.orders)}\n deltaPct={summary.ordersDeltaPct}\n deltaCaption=\"vs the previous 30 days\"\n />\n <CommerceStatTile\n label=\"Avg order value\"\n value={`$${(summary.aovCents / 100).toFixed(2)}`}\n deltaPct={summary.aovDeltaPct}\n deltaCaption=\"vs the previous 30 days\"\n />\n </Stack>\n ) : null}\n {showStats && statsWindow.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Figures count the ${STATS_ORDER_CEILING} most recent orders of the last 60 days.`}\n </Typography>\n ) : null}\n {openDisputes.count > 0 ? (\n <Alert\n severity={openDisputes.overdue ? 'error' : 'warning'}\n action={\n showingOpenDisputes ? undefined : (\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={() =>\n setClauses(\n upsertListFilterClause(\n clauses,\n OPEN_DISPUTE_CLAUSE.field,\n OPEN_DISPUTE_CLAUSE,\n ),\n )\n }\n >\n {'Show them'}\n </Button>\n )\n }\n >\n <AlertTitle>\n {moreDisputes\n ? `More than ${OPEN_DISPUTE_CEILING} charges are disputed with the shopper’s bank`\n : openDisputes.count === 1\n ? 'A shopper has disputed a charge with their bank'\n : `${openDisputes.count} charges are disputed with the shopper’s bank`}\n </AlertTitle>\n {openDisputes.soonestDaysLeft === undefined\n ? 'Answer in the Stripe dashboard while the case is open — an unanswered dispute is decided for the shopper.'\n : openDisputes.soonestDaysLeft < 0\n ? 'The evidence deadline has passed on at least one of these. Stripe decides an unanswered dispute for the shopper.'\n : `Evidence is due to Stripe in ${openDisputes.soonestDaysLeft} day${\n openDisputes.soonestDaysLeft === 1 ? '' : 's'\n } on the tightest of these. An unanswered dispute is decided for the shopper.`}\n </Alert>\n ) : null}\n {/*\n The buttons stay beside the grid: Export CSV writes the model's\n own order columns (`buildOrdersCsv`) for every order the query\n matches, which the grid's own export — one page — cannot.\n */}\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', justifyContent: 'flex-end' }}\n >\n <Button\n size=\"small\"\n onClick={() => void handleExportCsv()}\n disabled={exporting}\n >\n {'Export CSV'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={openDraft}\n >\n {'Draft order'}\n </Button>\n </Stack>\n <ListFilterChips\n fields={ORDER_LIST_FIELDS}\n headers={ORDER_LIST_HEADERS}\n clauses={clauses}\n onChange={setClauses}\n options={filterOptions}\n />\n <ListQueryNotices refused={refused} notices={plan.notices} />\n <ListTable\n aria-label=\"Orders\"\n rows={orderRows}\n columns={columns}\n onOpen={(id) => setSelectedId(id)}\n loading={listStatus === 'loading'}\n /*\n * The grid must NOT also filter, search or sort: the query\n * answers all three, and the list has one order — newest first —\n * which the query already holds.\n */\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n sortingMode=\"server\"\n disableColumnSorting\n // `ListPagination` below pages the query.\n hideFooter\n initialState={{\n columns: { columnVisibilityModel: ORDER_HIDDEN_COLUMNS },\n }}\n noRowsLabel=\"No orders match these filters\"\n />\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={orderRows.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n )}\n {selectedOrder ? (\n <OrderDetailDialog\n hostId={hostId}\n order={selectedOrder}\n onClose={() => setSelectedId(null)}\n />\n ) : null}\n <Dialog\n open={Boolean(draft)}\n onClose={() => setDraft(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>{'Draft order'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <TextField\n label=\"Product\"\n value={draft?.productId ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev\n ? { ...prev, productId: event.target.value, variantId: '' }\n : prev,\n )\n }\n size=\"small\"\n select\n sx={{ mt: 1 }}\n /*\n * The second dead end behind the first (AGL-1805): a store with\n * no orders may have no products either, and a draft order is\n * composed from one — so \"Create & copy link\" stays disabled. Say\n * why, rather than opening an empty menu onto nothing.\n */\n helperText={\n selectableProducts.length === 0\n ? 'Add a product to this store first — a draft order is built from one.'\n : undefined\n }\n >\n {selectableProducts.map((product: any) => (\n <MenuItem key={product.$id} value={product.$id}>\n {product.name ?? product.$id}\n </MenuItem>\n ))}\n </TextField>\n {(() => {\n const product = productWindow.rows.find(\n (item: any) => item.$id === draft?.productId,\n )\n const variants = product\n ? CommerceModel.liftLegacyProduct(product).variants\n : []\n return variants.length > 1 ? (\n <TextField\n label=\"Variant\"\n value={draft?.variantId ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, variantId: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n >\n {variants.map((variant) => (\n <MenuItem key={variant.id} value={variant.id}>\n {`${Object.values(variant.options ?? {}).join(' / ') || 'Default'} — $${variant.priceUsd}`}\n </MenuItem>\n ))}\n </TextField>\n ) : null\n })()}\n <TextField\n label=\"Quantity\"\n value={draft?.quantity ?? '1'}\n onChange={(event) =>\n setDraft((prev) =>\n prev\n ? {\n ...prev,\n quantity: event.target.value.replace(/[^0-9]/g, ''),\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'numeric' } }}\n />\n <TextField\n label=\"Buyer email (optional)\"\n value={draft?.email ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, email: event.target.value } : prev,\n )\n }\n size=\"small\"\n />\n {draft?.shipCountries?.length ? (\n <TextField\n label=\"Ships to\"\n value={draft?.shipTo ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, shipTo: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n helperText={\n 'This store’s shipping rates differ by destination, so the ' +\n 'payment link has to be priced for one.'\n }\n >\n {draft.shipCountries.map((code) => (\n <MenuItem key={code} value={code}>\n {CommerceModel.CHECKOUT_SHIPPING_COUNTRY_NAMES[code] ?? code}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setDraft(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n !draft?.productId ||\n draft?.busy ||\n // Once asked, the answer is required: retrying without one is\n // refused again, so the button would only look broken.\n Boolean(draft?.shipCountries?.length && !draft?.shipTo)\n }\n onClick={handleDraftCreate}\n >\n {'Create & copy link'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nHostOrdersCard.displayName = 'HostOrdersCard'\n\nexport default HostOrdersCard\n"],"names":["CommerceModel","CardDisplay","ListFilterChips","ListPagination","ListQueryNotices","listQueryRefusals","ListTable","hiddenFilterVisibility","listFilterGridColumns","upsertListFilterClause","useListGridFilter","useSnackbar","checkEntitlement","ORDERS_CUSTOMER_PARAM","ORDERS_ORDER_PARAM","pluginDocsHelp","Alert","AlertTitle","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Tooltip","Typography","collection","doc","getDoc","getDocs","limit","orderBy","query","startAfter","where","useSearchParams","useCallback","useEffect","useMemo","useRef","useState","ceilingedWindow","collectionCeiling","useFirestore","useOrgPlan","useUser","useFirestoreCollection","listQueryConstraints","useListQuery","authorizedFetch","OPEN_DISPUTE_CLAUSE","ORDER_CHANNEL_OPTIONS","ORDER_DISPUTE_OPTIONS","ORDER_LIST_FIELDS","ORDER_LIST_HEADERS","ORDER_LIST_QUERY","ORDER_LIST_SELECT_FIELDS","ORDER_STATUS_OPTIONS","ordersCustomerClause","CommerceStatTile","OrderDetailDialog","DISPUTE_COLOR","PRODUCT_NAME_WINDOW","OPEN_DISPUTE_CEILING","STATS_WINDOW_MS","STATS_ORDER_CEILING","ORDERS_EXPORT_CEILING","ORDERS_EXPORT_PAGE","ORDER_VISIBLE_COLUMNS","ORDER_HIDDEN_COLUMNS","HostOrdersCard","props","loadedOrders","draft","hostId","firestore","ordersRef","data","productDocs","idField","productWindow","undefined","productNames","map","product","rows","$id","name","org","ready","orgReady","showStats","searchParams","clauses","setClauses","customer","get","gridFilter","onChange","selectFields","filtering","length","searchWords","some","word","trim","disputedRows","hasMore","moreDisputes","declaration","request","deps","pageSize","confirmDisappearances","orders","page","setPage","setPageSize","plan","status","listStatus","search","selectedId","setSelectedId","seededOrderId","seededOrder","setSeededOrder","seededOpened","current","order","then","snapshot","id","exists","catch","setDraft","user","enqueueSnackbar","orderRows","lifted","liftLegacyOrder","orderLabel","formatOrderNumber","itemName","lineItems","productId","statusKey","channelKey","channel","netCents","orderNetCents","disputeBadge","describeOrderDispute","productOptions","value","String","label","filterOptions","productIds","disputeKey","refused","fields","headers","options","showingOpenDisputes","clause","field","op","openDisputes","summariseOpenDisputes","statsSince","Date","now","statsDocs","statsWindow","summary","summarizeOrderWindow","nowMs","exporting","setExporting","handleExportCsv","matched","cursor","more","docs","take","Math","min","slice","entry","push","csv","buildOrdersCsv","url","URL","createObjectURL","Blob","type","anchor","document","createElement","href","download","click","revokeObjectURL","toLocaleString","variant","allowDuplicate","error","console","draftAttemptKey","variantId","quantity","email","shipTo","handleDraftCreate","prev","busy","globalThis","crypto","randomUUID","random","toString","response","method","body","JSON","stringify","Number","shippingCountry","payload","json","ok","needsShippingCountry","shipCountries","shippingCountries","CHECKOUT_SHIPPING_COUNTRIES","navigator","clipboard","writeText","persist","selectedOrder","find","openDraft","selectableProducts","filter","deletedAt","orderColumns","headerName","flex","minWidth","renderCell","row","extraItems","max","sx","noWrap","color","customerEmail","width","orderChannelLabel","align","headerAlign","alignItems","toFixed","refundedCents","totals","totalCents","amountCents","dispute","direction","spacing","ORDER_STATUS_LABELS","size","ORDER_STATUS_COLOR","title","detail","tone","evidenceDaysLeft","component","createdAt","toDate","createdAtMs","toLocaleDateString","toLocaleTimeString","columns","empty","header","help","excerpt","contentGutterX","contentGutterY","onClick","flexWrap","rowGap","revenueCents","deltaPct","revenueDeltaPct","deltaCaption","ordersDeltaPct","aovCents","aovDeltaPct","truncated","count","severity","overdue","action","soonestDaysLeft","justifyContent","disabled","notices","aria-label","onOpen","loading","filterMode","filterModel","onFilterModelChange","quickFilter","sortingMode","disableColumnSorting","hideFooter","initialState","columnVisibilityModel","noRowsLabel","rowCount","onPageChange","onPageSizeChange","onClose","open","Boolean","maxWidth","fullWidth","display","flexDirection","gap","event","target","select","mt","helperText","item","variants","liftLegacyProduct","Object","values","join","priceUsd","replace","slotProps","htmlInput","inputMode","code","CHECKOUT_SHIPPING_COUNTRY_NAMES","displayName"],"mappings":"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"}
@@ -1,4 +1,18 @@
1
1
  import type { ConsolePluginPageProps } from '@aglyn/aglyn';
2
+ /**
3
+ * The products hub's query (AGL-3321), narrowed to what the till may sell:
4
+ * live products (`deletedAt == null`, the list's scope), `status == active`,
5
+ * and any typed word as the search on `nameTokens` — or, for a scan, the
6
+ * whole scanned code as a SKU or barcode clause. Every shape is one the hub's
7
+ * composites already serve.
8
+ */
9
+ export declare function posProductPlan(options: {
10
+ search?: string;
11
+ code?: {
12
+ field: 'barcodes' | 'skus';
13
+ value: string;
14
+ };
15
+ }): import("@aglyn/shared-ui-jsx/const/list-query-plan").ListQueryPlan;
2
16
  /**
3
17
  * POS register (AGL-312): touch-first full-screen sale surface —
4
18
  * product grid with search/barcode (keyboard-wedge scanners type into
@@ -18,19 +18,58 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
18
18
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
19
19
  import * as Aglyn from "@aglyn/aglyn";
20
20
  import * as CommerceModel from "../../model/index.js";
21
- import { PRODUCT_LIST_FILTER_FIELDS } from "../../constants/product-filters.js";
21
+ import { PRODUCT_LIST_BASE, PRODUCT_LIST_QUERY } from "../../constants/product-list-query.js";
22
22
  import { escapeHtml } from "../../utils/escape-html.js";
23
23
  import { NextPageTitle } from "@aglyn/shared-ui-next/contexts/next-page-title-provider";
24
24
  import { useSnackbar } from "@aglyn/shared-ui-snackstack";
25
+ import { ListQueryNotices } from "@aglyn/shared-ui-jsx/components/list-query-notices.component";
26
+ import { planListQuery } from "@aglyn/shared-ui-jsx/const/list-query-plan";
27
+ import { nameSearchNormalizers } from "@aglyn/aglyn/app-utils/name-search";
25
28
  import { Alert, Box, Button, Card, CardActionArea, Chip, Dialog, DialogActions, DialogContent, DialogTitle, MenuItem, Stack, TextField, Typography } from "@mui/material";
26
29
  import { QRCodeSVG } from "qrcode.react";
27
- import { collection, documentId, getDocs, limit, orderBy, query, where } from "firebase/firestore";
30
+ import { collection, getDocs, limit, query, where } from "firebase/firestore";
28
31
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
29
32
  import { useFirestore, useUser } from "@aglyn/tenant-feature-instance";
30
- import { listFilterConstraints, useFirestoreCollection } from "@aglyn/tenant-feature-instance";
33
+ import { useFirestoreCollection } from "@aglyn/tenant-feature-instance";
34
+ import { listQueryConstraints } from "@aglyn/tenant-feature-instance/hooks/use-list-query";
31
35
  import { useOrgPlan } from "@aglyn/tenant-feature-instance";
32
36
  import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
33
37
  const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
38
+ /** The till sells active products only; every read of the catalog asks it. */ const SELLABLE = {
39
+ field: 'status',
40
+ op: 'equals',
41
+ value: 'active'
42
+ };
43
+ /**
44
+ * How many products the till's grid shows. The grid is a picker, not the
45
+ * catalog: typing narrows it on the query, so a name reaches every product.
46
+ */ const POS_GRID_CEILING = 500;
47
+ /**
48
+ * The products hub's query (AGL-3321), narrowed to what the till may sell:
49
+ * live products (`deletedAt == null`, the list's scope), `status == active`,
50
+ * and any typed word as the search on `nameTokens` — or, for a scan, the
51
+ * whole scanned code as a SKU or barcode clause. Every shape is one the hub's
52
+ * composites already serve.
53
+ */ export function posProductPlan(options) {
54
+ var _options_search;
55
+ const typed = (_options_search = options.search) == null ? void 0 : _options_search.trim();
56
+ return planListQuery(PRODUCT_LIST_QUERY, {
57
+ base: PRODUCT_LIST_BASE,
58
+ clauses: [
59
+ SELLABLE,
60
+ ...options.code ? [
61
+ {
62
+ field: options.code.field,
63
+ op: 'contains',
64
+ value: options.code.value
65
+ }
66
+ ] : []
67
+ ],
68
+ search: typed ? [
69
+ typed
70
+ ] : []
71
+ }, nameSearchNormalizers);
72
+ }
34
73
  /**
35
74
  * POS register (AGL-312): touch-first full-screen sale surface —
36
75
  * product grid with search/barcode (keyboard-wedge scanners type into
@@ -55,41 +94,26 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
55
94
  const { data: user } = useUser();
56
95
  const { enqueueSnackbar } = useSnackbar();
57
96
  const [search, setSearch] = useState('');
58
- const { data: productDocs } = useFirestoreCollection(()=>{
59
- /*
60
- * The till's grid, narrowed by the QUERY rather than by the rows it
61
- * happened to fetch (AGL-2501, AGL-2292).
62
- *
63
- * `limit(500)` with no `orderBy` is document-id order over
64
- * `createResourceUid()` — an arbitrary five hundred. Both filters then
65
- * ran over that sample: `status === 'active'` below, and the search.
66
- * The status one is the quieter of the two, because a catalog of five
67
- * hundred archived products and a hundred live ones would fill the
68
- * window with the archived and leave the register showing almost
69
- * nothing to sell.
70
- *
71
- * Status moves into the query, so the window is five hundred SELLABLE
72
- * products; the typed search moves in beside it, so a name reaches the
73
- * whole catalog. The scan does not come through here at all — see
74
- * `handleSearchEnter`, which is a lookup rather than a filter.
75
- */ const typed = listFilterConstraints(PRODUCT_LIST_FILTER_FIELDS, search.trim() ? {
76
- field: 'name',
77
- op: 'contains',
78
- value: search.trim()
79
- } : null, // Status is already an equality on this query, so the translator must
80
- // not add an ordering that would have to be the first `orderBy`.
81
- {
82
- fixedOrderBy: 'nameLower'
83
- });
84
- return query(collection(firestore, 'hosts', hostId, 'products'), where('status', '==', 'active'), ...typed != null ? typed : [], ...typed ? [
85
- orderBy('nameLower')
86
- ] : [
87
- orderBy(documentId())
88
- ], limit(500));
89
- }, [
97
+ const gridPlan = useMemo(()=>posProductPlan({
98
+ search
99
+ }), [
100
+ search
101
+ ]);
102
+ const { data: productDocs } = useFirestoreCollection(/*
103
+ * The till's grid, narrowed by the QUERY rather than by the rows it
104
+ * happened to fetch (AGL-2501, AGL-2292, AGL-3321).
105
+ *
106
+ * `limit(500)` with no `orderBy` was document-id order over
107
+ * `createResourceUid()` — an arbitrary five hundred, filtered afterwards
108
+ * for status, deletion and the typed search. All three are the query's
109
+ * now (`posProductPlan`), so the window is the first five hundred
110
+ * SELLABLE products by name and a typed name reaches the whole catalog.
111
+ * The scan does not come through here at all — see `handleSearchEnter`,
112
+ * which is a lookup rather than a filter.
113
+ */ ()=>query(collection(firestore, 'hosts', hostId, 'products'), ...listQueryConstraints(gridPlan), limit(POS_GRID_CEILING)), [
90
114
  firestore,
91
115
  hostId,
92
- search
116
+ gridPlan
93
117
  ], {
94
118
  idField: '$id'
95
119
  });
@@ -125,13 +149,18 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
125
149
  const registerCap = Aglyn.checkHostRegisterQuota(org, hostId, registers.length).limit;
126
150
  const withinCap = CommerceModel.registersWithinCap(registers, registerCap);
127
151
  const usableRegisters = planReady ? registers.filter((r)=>withinCap.has(r.$id)) : [];
128
- const { data: reservationDocs } = useFirestoreCollection(()=>query(collection(firestore, 'hosts', hostId, 'reservations'), limit(100)), [
152
+ /*
153
+ * The stays a sale can be charged to, asked for BY STATUS (AGL-3321). This
154
+ * read the first hundred reservations in document-id order and kept the
155
+ * checked-in ones, so a guest in the house was missing from the folio
156
+ * picker whenever a hundred other bookings sorted before theirs.
157
+ */ const { data: reservationDocs } = useFirestoreCollection(()=>query(collection(firestore, 'hosts', hostId, 'reservations'), where('status', '==', 'checked_in'), limit(100)), [
129
158
  firestore,
130
159
  hostId
131
160
  ], {
132
161
  idField: '$id'
133
162
  });
134
- const openStays = (reservationDocs != null ? reservationDocs : []).filter((reservation)=>reservation.status === 'checked_in');
163
+ const openStays = reservationDocs != null ? reservationDocs : [];
135
164
  const [lines, setLines] = useState([]);
136
165
  const [discountPct, setDiscountPct] = useState(0);
137
166
  const [customerEmail, setCustomerEmail] = useState('');
@@ -194,11 +223,10 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
194
223
  discountPct
195
224
  ]);
196
225
  const [lastReceipt, setLastReceipt] = useState(null);
197
- const products = useMemo(()=>[
226
+ const products = useMemo(()=>// Status, deletion and the search are all the query's (`posProductPlan`).
227
+ [
198
228
  ...productDocs != null ? productDocs : []
199
- ]// `status` is now the query's; `deletedAt` stays here because
200
- // Firestore cannot ask for documents that LACK a field.
201
- .filter((product)=>!product.deletedAt).map((product)=>_extends({}, CommerceModel.liftLegacyProduct(product), {
229
+ ].map((product)=>_extends({}, CommerceModel.liftLegacyProduct(product), {
202
230
  $id: product.$id
203
231
  })), [
204
232
  productDocs
@@ -296,11 +324,15 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
296
324
  * had not fired — the cashier's only signal was that nothing happened.
297
325
  */ const handleSearchEnter = useCallback(async ()=>{
298
326
  var _product_variants_find;
299
- var _hit_data;
300
327
  const needle = search.trim().toLowerCase();
301
328
  if (!needle) return;
302
329
  const lookup = async (field)=>{
303
- const found = await getDocs(query(collection(firestore, 'hosts', hostId, 'products'), where('status', '==', 'active'), where(field, 'array-contains', needle), limit(1)));
330
+ const found = await getDocs(query(collection(firestore, 'hosts', hostId, 'products'), ...listQueryConstraints(posProductPlan({
331
+ code: {
332
+ field,
333
+ value: needle
334
+ }
335
+ })), limit(1)));
304
336
  return found.docs[0];
305
337
  };
306
338
  let hit;
@@ -314,7 +346,7 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
314
346
  persist: false
315
347
  });
316
348
  }
317
- if (!hit || ((_hit_data = hit.data()) == null ? void 0 : _hit_data['deletedAt'])) {
349
+ if (!hit) {
318
350
  return void enqueueSnackbar(`No product matches “${search.trim()}”`, {
319
351
  variant: 'warning',
320
352
  persist: false
@@ -494,6 +526,15 @@ const usd = (cents)=>`$${(cents / 100).toFixed(2)}`;
494
526
  mb: 2
495
527
  }
496
528
  }),
529
+ /*#__PURE__*/ _jsx(Box, {
530
+ sx: {
531
+ mb: gridPlan.notices.length ? 2 : 0
532
+ },
533
+ children: /*#__PURE__*/ _jsx(ListQueryNotices, {
534
+ refused: [],
535
+ notices: gridPlan.notices
536
+ })
537
+ }),
497
538
  /*#__PURE__*/ _jsx(Box, {
498
539
  sx: {
499
540
  display: 'grid',