@stackframe/stack 2.8.60 → 2.8.62
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/dist/components-page/account-settings/payments/payments-panel.js +69 -2
- package/dist/components-page/account-settings/payments/payments-panel.js.map +1 -1
- package/dist/esm/components-page/account-settings/payments/payments-panel.js +70 -3
- package/dist/esm/components-page/account-settings/payments/payments-panel.js.map +1 -1
- package/dist/esm/generated/global-css.js +1 -1
- package/dist/esm/generated/global-css.js.map +1 -1
- package/dist/esm/generated/quetzal-translations.js +4086 -3904
- package/dist/esm/generated/quetzal-translations.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js +49 -1
- package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js +55 -2
- package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js +17 -6
- package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/interfaces/server-app.js.map +1 -1
- package/dist/esm/lib/stack-app/index.js.map +1 -1
- package/dist/esm/lib/stack-app/projects/index.js.map +1 -1
- package/dist/generated/global-css.js +1 -1
- package/dist/generated/global-css.js.map +1 -1
- package/dist/generated/quetzal-translations.js +4086 -3904
- package/dist/generated/quetzal-translations.js.map +1 -1
- package/dist/index.d.mts +107 -4
- package/dist/index.d.ts +107 -4
- package/dist/lib/stack-app/apps/implementations/admin-app-impl.js +49 -1
- package/dist/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/client-app-impl.js +55 -2
- package/dist/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/common.js +1 -1
- package/dist/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/server-app-impl.js +17 -6
- package/dist/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/lib/stack-app/apps/interfaces/server-app.js.map +1 -1
- package/dist/lib/stack-app/customers/index.js.map +1 -1
- package/dist/lib/stack-app/index.js.map +1 -1
- package/dist/lib/stack-app/projects/index.js.map +1 -1
- package/package.json +5 -5
|
@@ -27,7 +27,6 @@ __export(payments_panel_exports, {
|
|
|
27
27
|
module.exports = __toCommonJS(payments_panel_exports);
|
|
28
28
|
var import_stack_shared = require("@stackframe/stack-shared");
|
|
29
29
|
var import_promises = require("@stackframe/stack-shared/dist/utils/promises");
|
|
30
|
-
var import_results = require("@stackframe/stack-shared/dist/utils/results");
|
|
31
30
|
var import_stack_ui = require("@stackframe/stack-ui");
|
|
32
31
|
var import_react_stripe_js = require("@stripe/react-stripe-js");
|
|
33
32
|
var import_stripe_js = require("@stripe/stripe-js");
|
|
@@ -35,6 +34,7 @@ var import_react = require("react");
|
|
|
35
34
|
var import__ = require("../../../index.js");
|
|
36
35
|
var import_translations = require("../../../lib/translations.js");
|
|
37
36
|
var import_section = require("../section.js");
|
|
37
|
+
var import_results = require("@stackframe/stack-shared/dist/utils/results");
|
|
38
38
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
39
39
|
function formatPaymentMethod(pm) {
|
|
40
40
|
const details = [
|
|
@@ -44,6 +44,45 @@ function formatPaymentMethod(pm) {
|
|
|
44
44
|
].filter(Boolean);
|
|
45
45
|
return details.join(" \xB7 ");
|
|
46
46
|
}
|
|
47
|
+
var formatInvoiceStatus = (status, t) => {
|
|
48
|
+
if (!status) {
|
|
49
|
+
return t("Unknown");
|
|
50
|
+
}
|
|
51
|
+
switch (status) {
|
|
52
|
+
case "draft": {
|
|
53
|
+
return t("Draft");
|
|
54
|
+
}
|
|
55
|
+
case "open": {
|
|
56
|
+
return t("Open");
|
|
57
|
+
}
|
|
58
|
+
case "paid": {
|
|
59
|
+
return t("Paid");
|
|
60
|
+
}
|
|
61
|
+
case "uncollectible": {
|
|
62
|
+
return t("Uncollectible");
|
|
63
|
+
}
|
|
64
|
+
case "void": {
|
|
65
|
+
return t("Void");
|
|
66
|
+
}
|
|
67
|
+
default: {
|
|
68
|
+
return t("Unknown");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var formatInvoiceAmount = (amountTotal, t) => {
|
|
73
|
+
if (typeof amountTotal !== "number" || Number.isNaN(amountTotal)) {
|
|
74
|
+
return t("Unknown");
|
|
75
|
+
}
|
|
76
|
+
const normalized = amountTotal / 100;
|
|
77
|
+
const formatted = new Intl.NumberFormat(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(normalized);
|
|
78
|
+
return `$${formatted}`;
|
|
79
|
+
};
|
|
80
|
+
var formatInvoiceDate = (date, t) => {
|
|
81
|
+
if (!date || Number.isNaN(date.getTime())) {
|
|
82
|
+
return t("Unknown");
|
|
83
|
+
}
|
|
84
|
+
return new Intl.DateTimeFormat(void 0, { year: "numeric", month: "short", day: "numeric" }).format(date);
|
|
85
|
+
};
|
|
47
86
|
function SetDefaultPaymentMethodForm(props) {
|
|
48
87
|
const stripe = (0, import_react_stripe_js.useStripe)();
|
|
49
88
|
const elements = (0, import_react_stripe_js.useElements)();
|
|
@@ -148,6 +187,7 @@ function RealPaymentsPanel(props) {
|
|
|
148
187
|
const billing = props.customer.useBilling();
|
|
149
188
|
const defaultPaymentMethod = billing.defaultPaymentMethod;
|
|
150
189
|
const products = props.customer.useProducts();
|
|
190
|
+
const invoices = props.customer.useInvoices({ limit: 10 });
|
|
151
191
|
const productsForCustomerType = products.filter((product) => product.customerType === props.customerType);
|
|
152
192
|
const [paymentDialogOpen, setPaymentDialogOpen] = (0, import_react.useState)(false);
|
|
153
193
|
const [setupIntentClientSecret, setSetupIntentClientSecret] = (0, import_react.useState)(null);
|
|
@@ -364,7 +404,34 @@ ${error}`);
|
|
|
364
404
|
)
|
|
365
405
|
]
|
|
366
406
|
}
|
|
367
|
-
)
|
|
407
|
+
),
|
|
408
|
+
invoices.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
409
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Separator, {}),
|
|
410
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
|
|
411
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-1", children: [
|
|
412
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { className: "font-medium", children: t("Invoices") }),
|
|
413
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", type: "footnote", children: t("Review past invoices and receipts.") })
|
|
414
|
+
] }),
|
|
415
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "border rounded-md", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.Table, { children: [
|
|
416
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableRow, { children: [
|
|
417
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[140px]", children: t("Date") }),
|
|
418
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[120px]", children: t("Status") }),
|
|
419
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[120px]", children: t("Amount") }),
|
|
420
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[120px] text-right", children: t("Invoice") })
|
|
421
|
+
] }) }),
|
|
422
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableBody, { children: invoices.map((invoice, index) => {
|
|
423
|
+
const createdAtTime = invoice.createdAt.getTime();
|
|
424
|
+
const invoiceKey = Number.isNaN(createdAtTime) ? `invoice-${index}` : `invoice-${createdAtTime}-${index}`;
|
|
425
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableRow, { children: [
|
|
426
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: formatInvoiceDate(invoice.createdAt, t) }) }),
|
|
427
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: formatInvoiceStatus(invoice.status, t) }) }),
|
|
428
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: formatInvoiceAmount(invoice.amountTotal, t) }) }),
|
|
429
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { align: "right", children: invoice.hostedInvoiceUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { asChild: true, variant: "secondary", color: "neutral", size: "sm", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: invoice.hostedInvoiceUrl, target: "_blank", rel: "noreferrer", children: t("View") }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", type: "footnote", children: t("Unavailable") }) })
|
|
430
|
+
] }, invoiceKey);
|
|
431
|
+
}) })
|
|
432
|
+
] }) })
|
|
433
|
+
] })
|
|
434
|
+
] })
|
|
368
435
|
] });
|
|
369
436
|
}
|
|
370
437
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components-page/account-settings/payments/payments-panel.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { KnownErrors } from \"@stackframe/stack-shared\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Result } from \"@stackframe/stack-shared/dist/utils/results\";\nimport { ActionDialog, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Skeleton, toast, Typography } from \"@stackframe/stack-ui\";\nimport { CardElement, Elements, useElements, useStripe } from \"@stripe/react-stripe-js\";\nimport { loadStripe } from \"@stripe/stripe-js\";\nimport { useMemo, useState } from \"react\";\nimport { useStackApp } from \"../../..\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\ntype PaymentMethodSummary = {\n id: string,\n brand: string | null,\n last4: string | null,\n exp_month: number | null,\n exp_year: number | null,\n} | null;\n\nfunction formatPaymentMethod(pm: NonNullable<PaymentMethodSummary>) {\n const details = [\n pm.brand ? pm.brand.toUpperCase() : null,\n pm.last4 ? `•••• ${pm.last4}` : null,\n pm.exp_month && pm.exp_year ? `exp ${pm.exp_month}/${pm.exp_year}` : null,\n ].filter(Boolean);\n return details.join(\" · \");\n}\n\ntype CustomerBilling = {\n hasCustomer: boolean,\n defaultPaymentMethod: PaymentMethodSummary,\n};\n\ntype CustomerPaymentMethodSetupIntent = {\n clientSecret: string,\n stripeAccountId: string,\n};\n\ntype CustomerLike = {\n id: string,\n useBilling: () => CustomerBilling,\n useProducts: () => Array<{\n id: string | null,\n quantity: number,\n displayName: string,\n customerType: \"user\" | \"team\" | \"custom\",\n type: \"one_time\" | \"subscription\",\n switchOptions?: Array<{\n productId: string,\n displayName: string,\n prices: Record<string, { interval?: [number, \"day\" | \"week\" | \"month\" | \"year\"] }>,\n }>,\n subscription: null | {\n currentPeriodEnd: Date | null,\n cancelAtPeriodEnd: boolean,\n isCancelable: boolean,\n },\n }>,\n createPaymentMethodSetupIntent: () => Promise<CustomerPaymentMethodSetupIntent>,\n setDefaultPaymentMethodFromSetupIntent: (setupIntentId: string) => Promise<PaymentMethodSummary>,\n switchSubscription: (options: { fromProductId: string, toProductId: string, priceId?: string, quantity?: number }) => Promise<void>,\n};\n\nfunction SetDefaultPaymentMethodForm(props: {\n clientSecret: string,\n onSetupIntentSucceeded: (setupIntentId: string) => Promise<void>,\n}) {\n const stripe = useStripe();\n const elements = useElements();\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const darkMode = \"color-scheme\" in document.documentElement.style && document.documentElement.style[\"color-scheme\"] === \"dark\";\n\n return (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Typography className=\"font-medium\">Card details</Typography>\n <div className=\"rounded-md border border-input p-3\">\n <CardElement options={{ hidePostalCode: true, style: { base: { color: darkMode ? \"white\" : \"black\" } } }} />\n </div>\n </div>\n {errorMessage && (\n <Typography variant=\"secondary\" type=\"footnote\">\n {errorMessage}\n </Typography>\n )}\n <Button\n onClick={async () => {\n if (!stripe || !elements) {\n setErrorMessage(\"Stripe is still loading. Please try again.\");\n return;\n }\n const card = elements.getElement(CardElement);\n if (!card) {\n setErrorMessage(\"Card element not found.\");\n return;\n }\n\n const result = await stripe.confirmCardSetup(props.clientSecret, {\n payment_method: { card },\n });\n if (result.error) {\n setErrorMessage(result.error.message ?? \"Failed to save payment method.\");\n return;\n }\n if (!result.setupIntent.id) {\n setErrorMessage(\"No setup intent returned from Stripe.\");\n return;\n }\n await props.onSetupIntentSucceeded(result.setupIntent.id);\n }}\n >\n Save payment method\n </Button>\n </div>\n );\n}\n\nexport function PaymentsPanel(props: {\n title?: string,\n customer?: CustomerLike,\n customerType?: \"user\" | \"team\",\n mockMode?: boolean,\n}) {\n if (props.mockMode) {\n return <MockPaymentsPanel title={props.title} />;\n }\n if (!props.customer) {\n return null;\n }\n return <RealPaymentsPanel title={props.title} customer={props.customer} customerType={props.customerType ?? \"user\"} />;\n}\n\nfunction MockPaymentsPanel(props: { title?: string }) {\n const { t } = useTranslation();\n const defaultPaymentMethod: PaymentMethodSummary = {\n id: \"pm_mock\",\n brand: \"visa\",\n last4: \"4242\",\n exp_month: 12,\n exp_year: 2030,\n };\n\n return (\n <div className=\"space-y-4\">\n {props.title && <Typography className=\"font-medium\">{props.title}</Typography>}\n <Section\n title={t(\"Payment method\")}\n description={t(\"Manage the default payment method used for subscriptions and invoices.\")}\n >\n <Typography>{formatPaymentMethod(defaultPaymentMethod)}</Typography>\n <Button disabled>\n {t(\"Update payment method\")}\n </Button>\n </Section>\n\n <Section\n title={t(\"Active plans\")}\n description={t(\"View your active plans and purchases.\")}\n >\n <div className=\"space-y-3\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{t(\"Pro\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"Renews on\")} Jan 1, 2030</Typography>\n </div>\n <Button disabled variant=\"secondary\" color=\"neutral\">\n {t(\"Cancel subscription\")}\n </Button>\n </div>\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{t(\"Credits pack\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"One-time purchase\")}</Typography>\n </div>\n </div>\n </div>\n </Section>\n </div>\n );\n}\n\nfunction RealPaymentsPanel(props: { title?: string, customer: CustomerLike, customerType: \"user\" | \"team\" }) {\n const { t } = useTranslation();\n const stackApp = useStackApp();\n const billing = props.customer.useBilling();\n const defaultPaymentMethod = billing.defaultPaymentMethod;\n const products = props.customer.useProducts();\n const productsForCustomerType = products.filter(product => product.customerType === props.customerType);\n\n const [paymentDialogOpen, setPaymentDialogOpen] = useState(false);\n const [setupIntentClientSecret, setSetupIntentClientSecret] = useState<string | null>(null);\n const [setupIntentStripeAccountId, setSetupIntentStripeAccountId] = useState<string | null>(null);\n const [cancelProductId, setCancelProductId] = useState<string | null>(null);\n const [switchFromProductId, setSwitchFromProductId] = useState<string | null>(null);\n const [switchToProductId, setSwitchToProductId] = useState<string | null>(null);\n\n const stripePromise = useMemo(() => {\n if (!setupIntentStripeAccountId) return null;\n const publishableKey = process.env.NEXT_PUBLIC_STACK_STRIPE_PUBLISHABLE_KEY;\n if (!publishableKey) return null;\n return loadStripe(publishableKey, { stripeAccount: setupIntentStripeAccountId });\n }, [setupIntentStripeAccountId]);\n\n const handleAsyncError = (error: unknown) => {\n if (error instanceof KnownErrors.DefaultPaymentMethodRequired) {\n toast({\n title: t(\"No default payment method\"),\n description: t(\"Add a payment method before switching plans.\"),\n variant: \"destructive\",\n });\n return;\n }\n alert(`An unhandled error occurred. Please ${process.env.NODE_ENV === \"development\" ? \"check the browser console for the full error.\" : \"report this to the developer.\"}\\n\\n${error}`);\n };\n\n const openPaymentDialog = () => {\n runAsynchronously(async () => {\n setPaymentDialogOpen(true);\n const res = await props.customer.createPaymentMethodSetupIntent();\n setSetupIntentClientSecret(res.clientSecret);\n setSetupIntentStripeAccountId(res.stripeAccountId);\n }, { onError: handleAsyncError });\n };\n\n const closePaymentDialog = () => {\n setPaymentDialogOpen(false);\n setSetupIntentClientSecret(null);\n setSetupIntentStripeAccountId(null);\n };\n\n const openSwitchDialog = (productId: string, firstOptionId: string | null) => {\n setSwitchFromProductId(productId);\n setSwitchToProductId(firstOptionId);\n };\n\n const closeSwitchDialog = () => {\n setSwitchFromProductId(null);\n setSwitchToProductId(null);\n };\n\n const switchSourceProduct = switchFromProductId\n ? productsForCustomerType.find((product) => product.id === switchFromProductId) ?? null\n : null;\n const switchOptions = switchSourceProduct?.switchOptions ?? [];\n const selectedSwitchOption = switchOptions.find((option) => option.productId === switchToProductId) ?? null;\n const selectedPriceId = selectedSwitchOption ? (Object.keys(selectedSwitchOption.prices)[0] ?? null) : null;\n\n return (\n <div className=\"space-y-4\">\n {props.title && <Typography className=\"font-medium\">{props.title}</Typography>}\n\n {defaultPaymentMethod && (\n <Section\n title={t(\"Payment method\")}\n description={t(\"Manage the default payment method used for subscriptions and invoices.\")}\n >\n <Typography>{formatPaymentMethod(defaultPaymentMethod)}</Typography>\n\n <Button onClick={openPaymentDialog}>\n {t(\"Update payment method\")}\n </Button>\n\n <ActionDialog\n open={paymentDialogOpen}\n onOpenChange={(open) => {\n if (!open) {\n closePaymentDialog();\n } else {\n setPaymentDialogOpen(true);\n }\n }}\n title={t(\"Update payment method\")}\n >\n {!setupIntentClientSecret || !setupIntentStripeAccountId || !stripePromise ? (\n <Skeleton className=\"h-10 w-full\" />\n ) : (\n <Elements\n stripe={stripePromise}\n options={{\n clientSecret: setupIntentClientSecret,\n }}\n >\n <SetDefaultPaymentMethodForm\n clientSecret={setupIntentClientSecret}\n onSetupIntentSucceeded={async (setupIntentId) => {\n await props.customer.setDefaultPaymentMethodFromSetupIntent(setupIntentId);\n closePaymentDialog();\n }}\n />\n </Elements>\n )}\n </ActionDialog>\n </Section>\n )}\n\n {productsForCustomerType.length > 0 && (\n <Section\n title={t(\"Active plans\")}\n description={t(\"View your active plans and purchases.\")}\n >\n <div className=\"space-y-3\">\n {productsForCustomerType.map((product, index) => {\n const quantitySuffix = product.quantity !== 1 ? ` ×${product.quantity}` : \"\";\n const isSubscription = product.type === \"subscription\";\n const isCancelable = isSubscription && !!product.id && !!product.subscription?.isCancelable;\n const canSwitchPlans = isSubscription && defaultPaymentMethod && !!product.id && (product.switchOptions?.length ?? 0) > 0;\n const renewsAt = isSubscription ? (product.subscription?.currentPeriodEnd ?? null) : null;\n\n const subtitle =\n product.type === \"one_time\"\n ? t(\"One-time purchase\")\n : renewsAt\n ? `${t(\"Renews on\")} ${new Intl.DateTimeFormat(undefined, { year: \"numeric\", month: \"short\", day: \"numeric\" }).format(renewsAt)}`\n : t(\"Subscription\");\n\n return (\n <div key={product.id ?? `${product.displayName}-${index}`} className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{product.displayName}{quantitySuffix}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{subtitle}</Typography>\n </div>\n\n <div className=\"flex flex-col items-end gap-2\">\n {canSwitchPlans && (\n <Button\n variant=\"secondary\"\n color=\"neutral\"\n onClick={() => openSwitchDialog(product.id!, product.switchOptions?.[0]?.productId ?? null)}\n >\n {t(\"Change plan\")}\n </Button>\n )}\n {isCancelable && (\n <Button\n variant=\"secondary\"\n color=\"neutral\"\n onClick={() => setCancelProductId(product.id)}\n >\n {t(\"Cancel subscription\")}\n </Button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n <ActionDialog\n open={cancelProductId !== null}\n onOpenChange={(open) => {\n if (!open) setCancelProductId(null);\n }}\n title={t(\"Cancel subscription\")}\n description={t(\"Canceling will stop future renewals for this subscription.\")}\n danger\n cancelButton\n okButton={{\n label: t(\"Cancel subscription\"),\n onClick: async () => {\n const productId = cancelProductId;\n if (!productId) return;\n if (props.customerType === \"team\") {\n await stackApp.cancelSubscription({ teamId: props.customer.id, productId });\n } else {\n await stackApp.cancelSubscription({ productId });\n }\n setCancelProductId(null);\n },\n }}\n />\n\n <ActionDialog\n open={switchFromProductId !== null}\n onOpenChange={(open) => {\n if (!open) closeSwitchDialog();\n }}\n title={t(\"Change plan\")}\n description={t(\"Select a new plan from the same product line.\")}\n cancelButton\n okButton={{\n label: t(\"Switch plan\"),\n onClick: async () => {\n const fromProductId = switchFromProductId;\n const toProductId = switchToProductId;\n if (!fromProductId || !toProductId) return;\n if (!selectedPriceId) return;\n const result = await Result.fromThrowingAsync(() => props.customer.switchSubscription({\n fromProductId,\n toProductId,\n priceId: selectedPriceId,\n }));\n if (result.status === \"error\") {\n handleAsyncError(result.error);\n return \"prevent-close\";\n }\n closeSwitchDialog();\n },\n props: {\n disabled: !switchFromProductId || !switchToProductId || !selectedPriceId,\n },\n }}\n >\n <div className=\"space-y-2\">\n {switchOptions.length === 0 ? (\n <Typography variant=\"secondary\" type=\"footnote\">\n {t(\"No other plans available for this subscription.\")}\n </Typography>\n ) : (\n <>\n <Typography type=\"footnote\">{t(\"Choose a plan\")}</Typography>\n <Select\n value={switchToProductId ?? undefined}\n onValueChange={(value) => setSwitchToProductId(value || null)}\n >\n <SelectTrigger className=\"w-full\">\n <SelectValue placeholder={t(\"Choose a plan\")} />\n </SelectTrigger>\n <SelectContent>\n {switchOptions.map((option: NonNullable<typeof switchOptions>[number]) => (\n <SelectItem key={option.productId} value={option.productId}>\n {option.displayName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </>\n )}\n </div>\n </ActionDialog>\n </Section>\n )\n }\n </div >\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components-page/account-settings/payments/payments-panel.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { KnownErrors } from \"@stackframe/stack-shared\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { ActionDialog, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, Typography } from \"@stackframe/stack-ui\";\nimport { CardElement, Elements, useElements, useStripe } from \"@stripe/react-stripe-js\";\nimport { loadStripe } from \"@stripe/stripe-js\";\nimport { useMemo, useState } from \"react\";\nimport { useStackApp } from \"../../..\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\nimport { Result } from \"@stackframe/stack-shared/dist/utils/results\";\nimport type { CustomerInvoiceStatus, CustomerInvoicesList, CustomerInvoicesListOptions } from \"../../../lib/stack-app/customers\";\n\ntype PaymentMethodSummary = {\n id: string,\n brand: string | null,\n last4: string | null,\n exp_month: number | null,\n exp_year: number | null,\n} | null;\n\nfunction formatPaymentMethod(pm: NonNullable<PaymentMethodSummary>) {\n const details = [\n pm.brand ? pm.brand.toUpperCase() : null,\n pm.last4 ? `•••• ${pm.last4}` : null,\n pm.exp_month && pm.exp_year ? `exp ${pm.exp_month}/${pm.exp_year}` : null,\n ].filter(Boolean);\n return details.join(\" · \");\n}\n\nconst formatInvoiceStatus = (status: CustomerInvoiceStatus, t: (value: string) => string) => {\n if (!status) {\n return t(\"Unknown\");\n }\n switch (status) {\n case \"draft\": {\n return t(\"Draft\");\n }\n case \"open\": {\n return t(\"Open\");\n }\n case \"paid\": {\n return t(\"Paid\");\n }\n case \"uncollectible\": {\n return t(\"Uncollectible\");\n }\n case \"void\": {\n return t(\"Void\");\n }\n default: {\n return t(\"Unknown\");\n }\n }\n};\n\nconst formatInvoiceAmount = (amountTotal: number | null | undefined, t: (value: string) => string) => {\n if (typeof amountTotal !== \"number\" || Number.isNaN(amountTotal)) {\n return t(\"Unknown\");\n }\n const normalized = amountTotal / 100;\n const formatted = new Intl.NumberFormat(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(normalized);\n return `$${formatted}`;\n};\n\nconst formatInvoiceDate = (date: Date | null | undefined, t: (value: string) => string) => {\n if (!date || Number.isNaN(date.getTime())) {\n return t(\"Unknown\");\n }\n return new Intl.DateTimeFormat(undefined, { year: \"numeric\", month: \"short\", day: \"numeric\" }).format(date);\n};\n\ntype CustomerBilling = {\n hasCustomer: boolean,\n defaultPaymentMethod: PaymentMethodSummary,\n};\n\ntype CustomerPaymentMethodSetupIntent = {\n clientSecret: string,\n stripeAccountId: string,\n};\n\ntype CustomerLike = {\n id: string,\n useBilling: () => CustomerBilling,\n useProducts: () => Array<{\n id: string | null,\n quantity: number,\n displayName: string,\n customerType: \"user\" | \"team\" | \"custom\",\n type: \"one_time\" | \"subscription\",\n switchOptions?: Array<{\n productId: string,\n displayName: string,\n prices: Record<string, { interval?: [number, \"day\" | \"week\" | \"month\" | \"year\"] }>,\n }>,\n subscription: null | {\n currentPeriodEnd: Date | null,\n cancelAtPeriodEnd: boolean,\n isCancelable: boolean,\n },\n }>,\n useInvoices: (options?: CustomerInvoicesListOptions) => CustomerInvoicesList,\n createPaymentMethodSetupIntent: () => Promise<CustomerPaymentMethodSetupIntent>,\n setDefaultPaymentMethodFromSetupIntent: (setupIntentId: string) => Promise<PaymentMethodSummary>,\n switchSubscription: (options: { fromProductId: string, toProductId: string, priceId?: string, quantity?: number }) => Promise<void>,\n};\n\nfunction SetDefaultPaymentMethodForm(props: {\n clientSecret: string,\n onSetupIntentSucceeded: (setupIntentId: string) => Promise<void>,\n}) {\n const stripe = useStripe();\n const elements = useElements();\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const darkMode = \"color-scheme\" in document.documentElement.style && document.documentElement.style[\"color-scheme\"] === \"dark\";\n\n return (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Typography className=\"font-medium\">Card details</Typography>\n <div className=\"rounded-md border border-input p-3\">\n <CardElement options={{ hidePostalCode: true, style: { base: { color: darkMode ? \"white\" : \"black\" } } }} />\n </div>\n </div>\n {errorMessage && (\n <Typography variant=\"secondary\" type=\"footnote\">\n {errorMessage}\n </Typography>\n )}\n <Button\n onClick={async () => {\n if (!stripe || !elements) {\n setErrorMessage(\"Stripe is still loading. Please try again.\");\n return;\n }\n const card = elements.getElement(CardElement);\n if (!card) {\n setErrorMessage(\"Card element not found.\");\n return;\n }\n\n const result = await stripe.confirmCardSetup(props.clientSecret, {\n payment_method: { card },\n });\n if (result.error) {\n setErrorMessage(result.error.message ?? \"Failed to save payment method.\");\n return;\n }\n if (!result.setupIntent.id) {\n setErrorMessage(\"No setup intent returned from Stripe.\");\n return;\n }\n await props.onSetupIntentSucceeded(result.setupIntent.id);\n }}\n >\n Save payment method\n </Button>\n </div>\n );\n}\n\nexport function PaymentsPanel(props: {\n title?: string,\n customer?: CustomerLike,\n customerType?: \"user\" | \"team\",\n mockMode?: boolean,\n}) {\n if (props.mockMode) {\n return <MockPaymentsPanel title={props.title} />;\n }\n if (!props.customer) {\n return null;\n }\n return <RealPaymentsPanel title={props.title} customer={props.customer} customerType={props.customerType ?? \"user\"} />;\n}\n\nfunction MockPaymentsPanel(props: { title?: string }) {\n const { t } = useTranslation();\n const defaultPaymentMethod: PaymentMethodSummary = {\n id: \"pm_mock\",\n brand: \"visa\",\n last4: \"4242\",\n exp_month: 12,\n exp_year: 2030,\n };\n\n return (\n <div className=\"space-y-4\">\n {props.title && <Typography className=\"font-medium\">{props.title}</Typography>}\n <Section\n title={t(\"Payment method\")}\n description={t(\"Manage the default payment method used for subscriptions and invoices.\")}\n >\n <Typography>{formatPaymentMethod(defaultPaymentMethod)}</Typography>\n <Button disabled>\n {t(\"Update payment method\")}\n </Button>\n </Section>\n\n <Section\n title={t(\"Active plans\")}\n description={t(\"View your active plans and purchases.\")}\n >\n <div className=\"space-y-3\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{t(\"Pro\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"Renews on\")} Jan 1, 2030</Typography>\n </div>\n <Button disabled variant=\"secondary\" color=\"neutral\">\n {t(\"Cancel subscription\")}\n </Button>\n </div>\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{t(\"Credits pack\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"One-time purchase\")}</Typography>\n </div>\n </div>\n </div>\n </Section>\n </div>\n );\n}\n\nfunction RealPaymentsPanel(props: { title?: string, customer: CustomerLike, customerType: \"user\" | \"team\" }) {\n const { t } = useTranslation();\n const stackApp = useStackApp();\n const billing = props.customer.useBilling();\n const defaultPaymentMethod = billing.defaultPaymentMethod;\n const products = props.customer.useProducts();\n const invoices = props.customer.useInvoices({ limit: 10 });\n const productsForCustomerType = products.filter(product => product.customerType === props.customerType);\n\n const [paymentDialogOpen, setPaymentDialogOpen] = useState(false);\n const [setupIntentClientSecret, setSetupIntentClientSecret] = useState<string | null>(null);\n const [setupIntentStripeAccountId, setSetupIntentStripeAccountId] = useState<string | null>(null);\n const [cancelProductId, setCancelProductId] = useState<string | null>(null);\n const [switchFromProductId, setSwitchFromProductId] = useState<string | null>(null);\n const [switchToProductId, setSwitchToProductId] = useState<string | null>(null);\n\n const stripePromise = useMemo(() => {\n if (!setupIntentStripeAccountId) return null;\n const publishableKey = process.env.NEXT_PUBLIC_STACK_STRIPE_PUBLISHABLE_KEY;\n if (!publishableKey) return null;\n return loadStripe(publishableKey, { stripeAccount: setupIntentStripeAccountId });\n }, [setupIntentStripeAccountId]);\n\n const handleAsyncError = (error: unknown) => {\n if (error instanceof KnownErrors.DefaultPaymentMethodRequired) {\n toast({\n title: t(\"No default payment method\"),\n description: t(\"Add a payment method before switching plans.\"),\n variant: \"destructive\",\n });\n return;\n }\n alert(`An unhandled error occurred. Please ${process.env.NODE_ENV === \"development\" ? \"check the browser console for the full error.\" : \"report this to the developer.\"}\\n\\n${error}`);\n };\n\n const openPaymentDialog = () => {\n runAsynchronously(async () => {\n setPaymentDialogOpen(true);\n const res = await props.customer.createPaymentMethodSetupIntent();\n setSetupIntentClientSecret(res.clientSecret);\n setSetupIntentStripeAccountId(res.stripeAccountId);\n }, { onError: handleAsyncError });\n };\n\n const closePaymentDialog = () => {\n setPaymentDialogOpen(false);\n setSetupIntentClientSecret(null);\n setSetupIntentStripeAccountId(null);\n };\n\n const openSwitchDialog = (productId: string, firstOptionId: string | null) => {\n setSwitchFromProductId(productId);\n setSwitchToProductId(firstOptionId);\n };\n\n const closeSwitchDialog = () => {\n setSwitchFromProductId(null);\n setSwitchToProductId(null);\n };\n\n const switchSourceProduct = switchFromProductId\n ? productsForCustomerType.find((product) => product.id === switchFromProductId) ?? null\n : null;\n const switchOptions = switchSourceProduct?.switchOptions ?? [];\n const selectedSwitchOption = switchOptions.find((option) => option.productId === switchToProductId) ?? null;\n const selectedPriceId = selectedSwitchOption ? (Object.keys(selectedSwitchOption.prices)[0] ?? null) : null;\n\n return (\n <div className=\"space-y-4\">\n {props.title && <Typography className=\"font-medium\">{props.title}</Typography>}\n\n {defaultPaymentMethod && (\n <Section\n title={t(\"Payment method\")}\n description={t(\"Manage the default payment method used for subscriptions and invoices.\")}\n >\n <Typography>{formatPaymentMethod(defaultPaymentMethod)}</Typography>\n\n <Button onClick={openPaymentDialog}>\n {t(\"Update payment method\")}\n </Button>\n\n <ActionDialog\n open={paymentDialogOpen}\n onOpenChange={(open) => {\n if (!open) {\n closePaymentDialog();\n } else {\n setPaymentDialogOpen(true);\n }\n }}\n title={t(\"Update payment method\")}\n >\n {!setupIntentClientSecret || !setupIntentStripeAccountId || !stripePromise ? (\n <Skeleton className=\"h-10 w-full\" />\n ) : (\n <Elements\n stripe={stripePromise}\n options={{\n clientSecret: setupIntentClientSecret,\n }}\n >\n <SetDefaultPaymentMethodForm\n clientSecret={setupIntentClientSecret}\n onSetupIntentSucceeded={async (setupIntentId) => {\n await props.customer.setDefaultPaymentMethodFromSetupIntent(setupIntentId);\n closePaymentDialog();\n }}\n />\n </Elements>\n )}\n </ActionDialog>\n </Section>\n )}\n\n {productsForCustomerType.length > 0 && (\n <Section\n title={t(\"Active plans\")}\n description={t(\"View your active plans and purchases.\")}\n >\n <div className=\"space-y-3\">\n {productsForCustomerType.map((product, index) => {\n const quantitySuffix = product.quantity !== 1 ? ` ×${product.quantity}` : \"\";\n const isSubscription = product.type === \"subscription\";\n const isCancelable = isSubscription && !!product.id && !!product.subscription?.isCancelable;\n const canSwitchPlans = isSubscription && defaultPaymentMethod && !!product.id && (product.switchOptions?.length ?? 0) > 0;\n const renewsAt = isSubscription ? (product.subscription?.currentPeriodEnd ?? null) : null;\n\n const subtitle =\n product.type === \"one_time\"\n ? t(\"One-time purchase\")\n : renewsAt\n ? `${t(\"Renews on\")} ${new Intl.DateTimeFormat(undefined, { year: \"numeric\", month: \"short\", day: \"numeric\" }).format(renewsAt)}`\n : t(\"Subscription\");\n\n return (\n <div key={product.id ?? `${product.displayName}-${index}`} className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{product.displayName}{quantitySuffix}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{subtitle}</Typography>\n </div>\n\n <div className=\"flex flex-col items-end gap-2\">\n {canSwitchPlans && (\n <Button\n variant=\"secondary\"\n color=\"neutral\"\n onClick={() => openSwitchDialog(product.id!, product.switchOptions?.[0]?.productId ?? null)}\n >\n {t(\"Change plan\")}\n </Button>\n )}\n {isCancelable && (\n <Button\n variant=\"secondary\"\n color=\"neutral\"\n onClick={() => setCancelProductId(product.id)}\n >\n {t(\"Cancel subscription\")}\n </Button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n <ActionDialog\n open={cancelProductId !== null}\n onOpenChange={(open) => {\n if (!open) setCancelProductId(null);\n }}\n title={t(\"Cancel subscription\")}\n description={t(\"Canceling will stop future renewals for this subscription.\")}\n danger\n cancelButton\n okButton={{\n label: t(\"Cancel subscription\"),\n onClick: async () => {\n const productId = cancelProductId;\n if (!productId) return;\n if (props.customerType === \"team\") {\n await stackApp.cancelSubscription({ teamId: props.customer.id, productId });\n } else {\n await stackApp.cancelSubscription({ productId });\n }\n setCancelProductId(null);\n },\n }}\n />\n\n <ActionDialog\n open={switchFromProductId !== null}\n onOpenChange={(open) => {\n if (!open) closeSwitchDialog();\n }}\n title={t(\"Change plan\")}\n description={t(\"Select a new plan from the same product line.\")}\n cancelButton\n okButton={{\n label: t(\"Switch plan\"),\n onClick: async () => {\n const fromProductId = switchFromProductId;\n const toProductId = switchToProductId;\n if (!fromProductId || !toProductId) return;\n if (!selectedPriceId) return;\n const result = await Result.fromThrowingAsync(() => props.customer.switchSubscription({\n fromProductId,\n toProductId,\n priceId: selectedPriceId,\n }));\n if (result.status === \"error\") {\n handleAsyncError(result.error);\n return \"prevent-close\";\n }\n closeSwitchDialog();\n },\n props: {\n disabled: !switchFromProductId || !switchToProductId || !selectedPriceId,\n },\n }}\n >\n <div className=\"space-y-2\">\n {switchOptions.length === 0 ? (\n <Typography variant=\"secondary\" type=\"footnote\">\n {t(\"No other plans available for this subscription.\")}\n </Typography>\n ) : (\n <>\n <Typography type=\"footnote\">{t(\"Choose a plan\")}</Typography>\n <Select\n value={switchToProductId ?? undefined}\n onValueChange={(value) => setSwitchToProductId(value || null)}\n >\n <SelectTrigger className=\"w-full\">\n <SelectValue placeholder={t(\"Choose a plan\")} />\n </SelectTrigger>\n <SelectContent>\n {switchOptions.map((option: NonNullable<typeof switchOptions>[number]) => (\n <SelectItem key={option.productId} value={option.productId}>\n {option.displayName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </>\n )}\n </div>\n </ActionDialog>\n </Section>\n )\n }\n {invoices.length > 0 && (\n <>\n <Separator />\n <div className=\"space-y-2\">\n <div className=\"space-y-1\">\n <Typography className=\"font-medium\">{t(\"Invoices\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"Review past invoices and receipts.\")}</Typography>\n </div>\n <div className=\"border rounded-md\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-[140px]\">{t(\"Date\")}</TableHead>\n <TableHead className=\"w-[120px]\">{t(\"Status\")}</TableHead>\n <TableHead className=\"w-[120px]\">{t(\"Amount\")}</TableHead>\n <TableHead className=\"w-[120px] text-right\">{t(\"Invoice\")}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {invoices.map((invoice, index) => {\n const createdAtTime = invoice.createdAt.getTime();\n const invoiceKey = Number.isNaN(createdAtTime) ? `invoice-${index}` : `invoice-${createdAtTime}-${index}`;\n return (\n <TableRow key={invoiceKey}>\n <TableCell>\n <Typography>{formatInvoiceDate(invoice.createdAt, t)}</Typography>\n </TableCell>\n <TableCell>\n <Typography>{formatInvoiceStatus(invoice.status, t)}</Typography>\n </TableCell>\n <TableCell>\n <Typography>{formatInvoiceAmount(invoice.amountTotal, t)}</Typography>\n </TableCell>\n <TableCell align=\"right\">\n {invoice.hostedInvoiceUrl ? (\n <Button asChild variant=\"secondary\" color=\"neutral\" size=\"sm\">\n <a href={invoice.hostedInvoiceUrl} target=\"_blank\" rel=\"noreferrer\">\n {t(\"View\")}\n </a>\n </Button>\n ) : (\n <Typography variant=\"secondary\" type=\"footnote\">\n {t(\"Unavailable\")}\n </Typography>\n )}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n </div>\n </>\n )}\n </div >\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
// src/components-page/account-settings/payments/payments-panel.tsx
|
|
5
5
|
import { KnownErrors } from "@stackframe/stack-shared";
|
|
6
6
|
import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
|
|
7
|
-
import {
|
|
8
|
-
import { ActionDialog, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Skeleton, toast, Typography } from "@stackframe/stack-ui";
|
|
7
|
+
import { ActionDialog, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, Typography } from "@stackframe/stack-ui";
|
|
9
8
|
import { CardElement, Elements, useElements, useStripe } from "@stripe/react-stripe-js";
|
|
10
9
|
import { loadStripe } from "@stripe/stripe-js";
|
|
11
10
|
import { useMemo, useState } from "react";
|
|
12
11
|
import { useStackApp } from "../../../index.js";
|
|
13
12
|
import { useTranslation } from "../../../lib/translations.js";
|
|
14
13
|
import { Section } from "../section.js";
|
|
14
|
+
import { Result } from "@stackframe/stack-shared/dist/utils/results";
|
|
15
15
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
16
16
|
function formatPaymentMethod(pm) {
|
|
17
17
|
const details = [
|
|
@@ -21,6 +21,45 @@ function formatPaymentMethod(pm) {
|
|
|
21
21
|
].filter(Boolean);
|
|
22
22
|
return details.join(" \xB7 ");
|
|
23
23
|
}
|
|
24
|
+
var formatInvoiceStatus = (status, t) => {
|
|
25
|
+
if (!status) {
|
|
26
|
+
return t("Unknown");
|
|
27
|
+
}
|
|
28
|
+
switch (status) {
|
|
29
|
+
case "draft": {
|
|
30
|
+
return t("Draft");
|
|
31
|
+
}
|
|
32
|
+
case "open": {
|
|
33
|
+
return t("Open");
|
|
34
|
+
}
|
|
35
|
+
case "paid": {
|
|
36
|
+
return t("Paid");
|
|
37
|
+
}
|
|
38
|
+
case "uncollectible": {
|
|
39
|
+
return t("Uncollectible");
|
|
40
|
+
}
|
|
41
|
+
case "void": {
|
|
42
|
+
return t("Void");
|
|
43
|
+
}
|
|
44
|
+
default: {
|
|
45
|
+
return t("Unknown");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var formatInvoiceAmount = (amountTotal, t) => {
|
|
50
|
+
if (typeof amountTotal !== "number" || Number.isNaN(amountTotal)) {
|
|
51
|
+
return t("Unknown");
|
|
52
|
+
}
|
|
53
|
+
const normalized = amountTotal / 100;
|
|
54
|
+
const formatted = new Intl.NumberFormat(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(normalized);
|
|
55
|
+
return `$${formatted}`;
|
|
56
|
+
};
|
|
57
|
+
var formatInvoiceDate = (date, t) => {
|
|
58
|
+
if (!date || Number.isNaN(date.getTime())) {
|
|
59
|
+
return t("Unknown");
|
|
60
|
+
}
|
|
61
|
+
return new Intl.DateTimeFormat(void 0, { year: "numeric", month: "short", day: "numeric" }).format(date);
|
|
62
|
+
};
|
|
24
63
|
function SetDefaultPaymentMethodForm(props) {
|
|
25
64
|
const stripe = useStripe();
|
|
26
65
|
const elements = useElements();
|
|
@@ -125,6 +164,7 @@ function RealPaymentsPanel(props) {
|
|
|
125
164
|
const billing = props.customer.useBilling();
|
|
126
165
|
const defaultPaymentMethod = billing.defaultPaymentMethod;
|
|
127
166
|
const products = props.customer.useProducts();
|
|
167
|
+
const invoices = props.customer.useInvoices({ limit: 10 });
|
|
128
168
|
const productsForCustomerType = products.filter((product) => product.customerType === props.customerType);
|
|
129
169
|
const [paymentDialogOpen, setPaymentDialogOpen] = useState(false);
|
|
130
170
|
const [setupIntentClientSecret, setSetupIntentClientSecret] = useState(null);
|
|
@@ -341,7 +381,34 @@ ${error}`);
|
|
|
341
381
|
)
|
|
342
382
|
]
|
|
343
383
|
}
|
|
344
|
-
)
|
|
384
|
+
),
|
|
385
|
+
invoices.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
386
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
387
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
388
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
389
|
+
/* @__PURE__ */ jsx(Typography, { className: "font-medium", children: t("Invoices") }),
|
|
390
|
+
/* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "footnote", children: t("Review past invoices and receipts.") })
|
|
391
|
+
] }),
|
|
392
|
+
/* @__PURE__ */ jsx("div", { className: "border rounded-md", children: /* @__PURE__ */ jsxs(Table, { children: [
|
|
393
|
+
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
394
|
+
/* @__PURE__ */ jsx(TableHead, { className: "w-[140px]", children: t("Date") }),
|
|
395
|
+
/* @__PURE__ */ jsx(TableHead, { className: "w-[120px]", children: t("Status") }),
|
|
396
|
+
/* @__PURE__ */ jsx(TableHead, { className: "w-[120px]", children: t("Amount") }),
|
|
397
|
+
/* @__PURE__ */ jsx(TableHead, { className: "w-[120px] text-right", children: t("Invoice") })
|
|
398
|
+
] }) }),
|
|
399
|
+
/* @__PURE__ */ jsx(TableBody, { children: invoices.map((invoice, index) => {
|
|
400
|
+
const createdAtTime = invoice.createdAt.getTime();
|
|
401
|
+
const invoiceKey = Number.isNaN(createdAtTime) ? `invoice-${index}` : `invoice-${createdAtTime}-${index}`;
|
|
402
|
+
return /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
403
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Typography, { children: formatInvoiceDate(invoice.createdAt, t) }) }),
|
|
404
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Typography, { children: formatInvoiceStatus(invoice.status, t) }) }),
|
|
405
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Typography, { children: formatInvoiceAmount(invoice.amountTotal, t) }) }),
|
|
406
|
+
/* @__PURE__ */ jsx(TableCell, { align: "right", children: invoice.hostedInvoiceUrl ? /* @__PURE__ */ jsx(Button, { asChild: true, variant: "secondary", color: "neutral", size: "sm", children: /* @__PURE__ */ jsx("a", { href: invoice.hostedInvoiceUrl, target: "_blank", rel: "noreferrer", children: t("View") }) }) : /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "footnote", children: t("Unavailable") }) })
|
|
407
|
+
] }, invoiceKey);
|
|
408
|
+
}) })
|
|
409
|
+
] }) })
|
|
410
|
+
] })
|
|
411
|
+
] })
|
|
345
412
|
] });
|
|
346
413
|
}
|
|
347
414
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components-page/account-settings/payments/payments-panel.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { KnownErrors } from \"@stackframe/stack-shared\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Result } from \"@stackframe/stack-shared/dist/utils/results\";\nimport { ActionDialog, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Skeleton, toast, Typography } from \"@stackframe/stack-ui\";\nimport { CardElement, Elements, useElements, useStripe } from \"@stripe/react-stripe-js\";\nimport { loadStripe } from \"@stripe/stripe-js\";\nimport { useMemo, useState } from \"react\";\nimport { useStackApp } from \"../../..\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\ntype PaymentMethodSummary = {\n id: string,\n brand: string | null,\n last4: string | null,\n exp_month: number | null,\n exp_year: number | null,\n} | null;\n\nfunction formatPaymentMethod(pm: NonNullable<PaymentMethodSummary>) {\n const details = [\n pm.brand ? pm.brand.toUpperCase() : null,\n pm.last4 ? `•••• ${pm.last4}` : null,\n pm.exp_month && pm.exp_year ? `exp ${pm.exp_month}/${pm.exp_year}` : null,\n ].filter(Boolean);\n return details.join(\" · \");\n}\n\ntype CustomerBilling = {\n hasCustomer: boolean,\n defaultPaymentMethod: PaymentMethodSummary,\n};\n\ntype CustomerPaymentMethodSetupIntent = {\n clientSecret: string,\n stripeAccountId: string,\n};\n\ntype CustomerLike = {\n id: string,\n useBilling: () => CustomerBilling,\n useProducts: () => Array<{\n id: string | null,\n quantity: number,\n displayName: string,\n customerType: \"user\" | \"team\" | \"custom\",\n type: \"one_time\" | \"subscription\",\n switchOptions?: Array<{\n productId: string,\n displayName: string,\n prices: Record<string, { interval?: [number, \"day\" | \"week\" | \"month\" | \"year\"] }>,\n }>,\n subscription: null | {\n currentPeriodEnd: Date | null,\n cancelAtPeriodEnd: boolean,\n isCancelable: boolean,\n },\n }>,\n createPaymentMethodSetupIntent: () => Promise<CustomerPaymentMethodSetupIntent>,\n setDefaultPaymentMethodFromSetupIntent: (setupIntentId: string) => Promise<PaymentMethodSummary>,\n switchSubscription: (options: { fromProductId: string, toProductId: string, priceId?: string, quantity?: number }) => Promise<void>,\n};\n\nfunction SetDefaultPaymentMethodForm(props: {\n clientSecret: string,\n onSetupIntentSucceeded: (setupIntentId: string) => Promise<void>,\n}) {\n const stripe = useStripe();\n const elements = useElements();\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const darkMode = \"color-scheme\" in document.documentElement.style && document.documentElement.style[\"color-scheme\"] === \"dark\";\n\n return (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Typography className=\"font-medium\">Card details</Typography>\n <div className=\"rounded-md border border-input p-3\">\n <CardElement options={{ hidePostalCode: true, style: { base: { color: darkMode ? \"white\" : \"black\" } } }} />\n </div>\n </div>\n {errorMessage && (\n <Typography variant=\"secondary\" type=\"footnote\">\n {errorMessage}\n </Typography>\n )}\n <Button\n onClick={async () => {\n if (!stripe || !elements) {\n setErrorMessage(\"Stripe is still loading. Please try again.\");\n return;\n }\n const card = elements.getElement(CardElement);\n if (!card) {\n setErrorMessage(\"Card element not found.\");\n return;\n }\n\n const result = await stripe.confirmCardSetup(props.clientSecret, {\n payment_method: { card },\n });\n if (result.error) {\n setErrorMessage(result.error.message ?? \"Failed to save payment method.\");\n return;\n }\n if (!result.setupIntent.id) {\n setErrorMessage(\"No setup intent returned from Stripe.\");\n return;\n }\n await props.onSetupIntentSucceeded(result.setupIntent.id);\n }}\n >\n Save payment method\n </Button>\n </div>\n );\n}\n\nexport function PaymentsPanel(props: {\n title?: string,\n customer?: CustomerLike,\n customerType?: \"user\" | \"team\",\n mockMode?: boolean,\n}) {\n if (props.mockMode) {\n return <MockPaymentsPanel title={props.title} />;\n }\n if (!props.customer) {\n return null;\n }\n return <RealPaymentsPanel title={props.title} customer={props.customer} customerType={props.customerType ?? \"user\"} />;\n}\n\nfunction MockPaymentsPanel(props: { title?: string }) {\n const { t } = useTranslation();\n const defaultPaymentMethod: PaymentMethodSummary = {\n id: \"pm_mock\",\n brand: \"visa\",\n last4: \"4242\",\n exp_month: 12,\n exp_year: 2030,\n };\n\n return (\n <div className=\"space-y-4\">\n {props.title && <Typography className=\"font-medium\">{props.title}</Typography>}\n <Section\n title={t(\"Payment method\")}\n description={t(\"Manage the default payment method used for subscriptions and invoices.\")}\n >\n <Typography>{formatPaymentMethod(defaultPaymentMethod)}</Typography>\n <Button disabled>\n {t(\"Update payment method\")}\n </Button>\n </Section>\n\n <Section\n title={t(\"Active plans\")}\n description={t(\"View your active plans and purchases.\")}\n >\n <div className=\"space-y-3\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{t(\"Pro\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"Renews on\")} Jan 1, 2030</Typography>\n </div>\n <Button disabled variant=\"secondary\" color=\"neutral\">\n {t(\"Cancel subscription\")}\n </Button>\n </div>\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{t(\"Credits pack\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"One-time purchase\")}</Typography>\n </div>\n </div>\n </div>\n </Section>\n </div>\n );\n}\n\nfunction RealPaymentsPanel(props: { title?: string, customer: CustomerLike, customerType: \"user\" | \"team\" }) {\n const { t } = useTranslation();\n const stackApp = useStackApp();\n const billing = props.customer.useBilling();\n const defaultPaymentMethod = billing.defaultPaymentMethod;\n const products = props.customer.useProducts();\n const productsForCustomerType = products.filter(product => product.customerType === props.customerType);\n\n const [paymentDialogOpen, setPaymentDialogOpen] = useState(false);\n const [setupIntentClientSecret, setSetupIntentClientSecret] = useState<string | null>(null);\n const [setupIntentStripeAccountId, setSetupIntentStripeAccountId] = useState<string | null>(null);\n const [cancelProductId, setCancelProductId] = useState<string | null>(null);\n const [switchFromProductId, setSwitchFromProductId] = useState<string | null>(null);\n const [switchToProductId, setSwitchToProductId] = useState<string | null>(null);\n\n const stripePromise = useMemo(() => {\n if (!setupIntentStripeAccountId) return null;\n const publishableKey = process.env.NEXT_PUBLIC_STACK_STRIPE_PUBLISHABLE_KEY;\n if (!publishableKey) return null;\n return loadStripe(publishableKey, { stripeAccount: setupIntentStripeAccountId });\n }, [setupIntentStripeAccountId]);\n\n const handleAsyncError = (error: unknown) => {\n if (error instanceof KnownErrors.DefaultPaymentMethodRequired) {\n toast({\n title: t(\"No default payment method\"),\n description: t(\"Add a payment method before switching plans.\"),\n variant: \"destructive\",\n });\n return;\n }\n alert(`An unhandled error occurred. Please ${process.env.NODE_ENV === \"development\" ? \"check the browser console for the full error.\" : \"report this to the developer.\"}\\n\\n${error}`);\n };\n\n const openPaymentDialog = () => {\n runAsynchronously(async () => {\n setPaymentDialogOpen(true);\n const res = await props.customer.createPaymentMethodSetupIntent();\n setSetupIntentClientSecret(res.clientSecret);\n setSetupIntentStripeAccountId(res.stripeAccountId);\n }, { onError: handleAsyncError });\n };\n\n const closePaymentDialog = () => {\n setPaymentDialogOpen(false);\n setSetupIntentClientSecret(null);\n setSetupIntentStripeAccountId(null);\n };\n\n const openSwitchDialog = (productId: string, firstOptionId: string | null) => {\n setSwitchFromProductId(productId);\n setSwitchToProductId(firstOptionId);\n };\n\n const closeSwitchDialog = () => {\n setSwitchFromProductId(null);\n setSwitchToProductId(null);\n };\n\n const switchSourceProduct = switchFromProductId\n ? productsForCustomerType.find((product) => product.id === switchFromProductId) ?? null\n : null;\n const switchOptions = switchSourceProduct?.switchOptions ?? [];\n const selectedSwitchOption = switchOptions.find((option) => option.productId === switchToProductId) ?? null;\n const selectedPriceId = selectedSwitchOption ? (Object.keys(selectedSwitchOption.prices)[0] ?? null) : null;\n\n return (\n <div className=\"space-y-4\">\n {props.title && <Typography className=\"font-medium\">{props.title}</Typography>}\n\n {defaultPaymentMethod && (\n <Section\n title={t(\"Payment method\")}\n description={t(\"Manage the default payment method used for subscriptions and invoices.\")}\n >\n <Typography>{formatPaymentMethod(defaultPaymentMethod)}</Typography>\n\n <Button onClick={openPaymentDialog}>\n {t(\"Update payment method\")}\n </Button>\n\n <ActionDialog\n open={paymentDialogOpen}\n onOpenChange={(open) => {\n if (!open) {\n closePaymentDialog();\n } else {\n setPaymentDialogOpen(true);\n }\n }}\n title={t(\"Update payment method\")}\n >\n {!setupIntentClientSecret || !setupIntentStripeAccountId || !stripePromise ? (\n <Skeleton className=\"h-10 w-full\" />\n ) : (\n <Elements\n stripe={stripePromise}\n options={{\n clientSecret: setupIntentClientSecret,\n }}\n >\n <SetDefaultPaymentMethodForm\n clientSecret={setupIntentClientSecret}\n onSetupIntentSucceeded={async (setupIntentId) => {\n await props.customer.setDefaultPaymentMethodFromSetupIntent(setupIntentId);\n closePaymentDialog();\n }}\n />\n </Elements>\n )}\n </ActionDialog>\n </Section>\n )}\n\n {productsForCustomerType.length > 0 && (\n <Section\n title={t(\"Active plans\")}\n description={t(\"View your active plans and purchases.\")}\n >\n <div className=\"space-y-3\">\n {productsForCustomerType.map((product, index) => {\n const quantitySuffix = product.quantity !== 1 ? ` ×${product.quantity}` : \"\";\n const isSubscription = product.type === \"subscription\";\n const isCancelable = isSubscription && !!product.id && !!product.subscription?.isCancelable;\n const canSwitchPlans = isSubscription && defaultPaymentMethod && !!product.id && (product.switchOptions?.length ?? 0) > 0;\n const renewsAt = isSubscription ? (product.subscription?.currentPeriodEnd ?? null) : null;\n\n const subtitle =\n product.type === \"one_time\"\n ? t(\"One-time purchase\")\n : renewsAt\n ? `${t(\"Renews on\")} ${new Intl.DateTimeFormat(undefined, { year: \"numeric\", month: \"short\", day: \"numeric\" }).format(renewsAt)}`\n : t(\"Subscription\");\n\n return (\n <div key={product.id ?? `${product.displayName}-${index}`} className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{product.displayName}{quantitySuffix}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{subtitle}</Typography>\n </div>\n\n <div className=\"flex flex-col items-end gap-2\">\n {canSwitchPlans && (\n <Button\n variant=\"secondary\"\n color=\"neutral\"\n onClick={() => openSwitchDialog(product.id!, product.switchOptions?.[0]?.productId ?? null)}\n >\n {t(\"Change plan\")}\n </Button>\n )}\n {isCancelable && (\n <Button\n variant=\"secondary\"\n color=\"neutral\"\n onClick={() => setCancelProductId(product.id)}\n >\n {t(\"Cancel subscription\")}\n </Button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n <ActionDialog\n open={cancelProductId !== null}\n onOpenChange={(open) => {\n if (!open) setCancelProductId(null);\n }}\n title={t(\"Cancel subscription\")}\n description={t(\"Canceling will stop future renewals for this subscription.\")}\n danger\n cancelButton\n okButton={{\n label: t(\"Cancel subscription\"),\n onClick: async () => {\n const productId = cancelProductId;\n if (!productId) return;\n if (props.customerType === \"team\") {\n await stackApp.cancelSubscription({ teamId: props.customer.id, productId });\n } else {\n await stackApp.cancelSubscription({ productId });\n }\n setCancelProductId(null);\n },\n }}\n />\n\n <ActionDialog\n open={switchFromProductId !== null}\n onOpenChange={(open) => {\n if (!open) closeSwitchDialog();\n }}\n title={t(\"Change plan\")}\n description={t(\"Select a new plan from the same product line.\")}\n cancelButton\n okButton={{\n label: t(\"Switch plan\"),\n onClick: async () => {\n const fromProductId = switchFromProductId;\n const toProductId = switchToProductId;\n if (!fromProductId || !toProductId) return;\n if (!selectedPriceId) return;\n const result = await Result.fromThrowingAsync(() => props.customer.switchSubscription({\n fromProductId,\n toProductId,\n priceId: selectedPriceId,\n }));\n if (result.status === \"error\") {\n handleAsyncError(result.error);\n return \"prevent-close\";\n }\n closeSwitchDialog();\n },\n props: {\n disabled: !switchFromProductId || !switchToProductId || !selectedPriceId,\n },\n }}\n >\n <div className=\"space-y-2\">\n {switchOptions.length === 0 ? (\n <Typography variant=\"secondary\" type=\"footnote\">\n {t(\"No other plans available for this subscription.\")}\n </Typography>\n ) : (\n <>\n <Typography type=\"footnote\">{t(\"Choose a plan\")}</Typography>\n <Select\n value={switchToProductId ?? undefined}\n onValueChange={(value) => setSwitchToProductId(value || null)}\n >\n <SelectTrigger className=\"w-full\">\n <SelectValue placeholder={t(\"Choose a plan\")} />\n </SelectTrigger>\n <SelectContent>\n {switchOptions.map((option: NonNullable<typeof switchOptions>[number]) => (\n <SelectItem key={option.productId} value={option.productId}>\n {option.displayName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </>\n )}\n </div>\n </ActionDialog>\n </Section>\n )\n }\n </div >\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components-page/account-settings/payments/payments-panel.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { KnownErrors } from \"@stackframe/stack-shared\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { ActionDialog, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, Typography } from \"@stackframe/stack-ui\";\nimport { CardElement, Elements, useElements, useStripe } from \"@stripe/react-stripe-js\";\nimport { loadStripe } from \"@stripe/stripe-js\";\nimport { useMemo, useState } from \"react\";\nimport { useStackApp } from \"../../..\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\nimport { Result } from \"@stackframe/stack-shared/dist/utils/results\";\nimport type { CustomerInvoiceStatus, CustomerInvoicesList, CustomerInvoicesListOptions } from \"../../../lib/stack-app/customers\";\n\ntype PaymentMethodSummary = {\n id: string,\n brand: string | null,\n last4: string | null,\n exp_month: number | null,\n exp_year: number | null,\n} | null;\n\nfunction formatPaymentMethod(pm: NonNullable<PaymentMethodSummary>) {\n const details = [\n pm.brand ? pm.brand.toUpperCase() : null,\n pm.last4 ? `•••• ${pm.last4}` : null,\n pm.exp_month && pm.exp_year ? `exp ${pm.exp_month}/${pm.exp_year}` : null,\n ].filter(Boolean);\n return details.join(\" · \");\n}\n\nconst formatInvoiceStatus = (status: CustomerInvoiceStatus, t: (value: string) => string) => {\n if (!status) {\n return t(\"Unknown\");\n }\n switch (status) {\n case \"draft\": {\n return t(\"Draft\");\n }\n case \"open\": {\n return t(\"Open\");\n }\n case \"paid\": {\n return t(\"Paid\");\n }\n case \"uncollectible\": {\n return t(\"Uncollectible\");\n }\n case \"void\": {\n return t(\"Void\");\n }\n default: {\n return t(\"Unknown\");\n }\n }\n};\n\nconst formatInvoiceAmount = (amountTotal: number | null | undefined, t: (value: string) => string) => {\n if (typeof amountTotal !== \"number\" || Number.isNaN(amountTotal)) {\n return t(\"Unknown\");\n }\n const normalized = amountTotal / 100;\n const formatted = new Intl.NumberFormat(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(normalized);\n return `$${formatted}`;\n};\n\nconst formatInvoiceDate = (date: Date | null | undefined, t: (value: string) => string) => {\n if (!date || Number.isNaN(date.getTime())) {\n return t(\"Unknown\");\n }\n return new Intl.DateTimeFormat(undefined, { year: \"numeric\", month: \"short\", day: \"numeric\" }).format(date);\n};\n\ntype CustomerBilling = {\n hasCustomer: boolean,\n defaultPaymentMethod: PaymentMethodSummary,\n};\n\ntype CustomerPaymentMethodSetupIntent = {\n clientSecret: string,\n stripeAccountId: string,\n};\n\ntype CustomerLike = {\n id: string,\n useBilling: () => CustomerBilling,\n useProducts: () => Array<{\n id: string | null,\n quantity: number,\n displayName: string,\n customerType: \"user\" | \"team\" | \"custom\",\n type: \"one_time\" | \"subscription\",\n switchOptions?: Array<{\n productId: string,\n displayName: string,\n prices: Record<string, { interval?: [number, \"day\" | \"week\" | \"month\" | \"year\"] }>,\n }>,\n subscription: null | {\n currentPeriodEnd: Date | null,\n cancelAtPeriodEnd: boolean,\n isCancelable: boolean,\n },\n }>,\n useInvoices: (options?: CustomerInvoicesListOptions) => CustomerInvoicesList,\n createPaymentMethodSetupIntent: () => Promise<CustomerPaymentMethodSetupIntent>,\n setDefaultPaymentMethodFromSetupIntent: (setupIntentId: string) => Promise<PaymentMethodSummary>,\n switchSubscription: (options: { fromProductId: string, toProductId: string, priceId?: string, quantity?: number }) => Promise<void>,\n};\n\nfunction SetDefaultPaymentMethodForm(props: {\n clientSecret: string,\n onSetupIntentSucceeded: (setupIntentId: string) => Promise<void>,\n}) {\n const stripe = useStripe();\n const elements = useElements();\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const darkMode = \"color-scheme\" in document.documentElement.style && document.documentElement.style[\"color-scheme\"] === \"dark\";\n\n return (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Typography className=\"font-medium\">Card details</Typography>\n <div className=\"rounded-md border border-input p-3\">\n <CardElement options={{ hidePostalCode: true, style: { base: { color: darkMode ? \"white\" : \"black\" } } }} />\n </div>\n </div>\n {errorMessage && (\n <Typography variant=\"secondary\" type=\"footnote\">\n {errorMessage}\n </Typography>\n )}\n <Button\n onClick={async () => {\n if (!stripe || !elements) {\n setErrorMessage(\"Stripe is still loading. Please try again.\");\n return;\n }\n const card = elements.getElement(CardElement);\n if (!card) {\n setErrorMessage(\"Card element not found.\");\n return;\n }\n\n const result = await stripe.confirmCardSetup(props.clientSecret, {\n payment_method: { card },\n });\n if (result.error) {\n setErrorMessage(result.error.message ?? \"Failed to save payment method.\");\n return;\n }\n if (!result.setupIntent.id) {\n setErrorMessage(\"No setup intent returned from Stripe.\");\n return;\n }\n await props.onSetupIntentSucceeded(result.setupIntent.id);\n }}\n >\n Save payment method\n </Button>\n </div>\n );\n}\n\nexport function PaymentsPanel(props: {\n title?: string,\n customer?: CustomerLike,\n customerType?: \"user\" | \"team\",\n mockMode?: boolean,\n}) {\n if (props.mockMode) {\n return <MockPaymentsPanel title={props.title} />;\n }\n if (!props.customer) {\n return null;\n }\n return <RealPaymentsPanel title={props.title} customer={props.customer} customerType={props.customerType ?? \"user\"} />;\n}\n\nfunction MockPaymentsPanel(props: { title?: string }) {\n const { t } = useTranslation();\n const defaultPaymentMethod: PaymentMethodSummary = {\n id: \"pm_mock\",\n brand: \"visa\",\n last4: \"4242\",\n exp_month: 12,\n exp_year: 2030,\n };\n\n return (\n <div className=\"space-y-4\">\n {props.title && <Typography className=\"font-medium\">{props.title}</Typography>}\n <Section\n title={t(\"Payment method\")}\n description={t(\"Manage the default payment method used for subscriptions and invoices.\")}\n >\n <Typography>{formatPaymentMethod(defaultPaymentMethod)}</Typography>\n <Button disabled>\n {t(\"Update payment method\")}\n </Button>\n </Section>\n\n <Section\n title={t(\"Active plans\")}\n description={t(\"View your active plans and purchases.\")}\n >\n <div className=\"space-y-3\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{t(\"Pro\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"Renews on\")} Jan 1, 2030</Typography>\n </div>\n <Button disabled variant=\"secondary\" color=\"neutral\">\n {t(\"Cancel subscription\")}\n </Button>\n </div>\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{t(\"Credits pack\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"One-time purchase\")}</Typography>\n </div>\n </div>\n </div>\n </Section>\n </div>\n );\n}\n\nfunction RealPaymentsPanel(props: { title?: string, customer: CustomerLike, customerType: \"user\" | \"team\" }) {\n const { t } = useTranslation();\n const stackApp = useStackApp();\n const billing = props.customer.useBilling();\n const defaultPaymentMethod = billing.defaultPaymentMethod;\n const products = props.customer.useProducts();\n const invoices = props.customer.useInvoices({ limit: 10 });\n const productsForCustomerType = products.filter(product => product.customerType === props.customerType);\n\n const [paymentDialogOpen, setPaymentDialogOpen] = useState(false);\n const [setupIntentClientSecret, setSetupIntentClientSecret] = useState<string | null>(null);\n const [setupIntentStripeAccountId, setSetupIntentStripeAccountId] = useState<string | null>(null);\n const [cancelProductId, setCancelProductId] = useState<string | null>(null);\n const [switchFromProductId, setSwitchFromProductId] = useState<string | null>(null);\n const [switchToProductId, setSwitchToProductId] = useState<string | null>(null);\n\n const stripePromise = useMemo(() => {\n if (!setupIntentStripeAccountId) return null;\n const publishableKey = process.env.NEXT_PUBLIC_STACK_STRIPE_PUBLISHABLE_KEY;\n if (!publishableKey) return null;\n return loadStripe(publishableKey, { stripeAccount: setupIntentStripeAccountId });\n }, [setupIntentStripeAccountId]);\n\n const handleAsyncError = (error: unknown) => {\n if (error instanceof KnownErrors.DefaultPaymentMethodRequired) {\n toast({\n title: t(\"No default payment method\"),\n description: t(\"Add a payment method before switching plans.\"),\n variant: \"destructive\",\n });\n return;\n }\n alert(`An unhandled error occurred. Please ${process.env.NODE_ENV === \"development\" ? \"check the browser console for the full error.\" : \"report this to the developer.\"}\\n\\n${error}`);\n };\n\n const openPaymentDialog = () => {\n runAsynchronously(async () => {\n setPaymentDialogOpen(true);\n const res = await props.customer.createPaymentMethodSetupIntent();\n setSetupIntentClientSecret(res.clientSecret);\n setSetupIntentStripeAccountId(res.stripeAccountId);\n }, { onError: handleAsyncError });\n };\n\n const closePaymentDialog = () => {\n setPaymentDialogOpen(false);\n setSetupIntentClientSecret(null);\n setSetupIntentStripeAccountId(null);\n };\n\n const openSwitchDialog = (productId: string, firstOptionId: string | null) => {\n setSwitchFromProductId(productId);\n setSwitchToProductId(firstOptionId);\n };\n\n const closeSwitchDialog = () => {\n setSwitchFromProductId(null);\n setSwitchToProductId(null);\n };\n\n const switchSourceProduct = switchFromProductId\n ? productsForCustomerType.find((product) => product.id === switchFromProductId) ?? null\n : null;\n const switchOptions = switchSourceProduct?.switchOptions ?? [];\n const selectedSwitchOption = switchOptions.find((option) => option.productId === switchToProductId) ?? null;\n const selectedPriceId = selectedSwitchOption ? (Object.keys(selectedSwitchOption.prices)[0] ?? null) : null;\n\n return (\n <div className=\"space-y-4\">\n {props.title && <Typography className=\"font-medium\">{props.title}</Typography>}\n\n {defaultPaymentMethod && (\n <Section\n title={t(\"Payment method\")}\n description={t(\"Manage the default payment method used for subscriptions and invoices.\")}\n >\n <Typography>{formatPaymentMethod(defaultPaymentMethod)}</Typography>\n\n <Button onClick={openPaymentDialog}>\n {t(\"Update payment method\")}\n </Button>\n\n <ActionDialog\n open={paymentDialogOpen}\n onOpenChange={(open) => {\n if (!open) {\n closePaymentDialog();\n } else {\n setPaymentDialogOpen(true);\n }\n }}\n title={t(\"Update payment method\")}\n >\n {!setupIntentClientSecret || !setupIntentStripeAccountId || !stripePromise ? (\n <Skeleton className=\"h-10 w-full\" />\n ) : (\n <Elements\n stripe={stripePromise}\n options={{\n clientSecret: setupIntentClientSecret,\n }}\n >\n <SetDefaultPaymentMethodForm\n clientSecret={setupIntentClientSecret}\n onSetupIntentSucceeded={async (setupIntentId) => {\n await props.customer.setDefaultPaymentMethodFromSetupIntent(setupIntentId);\n closePaymentDialog();\n }}\n />\n </Elements>\n )}\n </ActionDialog>\n </Section>\n )}\n\n {productsForCustomerType.length > 0 && (\n <Section\n title={t(\"Active plans\")}\n description={t(\"View your active plans and purchases.\")}\n >\n <div className=\"space-y-3\">\n {productsForCustomerType.map((product, index) => {\n const quantitySuffix = product.quantity !== 1 ? ` ×${product.quantity}` : \"\";\n const isSubscription = product.type === \"subscription\";\n const isCancelable = isSubscription && !!product.id && !!product.subscription?.isCancelable;\n const canSwitchPlans = isSubscription && defaultPaymentMethod && !!product.id && (product.switchOptions?.length ?? 0) > 0;\n const renewsAt = isSubscription ? (product.subscription?.currentPeriodEnd ?? null) : null;\n\n const subtitle =\n product.type === \"one_time\"\n ? t(\"One-time purchase\")\n : renewsAt\n ? `${t(\"Renews on\")} ${new Intl.DateTimeFormat(undefined, { year: \"numeric\", month: \"short\", day: \"numeric\" }).format(renewsAt)}`\n : t(\"Subscription\");\n\n return (\n <div key={product.id ?? `${product.displayName}-${index}`} className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{product.displayName}{quantitySuffix}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{subtitle}</Typography>\n </div>\n\n <div className=\"flex flex-col items-end gap-2\">\n {canSwitchPlans && (\n <Button\n variant=\"secondary\"\n color=\"neutral\"\n onClick={() => openSwitchDialog(product.id!, product.switchOptions?.[0]?.productId ?? null)}\n >\n {t(\"Change plan\")}\n </Button>\n )}\n {isCancelable && (\n <Button\n variant=\"secondary\"\n color=\"neutral\"\n onClick={() => setCancelProductId(product.id)}\n >\n {t(\"Cancel subscription\")}\n </Button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n <ActionDialog\n open={cancelProductId !== null}\n onOpenChange={(open) => {\n if (!open) setCancelProductId(null);\n }}\n title={t(\"Cancel subscription\")}\n description={t(\"Canceling will stop future renewals for this subscription.\")}\n danger\n cancelButton\n okButton={{\n label: t(\"Cancel subscription\"),\n onClick: async () => {\n const productId = cancelProductId;\n if (!productId) return;\n if (props.customerType === \"team\") {\n await stackApp.cancelSubscription({ teamId: props.customer.id, productId });\n } else {\n await stackApp.cancelSubscription({ productId });\n }\n setCancelProductId(null);\n },\n }}\n />\n\n <ActionDialog\n open={switchFromProductId !== null}\n onOpenChange={(open) => {\n if (!open) closeSwitchDialog();\n }}\n title={t(\"Change plan\")}\n description={t(\"Select a new plan from the same product line.\")}\n cancelButton\n okButton={{\n label: t(\"Switch plan\"),\n onClick: async () => {\n const fromProductId = switchFromProductId;\n const toProductId = switchToProductId;\n if (!fromProductId || !toProductId) return;\n if (!selectedPriceId) return;\n const result = await Result.fromThrowingAsync(() => props.customer.switchSubscription({\n fromProductId,\n toProductId,\n priceId: selectedPriceId,\n }));\n if (result.status === \"error\") {\n handleAsyncError(result.error);\n return \"prevent-close\";\n }\n closeSwitchDialog();\n },\n props: {\n disabled: !switchFromProductId || !switchToProductId || !selectedPriceId,\n },\n }}\n >\n <div className=\"space-y-2\">\n {switchOptions.length === 0 ? (\n <Typography variant=\"secondary\" type=\"footnote\">\n {t(\"No other plans available for this subscription.\")}\n </Typography>\n ) : (\n <>\n <Typography type=\"footnote\">{t(\"Choose a plan\")}</Typography>\n <Select\n value={switchToProductId ?? undefined}\n onValueChange={(value) => setSwitchToProductId(value || null)}\n >\n <SelectTrigger className=\"w-full\">\n <SelectValue placeholder={t(\"Choose a plan\")} />\n </SelectTrigger>\n <SelectContent>\n {switchOptions.map((option: NonNullable<typeof switchOptions>[number]) => (\n <SelectItem key={option.productId} value={option.productId}>\n {option.displayName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </>\n )}\n </div>\n </ActionDialog>\n </Section>\n )\n }\n {invoices.length > 0 && (\n <>\n <Separator />\n <div className=\"space-y-2\">\n <div className=\"space-y-1\">\n <Typography className=\"font-medium\">{t(\"Invoices\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"Review past invoices and receipts.\")}</Typography>\n </div>\n <div className=\"border rounded-md\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-[140px]\">{t(\"Date\")}</TableHead>\n <TableHead className=\"w-[120px]\">{t(\"Status\")}</TableHead>\n <TableHead className=\"w-[120px]\">{t(\"Amount\")}</TableHead>\n <TableHead className=\"w-[120px] text-right\">{t(\"Invoice\")}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {invoices.map((invoice, index) => {\n const createdAtTime = invoice.createdAt.getTime();\n const invoiceKey = Number.isNaN(createdAtTime) ? `invoice-${index}` : `invoice-${createdAtTime}-${index}`;\n return (\n <TableRow key={invoiceKey}>\n <TableCell>\n <Typography>{formatInvoiceDate(invoice.createdAt, t)}</Typography>\n </TableCell>\n <TableCell>\n <Typography>{formatInvoiceStatus(invoice.status, t)}</Typography>\n </TableCell>\n <TableCell>\n <Typography>{formatInvoiceAmount(invoice.amountTotal, t)}</Typography>\n </TableCell>\n <TableCell align=\"right\">\n {invoice.hostedInvoiceUrl ? (\n <Button asChild variant=\"secondary\" color=\"neutral\" size=\"sm\">\n <a href={invoice.hostedInvoiceUrl} target=\"_blank\" rel=\"noreferrer\">\n {t(\"View\")}\n </a>\n </Button>\n ) : (\n <Typography variant=\"secondary\" type=\"footnote\">\n {t(\"Unavailable\")}\n </Typography>\n )}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n </div>\n </>\n )}\n </div >\n );\n}\n"],"mappings":";;;AAOA,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,cAAc,QAAQ,QAAQ,eAAe,YAAY,eAAe,aAAa,WAAW,UAAU,OAAO,WAAW,WAAW,WAAW,aAAa,UAAU,OAAO,kBAAkB;AAC3M,SAAS,aAAa,UAAU,aAAa,iBAAiB;AAC9D,SAAS,kBAAkB;AAC3B,SAAS,SAAS,gBAAgB;AAClC,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,eAAe;AACxB,SAAS,cAAc;AA6GjB,SA+UU,UA9UR,KADF;AAlGN,SAAS,oBAAoB,IAAuC;AAClE,QAAM,UAAU;AAAA,IACd,GAAG,QAAQ,GAAG,MAAM,YAAY,IAAI;AAAA,IACpC,GAAG,QAAQ,4BAAQ,GAAG,KAAK,KAAK;AAAA,IAChC,GAAG,aAAa,GAAG,WAAW,OAAO,GAAG,SAAS,IAAI,GAAG,QAAQ,KAAK;AAAA,EACvE,EAAE,OAAO,OAAO;AAChB,SAAO,QAAQ,KAAK,QAAK;AAC3B;AAEA,IAAM,sBAAsB,CAAC,QAA+B,MAAiC;AAC3F,MAAI,CAAC,QAAQ;AACX,WAAO,EAAE,SAAS;AAAA,EACpB;AACA,UAAQ,QAAQ;AAAA,IACd,KAAK,SAAS;AACZ,aAAO,EAAE,OAAO;AAAA,IAClB;AAAA,IACA,KAAK,QAAQ;AACX,aAAO,EAAE,MAAM;AAAA,IACjB;AAAA,IACA,KAAK,QAAQ;AACX,aAAO,EAAE,MAAM;AAAA,IACjB;AAAA,IACA,KAAK,iBAAiB;AACpB,aAAO,EAAE,eAAe;AAAA,IAC1B;AAAA,IACA,KAAK,QAAQ;AACX,aAAO,EAAE,MAAM;AAAA,IACjB;AAAA,IACA,SAAS;AACP,aAAO,EAAE,SAAS;AAAA,IACpB;AAAA,EACF;AACF;AAEA,IAAM,sBAAsB,CAAC,aAAwC,MAAiC;AACpG,MAAI,OAAO,gBAAgB,YAAY,OAAO,MAAM,WAAW,GAAG;AAChE,WAAO,EAAE,SAAS;AAAA,EACpB;AACA,QAAM,aAAa,cAAc;AACjC,QAAM,YAAY,IAAI,KAAK,aAAa,QAAW,EAAE,uBAAuB,GAAG,uBAAuB,EAAE,CAAC,EAAE,OAAO,UAAU;AAC5H,SAAO,IAAI,SAAS;AACtB;AAEA,IAAM,oBAAoB,CAAC,MAA+B,MAAiC;AACzF,MAAI,CAAC,QAAQ,OAAO,MAAM,KAAK,QAAQ,CAAC,GAAG;AACzC,WAAO,EAAE,SAAS;AAAA,EACpB;AACA,SAAO,IAAI,KAAK,eAAe,QAAW,EAAE,MAAM,WAAW,OAAO,SAAS,KAAK,UAAU,CAAC,EAAE,OAAO,IAAI;AAC5G;AAsCA,SAAS,4BAA4B,OAGlC;AACD,QAAM,SAAS,UAAU;AACzB,QAAM,WAAW,YAAY;AAC7B,QAAM,CAAC,cAAc,eAAe,IAAI,SAAwB,IAAI;AACpE,QAAM,WAAW,kBAAkB,SAAS,gBAAgB,SAAS,SAAS,gBAAgB,MAAM,cAAc,MAAM;AAExH,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,SAAI,WAAU,aACb;AAAA,0BAAC,cAAW,WAAU,eAAc,0BAAY;AAAA,MAChD,oBAAC,SAAI,WAAU,sCACb,8BAAC,eAAY,SAAS,EAAE,gBAAgB,MAAM,OAAO,EAAE,MAAM,EAAE,OAAO,WAAW,UAAU,QAAQ,EAAE,EAAE,GAAG,GAC5G;AAAA,OACF;AAAA,IACC,gBACC,oBAAC,cAAW,SAAQ,aAAY,MAAK,YAClC,wBACH;AAAA,IAEF;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,YAAY;AACnB,cAAI,CAAC,UAAU,CAAC,UAAU;AACxB,4BAAgB,4CAA4C;AAC5D;AAAA,UACF;AACA,gBAAM,OAAO,SAAS,WAAW,WAAW;AAC5C,cAAI,CAAC,MAAM;AACT,4BAAgB,yBAAyB;AACzC;AAAA,UACF;AAEA,gBAAM,SAAS,MAAM,OAAO,iBAAiB,MAAM,cAAc;AAAA,YAC/D,gBAAgB,EAAE,KAAK;AAAA,UACzB,CAAC;AACD,cAAI,OAAO,OAAO;AAChB,4BAAgB,OAAO,MAAM,WAAW,gCAAgC;AACxE;AAAA,UACF;AACA,cAAI,CAAC,OAAO,YAAY,IAAI;AAC1B,4BAAgB,uCAAuC;AACvD;AAAA,UACF;AACA,gBAAM,MAAM,uBAAuB,OAAO,YAAY,EAAE;AAAA,QAC1D;AAAA,QACD;AAAA;AAAA,IAED;AAAA,KACF;AAEJ;AAEO,SAAS,cAAc,OAK3B;AACD,MAAI,MAAM,UAAU;AAClB,WAAO,oBAAC,qBAAkB,OAAO,MAAM,OAAO;AAAA,EAChD;AACA,MAAI,CAAC,MAAM,UAAU;AACnB,WAAO;AAAA,EACT;AACA,SAAO,oBAAC,qBAAkB,OAAO,MAAM,OAAO,UAAU,MAAM,UAAU,cAAc,MAAM,gBAAgB,QAAQ;AACtH;AAEA,SAAS,kBAAkB,OAA2B;AACpD,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,uBAA6C;AAAA,IACjD,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,WAAW;AAAA,IACX,UAAU;AAAA,EACZ;AAEA,SACE,qBAAC,SAAI,WAAU,aACZ;AAAA,UAAM,SAAS,oBAAC,cAAW,WAAU,eAAe,gBAAM,OAAM;AAAA,IACjE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,gBAAgB;AAAA,QACzB,aAAa,EAAE,wEAAwE;AAAA,QAEvF;AAAA,8BAAC,cAAY,8BAAoB,oBAAoB,GAAE;AAAA,UACvD,oBAAC,UAAO,UAAQ,MACb,YAAE,uBAAuB,GAC5B;AAAA;AAAA;AAAA,IACF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,cAAc;AAAA,QACvB,aAAa,EAAE,uCAAuC;AAAA,QAEtD,+BAAC,SAAI,WAAU,aACb;AAAA,+BAAC,SAAI,WAAU,0CACb;AAAA,iCAAC,SAAI,WAAU,WACb;AAAA,kCAAC,cAAW,WAAU,YAAY,YAAE,KAAK,GAAE;AAAA,cAC3C,qBAAC,cAAW,SAAQ,aAAY,MAAK,YAAY;AAAA,kBAAE,WAAW;AAAA,gBAAE;AAAA,iBAAY;AAAA,eAC9E;AAAA,YACA,oBAAC,UAAO,UAAQ,MAAC,SAAQ,aAAY,OAAM,WACxC,YAAE,qBAAqB,GAC1B;AAAA,aACF;AAAA,UACA,oBAAC,SAAI,WAAU,0CACb,+BAAC,SAAI,WAAU,WACb;AAAA,gCAAC,cAAW,WAAU,YAAY,YAAE,cAAc,GAAE;AAAA,YACpD,oBAAC,cAAW,SAAQ,aAAY,MAAK,YAAY,YAAE,mBAAmB,GAAE;AAAA,aAC1E,GACF;AAAA,WACF;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AAEA,SAAS,kBAAkB,OAAkF;AAC3G,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,WAAW,YAAY;AAC7B,QAAM,UAAU,MAAM,SAAS,WAAW;AAC1C,QAAM,uBAAuB,QAAQ;AACrC,QAAM,WAAW,MAAM,SAAS,YAAY;AAC5C,QAAM,WAAW,MAAM,SAAS,YAAY,EAAE,OAAO,GAAG,CAAC;AACzD,QAAM,0BAA0B,SAAS,OAAO,aAAW,QAAQ,iBAAiB,MAAM,YAAY;AAEtG,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,KAAK;AAChE,QAAM,CAAC,yBAAyB,0BAA0B,IAAI,SAAwB,IAAI;AAC1F,QAAM,CAAC,4BAA4B,6BAA6B,IAAI,SAAwB,IAAI;AAChG,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAwB,IAAI;AAC1E,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,SAAwB,IAAI;AAClF,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAwB,IAAI;AAE9E,QAAM,gBAAgB,QAAQ,MAAM;AAClC,QAAI,CAAC,2BAA4B,QAAO;AACxC,UAAM,iBAAiB,QAAQ,IAAI;AACnC,QAAI,CAAC,eAAgB,QAAO;AAC5B,WAAO,WAAW,gBAAgB,EAAE,eAAe,2BAA2B,CAAC;AAAA,EACjF,GAAG,CAAC,0BAA0B,CAAC;AAE/B,QAAM,mBAAmB,CAAC,UAAmB;AAC3C,QAAI,iBAAiB,YAAY,8BAA8B;AAC7D,YAAM;AAAA,QACJ,OAAO,EAAE,2BAA2B;AAAA,QACpC,aAAa,EAAE,8CAA8C;AAAA,QAC7D,SAAS;AAAA,MACX,CAAC;AACD;AAAA,IACF;AACA,UAAM,uCAAuC,QAAQ,IAAI,aAAa,gBAAgB,kDAAkD,+BAA+B;AAAA;AAAA,EAAO,KAAK,EAAE;AAAA,EACvL;AAEA,QAAM,oBAAoB,MAAM;AAC9B,sBAAkB,YAAY;AAC5B,2BAAqB,IAAI;AACzB,YAAM,MAAM,MAAM,MAAM,SAAS,+BAA+B;AAChE,iCAA2B,IAAI,YAAY;AAC3C,oCAA8B,IAAI,eAAe;AAAA,IACnD,GAAG,EAAE,SAAS,iBAAiB,CAAC;AAAA,EAClC;AAEA,QAAM,qBAAqB,MAAM;AAC/B,yBAAqB,KAAK;AAC1B,+BAA2B,IAAI;AAC/B,kCAA8B,IAAI;AAAA,EACpC;AAEA,QAAM,mBAAmB,CAAC,WAAmB,kBAAiC;AAC5E,2BAAuB,SAAS;AAChC,yBAAqB,aAAa;AAAA,EACpC;AAEA,QAAM,oBAAoB,MAAM;AAC9B,2BAAuB,IAAI;AAC3B,yBAAqB,IAAI;AAAA,EAC3B;AAEA,QAAM,sBAAsB,sBACxB,wBAAwB,KAAK,CAAC,YAAY,QAAQ,OAAO,mBAAmB,KAAK,OACjF;AACJ,QAAM,gBAAgB,qBAAqB,iBAAiB,CAAC;AAC7D,QAAM,uBAAuB,cAAc,KAAK,CAAC,WAAW,OAAO,cAAc,iBAAiB,KAAK;AACvG,QAAM,kBAAkB,uBAAwB,OAAO,KAAK,qBAAqB,MAAM,EAAE,CAAC,KAAK,OAAQ;AAEvG,SACE,qBAAC,SAAI,WAAU,aACZ;AAAA,UAAM,SAAS,oBAAC,cAAW,WAAU,eAAe,gBAAM,OAAM;AAAA,IAEhE,wBACC;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,gBAAgB;AAAA,QACzB,aAAa,EAAE,wEAAwE;AAAA,QAEvF;AAAA,8BAAC,cAAY,8BAAoB,oBAAoB,GAAE;AAAA,UAEvD,oBAAC,UAAO,SAAS,mBACd,YAAE,uBAAuB,GAC5B;AAAA,UAEA;AAAA,YAAC;AAAA;AAAA,cACC,MAAM;AAAA,cACN,cAAc,CAAC,SAAS;AACtB,oBAAI,CAAC,MAAM;AACT,qCAAmB;AAAA,gBACrB,OAAO;AACL,uCAAqB,IAAI;AAAA,gBAC3B;AAAA,cACF;AAAA,cACA,OAAO,EAAE,uBAAuB;AAAA,cAE/B,WAAC,2BAA2B,CAAC,8BAA8B,CAAC,gBAC3D,oBAAC,YAAS,WAAU,eAAc,IAElC;AAAA,gBAAC;AAAA;AAAA,kBACC,QAAQ;AAAA,kBACR,SAAS;AAAA,oBACP,cAAc;AAAA,kBAChB;AAAA,kBAEA;AAAA,oBAAC;AAAA;AAAA,sBACC,cAAc;AAAA,sBACd,wBAAwB,OAAO,kBAAkB;AAC/C,8BAAM,MAAM,SAAS,uCAAuC,aAAa;AACzE,2CAAmB;AAAA,sBACrB;AAAA;AAAA,kBACF;AAAA;AAAA,cACF;AAAA;AAAA,UAEJ;AAAA;AAAA;AAAA,IACF;AAAA,IAGD,wBAAwB,SAAS,KAChC;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,cAAc;AAAA,QACvB,aAAa,EAAE,uCAAuC;AAAA,QAEtD;AAAA,8BAAC,SAAI,WAAU,aACZ,kCAAwB,IAAI,CAAC,SAAS,UAAU;AAC/C,kBAAM,iBAAiB,QAAQ,aAAa,IAAI,QAAK,QAAQ,QAAQ,KAAK;AAC1E,kBAAM,iBAAiB,QAAQ,SAAS;AACxC,kBAAM,eAAe,kBAAkB,CAAC,CAAC,QAAQ,MAAM,CAAC,CAAC,QAAQ,cAAc;AAC/E,kBAAM,iBAAiB,kBAAkB,wBAAwB,CAAC,CAAC,QAAQ,OAAO,QAAQ,eAAe,UAAU,KAAK;AACxH,kBAAM,WAAW,iBAAkB,QAAQ,cAAc,oBAAoB,OAAQ;AAErF,kBAAM,WACJ,QAAQ,SAAS,aACb,EAAE,mBAAmB,IACrB,WACE,GAAG,EAAE,WAAW,CAAC,IAAI,IAAI,KAAK,eAAe,QAAW,EAAE,MAAM,WAAW,OAAO,SAAS,KAAK,UAAU,CAAC,EAAE,OAAO,QAAQ,CAAC,KAC7H,EAAE,cAAc;AAExB,mBACE,qBAAC,SAA0D,WAAU,0CACnE;AAAA,mCAAC,SAAI,WAAU,WACb;AAAA,qCAAC,cAAW,WAAU,YAAY;AAAA,0BAAQ;AAAA,kBAAa;AAAA,mBAAe;AAAA,gBACtE,oBAAC,cAAW,SAAQ,aAAY,MAAK,YAAY,oBAAS;AAAA,iBAC5D;AAAA,cAEA,qBAAC,SAAI,WAAU,iCACZ;AAAA,kCACC;AAAA,kBAAC;AAAA;AAAA,oBACC,SAAQ;AAAA,oBACR,OAAM;AAAA,oBACN,SAAS,MAAM,iBAAiB,QAAQ,IAAK,QAAQ,gBAAgB,CAAC,GAAG,aAAa,IAAI;AAAA,oBAEzF,YAAE,aAAa;AAAA;AAAA,gBAClB;AAAA,gBAED,gBACC;AAAA,kBAAC;AAAA;AAAA,oBACC,SAAQ;AAAA,oBACR,OAAM;AAAA,oBACN,SAAS,MAAM,mBAAmB,QAAQ,EAAE;AAAA,oBAE3C,YAAE,qBAAqB;AAAA;AAAA,gBAC1B;AAAA,iBAEJ;AAAA,iBAzBQ,QAAQ,MAAM,GAAG,QAAQ,WAAW,IAAI,KAAK,EA0BvD;AAAA,UAEJ,CAAC,GACH;AAAA,UAEA;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,oBAAoB;AAAA,cAC1B,cAAc,CAAC,SAAS;AACtB,oBAAI,CAAC,KAAM,oBAAmB,IAAI;AAAA,cACpC;AAAA,cACA,OAAO,EAAE,qBAAqB;AAAA,cAC9B,aAAa,EAAE,4DAA4D;AAAA,cAC3E,QAAM;AAAA,cACN,cAAY;AAAA,cACZ,UAAU;AAAA,gBACR,OAAO,EAAE,qBAAqB;AAAA,gBAC9B,SAAS,YAAY;AACnB,wBAAM,YAAY;AAClB,sBAAI,CAAC,UAAW;AAChB,sBAAI,MAAM,iBAAiB,QAAQ;AACjC,0BAAM,SAAS,mBAAmB,EAAE,QAAQ,MAAM,SAAS,IAAI,UAAU,CAAC;AAAA,kBAC5E,OAAO;AACL,0BAAM,SAAS,mBAAmB,EAAE,UAAU,CAAC;AAAA,kBACjD;AACA,qCAAmB,IAAI;AAAA,gBACzB;AAAA,cACF;AAAA;AAAA,UACF;AAAA,UAEA;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,wBAAwB;AAAA,cAC9B,cAAc,CAAC,SAAS;AACtB,oBAAI,CAAC,KAAM,mBAAkB;AAAA,cAC/B;AAAA,cACA,OAAO,EAAE,aAAa;AAAA,cACtB,aAAa,EAAE,+CAA+C;AAAA,cAC9D,cAAY;AAAA,cACZ,UAAU;AAAA,gBACR,OAAO,EAAE,aAAa;AAAA,gBACtB,SAAS,YAAY;AACnB,wBAAM,gBAAgB;AACtB,wBAAM,cAAc;AACpB,sBAAI,CAAC,iBAAiB,CAAC,YAAa;AACpC,sBAAI,CAAC,gBAAiB;AACtB,wBAAM,SAAS,MAAM,OAAO,kBAAkB,MAAM,MAAM,SAAS,mBAAmB;AAAA,oBACpF;AAAA,oBACA;AAAA,oBACA,SAAS;AAAA,kBACX,CAAC,CAAC;AACF,sBAAI,OAAO,WAAW,SAAS;AAC7B,qCAAiB,OAAO,KAAK;AAC7B,2BAAO;AAAA,kBACT;AACA,oCAAkB;AAAA,gBACpB;AAAA,gBACA,OAAO;AAAA,kBACL,UAAU,CAAC,uBAAuB,CAAC,qBAAqB,CAAC;AAAA,gBAC3D;AAAA,cACF;AAAA,cAEA,8BAAC,SAAI,WAAU,aACZ,wBAAc,WAAW,IACxB,oBAAC,cAAW,SAAQ,aAAY,MAAK,YAClC,YAAE,iDAAiD,GACtD,IAEA,iCACE;AAAA,oCAAC,cAAW,MAAK,YAAY,YAAE,eAAe,GAAE;AAAA,gBAChD;AAAA,kBAAC;AAAA;AAAA,oBACC,OAAO,qBAAqB;AAAA,oBAC5B,eAAe,CAAC,UAAU,qBAAqB,SAAS,IAAI;AAAA,oBAE5D;AAAA,0CAAC,iBAAc,WAAU,UACvB,8BAAC,eAAY,aAAa,EAAE,eAAe,GAAG,GAChD;AAAA,sBACA,oBAAC,iBACE,wBAAc,IAAI,CAAC,WAClB,oBAAC,cAAkC,OAAO,OAAO,WAC9C,iBAAO,eADO,OAAO,SAExB,CACD,GACH;AAAA;AAAA;AAAA,gBACF;AAAA,iBACF,GAEJ;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IACF;AAAA,IAGD,SAAS,SAAS,KACjB,iCACE;AAAA,0BAAC,aAAU;AAAA,MACX,qBAAC,SAAI,WAAU,aACb;AAAA,6BAAC,SAAI,WAAU,aACb;AAAA,8BAAC,cAAW,WAAU,eAAe,YAAE,UAAU,GAAE;AAAA,UACnD,oBAAC,cAAW,SAAQ,aAAY,MAAK,YAAY,YAAE,oCAAoC,GAAE;AAAA,WAC3F;AAAA,QACA,oBAAC,SAAI,WAAU,qBACb,+BAAC,SACC;AAAA,8BAAC,eACC,+BAAC,YACC;AAAA,gCAAC,aAAU,WAAU,aAAa,YAAE,MAAM,GAAE;AAAA,YAC5C,oBAAC,aAAU,WAAU,aAAa,YAAE,QAAQ,GAAE;AAAA,YAC9C,oBAAC,aAAU,WAAU,aAAa,YAAE,QAAQ,GAAE;AAAA,YAC9C,oBAAC,aAAU,WAAU,wBAAwB,YAAE,SAAS,GAAE;AAAA,aAC5D,GACF;AAAA,UACA,oBAAC,aACE,mBAAS,IAAI,CAAC,SAAS,UAAU;AAChC,kBAAM,gBAAgB,QAAQ,UAAU,QAAQ;AAChD,kBAAM,aAAa,OAAO,MAAM,aAAa,IAAI,WAAW,KAAK,KAAK,WAAW,aAAa,IAAI,KAAK;AACvG,mBACE,qBAAC,YACC;AAAA,kCAAC,aACC,8BAAC,cAAY,4BAAkB,QAAQ,WAAW,CAAC,GAAE,GACvD;AAAA,cACA,oBAAC,aACC,8BAAC,cAAY,8BAAoB,QAAQ,QAAQ,CAAC,GAAE,GACtD;AAAA,cACA,oBAAC,aACC,8BAAC,cAAY,8BAAoB,QAAQ,aAAa,CAAC,GAAE,GAC3D;AAAA,cACA,oBAAC,aAAU,OAAM,SACd,kBAAQ,mBACP,oBAAC,UAAO,SAAO,MAAC,SAAQ,aAAY,OAAM,WAAU,MAAK,MACvD,8BAAC,OAAE,MAAM,QAAQ,kBAAkB,QAAO,UAAS,KAAI,cACpD,YAAE,MAAM,GACX,GACF,IAEA,oBAAC,cAAW,SAAQ,aAAY,MAAK,YAClC,YAAE,aAAa,GAClB,GAEJ;AAAA,iBAtBa,UAuBf;AAAA,UAEJ,CAAC,GACH;AAAA,WACF,GACF;AAAA,SACF;AAAA,OACF;AAAA,KAEJ;AAEJ;","names":[]}
|