@aglyn/plugins-commerce 1.0.0-beta.218 → 1.0.0-beta.220
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 +6 -0
- package/package.json +14 -14
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -19
- package/src/index.js.map +1 -1
- package/src/lib/components/cart.js +2 -1
- package/src/lib/components/cart.js.map +1 -1
- package/src/lib/components/console/catalog-organization-card.component.js +665 -459
- package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
- package/src/lib/components/console/host-orders-card.component.js +2 -1
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/member-posts-card.component.js +1 -1
- package/src/lib/components/console/member-posts-card.component.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +8 -7
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/payments-settings-card.component.js +2 -2
- package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
- package/src/lib/components/console/product-editor-dialog.component.js +1221 -889
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/product-zones.d.ts +184 -0
- package/src/lib/components/console/product-zones.js +54 -0
- package/src/lib/components/console/product-zones.js.map +1 -0
- package/src/lib/components/console/products-hub-card.component.js +3 -2
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/products-hub-zone.component.d.ts +1 -1
- package/src/lib/components/console/products-hub-zone.component.js +2 -1
- package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
- package/src/lib/components/console/site-member-purchases.component.d.ts +24 -0
- package/src/lib/components/console/site-member-purchases.component.js +339 -0
- package/src/lib/components/console/site-member-purchases.component.js.map +1 -0
- package/src/lib/components/console/store-settings-card.component.js +2 -2
- package/src/lib/components/console/store-settings-card.component.js.map +1 -1
- package/src/lib/components/console/storefront-tax-summary-card.component.js +1 -1
- package/src/lib/components/console/storefront-tax-summary-card.component.js.map +1 -1
- package/src/lib/components/member-signup.js +2 -2
- package/src/lib/components/member-signup.js.map +1 -1
- package/src/lib/components/newsletter-signup.js +2 -2
- package/src/lib/components/newsletter-signup.js.map +1 -1
- package/src/lib/components/product-detail.js +2 -1
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/components/product-grid.js +1 -1
- package/src/lib/components/product-grid.js.map +1 -1
- package/src/lib/constants/operator-alerts.js +5 -3
- package/src/lib/constants/operator-alerts.js.map +1 -1
- package/src/lib/constants/storefront-payments.d.ts +33 -0
- package/src/lib/constants/storefront-payments.js +35 -0
- package/src/lib/constants/storefront-payments.js.map +1 -0
- package/src/lib/declarations.server.d.ts +3 -1
- package/src/lib/declarations.server.js +19 -1
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/model/commerce-io.js +1 -1
- package/src/lib/model/commerce-io.js.map +1 -1
- package/src/lib/model/commerce-record-lists.d.ts +31 -0
- package/src/lib/model/commerce-record-lists.js +45 -0
- package/src/lib/model/commerce-record-lists.js.map +1 -0
- package/src/lib/model/commerce-record-routes.d.ts +45 -0
- package/src/lib/model/commerce-record-routes.js +72 -0
- package/src/lib/model/commerce-record-routes.js.map +1 -0
- package/src/lib/model/commerce-search-sources.d.ts +21 -0
- package/src/lib/model/commerce-search-sources.js +42 -0
- package/src/lib/model/commerce-search-sources.js.map +1 -0
- package/src/lib/model/commerce-topics.d.ts +20 -0
- package/src/lib/model/commerce-topics.js +33 -0
- package/src/lib/model/commerce-topics.js.map +1 -0
- package/src/lib/model/index.d.ts +1 -0
- package/src/lib/model/index.js +1 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/product-record.d.ts +32 -0
- package/src/lib/model/product-record.js +82 -0
- package/src/lib/model/product-record.js.map +1 -0
- package/src/lib/model/site-member-purchases.d.ts +48 -0
- package/src/lib/model/site-member-purchases.js +50 -0
- package/src/lib/model/site-member-purchases.js.map +1 -0
- package/src/lib/model/storefront-tax-summary.d.ts +121 -0
- package/src/lib/model/storefront-tax-summary.js +148 -0
- package/src/lib/model/storefront-tax-summary.js.map +1 -0
- package/src/lib/plan-entitlements.d.ts +35 -0
- package/src/lib/plan-entitlements.js +54 -0
- package/src/lib/plan-entitlements.js.map +1 -0
- package/src/lib/plugin.js +21 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/billing-webhook.js +387 -248
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/cart-checkout.js +3 -3
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/checkout.js +2 -2
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/connect.js +5 -5
- package/src/lib/server/connect.js.map +1 -1
- package/src/lib/server/draft-order.js +2 -2
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/gift-cards.js +7 -3
- package/src/lib/server/gift-cards.js.map +1 -1
- package/src/lib/server/low-stock.js +3 -1
- package/src/lib/server/low-stock.js.map +1 -1
- package/src/lib/server/member-post.js +2 -2
- package/src/lib/server/member-post.js.map +1 -1
- package/src/lib/server/membership-admin-password.js +4 -1
- package/src/lib/server/membership-admin-password.js.map +1 -1
- package/src/lib/server/membership-content.js +1 -1
- package/src/lib/server/membership-content.js.map +1 -1
- package/src/lib/server/membership-register.js +13 -2
- package/src/lib/server/membership-register.js.map +1 -1
- package/src/lib/server/newsletter.js +32 -19
- package/src/lib/server/newsletter.js.map +1 -1
- package/src/lib/server/pos-order.js +3 -3
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/process-abandoned.js +4 -2
- package/src/lib/server/process-abandoned.js.map +1 -1
- package/src/lib/server/process-restock.js +7 -5
- package/src/lib/server/process-restock.js.map +1 -1
- package/src/lib/server/product-record-index.d.ts +19 -0
- package/src/lib/server/product-record-index.js +83 -0
- package/src/lib/server/product-record-index.js.map +1 -0
- package/src/lib/server/reconcile-stock.js +1 -1
- package/src/lib/server/reconcile-stock.js.map +1 -1
- package/src/lib/server/reserve-stock.js +6 -1
- package/src/lib/server/reserve-stock.js.map +1 -1
- package/src/lib/server/reserve.js +2 -2
- package/src/lib/server/reserve.js.map +1 -1
- package/src/lib/server/revenue-source.d.ts +130 -0
- package/src/lib/server/revenue-source.js +286 -0
- package/src/lib/server/revenue-source.js.map +1 -0
- package/src/lib/server/supplier-outbox.js +16 -1
- package/src/lib/server/supplier-outbox.js.map +1 -1
- package/src/lib/server/supplier-update.js +19 -9
- package/src/lib/server/supplier-update.js.map +1 -1
- package/src/lib/server/tax-profile.d.ts +8 -5
- package/src/lib/server/tax-profile.js +22 -5
- package/src/lib/server/tax-profile.js.map +1 -1
- package/src/lib/server/tax-return-source.d.ts +125 -0
- package/src/lib/server/tax-return-source.js +524 -0
- package/src/lib/server/tax-return-source.js.map +1 -0
- package/src/lib/server/tax-summary.d.ts +92 -0
- package/src/lib/server/tax-summary.js +258 -0
- package/src/lib/server/tax-summary.js.map +1 -0
- package/src/lib/server.d.ts +1 -0
- package/src/lib/server.js +23 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/starter-templates.d.ts +19 -0
- package/src/lib/starter-templates.js +157 -0
- package/src/lib/starter-templates.js.map +1 -0
- package/src/lib/tenant-emails.d.ts +33 -0
- package/src/lib/tenant-emails.js +551 -0
- package/src/lib/tenant-emails.js.map +1 -0
- package/src/lib/testing/stand-in-crm-record-routes.d.ts +25 -0
- package/src/lib/testing/stand-in-crm-record-routes.js +46 -0
- package/src/lib/testing/stand-in-crm-record-routes.js.map +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/storefront-tax-summary-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\n\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport {\n Alert,\n Box,\n Button,\n Divider,\n MenuItem,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\n\nexport interface StorefrontTaxSummaryCardProps {\n hostId: string\n}\n\nconst usd = (cents: number) => `$${(cents / 100).toFixed(2)}`\n\ninterface Jurisdiction {\n transactionCount: number\n totalSalesCents: number\n taxableSalesCents: number\n taxCollectedCents: number\n}\n\ninterface Bucket {\n transactionCount: number\n grossCents: number\n taxableSalesCents: number\n taxCollectedCents: number\n byJurisdiction: Record<string, Jurisdiction>\n}\n\ninterface Summary {\n periodStart: string\n periodEnd: string\n transactionCount: number\n aglynLiable: Bucket\n merchantManual: Bucket\n connectedAccountLiable: Bucket\n attention: {\n rowsMissingTaxableBase: number\n rowsMissingAddress: number\n nonUsdRows: number\n rowsMissingPaidAt: number\n rowsUnclassified: number\n }\n}\n\ninterface Payload {\n summary: Summary\n truncated: boolean\n undatedRows: number\n caveats: { refundsNotReflected: boolean }\n}\n\n/** The last twelve calendar months, newest first, as UTC month bounds. */\nfunction monthOptions(): Array<{ value: string; label: string; from: string; to: string }> {\n const now = new Date()\n return Array.from({ length: 12 }, (_item, index) => {\n const start = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - index, 1),\n )\n const end = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - index + 1, 1),\n )\n return {\n value: start.toISOString(),\n label: start.toLocaleDateString(undefined, {\n month: 'long',\n year: 'numeric',\n timeZone: 'UTC',\n }),\n from: start.toISOString(),\n to: end.toISOString(),\n }\n })\n}\n\n/**\n * The sales tax this merchant's own storefront collected (AGL-2440).\n *\n * `storefrontTaxCollected` has recorded every taxed storefront sale since\n * AGL-1904 and every reader of it was Aglyn's — the staff tax return, the DSAR\n * export. The merchant, whose sales these are, could see one order's\n * `totals.taxCents` at a time and nothing else. This card is that absence\n * closed, and closing it is all it does.\n *\n * ## THE NUMBER, NOT THE LABEL\n *\n * This card presents the buckets SEPARATELY and prints no per-row verdict on\n * who must remit which one.\n *\n * The copy may not say that Aglyn makes no determination: Aglyn IS a\n * marketplace facilitator for storefront sales and the Terms publish that at\n * §10.7 (AGL-1956), so such a line would contradict the company's own\n * contract.\n *\n * Separate buckets and no per-row verdict still hold, because §10.7 is\n * narrow: it binds Aglyn only where applicable law gives it a collection\n * obligation for the transaction in question, which is a per-sale,\n * per-jurisdiction test and not a fact about a merchant. So the copy still\n * describes HOW each figure was computed — who calculated the rate, and\n * against whose registration — and adds only what §10.7 itself says about the\n * bucket Aglyn holds. There is still no \"yours to remit\" column and no total\n * across the buckets. A merged \"tax collected\" figure would be the\n * banned determination wearing a neutral name: it would sum tax Aglyn holds\n * and remits with tax the merchant owes, the exact conflation the module warns\n * about in bold — *the two store modes are DIFFERENT FACTS and must never be\n * summed*. That is why the three groups never share a total row, and why the\n * grand total in the header is a TRANSACTION COUNT rather than an amount.\n *\n * ## Why the caveats are on the card and not in a tooltip\n *\n * Both are properties of the DATA, not of the law, and both change what a\n * merchant should do with the number: refunds are not yet reflected, so the\n * figure over-states whenever they have refunded; and a row whose taxable base\n * Stripe did not state is counted out loud rather than treated as a zero-base\n * sale. A figure someone may file from cannot carry its completeness caveats\n * behind a hover.\n *\n * ## Why it is route-backed and not a listener\n *\n * `storefrontTaxCollected` is denied to every client in the rules — the\n * collection spans every merchant and a row carries a shopper's address. The\n * server route applies the `hostId` boundary. Nothing here can read another\n * merchant's sales because nothing here reads the collection at all.\n */\nexport function StorefrontTaxSummaryCard(props: StorefrontTaxSummaryCardProps) {\n const { hostId } = props\n const { data: user } = useUser()\n const months = useMemo(() => monthOptions(), [])\n const [period, setPeriod] = useState(months[0].value)\n const [payload, setPayload] = useState<Payload | null>(null)\n /**\n * Three outcomes, not two (the AGL-1380 rule). \"No tax collected\" is a claim\n * about this merchant's sales; rendering it because a request FAILED would\n * tell a merchant who collected tax that they collected none — on the one\n * surface where that mistake could reach a filing.\n */\n const [loadState, setLoadState] = useState<'pending' | 'error' | 'loaded'>(\n 'pending',\n )\n const [retryNonce, setRetryNonce] = useState(0)\n\n const selected = months.find((month) => month.value === period) ?? months[0]\n\n const load = useCallback(async () => {\n const search = new URLSearchParams({\n hostId,\n from: selected.from,\n to: selected.to,\n })\n const response = await authorizedFetch(\n user,\n `/api/hosts/tax-summary?${search.toString()}`,\n )\n if (!response.ok) throw new Error('tax summary failed')\n return (await response.json()) as Payload\n }, [user, hostId, selected.from, selected.to])\n\n useEffect(() => {\n if (!user) return\n let cancelled = false\n setLoadState('pending')\n load()\n .then((next) => {\n if (cancelled) return\n setPayload(next)\n setLoadState('loaded')\n })\n .catch(() => {\n if (!cancelled) setLoadState('error')\n })\n return () => {\n cancelled = true\n }\n }, [load, user, retryNonce])\n\n const groups = payload\n ? ([\n {\n key: 'aglynLiable',\n bucket: payload.summary.aglynLiable,\n title: `Tax Stripe calculated against ${PLATFORM_BRAND_NAME}’s registrations`,\n how:\n 'Automatic tax was on for these sales. Stripe computed the rate ' +\n `against the tax registrations on ${PLATFORM_BRAND_NAME}’s platform ` +\n `account, and ${PLATFORM_BRAND_NAME} holds what was collected.`,\n },\n {\n key: 'merchantManual',\n bucket: payload.summary.merchantManual,\n title: 'Tax your store calculated at your own rate',\n how:\n 'Automatic tax was off for these sales. The rate came from the ' +\n 'tax settings you configured on this store, applied to your own ' +\n 'declared origin.',\n },\n {\n key: 'connectedAccountLiable',\n bucket: payload.summary.connectedAccountLiable,\n title: 'Tax Stripe calculated against your connected account',\n how:\n 'Automatic tax was on and Stripe named your connected account ' +\n 'as the liable party for these sales.',\n },\n ] as const)\n : []\n\n return (\n <CardDisplay\n header={'Storefront sales tax'}\n subheader={'What your storefront collected, grouped by how it was calculated.'}\n help={pluginDocsHelp('commerce', {\n anchor: '#storefront-sales-tax',\n excerpt:\n 'What your storefront collected in sales tax, grouped by how each ' +\n `figure was calculated. ${PLATFORM_BRAND_NAME} does not provide tax advice.`,\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <TextField\n select\n size=\"small\"\n label=\"Period\"\n value={period}\n onChange={(event) => setPeriod(event.target.value)}\n sx={{ maxWidth: '16rem' }}\n >\n {months.map((month) => (\n <MenuItem key={month.value} value={month.value}>\n {month.label}\n </MenuItem>\n ))}\n </TextField>\n\n {loadState === 'pending' ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n Loading your sales tax…\n </Typography>\n ) : loadState === 'error' || !payload ? (\n <Alert\n severity=\"warning\"\n action={\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={() => setRetryNonce((nonce) => nonce + 1)}\n >\n Retry\n </Button>\n }\n >\n We couldn’t load your sales tax for this period. This figure is\n blank because the request failed, not because no tax was collected.\n </Alert>\n ) : (\n <>\n {/*\n THE NON-ADVISORY NOTE, first and unmissable. Rendered ABOVE the\n numbers because it governs how all of them should be read.\n\n It now states the position Terms §10.7 publishes (AGL-1956)\n instead of the sentence it used to carry — \"Aglyn does not make\n that determination for you\" — which the Terms themselves have\n contradicted since 2026-08-24. What it still does NOT do is\n reach a per-merchant or per-jurisdiction verdict: §10.7 binds\n Aglyn only where applicable law gives it an obligation, so the\n note describes which bucket Aglyn holds and remits and leaves\n the rest where the merchant's own rate put it.\n */}\n <Alert severity=\"info\">\n These figures show what was collected and how each was\n calculated. For storefront sales {PLATFORM_BRAND_NAME} acts as a\n marketplace facilitator: where applicable law gives{' '}\n {PLATFORM_BRAND_NAME} a collection obligation, the tax it\n calculated is added on top at checkout, held by{' '}\n {PLATFORM_BRAND_NAME} rather than transferred to your connected\n payment account, and remitted by {PLATFORM_BRAND_NAME}. Tax\n calculated at a rate you configured yourself stays yours. See\n Terms of Service §10.7.{' '}\n <strong>\n {PLATFORM_BRAND_NAME} does not provide tax advice.\n </strong>{' '}\n Please confirm your obligations with a qualified tax\n professional.\n </Alert>\n\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`${payload.summary.transactionCount} taxed ${\n payload.summary.transactionCount === 1 ? 'sale' : 'sales'\n } in this period.`}\n </Typography>\n\n {payload.summary.transactionCount === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n No storefront sale carried tax in this period.\n </Typography>\n ) : null}\n\n {groups\n .filter((group) => group.bucket.transactionCount > 0)\n .map((group) => (\n <Box key={group.key}>\n <Divider sx={{ mb: 1.5 }} />\n <Typography variant=\"subtitle2\">{group.title}</Typography>\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n component=\"div\"\n sx={{ mb: 1 }}\n >\n {group.how}\n </Typography>\n <Stack\n direction=\"row\"\n spacing={3}\n sx={{ flexWrap: 'wrap', rowGap: 1, mb: 1 }}\n >\n <Box>\n <Typography variant=\"h6\">\n {usd(group.bucket.taxCollectedCents)}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n tax collected\n </Typography>\n </Box>\n <Box>\n <Typography variant=\"h6\">\n {usd(group.bucket.grossCents)}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n gross, tax included\n </Typography>\n </Box>\n <Box>\n <Typography variant=\"h6\">\n {group.bucket.transactionCount}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {group.bucket.transactionCount === 1\n ? 'sale'\n : 'sales'}\n </Typography>\n </Box>\n </Stack>\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Jurisdiction'}</TableCell>\n <TableCell align=\"right\">{'Sales'}</TableCell>\n <TableCell align=\"right\">{'Taxable base'}</TableCell>\n <TableCell align=\"right\">{'Tax'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {Object.entries(\n group.bucket.byJurisdiction as Record<\n string,\n Jurisdiction\n >,\n ).map(([code, jurisdiction]) => (\n <TableRow key={code}>\n <TableCell>\n {code === 'unknown' ? 'Not stated' : code}\n </TableCell>\n <TableCell align=\"right\">\n {usd(jurisdiction.totalSalesCents)}\n </TableCell>\n <TableCell align=\"right\">\n {usd(jurisdiction.taxableSalesCents)}\n </TableCell>\n <TableCell align=\"right\">\n {usd(jurisdiction.taxCollectedCents)}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n </Box>\n ))}\n\n {/*\n WHAT THE NUMBER DOES NOT YET INCLUDE. Facts about the data, on\n the card rather than behind a hover, because each one changes\n what a merchant should do with the figure.\n */}\n <Divider />\n <Stack spacing={0.5}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Refunds are not reflected. A refunded sale keeps its full ' +\n 'tax here, so these figures over-state whenever you have ' +\n 'refunded in this period.'}\n </Typography>\n {payload.summary.attention.rowsMissingTaxableBase > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${payload.summary.attention.rowsMissingTaxableBase} sale(s) collected tax but Stripe did not state the amount the rate was applied to. Their tax is counted; their taxable base is not, so “Taxable base” above is understated.`}\n </Typography>\n ) : null}\n {payload.summary.attention.rowsMissingAddress > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${payload.summary.attention.rowsMissingAddress} sale(s) carried no customer address and are grouped under “Not stated”.`}\n </Typography>\n ) : null}\n {payload.summary.attention.nonUsdRows > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${payload.summary.attention.nonUsdRows} sale(s) were in a currency other than USD and are summed here without conversion.`}\n </Typography>\n ) : null}\n {payload.summary.attention.rowsUnclassified > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${payload.summary.attention.rowsUnclassified} sale(s) could not be classified and are excluded from every group above rather than guessed into one.`}\n </Typography>\n ) : null}\n {payload.undatedRows > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${payload.undatedRows} recorded sale(s) carry no payment date and fall outside every period, including this one.`}\n </Typography>\n ) : null}\n {payload.truncated ? (\n <Typography variant=\"caption\" color=\"warning.main\">\n {'This period has more sales than one report can read. The ' +\n 'figures above are PARTIAL — narrow the period.'}\n </Typography>\n ) : null}\n </Stack>\n </>\n )}\n </Stack>\n </CardDisplay>\n )\n}\nStorefrontTaxSummaryCard.displayName = 'StorefrontTaxSummaryCard'\n\nexport default StorefrontTaxSummaryCard\n"],"names":["PLATFORM_BRAND_NAME","pluginDocsHelp","CardDisplay","ScrollTable","Alert","Box","Button","Divider","MenuItem","Stack","TableBody","TableCell","TableHead","TableRow","TextField","Typography","useCallback","useEffect","useMemo","useState","useUser","authorizedFetch","usd","cents","toFixed","monthOptions","now","Date","Array","from","length","_item","index","start","UTC","getUTCFullYear","getUTCMonth","end","value","toISOString","label","toLocaleDateString","undefined","month","year","timeZone","to","StorefrontTaxSummaryCard","props","months","hostId","data","user","period","setPeriod","payload","setPayload","loadState","setLoadState","retryNonce","setRetryNonce","selected","find","load","search","URLSearchParams","response","toString","ok","Error","json","cancelled","then","next","catch","groups","key","bucket","summary","aglynLiable","title","how","merchantManual","connectedAccountLiable","header","subheader","help","anchor","excerpt","contentGutterX","contentGutterY","spacing","select","size","onChange","event","target","sx","maxWidth","map","variant","color","severity","action","onClick","nonce","strong","transactionCount","filter","group","mb","component","direction","flexWrap","rowGap","taxCollectedCents","grossCents","align","Object","entries","byJurisdiction","code","jurisdiction","totalSalesCents","taxableSalesCents","attention","rowsMissingTaxableBase","rowsMissingAddress","nonUsdRows","rowsUnclassified","undatedRows","truncated","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/storefront-tax-summary-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\n\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport {\n Alert,\n Box,\n Button,\n Divider,\n MenuItem,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\n\nexport interface StorefrontTaxSummaryCardProps {\n hostId: string\n}\n\nconst usd = (cents: number) => `$${(cents / 100).toFixed(2)}`\n\ninterface Jurisdiction {\n transactionCount: number\n totalSalesCents: number\n taxableSalesCents: number\n taxCollectedCents: number\n}\n\ninterface Bucket {\n transactionCount: number\n grossCents: number\n taxableSalesCents: number\n taxCollectedCents: number\n byJurisdiction: Record<string, Jurisdiction>\n}\n\ninterface Summary {\n periodStart: string\n periodEnd: string\n transactionCount: number\n aglynLiable: Bucket\n merchantManual: Bucket\n connectedAccountLiable: Bucket\n attention: {\n rowsMissingTaxableBase: number\n rowsMissingAddress: number\n nonUsdRows: number\n rowsMissingPaidAt: number\n rowsUnclassified: number\n }\n}\n\ninterface Payload {\n summary: Summary\n truncated: boolean\n undatedRows: number\n caveats: { refundsNotReflected: boolean }\n}\n\n/** The last twelve calendar months, newest first, as UTC month bounds. */\nfunction monthOptions(): Array<{ value: string; label: string; from: string; to: string }> {\n const now = new Date()\n return Array.from({ length: 12 }, (_item, index) => {\n const start = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - index, 1),\n )\n const end = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - index + 1, 1),\n )\n return {\n value: start.toISOString(),\n label: start.toLocaleDateString(undefined, {\n month: 'long',\n year: 'numeric',\n timeZone: 'UTC',\n }),\n from: start.toISOString(),\n to: end.toISOString(),\n }\n })\n}\n\n/**\n * The sales tax this merchant's own storefront collected (AGL-2440).\n *\n * `storefrontTaxCollected` has recorded every taxed storefront sale since\n * AGL-1904 and every reader of it was Aglyn's — the staff tax return, the DSAR\n * export. The merchant, whose sales these are, could see one order's\n * `totals.taxCents` at a time and nothing else. This card is that absence\n * closed, and closing it is all it does.\n *\n * ## THE NUMBER, NOT THE LABEL\n *\n * This card presents the buckets SEPARATELY and prints no per-row verdict on\n * who must remit which one.\n *\n * The copy may not say that Aglyn makes no determination: Aglyn IS a\n * marketplace facilitator for storefront sales and the Terms publish that at\n * §10.7 (AGL-1956), so such a line would contradict the company's own\n * contract.\n *\n * Separate buckets and no per-row verdict still hold, because §10.7 is\n * narrow: it binds Aglyn only where applicable law gives it a collection\n * obligation for the transaction in question, which is a per-sale,\n * per-jurisdiction test and not a fact about a merchant. So the copy still\n * describes HOW each figure was computed — who calculated the rate, and\n * against whose registration — and adds only what §10.7 itself says about the\n * bucket Aglyn holds. There is still no \"yours to remit\" column and no total\n * across the buckets. A merged \"tax collected\" figure would be the\n * banned determination wearing a neutral name: it would sum tax Aglyn holds\n * and remits with tax the merchant owes, the exact conflation the module warns\n * about in bold — *the two store modes are DIFFERENT FACTS and must never be\n * summed*. That is why the three groups never share a total row, and why the\n * grand total in the header is a TRANSACTION COUNT rather than an amount.\n *\n * ## Why the caveats are on the card and not in a tooltip\n *\n * Both are properties of the DATA, not of the law, and both change what a\n * merchant should do with the number: refunds are not yet reflected, so the\n * figure over-states whenever they have refunded; and a row whose taxable base\n * Stripe did not state is counted out loud rather than treated as a zero-base\n * sale. A figure someone may file from cannot carry its completeness caveats\n * behind a hover.\n *\n * ## Why it is route-backed and not a listener\n *\n * `storefrontTaxCollected` is denied to every client in the rules — the\n * collection spans every merchant and a row carries a shopper's address. The\n * server route applies the `hostId` boundary. Nothing here can read another\n * merchant's sales because nothing here reads the collection at all.\n */\nexport function StorefrontTaxSummaryCard(props: StorefrontTaxSummaryCardProps) {\n const { hostId } = props\n const { data: user } = useUser()\n const months = useMemo(() => monthOptions(), [])\n const [period, setPeriod] = useState(months[0].value)\n const [payload, setPayload] = useState<Payload | null>(null)\n /**\n * Three outcomes, not two (the AGL-1380 rule). \"No tax collected\" is a claim\n * about this merchant's sales; rendering it because a request FAILED would\n * tell a merchant who collected tax that they collected none — on the one\n * surface where that mistake could reach a filing.\n */\n const [loadState, setLoadState] = useState<'pending' | 'error' | 'loaded'>(\n 'pending',\n )\n const [retryNonce, setRetryNonce] = useState(0)\n\n const selected = months.find((month) => month.value === period) ?? months[0]\n\n const load = useCallback(async () => {\n const search = new URLSearchParams({\n hostId,\n from: selected.from,\n to: selected.to,\n })\n const response = await authorizedFetch(\n user,\n `/api/commerce/tax-summary?${search.toString()}`,\n )\n if (!response.ok) throw new Error('tax summary failed')\n return (await response.json()) as Payload\n }, [user, hostId, selected.from, selected.to])\n\n useEffect(() => {\n if (!user) return\n let cancelled = false\n setLoadState('pending')\n load()\n .then((next) => {\n if (cancelled) return\n setPayload(next)\n setLoadState('loaded')\n })\n .catch(() => {\n if (!cancelled) setLoadState('error')\n })\n return () => {\n cancelled = true\n }\n }, [load, user, retryNonce])\n\n const groups = payload\n ? ([\n {\n key: 'aglynLiable',\n bucket: payload.summary.aglynLiable,\n title: `Tax Stripe calculated against ${PLATFORM_BRAND_NAME}’s registrations`,\n how:\n 'Automatic tax was on for these sales. Stripe computed the rate ' +\n `against the tax registrations on ${PLATFORM_BRAND_NAME}’s platform ` +\n `account, and ${PLATFORM_BRAND_NAME} holds what was collected.`,\n },\n {\n key: 'merchantManual',\n bucket: payload.summary.merchantManual,\n title: 'Tax your store calculated at your own rate',\n how:\n 'Automatic tax was off for these sales. The rate came from the ' +\n 'tax settings you configured on this store, applied to your own ' +\n 'declared origin.',\n },\n {\n key: 'connectedAccountLiable',\n bucket: payload.summary.connectedAccountLiable,\n title: 'Tax Stripe calculated against your connected account',\n how:\n 'Automatic tax was on and Stripe named your connected account ' +\n 'as the liable party for these sales.',\n },\n ] as const)\n : []\n\n return (\n <CardDisplay\n header={'Storefront sales tax'}\n subheader={'What your storefront collected, grouped by how it was calculated.'}\n help={pluginDocsHelp('commerce', {\n anchor: '#storefront-sales-tax',\n excerpt:\n 'What your storefront collected in sales tax, grouped by how each ' +\n `figure was calculated. ${PLATFORM_BRAND_NAME} does not provide tax advice.`,\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <TextField\n select\n size=\"small\"\n label=\"Period\"\n value={period}\n onChange={(event) => setPeriod(event.target.value)}\n sx={{ maxWidth: '16rem' }}\n >\n {months.map((month) => (\n <MenuItem key={month.value} value={month.value}>\n {month.label}\n </MenuItem>\n ))}\n </TextField>\n\n {loadState === 'pending' ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n Loading your sales tax…\n </Typography>\n ) : loadState === 'error' || !payload ? (\n <Alert\n severity=\"warning\"\n action={\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={() => setRetryNonce((nonce) => nonce + 1)}\n >\n Retry\n </Button>\n }\n >\n We couldn’t load your sales tax for this period. This figure is\n blank because the request failed, not because no tax was collected.\n </Alert>\n ) : (\n <>\n {/*\n THE NON-ADVISORY NOTE, first and unmissable. Rendered ABOVE the\n numbers because it governs how all of them should be read.\n\n It now states the position Terms §10.7 publishes (AGL-1956)\n instead of the sentence it used to carry — \"Aglyn does not make\n that determination for you\" — which the Terms themselves have\n contradicted since 2026-08-24. What it still does NOT do is\n reach a per-merchant or per-jurisdiction verdict: §10.7 binds\n Aglyn only where applicable law gives it an obligation, so the\n note describes which bucket Aglyn holds and remits and leaves\n the rest where the merchant's own rate put it.\n */}\n <Alert severity=\"info\">\n These figures show what was collected and how each was\n calculated. For storefront sales {PLATFORM_BRAND_NAME} acts as a\n marketplace facilitator: where applicable law gives{' '}\n {PLATFORM_BRAND_NAME} a collection obligation, the tax it\n calculated is added on top at checkout, held by{' '}\n {PLATFORM_BRAND_NAME} rather than transferred to your connected\n payment account, and remitted by {PLATFORM_BRAND_NAME}. Tax\n calculated at a rate you configured yourself stays yours. See\n Terms of Service §10.7.{' '}\n <strong>\n {PLATFORM_BRAND_NAME} does not provide tax advice.\n </strong>{' '}\n Please confirm your obligations with a qualified tax\n professional.\n </Alert>\n\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`${payload.summary.transactionCount} taxed ${\n payload.summary.transactionCount === 1 ? 'sale' : 'sales'\n } in this period.`}\n </Typography>\n\n {payload.summary.transactionCount === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n No storefront sale carried tax in this period.\n </Typography>\n ) : null}\n\n {groups\n .filter((group) => group.bucket.transactionCount > 0)\n .map((group) => (\n <Box key={group.key}>\n <Divider sx={{ mb: 1.5 }} />\n <Typography variant=\"subtitle2\">{group.title}</Typography>\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n component=\"div\"\n sx={{ mb: 1 }}\n >\n {group.how}\n </Typography>\n <Stack\n direction=\"row\"\n spacing={3}\n sx={{ flexWrap: 'wrap', rowGap: 1, mb: 1 }}\n >\n <Box>\n <Typography variant=\"h6\">\n {usd(group.bucket.taxCollectedCents)}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n tax collected\n </Typography>\n </Box>\n <Box>\n <Typography variant=\"h6\">\n {usd(group.bucket.grossCents)}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n gross, tax included\n </Typography>\n </Box>\n <Box>\n <Typography variant=\"h6\">\n {group.bucket.transactionCount}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {group.bucket.transactionCount === 1\n ? 'sale'\n : 'sales'}\n </Typography>\n </Box>\n </Stack>\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Jurisdiction'}</TableCell>\n <TableCell align=\"right\">{'Sales'}</TableCell>\n <TableCell align=\"right\">{'Taxable base'}</TableCell>\n <TableCell align=\"right\">{'Tax'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {Object.entries(\n group.bucket.byJurisdiction as Record<\n string,\n Jurisdiction\n >,\n ).map(([code, jurisdiction]) => (\n <TableRow key={code}>\n <TableCell>\n {code === 'unknown' ? 'Not stated' : code}\n </TableCell>\n <TableCell align=\"right\">\n {usd(jurisdiction.totalSalesCents)}\n </TableCell>\n <TableCell align=\"right\">\n {usd(jurisdiction.taxableSalesCents)}\n </TableCell>\n <TableCell align=\"right\">\n {usd(jurisdiction.taxCollectedCents)}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n </Box>\n ))}\n\n {/*\n WHAT THE NUMBER DOES NOT YET INCLUDE. Facts about the data, on\n the card rather than behind a hover, because each one changes\n what a merchant should do with the figure.\n */}\n <Divider />\n <Stack spacing={0.5}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Refunds are not reflected. A refunded sale keeps its full ' +\n 'tax here, so these figures over-state whenever you have ' +\n 'refunded in this period.'}\n </Typography>\n {payload.summary.attention.rowsMissingTaxableBase > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${payload.summary.attention.rowsMissingTaxableBase} sale(s) collected tax but Stripe did not state the amount the rate was applied to. Their tax is counted; their taxable base is not, so “Taxable base” above is understated.`}\n </Typography>\n ) : null}\n {payload.summary.attention.rowsMissingAddress > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${payload.summary.attention.rowsMissingAddress} sale(s) carried no customer address and are grouped under “Not stated”.`}\n </Typography>\n ) : null}\n {payload.summary.attention.nonUsdRows > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${payload.summary.attention.nonUsdRows} sale(s) were in a currency other than USD and are summed here without conversion.`}\n </Typography>\n ) : null}\n {payload.summary.attention.rowsUnclassified > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${payload.summary.attention.rowsUnclassified} sale(s) could not be classified and are excluded from every group above rather than guessed into one.`}\n </Typography>\n ) : null}\n {payload.undatedRows > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${payload.undatedRows} recorded sale(s) carry no payment date and fall outside every period, including this one.`}\n </Typography>\n ) : null}\n {payload.truncated ? (\n <Typography variant=\"caption\" color=\"warning.main\">\n {'This period has more sales than one report can read. The ' +\n 'figures above are PARTIAL — narrow the period.'}\n </Typography>\n ) : null}\n </Stack>\n </>\n )}\n </Stack>\n </CardDisplay>\n )\n}\nStorefrontTaxSummaryCard.displayName = 'StorefrontTaxSummaryCard'\n\nexport default StorefrontTaxSummaryCard\n"],"names":["PLATFORM_BRAND_NAME","pluginDocsHelp","CardDisplay","ScrollTable","Alert","Box","Button","Divider","MenuItem","Stack","TableBody","TableCell","TableHead","TableRow","TextField","Typography","useCallback","useEffect","useMemo","useState","useUser","authorizedFetch","usd","cents","toFixed","monthOptions","now","Date","Array","from","length","_item","index","start","UTC","getUTCFullYear","getUTCMonth","end","value","toISOString","label","toLocaleDateString","undefined","month","year","timeZone","to","StorefrontTaxSummaryCard","props","months","hostId","data","user","period","setPeriod","payload","setPayload","loadState","setLoadState","retryNonce","setRetryNonce","selected","find","load","search","URLSearchParams","response","toString","ok","Error","json","cancelled","then","next","catch","groups","key","bucket","summary","aglynLiable","title","how","merchantManual","connectedAccountLiable","header","subheader","help","anchor","excerpt","contentGutterX","contentGutterY","spacing","select","size","onChange","event","target","sx","maxWidth","map","variant","color","severity","action","onClick","nonce","strong","transactionCount","filter","group","mb","component","direction","flexWrap","rowGap","taxCollectedCents","grossCents","align","Object","entries","byJurisdiction","code","jurisdiction","totalSalesCents","taxableSalesCents","attention","rowsMissingTaxableBase","rowsMissingAddress","nonUsdRows","rowsUnclassified","undatedRows","truncated","displayName"],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
18
18
|
* limitations under the License.
|
|
19
19
|
*/ import * as Aglyn from "@aglyn/aglyn";
|
|
20
20
|
import { trackEventBeforeNavigation } from "@aglyn/aglyn/app-utils/analytics-events";
|
|
21
|
-
import {
|
|
21
|
+
import { utmTouchField } from "@aglyn/aglyn/app-utils/utm-touch";
|
|
22
22
|
import { mdiAccountPlusOutline } from "@aglyn/shared-data-mdi";
|
|
23
23
|
import Alert from "@mui/material/Alert";
|
|
24
24
|
import Box from "@mui/material/Box";
|
|
@@ -76,7 +76,7 @@ export const ID = 'member-signup';
|
|
|
76
76
|
displayName
|
|
77
77
|
} : {}, marketingConsent ? {
|
|
78
78
|
marketingConsent: true
|
|
79
|
-
} : {},
|
|
79
|
+
} : {}, utmTouchField()))
|
|
80
80
|
});
|
|
81
81
|
if (!response.ok) {
|
|
82
82
|
var _ref;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/member-signup.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 { trackEventBeforeNavigation } from '@aglyn/aglyn/app-utils/analytics-events'\nimport { campaignTouchField } from '@aglyn/aglyn/app-utils/campaign-touch'\nimport { mdiAccountPlusOutline } from '@aglyn/shared-data-mdi'\nimport Alert from '@mui/material/Alert'\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Checkbox from '@mui/material/Checkbox'\nimport FormControlLabel from '@mui/material/FormControlLabel'\nimport Link from '@mui/material/Link'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef, useCallback, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport {\n continuePathFromLocation,\n memberNavigation,\n} from '../utils/member-continue'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'member-signup'\n\nexport interface MemberSignupProps {\n heading?: string\n signinPath?: string\n continueFallback?: string\n}\n\n/**\n * Standalone sign-up block (AGL-553), extracted from the customer-account\n * create-account tab. Registers through the AGL-109 membership API (with\n * the same belt-and-braces follow-up login the account block does), then\n * sends the new member to the validated `continue` query param (relative\n * paths only) or the configured fallback.\n */\nconst MemberSignup = forwardRef<HTMLDivElement, MemberSignupProps>(\n (props, ref) => {\n const { heading, signinPath, continueFallback, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { hostId } = Aglyn.useSite()\n const siteFetch = Aglyn.useSiteFetch()\n const [displayName, setDisplayName] = useState('')\n const [email, setEmail] = useState('')\n const [password, setPassword] = useState('')\n const [marketingConsent, setMarketingConsent] = useState(false)\n const [error, setError] = useState('')\n const [busy, setBusy] = useState(false)\n\n const handleSignup = useCallback(async () => {\n if (!hostId || busy) return\n setBusy(true)\n setError('')\n try {\n const response = await siteFetch('/api/membership/register', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n email,\n password,\n ...(displayName ? { displayName } : {}),\n ...(marketingConsent ? { marketingConsent: true } : {}),\n // The campaign this visitor came from, when they came from one.\n // A sign-up is the identify moment that also writes a lead, so\n // the touch has to arrive with the request that creates it.\n ...campaignTouchField(),\n }),\n })\n if (!response.ok) {\n const payload = await response.json().catch(() => ({}))\n setError(String(payload?.error ?? 'Sign-up failed'))\n return\n }\n // Register does not set the cookie on every deployment; sign in\n // right after to be safe (same as the customer-account block).\n await siteFetch('/api/membership/login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, email, password }),\n }).catch(() => undefined)\n /*\n * The host's own GA4 property, not Aglyn's (AGL-1591). A storefront\n * that switched its members to accounts had no way to see it happen:\n * `sign_up` is a GA4 recommended event and every other funnel step on\n * this site already reports one, so the account door was the single\n * hole in a funnel the operator can otherwise follow end to end.\n *\n * AWAITED, because the line below tears the document down. Delivery on\n * this surface is a synchronous `window.gtag` call today, so the await\n * costs a microtask — it is written this way because the property that\n * makes a bare call safe is invisible from here, and the day a host\n * registers an async transport the event would start disappearing on\n * exactly the branch that navigates.\n */\n await trackEventBeforeNavigation('sign_up', { method: 'password' })\n memberNavigation.assign(\n continuePathFromLocation(continueFallback || '/'),\n )\n } finally {\n setBusy(false)\n }\n }, [\n hostId,\n busy,\n displayName,\n email,\n password,\n marketingConsent,\n continueFallback,\n siteFetch,\n ])\n\n if (!hostId) {\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[\n {\n p: 3,\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n color: 'text.secondary',\n fontSize: 13,\n fontFamily: 'system-ui, sans-serif',\n },\n ...nodeSx,\n ]}\n >\n {'Member sign-up — the create-account form renders here'}\n </Box>\n )\n }\n\n return (\n <Box ref={ref} {...rest} sx={[{ maxWidth: 420 }, ...nodeSx]}>\n {heading ? (\n <Typography variant=\"h5\" gutterBottom>\n {heading}\n </Typography>\n ) : null}\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>\n <TextField\n label=\"Name\"\n value={displayName}\n onChange={(event) => setDisplayName(event.target.value)}\n size=\"small\"\n />\n <TextField\n label=\"Email\"\n type=\"email\"\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n size=\"small\"\n />\n <TextField\n label=\"Password\"\n type=\"password\"\n value={password}\n onChange={(event) => setPassword(event.target.value)}\n size=\"small\"\n helperText=\"At least 8 characters\"\n />\n <FormControlLabel\n control={\n <Checkbox\n size=\"small\"\n checked={marketingConsent}\n onChange={(event) =>\n setMarketingConsent(event.target.checked)\n }\n />\n }\n label=\"Email me about offers and updates\"\n />\n {error ? <Alert severity=\"error\">{error}</Alert> : null}\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={busy || !email || password.length < 8}\n onClick={handleSignup}\n >\n {busy ? 'Working…' : 'Create account'}\n </Button>\n <Typography variant=\"body2\">\n <Link href={signinPath || '/signin'} underline=\"hover\">\n {'Already a member? Sign in'}\n </Link>\n </Typography>\n </Box>\n </Box>\n )\n },\n)\nMemberSignup.displayName = 'AglynMemberSignup'\n\nexport const schema: Aglyn.ComponentSchema<MemberSignupProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Member sign-up',\n description: 'A create-account form for site membership.',\n category: Aglyn.ComponentCategory.MEMBERS,\n icon: { path: mdiAccountPlusOutline.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'heading',\n label: 'Heading',\n description:\n 'The title above the sign-up fields. Say what joining gets them; ' +\n 'blank renders no heading at all.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'signinPath',\n label: 'Sign-in path',\n description: 'Where \"Already a member?\" points (default /signin).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'continueFallback',\n label: 'After sign-up',\n description:\n 'Where a new member goes once the account EXISTS, when nothing sent ' +\n 'them here. A `?continue=` on the URL always wins. Point it at a ' +\n 'welcome or onboarding page rather than the home page if you have ' +\n 'one.',\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: 'Member sign-up',\n pluginId: BUNDLE_ID,\n description: 'Create-account form for site membership',\n category: Aglyn.ComponentCategory.MEMBERS,\n icon: { path: mdiAccountPlusOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { heading: 'Create your account' },\n },\n },\n]\n\nexport default MemberSignup\n"],"names":["Aglyn","trackEventBeforeNavigation","campaignTouchField","mdiAccountPlusOutline","Alert","Box","Button","Checkbox","FormControlLabel","Link","TextField","Typography","forwardRef","useCallback","useState","BUNDLE_ID","continuePathFromLocation","memberNavigation","generatePresetId","ID","MemberSignup","props","ref","heading","signinPath","continueFallback","rest","nodeSx","Array","isArray","hostId","useSite","siteFetch","useSiteFetch","displayName","setDisplayName","email","setEmail","password","setPassword","marketingConsent","setMarketingConsent","error","setError","busy","setBusy","handleSignup","response","method","headers","body","JSON","stringify","ok","payload","json","catch","String","undefined","assign","sx","p","border","borderColor","borderRadius","color","fontSize","fontFamily","maxWidth","variant","gutterBottom","display","flexDirection","gap","label","value","onChange","event","target","size","type","helperText","control","checked","severity","disabled","length","onClick","href","underline","schema","$id","pluginId","description","category","ComponentCategory","MEMBERS","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","name","component","FieldComponentType","TEXT_FIELD","presets","NodeType","PRESET","data","componentId"],"mappings":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,0BAA0B,QAAQ,0CAAyC;AACpF,SAASC,kBAAkB,QAAQ,wCAAuC;AAC1E,SAASC,qBAAqB,QAAQ,yBAAwB;AAC9D,OAAOC,WAAW,sBAAqB;AACvC,OAAOC,SAAS,oBAAmB;AACnC,OAAOC,YAAY,uBAAsB;AACzC,OAAOC,cAAc,yBAAwB;AAC7C,OAAOC,sBAAsB,iCAAgC;AAC7D,OAAOC,UAAU,qBAAoB;AACrC,OAAOC,eAAe,0BAAyB;AAC/C,OAAOC,gBAAgB,2BAA0B;AACjD,SAASC,UAAU,EAAEC,WAAW,EAAEC,QAAQ,QAAQ,QAAO;AACzD,SAASC,SAAS,QAAQ,gCAA4B;AACtD,SACEC,wBAAwB,EACxBC,gBAAgB,QACX,8BAA0B;AACjC,SAASC,gBAAgB,QAAQ,iCAA6B;AAE9D,iEAAiE;AACjE,OAAO,MAAMC,KAAwB,gBAAe;AAQpD;;;;;;CAMC,GACD,MAAMC,6BAAeR,WACnB,CAACS,OAAOC;IACN,MAAM,EAAEC,OAAO,EAAEC,UAAU,EAAEC,gBAAgB,EAAW,GAAGJ,OAATK,wCAASL;;;;;IAC3D,yEAAyE;IACzE,MAAMM,SAASC,MAAMC,OAAO,CAACR,KAAK,CAAC,KAAK,IAAIA,KAAK,CAAC,KAAK,GAAG;QAACA,KAAK,CAAC,KAAK;KAAC;IACvE,MAAM,EAAES,MAAM,EAAE,GAAG9B,MAAM+B,OAAO;IAChC,MAAMC,YAAYhC,MAAMiC,YAAY;IACpC,MAAM,CAACC,aAAaC,eAAe,GAAGrB,SAAS;IAC/C,MAAM,CAACsB,OAAOC,SAAS,GAAGvB,SAAS;IACnC,MAAM,CAACwB,UAAUC,YAAY,GAAGzB,SAAS;IACzC,MAAM,CAAC0B,kBAAkBC,oBAAoB,GAAG3B,SAAS;IACzD,MAAM,CAAC4B,OAAOC,SAAS,GAAG7B,SAAS;IACnC,MAAM,CAAC8B,MAAMC,QAAQ,GAAG/B,SAAS;IAEjC,MAAMgC,eAAejC,YAAY;QAC/B,IAAI,CAACiB,UAAUc,MAAM;QACrBC,QAAQ;QACRF,SAAS;QACT,IAAI;YACF,MAAMI,WAAW,MAAMf,UAAU,4BAA4B;gBAC3DgB,QAAQ;gBACRC,SAAS;oBAAE,gBAAgB;gBAAmB;gBAC9CC,MAAMC,KAAKC,SAAS,CAAC;oBACnBtB;oBACAM;oBACAE;mBACIJ,cAAc;oBAAEA;gBAAY,IAAI,CAAC,GACjCM,mBAAmB;oBAAEA,kBAAkB;gBAAK,IAAI,CAAC,GAIlDtC;YAEP;YACA,IAAI,CAAC6C,SAASM,EAAE,EAAE;;gBAChB,MAAMC,UAAU,MAAMP,SAASQ,IAAI,GAAGC,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA;gBACpDb,SAASc,eAAOH,2BAAAA,QAASZ,KAAK,mBAAI;gBAClC;YACF;YACA,gEAAgE;YAChE,+DAA+D;YAC/D,MAAMV,UAAU,yBAAyB;gBACvCgB,QAAQ;gBACRC,SAAS;oBAAE,gBAAgB;gBAAmB;gBAC9CC,MAAMC,KAAKC,SAAS,CAAC;oBAAEtB;oBAAQM;oBAAOE;gBAAS;YACjD,GAAGkB,KAAK,CAAC,IAAME;YACf;;;;;;;;;;;;;SAaC,GACD,MAAMzD,2BAA2B,WAAW;gBAAE+C,QAAQ;YAAW;YACjE/B,iBAAiB0C,MAAM,CACrB3C,yBAAyBS,oBAAoB;QAEjD,SAAU;YACRoB,QAAQ;QACV;IACF,GAAG;QACDf;QACAc;QACAV;QACAE;QACAE;QACAE;QACAf;QACAO;KACD;IAED,IAAI,CAACF,QAAQ;QACX,qBACE,KAACzB;YACCiB,KAAKA;WACDI;YACJkC,IAAI;gBACF;oBACEC,GAAG;oBACHC,QAAQ;oBACRC,aAAa;oBACbC,cAAc;oBACdC,OAAO;oBACPC,UAAU;oBACVC,YAAY;gBACd;mBACGxC;aACJ;sBAEA;;IAGP;IAEA,qBACE,MAACtB;QAAIiB,KAAKA;OAASI;QAAMkC,IAAI;YAAC;gBAAEQ,UAAU;YAAI;eAAMzC;SAAO;;YACxDJ,wBACC,KAACZ;gBAAW0D,SAAQ;gBAAKC,YAAY;0BAClC/C;iBAED;0BACJ,MAAClB;gBAAIuD,IAAI;oBAAEW,SAAS;oBAAQC,eAAe;oBAAUC,KAAK;gBAAI;;kCAC5D,KAAC/D;wBACCgE,OAAM;wBACNC,OAAOzC;wBACP0C,UAAU,CAACC,QAAU1C,eAAe0C,MAAMC,MAAM,CAACH,KAAK;wBACtDI,MAAK;;kCAEP,KAACrE;wBACCgE,OAAM;wBACNM,MAAK;wBACLL,OAAOvC;wBACPwC,UAAU,CAACC,QAAUxC,SAASwC,MAAMC,MAAM,CAACH,KAAK;wBAChDI,MAAK;;kCAEP,KAACrE;wBACCgE,OAAM;wBACNM,MAAK;wBACLL,OAAOrC;wBACPsC,UAAU,CAACC,QAAUtC,YAAYsC,MAAMC,MAAM,CAACH,KAAK;wBACnDI,MAAK;wBACLE,YAAW;;kCAEb,KAACzE;wBACC0E,uBACE,KAAC3E;4BACCwE,MAAK;4BACLI,SAAS3C;4BACToC,UAAU,CAACC,QACTpC,oBAAoBoC,MAAMC,MAAM,CAACK,OAAO;;wBAI9CT,OAAM;;oBAEPhC,sBAAQ,KAACtC;wBAAMgF,UAAS;kCAAS1C;yBAAiB;kCACnD,KAACpC;wBACC+D,SAAQ;wBACRJ,OAAM;wBACNoB,UAAUzC,QAAQ,CAACR,SAASE,SAASgD,MAAM,GAAG;wBAC9CC,SAASzC;kCAERF,OAAO,aAAa;;kCAEvB,KAACjC;wBAAW0D,SAAQ;kCAClB,cAAA,KAAC5D;4BAAK+E,MAAMhE,cAAc;4BAAWiE,WAAU;sCAC5C;;;;;;;AAMb;AAEFrE,aAAac,WAAW,GAAG;AAE3B,OAAO,MAAMwD,SAAmD;IAC9DC,KAAKxE;IACLyE,UAAU7E;IACVmB,aAAa;IACb2D,aAAa;IACbC,UAAU9F,MAAM+F,iBAAiB,CAACC,OAAO;IACzCC,MAAM;QAAEC,MAAM/F,sBAAsB+F,IAAI;QAAEtC,IAAI;YAAEK,OAAO;QAAU;IAAE;IACnEkC,OAAO;QAAEC,aAAapG,MAAMqG,YAAY,CAACC,OAAO;IAAC;IACjDC,YAAY;QACV;YACEC,MAAM;YACN9B,OAAO;YACPmB,aACE,qEACA;YACFY,WAAWzG,MAAM0G,kBAAkB,CAACC,UAAU;QAChD;QACA;YACEH,MAAM;YACN9B,OAAO;YACPmB,aAAa;YACbY,WAAWzG,MAAM0G,kBAAkB,CAACC,UAAU;QAChD;QACA;YACEH,MAAM;YACN9B,OAAO;YACPmB,aACE,wEACA,qEACA,sEACA;YACFY,WAAWzG,MAAM0G,kBAAkB,CAACC,UAAU;QAChD;KACD;AACH,EAAC;AAED,OAAO,MAAMC,UAAgC;IAC3C;QACEjB,KAAKzE,iBAAiBC;QACtB6D,MAAMhF,MAAM6G,QAAQ,CAACC,MAAM;QAC3B5E,aAAa;QACb0D,UAAU7E;QACV8E,aAAa;QACbC,UAAU9F,MAAM+F,iBAAiB,CAACC,OAAO;QACzCC,MAAM;YAAEC,MAAM/F,sBAAsB+F,IAAI;YAAEtC,IAAI;gBAAEK,OAAO;YAAU;QAAE;QACnE8C,MAAM;YACJpB,KAAK;YACLqB,aAAa7F;YACbyE,UAAU7E;YACVM,OAAO;gBAAEE,SAAS;YAAsB;QAC1C;IACF;CACD,CAAA;AAED,eAAeH,aAAY"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/member-signup.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 { trackEventBeforeNavigation } from '@aglyn/aglyn/app-utils/analytics-events'\nimport { utmTouchField } from '@aglyn/aglyn/app-utils/utm-touch'\nimport { mdiAccountPlusOutline } from '@aglyn/shared-data-mdi'\nimport Alert from '@mui/material/Alert'\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Checkbox from '@mui/material/Checkbox'\nimport FormControlLabel from '@mui/material/FormControlLabel'\nimport Link from '@mui/material/Link'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef, useCallback, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport {\n continuePathFromLocation,\n memberNavigation,\n} from '../utils/member-continue'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'member-signup'\n\nexport interface MemberSignupProps {\n heading?: string\n signinPath?: string\n continueFallback?: string\n}\n\n/**\n * Standalone sign-up block (AGL-553), extracted from the customer-account\n * create-account tab. Registers through the AGL-109 membership API (with\n * the same belt-and-braces follow-up login the account block does), then\n * sends the new member to the validated `continue` query param (relative\n * paths only) or the configured fallback.\n */\nconst MemberSignup = forwardRef<HTMLDivElement, MemberSignupProps>(\n (props, ref) => {\n const { heading, signinPath, continueFallback, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { hostId } = Aglyn.useSite()\n const siteFetch = Aglyn.useSiteFetch()\n const [displayName, setDisplayName] = useState('')\n const [email, setEmail] = useState('')\n const [password, setPassword] = useState('')\n const [marketingConsent, setMarketingConsent] = useState(false)\n const [error, setError] = useState('')\n const [busy, setBusy] = useState(false)\n\n const handleSignup = useCallback(async () => {\n if (!hostId || busy) return\n setBusy(true)\n setError('')\n try {\n const response = await siteFetch('/api/membership/register', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n email,\n password,\n ...(displayName ? { displayName } : {}),\n ...(marketingConsent ? { marketingConsent: true } : {}),\n // The campaign this visitor came from, when they came from one.\n // A sign-up is the identify moment that also writes a lead, so\n // the touch has to arrive with the request that creates it.\n ...utmTouchField(),\n }),\n })\n if (!response.ok) {\n const payload = await response.json().catch(() => ({}))\n setError(String(payload?.error ?? 'Sign-up failed'))\n return\n }\n // Register does not set the cookie on every deployment; sign in\n // right after to be safe (same as the customer-account block).\n await siteFetch('/api/membership/login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, email, password }),\n }).catch(() => undefined)\n /*\n * The host's own GA4 property, not Aglyn's (AGL-1591). A storefront\n * that switched its members to accounts had no way to see it happen:\n * `sign_up` is a GA4 recommended event and every other funnel step on\n * this site already reports one, so the account door was the single\n * hole in a funnel the operator can otherwise follow end to end.\n *\n * AWAITED, because the line below tears the document down. Delivery on\n * this surface is a synchronous `window.gtag` call today, so the await\n * costs a microtask — it is written this way because the property that\n * makes a bare call safe is invisible from here, and the day a host\n * registers an async transport the event would start disappearing on\n * exactly the branch that navigates.\n */\n await trackEventBeforeNavigation('sign_up', { method: 'password' })\n memberNavigation.assign(\n continuePathFromLocation(continueFallback || '/'),\n )\n } finally {\n setBusy(false)\n }\n }, [\n hostId,\n busy,\n displayName,\n email,\n password,\n marketingConsent,\n continueFallback,\n siteFetch,\n ])\n\n if (!hostId) {\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[\n {\n p: 3,\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n color: 'text.secondary',\n fontSize: 13,\n fontFamily: 'system-ui, sans-serif',\n },\n ...nodeSx,\n ]}\n >\n {'Member sign-up — the create-account form renders here'}\n </Box>\n )\n }\n\n return (\n <Box ref={ref} {...rest} sx={[{ maxWidth: 420 }, ...nodeSx]}>\n {heading ? (\n <Typography variant=\"h5\" gutterBottom>\n {heading}\n </Typography>\n ) : null}\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>\n <TextField\n label=\"Name\"\n value={displayName}\n onChange={(event) => setDisplayName(event.target.value)}\n size=\"small\"\n />\n <TextField\n label=\"Email\"\n type=\"email\"\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n size=\"small\"\n />\n <TextField\n label=\"Password\"\n type=\"password\"\n value={password}\n onChange={(event) => setPassword(event.target.value)}\n size=\"small\"\n helperText=\"At least 8 characters\"\n />\n <FormControlLabel\n control={\n <Checkbox\n size=\"small\"\n checked={marketingConsent}\n onChange={(event) =>\n setMarketingConsent(event.target.checked)\n }\n />\n }\n label=\"Email me about offers and updates\"\n />\n {error ? <Alert severity=\"error\">{error}</Alert> : null}\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={busy || !email || password.length < 8}\n onClick={handleSignup}\n >\n {busy ? 'Working…' : 'Create account'}\n </Button>\n <Typography variant=\"body2\">\n <Link href={signinPath || '/signin'} underline=\"hover\">\n {'Already a member? Sign in'}\n </Link>\n </Typography>\n </Box>\n </Box>\n )\n },\n)\nMemberSignup.displayName = 'AglynMemberSignup'\n\nexport const schema: Aglyn.ComponentSchema<MemberSignupProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Member sign-up',\n description: 'A create-account form for site membership.',\n category: Aglyn.ComponentCategory.MEMBERS,\n icon: { path: mdiAccountPlusOutline.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'heading',\n label: 'Heading',\n description:\n 'The title above the sign-up fields. Say what joining gets them; ' +\n 'blank renders no heading at all.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'signinPath',\n label: 'Sign-in path',\n description: 'Where \"Already a member?\" points (default /signin).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'continueFallback',\n label: 'After sign-up',\n description:\n 'Where a new member goes once the account EXISTS, when nothing sent ' +\n 'them here. A `?continue=` on the URL always wins. Point it at a ' +\n 'welcome or onboarding page rather than the home page if you have ' +\n 'one.',\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: 'Member sign-up',\n pluginId: BUNDLE_ID,\n description: 'Create-account form for site membership',\n category: Aglyn.ComponentCategory.MEMBERS,\n icon: { path: mdiAccountPlusOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { heading: 'Create your account' },\n },\n },\n]\n\nexport default MemberSignup\n"],"names":["Aglyn","trackEventBeforeNavigation","utmTouchField","mdiAccountPlusOutline","Alert","Box","Button","Checkbox","FormControlLabel","Link","TextField","Typography","forwardRef","useCallback","useState","BUNDLE_ID","continuePathFromLocation","memberNavigation","generatePresetId","ID","MemberSignup","props","ref","heading","signinPath","continueFallback","rest","nodeSx","Array","isArray","hostId","useSite","siteFetch","useSiteFetch","displayName","setDisplayName","email","setEmail","password","setPassword","marketingConsent","setMarketingConsent","error","setError","busy","setBusy","handleSignup","response","method","headers","body","JSON","stringify","ok","payload","json","catch","String","undefined","assign","sx","p","border","borderColor","borderRadius","color","fontSize","fontFamily","maxWidth","variant","gutterBottom","display","flexDirection","gap","label","value","onChange","event","target","size","type","helperText","control","checked","severity","disabled","length","onClick","href","underline","schema","$id","pluginId","description","category","ComponentCategory","MEMBERS","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","name","component","FieldComponentType","TEXT_FIELD","presets","NodeType","PRESET","data","componentId"],"mappings":"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"}
|
|
@@ -17,7 +17,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
17
17
|
* See the License for the specific language governing permissions and
|
|
18
18
|
* limitations under the License.
|
|
19
19
|
*/ import { trackEvent } from "@aglyn/aglyn/app-utils/analytics-events";
|
|
20
|
-
import {
|
|
20
|
+
import { utmTouchField } from "@aglyn/aglyn/app-utils/utm-touch";
|
|
21
21
|
import * as Aglyn from "@aglyn/aglyn";
|
|
22
22
|
import { mdiEmailOutline } from "@aglyn/shared-data-mdi";
|
|
23
23
|
import Box from "@mui/material/Box";
|
|
@@ -62,7 +62,7 @@ export const ID = 'newsletter-signup';
|
|
|
62
62
|
email
|
|
63
63
|
}, listId ? {
|
|
64
64
|
listId
|
|
65
|
-
} : {},
|
|
65
|
+
} : {}, utmTouchField()))
|
|
66
66
|
});
|
|
67
67
|
// GA4 lead conversion (AGL-1561) — AGL-301's subscribe element is a
|
|
68
68
|
// form completion in every sense that matters, and it shares nothing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/newsletter-signup.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 { trackEvent } from '@aglyn/aglyn/app-utils/analytics-events'\nimport { campaignTouchField } from '@aglyn/aglyn/app-utils/campaign-touch'\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiEmailOutline } from '@aglyn/shared-data-mdi'\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'newsletter-signup'\n\nexport interface NewsletterSignupProps {\n heading?: string\n buttonLabel?: string\n successText?: string\n /**\n * Enrolls the subscriber into this list's members (AGL-2499), in\n * addition to the always-on contacts upsert. This is the id shown on the\n * Audiences page's list settings — copy it from there. Left blank, the\n * block behaves exactly as before: contacts only.\n */\n listId?: string\n}\n\n/**\n * Newsletter signup block (AGL-301): consented email capture straight\n * into the contacts CRM (source `newsletter`), ready for email-campaign\n * audiences.\n */\nconst NewsletterSignup = forwardRef<HTMLDivElement, NewsletterSignupProps>(\n (props, ref) => {\n const { heading, buttonLabel, successText, listId, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { hostId } = Aglyn.useSite()\n const siteFetch = Aglyn.useSiteFetch()\n const [email, setEmail] = useState('')\n const [state, setState] = useState<'idle' | 'busy' | 'done' | 'error'>(\n 'idle',\n )\n\n const handleSubscribe = async () => {\n if (!hostId || state === 'busy') return\n setState('busy')\n try {\n const response = await siteFetch('/api/commerce/newsletter', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n email,\n ...(listId ? { listId } : {}),\n // The campaign this visitor came from, when they came from one.\n ...campaignTouchField(),\n }),\n })\n // GA4 lead conversion (AGL-1561) — AGL-301's subscribe element is a\n // form completion in every sense that matters, and it shares nothing\n // with the generic `Form` element, so it needs its own call. Only on\n // `response.ok`: a failed subscribe is not a lead. The email is\n // deliberately absent from the payload.\n if (response.ok) {\n trackEvent('generate_lead', {\n form_name: 'Newsletter',\n form_location: window.location.pathname,\n })\n }\n setState(response.ok ? 'done' : 'error')\n } catch {\n setState('error')\n }\n }\n\n if (state === 'done') {\n return (\n <Box ref={ref} {...rest} sx={[{ py: 1 }, ...nodeSx]}>\n <Typography variant=\"body2\">\n {successText || 'You’re on the list — thanks!'}\n </Typography>\n </Box>\n )\n }\n\n return (\n <Box ref={ref} {...rest}>\n {heading ? (\n // `component=\"p\"` because this introduces the field below it and is\n // not a section heading (AGL-2486). MUI's `defaultVariantMapping`\n // sends `subtitle1` to `<h6>`, so without this the signup block puts\n // a level-6 heading into the outline of every page it appears on. On\n // a blog listing the nearest preceding heading is an `h3`, which\n // makes it a skipped level and a `heading-order` failure.\n //\n // It is also not the field's label — see the `aria-label` below,\n // which is the input's only accessible name.\n <Typography component=\"p\" variant=\"subtitle1\" gutterBottom>\n {heading}\n </Typography>\n ) : null}\n <Box sx={{ display: 'flex', gap: 1 }}>\n <TextField\n type=\"email\"\n placeholder=\"you@example.com\"\n // The field's only accessible name (AGL-2392). `heading` is\n // optional and is a plain `subtitle1` above the row, never a\n // `<label>`, so without this the input ships nameless — which is\n // what aglyn.com/blog does today. A placeholder is not a name:\n // assistive tech is not required to announce it, and it vanishes\n // the moment the visitor types.\n slotProps={{ htmlInput: { 'aria-label': 'Email address' } }}\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n size=\"small\"\n sx={{ flex: 1, maxWidth: 320 }}\n error={state === 'error'}\n helperText={state === 'error' ? 'Enter a valid email' : undefined}\n />\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!hostId || state === 'busy' || !email.trim()}\n onClick={handleSubscribe}\n >\n {state === 'busy' ? '…' : buttonLabel || 'Subscribe'}\n </Button>\n </Box>\n </Box>\n )\n },\n)\nNewsletterSignup.displayName = 'AglynNewsletterSignup'\n\nexport const schema: Aglyn.ComponentSchema<NewsletterSignupProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Newsletter signup',\n description: 'A consented email capture that writes straight into contacts.',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiEmailOutline.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'heading',\n label: 'Heading',\n description: 'Shown above the field.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'buttonLabel',\n label: 'Button label',\n description: 'Defaults to \"Subscribe\".',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'successText',\n label: 'Success text',\n description: 'Shown after subscribing.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'listId',\n label: 'List',\n description:\n 'Optional — enrolls subscribers into this list too (id from Audiences → Lists). Blank keeps the old contacts-only behavior.',\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: 'Newsletter signup',\n pluginId: BUNDLE_ID,\n description: 'Consented email capture into contacts',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiEmailOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { heading: 'Get updates and offers' },\n },\n },\n]\n\nexport default NewsletterSignup\n"],"names":["trackEvent","campaignTouchField","Aglyn","mdiEmailOutline","Box","Button","TextField","Typography","forwardRef","useState","BUNDLE_ID","generatePresetId","ID","NewsletterSignup","props","ref","heading","buttonLabel","successText","listId","rest","nodeSx","Array","isArray","hostId","useSite","siteFetch","useSiteFetch","email","setEmail","state","setState","handleSubscribe","response","method","headers","body","JSON","stringify","ok","form_name","form_location","window","location","pathname","sx","py","variant","component","gutterBottom","display","gap","type","placeholder","slotProps","htmlInput","value","onChange","event","target","size","flex","maxWidth","error","helperText","undefined","color","disabled","trim","onClick","displayName","schema","$id","pluginId","description","category","ComponentCategory","COMMERCE","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","name","label","FieldComponentType","TEXT_FIELD","presets","NodeType","PRESET","data","componentId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/components/newsletter-signup.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 { trackEvent } from '@aglyn/aglyn/app-utils/analytics-events'\nimport { utmTouchField } from '@aglyn/aglyn/app-utils/utm-touch'\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiEmailOutline } from '@aglyn/shared-data-mdi'\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'newsletter-signup'\n\nexport interface NewsletterSignupProps {\n heading?: string\n buttonLabel?: string\n successText?: string\n /**\n * Enrolls the subscriber into this list's members (AGL-2499), in\n * addition to the always-on contacts upsert. This is the id shown on the\n * Audiences page's list settings — copy it from there. Left blank, the\n * block behaves exactly as before: contacts only.\n */\n listId?: string\n}\n\n/**\n * Newsletter signup block (AGL-301): consented email capture straight\n * into the contacts CRM (source `newsletter`), ready for email-campaign\n * audiences.\n */\nconst NewsletterSignup = forwardRef<HTMLDivElement, NewsletterSignupProps>(\n (props, ref) => {\n const { heading, buttonLabel, successText, listId, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { hostId } = Aglyn.useSite()\n const siteFetch = Aglyn.useSiteFetch()\n const [email, setEmail] = useState('')\n const [state, setState] = useState<'idle' | 'busy' | 'done' | 'error'>(\n 'idle',\n )\n\n const handleSubscribe = async () => {\n if (!hostId || state === 'busy') return\n setState('busy')\n try {\n const response = await siteFetch('/api/commerce/newsletter', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n email,\n ...(listId ? { listId } : {}),\n // The campaign this visitor came from, when they came from one.\n ...utmTouchField(),\n }),\n })\n // GA4 lead conversion (AGL-1561) — AGL-301's subscribe element is a\n // form completion in every sense that matters, and it shares nothing\n // with the generic `Form` element, so it needs its own call. Only on\n // `response.ok`: a failed subscribe is not a lead. The email is\n // deliberately absent from the payload.\n if (response.ok) {\n trackEvent('generate_lead', {\n form_name: 'Newsletter',\n form_location: window.location.pathname,\n })\n }\n setState(response.ok ? 'done' : 'error')\n } catch {\n setState('error')\n }\n }\n\n if (state === 'done') {\n return (\n <Box ref={ref} {...rest} sx={[{ py: 1 }, ...nodeSx]}>\n <Typography variant=\"body2\">\n {successText || 'You’re on the list — thanks!'}\n </Typography>\n </Box>\n )\n }\n\n return (\n <Box ref={ref} {...rest}>\n {heading ? (\n // `component=\"p\"` because this introduces the field below it and is\n // not a section heading (AGL-2486). MUI's `defaultVariantMapping`\n // sends `subtitle1` to `<h6>`, so without this the signup block puts\n // a level-6 heading into the outline of every page it appears on. On\n // a blog listing the nearest preceding heading is an `h3`, which\n // makes it a skipped level and a `heading-order` failure.\n //\n // It is also not the field's label — see the `aria-label` below,\n // which is the input's only accessible name.\n <Typography component=\"p\" variant=\"subtitle1\" gutterBottom>\n {heading}\n </Typography>\n ) : null}\n <Box sx={{ display: 'flex', gap: 1 }}>\n <TextField\n type=\"email\"\n placeholder=\"you@example.com\"\n // The field's only accessible name (AGL-2392). `heading` is\n // optional and is a plain `subtitle1` above the row, never a\n // `<label>`, so without this the input ships nameless — which is\n // what aglyn.com/blog does today. A placeholder is not a name:\n // assistive tech is not required to announce it, and it vanishes\n // the moment the visitor types.\n slotProps={{ htmlInput: { 'aria-label': 'Email address' } }}\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n size=\"small\"\n sx={{ flex: 1, maxWidth: 320 }}\n error={state === 'error'}\n helperText={state === 'error' ? 'Enter a valid email' : undefined}\n />\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!hostId || state === 'busy' || !email.trim()}\n onClick={handleSubscribe}\n >\n {state === 'busy' ? '…' : buttonLabel || 'Subscribe'}\n </Button>\n </Box>\n </Box>\n )\n },\n)\nNewsletterSignup.displayName = 'AglynNewsletterSignup'\n\nexport const schema: Aglyn.ComponentSchema<NewsletterSignupProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Newsletter signup',\n description: 'A consented email capture that writes straight into contacts.',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiEmailOutline.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'heading',\n label: 'Heading',\n description: 'Shown above the field.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'buttonLabel',\n label: 'Button label',\n description: 'Defaults to \"Subscribe\".',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'successText',\n label: 'Success text',\n description: 'Shown after subscribing.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'listId',\n label: 'List',\n description:\n 'Optional — enrolls subscribers into this list too (id from Audiences → Lists). Blank keeps the old contacts-only behavior.',\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: 'Newsletter signup',\n pluginId: BUNDLE_ID,\n description: 'Consented email capture into contacts',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiEmailOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { heading: 'Get updates and offers' },\n },\n },\n]\n\nexport default NewsletterSignup\n"],"names":["trackEvent","utmTouchField","Aglyn","mdiEmailOutline","Box","Button","TextField","Typography","forwardRef","useState","BUNDLE_ID","generatePresetId","ID","NewsletterSignup","props","ref","heading","buttonLabel","successText","listId","rest","nodeSx","Array","isArray","hostId","useSite","siteFetch","useSiteFetch","email","setEmail","state","setState","handleSubscribe","response","method","headers","body","JSON","stringify","ok","form_name","form_location","window","location","pathname","sx","py","variant","component","gutterBottom","display","gap","type","placeholder","slotProps","htmlInput","value","onChange","event","target","size","flex","maxWidth","error","helperText","undefined","color","disabled","trim","onClick","displayName","schema","$id","pluginId","description","category","ComponentCategory","COMMERCE","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","name","label","FieldComponentType","TEXT_FIELD","presets","NodeType","PRESET","data","componentId"],"mappings":"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"}
|
|
@@ -18,7 +18,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
18
18
|
* limitations under the License.
|
|
19
19
|
*/ import * as Aglyn from "@aglyn/aglyn";
|
|
20
20
|
import { buildAddToCartParams, buildBeginCheckoutParams, trackEvent, trackEventBeforeNavigation } from "@aglyn/aglyn/app-utils/analytics-events";
|
|
21
|
-
import { isPaymentsNotConfigured
|
|
21
|
+
import { isPaymentsNotConfigured } from "@aglyn/aglyn/app-utils/payments-configured";
|
|
22
|
+
import { storefrontPaymentsNotConfiguredText } from "../constants/storefront-payments.js";
|
|
22
23
|
import * as CommerceModel from "../model/index.js";
|
|
23
24
|
import { mdiTagOutline } from "@aglyn/shared-data-mdi";
|
|
24
25
|
import Alert from "@mui/material/Alert";
|