{ "name": "checkout-summary", "type": "registry:ui", "title": "Checkout Summary", "description": "The order total, broken into its lines. A charge that is not yet known renders as \"calculated at next step\" rather than as £0.00 — showing zero for a charge you will apply is a promise you cannot keep.", "category": "Commerce", "dependencies": [], "registryDependencies": [ "fmt", "lib-motion", "lib-styles", "lib-utils" ], "files": [ { "path": "components/ui/checkout-summary.tsx", "type": "registry:ui", "content": "import type { ComponentProps, ReactNode } from 'react'\nimport { Fmt } from '@/components/ui/fmt'\nimport { enterFade } from '@/lib/motion'\nimport { radius, surface } from '@/lib/styles'\nimport { cn } from '@/lib/utils'\n\n/**\n * The order total, broken into the lines that make it.\n *\n * Every adjustment is its own row. A total that silently includes tax and\n * shipping is the single most common cause of an abandoned basket, and in\n * several jurisdictions showing tax as a separate line is not optional.\n *\n * Amounts are integer minor units, and the total is summed here rather than\n * accepted as a prop. A total that disagrees with the rows above it is a\n * customer-service ticket at best.\n *\n * Unknown values are supported explicitly: shipping that depends on an address\n * not yet given renders as \"calculated at next step\" rather than as zero.\n * Showing £0.00 for a charge that will be applied is a promise you cannot keep.\n */\nexport type CheckoutLine = {\n id: string\n label: ReactNode\n /** Minor units. Negative for a discount. `null` when not yet known. */\n amount: number | null\n note?: ReactNode\n /** Renders in the discount tone. Inferred from a negative amount otherwise. */\n discount?: boolean\n}\n\nfunction CheckoutSummary({\n subtotal,\n lines = [],\n currency = 'USD',\n locale = 'en-GB',\n totalLabel = 'Total',\n subtotalLabel = 'Subtotal',\n pendingLabel = 'calculated at next step',\n pendingNote = 'Final total confirmed once delivery details are entered.',\n footer,\n className,\n ...props\n}: Omit, 'children'> & {\n /** Minor units, before adjustments. */\n subtotal: number\n lines?: CheckoutLine[]\n currency?: string\n locale?: string\n totalLabel?: ReactNode\n subtotalLabel?: ReactNode\n /** Stands in for an amount that is not yet known. */\n pendingLabel?: ReactNode\n /** Note shown while any line is still pending. */\n pendingNote?: ReactNode\n footer?: ReactNode\n}) {\n const money = (minor: number) => (\n \n )\n\n // Summed from the rows shown, so the two cannot disagree.\n const total = lines.reduce((sum, line) => sum + (line.amount ?? 0), subtotal)\n const pending = lines.some((line) => line.amount === null)\n\n return (\n \n
\n
\n
{subtotalLabel}
\n
{money(subtotal)}
\n
\n\n {lines.map((line) => {\n const isDiscount = line.discount ?? (line.amount !== null && line.amount < 0)\n return (\n
\n
\n {line.label}\n {line.note && (\n {line.note}\n )}\n
\n \n {line.amount === null ? pendingLabel : money(line.amount)}\n \n
\n )\n })}\n\n
\n
{totalLabel}
\n
\n {money(total)}\n {pending && +}\n
\n
\n
\n\n {pending && (\n

\n {pendingNote}\n

\n )}\n\n {footer &&
{footer}
}\n \n )\n}\n\nexport { CheckoutSummary }\n" } ] }