@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
|
@@ -62,12 +62,12 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
62
62
|
children: [/* @__PURE__ */ m(a, { className: `size-4 ${t.success.icon} shrink-0 mt-0.5` }), /* @__PURE__ */ h("div", {
|
|
63
63
|
className: "flex-1 min-w-0",
|
|
64
64
|
children: [/* @__PURE__ */ m("span", {
|
|
65
|
-
className: "text-sm text-
|
|
65
|
+
className: "text-sm text-text-primary",
|
|
66
66
|
children: e.name
|
|
67
67
|
}), e.limits && Object.keys(e.limits).length > 0 && /* @__PURE__ */ m("div", {
|
|
68
68
|
className: "mt-1 flex flex-wrap gap-1.5",
|
|
69
69
|
children: Object.entries(e.limits).map(([e, t]) => /* @__PURE__ */ h("span", {
|
|
70
|
-
className: "inline-flex items-center text-xs px-1.5 py-0.5 rounded bg-
|
|
70
|
+
className: "inline-flex items-center text-xs px-1.5 py-0.5 rounded bg-bg-sunken text-text-secondary",
|
|
71
71
|
children: [
|
|
72
72
|
e,
|
|
73
73
|
": ",
|
|
@@ -79,24 +79,24 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
79
79
|
}, e.id))
|
|
80
80
|
});
|
|
81
81
|
return /* @__PURE__ */ h("div", {
|
|
82
|
-
className: "rounded-lg border border-border bg-
|
|
82
|
+
className: "rounded-lg border border-border-subtle bg-bg-surface overflow-hidden",
|
|
83
83
|
children: [
|
|
84
84
|
/* @__PURE__ */ m("div", {
|
|
85
|
-
className: "px-4 py-3 border-b border-border bg-
|
|
85
|
+
className: "px-4 py-3 border-b border-border-subtle bg-bg-sunken",
|
|
86
86
|
children: /* @__PURE__ */ h("div", {
|
|
87
87
|
className: "flex items-center gap-2",
|
|
88
|
-
children: [/* @__PURE__ */ m(u, { className: "size-4 text-
|
|
89
|
-
className: "font-semibold text-
|
|
88
|
+
children: [/* @__PURE__ */ m(u, { className: "size-4 text-text-secondary" }), /* @__PURE__ */ m("h3", {
|
|
89
|
+
className: "font-semibold text-text-primary",
|
|
90
90
|
children: I("billing.checkout.orderSummary", "Order Summary")
|
|
91
91
|
})]
|
|
92
92
|
})
|
|
93
93
|
}),
|
|
94
94
|
/* @__PURE__ */ m("div", {
|
|
95
|
-
className: "divide-y divide-border",
|
|
95
|
+
className: "divide-y divide-border-subtle",
|
|
96
96
|
children: S.length === 0 ? /* @__PURE__ */ h("div", {
|
|
97
97
|
className: "p-6 text-center",
|
|
98
|
-
children: [/* @__PURE__ */ m(c, { className: "mx-auto size-8 text-
|
|
99
|
-
className: "mt-2 text-sm text-
|
|
98
|
+
children: [/* @__PURE__ */ m(c, { className: "mx-auto size-8 text-text-secondary" }), /* @__PURE__ */ m("p", {
|
|
99
|
+
className: "mt-2 text-sm text-text-secondary",
|
|
100
100
|
children: "No items in checkout"
|
|
101
101
|
})]
|
|
102
102
|
}) : S.map((e) => {
|
|
@@ -106,7 +106,7 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
106
106
|
children: /* @__PURE__ */ h("div", {
|
|
107
107
|
className: "flex items-start gap-3",
|
|
108
108
|
children: [/* @__PURE__ */ m("div", {
|
|
109
|
-
className: "p-2 rounded-lg bg-
|
|
109
|
+
className: "p-2 rounded-lg bg-bg-sunken text-text-secondary shrink-0",
|
|
110
110
|
children: L(e.type)
|
|
111
111
|
}), /* @__PURE__ */ h("div", {
|
|
112
112
|
className: "flex-1 min-w-0",
|
|
@@ -114,7 +114,7 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
114
114
|
/* @__PURE__ */ h("div", {
|
|
115
115
|
className: "flex items-center gap-2 flex-wrap",
|
|
116
116
|
children: [/* @__PURE__ */ m("span", {
|
|
117
|
-
className: "font-medium text-
|
|
117
|
+
className: "font-medium text-text-primary",
|
|
118
118
|
children: e.name
|
|
119
119
|
}), /* @__PURE__ */ m("span", {
|
|
120
120
|
className: `text-xs px-1.5 py-0.5 rounded ${t.className}`,
|
|
@@ -122,7 +122,7 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
122
122
|
})]
|
|
123
123
|
}),
|
|
124
124
|
e.description && /* @__PURE__ */ m("p", {
|
|
125
|
-
className: "text-sm text-
|
|
125
|
+
className: "text-sm text-text-secondary mt-0.5 line-clamp-2",
|
|
126
126
|
children: e.description
|
|
127
127
|
}),
|
|
128
128
|
/* @__PURE__ */ h("div", {
|
|
@@ -134,7 +134,7 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
134
134
|
type: "button",
|
|
135
135
|
onClick: () => M?.(e.id, e.quantity - 1),
|
|
136
136
|
disabled: e.quantity <= 1,
|
|
137
|
-
className: "p-2 rounded border border-border hover:bg-
|
|
137
|
+
className: "p-2 rounded border border-border-subtle hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed min-w-[36px] min-h-[36px] flex items-center justify-center",
|
|
138
138
|
"aria-label": `Decrease ${e.name} quantity`,
|
|
139
139
|
children: /* @__PURE__ */ m(s, { className: "size-3.5" })
|
|
140
140
|
}),
|
|
@@ -145,23 +145,23 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
145
145
|
/* @__PURE__ */ m("button", {
|
|
146
146
|
type: "button",
|
|
147
147
|
onClick: () => M?.(e.id, e.quantity + 1),
|
|
148
|
-
className: "p-2 rounded border border-border hover:bg-
|
|
148
|
+
className: "p-2 rounded border border-border-subtle hover:bg-bg-sunken min-w-[36px] min-h-[36px] flex items-center justify-center",
|
|
149
149
|
"aria-label": `Increase ${e.name} quantity`,
|
|
150
150
|
children: /* @__PURE__ */ m(l, { className: "size-3.5" })
|
|
151
151
|
})
|
|
152
152
|
]
|
|
153
153
|
}) : /* @__PURE__ */ h("span", {
|
|
154
|
-
className: "text-sm text-
|
|
154
|
+
className: "text-sm text-text-secondary",
|
|
155
155
|
children: ["Qty: ", e.quantity]
|
|
156
156
|
}), /* @__PURE__ */ h("div", {
|
|
157
157
|
className: "flex items-center gap-3",
|
|
158
158
|
children: [/* @__PURE__ */ m("span", {
|
|
159
|
-
className: "font-medium text-
|
|
159
|
+
className: "font-medium text-text-primary",
|
|
160
160
|
children: V(e.price * e.quantity, e)
|
|
161
161
|
}), P && N && /* @__PURE__ */ m("button", {
|
|
162
162
|
type: "button",
|
|
163
163
|
onClick: () => N(e.id),
|
|
164
|
-
className: "p-1.5 rounded text-
|
|
164
|
+
className: "p-1.5 rounded text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle",
|
|
165
165
|
"aria-label": `Remove ${e.name}`,
|
|
166
166
|
children: /* @__PURE__ */ m(f, { className: "size-4" })
|
|
167
167
|
})]
|
|
@@ -177,7 +177,7 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
177
177
|
value: "features",
|
|
178
178
|
className: "border-0",
|
|
179
179
|
children: [/* @__PURE__ */ m(b, {
|
|
180
|
-
className: "py-2 px-0 text-sm text-
|
|
180
|
+
className: "py-2 px-0 text-sm text-text-secondary hover:text-text-primary hover:no-underline",
|
|
181
181
|
children: /* @__PURE__ */ h("span", {
|
|
182
182
|
className: "flex items-center gap-1.5",
|
|
183
183
|
children: [
|
|
@@ -203,17 +203,17 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
203
203
|
})
|
|
204
204
|
}),
|
|
205
205
|
S.length > 0 && /* @__PURE__ */ m("div", {
|
|
206
|
-
className: "border-t border-border bg-
|
|
206
|
+
className: "border-t border-border-subtle bg-bg-sunken",
|
|
207
207
|
children: /* @__PURE__ */ h("div", {
|
|
208
208
|
className: "p-4 space-y-2",
|
|
209
209
|
children: [
|
|
210
210
|
/* @__PURE__ */ h("div", {
|
|
211
211
|
className: "flex items-center justify-between text-sm",
|
|
212
212
|
children: [/* @__PURE__ */ m("span", {
|
|
213
|
-
className: "text-
|
|
213
|
+
className: "text-text-secondary",
|
|
214
214
|
children: I("billing.checkout.subtotal", "Subtotal")
|
|
215
215
|
}), /* @__PURE__ */ m("span", {
|
|
216
|
-
className: "text-
|
|
216
|
+
className: "text-text-primary",
|
|
217
217
|
children: V(A ? B : C)
|
|
218
218
|
})]
|
|
219
219
|
}),
|
|
@@ -230,16 +230,16 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
230
230
|
j ? /* @__PURE__ */ h("div", {
|
|
231
231
|
className: "flex items-center justify-between text-sm",
|
|
232
232
|
children: [/* @__PURE__ */ m("span", {
|
|
233
|
-
className: "text-
|
|
233
|
+
className: "text-text-secondary",
|
|
234
234
|
children: "Tax"
|
|
235
235
|
}), /* @__PURE__ */ m("span", {
|
|
236
|
-
className: "text-
|
|
236
|
+
className: "text-text-secondary animate-pulse",
|
|
237
237
|
children: "Calculating…"
|
|
238
238
|
})]
|
|
239
239
|
}) : T > 0 && E > 0 ? /* @__PURE__ */ h("div", {
|
|
240
240
|
className: "flex items-center justify-between text-sm",
|
|
241
241
|
children: [/* @__PURE__ */ h("span", {
|
|
242
|
-
className: "text-
|
|
242
|
+
className: "text-text-secondary",
|
|
243
243
|
children: [
|
|
244
244
|
D || "Tax",
|
|
245
245
|
" (",
|
|
@@ -247,18 +247,18 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
247
247
|
"%)"
|
|
248
248
|
]
|
|
249
249
|
}), /* @__PURE__ */ m("span", {
|
|
250
|
-
className: "text-
|
|
250
|
+
className: "text-text-primary",
|
|
251
251
|
children: V(T)
|
|
252
252
|
})]
|
|
253
253
|
}) : null,
|
|
254
|
-
/* @__PURE__ */ m("div", { className: "border-t border-border my-2" }),
|
|
254
|
+
/* @__PURE__ */ m("div", { className: "border-t border-border-subtle my-2" }),
|
|
255
255
|
A ? /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ h("div", {
|
|
256
256
|
className: "flex items-center justify-between",
|
|
257
257
|
children: [/* @__PURE__ */ m("span", {
|
|
258
|
-
className: "font-semibold text-
|
|
258
|
+
className: "font-semibold text-text-primary",
|
|
259
259
|
children: I("billing.checkout.total", "Total")
|
|
260
260
|
}), /* @__PURE__ */ h("span", {
|
|
261
|
-
className: "text-xl font-bold text-
|
|
261
|
+
className: "text-xl font-bold text-text-primary",
|
|
262
262
|
children: [
|
|
263
263
|
z(B).toLocaleString(void 0, {
|
|
264
264
|
minimumFractionDigits: 0,
|
|
@@ -269,15 +269,15 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
269
269
|
]
|
|
270
270
|
})]
|
|
271
271
|
}), /* @__PURE__ */ m("p", {
|
|
272
|
-
className: "text-xs text-center text-
|
|
272
|
+
className: "text-xs text-center text-text-secondary pt-2",
|
|
273
273
|
children: "Paying with account credits"
|
|
274
274
|
})] }) : /* @__PURE__ */ m(p, { children: /* @__PURE__ */ h("div", {
|
|
275
275
|
className: "flex items-center justify-between",
|
|
276
276
|
children: [/* @__PURE__ */ h("span", {
|
|
277
|
-
className: "font-semibold text-
|
|
277
|
+
className: "font-semibold text-text-primary",
|
|
278
278
|
children: ["Total in ", k]
|
|
279
279
|
}), /* @__PURE__ */ m("span", {
|
|
280
|
-
className: "text-xl font-bold text-
|
|
280
|
+
className: "text-xl font-bold text-text-primary",
|
|
281
281
|
children: n(O, k)
|
|
282
282
|
})]
|
|
283
283
|
}) })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrderSummary.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/OrderSummary.tsx"],"sourcesContent":["/**\n * Order Summary Component\n * Displays checkout items, discounts, taxes, and total\n */\n\nimport { type FC, useMemo } from 'react';\nimport {\n Package,\n Blocks,\n Coins,\n Minus,\n Plus,\n Trash2,\n Receipt,\n Check,\n ShoppingCart,\n} from 'lucide-react';\nimport {\n Accordion,\n AccordionItem,\n AccordionTrigger,\n AccordionContent,\n} from '@burdenoff/fe-libs/ui';\nimport type { CheckoutItem, Currency, CheckoutFeature } from '../types';\nimport { formatCurrency } from '../../../shared/utils/format';\nimport { categoryTokens, statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n/**\n * Credit conversion rate: How many credits equal 1 USD\n * This should match the backend creditRate.rateUSD (default 0.01 means 100 credits = $1)\n */\nconst CREDITS_PER_USD = 100;\n\ninterface OrderSummaryProps {\n items: CheckoutItem[];\n subtotal: number;\n discountAmount: number;\n taxAmount: number;\n taxRate: number;\n taxType?: string;\n total: number;\n currency: Currency;\n showInCredits?: boolean;\n /** Whether tax is being calculated */\n isCalculatingTax?: boolean;\n onUpdateQuantity?: (itemId: string, quantity: number) => void;\n onRemoveItem?: (itemId: string) => void;\n isEditable?: boolean;\n}\n\nexport const OrderSummary: FC<OrderSummaryProps> = ({\n items,\n subtotal,\n discountAmount,\n taxAmount,\n taxRate,\n taxType,\n total,\n currency,\n showInCredits = false,\n isCalculatingTax = false,\n onUpdateQuantity,\n onRemoveItem,\n isEditable = true,\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 getItemIcon = (type: CheckoutItem['type']) => {\n switch (type) {\n case 'PLAN':\n return <Package className=\"size-5\" />;\n case 'ADDON':\n return <Blocks className=\"size-5\" />;\n case 'CREDITS':\n return <Coins className=\"size-5\" />;\n case 'ORDER':\n return <ShoppingCart className=\"size-5\" />;\n default:\n return <Package className=\"size-5\" />;\n }\n };\n\n const getItemTypeBadge = (type: CheckoutItem['type']) => {\n const badges = {\n PLAN: {\n label: 'Plan',\n className: `${categoryTokens.plan.bg} ${categoryTokens.plan.text}`,\n },\n ADDON: {\n label: 'Add-on',\n className: `${categoryTokens.addon.bg} ${categoryTokens.addon.text}`,\n },\n CREDITS: {\n label: 'Credits',\n className: `${categoryTokens.credits.bg} ${categoryTokens.credits.text}`,\n },\n ORDER: {\n label: 'Store Order',\n className: 'bg-status-success-bg-subtle text-status-success-text',\n },\n };\n return badges[type];\n };\n\n // Convert USD amount to credits\n const usdToCredits = (usdAmount: number) => usdAmount * CREDITS_PER_USD;\n\n // Subtotal in USD — used for credit conversion (items may be priced in local currency)\n const subtotalUSD = useMemo(\n () => items.reduce((sum, item) => sum + (item.priceUSD ?? item.price) * item.quantity, 0),\n [items]\n );\n\n // Format amount - show in credits if paying with credits, otherwise in selected currency\n // Items now have their prices stored in the selected currency (from currencyPrices)\n // For credit conversion always use priceUSD (base USD price) — not the localized price.\n const formatAmount = (amount: number, item?: CheckoutItem) => {\n if (showInCredits) {\n const usdAmount = item?.priceUSD ?? amount;\n const creditsAmount = usdToCredits(usdAmount);\n return `${creditsAmount.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 0 })} Credits`;\n }\n // Format in selected currency (items now have localized prices)\n return formatCurrency(amount, currency);\n };\n\n // Format limit value for display\n const formatLimitValue = (_key: string, value: unknown): string => {\n if (typeof value === 'boolean') {\n return value ? 'Included' : 'Not included';\n }\n if (typeof value === 'number') {\n if (value === -1 || value === Infinity) return 'Unlimited';\n return value.toLocaleString();\n }\n return String(value);\n };\n\n // Merge duplicate features by name, combining numeric limits\n const mergeFeatures = (features: CheckoutFeature[]): CheckoutFeature[] => {\n const merged = new Map<string, CheckoutFeature>();\n\n for (const feature of features) {\n const existing = merged.get(feature.name);\n if (existing) {\n // Merge limits - add numeric values together\n const mergedLimits: Record<string, number | string | boolean> = { ...existing.limits };\n if (feature.limits) {\n for (const [key, value] of Object.entries(feature.limits)) {\n const existingValue = mergedLimits[key];\n if (typeof value === 'number' && typeof existingValue === 'number') {\n // Both are numbers - add them (unless unlimited)\n if (existingValue === -1 || value === -1) {\n mergedLimits[key] = -1; // Unlimited stays unlimited\n } else {\n mergedLimits[key] = existingValue + value;\n }\n } else {\n // Non-numeric or first occurrence - just set\n mergedLimits[key] = value;\n }\n }\n }\n existing.limits = mergedLimits;\n } else {\n // Clone the feature to avoid mutating original\n merged.set(feature.name, {\n ...feature,\n limits: feature.limits ? { ...feature.limits } : undefined,\n });\n }\n }\n\n return Array.from(merged.values());\n };\n\n // Render features list\n const renderFeatures = (features: CheckoutFeature[]) => {\n if (!features || features.length === 0) return null;\n\n const mergedFeatures = mergeFeatures(features);\n\n return (\n <ul className=\"space-y-2\">\n {mergedFeatures.map((feature) => (\n <li key={feature.id} className=\"flex items-start gap-2\">\n <Check className={`size-4 ${statusTokens.success.icon} shrink-0 mt-0.5`} />\n <div className=\"flex-1 min-w-0\">\n <span className=\"text-sm text-foreground\">{feature.name}</span>\n {feature.limits && Object.keys(feature.limits).length > 0 && (\n <div className=\"mt-1 flex flex-wrap gap-1.5\">\n {Object.entries(feature.limits).map(([key, value]) => (\n <span\n key={key}\n className=\"inline-flex items-center text-xs px-1.5 py-0.5 rounded bg-muted text-muted-foreground\"\n >\n {key}: {formatLimitValue(key, value)}\n </span>\n ))}\n </div>\n )}\n </div>\n </li>\n ))}\n </ul>\n );\n };\n\n return (\n <div className=\"rounded-lg border border-border bg-card overflow-hidden\">\n {/* Header */}\n <div className=\"px-4 py-3 border-b border-border bg-muted/30\">\n <div className=\"flex items-center gap-2\">\n <Receipt className=\"size-4 text-muted-foreground\" />\n <h3 className=\"font-semibold text-foreground\">\n {tr('billing.checkout.orderSummary', 'Order Summary')}\n </h3>\n </div>\n </div>\n\n {/* Items */}\n <div className=\"divide-y divide-border\">\n {items.length === 0 ? (\n <div className=\"p-6 text-center\">\n <Package className=\"mx-auto size-8 text-muted-foreground\" />\n <p className=\"mt-2 text-sm text-muted-foreground\">No items in checkout</p>\n </div>\n ) : (\n items.map((item) => {\n const badge = getItemTypeBadge(item.type);\n const hasFeatures = item.features && item.features.length > 0;\n\n return (\n <div key={item.id} className=\"p-4\">\n <div className=\"flex items-start gap-3\">\n {/* Icon */}\n <div className=\"p-2 rounded-lg bg-muted text-muted-foreground shrink-0\">\n {getItemIcon(item.type)}\n </div>\n\n {/* Item details */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-medium text-foreground\">{item.name}</span>\n <span className={`text-xs px-1.5 py-0.5 rounded ${badge.className}`}>\n {badge.label}\n </span>\n </div>\n {item.description && (\n <p className=\"text-sm text-muted-foreground mt-0.5 line-clamp-2\">\n {item.description}\n </p>\n )}\n\n {/* Quantity controls */}\n <div className=\"flex items-center justify-between mt-3\">\n {isEditable && item.type !== 'PLAN' ? (\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity?.(item.id, item.quantity - 1)}\n disabled={item.quantity <= 1}\n className=\"p-2 rounded border border-border hover:bg-muted disabled:opacity-50 disabled:cursor-not-allowed min-w-[36px] min-h-[36px] flex items-center justify-center\"\n aria-label={`Decrease ${item.name} quantity`}\n >\n <Minus className=\"size-3.5\" />\n </button>\n <span className=\"w-8 text-center text-sm font-medium\">\n {item.quantity}\n </span>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity?.(item.id, item.quantity + 1)}\n className=\"p-2 rounded border border-border hover:bg-muted min-w-[36px] min-h-[36px] flex items-center justify-center\"\n aria-label={`Increase ${item.name} quantity`}\n >\n <Plus className=\"size-3.5\" />\n </button>\n </div>\n ) : (\n <span className=\"text-sm text-muted-foreground\">Qty: {item.quantity}</span>\n )}\n\n <div className=\"flex items-center gap-3\">\n <span className=\"font-medium text-foreground\">\n {formatAmount(item.price * item.quantity, item)}\n </span>\n {isEditable && onRemoveItem && (\n <button\n type=\"button\"\n onClick={() => onRemoveItem(item.id)}\n className=\"p-1.5 rounded text-muted-foreground hover:text-destructive hover:bg-destructive/10\"\n aria-label={`Remove ${item.name}`}\n >\n <Trash2 className=\"size-4\" />\n </button>\n )}\n </div>\n </div>\n\n {/* Features - Collapsible */}\n {hasFeatures &&\n (() => {\n const mergedCount = mergeFeatures(item.features!).length;\n return (\n <Accordion type=\"single\" collapsible className=\"mt-3\">\n <AccordionItem value=\"features\" className=\"border-0\">\n <AccordionTrigger className=\"py-2 px-0 text-sm text-muted-foreground hover:text-foreground hover:no-underline\">\n <span className=\"flex items-center gap-1.5\">\n <Check className=\"size-3.5\" />\n {mergedCount} {mergedCount === 1 ? 'feature' : 'features'}{' '}\n included\n </span>\n </AccordionTrigger>\n <AccordionContent className=\"pt-2 pb-0\">\n {renderFeatures(item.features!)}\n </AccordionContent>\n </AccordionItem>\n </Accordion>\n );\n })()}\n </div>\n </div>\n </div>\n );\n })\n )}\n </div>\n\n {/* Totals */}\n {items.length > 0 && (\n <div className=\"border-t border-border bg-muted/20\">\n <div className=\"p-4 space-y-2\">\n {/* Subtotal - already in selected currency (from currencyPrices) */}\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-muted-foreground\">\n {tr('billing.checkout.subtotal', 'Subtotal')}\n </span>\n <span className=\"text-foreground\">\n {showInCredits ? formatAmount(subtotalUSD) : formatAmount(subtotal)}\n </span>\n </div>\n\n {/* Discount */}\n {discountAmount > 0 && (\n <div className=\"flex items-center justify-between text-sm\">\n <span className={statusTokens.success.text}>Discount</span>\n <span className={statusTokens.success.text}>-{formatAmount(discountAmount)}</span>\n </div>\n )}\n\n {/* Tax - calculated on the subtotal in selected currency */}\n {isCalculatingTax ? (\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-muted-foreground\">Tax</span>\n <span className=\"text-muted-foreground animate-pulse\">Calculating…</span>\n </div>\n ) : taxAmount > 0 && taxRate > 0 ? (\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-muted-foreground\">\n {taxType || 'Tax'} ({(taxRate * 100).toFixed(0)}%)\n </span>\n <span className=\"text-foreground\">{formatAmount(taxAmount)}</span>\n </div>\n ) : null}\n\n {/* Divider */}\n <div className=\"border-t border-border my-2\" />\n\n {/* Total - show in credits or selected currency */}\n {showInCredits ? (\n <>\n {/* Credit payment: Show total in credits */}\n <div className=\"flex items-center justify-between\">\n <span className=\"font-semibold text-foreground\">\n {tr('billing.checkout.total', 'Total')}\n </span>\n <span className=\"text-xl font-bold text-foreground\">\n {usdToCredits(subtotalUSD).toLocaleString(undefined, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n })}{' '}\n Credits\n </span>\n </div>\n <p className=\"text-xs text-center text-muted-foreground pt-2\">\n Paying with account credits\n </p>\n </>\n ) : (\n <>\n {/* Card payment: Show total in selected currency */}\n {/* Items already have localized prices from currencyPrices - no conversion needed */}\n <div className=\"flex items-center justify-between\">\n <span className=\"font-semibold text-foreground\">Total in {currency}</span>\n <span className=\"text-xl font-bold text-foreground\">\n {formatCurrency(total, currency)}\n </span>\n </div>\n </>\n )}\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OrderSummary.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/OrderSummary.tsx"],"sourcesContent":["/**\n * Order Summary Component\n * Displays checkout items, discounts, taxes, and total\n */\n\nimport { type FC, useMemo } from 'react';\nimport {\n Package,\n Blocks,\n Coins,\n Minus,\n Plus,\n Trash2,\n Receipt,\n Check,\n ShoppingCart,\n} from 'lucide-react';\nimport {\n Accordion,\n AccordionItem,\n AccordionTrigger,\n AccordionContent,\n} from '@burdenoff/fe-libs/ui';\nimport type { CheckoutItem, Currency, CheckoutFeature } from '../types';\nimport { formatCurrency } from '../../../shared/utils/format';\nimport { categoryTokens, statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n/**\n * Credit conversion rate: How many credits equal 1 USD\n * This should match the backend creditRate.rateUSD (default 0.01 means 100 credits = $1)\n */\nconst CREDITS_PER_USD = 100;\n\ninterface OrderSummaryProps {\n items: CheckoutItem[];\n subtotal: number;\n discountAmount: number;\n taxAmount: number;\n taxRate: number;\n taxType?: string;\n total: number;\n currency: Currency;\n showInCredits?: boolean;\n /** Whether tax is being calculated */\n isCalculatingTax?: boolean;\n onUpdateQuantity?: (itemId: string, quantity: number) => void;\n onRemoveItem?: (itemId: string) => void;\n isEditable?: boolean;\n}\n\nexport const OrderSummary: FC<OrderSummaryProps> = ({\n items,\n subtotal,\n discountAmount,\n taxAmount,\n taxRate,\n taxType,\n total,\n currency,\n showInCredits = false,\n isCalculatingTax = false,\n onUpdateQuantity,\n onRemoveItem,\n isEditable = true,\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 getItemIcon = (type: CheckoutItem['type']) => {\n switch (type) {\n case 'PLAN':\n return <Package className=\"size-5\" />;\n case 'ADDON':\n return <Blocks className=\"size-5\" />;\n case 'CREDITS':\n return <Coins className=\"size-5\" />;\n case 'ORDER':\n return <ShoppingCart className=\"size-5\" />;\n default:\n return <Package className=\"size-5\" />;\n }\n };\n\n const getItemTypeBadge = (type: CheckoutItem['type']) => {\n const badges = {\n PLAN: {\n label: 'Plan',\n className: `${categoryTokens.plan.bg} ${categoryTokens.plan.text}`,\n },\n ADDON: {\n label: 'Add-on',\n className: `${categoryTokens.addon.bg} ${categoryTokens.addon.text}`,\n },\n CREDITS: {\n label: 'Credits',\n className: `${categoryTokens.credits.bg} ${categoryTokens.credits.text}`,\n },\n ORDER: {\n label: 'Store Order',\n className: 'bg-status-success-bg-subtle text-status-success-text',\n },\n };\n return badges[type];\n };\n\n // Convert USD amount to credits\n const usdToCredits = (usdAmount: number) => usdAmount * CREDITS_PER_USD;\n\n // Subtotal in USD — used for credit conversion (items may be priced in local currency)\n const subtotalUSD = useMemo(\n () => items.reduce((sum, item) => sum + (item.priceUSD ?? item.price) * item.quantity, 0),\n [items]\n );\n\n // Format amount - show in credits if paying with credits, otherwise in selected currency\n // Items now have their prices stored in the selected currency (from currencyPrices)\n // For credit conversion always use priceUSD (base USD price) — not the localized price.\n const formatAmount = (amount: number, item?: CheckoutItem) => {\n if (showInCredits) {\n const usdAmount = item?.priceUSD ?? amount;\n const creditsAmount = usdToCredits(usdAmount);\n return `${creditsAmount.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 0 })} Credits`;\n }\n // Format in selected currency (items now have localized prices)\n return formatCurrency(amount, currency);\n };\n\n // Format limit value for display\n const formatLimitValue = (_key: string, value: unknown): string => {\n if (typeof value === 'boolean') {\n return value ? 'Included' : 'Not included';\n }\n if (typeof value === 'number') {\n if (value === -1 || value === Infinity) return 'Unlimited';\n return value.toLocaleString();\n }\n return String(value);\n };\n\n // Merge duplicate features by name, combining numeric limits\n const mergeFeatures = (features: CheckoutFeature[]): CheckoutFeature[] => {\n const merged = new Map<string, CheckoutFeature>();\n\n for (const feature of features) {\n const existing = merged.get(feature.name);\n if (existing) {\n // Merge limits - add numeric values together\n const mergedLimits: Record<string, number | string | boolean> = { ...existing.limits };\n if (feature.limits) {\n for (const [key, value] of Object.entries(feature.limits)) {\n const existingValue = mergedLimits[key];\n if (typeof value === 'number' && typeof existingValue === 'number') {\n // Both are numbers - add them (unless unlimited)\n if (existingValue === -1 || value === -1) {\n mergedLimits[key] = -1; // Unlimited stays unlimited\n } else {\n mergedLimits[key] = existingValue + value;\n }\n } else {\n // Non-numeric or first occurrence - just set\n mergedLimits[key] = value;\n }\n }\n }\n existing.limits = mergedLimits;\n } else {\n // Clone the feature to avoid mutating original\n merged.set(feature.name, {\n ...feature,\n limits: feature.limits ? { ...feature.limits } : undefined,\n });\n }\n }\n\n return Array.from(merged.values());\n };\n\n // Render features list\n const renderFeatures = (features: CheckoutFeature[]) => {\n if (!features || features.length === 0) return null;\n\n const mergedFeatures = mergeFeatures(features);\n\n return (\n <ul className=\"space-y-2\">\n {mergedFeatures.map((feature) => (\n <li key={feature.id} className=\"flex items-start gap-2\">\n <Check className={`size-4 ${statusTokens.success.icon} shrink-0 mt-0.5`} />\n <div className=\"flex-1 min-w-0\">\n <span className=\"text-sm text-text-primary\">{feature.name}</span>\n {feature.limits && Object.keys(feature.limits).length > 0 && (\n <div className=\"mt-1 flex flex-wrap gap-1.5\">\n {Object.entries(feature.limits).map(([key, value]) => (\n <span\n key={key}\n className=\"inline-flex items-center text-xs px-1.5 py-0.5 rounded bg-bg-sunken text-text-secondary\"\n >\n {key}: {formatLimitValue(key, value)}\n </span>\n ))}\n </div>\n )}\n </div>\n </li>\n ))}\n </ul>\n );\n };\n\n return (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface overflow-hidden\">\n {/* Header */}\n <div className=\"px-4 py-3 border-b border-border-subtle bg-bg-sunken\">\n <div className=\"flex items-center gap-2\">\n <Receipt className=\"size-4 text-text-secondary\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('billing.checkout.orderSummary', 'Order Summary')}\n </h3>\n </div>\n </div>\n\n {/* Items */}\n <div className=\"divide-y divide-border-subtle\">\n {items.length === 0 ? (\n <div className=\"p-6 text-center\">\n <Package className=\"mx-auto size-8 text-text-secondary\" />\n <p className=\"mt-2 text-sm text-text-secondary\">No items in checkout</p>\n </div>\n ) : (\n items.map((item) => {\n const badge = getItemTypeBadge(item.type);\n const hasFeatures = item.features && item.features.length > 0;\n\n return (\n <div key={item.id} className=\"p-4\">\n <div className=\"flex items-start gap-3\">\n {/* Icon */}\n <div className=\"p-2 rounded-lg bg-bg-sunken text-text-secondary shrink-0\">\n {getItemIcon(item.type)}\n </div>\n\n {/* Item details */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-medium text-text-primary\">{item.name}</span>\n <span className={`text-xs px-1.5 py-0.5 rounded ${badge.className}`}>\n {badge.label}\n </span>\n </div>\n {item.description && (\n <p className=\"text-sm text-text-secondary mt-0.5 line-clamp-2\">\n {item.description}\n </p>\n )}\n\n {/* Quantity controls */}\n <div className=\"flex items-center justify-between mt-3\">\n {isEditable && item.type !== 'PLAN' ? (\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity?.(item.id, item.quantity - 1)}\n disabled={item.quantity <= 1}\n className=\"p-2 rounded border border-border-subtle hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed min-w-[36px] min-h-[36px] flex items-center justify-center\"\n aria-label={`Decrease ${item.name} quantity`}\n >\n <Minus className=\"size-3.5\" />\n </button>\n <span className=\"w-8 text-center text-sm font-medium\">\n {item.quantity}\n </span>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity?.(item.id, item.quantity + 1)}\n className=\"p-2 rounded border border-border-subtle hover:bg-bg-sunken min-w-[36px] min-h-[36px] flex items-center justify-center\"\n aria-label={`Increase ${item.name} quantity`}\n >\n <Plus className=\"size-3.5\" />\n </button>\n </div>\n ) : (\n <span className=\"text-sm text-text-secondary\">Qty: {item.quantity}</span>\n )}\n\n <div className=\"flex items-center gap-3\">\n <span className=\"font-medium text-text-primary\">\n {formatAmount(item.price * item.quantity, item)}\n </span>\n {isEditable && onRemoveItem && (\n <button\n type=\"button\"\n onClick={() => onRemoveItem(item.id)}\n className=\"p-1.5 rounded text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle\"\n aria-label={`Remove ${item.name}`}\n >\n <Trash2 className=\"size-4\" />\n </button>\n )}\n </div>\n </div>\n\n {/* Features - Collapsible */}\n {hasFeatures &&\n (() => {\n const mergedCount = mergeFeatures(item.features!).length;\n return (\n <Accordion type=\"single\" collapsible className=\"mt-3\">\n <AccordionItem value=\"features\" className=\"border-0\">\n <AccordionTrigger className=\"py-2 px-0 text-sm text-text-secondary hover:text-text-primary hover:no-underline\">\n <span className=\"flex items-center gap-1.5\">\n <Check className=\"size-3.5\" />\n {mergedCount} {mergedCount === 1 ? 'feature' : 'features'}{' '}\n included\n </span>\n </AccordionTrigger>\n <AccordionContent className=\"pt-2 pb-0\">\n {renderFeatures(item.features!)}\n </AccordionContent>\n </AccordionItem>\n </Accordion>\n );\n })()}\n </div>\n </div>\n </div>\n );\n })\n )}\n </div>\n\n {/* Totals */}\n {items.length > 0 && (\n <div className=\"border-t border-border-subtle bg-bg-sunken\">\n <div className=\"p-4 space-y-2\">\n {/* Subtotal - already in selected currency (from currencyPrices) */}\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n {tr('billing.checkout.subtotal', 'Subtotal')}\n </span>\n <span className=\"text-text-primary\">\n {showInCredits ? formatAmount(subtotalUSD) : formatAmount(subtotal)}\n </span>\n </div>\n\n {/* Discount */}\n {discountAmount > 0 && (\n <div className=\"flex items-center justify-between text-sm\">\n <span className={statusTokens.success.text}>Discount</span>\n <span className={statusTokens.success.text}>-{formatAmount(discountAmount)}</span>\n </div>\n )}\n\n {/* Tax - calculated on the subtotal in selected currency */}\n {isCalculatingTax ? (\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">Tax</span>\n <span className=\"text-text-secondary animate-pulse\">Calculating…</span>\n </div>\n ) : taxAmount > 0 && taxRate > 0 ? (\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n {taxType || 'Tax'} ({(taxRate * 100).toFixed(0)}%)\n </span>\n <span className=\"text-text-primary\">{formatAmount(taxAmount)}</span>\n </div>\n ) : null}\n\n {/* Divider */}\n <div className=\"border-t border-border-subtle my-2\" />\n\n {/* Total - show in credits or selected currency */}\n {showInCredits ? (\n <>\n {/* Credit payment: Show total in credits */}\n <div className=\"flex items-center justify-between\">\n <span className=\"font-semibold text-text-primary\">\n {tr('billing.checkout.total', 'Total')}\n </span>\n <span className=\"text-xl font-bold text-text-primary\">\n {usdToCredits(subtotalUSD).toLocaleString(undefined, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n })}{' '}\n Credits\n </span>\n </div>\n <p className=\"text-xs text-center text-text-secondary pt-2\">\n Paying with account credits\n </p>\n </>\n ) : (\n <>\n {/* Card payment: Show total in selected currency */}\n {/* Items already have localized prices from currencyPrices - no conversion needed */}\n <div className=\"flex items-center justify-between\">\n <span className=\"font-semibold text-text-primary\">Total in {currency}</span>\n <span className=\"text-xl font-bold text-text-primary\">\n {formatCurrency(total, currency)}\n </span>\n </div>\n </>\n )}\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -57,14 +57,14 @@ var u = ({ value: u, onChange: d, onValidate: f, onClear: p, isApplied: m, disco
|
|
|
57
57
|
className: "space-y-2",
|
|
58
58
|
children: [
|
|
59
59
|
/* @__PURE__ */ s("label", {
|
|
60
|
-
className: "text-sm font-medium text-
|
|
60
|
+
className: "text-sm font-medium text-text-primary",
|
|
61
61
|
children: b("billing.checkout.promoCode", "Promo Code")
|
|
62
62
|
}),
|
|
63
63
|
/* @__PURE__ */ c("div", {
|
|
64
64
|
className: "flex gap-2",
|
|
65
65
|
children: [/* @__PURE__ */ c("div", {
|
|
66
66
|
className: "relative flex-1",
|
|
67
|
-
children: [/* @__PURE__ */ s(a, { className: "absolute left-3 top-1/2 -translate-y-1/2 size-4 text-
|
|
67
|
+
children: [/* @__PURE__ */ s(a, { className: "absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary" }), /* @__PURE__ */ s("input", {
|
|
68
68
|
type: "text",
|
|
69
69
|
value: u,
|
|
70
70
|
onChange: (e) => {
|
|
@@ -74,23 +74,23 @@ var u = ({ value: u, onChange: d, onValidate: f, onClear: p, isApplied: m, disco
|
|
|
74
74
|
placeholder: "Enter code",
|
|
75
75
|
disabled: _,
|
|
76
76
|
className: `
|
|
77
|
-
w-full pl-[3.25rem] pr-4 py-2.5 rounded-lg border bg-
|
|
78
|
-
placeholder:text-
|
|
79
|
-
focus:outline-none focus:ring-2 focus:ring-
|
|
77
|
+
w-full pl-[3.25rem] pr-4 py-2.5 rounded-lg border bg-bg-surface text-text-primary
|
|
78
|
+
placeholder:text-text-placeholder
|
|
79
|
+
focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong
|
|
80
80
|
disabled:opacity-50 disabled:cursor-not-allowed
|
|
81
|
-
${E ? "border-
|
|
81
|
+
${E ? "border-status-error-border focus:ring-status-error-border focus:border-status-error-border" : "border-border-subtle"}
|
|
82
82
|
`
|
|
83
83
|
})]
|
|
84
84
|
}), /* @__PURE__ */ s("button", {
|
|
85
85
|
type: "button",
|
|
86
86
|
onClick: C,
|
|
87
87
|
disabled: !u.trim() || _,
|
|
88
|
-
className: "px-4 py-2.5 rounded-lg border border-border bg-
|
|
88
|
+
className: "px-4 py-2.5 rounded-lg border border-border-subtle bg-bg-sunken text-text-primary font-medium\n hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed\n transition-colors duration-200",
|
|
89
89
|
children: _ ? /* @__PURE__ */ s(i, { className: "size-4 animate-spin" }) : "Apply"
|
|
90
90
|
})]
|
|
91
91
|
}),
|
|
92
92
|
E && /* @__PURE__ */ c("p", {
|
|
93
|
-
className: "text-sm text-
|
|
93
|
+
className: "text-sm text-status-error-text flex items-center gap-1.5",
|
|
94
94
|
children: [/* @__PURE__ */ s(o, { className: "size-3.5" }), E]
|
|
95
95
|
})
|
|
96
96
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PromoCodeInput.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/PromoCodeInput.tsx"],"sourcesContent":["/**\n * Promo Code Input Component\n * Input field for entering and validating discount codes\n */\n\nimport { type FC, useState, useCallback } from 'react';\nimport { Tag, Check, X, Loader2 } from 'lucide-react';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface PromoCodeInputProps {\n value: string;\n onChange: (value: string) => void;\n onValidate: () => Promise<{ valid: boolean; error?: string }>;\n onClear: () => void;\n isApplied: boolean;\n discountValue?: number;\n discountType?: 'PERCENTAGE' | 'FIXED';\n isValidating?: boolean;\n error?: string;\n}\n\nexport const PromoCodeInput: FC<PromoCodeInputProps> = ({\n value,\n onChange,\n onValidate,\n onClear,\n isApplied,\n discountValue,\n discountType: _discountType,\n isValidating = false,\n error,\n}) => {\n void _discountType;\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 [localError, setLocalError] = useState<string | null>(null);\n\n const handleValidate = useCallback(async () => {\n if (!value.trim()) {\n setLocalError('Please enter a promo code');\n return;\n }\n setLocalError(null);\n const result = await onValidate();\n if (!result.valid && result.error) {\n setLocalError(result.error);\n }\n }, [value, onValidate]);\n\n const handleClear = useCallback(() => {\n setLocalError(null);\n onClear();\n }, [onClear]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n handleValidate();\n }\n },\n [handleValidate]\n );\n\n const displayError = error || localError;\n\n if (isApplied && discountValue) {\n // Show applied discount\n return (\n <div\n className={`rounded-lg border ${statusTokens.success.border} ${statusTokens.success.bg} p-3`}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <div\n className={`p-1.5 rounded-full ${statusTokens.success.bg} ${statusTokens.success.icon}`}\n >\n <Check className=\"size-4\" />\n </div>\n <div>\n <div className=\"flex items-center gap-1.5\">\n <span className={`font-medium ${statusTokens.success.text}`}>\n {value.toUpperCase()}\n </span>\n <span className={`text-sm ${statusTokens.success.text}`}>applied</span>\n </div>\n <p className={`text-sm ${statusTokens.success.text}`}>\n {/* discountValue now contains the actual discount amount, not percentage */}\n <span className=\"flex items-center gap-0.5\">\n {discountValue.toFixed(2)} off your order\n </span>\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={handleClear}\n className={`p-1.5 rounded-full hover:${statusTokens.success.bg} ${statusTokens.success.icon}`}\n >\n <X className=\"size-4\" />\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium text-
|
|
1
|
+
{"version":3,"file":"PromoCodeInput.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/PromoCodeInput.tsx"],"sourcesContent":["/**\n * Promo Code Input Component\n * Input field for entering and validating discount codes\n */\n\nimport { type FC, useState, useCallback } from 'react';\nimport { Tag, Check, X, Loader2 } from 'lucide-react';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface PromoCodeInputProps {\n value: string;\n onChange: (value: string) => void;\n onValidate: () => Promise<{ valid: boolean; error?: string }>;\n onClear: () => void;\n isApplied: boolean;\n discountValue?: number;\n discountType?: 'PERCENTAGE' | 'FIXED';\n isValidating?: boolean;\n error?: string;\n}\n\nexport const PromoCodeInput: FC<PromoCodeInputProps> = ({\n value,\n onChange,\n onValidate,\n onClear,\n isApplied,\n discountValue,\n discountType: _discountType,\n isValidating = false,\n error,\n}) => {\n void _discountType;\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 [localError, setLocalError] = useState<string | null>(null);\n\n const handleValidate = useCallback(async () => {\n if (!value.trim()) {\n setLocalError('Please enter a promo code');\n return;\n }\n setLocalError(null);\n const result = await onValidate();\n if (!result.valid && result.error) {\n setLocalError(result.error);\n }\n }, [value, onValidate]);\n\n const handleClear = useCallback(() => {\n setLocalError(null);\n onClear();\n }, [onClear]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n handleValidate();\n }\n },\n [handleValidate]\n );\n\n const displayError = error || localError;\n\n if (isApplied && discountValue) {\n // Show applied discount\n return (\n <div\n className={`rounded-lg border ${statusTokens.success.border} ${statusTokens.success.bg} p-3`}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <div\n className={`p-1.5 rounded-full ${statusTokens.success.bg} ${statusTokens.success.icon}`}\n >\n <Check className=\"size-4\" />\n </div>\n <div>\n <div className=\"flex items-center gap-1.5\">\n <span className={`font-medium ${statusTokens.success.text}`}>\n {value.toUpperCase()}\n </span>\n <span className={`text-sm ${statusTokens.success.text}`}>applied</span>\n </div>\n <p className={`text-sm ${statusTokens.success.text}`}>\n {/* discountValue now contains the actual discount amount, not percentage */}\n <span className=\"flex items-center gap-0.5\">\n {discountValue.toFixed(2)} off your order\n </span>\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={handleClear}\n className={`p-1.5 rounded-full hover:${statusTokens.success.bg} ${statusTokens.success.icon}`}\n >\n <X className=\"size-4\" />\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium text-text-primary\">\n {tr('billing.checkout.promoCode', 'Promo Code')}\n </label>\n <div className=\"flex gap-2\">\n <div className=\"relative flex-1\">\n <Tag className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n type=\"text\"\n value={value}\n onChange={(e) => {\n onChange(e.target.value.toUpperCase());\n setLocalError(null);\n }}\n onKeyDown={handleKeyDown}\n placeholder=\"Enter code\"\n disabled={isValidating}\n className={`\n w-full pl-[3.25rem] pr-4 py-2.5 rounded-lg border bg-bg-surface text-text-primary\n placeholder:text-text-placeholder\n focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n disabled:opacity-50 disabled:cursor-not-allowed\n ${\n displayError\n ? 'border-status-error-border focus:ring-status-error-border focus:border-status-error-border'\n : 'border-border-subtle'\n }\n `}\n />\n </div>\n <button\n type=\"button\"\n onClick={handleValidate}\n disabled={!value.trim() || isValidating}\n className=\"px-4 py-2.5 rounded-lg border border-border-subtle bg-bg-sunken text-text-primary font-medium\n hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed\n transition-colors duration-200\"\n >\n {isValidating ? <Loader2 className=\"size-4 animate-spin\" /> : 'Apply'}\n </button>\n </div>\n\n {/* Error message */}\n {displayError && (\n <p className=\"text-sm text-status-error-text flex items-center gap-1.5\">\n <X className=\"size-3.5\" />\n {displayError}\n </p>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;AAsBA,IAAa,KAA2C,EACtD,UACA,aACA,eACA,YACA,cACA,kBACA,cAAc,GACd,kBAAe,IACf,eACI;CAEJ,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,CAAC,GAAY,KAAiB,EAAwB,KAAK,EAE3D,IAAiB,EAAY,YAAY;AAC7C,MAAI,CAAC,EAAM,MAAM,EAAE;AACjB,KAAc,4BAA4B;AAC1C;;AAEF,IAAc,KAAK;EACnB,IAAM,IAAS,MAAM,GAAY;AACjC,EAAI,CAAC,EAAO,SAAS,EAAO,SAC1B,EAAc,EAAO,MAAM;IAE5B,CAAC,GAAO,EAAW,CAAC,EAEjB,IAAc,QAAkB;AAEpC,EADA,EAAc,KAAK,EACnB,GAAS;IACR,CAAC,EAAQ,CAAC,EAEP,IAAgB,GACnB,MAA2B;AAC1B,EAAI,EAAE,QAAQ,YACZ,EAAE,gBAAgB,EAClB,GAAgB;IAGpB,CAAC,EAAe,CACjB,EAEK,IAAe,KAAS;AA0C9B,QAxCI,KAAa,IAGb,kBAAC,OAAD;EACE,WAAW,qBAAqB,EAAa,QAAQ,OAAO,GAAG,EAAa,QAAQ,GAAG;YAEvF,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KACE,WAAW,sBAAsB,EAAa,QAAQ,GAAG,GAAG,EAAa,QAAQ;eAEjF,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;KACxB,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAW,eAAe,EAAa,QAAQ;gBAClD,EAAM,aAAa;MACf,CAAA,EACP,kBAAC,QAAD;MAAM,WAAW,WAAW,EAAa,QAAQ;gBAAQ;MAAc,CAAA,CACnE;QACN,kBAAC,KAAD;KAAG,WAAW,WAAW,EAAa,QAAQ;eAE5C,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACG,EAAc,QAAQ,EAAE,EAAC,kBACrB;;KACL,CAAA,CACA,EAAA,CAAA,CACF;OACN,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,WAAW,4BAA4B,EAAa,QAAQ,GAAG,GAAG,EAAa,QAAQ;cAEvF,kBAAC,GAAD,EAAG,WAAU,UAAW,CAAA;IACjB,CAAA,CACL;;EACF,CAAA,GAKR,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,SAAD;IAAO,WAAU;cACd,EAAG,8BAA8B,aAAa;IACzC,CAAA;GACR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAK,WAAU,uEAAwE,CAAA,EACvF,kBAAC,SAAD;MACE,MAAK;MACE;MACP,WAAW,MAAM;AAEf,OADA,EAAS,EAAE,OAAO,MAAM,aAAa,CAAC,EACtC,EAAc,KAAK;;MAErB,WAAW;MACX,aAAY;MACZ,UAAU;MACV,WAAW;;;;;gBAMP,IACI,+FACA,uBACL;;MAEH,CAAA,CACE;QACN,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,UAAU,CAAC,EAAM,MAAM,IAAI;KAC3B,WAAU;eAIT,IAAe,kBAAC,GAAD,EAAS,WAAU,uBAAwB,CAAA,GAAG;KACvD,CAAA,CACL;;GAGL,KACC,kBAAC,KAAD;IAAG,WAAU;cAAb,CACE,kBAAC,GAAD,EAAG,WAAU,YAAa,CAAA,EACzB,EACC;;GAEF"}
|
|
@@ -96,15 +96,15 @@ var f = ({ orderId: f, keyId: p, amount: m, currency: h, name: g = "Burdenoff",
|
|
|
96
96
|
className: "space-y-4",
|
|
97
97
|
children: [
|
|
98
98
|
/* @__PURE__ */ u("div", {
|
|
99
|
-
className: "flex items-center justify-center gap-1.5 text-xs text-
|
|
99
|
+
className: "flex items-center justify-center gap-1.5 text-xs text-text-secondary",
|
|
100
100
|
children: [/* @__PURE__ */ l(s, { className: "size-3" }), "Secured by Razorpay"]
|
|
101
101
|
}),
|
|
102
102
|
k && /* @__PURE__ */ u("div", {
|
|
103
|
-
className: "flex items-center gap-2 p-3 rounded-lg bg-
|
|
103
|
+
className: "flex items-center gap-2 p-3 rounded-lg bg-status-error-bg-subtle text-status-error-text text-sm",
|
|
104
104
|
children: [/* @__PURE__ */ l(i, { className: "size-4 shrink-0" }), k]
|
|
105
105
|
}),
|
|
106
106
|
!j && /* @__PURE__ */ u("div", {
|
|
107
|
-
className: "flex items-center justify-center gap-2 py-4 text-
|
|
107
|
+
className: "flex items-center justify-center gap-2 py-4 text-text-secondary",
|
|
108
108
|
children: [/* @__PURE__ */ l(o, { className: "size-4 animate-spin" }), /* @__PURE__ */ l("span", {
|
|
109
109
|
className: "text-sm",
|
|
110
110
|
children: E("billing.checkout.loadingGateway", "Loading payment gateway...")
|
|
@@ -116,7 +116,7 @@ var f = ({ orderId: f, keyId: p, amount: m, currency: h, name: g = "Burdenoff",
|
|
|
116
116
|
type: "button",
|
|
117
117
|
onClick: C,
|
|
118
118
|
disabled: D === "processing" || w,
|
|
119
|
-
className: "flex-1 px-4 py-3 rounded-lg border border-border text-
|
|
119
|
+
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",
|
|
120
120
|
children: "Cancel"
|
|
121
121
|
}), /* @__PURE__ */ l("button", {
|
|
122
122
|
type: "button",
|
|
@@ -127,7 +127,7 @@ var f = ({ orderId: f, keyId: p, amount: m, currency: h, name: g = "Burdenoff",
|
|
|
127
127
|
})]
|
|
128
128
|
}),
|
|
129
129
|
/* @__PURE__ */ l("p", {
|
|
130
|
-
className: "text-center text-xs text-
|
|
130
|
+
className: "text-center text-xs text-text-secondary",
|
|
131
131
|
children: "Your payment is secured with 256-bit encryption. Powered by Razorpay."
|
|
132
132
|
})
|
|
133
133
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RazorpayPaymentButton.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/RazorpayPaymentButton.tsx"],"sourcesContent":["/**\n * {tr('billing.checkout.razorpayPayment', 'Razorpay Payment')} Button Component\n * Integrates with Razorpay checkout for INR payments\n */\n\nimport { type FC, useState, useCallback, useEffect } from 'react';\nimport { Lock, Loader2, Check, AlertCircle } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type {\n RazorpaySuccessResponse,\n CheckoutRazorpayOptions,\n CheckoutRazorpayInstance,\n} from '../types';\nimport { getPaymentProviderThemeColor } from '../../../shared/utils';\n\ntype RazorpayCheckoutOptions = CheckoutRazorpayOptions & {\n customer_id?: string;\n remember_customer?: boolean;\n handler: (response: RazorpaySuccessResponse) => void;\n};\n\ninterface RazorpayPaymentButtonProps {\n orderId: string;\n keyId: string;\n amount: number; // In paise (smallest currency unit)\n currency: string;\n name?: string;\n description?: string;\n customerId?: string; // Razorpay customer ID for saved cards\n prefill?: {\n name?: string;\n email?: string;\n contact?: string;\n };\n notes?: Record<string, string>;\n onSuccess: (response: RazorpaySuccessResponse) => void;\n onError: (error: string) => void;\n onCancel?: () => void;\n disabled?: boolean;\n}\n\ntype PaymentState = 'idle' | 'loading' | 'processing' | 'success' | 'error';\n\nexport const RazorpayPaymentButton: FC<RazorpayPaymentButtonProps> = ({\n orderId,\n keyId,\n amount,\n currency,\n name = 'Burdenoff',\n description = 'Purchase',\n customerId,\n prefill,\n notes,\n onSuccess,\n onError,\n onCancel,\n disabled = 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 [isRazorpayLoaded, setIsRazorpayLoaded] = useState(false);\n\n // Load Razorpay SDK dynamically\n useEffect(() => {\n if (typeof window !== 'undefined' && !window.Razorpay) {\n const script = document.createElement('script');\n script.src = 'https://checkout.razorpay.com/v1/checkout.js';\n script.async = true;\n script.onload = () => {\n setIsRazorpayLoaded(true);\n };\n script.onerror = () => {\n setError('Failed to load payment gateway');\n };\n document.body.appendChild(script);\n } else if (window.Razorpay) {\n setIsRazorpayLoaded(true);\n }\n }, []);\n\n const handlePayment = useCallback(() => {\n if (!isRazorpayLoaded || !window.Razorpay) {\n setError('Payment gateway not ready');\n return;\n }\n\n setPaymentState('processing');\n setError(null);\n\n // Build Razorpay options - customer_id enables saved cards feature\n const options: RazorpayCheckoutOptions = {\n key: keyId,\n amount: amount, // Amount in paise\n currency: currency.toUpperCase(),\n name,\n description,\n order_id: orderId,\n prefill: prefill || {},\n notes: notes || {},\n handler: (response: RazorpaySuccessResponse) => {\n setPaymentState('success');\n onSuccess(response);\n },\n modal: {\n ondismiss: () => {\n setPaymentState('idle');\n onCancel?.();\n },\n escape: true,\n confirm_close: true,\n },\n };\n\n const providerThemeColor = getPaymentProviderThemeColor();\n if (providerThemeColor) {\n options.theme = { color: providerThemeColor };\n }\n\n // Add customer_id for saved cards feature\n if (customerId) {\n options.customer_id = customerId;\n options.remember_customer = true;\n }\n\n try {\n const RazorpayConstructor = (\n window as unknown as {\n Razorpay?: new (options: RazorpayCheckoutOptions) => CheckoutRazorpayInstance;\n }\n ).Razorpay;\n\n if (!RazorpayConstructor) {\n throw new Error('Payment gateway not ready');\n }\n\n const razorpay = new RazorpayConstructor(options);\n\n razorpay.on('payment.failed', (response: unknown) => {\n const errorResponse = response as {\n error?: {\n code?: string;\n description?: string;\n reason?: string;\n };\n };\n const errorMessage =\n errorResponse?.error?.description || errorResponse?.error?.reason || 'Payment failed';\n setError(errorMessage);\n setPaymentState('error');\n onError(errorMessage);\n });\n\n razorpay.open();\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Failed to open payment gateway';\n setError(errorMessage);\n setPaymentState('error');\n onError(errorMessage);\n }\n }, [\n isRazorpayLoaded,\n keyId,\n amount,\n currency,\n name,\n description,\n customerId,\n orderId,\n prefill,\n notes,\n onSuccess,\n onError,\n onCancel,\n ]);\n\n // Format amount for display (convert from paise to rupees)\n const displayAmount = (amount / 100).toLocaleString('en-IN', {\n style: 'currency',\n currency: 'INR',\n });\n\n // Render success state\n if (paymentState === 'success') {\n return (\n <div className=\"p-6 rounded-lg border border-border-subtle bg-status-success-bg-subtle\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-full bg-status-success-bg-subtle text-status-success-text\">\n <Check className=\"size-6\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-status-success-text\">Payment Successful!</h3>\n <p className=\"text-sm text-status-success-text\">\n Your payment has been processed successfully.\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {/* Security badge */}\n <div className=\"flex items-center justify-center gap-1.5 text-xs text-muted-foreground\">\n <Lock className=\"size-3\" />\n Secured by Razorpay\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 {/* Loading state */}\n {!isRazorpayLoaded && (\n <div className=\"flex items-center justify-center gap-2 py-4 text-muted-foreground\">\n <Loader2 className=\"size-4 animate-spin\" />\n <span className=\"text-sm\">\n {tr('billing.checkout.loadingGateway', 'Loading payment gateway...')}\n </span>\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=\"button\"\n onClick={handlePayment}\n disabled={!isRazorpayLoaded || paymentState === 'processing' || disabled}\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-bg/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 <>Pay {displayAmount}</>\n )}\n </button>\n </div>\n\n {/* Security note */}\n <p className=\"text-center text-xs text-muted-foreground\">\n Your payment is secured with 256-bit encryption. Powered by Razorpay.\n </p>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RazorpayPaymentButton.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/RazorpayPaymentButton.tsx"],"sourcesContent":["/**\n * {tr('billing.checkout.razorpayPayment', 'Razorpay Payment')} Button Component\n * Integrates with Razorpay checkout for INR payments\n */\n\nimport { type FC, useState, useCallback, useEffect } from 'react';\nimport { Lock, Loader2, Check, AlertCircle } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type {\n RazorpaySuccessResponse,\n CheckoutRazorpayOptions,\n CheckoutRazorpayInstance,\n} from '../types';\nimport { getPaymentProviderThemeColor } from '../../../shared/utils';\n\ntype RazorpayCheckoutOptions = CheckoutRazorpayOptions & {\n customer_id?: string;\n remember_customer?: boolean;\n handler: (response: RazorpaySuccessResponse) => void;\n};\n\ninterface RazorpayPaymentButtonProps {\n orderId: string;\n keyId: string;\n amount: number; // In paise (smallest currency unit)\n currency: string;\n name?: string;\n description?: string;\n customerId?: string; // Razorpay customer ID for saved cards\n prefill?: {\n name?: string;\n email?: string;\n contact?: string;\n };\n notes?: Record<string, string>;\n onSuccess: (response: RazorpaySuccessResponse) => void;\n onError: (error: string) => void;\n onCancel?: () => void;\n disabled?: boolean;\n}\n\ntype PaymentState = 'idle' | 'loading' | 'processing' | 'success' | 'error';\n\nexport const RazorpayPaymentButton: FC<RazorpayPaymentButtonProps> = ({\n orderId,\n keyId,\n amount,\n currency,\n name = 'Burdenoff',\n description = 'Purchase',\n customerId,\n prefill,\n notes,\n onSuccess,\n onError,\n onCancel,\n disabled = 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 [isRazorpayLoaded, setIsRazorpayLoaded] = useState(false);\n\n // Load Razorpay SDK dynamically\n useEffect(() => {\n if (typeof window !== 'undefined' && !window.Razorpay) {\n const script = document.createElement('script');\n script.src = 'https://checkout.razorpay.com/v1/checkout.js';\n script.async = true;\n script.onload = () => {\n setIsRazorpayLoaded(true);\n };\n script.onerror = () => {\n setError('Failed to load payment gateway');\n };\n document.body.appendChild(script);\n } else if (window.Razorpay) {\n setIsRazorpayLoaded(true);\n }\n }, []);\n\n const handlePayment = useCallback(() => {\n if (!isRazorpayLoaded || !window.Razorpay) {\n setError('Payment gateway not ready');\n return;\n }\n\n setPaymentState('processing');\n setError(null);\n\n // Build Razorpay options - customer_id enables saved cards feature\n const options: RazorpayCheckoutOptions = {\n key: keyId,\n amount: amount, // Amount in paise\n currency: currency.toUpperCase(),\n name,\n description,\n order_id: orderId,\n prefill: prefill || {},\n notes: notes || {},\n handler: (response: RazorpaySuccessResponse) => {\n setPaymentState('success');\n onSuccess(response);\n },\n modal: {\n ondismiss: () => {\n setPaymentState('idle');\n onCancel?.();\n },\n escape: true,\n confirm_close: true,\n },\n };\n\n const providerThemeColor = getPaymentProviderThemeColor();\n if (providerThemeColor) {\n options.theme = { color: providerThemeColor };\n }\n\n // Add customer_id for saved cards feature\n if (customerId) {\n options.customer_id = customerId;\n options.remember_customer = true;\n }\n\n try {\n const RazorpayConstructor = (\n window as unknown as {\n Razorpay?: new (options: RazorpayCheckoutOptions) => CheckoutRazorpayInstance;\n }\n ).Razorpay;\n\n if (!RazorpayConstructor) {\n throw new Error('Payment gateway not ready');\n }\n\n const razorpay = new RazorpayConstructor(options);\n\n razorpay.on('payment.failed', (response: unknown) => {\n const errorResponse = response as {\n error?: {\n code?: string;\n description?: string;\n reason?: string;\n };\n };\n const errorMessage =\n errorResponse?.error?.description || errorResponse?.error?.reason || 'Payment failed';\n setError(errorMessage);\n setPaymentState('error');\n onError(errorMessage);\n });\n\n razorpay.open();\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Failed to open payment gateway';\n setError(errorMessage);\n setPaymentState('error');\n onError(errorMessage);\n }\n }, [\n isRazorpayLoaded,\n keyId,\n amount,\n currency,\n name,\n description,\n customerId,\n orderId,\n prefill,\n notes,\n onSuccess,\n onError,\n onCancel,\n ]);\n\n // Format amount for display (convert from paise to rupees)\n const displayAmount = (amount / 100).toLocaleString('en-IN', {\n style: 'currency',\n currency: 'INR',\n });\n\n // Render success state\n if (paymentState === 'success') {\n return (\n <div className=\"p-6 rounded-lg border border-border-subtle bg-status-success-bg-subtle\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-full bg-status-success-bg-subtle text-status-success-text\">\n <Check className=\"size-6\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-status-success-text\">Payment Successful!</h3>\n <p className=\"text-sm text-status-success-text\">\n Your payment has been processed successfully.\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {/* Security badge */}\n <div className=\"flex items-center justify-center gap-1.5 text-xs text-text-secondary\">\n <Lock className=\"size-3\" />\n Secured by Razorpay\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 {/* Loading state */}\n {!isRazorpayLoaded && (\n <div className=\"flex items-center justify-center gap-2 py-4 text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin\" />\n <span className=\"text-sm\">\n {tr('billing.checkout.loadingGateway', 'Loading payment gateway...')}\n </span>\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=\"button\"\n onClick={handlePayment}\n disabled={!isRazorpayLoaded || paymentState === 'processing' || disabled}\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-bg/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 <>Pay {displayAmount}</>\n )}\n </button>\n </div>\n\n {/* Security note */}\n <p className=\"text-center text-xs text-text-secondary\">\n Your payment is secured with 256-bit encryption. Powered by Razorpay.\n </p>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ var o = ({ enabled: o, onToggle: s, hasPaymentMethodSelected: c, selectedPayment
|
|
|
9
9
|
return n === e ? t : n;
|
|
10
10
|
}, [m, h] = e(!1), g = c;
|
|
11
11
|
return /* @__PURE__ */ i("div", {
|
|
12
|
-
className: "pt-3 border-t border-border space-y-2",
|
|
12
|
+
className: "pt-3 border-t border-border-subtle space-y-2",
|
|
13
13
|
children: [/* @__PURE__ */ i("div", {
|
|
14
14
|
className: "flex items-center gap-2",
|
|
15
15
|
children: [/* @__PURE__ */ r("button", {
|
|
@@ -20,8 +20,8 @@ var o = ({ enabled: o, onToggle: s, hasPaymentMethodSelected: c, selectedPayment
|
|
|
20
20
|
disabled: d || !g,
|
|
21
21
|
className: `
|
|
22
22
|
relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent
|
|
23
|
-
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-
|
|
24
|
-
${o ? "bg-primary" : "bg-
|
|
23
|
+
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:ring-offset-2
|
|
24
|
+
${o ? "bg-action-primary-bg" : "bg-bg-sunken"}
|
|
25
25
|
${(!g || d) && "opacity-50 cursor-not-allowed"}
|
|
26
26
|
`,
|
|
27
27
|
children: /* @__PURE__ */ r("span", { className: `
|
|
@@ -34,16 +34,16 @@ var o = ({ enabled: o, onToggle: s, hasPaymentMethodSelected: c, selectedPayment
|
|
|
34
34
|
children: [/* @__PURE__ */ i("div", {
|
|
35
35
|
className: "flex items-center gap-1.5",
|
|
36
36
|
children: [
|
|
37
|
-
/* @__PURE__ */ r(n, { className: "size-3.5 text-
|
|
37
|
+
/* @__PURE__ */ r(n, { className: "size-3.5 text-text-secondary shrink-0" }),
|
|
38
38
|
/* @__PURE__ */ r("span", {
|
|
39
|
-
className: "text-xs font-medium text-
|
|
39
|
+
className: "text-xs font-medium text-text-primary",
|
|
40
40
|
children: p("billing.checkout.autoRenew", "Auto-renew subscription")
|
|
41
41
|
}),
|
|
42
42
|
/* @__PURE__ */ r("button", {
|
|
43
43
|
type: "button",
|
|
44
44
|
onClick: () => h(!m),
|
|
45
|
-
className: "p-0.5 hover:bg-
|
|
46
|
-
children: /* @__PURE__ */ r(t, { className: "size-3.5 text-
|
|
45
|
+
className: "p-0.5 hover:bg-bg-sunken rounded transition-colors",
|
|
46
|
+
children: /* @__PURE__ */ r(t, { className: "size-3.5 text-text-secondary" })
|
|
47
47
|
})
|
|
48
48
|
]
|
|
49
49
|
}), o && l && u ? /* @__PURE__ */ r("div", {
|
|
@@ -58,9 +58,9 @@ var o = ({ enabled: o, onToggle: s, hasPaymentMethodSelected: c, selectedPayment
|
|
|
58
58
|
})]
|
|
59
59
|
})]
|
|
60
60
|
}), m && /* @__PURE__ */ i("div", {
|
|
61
|
-
className: "p-3 rounded-lg bg-
|
|
61
|
+
className: "p-3 rounded-lg bg-bg-sunken border border-border-subtle",
|
|
62
62
|
children: [/* @__PURE__ */ r("p", {
|
|
63
|
-
className: "text-xs text-
|
|
63
|
+
className: "text-xs text-text-secondary",
|
|
64
64
|
children: g ? "Your subscription will automatically renew when it expires using the payment method selected on the left." : "Enable auto-renewal to automatically charge your payment method when the subscription expires. Select a payment method first."
|
|
65
65
|
}), !g && /* @__PURE__ */ i("div", {
|
|
66
66
|
className: "flex items-start gap-2 p-2 rounded bg-warning/10 mt-2",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SimpleAutoPayToggle.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/SimpleAutoPayToggle.tsx"],"sourcesContent":["/**\n * Simple Auto-Pay Toggle Component\n * Just enables/disables auto-renewal using the selected payment method\n */\n\nimport { type FC, useState } from 'react';\nimport { RefreshCw, Info } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface SimpleAutoPayToggleProps {\n enabled: boolean;\n onToggle: (enabled: boolean) => void;\n hasPaymentMethodSelected: boolean;\n selectedPaymentMethodDisplay?: string;\n isNewCard?: boolean; // Whether the selected payment method is a new card (not saved)\n disabled?: boolean;\n}\n\nexport const SimpleAutoPayToggle: FC<SimpleAutoPayToggleProps> = ({\n enabled,\n onToggle,\n hasPaymentMethodSelected,\n selectedPaymentMethodDisplay,\n isNewCard = false,\n disabled = 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 [showInfo, setShowInfo] = useState(false);\n const canEnable = hasPaymentMethodSelected;\n\n return (\n <div className=\"pt-3 border-t border-border space-y-2\">\n {/* Toggle Row */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n if (!canEnable) return;\n onToggle(!enabled);\n }}\n disabled={disabled || !canEnable}\n className={`\n relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent\n transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-
|
|
1
|
+
{"version":3,"file":"SimpleAutoPayToggle.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/SimpleAutoPayToggle.tsx"],"sourcesContent":["/**\n * Simple Auto-Pay Toggle Component\n * Just enables/disables auto-renewal using the selected payment method\n */\n\nimport { type FC, useState } from 'react';\nimport { RefreshCw, Info } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface SimpleAutoPayToggleProps {\n enabled: boolean;\n onToggle: (enabled: boolean) => void;\n hasPaymentMethodSelected: boolean;\n selectedPaymentMethodDisplay?: string;\n isNewCard?: boolean; // Whether the selected payment method is a new card (not saved)\n disabled?: boolean;\n}\n\nexport const SimpleAutoPayToggle: FC<SimpleAutoPayToggleProps> = ({\n enabled,\n onToggle,\n hasPaymentMethodSelected,\n selectedPaymentMethodDisplay,\n isNewCard = false,\n disabled = 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 [showInfo, setShowInfo] = useState(false);\n const canEnable = hasPaymentMethodSelected;\n\n return (\n <div className=\"pt-3 border-t border-border-subtle space-y-2\">\n {/* Toggle Row */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n if (!canEnable) return;\n onToggle(!enabled);\n }}\n disabled={disabled || !canEnable}\n className={`\n relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent\n transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:ring-offset-2\n ${enabled ? 'bg-action-primary-bg' : 'bg-bg-sunken'}\n ${(!canEnable || disabled) && 'opacity-50 cursor-not-allowed'}\n `}\n >\n <span\n className={`\n pointer-events-none inline-block size-4 transform rounded-full bg-bg-surface shadow ring-0\n transition duration-200 ease-in-out\n ${enabled ? 'translate-x-4' : 'translate-x-0'}\n `}\n />\n </button>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <RefreshCw className=\"size-3.5 text-text-secondary shrink-0\" />\n <span className=\"text-xs font-medium text-text-primary\">\n {tr('billing.checkout.autoRenew', 'Auto-renew subscription')}\n </span>\n <button\n type=\"button\"\n onClick={() => setShowInfo(!showInfo)}\n className=\"p-0.5 hover:bg-bg-sunken rounded transition-colors\"\n >\n <Info className=\"size-3.5 text-text-secondary\" />\n </button>\n </div>\n\n {/* Status Message */}\n {enabled && selectedPaymentMethodDisplay && isNewCard ? (\n <div className=\"text-xs text-accent-blue mt-0.5\">\n ℹ️ This payment method will be saved for future renewals\n </div>\n ) : enabled && selectedPaymentMethodDisplay ? (\n <div className=\"text-xs text-success mt-0.5\">\n ✓ Will renew using {selectedPaymentMethodDisplay}\n </div>\n ) : !canEnable ? (\n <div className=\"text-xs text-warning mt-0.5\">\n {tr('billing.checkout.selectPaymentFirst', 'Select a payment method first')}\n </div>\n ) : null}\n </div>\n </div>\n\n {/* Info Panel */}\n {showInfo && (\n <div className=\"p-3 rounded-lg bg-bg-sunken border border-border-subtle\">\n <p className=\"text-xs text-text-secondary\">\n {canEnable\n ? 'Your subscription will automatically renew when it expires using the payment method selected on the left.'\n : 'Enable auto-renewal to automatically charge your payment method when the subscription expires. Select a payment method first.'}\n </p>\n\n {!canEnable && (\n <div className=\"flex items-start gap-2 p-2 rounded bg-warning/10 mt-2\">\n <Info className=\"size-3.5 text-warning shrink-0 mt-0.5\" />\n <p className=\"text-xs text-warning\">\n You can save a card during this purchase, then enable auto-renew for future\n subscriptions.\n </p>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;AAkBA,IAAa,KAAqD,EAChE,YACA,aACA,6BACA,iCACA,eAAY,IACZ,cAAW,SACP;CACJ,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,IAAY;AAElB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe;AACR,UACL,EAAS,CAAC,EAAQ;;IAEpB,UAAU,KAAY,CAAC;IACvB,WAAW;;;cAGP,IAAU,yBAAyB,eAAe;eACjD,CAAC,KAAa,MAAa,gCAAgC;;cAGhE,kBAAC,QAAD,EACE,WAAW;;;gBAGP,IAAU,kBAAkB,gBAAgB;eAEhD,CAAA;IACK,CAAA,EAET,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAW,WAAU,yCAA0C,CAAA;MAC/D,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAG,8BAA8B,0BAA0B;OACvD,CAAA;MACP,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAY,CAAC,EAAS;OACrC,WAAU;iBAEV,kBAAC,GAAD,EAAM,WAAU,gCAAiC,CAAA;OAC1C,CAAA;MACL;QAGL,KAAW,KAAgC,IAC1C,kBAAC,OAAD;KAAK,WAAU;eAAkC;KAE3C,CAAA,GACJ,KAAW,IACb,kBAAC,OAAD;KAAK,WAAU;eAAf,CAA6C,uBACvB,EAChB;SACH,IAID,OAHF,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAG,uCAAuC,gCAAgC;KACvE,CAAA,CAEJ;MACF;MAGL,KACC,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cACV,IACG,8GACA;IACF,CAAA,EAEH,CAAC,KACA,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAM,WAAU,yCAA0C,CAAA,EAC1D,kBAAC,KAAD;KAAG,WAAU;eAAuB;KAGhC,CAAA,CACA;MAEJ;KAEJ"}
|