@aglyn/plugins-commerce 1.0.0-beta.287 → 1.0.0-beta.289
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +13 -13
- package/src/lib/components/commerce-console-page.js +29 -2
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/console/commerce-analytics-card.component.js +4 -2
- package/src/lib/components/console/commerce-analytics-card.component.js.map +1 -1
- package/src/lib/components/console/host-orders-card.component.js +5 -1
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/host-quotes-card.component.d.ts +34 -0
- package/src/lib/components/console/host-quotes-card.component.js +247 -0
- package/src/lib/components/console/host-quotes-card.component.js.map +1 -0
- package/src/lib/components/console/order-webhooks-card.component.js +1 -1
- package/src/lib/components/console/order-webhooks-card.component.js.map +1 -1
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +33 -28
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -1
- package/src/lib/components/console/quote-editor-dialog.component.d.ts +45 -0
- package/src/lib/components/console/quote-editor-dialog.component.js +1266 -0
- package/src/lib/components/console/quote-editor-dialog.component.js.map +1 -0
- package/src/lib/components/console/quote-pay-link.component.d.ts +38 -0
- package/src/lib/components/console/quote-pay-link.component.js +213 -0
- package/src/lib/components/console/quote-pay-link.component.js.map +1 -0
- package/src/lib/components/console/quote-settings-card.component.d.ts +29 -0
- package/src/lib/components/console/quote-settings-card.component.js +121 -0
- package/src/lib/components/console/quote-settings-card.component.js.map +1 -0
- package/src/lib/components/console/tax-settings-card.component.js +4 -1
- package/src/lib/components/console/tax-settings-card.component.js.map +1 -1
- package/src/lib/components/quote-page-body.d.ts +33 -0
- package/src/lib/components/quote-page-body.js +696 -0
- package/src/lib/components/quote-page-body.js.map +1 -0
- package/src/lib/components/quote-page.d.ts +29 -0
- package/src/lib/components/quote-page.js +96 -0
- package/src/lib/components/quote-page.js.map +1 -0
- package/src/lib/components/storefront-payment-element.d.ts +7 -1
- package/src/lib/components/storefront-payment-element.js +8 -3
- package/src/lib/components/storefront-payment-element.js.map +1 -1
- package/src/lib/constants/orders-list-query.js +6 -1
- package/src/lib/constants/orders-list-query.js.map +1 -1
- package/src/lib/constants/quote-page.d.ts +30 -0
- package/src/lib/constants/quote-page.js +35 -0
- package/src/lib/constants/quote-page.js.map +1 -0
- package/src/lib/declarations.js +5 -1
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/model/commerce-orders.d.ts +25 -2
- package/src/lib/model/commerce-orders.js +10 -1
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce-quote-pay.d.ts +229 -0
- package/src/lib/model/commerce-quote-pay.js +393 -0
- package/src/lib/model/commerce-quote-pay.js.map +1 -0
- package/src/lib/model/commerce-quotes.d.ts +279 -0
- package/src/lib/model/commerce-quotes.js +522 -0
- package/src/lib/model/commerce-quotes.js.map +1 -0
- package/src/lib/model/index.d.ts +2 -0
- package/src/lib/model/index.js +2 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/plugin-permissions.d.ts +8 -1
- package/src/lib/model/plugin-permissions.js +21 -1
- package/src/lib/model/plugin-permissions.js.map +1 -1
- package/src/lib/model/workspace-site-permissions.d.ts +26 -0
- package/src/lib/model/workspace-site-permissions.js +81 -0
- package/src/lib/model/workspace-site-permissions.js.map +1 -0
- package/src/lib/server/api-v1/orders-and-products.js +6 -2
- package/src/lib/server/api-v1/orders-and-products.js.map +1 -1
- package/src/lib/server/billing-webhook.js +127 -52
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/checkout-session-expiry.d.ts +36 -0
- package/src/lib/server/checkout-session-expiry.js +63 -0
- package/src/lib/server/checkout-session-expiry.js.map +1 -0
- package/src/lib/server/commerce-site-keys.d.ts +19 -0
- package/src/lib/server/commerce-site-keys.js +23 -2
- package/src/lib/server/commerce-site-keys.js.map +1 -1
- package/src/lib/server/draft-order.js +16 -8
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/order-webhooks.js +8 -8
- package/src/lib/server/order-webhooks.js.map +1 -1
- package/src/lib/server/person-eraser.d.ts +7 -0
- package/src/lib/server/person-eraser.js +24 -2
- package/src/lib/server/person-eraser.js.map +1 -1
- package/src/lib/server/pos-ops-gate.d.ts +13 -5
- package/src/lib/server/pos-ops-gate.js +12 -14
- package/src/lib/server/pos-ops-gate.js.map +1 -1
- package/src/lib/server/pos-refund-authority.d.ts +3 -1
- package/src/lib/server/pos-refund-authority.js +3 -2
- package/src/lib/server/pos-refund-authority.js.map +1 -1
- package/src/lib/server/pos-return.js +6 -7
- package/src/lib/server/pos-return.js.map +1 -1
- package/src/lib/server/pos-staff-pin.js +7 -7
- package/src/lib/server/pos-staff-pin.js.map +1 -1
- package/src/lib/server/quote-link-token.d.ts +39 -0
- package/src/lib/server/quote-link-token.js +97 -0
- package/src/lib/server/quote-link-token.js.map +1 -0
- package/src/lib/server/quote-pay.d.ts +83 -0
- package/src/lib/server/quote-pay.js +606 -0
- package/src/lib/server/quote-pay.js.map +1 -0
- package/src/lib/server/quote.d.ts +49 -0
- package/src/lib/server/quote.js +513 -0
- package/src/lib/server/quote.js.map +1 -0
- package/src/lib/server/refund.js +26 -36
- package/src/lib/server/refund.js.map +1 -1
- package/src/lib/server/related.js +2 -0
- package/src/lib/server/related.js.map +1 -1
- package/src/lib/server/site-page-resolver.d.ts +2 -0
- package/src/lib/server/site-page-resolver.js +75 -0
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server.js +14 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/site.js +6 -0
- package/src/lib/site.js.map +1 -1
- package/src/lib/transfer/tracking.server.js +6 -0
- package/src/lib/transfer/tracking.server.js.map +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/quote-page-body.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\nimport * as Aglyn from '@aglyn/aglyn'\nimport Alert from '@mui/material/Alert'\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Chip from '@mui/material/Chip'\nimport CircularProgress from '@mui/material/CircularProgress'\nimport Divider from '@mui/material/Divider'\nimport Paper from '@mui/material/Paper'\nimport Stack from '@mui/material/Stack'\nimport Typography from '@mui/material/Typography'\nimport type { StripeCheckoutContact } from '@stripe/stripe-js'\nimport { forwardRef, lazy, Suspense, useCallback, useEffect, useRef, useState } from 'react'\nimport {\n formatOrderMoney,\n readStorefrontCheckoutWallets,\n type QuotePageAddress,\n type QuotePageView,\n type StorefrontCheckoutWallets,\n} from '../model'\nimport type { QuotePageProps } from './quote-page'\nimport { StorefrontPaymentElementFallback } from './storefront-payment-element-fallback'\n\n/** The Payment Element, lazily: Stripe.js loads only when a customer presses Pay. */\nconst StorefrontPaymentElement = lazy(() =>\n import('./storefront-payment-element').then((module) => ({\n default: module.StorefrontPaymentElement,\n })),\n)\n\ntype LoadState =\n | { kind: 'loading' }\n | { kind: 'missing'; message: string }\n | { kind: 'replaced'; storeName: string }\n | { kind: 'ready'; view: QuotePageView }\n\n/** After a payment returns here, how often and how long the page asks whether it landed. */\nconst CONFIRM_POLL_MS = 2_000\nconst CONFIRM_POLLS = 15\n\nfunction dateLabel(atMs: number | null): string {\n if (!atMs) return ''\n return new Date(atMs).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\nfunction addressLines(address: QuotePageAddress): string[] {\n const cityLine = [address.city, [address.state, address.postalCode].filter(Boolean).join(' ')]\n .filter(Boolean)\n .join(', ')\n return [address.name, address.line1, address.line2, cityLine, address.country].filter(\n (line): line is string => Boolean(line),\n )\n}\n\nfunction stripeContact(address: QuotePageAddress | null): StripeCheckoutContact | undefined {\n if (!address?.country) return undefined\n return {\n name: address.name,\n address: {\n country: address.country,\n line1: address.line1,\n line2: address.line2,\n city: address.city,\n state: address.state,\n postal_code: address.postalCode,\n },\n }\n}\n\nfunction AddressBlock({ label, lines }: { label: string; lines: string[] }) {\n return (\n <Stack spacing={0.25} sx={{ minWidth: 0, flex: 1 }}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {label}\n </Typography>\n {lines.map((line, index) => (\n <Typography key={index} variant=\"body2\">\n {line}\n </Typography>\n ))}\n </Stack>\n )\n}\n\nfunction TotalRow({ label, value, strong }: { label: string; value: string; strong?: boolean }) {\n return (\n <Stack direction=\"row\" spacing={2} sx={{ justifyContent: 'space-between' }}>\n <Typography variant={strong ? 'subtitle1' : 'body2'} color={strong ? 'text.primary' : 'text.secondary'}>\n {label}\n </Typography>\n <Typography variant={strong ? 'subtitle1' : 'body2'} color={strong ? 'text.primary' : 'text.secondary'}>\n {value}\n </Typography>\n </Stack>\n )\n}\n\n/**\n * The customer's quote page (AGL-4094), loaded when the page draws it (see\n * `quote-page.tsx`): what a customer sees when they open\n * the pay link a business sent them. The commerce page resolver serves it at\n * `/pay-quote` inside the site's own header and footer; a business can also\n * place it on a page of their own design at that address.\n *\n * The quote is read AFTER hydration from `?q=&t=`, so the page itself is the\n * same for every customer and may be cached, and the quote never is. Pay asks\n * the server for a fresh payment for the quote as the page shows it: Stripe's\n * own page, or the payment form here when in-page payments are on. Nothing\n * here decides a quote is paid; after paying, the page reads that from the\n * server like any other visit.\n */\nconst QuotePageBody = forwardRef<HTMLDivElement, QuotePageProps>((props, ref) => {\n const { heading, ...rest } = props\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { hostId } = Aglyn.useSite()\n const siteFetch = Aglyn.useSiteFetch()\n const [state, setState] = useState<LoadState>({ kind: 'loading' })\n const [paying, setPaying] = useState(false)\n const [payError, setPayError] = useState('')\n const [nativeCheckout, setNativeCheckout] = useState<{\n clientSecret: string\n publishableKey: string\n wallets?: StorefrontCheckoutWallets\n } | null>(null)\n /** What a return from paying turned out to be: still being confirmed, or not paid. */\n const [returned, setReturned] = useState<'confirming' | 'unconfirmed' | 'failed' | null>(null)\n const link = useRef({ quoteId: '', token: '', sessionId: '' })\n\n const load = useCallback(async (): Promise<LoadState> => {\n const { quoteId, token } = link.current\n try {\n const response = await siteFetch(\n `/api/commerce/quote-view?hostId=${encodeURIComponent(hostId ?? '')}` +\n `&q=${encodeURIComponent(quoteId)}&t=${encodeURIComponent(token)}`,\n )\n const payload = await response.json().catch(() => ({}))\n if (!response.ok) {\n return { kind: 'missing', message: String(payload?.error ?? 'We could not find that quote.') }\n }\n if (payload?.state === 'replaced') return { kind: 'replaced', storeName: String(payload.storeName ?? '') }\n return { kind: 'ready', view: payload as QuotePageView }\n } catch {\n return { kind: 'missing', message: 'This quote could not be loaded. Check your connection and try again.' }\n }\n }, [hostId, siteFetch])\n\n useEffect(() => {\n if (!hostId) return\n const params = new URLSearchParams(window.location.search)\n link.current = {\n quoteId: params.get('q') ?? '',\n token: params.get('t') ?? '',\n sessionId: params.get('session_id') ?? '',\n }\n if (!link.current.quoteId || !link.current.token) {\n setState({ kind: 'missing', message: 'Open this page from the link you were sent with your quote.' })\n return\n }\n let live = true\n void (async () => {\n const first = await load()\n if (!live) return\n setState(first)\n const sessionId = link.current.sessionId\n if (first.kind !== 'ready' || first.view.status !== 'unpaid' || !sessionId) return\n // Back from paying: the payment may have landed before the record of it.\n const status = await siteFetch(\n `/api/commerce/checkout-status?hostId=${encodeURIComponent(hostId)}` +\n `&sessionId=${encodeURIComponent(sessionId)}`,\n )\n .then((response) => (response.ok ? response.json() : null))\n .catch(() => null)\n if (!live) return\n if (status?.status === 'open') {\n setReturned('failed')\n return\n }\n if (status?.status !== 'complete') return\n setReturned('confirming')\n for (let poll = 0; poll < CONFIRM_POLLS && live; poll++) {\n await new Promise((resolve) => setTimeout(resolve, CONFIRM_POLL_MS))\n if (!live) return\n const next = await load()\n if (!live) return\n if (next.kind === 'ready' && next.view.status !== 'unpaid') {\n setState(next)\n setReturned(null)\n return\n }\n }\n if (live) setReturned('unconfirmed')\n })()\n return () => {\n live = false\n }\n }, [hostId, load, siteFetch])\n\n const view = state.kind === 'ready' ? state.view : null\n\n const pay = useCallback(async () => {\n if (!view || paying) return\n setPaying(true)\n setPayError('')\n setReturned(null)\n // One key per press: a press is one attempt, and a retry after a failure is a new one.\n const attemptKey =\n globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`\n try {\n const response = await siteFetch('/api/commerce/quote-pay', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Idempotency-Key': attemptKey },\n body: JSON.stringify({\n hostId,\n q: link.current.quoteId,\n t: link.current.token,\n v: view.version,\n }),\n })\n const payload = await response.json().catch(() => ({}))\n // A session with a client secret is the in-page form; checked before the\n // redirect, so a form is never navigated away from.\n if (response.ok && payload?.clientSecret && payload?.publishableKey) {\n setNativeCheckout({\n clientSecret: String(payload.clientSecret),\n publishableKey: String(payload.publishableKey),\n wallets: readStorefrontCheckoutWallets(payload.wallets),\n })\n setPaying(false)\n return\n }\n if (response.ok && payload?.url) {\n window.location.assign(String(payload.url))\n return\n }\n if (payload?.replaced) {\n setState({ kind: 'replaced', storeName: view.storeName })\n return\n }\n if (payload?.reason === 'paid' || payload?.reason === 'updated') {\n const next = await load()\n setState(next)\n }\n setPayError(String(payload?.error ?? 'Your payment could not be set up. Try again.'))\n setPaying(false)\n } catch {\n setPayError('Your payment could not be set up. Check your connection and try again.')\n setPaying(false)\n }\n }, [view, paying, siteFetch, hostId, load])\n\n if (!hostId) {\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[{ p: 3, border: '1px dashed', borderColor: 'divider', borderRadius: 1 }, ...nodeSx]}\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Quote — a customer’s quote and its Pay button render here'}\n </Typography>\n </Box>\n )\n }\n\n if (state.kind === 'loading') {\n return (\n <Box ref={ref} {...rest} sx={[{ py: 6, textAlign: 'center' }, ...nodeSx]}>\n <CircularProgress size={28} aria-label=\"Loading your quote\" />\n </Box>\n )\n }\n\n if (state.kind === 'missing') {\n return (\n <Box ref={ref} {...rest} sx={nodeSx}>\n <Stack spacing={2}>\n <Typography variant=\"h4\" component=\"h1\">\n {heading || 'Your quote'}\n </Typography>\n <Alert severity=\"info\">{state.message}</Alert>\n </Stack>\n </Box>\n )\n }\n\n if (state.kind === 'replaced') {\n const business = state.storeName || 'The business'\n return (\n <Box ref={ref} {...rest} sx={nodeSx}>\n <Stack spacing={2}>\n {state.storeName ? (\n <Typography variant=\"overline\" color=\"text.secondary\">\n {state.storeName}\n </Typography>\n ) : null}\n <Typography variant=\"h4\" component=\"h1\">\n {'This quote was updated'}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n {`${business} updated this quote and sent you a newer link. Open their most recent message to see it.`}\n </Typography>\n </Stack>\n </Box>\n )\n }\n\n const quote = state.view\n const money = (cents: number) => formatOrderMoney(cents, quote.currency)\n const unpaid = quote.status === 'unpaid'\n const addresses = [\n quote.customerName || quote.company\n ? { label: 'Prepared for', lines: [quote.customerName, quote.company].filter((line): line is string => Boolean(line)) }\n : null,\n quote.billTo ? { label: 'Bill to', lines: addressLines(quote.billTo) } : null,\n quote.shipTo ? { label: 'Ship to', lines: addressLines(quote.shipTo) } : null,\n ].filter((block): block is { label: string; lines: string[] } => Boolean(block && block.lines.length))\n const totalRows = (\n [\n ['Subtotal', money(quote.totals.itemsCents), true],\n [\n quote.discountLabel ? `Discount (${quote.discountLabel})` : 'Discount',\n `−${money(quote.totals.discountCents)}`,\n quote.totals.discountCents > 0,\n ],\n ['Shipping', money(quote.totals.shippingCents), quote.totals.shippingCents > 0],\n [quote.taxLabel, money(quote.totals.taxCents), quote.totals.taxCents > 0],\n // `Tax is added at payment`, with no figure: Stripe works it out from the address.\n [quote.taxLabel, '', quote.taxAtPayment],\n ] as Array<[string, string, boolean]>\n ).filter(([, , shown]) => shown)\n\n return (\n <Box ref={ref} {...rest} sx={nodeSx}>\n <Stack spacing={3}>\n <Stack spacing={1}>\n {quote.storeName ? (\n <Typography variant=\"overline\" color=\"text.secondary\">\n {quote.storeName}\n </Typography>\n ) : null}\n <Stack direction=\"row\" spacing={1.5} sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}>\n <Typography variant=\"h4\" component=\"h1\">\n {heading || quote.number}\n </Typography>\n <Chip\n label={quote.statusLabel}\n color={quote.status === 'paid' ? 'success' : unpaid ? 'primary' : 'default'}\n size=\"small\"\n />\n </Stack>\n {quote.dateMs ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Dated ${dateLabel(quote.dateMs)}`}\n </Typography>\n ) : null}\n </Stack>\n\n {addresses.length ? (\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={{ xs: 2, sm: 3 }}>\n {addresses.map((block) => (\n <AddressBlock key={block.label} label={block.label} lines={block.lines} />\n ))}\n </Stack>\n ) : null}\n\n <Stack spacing={1.5}>\n <Typography variant=\"h6\" component=\"h2\">\n {'Items'}\n </Typography>\n {quote.lines.map((line, index) => (\n <Stack key={index} direction=\"row\" spacing={2} sx={{ justifyContent: 'space-between' }}>\n <Stack spacing={0.25} sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\">\n {line.name}\n {line.variantLabel ? ` (${line.variantLabel})` : ''}\n </Typography>\n {line.description ? (\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ whiteSpace: 'pre-line' }}>\n {line.description}\n </Typography>\n ) : null}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {[\n line.included ? `${line.quantity} included` : `${line.quantity} × ${money(line.unitAmountCents)}`,\n line.sku ? `SKU ${line.sku}` : '',\n ]\n .filter(Boolean)\n .join(' · ')}\n </Typography>\n </Stack>\n <Typography variant=\"body2\" sx={{ flexShrink: 0 }}>\n {line.included ? 'Included' : money(line.amountCents)}\n </Typography>\n </Stack>\n ))}\n <Divider />\n {totalRows.map(([label, value]) => (\n <TotalRow key={`${label}-${value}`} label={label} value={value} />\n ))}\n <TotalRow label=\"Total\" value={money(quote.totals.totalCents)} strong />\n </Stack>\n\n {quote.notes ? (\n <Stack spacing={0.5}>\n <Typography variant=\"h6\" component=\"h2\">\n {'Notes'}\n </Typography>\n <Typography variant=\"body2\" sx={{ whiteSpace: 'pre-line' }}>\n {quote.notes}\n </Typography>\n </Stack>\n ) : null}\n {quote.terms ? (\n <Stack spacing={0.5}>\n <Typography variant=\"h6\" component=\"h2\">\n {'Terms'}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ whiteSpace: 'pre-line' }}>\n {quote.terms}\n </Typography>\n </Stack>\n ) : null}\n\n {unpaid ? (\n <Paper variant=\"outlined\" sx={{ p: { xs: 2, sm: 3 } }}>\n <Stack spacing={2}>\n {returned === 'confirming' ? (\n <Alert severity=\"info\" icon={<CircularProgress size={20} />}>\n {'Thank you. We’re confirming your payment.'}\n </Alert>\n ) : returned === 'unconfirmed' ? (\n <Alert severity=\"info\">\n {'Your payment is still being confirmed. Your receipt will arrive by email.'}\n </Alert>\n ) : quote.approvalRequired ? (\n <Alert severity=\"info\">\n {`${quote.storeName || 'The business'} asks you to approve the proof before you pay.`}\n </Alert>\n ) : quote.payable ? (\n <>\n {returned === 'failed' ? (\n <Alert severity=\"warning\">{'Your payment did not go through. Nothing was charged.'}</Alert>\n ) : null}\n {nativeCheckout ? (\n <Suspense fallback={<StorefrontPaymentElementFallback />}>\n <StorefrontPaymentElement\n clientSecret={nativeCheckout.clientSecret}\n publishableKey={nativeCheckout.publishableKey}\n wallets={nativeCheckout.wallets}\n payLabel={quote.taxAtPayment ? 'Pay now' : `Pay ${money(quote.totals.totalCents)}`}\n defaultShippingAddress={stripeContact(quote.shipTo)}\n // An expired or unloadable session is replaced by a new press.\n onRestart={() => {\n setNativeCheckout(null)\n void pay()\n }}\n onCancel={() => setNativeCheckout(null)}\n />\n </Suspense>\n ) : (\n <Button\n variant=\"contained\"\n size=\"large\"\n onClick={() => void pay()}\n disabled={paying}\n sx={{ alignSelf: { xs: 'stretch', sm: 'flex-start' } }}\n >\n {paying\n ? 'Opening…'\n : quote.taxAtPayment\n ? `Pay ${money(quote.totals.totalCents)} plus tax`\n : `Pay ${money(quote.totals.totalCents)}`}\n </Button>\n )}\n {payError ? <Alert severity=\"error\">{payError}</Alert> : null}\n </>\n ) : (\n <Alert severity=\"info\">\n {`Contact ${quote.storeName || 'the business'} to pay this quote.`}\n </Alert>\n )}\n </Stack>\n </Paper>\n ) : (\n <Paper variant=\"outlined\" sx={{ p: { xs: 2, sm: 3 } }}>\n <Stack spacing={1.5} sx={{ alignItems: 'flex-start' }}>\n <Typography variant=\"body1\">\n {quote.status === 'paid'\n ? quote.paidAtMs\n ? `Paid ${dateLabel(quote.paidAtMs)}. Thank you.`\n : 'Paid. Thank you.'\n : `This quote is ${quote.statusLabel.toLowerCase()}.`}\n </Typography>\n {quote.receiptUrl ? (\n <Button variant=\"outlined\" href={quote.receiptUrl}>\n {'View your order'}\n </Button>\n ) : null}\n </Stack>\n </Paper>\n )}\n\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Questions about this quote? Contact ${quote.storeName || 'the business'}.`}\n </Typography>\n </Stack>\n </Box>\n )\n})\nQuotePageBody.displayName = 'QuotePageBody'\n\nexport default QuotePageBody\n\n"],"names":["Aglyn","Alert","Box","Button","Chip","CircularProgress","Divider","Paper","Stack","Typography","forwardRef","lazy","Suspense","useCallback","useEffect","useRef","useState","formatOrderMoney","readStorefrontCheckoutWallets","StorefrontPaymentElementFallback","StorefrontPaymentElement","then","module","default","CONFIRM_POLL_MS","CONFIRM_POLLS","dateLabel","atMs","Date","toLocaleDateString","undefined","month","day","year","addressLines","address","cityLine","city","state","postalCode","filter","Boolean","join","name","line1","line2","country","line","stripeContact","postal_code","AddressBlock","label","lines","spacing","sx","minWidth","flex","variant","color","map","index","TotalRow","value","strong","direction","justifyContent","QuotePageBody","props","ref","heading","rest","nodeSx","Array","isArray","hostId","useSite","siteFetch","useSiteFetch","setState","kind","paying","setPaying","payError","setPayError","nativeCheckout","setNativeCheckout","returned","setReturned","link","quoteId","token","sessionId","load","current","payload","response","encodeURIComponent","json","catch","ok","message","String","error","storeName","view","params","URLSearchParams","window","location","search","get","live","first","status","poll","Promise","resolve","setTimeout","next","pay","globalThis","attemptKey","crypto","randomUUID","now","Math","random","toString","slice","method","headers","body","JSON","stringify","q","t","v","version","clientSecret","publishableKey","wallets","url","assign","replaced","reason","p","border","borderColor","borderRadius","py","textAlign","size","aria-label","component","severity","business","quote","money","cents","currency","unpaid","addresses","customerName","company","billTo","shipTo","block","length","totalRows","totals","itemsCents","discountLabel","discountCents","shippingCents","taxLabel","taxCents","taxAtPayment","shown","alignItems","flexWrap","rowGap","number","statusLabel","dateMs","xs","sm","variantLabel","description","whiteSpace","included","quantity","unitAmountCents","sku","flexShrink","amountCents","totalCents","notes","terms","icon","approvalRequired","payable","fallback","payLabel","defaultShippingAddress","onRestart","onCancel","onClick","disabled","alignSelf","paidAtMs","toLowerCase","receiptUrl","href","displayName"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
*/
|
|
17
|
+
import * as Aglyn from '@aglyn/aglyn';
|
|
18
|
+
export declare const ID: Aglyn.ComponentId;
|
|
19
|
+
export interface QuotePageProps {
|
|
20
|
+
heading?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The customer's quote page (AGL-4094), where a quote's pay link opens: the
|
|
24
|
+
* quote, and its Pay button. See `quote-page-body.tsx`.
|
|
25
|
+
*/
|
|
26
|
+
declare const QuotePage: import("react").ForwardRefExoticComponent<QuotePageProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
27
|
+
export declare const schema: Aglyn.ComponentSchema<QuotePageProps>;
|
|
28
|
+
export declare const presets: Aglyn.PresetSchema[];
|
|
29
|
+
export default QuotePage;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright 2026 Aglyn LLC
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/ import * as Aglyn from "@aglyn/aglyn";
|
|
19
|
+
import { mdiFileDocumentOutline } from "@aglyn/shared-data-mdi";
|
|
20
|
+
import Box from "@mui/material/Box";
|
|
21
|
+
import CircularProgress from "@mui/material/CircularProgress";
|
|
22
|
+
import { forwardRef, lazy, Suspense } from "react";
|
|
23
|
+
import { BUNDLE_ID } from "../constants/bundle-common.js";
|
|
24
|
+
import { QUOTE_PAGE_COMPONENT_ID } from "../constants/quote-page.js";
|
|
25
|
+
import { generatePresetId } from "../utils/generate-preset-id.js";
|
|
26
|
+
// Component ids are persisted in screen documents; never rename.
|
|
27
|
+
export const ID = QUOTE_PAGE_COMPONENT_ID;
|
|
28
|
+
/** The page itself, loaded only where a quote is drawn, so no other page of the site carries it. */ const QuotePageBody = /*#__PURE__*/ lazy(()=>import("./quote-page-body.js"));
|
|
29
|
+
/**
|
|
30
|
+
* The customer's quote page (AGL-4094), where a quote's pay link opens: the
|
|
31
|
+
* quote, and its Pay button. See `quote-page-body.tsx`.
|
|
32
|
+
*/ const QuotePage = /*#__PURE__*/ forwardRef((props, ref)=>/*#__PURE__*/ _jsx(Suspense, {
|
|
33
|
+
fallback: /*#__PURE__*/ _jsx(Box, {
|
|
34
|
+
sx: {
|
|
35
|
+
py: 6,
|
|
36
|
+
textAlign: 'center'
|
|
37
|
+
},
|
|
38
|
+
children: /*#__PURE__*/ _jsx(CircularProgress, {
|
|
39
|
+
size: 28,
|
|
40
|
+
"aria-label": "Loading your quote"
|
|
41
|
+
})
|
|
42
|
+
}),
|
|
43
|
+
children: /*#__PURE__*/ _jsx(QuotePageBody, _extends({
|
|
44
|
+
ref: ref
|
|
45
|
+
}, props))
|
|
46
|
+
}));
|
|
47
|
+
QuotePage.displayName = 'QuotePage';
|
|
48
|
+
export const schema = {
|
|
49
|
+
$id: ID,
|
|
50
|
+
pluginId: BUNDLE_ID,
|
|
51
|
+
displayName: 'Quote',
|
|
52
|
+
description: 'A customer’s quote and its Pay button, opened from the pay link a business sends.',
|
|
53
|
+
category: Aglyn.ComponentCategory.COMMERCE,
|
|
54
|
+
icon: {
|
|
55
|
+
path: mdiFileDocumentOutline.path,
|
|
56
|
+
sx: {
|
|
57
|
+
color: 'primary.main'
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
flags: {
|
|
61
|
+
selfClosing: Aglyn.FEATURE_FLAG.ENABLED
|
|
62
|
+
},
|
|
63
|
+
attributes: [
|
|
64
|
+
{
|
|
65
|
+
name: 'heading',
|
|
66
|
+
label: 'Heading',
|
|
67
|
+
description: 'Shown above the quote. Defaults to the quote number.',
|
|
68
|
+
component: Aglyn.FieldComponentType.TEXT_FIELD
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
};
|
|
72
|
+
export const presets = [
|
|
73
|
+
{
|
|
74
|
+
$id: generatePresetId(ID),
|
|
75
|
+
type: Aglyn.NodeType.PRESET,
|
|
76
|
+
displayName: 'Quote',
|
|
77
|
+
pluginId: BUNDLE_ID,
|
|
78
|
+
description: 'A customer’s quote and its Pay button, for the /pay-quote page',
|
|
79
|
+
category: Aglyn.ComponentCategory.COMMERCE,
|
|
80
|
+
icon: {
|
|
81
|
+
path: mdiFileDocumentOutline.path,
|
|
82
|
+
sx: {
|
|
83
|
+
color: 'primary.main'
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
data: {
|
|
87
|
+
$id: null,
|
|
88
|
+
componentId: ID,
|
|
89
|
+
pluginId: BUNDLE_ID,
|
|
90
|
+
props: {}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
];
|
|
94
|
+
export default QuotePage;
|
|
95
|
+
|
|
96
|
+
//# sourceMappingURL=quote-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/quote-page.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\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiFileDocumentOutline } from '@aglyn/shared-data-mdi'\nimport Box from '@mui/material/Box'\nimport CircularProgress from '@mui/material/CircularProgress'\nimport { forwardRef, lazy, Suspense } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { QUOTE_PAGE_COMPONENT_ID } from '../constants/quote-page'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = QUOTE_PAGE_COMPONENT_ID\n\nexport interface QuotePageProps {\n heading?: string\n}\n\n/** The page itself, loaded only where a quote is drawn, so no other page of the site carries it. */\nconst QuotePageBody = lazy(() => import('./quote-page-body'))\n\n/**\n * The customer's quote page (AGL-4094), where a quote's pay link opens: the\n * quote, and its Pay button. See `quote-page-body.tsx`.\n */\nconst QuotePage = forwardRef<HTMLDivElement, QuotePageProps>((props, ref) => (\n <Suspense\n fallback={\n <Box sx={{ py: 6, textAlign: 'center' }}>\n <CircularProgress size={28} aria-label=\"Loading your quote\" />\n </Box>\n }\n >\n <QuotePageBody ref={ref} {...props} />\n </Suspense>\n))\nQuotePage.displayName = 'QuotePage'\n\nexport const schema: Aglyn.ComponentSchema<QuotePageProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Quote',\n description: 'A customer’s quote and its Pay button, opened from the pay link a business sends.',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiFileDocumentOutline.path, sx: { color: 'primary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'heading',\n label: 'Heading',\n description: 'Shown above the quote. Defaults to the quote number.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Quote',\n pluginId: BUNDLE_ID,\n description: 'A customer’s quote and its Pay button, for the /pay-quote page',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiFileDocumentOutline.path, sx: { color: 'primary.main' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n]\n\nexport default QuotePage\n"],"names":["Aglyn","mdiFileDocumentOutline","Box","CircularProgress","forwardRef","lazy","Suspense","BUNDLE_ID","QUOTE_PAGE_COMPONENT_ID","generatePresetId","ID","QuotePageBody","QuotePage","props","ref","fallback","sx","py","textAlign","size","aria-label","displayName","schema","$id","pluginId","description","category","ComponentCategory","COMMERCE","icon","path","color","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","name","label","component","FieldComponentType","TEXT_FIELD","presets","type","NodeType","PRESET","data","componentId"],"mappings":";;AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,sBAAsB,QAAQ,yBAAwB;AAC/D,OAAOC,SAAS,oBAAmB;AACnC,OAAOC,sBAAsB,iCAAgC;AAC7D,SAASC,UAAU,EAAEC,IAAI,EAAEC,QAAQ,QAAQ,QAAO;AAClD,SAASC,SAAS,QAAQ,gCAA4B;AACtD,SAASC,uBAAuB,QAAQ,6BAAyB;AACjE,SAASC,gBAAgB,QAAQ,iCAA6B;AAE9D,iEAAiE;AACjE,OAAO,MAAMC,KAAwBF,wBAAuB;AAM5D,kGAAkG,GAClG,MAAMG,8BAAgBN,KAAK,IAAM,MAAM,CAAC;AAExC;;;CAGC,GACD,MAAMO,0BAAYR,WAA2C,CAACS,OAAOC,oBACnE,KAACR;QACCS,wBACE,KAACb;YAAIc,IAAI;gBAAEC,IAAI;gBAAGC,WAAW;YAAS;sBACpC,cAAA,KAACf;gBAAiBgB,MAAM;gBAAIC,cAAW;;;kBAI3C,cAAA,KAACT;YAAcG,KAAKA;WAASD;;AAGjCD,UAAUS,WAAW,GAAG;AAExB,OAAO,MAAMC,SAAgD;IAC3DC,KAAKb;IACLc,UAAUjB;IACVc,aAAa;IACbI,aAAa;IACbC,UAAU1B,MAAM2B,iBAAiB,CAACC,QAAQ;IAC1CC,MAAM;QAAEC,MAAM7B,uBAAuB6B,IAAI;QAAEd,IAAI;YAAEe,OAAO;QAAe;IAAE;IACzEC,OAAO;QAAEC,aAAajC,MAAMkC,YAAY,CAACC,OAAO;IAAC;IACjDC,YAAY;QACV;YACEC,MAAM;YACNC,OAAO;YACPb,aAAa;YACbc,WAAWvC,MAAMwC,kBAAkB,CAACC,UAAU;QAChD;KACD;AACH,EAAC;AAED,OAAO,MAAMC,UAAgC;IAC3C;QACEnB,KAAKd,iBAAiBC;QACtBiC,MAAM3C,MAAM4C,QAAQ,CAACC,MAAM;QAC3BxB,aAAa;QACbG,UAAUjB;QACVkB,aAAa;QACbC,UAAU1B,MAAM2B,iBAAiB,CAACC,QAAQ;QAC1CC,MAAM;YAAEC,MAAM7B,uBAAuB6B,IAAI;YAAEd,IAAI;gBAAEe,OAAO;YAAe;QAAE;QACzEe,MAAM;YACJvB,KAAK;YACLwB,aAAarC;YACbc,UAAUjB;YACVM,OAAO,CAAC;QACV;IACF;CACD,CAAA;AAED,eAAeD,UAAS"}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
+
import type { StripeCheckoutContact } from '@stripe/stripe-js';
|
|
17
18
|
import type { StorefrontCheckoutWallets } from '../model/commerce-payment-methods';
|
|
18
19
|
/**
|
|
19
20
|
* The storefront Payment Element (AGL-1944) — the shopper pays on the
|
|
@@ -118,10 +119,15 @@ export interface StorefrontPaymentElementProps {
|
|
|
118
119
|
* Absent means every wallet the device supports shows.
|
|
119
120
|
*/
|
|
120
121
|
wallets?: StorefrontCheckoutWallets;
|
|
122
|
+
/**
|
|
123
|
+
* An address the shipping fields start with, where the session asks for
|
|
124
|
+
* one: a quote's ship-to (AGL-4094). The shopper can still change it.
|
|
125
|
+
*/
|
|
126
|
+
defaultShippingAddress?: StripeCheckoutContact;
|
|
121
127
|
}
|
|
122
128
|
/** Test seam: `loadStripe` caches per key across a file's tests otherwise. */
|
|
123
129
|
export declare function __resetStripePromises(): void;
|
|
124
|
-
export declare function StorefrontPaymentElement({ clientSecret, publishableKey, payLabel, defaultEmail, onCancel, onRestart, wallets, }: StorefrontPaymentElementProps): import("react").JSX.Element;
|
|
130
|
+
export declare function StorefrontPaymentElement({ clientSecret, publishableKey, payLabel, defaultEmail, onCancel, onRestart, wallets, defaultShippingAddress, }: StorefrontPaymentElementProps): import("react").JSX.Element;
|
|
125
131
|
/**
|
|
126
132
|
* Re-exported so the old import path still resolves, but the definition now
|
|
127
133
|
* lives in its own module (AGL-2486): a fallback imported FROM the lazily
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ 'use client';
|
|
17
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
18
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
19
|
// The `/checkout` entry, not the main one: react-stripe-js v6 moved the
|
|
19
20
|
// Checkout Sessions provider, its hook and its own `PaymentElement` there, and
|
|
@@ -430,7 +431,7 @@ function PaymentForm({ payLabel, defaultEmail, onCancel, onRestart, wallets }) {
|
|
|
430
431
|
]
|
|
431
432
|
});
|
|
432
433
|
}
|
|
433
|
-
export function StorefrontPaymentElement({ clientSecret, publishableKey, payLabel, defaultEmail, onCancel, onRestart, wallets }) {
|
|
434
|
+
export function StorefrontPaymentElement({ clientSecret, publishableKey, payLabel, defaultEmail, onCancel, onRestart, wallets, defaultShippingAddress }) {
|
|
434
435
|
const stripe = useMemo(()=>getStripe(publishableKey), [
|
|
435
436
|
publishableKey
|
|
436
437
|
]);
|
|
@@ -440,13 +441,17 @@ export function StorefrontPaymentElement({ clientSecret, publishableKey, payLabe
|
|
|
440
441
|
// suggest a re-theme that never happens.
|
|
441
442
|
const options = useMemo(()=>{
|
|
442
443
|
var _theme_typography;
|
|
443
|
-
return {
|
|
444
|
+
return _extends({
|
|
444
445
|
clientSecret,
|
|
445
446
|
elementsOptions: {
|
|
446
447
|
appearance: storefrontPaymentAppearance(theme),
|
|
447
448
|
fonts: storefrontPaymentFonts(typeof ((_theme_typography = theme.typography) == null ? void 0 : _theme_typography.fontFamily) === 'string' ? theme.typography.fontFamily : undefined)
|
|
448
449
|
}
|
|
449
|
-
}
|
|
450
|
+
}, defaultShippingAddress ? {
|
|
451
|
+
defaultValues: {
|
|
452
|
+
shippingAddress: defaultShippingAddress
|
|
453
|
+
}
|
|
454
|
+
} : {});
|
|
450
455
|
}, // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
451
456
|
[
|
|
452
457
|
clientSecret
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/storefront-payment-element.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\n// The `/checkout` entry, not the main one: react-stripe-js v6 moved the\n// Checkout Sessions provider, its hook and its own `PaymentElement` there, and\n// the main entry's `PaymentElement` is the Elements one, which does not mount\n// under a Checkout provider.\nimport {\n CheckoutElementsProvider,\n PaymentElement,\n ShippingAddressElement,\n useCheckoutElements,\n} from '@stripe/react-stripe-js/checkout'\n// `/pure` deliberately, not the bare entry (AGL-2486). The bare entry ends\n// with a module-scope `Promise.resolve().then(() => getStripePromise())`,\n// so merely EVALUATING it injects the js.stripe.com/v3 script tag —\n// 249 KB gzipped — on any page that reaches this module, whether or not\n// `loadStripe` is ever called. `/pure` is the same API without that\n// statement, so the script is fetched when we ask for it and not before.\nimport { loadStripe } from '@stripe/stripe-js/pure'\n// The TYPE only, and from the bare entry because `/pure` does not re-export\n// it. `import type` is erased entirely at compile time, so this adds no\n// runtime import and cannot re-introduce the script injection above.\nimport type {\n Stripe,\n StripeAddressElementChangeEvent,\n StripeCheckoutElementsSdkOptions,\n} from '@stripe/stripe-js'\nimport Alert from '@mui/material/Alert'\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport CircularProgress from '@mui/material/CircularProgress'\nimport Divider from '@mui/material/Divider'\nimport FormControlLabel from '@mui/material/FormControlLabel'\nimport Radio from '@mui/material/Radio'\nimport RadioGroup from '@mui/material/RadioGroup'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\nimport { useTheme } from '@mui/material/styles'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n storefrontPaymentAppearance,\n storefrontPaymentFonts,\n} from './storefront-payment-appearance'\nimport type { StorefrontCheckoutWallets } from '../model/commerce-payment-methods'\n\n/**\n * The storefront Payment Element (AGL-1944) — the shopper pays on the\n * merchant's own domain instead of being sent to `checkout.stripe.com`.\n *\n * ## THIS COMPONENT DOES NOT FULFIL ANYTHING\n *\n * It has no success callback, and that absence is the design rather than an\n * omission. `libs/plugins/commerce/src/lib/server/billing-webhook.ts` creates\n * the order on `checkout.session.completed` and is the only thing that ever\n * does. An in-page flow makes trusting the browser far more tempting than a\n * redirect does — the form is right there, `confirm()` resolves in hand, and\n * posting \"done, please fulfil\" is one line away — and the browser is the one\n * participant in a payment that can lie about having paid.\n *\n * It is also the participant that can VANISH. A shopper who confirms and\n * closes the tab never runs a callback, so a flow that fulfils here loses the\n * order outright while keeping the money; a shopper who refreshes the return\n * page runs it twice. Neither is reachable from a design with no callback in\n * it. What happens after a successful confirm is a redirect to `return_url`,\n * carrying the `session_id` the server put there — so the page can SHOW the\n * shopper a result by looking the order up, without being TOLD the outcome by\n * this code.\n *\n * ## Declines and 3DS\n *\n * Both were handled for us by the redirect and now have to be handled here.\n *\n * A decline resolves as `{ type: 'error' }` and is rendered inline WITHOUT\n * unmounting the form: the shopper's basket, address and chosen method are all\n * still on screen and they can try another card. That is the whole difference\n * between a recoverable decline and a dead end, and it is why the error state\n * below sits beside the element rather than replacing it.\n *\n * A 3DS challenge is Stripe's own: `confirm()` presents the challenge and, for\n * redirect-based methods, sends the browser to `return_url` and back. Nothing\n * here has to model it — but the button must stay disabled across the whole\n * await, because a second `confirm()` during a challenge is exactly the\n * double-submit the server's idempotency claim exists to survive and there is\n * no reason to make it lean on that.\n *\n * ## Everything the hosted page collected, collected here (AGL-3606)\n *\n * The hosted page asked for an email, a shipping address and a shipping\n * method, and showed a running total. A `ui_mode: 'elements'` session asks\n * for none of that on its own — the Payment Element is only the payment\n * method — so each is mounted here, read off the SESSION rather than off\n * anything the storefront believes:\n *\n * - **Email.** A session created with `customer_email` (the cart's email\n * field) already has one and Stripe refuses `updateEmail` on it, so it is\n * shown read-only. Otherwise a required field, sent with `updateEmail`\n * before `confirm()` — the receipt and the order's customer both come from\n * it, and a sale without one is an order nobody can be told about.\n * - **Shipping.** Only when the session carries shipping options, which the\n * server attaches only when the store ships to the destination. The\n * Shipping Address Element collects the address (and, under Stripe Tax,\n * is where the tax is computed from); the picker lists the session's own\n * options and selects through `updateShippingOption`.\n * - **Tax address without shipping.** Left to the Payment Element, which\n * collects the country and postal code Stripe Tax needs when the session\n * has `automatic_tax` and no shipping address. A Billing Address Element\n * there would make `updateBillingAddress` conflict with it.\n * - **The total.** Rendered from `session.total`, which Stripe recomputes on\n * every address and option change — tax and shipping included — so the\n * number on the button is the number charged.\n * - **The look.** `elementsOptions.appearance` and `fonts` come from the site\n * theme (`storefront-payment-appearance.ts`), so Stripe's frames wear the\n * merchant's palette, radius and typeface.\n *\n * ## Expired and redirect-returned sessions\n *\n * A session that expired, or that cannot be loaded, offers a fresh one through\n * `onRestart` (a NEW attempt key, so the server does not replay the dead\n * session). A redirect-based method that fails comes back to `return_url`\n * with the session still open; `use-checkout-return-notice.ts` reads that on\n * the return page and says nothing was charged.\n */\n\nexport interface StorefrontPaymentElementProps {\n /** Stripe's instruction to the browser. Not a claim that money moved. */\n clientSecret: string\n publishableKey: string\n /** Shown on the pay button, so the merchant's own total reads through. */\n payLabel?: string\n /**\n * An email the storefront already knows but did not put on the session\n * (it prefills the field; the shopper can still change it). A session the\n * server created WITH an email shows that one read-only instead.\n */\n defaultEmail?: string\n /** Abandon the in-page form and return to the store. */\n onCancel?: () => void\n /**\n * Open a fresh session after this one expired or could not be loaded. The\n * caller must mint a NEW attempt key first, or the server replays the dead\n * session for the old one.\n */\n onRestart?: () => void\n /**\n * The wallets the merchant hid (AGL-3629). Stripe cannot exclude Apple Pay,\n * Google Pay or Link on the session, so the Payment Element is told instead.\n * Absent means every wallet the device supports shows.\n */\n wallets?: StorefrontCheckoutWallets\n}\n\n/**\n * `loadStripe` injects a script tag, so it is memoized per key rather than\n * called per render. Keyed because a page could in principle mount this for\n * two different merchants; in practice it is one, and the map costs nothing.\n */\nconst stripePromises = new Map<string, Promise<Stripe | null>>()\nfunction getStripe(publishableKey: string): Promise<Stripe | null> | null {\n if (!publishableKey) return null\n let promise = stripePromises.get(publishableKey)\n if (!promise) {\n promise = loadStripe(publishableKey)\n stripePromises.set(publishableKey, promise)\n }\n return promise\n}\n\n/** Test seam: `loadStripe` caches per key across a file's tests otherwise. */\nexport function __resetStripePromises(): void {\n stripePromises.clear()\n}\n\nconst GENERIC_FAILURE = 'That payment could not be completed. Try another card.'\n\n/** How long the address must sit still before the session re-prices on it. */\nconst ADDRESS_SETTLE_MS = 400\n\nfunction SummaryRow({\n label,\n value,\n strong,\n}: {\n label: string\n value: string\n strong?: boolean\n}) {\n return (\n <Box sx={{ display: 'flex', justifyContent: 'space-between', gap: 2 }}>\n <Typography\n variant={strong ? 'subtitle1' : 'body2'}\n color={strong ? 'text.primary' : 'text.secondary'}\n >\n {label}\n </Typography>\n <Typography variant={strong ? 'subtitle1' : 'body2'}>{value}</Typography>\n </Box>\n )\n}\n\nfunction SessionNotice({\n message,\n onCancel,\n onRestart,\n}: {\n message: string\n onCancel?: () => void\n onRestart?: () => void\n}) {\n return (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Alert severity=\"error\" role=\"alert\">\n {message}\n </Alert>\n <Box sx={{ display: 'flex', gap: 1 }}>\n {onRestart ? (\n <Button variant=\"contained\" onClick={onRestart}>\n {'Start checkout again'}\n </Button>\n ) : null}\n {onCancel ? (\n <Button variant=\"text\" onClick={onCancel}>\n {'Back to the store'}\n </Button>\n ) : null}\n </Box>\n </Box>\n )\n}\n\nfunction PaymentForm({\n payLabel,\n defaultEmail,\n onCancel,\n onRestart,\n wallets,\n}: Pick<\n StorefrontPaymentElementProps,\n 'payLabel' | 'defaultEmail' | 'onCancel' | 'onRestart' | 'wallets'\n>) {\n // A `{ type: 'loading' | 'success' | 'error' }` union since react-stripe-js\n // v6: the session is not usable until Stripe.js has booted against it.\n const checkoutState = useCheckoutElements()\n const checkout =\n checkoutState.type === 'success' ? checkoutState.checkout : null\n const [status, setStatus] = useState<'idle' | 'confirming' | 'error'>('idle')\n const [message, setMessage] = useState('')\n const [email, setEmail] = useState(defaultEmail ?? '')\n const [emailError, setEmailError] = useState('')\n const [shippingBusy, setShippingBusy] = useState(false)\n\n /**\n * The email the SERVER put on the session, latched at first load. Stripe\n * refuses `updateEmail` on such a session, so it is shown, not edited. Read\n * once rather than live because the session's email also becomes set the\n * moment THIS form calls `updateEmail` — which must not turn the field the\n * shopper is typing in into read-only text.\n */\n const serverEmail = useRef<string | null | undefined>(undefined)\n if (checkout && serverEmail.current === undefined) {\n serverEmail.current = checkout.email ? String(checkout.email) : null\n }\n const emailLocked = Boolean(serverEmail.current)\n\n const sessionShippingOptions = checkout?.shippingOptions\n const shippingOptions = useMemo(\n () => sessionShippingOptions ?? [],\n [sessionShippingOptions],\n )\n const collectsShipping = shippingOptions.length > 0\n const selectedShippingId = checkout?.shipping?.shippingOption?.id ?? ''\n\n // The hosted page pre-selects the first rate, so the in-page one does too:\n // a session with options and no selection cannot be confirmed, and making\n // the shopper discover that at the pay button is a decline they did not\n // cause. Once per session — a later deselect is not something Stripe does.\n const autoSelected = useRef(false)\n useEffect(() => {\n if (!checkout || !collectsShipping || selectedShippingId) return\n if (autoSelected.current) return\n autoSelected.current = true\n void checkout.updateShippingOption(shippingOptions[0].id)\n }, [checkout, collectsShipping, selectedShippingId, shippingOptions])\n\n const chooseShipping = useCallback(\n async (id: string) => {\n if (!checkout || id === selectedShippingId) return\n setShippingBusy(true)\n try {\n const result = await checkout.updateShippingOption(id)\n if (result?.type === 'error') {\n setMessage(\n String(result.error?.message ?? '') ||\n 'That shipping option could not be selected.',\n )\n }\n } finally {\n setShippingBusy(false)\n }\n },\n [checkout, selectedShippingId],\n )\n\n // Re-price on the address once it is complete and has stopped changing:\n // the Address Element fires per keystroke, and under Stripe Tax every\n // update is a tax calculation. Confirm sends the element's value anyway, so\n // this exists for the TOTAL — the shopper sees tax before they pay.\n const addressTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n useEffect(\n () => () => {\n if (addressTimer.current) clearTimeout(addressTimer.current)\n },\n [],\n )\n const onShippingAddressChange = useCallback(\n (event: StripeAddressElementChangeEvent) => {\n if (!checkout || !event.complete) return\n if (addressTimer.current) clearTimeout(addressTimer.current)\n const { name, address } = event.value\n addressTimer.current = setTimeout(() => {\n void checkout\n .updateShippingAddress({ name, address })\n .catch(() => undefined)\n }, ADDRESS_SETTLE_MS)\n },\n [checkout],\n )\n\n const commitEmail = useCallback(async (): Promise<boolean> => {\n if (!checkout || emailLocked) return true\n const trimmed = email.trim()\n if (!trimmed) {\n setEmailError('Enter your email for the receipt.')\n return false\n }\n if (checkout.email === trimmed) return true\n const result = await checkout.updateEmail(trimmed)\n if (result?.type === 'error') {\n setEmailError(\n String(result.error?.message ?? '') || 'Enter a valid email address.',\n )\n return false\n }\n setEmailError('')\n return true\n }, [checkout, email, emailLocked])\n\n const handlePay = useCallback(async () => {\n // Guarded rather than merely disabled: a disabled button is a rendering\n // fact, and a keyboard or a slow re-render can still land a second call\n // during a 3DS challenge.\n if (status === 'confirming' || !checkout) return\n setStatus('confirming')\n setMessage('')\n try {\n if (!(await commitEmail())) {\n setStatus('idle')\n return\n }\n const result = await checkout.confirm()\n if (result?.type === 'error') {\n // A decline, an expired card, an incomplete field, a failed 3DS\n // challenge. Recoverable: the form stays mounted with everything the\n // shopper typed still in it.\n setMessage(String(result.error?.message ?? '') || GENERIC_FAILURE)\n setStatus('error')\n return\n }\n // Success does NOT mean fulfilled, and nothing is reported to our server\n // here. Stripe redirects to the `return_url` the session carries; if it\n // has not by the time this resolves, the confirm is still in flight and\n // the button stays disabled rather than inviting a second attempt.\n } catch {\n // A throw is an integration error on Stripe's side of the contract, not\n // something the shopper did; its text is not written for them.\n setMessage(GENERIC_FAILURE)\n setStatus('error')\n }\n }, [checkout, commitEmail, status])\n\n if (checkoutState.type === 'error') {\n // The session could not be loaded — expired, or already completed in\n // another tab. Nothing typed here could be charged, so say so and offer a\n // fresh session or the way back rather than a form that cannot submit.\n return (\n <SessionNotice\n message=\"This checkout could not be loaded. Start again, or return to the store.\"\n onCancel={onCancel}\n onRestart={onRestart}\n />\n )\n }\n\n if (!checkout) {\n return (\n <Box sx={{ display: 'flex', justifyContent: 'center', py: 3 }}>\n <CircularProgress size={28} aria-label=\"Loading checkout\" />\n </Box>\n )\n }\n\n if (checkout.status?.type === 'expired') {\n return (\n <SessionNotice\n message=\"This checkout expired before it was paid. Nothing was charged.\"\n onCancel={onCancel}\n onRestart={onRestart}\n />\n )\n }\n\n if (checkout.status?.type === 'complete') {\n // Paid in another tab, or the return redirect is already under way.\n // Offering a restart here would be offering to charge twice.\n return (\n <Alert severity=\"success\" role=\"status\">\n {'This order is already paid.'}\n </Alert>\n )\n }\n\n const total = checkout.total\n const busy = status === 'confirming'\n const discount = total?.discount?.minorUnitsAmount ?? 0\n const tax = total?.taxExclusive?.minorUnitsAmount ?? 0\n const taxPending = checkout.tax?.status && checkout.tax.status !== 'ready'\n\n return (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n {emailLocked ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Receipt to ${serverEmail.current}`}\n </Typography>\n ) : (\n <TextField\n type=\"email\"\n label=\"Email\"\n required\n fullWidth\n autoComplete=\"email\"\n value={email}\n onChange={(event) => {\n setEmail(event.target.value)\n if (emailError) setEmailError('')\n }}\n onBlur={() => {\n if (email.trim()) void commitEmail()\n }}\n error={Boolean(emailError)}\n helperText={emailError || 'Your receipt is sent here.'}\n disabled={busy}\n />\n )}\n {collectsShipping ? (\n <Box\n sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}\n data-testid=\"storefront-shipping\"\n >\n <Typography variant=\"subtitle2\">{'Shipping address'}</Typography>\n <ShippingAddressElement onChange={onShippingAddressChange} />\n <Typography variant=\"subtitle2\">{'Shipping method'}</Typography>\n <RadioGroup\n aria-label=\"Shipping method\"\n value={selectedShippingId}\n onChange={(event) => void chooseShipping(event.target.value)}\n >\n {shippingOptions.map((option) => (\n <FormControlLabel\n key={option.id}\n value={option.id}\n disabled={busy || shippingBusy}\n control={<Radio />}\n label={`${option.displayName || 'Shipping'} — ${option.amount}`}\n />\n ))}\n </RadioGroup>\n </Box>\n ) : null}\n <PaymentElement options={wallets ? { wallets } : undefined} />\n {total ? (\n <Box\n sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}\n data-testid=\"storefront-checkout-total\"\n >\n <SummaryRow label=\"Subtotal\" value={total.subtotal.amount} />\n {discount ? (\n <SummaryRow label=\"Discount\" value={`−${total.discount.amount}`} />\n ) : null}\n {collectsShipping ? (\n <SummaryRow\n label=\"Shipping\"\n value={selectedShippingId ? total.shippingRate.amount : '—'}\n />\n ) : null}\n {tax || taxPending ? (\n <SummaryRow\n label=\"Tax\"\n value={taxPending ? 'Calculated from your address' : total.taxExclusive.amount}\n />\n ) : null}\n <Divider />\n <SummaryRow label=\"Total\" value={total.total.amount} strong />\n </Box>\n ) : null}\n {message ? (\n // `error` severity is right here and wrong for a lockdown pause — this\n // really is \"your payment did not go through\", which is the one thing\n // a shopper must be told plainly.\n <Alert severity=\"error\" role=\"alert\">\n {message}\n </Alert>\n ) : null}\n <Box sx={{ display: 'flex', gap: 1 }}>\n <Button\n variant=\"contained\"\n onClick={handlePay}\n disabled={busy || shippingBusy}\n fullWidth\n >\n {busy ? 'Paying…' : payLabel || 'Pay now'}\n </Button>\n {onCancel ? (\n <Button variant=\"text\" onClick={onCancel} disabled={busy}>\n {'Cancel'}\n </Button>\n ) : null}\n </Box>\n </Box>\n )\n}\n\nexport function StorefrontPaymentElement({\n clientSecret,\n publishableKey,\n payLabel,\n defaultEmail,\n onCancel,\n onRestart,\n wallets,\n}: StorefrontPaymentElementProps) {\n const stripe = useMemo(() => getStripe(publishableKey), [publishableKey])\n const theme = useTheme()\n // Built once per session: the provider reads its options at mount and\n // ignores later changes, so a memo keyed on the theme as well would only\n // suggest a re-theme that never happens.\n const options = useMemo<StripeCheckoutElementsSdkOptions>(\n () => ({\n clientSecret,\n elementsOptions: {\n appearance: storefrontPaymentAppearance(theme),\n fonts: storefrontPaymentFonts(\n typeof theme.typography?.fontFamily === 'string'\n ? theme.typography.fontFamily\n : undefined,\n ),\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [clientSecret],\n )\n // Belt and braces with the server's own gate, which already refuses native\n // mode without a publishable key: rendering nothing beats rendering an empty\n // white box that looks like a broken payment form.\n if (!clientSecret || !stripe) return null\n return (\n <CheckoutElementsProvider stripe={stripe} options={options}>\n <Box sx={{ py: 2 }} data-testid=\"storefront-payment-element\">\n <PaymentForm\n payLabel={payLabel}\n defaultEmail={defaultEmail}\n onCancel={onCancel}\n onRestart={onRestart}\n wallets={wallets}\n />\n </Box>\n </CheckoutElementsProvider>\n )\n}\n\n/**\n * Re-exported so the old import path still resolves, but the definition now\n * lives in its own module (AGL-2486): a fallback imported FROM the lazily\n * loaded module drags that module into the eager bundle and cancels the\n * `lazy()`. Callers must import it from `./storefront-payment-element-fallback`\n * — importing it from here is exactly the bug, and re-exporting it does not\n * make that safe.\n */\nexport { StorefrontPaymentElementFallback } from './storefront-payment-element-fallback'\n\nexport default StorefrontPaymentElement\n"],"names":["CheckoutElementsProvider","PaymentElement","ShippingAddressElement","useCheckoutElements","loadStripe","Alert","Box","Button","CircularProgress","Divider","FormControlLabel","Radio","RadioGroup","TextField","Typography","useTheme","useCallback","useEffect","useMemo","useRef","useState","storefrontPaymentAppearance","storefrontPaymentFonts","stripePromises","Map","getStripe","publishableKey","promise","get","set","__resetStripePromises","clear","GENERIC_FAILURE","ADDRESS_SETTLE_MS","SummaryRow","label","value","strong","sx","display","justifyContent","gap","variant","color","SessionNotice","message","onCancel","onRestart","flexDirection","severity","role","onClick","PaymentForm","payLabel","defaultEmail","wallets","checkout","total","checkoutState","type","status","setStatus","setMessage","email","setEmail","emailError","setEmailError","shippingBusy","setShippingBusy","serverEmail","undefined","current","String","emailLocked","Boolean","sessionShippingOptions","shippingOptions","collectsShipping","length","selectedShippingId","shipping","shippingOption","id","autoSelected","updateShippingOption","chooseShipping","result","error","addressTimer","clearTimeout","onShippingAddressChange","event","complete","name","address","setTimeout","updateShippingAddress","catch","commitEmail","trimmed","trim","updateEmail","handlePay","confirm","py","size","aria-label","busy","discount","minorUnitsAmount","tax","taxExclusive","taxPending","required","fullWidth","autoComplete","onChange","target","onBlur","helperText","disabled","data-testid","map","option","control","displayName","amount","options","subtotal","shippingRate","StorefrontPaymentElement","clientSecret","stripe","theme","elementsOptions","appearance","fonts","typography","fontFamily","StorefrontPaymentElementFallback"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/storefront-payment-element.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\n// The `/checkout` entry, not the main one: react-stripe-js v6 moved the\n// Checkout Sessions provider, its hook and its own `PaymentElement` there, and\n// the main entry's `PaymentElement` is the Elements one, which does not mount\n// under a Checkout provider.\nimport {\n CheckoutElementsProvider,\n PaymentElement,\n ShippingAddressElement,\n useCheckoutElements,\n} from '@stripe/react-stripe-js/checkout'\n// `/pure` deliberately, not the bare entry (AGL-2486). The bare entry ends\n// with a module-scope `Promise.resolve().then(() => getStripePromise())`,\n// so merely EVALUATING it injects the js.stripe.com/v3 script tag —\n// 249 KB gzipped — on any page that reaches this module, whether or not\n// `loadStripe` is ever called. `/pure` is the same API without that\n// statement, so the script is fetched when we ask for it and not before.\nimport { loadStripe } from '@stripe/stripe-js/pure'\n// The TYPE only, and from the bare entry because `/pure` does not re-export\n// it. `import type` is erased entirely at compile time, so this adds no\n// runtime import and cannot re-introduce the script injection above.\nimport type {\n Stripe,\n StripeAddressElementChangeEvent,\n StripeCheckoutContact,\n StripeCheckoutElementsSdkOptions,\n} from '@stripe/stripe-js'\nimport Alert from '@mui/material/Alert'\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport CircularProgress from '@mui/material/CircularProgress'\nimport Divider from '@mui/material/Divider'\nimport FormControlLabel from '@mui/material/FormControlLabel'\nimport Radio from '@mui/material/Radio'\nimport RadioGroup from '@mui/material/RadioGroup'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\nimport { useTheme } from '@mui/material/styles'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n storefrontPaymentAppearance,\n storefrontPaymentFonts,\n} from './storefront-payment-appearance'\nimport type { StorefrontCheckoutWallets } from '../model/commerce-payment-methods'\n\n/**\n * The storefront Payment Element (AGL-1944) — the shopper pays on the\n * merchant's own domain instead of being sent to `checkout.stripe.com`.\n *\n * ## THIS COMPONENT DOES NOT FULFIL ANYTHING\n *\n * It has no success callback, and that absence is the design rather than an\n * omission. `libs/plugins/commerce/src/lib/server/billing-webhook.ts` creates\n * the order on `checkout.session.completed` and is the only thing that ever\n * does. An in-page flow makes trusting the browser far more tempting than a\n * redirect does — the form is right there, `confirm()` resolves in hand, and\n * posting \"done, please fulfil\" is one line away — and the browser is the one\n * participant in a payment that can lie about having paid.\n *\n * It is also the participant that can VANISH. A shopper who confirms and\n * closes the tab never runs a callback, so a flow that fulfils here loses the\n * order outright while keeping the money; a shopper who refreshes the return\n * page runs it twice. Neither is reachable from a design with no callback in\n * it. What happens after a successful confirm is a redirect to `return_url`,\n * carrying the `session_id` the server put there — so the page can SHOW the\n * shopper a result by looking the order up, without being TOLD the outcome by\n * this code.\n *\n * ## Declines and 3DS\n *\n * Both were handled for us by the redirect and now have to be handled here.\n *\n * A decline resolves as `{ type: 'error' }` and is rendered inline WITHOUT\n * unmounting the form: the shopper's basket, address and chosen method are all\n * still on screen and they can try another card. That is the whole difference\n * between a recoverable decline and a dead end, and it is why the error state\n * below sits beside the element rather than replacing it.\n *\n * A 3DS challenge is Stripe's own: `confirm()` presents the challenge and, for\n * redirect-based methods, sends the browser to `return_url` and back. Nothing\n * here has to model it — but the button must stay disabled across the whole\n * await, because a second `confirm()` during a challenge is exactly the\n * double-submit the server's idempotency claim exists to survive and there is\n * no reason to make it lean on that.\n *\n * ## Everything the hosted page collected, collected here (AGL-3606)\n *\n * The hosted page asked for an email, a shipping address and a shipping\n * method, and showed a running total. A `ui_mode: 'elements'` session asks\n * for none of that on its own — the Payment Element is only the payment\n * method — so each is mounted here, read off the SESSION rather than off\n * anything the storefront believes:\n *\n * - **Email.** A session created with `customer_email` (the cart's email\n * field) already has one and Stripe refuses `updateEmail` on it, so it is\n * shown read-only. Otherwise a required field, sent with `updateEmail`\n * before `confirm()` — the receipt and the order's customer both come from\n * it, and a sale without one is an order nobody can be told about.\n * - **Shipping.** Only when the session carries shipping options, which the\n * server attaches only when the store ships to the destination. The\n * Shipping Address Element collects the address (and, under Stripe Tax,\n * is where the tax is computed from); the picker lists the session's own\n * options and selects through `updateShippingOption`.\n * - **Tax address without shipping.** Left to the Payment Element, which\n * collects the country and postal code Stripe Tax needs when the session\n * has `automatic_tax` and no shipping address. A Billing Address Element\n * there would make `updateBillingAddress` conflict with it.\n * - **The total.** Rendered from `session.total`, which Stripe recomputes on\n * every address and option change — tax and shipping included — so the\n * number on the button is the number charged.\n * - **The look.** `elementsOptions.appearance` and `fonts` come from the site\n * theme (`storefront-payment-appearance.ts`), so Stripe's frames wear the\n * merchant's palette, radius and typeface.\n *\n * ## Expired and redirect-returned sessions\n *\n * A session that expired, or that cannot be loaded, offers a fresh one through\n * `onRestart` (a NEW attempt key, so the server does not replay the dead\n * session). A redirect-based method that fails comes back to `return_url`\n * with the session still open; `use-checkout-return-notice.ts` reads that on\n * the return page and says nothing was charged.\n */\n\nexport interface StorefrontPaymentElementProps {\n /** Stripe's instruction to the browser. Not a claim that money moved. */\n clientSecret: string\n publishableKey: string\n /** Shown on the pay button, so the merchant's own total reads through. */\n payLabel?: string\n /**\n * An email the storefront already knows but did not put on the session\n * (it prefills the field; the shopper can still change it). A session the\n * server created WITH an email shows that one read-only instead.\n */\n defaultEmail?: string\n /** Abandon the in-page form and return to the store. */\n onCancel?: () => void\n /**\n * Open a fresh session after this one expired or could not be loaded. The\n * caller must mint a NEW attempt key first, or the server replays the dead\n * session for the old one.\n */\n onRestart?: () => void\n /**\n * The wallets the merchant hid (AGL-3629). Stripe cannot exclude Apple Pay,\n * Google Pay or Link on the session, so the Payment Element is told instead.\n * Absent means every wallet the device supports shows.\n */\n wallets?: StorefrontCheckoutWallets\n /**\n * An address the shipping fields start with, where the session asks for\n * one: a quote's ship-to (AGL-4094). The shopper can still change it.\n */\n defaultShippingAddress?: StripeCheckoutContact\n}\n\n/**\n * `loadStripe` injects a script tag, so it is memoized per key rather than\n * called per render. Keyed because a page could in principle mount this for\n * two different merchants; in practice it is one, and the map costs nothing.\n */\nconst stripePromises = new Map<string, Promise<Stripe | null>>()\nfunction getStripe(publishableKey: string): Promise<Stripe | null> | null {\n if (!publishableKey) return null\n let promise = stripePromises.get(publishableKey)\n if (!promise) {\n promise = loadStripe(publishableKey)\n stripePromises.set(publishableKey, promise)\n }\n return promise\n}\n\n/** Test seam: `loadStripe` caches per key across a file's tests otherwise. */\nexport function __resetStripePromises(): void {\n stripePromises.clear()\n}\n\nconst GENERIC_FAILURE = 'That payment could not be completed. Try another card.'\n\n/** How long the address must sit still before the session re-prices on it. */\nconst ADDRESS_SETTLE_MS = 400\n\nfunction SummaryRow({\n label,\n value,\n strong,\n}: {\n label: string\n value: string\n strong?: boolean\n}) {\n return (\n <Box sx={{ display: 'flex', justifyContent: 'space-between', gap: 2 }}>\n <Typography\n variant={strong ? 'subtitle1' : 'body2'}\n color={strong ? 'text.primary' : 'text.secondary'}\n >\n {label}\n </Typography>\n <Typography variant={strong ? 'subtitle1' : 'body2'}>{value}</Typography>\n </Box>\n )\n}\n\nfunction SessionNotice({\n message,\n onCancel,\n onRestart,\n}: {\n message: string\n onCancel?: () => void\n onRestart?: () => void\n}) {\n return (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Alert severity=\"error\" role=\"alert\">\n {message}\n </Alert>\n <Box sx={{ display: 'flex', gap: 1 }}>\n {onRestart ? (\n <Button variant=\"contained\" onClick={onRestart}>\n {'Start checkout again'}\n </Button>\n ) : null}\n {onCancel ? (\n <Button variant=\"text\" onClick={onCancel}>\n {'Back to the store'}\n </Button>\n ) : null}\n </Box>\n </Box>\n )\n}\n\nfunction PaymentForm({\n payLabel,\n defaultEmail,\n onCancel,\n onRestart,\n wallets,\n}: Pick<\n StorefrontPaymentElementProps,\n 'payLabel' | 'defaultEmail' | 'onCancel' | 'onRestart' | 'wallets'\n>) {\n // A `{ type: 'loading' | 'success' | 'error' }` union since react-stripe-js\n // v6: the session is not usable until Stripe.js has booted against it.\n const checkoutState = useCheckoutElements()\n const checkout =\n checkoutState.type === 'success' ? checkoutState.checkout : null\n const [status, setStatus] = useState<'idle' | 'confirming' | 'error'>('idle')\n const [message, setMessage] = useState('')\n const [email, setEmail] = useState(defaultEmail ?? '')\n const [emailError, setEmailError] = useState('')\n const [shippingBusy, setShippingBusy] = useState(false)\n\n /**\n * The email the SERVER put on the session, latched at first load. Stripe\n * refuses `updateEmail` on such a session, so it is shown, not edited. Read\n * once rather than live because the session's email also becomes set the\n * moment THIS form calls `updateEmail` — which must not turn the field the\n * shopper is typing in into read-only text.\n */\n const serverEmail = useRef<string | null | undefined>(undefined)\n if (checkout && serverEmail.current === undefined) {\n serverEmail.current = checkout.email ? String(checkout.email) : null\n }\n const emailLocked = Boolean(serverEmail.current)\n\n const sessionShippingOptions = checkout?.shippingOptions\n const shippingOptions = useMemo(\n () => sessionShippingOptions ?? [],\n [sessionShippingOptions],\n )\n const collectsShipping = shippingOptions.length > 0\n const selectedShippingId = checkout?.shipping?.shippingOption?.id ?? ''\n\n // The hosted page pre-selects the first rate, so the in-page one does too:\n // a session with options and no selection cannot be confirmed, and making\n // the shopper discover that at the pay button is a decline they did not\n // cause. Once per session — a later deselect is not something Stripe does.\n const autoSelected = useRef(false)\n useEffect(() => {\n if (!checkout || !collectsShipping || selectedShippingId) return\n if (autoSelected.current) return\n autoSelected.current = true\n void checkout.updateShippingOption(shippingOptions[0].id)\n }, [checkout, collectsShipping, selectedShippingId, shippingOptions])\n\n const chooseShipping = useCallback(\n async (id: string) => {\n if (!checkout || id === selectedShippingId) return\n setShippingBusy(true)\n try {\n const result = await checkout.updateShippingOption(id)\n if (result?.type === 'error') {\n setMessage(\n String(result.error?.message ?? '') ||\n 'That shipping option could not be selected.',\n )\n }\n } finally {\n setShippingBusy(false)\n }\n },\n [checkout, selectedShippingId],\n )\n\n // Re-price on the address once it is complete and has stopped changing:\n // the Address Element fires per keystroke, and under Stripe Tax every\n // update is a tax calculation. Confirm sends the element's value anyway, so\n // this exists for the TOTAL — the shopper sees tax before they pay.\n const addressTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n useEffect(\n () => () => {\n if (addressTimer.current) clearTimeout(addressTimer.current)\n },\n [],\n )\n const onShippingAddressChange = useCallback(\n (event: StripeAddressElementChangeEvent) => {\n if (!checkout || !event.complete) return\n if (addressTimer.current) clearTimeout(addressTimer.current)\n const { name, address } = event.value\n addressTimer.current = setTimeout(() => {\n void checkout\n .updateShippingAddress({ name, address })\n .catch(() => undefined)\n }, ADDRESS_SETTLE_MS)\n },\n [checkout],\n )\n\n const commitEmail = useCallback(async (): Promise<boolean> => {\n if (!checkout || emailLocked) return true\n const trimmed = email.trim()\n if (!trimmed) {\n setEmailError('Enter your email for the receipt.')\n return false\n }\n if (checkout.email === trimmed) return true\n const result = await checkout.updateEmail(trimmed)\n if (result?.type === 'error') {\n setEmailError(\n String(result.error?.message ?? '') || 'Enter a valid email address.',\n )\n return false\n }\n setEmailError('')\n return true\n }, [checkout, email, emailLocked])\n\n const handlePay = useCallback(async () => {\n // Guarded rather than merely disabled: a disabled button is a rendering\n // fact, and a keyboard or a slow re-render can still land a second call\n // during a 3DS challenge.\n if (status === 'confirming' || !checkout) return\n setStatus('confirming')\n setMessage('')\n try {\n if (!(await commitEmail())) {\n setStatus('idle')\n return\n }\n const result = await checkout.confirm()\n if (result?.type === 'error') {\n // A decline, an expired card, an incomplete field, a failed 3DS\n // challenge. Recoverable: the form stays mounted with everything the\n // shopper typed still in it.\n setMessage(String(result.error?.message ?? '') || GENERIC_FAILURE)\n setStatus('error')\n return\n }\n // Success does NOT mean fulfilled, and nothing is reported to our server\n // here. Stripe redirects to the `return_url` the session carries; if it\n // has not by the time this resolves, the confirm is still in flight and\n // the button stays disabled rather than inviting a second attempt.\n } catch {\n // A throw is an integration error on Stripe's side of the contract, not\n // something the shopper did; its text is not written for them.\n setMessage(GENERIC_FAILURE)\n setStatus('error')\n }\n }, [checkout, commitEmail, status])\n\n if (checkoutState.type === 'error') {\n // The session could not be loaded — expired, or already completed in\n // another tab. Nothing typed here could be charged, so say so and offer a\n // fresh session or the way back rather than a form that cannot submit.\n return (\n <SessionNotice\n message=\"This checkout could not be loaded. Start again, or return to the store.\"\n onCancel={onCancel}\n onRestart={onRestart}\n />\n )\n }\n\n if (!checkout) {\n return (\n <Box sx={{ display: 'flex', justifyContent: 'center', py: 3 }}>\n <CircularProgress size={28} aria-label=\"Loading checkout\" />\n </Box>\n )\n }\n\n if (checkout.status?.type === 'expired') {\n return (\n <SessionNotice\n message=\"This checkout expired before it was paid. Nothing was charged.\"\n onCancel={onCancel}\n onRestart={onRestart}\n />\n )\n }\n\n if (checkout.status?.type === 'complete') {\n // Paid in another tab, or the return redirect is already under way.\n // Offering a restart here would be offering to charge twice.\n return (\n <Alert severity=\"success\" role=\"status\">\n {'This order is already paid.'}\n </Alert>\n )\n }\n\n const total = checkout.total\n const busy = status === 'confirming'\n const discount = total?.discount?.minorUnitsAmount ?? 0\n const tax = total?.taxExclusive?.minorUnitsAmount ?? 0\n const taxPending = checkout.tax?.status && checkout.tax.status !== 'ready'\n\n return (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n {emailLocked ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Receipt to ${serverEmail.current}`}\n </Typography>\n ) : (\n <TextField\n type=\"email\"\n label=\"Email\"\n required\n fullWidth\n autoComplete=\"email\"\n value={email}\n onChange={(event) => {\n setEmail(event.target.value)\n if (emailError) setEmailError('')\n }}\n onBlur={() => {\n if (email.trim()) void commitEmail()\n }}\n error={Boolean(emailError)}\n helperText={emailError || 'Your receipt is sent here.'}\n disabled={busy}\n />\n )}\n {collectsShipping ? (\n <Box\n sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}\n data-testid=\"storefront-shipping\"\n >\n <Typography variant=\"subtitle2\">{'Shipping address'}</Typography>\n <ShippingAddressElement onChange={onShippingAddressChange} />\n <Typography variant=\"subtitle2\">{'Shipping method'}</Typography>\n <RadioGroup\n aria-label=\"Shipping method\"\n value={selectedShippingId}\n onChange={(event) => void chooseShipping(event.target.value)}\n >\n {shippingOptions.map((option) => (\n <FormControlLabel\n key={option.id}\n value={option.id}\n disabled={busy || shippingBusy}\n control={<Radio />}\n label={`${option.displayName || 'Shipping'} — ${option.amount}`}\n />\n ))}\n </RadioGroup>\n </Box>\n ) : null}\n <PaymentElement options={wallets ? { wallets } : undefined} />\n {total ? (\n <Box\n sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}\n data-testid=\"storefront-checkout-total\"\n >\n <SummaryRow label=\"Subtotal\" value={total.subtotal.amount} />\n {discount ? (\n <SummaryRow label=\"Discount\" value={`−${total.discount.amount}`} />\n ) : null}\n {collectsShipping ? (\n <SummaryRow\n label=\"Shipping\"\n value={selectedShippingId ? total.shippingRate.amount : '—'}\n />\n ) : null}\n {tax || taxPending ? (\n <SummaryRow\n label=\"Tax\"\n value={taxPending ? 'Calculated from your address' : total.taxExclusive.amount}\n />\n ) : null}\n <Divider />\n <SummaryRow label=\"Total\" value={total.total.amount} strong />\n </Box>\n ) : null}\n {message ? (\n // `error` severity is right here and wrong for a lockdown pause — this\n // really is \"your payment did not go through\", which is the one thing\n // a shopper must be told plainly.\n <Alert severity=\"error\" role=\"alert\">\n {message}\n </Alert>\n ) : null}\n <Box sx={{ display: 'flex', gap: 1 }}>\n <Button\n variant=\"contained\"\n onClick={handlePay}\n disabled={busy || shippingBusy}\n fullWidth\n >\n {busy ? 'Paying…' : payLabel || 'Pay now'}\n </Button>\n {onCancel ? (\n <Button variant=\"text\" onClick={onCancel} disabled={busy}>\n {'Cancel'}\n </Button>\n ) : null}\n </Box>\n </Box>\n )\n}\n\nexport function StorefrontPaymentElement({\n clientSecret,\n publishableKey,\n payLabel,\n defaultEmail,\n onCancel,\n onRestart,\n wallets,\n defaultShippingAddress,\n}: StorefrontPaymentElementProps) {\n const stripe = useMemo(() => getStripe(publishableKey), [publishableKey])\n const theme = useTheme()\n // Built once per session: the provider reads its options at mount and\n // ignores later changes, so a memo keyed on the theme as well would only\n // suggest a re-theme that never happens.\n const options = useMemo<StripeCheckoutElementsSdkOptions>(\n () => ({\n clientSecret,\n elementsOptions: {\n appearance: storefrontPaymentAppearance(theme),\n fonts: storefrontPaymentFonts(\n typeof theme.typography?.fontFamily === 'string'\n ? theme.typography.fontFamily\n : undefined,\n ),\n },\n ...(defaultShippingAddress ? { defaultValues: { shippingAddress: defaultShippingAddress } } : {}),\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [clientSecret],\n )\n // Belt and braces with the server's own gate, which already refuses native\n // mode without a publishable key: rendering nothing beats rendering an empty\n // white box that looks like a broken payment form.\n if (!clientSecret || !stripe) return null\n return (\n <CheckoutElementsProvider stripe={stripe} options={options}>\n <Box sx={{ py: 2 }} data-testid=\"storefront-payment-element\">\n <PaymentForm\n payLabel={payLabel}\n defaultEmail={defaultEmail}\n onCancel={onCancel}\n onRestart={onRestart}\n wallets={wallets}\n />\n </Box>\n </CheckoutElementsProvider>\n )\n}\n\n/**\n * Re-exported so the old import path still resolves, but the definition now\n * lives in its own module (AGL-2486): a fallback imported FROM the lazily\n * loaded module drags that module into the eager bundle and cancels the\n * `lazy()`. Callers must import it from `./storefront-payment-element-fallback`\n * — importing it from here is exactly the bug, and re-exporting it does not\n * make that safe.\n */\nexport { StorefrontPaymentElementFallback } from './storefront-payment-element-fallback'\n\nexport default StorefrontPaymentElement\n"],"names":["CheckoutElementsProvider","PaymentElement","ShippingAddressElement","useCheckoutElements","loadStripe","Alert","Box","Button","CircularProgress","Divider","FormControlLabel","Radio","RadioGroup","TextField","Typography","useTheme","useCallback","useEffect","useMemo","useRef","useState","storefrontPaymentAppearance","storefrontPaymentFonts","stripePromises","Map","getStripe","publishableKey","promise","get","set","__resetStripePromises","clear","GENERIC_FAILURE","ADDRESS_SETTLE_MS","SummaryRow","label","value","strong","sx","display","justifyContent","gap","variant","color","SessionNotice","message","onCancel","onRestart","flexDirection","severity","role","onClick","PaymentForm","payLabel","defaultEmail","wallets","checkout","total","checkoutState","type","status","setStatus","setMessage","email","setEmail","emailError","setEmailError","shippingBusy","setShippingBusy","serverEmail","undefined","current","String","emailLocked","Boolean","sessionShippingOptions","shippingOptions","collectsShipping","length","selectedShippingId","shipping","shippingOption","id","autoSelected","updateShippingOption","chooseShipping","result","error","addressTimer","clearTimeout","onShippingAddressChange","event","complete","name","address","setTimeout","updateShippingAddress","catch","commitEmail","trimmed","trim","updateEmail","handlePay","confirm","py","size","aria-label","busy","discount","minorUnitsAmount","tax","taxExclusive","taxPending","required","fullWidth","autoComplete","onChange","target","onBlur","helperText","disabled","data-testid","map","option","control","displayName","amount","options","subtotal","shippingRate","StorefrontPaymentElement","clientSecret","defaultShippingAddress","stripe","theme","elementsOptions","appearance","fonts","typography","fontFamily","defaultValues","shippingAddress","StorefrontPaymentElementFallback"],"mappings":"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"}
|
|
@@ -229,7 +229,12 @@ export const ORDER_STATUS_OPTIONS = Object.keys(ORDER_STATUS_LABELS).map((status
|
|
|
229
229
|
value: status,
|
|
230
230
|
label: ORDER_STATUS_LABELS[status]
|
|
231
231
|
}));
|
|
232
|
-
|
|
232
|
+
/*
|
|
233
|
+
* Every channel, `quote` included: a quote is listed here once it is paid
|
|
234
|
+
* (AGL-4094), as the order it became, and "Quote" finds those orders. An
|
|
235
|
+
* unpaid quote is never on this list (`commerce-quotes.ts`); the Quotes card
|
|
236
|
+
* lists it.
|
|
237
|
+
*/ export const ORDER_CHANNEL_OPTIONS = Object.keys(ORDER_CHANNEL_LABELS).map((channel)=>({
|
|
233
238
|
value: channel,
|
|
234
239
|
label: ORDER_CHANNEL_LABELS[channel]
|
|
235
240
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/constants/orders-list-query.ts"],"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\nimport type { ListFilterField } from '@aglyn/shared-ui-jsx/const/list-filter'\nimport type {\n ListFilterClause,\n ListFilterOption,\n} from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport type {\n ListQueryDeclaration,\n ListQuerySort,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport {\n ORDER_CHANNEL_LABELS,\n ORDER_STATUS_LABELS,\n type OrderChannel,\n type OrderStatus,\n} from '../model/commerce-orders'\n\n/*\n * THE ORDERS LIST, ON ITS QUERY (AGL-3321).\n *\n * `hosts/{hostId}/orders`, newest first. Every filter the grid's panel offers\n * and the quick search's word is a predicate on the ONE Firestore query that\n * pages the list (`planListQuery` through `useListQuery`), over the fields\n * every writer stamps with `orderListFields` — so page two of a filtered list\n * is page two of the matches, not page two of the store narrowed afterwards.\n *\n * One order: `createdAtMs` descending, the field every writer stamps. Each\n * filterable field is then ONE `(field, createdAtMs DESC)` composite, and\n * index merging serves every combination of them; the date range is on that\n * same field, so it adds none. Eight composites in all, pinned by\n * `orders-list-query.spec.ts` against `cloud/firebase-firestore.indexes.json`.\n *\n * What one query cannot hold is refused by name above the grid\n * (`ListQueryNotices`, labelled by `listQueryRefusals` from these fields,\n * headers and options), never matched over the rows it returned: the search,\n * Customer `contains`, Order `contains` and Product each take the query's one\n * array clause, so two of them together keep the first and refuse the second.\n *\n * A second range (a total, say) would be a new `sorts` entry and one more\n * composite per filterable field beneath it — eight — plus the range field's\n * own order; nothing else here moves.\n *\n * ## Every header sorts (AGL-3680)\n *\n * `ORDER_LIST_COLUMN_SORTS`: Date either way, and Order, Customer, Channel and\n * Status, each on the field `orderListFields` stamps on every order —\n * `number` (null on an order with none: POS, draft and channel orders are\n * unnumbered), `customerEmailLower`, `channel`, `status`.\n * `tools/scripts/backfill-orders-list-fields.mjs` stamps `number` and an\n * absent `customerEmailLower` on the orders written before. Every order but\n * the default is `alone`: served with no filter or search on, it costs no\n * composite on this unscoped list, and asked under a filter it falls back to\n * newest first and says so. Total is computed from the refunds, so it sorts\n * the page on screen.\n */\n\n/** The grid columns, each as the query reads it. */\nexport const ORDER_LIST_FIELDS: readonly ListFilterField[] = [\n {\n // The number as typed, `#1042` or `1042`, by word prefix.\n column: 'orderLabel',\n kind: 'text',\n path: 'number',\n tokensPath: 'orderLabelTokens',\n operators: ['contains'],\n },\n {\n // `is` a whole address; `contains` an address's piece or its domain.\n column: 'customerEmail',\n kind: 'text',\n path: 'customerEmail',\n lowerPath: 'customerEmailLower',\n tokensPath: 'customerEmailTokens',\n operators: ['contains', 'equals'],\n },\n {\n column: 'channelKey',\n kind: 'exact',\n path: 'channel',\n presence: 'always',\n operators: ['equals', 'isAnyOf'],\n },\n {\n column: 'statusKey',\n kind: 'exact',\n path: 'status',\n presence: 'always',\n operators: ['equals', 'isAnyOf'],\n },\n {\n column: 'createdAtMs',\n kind: 'date',\n path: 'createdAtMs',\n storedAs: 'millis',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n },\n {\n // Every product in the order, matched whole — ids keep their case.\n column: 'productIds',\n kind: 'exact',\n path: 'productIds',\n tokensPath: 'productIds',\n verbatimTokens: true,\n operators: ['isAnyOf'],\n },\n {\n column: 'disputeKey',\n kind: 'exact',\n path: 'disputeKey',\n operators: ['equals', 'isAnyOf'],\n },\n]\n\n/**\n * The orders the grid's headers ask for, newest first FIRST — see \"Every\n * header sorts\" above.\n */\nexport const ORDER_LIST_COLUMN_SORTS: readonly ListQuerySort[] = [\n { path: 'createdAtMs', direction: 'desc', column: 'createdAtMs', label: 'Date' },\n { path: 'createdAtMs', direction: 'asc', column: 'createdAtMs', label: 'Date', alone: true },\n { path: 'number', direction: 'desc', column: 'orderLabel', label: 'Order', alone: true },\n { path: 'number', direction: 'asc', column: 'orderLabel', label: 'Order', alone: true },\n { path: 'customerEmailLower', direction: 'asc', column: 'customerEmail', label: 'Customer', alone: true },\n { path: 'customerEmailLower', direction: 'desc', column: 'customerEmail', label: 'Customer', alone: true },\n { path: 'channel', direction: 'asc', column: 'channelKey', label: 'Channel', alone: true },\n { path: 'channel', direction: 'desc', column: 'channelKey', label: 'Channel', alone: true },\n { path: 'status', direction: 'asc', column: 'statusKey', label: 'Status', alone: true },\n { path: 'status', direction: 'desc', column: 'statusKey', label: 'Status', alone: true },\n]\n\n/** The list's query: its fields, its header orders and its search. */\nexport const ORDER_LIST_QUERY: ListQueryDeclaration = {\n fields: ORDER_LIST_FIELDS,\n sorts: ORDER_LIST_COLUMN_SORTS,\n search: { tokensPath: 'searchTokens' },\n}\n\nexport const ORDER_LIST_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}\n\n/** The fields picked from choices, which the panel shows as a select. */\nexport const ORDER_LIST_SELECT_FIELDS: readonly string[] = [\n 'channelKey',\n 'statusKey',\n 'productIds',\n 'disputeKey',\n]\n\nexport const ORDER_STATUS_OPTIONS: readonly ListFilterOption[] = (\n Object.keys(ORDER_STATUS_LABELS) as OrderStatus[]\n).map((status) => ({ value: status, label: ORDER_STATUS_LABELS[status] }))\n\nexport const ORDER_CHANNEL_OPTIONS: readonly ListFilterOption[] = (\n Object.keys(ORDER_CHANNEL_LABELS) as OrderChannel[]\n).map((channel) => ({ value: channel, label: ORDER_CHANNEL_LABELS[channel] }))\n\n/*\n * The dispute badge's tones (`orderDisputeKey`), labelled as the badge\n * labels them. `lost` is the tone, not the raw dispute status: a dispute\n * that was WON also closes with money untouched, and listing it as charged\n * back would tell a merchant they lost a case they won.\n */\nexport const ORDER_DISPUTE_OPTIONS: readonly ListFilterOption[] = [\n { value: 'open', label: 'Open dispute' },\n { value: 'lost', label: 'Charged back' },\n { value: 'won', label: 'Dispute won' },\n { value: 'settled', label: 'Dispute closed' },\n]\n\n/** The clause the open-dispute banner's \"Show them\" sets. */\nexport const OPEN_DISPUTE_CLAUSE: ListFilterClause = {\n field: 'disputeKey',\n op: 'equals',\n value: 'open',\n}\n\n/**\n * The Customer clause the CRM's link seeds from `?email=` (AGL-2622).\n *\n * A whole address is `is`, served by the lower-cased key, so it finds that\n * buyer and no one whose address merely begins the same way. Anything else —\n * a domain, `@acme.com`, a name — is `contains`, a word prefix of the\n * address, so a domain finds every buyer at a company.\n */\nexport function ordersCustomerClause(raw: string): ListFilterClause | null {\n const value = raw.trim().replace(/^@+/, '')\n if (!value) return null\n const at = value.indexOf('@')\n return at > 0 && at < value.length - 1\n ? { field: 'customerEmail', op: 'equals', value }\n : { field: 'customerEmail', op: 'contains', value }\n}\n"],"names":["ORDER_CHANNEL_LABELS","ORDER_STATUS_LABELS","ORDER_LIST_FIELDS","column","kind","path","tokensPath","operators","lowerPath","presence","storedAs","verbatimTokens","ORDER_LIST_COLUMN_SORTS","direction","label","alone","ORDER_LIST_QUERY","fields","sorts","search","ORDER_LIST_HEADERS","orderLabel","customerEmail","channelKey","statusKey","createdAtMs","productIds","disputeKey","ORDER_LIST_SELECT_FIELDS","ORDER_STATUS_OPTIONS","Object","keys","map","status","value","ORDER_CHANNEL_OPTIONS","channel","ORDER_DISPUTE_OPTIONS","OPEN_DISPUTE_CLAUSE","field","op","ordersCustomerClause","raw","trim","replace","at","indexOf","length"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/constants/orders-list-query.ts"],"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\nimport type { ListFilterField } from '@aglyn/shared-ui-jsx/const/list-filter'\nimport type {\n ListFilterClause,\n ListFilterOption,\n} from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport type {\n ListQueryDeclaration,\n ListQuerySort,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport {\n ORDER_CHANNEL_LABELS,\n ORDER_STATUS_LABELS,\n type OrderChannel,\n type OrderStatus,\n} from '../model/commerce-orders'\n\n/*\n * THE ORDERS LIST, ON ITS QUERY (AGL-3321).\n *\n * `hosts/{hostId}/orders`, newest first. Every filter the grid's panel offers\n * and the quick search's word is a predicate on the ONE Firestore query that\n * pages the list (`planListQuery` through `useListQuery`), over the fields\n * every writer stamps with `orderListFields` — so page two of a filtered list\n * is page two of the matches, not page two of the store narrowed afterwards.\n *\n * One order: `createdAtMs` descending, the field every writer stamps. Each\n * filterable field is then ONE `(field, createdAtMs DESC)` composite, and\n * index merging serves every combination of them; the date range is on that\n * same field, so it adds none. Eight composites in all, pinned by\n * `orders-list-query.spec.ts` against `cloud/firebase-firestore.indexes.json`.\n *\n * What one query cannot hold is refused by name above the grid\n * (`ListQueryNotices`, labelled by `listQueryRefusals` from these fields,\n * headers and options), never matched over the rows it returned: the search,\n * Customer `contains`, Order `contains` and Product each take the query's one\n * array clause, so two of them together keep the first and refuse the second.\n *\n * A second range (a total, say) would be a new `sorts` entry and one more\n * composite per filterable field beneath it — eight — plus the range field's\n * own order; nothing else here moves.\n *\n * ## Every header sorts (AGL-3680)\n *\n * `ORDER_LIST_COLUMN_SORTS`: Date either way, and Order, Customer, Channel and\n * Status, each on the field `orderListFields` stamps on every order —\n * `number` (null on an order with none: POS, draft and channel orders are\n * unnumbered), `customerEmailLower`, `channel`, `status`.\n * `tools/scripts/backfill-orders-list-fields.mjs` stamps `number` and an\n * absent `customerEmailLower` on the orders written before. Every order but\n * the default is `alone`: served with no filter or search on, it costs no\n * composite on this unscoped list, and asked under a filter it falls back to\n * newest first and says so. Total is computed from the refunds, so it sorts\n * the page on screen.\n */\n\n/** The grid columns, each as the query reads it. */\nexport const ORDER_LIST_FIELDS: readonly ListFilterField[] = [\n {\n // The number as typed, `#1042` or `1042`, by word prefix.\n column: 'orderLabel',\n kind: 'text',\n path: 'number',\n tokensPath: 'orderLabelTokens',\n operators: ['contains'],\n },\n {\n // `is` a whole address; `contains` an address's piece or its domain.\n column: 'customerEmail',\n kind: 'text',\n path: 'customerEmail',\n lowerPath: 'customerEmailLower',\n tokensPath: 'customerEmailTokens',\n operators: ['contains', 'equals'],\n },\n {\n column: 'channelKey',\n kind: 'exact',\n path: 'channel',\n presence: 'always',\n operators: ['equals', 'isAnyOf'],\n },\n {\n column: 'statusKey',\n kind: 'exact',\n path: 'status',\n presence: 'always',\n operators: ['equals', 'isAnyOf'],\n },\n {\n column: 'createdAtMs',\n kind: 'date',\n path: 'createdAtMs',\n storedAs: 'millis',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n },\n {\n // Every product in the order, matched whole — ids keep their case.\n column: 'productIds',\n kind: 'exact',\n path: 'productIds',\n tokensPath: 'productIds',\n verbatimTokens: true,\n operators: ['isAnyOf'],\n },\n {\n column: 'disputeKey',\n kind: 'exact',\n path: 'disputeKey',\n operators: ['equals', 'isAnyOf'],\n },\n]\n\n/**\n * The orders the grid's headers ask for, newest first FIRST — see \"Every\n * header sorts\" above.\n */\nexport const ORDER_LIST_COLUMN_SORTS: readonly ListQuerySort[] = [\n { path: 'createdAtMs', direction: 'desc', column: 'createdAtMs', label: 'Date' },\n { path: 'createdAtMs', direction: 'asc', column: 'createdAtMs', label: 'Date', alone: true },\n { path: 'number', direction: 'desc', column: 'orderLabel', label: 'Order', alone: true },\n { path: 'number', direction: 'asc', column: 'orderLabel', label: 'Order', alone: true },\n { path: 'customerEmailLower', direction: 'asc', column: 'customerEmail', label: 'Customer', alone: true },\n { path: 'customerEmailLower', direction: 'desc', column: 'customerEmail', label: 'Customer', alone: true },\n { path: 'channel', direction: 'asc', column: 'channelKey', label: 'Channel', alone: true },\n { path: 'channel', direction: 'desc', column: 'channelKey', label: 'Channel', alone: true },\n { path: 'status', direction: 'asc', column: 'statusKey', label: 'Status', alone: true },\n { path: 'status', direction: 'desc', column: 'statusKey', label: 'Status', alone: true },\n]\n\n/** The list's query: its fields, its header orders and its search. */\nexport const ORDER_LIST_QUERY: ListQueryDeclaration = {\n fields: ORDER_LIST_FIELDS,\n sorts: ORDER_LIST_COLUMN_SORTS,\n search: { tokensPath: 'searchTokens' },\n}\n\nexport const ORDER_LIST_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}\n\n/** The fields picked from choices, which the panel shows as a select. */\nexport const ORDER_LIST_SELECT_FIELDS: readonly string[] = [\n 'channelKey',\n 'statusKey',\n 'productIds',\n 'disputeKey',\n]\n\nexport const ORDER_STATUS_OPTIONS: readonly ListFilterOption[] = (\n Object.keys(ORDER_STATUS_LABELS) as OrderStatus[]\n).map((status) => ({ value: status, label: ORDER_STATUS_LABELS[status] }))\n\n/*\n * Every channel, `quote` included: a quote is listed here once it is paid\n * (AGL-4094), as the order it became, and \"Quote\" finds those orders. An\n * unpaid quote is never on this list (`commerce-quotes.ts`); the Quotes card\n * lists it.\n */\nexport const ORDER_CHANNEL_OPTIONS: readonly ListFilterOption[] = (\n Object.keys(ORDER_CHANNEL_LABELS) as OrderChannel[]\n).map((channel) => ({ value: channel, label: ORDER_CHANNEL_LABELS[channel] }))\n\n/*\n * The dispute badge's tones (`orderDisputeKey`), labelled as the badge\n * labels them. `lost` is the tone, not the raw dispute status: a dispute\n * that was WON also closes with money untouched, and listing it as charged\n * back would tell a merchant they lost a case they won.\n */\nexport const ORDER_DISPUTE_OPTIONS: readonly ListFilterOption[] = [\n { value: 'open', label: 'Open dispute' },\n { value: 'lost', label: 'Charged back' },\n { value: 'won', label: 'Dispute won' },\n { value: 'settled', label: 'Dispute closed' },\n]\n\n/** The clause the open-dispute banner's \"Show them\" sets. */\nexport const OPEN_DISPUTE_CLAUSE: ListFilterClause = {\n field: 'disputeKey',\n op: 'equals',\n value: 'open',\n}\n\n/**\n * The Customer clause the CRM's link seeds from `?email=` (AGL-2622).\n *\n * A whole address is `is`, served by the lower-cased key, so it finds that\n * buyer and no one whose address merely begins the same way. Anything else —\n * a domain, `@acme.com`, a name — is `contains`, a word prefix of the\n * address, so a domain finds every buyer at a company.\n */\nexport function ordersCustomerClause(raw: string): ListFilterClause | null {\n const value = raw.trim().replace(/^@+/, '')\n if (!value) return null\n const at = value.indexOf('@')\n return at > 0 && at < value.length - 1\n ? { field: 'customerEmail', op: 'equals', value }\n : { field: 'customerEmail', op: 'contains', value }\n}\n"],"names":["ORDER_CHANNEL_LABELS","ORDER_STATUS_LABELS","ORDER_LIST_FIELDS","column","kind","path","tokensPath","operators","lowerPath","presence","storedAs","verbatimTokens","ORDER_LIST_COLUMN_SORTS","direction","label","alone","ORDER_LIST_QUERY","fields","sorts","search","ORDER_LIST_HEADERS","orderLabel","customerEmail","channelKey","statusKey","createdAtMs","productIds","disputeKey","ORDER_LIST_SELECT_FIELDS","ORDER_STATUS_OPTIONS","Object","keys","map","status","value","ORDER_CHANNEL_OPTIONS","channel","ORDER_DISPUTE_OPTIONS","OPEN_DISPUTE_CLAUSE","field","op","ordersCustomerClause","raw","trim","replace","at","indexOf","length"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The customer's quote page (AGL-4094), where a quote's pay link opens.
|
|
19
|
+
*
|
|
20
|
+
* The component id is persisted in screen documents; never rename it. The
|
|
21
|
+
* path is where the site serves the page when it has no page of its own
|
|
22
|
+
* there. It is `pay-quote` rather than `quote` because a business that sells
|
|
23
|
+
* made-to-order work often has a "Request a quote" page at `/quote` already,
|
|
24
|
+
* and a page of the site's own at an address always wins over this one: a pay
|
|
25
|
+
* link there would open the request form.
|
|
26
|
+
*/
|
|
27
|
+
export declare const QUOTE_PAGE_COMPONENT_ID = "quote-page";
|
|
28
|
+
export declare const QUOTE_PAGE_PATH = "pay-quote";
|
|
29
|
+
/** The page's address for one quote and its signed link token, relative to the site. */
|
|
30
|
+
export declare function quotePageHref(quoteId: string, token: string): string;
|