@b3dotfun/sdk 0.1.69-alpha.20 → 0.1.69-alpha.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/cjs/anyspend/react/components/checkout/AnySpendCheckout.js +6 -5
  2. package/dist/cjs/anyspend/react/components/checkout/CartItemRow.d.ts +2 -1
  3. package/dist/cjs/anyspend/react/components/checkout/CartSummary.d.ts +6 -4
  4. package/dist/cjs/anyspend/react/components/checkout/CartSummary.js +13 -11
  5. package/dist/cjs/anyspend/react/components/checkout/CheckoutCartPanel.d.ts +3 -1
  6. package/dist/cjs/anyspend/react/components/checkout/CheckoutCartPanel.js +5 -4
  7. package/dist/cjs/anyspend/react/components/checkout/CheckoutFormPanel.d.ts +3 -1
  8. package/dist/cjs/anyspend/react/components/checkout/CheckoutFormPanel.js +2 -2
  9. package/dist/cjs/anyspend/react/components/checkout/DiscountCodeInput.d.ts +3 -1
  10. package/dist/cjs/anyspend/react/components/checkout/DiscountCodeInput.js +3 -6
  11. package/dist/cjs/anyspend/react/components/checkout/PriceSkeleton.d.ts +5 -0
  12. package/dist/cjs/anyspend/react/components/checkout/PriceSkeleton.js +9 -0
  13. package/dist/cjs/anyspend/react/components/checkout/ShippingSelector.d.ts +3 -1
  14. package/dist/cjs/anyspend/react/components/checkout/ShippingSelector.js +3 -2
  15. package/dist/cjs/global-account/react/components/SignInWithB3/BetterAuthResetPassword.js +3 -2
  16. package/dist/cjs/global-account/react/components/SignInWithB3/BetterAuthSignIn.js +13 -3
  17. package/dist/cjs/global-account/react/components/SignInWithB3/components/PasswordInput.d.ts +10 -0
  18. package/dist/cjs/global-account/react/components/SignInWithB3/components/PasswordInput.js +10 -0
  19. package/dist/cjs/global-account/react/components/SignInWithB3/steps/LoginStepBetterAuth.js +6 -3
  20. package/dist/esm/anyspend/react/components/checkout/AnySpendCheckout.js +6 -5
  21. package/dist/esm/anyspend/react/components/checkout/CartItemRow.d.ts +2 -1
  22. package/dist/esm/anyspend/react/components/checkout/CartSummary.d.ts +6 -4
  23. package/dist/esm/anyspend/react/components/checkout/CartSummary.js +13 -11
  24. package/dist/esm/anyspend/react/components/checkout/CheckoutCartPanel.d.ts +3 -1
  25. package/dist/esm/anyspend/react/components/checkout/CheckoutCartPanel.js +5 -4
  26. package/dist/esm/anyspend/react/components/checkout/CheckoutFormPanel.d.ts +3 -1
  27. package/dist/esm/anyspend/react/components/checkout/CheckoutFormPanel.js +2 -2
  28. package/dist/esm/anyspend/react/components/checkout/DiscountCodeInput.d.ts +3 -1
  29. package/dist/esm/anyspend/react/components/checkout/DiscountCodeInput.js +3 -6
  30. package/dist/esm/anyspend/react/components/checkout/PriceSkeleton.d.ts +5 -0
  31. package/dist/esm/anyspend/react/components/checkout/PriceSkeleton.js +6 -0
  32. package/dist/esm/anyspend/react/components/checkout/ShippingSelector.d.ts +3 -1
  33. package/dist/esm/anyspend/react/components/checkout/ShippingSelector.js +3 -2
  34. package/dist/esm/global-account/react/components/SignInWithB3/BetterAuthResetPassword.js +4 -3
  35. package/dist/esm/global-account/react/components/SignInWithB3/BetterAuthSignIn.js +14 -4
  36. package/dist/esm/global-account/react/components/SignInWithB3/components/PasswordInput.d.ts +10 -0
  37. package/dist/esm/global-account/react/components/SignInWithB3/components/PasswordInput.js +7 -0
  38. package/dist/esm/global-account/react/components/SignInWithB3/steps/LoginStepBetterAuth.js +6 -3
  39. package/dist/styles/index.css +1 -1
  40. package/dist/types/anyspend/react/components/checkout/CartItemRow.d.ts +2 -1
  41. package/dist/types/anyspend/react/components/checkout/CartSummary.d.ts +6 -4
  42. package/dist/types/anyspend/react/components/checkout/CheckoutCartPanel.d.ts +3 -1
  43. package/dist/types/anyspend/react/components/checkout/CheckoutFormPanel.d.ts +3 -1
  44. package/dist/types/anyspend/react/components/checkout/DiscountCodeInput.d.ts +3 -1
  45. package/dist/types/anyspend/react/components/checkout/PriceSkeleton.d.ts +5 -0
  46. package/dist/types/anyspend/react/components/checkout/ShippingSelector.d.ts +3 -1
  47. package/dist/types/global-account/react/components/SignInWithB3/components/PasswordInput.d.ts +10 -0
  48. package/package.json +1 -1
  49. package/src/anyspend/react/components/checkout/AnySpendCheckout.tsx +10 -4
  50. package/src/anyspend/react/components/checkout/CartItemRow.tsx +2 -1
  51. package/src/anyspend/react/components/checkout/CartSummary.tsx +24 -20
  52. package/src/anyspend/react/components/checkout/CheckoutCartPanel.tsx +12 -3
  53. package/src/anyspend/react/components/checkout/CheckoutFormPanel.tsx +5 -0
  54. package/src/anyspend/react/components/checkout/DiscountCodeInput.tsx +15 -5
  55. package/src/anyspend/react/components/checkout/PriceSkeleton.tsx +19 -0
  56. package/src/anyspend/react/components/checkout/ShippingSelector.tsx +5 -1
  57. package/src/global-account/react/components/SignInWithB3/BetterAuthResetPassword.tsx +6 -7
  58. package/src/global-account/react/components/SignInWithB3/BetterAuthSignIn.tsx +20 -6
  59. package/src/global-account/react/components/SignInWithB3/components/PasswordInput.tsx +62 -0
  60. package/src/global-account/react/components/SignInWithB3/steps/LoginStepBetterAuth.tsx +9 -4
@@ -3,15 +3,17 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { cn } from "../../../../shared/utils/cn.js";
4
4
  import { formatTokenAmount, safeBigInt } from "../../../../shared/utils/number.js";
5
5
  import { useMemo } from "react";
6
- export function CartSummary({ total, tokenSymbol, classes, subtotal, tokenDecimals = 18, shipping, tax, discount, summaryLines, usdEquivalent, }) {
7
- const hasAdjustments = !!shipping?.amount || !!tax?.amount || !!discount?.amount || (summaryLines && summaryLines.length > 0);
8
- const formattedShipping = useMemo(() => (shipping?.amount ? formatTokenAmount(safeBigInt(shipping.amount), tokenDecimals) : null), [shipping?.amount, tokenDecimals]);
9
- const formattedTax = useMemo(() => (tax?.amount ? formatTokenAmount(safeBigInt(tax.amount), tokenDecimals) : null), [tax?.amount, tokenDecimals]);
10
- const formattedDiscount = useMemo(() => (discount?.amount ? formatTokenAmount(safeBigInt(discount.amount), tokenDecimals) : null), [discount?.amount, tokenDecimals]);
11
- const formattedSummaryLines = useMemo(() => summaryLines?.map(line => ({
12
- ...line,
13
- formattedAmount: formatTokenAmount(safeBigInt(line.amount), tokenDecimals),
14
- isNegative: safeBigInt(line.amount) < BigInt(0),
15
- })), [summaryLines, tokenDecimals]);
16
- return (_jsxs("div", { className: cn("border-t border-gray-200 pt-3 dark:border-neutral-700", classes?.cartSummary), children: [hasAdjustments && subtotal && (_jsxs("div", { className: cn("flex items-center justify-between py-1", classes?.cartSubtotal), children: [_jsx("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Subtotal" }), _jsxs("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: [subtotal, " ", tokenSymbol] })] })), formattedShipping && (_jsxs("div", { className: cn("flex items-center justify-between py-1", classes?.cartSummaryLine), children: [_jsx("span", { className: cn("text-sm text-gray-500 dark:text-gray-400", classes?.cartSummaryLineLabel), children: shipping?.label || "Shipping" }), _jsxs("span", { className: cn("text-sm text-gray-500 dark:text-gray-400", classes?.cartSummaryLineAmount), children: [formattedShipping, " ", tokenSymbol] })] })), formattedTax && (_jsxs("div", { className: cn("flex items-center justify-between py-1", classes?.cartSummaryLine), children: [_jsxs("span", { className: cn("text-sm text-gray-500 dark:text-gray-400", classes?.cartSummaryLineLabel), children: [tax?.label || "Tax", tax?.rate && _jsxs("span", { className: "ml-1 text-xs text-gray-400 dark:text-gray-500", children: ["(", tax.rate, ")"] })] }), _jsxs("span", { className: cn("text-sm text-gray-500 dark:text-gray-400", classes?.cartSummaryLineAmount), children: [formattedTax, " ", tokenSymbol] })] })), formattedDiscount && (_jsxs("div", { className: cn("flex items-center justify-between py-1", classes?.cartDiscount), children: [_jsxs("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: [discount?.label || "Discount", discount?.code && (_jsx("span", { className: "ml-1 rounded bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-600 dark:bg-green-900/20 dark:text-green-400", children: discount.code }))] }), _jsxs("span", { className: "text-sm font-medium text-green-600 dark:text-green-400", children: ["-", formattedDiscount, " ", tokenSymbol] })] })), formattedSummaryLines?.map(line => (_jsxs("div", { className: cn("flex items-center justify-between py-1", classes?.cartSummaryLine), children: [_jsxs("span", { className: cn("text-sm text-gray-500 dark:text-gray-400", classes?.cartSummaryLineLabel), children: [line.label, line.description && (_jsxs("span", { className: "ml-1 text-xs text-gray-400 dark:text-gray-500", children: ["(", line.description, ")"] }))] }), _jsxs("span", { className: cn("text-sm", line.isNegative ? "font-medium text-green-600 dark:text-green-400" : "text-gray-500 dark:text-gray-400", classes?.cartSummaryLineAmount), children: [line.formattedAmount, " ", tokenSymbol] })] }, line.label))), _jsxs("div", { className: cn("flex flex-col", hasAdjustments && "mt-1 border-t border-gray-100 pt-2 dark:border-neutral-800"), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: "Total" }), _jsxs("span", { className: cn("text-base font-semibold text-gray-900 dark:text-gray-100", classes?.cartTotal), children: [total, " ", tokenSymbol] })] }), usdEquivalent && (_jsx("div", { className: "flex justify-end", children: _jsxs("span", { className: "text-xs text-gray-400 dark:text-gray-500", children: ["~", usdEquivalent, " USD"] }) }))] })] }));
6
+ export function CartSummary({ total, tokenSymbol, classes, subtotal, tokenDecimals = 18, shipping, tax, discount, summaryLines, usdEquivalent, pricesLoading = false, }) {
7
+ const formattedShipping = useMemo(() => (!pricesLoading && shipping?.amount ? formatTokenAmount(safeBigInt(shipping.amount), tokenDecimals) : null), [pricesLoading, shipping?.amount, tokenDecimals]);
8
+ const formattedTax = useMemo(() => (!pricesLoading && tax?.amount ? formatTokenAmount(safeBigInt(tax.amount), tokenDecimals) : null), [pricesLoading, tax?.amount, tokenDecimals]);
9
+ const formattedDiscount = useMemo(() => (!pricesLoading && discount?.amount ? formatTokenAmount(safeBigInt(discount.amount), tokenDecimals) : null), [pricesLoading, discount?.amount, tokenDecimals]);
10
+ const formattedSummaryLines = useMemo(() => pricesLoading
11
+ ? undefined
12
+ : summaryLines?.map(line => ({
13
+ ...line,
14
+ formattedAmount: formatTokenAmount(safeBigInt(line.amount), tokenDecimals),
15
+ isNegative: safeBigInt(line.amount) < BigInt(0),
16
+ })), [pricesLoading, summaryLines, tokenDecimals]);
17
+ const hasAdjustments = !!formattedShipping || !!formattedTax || !!formattedDiscount || !!formattedSummaryLines?.length;
18
+ return (_jsxs("div", { className: cn("border-t border-gray-200 pt-3 dark:border-neutral-700", classes?.cartSummary), children: [hasAdjustments && subtotal && (_jsxs("div", { className: cn("flex items-center justify-between py-1", classes?.cartSubtotal), children: [_jsx("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Subtotal" }), _jsxs("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: [subtotal, " ", tokenSymbol] })] })), formattedShipping && (_jsxs("div", { className: cn("flex items-center justify-between py-1", classes?.cartSummaryLine), children: [_jsx("span", { className: cn("text-sm text-gray-500 dark:text-gray-400", classes?.cartSummaryLineLabel), children: shipping?.label || "Shipping" }), _jsxs("span", { className: cn("text-sm text-gray-500 dark:text-gray-400", classes?.cartSummaryLineAmount), children: [formattedShipping, " ", tokenSymbol] })] })), formattedTax && (_jsxs("div", { className: cn("flex items-center justify-between py-1", classes?.cartSummaryLine), children: [_jsxs("span", { className: cn("text-sm text-gray-500 dark:text-gray-400", classes?.cartSummaryLineLabel), children: [tax?.label || "Tax", tax?.rate && _jsxs("span", { className: "ml-1 text-xs text-gray-400 dark:text-gray-500", children: ["(", tax.rate, ")"] })] }), _jsxs("span", { className: cn("text-sm text-gray-500 dark:text-gray-400", classes?.cartSummaryLineAmount), children: [formattedTax, " ", tokenSymbol] })] })), formattedDiscount && (_jsxs("div", { className: cn("flex items-center justify-between py-1", classes?.cartDiscount), children: [_jsxs("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: [discount?.label || "Discount", discount?.code && (_jsx("span", { className: "ml-1 rounded bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-600 dark:bg-green-900/20 dark:text-green-400", children: discount.code }))] }), _jsxs("span", { className: "text-sm font-medium text-green-600 dark:text-green-400", children: ["-", formattedDiscount, " ", tokenSymbol] })] })), formattedSummaryLines?.map(line => (_jsxs("div", { className: cn("flex items-center justify-between py-1", classes?.cartSummaryLine), children: [_jsxs("span", { className: cn("text-sm text-gray-500 dark:text-gray-400", classes?.cartSummaryLineLabel), children: [line.label, line.description && (_jsxs("span", { className: "ml-1 text-xs text-gray-400 dark:text-gray-500", children: ["(", line.description, ")"] }))] }), _jsxs("span", { className: cn("text-sm", line.isNegative ? "font-medium text-green-600 dark:text-green-400" : "text-gray-500 dark:text-gray-400", classes?.cartSummaryLineAmount), children: [line.formattedAmount, " ", tokenSymbol] })] }, line.label))), _jsxs("div", { className: cn("flex flex-col", hasAdjustments && "mt-1 border-t border-gray-100 pt-2 dark:border-neutral-800"), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: "Total" }), _jsxs("span", { className: cn("text-base font-semibold text-gray-900 dark:text-gray-100", classes?.cartTotal), children: [total, " ", tokenSymbol] })] }), !pricesLoading && usdEquivalent && (_jsx("div", { className: "flex justify-end", children: _jsxs("span", { className: "text-xs text-gray-400 dark:text-gray-500", children: ["~", usdEquivalent, " USD"] }) }))] })] }));
17
19
  }
@@ -5,6 +5,8 @@ interface CheckoutCartPanelProps {
5
5
  totalAmount: string;
6
6
  tokenSymbol?: string;
7
7
  tokenDecimals?: number;
8
+ /** True while token decimals/symbol are still loading — prevents rendering mis-decimalized prices. */
9
+ pricesLoading?: boolean;
8
10
  organizationName?: string;
9
11
  organizationLogo?: string;
10
12
  classes?: AnySpendCheckoutClasses;
@@ -28,5 +30,5 @@ interface CheckoutCartPanelProps {
28
30
  /** Formatted USD equivalent (e.g. "$5.56") — shown for non-stablecoin tokens */
29
31
  usdEquivalent?: string | null;
30
32
  }
31
- export declare function CheckoutCartPanel({ items, totalAmount, tokenSymbol, tokenDecimals, organizationName, organizationLogo, classes, footer, shipping, tax, discount, summaryLines, usdEquivalent, }: CheckoutCartPanelProps): import("react/jsx-runtime").JSX.Element;
33
+ export declare function CheckoutCartPanel({ items, totalAmount, tokenSymbol, tokenDecimals, pricesLoading, organizationName, organizationLogo, classes, footer, shipping, tax, discount, summaryLines, usdEquivalent, }: CheckoutCartPanelProps): import("react/jsx-runtime").JSX.Element;
32
34
  export {};
@@ -6,7 +6,8 @@ import { useMemo } from "react";
6
6
  import { CartItemRow } from "./CartItemRow.js";
7
7
  import { CartSummary } from "./CartSummary.js";
8
8
  import { PoweredByBranding } from "./PoweredByBranding.js";
9
- export function CheckoutCartPanel({ items, totalAmount, tokenSymbol = "", tokenDecimals = 18, organizationName, organizationLogo, classes, footer, shipping, tax, discount, summaryLines, usdEquivalent, }) {
9
+ import { PriceSkeleton } from "./PriceSkeleton.js";
10
+ export function CheckoutCartPanel({ items, totalAmount, tokenSymbol = "", tokenDecimals = 18, pricesLoading = false, organizationName, organizationLogo, classes, footer, shipping, tax, discount, summaryLines, usdEquivalent, }) {
10
11
  const formattedTotal = useMemo(() => formatTokenAmount(safeBigInt(totalAmount), tokenDecimals), [totalAmount, tokenDecimals]);
11
12
  // Compute subtotal from items only (before adjustments)
12
13
  const formattedSubtotal = useMemo(() => {
@@ -16,9 +17,9 @@ export function CheckoutCartPanel({ items, totalAmount, tokenSymbol = "", tokenD
16
17
  }
17
18
  return formatTokenAmount(subtotal, tokenDecimals);
18
19
  }, [items, tokenDecimals]);
19
- return (_jsxs("div", { className: cn("anyspend-cart-panel flex flex-col", classes?.cartPanel), children: [_jsx("h2", { className: cn("anyspend-cart-title mb-4 text-lg font-semibold text-gray-900 dark:text-gray-100", classes?.cartTitle), children: "Order Summary" }), _jsx("div", { className: "anyspend-cart-items divide-y divide-gray-100 dark:divide-gray-800", children: items.map((item, index) => {
20
+ return (_jsxs("div", { className: cn("anyspend-cart-panel flex flex-col", classes?.cartPanel), "aria-busy": pricesLoading || undefined, children: [_jsx("h2", { className: cn("anyspend-cart-title mb-4 text-lg font-semibold text-gray-900 dark:text-gray-100", classes?.cartTitle), children: "Order Summary" }), _jsx("div", { className: "anyspend-cart-items divide-y divide-gray-100 dark:divide-gray-800", children: items.map((item, index) => {
20
21
  const itemTotal = safeBigInt(item.amount) * BigInt(item.quantity);
21
- const formattedPrice = `${formatTokenAmount(itemTotal, tokenDecimals)} ${tokenSymbol}`;
22
+ const formattedPrice = pricesLoading ? (_jsx(PriceSkeleton, {})) : (`${formatTokenAmount(itemTotal, tokenDecimals)} ${tokenSymbol}`);
22
23
  return _jsx(CartItemRow, { item: item, formattedPrice: formattedPrice, classes: classes }, item.id || index);
23
- }) }), _jsx(CartSummary, { total: formattedTotal, tokenSymbol: tokenSymbol, classes: classes, subtotal: formattedSubtotal, tokenDecimals: tokenDecimals, shipping: shipping, tax: tax, discount: discount, summaryLines: summaryLines, usdEquivalent: usdEquivalent }), footer === undefined ? (_jsx(PoweredByBranding, { organizationName: organizationName, organizationLogo: organizationLogo, classes: classes })) : (footer)] }));
24
+ }) }), _jsx(CartSummary, { total: pricesLoading ? _jsx(PriceSkeleton, { className: "w-20" }) : formattedTotal, tokenSymbol: tokenSymbol, classes: classes, subtotal: formattedSubtotal, tokenDecimals: tokenDecimals, shipping: shipping, tax: tax, discount: discount, summaryLines: summaryLines, usdEquivalent: usdEquivalent, pricesLoading: pricesLoading }), footer === undefined ? (_jsx(PoweredByBranding, { organizationName: organizationName, organizationLogo: organizationLogo, classes: classes })) : (footer)] }));
24
25
  }
@@ -16,6 +16,8 @@ interface CheckoutFormPanelProps {
16
16
  /** Token info for display */
17
17
  tokenSymbol?: string;
18
18
  tokenDecimals?: number;
19
+ /** True while token decimals/symbol are still loading — prevents rendering mis-decimalized prices. */
20
+ pricesLoading?: boolean;
19
21
  /** CSS class overrides */
20
22
  classes?: AnySpendCheckoutClasses;
21
23
  /** Current form data (lifted state) */
@@ -35,5 +37,5 @@ interface CheckoutFormPanelProps {
35
37
  /** Slot overrides */
36
38
  checkoutFormSlot?: (props: CheckoutFormComponentProps) => React.ReactNode;
37
39
  }
38
- export declare function CheckoutFormPanel({ formSchema, formComponent: FormComponent, shippingOptions, collectShippingAddress, enableDiscountCode, validateDiscount, tokenSymbol, tokenDecimals, classes, formData, onFormDataChange, selectedShipping, onShippingChange, appliedDiscount, onDiscountApplied, onDiscountRemoved, shippingAddress, onShippingAddressChange, checkoutFormSlot, }: CheckoutFormPanelProps): import("react/jsx-runtime").JSX.Element | null;
40
+ export declare function CheckoutFormPanel({ formSchema, formComponent: FormComponent, shippingOptions, collectShippingAddress, enableDiscountCode, validateDiscount, tokenSymbol, tokenDecimals, pricesLoading, classes, formData, onFormDataChange, selectedShipping, onShippingChange, appliedDiscount, onDiscountApplied, onDiscountRemoved, shippingAddress, onShippingAddressChange, checkoutFormSlot, }: CheckoutFormPanelProps): import("react/jsx-runtime").JSX.Element | null;
39
41
  export {};
@@ -7,7 +7,7 @@ import { AddressForm } from "./AddressForm.js";
7
7
  import { ShippingSelector } from "./ShippingSelector.js";
8
8
  import { DiscountCodeInput } from "./DiscountCodeInput.js";
9
9
  const emptyAddress = { street: "", city: "", state: "", zip: "", country: "" };
10
- export function CheckoutFormPanel({ formSchema, formComponent: FormComponent, shippingOptions, collectShippingAddress, enableDiscountCode, validateDiscount, tokenSymbol, tokenDecimals, classes, formData, onFormDataChange, selectedShipping, onShippingChange, appliedDiscount, onDiscountApplied, onDiscountRemoved, shippingAddress, onShippingAddressChange, checkoutFormSlot, }) {
10
+ export function CheckoutFormPanel({ formSchema, formComponent: FormComponent, shippingOptions, collectShippingAddress, enableDiscountCode, validateDiscount, tokenSymbol, tokenDecimals, pricesLoading = false, classes, formData, onFormDataChange, selectedShipping, onShippingChange, appliedDiscount, onDiscountApplied, onDiscountRemoved, shippingAddress, onShippingAddressChange, checkoutFormSlot, }) {
11
11
  const [errors, setErrors] = useState({});
12
12
  const hasFormFields = formSchema && formSchema.fields.length > 0;
13
13
  const hasShipping = shippingOptions && shippingOptions.length > 0;
@@ -53,7 +53,7 @@ export function CheckoutFormPanel({ formSchema, formComponent: FormComponent, sh
53
53
  if (!hasAnyContent)
54
54
  return null;
55
55
  // Shared shipping + discount section
56
- const shippingAndDiscount = (_jsxs(_Fragment, { children: [hasShipping && shippingOptions && (_jsx(ShippingSelector, { options: shippingOptions, selectedId: selectedShipping?.id || null, onSelect: onShippingChange, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals })), enableDiscountCode && validateDiscount && (_jsx(DiscountCodeInput, { onApply: handleDiscountApply, appliedDiscount: appliedDiscount, onRemove: onDiscountRemoved, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals }))] }));
56
+ const shippingAndDiscount = (_jsxs(_Fragment, { children: [hasShipping && shippingOptions && (_jsx(ShippingSelector, { options: shippingOptions, selectedId: selectedShipping?.id || null, onSelect: onShippingChange, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, pricesLoading: pricesLoading })), enableDiscountCode && validateDiscount && (_jsx(DiscountCodeInput, { onApply: handleDiscountApply, appliedDiscount: appliedDiscount, onRemove: onDiscountRemoved, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, pricesLoading: pricesLoading }))] }));
57
57
  // Render custom form slot if provided
58
58
  if (checkoutFormSlot) {
59
59
  return (_jsxs("div", { className: cn("anyspend-form-panel space-y-4", classes?.formPanel), children: [_jsx("div", { className: "anyspend-form-slot", children: checkoutFormSlot({
@@ -6,7 +6,9 @@ interface DiscountCodeInputProps {
6
6
  loading?: boolean;
7
7
  tokenSymbol?: string;
8
8
  tokenDecimals?: number;
9
+ /** True while token decimals/symbol are still loading — renders applied amount as skeleton. */
10
+ pricesLoading?: boolean;
9
11
  className?: string;
10
12
  }
11
- export declare function DiscountCodeInput({ onApply, appliedDiscount, onRemove, loading, tokenSymbol, tokenDecimals, className, }: DiscountCodeInputProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function DiscountCodeInput({ onApply, appliedDiscount, onRemove, loading, tokenSymbol, tokenDecimals, pricesLoading, className, }: DiscountCodeInputProps): import("react/jsx-runtime").JSX.Element;
12
14
  export {};
@@ -4,13 +4,14 @@ import { cn } from "../../../../shared/utils/cn.js";
4
4
  import { formatTokenAmount, safeBigInt } from "../../../../shared/utils/number.js";
5
5
  import { X, Loader2, Check } from "lucide-react";
6
6
  import { useState, useCallback } from "react";
7
+ import { PriceSkeleton } from "./PriceSkeleton.js";
7
8
  function formatAmount(amount, decimals, symbol) {
8
9
  const bi = safeBigInt(amount);
9
10
  if (bi === BigInt(0))
10
11
  return "Free";
11
12
  return `${formatTokenAmount(bi, decimals)} ${symbol}`;
12
13
  }
13
- export function DiscountCodeInput({ onApply, appliedDiscount, onRemove, loading = false, tokenSymbol = "", tokenDecimals = 6, className, }) {
14
+ export function DiscountCodeInput({ onApply, appliedDiscount, onRemove, loading = false, tokenSymbol = "", tokenDecimals = 6, pricesLoading = false, className, }) {
14
15
  const [code, setCode] = useState("");
15
16
  const [error, setError] = useState(null);
16
17
  const [isValidating, setIsValidating] = useState(false);
@@ -41,11 +42,7 @@ export function DiscountCodeInput({ onApply, appliedDiscount, onRemove, loading
41
42
  };
42
43
  // Show applied discount state
43
44
  if (appliedDiscount?.valid) {
44
- return (_jsxs("div", { className: cn("anyspend-discount anyspend-discount-applied space-y-2", className), children: [_jsx("div", { className: "anyspend-discount-title text-sm font-semibold text-gray-900 dark:text-gray-100", children: "Discount" }), _jsxs("div", { className: "anyspend-discount-badge flex items-center justify-between rounded-lg border border-green-200 bg-green-50 px-3 py-2 dark:border-green-800 dark:bg-green-900/20", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Check, { className: "h-4 w-4 text-green-600 dark:text-green-400" }), _jsx("span", { className: "anyspend-discount-value text-sm font-medium text-green-700 dark:text-green-300", children: appliedDiscount.discount_type === "percentage"
45
- ? `${appliedDiscount.discount_value}% off`
46
- : appliedDiscount.discount_amount
47
- ? `-${formatAmount(appliedDiscount.discount_amount, tokenDecimals, tokenSymbol)}`
48
- : "Discount applied" })] }), _jsx("button", { type: "button", onClick: handleRemove, className: "anyspend-discount-remove rounded p-1 text-gray-400 hover:bg-gray-200 hover:text-gray-600 dark:hover:bg-gray-700 dark:hover:text-gray-300", children: _jsx(X, { className: "h-3.5 w-3.5" }) })] })] }));
45
+ return (_jsxs("div", { className: cn("anyspend-discount anyspend-discount-applied space-y-2", className), children: [_jsx("div", { className: "anyspend-discount-title text-sm font-semibold text-gray-900 dark:text-gray-100", children: "Discount" }), _jsxs("div", { className: "anyspend-discount-badge flex items-center justify-between rounded-lg border border-green-200 bg-green-50 px-3 py-2 dark:border-green-800 dark:bg-green-900/20", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Check, { className: "h-4 w-4 text-green-600 dark:text-green-400" }), _jsx("span", { className: "anyspend-discount-value text-sm font-medium text-green-700 dark:text-green-300", children: appliedDiscount.discount_type === "percentage" ? (`${appliedDiscount.discount_value}% off`) : appliedDiscount.discount_amount ? (pricesLoading ? (_jsx(PriceSkeleton, {})) : (`-${formatAmount(appliedDiscount.discount_amount, tokenDecimals, tokenSymbol)}`)) : ("Discount applied") })] }), _jsx("button", { type: "button", onClick: handleRemove, className: "anyspend-discount-remove rounded p-1 text-gray-400 hover:bg-gray-200 hover:text-gray-600 dark:hover:bg-gray-700 dark:hover:text-gray-300", children: _jsx(X, { className: "h-3.5 w-3.5" }) })] })] }));
49
46
  }
50
47
  return (_jsxs("div", { className: cn("anyspend-discount space-y-2", className), children: [_jsx("div", { className: "anyspend-discount-title text-sm font-semibold text-gray-900 dark:text-gray-100", children: "Discount Code" }), _jsxs("div", { className: "anyspend-discount-input-row flex gap-2", children: [_jsx("input", { type: "text", value: code, onChange: e => {
51
48
  setCode(e.target.value.toUpperCase());
@@ -0,0 +1,5 @@
1
+ interface PriceSkeletonProps {
2
+ className?: string;
3
+ }
4
+ export declare function PriceSkeleton({ className }: PriceSkeletonProps): import("react/jsx-runtime").JSX.Element;
5
+ export {};
@@ -0,0 +1,6 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { cn } from "../../../../shared/utils/cn.js";
4
+ export function PriceSkeleton({ className }) {
5
+ return (_jsx("span", { className: cn("animate-pulse-fade bg-b3-react-background inline-block h-4 w-16 rounded-md align-middle", className), "aria-hidden": "true" }));
6
+ }
@@ -5,7 +5,9 @@ interface ShippingSelectorProps {
5
5
  onSelect: (option: ShippingOption) => void;
6
6
  tokenSymbol?: string;
7
7
  tokenDecimals?: number;
8
+ /** True while token decimals/symbol are still loading — renders price as skeleton. */
9
+ pricesLoading?: boolean;
8
10
  className?: string;
9
11
  }
10
- export declare function ShippingSelector({ options, selectedId, onSelect, tokenSymbol, tokenDecimals, className, }: ShippingSelectorProps): import("react/jsx-runtime").JSX.Element | null;
12
+ export declare function ShippingSelector({ options, selectedId, onSelect, tokenSymbol, tokenDecimals, pricesLoading, className, }: ShippingSelectorProps): import("react/jsx-runtime").JSX.Element | null;
11
13
  export {};
@@ -2,16 +2,17 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { cn } from "../../../../shared/utils/cn.js";
4
4
  import { formatTokenAmount, safeBigInt } from "../../../../shared/utils/number.js";
5
+ import { PriceSkeleton } from "./PriceSkeleton.js";
5
6
  function formatAmount(amount, decimals, symbol) {
6
7
  const bi = safeBigInt(amount);
7
8
  if (bi === BigInt(0))
8
9
  return "Free";
9
10
  return `${formatTokenAmount(bi, decimals)} ${symbol}`;
10
11
  }
11
- export function ShippingSelector({ options, selectedId, onSelect, tokenSymbol = "", tokenDecimals = 6, className, }) {
12
+ export function ShippingSelector({ options, selectedId, onSelect, tokenSymbol = "", tokenDecimals = 6, pricesLoading = false, className, }) {
12
13
  if (options.length === 0)
13
14
  return null;
14
15
  return (_jsxs("div", { className: cn("anyspend-shipping-selector space-y-2", className), children: [_jsx("div", { className: "anyspend-shipping-title text-sm font-semibold text-gray-900 dark:text-gray-100", children: "Shipping" }), _jsx("div", { className: "anyspend-shipping-options space-y-2", children: options.map(option => (_jsxs("label", { className: cn("anyspend-shipping-option flex cursor-pointer items-center justify-between rounded-lg border p-3 transition-colors", selectedId === option.id
15
16
  ? "anyspend-shipping-option-selected border-blue-500 bg-blue-50 dark:border-blue-400 dark:bg-blue-900/20"
16
- : "border-gray-200 bg-white hover:border-gray-300 dark:border-neutral-600 dark:bg-neutral-800 dark:hover:border-neutral-500"), children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("input", { type: "radio", name: "shipping", checked: selectedId === option.id, onChange: () => onSelect(option), className: "h-4 w-4 border-gray-300 text-blue-500 focus:ring-blue-500" }), _jsxs("div", { children: [_jsx("div", { className: "anyspend-shipping-option-name text-sm font-medium text-gray-900 dark:text-gray-100", children: option.name }), (option.description || option.estimated_days) && (_jsx("div", { className: "anyspend-shipping-option-detail text-xs text-gray-500 dark:text-gray-400", children: option.description || (option.estimated_days && `${option.estimated_days}`) }))] })] }), _jsx("div", { className: "anyspend-shipping-option-price text-sm font-medium text-gray-900 dark:text-gray-100", children: formatAmount(option.amount, tokenDecimals, tokenSymbol) })] }, option.id))) })] }));
17
+ : "border-gray-200 bg-white hover:border-gray-300 dark:border-neutral-600 dark:bg-neutral-800 dark:hover:border-neutral-500"), children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("input", { type: "radio", name: "shipping", checked: selectedId === option.id, onChange: () => onSelect(option), className: "h-4 w-4 border-gray-300 text-blue-500 focus:ring-blue-500" }), _jsxs("div", { children: [_jsx("div", { className: "anyspend-shipping-option-name text-sm font-medium text-gray-900 dark:text-gray-100", children: option.name }), (option.description || option.estimated_days) && (_jsx("div", { className: "anyspend-shipping-option-detail text-xs text-gray-500 dark:text-gray-400", children: option.description || (option.estimated_days && `${option.estimated_days}`) }))] })] }), _jsx("div", { className: "anyspend-shipping-option-price text-sm font-medium text-gray-900 dark:text-gray-100", children: pricesLoading ? _jsx(PriceSkeleton, {}) : formatAmount(option.amount, tokenDecimals, tokenSymbol) })] }, option.id))) })] }));
17
18
  }
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button, Input } from "../../../../global-account/react/index.js";
2
+ import { Button } from "../../../../global-account/react/index.js";
3
3
  import { debugB3React } from "../../../../shared/utils/debug.js";
4
4
  import { useState } from "react";
5
5
  import { useBetterAuth } from "../../hooks/useBetterAuth.js";
6
+ import { PasswordInput } from "./components/PasswordInput.js";
6
7
  const debug = debugB3React("BetterAuthResetPassword");
7
8
  /**
8
9
  * Standalone reset password form. Render this on your reset password page.
@@ -57,8 +58,8 @@ export function BetterAuthResetPassword({ token, onSuccess, onError, className }
57
58
  if (!token) {
58
59
  return (_jsx("div", { className: `w-full max-w-[400px] px-6 text-center ${className || ""}`, children: _jsx("p", { className: "text-sm text-red-500", children: "Invalid or missing reset token." }) }));
59
60
  }
60
- return (_jsxs("div", { className: `w-full max-w-[400px] px-6 ${className || ""}`, children: [_jsxs("div", { className: "mb-10 text-center", children: [_jsx("h1", { className: "text-[28px] font-semibold tracking-tight text-gray-900 dark:text-gray-100", children: success ? "Password reset" : "Set new password" }), _jsx("p", { className: "mt-3 text-[15px] text-gray-500 dark:text-gray-400", children: success ? "Your password has been updated." : "Enter your new password below." })] }), success ? (_jsx("div", { className: "space-y-4 text-center", children: _jsx("div", { className: "mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100", children: _jsx("svg", { className: "h-6 w-6 text-green-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) }) }) })) : (_jsxs("div", { className: "space-y-5", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-xs font-medium uppercase tracking-wide text-gray-700 dark:text-gray-300", children: "New password" }), _jsx(Input, { type: "password", placeholder: "At least 8 characters", value: password, onChange: e => setPassword(e.target.value), disabled: isLoading, className: "h-11 px-4 text-[15px]" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-xs font-medium uppercase tracking-wide text-gray-700 dark:text-gray-300", children: "Confirm password" }), _jsx(Input, { type: "password", placeholder: "Repeat your password", value: confirmPassword, onChange: e => setConfirmPassword(e.target.value), disabled: isLoading, onKeyDown: e => {
61
+ return (_jsxs("div", { className: `w-full max-w-[400px] px-6 ${className || ""}`, children: [_jsxs("div", { className: "mb-10 text-center", children: [_jsx("h1", { className: "text-[28px] font-semibold tracking-tight text-gray-900 dark:text-gray-100", children: success ? "Password reset" : "Set new password" }), _jsx("p", { className: "mt-3 text-[15px] text-gray-500 dark:text-gray-400", children: success ? "Your password has been updated." : "Enter your new password below." })] }), success ? (_jsx("div", { className: "space-y-4 text-center", children: _jsx("div", { className: "mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100", children: _jsx("svg", { className: "h-6 w-6 text-green-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) }) }) })) : (_jsxs("div", { className: "space-y-5", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-xs font-medium uppercase tracking-wide text-gray-700 dark:text-gray-300", children: "New password" }), _jsx(PasswordInput, { placeholder: "At least 8 characters", value: password, onChange: e => setPassword(e.target.value), disabled: isLoading, className: "h-11 px-4 pr-11 text-[15px]" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-xs font-medium uppercase tracking-wide text-gray-700 dark:text-gray-300", children: "Confirm password" }), _jsx(PasswordInput, { placeholder: "Repeat your password", value: confirmPassword, onChange: e => setConfirmPassword(e.target.value), disabled: isLoading, onKeyDown: e => {
61
62
  if (e.key === "Enter")
62
63
  handleSubmit();
63
- }, className: "h-11 px-4 text-[15px]" })] }), error && _jsx("p", { className: "text-sm text-red-500", children: error }), _jsx(Button, { onClick: handleSubmit, disabled: isLoading, className: "h-11 w-full bg-gray-900 text-[15px] font-medium text-white hover:bg-gray-800 dark:bg-white dark:text-gray-900 dark:hover:bg-gray-100", children: isLoading ? "Resetting..." : "Reset password" })] }))] }));
64
+ }, className: "h-11 px-4 pr-11 text-[15px]" })] }), error && _jsx("p", { className: "text-sm text-red-500", children: error }), _jsx(Button, { onClick: handleSubmit, disabled: isLoading, className: "h-11 w-full bg-gray-900 text-[15px] font-medium text-white hover:bg-gray-800 dark:bg-white dark:text-gray-900 dark:hover:bg-gray-100", children: isLoading ? "Resetting..." : "Reset password" })] }))] }));
64
65
  }
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Button, Input, useAuthStore } from "../../../../global-account/react/index.js";
2
+ import { Button, Input, Loading, useAuthStore } from "../../../../global-account/react/index.js";
3
3
  import { debugB3React } from "../../../../shared/utils/debug.js";
4
4
  import { useState } from "react";
5
5
  import { EmailVerificationRequiredError, useBetterAuth, } from "../../hooks/useBetterAuth.js";
6
+ import { PasswordInput } from "./components/PasswordInput.js";
6
7
  import { strategyIcons, strategyLabels } from "./utils/signInUtils.js";
7
8
  const debug = debugB3React("BetterAuthSignIn");
8
9
  const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@@ -30,6 +31,8 @@ const DEFAULT_SOCIAL_PROVIDERS = [
30
31
  export function BetterAuthSignIn({ title, subtitle = "Enter your credentials to access your account", socialProviders = DEFAULT_SOCIAL_PROVIDERS.map(p => p.id), showEmail = true, passwordResetRedirectTo, onSuccess, onError, className, }) {
31
32
  const { signInWithEmail, signUpWithEmail, signInWithSocial, requestPasswordReset } = useBetterAuth();
32
33
  const isAuthenticated = useAuthStore(state => state.isAuthenticated);
34
+ const isAuthenticating = useAuthStore(state => state.isAuthenticating);
35
+ const hasStartedConnecting = useAuthStore(state => state.hasStartedConnecting);
33
36
  const [mode, setMode] = useState("sign-in");
34
37
  const [email, setEmail] = useState("");
35
38
  const [password, setPassword] = useState("");
@@ -42,7 +45,9 @@ export function BetterAuthSignIn({ title, subtitle = "Enter your credentials to
42
45
  const resolvedTitle = title ||
43
46
  (mode === "forgot-password" ? "Reset password" : mode === "sign-in" ? "Welcome back" : "Create an account");
44
47
  const resolvedSubtitle = mode === "forgot-password"
45
- ? "Enter your email and we'll send you a reset link"
48
+ ? resetEmailSent
49
+ ? "We've sent a password reset link to your email"
50
+ : "Enter your email and we'll send you a reset link"
46
51
  : subtitle || "Enter your credentials to access your account";
47
52
  const handleForgotPassword = async () => {
48
53
  const normalizedEmail = email.trim().toLowerCase();
@@ -67,6 +72,11 @@ export function BetterAuthSignIn({ title, subtitle = "Enter your credentials to
67
72
  const providers = socialProviders
68
73
  .map(id => DEFAULT_SOCIAL_PROVIDERS.find(p => p.id === id))
69
74
  .filter((p) => !!p);
75
+ // Show loading during session restore (before any user interaction) to prevent
76
+ // the login form from flashing briefly after OAuth redirect.
77
+ if (isAuthenticating && !hasStartedConnecting) {
78
+ return (_jsx("div", { className: `flex w-full max-w-[400px] items-center justify-center px-6 py-20 ${className || ""}`, children: _jsx(Loading, { variant: "primary", size: "lg" }) }));
79
+ }
70
80
  if (isAuthenticated) {
71
81
  return null;
72
82
  }
@@ -145,10 +155,10 @@ export function BetterAuthSignIn({ title, subtitle = "Enter your credentials to
145
155
  }, className: "font-medium text-blue-600 hover:text-blue-500 dark:text-blue-400", children: "Back to sign in" }) })] })), showEmail && mode !== "forgot-password" && (_jsxs("div", { className: "space-y-5", children: [mode === "sign-up" && (_jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-xs font-medium uppercase tracking-wide text-gray-700 dark:text-gray-300", children: "Name" }), _jsx(Input, { type: "text", placeholder: "Your name", value: name, onChange: e => setName(e.target.value), disabled: isLoading, className: "h-11 px-4 text-[15px]" })] })), _jsxs("div", { children: [_jsx("label", { className: "mb-2 block text-xs font-medium uppercase tracking-wide text-gray-700 dark:text-gray-300", children: "Email" }), _jsx(Input, { type: "email", placeholder: "name@company.com", value: email, onChange: e => setEmail(e.target.value), disabled: isLoading, className: "h-11 px-4 text-[15px]" })] }), _jsxs("div", { children: [_jsxs("div", { className: "mb-2 flex items-center justify-between", children: [_jsx("label", { className: "block text-xs font-medium uppercase tracking-wide text-gray-700 dark:text-gray-300", children: "Password" }), mode === "sign-in" && (_jsx("button", { type: "button", onClick: () => {
146
156
  setMode("forgot-password");
147
157
  setError(null);
148
- }, className: "text-xs font-medium text-blue-600 hover:text-blue-500 dark:text-blue-400", children: "Forgot password?" }))] }), _jsx(Input, { type: "password", placeholder: "Password", value: password, onChange: e => setPassword(e.target.value), disabled: isLoading, onKeyDown: e => {
158
+ }, className: "text-xs font-medium text-blue-600 hover:text-blue-500 dark:text-blue-400", children: "Forgot password?" }))] }), _jsx(PasswordInput, { value: password, onChange: e => setPassword(e.target.value), disabled: isLoading, onKeyDown: e => {
149
159
  if (e.key === "Enter")
150
160
  handleEmailSubmit();
151
- }, className: "h-11 px-4 text-[15px]" })] }), error && _jsx("p", { className: "text-sm text-red-500", children: error }), _jsx(Button, { onClick: handleEmailSubmit, disabled: isLoading, className: "h-11 w-full bg-gray-900 text-[15px] font-medium text-white hover:bg-gray-800 dark:bg-white dark:text-gray-900 dark:hover:bg-gray-100", children: isLoading ? "Loading..." : mode === "sign-in" ? "Sign in" : "Sign up" })] })), providers.length > 0 && mode !== "forgot-password" && (_jsxs(_Fragment, { children: [showEmail && (_jsxs("div", { className: "my-8 flex items-center gap-4", children: [_jsx("div", { className: "h-px flex-1 bg-gray-200 dark:bg-gray-700" }), _jsx("span", { className: "text-xs text-gray-400", children: "Or continue with" }), _jsx("div", { className: "h-px flex-1 bg-gray-200 dark:bg-gray-700" })] })), _jsx("div", { className: "space-y-4", children: providers.map(provider => {
161
+ }, className: "h-11 px-4 pr-11 text-[15px]" }, mode)] }), error && _jsx("p", { className: "text-sm text-red-500", children: error }), _jsx(Button, { onClick: handleEmailSubmit, disabled: isLoading, className: "h-11 w-full bg-gray-900 text-[15px] font-medium text-white hover:bg-gray-800 dark:bg-white dark:text-gray-900 dark:hover:bg-gray-100", children: isLoading ? "Loading..." : mode === "sign-in" ? "Sign in" : "Sign up" })] })), providers.length > 0 && mode !== "forgot-password" && (_jsxs(_Fragment, { children: [showEmail && (_jsxs("div", { className: "my-8 flex items-center gap-4", children: [_jsx("div", { className: "h-px flex-1 bg-gray-200 dark:bg-gray-700" }), _jsx("span", { className: "text-xs text-gray-400", children: "Or continue with" }), _jsx("div", { className: "h-px flex-1 bg-gray-200 dark:bg-gray-700" })] })), _jsx("div", { className: "space-y-4", children: providers.map(provider => {
152
162
  const icon = strategyIcons[provider.id];
153
163
  const label = strategyLabels[provider.id] || provider.label;
154
164
  const isProviderLoading = loadingProvider === provider.id;
@@ -0,0 +1,10 @@
1
+ interface PasswordInputProps {
2
+ value: string;
3
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
4
+ disabled?: boolean;
5
+ placeholder?: string;
6
+ onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
7
+ className?: string;
8
+ }
9
+ export declare function PasswordInput({ value, onChange, disabled, placeholder, onKeyDown, className, }: PasswordInputProps): import("react/jsx-runtime").JSX.Element;
10
+ export {};
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Input } from "../../../../../global-account/react/index.js";
3
+ import { useState } from "react";
4
+ export function PasswordInput({ value, onChange, disabled, placeholder = "Password", onKeyDown, className, }) {
5
+ const [showPassword, setShowPassword] = useState(false);
6
+ return (_jsxs("div", { className: "relative", children: [_jsx(Input, { type: showPassword ? "text" : "password", placeholder: placeholder, value: value, onChange: onChange, disabled: disabled, onKeyDown: onKeyDown, className: className }), _jsx("button", { type: "button", onClick: () => setShowPassword(!showPassword), "aria-label": showPassword ? "Hide password" : "Show password", className: "absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300", tabIndex: -1, children: showPassword ? (_jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88" }) })) : (_jsxs("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: [_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z" })] })) })] }));
7
+ }
@@ -4,6 +4,7 @@ import { debugB3React } from "../../../../../shared/utils/debug.js";
4
4
  import { useState } from "react";
5
5
  import { EmailVerificationRequiredError, useBetterAuth, } from "../../../hooks/useBetterAuth.js";
6
6
  import { AuthButton } from "../components/AuthButton.js";
7
+ import { PasswordInput } from "../components/PasswordInput.js";
7
8
  const debug = debugB3React("LoginStepBetterAuth");
8
9
  const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
9
10
  const SOCIAL_PROVIDERS = [
@@ -111,7 +112,9 @@ export function LoginStepBetterAuth({ onSuccess, onError }) {
111
112
  setPassword("");
112
113
  }, className: "text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300", children: "Back to sign in" })] }) }));
113
114
  }
114
- return (_jsx(LoginStepContainer, { partnerId: partnerId, children: mode === "forgot-password" ? (_jsxs("div", { className: "mb-6 w-full space-y-3 px-3", children: [_jsx("p", { className: "text-center text-sm font-medium text-gray-900 dark:text-gray-100", children: "Reset password" }), _jsx("p", { className: "text-center text-xs text-gray-500", children: "Enter your email and we'll send you a reset link" }), resetEmailSent ? (_jsx("div", { className: "space-y-3 py-4 text-center", children: _jsx("p", { className: "text-sm text-green-600", children: "Check your email for a reset link." }) })) : (_jsxs(_Fragment, { children: [_jsx(Input, { type: "email", placeholder: "you@example.com", value: email, onChange: event => setEmail(event.target.value), disabled: isLoading, onKeyDown: event => {
115
+ return (_jsx(LoginStepContainer, { partnerId: partnerId, children: mode === "forgot-password" ? (_jsxs("div", { className: "mb-6 w-full space-y-3 px-3", children: [_jsx("p", { className: "text-center text-sm font-medium text-gray-900 dark:text-gray-100", children: "Reset password" }), _jsx("p", { className: "text-center text-xs text-gray-500", children: resetEmailSent
116
+ ? "We've sent a password reset link to your email"
117
+ : "Enter your email and we'll send you a reset link" }), resetEmailSent ? (_jsx("div", { className: "space-y-3 py-4 text-center", children: _jsx("p", { className: "text-sm text-green-600", children: "Check your email for a reset link." }) })) : (_jsxs(_Fragment, { children: [_jsx(Input, { type: "email", placeholder: "you@example.com", value: email, onChange: event => setEmail(event.target.value), disabled: isLoading, onKeyDown: event => {
115
118
  if (event.key === "Enter")
116
119
  handleForgotPassword();
117
120
  } }), error && _jsx("p", { className: "text-sm text-red-500", children: error }), _jsx(Button, { onClick: handleForgotPassword, disabled: isLoading, className: "w-full", children: isLoading ? "Sending..." : "Send reset link" })] })), _jsx("button", { onClick: () => {
@@ -119,10 +122,10 @@ export function LoginStepBetterAuth({ onSuccess, onError }) {
119
122
  setShowEmailForm(true);
120
123
  setError(null);
121
124
  setResetEmailSent(false);
122
- }, className: "w-full text-center text-sm text-gray-500 hover:text-gray-700", children: "Back to sign in" })] })) : showEmailForm ? (_jsxs("div", { className: "mb-6 w-full space-y-3 px-3", children: [_jsx("p", { className: "text-center text-sm font-medium text-gray-900 dark:text-gray-100", children: mode === "sign-in" ? "Sign in with email" : "Create an account" }), mode === "sign-up" && (_jsx(Input, { type: "text", placeholder: "Your name", value: name, onChange: event => setName(event.target.value), disabled: isLoading })), _jsx(Input, { type: "email", placeholder: "you@example.com", value: email, onChange: event => setEmail(event.target.value), disabled: isLoading }), _jsx(Input, { type: "password", placeholder: "Password", value: password, onChange: event => setPassword(event.target.value), disabled: isLoading, onKeyDown: event => {
125
+ }, className: "w-full text-center text-sm text-gray-500 hover:text-gray-700", children: "Back to sign in" })] })) : showEmailForm ? (_jsxs("div", { className: "mb-6 w-full space-y-3 px-3", children: [_jsx("p", { className: "text-center text-sm font-medium text-gray-900 dark:text-gray-100", children: mode === "sign-in" ? "Sign in with email" : "Create an account" }), mode === "sign-up" && (_jsx(Input, { type: "text", placeholder: "Your name", value: name, onChange: event => setName(event.target.value), disabled: isLoading })), _jsx(Input, { type: "email", placeholder: "you@example.com", value: email, onChange: event => setEmail(event.target.value), disabled: isLoading }), _jsx(PasswordInput, { value: password, onChange: event => setPassword(event.target.value), disabled: isLoading, onKeyDown: event => {
123
126
  if (event.key === "Enter")
124
127
  handleEmailSubmit();
125
- } }), error && _jsx("p", { className: "text-sm text-red-500", children: error }), _jsx(Button, { onClick: handleEmailSubmit, disabled: isLoading, className: "w-full", children: isLoading ? "Loading..." : mode === "sign-in" ? "Sign in" : "Sign up" }), mode === "sign-in" && (_jsx("button", { onClick: () => {
128
+ }, className: "pr-11" }, mode), error && _jsx("p", { className: "text-sm text-red-500", children: error }), _jsx(Button, { onClick: handleEmailSubmit, disabled: isLoading, className: "w-full", children: isLoading ? "Loading..." : mode === "sign-in" ? "Sign in" : "Sign up" }), mode === "sign-in" && (_jsx("button", { onClick: () => {
126
129
  setMode("forgot-password");
127
130
  setError(null);
128
131
  }, disabled: isLoading, className: "w-full text-center text-xs text-gray-500 hover:text-gray-700", children: "Forgot password?" })), _jsx("button", { onClick: () => {