@burdenoff/microfe-billing 2026.614.1 → 2026.623.2
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/billing/BillingAdminRoutes.js +7 -6
- package/dist/billing/BillingAdminRoutes.js.map +1 -1
- package/dist/billing/BillingRoot.js +1 -1
- package/dist/billing/BillingRoot.js.map +1 -1
- package/dist/billing/modules/addons/components/FloatingCartDrawer.js +22 -22
- package/dist/billing/modules/addons/components/FloatingCartDrawer.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js +31 -31
- package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonDetailPage.js +39 -39
- package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonEditPage.js +29 -29
- package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js +91 -91
- package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js +40 -40
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +115 -115
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +75 -72
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js +126 -126
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/UserRefundsPage.js +45 -45
- package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js +25 -25
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js +31 -35
- package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/checkout/components/CreditsAmountInput.js +9 -9
- package/dist/billing/modules/checkout/components/CreditsAmountInput.js.map +1 -1
- package/dist/billing/modules/checkout/components/OrderSummary.js +31 -31
- package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
- package/dist/billing/modules/checkout/components/PromoCodeInput.js +8 -8
- package/dist/billing/modules/checkout/components/PromoCodeInput.js.map +1 -1
- package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js +5 -5
- package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
- package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js +9 -9
- package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js.map +1 -1
- package/dist/billing/modules/checkout/components/StripePaymentForm.js +21 -21
- package/dist/billing/modules/checkout/components/StripePaymentForm.js.map +1 -1
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js +11 -11
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js +31 -31
- package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js +23 -23
- package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.js +91 -91
- package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js +62 -62
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponsListPage.js +72 -60
- package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js +66 -66
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/EditCouponPage.js +46 -46
- package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js +19 -19
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +37 -37
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +90 -90
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditsPage.js +41 -41
- package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js +31 -31
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js +35 -35
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js +24 -24
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js +99 -99
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +23 -23
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +22 -22
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js +58 -58
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js +126 -126
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +83 -79
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +16 -16
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +40 -40
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js +35 -35
- package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanDetailPage.js +57 -57
- package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanEditPage.js +35 -35
- package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js +57 -57
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansListPage.js +52 -52
- package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +40 -39
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js +22 -19
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +34 -34
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +47 -47
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +39 -39
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +52 -52
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js +23 -23
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +259 -246
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +88 -85
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +51 -51
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
- package/dist/billing/modules/usage/pages/UsagePage.js +206 -196
- package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
- package/dist/billing/shared/components/ServerError.js +4 -4
- package/dist/billing/shared/components/ServerError.js.map +1 -1
- package/dist/billing/shared/components/StatCard.js +11 -33
- package/dist/billing/shared/components/StatCard.js.map +1 -1
- package/dist/billing/shared/ui/Card.js +15 -0
- package/dist/billing/shared/ui/Card.js.map +1 -0
- package/dist/billing/shared/ui/MetricCard.js +44 -0
- package/dist/billing/shared/ui/MetricCard.js.map +1 -0
- package/dist/billing/shared/ui/ProgressBar.js +30 -0
- package/dist/billing/shared/ui/ProgressBar.js.map +1 -0
- package/dist/billing/shared/ui/SectionHeader.js +2 -0
- package/dist/billing/shared/ui/StatusPill.js +28 -0
- package/dist/billing/shared/ui/StatusPill.js.map +1 -0
- package/dist/billing/shared/ui/index.js +5 -0
- package/dist/billing/shared/utils/tokens.js +13 -30
- package/dist/billing/shared/utils/tokens.js.map +1 -1
- package/package.json +1 -1
|
@@ -133,15 +133,15 @@ var h = () => new Promise((e, t) => {
|
|
|
133
133
|
children: [
|
|
134
134
|
/* @__PURE__ */ p("div", {
|
|
135
135
|
className: "flex items-center gap-2",
|
|
136
|
-
children: [/* @__PURE__ */ f(u, { className: "size-5 text-
|
|
137
|
-
className: "font-medium text-
|
|
136
|
+
children: [/* @__PURE__ */ f(u, { className: "size-5 text-text-link" }), /* @__PURE__ */ f("span", {
|
|
137
|
+
className: "font-medium text-text-primary",
|
|
138
138
|
children: "Secure Payment Verification"
|
|
139
139
|
})]
|
|
140
140
|
}),
|
|
141
141
|
D === "processing" && /* @__PURE__ */ p("div", {
|
|
142
|
-
className: "p-6 rounded-lg border border-border bg-
|
|
143
|
-
children: [/* @__PURE__ */ f(l, { className: "size-8 animate-spin text-
|
|
144
|
-
className: "text-sm text-
|
|
142
|
+
className: "p-6 rounded-lg border border-border-subtle bg-bg-sunken flex flex-col items-center justify-center space-y-3",
|
|
143
|
+
children: [/* @__PURE__ */ f(l, { className: "size-8 animate-spin text-text-link" }), /* @__PURE__ */ p("p", {
|
|
144
|
+
className: "text-sm text-text-secondary text-center",
|
|
145
145
|
children: [
|
|
146
146
|
"Confirming payment with your saved card…",
|
|
147
147
|
/* @__PURE__ */ f("br", {}),
|
|
@@ -153,7 +153,7 @@ var h = () => new Promise((e, t) => {
|
|
|
153
153
|
})]
|
|
154
154
|
}),
|
|
155
155
|
k && /* @__PURE__ */ p("div", {
|
|
156
|
-
className: "flex items-center gap-2 p-3 rounded-lg bg-
|
|
156
|
+
className: "flex items-center gap-2 p-3 rounded-lg bg-status-error-bg-subtle text-status-error-text text-sm",
|
|
157
157
|
children: [/* @__PURE__ */ f(o, { className: "size-4 shrink-0" }), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("p", {
|
|
158
158
|
className: "font-medium",
|
|
159
159
|
children: E("billing.checkout.paymentFailed", "Payment Failed")
|
|
@@ -167,7 +167,7 @@ var h = () => new Promise((e, t) => {
|
|
|
167
167
|
children: /* @__PURE__ */ f("button", {
|
|
168
168
|
type: "button",
|
|
169
169
|
onClick: S,
|
|
170
|
-
className: "flex-1 px-4 py-3 rounded-lg border border-border text-
|
|
170
|
+
className: "flex-1 px-4 py-3 rounded-lg border border-border-subtle text-text-primary font-medium\n hover:bg-bg-sunken transition-colors",
|
|
171
171
|
children: "Back to Checkout"
|
|
172
172
|
})
|
|
173
173
|
})
|
|
@@ -180,12 +180,12 @@ var h = () => new Promise((e, t) => {
|
|
|
180
180
|
className: "flex items-center justify-between",
|
|
181
181
|
children: [/* @__PURE__ */ p("div", {
|
|
182
182
|
className: "flex items-center gap-2",
|
|
183
|
-
children: [/* @__PURE__ */ f(c, { className: "size-5 text-
|
|
184
|
-
className: "font-medium text-
|
|
183
|
+
children: [/* @__PURE__ */ f(c, { className: "size-5 text-text-secondary" }), /* @__PURE__ */ f("span", {
|
|
184
|
+
className: "font-medium text-text-primary",
|
|
185
185
|
children: "Card Payment"
|
|
186
186
|
})]
|
|
187
187
|
}), /* @__PURE__ */ p("div", {
|
|
188
|
-
className: "flex items-center gap-1 text-xs text-
|
|
188
|
+
className: "flex items-center gap-1 text-xs text-text-secondary",
|
|
189
189
|
children: [/* @__PURE__ */ f(u, { className: "size-3" }), "Secured by Stripe"]
|
|
190
190
|
})]
|
|
191
191
|
}),
|
|
@@ -193,14 +193,14 @@ var h = () => new Promise((e, t) => {
|
|
|
193
193
|
className: "space-y-2",
|
|
194
194
|
children: [/* @__PURE__ */ f("div", {
|
|
195
195
|
ref: L,
|
|
196
|
-
className: "min-h-[50px] rounded-lg border border-border bg-
|
|
196
|
+
className: "min-h-[50px] rounded-lg border border-border-subtle bg-bg-surface p-4"
|
|
197
197
|
}), F && /* @__PURE__ */ f("p", {
|
|
198
|
-
className: "text-sm text-
|
|
198
|
+
className: "text-sm text-status-error-text",
|
|
199
199
|
children: F
|
|
200
200
|
})]
|
|
201
201
|
}),
|
|
202
202
|
k && /* @__PURE__ */ p("div", {
|
|
203
|
-
className: "flex items-center gap-2 p-3 rounded-lg bg-
|
|
203
|
+
className: "flex items-center gap-2 p-3 rounded-lg bg-status-error-bg-subtle text-status-error-text text-sm",
|
|
204
204
|
children: [/* @__PURE__ */ f(o, { className: "size-4 shrink-0" }), k]
|
|
205
205
|
}),
|
|
206
206
|
/* @__PURE__ */ p("div", {
|
|
@@ -209,12 +209,12 @@ var h = () => new Promise((e, t) => {
|
|
|
209
209
|
type: "button",
|
|
210
210
|
onClick: S,
|
|
211
211
|
disabled: D === "processing" || C,
|
|
212
|
-
className: "flex-1 px-4 py-3 rounded-lg border border-border text-
|
|
212
|
+
className: "flex-1 px-4 py-3 rounded-lg border border-border-subtle text-text-primary font-medium\n hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
|
|
213
213
|
children: "Cancel"
|
|
214
214
|
}), /* @__PURE__ */ f("button", {
|
|
215
215
|
type: "submit",
|
|
216
216
|
disabled: D === "processing" || C || !N,
|
|
217
|
-
className: "flex-1 px-4 py-3 rounded-lg bg-primary text-primary-
|
|
217
|
+
className: "flex-1 px-4 py-3 rounded-lg bg-action-primary-bg text-action-primary-text font-medium\n hover:bg-action-primary-bgHover disabled:opacity-50 disabled:cursor-not-allowed transition-colors\n flex items-center justify-center gap-2",
|
|
218
218
|
children: D === "processing" ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(l, { className: "size-4 animate-spin" }), "Processing…"] }) : /* @__PURE__ */ p(d, { children: [
|
|
219
219
|
"Pay",
|
|
220
220
|
" ",
|
|
@@ -226,7 +226,7 @@ var h = () => new Promise((e, t) => {
|
|
|
226
226
|
})]
|
|
227
227
|
}),
|
|
228
228
|
/* @__PURE__ */ f("p", {
|
|
229
|
-
className: "text-center text-xs text-
|
|
229
|
+
className: "text-center text-xs text-text-secondary",
|
|
230
230
|
children: "Your payment information is encrypted and secure. We never store your full card details."
|
|
231
231
|
})
|
|
232
232
|
]
|
|
@@ -234,14 +234,14 @@ var h = () => new Promise((e, t) => {
|
|
|
234
234
|
className: "space-y-4",
|
|
235
235
|
children: [/* @__PURE__ */ p("div", {
|
|
236
236
|
className: "flex items-center gap-2",
|
|
237
|
-
children: [/* @__PURE__ */ f(c, { className: "size-5 text-
|
|
238
|
-
className: "font-medium text-
|
|
237
|
+
children: [/* @__PURE__ */ f(c, { className: "size-5 text-text-secondary" }), /* @__PURE__ */ f("span", {
|
|
238
|
+
className: "font-medium text-text-primary",
|
|
239
239
|
children: E("billing.checkout.cardPayment", "Card Payment")
|
|
240
240
|
})]
|
|
241
241
|
}), /* @__PURE__ */ p("div", {
|
|
242
|
-
className: "p-6 rounded-lg border border-border bg-
|
|
243
|
-
children: [/* @__PURE__ */ f(l, { className: "size-6 animate-spin text-
|
|
244
|
-
className: "ml-2 text-
|
|
242
|
+
className: "p-6 rounded-lg border border-border-subtle bg-bg-sunken flex items-center justify-center",
|
|
243
|
+
children: [/* @__PURE__ */ f(l, { className: "size-6 animate-spin text-text-secondary" }), /* @__PURE__ */ f("span", {
|
|
244
|
+
className: "ml-2 text-text-secondary",
|
|
245
245
|
children: E("billing.checkout.loadingPaymentForm", "Loading payment form...")
|
|
246
246
|
})]
|
|
247
247
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StripePaymentForm.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/StripePaymentForm.tsx"],"sourcesContent":["/**\n * Stripe Payment Form Component\n * Integrates with Stripe Elements for card payment\n */\n\nimport { type FC, useState, useCallback, useEffect, useRef } from 'react';\nimport { CreditCard, Lock, Loader2, Check, AlertCircle } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { getStripeCardElementStyle, statusTokens } from '../../../shared/utils';\n\n// Stripe types - local definitions to avoid conflicts with external type declarations\ninterface LocalStripeCardElement {\n mount: (element: HTMLElement) => void;\n unmount: () => void;\n destroy: () => void;\n on: (event: string, handler: (event: LocalStripeCardChangeEvent) => void) => void;\n}\n\ninterface LocalStripeCardChangeEvent {\n complete: boolean;\n error?: { message: string };\n}\n\ninterface LocalStripeElements {\n create: (type: 'card', options?: Record<string, unknown>) => LocalStripeCardElement;\n}\n\ninterface LocalStripePaymentMethod {\n id: string;\n card?: {\n last4: string;\n brand: string;\n };\n}\n\ninterface LocalStripeInstance {\n elements: () => LocalStripeElements;\n createPaymentMethod: (options: {\n type: 'card';\n card: LocalStripeCardElement;\n }) => Promise<{ paymentMethod?: LocalStripePaymentMethod; error?: { message: string } }>;\n confirmCardPayment: (\n clientSecret: string,\n options?: { payment_method: string }\n ) => Promise<{\n paymentIntent?: { id: string; status: string };\n error?: { message: string };\n }>;\n}\n\n// Use type assertion when accessing window.Stripe\ntype StripeFactory = (key: string) => LocalStripeInstance;\n\ninterface StripePaymentFormProps {\n clientSecret: string;\n publishableKey: string;\n amount: number;\n currency: string;\n onSuccess: (paymentIntentId: string) => void;\n onError: (error: string) => void;\n onCancel?: () => void;\n disabled?: boolean;\n isCompletingExistingPayment?: boolean; // True for saved cards that need 3D Secure\n}\n\ntype PaymentState = 'idle' | 'loading' | 'processing' | 'success' | 'error';\n\n/**\n * Load Stripe.js SDK dynamically\n */\nconst loadStripeSDK = (): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (window.Stripe) {\n resolve();\n return;\n }\n\n const existingScript = document.querySelector('script[src=\"https://js.stripe.com/v3/\"]');\n if (existingScript) {\n // Wait for existing script to load\n const checkStripe = setInterval(() => {\n if (window.Stripe) {\n clearInterval(checkStripe);\n resolve();\n }\n }, 100);\n return;\n }\n\n const script = document.createElement('script');\n script.src = 'https://js.stripe.com/v3/';\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error('Failed to load Stripe SDK'));\n document.body.appendChild(script);\n });\n};\n\nexport const StripePaymentForm: FC<StripePaymentFormProps> = ({\n clientSecret,\n publishableKey,\n amount,\n currency,\n onSuccess,\n onError,\n onCancel,\n disabled = false,\n isCompletingExistingPayment = false,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const [paymentState, setPaymentState] = useState<PaymentState>('idle');\n const [error, setError] = useState<string | null>(null);\n const [sdkLoaded, setSdkLoaded] = useState(false);\n const [cardComplete, setCardComplete] = useState(false);\n const [cardError, setCardError] = useState<string | null>(null);\n\n const cardElementRef = useRef<HTMLDivElement>(null);\n const stripeRef = useRef<LocalStripeInstance | null>(null);\n const cardElementInstanceRef = useRef<LocalStripeCardElement | null>(null);\n const isMountedRef = useRef(false);\n\n // Load Stripe SDK\n useEffect(() => {\n loadStripeSDK()\n .then(() => {\n setSdkLoaded(true);\n })\n .catch(() => {\n setError('Failed to load payment gateway');\n });\n }, []);\n\n // Initialize Stripe and mount card element (only for new card payments)\n useEffect(() => {\n // Skip card input for completing existing payments (saved cards with 3D Secure)\n if (isCompletingExistingPayment) {\n return;\n }\n\n const windowStripe = (window as unknown as { Stripe?: StripeFactory }).Stripe;\n if (!sdkLoaded || !publishableKey || !windowStripe || isMountedRef.current) {\n return;\n }\n\n // Initialize Stripe\n const stripeInstance = windowStripe(publishableKey);\n stripeRef.current = stripeInstance;\n\n // Create elements\n const elements = stripeInstance.elements();\n const cardElement = elements.create('card', {\n style: getStripeCardElementStyle(),\n });\n cardElementInstanceRef.current = cardElement;\n\n // Mount card element\n if (cardElementRef.current) {\n cardElement.mount(cardElementRef.current);\n isMountedRef.current = true;\n\n // Listen to card element changes\n cardElement.on('change', (event: LocalStripeCardChangeEvent) => {\n setCardError(event.error ? event.error.message : null);\n setCardComplete(event.complete);\n });\n }\n\n // Cleanup\n return () => {\n if (cardElementInstanceRef.current && isMountedRef.current) {\n try {\n cardElementInstanceRef.current.unmount();\n cardElementInstanceRef.current.destroy();\n } catch {\n // Element might already be destroyed\n }\n cardElementInstanceRef.current = null;\n isMountedRef.current = false;\n }\n };\n }, [sdkLoaded, publishableKey, isCompletingExistingPayment]);\n\n // Auto-confirm payment for saved cards (requires 3D Secure or similar action)\n useEffect(() => {\n if (!isCompletingExistingPayment || !sdkLoaded || !publishableKey || paymentState !== 'idle') {\n return;\n }\n\n const confirmExistingPayment = async () => {\n const windowStripe = (window as unknown as { Stripe?: StripeFactory }).Stripe;\n if (!windowStripe) {\n setError('Payment gateway not loaded');\n return;\n }\n\n // Initialize Stripe if not already done\n if (!stripeRef.current) {\n stripeRef.current = windowStripe(publishableKey);\n }\n\n setPaymentState('processing');\n setError(null);\n\n try {\n // Confirm the payment (payment method already attached by backend)\n // This will show 3D Secure popup if needed\n const { paymentIntent, error: confirmError } =\n await stripeRef.current.confirmCardPayment(clientSecret);\n\n if (confirmError) {\n throw new Error(confirmError.message || 'Payment confirmation failed');\n }\n\n if (paymentIntent?.status === 'succeeded') {\n setPaymentState('success');\n onSuccess(paymentIntent.id);\n } else if (paymentIntent?.status === 'processing') {\n setPaymentState('processing');\n // Keep in processing state - webhook will handle completion\n } else {\n throw new Error('Payment was not successful');\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Payment confirmation failed';\n setError(errorMessage);\n setPaymentState('error');\n onError(errorMessage);\n }\n };\n\n confirmExistingPayment();\n }, [\n isCompletingExistingPayment,\n sdkLoaded,\n publishableKey,\n clientSecret,\n paymentState,\n onSuccess,\n onError,\n ]);\n\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n\n const stripe = stripeRef.current;\n const cardElement = cardElementInstanceRef.current;\n\n if (!stripe || !cardElement) {\n setError('Payment form not ready');\n return;\n }\n\n if (!cardComplete) {\n setCardError('Please enter complete card details');\n return;\n }\n\n setPaymentState('processing');\n setError(null);\n setCardError(null);\n\n try {\n // Step 1: Create PaymentMethod\n const { paymentMethod, error: pmError } = await stripe.createPaymentMethod({\n type: 'card',\n card: cardElement,\n });\n\n if (pmError) {\n throw new Error(pmError.message || 'Failed to create payment method');\n }\n\n if (!paymentMethod) {\n throw new Error('No payment method returned');\n }\n\n // Step 2: Confirm the payment with the client secret\n const { paymentIntent, error: confirmError } = await stripe.confirmCardPayment(\n clientSecret,\n { payment_method: paymentMethod.id }\n );\n\n if (confirmError) {\n throw new Error(confirmError.message || 'Payment failed');\n }\n\n if (paymentIntent?.status === 'succeeded') {\n setPaymentState('success');\n onSuccess(paymentIntent.id);\n } else if (paymentIntent?.status === 'processing') {\n setPaymentState('processing');\n } else {\n throw new Error('Payment was not successful');\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Payment failed';\n setError(errorMessage);\n setPaymentState('error');\n onError(errorMessage);\n }\n },\n [clientSecret, cardComplete, onSuccess, onError]\n );\n\n // Render success state\n if (paymentState === 'success') {\n return (\n <div\n className={`p-6 rounded-lg border ${statusTokens.success.border} ${statusTokens.success.bg}`}\n >\n <div className=\"flex items-center gap-3\">\n <div\n className={`p-2 rounded-full ${statusTokens.success.bg} ${statusTokens.success.icon}`}\n >\n <Check className=\"size-6\" />\n </div>\n <div>\n <h3 className={`font-semibold ${statusTokens.success.text}`}>Payment Successful!</h3>\n <p className={`text-sm ${statusTokens.success.text}`}>\n Your payment has been processed successfully.\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n // Show loading while SDK loads\n if (!sdkLoaded) {\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-2\">\n <CreditCard className=\"size-5 text-muted-foreground\" />\n <span className=\"font-medium text-foreground\">\n {tr('billing.checkout.cardPayment', 'Card Payment')}\n </span>\n </div>\n <div className=\"p-6 rounded-lg border border-border bg-muted/30 flex items-center justify-center\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n <span className=\"ml-2 text-muted-foreground\">\n {tr('billing.checkout.loadingPaymentForm', 'Loading payment form...')}\n </span>\n </div>\n </div>\n );\n }\n\n // For saved card payments that need confirmation (3D Secure, etc.)\n if (isCompletingExistingPayment) {\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Lock className=\"size-5 text-primary\" />\n <span className=\"font-medium text-foreground\">Secure Payment Verification</span>\n </div>\n\n {paymentState === 'processing' && (\n <div className=\"p-6 rounded-lg border border-border bg-muted/30 flex flex-col items-center justify-center space-y-3\">\n <Loader2 className=\"size-8 animate-spin text-primary\" />\n <p className=\"text-sm text-muted-foreground text-center\">\n Confirming payment with your saved card…\n <br />\n <span className=\"text-xs\">Please complete any security verification if prompted</span>\n </p>\n </div>\n )}\n\n {error && (\n <div className=\"flex items-center gap-2 p-3 rounded-lg bg-destructive/10 text-destructive text-sm\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <div>\n <p className=\"font-medium\">\n {tr('billing.checkout.paymentFailed', 'Payment Failed')}\n </p>\n <p className=\"text-xs\">{error}</p>\n </div>\n </div>\n )}\n\n {(paymentState === 'error' || error) && onCancel && (\n <div className=\"flex gap-3\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"flex-1 px-4 py-3 rounded-lg border border-border text-foreground font-medium\n hover:bg-muted transition-colors\"\n >\n Back to Checkout\n </button>\n </div>\n )}\n </div>\n );\n }\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <CreditCard className=\"size-5 text-muted-foreground\" />\n <span className=\"font-medium text-foreground\">Card Payment</span>\n </div>\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <Lock className=\"size-3\" />\n Secured by Stripe\n </div>\n </div>\n\n {/* Stripe Card Element container - NO conditional rendering inside */}\n <div className=\"space-y-2\">\n <div\n ref={cardElementRef}\n className=\"min-h-[50px] rounded-lg border border-border bg-background p-4\"\n />\n {cardError && <p className=\"text-sm text-destructive\">{cardError}</p>}\n </div>\n\n {/* Error message */}\n {error && (\n <div className=\"flex items-center gap-2 p-3 rounded-lg bg-destructive/10 text-destructive text-sm\">\n <AlertCircle className=\"size-4 shrink-0\" />\n {error}\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex gap-3\">\n {onCancel && (\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={paymentState === 'processing' || disabled}\n className=\"flex-1 px-4 py-3 rounded-lg border border-border text-foreground font-medium\n hover:bg-muted disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n Cancel\n </button>\n )}\n <button\n type=\"submit\"\n disabled={paymentState === 'processing' || disabled || !cardComplete}\n className=\"flex-1 px-4 py-3 rounded-lg bg-primary text-primary-foreground font-medium\n hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\n flex items-center justify-center gap-2\"\n >\n {paymentState === 'processing' ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n Processing…\n </>\n ) : (\n <>\n Pay{' '}\n {new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency.toUpperCase(),\n }).format(amount / 100)}\n </>\n )}\n </button>\n </div>\n\n {/* Security note */}\n <p className=\"text-center text-xs text-muted-foreground\">\n Your payment information is encrypted and secure. We never store your full card details.\n </p>\n </form>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"StripePaymentForm.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/StripePaymentForm.tsx"],"sourcesContent":["/**\n * Stripe Payment Form Component\n * Integrates with Stripe Elements for card payment\n */\n\nimport { type FC, useState, useCallback, useEffect, useRef } from 'react';\nimport { CreditCard, Lock, Loader2, Check, AlertCircle } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { getStripeCardElementStyle, statusTokens } from '../../../shared/utils';\n\n// Stripe types - local definitions to avoid conflicts with external type declarations\ninterface LocalStripeCardElement {\n mount: (element: HTMLElement) => void;\n unmount: () => void;\n destroy: () => void;\n on: (event: string, handler: (event: LocalStripeCardChangeEvent) => void) => void;\n}\n\ninterface LocalStripeCardChangeEvent {\n complete: boolean;\n error?: { message: string };\n}\n\ninterface LocalStripeElements {\n create: (type: 'card', options?: Record<string, unknown>) => LocalStripeCardElement;\n}\n\ninterface LocalStripePaymentMethod {\n id: string;\n card?: {\n last4: string;\n brand: string;\n };\n}\n\ninterface LocalStripeInstance {\n elements: () => LocalStripeElements;\n createPaymentMethod: (options: {\n type: 'card';\n card: LocalStripeCardElement;\n }) => Promise<{ paymentMethod?: LocalStripePaymentMethod; error?: { message: string } }>;\n confirmCardPayment: (\n clientSecret: string,\n options?: { payment_method: string }\n ) => Promise<{\n paymentIntent?: { id: string; status: string };\n error?: { message: string };\n }>;\n}\n\n// Use type assertion when accessing window.Stripe\ntype StripeFactory = (key: string) => LocalStripeInstance;\n\ninterface StripePaymentFormProps {\n clientSecret: string;\n publishableKey: string;\n amount: number;\n currency: string;\n onSuccess: (paymentIntentId: string) => void;\n onError: (error: string) => void;\n onCancel?: () => void;\n disabled?: boolean;\n isCompletingExistingPayment?: boolean; // True for saved cards that need 3D Secure\n}\n\ntype PaymentState = 'idle' | 'loading' | 'processing' | 'success' | 'error';\n\n/**\n * Load Stripe.js SDK dynamically\n */\nconst loadStripeSDK = (): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (window.Stripe) {\n resolve();\n return;\n }\n\n const existingScript = document.querySelector('script[src=\"https://js.stripe.com/v3/\"]');\n if (existingScript) {\n // Wait for existing script to load\n const checkStripe = setInterval(() => {\n if (window.Stripe) {\n clearInterval(checkStripe);\n resolve();\n }\n }, 100);\n return;\n }\n\n const script = document.createElement('script');\n script.src = 'https://js.stripe.com/v3/';\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error('Failed to load Stripe SDK'));\n document.body.appendChild(script);\n });\n};\n\nexport const StripePaymentForm: FC<StripePaymentFormProps> = ({\n clientSecret,\n publishableKey,\n amount,\n currency,\n onSuccess,\n onError,\n onCancel,\n disabled = false,\n isCompletingExistingPayment = false,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const [paymentState, setPaymentState] = useState<PaymentState>('idle');\n const [error, setError] = useState<string | null>(null);\n const [sdkLoaded, setSdkLoaded] = useState(false);\n const [cardComplete, setCardComplete] = useState(false);\n const [cardError, setCardError] = useState<string | null>(null);\n\n const cardElementRef = useRef<HTMLDivElement>(null);\n const stripeRef = useRef<LocalStripeInstance | null>(null);\n const cardElementInstanceRef = useRef<LocalStripeCardElement | null>(null);\n const isMountedRef = useRef(false);\n\n // Load Stripe SDK\n useEffect(() => {\n loadStripeSDK()\n .then(() => {\n setSdkLoaded(true);\n })\n .catch(() => {\n setError('Failed to load payment gateway');\n });\n }, []);\n\n // Initialize Stripe and mount card element (only for new card payments)\n useEffect(() => {\n // Skip card input for completing existing payments (saved cards with 3D Secure)\n if (isCompletingExistingPayment) {\n return;\n }\n\n const windowStripe = (window as unknown as { Stripe?: StripeFactory }).Stripe;\n if (!sdkLoaded || !publishableKey || !windowStripe || isMountedRef.current) {\n return;\n }\n\n // Initialize Stripe\n const stripeInstance = windowStripe(publishableKey);\n stripeRef.current = stripeInstance;\n\n // Create elements\n const elements = stripeInstance.elements();\n const cardElement = elements.create('card', {\n style: getStripeCardElementStyle(),\n });\n cardElementInstanceRef.current = cardElement;\n\n // Mount card element\n if (cardElementRef.current) {\n cardElement.mount(cardElementRef.current);\n isMountedRef.current = true;\n\n // Listen to card element changes\n cardElement.on('change', (event: LocalStripeCardChangeEvent) => {\n setCardError(event.error ? event.error.message : null);\n setCardComplete(event.complete);\n });\n }\n\n // Cleanup\n return () => {\n if (cardElementInstanceRef.current && isMountedRef.current) {\n try {\n cardElementInstanceRef.current.unmount();\n cardElementInstanceRef.current.destroy();\n } catch {\n // Element might already be destroyed\n }\n cardElementInstanceRef.current = null;\n isMountedRef.current = false;\n }\n };\n }, [sdkLoaded, publishableKey, isCompletingExistingPayment]);\n\n // Auto-confirm payment for saved cards (requires 3D Secure or similar action)\n useEffect(() => {\n if (!isCompletingExistingPayment || !sdkLoaded || !publishableKey || paymentState !== 'idle') {\n return;\n }\n\n const confirmExistingPayment = async () => {\n const windowStripe = (window as unknown as { Stripe?: StripeFactory }).Stripe;\n if (!windowStripe) {\n setError('Payment gateway not loaded');\n return;\n }\n\n // Initialize Stripe if not already done\n if (!stripeRef.current) {\n stripeRef.current = windowStripe(publishableKey);\n }\n\n setPaymentState('processing');\n setError(null);\n\n try {\n // Confirm the payment (payment method already attached by backend)\n // This will show 3D Secure popup if needed\n const { paymentIntent, error: confirmError } =\n await stripeRef.current.confirmCardPayment(clientSecret);\n\n if (confirmError) {\n throw new Error(confirmError.message || 'Payment confirmation failed');\n }\n\n if (paymentIntent?.status === 'succeeded') {\n setPaymentState('success');\n onSuccess(paymentIntent.id);\n } else if (paymentIntent?.status === 'processing') {\n setPaymentState('processing');\n // Keep in processing state - webhook will handle completion\n } else {\n throw new Error('Payment was not successful');\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Payment confirmation failed';\n setError(errorMessage);\n setPaymentState('error');\n onError(errorMessage);\n }\n };\n\n confirmExistingPayment();\n }, [\n isCompletingExistingPayment,\n sdkLoaded,\n publishableKey,\n clientSecret,\n paymentState,\n onSuccess,\n onError,\n ]);\n\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n\n const stripe = stripeRef.current;\n const cardElement = cardElementInstanceRef.current;\n\n if (!stripe || !cardElement) {\n setError('Payment form not ready');\n return;\n }\n\n if (!cardComplete) {\n setCardError('Please enter complete card details');\n return;\n }\n\n setPaymentState('processing');\n setError(null);\n setCardError(null);\n\n try {\n // Step 1: Create PaymentMethod\n const { paymentMethod, error: pmError } = await stripe.createPaymentMethod({\n type: 'card',\n card: cardElement,\n });\n\n if (pmError) {\n throw new Error(pmError.message || 'Failed to create payment method');\n }\n\n if (!paymentMethod) {\n throw new Error('No payment method returned');\n }\n\n // Step 2: Confirm the payment with the client secret\n const { paymentIntent, error: confirmError } = await stripe.confirmCardPayment(\n clientSecret,\n { payment_method: paymentMethod.id }\n );\n\n if (confirmError) {\n throw new Error(confirmError.message || 'Payment failed');\n }\n\n if (paymentIntent?.status === 'succeeded') {\n setPaymentState('success');\n onSuccess(paymentIntent.id);\n } else if (paymentIntent?.status === 'processing') {\n setPaymentState('processing');\n } else {\n throw new Error('Payment was not successful');\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Payment failed';\n setError(errorMessage);\n setPaymentState('error');\n onError(errorMessage);\n }\n },\n [clientSecret, cardComplete, onSuccess, onError]\n );\n\n // Render success state\n if (paymentState === 'success') {\n return (\n <div\n className={`p-6 rounded-lg border ${statusTokens.success.border} ${statusTokens.success.bg}`}\n >\n <div className=\"flex items-center gap-3\">\n <div\n className={`p-2 rounded-full ${statusTokens.success.bg} ${statusTokens.success.icon}`}\n >\n <Check className=\"size-6\" />\n </div>\n <div>\n <h3 className={`font-semibold ${statusTokens.success.text}`}>Payment Successful!</h3>\n <p className={`text-sm ${statusTokens.success.text}`}>\n Your payment has been processed successfully.\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n // Show loading while SDK loads\n if (!sdkLoaded) {\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-2\">\n <CreditCard className=\"size-5 text-text-secondary\" />\n <span className=\"font-medium text-text-primary\">\n {tr('billing.checkout.cardPayment', 'Card Payment')}\n </span>\n </div>\n <div className=\"p-6 rounded-lg border border-border-subtle bg-bg-sunken flex items-center justify-center\">\n <Loader2 className=\"size-6 animate-spin text-text-secondary\" />\n <span className=\"ml-2 text-text-secondary\">\n {tr('billing.checkout.loadingPaymentForm', 'Loading payment form...')}\n </span>\n </div>\n </div>\n );\n }\n\n // For saved card payments that need confirmation (3D Secure, etc.)\n if (isCompletingExistingPayment) {\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Lock className=\"size-5 text-text-link\" />\n <span className=\"font-medium text-text-primary\">Secure Payment Verification</span>\n </div>\n\n {paymentState === 'processing' && (\n <div className=\"p-6 rounded-lg border border-border-subtle bg-bg-sunken flex flex-col items-center justify-center space-y-3\">\n <Loader2 className=\"size-8 animate-spin text-text-link\" />\n <p className=\"text-sm text-text-secondary text-center\">\n Confirming payment with your saved card…\n <br />\n <span className=\"text-xs\">Please complete any security verification if prompted</span>\n </p>\n </div>\n )}\n\n {error && (\n <div className=\"flex items-center gap-2 p-3 rounded-lg bg-status-error-bg-subtle text-status-error-text text-sm\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <div>\n <p className=\"font-medium\">\n {tr('billing.checkout.paymentFailed', 'Payment Failed')}\n </p>\n <p className=\"text-xs\">{error}</p>\n </div>\n </div>\n )}\n\n {(paymentState === 'error' || error) && onCancel && (\n <div className=\"flex gap-3\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"flex-1 px-4 py-3 rounded-lg border border-border-subtle text-text-primary font-medium\n hover:bg-bg-sunken transition-colors\"\n >\n Back to Checkout\n </button>\n </div>\n )}\n </div>\n );\n }\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <CreditCard className=\"size-5 text-text-secondary\" />\n <span className=\"font-medium text-text-primary\">Card Payment</span>\n </div>\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Lock className=\"size-3\" />\n Secured by Stripe\n </div>\n </div>\n\n {/* Stripe Card Element container - NO conditional rendering inside */}\n <div className=\"space-y-2\">\n <div\n ref={cardElementRef}\n className=\"min-h-[50px] rounded-lg border border-border-subtle bg-bg-surface p-4\"\n />\n {cardError && <p className=\"text-sm text-status-error-text\">{cardError}</p>}\n </div>\n\n {/* Error message */}\n {error && (\n <div className=\"flex items-center gap-2 p-3 rounded-lg bg-status-error-bg-subtle text-status-error-text text-sm\">\n <AlertCircle className=\"size-4 shrink-0\" />\n {error}\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex gap-3\">\n {onCancel && (\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={paymentState === 'processing' || disabled}\n className=\"flex-1 px-4 py-3 rounded-lg border border-border-subtle text-text-primary font-medium\n hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n Cancel\n </button>\n )}\n <button\n type=\"submit\"\n disabled={paymentState === 'processing' || disabled || !cardComplete}\n className=\"flex-1 px-4 py-3 rounded-lg bg-action-primary-bg text-action-primary-text font-medium\n hover:bg-action-primary-bgHover disabled:opacity-50 disabled:cursor-not-allowed transition-colors\n flex items-center justify-center gap-2\"\n >\n {paymentState === 'processing' ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n Processing…\n </>\n ) : (\n <>\n Pay{' '}\n {new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency.toUpperCase(),\n }).format(amount / 100)}\n </>\n )}\n </button>\n </div>\n\n {/* Security note */}\n <p className=\"text-center text-xs text-text-secondary\">\n Your payment information is encrypted and secure. We never store your full card details.\n </p>\n </form>\n );\n};\n"],"mappings":"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"}
|
|
@@ -25,8 +25,8 @@ var u = ({ subscriptions: u, selectedSubscriptionId: d, onSelect: f, isLoading:
|
|
|
25
25
|
let t = e.plan?.name || "Subscription", n = e.plan?.product?.name;
|
|
26
26
|
return n ? `${n} - ${t}` : t;
|
|
27
27
|
};
|
|
28
|
-
return p ? /* @__PURE__ */ s("div", { className: "h-9 bg-
|
|
29
|
-
className: `flex items-center gap-2 p-2 rounded-
|
|
28
|
+
return p ? /* @__PURE__ */ s("div", { className: "h-9 bg-bg-sunken animate-pulse rounded-button" }) : u.length === 0 ? /* @__PURE__ */ c("div", {
|
|
29
|
+
className: `flex items-center gap-2 p-2 rounded-button ${e.warning.bg} border ${e.warning.border}`,
|
|
30
30
|
children: [/* @__PURE__ */ s(i, { className: `size-4 ${e.warning.icon} shrink-0` }), /* @__PURE__ */ s("span", {
|
|
31
31
|
className: `text-sm ${e.warning.text}`,
|
|
32
32
|
children: "No active subscription. Subscribe to a plan first."
|
|
@@ -37,26 +37,26 @@ var u = ({ subscriptions: u, selectedSubscriptionId: d, onSelect: f, isLoading:
|
|
|
37
37
|
children: [/* @__PURE__ */ c("div", {
|
|
38
38
|
className: "flex items-center gap-2",
|
|
39
39
|
children: [/* @__PURE__ */ s("span", {
|
|
40
|
-
className: "text-sm text-
|
|
40
|
+
className: "text-sm text-text-secondary shrink-0",
|
|
41
41
|
children: h("billing.checkout.attachTo", "Attach to:")
|
|
42
42
|
}), /* @__PURE__ */ c("button", {
|
|
43
43
|
type: "button",
|
|
44
44
|
onClick: () => _(!g),
|
|
45
45
|
className: `
|
|
46
|
-
flex-1 flex items-center justify-between gap-2 px-3 py-1.5 rounded-
|
|
46
|
+
flex-1 flex items-center justify-between gap-2 px-3 py-1.5 rounded-button border text-sm
|
|
47
47
|
transition-colors text-left
|
|
48
|
-
${g ? "border-
|
|
48
|
+
${g ? "border-border-strong ring-1 ring-[var(--color-focus-ring)]" : d ? "border-border-subtle hover:border-border-strong bg-bg-surface" : `${e.warning.border} ${e.warning.bg}`}
|
|
49
49
|
`,
|
|
50
50
|
children: [y ? /* @__PURE__ */ s("span", {
|
|
51
|
-
className: "font-medium text-
|
|
51
|
+
className: "font-medium text-text-primary truncate",
|
|
52
52
|
children: b(y)
|
|
53
53
|
}) : /* @__PURE__ */ s("span", {
|
|
54
54
|
className: e.warning.text,
|
|
55
55
|
children: h("billing.checkout.selectSubscription", "Select subscription")
|
|
56
|
-
}), /* @__PURE__ */ s(o, { className: `size-3.5 shrink-0 text-
|
|
56
|
+
}), /* @__PURE__ */ s(o, { className: `size-3.5 shrink-0 text-text-secondary transition-transform ${g ? "rotate-180" : ""}` })]
|
|
57
57
|
})]
|
|
58
58
|
}), g && /* @__PURE__ */ s("div", {
|
|
59
|
-
className: "absolute left-0 right-0 z-50 mt-1 max-h-48 overflow-auto rounded-
|
|
59
|
+
className: "absolute left-0 right-0 z-50 mt-1 max-h-48 overflow-auto rounded-card border border-border-subtle bg-bg-elevated shadow-elevation-3",
|
|
60
60
|
children: u.map((e) => {
|
|
61
61
|
let t = e.id === d;
|
|
62
62
|
return /* @__PURE__ */ c("button", {
|
|
@@ -67,12 +67,12 @@ var u = ({ subscriptions: u, selectedSubscriptionId: d, onSelect: f, isLoading:
|
|
|
67
67
|
className: `
|
|
68
68
|
w-full flex items-center justify-between gap-2 px-3 py-2 text-sm text-left
|
|
69
69
|
transition-colors
|
|
70
|
-
${t ? "bg-
|
|
70
|
+
${t ? "bg-[var(--color-accent-soft)]" : "hover:bg-bg-sunken"}
|
|
71
71
|
`,
|
|
72
72
|
children: [/* @__PURE__ */ s("span", {
|
|
73
|
-
className: "font-medium text-
|
|
73
|
+
className: "font-medium text-text-primary truncate",
|
|
74
74
|
children: b(e)
|
|
75
|
-
}), t && /* @__PURE__ */ s(a, { className: "size-3.5 text-
|
|
75
|
+
}), t && /* @__PURE__ */ s(a, { className: "size-3.5 text-text-link shrink-0" })]
|
|
76
76
|
}, e.id);
|
|
77
77
|
})
|
|
78
78
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubscriptionSelector.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/SubscriptionSelector.tsx"],"sourcesContent":["/**\n * Subscription Selector Component\n * Compact inline dropdown for choosing subscription when purchasing addons\n * Only shows when multiple subscriptions exist - single subscription auto-selects silently\n */\n\nimport { type FC, useState, useRef, useEffect } from 'react';\nimport { ChevronDown, Check, AlertCircle } from 'lucide-react';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface Subscription {\n id: string;\n status: string;\n startDate: string;\n endDate: string;\n plan?: {\n id: string;\n name: string;\n productID?: string;\n product?: {\n id: string;\n name: string;\n } | null;\n } | null;\n}\n\ninterface SubscriptionSelectorProps {\n subscriptions: Subscription[];\n selectedSubscriptionId: string | null;\n onSelect: (subscriptionId: string | null) => void;\n isLoading?: boolean;\n}\n\nexport const SubscriptionSelector: FC<SubscriptionSelectorProps> = ({\n subscriptions,\n selectedSubscriptionId,\n onSelect,\n isLoading = false,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Auto-select if only one subscription (silently, no UI shown)\n useEffect(() => {\n if (subscriptions.length === 1 && !selectedSubscriptionId) {\n onSelect(subscriptions[0].id);\n }\n }, [subscriptions, selectedSubscriptionId, onSelect]);\n\n const selectedSubscription = subscriptions.find((s) => s.id === selectedSubscriptionId);\n\n // Format subscription display name: \"Product - Plan\" or just \"Plan\"\n const getDisplayName = (subscription: Subscription) => {\n const planName = subscription.plan?.name || 'Subscription';\n const productName = subscription.plan?.product?.name;\n return productName ? `${productName} - ${planName}` : planName;\n };\n\n // Loading state - compact\n if (isLoading) {\n return <div className=\"h-9 bg-
|
|
1
|
+
{"version":3,"file":"SubscriptionSelector.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/SubscriptionSelector.tsx"],"sourcesContent":["/**\n * Subscription Selector Component\n * Compact inline dropdown for choosing subscription when purchasing addons\n * Only shows when multiple subscriptions exist - single subscription auto-selects silently\n */\n\nimport { type FC, useState, useRef, useEffect } from 'react';\nimport { ChevronDown, Check, AlertCircle } from 'lucide-react';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface Subscription {\n id: string;\n status: string;\n startDate: string;\n endDate: string;\n plan?: {\n id: string;\n name: string;\n productID?: string;\n product?: {\n id: string;\n name: string;\n } | null;\n } | null;\n}\n\ninterface SubscriptionSelectorProps {\n subscriptions: Subscription[];\n selectedSubscriptionId: string | null;\n onSelect: (subscriptionId: string | null) => void;\n isLoading?: boolean;\n}\n\nexport const SubscriptionSelector: FC<SubscriptionSelectorProps> = ({\n subscriptions,\n selectedSubscriptionId,\n onSelect,\n isLoading = false,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Auto-select if only one subscription (silently, no UI shown)\n useEffect(() => {\n if (subscriptions.length === 1 && !selectedSubscriptionId) {\n onSelect(subscriptions[0].id);\n }\n }, [subscriptions, selectedSubscriptionId, onSelect]);\n\n const selectedSubscription = subscriptions.find((s) => s.id === selectedSubscriptionId);\n\n // Format subscription display name: \"Product - Plan\" or just \"Plan\"\n const getDisplayName = (subscription: Subscription) => {\n const planName = subscription.plan?.name || 'Subscription';\n const productName = subscription.plan?.product?.name;\n return productName ? `${productName} - ${planName}` : planName;\n };\n\n // Loading state - compact\n if (isLoading) {\n return <div className=\"h-9 bg-bg-sunken animate-pulse rounded-button\" />;\n }\n\n // No subscriptions - compact warning\n if (subscriptions.length === 0) {\n return (\n <div\n className={`flex items-center gap-2 p-2 rounded-button ${statusTokens.warning.bg} border ${statusTokens.warning.border}`}\n >\n <AlertCircle className={`size-4 ${statusTokens.warning.icon} shrink-0`} />\n <span className={`text-sm ${statusTokens.warning.text}`}>\n No active subscription. Subscribe to a plan first.\n </span>\n </div>\n );\n }\n\n // Only one subscription - don't show anything, auto-select happens silently\n if (subscriptions.length === 1) {\n return null;\n }\n\n // Multiple subscriptions - compact dropdown\n return (\n <div className=\"relative\" ref={dropdownRef}>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary shrink-0\">\n {tr('billing.checkout.attachTo', 'Attach to:')}\n </span>\n <button\n type=\"button\"\n onClick={() => setIsOpen(!isOpen)}\n className={`\n flex-1 flex items-center justify-between gap-2 px-3 py-1.5 rounded-button border text-sm\n transition-colors text-left\n ${\n isOpen\n ? 'border-border-strong ring-1 ring-[var(--color-focus-ring)]'\n : selectedSubscriptionId\n ? 'border-border-subtle hover:border-border-strong bg-bg-surface'\n : `${statusTokens.warning.border} ${statusTokens.warning.bg}`\n }\n `}\n >\n {selectedSubscription ? (\n <span className=\"font-medium text-text-primary truncate\">\n {getDisplayName(selectedSubscription)}\n </span>\n ) : (\n <span className={statusTokens.warning.text}>\n {tr('billing.checkout.selectSubscription', 'Select subscription')}\n </span>\n )}\n <ChevronDown\n className={`size-3.5 shrink-0 text-text-secondary transition-transform ${\n isOpen ? 'rotate-180' : ''\n }`}\n />\n </button>\n </div>\n\n {/* Dropdown menu */}\n {isOpen && (\n <div className=\"absolute left-0 right-0 z-50 mt-1 max-h-48 overflow-auto rounded-card border border-border-subtle bg-bg-elevated shadow-elevation-3\">\n {subscriptions.map((subscription) => {\n const isSelected = subscription.id === selectedSubscriptionId;\n return (\n <button\n key={subscription.id}\n type=\"button\"\n onClick={() => {\n onSelect(subscription.id);\n setIsOpen(false);\n }}\n className={`\n w-full flex items-center justify-between gap-2 px-3 py-2 text-sm text-left\n transition-colors\n ${isSelected ? 'bg-[var(--color-accent-soft)]' : 'hover:bg-bg-sunken'}\n `}\n >\n <span className=\"font-medium text-text-primary truncate\">\n {getDisplayName(subscription)}\n </span>\n {isSelected && <Check className=\"size-3.5 text-text-link shrink-0\" />}\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;AAkCA,IAAa,KAAuD,EAClE,kBACA,2BACA,aACA,eAAY,SACR;CACJ,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,IAAc,EAAuB,KAAK;AAehD,CAZA,QAAgB;EACd,IAAM,KAAsB,MAAsB;AAChD,GAAI,EAAY,WAAW,CAAC,EAAY,QAAQ,SAAS,EAAM,OAAe,IAC5E,EAAU,GAAM;;AAKpB,SADA,SAAS,iBAAiB,aAAa,EAAmB,QAC7C,SAAS,oBAAoB,aAAa,EAAmB;IACzE,EAAE,CAAC,EAGN,QAAgB;AACd,EAAI,EAAc,WAAW,KAAK,CAAC,KACjC,EAAS,EAAc,GAAG,GAAG;IAE9B;EAAC;EAAe;EAAwB;EAAS,CAAC;CAErD,IAAM,IAAuB,EAAc,MAAM,MAAM,EAAE,OAAO,EAAuB,EAGjF,KAAkB,MAA+B;EACrD,IAAM,IAAW,EAAa,MAAM,QAAQ,gBACtC,IAAc,EAAa,MAAM,SAAS;AAChD,SAAO,IAAc,GAAG,EAAY,KAAK,MAAa;;AA4BxD,QAxBI,IACK,kBAAC,OAAD,EAAK,WAAU,iDAAkD,CAAA,GAItE,EAAc,WAAW,IAEzB,kBAAC,OAAD;EACE,WAAW,8CAA8C,EAAa,QAAQ,GAAG,UAAU,EAAa,QAAQ;YADlH,CAGE,kBAAC,GAAD,EAAa,WAAW,UAAU,EAAa,QAAQ,KAAK,YAAc,CAAA,EAC1E,kBAAC,QAAD;GAAM,WAAW,WAAW,EAAa,QAAQ;aAAQ;GAElD,CAAA,CACH;MAKN,EAAc,WAAW,IACpB,OAKP,kBAAC,OAAD;EAAK,WAAU;EAAW,KAAK;YAA/B,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,QAAD;IAAM,WAAU;cACb,EAAG,6BAA6B,aAAa;IACzC,CAAA,EACP,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAU,CAAC,EAAO;IACjC,WAAW;;;cAIP,IACI,+DACA,IACE,kEACA,GAAG,EAAa,QAAQ,OAAO,GAAG,EAAa,QAAQ,KAC9D;;cAZL,CAeG,IACC,kBAAC,QAAD;KAAM,WAAU;eACb,EAAe,EAAqB;KAChC,CAAA,GAEP,kBAAC,QAAD;KAAM,WAAW,EAAa,QAAQ;eACnC,EAAG,uCAAuC,sBAAsB;KAC5D,CAAA,EAET,kBAAC,GAAD,EACE,WAAW,8DACT,IAAS,eAAe,MAE1B,CAAA,CACK;MACL;MAGL,KACC,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAc,KAAK,MAAiB;IACnC,IAAM,IAAa,EAAa,OAAO;AACvC,WACE,kBAAC,UAAD;KAEE,MAAK;KACL,eAAe;AAEb,MADA,EAAS,EAAa,GAAG,EACzB,EAAU,GAAM;;KAElB,WAAW;;;oBAGP,IAAa,kCAAkC,qBAAqB;;eAV1E,CAaE,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAe,EAAa;MACxB,CAAA,EACN,KAAc,kBAAC,GAAD,EAAO,WAAU,oCAAqC,CAAA,CAC9D;OAhBF,EAAa,GAgBX;KAEX;GACE,CAAA,CAEJ"}
|
|
@@ -16,11 +16,11 @@ var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `$
|
|
|
16
16
|
return /* @__PURE__ */ s("div", {
|
|
17
17
|
className: "space-y-3",
|
|
18
18
|
children: [/* @__PURE__ */ o("h3", {
|
|
19
|
-
className: "text-sm font-medium text-
|
|
19
|
+
className: "text-sm font-medium text-text-primary",
|
|
20
20
|
children: E("billing.checkout.paymentMethod", "Payment Method")
|
|
21
21
|
}), w ? /* @__PURE__ */ s("div", {
|
|
22
22
|
className: "space-y-2",
|
|
23
|
-
children: [/* @__PURE__ */ o("div", { className: "h-16 rounded-lg border border-border bg-
|
|
23
|
+
children: [/* @__PURE__ */ o("div", { className: "h-16 rounded-lg border border-border-subtle bg-bg-sunken animate-pulse" }), /* @__PURE__ */ o("div", { className: "h-16 rounded-lg border border-border-subtle bg-bg-sunken animate-pulse" })]
|
|
24
24
|
}) : /* @__PURE__ */ s("div", {
|
|
25
25
|
className: "space-y-2",
|
|
26
26
|
children: [
|
|
@@ -30,18 +30,18 @@ var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `$
|
|
|
30
30
|
disabled: !k,
|
|
31
31
|
className: `
|
|
32
32
|
w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left
|
|
33
|
-
${P("new", "CREDITS") ? "border-
|
|
33
|
+
${P("new", "CREDITS") ? "border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]" : "border-border-subtle hover:border-border-strong hover:bg-bg-sunken"}
|
|
34
34
|
${!k && "opacity-50 cursor-not-allowed"}
|
|
35
35
|
`,
|
|
36
36
|
children: [
|
|
37
|
-
/* @__PURE__ */ o(n, { className: `size-5 shrink-0 ${P("new", "CREDITS") ? "text-
|
|
37
|
+
/* @__PURE__ */ o(n, { className: `size-5 shrink-0 ${P("new", "CREDITS") ? "text-text-link" : "text-text-secondary"}` }),
|
|
38
38
|
/* @__PURE__ */ s("div", {
|
|
39
39
|
className: "flex-1 min-w-0",
|
|
40
40
|
children: [/* @__PURE__ */ o("div", {
|
|
41
|
-
className: "text-sm font-medium text-
|
|
41
|
+
className: "text-sm font-medium text-text-primary",
|
|
42
42
|
children: "Pay with Credits"
|
|
43
43
|
}), /* @__PURE__ */ o("div", {
|
|
44
|
-
className: "text-xs text-
|
|
44
|
+
className: "text-xs text-text-secondary",
|
|
45
45
|
children: k ? `Available: ${v.toLocaleString()} credits` : `Insufficient credits (need ${y.toLocaleString()})`
|
|
46
46
|
})]
|
|
47
47
|
}),
|
|
@@ -53,7 +53,7 @@ var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `$
|
|
|
53
53
|
}),
|
|
54
54
|
i.length > 0 && /* @__PURE__ */ s(a, { children: [
|
|
55
55
|
/* @__PURE__ */ o("div", {
|
|
56
|
-
className: "text-xs font-medium text-
|
|
56
|
+
className: "text-xs font-medium text-text-secondary uppercase tracking-wide pt-2",
|
|
57
57
|
children: "Saved Cards"
|
|
58
58
|
}),
|
|
59
59
|
j && /* @__PURE__ */ s("button", {
|
|
@@ -61,17 +61,17 @@ var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `$
|
|
|
61
61
|
onClick: () => N("saved", j.id),
|
|
62
62
|
className: `
|
|
63
63
|
w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left
|
|
64
|
-
${P("saved", j.id) ? "border-
|
|
64
|
+
${P("saved", j.id) ? "border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]" : "border-border-subtle hover:border-border-strong hover:bg-bg-sunken"}
|
|
65
65
|
`,
|
|
66
66
|
children: [
|
|
67
67
|
l(j.type),
|
|
68
68
|
/* @__PURE__ */ s("div", {
|
|
69
69
|
className: "flex-1 min-w-0",
|
|
70
70
|
children: [/* @__PURE__ */ o("div", {
|
|
71
|
-
className: "text-sm font-medium text-
|
|
71
|
+
className: "text-sm font-medium text-text-primary",
|
|
72
72
|
children: u(j)
|
|
73
73
|
}), j.expiryMonth && j.expiryYear && /* @__PURE__ */ s("div", {
|
|
74
|
-
className: "text-xs text-
|
|
74
|
+
className: "text-xs text-text-secondary",
|
|
75
75
|
children: [
|
|
76
76
|
"Expires ",
|
|
77
77
|
j.expiryMonth.toString().padStart(2, "0"),
|
|
@@ -81,7 +81,7 @@ var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `$
|
|
|
81
81
|
})]
|
|
82
82
|
}),
|
|
83
83
|
j.isDefault && /* @__PURE__ */ o("span", {
|
|
84
|
-
className: "text-xs px-2 py-0.5 rounded bg-
|
|
84
|
+
className: "text-xs px-2 py-0.5 rounded bg-[var(--color-accent-soft)] text-text-link",
|
|
85
85
|
children: "Default"
|
|
86
86
|
})
|
|
87
87
|
]
|
|
@@ -91,15 +91,15 @@ var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `$
|
|
|
91
91
|
onClick: () => N("saved", e.id),
|
|
92
92
|
className: `
|
|
93
93
|
w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left
|
|
94
|
-
${P("saved", e.id) ? "border-
|
|
94
|
+
${P("saved", e.id) ? "border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]" : "border-border-subtle hover:border-border-strong hover:bg-bg-sunken"}
|
|
95
95
|
`,
|
|
96
96
|
children: [l(e.type), /* @__PURE__ */ s("div", {
|
|
97
97
|
className: "flex-1 min-w-0",
|
|
98
98
|
children: [/* @__PURE__ */ o("div", {
|
|
99
|
-
className: "text-sm font-medium text-
|
|
99
|
+
className: "text-sm font-medium text-text-primary",
|
|
100
100
|
children: u(e)
|
|
101
101
|
}), e.expiryMonth && e.expiryYear && /* @__PURE__ */ s("div", {
|
|
102
|
-
className: "text-xs text-
|
|
102
|
+
className: "text-xs text-text-secondary",
|
|
103
103
|
children: [
|
|
104
104
|
"Expires ",
|
|
105
105
|
e.expiryMonth.toString().padStart(2, "0"),
|
|
@@ -111,12 +111,12 @@ var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `$
|
|
|
111
111
|
}, e.id)), /* @__PURE__ */ s("button", {
|
|
112
112
|
type: "button",
|
|
113
113
|
onClick: () => O(!1),
|
|
114
|
-
className: "text-xs text-
|
|
114
|
+
className: "text-xs text-text-link hover:underline flex items-center gap-1 pl-1",
|
|
115
115
|
children: [/* @__PURE__ */ o(t, { className: "size-3 rotate-180" }), "Hide other cards"]
|
|
116
116
|
})] }) : /* @__PURE__ */ s("button", {
|
|
117
117
|
type: "button",
|
|
118
118
|
onClick: () => O(!0),
|
|
119
|
-
className: "text-xs text-
|
|
119
|
+
className: "text-xs text-text-link hover:underline flex items-center gap-1 pl-1",
|
|
120
120
|
children: [
|
|
121
121
|
/* @__PURE__ */ o(t, { className: "size-3" }),
|
|
122
122
|
"Show ",
|
|
@@ -127,7 +127,7 @@ var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `$
|
|
|
127
127
|
}) })
|
|
128
128
|
] }),
|
|
129
129
|
/* @__PURE__ */ o("div", {
|
|
130
|
-
className: "text-xs font-medium text-
|
|
130
|
+
className: "text-xs font-medium text-text-secondary uppercase tracking-wide pt-2",
|
|
131
131
|
children: i.length > 0 ? "Or use new card" : _ ? "Or pay with card" : "Pay with Card"
|
|
132
132
|
}),
|
|
133
133
|
g && A && /* @__PURE__ */ s("button", {
|
|
@@ -135,17 +135,17 @@ var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `$
|
|
|
135
135
|
onClick: () => N("new", "RAZORPAY"),
|
|
136
136
|
className: `
|
|
137
137
|
w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left
|
|
138
|
-
${P("new", "RAZORPAY") ? "border-
|
|
138
|
+
${P("new", "RAZORPAY") ? "border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]" : "border-border-subtle hover:border-border-strong hover:bg-bg-sunken"}
|
|
139
139
|
`,
|
|
140
140
|
children: [
|
|
141
|
-
/* @__PURE__ */ o(r, { className: `size-5 shrink-0 ${P("new", "RAZORPAY") ? "text-
|
|
141
|
+
/* @__PURE__ */ o(r, { className: `size-5 shrink-0 ${P("new", "RAZORPAY") ? "text-text-link" : "text-text-secondary"}` }),
|
|
142
142
|
/* @__PURE__ */ s("div", {
|
|
143
143
|
className: "flex-1 min-w-0",
|
|
144
144
|
children: [/* @__PURE__ */ o("div", {
|
|
145
|
-
className: "text-sm font-medium text-
|
|
145
|
+
className: "text-sm font-medium text-text-primary",
|
|
146
146
|
children: "UPI / Cards / Netbanking"
|
|
147
147
|
}), /* @__PURE__ */ o("div", {
|
|
148
|
-
className: "text-xs text-
|
|
148
|
+
className: "text-xs text-text-secondary",
|
|
149
149
|
children: "Razorpay"
|
|
150
150
|
})]
|
|
151
151
|
}),
|
|
@@ -160,17 +160,17 @@ var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `$
|
|
|
160
160
|
onClick: () => N("new", "STRIPE"),
|
|
161
161
|
className: `
|
|
162
162
|
w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left
|
|
163
|
-
${P("new", "STRIPE") ? "border-
|
|
163
|
+
${P("new", "STRIPE") ? "border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]" : "border-border-subtle hover:border-border-strong hover:bg-bg-sunken"}
|
|
164
164
|
`,
|
|
165
165
|
children: [
|
|
166
|
-
/* @__PURE__ */ o(r, { className: `size-5 shrink-0 ${P("new", "STRIPE") ? "text-
|
|
166
|
+
/* @__PURE__ */ o(r, { className: `size-5 shrink-0 ${P("new", "STRIPE") ? "text-text-link" : "text-text-secondary"}` }),
|
|
167
167
|
/* @__PURE__ */ s("div", {
|
|
168
168
|
className: "flex-1 min-w-0",
|
|
169
169
|
children: [/* @__PURE__ */ o("div", {
|
|
170
|
-
className: "text-sm font-medium text-
|
|
170
|
+
className: "text-sm font-medium text-text-primary",
|
|
171
171
|
children: "Credit / Debit Cards"
|
|
172
172
|
}), /* @__PURE__ */ o("div", {
|
|
173
|
-
className: "text-xs text-
|
|
173
|
+
className: "text-xs text-text-secondary",
|
|
174
174
|
children: "Stripe - Multi-currency"
|
|
175
175
|
})]
|
|
176
176
|
}),
|
|
@@ -181,30 +181,30 @@ var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `$
|
|
|
181
181
|
]
|
|
182
182
|
}),
|
|
183
183
|
!d && p !== "CREDITS" && p !== "AUTO" && !C && /* @__PURE__ */ o("div", {
|
|
184
|
-
className: "mt-4 pt-4 border-t border-border",
|
|
184
|
+
className: "mt-4 pt-4 border-t border-border-subtle",
|
|
185
185
|
children: /* @__PURE__ */ s("label", {
|
|
186
186
|
className: "flex items-start gap-3 cursor-pointer group",
|
|
187
187
|
children: [/* @__PURE__ */ o("input", {
|
|
188
188
|
type: "checkbox",
|
|
189
189
|
checked: x,
|
|
190
190
|
onChange: (e) => S(e.target.checked),
|
|
191
|
-
className: "mt-0.5 size-4 rounded border-border text-
|
|
191
|
+
className: "mt-0.5 size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)] focus:ring-offset-0"
|
|
192
192
|
}), /* @__PURE__ */ s("div", {
|
|
193
193
|
className: "flex-1",
|
|
194
194
|
children: [/* @__PURE__ */ o("div", {
|
|
195
|
-
className: "text-sm font-medium text-
|
|
195
|
+
className: "text-sm font-medium text-text-primary group-hover:text-text-link transition-colors",
|
|
196
196
|
children: "Save card for future use"
|
|
197
197
|
}), /* @__PURE__ */ o("div", {
|
|
198
|
-
className: "text-xs text-
|
|
198
|
+
className: "text-xs text-text-secondary mt-0.5",
|
|
199
199
|
children: "Securely save this payment method for faster checkout next time"
|
|
200
200
|
})]
|
|
201
201
|
})]
|
|
202
202
|
})
|
|
203
203
|
}),
|
|
204
204
|
!d && p !== "CREDITS" && p !== "AUTO" && C && /* @__PURE__ */ o("div", {
|
|
205
|
-
className: "mt-4 pt-4 border-t border-border",
|
|
205
|
+
className: "mt-4 pt-4 border-t border-border-subtle",
|
|
206
206
|
children: /* @__PURE__ */ s("div", {
|
|
207
|
-
className: "flex items-start gap-2 text-xs text-
|
|
207
|
+
className: "flex items-start gap-2 text-xs text-text-secondary bg-bg-sunken p-3 rounded-lg",
|
|
208
208
|
children: [/* @__PURE__ */ o("svg", {
|
|
209
209
|
className: "size-4 mt-0.5 flex-shrink-0",
|
|
210
210
|
fill: "none",
|