@carlonicora/nextjs-jsonapi 1.23.0 → 1.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/ApiDataInterface-DPP8s46n.d.mts +21 -0
  2. package/dist/ApiDataInterface-DPP8s46n.d.ts +21 -0
  3. package/dist/ApiRequestDataTypeInterface-CUKFDBx2.d.mts +20 -0
  4. package/dist/ApiRequestDataTypeInterface-CUKFDBx2.d.ts +20 -0
  5. package/dist/{ApiResponseInterface-BKyod24U.d.ts → ApiResponseInterface-CAIAeP5d.d.ts} +1 -1
  6. package/dist/{ApiResponseInterface-Dqvu09tz.d.mts → ApiResponseInterface-zeewugD7.d.mts} +1 -1
  7. package/dist/AuthComponent-hxOPs9o8.d.mts +11 -0
  8. package/dist/AuthComponent-hxOPs9o8.d.ts +11 -0
  9. package/dist/{BlockNoteEditor-KQTKURH6.js → BlockNoteEditor-UCHRVVVZ.js} +15 -15
  10. package/dist/{BlockNoteEditor-KQTKURH6.js.map → BlockNoteEditor-UCHRVVVZ.js.map} +1 -1
  11. package/dist/{BlockNoteEditor-VKN4LZUV.mjs → BlockNoteEditor-ZYZZ6B45.mjs} +5 -5
  12. package/dist/JsonApiRequest-GR3L56A5.js +24 -0
  13. package/dist/{JsonApiRequest-54ZBO7WQ.js.map → JsonApiRequest-GR3L56A5.js.map} +1 -1
  14. package/dist/{JsonApiRequest-XWQWTFEQ.mjs → JsonApiRequest-K5BRU7RE.mjs} +2 -2
  15. package/dist/billing/index.d.mts +249 -0
  16. package/dist/billing/index.d.ts +249 -0
  17. package/dist/billing/index.js +3479 -0
  18. package/dist/billing/index.js.map +1 -0
  19. package/dist/billing/index.mjs +3479 -0
  20. package/dist/billing/index.mjs.map +1 -0
  21. package/dist/{chunk-KUFWHMMY.mjs → chunk-5U4NJJOF.mjs} +5 -5
  22. package/dist/{chunk-YF5KBA7H.mjs → chunk-CU4RXSNY.mjs} +168 -3578
  23. package/dist/chunk-CU4RXSNY.mjs.map +1 -0
  24. package/dist/{chunk-LSIUPIYQ.js → chunk-EW6QPMN3.js} +3 -3
  25. package/dist/{chunk-LSIUPIYQ.js.map → chunk-EW6QPMN3.js.map} +1 -1
  26. package/dist/{chunk-LI6CPNJI.js → chunk-FM6WRAN5.js} +1 -1
  27. package/dist/{chunk-LI6CPNJI.js.map → chunk-FM6WRAN5.js.map} +1 -1
  28. package/dist/{chunk-L6XLESU5.mjs → chunk-GR4QPP36.mjs} +2 -2
  29. package/dist/{chunk-ODNMW2CG.js → chunk-ILKUML3Z.js} +761 -4171
  30. package/dist/chunk-ILKUML3Z.js.map +1 -0
  31. package/dist/{chunk-UYY34W7R.js → chunk-NQVPCNRS.js} +26 -26
  32. package/dist/{chunk-UYY34W7R.js.map → chunk-NQVPCNRS.js.map} +1 -1
  33. package/dist/{chunk-3VM3WAOV.mjs → chunk-U4MTVHOC.mjs} +1 -1
  34. package/dist/client/index.d.mts +7 -6
  35. package/dist/client/index.d.ts +7 -6
  36. package/dist/client/index.js +5 -5
  37. package/dist/client/index.mjs +4 -4
  38. package/dist/components/index.d.mts +8 -250
  39. package/dist/components/index.d.ts +8 -250
  40. package/dist/components/index.js +5 -83
  41. package/dist/components/index.js.map +1 -1
  42. package/dist/components/index.mjs +4 -82
  43. package/dist/{config--nwiW74Z.d.ts → config-D_91hrI-.d.ts} +1 -1
  44. package/dist/{config-BKSQmUWU.d.mts → config-h0hNLceh.d.mts} +1 -1
  45. package/dist/{content.interface-4VICFRA0.d.ts → content.interface-CUIEQ0jk.d.ts} +2 -2
  46. package/dist/{content.interface-CFc97-Cj.d.mts → content.interface-QcsFR5Ad.d.mts} +2 -2
  47. package/dist/contexts/index.d.mts +4 -3
  48. package/dist/contexts/index.d.ts +4 -3
  49. package/dist/contexts/index.js +5 -5
  50. package/dist/contexts/index.mjs +4 -4
  51. package/dist/core/index.d.mts +12 -10
  52. package/dist/core/index.d.ts +12 -10
  53. package/dist/core/index.js +3 -3
  54. package/dist/core/index.mjs +2 -2
  55. package/dist/index.d.mts +9 -7
  56. package/dist/index.d.ts +9 -7
  57. package/dist/index.js +4 -4
  58. package/dist/index.mjs +3 -3
  59. package/dist/{notification.interface-CqwaOIgM.d.ts → notification.interface-D7_g5SnS.d.ts} +2 -1
  60. package/dist/{notification.interface-BGaPiCUM.d.mts → notification.interface-blT8r47t.d.mts} +2 -1
  61. package/dist/{s3.service-BYs88XEE.d.ts → s3.service-B83hUhqV.d.ts} +4 -3
  62. package/dist/{s3.service-C0BjOdvn.d.mts → s3.service-DSDfcr0S.d.mts} +4 -3
  63. package/dist/server/index.d.mts +6 -5
  64. package/dist/server/index.d.ts +6 -5
  65. package/dist/server/index.js +15 -15
  66. package/dist/server/index.js.map +1 -1
  67. package/dist/server/index.mjs +5 -5
  68. package/dist/{stripe-subscription.interface-B-TM40Io.d.ts → stripe-subscription.interface-CFtupgYh.d.mts} +2 -12
  69. package/dist/{stripe-subscription.interface-DDxnpj0F.d.mts → stripe-subscription.interface-CNTsrbAx.d.ts} +2 -12
  70. package/dist/testing/index.d.mts +3 -2
  71. package/dist/testing/index.d.ts +3 -2
  72. package/dist/{useSocket-BNj9PrRw.d.mts → useSocket-B5M_a4bD.d.mts} +1 -1
  73. package/dist/{useSocket-Dwt8cz1x.d.ts → useSocket-Dd03muLJ.d.ts} +1 -1
  74. package/package.json +36 -31
  75. package/src/billing/index.ts +8 -0
  76. package/src/components/index.ts +1 -7
  77. package/src/components/pages/PageContentContainer.tsx +1 -2
  78. package/src/shadcnui/ui/resizable.tsx +7 -7
  79. package/dist/ApiRequestDataTypeInterface-DIEOFn9s.d.mts +0 -40
  80. package/dist/ApiRequestDataTypeInterface-DIEOFn9s.d.ts +0 -40
  81. package/dist/JsonApiRequest-54ZBO7WQ.js +0 -24
  82. package/dist/chunk-ODNMW2CG.js.map +0 -1
  83. package/dist/chunk-YF5KBA7H.mjs.map +0 -1
  84. /package/dist/{BlockNoteEditor-VKN4LZUV.mjs.map → BlockNoteEditor-ZYZZ6B45.mjs.map} +0 -0
  85. /package/dist/{JsonApiRequest-XWQWTFEQ.mjs.map → JsonApiRequest-K5BRU7RE.mjs.map} +0 -0
  86. /package/dist/{chunk-KUFWHMMY.mjs.map → chunk-5U4NJJOF.mjs.map} +0 -0
  87. /package/dist/{chunk-L6XLESU5.mjs.map → chunk-GR4QPP36.mjs.map} +0 -0
  88. /package/dist/{chunk-3VM3WAOV.mjs.map → chunk-U4MTVHOC.mjs.map} +0 -0
@@ -0,0 +1,3479 @@
1
+ "use client";
2
+ import {
3
+ Alert,
4
+ AlertDescription,
5
+ AlertDialog,
6
+ AlertDialogAction,
7
+ AlertDialogCancel,
8
+ AlertDialogContent,
9
+ AlertDialogDescription,
10
+ AlertDialogFooter,
11
+ AlertDialogHeader,
12
+ AlertDialogTitle,
13
+ AlertTitle,
14
+ Badge,
15
+ Button,
16
+ Card,
17
+ CardContent,
18
+ CardDescription,
19
+ CardFooter,
20
+ CardHeader,
21
+ CardTitle,
22
+ Checkbox,
23
+ CommonEditorButtons,
24
+ Dialog,
25
+ DialogContent,
26
+ DialogDescription,
27
+ DialogHeader,
28
+ DialogTitle,
29
+ DropdownMenu,
30
+ DropdownMenuContent,
31
+ DropdownMenuItem,
32
+ DropdownMenuTrigger,
33
+ Form,
34
+ FormCheckbox,
35
+ FormControl,
36
+ FormInput,
37
+ FormItem,
38
+ FormLabel,
39
+ FormSelect,
40
+ FormTextarea,
41
+ Input,
42
+ Label,
43
+ Skeleton,
44
+ Table,
45
+ TableBody,
46
+ TableCell,
47
+ TableHead,
48
+ TableHeader,
49
+ TableRow,
50
+ Tabs,
51
+ TabsList,
52
+ TabsTrigger,
53
+ useCurrentUserContext
54
+ } from "../chunk-CU4RXSNY.mjs";
55
+ import {
56
+ getRoleId,
57
+ getStripePublishableKey
58
+ } from "../chunk-GR4QPP36.mjs";
59
+ import {
60
+ StripeCustomerService,
61
+ StripeInvoiceService,
62
+ StripePriceService,
63
+ StripeProductService,
64
+ StripeSubscriptionService,
65
+ StripeUsageService,
66
+ cn
67
+ } from "../chunk-5U4NJJOF.mjs";
68
+ import "../chunk-AUXK7QSA.mjs";
69
+ import "../chunk-C7C7VY4F.mjs";
70
+ import "../chunk-U4MTVHOC.mjs";
71
+ import "../chunk-VOXD3ZLY.mjs";
72
+ import {
73
+ __name
74
+ } from "../chunk-PAWJFY3S.mjs";
75
+
76
+ // src/features/billing/components/cards/SubscriptionSummaryCard.tsx
77
+ import { ChevronRight, CreditCard } from "lucide-react";
78
+ import { jsx, jsxs } from "react/jsx-runtime";
79
+ function getStatusBadgeVariant(status) {
80
+ switch (status) {
81
+ case "active" /* ACTIVE */:
82
+ return "default";
83
+ case "trialing" /* TRIALING */:
84
+ return "secondary";
85
+ case "past_due" /* PAST_DUE */:
86
+ case "unpaid" /* UNPAID */:
87
+ case "canceled" /* CANCELED */:
88
+ return "destructive";
89
+ default:
90
+ return "outline";
91
+ }
92
+ }
93
+ __name(getStatusBadgeVariant, "getStatusBadgeVariant");
94
+ function formatDate(date) {
95
+ return new Date(date).toLocaleDateString(void 0, {
96
+ year: "numeric",
97
+ month: "short",
98
+ day: "numeric"
99
+ });
100
+ }
101
+ __name(formatDate, "formatDate");
102
+ function formatPrice(amount, currency) {
103
+ if (amount === void 0) return "N/A";
104
+ const currencyCode = currency?.toUpperCase() || "USD";
105
+ return new Intl.NumberFormat(void 0, {
106
+ style: "currency",
107
+ currency: currencyCode
108
+ }).format(amount / 100);
109
+ }
110
+ __name(formatPrice, "formatPrice");
111
+ function formatPlanName(subscription) {
112
+ const productName = subscription.price?.product?.name || "";
113
+ const nickname = subscription.price?.nickname || "";
114
+ if (productName && nickname) {
115
+ return `${productName} - ${nickname}`;
116
+ }
117
+ return productName || nickname || "Subscription";
118
+ }
119
+ __name(formatPlanName, "formatPlanName");
120
+ function SubscriptionSummaryCard({
121
+ subscriptions,
122
+ loading,
123
+ error,
124
+ onManageClick
125
+ }) {
126
+ if (loading) {
127
+ return /* @__PURE__ */ jsxs(Card, { children: [
128
+ /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
129
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium", children: "Subscriptions" }),
130
+ /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-muted-foreground" })
131
+ ] }),
132
+ /* @__PURE__ */ jsxs(CardContent, { children: [
133
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-32 mb-2" }),
134
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-24 mb-1" }),
135
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-40" })
136
+ ] })
137
+ ] });
138
+ }
139
+ if (error) {
140
+ return /* @__PURE__ */ jsxs(Card, { children: [
141
+ /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
142
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium", children: "Subscriptions" }),
143
+ /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-muted-foreground" })
144
+ ] }),
145
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }) })
146
+ ] });
147
+ }
148
+ const activeSubscriptions = subscriptions.filter(
149
+ (sub) => sub.status === "active" /* ACTIVE */ || sub.status === "trialing" /* TRIALING */
150
+ );
151
+ const primarySubscription = activeSubscriptions[0];
152
+ return /* @__PURE__ */ jsxs(Card, { className: "cursor-pointer hover:bg-accent/50 transition-colors", onClick: onManageClick, children: [
153
+ /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
154
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium", children: "Subscriptions" }),
155
+ /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-muted-foreground" })
156
+ ] }),
157
+ /* @__PURE__ */ jsx(CardContent, { children: subscriptions.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
158
+ /* @__PURE__ */ jsx("p", { className: "text-xl font-bold text-muted-foreground", children: "No active plan" }),
159
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Subscribe to get started" }),
160
+ /* @__PURE__ */ jsxs(
161
+ Button,
162
+ {
163
+ variant: "outline",
164
+ size: "sm",
165
+ className: "mt-2",
166
+ onClick: (e) => {
167
+ e.stopPropagation();
168
+ onManageClick();
169
+ },
170
+ children: [
171
+ "View Plans",
172
+ /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 ml-1" })
173
+ ]
174
+ }
175
+ )
176
+ ] }) : primarySubscription ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
177
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
178
+ /* @__PURE__ */ jsx("p", { className: "text-xl font-bold", children: formatPlanName(primarySubscription) }),
179
+ /* @__PURE__ */ jsx(Badge, { variant: primarySubscription.cancelAtPeriodEnd ? "secondary" : getStatusBadgeVariant(primarySubscription.status), children: primarySubscription.cancelAtPeriodEnd ? "Canceling" : primarySubscription.status })
180
+ ] }),
181
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
182
+ formatPrice(primarySubscription.price?.unitAmount, primarySubscription.price?.currency),
183
+ primarySubscription.price?.recurring && /* @__PURE__ */ jsxs("span", { children: [
184
+ "/",
185
+ primarySubscription.price.recurring.interval
186
+ ] })
187
+ ] }),
188
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: primarySubscription.cancelAtPeriodEnd ? `Cancels on ${formatDate(primarySubscription.currentPeriodEnd)}` : `Renews on ${formatDate(primarySubscription.currentPeriodEnd)}` }),
189
+ activeSubscriptions.length > 1 && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
190
+ "+",
191
+ activeSubscriptions.length - 1,
192
+ " more subscription(s)"
193
+ ] })
194
+ ] }) : null })
195
+ ] });
196
+ }
197
+ __name(SubscriptionSummaryCard, "SubscriptionSummaryCard");
198
+
199
+ // src/features/billing/components/cards/PaymentMethodSummaryCard.tsx
200
+ import { Wallet, ChevronRight as ChevronRight2 } from "lucide-react";
201
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
202
+ function getCardBrandIcon(brand) {
203
+ const brandMap = {
204
+ visa: "Visa",
205
+ mastercard: "Mastercard",
206
+ amex: "Amex",
207
+ discover: "Discover",
208
+ diners: "Diners",
209
+ jcb: "JCB",
210
+ unionpay: "UnionPay"
211
+ };
212
+ return brandMap[brand.toLowerCase()] || brand;
213
+ }
214
+ __name(getCardBrandIcon, "getCardBrandIcon");
215
+ function PaymentMethodSummaryCard({
216
+ paymentMethods,
217
+ defaultPaymentMethodId,
218
+ loading,
219
+ error,
220
+ onManageClick
221
+ }) {
222
+ if (loading) {
223
+ return /* @__PURE__ */ jsxs2(Card, { children: [
224
+ /* @__PURE__ */ jsxs2(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
225
+ /* @__PURE__ */ jsx2(CardTitle, { className: "text-sm font-medium", children: "Payment Method" }),
226
+ /* @__PURE__ */ jsx2(Wallet, { className: "h-4 w-4 text-muted-foreground" })
227
+ ] }),
228
+ /* @__PURE__ */ jsxs2(CardContent, { children: [
229
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-6 w-32 mb-2" }),
230
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-24" })
231
+ ] })
232
+ ] });
233
+ }
234
+ if (error) {
235
+ return /* @__PURE__ */ jsxs2(Card, { children: [
236
+ /* @__PURE__ */ jsxs2(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
237
+ /* @__PURE__ */ jsx2(CardTitle, { className: "text-sm font-medium", children: "Payment Method" }),
238
+ /* @__PURE__ */ jsx2(Wallet, { className: "h-4 w-4 text-muted-foreground" })
239
+ ] }),
240
+ /* @__PURE__ */ jsx2(CardContent, { children: /* @__PURE__ */ jsx2("p", { className: "text-sm text-destructive", children: error }) })
241
+ ] });
242
+ }
243
+ const defaultMethod = paymentMethods.find((pm) => pm.id === defaultPaymentMethodId) || paymentMethods[0];
244
+ return /* @__PURE__ */ jsxs2(Card, { className: "cursor-pointer hover:bg-accent/50 transition-colors", onClick: onManageClick, children: [
245
+ /* @__PURE__ */ jsxs2(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
246
+ /* @__PURE__ */ jsx2(CardTitle, { className: "text-sm font-medium", children: "Payment Method" }),
247
+ /* @__PURE__ */ jsx2(Wallet, { className: "h-4 w-4 text-muted-foreground" })
248
+ ] }),
249
+ /* @__PURE__ */ jsx2(CardContent, { children: paymentMethods.length === 0 ? /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
250
+ /* @__PURE__ */ jsx2("p", { className: "text-xl font-bold text-muted-foreground", children: "No payment method" }),
251
+ /* @__PURE__ */ jsx2("p", { className: "text-xs text-muted-foreground", children: "Add a card to enable subscriptions" }),
252
+ /* @__PURE__ */ jsxs2(Button, { variant: "outline", size: "sm", className: "mt-2", onClick: (e) => {
253
+ e.stopPropagation();
254
+ onManageClick();
255
+ }, children: [
256
+ "Add Card",
257
+ /* @__PURE__ */ jsx2(ChevronRight2, { className: "h-4 w-4 ml-1" })
258
+ ] })
259
+ ] }) : defaultMethod?.card ? /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
260
+ /* @__PURE__ */ jsxs2("p", { className: "text-xl font-bold", children: [
261
+ getCardBrandIcon(defaultMethod.card.brand),
262
+ " ****",
263
+ defaultMethod.card.last4
264
+ ] }),
265
+ /* @__PURE__ */ jsxs2("p", { className: "text-sm text-muted-foreground", children: [
266
+ "Expires ",
267
+ String(defaultMethod.card.expMonth).padStart(2, "0"),
268
+ "/",
269
+ defaultMethod.card.expYear
270
+ ] }),
271
+ paymentMethods.length > 1 && /* @__PURE__ */ jsxs2("p", { className: "text-xs text-muted-foreground", children: [
272
+ "+",
273
+ paymentMethods.length - 1,
274
+ " more card(s)"
275
+ ] })
276
+ ] }) : /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
277
+ /* @__PURE__ */ jsx2("p", { className: "text-xl font-bold", children: defaultMethod?.type || "Payment Method" }),
278
+ paymentMethods.length > 1 && /* @__PURE__ */ jsxs2("p", { className: "text-xs text-muted-foreground", children: [
279
+ "+",
280
+ paymentMethods.length - 1,
281
+ " more method(s)"
282
+ ] })
283
+ ] }) })
284
+ ] });
285
+ }
286
+ __name(PaymentMethodSummaryCard, "PaymentMethodSummaryCard");
287
+
288
+ // src/features/billing/components/cards/CustomerInfoCard.tsx
289
+ import { ExternalLink, User } from "lucide-react";
290
+ import { useState } from "react";
291
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
292
+ function formatBalance(balance, currency) {
293
+ if (balance === void 0 || balance === 0) return "$0.00";
294
+ const currencyCode = currency?.toUpperCase() || "USD";
295
+ const displayBalance = -balance;
296
+ return new Intl.NumberFormat(void 0, {
297
+ style: "currency",
298
+ currency: currencyCode
299
+ }).format(displayBalance / 100);
300
+ }
301
+ __name(formatBalance, "formatBalance");
302
+ function CustomerInfoCard({ customer, loading, error }) {
303
+ const [portalLoading, setPortalLoading] = useState(false);
304
+ const handlePortalClick = /* @__PURE__ */ __name(async (e) => {
305
+ e.stopPropagation();
306
+ setPortalLoading(true);
307
+ try {
308
+ const { url } = await StripeCustomerService.createPortalSession();
309
+ window.open(url, "_blank");
310
+ } catch (err) {
311
+ console.error("[CustomerInfoCard] Failed to create portal session:", err);
312
+ } finally {
313
+ setPortalLoading(false);
314
+ }
315
+ }, "handlePortalClick");
316
+ if (loading) {
317
+ return /* @__PURE__ */ jsxs3(Card, { children: [
318
+ /* @__PURE__ */ jsxs3(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
319
+ /* @__PURE__ */ jsx3(CardTitle, { className: "text-sm font-medium", children: "Billing Account" }),
320
+ /* @__PURE__ */ jsx3(User, { className: "h-4 w-4 text-muted-foreground" })
321
+ ] }),
322
+ /* @__PURE__ */ jsxs3(CardContent, { children: [
323
+ /* @__PURE__ */ jsx3(Skeleton, { className: "h-6 w-32 mb-2" }),
324
+ /* @__PURE__ */ jsx3(Skeleton, { className: "h-4 w-48 mb-1" }),
325
+ /* @__PURE__ */ jsx3(Skeleton, { className: "h-4 w-24" })
326
+ ] })
327
+ ] });
328
+ }
329
+ if (error) {
330
+ return /* @__PURE__ */ jsxs3(Card, { children: [
331
+ /* @__PURE__ */ jsxs3(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
332
+ /* @__PURE__ */ jsx3(CardTitle, { className: "text-sm font-medium", children: "Billing Account" }),
333
+ /* @__PURE__ */ jsx3(User, { className: "h-4 w-4 text-muted-foreground" })
334
+ ] }),
335
+ /* @__PURE__ */ jsx3(CardContent, { children: /* @__PURE__ */ jsx3("p", { className: "text-sm text-destructive", children: error }) })
336
+ ] });
337
+ }
338
+ if (!customer) {
339
+ return /* @__PURE__ */ jsxs3(Card, { children: [
340
+ /* @__PURE__ */ jsxs3(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
341
+ /* @__PURE__ */ jsx3(CardTitle, { className: "text-sm font-medium", children: "Billing Account" }),
342
+ /* @__PURE__ */ jsx3(User, { className: "h-4 w-4 text-muted-foreground" })
343
+ ] }),
344
+ /* @__PURE__ */ jsxs3(CardContent, { children: [
345
+ /* @__PURE__ */ jsx3("p", { className: "text-xl font-bold text-muted-foreground", children: "Not set up" }),
346
+ /* @__PURE__ */ jsx3("p", { className: "text-xs text-muted-foreground", children: "Billing account will be created when you subscribe" })
347
+ ] })
348
+ ] });
349
+ }
350
+ return /* @__PURE__ */ jsxs3(Card, { children: [
351
+ /* @__PURE__ */ jsxs3(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
352
+ /* @__PURE__ */ jsx3(CardTitle, { className: "text-sm font-medium", children: "Billing Account" }),
353
+ /* @__PURE__ */ jsx3(User, { className: "h-4 w-4 text-muted-foreground" })
354
+ ] }),
355
+ /* @__PURE__ */ jsx3(CardContent, { children: /* @__PURE__ */ jsxs3("div", { className: "space-y-2", children: [
356
+ customer.name && /* @__PURE__ */ jsx3("p", { className: "text-xl font-bold", children: customer.name }),
357
+ customer.email && /* @__PURE__ */ jsx3("p", { className: "text-sm text-muted-foreground", children: customer.email }),
358
+ customer.balance !== void 0 && customer.balance !== 0 && /* @__PURE__ */ jsxs3("p", { className: "text-sm", children: [
359
+ /* @__PURE__ */ jsx3("span", { className: "text-muted-foreground", children: "Credit Balance: " }),
360
+ /* @__PURE__ */ jsx3("span", { className: customer.balance < 0 ? "text-green-600" : "text-destructive", children: formatBalance(customer.balance, customer.currency) })
361
+ ] }),
362
+ /* @__PURE__ */ jsxs3(Button, { variant: "outline", size: "sm", className: "mt-2", onClick: handlePortalClick, disabled: portalLoading, children: [
363
+ portalLoading ? "Loading..." : "Manage in Stripe Portal",
364
+ /* @__PURE__ */ jsx3(ExternalLink, { className: "h-4 w-4 ml-1" })
365
+ ] })
366
+ ] }) })
367
+ ] });
368
+ }
369
+ __name(CustomerInfoCard, "CustomerInfoCard");
370
+
371
+ // src/features/billing/components/cards/InvoicesSummaryCard.tsx
372
+ import { ChevronRight as ChevronRight3, ReceiptIcon } from "lucide-react";
373
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
374
+ function getStatusBadgeVariant2(status) {
375
+ switch (status) {
376
+ case "paid" /* PAID */:
377
+ return "default";
378
+ case "open" /* OPEN */:
379
+ return "secondary";
380
+ case "uncollectible" /* UNCOLLECTIBLE */:
381
+ case "void" /* VOID */:
382
+ return "destructive";
383
+ default:
384
+ return "outline";
385
+ }
386
+ }
387
+ __name(getStatusBadgeVariant2, "getStatusBadgeVariant");
388
+ function formatDate2(date) {
389
+ return new Date(date).toLocaleDateString(void 0, {
390
+ year: "numeric",
391
+ month: "short",
392
+ day: "numeric"
393
+ });
394
+ }
395
+ __name(formatDate2, "formatDate");
396
+ function formatAmount(amount, currency) {
397
+ const currencyCode = currency?.toUpperCase() || "USD";
398
+ return new Intl.NumberFormat(void 0, {
399
+ style: "currency",
400
+ currency: currencyCode
401
+ }).format(amount / 100);
402
+ }
403
+ __name(formatAmount, "formatAmount");
404
+ function InvoicesSummaryCard({ invoices, loading, error, onViewAllClick }) {
405
+ if (loading) {
406
+ return /* @__PURE__ */ jsxs4(Card, { children: [
407
+ /* @__PURE__ */ jsxs4(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
408
+ /* @__PURE__ */ jsx4(CardTitle, { className: "text-sm font-medium", children: "Recent Invoices" }),
409
+ /* @__PURE__ */ jsx4(ReceiptIcon, { className: "h-4 w-4 text-muted-foreground" })
410
+ ] }),
411
+ /* @__PURE__ */ jsxs4(CardContent, { children: [
412
+ /* @__PURE__ */ jsx4(Skeleton, { className: "h-6 w-24 mb-2" }),
413
+ /* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-32 mb-1" }),
414
+ /* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-20" })
415
+ ] })
416
+ ] });
417
+ }
418
+ if (error) {
419
+ return /* @__PURE__ */ jsxs4(Card, { children: [
420
+ /* @__PURE__ */ jsxs4(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
421
+ /* @__PURE__ */ jsx4(CardTitle, { className: "text-sm font-medium", children: "Recent Invoices" }),
422
+ /* @__PURE__ */ jsx4(ReceiptIcon, { className: "h-4 w-4 text-muted-foreground" })
423
+ ] }),
424
+ /* @__PURE__ */ jsx4(CardContent, { children: /* @__PURE__ */ jsx4("p", { className: "text-sm text-destructive", children: error }) })
425
+ ] });
426
+ }
427
+ const latestInvoice = invoices[0];
428
+ const paidInvoices = invoices.filter((inv) => inv.status === "paid" /* PAID */);
429
+ const openInvoices = invoices.filter((inv) => inv.status === "open" /* OPEN */);
430
+ return /* @__PURE__ */ jsxs4(Card, { className: "cursor-pointer hover:bg-accent/50 transition-colors", onClick: onViewAllClick, children: [
431
+ /* @__PURE__ */ jsxs4(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
432
+ /* @__PURE__ */ jsx4(CardTitle, { className: "text-sm font-medium", children: "Recent Invoices" }),
433
+ /* @__PURE__ */ jsx4(ReceiptIcon, { className: "h-4 w-4 text-muted-foreground" })
434
+ ] }),
435
+ /* @__PURE__ */ jsx4(CardContent, { children: invoices.length === 0 ? /* @__PURE__ */ jsxs4("div", { className: "space-y-2", children: [
436
+ /* @__PURE__ */ jsx4("p", { className: "text-xl font-bold text-muted-foreground", children: "No invoices yet" }),
437
+ /* @__PURE__ */ jsx4("p", { className: "text-xs text-muted-foreground", children: "Invoices will appear after your first billing cycle" })
438
+ ] }) : latestInvoice ? /* @__PURE__ */ jsxs4("div", { className: "space-y-2", children: [
439
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
440
+ /* @__PURE__ */ jsx4("p", { className: "text-xl font-bold", children: formatAmount(latestInvoice.total, latestInvoice.currency) }),
441
+ /* @__PURE__ */ jsx4(Badge, { variant: getStatusBadgeVariant2(latestInvoice.status), children: latestInvoice.status })
442
+ ] }),
443
+ /* @__PURE__ */ jsx4("p", { className: "text-sm text-muted-foreground", children: latestInvoice.stripeInvoiceNumber || `Invoice from ${formatDate2(latestInvoice.periodStart)}` }),
444
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-4 text-xs text-muted-foreground", children: [
445
+ paidInvoices.length > 0 && /* @__PURE__ */ jsxs4("span", { children: [
446
+ paidInvoices.length,
447
+ " paid"
448
+ ] }),
449
+ openInvoices.length > 0 && /* @__PURE__ */ jsxs4("span", { className: "text-orange-600", children: [
450
+ openInvoices.length,
451
+ " open"
452
+ ] }),
453
+ /* @__PURE__ */ jsxs4("span", { className: "flex items-center", children: [
454
+ "View all",
455
+ /* @__PURE__ */ jsx4(ChevronRight3, { className: "h-3 w-3 ml-1" })
456
+ ] })
457
+ ] })
458
+ ] }) : null })
459
+ ] });
460
+ }
461
+ __name(InvoicesSummaryCard, "InvoicesSummaryCard");
462
+
463
+ // src/features/billing/components/cards/BillingUsageSummaryCard.tsx
464
+ import { Activity, ChevronRight as ChevronRight4 } from "lucide-react";
465
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
466
+ function formatNumber(value) {
467
+ return new Intl.NumberFormat(void 0, {
468
+ notation: "compact",
469
+ compactDisplay: "short"
470
+ }).format(value);
471
+ }
472
+ __name(formatNumber, "formatNumber");
473
+ function BillingUsageSummaryCard({
474
+ meters,
475
+ summaries,
476
+ loading,
477
+ error,
478
+ onViewDetailsClick
479
+ }) {
480
+ if (loading) {
481
+ return /* @__PURE__ */ jsxs5(Card, { children: [
482
+ /* @__PURE__ */ jsxs5(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
483
+ /* @__PURE__ */ jsx5(CardTitle, { className: "text-sm font-medium", children: "Usage This Month" }),
484
+ /* @__PURE__ */ jsx5(Activity, { className: "h-4 w-4 text-muted-foreground" })
485
+ ] }),
486
+ /* @__PURE__ */ jsxs5(CardContent, { children: [
487
+ /* @__PURE__ */ jsx5(Skeleton, { className: "h-6 w-24 mb-2" }),
488
+ /* @__PURE__ */ jsx5(Skeleton, { className: "h-4 w-32" })
489
+ ] })
490
+ ] });
491
+ }
492
+ if (error) {
493
+ return /* @__PURE__ */ jsxs5(Card, { children: [
494
+ /* @__PURE__ */ jsxs5(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
495
+ /* @__PURE__ */ jsx5(CardTitle, { className: "text-sm font-medium", children: "Usage This Month" }),
496
+ /* @__PURE__ */ jsx5(Activity, { className: "h-4 w-4 text-muted-foreground" })
497
+ ] }),
498
+ /* @__PURE__ */ jsx5(CardContent, { children: /* @__PURE__ */ jsx5("p", { className: "text-sm text-destructive", children: error }) })
499
+ ] });
500
+ }
501
+ const totalUsage = Object.values(summaries).reduce((acc, summary) => {
502
+ return acc + (summary?.aggregatedValue || 0);
503
+ }, 0);
504
+ const primaryMeter = meters.find((m) => summaries[m.id]?.aggregatedValue);
505
+ const primarySummary = primaryMeter ? summaries[primaryMeter.id] : null;
506
+ return /* @__PURE__ */ jsxs5(Card, { className: "cursor-pointer hover:bg-accent/50 transition-colors", onClick: onViewDetailsClick, children: [
507
+ /* @__PURE__ */ jsxs5(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
508
+ /* @__PURE__ */ jsx5(CardTitle, { className: "text-sm font-medium", children: "Usage This Month" }),
509
+ /* @__PURE__ */ jsx5(Activity, { className: "h-4 w-4 text-muted-foreground" })
510
+ ] }),
511
+ /* @__PURE__ */ jsx5(CardContent, { children: meters.length === 0 ? /* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
512
+ /* @__PURE__ */ jsx5("p", { className: "text-xl font-bold text-muted-foreground", children: "No meters" }),
513
+ /* @__PURE__ */ jsx5("p", { className: "text-xs text-muted-foreground", children: "No usage meters are configured" })
514
+ ] }) : /* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
515
+ /* @__PURE__ */ jsxs5("p", { className: "text-xl font-bold", children: [
516
+ formatNumber(totalUsage),
517
+ " units"
518
+ ] }),
519
+ primaryMeter && primarySummary && /* @__PURE__ */ jsxs5("p", { className: "text-sm text-muted-foreground", children: [
520
+ primaryMeter.displayName,
521
+ ": ",
522
+ formatNumber(primarySummary.aggregatedValue)
523
+ ] }),
524
+ meters.length > 1 && /* @__PURE__ */ jsxs5("p", { className: "text-xs text-muted-foreground", children: [
525
+ "Across ",
526
+ meters.length,
527
+ " meters"
528
+ ] }),
529
+ /* @__PURE__ */ jsxs5("span", { className: "flex items-center text-xs text-muted-foreground", children: [
530
+ "View details",
531
+ /* @__PURE__ */ jsx5(ChevronRight4, { className: "h-3 w-3 ml-1" })
532
+ ] })
533
+ ] }) })
534
+ ] });
535
+ }
536
+ __name(BillingUsageSummaryCard, "BillingUsageSummaryCard");
537
+
538
+ // src/features/billing/components/containers/BillingDashboardContainer.tsx
539
+ import { CreditCard as CreditCard4, Loader2 as Loader23, Wallet as Wallet2 } from "lucide-react";
540
+ import { useCallback as useCallback2, useEffect as useEffect8, useState as useState14 } from "react";
541
+
542
+ // src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx
543
+ import { CreditCard as CreditCard2 } from "lucide-react";
544
+ import { useEffect as useEffect3, useState as useState4 } from "react";
545
+
546
+ // src/features/billing/stripe-customer/components/forms/PaymentMethodEditor.tsx
547
+ import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
548
+ import { useEffect, useState as useState2 } from "react";
549
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
550
+ function PaymentMethodEditor({ open, onOpenChange, onSuccess }) {
551
+ const stripe = useStripe();
552
+ const elements = useElements();
553
+ const [setupIntent, setSetupIntent] = useState2(null);
554
+ const [loading, setLoading] = useState2(true);
555
+ const [isSubmitting, setIsSubmitting] = useState2(false);
556
+ const [error, setError] = useState2(null);
557
+ const [setAsDefault, setSetAsDefault] = useState2(true);
558
+ useEffect(() => {
559
+ const fetchSetupIntent = /* @__PURE__ */ __name(async () => {
560
+ setLoading(true);
561
+ try {
562
+ const intent = await StripeCustomerService.createSetupIntent();
563
+ setSetupIntent(intent);
564
+ } catch (err) {
565
+ console.error("[PaymentMethodEditor] Failed to create setup intent:", err);
566
+ setError("Failed to initialize payment form. Please try again.");
567
+ } finally {
568
+ setLoading(false);
569
+ }
570
+ }, "fetchSetupIntent");
571
+ if (open) {
572
+ fetchSetupIntent();
573
+ }
574
+ }, [open]);
575
+ const handleSubmit = /* @__PURE__ */ __name(async (e) => {
576
+ e.preventDefault();
577
+ if (!stripe || !elements || !setupIntent) {
578
+ return;
579
+ }
580
+ setIsSubmitting(true);
581
+ setError(null);
582
+ try {
583
+ const cardElement = elements.getElement(CardElement);
584
+ if (!cardElement) {
585
+ throw new Error("Card element not found");
586
+ }
587
+ const { error: stripeError, setupIntent: confirmedSetupIntent } = await stripe.confirmCardSetup(
588
+ setupIntent.clientSecret,
589
+ {
590
+ payment_method: {
591
+ card: cardElement
592
+ }
593
+ }
594
+ );
595
+ if (stripeError) {
596
+ console.error("[PaymentMethodEditor] Stripe error:", stripeError);
597
+ setError(stripeError.message || "Failed to add payment method. Please check your card details.");
598
+ setIsSubmitting(false);
599
+ return;
600
+ }
601
+ if (setAsDefault && confirmedSetupIntent?.payment_method) {
602
+ await StripeCustomerService.setDefaultPaymentMethod({
603
+ paymentMethodId: typeof confirmedSetupIntent.payment_method === "string" ? confirmedSetupIntent.payment_method : confirmedSetupIntent.payment_method.id
604
+ });
605
+ }
606
+ onSuccess();
607
+ onOpenChange(false);
608
+ } catch (err) {
609
+ console.error("[PaymentMethodEditor] Error:", err);
610
+ setError(err.message || "An unexpected error occurred. Please try again.");
611
+ } finally {
612
+ setIsSubmitting(false);
613
+ }
614
+ }, "handleSubmit");
615
+ return /* @__PURE__ */ jsx6(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs6(DialogContent, { className: "max-w-md", children: [
616
+ /* @__PURE__ */ jsxs6(DialogHeader, { children: [
617
+ /* @__PURE__ */ jsx6(DialogTitle, { children: "Add Payment Method" }),
618
+ /* @__PURE__ */ jsx6(DialogDescription, { children: "Add a new payment method to your account. Your card information is securely processed by Stripe." })
619
+ ] }),
620
+ loading && /* @__PURE__ */ jsx6("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx6("p", { className: "text-muted-foreground", children: "Loading payment form..." }) }),
621
+ !loading && setupIntent && /* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
622
+ /* @__PURE__ */ jsx6("div", { className: "rounded-md border border-gray-300 p-3", children: /* @__PURE__ */ jsx6(
623
+ CardElement,
624
+ {
625
+ options: {
626
+ style: {
627
+ base: {
628
+ fontSize: "16px",
629
+ color: "#424770",
630
+ "::placeholder": {
631
+ color: "#aab7c4"
632
+ }
633
+ },
634
+ invalid: {
635
+ color: "#9e2146"
636
+ }
637
+ }
638
+ }
639
+ }
640
+ ) }),
641
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-x-2", children: [
642
+ /* @__PURE__ */ jsx6(
643
+ Checkbox,
644
+ {
645
+ id: "setAsDefault",
646
+ checked: setAsDefault,
647
+ onCheckedChange: (checked) => setSetAsDefault(!!checked)
648
+ }
649
+ ),
650
+ /* @__PURE__ */ jsx6(Label, { htmlFor: "setAsDefault", className: "text-sm font-normal", children: "Set as default payment method" })
651
+ ] }),
652
+ error && /* @__PURE__ */ jsx6(Alert, { variant: "destructive", className: "bg-red-50 border-red-200", children: /* @__PURE__ */ jsx6(AlertDescription, { children: error }) }),
653
+ /* @__PURE__ */ jsxs6("div", { className: "flex justify-end gap-x-2", children: [
654
+ /* @__PURE__ */ jsx6(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isSubmitting, children: "Cancel" }),
655
+ /* @__PURE__ */ jsx6(Button, { type: "submit", disabled: !stripe || isSubmitting, children: isSubmitting ? "Processing..." : "Add Card" })
656
+ ] })
657
+ ] }),
658
+ !loading && !setupIntent && error && /* @__PURE__ */ jsx6(Alert, { variant: "destructive", className: "bg-red-50 border-red-200", children: /* @__PURE__ */ jsx6(AlertDescription, { children: error }) })
659
+ ] }) });
660
+ }
661
+ __name(PaymentMethodEditor, "PaymentMethodEditor");
662
+
663
+ // src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx
664
+ import { MoreVertical } from "lucide-react";
665
+ import { useEffect as useEffect2, useState as useState3 } from "react";
666
+ import { Fragment, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
667
+ var brandIcons = {
668
+ visa: "\u{1F4B3}",
669
+ mastercard: "\u{1F4B3}",
670
+ amex: "\u{1F4B3}",
671
+ discover: "\u{1F4B3}"
672
+ };
673
+ function PaymentMethodCard({ paymentMethod, onUpdate }) {
674
+ const [loading, setLoading] = useState3(false);
675
+ const [customer, setCustomer] = useState3(null);
676
+ const [showRemoveDialog, setShowRemoveDialog] = useState3(false);
677
+ useEffect2(() => {
678
+ const loadCustomer = /* @__PURE__ */ __name(async () => {
679
+ try {
680
+ const fetchedCustomer = await StripeCustomerService.getCustomer();
681
+ setCustomer(fetchedCustomer);
682
+ } catch (error) {
683
+ console.error("[PaymentMethodCard] Failed to load customer:", error);
684
+ }
685
+ }, "loadCustomer");
686
+ loadCustomer();
687
+ }, []);
688
+ const isDefault = customer?.defaultPaymentMethodId === paymentMethod.id;
689
+ const brand = paymentMethod.card?.brand || "card";
690
+ const last4 = paymentMethod.card?.last4 || "****";
691
+ const expMonth = paymentMethod.card?.expMonth || 0;
692
+ const expYear = paymentMethod.card?.expYear || 0;
693
+ const brandIcon = brandIcons[brand.toLowerCase()] || "\u{1F4B3}";
694
+ const handleSetDefault = /* @__PURE__ */ __name(async () => {
695
+ setLoading(true);
696
+ try {
697
+ await StripeCustomerService.setDefaultPaymentMethod({ paymentMethodId: paymentMethod.id });
698
+ onUpdate();
699
+ } catch (error) {
700
+ console.error("[PaymentMethodCard] Failed to set as default:", error);
701
+ } finally {
702
+ setLoading(false);
703
+ }
704
+ }, "handleSetDefault");
705
+ const handleRemove = /* @__PURE__ */ __name(async () => {
706
+ setLoading(true);
707
+ try {
708
+ await StripeCustomerService.removePaymentMethod({ paymentMethodId: paymentMethod.id });
709
+ setShowRemoveDialog(false);
710
+ onUpdate();
711
+ } catch (error) {
712
+ console.error("[PaymentMethodCard] Failed to remove:", error);
713
+ setLoading(false);
714
+ }
715
+ }, "handleRemove");
716
+ return /* @__PURE__ */ jsxs7(Fragment, { children: [
717
+ /* @__PURE__ */ jsxs7(Card, { className: "relative", children: [
718
+ isDefault && /* @__PURE__ */ jsx7(Badge, { className: "absolute right-2 top-2 bg-green-100 text-green-800 hover:bg-green-100", children: "Default" }),
719
+ /* @__PURE__ */ jsxs7(CardHeader, { className: "flex flex-row items-center justify-between pb-2", children: [
720
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-x-2", children: [
721
+ /* @__PURE__ */ jsx7("span", { className: "text-2xl", children: brandIcon }),
722
+ /* @__PURE__ */ jsx7("span", { className: "text-sm font-medium capitalize", children: brand })
723
+ ] }),
724
+ /* @__PURE__ */ jsxs7(DropdownMenu, { children: [
725
+ /* @__PURE__ */ jsx7(DropdownMenuTrigger, { children: /* @__PURE__ */ jsx7(Button, { render: /* @__PURE__ */ jsx7("div", {}), nativeButton: false, variant: "ghost", size: "sm", disabled: loading, className: "h-8 w-8 p-0", children: /* @__PURE__ */ jsx7(MoreVertical, { className: "h-4 w-4" }) }) }),
726
+ /* @__PURE__ */ jsxs7(DropdownMenuContent, { align: "end", children: [
727
+ !isDefault && /* @__PURE__ */ jsx7(DropdownMenuItem, { onClick: handleSetDefault, disabled: loading, children: "Set as Default" }),
728
+ /* @__PURE__ */ jsx7(DropdownMenuItem, { onClick: () => setShowRemoveDialog(true), disabled: loading, className: "text-red-600", children: "Remove" })
729
+ ] })
730
+ ] })
731
+ ] }),
732
+ /* @__PURE__ */ jsx7(CardContent, { children: /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-y-1", children: [
733
+ /* @__PURE__ */ jsxs7("p", { className: "text-lg font-semibold", children: [
734
+ "\u2022\u2022\u2022\u2022 ",
735
+ last4
736
+ ] }),
737
+ /* @__PURE__ */ jsxs7("p", { className: "text-sm text-muted-foreground", children: [
738
+ "Expires ",
739
+ String(expMonth).padStart(2, "0"),
740
+ "/",
741
+ expYear
742
+ ] })
743
+ ] }) })
744
+ ] }),
745
+ /* @__PURE__ */ jsx7(AlertDialog, { open: showRemoveDialog, onOpenChange: setShowRemoveDialog, children: /* @__PURE__ */ jsxs7(AlertDialogContent, { children: [
746
+ /* @__PURE__ */ jsxs7(AlertDialogHeader, { children: [
747
+ /* @__PURE__ */ jsx7(AlertDialogTitle, { children: "Remove Payment Method" }),
748
+ /* @__PURE__ */ jsxs7(AlertDialogDescription, { children: [
749
+ "Are you sure you want to remove this payment method? This action cannot be undone.",
750
+ isDefault && " This is your default payment method."
751
+ ] })
752
+ ] }),
753
+ /* @__PURE__ */ jsxs7(AlertDialogFooter, { children: [
754
+ /* @__PURE__ */ jsx7(AlertDialogCancel, { disabled: loading, children: "Cancel" }),
755
+ /* @__PURE__ */ jsx7(AlertDialogAction, { onClick: handleRemove, disabled: loading, className: "bg-red-600 hover:bg-red-700", children: loading ? "Removing..." : "Remove" })
756
+ ] })
757
+ ] }) })
758
+ ] });
759
+ }
760
+ __name(PaymentMethodCard, "PaymentMethodCard");
761
+
762
+ // src/features/billing/stripe-customer/components/lists/PaymentMethodsList.tsx
763
+ import { jsx as jsx8 } from "react/jsx-runtime";
764
+ function PaymentMethodsList({ paymentMethods, onUpdate }) {
765
+ return /* @__PURE__ */ jsx8("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3", children: paymentMethods.map((paymentMethod) => /* @__PURE__ */ jsx8(PaymentMethodCard, { paymentMethod, onUpdate }, paymentMethod.id)) });
766
+ }
767
+ __name(PaymentMethodsList, "PaymentMethodsList");
768
+
769
+ // src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx
770
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
771
+ function PaymentMethodsContainer() {
772
+ const [paymentMethods, setPaymentMethods] = useState4([]);
773
+ const [loading, setLoading] = useState4(true);
774
+ const [showAddPaymentMethod, setShowAddPaymentMethod] = useState4(false);
775
+ const loadPaymentMethods = /* @__PURE__ */ __name(async () => {
776
+ setLoading(true);
777
+ try {
778
+ const fetchedPaymentMethods = await StripeCustomerService.listPaymentMethods();
779
+ setPaymentMethods(fetchedPaymentMethods);
780
+ } catch (error) {
781
+ console.error("[PaymentMethodsContainer] Failed to load payment methods:", error);
782
+ } finally {
783
+ setLoading(false);
784
+ }
785
+ }, "loadPaymentMethods");
786
+ useEffect3(() => {
787
+ loadPaymentMethods();
788
+ }, []);
789
+ if (loading) {
790
+ return /* @__PURE__ */ jsx9("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx9("p", { className: "text-muted-foreground", children: "Loading payment methods..." }) });
791
+ }
792
+ return /* @__PURE__ */ jsxs8("div", { className: "flex w-full flex-col gap-y-6", children: [
793
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between", children: [
794
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-x-3", children: [
795
+ /* @__PURE__ */ jsx9(CreditCard2, { className: "h-8 w-8" }),
796
+ /* @__PURE__ */ jsx9("h1", { className: "text-3xl font-bold", children: "Payment Methods" })
797
+ ] }),
798
+ /* @__PURE__ */ jsx9(Button, { onClick: () => setShowAddPaymentMethod(true), children: "Add Payment Method" })
799
+ ] }),
800
+ paymentMethods.length === 0 && /* @__PURE__ */ jsxs8("div", { className: "flex flex-col items-center justify-center gap-y-4 rounded-lg border-2 border-dashed border-gray-300 bg-muted/50 p-12", children: [
801
+ /* @__PURE__ */ jsx9(CreditCard2, { className: "h-16 w-16 text-muted-foreground" }),
802
+ /* @__PURE__ */ jsxs8("div", { className: "text-center", children: [
803
+ /* @__PURE__ */ jsx9("h3", { className: "mb-2 text-xl font-semibold", children: "No payment methods" }),
804
+ /* @__PURE__ */ jsx9("p", { className: "mb-4 text-muted-foreground", children: "Add a payment method to enable subscriptions and secure checkout." }),
805
+ /* @__PURE__ */ jsx9(Button, { onClick: () => setShowAddPaymentMethod(true), children: "Add Your First Card" })
806
+ ] })
807
+ ] }),
808
+ paymentMethods.length > 0 && /* @__PURE__ */ jsx9(PaymentMethodsList, { paymentMethods, onUpdate: loadPaymentMethods }),
809
+ showAddPaymentMethod && /* @__PURE__ */ jsx9(
810
+ PaymentMethodEditor,
811
+ {
812
+ open: showAddPaymentMethod,
813
+ onOpenChange: setShowAddPaymentMethod,
814
+ onSuccess: loadPaymentMethods
815
+ }
816
+ )
817
+ ] });
818
+ }
819
+ __name(PaymentMethodsContainer, "PaymentMethodsContainer");
820
+
821
+ // src/features/billing/stripe-invoice/components/containers/InvoicesContainer.tsx
822
+ import { useEffect as useEffect4, useState as useState6 } from "react";
823
+
824
+ // src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx
825
+ import { useState as useState5 } from "react";
826
+
827
+ // src/features/billing/components/utils/currency.ts
828
+ function formatInterval(price) {
829
+ if (price.priceType === "one_time" || !price.recurring) {
830
+ return "one-time";
831
+ }
832
+ const { interval, intervalCount } = price.recurring;
833
+ if (intervalCount === 1) {
834
+ return `/${interval}`;
835
+ }
836
+ const pluralInterval = interval === "day" ? "days" : interval === "week" ? "weeks" : interval === "month" ? "months" : "years";
837
+ return `/${intervalCount} ${pluralInterval}`;
838
+ }
839
+ __name(formatInterval, "formatInterval");
840
+ function formatCurrency(amount, currency) {
841
+ if (amount === void 0) return "$0.00";
842
+ const dollars = amount / 100;
843
+ try {
844
+ return new Intl.NumberFormat("en-US", {
845
+ style: "currency",
846
+ currency: currency.toUpperCase(),
847
+ minimumFractionDigits: 2,
848
+ maximumFractionDigits: 2
849
+ }).format(dollars);
850
+ } catch (error) {
851
+ console.error("Error formatting currency:", error);
852
+ return `$${dollars.toFixed(2)}`;
853
+ }
854
+ }
855
+ __name(formatCurrency, "formatCurrency");
856
+
857
+ // src/features/billing/components/utils/date.ts
858
+ function formatDate3(date) {
859
+ if (!date) return "N/A";
860
+ const dateObj = typeof date === "string" ? new Date(date) : date;
861
+ try {
862
+ return new Intl.DateTimeFormat("en-US", {
863
+ month: "short",
864
+ day: "numeric",
865
+ year: "numeric"
866
+ }).format(dateObj);
867
+ } catch (error) {
868
+ console.error("Error formatting date:", error);
869
+ return "Invalid Date";
870
+ }
871
+ }
872
+ __name(formatDate3, "formatDate");
873
+
874
+ // src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx
875
+ import { Download, ExternalLink as ExternalLink2, RefreshCw } from "lucide-react";
876
+
877
+ // src/features/billing/stripe-invoice/components/widgets/InvoiceStatusBadge.tsx
878
+ import { jsx as jsx10 } from "react/jsx-runtime";
879
+ var statusConfig = {
880
+ ["draft" /* DRAFT */]: {
881
+ label: "Draft",
882
+ color: "bg-gray-100 text-gray-800"
883
+ },
884
+ ["open" /* OPEN */]: {
885
+ label: "Open",
886
+ color: "bg-blue-100 text-blue-800"
887
+ },
888
+ ["paid" /* PAID */]: {
889
+ label: "Paid",
890
+ color: "bg-green-100 text-green-800"
891
+ },
892
+ ["void" /* VOID */]: {
893
+ label: "Void",
894
+ color: "bg-gray-100 text-gray-800"
895
+ },
896
+ ["uncollectible" /* UNCOLLECTIBLE */]: {
897
+ label: "Uncollectible",
898
+ color: "bg-red-100 text-red-800"
899
+ }
900
+ };
901
+ function InvoiceStatusBadge({ status }) {
902
+ const config = statusConfig[status] || statusConfig["draft" /* DRAFT */];
903
+ return /* @__PURE__ */ jsx10("span", { className: `${config.color} text-xs px-2 py-1 rounded-full font-medium`, children: config.label });
904
+ }
905
+ __name(InvoiceStatusBadge, "InvoiceStatusBadge");
906
+
907
+ // src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx
908
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
909
+ function InvoiceDetails({ invoice, open, onOpenChange, onInvoiceChange }) {
910
+ const handleDownloadPDF = /* @__PURE__ */ __name(() => {
911
+ if (invoice.stripePdfUrl) {
912
+ window.open(invoice.stripePdfUrl, "_blank");
913
+ }
914
+ }, "handleDownloadPDF");
915
+ const handleRetryPayment = /* @__PURE__ */ __name(async () => {
916
+ }, "handleRetryPayment");
917
+ const handleViewInStripe = /* @__PURE__ */ __name(() => {
918
+ if (invoice.stripeHostedInvoiceUrl) {
919
+ window.open(invoice.stripeHostedInvoiceUrl, "_blank");
920
+ }
921
+ }, "handleViewInStripe");
922
+ const getInvoiceNumber = /* @__PURE__ */ __name(() => {
923
+ if (invoice.stripeInvoiceNumber) {
924
+ return invoice.stripeInvoiceNumber;
925
+ }
926
+ return invoice.stripeInvoiceId.slice(-8);
927
+ }, "getInvoiceNumber");
928
+ const productName = invoice.subscription?.price?.product?.name || "Subscription";
929
+ return /* @__PURE__ */ jsx11(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs9(DialogContent, { className: "max-w-2xl", children: [
930
+ /* @__PURE__ */ jsxs9(DialogHeader, { children: [
931
+ /* @__PURE__ */ jsxs9(DialogTitle, { children: [
932
+ "Invoice ",
933
+ getInvoiceNumber()
934
+ ] }),
935
+ /* @__PURE__ */ jsx11(DialogDescription, { children: formatDate3(invoice.periodStart) })
936
+ ] }),
937
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-6", children: [
938
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-x-3", children: [
939
+ /* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-muted-foreground", children: "Status:" }),
940
+ /* @__PURE__ */ jsx11(InvoiceStatusBadge, { status: invoice.status })
941
+ ] }),
942
+ /* @__PURE__ */ jsxs9("div", { className: "grid grid-cols-2 gap-4", children: [
943
+ /* @__PURE__ */ jsxs9("div", { children: [
944
+ /* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-muted-foreground", children: "Billing Period:" }),
945
+ /* @__PURE__ */ jsxs9("p", { className: "font-medium", children: [
946
+ formatDate3(invoice.periodStart),
947
+ " - ",
948
+ formatDate3(invoice.periodEnd)
949
+ ] })
950
+ ] }),
951
+ invoice.dueDate && /* @__PURE__ */ jsxs9("div", { children: [
952
+ /* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-muted-foreground", children: "Due Date:" }),
953
+ /* @__PURE__ */ jsx11("p", { className: "font-medium", children: formatDate3(invoice.dueDate) })
954
+ ] }),
955
+ invoice.paidAt && /* @__PURE__ */ jsxs9("div", { children: [
956
+ /* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-muted-foreground", children: "Paid Date:" }),
957
+ /* @__PURE__ */ jsx11("p", { className: "font-medium", children: formatDate3(invoice.paidAt) })
958
+ ] }),
959
+ /* @__PURE__ */ jsxs9("div", { children: [
960
+ /* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-muted-foreground", children: "Attempt Count:" }),
961
+ /* @__PURE__ */ jsx11("p", { className: "font-medium", children: invoice.attemptCount })
962
+ ] })
963
+ ] }),
964
+ /* @__PURE__ */ jsxs9("div", { children: [
965
+ /* @__PURE__ */ jsx11("h4", { className: "text-sm font-medium text-muted-foreground mb-2", children: "Line Items" }),
966
+ /* @__PURE__ */ jsx11("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs9("table", { className: "w-full", children: [
967
+ /* @__PURE__ */ jsx11("thead", { className: "bg-muted", children: /* @__PURE__ */ jsxs9("tr", { children: [
968
+ /* @__PURE__ */ jsx11("th", { className: "text-left p-3 text-sm font-medium", children: "Description" }),
969
+ /* @__PURE__ */ jsx11("th", { className: "text-right p-3 text-sm font-medium", children: "Amount" })
970
+ ] }) }),
971
+ /* @__PURE__ */ jsx11("tbody", { children: /* @__PURE__ */ jsxs9("tr", { className: "border-t", children: [
972
+ /* @__PURE__ */ jsx11("td", { className: "p-3", children: productName }),
973
+ /* @__PURE__ */ jsx11("td", { className: "p-3 text-right", children: formatCurrency(invoice.subtotal, invoice.currency) })
974
+ ] }) })
975
+ ] }) })
976
+ ] }),
977
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-2 border-t pt-4", children: [
978
+ /* @__PURE__ */ jsxs9("div", { className: "flex justify-between", children: [
979
+ /* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-muted-foreground", children: "Subtotal:" }),
980
+ /* @__PURE__ */ jsx11("span", { className: "font-medium", children: formatCurrency(invoice.subtotal, invoice.currency) })
981
+ ] }),
982
+ invoice.tax !== void 0 && invoice.tax > 0 && /* @__PURE__ */ jsxs9("div", { className: "flex justify-between", children: [
983
+ /* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-muted-foreground", children: "Tax:" }),
984
+ /* @__PURE__ */ jsx11("span", { className: "font-medium", children: formatCurrency(invoice.tax, invoice.currency) })
985
+ ] }),
986
+ /* @__PURE__ */ jsxs9("div", { className: "flex justify-between text-lg font-semibold border-t pt-2", children: [
987
+ /* @__PURE__ */ jsx11("span", { children: "Total:" }),
988
+ /* @__PURE__ */ jsx11("span", { children: formatCurrency(invoice.total, invoice.currency) })
989
+ ] }),
990
+ invoice.amountPaid > 0 && /* @__PURE__ */ jsxs9("div", { className: "flex justify-between", children: [
991
+ /* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-muted-foreground", children: "Amount Paid:" }),
992
+ /* @__PURE__ */ jsx11("span", { className: "font-medium text-green-600", children: formatCurrency(invoice.amountPaid, invoice.currency) })
993
+ ] }),
994
+ invoice.amountRemaining > 0 && /* @__PURE__ */ jsxs9("div", { className: "flex justify-between", children: [
995
+ /* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-muted-foreground", children: "Amount Due:" }),
996
+ /* @__PURE__ */ jsx11("span", { className: "font-medium text-red-600", children: formatCurrency(invoice.amountRemaining, invoice.currency) })
997
+ ] })
998
+ ] }),
999
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-wrap gap-2 pt-4 border-t", children: [
1000
+ invoice.stripePdfUrl && /* @__PURE__ */ jsxs9(Button, { variant: "outline", onClick: handleDownloadPDF, children: [
1001
+ /* @__PURE__ */ jsx11(Download, { className: "mr-2 h-4 w-4" }),
1002
+ "Download PDF"
1003
+ ] }),
1004
+ invoice.status === "open" /* OPEN */ && invoice.attempted && /* @__PURE__ */ jsxs9(Button, { variant: "default", onClick: handleRetryPayment, children: [
1005
+ /* @__PURE__ */ jsx11(RefreshCw, { className: "mr-2 h-4 w-4" }),
1006
+ "Retry Payment"
1007
+ ] }),
1008
+ invoice.stripeHostedInvoiceUrl && /* @__PURE__ */ jsxs9(Button, { variant: "outline", onClick: handleViewInStripe, children: [
1009
+ /* @__PURE__ */ jsx11(ExternalLink2, { className: "mr-2 h-4 w-4" }),
1010
+ "View in Stripe"
1011
+ ] })
1012
+ ] })
1013
+ ] })
1014
+ ] }) });
1015
+ }
1016
+ __name(InvoiceDetails, "InvoiceDetails");
1017
+
1018
+ // src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx
1019
+ import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1020
+ function InvoicesList({ invoices, onInvoicesChange }) {
1021
+ const [selectedInvoice, setSelectedInvoice] = useState5(null);
1022
+ const handleRowClick = /* @__PURE__ */ __name((invoice) => {
1023
+ setSelectedInvoice(invoice);
1024
+ }, "handleRowClick");
1025
+ const getInvoiceNumber = /* @__PURE__ */ __name((invoice) => {
1026
+ if (invoice.stripeInvoiceNumber) {
1027
+ return invoice.stripeInvoiceNumber;
1028
+ }
1029
+ return invoice.stripeInvoiceId.slice(-8);
1030
+ }, "getInvoiceNumber");
1031
+ return /* @__PURE__ */ jsxs10(Fragment2, { children: [
1032
+ /* @__PURE__ */ jsx12("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs10(Table, { children: [
1033
+ /* @__PURE__ */ jsx12(TableHeader, { className: "bg-muted", children: /* @__PURE__ */ jsxs10(TableRow, { children: [
1034
+ /* @__PURE__ */ jsx12(TableHead, { children: "Invoice #" }),
1035
+ /* @__PURE__ */ jsx12(TableHead, { children: "Date" }),
1036
+ /* @__PURE__ */ jsx12(TableHead, { children: "Status" }),
1037
+ /* @__PURE__ */ jsx12(TableHead, { className: "text-right", children: "Amount" }),
1038
+ /* @__PURE__ */ jsx12(TableHead, { children: "Period" })
1039
+ ] }) }),
1040
+ /* @__PURE__ */ jsx12(TableBody, { children: invoices.map((invoice) => {
1041
+ const invoiceNumber = getInvoiceNumber(invoice);
1042
+ const date = formatDate3(invoice.periodStart);
1043
+ const amount = formatCurrency(invoice.total, invoice.currency);
1044
+ const period = `${formatDate3(invoice.periodStart)} - ${formatDate3(invoice.periodEnd)}`;
1045
+ return /* @__PURE__ */ jsxs10(
1046
+ TableRow,
1047
+ {
1048
+ onClick: () => handleRowClick(invoice),
1049
+ className: "cursor-pointer hover:bg-muted/50",
1050
+ children: [
1051
+ /* @__PURE__ */ jsx12(TableCell, { className: "font-medium", children: invoiceNumber }),
1052
+ /* @__PURE__ */ jsx12(TableCell, { className: "text-muted-foreground text-sm", children: date }),
1053
+ /* @__PURE__ */ jsx12(TableCell, { children: /* @__PURE__ */ jsx12(InvoiceStatusBadge, { status: invoice.status }) }),
1054
+ /* @__PURE__ */ jsx12(TableCell, { className: "text-right font-medium", children: amount }),
1055
+ /* @__PURE__ */ jsx12(TableCell, { className: "text-muted-foreground text-sm", children: period })
1056
+ ]
1057
+ },
1058
+ invoice.id
1059
+ );
1060
+ }) })
1061
+ ] }) }),
1062
+ selectedInvoice && /* @__PURE__ */ jsx12(
1063
+ InvoiceDetails,
1064
+ {
1065
+ invoice: selectedInvoice,
1066
+ open: !!selectedInvoice,
1067
+ onOpenChange: (open) => !open && setSelectedInvoice(null),
1068
+ onInvoiceChange: () => {
1069
+ onInvoicesChange();
1070
+ setSelectedInvoice(null);
1071
+ }
1072
+ }
1073
+ )
1074
+ ] });
1075
+ }
1076
+ __name(InvoicesList, "InvoicesList");
1077
+
1078
+ // src/features/billing/stripe-invoice/components/containers/InvoicesContainer.tsx
1079
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1080
+ function InvoicesContainer() {
1081
+ const [invoices, setInvoices] = useState6([]);
1082
+ const [loading, setLoading] = useState6(true);
1083
+ const [statusFilter, setStatusFilter] = useState6("all");
1084
+ const loadInvoices = /* @__PURE__ */ __name(async () => {
1085
+ setLoading(true);
1086
+ try {
1087
+ const params = statusFilter !== "all" ? { status: statusFilter } : void 0;
1088
+ const data = await StripeInvoiceService.listInvoices(params);
1089
+ setInvoices(data);
1090
+ } catch (error) {
1091
+ console.error("[InvoicesContainer] Failed to load invoices:", error);
1092
+ setInvoices([]);
1093
+ } finally {
1094
+ setLoading(false);
1095
+ }
1096
+ }, "loadInvoices");
1097
+ useEffect4(() => {
1098
+ loadInvoices();
1099
+ }, [statusFilter]);
1100
+ const handleFilterChange = /* @__PURE__ */ __name((value) => {
1101
+ setStatusFilter(value);
1102
+ }, "handleFilterChange");
1103
+ return /* @__PURE__ */ jsxs11("div", { className: "space-y-4", children: [
1104
+ /* @__PURE__ */ jsx13(Tabs, { value: statusFilter, onValueChange: handleFilterChange, children: /* @__PURE__ */ jsxs11(TabsList, { children: [
1105
+ /* @__PURE__ */ jsx13(TabsTrigger, { value: "all", children: "All" }),
1106
+ /* @__PURE__ */ jsx13(TabsTrigger, { value: "paid" /* PAID */, children: "Paid" }),
1107
+ /* @__PURE__ */ jsx13(TabsTrigger, { value: "open" /* OPEN */, children: "Open" }),
1108
+ /* @__PURE__ */ jsx13(TabsTrigger, { value: "void" /* VOID */, children: "Void" }),
1109
+ /* @__PURE__ */ jsx13(TabsTrigger, { value: "uncollectible" /* UNCOLLECTIBLE */, children: "Uncollectible" })
1110
+ ] }) }),
1111
+ loading && /* @__PURE__ */ jsx13("div", { className: "text-center py-8 text-muted-foreground", children: "Loading invoices..." }),
1112
+ !loading && invoices.length === 0 && /* @__PURE__ */ jsxs11("div", { className: "border border-dashed border-gray-300 rounded-lg p-8 text-center", children: [
1113
+ /* @__PURE__ */ jsx13("p", { className: "text-lg font-medium text-muted-foreground mb-2", children: "No invoices yet" }),
1114
+ /* @__PURE__ */ jsx13("p", { className: "text-sm text-muted-foreground", children: "Invoices will appear here after your first billing cycle" })
1115
+ ] }),
1116
+ !loading && invoices.length > 0 && /* @__PURE__ */ jsx13(InvoicesList, { invoices, onInvoicesChange: loadInvoices })
1117
+ ] });
1118
+ }
1119
+ __name(InvoicesContainer, "InvoicesContainer");
1120
+
1121
+ // src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx
1122
+ import { CheckCircle as CheckCircle2, CreditCard as CreditCard3, Loader2 as Loader22 } from "lucide-react";
1123
+ import { useEffect as useEffect6, useState as useState12 } from "react";
1124
+ import { v4 as v42 } from "uuid";
1125
+
1126
+ // src/features/billing/stripe-subscription/hooks/useConfirmSubscriptionPayment.ts
1127
+ import { useStripe as useStripe2 } from "@stripe/react-stripe-js";
1128
+ import { useCallback, useState as useState7 } from "react";
1129
+ function useConfirmSubscriptionPayment() {
1130
+ const stripe = useStripe2();
1131
+ const [isConfirming, setIsConfirming] = useState7(false);
1132
+ const confirmPayment = useCallback(
1133
+ async (clientSecret) => {
1134
+ if (!stripe) {
1135
+ console.error("[useConfirmSubscriptionPayment] Stripe not initialized");
1136
+ return {
1137
+ success: false,
1138
+ error: "Payment system not initialized. Please refresh the page and try again."
1139
+ };
1140
+ }
1141
+ if (!clientSecret) {
1142
+ console.error("[useConfirmSubscriptionPayment] No client secret provided");
1143
+ return {
1144
+ success: false,
1145
+ error: "Payment confirmation failed. Missing payment details."
1146
+ };
1147
+ }
1148
+ setIsConfirming(true);
1149
+ try {
1150
+ const { error: stripeError, paymentIntent } = await stripe.confirmCardPayment(clientSecret);
1151
+ if (stripeError) {
1152
+ console.error("[useConfirmSubscriptionPayment] Stripe error:", stripeError);
1153
+ return {
1154
+ success: false,
1155
+ error: stripeError.message || "Payment confirmation failed. Please try again."
1156
+ };
1157
+ }
1158
+ if (paymentIntent?.status === "succeeded") {
1159
+ return { success: true };
1160
+ }
1161
+ if (paymentIntent?.status === "requires_action") {
1162
+ return {
1163
+ success: false,
1164
+ error: "Additional authentication required. Please complete the verification."
1165
+ };
1166
+ }
1167
+ return {
1168
+ success: false,
1169
+ error: "Payment could not be completed. Please try again."
1170
+ };
1171
+ } catch (err) {
1172
+ console.error("[useConfirmSubscriptionPayment] Unexpected error:", err);
1173
+ return {
1174
+ success: false,
1175
+ error: err.message || "An unexpected error occurred during payment confirmation."
1176
+ };
1177
+ } finally {
1178
+ setIsConfirming(false);
1179
+ }
1180
+ },
1181
+ [stripe]
1182
+ );
1183
+ return {
1184
+ confirmPayment,
1185
+ isConfirming
1186
+ };
1187
+ }
1188
+ __name(useConfirmSubscriptionPayment, "useConfirmSubscriptionPayment");
1189
+
1190
+ // src/features/billing/stripe-subscription/components/forms/CancelSubscriptionDialog.tsx
1191
+ import { zodResolver } from "@hookform/resolvers/zod";
1192
+ import { useState as useState8 } from "react";
1193
+ import { useForm } from "react-hook-form";
1194
+ import { z } from "zod";
1195
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1196
+ var formSchema = z.object({
1197
+ cancelImmediately: z.boolean(),
1198
+ reason: z.string().optional()
1199
+ });
1200
+ function CancelSubscriptionDialog({
1201
+ subscription,
1202
+ open,
1203
+ onOpenChange,
1204
+ onSuccess
1205
+ }) {
1206
+ const [isSubmitting, setIsSubmitting] = useState8(false);
1207
+ const form = useForm({
1208
+ resolver: zodResolver(formSchema),
1209
+ defaultValues: {
1210
+ cancelImmediately: false,
1211
+ reason: ""
1212
+ }
1213
+ });
1214
+ const cancelImmediately = form.watch("cancelImmediately");
1215
+ const onSubmit = /* @__PURE__ */ __name(async (values) => {
1216
+ setIsSubmitting(true);
1217
+ try {
1218
+ await StripeSubscriptionService.cancelSubscription({
1219
+ id: subscription.id,
1220
+ cancelImmediately: values.cancelImmediately
1221
+ });
1222
+ onSuccess();
1223
+ onOpenChange(false);
1224
+ } catch (error) {
1225
+ console.error("[CancelSubscriptionDialog] Failed to cancel subscription:", error);
1226
+ } finally {
1227
+ setIsSubmitting(false);
1228
+ }
1229
+ }, "onSubmit");
1230
+ const periodEndDate = formatDate3(subscription.currentPeriodEnd);
1231
+ return /* @__PURE__ */ jsx14(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs12(DialogContent, { className: "max-w-md", children: [
1232
+ /* @__PURE__ */ jsxs12(DialogHeader, { children: [
1233
+ /* @__PURE__ */ jsx14(DialogTitle, { children: "Cancel Subscription" }),
1234
+ /* @__PURE__ */ jsx14(DialogDescription, { children: "Are you sure you want to cancel this subscription? This action cannot be undone." })
1235
+ ] }),
1236
+ /* @__PURE__ */ jsx14(Form, { ...form, children: /* @__PURE__ */ jsxs12("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-col gap-y-4", children: [
1237
+ /* @__PURE__ */ jsx14(FormCheckbox, { form, id: "cancelImmediately", name: "Cancel Immediately" }),
1238
+ cancelImmediately ? /* @__PURE__ */ jsx14("div", { className: "bg-red-50 border border-red-200 rounded-lg p-3 text-sm text-red-800", children: "Your subscription will be canceled immediately and you will lose access right away." }) : /* @__PURE__ */ jsxs12("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-3 text-sm text-blue-800", children: [
1239
+ "Your subscription will remain active until ",
1240
+ periodEndDate,
1241
+ ". You can continue using the service until then."
1242
+ ] }),
1243
+ /* @__PURE__ */ jsx14(
1244
+ FormTextarea,
1245
+ {
1246
+ form,
1247
+ id: "reason",
1248
+ name: "Reason (Optional)",
1249
+ placeholder: "Let us know why you're canceling...",
1250
+ className: "min-h-24"
1251
+ }
1252
+ ),
1253
+ /* @__PURE__ */ jsxs12("div", { className: "flex gap-x-2 justify-end pt-2", children: [
1254
+ /* @__PURE__ */ jsx14(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isSubmitting, children: "Keep Subscription" }),
1255
+ /* @__PURE__ */ jsx14(Button, { type: "submit", variant: "destructive", disabled: isSubmitting, children: isSubmitting ? "Canceling..." : "Confirm Cancellation" })
1256
+ ] })
1257
+ ] }) })
1258
+ ] }) });
1259
+ }
1260
+ __name(CancelSubscriptionDialog, "CancelSubscriptionDialog");
1261
+
1262
+ // src/features/billing/stripe-subscription/components/forms/SubscriptionEditor.tsx
1263
+ import { CheckCircle, Loader2 } from "lucide-react";
1264
+ import { useEffect as useEffect5, useState as useState9 } from "react";
1265
+ import { v4 } from "uuid";
1266
+
1267
+ // src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx
1268
+ import { Check } from "lucide-react";
1269
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1270
+ function PricingCard({ price, isCurrentPlan = false, isSelected = false, isDisabled = false, isLoading = false, onSelect }) {
1271
+ const description = price.description || price.nickname || "Standard";
1272
+ const features = price.features || [];
1273
+ const formattedPrice = formatCurrency(price.unitAmount, price.currency);
1274
+ const interval = formatInterval(price);
1275
+ const handleKeyDown = /* @__PURE__ */ __name((e) => {
1276
+ if ((e.key === "Enter" || e.key === " ") && !isDisabled && !isCurrentPlan) {
1277
+ e.preventDefault();
1278
+ onSelect(price);
1279
+ }
1280
+ }, "handleKeyDown");
1281
+ const handleClick = /* @__PURE__ */ __name(() => {
1282
+ if (!isDisabled && !isCurrentPlan && !isLoading) {
1283
+ onSelect(price);
1284
+ }
1285
+ }, "handleClick");
1286
+ return /* @__PURE__ */ jsxs13(
1287
+ Card,
1288
+ {
1289
+ role: "radio",
1290
+ "aria-checked": isSelected,
1291
+ "aria-label": `${description} plan at ${formattedPrice} ${interval}`,
1292
+ tabIndex: isDisabled ? -1 : 0,
1293
+ onKeyDown: handleKeyDown,
1294
+ onClick: handleClick,
1295
+ className: cn(
1296
+ "relative cursor-pointer transition-all duration-200 flex flex-col h-full",
1297
+ "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
1298
+ isCurrentPlan && "bg-muted/30",
1299
+ isSelected && !isCurrentPlan && "ring-2 ring-primary",
1300
+ !isDisabled && !isCurrentPlan && "hover:shadow-md hover:border-primary/50",
1301
+ isDisabled && "opacity-50 pointer-events-none",
1302
+ isLoading && "pointer-events-none"
1303
+ ),
1304
+ children: [
1305
+ isCurrentPlan && /* @__PURE__ */ jsx15(Badge, { variant: "secondary", className: "absolute top-2 right-2", children: "Current" }),
1306
+ /* @__PURE__ */ jsx15(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx15("h3", { className: "font-semibold text-lg", children: description }) }),
1307
+ /* @__PURE__ */ jsxs13(CardContent, { className: "pb-4 grow", children: [
1308
+ /* @__PURE__ */ jsxs13("div", { className: "mb-4", children: [
1309
+ /* @__PURE__ */ jsx15("span", { className: "text-3xl font-bold", children: formattedPrice }),
1310
+ /* @__PURE__ */ jsx15("span", { className: "text-muted-foreground ml-1", children: interval })
1311
+ ] }),
1312
+ features.length > 0 && /* @__PURE__ */ jsx15("ul", { className: "space-y-2", children: features.map((feature, index) => /* @__PURE__ */ jsxs13("li", { className: "flex items-start gap-2", children: [
1313
+ /* @__PURE__ */ jsx15(Check, { className: "h-4 w-4 text-green-500 mt-0.5 shrink-0" }),
1314
+ /* @__PURE__ */ jsx15("span", { className: "text-sm text-muted-foreground", children: feature })
1315
+ ] }, index)) })
1316
+ ] }),
1317
+ /* @__PURE__ */ jsx15(CardFooter, { children: /* @__PURE__ */ jsx15(
1318
+ Button,
1319
+ {
1320
+ variant: isCurrentPlan ? "secondary" : isSelected ? "default" : "outline",
1321
+ className: "w-full",
1322
+ disabled: isDisabled || isCurrentPlan || isLoading,
1323
+ children: isLoading ? "Processing..." : isCurrentPlan ? "Current Plan" : isSelected ? "Selected" : "Select Plan"
1324
+ }
1325
+ ) })
1326
+ ]
1327
+ }
1328
+ );
1329
+ }
1330
+ __name(PricingCard, "PricingCard");
1331
+
1332
+ // src/features/billing/stripe-subscription/components/widgets/PricingCardsGrid.tsx
1333
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
1334
+ function PricingCardsGrid({
1335
+ products,
1336
+ pricesByProduct,
1337
+ currentPriceId,
1338
+ selectedPriceId,
1339
+ loadingPriceId,
1340
+ loading = false,
1341
+ onSelectPrice
1342
+ }) {
1343
+ if (loading) {
1344
+ return /* @__PURE__ */ jsx16(PricingCardsGridSkeleton, {});
1345
+ }
1346
+ if (products.length === 0) {
1347
+ return /* @__PURE__ */ jsx16("div", { className: "text-center py-8 text-muted-foreground", children: "No plans available" });
1348
+ }
1349
+ return /* @__PURE__ */ jsx16("div", { className: "space-y-8", role: "radiogroup", "aria-label": "Available pricing plans", children: products.map((product) => {
1350
+ const prices = pricesByProduct.get(product.id) || [];
1351
+ if (prices.length === 0) return null;
1352
+ const sortedPrices = [...prices].sort((a, b) => (a.unitAmount ?? 0) - (b.unitAmount ?? 0));
1353
+ return /* @__PURE__ */ jsxs14("div", { className: "space-y-4", children: [
1354
+ /* @__PURE__ */ jsx16("h3", { className: "text-lg font-semibold", children: product.name }),
1355
+ /* @__PURE__ */ jsx16("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: sortedPrices.map((price) => /* @__PURE__ */ jsx16(
1356
+ PricingCard,
1357
+ {
1358
+ price,
1359
+ isCurrentPlan: price.stripePriceId === currentPriceId,
1360
+ isSelected: price.stripePriceId === selectedPriceId,
1361
+ isLoading: price.stripePriceId === loadingPriceId,
1362
+ onSelect: onSelectPrice
1363
+ },
1364
+ price.stripePriceId
1365
+ )) })
1366
+ ] }, product.id);
1367
+ }) });
1368
+ }
1369
+ __name(PricingCardsGrid, "PricingCardsGrid");
1370
+ function PricingCardsGridSkeleton() {
1371
+ return /* @__PURE__ */ jsx16("div", { className: "space-y-8", children: [1, 2].map((productIndex) => /* @__PURE__ */ jsxs14("div", { className: "space-y-4", children: [
1372
+ /* @__PURE__ */ jsx16(Skeleton, { className: "h-6 w-32" }),
1373
+ /* @__PURE__ */ jsx16("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: [1, 2, 3].map((cardIndex) => /* @__PURE__ */ jsxs14(Card, { className: "animate-pulse", children: [
1374
+ /* @__PURE__ */ jsx16(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx16(Skeleton, { className: "h-5 w-24" }) }),
1375
+ /* @__PURE__ */ jsxs14(CardContent, { className: "pb-4", children: [
1376
+ /* @__PURE__ */ jsxs14("div", { className: "mb-4", children: [
1377
+ /* @__PURE__ */ jsx16(Skeleton, { className: "h-9 w-20 inline-block" }),
1378
+ /* @__PURE__ */ jsx16(Skeleton, { className: "h-4 w-12 inline-block ml-2" })
1379
+ ] }),
1380
+ /* @__PURE__ */ jsxs14("div", { className: "space-y-2", children: [
1381
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
1382
+ /* @__PURE__ */ jsx16(Skeleton, { className: "h-4 w-4 rounded-full" }),
1383
+ /* @__PURE__ */ jsx16(Skeleton, { className: "h-4 w-32" })
1384
+ ] }),
1385
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
1386
+ /* @__PURE__ */ jsx16(Skeleton, { className: "h-4 w-4 rounded-full" }),
1387
+ /* @__PURE__ */ jsx16(Skeleton, { className: "h-4 w-28" })
1388
+ ] })
1389
+ ] })
1390
+ ] }),
1391
+ /* @__PURE__ */ jsx16(CardFooter, { children: /* @__PURE__ */ jsx16(Skeleton, { className: "h-9 w-full" }) })
1392
+ ] }, cardIndex)) })
1393
+ ] }, productIndex)) });
1394
+ }
1395
+ __name(PricingCardsGridSkeleton, "PricingCardsGridSkeleton");
1396
+
1397
+ // src/features/billing/stripe-subscription/components/widgets/ProrationPreview.tsx
1398
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
1399
+ function ProrationPreview({ preview }) {
1400
+ return /* @__PURE__ */ jsxs15("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-4", children: [
1401
+ /* @__PURE__ */ jsx17("h4", { className: "font-semibold text-blue-900 mb-3", children: "Proration Breakdown" }),
1402
+ /* @__PURE__ */ jsxs15("div", { className: "space-y-2", children: [
1403
+ preview.lineItems.map((item, index) => /* @__PURE__ */ jsxs15("div", { className: "flex justify-between text-sm", children: [
1404
+ /* @__PURE__ */ jsx17("span", { className: "text-blue-800", children: item.description }),
1405
+ /* @__PURE__ */ jsx17("span", { className: `font-medium ${item.amount < 0 ? "text-green-600" : "text-blue-900"}`, children: formatCurrency(item.amount, preview.currency) })
1406
+ ] }, index)),
1407
+ /* @__PURE__ */ jsx17("div", { className: "border-t border-blue-200 pt-2 mt-2", children: /* @__PURE__ */ jsxs15("div", { className: "flex justify-between font-semibold", children: [
1408
+ /* @__PURE__ */ jsx17("span", { className: "text-blue-900", children: "Net Due Today" }),
1409
+ /* @__PURE__ */ jsx17("span", { className: "text-blue-900", children: formatCurrency(preview.immediateCharge, preview.currency) })
1410
+ ] }) }),
1411
+ preview.lineItems.length > 0 && preview.lineItems[0].period && /* @__PURE__ */ jsxs15("div", { className: "text-xs text-blue-700 mt-2", children: [
1412
+ "Next invoice on ",
1413
+ formatDate3(preview.lineItems[0].period.end),
1414
+ " for",
1415
+ " ",
1416
+ formatCurrency(preview.amountDue, preview.currency)
1417
+ ] })
1418
+ ] })
1419
+ ] });
1420
+ }
1421
+ __name(ProrationPreview, "ProrationPreview");
1422
+
1423
+ // src/features/billing/stripe-subscription/components/forms/SubscriptionEditor.tsx
1424
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
1425
+ function SubscriptionEditor({
1426
+ subscription,
1427
+ open,
1428
+ onOpenChange,
1429
+ onSuccess,
1430
+ onAddPaymentMethod
1431
+ }) {
1432
+ const { confirmPayment, isConfirming } = useConfirmSubscriptionPayment();
1433
+ const [products, setProducts] = useState9([]);
1434
+ const [pricesByProduct, setPricesByProduct] = useState9(/* @__PURE__ */ new Map());
1435
+ const [loading, setLoading] = useState9(true);
1436
+ const [selectedPriceId, setSelectedPriceId] = useState9(null);
1437
+ const [loadingPriceId, setLoadingPriceId] = useState9(null);
1438
+ const [prorationPreview, setProrationPreview] = useState9(null);
1439
+ const [loadingProration, setLoadingProration] = useState9(false);
1440
+ const [hasPaymentMethod, setHasPaymentMethod] = useState9(true);
1441
+ const [loadingPaymentMethods, setLoadingPaymentMethods] = useState9(true);
1442
+ const [paymentRequiredError, setPaymentRequiredError] = useState9(false);
1443
+ const [paymentConfirmationState, setPaymentConfirmationState] = useState9("idle");
1444
+ const [paymentError, setPaymentError] = useState9(null);
1445
+ const currentPriceId = subscription?.price?.id;
1446
+ const isEditMode = !!subscription;
1447
+ useEffect5(() => {
1448
+ const checkPaymentMethods = /* @__PURE__ */ __name(async () => {
1449
+ if (subscription) {
1450
+ setLoadingPaymentMethods(false);
1451
+ return;
1452
+ }
1453
+ setLoadingPaymentMethods(true);
1454
+ try {
1455
+ const paymentMethods = await StripeCustomerService.listPaymentMethods();
1456
+ const hasMethod = paymentMethods.length > 0;
1457
+ setHasPaymentMethod(hasMethod);
1458
+ } catch (error) {
1459
+ console.error("[SubscriptionEditor] Failed to check payment methods:", error);
1460
+ setHasPaymentMethod(false);
1461
+ } finally {
1462
+ setLoadingPaymentMethods(false);
1463
+ }
1464
+ }, "checkPaymentMethods");
1465
+ if (open) {
1466
+ checkPaymentMethods();
1467
+ }
1468
+ }, [open, subscription]);
1469
+ useEffect5(() => {
1470
+ const loadData = /* @__PURE__ */ __name(async () => {
1471
+ setLoading(true);
1472
+ try {
1473
+ const fetchedProducts = await StripeProductService.listProducts({ active: true });
1474
+ const grouped = /* @__PURE__ */ new Map();
1475
+ for (const product of fetchedProducts) {
1476
+ if (product.stripePrices && product.stripePrices.length > 0) {
1477
+ grouped.set(product.id, product.stripePrices);
1478
+ }
1479
+ }
1480
+ setProducts(fetchedProducts);
1481
+ setPricesByProduct(grouped);
1482
+ } catch (error) {
1483
+ console.error("[SubscriptionEditor] Failed to load products/prices:", error);
1484
+ } finally {
1485
+ setLoading(false);
1486
+ }
1487
+ }, "loadData");
1488
+ if (open) {
1489
+ loadData();
1490
+ }
1491
+ }, [open]);
1492
+ useEffect5(() => {
1493
+ const loadProration = /* @__PURE__ */ __name(async () => {
1494
+ if (!subscription || !selectedPriceId || selectedPriceId === currentPriceId) {
1495
+ setProrationPreview(null);
1496
+ return;
1497
+ }
1498
+ setLoadingProration(true);
1499
+ try {
1500
+ const preview = await StripeSubscriptionService.getProrationPreview({
1501
+ subscriptionId: subscription.id,
1502
+ newPriceId: selectedPriceId
1503
+ });
1504
+ setProrationPreview(preview);
1505
+ } catch (error) {
1506
+ console.error("[SubscriptionEditor] Failed to load proration preview:", error);
1507
+ setProrationPreview(null);
1508
+ } finally {
1509
+ setLoadingProration(false);
1510
+ }
1511
+ }, "loadProration");
1512
+ loadProration();
1513
+ }, [selectedPriceId, subscription, currentPriceId]);
1514
+ const handleSelectPrice = /* @__PURE__ */ __name(async (price) => {
1515
+ const priceId = price.id;
1516
+ if (isEditMode) {
1517
+ setSelectedPriceId(priceId);
1518
+ } else {
1519
+ setLoadingPriceId(priceId);
1520
+ setSelectedPriceId(priceId);
1521
+ setPaymentError(null);
1522
+ setPaymentConfirmationState("idle");
1523
+ try {
1524
+ const result = await StripeSubscriptionService.createSubscription({
1525
+ id: v4(),
1526
+ priceId
1527
+ });
1528
+ if (result.meta.requiresAction && result.meta.clientSecret) {
1529
+ setPaymentConfirmationState("confirming");
1530
+ const confirmation = await confirmPayment(result.meta.clientSecret);
1531
+ if (!confirmation.success) {
1532
+ console.error("[SubscriptionEditor] Payment confirmation failed:", confirmation.error);
1533
+ setPaymentConfirmationState("error");
1534
+ setPaymentError(confirmation.error || "Payment confirmation failed");
1535
+ setLoadingPriceId(null);
1536
+ return;
1537
+ }
1538
+ await StripeSubscriptionService.syncSubscription({
1539
+ subscriptionId: result.subscription.id
1540
+ });
1541
+ }
1542
+ setPaymentConfirmationState("success");
1543
+ setTimeout(() => {
1544
+ onSuccess();
1545
+ onOpenChange(false);
1546
+ }, 1e3);
1547
+ } catch (error) {
1548
+ console.error("[SubscriptionEditor] Failed to create subscription:", error);
1549
+ if (error?.status === 402 || error?.response?.status === 402) {
1550
+ setPaymentRequiredError(true);
1551
+ setHasPaymentMethod(false);
1552
+ } else {
1553
+ setPaymentConfirmationState("error");
1554
+ setPaymentError(error?.message || "Failed to create subscription");
1555
+ }
1556
+ setLoadingPriceId(null);
1557
+ }
1558
+ }
1559
+ }, "handleSelectPrice");
1560
+ const handleConfirmPlanChange = /* @__PURE__ */ __name(async () => {
1561
+ if (!subscription || !selectedPriceId) return;
1562
+ setLoadingPriceId(selectedPriceId);
1563
+ try {
1564
+ await StripeSubscriptionService.changePlan({
1565
+ id: subscription.id,
1566
+ newPriceId: selectedPriceId
1567
+ });
1568
+ onSuccess();
1569
+ onOpenChange(false);
1570
+ } catch (error) {
1571
+ console.error("[SubscriptionEditor] Failed to change plan:", error);
1572
+ } finally {
1573
+ setLoadingPriceId(null);
1574
+ }
1575
+ }, "handleConfirmPlanChange");
1576
+ const handleCancel = /* @__PURE__ */ __name(() => {
1577
+ setSelectedPriceId(null);
1578
+ setProrationPreview(null);
1579
+ }, "handleCancel");
1580
+ return /* @__PURE__ */ jsx18(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs16(DialogContent, { className: "max-w-4xl max-h-[90vh] overflow-y-auto", children: [
1581
+ /* @__PURE__ */ jsxs16(DialogHeader, { children: [
1582
+ /* @__PURE__ */ jsx18(DialogTitle, { children: subscription ? "Change Plan" : "Subscribe to a Plan" }),
1583
+ /* @__PURE__ */ jsx18(DialogDescription, { children: subscription ? "Select a new plan to switch to. You'll see a proration preview before confirming." : "Choose a plan to start your subscription." })
1584
+ ] }),
1585
+ loadingPaymentMethods && !subscription ? /* @__PURE__ */ jsx18("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx18("div", { className: "text-muted-foreground", children: "Checking payment methods..." }) }) : !hasPaymentMethod && !subscription ? /* @__PURE__ */ jsxs16(Alert, { variant: "destructive", children: [
1586
+ /* @__PURE__ */ jsx18(AlertTitle, { children: "Payment Method Required" }),
1587
+ /* @__PURE__ */ jsxs16(AlertDescription, { className: "mt-2", children: [
1588
+ /* @__PURE__ */ jsx18("p", { className: "mb-4", children: paymentRequiredError ? "Your subscription could not be created because no payment method is on file." : "You need to add a payment method before you can subscribe to a plan." }),
1589
+ onAddPaymentMethod && /* @__PURE__ */ jsx18(Button, { onClick: onAddPaymentMethod, variant: "outline", children: "Add Payment Method" })
1590
+ ] })
1591
+ ] }) : paymentConfirmationState === "confirming" || isConfirming ? /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-center justify-center py-12 space-y-4", children: [
1592
+ /* @__PURE__ */ jsx18(Loader2, { className: "h-8 w-8 animate-spin text-primary" }),
1593
+ /* @__PURE__ */ jsxs16("div", { className: "text-center", children: [
1594
+ /* @__PURE__ */ jsx18("p", { className: "font-medium", children: "Processing payment..." }),
1595
+ /* @__PURE__ */ jsx18("p", { className: "text-sm text-muted-foreground", children: "Please complete any verification if prompted." })
1596
+ ] })
1597
+ ] }) : paymentConfirmationState === "success" ? /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-center justify-center py-12 space-y-4", children: [
1598
+ /* @__PURE__ */ jsx18(CheckCircle, { className: "h-12 w-12 text-green-500" }),
1599
+ /* @__PURE__ */ jsxs16("div", { className: "text-center", children: [
1600
+ /* @__PURE__ */ jsx18("p", { className: "font-medium text-green-600", children: "Payment successful!" }),
1601
+ /* @__PURE__ */ jsx18("p", { className: "text-sm text-muted-foreground", children: "Your subscription is now active." })
1602
+ ] })
1603
+ ] }) : paymentConfirmationState === "error" ? /* @__PURE__ */ jsx18("div", { className: "space-y-4", children: /* @__PURE__ */ jsxs16(Alert, { variant: "destructive", children: [
1604
+ /* @__PURE__ */ jsx18(AlertTitle, { children: "Payment Failed" }),
1605
+ /* @__PURE__ */ jsxs16(AlertDescription, { className: "mt-2", children: [
1606
+ /* @__PURE__ */ jsx18("p", { className: "mb-4", children: paymentError || "We couldn't process your payment. Please try again." }),
1607
+ /* @__PURE__ */ jsx18(
1608
+ Button,
1609
+ {
1610
+ onClick: () => {
1611
+ setPaymentConfirmationState("idle");
1612
+ setPaymentError(null);
1613
+ setLoadingPriceId(null);
1614
+ },
1615
+ variant: "outline",
1616
+ children: "Try Again"
1617
+ }
1618
+ )
1619
+ ] })
1620
+ ] }) }) : /* @__PURE__ */ jsxs16("div", { className: "space-y-6", children: [
1621
+ /* @__PURE__ */ jsx18(
1622
+ PricingCardsGrid,
1623
+ {
1624
+ products,
1625
+ pricesByProduct,
1626
+ currentPriceId,
1627
+ selectedPriceId: selectedPriceId ?? void 0,
1628
+ loadingPriceId: loadingPriceId ?? void 0,
1629
+ loading,
1630
+ onSelectPrice: handleSelectPrice
1631
+ }
1632
+ ),
1633
+ isEditMode && loadingProration && /* @__PURE__ */ jsx18("div", { className: "bg-muted/50 rounded-lg p-4 text-sm text-muted-foreground text-center", children: "Loading proration preview..." }),
1634
+ isEditMode && prorationPreview && !loadingProration && /* @__PURE__ */ jsxs16("div", { className: "space-y-4", children: [
1635
+ /* @__PURE__ */ jsx18(ProrationPreview, { preview: prorationPreview }),
1636
+ /* @__PURE__ */ jsxs16("div", { className: "flex justify-end gap-3", children: [
1637
+ /* @__PURE__ */ jsx18(Button, { variant: "outline", onClick: handleCancel, disabled: !!loadingPriceId, children: "Cancel" }),
1638
+ /* @__PURE__ */ jsx18(Button, { onClick: handleConfirmPlanChange, disabled: !!loadingPriceId, children: loadingPriceId ? "Processing..." : "Confirm Plan Change" })
1639
+ ] })
1640
+ ] })
1641
+ ] })
1642
+ ] }) });
1643
+ }
1644
+ __name(SubscriptionEditor, "SubscriptionEditor");
1645
+
1646
+ // src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx
1647
+ import { useState as useState11 } from "react";
1648
+
1649
+ // src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx
1650
+ import { useState as useState10 } from "react";
1651
+
1652
+ // src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx
1653
+ import { jsx as jsx19 } from "react/jsx-runtime";
1654
+ var statusConfig2 = {
1655
+ ["active" /* ACTIVE */]: {
1656
+ label: "Active",
1657
+ color: "bg-green-100 text-green-800"
1658
+ },
1659
+ ["trialing" /* TRIALING */]: {
1660
+ label: "Trial",
1661
+ color: "bg-blue-100 text-blue-800"
1662
+ },
1663
+ ["past_due" /* PAST_DUE */]: {
1664
+ label: "Past Due",
1665
+ color: "bg-red-100 text-red-800"
1666
+ },
1667
+ ["canceled" /* CANCELED */]: {
1668
+ label: "Canceled",
1669
+ color: "bg-gray-100 text-gray-800"
1670
+ },
1671
+ ["paused" /* PAUSED */]: {
1672
+ label: "Paused",
1673
+ color: "bg-yellow-100 text-yellow-800"
1674
+ },
1675
+ ["unpaid" /* UNPAID */]: {
1676
+ label: "Unpaid",
1677
+ color: "bg-orange-100 text-orange-800"
1678
+ },
1679
+ ["incomplete" /* INCOMPLETE */]: {
1680
+ label: "Incomplete",
1681
+ color: "bg-gray-100 text-gray-800"
1682
+ },
1683
+ ["incomplete_expired" /* INCOMPLETE_EXPIRED */]: {
1684
+ label: "Expired",
1685
+ color: "bg-gray-100 text-gray-800"
1686
+ }
1687
+ };
1688
+ var cancelingConfig = {
1689
+ label: "Canceling",
1690
+ color: "bg-amber-100 text-amber-800"
1691
+ };
1692
+ function SubscriptionStatusBadge({ status, cancelAtPeriodEnd }) {
1693
+ const config = cancelAtPeriodEnd ? cancelingConfig : statusConfig2[status] || statusConfig2["canceled" /* CANCELED */];
1694
+ return /* @__PURE__ */ jsx19("span", { className: `${config.color} text-xs px-2 py-1 rounded-full font-medium`, children: config.label });
1695
+ }
1696
+ __name(SubscriptionStatusBadge, "SubscriptionStatusBadge");
1697
+
1698
+ // src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx
1699
+ import { Fragment as Fragment3, jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
1700
+ function formatPlanName2(price) {
1701
+ if (!price) return "N/A";
1702
+ const productName = price.product?.name || "";
1703
+ const nickname = price.nickname || "";
1704
+ let interval = "";
1705
+ if (price.recurring?.interval) {
1706
+ const intervalMap = {
1707
+ day: "Daily",
1708
+ week: "Weekly",
1709
+ month: "Monthly",
1710
+ year: "Yearly"
1711
+ };
1712
+ interval = intervalMap[price.recurring.interval] || price.recurring.interval;
1713
+ }
1714
+ const parts = [productName, nickname].filter(Boolean);
1715
+ const planLabel = parts.join(" - ");
1716
+ return interval ? `${planLabel} (${interval})` : planLabel || "N/A";
1717
+ }
1718
+ __name(formatPlanName2, "formatPlanName");
1719
+ function formatBillingAmount(price) {
1720
+ if (!price?.unitAmount) return "N/A";
1721
+ return formatCurrency(price.unitAmount, price.currency);
1722
+ }
1723
+ __name(formatBillingAmount, "formatBillingAmount");
1724
+ function SubscriptionDetails({
1725
+ subscription,
1726
+ open,
1727
+ onOpenChange,
1728
+ onSubscriptionChange
1729
+ }) {
1730
+ const [showEdit, setShowEdit] = useState10(false);
1731
+ const [showCancel, setShowCancel] = useState10(false);
1732
+ const [isProcessing, setIsProcessing] = useState10(false);
1733
+ const handlePause = /* @__PURE__ */ __name(async () => {
1734
+ setIsProcessing(true);
1735
+ try {
1736
+ await StripeSubscriptionService.pauseSubscription({ subscriptionId: subscription.id });
1737
+ onSubscriptionChange();
1738
+ } catch (error) {
1739
+ console.error("[SubscriptionDetails] Failed to pause subscription:", error);
1740
+ } finally {
1741
+ setIsProcessing(false);
1742
+ }
1743
+ }, "handlePause");
1744
+ const handleResume = /* @__PURE__ */ __name(async () => {
1745
+ setIsProcessing(true);
1746
+ try {
1747
+ await StripeSubscriptionService.resumeSubscription({ subscriptionId: subscription.id });
1748
+ onSubscriptionChange();
1749
+ } catch (error) {
1750
+ console.error("[SubscriptionDetails] Failed to resume subscription:", error);
1751
+ } finally {
1752
+ setIsProcessing(false);
1753
+ }
1754
+ }, "handleResume");
1755
+ const handleManageViaPortal = /* @__PURE__ */ __name(async () => {
1756
+ try {
1757
+ const { url } = await StripeCustomerService.createPortalSession();
1758
+ window.open(url, "_blank");
1759
+ } catch (error) {
1760
+ console.error("[SubscriptionDetails] Failed to create portal session:", error);
1761
+ }
1762
+ }, "handleManageViaPortal");
1763
+ const canPause = subscription.status === "active" /* ACTIVE */;
1764
+ const canResume = subscription.status === "paused" /* PAUSED */;
1765
+ const canCancel = subscription.status === "active" /* ACTIVE */ || subscription.status === "trialing" /* TRIALING */ || subscription.status === "paused" /* PAUSED */;
1766
+ return /* @__PURE__ */ jsxs17(Fragment3, { children: [
1767
+ /* @__PURE__ */ jsx20(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs17(DialogContent, { className: "max-w-2xl", children: [
1768
+ /* @__PURE__ */ jsxs17(DialogHeader, { children: [
1769
+ /* @__PURE__ */ jsx20(DialogTitle, { children: "Subscription Details" }),
1770
+ /* @__PURE__ */ jsx20(DialogDescription, { children: "View and manage your subscription" })
1771
+ ] }),
1772
+ /* @__PURE__ */ jsxs17("div", { className: "space-y-6", children: [
1773
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-x-3", children: [
1774
+ /* @__PURE__ */ jsx20("span", { className: "text-sm font-medium text-muted-foreground", children: "Status:" }),
1775
+ /* @__PURE__ */ jsx20(SubscriptionStatusBadge, { status: subscription.status, cancelAtPeriodEnd: subscription.cancelAtPeriodEnd })
1776
+ ] }),
1777
+ /* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
1778
+ /* @__PURE__ */ jsxs17("div", { className: "flex justify-between", children: [
1779
+ /* @__PURE__ */ jsx20("span", { className: "text-sm font-medium text-muted-foreground", children: "Plan:" }),
1780
+ /* @__PURE__ */ jsx20("span", { className: "font-medium", children: formatPlanName2(subscription.price) })
1781
+ ] }),
1782
+ /* @__PURE__ */ jsxs17("div", { className: "flex justify-between", children: [
1783
+ /* @__PURE__ */ jsx20("span", { className: "text-sm font-medium text-muted-foreground", children: "Billing Amount:" }),
1784
+ /* @__PURE__ */ jsx20("span", { className: "font-medium", children: formatBillingAmount(subscription.price) })
1785
+ ] })
1786
+ ] }),
1787
+ /* @__PURE__ */ jsx20("div", { className: "space-y-2", children: /* @__PURE__ */ jsxs17("div", { className: "flex justify-between", children: [
1788
+ /* @__PURE__ */ jsx20("span", { className: "text-sm font-medium text-muted-foreground", children: "Current Period:" }),
1789
+ /* @__PURE__ */ jsxs17("span", { className: "font-medium", children: [
1790
+ formatDate3(subscription.currentPeriodStart),
1791
+ " - ",
1792
+ formatDate3(subscription.currentPeriodEnd)
1793
+ ] })
1794
+ ] }) }),
1795
+ subscription.trialEnd && /* @__PURE__ */ jsxs17("div", { className: "flex justify-between", children: [
1796
+ /* @__PURE__ */ jsx20("span", { className: "text-sm font-medium text-muted-foreground", children: "Trial Ends:" }),
1797
+ /* @__PURE__ */ jsx20("span", { className: "font-medium", children: formatDate3(subscription.trialEnd) })
1798
+ ] }),
1799
+ subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsx20("div", { className: "bg-yellow-50 border border-yellow-200 rounded-lg p-3", children: /* @__PURE__ */ jsxs17("p", { className: "text-sm text-yellow-800", children: [
1800
+ "This subscription will be canceled at the end of the current period on",
1801
+ " ",
1802
+ formatDate3(subscription.currentPeriodEnd),
1803
+ "."
1804
+ ] }) }),
1805
+ /* @__PURE__ */ jsxs17("div", { className: "flex flex-wrap gap-2 pt-4 border-t", children: [
1806
+ /* @__PURE__ */ jsx20(Button, { variant: "default", onClick: () => setShowEdit(true), children: "Change Plan" }),
1807
+ canPause && /* @__PURE__ */ jsx20(Button, { variant: "outline", onClick: handlePause, disabled: isProcessing, children: isProcessing ? "Pausing..." : "Pause" }),
1808
+ canResume && /* @__PURE__ */ jsx20(Button, { variant: "outline", onClick: handleResume, disabled: isProcessing, children: isProcessing ? "Resuming..." : "Resume" }),
1809
+ canCancel && /* @__PURE__ */ jsx20(Button, { variant: "destructive", onClick: () => setShowCancel(true), children: "Cancel" }),
1810
+ /* @__PURE__ */ jsx20(Button, { variant: "outline", onClick: handleManageViaPortal, children: "Manage via Portal" })
1811
+ ] })
1812
+ ] })
1813
+ ] }) }),
1814
+ showEdit && /* @__PURE__ */ jsx20(
1815
+ SubscriptionEditor,
1816
+ {
1817
+ subscription,
1818
+ open: showEdit,
1819
+ onOpenChange: setShowEdit,
1820
+ onSuccess: () => {
1821
+ onSubscriptionChange();
1822
+ setShowEdit(false);
1823
+ }
1824
+ }
1825
+ ),
1826
+ showCancel && /* @__PURE__ */ jsx20(
1827
+ CancelSubscriptionDialog,
1828
+ {
1829
+ subscription,
1830
+ open: showCancel,
1831
+ onOpenChange: setShowCancel,
1832
+ onSuccess: () => {
1833
+ onSubscriptionChange();
1834
+ setShowCancel(false);
1835
+ }
1836
+ }
1837
+ )
1838
+ ] });
1839
+ }
1840
+ __name(SubscriptionDetails, "SubscriptionDetails");
1841
+
1842
+ // src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx
1843
+ import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
1844
+ function formatPlanName3(price) {
1845
+ if (!price) return "N/A";
1846
+ const productName = price.product?.name || "";
1847
+ const nickname = price.nickname || "";
1848
+ let interval = "";
1849
+ if (price.recurring?.interval) {
1850
+ const intervalMap = {
1851
+ day: "Daily",
1852
+ week: "Weekly",
1853
+ month: "Monthly",
1854
+ year: "Yearly"
1855
+ };
1856
+ interval = intervalMap[price.recurring.interval] || price.recurring.interval;
1857
+ }
1858
+ const parts = [productName, nickname].filter(Boolean);
1859
+ const planLabel = parts.join(" - ");
1860
+ return interval ? `${planLabel} (${interval})` : planLabel || "N/A";
1861
+ }
1862
+ __name(formatPlanName3, "formatPlanName");
1863
+ function SubscriptionsList({ subscriptions, onSubscriptionsChange }) {
1864
+ const [selectedSub, setSelectedSub] = useState11(null);
1865
+ const handleRowClick = /* @__PURE__ */ __name((subscription) => {
1866
+ setSelectedSub(subscription);
1867
+ }, "handleRowClick");
1868
+ return /* @__PURE__ */ jsxs18(Fragment4, { children: [
1869
+ /* @__PURE__ */ jsx21("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs18(Table, { children: [
1870
+ /* @__PURE__ */ jsx21(TableHeader, { className: "bg-muted", children: /* @__PURE__ */ jsxs18(TableRow, { children: [
1871
+ /* @__PURE__ */ jsx21(TableHead, { children: "Status" }),
1872
+ /* @__PURE__ */ jsx21(TableHead, { children: "Plan" }),
1873
+ /* @__PURE__ */ jsx21(TableHead, { children: "Period" }),
1874
+ /* @__PURE__ */ jsx21(TableHead, { className: "text-right", children: "Amount" })
1875
+ ] }) }),
1876
+ /* @__PURE__ */ jsx21(TableBody, { children: subscriptions.map((subscription) => {
1877
+ const price = subscription.price;
1878
+ const amount = price?.unitAmount ? formatCurrency(price.unitAmount, price.currency) : "N/A";
1879
+ const period = `${formatDate3(subscription.currentPeriodStart)} - ${formatDate3(subscription.currentPeriodEnd)}`;
1880
+ return /* @__PURE__ */ jsxs18(
1881
+ TableRow,
1882
+ {
1883
+ onClick: () => handleRowClick(subscription),
1884
+ className: "cursor-pointer hover:bg-muted/50",
1885
+ children: [
1886
+ /* @__PURE__ */ jsx21(TableCell, { children: /* @__PURE__ */ jsx21(SubscriptionStatusBadge, { status: subscription.status, cancelAtPeriodEnd: subscription.cancelAtPeriodEnd }) }),
1887
+ /* @__PURE__ */ jsx21(TableCell, { className: "font-medium", children: formatPlanName3(price) }),
1888
+ /* @__PURE__ */ jsx21(TableCell, { className: "text-muted-foreground text-sm", children: period }),
1889
+ /* @__PURE__ */ jsx21(TableCell, { className: "text-right font-medium", children: amount })
1890
+ ]
1891
+ },
1892
+ subscription.id
1893
+ );
1894
+ }) })
1895
+ ] }) }),
1896
+ selectedSub && /* @__PURE__ */ jsx21(
1897
+ SubscriptionDetails,
1898
+ {
1899
+ subscription: selectedSub,
1900
+ open: !!selectedSub,
1901
+ onOpenChange: (open) => !open && setSelectedSub(null),
1902
+ onSubscriptionChange: () => {
1903
+ onSubscriptionsChange();
1904
+ setSelectedSub(null);
1905
+ }
1906
+ }
1907
+ )
1908
+ ] });
1909
+ }
1910
+ __name(SubscriptionsList, "SubscriptionsList");
1911
+
1912
+ // src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx
1913
+ import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
1914
+ function SubscriptionsContainer() {
1915
+ const { confirmPayment, isConfirming } = useConfirmSubscriptionPayment();
1916
+ const [subscriptions, setSubscriptions] = useState12([]);
1917
+ const [loading, setLoading] = useState12(true);
1918
+ const [showCreateSubscription, setShowCreateSubscription] = useState12(false);
1919
+ const [showPaymentMethodEditor, setShowPaymentMethodEditor] = useState12(false);
1920
+ const [products, setProducts] = useState12([]);
1921
+ const [pricesByProduct, setPricesByProduct] = useState12(/* @__PURE__ */ new Map());
1922
+ const [loadingPricing, setLoadingPricing] = useState12(false);
1923
+ const [hasPaymentMethod, setHasPaymentMethod] = useState12(null);
1924
+ const [pendingPriceId, setPendingPriceId] = useState12(null);
1925
+ const [creatingSubscription, setCreatingSubscription] = useState12(false);
1926
+ const [paymentConfirmationState, setPaymentConfirmationState] = useState12("idle");
1927
+ const [paymentError, setPaymentError] = useState12(null);
1928
+ const loadSubscriptions = /* @__PURE__ */ __name(async () => {
1929
+ setLoading(true);
1930
+ try {
1931
+ const fetchedSubscriptions = await StripeSubscriptionService.listSubscriptions();
1932
+ setSubscriptions(fetchedSubscriptions);
1933
+ } catch (error) {
1934
+ console.error("[SubscriptionsContainer] Failed to load subscriptions:", error);
1935
+ } finally {
1936
+ setLoading(false);
1937
+ }
1938
+ }, "loadSubscriptions");
1939
+ const loadPricingData = /* @__PURE__ */ __name(async () => {
1940
+ setLoadingPricing(true);
1941
+ try {
1942
+ const fetchedProducts = await StripeProductService.listProducts({ active: true });
1943
+ const grouped = /* @__PURE__ */ new Map();
1944
+ for (const product of fetchedProducts) {
1945
+ if (product.stripePrices && product.stripePrices.length > 0) {
1946
+ grouped.set(product.id, product.stripePrices);
1947
+ }
1948
+ }
1949
+ setProducts(fetchedProducts);
1950
+ setPricesByProduct(grouped);
1951
+ } catch (error) {
1952
+ console.error("[SubscriptionsContainer] Failed to load pricing data:", error);
1953
+ } finally {
1954
+ setLoadingPricing(false);
1955
+ }
1956
+ }, "loadPricingData");
1957
+ const checkPaymentMethod = /* @__PURE__ */ __name(async () => {
1958
+ try {
1959
+ const paymentMethods = await StripeCustomerService.listPaymentMethods();
1960
+ const hasMethod = paymentMethods.length > 0;
1961
+ setHasPaymentMethod(hasMethod);
1962
+ } catch (error) {
1963
+ console.error("[SubscriptionsContainer] Failed to check payment methods:", error);
1964
+ setHasPaymentMethod(false);
1965
+ }
1966
+ }, "checkPaymentMethod");
1967
+ const createSubscriptionWithPrice = /* @__PURE__ */ __name(async (priceId) => {
1968
+ setCreatingSubscription(true);
1969
+ setPaymentError(null);
1970
+ setPaymentConfirmationState("idle");
1971
+ try {
1972
+ const result = await StripeSubscriptionService.createSubscription({
1973
+ id: v42(),
1974
+ priceId
1975
+ });
1976
+ if (result.meta.requiresAction && result.meta.clientSecret) {
1977
+ setPaymentConfirmationState("confirming");
1978
+ const confirmation = await confirmPayment(result.meta.clientSecret);
1979
+ if (!confirmation.success) {
1980
+ console.error("[SubscriptionsContainer] Payment confirmation failed:", confirmation.error);
1981
+ setPaymentConfirmationState("error");
1982
+ setPaymentError(confirmation.error || "Payment confirmation failed");
1983
+ setCreatingSubscription(false);
1984
+ return;
1985
+ }
1986
+ await StripeSubscriptionService.syncSubscription({
1987
+ subscriptionId: result.subscription.id
1988
+ });
1989
+ }
1990
+ setPaymentConfirmationState("success");
1991
+ await loadSubscriptions();
1992
+ } catch (error) {
1993
+ console.error("[SubscriptionsContainer] Failed to create subscription:", error);
1994
+ if (error?.status === 402 || error?.response?.status === 402) {
1995
+ setPendingPriceId(priceId);
1996
+ setHasPaymentMethod(false);
1997
+ setShowPaymentMethodEditor(true);
1998
+ } else {
1999
+ setPaymentConfirmationState("error");
2000
+ setPaymentError(error?.message || "Failed to create subscription");
2001
+ }
2002
+ } finally {
2003
+ setCreatingSubscription(false);
2004
+ }
2005
+ }, "createSubscriptionWithPrice");
2006
+ const handleSelectPrice = /* @__PURE__ */ __name(async (price) => {
2007
+ const priceId = price.id;
2008
+ if (!hasPaymentMethod) {
2009
+ setPendingPriceId(priceId);
2010
+ setShowPaymentMethodEditor(true);
2011
+ return;
2012
+ }
2013
+ await createSubscriptionWithPrice(priceId);
2014
+ }, "handleSelectPrice");
2015
+ const handlePaymentMethodSuccess = /* @__PURE__ */ __name(async () => {
2016
+ setShowPaymentMethodEditor(false);
2017
+ setHasPaymentMethod(true);
2018
+ if (pendingPriceId) {
2019
+ await createSubscriptionWithPrice(pendingPriceId);
2020
+ setPendingPriceId(null);
2021
+ }
2022
+ }, "handlePaymentMethodSuccess");
2023
+ useEffect6(() => {
2024
+ loadSubscriptions();
2025
+ }, []);
2026
+ useEffect6(() => {
2027
+ if (!loading && subscriptions.length === 0) {
2028
+ loadPricingData();
2029
+ checkPaymentMethod();
2030
+ }
2031
+ }, [loading, subscriptions.length]);
2032
+ const criticalSubscriptions = subscriptions.filter(
2033
+ (sub) => sub.status === "past_due" /* PAST_DUE */ || sub.status === "trialing" /* TRIALING */ && sub.trialEnd && new Date(sub.trialEnd).getTime() - (/* @__PURE__ */ new Date()).getTime() <= 7 * 24 * 60 * 60 * 1e3
2034
+ );
2035
+ if (loading) {
2036
+ return /* @__PURE__ */ jsx22("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx22("p", { className: "text-muted-foreground", children: "Loading subscriptions..." }) });
2037
+ }
2038
+ return /* @__PURE__ */ jsxs19("div", { className: "flex w-full flex-col gap-y-6", children: [
2039
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center justify-between", children: [
2040
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-x-3", children: [
2041
+ /* @__PURE__ */ jsx22(CreditCard3, { className: "h-8 w-8" }),
2042
+ /* @__PURE__ */ jsx22("h1", { className: "text-3xl font-bold", children: "Subscriptions" })
2043
+ ] }),
2044
+ subscriptions.length > 0 && /* @__PURE__ */ jsx22(Button, { onClick: () => setShowCreateSubscription(true), children: "Subscribe to a Plan" })
2045
+ ] }),
2046
+ criticalSubscriptions.map((subscription) => /* @__PURE__ */ jsx22(BillingAlertBanner, { subscription }, subscription.id)),
2047
+ subscriptions.length === 0 && /* @__PURE__ */ jsxs19("div", { className: "space-y-6", children: [
2048
+ (paymentConfirmationState === "confirming" || isConfirming) && /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center justify-center py-12 space-y-4 bg-muted/50 rounded-lg", children: [
2049
+ /* @__PURE__ */ jsx22(Loader22, { className: "h-8 w-8 animate-spin text-primary" }),
2050
+ /* @__PURE__ */ jsxs19("div", { className: "text-center", children: [
2051
+ /* @__PURE__ */ jsx22("p", { className: "font-medium", children: "Processing payment..." }),
2052
+ /* @__PURE__ */ jsx22("p", { className: "text-sm text-muted-foreground", children: "Please complete any verification if prompted." })
2053
+ ] })
2054
+ ] }),
2055
+ paymentConfirmationState === "success" && /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center justify-center py-8 space-y-4 bg-green-50 rounded-lg border border-green-200", children: [
2056
+ /* @__PURE__ */ jsx22(CheckCircle2, { className: "h-12 w-12 text-green-500" }),
2057
+ /* @__PURE__ */ jsxs19("div", { className: "text-center", children: [
2058
+ /* @__PURE__ */ jsx22("p", { className: "font-medium text-green-600", children: "Payment successful!" }),
2059
+ /* @__PURE__ */ jsx22("p", { className: "text-sm text-muted-foreground", children: "Your subscription is now active." })
2060
+ ] })
2061
+ ] }),
2062
+ paymentConfirmationState === "error" && /* @__PURE__ */ jsxs19(Alert, { variant: "destructive", children: [
2063
+ /* @__PURE__ */ jsx22(AlertTitle, { children: "Payment Failed" }),
2064
+ /* @__PURE__ */ jsxs19(AlertDescription, { className: "mt-2", children: [
2065
+ /* @__PURE__ */ jsx22("p", { className: "mb-4", children: paymentError || "We couldn't process your payment. Please try again." }),
2066
+ /* @__PURE__ */ jsx22(
2067
+ Button,
2068
+ {
2069
+ onClick: () => {
2070
+ setPaymentConfirmationState("idle");
2071
+ setPaymentError(null);
2072
+ },
2073
+ variant: "outline",
2074
+ children: "Try Again"
2075
+ }
2076
+ )
2077
+ ] })
2078
+ ] }),
2079
+ paymentConfirmationState === "idle" && !isConfirming && /* @__PURE__ */ jsxs19(Fragment5, { children: [
2080
+ /* @__PURE__ */ jsxs19("div", { className: "text-center", children: [
2081
+ /* @__PURE__ */ jsx22(CreditCard3, { className: "text-muted-foreground mx-auto h-16 w-16 mb-4" }),
2082
+ /* @__PURE__ */ jsx22("h3", { className: "mb-2 text-xl font-semibold", children: "Choose Your Plan" }),
2083
+ /* @__PURE__ */ jsx22("p", { className: "text-muted-foreground mb-6", children: "Select a subscription plan to get started with our services." })
2084
+ ] }),
2085
+ /* @__PURE__ */ jsx22(
2086
+ PricingCardsGrid,
2087
+ {
2088
+ products,
2089
+ pricesByProduct,
2090
+ loading: loadingPricing,
2091
+ loadingPriceId: creatingSubscription ? pendingPriceId ?? void 0 : void 0,
2092
+ onSelectPrice: handleSelectPrice
2093
+ }
2094
+ )
2095
+ ] })
2096
+ ] }),
2097
+ subscriptions.length > 0 && /* @__PURE__ */ jsx22(SubscriptionsList, { subscriptions, onSubscriptionsChange: loadSubscriptions }),
2098
+ showCreateSubscription && /* @__PURE__ */ jsx22(
2099
+ SubscriptionEditor,
2100
+ {
2101
+ open: showCreateSubscription,
2102
+ onOpenChange: setShowCreateSubscription,
2103
+ onSuccess: loadSubscriptions,
2104
+ onAddPaymentMethod: () => {
2105
+ setShowCreateSubscription(false);
2106
+ setShowPaymentMethodEditor(true);
2107
+ }
2108
+ }
2109
+ ),
2110
+ showPaymentMethodEditor && /* @__PURE__ */ jsx22(
2111
+ PaymentMethodEditor,
2112
+ {
2113
+ open: showPaymentMethodEditor,
2114
+ onOpenChange: (open) => {
2115
+ setShowPaymentMethodEditor(open);
2116
+ if (!open) {
2117
+ setPendingPriceId(null);
2118
+ }
2119
+ },
2120
+ onSuccess: handlePaymentMethodSuccess
2121
+ }
2122
+ )
2123
+ ] });
2124
+ }
2125
+ __name(SubscriptionsContainer, "SubscriptionsContainer");
2126
+
2127
+ // src/features/billing/stripe-usage/components/containers/UsageContainer.tsx
2128
+ import { Activity as Activity3 } from "lucide-react";
2129
+ import { useEffect as useEffect7, useState as useState13 } from "react";
2130
+
2131
+ // src/features/billing/stripe-usage/components/details/UsageSummaryCard.tsx
2132
+ import { Activity as Activity2 } from "lucide-react";
2133
+ import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
2134
+ function getProgressColor(percentage) {
2135
+ if (percentage === null) return "bg-blue-500";
2136
+ if (percentage >= 90) return "bg-red-500";
2137
+ if (percentage >= 75) return "bg-orange-500";
2138
+ return "bg-green-500";
2139
+ }
2140
+ __name(getProgressColor, "getProgressColor");
2141
+ function formatDate4(date) {
2142
+ if (!date) return "N/A";
2143
+ try {
2144
+ return new Intl.DateTimeFormat("en-US", {
2145
+ month: "short",
2146
+ day: "numeric"
2147
+ }).format(new Date(date));
2148
+ } catch (error) {
2149
+ return "Invalid Date";
2150
+ }
2151
+ }
2152
+ __name(formatDate4, "formatDate");
2153
+ function UsageSummaryCard({ meter, summary }) {
2154
+ const currentUsage = summary?.aggregatedValue ?? 0;
2155
+ const limit = meter.limit;
2156
+ const percentage = limit && limit > 0 ? currentUsage / limit * 100 : null;
2157
+ const progressColor = getProgressColor(percentage);
2158
+ const progressWidth = percentage !== null ? Math.min(percentage, 100) : 0;
2159
+ const displayName = meter.displayName || meter.eventName;
2160
+ const hasLimit = limit !== null && limit !== void 0;
2161
+ return /* @__PURE__ */ jsxs20(Card, { children: [
2162
+ /* @__PURE__ */ jsxs20(CardHeader, { className: "flex flex-row items-center gap-x-3 pb-3", children: [
2163
+ /* @__PURE__ */ jsx23("div", { className: "flex h-10 w-10 items-center justify-center rounded-lg bg-blue-100 text-blue-600", children: /* @__PURE__ */ jsx23(Activity2, { className: "h-5 w-5" }) }),
2164
+ /* @__PURE__ */ jsxs20("div", { className: "flex flex-col", children: [
2165
+ /* @__PURE__ */ jsx23("h3", { className: "font-semibold", children: displayName }),
2166
+ /* @__PURE__ */ jsx23("p", { className: "text-xs text-gray-500", children: meter.id })
2167
+ ] })
2168
+ ] }),
2169
+ /* @__PURE__ */ jsxs20(CardContent, { className: "flex flex-col gap-y-4", children: [
2170
+ /* @__PURE__ */ jsxs20("div", { children: [
2171
+ /* @__PURE__ */ jsx23("p", { className: "text-3xl font-bold", children: currentUsage.toLocaleString() }),
2172
+ hasLimit && /* @__PURE__ */ jsxs20("p", { className: "text-sm text-gray-500", children: [
2173
+ "of ",
2174
+ limit.toLocaleString(),
2175
+ " used"
2176
+ ] })
2177
+ ] }),
2178
+ hasLimit ? /* @__PURE__ */ jsxs20("div", { className: "flex flex-col gap-y-2", children: [
2179
+ /* @__PURE__ */ jsx23("div", { className: "h-2 w-full overflow-hidden rounded-full bg-gray-200", children: /* @__PURE__ */ jsx23("div", { className: `h-full transition-all ${progressColor}`, style: { width: `${progressWidth}%` } }) }),
2180
+ /* @__PURE__ */ jsxs20("p", { className: "text-sm text-gray-500", children: [
2181
+ percentage?.toFixed(1),
2182
+ "% used"
2183
+ ] })
2184
+ ] }) : /* @__PURE__ */ jsx23("p", { className: "text-sm text-gray-500", children: "No limit set" }),
2185
+ summary && summary.start && summary.end && /* @__PURE__ */ jsx23("div", { className: "border-t pt-3", children: /* @__PURE__ */ jsxs20("p", { className: "text-xs text-gray-500", children: [
2186
+ "Period: ",
2187
+ formatDate4(summary.start),
2188
+ " - ",
2189
+ formatDate4(summary.end)
2190
+ ] }) })
2191
+ ] })
2192
+ ] });
2193
+ }
2194
+ __name(UsageSummaryCard, "UsageSummaryCard");
2195
+
2196
+ // src/features/billing/stripe-usage/components/widgets/UsageSummaryCards.tsx
2197
+ import { jsx as jsx24 } from "react/jsx-runtime";
2198
+ function UsageSummaryCards({ meters, summaries }) {
2199
+ return /* @__PURE__ */ jsx24("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: meters.map((meter) => /* @__PURE__ */ jsx24(UsageSummaryCard, { meter, summary: summaries[meter.id] || null }, meter.id)) });
2200
+ }
2201
+ __name(UsageSummaryCards, "UsageSummaryCards");
2202
+
2203
+ // src/features/billing/stripe-usage/components/containers/UsageContainer.tsx
2204
+ import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
2205
+ function UsageContainer() {
2206
+ const [meters, setMeters] = useState13([]);
2207
+ const [summaries, setSummaries] = useState13({});
2208
+ const [loading, setLoading] = useState13(true);
2209
+ const [subscriptions, setSubscriptions] = useState13([]);
2210
+ useEffect7(() => {
2211
+ loadUsageData();
2212
+ }, []);
2213
+ const loadUsageData = /* @__PURE__ */ __name(async () => {
2214
+ setLoading(true);
2215
+ try {
2216
+ const fetchedSubscriptions = await StripeSubscriptionService.listSubscriptions();
2217
+ setSubscriptions(fetchedSubscriptions);
2218
+ const hasMeteredSubscriptions2 = fetchedSubscriptions.some((sub) => sub.price?.recurring?.usageType === "metered");
2219
+ if (!hasMeteredSubscriptions2) {
2220
+ setLoading(false);
2221
+ return;
2222
+ }
2223
+ const fetchedMeters = await StripeUsageService.listMeters();
2224
+ setMeters(fetchedMeters);
2225
+ const summariesMap = {};
2226
+ const now = /* @__PURE__ */ new Date();
2227
+ const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
2228
+ const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
2229
+ for (const meter of fetchedMeters) {
2230
+ try {
2231
+ const meterSummaries = await StripeUsageService.getMeterSummaries({
2232
+ meterId: meter.id,
2233
+ startTime: startOfMonth,
2234
+ endTime: endOfMonth
2235
+ });
2236
+ summariesMap[meter.id] = meterSummaries.length > 0 ? meterSummaries[0] : null;
2237
+ } catch (error) {
2238
+ console.error(`[UsageContainer] Failed to load summaries for meter ${meter.id}:`, error);
2239
+ summariesMap[meter.id] = null;
2240
+ }
2241
+ }
2242
+ setSummaries(summariesMap);
2243
+ } catch (error) {
2244
+ console.error("[UsageContainer] Failed to load usage data:", error);
2245
+ } finally {
2246
+ setLoading(false);
2247
+ }
2248
+ }, "loadUsageData");
2249
+ const hasMeteredSubscriptions = subscriptions.some((sub) => sub.price?.recurring?.usageType === "metered");
2250
+ if (!loading && !hasMeteredSubscriptions) {
2251
+ return null;
2252
+ }
2253
+ if (loading) {
2254
+ return /* @__PURE__ */ jsx25("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx25("p", { className: "text-muted-foreground", children: "Loading usage data..." }) });
2255
+ }
2256
+ return /* @__PURE__ */ jsxs21("div", { className: "flex w-full flex-col gap-y-6", children: [
2257
+ /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-x-3", children: [
2258
+ /* @__PURE__ */ jsx25(Activity3, { className: "h-8 w-8" }),
2259
+ /* @__PURE__ */ jsx25("h1", { className: "text-3xl font-bold", children: "Usage Tracking" })
2260
+ ] }),
2261
+ meters.length === 0 && /* @__PURE__ */ jsxs21("div", { className: "bg-muted/50 flex flex-col items-center justify-center gap-y-4 rounded-lg border-2 border-dashed border-gray-300 p-12", children: [
2262
+ /* @__PURE__ */ jsx25(Activity3, { className: "text-muted-foreground h-16 w-16" }),
2263
+ /* @__PURE__ */ jsxs21("div", { className: "text-center", children: [
2264
+ /* @__PURE__ */ jsx25("h3", { className: "mb-2 text-xl font-semibold", children: "No usage meters configured" }),
2265
+ /* @__PURE__ */ jsx25("p", { className: "text-muted-foreground", children: "Usage tracking will appear here when you have metered subscriptions with configured meters." })
2266
+ ] })
2267
+ ] }),
2268
+ meters.length > 0 && /* @__PURE__ */ jsx25(UsageSummaryCards, { meters, summaries })
2269
+ ] });
2270
+ }
2271
+ __name(UsageContainer, "UsageContainer");
2272
+
2273
+ // src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx
2274
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
2275
+ function formatDateTime(date) {
2276
+ if (!date) return "N/A";
2277
+ const dateObj = typeof date === "string" ? new Date(date) : date;
2278
+ try {
2279
+ return new Intl.DateTimeFormat("en-US", {
2280
+ month: "short",
2281
+ day: "numeric",
2282
+ year: "numeric",
2283
+ hour: "numeric",
2284
+ minute: "2-digit"
2285
+ }).format(dateObj);
2286
+ } catch (error) {
2287
+ return "Invalid Date";
2288
+ }
2289
+ }
2290
+ __name(formatDateTime, "formatDateTime");
2291
+ function UsageHistoryTable({ usageRecords }) {
2292
+ if (usageRecords.length === 0) {
2293
+ return /* @__PURE__ */ jsx26("div", { className: "rounded-lg border p-8 text-center", children: /* @__PURE__ */ jsx26("p", { className: "text-muted-foreground", children: "No usage history available." }) });
2294
+ }
2295
+ return /* @__PURE__ */ jsxs22("div", { className: "flex w-full flex-col gap-y-4", children: [
2296
+ /* @__PURE__ */ jsx26("h2", { className: "text-xl font-semibold", children: "Usage History" }),
2297
+ /* @__PURE__ */ jsx26("div", { className: "overflow-hidden rounded-lg border", children: /* @__PURE__ */ jsxs22(Table, { children: [
2298
+ /* @__PURE__ */ jsx26(TableHeader, { className: "bg-muted", children: /* @__PURE__ */ jsxs22(TableRow, { children: [
2299
+ /* @__PURE__ */ jsx26(TableHead, { children: "Date & Time" }),
2300
+ /* @__PURE__ */ jsx26(TableHead, { children: "Meter Event" }),
2301
+ /* @__PURE__ */ jsx26(TableHead, { className: "text-right", children: "Quantity" }),
2302
+ /* @__PURE__ */ jsx26(TableHead, { children: "Event ID" })
2303
+ ] }) }),
2304
+ /* @__PURE__ */ jsx26(TableBody, { children: usageRecords.map((record) => {
2305
+ const dateTime = formatDateTime(record.timestamp);
2306
+ const quantity = record.quantity.toLocaleString();
2307
+ return /* @__PURE__ */ jsxs22(TableRow, { children: [
2308
+ /* @__PURE__ */ jsx26(TableCell, { className: "font-medium", children: dateTime }),
2309
+ /* @__PURE__ */ jsx26(TableCell, { className: "text-muted-foreground", children: record.meterEventName }),
2310
+ /* @__PURE__ */ jsx26(TableCell, { className: "text-right font-medium", children: quantity }),
2311
+ /* @__PURE__ */ jsx26(TableCell, { className: "text-muted-foreground text-sm font-mono", children: record.stripeEventId })
2312
+ ] }, record.id);
2313
+ }) })
2314
+ ] }) })
2315
+ ] });
2316
+ }
2317
+ __name(UsageHistoryTable, "UsageHistoryTable");
2318
+
2319
+ // src/features/billing/components/modals/BillingDetailModal.tsx
2320
+ import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
2321
+ function BillingDetailModal({
2322
+ open,
2323
+ onOpenChange,
2324
+ title,
2325
+ children,
2326
+ className
2327
+ }) {
2328
+ return /* @__PURE__ */ jsx27(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs23(DialogContent, { className: className ?? "max-w-4xl max-h-[90vh] overflow-y-auto", children: [
2329
+ /* @__PURE__ */ jsx27(DialogHeader, { children: /* @__PURE__ */ jsx27(DialogTitle, { children: title }) }),
2330
+ children
2331
+ ] }) });
2332
+ }
2333
+ __name(BillingDetailModal, "BillingDetailModal");
2334
+
2335
+ // src/features/billing/components/widgets/BillingAlertBanner.tsx
2336
+ import { AlertCircle } from "lucide-react";
2337
+ import { jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
2338
+ function BillingAlertBanner({ subscription, onUpdatePayment, onAddPayment }) {
2339
+ if (subscription.status === "past_due" /* PAST_DUE */) {
2340
+ return /* @__PURE__ */ jsxs24("div", { className: "bg-red-50 border border-red-200 rounded-lg p-4 flex items-start gap-x-3", children: [
2341
+ /* @__PURE__ */ jsx28(AlertCircle, { className: "h-5 w-5 text-red-600 mt-0.5" }),
2342
+ /* @__PURE__ */ jsxs24("div", { className: "flex-1", children: [
2343
+ /* @__PURE__ */ jsx28("h3", { className: "font-semibold text-red-900", children: "Payment Failed" }),
2344
+ /* @__PURE__ */ jsx28("p", { className: "text-sm text-red-700 mt-1", children: "Your last payment failed. Please update your payment method to avoid service interruption." })
2345
+ ] }),
2346
+ onUpdatePayment && /* @__PURE__ */ jsx28(Button, { variant: "outline", size: "sm", onClick: onUpdatePayment, className: "border-red-300 text-red-700", children: "Update Payment Method" })
2347
+ ] });
2348
+ }
2349
+ if (subscription.status === "trialing" /* TRIALING */ && subscription.trialEnd) {
2350
+ const trialEnd = new Date(subscription.trialEnd);
2351
+ const now = /* @__PURE__ */ new Date();
2352
+ const daysRemaining = Math.ceil((trialEnd.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24));
2353
+ if (daysRemaining <= 7) {
2354
+ return /* @__PURE__ */ jsxs24("div", { className: "bg-yellow-50 border border-yellow-200 rounded-lg p-4 flex items-start gap-x-3", children: [
2355
+ /* @__PURE__ */ jsx28(AlertCircle, { className: "h-5 w-5 text-yellow-600 mt-0.5" }),
2356
+ /* @__PURE__ */ jsxs24("div", { className: "flex-1", children: [
2357
+ /* @__PURE__ */ jsx28("h3", { className: "font-semibold text-yellow-900", children: "Trial Ending Soon" }),
2358
+ /* @__PURE__ */ jsxs24("p", { className: "text-sm text-yellow-700 mt-1", children: [
2359
+ "Your trial ends in ",
2360
+ daysRemaining,
2361
+ " ",
2362
+ daysRemaining === 1 ? "day" : "days",
2363
+ ". Add a payment method to continue your subscription."
2364
+ ] })
2365
+ ] }),
2366
+ onAddPayment && /* @__PURE__ */ jsx28(Button, { variant: "outline", size: "sm", onClick: onAddPayment, className: "border-yellow-300 text-yellow-700", children: "Add Payment Method" })
2367
+ ] });
2368
+ }
2369
+ }
2370
+ return null;
2371
+ }
2372
+ __name(BillingAlertBanner, "BillingAlertBanner");
2373
+
2374
+ // src/features/billing/components/containers/BillingDashboardContainer.tsx
2375
+ import { Fragment as Fragment6, jsx as jsx29, jsxs as jsxs25 } from "react/jsx-runtime";
2376
+ function BillingDashboardContainer() {
2377
+ const [data, setData] = useState14({
2378
+ customer: null,
2379
+ subscriptions: [],
2380
+ paymentMethods: [],
2381
+ invoices: [],
2382
+ meters: [],
2383
+ meterSummaries: {}
2384
+ });
2385
+ const [loading, setLoading] = useState14({
2386
+ customer: true,
2387
+ subscriptions: true,
2388
+ paymentMethods: true,
2389
+ invoices: true,
2390
+ usage: true
2391
+ });
2392
+ const [errors, setErrors] = useState14({
2393
+ customer: null,
2394
+ subscriptions: null,
2395
+ paymentMethods: null,
2396
+ invoices: null,
2397
+ usage: null
2398
+ });
2399
+ const [activeModal, setActiveModal] = useState14(null);
2400
+ const [noCustomerExists, setNoCustomerExists] = useState14(false);
2401
+ const [creatingCustomer, setCreatingCustomer] = useState14(false);
2402
+ const hasMeteredSubscriptions = useCallback2(() => {
2403
+ return data.subscriptions.some((sub) => sub.price?.recurring?.usageType === "metered");
2404
+ }, [data.subscriptions]);
2405
+ const fetchAllData = useCallback2(async () => {
2406
+ setNoCustomerExists(false);
2407
+ let customer = null;
2408
+ try {
2409
+ customer = await StripeCustomerService.getCustomer();
2410
+ setData((prev) => ({ ...prev, customer }));
2411
+ setErrors((prev) => ({ ...prev, customer: null }));
2412
+ setNoCustomerExists(false);
2413
+ } catch (error) {
2414
+ console.error("[BillingDashboard] Failed to load customer:", error);
2415
+ const errorMessage = error instanceof Error ? error.message : String(error);
2416
+ if (errorMessage.includes("Not Found") || errorMessage.includes("not found")) {
2417
+ setNoCustomerExists(true);
2418
+ setLoading({
2419
+ customer: false,
2420
+ subscriptions: false,
2421
+ paymentMethods: false,
2422
+ invoices: false,
2423
+ usage: false
2424
+ });
2425
+ return;
2426
+ }
2427
+ setErrors((prev) => ({ ...prev, customer: "Failed to load billing account" }));
2428
+ } finally {
2429
+ setLoading((prev) => ({ ...prev, customer: false }));
2430
+ }
2431
+ if (customer) {
2432
+ const fetchSubscriptions = /* @__PURE__ */ __name(async () => {
2433
+ try {
2434
+ const subscriptions2 = await StripeSubscriptionService.listSubscriptions();
2435
+ setData((prev) => ({ ...prev, subscriptions: subscriptions2 }));
2436
+ setErrors((prev) => ({ ...prev, subscriptions: null }));
2437
+ return subscriptions2;
2438
+ } catch (error) {
2439
+ console.error("[BillingDashboard] Failed to load subscriptions:", error);
2440
+ setErrors((prev) => ({ ...prev, subscriptions: "Failed to load subscriptions" }));
2441
+ return [];
2442
+ } finally {
2443
+ setLoading((prev) => ({ ...prev, subscriptions: false }));
2444
+ }
2445
+ }, "fetchSubscriptions");
2446
+ const fetchPaymentMethods = /* @__PURE__ */ __name(async () => {
2447
+ try {
2448
+ const paymentMethods = await StripeCustomerService.listPaymentMethods();
2449
+ setData((prev) => ({ ...prev, paymentMethods }));
2450
+ setErrors((prev) => ({ ...prev, paymentMethods: null }));
2451
+ } catch (error) {
2452
+ console.error("[BillingDashboard] Failed to load payment methods:", error);
2453
+ setErrors((prev) => ({ ...prev, paymentMethods: "Failed to load payment methods" }));
2454
+ } finally {
2455
+ setLoading((prev) => ({ ...prev, paymentMethods: false }));
2456
+ }
2457
+ }, "fetchPaymentMethods");
2458
+ const fetchInvoices = /* @__PURE__ */ __name(async () => {
2459
+ try {
2460
+ const invoices = await StripeInvoiceService.listInvoices();
2461
+ setData((prev) => ({ ...prev, invoices }));
2462
+ setErrors((prev) => ({ ...prev, invoices: null }));
2463
+ } catch (error) {
2464
+ console.error("[BillingDashboard] Failed to load invoices:", error);
2465
+ setErrors((prev) => ({ ...prev, invoices: "Failed to load invoices" }));
2466
+ } finally {
2467
+ setLoading((prev) => ({ ...prev, invoices: false }));
2468
+ }
2469
+ }, "fetchInvoices");
2470
+ const [subscriptions] = await Promise.all([fetchSubscriptions(), fetchPaymentMethods(), fetchInvoices()]);
2471
+ const hasMetered = subscriptions.some(
2472
+ (sub) => sub.price?.recurring?.usageType === "metered"
2473
+ );
2474
+ if (hasMetered) {
2475
+ await fetchUsageData();
2476
+ } else {
2477
+ setLoading((prev) => ({ ...prev, usage: false }));
2478
+ }
2479
+ }
2480
+ }, []);
2481
+ const handleCreateCustomer = /* @__PURE__ */ __name(async () => {
2482
+ setCreatingCustomer(true);
2483
+ try {
2484
+ await StripeCustomerService.createCustomer();
2485
+ setNoCustomerExists(false);
2486
+ await fetchAllData();
2487
+ } catch (error) {
2488
+ console.error("[BillingDashboard] Failed to create customer:", error);
2489
+ setErrors((prev) => ({ ...prev, customer: "Failed to set up billing" }));
2490
+ } finally {
2491
+ setCreatingCustomer(false);
2492
+ }
2493
+ }, "handleCreateCustomer");
2494
+ const fetchUsageData = /* @__PURE__ */ __name(async () => {
2495
+ try {
2496
+ const meters = await StripeUsageService.listMeters();
2497
+ setData((prev) => ({ ...prev, meters }));
2498
+ const summariesMap = {};
2499
+ const now = /* @__PURE__ */ new Date();
2500
+ const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
2501
+ const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
2502
+ for (const meter of meters) {
2503
+ try {
2504
+ const meterSummaries = await StripeUsageService.getMeterSummaries({
2505
+ meterId: meter.id,
2506
+ startTime: startOfMonth,
2507
+ endTime: endOfMonth
2508
+ });
2509
+ summariesMap[meter.id] = meterSummaries.length > 0 ? meterSummaries[0] : null;
2510
+ } catch (error) {
2511
+ console.error(`[BillingDashboard] Failed to load summaries for meter ${meter.id}:`, error);
2512
+ summariesMap[meter.id] = null;
2513
+ }
2514
+ }
2515
+ setData((prev) => ({ ...prev, meterSummaries: summariesMap }));
2516
+ setErrors((prev) => ({ ...prev, usage: null }));
2517
+ } catch (error) {
2518
+ console.error("[BillingDashboard] Failed to load usage data:", error);
2519
+ setErrors((prev) => ({ ...prev, usage: "Failed to load usage data" }));
2520
+ } finally {
2521
+ setLoading((prev) => ({ ...prev, usage: false }));
2522
+ }
2523
+ }, "fetchUsageData");
2524
+ const refreshData = useCallback2(async () => {
2525
+ setLoading({
2526
+ customer: true,
2527
+ subscriptions: true,
2528
+ paymentMethods: true,
2529
+ invoices: true,
2530
+ usage: true
2531
+ });
2532
+ await fetchAllData();
2533
+ }, [fetchAllData]);
2534
+ useEffect8(() => {
2535
+ fetchAllData();
2536
+ }, [fetchAllData]);
2537
+ const criticalSubscriptions = data.subscriptions.filter(
2538
+ (sub) => sub.status === "past_due" /* PAST_DUE */ || sub.status === "trialing" /* TRIALING */ && sub.trialEnd && new Date(sub.trialEnd).getTime() - (/* @__PURE__ */ new Date()).getTime() <= 7 * 24 * 60 * 60 * 1e3
2539
+ );
2540
+ const handleModalClose = /* @__PURE__ */ __name((open) => {
2541
+ if (!open) {
2542
+ setActiveModal(null);
2543
+ refreshData();
2544
+ }
2545
+ }, "handleModalClose");
2546
+ const getModalTitle = /* @__PURE__ */ __name((type) => {
2547
+ switch (type) {
2548
+ case "subscriptions":
2549
+ return "Manage Subscriptions";
2550
+ case "payment-methods":
2551
+ return "Payment Methods";
2552
+ case "invoices":
2553
+ return "Invoice History";
2554
+ case "usage":
2555
+ return "Usage Tracking";
2556
+ default:
2557
+ return "";
2558
+ }
2559
+ }, "getModalTitle");
2560
+ const isInitialLoading = loading.customer && !noCustomerExists && !data.customer;
2561
+ return /* @__PURE__ */ jsxs25("div", { className: "flex w-full flex-col gap-y-6", children: [
2562
+ /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-x-3", children: [
2563
+ /* @__PURE__ */ jsx29(Wallet2, { className: "h-8 w-8" }),
2564
+ /* @__PURE__ */ jsx29("h1", { className: "text-3xl font-bold", children: "Billing" })
2565
+ ] }),
2566
+ isInitialLoading && /* @__PURE__ */ jsx29(Card, { children: /* @__PURE__ */ jsx29(CardContent, { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx29(Loader23, { className: "h-8 w-8 animate-spin text-muted-foreground" }) }) }),
2567
+ noCustomerExists && !isInitialLoading && /* @__PURE__ */ jsxs25(Card, { children: [
2568
+ /* @__PURE__ */ jsxs25(CardHeader, { className: "text-center", children: [
2569
+ /* @__PURE__ */ jsx29("div", { className: "mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsx29(CreditCard4, { className: "h-8 w-8 text-primary" }) }),
2570
+ /* @__PURE__ */ jsx29(CardTitle, { children: "Set Up Billing" }),
2571
+ /* @__PURE__ */ jsx29(CardDescription, { children: "Your company doesn't have a billing account yet. Set one up to manage subscriptions, payment methods, and view invoices." })
2572
+ ] }),
2573
+ /* @__PURE__ */ jsx29(CardContent, { className: "flex justify-center pb-8", children: /* @__PURE__ */ jsx29(Button, { onClick: handleCreateCustomer, disabled: creatingCustomer, size: "lg", children: creatingCustomer ? /* @__PURE__ */ jsxs25(Fragment6, { children: [
2574
+ /* @__PURE__ */ jsx29(Loader23, { className: "mr-2 h-4 w-4 animate-spin" }),
2575
+ "Setting up..."
2576
+ ] }) : "Set Up Billing Account" }) }),
2577
+ errors.customer && /* @__PURE__ */ jsx29(CardContent, { className: "pt-0", children: /* @__PURE__ */ jsx29("p", { className: "text-center text-sm text-destructive", children: errors.customer }) })
2578
+ ] }),
2579
+ !noCustomerExists && !isInitialLoading && /* @__PURE__ */ jsxs25(Fragment6, { children: [
2580
+ criticalSubscriptions.map((subscription) => /* @__PURE__ */ jsx29(
2581
+ BillingAlertBanner,
2582
+ {
2583
+ subscription,
2584
+ onUpdatePayment: () => setActiveModal("payment-methods"),
2585
+ onAddPayment: () => setActiveModal("payment-methods")
2586
+ },
2587
+ subscription.id
2588
+ )),
2589
+ /* @__PURE__ */ jsxs25("div", { className: "grid gap-4 md:grid-cols-2", children: [
2590
+ /* @__PURE__ */ jsx29(
2591
+ SubscriptionSummaryCard,
2592
+ {
2593
+ subscriptions: data.subscriptions,
2594
+ loading: loading.subscriptions,
2595
+ error: errors.subscriptions || void 0,
2596
+ onManageClick: () => setActiveModal("subscriptions")
2597
+ }
2598
+ ),
2599
+ /* @__PURE__ */ jsx29(
2600
+ PaymentMethodSummaryCard,
2601
+ {
2602
+ paymentMethods: data.paymentMethods,
2603
+ defaultPaymentMethodId: data.customer?.defaultPaymentMethodId,
2604
+ loading: loading.paymentMethods,
2605
+ error: errors.paymentMethods || void 0,
2606
+ onManageClick: () => setActiveModal("payment-methods")
2607
+ }
2608
+ ),
2609
+ /* @__PURE__ */ jsx29(
2610
+ CustomerInfoCard,
2611
+ {
2612
+ customer: data.customer,
2613
+ loading: loading.customer,
2614
+ error: errors.customer || void 0
2615
+ }
2616
+ ),
2617
+ /* @__PURE__ */ jsx29(
2618
+ InvoicesSummaryCard,
2619
+ {
2620
+ invoices: data.invoices,
2621
+ loading: loading.invoices,
2622
+ error: errors.invoices || void 0,
2623
+ onViewAllClick: () => setActiveModal("invoices")
2624
+ }
2625
+ ),
2626
+ hasMeteredSubscriptions() && /* @__PURE__ */ jsx29(
2627
+ BillingUsageSummaryCard,
2628
+ {
2629
+ meters: data.meters,
2630
+ summaries: data.meterSummaries,
2631
+ loading: loading.usage,
2632
+ error: errors.usage || void 0,
2633
+ onViewDetailsClick: () => setActiveModal("usage")
2634
+ }
2635
+ )
2636
+ ] }),
2637
+ /* @__PURE__ */ jsx29(
2638
+ BillingDetailModal,
2639
+ {
2640
+ open: activeModal === "subscriptions",
2641
+ onOpenChange: handleModalClose,
2642
+ title: getModalTitle("subscriptions"),
2643
+ children: /* @__PURE__ */ jsx29(SubscriptionsContainer, {})
2644
+ }
2645
+ ),
2646
+ /* @__PURE__ */ jsx29(
2647
+ BillingDetailModal,
2648
+ {
2649
+ open: activeModal === "payment-methods",
2650
+ onOpenChange: handleModalClose,
2651
+ title: getModalTitle("payment-methods"),
2652
+ children: /* @__PURE__ */ jsx29(PaymentMethodsContainer, {})
2653
+ }
2654
+ ),
2655
+ /* @__PURE__ */ jsx29(
2656
+ BillingDetailModal,
2657
+ {
2658
+ open: activeModal === "invoices",
2659
+ onOpenChange: handleModalClose,
2660
+ title: getModalTitle("invoices"),
2661
+ children: /* @__PURE__ */ jsx29(InvoicesContainer, {})
2662
+ }
2663
+ ),
2664
+ /* @__PURE__ */ jsx29(
2665
+ BillingDetailModal,
2666
+ {
2667
+ open: activeModal === "usage",
2668
+ onOpenChange: handleModalClose,
2669
+ title: getModalTitle("usage"),
2670
+ children: /* @__PURE__ */ jsx29(UsageContainer, {})
2671
+ }
2672
+ )
2673
+ ] })
2674
+ ] });
2675
+ }
2676
+ __name(BillingDashboardContainer, "BillingDashboardContainer");
2677
+
2678
+ // src/features/billing/components/providers/StripeProvider.tsx
2679
+ import { Elements } from "@stripe/react-stripe-js";
2680
+ import { loadStripe } from "@stripe/stripe-js";
2681
+ import { useMemo } from "react";
2682
+ import { Fragment as Fragment7, jsx as jsx30 } from "react/jsx-runtime";
2683
+ var stripePromiseCache = null;
2684
+ function getStripePromise(publishableKey) {
2685
+ if (!publishableKey) {
2686
+ return Promise.resolve(null);
2687
+ }
2688
+ if (stripePromiseCache?.key === publishableKey) {
2689
+ return stripePromiseCache.promise;
2690
+ }
2691
+ const promise = loadStripe(publishableKey);
2692
+ stripePromiseCache = { key: publishableKey, promise };
2693
+ return promise;
2694
+ }
2695
+ __name(getStripePromise, "getStripePromise");
2696
+ function StripeProvider({ children }) {
2697
+ const publishableKey = getStripePublishableKey();
2698
+ const stripePromise = useMemo(() => getStripePromise(publishableKey), [publishableKey]);
2699
+ const options = useMemo(() => ({}), []);
2700
+ if (!publishableKey) {
2701
+ return /* @__PURE__ */ jsx30(Fragment7, { children });
2702
+ }
2703
+ return /* @__PURE__ */ jsx30(Elements, { stripe: stripePromise, options, children });
2704
+ }
2705
+ __name(StripeProvider, "StripeProvider");
2706
+ function isStripeConfigured() {
2707
+ return !!getStripePublishableKey();
2708
+ }
2709
+ __name(isStripeConfigured, "isStripeConfigured");
2710
+
2711
+ // src/features/billing/stripe-price/components/forms/PriceEditor.tsx
2712
+ import { zodResolver as zodResolver2 } from "@hookform/resolvers/zod";
2713
+ import { AlertCircle as AlertCircle2, PlusIcon, XIcon } from "lucide-react";
2714
+ import { useState as useState15 } from "react";
2715
+ import { useForm as useForm2 } from "react-hook-form";
2716
+ import { v4 as v43 } from "uuid";
2717
+ import { z as z2 } from "zod";
2718
+ import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
2719
+ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
2720
+ const [isSubmitting, setIsSubmitting] = useState15(false);
2721
+ const formSchema2 = z2.object({
2722
+ unitAmount: z2.preprocess(
2723
+ (val) => typeof val === "string" ? parseFloat(val) : val,
2724
+ z2.number().min(0, { message: "Amount must be 0 or greater" })
2725
+ ),
2726
+ currency: z2.string().min(1, { message: "Currency is required" }),
2727
+ interval: z2.enum(["one_time", "day", "week", "month", "year"]),
2728
+ intervalCount: z2.preprocess(
2729
+ (val) => val === "" || val === void 0 ? void 0 : typeof val === "string" ? parseInt(val, 10) : val,
2730
+ z2.number().min(1).optional()
2731
+ ),
2732
+ usageType: z2.enum(["licensed", "metered"]).optional(),
2733
+ nickname: z2.string().optional(),
2734
+ active: z2.boolean(),
2735
+ description: z2.string().optional(),
2736
+ features: z2.array(z2.string())
2737
+ });
2738
+ const isEditMode = !!price;
2739
+ const defaultUnitAmount = price?.unitAmount ? price.unitAmount / 100 : 0;
2740
+ const form = useForm2({
2741
+ resolver: zodResolver2(formSchema2),
2742
+ defaultValues: {
2743
+ unitAmount: defaultUnitAmount,
2744
+ currency: price?.currency || "usd",
2745
+ interval: price?.priceType === "one_time" ? "one_time" : price?.recurring?.interval || "month",
2746
+ intervalCount: price?.recurring?.intervalCount || 1,
2747
+ usageType: price?.recurring?.usageType || "licensed",
2748
+ nickname: price?.nickname || "",
2749
+ active: price?.active ?? true,
2750
+ description: price?.description || "",
2751
+ features: price?.features || []
2752
+ }
2753
+ });
2754
+ const watchInterval = form.watch("interval");
2755
+ const isRecurring = watchInterval !== "one_time";
2756
+ const onSubmit = /* @__PURE__ */ __name(async (values) => {
2757
+ setIsSubmitting(true);
2758
+ try {
2759
+ const unitAmountInCents = Math.round(values.unitAmount * 100);
2760
+ if (isEditMode) {
2761
+ await StripePriceService.updatePrice({
2762
+ id: price.id,
2763
+ nickname: values.nickname || void 0,
2764
+ description: values.description || void 0,
2765
+ features: values.features.filter((f) => f.trim()) || void 0
2766
+ });
2767
+ } else {
2768
+ const createInput = {
2769
+ id: v43(),
2770
+ productId,
2771
+ currency: values.currency,
2772
+ unitAmount: unitAmountInCents
2773
+ };
2774
+ if (isRecurring) {
2775
+ createInput.recurring = {
2776
+ interval: values.interval,
2777
+ intervalCount: values.intervalCount || 1,
2778
+ usageType: values.usageType || "licensed"
2779
+ };
2780
+ }
2781
+ if (values.nickname) {
2782
+ createInput.nickname = values.nickname;
2783
+ }
2784
+ if (values.description) {
2785
+ createInput.description = values.description;
2786
+ }
2787
+ const filteredFeatures = values.features.filter((f) => f.trim());
2788
+ if (filteredFeatures.length > 0) {
2789
+ createInput.features = filteredFeatures;
2790
+ }
2791
+ await StripePriceService.createPrice(createInput);
2792
+ }
2793
+ onSuccess();
2794
+ onOpenChange(false);
2795
+ } catch (error) {
2796
+ console.error("[PriceEditor] Failed to save price:", error);
2797
+ } finally {
2798
+ setIsSubmitting(false);
2799
+ }
2800
+ }, "onSubmit");
2801
+ const currencyOptions = [
2802
+ { id: "usd", text: "USD ($)" },
2803
+ { id: "eur", text: "EUR (\u20AC)" },
2804
+ { id: "gbp", text: "GBP (\xA3)" }
2805
+ ];
2806
+ const intervalOptions = [
2807
+ { id: "one_time", text: "One-time" },
2808
+ { id: "day", text: "Daily" },
2809
+ { id: "week", text: "Weekly" },
2810
+ { id: "month", text: "Monthly" },
2811
+ { id: "year", text: "Yearly" }
2812
+ ];
2813
+ const usageTypeOptions = [
2814
+ { id: "licensed", text: "Licensed (per unit)" },
2815
+ { id: "metered", text: "Metered (usage-based)" }
2816
+ ];
2817
+ return /* @__PURE__ */ jsx31(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs26(DialogContent, { className: "max-w-2xl", children: [
2818
+ /* @__PURE__ */ jsxs26(DialogHeader, { children: [
2819
+ /* @__PURE__ */ jsx31(DialogTitle, { children: isEditMode ? "Edit Price" : "Create Price" }),
2820
+ /* @__PURE__ */ jsx31(DialogDescription, { children: isEditMode ? "Update the price details. Note: Only nickname and active status can be changed." : "Create a new price for this product" })
2821
+ ] }),
2822
+ isEditMode && /* @__PURE__ */ jsxs26("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-4 flex gap-x-3", children: [
2823
+ /* @__PURE__ */ jsx31(AlertCircle2, { className: "h-5 w-5 text-blue-600 flex-shrink-0 mt-0.5" }),
2824
+ /* @__PURE__ */ jsxs26("div", { className: "text-sm text-blue-800", children: [
2825
+ /* @__PURE__ */ jsx31("p", { className: "font-semibold mb-1", children: "Stripe Price Immutability" }),
2826
+ /* @__PURE__ */ jsx31("p", { children: "Due to Stripe's architecture, only the nickname and active status can be modified after creation. To change amount, currency, or billing interval, create a new price." })
2827
+ ] })
2828
+ ] }),
2829
+ /* @__PURE__ */ jsx31(Form, { ...form, children: /* @__PURE__ */ jsxs26("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-col gap-y-4", children: [
2830
+ /* @__PURE__ */ jsxs26("div", { className: "grid grid-cols-2 gap-x-4", children: [
2831
+ /* @__PURE__ */ jsx31(
2832
+ FormInput,
2833
+ {
2834
+ form,
2835
+ id: "unitAmount",
2836
+ name: "Amount (in dollars)",
2837
+ placeholder: "9.99",
2838
+ disabled: isEditMode,
2839
+ isRequired: true
2840
+ }
2841
+ ),
2842
+ /* @__PURE__ */ jsx31(FormSelect, { form, id: "currency", name: "Currency", values: currencyOptions, disabled: isEditMode })
2843
+ ] }),
2844
+ /* @__PURE__ */ jsx31(
2845
+ FormSelect,
2846
+ {
2847
+ form,
2848
+ id: "interval",
2849
+ name: "Billing Interval",
2850
+ values: intervalOptions,
2851
+ disabled: isEditMode
2852
+ }
2853
+ ),
2854
+ isRecurring && /* @__PURE__ */ jsxs26("div", { className: "grid grid-cols-2 gap-x-4", children: [
2855
+ /* @__PURE__ */ jsx31(
2856
+ FormInput,
2857
+ {
2858
+ form,
2859
+ id: "intervalCount",
2860
+ name: "Interval Count",
2861
+ placeholder: "1",
2862
+ type: "number",
2863
+ disabled: isEditMode
2864
+ }
2865
+ ),
2866
+ /* @__PURE__ */ jsx31(
2867
+ FormSelect,
2868
+ {
2869
+ form,
2870
+ id: "usageType",
2871
+ name: "Usage Type",
2872
+ values: usageTypeOptions,
2873
+ disabled: isEditMode
2874
+ }
2875
+ )
2876
+ ] }),
2877
+ /* @__PURE__ */ jsx31(
2878
+ FormInput,
2879
+ {
2880
+ form,
2881
+ id: "nickname",
2882
+ name: "Nickname (optional)",
2883
+ placeholder: "e.g., Standard Plan, Pro Tier"
2884
+ }
2885
+ ),
2886
+ /* @__PURE__ */ jsx31(
2887
+ FormTextarea,
2888
+ {
2889
+ form,
2890
+ id: "description",
2891
+ name: "Description (optional)",
2892
+ placeholder: "Describe what this price tier includes...",
2893
+ className: "min-h-24"
2894
+ }
2895
+ ),
2896
+ /* @__PURE__ */ jsxs26(FormItem, { children: [
2897
+ /* @__PURE__ */ jsx31(FormLabel, { children: "Features (optional)" }),
2898
+ /* @__PURE__ */ jsxs26("div", { className: "space-y-2", children: [
2899
+ form.watch("features").map((_, index) => /* @__PURE__ */ jsxs26("div", { className: "flex gap-2", children: [
2900
+ /* @__PURE__ */ jsx31(FormControl, { children: /* @__PURE__ */ jsx31(
2901
+ Input,
2902
+ {
2903
+ ...form.register(`features.${index}`),
2904
+ placeholder: `Feature ${index + 1}`,
2905
+ className: "flex-1"
2906
+ }
2907
+ ) }),
2908
+ /* @__PURE__ */ jsx31(
2909
+ Button,
2910
+ {
2911
+ type: "button",
2912
+ variant: "outline",
2913
+ size: "icon",
2914
+ onClick: () => {
2915
+ const currentFeatures = form.getValues("features");
2916
+ form.setValue(
2917
+ "features",
2918
+ currentFeatures.filter((_2, i) => i !== index)
2919
+ );
2920
+ },
2921
+ children: /* @__PURE__ */ jsx31(XIcon, { className: "h-4 w-4" })
2922
+ }
2923
+ )
2924
+ ] }, index)),
2925
+ /* @__PURE__ */ jsxs26(
2926
+ Button,
2927
+ {
2928
+ type: "button",
2929
+ variant: "outline",
2930
+ size: "sm",
2931
+ onClick: () => {
2932
+ const currentFeatures = form.getValues("features");
2933
+ form.setValue("features", [...currentFeatures, ""]);
2934
+ },
2935
+ className: "mt-2",
2936
+ children: [
2937
+ /* @__PURE__ */ jsx31(PlusIcon, { className: "h-4 w-4 mr-2" }),
2938
+ "Add Feature"
2939
+ ]
2940
+ }
2941
+ )
2942
+ ] })
2943
+ ] }),
2944
+ /* @__PURE__ */ jsx31(FormCheckbox, { form, id: "active", name: "Active" }),
2945
+ /* @__PURE__ */ jsx31(CommonEditorButtons, { isEdit: isEditMode, form, disabled: isSubmitting, setOpen: onOpenChange })
2946
+ ] }) })
2947
+ ] }) });
2948
+ }
2949
+ __name(PriceEditor, "PriceEditor");
2950
+
2951
+ // src/features/billing/stripe-price/components/lists/PricesList.tsx
2952
+ import { Archive, DollarSign, Edit, RotateCcw } from "lucide-react";
2953
+ import { useEffect as useEffect9, useState as useState16 } from "react";
2954
+ import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
2955
+ function PricesList({ productId, onPricesChange }) {
2956
+ const [prices, setPrices] = useState16([]);
2957
+ const [loading, setLoading] = useState16(true);
2958
+ const [showCreatePrice, setShowCreatePrice] = useState16(false);
2959
+ const [editingPrice, setEditingPrice] = useState16(null);
2960
+ const [priceToArchive, setPriceToArchive] = useState16(null);
2961
+ const [priceToReactivate, setPriceToReactivate] = useState16(null);
2962
+ const [archivingPriceId, setArchivingPriceId] = useState16(null);
2963
+ const [reactivatingPriceId, setReactivatingPriceId] = useState16(null);
2964
+ const loadPrices = /* @__PURE__ */ __name(async () => {
2965
+ setLoading(true);
2966
+ try {
2967
+ const fetchedPrices = await StripePriceService.listPrices({ productId });
2968
+ setPrices(fetchedPrices);
2969
+ } catch (error) {
2970
+ console.error("[PricesList] Failed to load prices:", error);
2971
+ } finally {
2972
+ setLoading(false);
2973
+ }
2974
+ }, "loadPrices");
2975
+ useEffect9(() => {
2976
+ loadPrices();
2977
+ }, [productId]);
2978
+ const handleArchive = /* @__PURE__ */ __name(async () => {
2979
+ if (!priceToArchive) {
2980
+ return;
2981
+ }
2982
+ setArchivingPriceId(priceToArchive.id);
2983
+ try {
2984
+ await StripePriceService.archivePrice({ id: priceToArchive.id });
2985
+ setPriceToArchive(null);
2986
+ await loadPrices();
2987
+ onPricesChange();
2988
+ } catch (error) {
2989
+ console.error("[PricesList] Failed to archive price:", error);
2990
+ } finally {
2991
+ setArchivingPriceId(null);
2992
+ }
2993
+ }, "handleArchive");
2994
+ const handleReactivate = /* @__PURE__ */ __name(async () => {
2995
+ if (!priceToReactivate) {
2996
+ return;
2997
+ }
2998
+ setReactivatingPriceId(priceToReactivate.id);
2999
+ try {
3000
+ await StripePriceService.reactivatePrice({ id: priceToReactivate.id });
3001
+ setPriceToReactivate(null);
3002
+ await loadPrices();
3003
+ onPricesChange();
3004
+ } catch (error) {
3005
+ console.error("[PricesList] Failed to reactivate price:", error);
3006
+ } finally {
3007
+ setReactivatingPriceId(null);
3008
+ }
3009
+ }, "handleReactivate");
3010
+ const formatInterval2 = /* @__PURE__ */ __name((price) => {
3011
+ if (price.priceType === "one_time") {
3012
+ return "one-time";
3013
+ }
3014
+ if (price.recurring) {
3015
+ const count = price.recurring.intervalCount;
3016
+ const interval = price.recurring.interval;
3017
+ if (count === 1) {
3018
+ return `/ ${interval}`;
3019
+ } else {
3020
+ return `/ ${count} ${interval}s`;
3021
+ }
3022
+ }
3023
+ return "";
3024
+ }, "formatInterval");
3025
+ if (loading) {
3026
+ return /* @__PURE__ */ jsx32("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx32("p", { className: "text-muted-foreground", children: "Loading prices..." }) });
3027
+ }
3028
+ return /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-y-4", children: [
3029
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center justify-between mb-4", children: [
3030
+ /* @__PURE__ */ jsx32("h4", { className: "text-lg font-semibold", children: "Prices" }),
3031
+ /* @__PURE__ */ jsx32(Button, { size: "sm", onClick: () => setShowCreatePrice(true), children: "Add Price" })
3032
+ ] }),
3033
+ prices.length === 0 && /* @__PURE__ */ jsxs27("div", { className: "bg-background flex flex-col items-center justify-center gap-y-3 rounded-lg border border-dashed p-8", children: [
3034
+ /* @__PURE__ */ jsx32(DollarSign, { className: "text-muted-foreground h-12 w-12" }),
3035
+ /* @__PURE__ */ jsx32("p", { className: "text-muted-foreground text-sm", children: "No prices yet. Add a price to enable subscriptions." }),
3036
+ /* @__PURE__ */ jsx32(Button, { size: "sm", onClick: () => setShowCreatePrice(true), children: "Add Price" })
3037
+ ] }),
3038
+ prices.length > 0 && /* @__PURE__ */ jsx32("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: prices.map((price) => {
3039
+ const isArchiving = archivingPriceId === price.id;
3040
+ const isReactivating = reactivatingPriceId === price.id;
3041
+ return /* @__PURE__ */ jsxs27("div", { className: "border rounded-lg bg-white p-4 hover:shadow-sm transition-shadow", children: [
3042
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-start justify-between mb-3", children: [
3043
+ /* @__PURE__ */ jsx32(DollarSign, { className: "h-5 w-5 text-primary" }),
3044
+ /* @__PURE__ */ jsxs27("div", { className: "flex gap-1", children: [
3045
+ /* @__PURE__ */ jsx32(Button, { variant: "ghost", size: "sm", onClick: () => setEditingPrice(price), className: "h-8 w-8 p-0", children: /* @__PURE__ */ jsx32(Edit, { className: "h-4 w-4" }) }),
3046
+ price.active ? /* @__PURE__ */ jsx32(
3047
+ Button,
3048
+ {
3049
+ variant: "ghost",
3050
+ size: "sm",
3051
+ onClick: () => setPriceToArchive(price),
3052
+ className: "h-8 w-8 p-0",
3053
+ disabled: isArchiving,
3054
+ children: /* @__PURE__ */ jsx32(Archive, { className: "h-4 w-4" })
3055
+ }
3056
+ ) : /* @__PURE__ */ jsx32(
3057
+ Button,
3058
+ {
3059
+ variant: "ghost",
3060
+ size: "sm",
3061
+ onClick: () => setPriceToReactivate(price),
3062
+ className: "h-8 w-8 p-0",
3063
+ disabled: isReactivating,
3064
+ children: /* @__PURE__ */ jsx32(RotateCcw, { className: "h-4 w-4" })
3065
+ }
3066
+ )
3067
+ ] })
3068
+ ] }),
3069
+ /* @__PURE__ */ jsx32("div", { className: "mb-2", children: /* @__PURE__ */ jsxs27("div", { className: "text-2xl font-bold", children: [
3070
+ formatCurrency(price.unitAmount, price.currency),
3071
+ " ",
3072
+ /* @__PURE__ */ jsx32("span", { className: "text-muted-foreground text-sm font-normal", children: formatInterval2(price) })
3073
+ ] }) }),
3074
+ price.metadata?.nickname && /* @__PURE__ */ jsx32("p", { className: "text-sm font-medium mb-2", children: price.metadata.nickname }),
3075
+ /* @__PURE__ */ jsxs27("div", { className: "flex flex-wrap gap-2", children: [
3076
+ price.active ? /* @__PURE__ */ jsx32("span", { className: "bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full font-medium", children: "Active" }) : /* @__PURE__ */ jsx32("span", { className: "bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full font-medium", children: "Inactive" }),
3077
+ price.recurring?.usageType === "metered" && /* @__PURE__ */ jsx32("span", { className: "bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full font-medium", children: "Metered" }),
3078
+ /* @__PURE__ */ jsx32("span", { className: "bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full font-medium uppercase", children: price.currency })
3079
+ ] })
3080
+ ] }, price.id);
3081
+ }) }),
3082
+ showCreatePrice && /* @__PURE__ */ jsx32(
3083
+ PriceEditor,
3084
+ {
3085
+ productId,
3086
+ open: showCreatePrice,
3087
+ onOpenChange: setShowCreatePrice,
3088
+ onSuccess: () => {
3089
+ loadPrices();
3090
+ onPricesChange();
3091
+ }
3092
+ }
3093
+ ),
3094
+ editingPrice && /* @__PURE__ */ jsx32(
3095
+ PriceEditor,
3096
+ {
3097
+ productId,
3098
+ price: editingPrice,
3099
+ open: !!editingPrice,
3100
+ onOpenChange: (open) => !open && setEditingPrice(null),
3101
+ onSuccess: () => {
3102
+ loadPrices();
3103
+ onPricesChange();
3104
+ setEditingPrice(null);
3105
+ }
3106
+ }
3107
+ ),
3108
+ /* @__PURE__ */ jsx32(AlertDialog, { open: !!priceToArchive, onOpenChange: (open) => !open && setPriceToArchive(null), children: /* @__PURE__ */ jsxs27(AlertDialogContent, { children: [
3109
+ /* @__PURE__ */ jsxs27(AlertDialogHeader, { children: [
3110
+ /* @__PURE__ */ jsx32(AlertDialogTitle, { children: "Archive Price" }),
3111
+ /* @__PURE__ */ jsxs27(AlertDialogDescription, { children: [
3112
+ "Are you sure you want to archive the price for",
3113
+ " ",
3114
+ priceToArchive && `${formatCurrency(priceToArchive.unitAmount, priceToArchive.currency)} ${formatInterval2(priceToArchive)}`,
3115
+ "? This will prevent new subscriptions but existing ones will continue."
3116
+ ] })
3117
+ ] }),
3118
+ /* @__PURE__ */ jsxs27(AlertDialogFooter, { children: [
3119
+ /* @__PURE__ */ jsx32(AlertDialogCancel, { disabled: !!archivingPriceId, children: "Cancel" }),
3120
+ /* @__PURE__ */ jsx32(
3121
+ AlertDialogAction,
3122
+ {
3123
+ onClick: handleArchive,
3124
+ disabled: !!archivingPriceId,
3125
+ className: "bg-red-600 hover:bg-red-700",
3126
+ children: archivingPriceId ? "Archiving..." : "Archive"
3127
+ }
3128
+ )
3129
+ ] })
3130
+ ] }) }),
3131
+ /* @__PURE__ */ jsx32(AlertDialog, { open: !!priceToReactivate, onOpenChange: (open) => !open && setPriceToReactivate(null), children: /* @__PURE__ */ jsxs27(AlertDialogContent, { children: [
3132
+ /* @__PURE__ */ jsxs27(AlertDialogHeader, { children: [
3133
+ /* @__PURE__ */ jsx32(AlertDialogTitle, { children: "Reactivate Price" }),
3134
+ /* @__PURE__ */ jsxs27(AlertDialogDescription, { children: [
3135
+ "Are you sure you want to reactivate the price for",
3136
+ " ",
3137
+ priceToReactivate && `${formatCurrency(priceToReactivate.unitAmount, priceToReactivate.currency)} ${formatInterval2(priceToReactivate)}`,
3138
+ "? This will allow new subscriptions again."
3139
+ ] })
3140
+ ] }),
3141
+ /* @__PURE__ */ jsxs27(AlertDialogFooter, { children: [
3142
+ /* @__PURE__ */ jsx32(AlertDialogCancel, { disabled: !!reactivatingPriceId, children: "Cancel" }),
3143
+ /* @__PURE__ */ jsx32(
3144
+ AlertDialogAction,
3145
+ {
3146
+ onClick: handleReactivate,
3147
+ disabled: !!reactivatingPriceId,
3148
+ className: "bg-green-600 hover:bg-green-700",
3149
+ children: reactivatingPriceId ? "Reactivating..." : "Reactivate"
3150
+ }
3151
+ )
3152
+ ] })
3153
+ ] }) })
3154
+ ] });
3155
+ }
3156
+ __name(PricesList, "PricesList");
3157
+
3158
+ // src/features/billing/stripe-product/components/containers/ProductsAdminContainer.tsx
3159
+ import { Package as Package2 } from "lucide-react";
3160
+ import { useEffect as useEffect10, useState as useState19 } from "react";
3161
+
3162
+ // src/features/billing/stripe-product/components/forms/ProductEditor.tsx
3163
+ import { zodResolver as zodResolver3 } from "@hookform/resolvers/zod";
3164
+ import { useState as useState17 } from "react";
3165
+ import { useForm as useForm3 } from "react-hook-form";
3166
+ import { v4 as v44 } from "uuid";
3167
+ import { z as z3 } from "zod";
3168
+ import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
3169
+ function ProductEditor({ product, open, onOpenChange, onSuccess }) {
3170
+ const [isSubmitting, setIsSubmitting] = useState17(false);
3171
+ const formSchema2 = z3.object({
3172
+ name: z3.string().min(1, { message: "Product name is required" }),
3173
+ description: z3.string().optional(),
3174
+ active: z3.boolean()
3175
+ });
3176
+ const form = useForm3({
3177
+ resolver: zodResolver3(formSchema2),
3178
+ defaultValues: {
3179
+ name: product?.name || "",
3180
+ description: product?.description || "",
3181
+ active: product?.active ?? true
3182
+ }
3183
+ });
3184
+ const onSubmit = /* @__PURE__ */ __name(async (values) => {
3185
+ setIsSubmitting(true);
3186
+ try {
3187
+ if (product) {
3188
+ await StripeProductService.updateProduct({
3189
+ id: product.id,
3190
+ name: values.name,
3191
+ description: values.description,
3192
+ active: values.active
3193
+ });
3194
+ } else {
3195
+ await StripeProductService.createProduct({
3196
+ id: v44(),
3197
+ name: values.name,
3198
+ description: values.description,
3199
+ active: values.active
3200
+ });
3201
+ }
3202
+ onSuccess();
3203
+ onOpenChange(false);
3204
+ } catch (error) {
3205
+ console.error("[ProductEditor] Failed to save product:", error);
3206
+ } finally {
3207
+ setIsSubmitting(false);
3208
+ }
3209
+ }, "onSubmit");
3210
+ return /* @__PURE__ */ jsx33(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs28(DialogContent, { className: "max-w-2xl", children: [
3211
+ /* @__PURE__ */ jsxs28(DialogHeader, { children: [
3212
+ /* @__PURE__ */ jsx33(DialogTitle, { children: product ? "Edit Product" : "Create Product" }),
3213
+ /* @__PURE__ */ jsx33(DialogDescription, { children: product ? `Update the details for ${product.name}` : "Create a new product to offer to your customers" })
3214
+ ] }),
3215
+ /* @__PURE__ */ jsx33(Form, { ...form, children: /* @__PURE__ */ jsxs28("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-col gap-y-4", children: [
3216
+ /* @__PURE__ */ jsx33(FormInput, { form, id: "name", name: "Product Name", placeholder: "Enter product name", isRequired: true }),
3217
+ /* @__PURE__ */ jsx33(
3218
+ FormTextarea,
3219
+ {
3220
+ form,
3221
+ id: "description",
3222
+ name: "Description",
3223
+ placeholder: "Enter product description (optional)",
3224
+ className: "min-h-32"
3225
+ }
3226
+ ),
3227
+ /* @__PURE__ */ jsx33(FormCheckbox, { form, id: "active", name: "Active" }),
3228
+ /* @__PURE__ */ jsx33(CommonEditorButtons, { isEdit: !!product, form, disabled: isSubmitting, setOpen: onOpenChange })
3229
+ ] }) })
3230
+ ] }) });
3231
+ }
3232
+ __name(ProductEditor, "ProductEditor");
3233
+
3234
+ // src/features/billing/stripe-product/components/lists/ProductsList.tsx
3235
+ import { Archive as Archive2, ChevronDown, ChevronUp, Edit as Edit2, Package, RefreshCw as RefreshCw2 } from "lucide-react";
3236
+ import { useState as useState18 } from "react";
3237
+ import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
3238
+ function ProductsList({ products, onProductsChange }) {
3239
+ const [expandedProductId, setExpandedProductId] = useState18(null);
3240
+ const [editingProduct, setEditingProduct] = useState18(null);
3241
+ const [archivingProductId, setArchivingProductId] = useState18(null);
3242
+ const [reactivatingProductId, setReactivatingProductId] = useState18(null);
3243
+ const [productToArchive, setProductToArchive] = useState18(null);
3244
+ const [productToReactivate, setProductToReactivate] = useState18(null);
3245
+ const handleArchive = /* @__PURE__ */ __name(async () => {
3246
+ if (!productToArchive) {
3247
+ return;
3248
+ }
3249
+ setArchivingProductId(productToArchive.id);
3250
+ try {
3251
+ const archivedProduct = await StripeProductService.archiveProduct({ id: productToArchive.id });
3252
+ setProductToArchive(null);
3253
+ onProductsChange();
3254
+ } catch (error) {
3255
+ console.error("[ProductsList] Failed to archive product:", error);
3256
+ } finally {
3257
+ setArchivingProductId(null);
3258
+ }
3259
+ }, "handleArchive");
3260
+ const handleReactivate = /* @__PURE__ */ __name(async () => {
3261
+ if (!productToReactivate) {
3262
+ return;
3263
+ }
3264
+ setReactivatingProductId(productToReactivate.id);
3265
+ try {
3266
+ const reactivatedProduct = await StripeProductService.reactivateProduct({ id: productToReactivate.id });
3267
+ setProductToReactivate(null);
3268
+ onProductsChange();
3269
+ } catch (error) {
3270
+ } finally {
3271
+ setReactivatingProductId(null);
3272
+ }
3273
+ }, "handleReactivate");
3274
+ const toggleExpand = /* @__PURE__ */ __name((productId) => {
3275
+ setExpandedProductId(expandedProductId === productId ? null : productId);
3276
+ }, "toggleExpand");
3277
+ return /* @__PURE__ */ jsxs29("div", { className: "flex flex-col gap-y-4", children: [
3278
+ products.map((product) => {
3279
+ const isExpanded = expandedProductId === product.id;
3280
+ const isArchiving = archivingProductId === product.id;
3281
+ const isReactivating = reactivatingProductId === product.id;
3282
+ return /* @__PURE__ */ jsxs29("div", { className: "border rounded-lg bg-white shadow-sm hover:shadow-md transition-shadow", children: [
3283
+ /* @__PURE__ */ jsxs29("div", { className: "flex items-center justify-between p-6", children: [
3284
+ /* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-x-4 flex-1", children: [
3285
+ /* @__PURE__ */ jsx34(Package, { className: "h-6 w-6 text-primary" }),
3286
+ /* @__PURE__ */ jsxs29("div", { className: "flex-1", children: [
3287
+ /* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-x-3", children: [
3288
+ /* @__PURE__ */ jsx34("h3", { className: "text-lg font-semibold", children: product.name }),
3289
+ product.active ? /* @__PURE__ */ jsx34("span", { className: "bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full font-medium", children: "Active" }) : /* @__PURE__ */ jsx34("span", { className: "bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full font-medium", children: "Inactive" })
3290
+ ] }),
3291
+ product.description && /* @__PURE__ */ jsx34("p", { className: "text-muted-foreground text-sm mt-1", children: product.description })
3292
+ ] })
3293
+ ] }),
3294
+ /* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-x-2", children: [
3295
+ /* @__PURE__ */ jsxs29(Button, { variant: "outline", size: "sm", onClick: () => setEditingProduct(product), children: [
3296
+ /* @__PURE__ */ jsx34(Edit2, { className: "h-4 w-4 mr-1" }),
3297
+ "Edit"
3298
+ ] }),
3299
+ product.active ? /* @__PURE__ */ jsxs29(
3300
+ Button,
3301
+ {
3302
+ variant: "outline",
3303
+ size: "sm",
3304
+ onClick: () => setProductToArchive(product),
3305
+ disabled: isArchiving,
3306
+ children: [
3307
+ /* @__PURE__ */ jsx34(Archive2, { className: "h-4 w-4 mr-1" }),
3308
+ isArchiving ? "Archiving..." : "Archive"
3309
+ ]
3310
+ }
3311
+ ) : /* @__PURE__ */ jsxs29(
3312
+ Button,
3313
+ {
3314
+ variant: "outline",
3315
+ size: "sm",
3316
+ onClick: () => setProductToReactivate(product),
3317
+ disabled: isReactivating,
3318
+ children: [
3319
+ /* @__PURE__ */ jsx34(RefreshCw2, { className: "h-4 w-4 mr-1" }),
3320
+ isReactivating ? "Reactivating..." : "Reactivate"
3321
+ ]
3322
+ }
3323
+ ),
3324
+ /* @__PURE__ */ jsx34(Button, { variant: "ghost", size: "sm", onClick: () => toggleExpand(product.id), children: isExpanded ? /* @__PURE__ */ jsx34(ChevronUp, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx34(ChevronDown, { className: "h-5 w-5" }) })
3325
+ ] })
3326
+ ] }),
3327
+ isExpanded && /* @__PURE__ */ jsx34("div", { className: "border-t bg-muted/30 p-6", children: /* @__PURE__ */ jsx34(PricesList, { productId: product.id, onPricesChange: onProductsChange }) })
3328
+ ] }, product.id);
3329
+ }),
3330
+ editingProduct && /* @__PURE__ */ jsx34(
3331
+ ProductEditor,
3332
+ {
3333
+ product: editingProduct,
3334
+ open: !!editingProduct,
3335
+ onOpenChange: (open) => !open && setEditingProduct(null),
3336
+ onSuccess: () => {
3337
+ onProductsChange();
3338
+ setEditingProduct(null);
3339
+ }
3340
+ }
3341
+ ),
3342
+ /* @__PURE__ */ jsx34(AlertDialog, { open: !!productToArchive, onOpenChange: (open) => !open && setProductToArchive(null), children: /* @__PURE__ */ jsxs29(AlertDialogContent, { children: [
3343
+ /* @__PURE__ */ jsxs29(AlertDialogHeader, { children: [
3344
+ /* @__PURE__ */ jsx34(AlertDialogTitle, { children: "Archive Product" }),
3345
+ /* @__PURE__ */ jsxs29(AlertDialogDescription, { children: [
3346
+ 'Are you sure you want to archive "',
3347
+ productToArchive?.name,
3348
+ '"? This will deactivate it and it will no longer be available for new subscriptions.'
3349
+ ] })
3350
+ ] }),
3351
+ /* @__PURE__ */ jsxs29(AlertDialogFooter, { children: [
3352
+ /* @__PURE__ */ jsx34(AlertDialogCancel, { disabled: !!archivingProductId, children: "Cancel" }),
3353
+ /* @__PURE__ */ jsx34(
3354
+ AlertDialogAction,
3355
+ {
3356
+ onClick: handleArchive,
3357
+ disabled: !!archivingProductId,
3358
+ className: "bg-red-600 hover:bg-red-700",
3359
+ children: archivingProductId ? "Archiving..." : "Archive"
3360
+ }
3361
+ )
3362
+ ] })
3363
+ ] }) }),
3364
+ /* @__PURE__ */ jsx34(AlertDialog, { open: !!productToReactivate, onOpenChange: (open) => !open && setProductToReactivate(null), children: /* @__PURE__ */ jsxs29(AlertDialogContent, { children: [
3365
+ /* @__PURE__ */ jsxs29(AlertDialogHeader, { children: [
3366
+ /* @__PURE__ */ jsx34(AlertDialogTitle, { children: "Reactivate Product" }),
3367
+ /* @__PURE__ */ jsxs29(AlertDialogDescription, { children: [
3368
+ 'Are you sure you want to reactivate "',
3369
+ productToReactivate?.name,
3370
+ '"? This will make it available for new subscriptions again.'
3371
+ ] })
3372
+ ] }),
3373
+ /* @__PURE__ */ jsxs29(AlertDialogFooter, { children: [
3374
+ /* @__PURE__ */ jsx34(AlertDialogCancel, { disabled: !!reactivatingProductId, children: "Cancel" }),
3375
+ /* @__PURE__ */ jsx34(
3376
+ AlertDialogAction,
3377
+ {
3378
+ onClick: handleReactivate,
3379
+ disabled: !!reactivatingProductId,
3380
+ className: "bg-green-600 hover:bg-green-700",
3381
+ children: reactivatingProductId ? "Reactivating..." : "Reactivate"
3382
+ }
3383
+ )
3384
+ ] })
3385
+ ] }) })
3386
+ ] });
3387
+ }
3388
+ __name(ProductsList, "ProductsList");
3389
+
3390
+ // src/features/billing/stripe-product/components/containers/ProductsAdminContainer.tsx
3391
+ import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
3392
+ function ProductsAdminContainer() {
3393
+ const { hasRole } = useCurrentUserContext();
3394
+ const [products, setProducts] = useState19([]);
3395
+ const [loading, setLoading] = useState19(true);
3396
+ const [showCreateProduct, setShowCreateProduct] = useState19(false);
3397
+ if (!hasRole(getRoleId().Administrator)) {
3398
+ return /* @__PURE__ */ jsx35("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx35("p", { className: "text-red-600 font-semibold", children: "Permission denied. Administrator access required." }) });
3399
+ }
3400
+ const loadProducts = /* @__PURE__ */ __name(async () => {
3401
+ setLoading(true);
3402
+ try {
3403
+ const fetchedProducts = await StripeProductService.listProducts();
3404
+ setProducts(fetchedProducts);
3405
+ } catch (error) {
3406
+ console.error("[ProductsAdminContainer] Failed to load products:", error);
3407
+ } finally {
3408
+ setLoading(false);
3409
+ }
3410
+ }, "loadProducts");
3411
+ useEffect10(() => {
3412
+ loadProducts();
3413
+ }, []);
3414
+ if (loading) {
3415
+ return /* @__PURE__ */ jsx35("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx35("p", { className: "text-muted-foreground", children: "Loading products..." }) });
3416
+ }
3417
+ return /* @__PURE__ */ jsxs30("div", { className: "flex w-full flex-col gap-y-6", children: [
3418
+ /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between", children: [
3419
+ /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-x-3", children: [
3420
+ /* @__PURE__ */ jsx35(Package2, { className: "h-8 w-8" }),
3421
+ /* @__PURE__ */ jsx35("h1", { className: "text-3xl font-bold", children: "Product & Price Management" })
3422
+ ] }),
3423
+ /* @__PURE__ */ jsx35(Button, { onClick: () => setShowCreateProduct(true), children: "Create Product" })
3424
+ ] }),
3425
+ products.length === 0 && /* @__PURE__ */ jsxs30("div", { className: "bg-muted/50 flex flex-col items-center justify-center gap-y-4 rounded-lg border-2 border-dashed p-12", children: [
3426
+ /* @__PURE__ */ jsx35(Package2, { className: "text-muted-foreground h-16 w-16" }),
3427
+ /* @__PURE__ */ jsxs30("div", { className: "text-center", children: [
3428
+ /* @__PURE__ */ jsx35("h3", { className: "mb-2 text-xl font-semibold", children: "No products yet" }),
3429
+ /* @__PURE__ */ jsx35("p", { className: "text-muted-foreground mb-4", children: "Create your first product to start offering subscriptions to your customers." }),
3430
+ /* @__PURE__ */ jsx35(Button, { onClick: () => setShowCreateProduct(true), children: "Create Your First Product" })
3431
+ ] })
3432
+ ] }),
3433
+ products.length > 0 && /* @__PURE__ */ jsx35(ProductsList, { products, onProductsChange: loadProducts }),
3434
+ showCreateProduct && /* @__PURE__ */ jsx35(ProductEditor, { open: showCreateProduct, onOpenChange: setShowCreateProduct, onSuccess: loadProducts })
3435
+ ] });
3436
+ }
3437
+ __name(ProductsAdminContainer, "ProductsAdminContainer");
3438
+ export {
3439
+ BillingAlertBanner,
3440
+ BillingDashboardContainer,
3441
+ BillingDetailModal,
3442
+ BillingUsageSummaryCard,
3443
+ CancelSubscriptionDialog,
3444
+ CustomerInfoCard,
3445
+ InvoiceDetails,
3446
+ InvoiceStatusBadge,
3447
+ InvoicesContainer,
3448
+ InvoicesList,
3449
+ InvoicesSummaryCard,
3450
+ PaymentMethodCard,
3451
+ PaymentMethodEditor,
3452
+ PaymentMethodSummaryCard,
3453
+ PaymentMethodsContainer,
3454
+ PaymentMethodsList,
3455
+ PriceEditor,
3456
+ PricesList,
3457
+ PricingCard,
3458
+ PricingCardsGrid,
3459
+ ProductEditor,
3460
+ ProductsAdminContainer,
3461
+ ProductsList,
3462
+ ProrationPreview,
3463
+ StripeProvider,
3464
+ SubscriptionDetails,
3465
+ SubscriptionEditor,
3466
+ SubscriptionStatusBadge,
3467
+ SubscriptionSummaryCard,
3468
+ SubscriptionsContainer,
3469
+ SubscriptionsList,
3470
+ UsageContainer,
3471
+ UsageHistoryTable,
3472
+ UsageSummaryCard,
3473
+ UsageSummaryCards,
3474
+ formatCurrency,
3475
+ formatDate3 as formatDate,
3476
+ formatInterval,
3477
+ isStripeConfigured
3478
+ };
3479
+ //# sourceMappingURL=index.mjs.map