@aglyn/plugins-commerce 1.0.0-beta.199 → 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/gift-cards-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 { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Button,\n Chip,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport {\n collection,\n count,\n getAggregateFromServer,\n limit,\n orderBy,\n query,\n sum,\n where,\n} from 'firebase/firestore'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport {\n useFirestore,\n usePagedCollection,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { EntitlementGatedCard } from './entitlement-gate.component'\n\nexport interface GiftCardsCardProps {\n hostId: string\n}\n\nconst usd = (cents: number | undefined) => `$${((cents ?? 0) / 100).toFixed(2)}`\n\nconst giftCardsHelp = pluginDocsHelp('commerce', {\n anchor: '#gift-cards',\n title: 'Gift cards',\n excerpt:\n 'Every gift card your store has issued, what is left on it, and what ' +\n 'that adds up to in outstanding store credit.',\n})\n\n/**\n * Gift cards & store credit (AGL-2226).\n *\n * `hosts/{hostId}/giftCards` has existed since AGL-322 with no console at\n * either end: `billing-webhook.ts` mints a card when one is bought,\n * `cart-checkout.ts` decrements it at redemption, and nothing in the product\n * ever showed the merchant a code, a balance, or the total they owe.\n *\n * The total is the point. Outstanding gift-card balance is a real liability\n * on the merchant's books, and it was invisible — which is also why AGL-1767\n * (a redemption resurrecting a deleted card at a NEGATIVE balance,\n * understating that liability) could not have been noticed from the console.\n * The sum below deliberately floors each card at zero so a stray negative row\n * cannot flatter the number the way that bug did.\n *\n * Reads are client-side because the Firestore host catch-all already grants\n * them and the merchant wants the list live. The two operations that MOVE\n * MONEY — issue and void — go through `commerce/gift-cards`, because that same\n * catch-all would otherwise let any site admin write their own `balanceCents`\n * and redeem it at checkout.\n */\nexport function GiftCardsCard(props: GiftCardsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n /*\n * Ordered by the server, and a growing window rather than a fixed 300.\n *\n * `limit(300)` with no `orderBy` returns DOCUMENT-ID order, and a gift card\n * is keyed by its CODE, so the window was three hundred cards chosen by\n * code — sorted by date afterwards to look newest-first. Past three hundred\n * cards, one issued this morning was not in it.\n */\n const {\n rows: allCards,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n } = usePagedCollection<any>(\n (pageLimit) =>\n query(\n collection(firestore, 'hosts', hostId, 'giftCards'),\n orderBy('createdAtMs', 'desc'),\n limit(pageLimit),\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const [search, setSearch] = useState('')\n const [amount, setAmount] = useState('')\n const [recipient, setRecipient] = useState('')\n const [busy, setBusy] = useState(false)\n\n const cards = useMemo(() => {\n const term = search.trim().toUpperCase()\n if (!term) return allCards\n return allCards.filter(\n (card: any) =>\n card.$id.includes(term) ||\n String(card.recipientEmail ?? '').toUpperCase().includes(term),\n )\n }, [allCards, search])\n\n /*==========================================\n * OUTSTANDING LIABILITY IS A SERVER AGGREGATE, not a page total.\n *\n * These two numbers are what the merchant owes their customers, and they\n * were `reduce`d over whatever the window happened to hold — 300 cards\n * chosen by code. A shop with 400 gift cards under-reported its own\n * liability and had no way to know: the chip does not say \"of the cards on\n * screen\", and there is no reading of \"$X outstanding\" under which a\n * sample is the answer.\n *\n * `where('balanceCents','>',0)` IS the per-card floor the reduce applied\n * by hand. A negative balance is a data fault (AGL-1767), not credit the\n * merchant gets back; excluding it contributes zero, exactly as flooring\n * it did. Zero-balance cards contribute nothing either way and are what\n * `liveCount` was already counting.\n *\n * An aggregation is billed per 1,000 documents scanned rather than per\n * document, so this is cheaper than the read it replaces as well as\n * correct.\n *=========================================*/\n const [totalsEpoch, setTotalsEpoch] = useState(0)\n const [totals, setTotals] = useState<{\n outstandingCents: number\n liveCount: number\n issuedCount: number\n } | null>(null)\n useEffect(() => {\n let active = true\n const cardsRef = collection(firestore, 'hosts', hostId, 'giftCards')\n void Promise.all([\n getAggregateFromServer(query(cardsRef, where('balanceCents', '>', 0)), {\n outstandingCents: sum('balanceCents'),\n liveCount: count(),\n }),\n // \"Issued\" is every card ever, so this one carries no predicate.\n getAggregateFromServer(cardsRef, { issuedCount: count() }),\n ])\n .then(([balances, issued]) => {\n if (!active) return\n setTotals({\n outstandingCents: Number(balances.data().outstandingCents ?? 0),\n liveCount: Number(balances.data().liveCount ?? 0),\n issuedCount: Number(issued.data().issuedCount ?? 0),\n })\n })\n .catch(() => {\n // Held at null rather than zeroed. \"$0.00 outstanding\" is a\n // confident wrong number in the flattering direction; an absent one\n // renders as unknown and cannot be mistaken for a settled book.\n if (active) setTotals(null)\n })\n return () => {\n active = false\n }\n }, [firestore, hostId, totalsEpoch])\n\n const post = useCallback(\n async (payload: Record<string, unknown>, success: (body: any) => string) => {\n setBusy(true)\n try {\n const response = await authorizedFetch(\n user,\n '/api/commerce/gift-cards',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, ...payload }),\n },\n )\n const body = await response.json().catch(() => ({}))\n if (!response.ok) {\n return void enqueueSnackbar(body?.error ?? 'Gift card action failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n enqueueSnackbar(success(body), { variant: 'success', persist: false })\n // Issuing, voiding or adjusting moves the liability. The list is a\n // live listener and updates itself; the aggregate is a one-shot read\n // and would otherwise keep reporting the balance from before.\n setTotalsEpoch((epoch) => epoch + 1)\n } finally {\n setBusy(false)\n }\n },\n [user, hostId, enqueueSnackbar],\n )\n\n const handleIssue = useCallback(async () => {\n const dollars = Number(amount)\n if (!Number.isFinite(dollars) || dollars <= 0) {\n return void enqueueSnackbar('Enter an amount above zero', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n await post(\n {\n action: 'issue',\n amountCents: Math.round(dollars * 100),\n recipientEmail: recipient.trim(),\n },\n (body) =>\n body.emailed\n ? `Issued ${body.code} — emailed to ${recipient.trim()}`\n : `Issued ${body.code}`,\n )\n setAmount('')\n setRecipient('')\n }, [amount, recipient, post, enqueueSnackbar])\n\n const handleVoid = useCallback(\n async (card: any) => {\n // This provider REJECTS on dismissal rather than resolving false, so\n // the `.then/.catch` pair is the read — awaiting the promise bare would\n // throw out of the handler on a cancel.\n const confirmed = await confirm({\n title: `Void ${card.$id}?`,\n description:\n `This zeroes the ${usd(card.balanceCents)} left on the card. The ` +\n 'holder will no longer be able to redeem it, and it cannot be undone.',\n confirmationText: 'Void gift card',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await post({ action: 'void', code: card.$id }, () => `${card.$id} voided`)\n },\n [confirm, post],\n )\n\n return (\n <EntitlementGatedCard\n help={giftCardsHelp}\n hostId={hostId}\n feature=\"giftCards\"\n header={'Gift cards'}\n upsell={\n 'Gift cards let shoppers buy store credit for someone else, and let ' +\n 'you issue credit by hand for a goodwill gesture or a service ' +\n 'recovery. Balances apply automatically at checkout.'\n }\n >\n <CardDisplay\n header={'Gift cards'}\n help={giftCardsHelp}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n <Chip\n size=\"small\"\n color={totals?.outstandingCents ? 'warning' : 'default'}\n label={\n totals\n ? `${usd(totals.outstandingCents)} outstanding`\n : 'Outstanding unavailable'\n }\n />\n {totals ? (\n <Chip\n size=\"small\"\n label={`${totals.liveCount} with a balance`}\n />\n ) : null}\n {totals ? (\n <Chip size=\"small\" label={`${totals.issuedCount} issued`} />\n ) : null}\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Outstanding balance is store credit your customers have already ' +\n 'paid for and not yet spent — it is a liability, not revenue.'}\n </Typography>\n\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n <TextField\n label=\"Issue amount (USD)\"\n value={amount}\n onChange={(event) => setAmount(event.target.value)}\n size=\"small\"\n type=\"number\"\n sx={{ maxWidth: 180 }}\n />\n <TextField\n label=\"Email it to (optional)\"\n value={recipient}\n onChange={(event) => setRecipient(event.target.value)}\n size=\"small\"\n sx={{ minWidth: 240 }}\n />\n <Button\n variant=\"contained\"\n size=\"small\"\n disabled={busy || !amount}\n onClick={handleIssue}\n >\n {'Issue card'}\n </Button>\n </Stack>\n\n <TextField\n label=\"Find in the cards below\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n size=\"small\"\n sx={{ maxWidth: 320 }}\n />\n\n {cards.length ? (\n <Stack spacing={1}>\n {cards.map((card: any) => (\n <Stack\n key={card.$id}\n direction=\"row\"\n spacing={1}\n useFlexGap\n sx={{ flexWrap: 'wrap', alignItems: 'center' }}\n >\n <Typography variant=\"body2\" sx={{ fontFamily: 'monospace' }}>\n {card.$id}\n </Typography>\n <Chip\n size=\"small\"\n color={\n Number(card.balanceCents ?? 0) > 0 ? 'success' : 'default'\n }\n label={`${usd(card.balanceCents)} of ${usd(card.initialCents)}`}\n />\n {card.recipientEmail ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {card.recipientEmail}\n </Typography>\n ) : null}\n {card.orderId ? null : (\n <Chip size=\"small\" variant=\"outlined\" label={'Issued by hand'} />\n )}\n {card.voidedAtMs ? (\n <Chip size=\"small\" variant=\"outlined\" label={'Voided'} />\n ) : Number(card.balanceCents ?? 0) > 0 ? (\n <Button\n size=\"small\"\n color=\"error\"\n disabled={busy}\n onClick={() => handleVoid(card)}\n >\n {'Void'}\n </Button>\n ) : null}\n </Stack>\n ))}\n </Stack>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {search\n ? 'No gift card on this page matches that code or email — ' +\n 'the search reads the page in front of you.'\n : 'No gift cards yet. Sell a gift-card product, or issue one above.'}\n </Typography>\n )}\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={cards.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n </CardDisplay>\n </EntitlementGatedCard>\n )\n}\nGiftCardsCard.displayName = 'GiftCardsCard'\n\nexport default GiftCardsCard\n"],"names":["CardDisplay","useConfirmationContext","ListPagination","useSnackbar","Button","Chip","Stack","TextField","Typography","collection","count","getAggregateFromServer","limit","orderBy","query","sum","where","useCallback","useEffect","useMemo","useState","useFirestore","usePagedCollection","useUser","pluginDocsHelp","authorizedFetch","EntitlementGatedCard","usd","cents","toFixed","giftCardsHelp","anchor","title","excerpt","GiftCardsCard","props","hostId","firestore","data","user","enqueueSnackbar","confirm","rows","allCards","hasMore","page","setPage","pageSize","setPageSize","pageLimit","idField","search","setSearch","amount","setAmount","recipient","setRecipient","busy","setBusy","cards","term","trim","toUpperCase","filter","card","$id","includes","String","recipientEmail","totalsEpoch","setTotalsEpoch","totals","setTotals","active","cardsRef","Promise","all","outstandingCents","liveCount","issuedCount","then","balances","issued","Number","catch","post","payload","success","response","method","headers","body","JSON","stringify","json","ok","error","variant","allowDuplicate","persist","epoch","handleIssue","dollars","isFinite","action","amountCents","Math","round","emailed","code","handleVoid","confirmed","description","balanceCents","confirmationText","confirmationButtonProps","color","help","feature","header","upsell","contentGutterX","contentGutterY","spacing","direction","useFlexGap","sx","flexWrap","size","label","value","onChange","event","target","type","maxWidth","minWidth","disabled","onClick","length","map","alignItems","fontFamily","initialCents","orderId","voidedAtMs","rowCount","onPageChange","onPageSizeChange","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/gift-cards-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 { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\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 type { ListQueryDeclaration } from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Button,\n Chip,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport {\n collection,\n count,\n getAggregateFromServer,\n query,\n sum,\n where,\n} from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport { useListQuery } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { GIFT_CARD_SEARCH_TOKENS_PATH } from '../../model/gift-card-search'\nimport { EntitlementGatedCard } from './entitlement-gate.component'\n\nexport interface GiftCardsCardProps {\n hostId: string\n}\n\nconst usd = (cents: number | undefined) => `$${((cents ?? 0) / 100).toFixed(2)}`\n\n/**\n * The card list's one query (AGL-3321): newest first, and the search an\n * `array-contains` on the tokens every card writer stamps from its code and\n * its recipient's address (`giftCardSearchTokens`), so a card on page forty\n * is found exactly as one on page one is. One composite:\n * `(searchTokens CONTAINS, createdAtMs DESC)`.\n */\nexport const GIFT_CARD_LIST_QUERY: ListQueryDeclaration = {\n fields: [],\n sorts: [{ path: 'createdAtMs', direction: 'desc' }],\n search: { tokensPath: GIFT_CARD_SEARCH_TOKENS_PATH },\n}\n\nconst giftCardsHelp = pluginDocsHelp('commerce', {\n anchor: '#gift-cards',\n title: 'Gift cards',\n excerpt:\n 'Every gift card your store has issued, what is left on it, and what ' +\n 'that adds up to in outstanding store credit.',\n})\n\n/**\n * Gift cards & store credit (AGL-2226).\n *\n * `hosts/{hostId}/giftCards` has existed since AGL-322 with no console at\n * either end: `billing-webhook.ts` mints a card when one is bought,\n * `cart-checkout.ts` decrements it at redemption, and nothing in the product\n * ever showed the merchant a code, a balance, or the total they owe.\n *\n * The total is the point. Outstanding gift-card balance is a real liability\n * on the merchant's books, and it was invisible — which is also why AGL-1767\n * (a redemption resurrecting a deleted card at a NEGATIVE balance,\n * understating that liability) could not have been noticed from the console.\n * The sum below deliberately floors each card at zero so a stray negative row\n * cannot flatter the number the way that bug did.\n *\n * Reads are client-side because the Firestore host catch-all already grants\n * them and the merchant wants the list live. The two operations that MOVE\n * MONEY — issue and void — go through `commerce/gift-cards`, because that same\n * catch-all would otherwise let any site admin write their own `balanceCents`\n * and redeem it at checkout.\n */\nexport function GiftCardsCard(props: GiftCardsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const [search, setSearch] = useState('')\n /*\n * Ordered by the server and paged by it, and the search is the QUERY's\n * (AGL-3321): it used to match the code and the address over the page in\n * front of the merchant, and said so.\n */\n const {\n rows: cards,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n plan,\n } = useListQuery<any>({\n collection: collection(firestore, 'hosts', hostId, 'giftCards'),\n declaration: GIFT_CARD_LIST_QUERY,\n request: { clauses: [], search: search.trim() ? [search.trim()] : [] },\n deps: [firestore, hostId],\n idField: '$id',\n })\n const [amount, setAmount] = useState('')\n const [recipient, setRecipient] = useState('')\n const [busy, setBusy] = useState(false)\n\n\n /*==========================================\n * OUTSTANDING LIABILITY IS A SERVER AGGREGATE, not a page total.\n *\n * These two numbers are what the merchant owes their customers, and they\n * were `reduce`d over whatever the window happened to hold — 300 cards\n * chosen by code. A shop with 400 gift cards under-reported its own\n * liability and had no way to know: the chip does not say \"of the cards on\n * screen\", and there is no reading of \"$X outstanding\" under which a\n * sample is the answer.\n *\n * `where('balanceCents','>',0)` IS the per-card floor the reduce applied\n * by hand. A negative balance is a data fault (AGL-1767), not credit the\n * merchant gets back; excluding it contributes zero, exactly as flooring\n * it did. Zero-balance cards contribute nothing either way and are what\n * `liveCount` was already counting.\n *\n * An aggregation is billed per 1,000 documents scanned rather than per\n * document, so this is cheaper than the read it replaces as well as\n * correct.\n *=========================================*/\n const [totalsEpoch, setTotalsEpoch] = useState(0)\n const [totals, setTotals] = useState<{\n outstandingCents: number\n liveCount: number\n issuedCount: number\n } | null>(null)\n useEffect(() => {\n let active = true\n const cardsRef = collection(firestore, 'hosts', hostId, 'giftCards')\n void Promise.all([\n getAggregateFromServer(query(cardsRef, where('balanceCents', '>', 0)), {\n outstandingCents: sum('balanceCents'),\n liveCount: count(),\n }),\n // \"Issued\" is every card ever, so this one carries no predicate.\n getAggregateFromServer(cardsRef, { issuedCount: count() }),\n ])\n .then(([balances, issued]) => {\n if (!active) return\n setTotals({\n outstandingCents: Number(balances.data().outstandingCents ?? 0),\n liveCount: Number(balances.data().liveCount ?? 0),\n issuedCount: Number(issued.data().issuedCount ?? 0),\n })\n })\n .catch(() => {\n // Held at null rather than zeroed. \"$0.00 outstanding\" is a\n // confident wrong number in the flattering direction; an absent one\n // renders as unknown and cannot be mistaken for a settled book.\n if (active) setTotals(null)\n })\n return () => {\n active = false\n }\n }, [firestore, hostId, totalsEpoch])\n\n const post = useCallback(\n async (payload: Record<string, unknown>, success: (body: any) => string) => {\n setBusy(true)\n try {\n const response = await authorizedFetch(\n user,\n '/api/commerce/gift-cards',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, ...payload }),\n },\n )\n const body = await response.json().catch(() => ({}))\n if (!response.ok) {\n return void enqueueSnackbar(body?.error ?? 'Gift card action failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n enqueueSnackbar(success(body), { variant: 'success', persist: false })\n // Issuing, voiding or adjusting moves the liability. The list is a\n // live listener and updates itself; the aggregate is a one-shot read\n // and would otherwise keep reporting the balance from before.\n setTotalsEpoch((epoch) => epoch + 1)\n } finally {\n setBusy(false)\n }\n },\n [user, hostId, enqueueSnackbar],\n )\n\n const handleIssue = useCallback(async () => {\n const dollars = Number(amount)\n if (!Number.isFinite(dollars) || dollars <= 0) {\n return void enqueueSnackbar('Enter an amount above zero', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n await post(\n {\n action: 'issue',\n amountCents: Math.round(dollars * 100),\n recipientEmail: recipient.trim(),\n },\n (body) =>\n body.emailed\n ? `Issued ${body.code} — emailed to ${recipient.trim()}`\n : `Issued ${body.code}`,\n )\n setAmount('')\n setRecipient('')\n }, [amount, recipient, post, enqueueSnackbar])\n\n const handleVoid = useCallback(\n async (card: any) => {\n // This provider REJECTS on dismissal rather than resolving false, so\n // the `.then/.catch` pair is the read — awaiting the promise bare would\n // throw out of the handler on a cancel.\n const confirmed = await confirm({\n title: `Void ${card.$id}?`,\n description:\n `This zeroes the ${usd(card.balanceCents)} left on the card. The ` +\n 'holder will no longer be able to redeem it, and it cannot be undone.',\n confirmationText: 'Void gift card',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await post({ action: 'void', code: card.$id }, () => `${card.$id} voided`)\n },\n [confirm, post],\n )\n\n return (\n <EntitlementGatedCard\n help={giftCardsHelp}\n hostId={hostId}\n feature=\"giftCards\"\n header={'Gift cards'}\n upsell={\n 'Gift cards let shoppers buy store credit for someone else, and let ' +\n 'you issue credit by hand for a goodwill gesture or a service ' +\n 'recovery. Balances apply automatically at checkout.'\n }\n >\n <CardDisplay\n header={'Gift cards'}\n help={giftCardsHelp}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n <Chip\n size=\"small\"\n color={totals?.outstandingCents ? 'warning' : 'default'}\n label={\n totals\n ? `${usd(totals.outstandingCents)} outstanding`\n : 'Outstanding unavailable'\n }\n />\n {totals ? (\n <Chip\n size=\"small\"\n label={`${totals.liveCount} with a balance`}\n />\n ) : null}\n {totals ? (\n <Chip size=\"small\" label={`${totals.issuedCount} issued`} />\n ) : null}\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Outstanding balance is store credit your customers have already ' +\n 'paid for and not yet spent — it is a liability, not revenue.'}\n </Typography>\n\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n <TextField\n label=\"Issue amount (USD)\"\n value={amount}\n onChange={(event) => setAmount(event.target.value)}\n size=\"small\"\n type=\"number\"\n sx={{ maxWidth: 180 }}\n />\n <TextField\n label=\"Email it to (optional)\"\n value={recipient}\n onChange={(event) => setRecipient(event.target.value)}\n size=\"small\"\n sx={{ minWidth: 240 }}\n />\n <Button\n variant=\"contained\"\n size=\"small\"\n disabled={busy || !amount}\n onClick={handleIssue}\n >\n {'Issue card'}\n </Button>\n </Stack>\n\n <TextField\n label=\"Search by code or email\"\n value={search}\n onChange={(event) => {\n setSearch(event.target.value)\n setPage(0)\n }}\n size=\"small\"\n sx={{ maxWidth: 320 }}\n />\n <ListQueryNotices\n refused={listQueryRefusals(plan.refused, { fields: GIFT_CARD_LIST_QUERY.fields })}\n notices={plan.notices}\n />\n\n {cards.length ? (\n <Stack spacing={1}>\n {cards.map((card: any) => (\n <Stack\n key={card.$id}\n direction=\"row\"\n spacing={1}\n useFlexGap\n sx={{ flexWrap: 'wrap', alignItems: 'center' }}\n >\n <Typography variant=\"body2\" sx={{ fontFamily: 'monospace' }}>\n {card.$id}\n </Typography>\n <Chip\n size=\"small\"\n color={\n Number(card.balanceCents ?? 0) > 0 ? 'success' : 'default'\n }\n label={`${usd(card.balanceCents)} of ${usd(card.initialCents)}`}\n />\n {card.recipientEmail ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {card.recipientEmail}\n </Typography>\n ) : null}\n {card.orderId ? null : (\n <Chip size=\"small\" variant=\"outlined\" label={'Issued by hand'} />\n )}\n {card.voidedAtMs ? (\n <Chip size=\"small\" variant=\"outlined\" label={'Voided'} />\n ) : Number(card.balanceCents ?? 0) > 0 ? (\n <Button\n size=\"small\"\n color=\"error\"\n disabled={busy}\n onClick={() => handleVoid(card)}\n >\n {'Void'}\n </Button>\n ) : null}\n </Stack>\n ))}\n </Stack>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {search.trim()\n ? 'No gift card matches that code or email.'\n : 'No gift cards yet. Sell a gift-card product, or issue one above.'}\n </Typography>\n )}\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={cards.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n </CardDisplay>\n </EntitlementGatedCard>\n )\n}\nGiftCardsCard.displayName = 'GiftCardsCard'\n\nexport default GiftCardsCard\n"],"names":["CardDisplay","useConfirmationContext","ListPagination","ListQueryNotices","listQueryRefusals","useSnackbar","Button","Chip","Stack","TextField","Typography","collection","count","getAggregateFromServer","query","sum","where","useCallback","useEffect","useState","useFirestore","useUser","useListQuery","pluginDocsHelp","authorizedFetch","GIFT_CARD_SEARCH_TOKENS_PATH","EntitlementGatedCard","usd","cents","toFixed","GIFT_CARD_LIST_QUERY","fields","sorts","path","direction","search","tokensPath","giftCardsHelp","anchor","title","excerpt","GiftCardsCard","props","hostId","firestore","data","user","enqueueSnackbar","confirm","setSearch","rows","cards","hasMore","page","setPage","pageSize","setPageSize","plan","declaration","request","clauses","trim","deps","idField","amount","setAmount","recipient","setRecipient","busy","setBusy","totalsEpoch","setTotalsEpoch","totals","setTotals","active","cardsRef","Promise","all","outstandingCents","liveCount","issuedCount","then","balances","issued","Number","catch","post","payload","success","response","method","headers","body","JSON","stringify","json","ok","error","variant","allowDuplicate","persist","epoch","handleIssue","dollars","isFinite","action","amountCents","Math","round","recipientEmail","emailed","code","handleVoid","card","confirmed","$id","description","balanceCents","confirmationText","confirmationButtonProps","color","help","feature","header","upsell","contentGutterX","contentGutterY","spacing","useFlexGap","sx","flexWrap","size","label","value","onChange","event","target","type","maxWidth","minWidth","disabled","onClick","refused","notices","length","map","alignItems","fontFamily","initialCents","orderId","voidedAtMs","rowCount","onPageChange","onPageSizeChange","displayName"],"mappings":"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"}
@@ -1,3 +1,19 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
1
17
  export interface HostOrdersCardProps {
2
18
  hostId: string;
3
19
  }
@@ -5,6 +21,16 @@ export interface HostOrdersCardProps {
5
21
  * Orders console (AGL-287): filterable list over webhook-written order
6
22
  * docs with a detail dialog (timeline, fulfill, refund, cancel, notes,
7
23
  * packing slip) and draft orders that send the buyer a payment link.
24
+ *
25
+ * ## Every filter and the search are on the query (AGL-3321)
26
+ *
27
+ * The list used to read the newest two hundred orders and match its Filters
28
+ * panel and search over them, so a Status filter on a busy store answered
29
+ * "none" about every order past the two hundredth. Now each clause and the
30
+ * search word are predicates on ONE Firestore query (`ORDER_LIST_QUERY`),
31
+ * over the fields every writer stamps (`orderListFields`), and the footer
32
+ * pages that query's answer. A combination one query cannot hold is refused
33
+ * by name above the grid and never applied to some rows only.
8
34
  */
9
35
  export declare function HostOrdersCard(props: HostOrdersCardProps): import("react").JSX.Element;
10
36
  export declare namespace HostOrdersCard {