@carlonicora/nextjs-jsonapi 5.5.0 → 5.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BlockNoteEditor-GVQCKRMU.mjs → BlockNoteEditor-R3WXUFHX.mjs} +4 -4
- package/dist/{BlockNoteEditor-THBJH2UR.js → BlockNoteEditor-ZHXXFWUJ.js} +19 -19
- package/dist/{BlockNoteEditor-THBJH2UR.js.map → BlockNoteEditor-ZHXXFWUJ.js.map} +1 -1
- package/dist/{BlockNoteViewer-U6E3DS4F.mjs → BlockNoteViewer-BVVN6D3T.mjs} +2 -2
- package/dist/{BlockNoteViewer-VZOVKTFO.js → BlockNoteViewer-SD3ZFIXD.js} +3 -3
- package/dist/{BlockNoteViewer-VZOVKTFO.js.map → BlockNoteViewer-SD3ZFIXD.js.map} +1 -1
- package/dist/billing/index.d.mts +197 -46
- package/dist/billing/index.d.ts +197 -46
- package/dist/billing/index.js +1166 -589
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +803 -226
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-WPYT4UKJ.js → chunk-2TXE57LE.js} +3 -3
- package/dist/{chunk-WPYT4UKJ.js.map → chunk-2TXE57LE.js.map} +1 -1
- package/dist/{chunk-G6UE6MEP.js → chunk-5TH2DSAH.js} +901 -204
- package/dist/chunk-5TH2DSAH.js.map +1 -0
- package/dist/{chunk-X5WIUUNG.mjs → chunk-CFB6FHIY.mjs} +3342 -2377
- package/dist/chunk-CFB6FHIY.mjs.map +1 -0
- package/dist/{chunk-VC5A3AUZ.mjs → chunk-FK26NX3S.mjs} +701 -4
- package/dist/chunk-FK26NX3S.mjs.map +1 -0
- package/dist/{chunk-YDYVJM4I.js → chunk-IJME5SAM.js} +4 -4
- package/dist/{chunk-YDYVJM4I.js.map → chunk-IJME5SAM.js.map} +1 -1
- package/dist/{chunk-W7F5BDSV.mjs → chunk-OKCV4GPJ.mjs} +2 -2
- package/dist/{chunk-3CGROPF3.mjs → chunk-RGQYFSJF.mjs} +2 -2
- package/dist/{chunk-6SQYXUVP.mjs → chunk-WCHLCOZG.mjs} +2 -2
- package/dist/{chunk-S4YJ5WCW.js → chunk-XOSC3FFP.js} +8 -8
- package/dist/{chunk-S4YJ5WCW.js.map → chunk-XOSC3FFP.js.map} +1 -1
- package/dist/{chunk-TY5FYP3G.js → chunk-ZCOPOO7T.js} +2024 -1059
- package/dist/chunk-ZCOPOO7T.js.map +1 -0
- package/dist/client/index.js +4 -4
- package/dist/client/index.mjs +3 -3
- package/dist/components/index.js +4 -4
- package/dist/components/index.mjs +3 -3
- package/dist/contexts/index.js +4 -4
- package/dist/contexts/index.mjs +3 -3
- package/dist/core/index.d.mts +248 -3
- package/dist/core/index.d.ts +248 -3
- package/dist/core/index.js +42 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +41 -1
- package/dist/{stripe-promotion-code.interface-ClZ7DxS9.d.mts → currency-DT_dveBx.d.mts} +137 -1
- package/dist/{stripe-promotion-code.interface-C3qqh3mi.d.ts → currency-pK9eRYRC.d.ts} +137 -1
- package/dist/email-automations/index.js +92 -92
- package/dist/email-automations/index.mjs +3 -3
- package/dist/features/analytics/index.js +121 -121
- package/dist/features/analytics/index.mjs +4 -4
- package/dist/features/analytics/server.js +4 -4
- package/dist/features/analytics/server.mjs +2 -2
- package/dist/features/blog/index.js +150 -150
- package/dist/features/blog/index.mjs +3 -3
- package/dist/features/blog/server.js +16 -16
- package/dist/features/blog/server.mjs +1 -1
- package/dist/features/help/index.js +37 -37
- package/dist/features/help/index.mjs +3 -3
- package/dist/features/tokenusage/index.js +124 -124
- package/dist/features/tokenusage/index.mjs +4 -4
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +43 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +42 -2
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +2 -2
- package/messages/en.json +125 -3
- package/messages/it.json +125 -3
- package/package.json +1 -1
- package/scripts/validate-messages.mjs +1 -0
- package/src/billing/index.ts +1 -0
- package/src/client/index.ts +4 -0
- package/src/core/index.ts +4 -0
- package/src/core/registry/ModuleRegistry.ts +3 -0
- package/src/features/billing/components/cards/InvoicesSummaryCard.tsx +3 -2
- package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +4 -3
- package/src/features/billing/components/containers/BillingDashboardContainer.tsx +22 -5
- package/src/features/billing/components/utils/__tests__/discount.spec.ts +59 -0
- package/src/features/billing/components/utils/__tests__/url.spec.ts +32 -0
- package/src/features/billing/components/utils/discount.ts +38 -0
- package/src/features/billing/components/utils/index.ts +2 -0
- package/src/features/billing/components/utils/url.ts +12 -0
- package/src/features/billing/contexts/CouponContext.tsx +209 -0
- package/src/features/billing/contexts/index.ts +1 -0
- package/src/features/billing/i18n-keys.ts +79 -0
- package/src/features/billing/stripe-coupon/components/containers/CouponContainer.tsx +38 -0
- package/src/features/billing/stripe-coupon/components/containers/index.ts +1 -0
- package/src/features/billing/stripe-coupon/components/details/CouponDetails.tsx +72 -0
- package/src/features/billing/stripe-coupon/components/details/index.ts +1 -0
- package/src/features/billing/stripe-coupon/components/forms/CouponCodeEditor.tsx +80 -0
- package/src/features/billing/stripe-coupon/components/forms/CouponCodeFields.tsx +214 -0
- package/src/features/billing/stripe-coupon/components/forms/CouponEditor.tsx +425 -0
- package/src/features/billing/stripe-coupon/components/forms/CouponRefresher.tsx +45 -0
- package/src/features/billing/stripe-coupon/components/forms/index.ts +6 -0
- package/src/features/billing/stripe-coupon/components/index.ts +5 -0
- package/src/features/billing/stripe-coupon/components/lists/CouponCodesList.tsx +61 -0
- package/src/features/billing/stripe-coupon/components/lists/CouponsList.tsx +67 -0
- package/src/features/billing/stripe-coupon/components/lists/index.ts +2 -0
- package/src/features/billing/stripe-coupon/components/widgets/CouponCodeActions.tsx +81 -0
- package/src/features/billing/stripe-coupon/components/widgets/index.ts +1 -0
- package/src/features/billing/stripe-coupon/data/__tests__/stripe-coupon-models.spec.ts +114 -0
- package/src/features/billing/stripe-coupon/data/index.ts +10 -0
- package/src/features/billing/stripe-coupon/data/stripe-coupon-code.fields.ts +8 -0
- package/src/features/billing/stripe-coupon/data/stripe-coupon-code.interface.ts +44 -0
- package/src/features/billing/stripe-coupon/data/stripe-coupon-code.service.ts +92 -0
- package/src/features/billing/stripe-coupon/data/stripe-coupon-code.ts +132 -0
- package/src/features/billing/stripe-coupon/data/stripe-coupon-offer.interface.ts +17 -0
- package/src/features/billing/stripe-coupon/data/stripe-coupon-offer.ts +70 -0
- package/src/features/billing/stripe-coupon/data/stripe-coupon.fields.ts +7 -0
- package/src/features/billing/stripe-coupon/data/stripe-coupon.interface.ts +55 -0
- package/src/features/billing/stripe-coupon/data/stripe-coupon.service.ts +93 -0
- package/src/features/billing/stripe-coupon/data/stripe-coupon.ts +167 -0
- package/src/features/billing/stripe-coupon/hooks/useStripeCouponCodeTableStructure.tsx +161 -0
- package/src/features/billing/stripe-coupon/hooks/useStripeCouponTableStructure.tsx +113 -0
- package/src/features/billing/stripe-coupon/index.ts +5 -0
- package/src/features/billing/stripe-coupon/stripe-coupon-code.module.ts +15 -0
- package/src/features/billing/stripe-coupon/stripe-coupon-offer.module.ts +10 -0
- package/src/features/billing/stripe-coupon/stripe-coupon.module.ts +25 -0
- package/src/features/billing/stripe-coupon/utils/__tests__/coupon-places.spec.ts +19 -0
- package/src/features/billing/stripe-coupon/utils/__tests__/to-promotion-code-result.spec.ts +41 -0
- package/src/features/billing/stripe-coupon/utils/coupon-places.ts +23 -0
- package/src/features/billing/stripe-coupon/utils/format-coupon-discount.ts +36 -0
- package/src/features/billing/stripe-coupon/utils/index.ts +3 -0
- package/src/features/billing/stripe-coupon/utils/to-promotion-code-result.ts +23 -0
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodForm.tsx +24 -8
- package/src/features/billing/stripe-product/components/containers/ProductsListContainer.tsx +21 -6
- package/src/features/billing/stripe-promotion-code/components/PromoCodeInput.tsx +60 -19
- package/src/features/billing/stripe-promotion-code/data/stripe-promotion-code.interface.ts +2 -0
- package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +24 -5
- package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +21 -2
- package/src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx +1 -1
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx +1 -1
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +60 -49
- package/src/features/billing/stripe-subscription/components/wizards/__tests__/review-card.spec.ts +34 -0
- package/src/features/billing/stripe-subscription/components/wizards/review-card.ts +24 -0
- package/src/features/billing/stripe-subscription/data/__tests__/stripe-subscription.discount.spec.ts +67 -0
- package/src/features/billing/stripe-subscription/data/stripe-subscription.interface.ts +14 -0
- package/src/features/billing/stripe-subscription/data/stripe-subscription.service.ts +39 -0
- package/src/features/billing/stripe-subscription/data/stripe-subscription.ts +21 -0
- package/src/features/billing/stripe-subscription/hooks/__tests__/wizard-payment.spec.ts +48 -0
- package/src/features/billing/stripe-subscription/hooks/__tests__/wizard-promotion.spec.ts +132 -0
- package/src/features/billing/stripe-subscription/hooks/useSubscriptionWizard.ts +138 -12
- package/src/features/billing/stripe-subscription/hooks/wizard-payment.ts +30 -0
- package/src/features/billing/stripe-subscription/hooks/wizard-promotion.ts +61 -0
- package/src/features/user/contexts/CurrentUserContext.tsx +25 -0
- package/src/features/user/contexts/__tests__/CurrentUserContext.spec.tsx +121 -2
- package/dist/chunk-G6UE6MEP.js.map +0 -1
- package/dist/chunk-TY5FYP3G.js.map +0 -1
- package/dist/chunk-VC5A3AUZ.mjs.map +0 -1
- package/dist/chunk-X5WIUUNG.mjs.map +0 -1
- /package/dist/{BlockNoteEditor-GVQCKRMU.mjs.map → BlockNoteEditor-R3WXUFHX.mjs.map} +0 -0
- /package/dist/{BlockNoteViewer-U6E3DS4F.mjs.map → BlockNoteViewer-BVVN6D3T.mjs.map} +0 -0
- /package/dist/{chunk-W7F5BDSV.mjs.map → chunk-OKCV4GPJ.mjs.map} +0 -0
- /package/dist/{chunk-3CGROPF3.mjs.map → chunk-RGQYFSJF.mjs.map} +0 -0
- /package/dist/{chunk-6SQYXUVP.mjs.map → chunk-WCHLCOZG.mjs.map} +0 -0
package/dist/billing/index.mjs
CHANGED
|
@@ -22,6 +22,11 @@ import {
|
|
|
22
22
|
Checkbox,
|
|
23
23
|
ConfirmDialog,
|
|
24
24
|
ContentListTable,
|
|
25
|
+
CouponCodeActions,
|
|
26
|
+
CouponCodeFields,
|
|
27
|
+
CouponEditor,
|
|
28
|
+
CouponProvider,
|
|
29
|
+
CouponRefresher,
|
|
25
30
|
DetailField,
|
|
26
31
|
Dialog,
|
|
27
32
|
DialogContent,
|
|
@@ -56,19 +61,29 @@ import {
|
|
|
56
61
|
Tabs,
|
|
57
62
|
TabsList,
|
|
58
63
|
TabsTrigger,
|
|
64
|
+
applyDiscount,
|
|
65
|
+
couponCodeDefaults,
|
|
66
|
+
couponCodeSchema,
|
|
67
|
+
couponCurrencyOptions,
|
|
59
68
|
errorToast,
|
|
60
|
-
formatCurrency,
|
|
61
69
|
formatDate,
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
optionalDate,
|
|
71
|
+
optionalDecimal,
|
|
72
|
+
optionalPositiveInt,
|
|
73
|
+
parseOptionalDecimal,
|
|
74
|
+
parseOptionalPositiveInt,
|
|
75
|
+
promotionCodeDiscount,
|
|
76
|
+
subscriptionDiscount,
|
|
77
|
+
toCouponCodeInput,
|
|
64
78
|
useBillingFormatOptions,
|
|
79
|
+
useCouponContext,
|
|
65
80
|
useCurrentUserContext,
|
|
66
81
|
useDataListRetriever,
|
|
67
82
|
usePageUrlGenerator
|
|
68
|
-
} from "../chunk-
|
|
83
|
+
} from "../chunk-CFB6FHIY.mjs";
|
|
69
84
|
import {
|
|
70
85
|
getStripePublishableKey
|
|
71
|
-
} from "../chunk-
|
|
86
|
+
} from "../chunk-WCHLCOZG.mjs";
|
|
72
87
|
import {
|
|
73
88
|
getRoleId
|
|
74
89
|
} from "../chunk-PW42LRSL.mjs";
|
|
@@ -76,6 +91,8 @@ import {
|
|
|
76
91
|
FeatureService,
|
|
77
92
|
InvoiceStatus,
|
|
78
93
|
Modules,
|
|
94
|
+
StripeCouponCodeService,
|
|
95
|
+
StripeCouponService,
|
|
79
96
|
StripeCustomerService,
|
|
80
97
|
StripeInvoiceService,
|
|
81
98
|
StripePriceService,
|
|
@@ -85,11 +102,16 @@ import {
|
|
|
85
102
|
StripeUsageService,
|
|
86
103
|
SubscriptionStatus,
|
|
87
104
|
cn,
|
|
105
|
+
formatCouponDiscount,
|
|
106
|
+
formatCurrency,
|
|
107
|
+
formatFixed,
|
|
108
|
+
formatInterval,
|
|
88
109
|
rehydrate,
|
|
89
110
|
showToast,
|
|
111
|
+
toPromotionCodeResult,
|
|
90
112
|
useI18nLocale,
|
|
91
113
|
useI18nRouter
|
|
92
|
-
} from "../chunk-
|
|
114
|
+
} from "../chunk-FK26NX3S.mjs";
|
|
93
115
|
import "../chunk-V5MI7RNB.mjs";
|
|
94
116
|
import "../chunk-AUXK7QSA.mjs";
|
|
95
117
|
import "../chunk-PI56A4DG.mjs";
|
|
@@ -166,7 +188,7 @@ function SubscriptionSummaryCard({
|
|
|
166
188
|
/* @__PURE__ */ jsx(CardTitle, { children: t("billing.ui.subscriptions.title") }),
|
|
167
189
|
/* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-muted-foreground" })
|
|
168
190
|
] }),
|
|
169
|
-
/* @__PURE__ */ jsx(CardContent, { children:
|
|
191
|
+
/* @__PURE__ */ jsx(CardContent, { children: !primarySubscription ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
170
192
|
/* @__PURE__ */ jsx("p", { className: "text-xl font-bold text-muted-foreground", children: t("billing.ui.cards.subscriptions.empty_title") }),
|
|
171
193
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("billing.ui.cards.subscriptions.empty_description") }),
|
|
172
194
|
/* @__PURE__ */ jsxs(
|
|
@@ -185,7 +207,7 @@ function SubscriptionSummaryCard({
|
|
|
185
207
|
]
|
|
186
208
|
}
|
|
187
209
|
)
|
|
188
|
-
] }) :
|
|
210
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
189
211
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
190
212
|
/* @__PURE__ */ jsx("p", { className: "text-xl font-bold", children: formatPlanName(primarySubscription, t("billing.ui.cards.subscriptions.fallback_name")) }),
|
|
191
213
|
/* @__PURE__ */ jsx(
|
|
@@ -204,7 +226,7 @@ function SubscriptionSummaryCard({
|
|
|
204
226
|
date: formatDate(primarySubscription.currentPeriodEnd, fmt)
|
|
205
227
|
}) }),
|
|
206
228
|
activeSubscriptions.length > 1 && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("billing.ui.cards.subscriptions.more", { count: activeSubscriptions.length - 1 }) })
|
|
207
|
-
] })
|
|
229
|
+
] }) })
|
|
208
230
|
] });
|
|
209
231
|
}
|
|
210
232
|
__name(SubscriptionSummaryCard, "SubscriptionSummaryCard");
|
|
@@ -438,7 +460,7 @@ function InvoicesSummaryCard({ invoices, loading, error, onViewAllClick }) {
|
|
|
438
460
|
/* @__PURE__ */ jsx4(CardContent, { children: /* @__PURE__ */ jsx4("p", { className: "text-destructive text-xs/relaxed", children: error }) })
|
|
439
461
|
] });
|
|
440
462
|
}
|
|
441
|
-
const latestInvoice = invoices[0];
|
|
463
|
+
const latestInvoice = invoices.find((inv) => inv.status !== "void" /* VOID */) ?? invoices[0];
|
|
442
464
|
const paidInvoices = invoices.filter((inv) => inv.status === "paid" /* PAID */);
|
|
443
465
|
const openInvoices = invoices.filter((inv) => inv.status === "open" /* OPEN */);
|
|
444
466
|
return /* @__PURE__ */ jsxs4(Card, { className: "cursor-pointer hover:bg-accent/50 transition-colors", onClick: onViewAllClick, children: [
|
|
@@ -540,10 +562,10 @@ function BillingUsageSummaryCard({
|
|
|
540
562
|
__name(BillingUsageSummaryCard, "BillingUsageSummaryCard");
|
|
541
563
|
|
|
542
564
|
// src/features/billing/components/containers/BillingDashboardContainer.tsx
|
|
543
|
-
import { useTranslations as
|
|
565
|
+
import { useTranslations as useTranslations37 } from "next-intl";
|
|
544
566
|
import { CreditCard as CreditCard4, Loader2 as Loader23, Wallet as Wallet2 } from "lucide-react";
|
|
545
567
|
import { useSearchParams } from "next/navigation";
|
|
546
|
-
import { useCallback as
|
|
568
|
+
import { useCallback as useCallback5, useEffect as useEffect10, useMemo as useMemo3, useState as useState16 } from "react";
|
|
547
569
|
|
|
548
570
|
// src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx
|
|
549
571
|
import { useTranslations as useTranslations9 } from "next-intl";
|
|
@@ -558,7 +580,12 @@ import { useTranslations as useTranslations6 } from "next-intl";
|
|
|
558
580
|
import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
|
|
559
581
|
import { useEffect, useState as useState2 } from "react";
|
|
560
582
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
561
|
-
function PaymentMethodForm({
|
|
583
|
+
function PaymentMethodForm({
|
|
584
|
+
onSuccess,
|
|
585
|
+
onCancel,
|
|
586
|
+
isLoading = false,
|
|
587
|
+
alwaysSetDefault = false
|
|
588
|
+
}) {
|
|
562
589
|
const t = useTranslations6();
|
|
563
590
|
const stripe = useStripe();
|
|
564
591
|
const elements = useElements();
|
|
@@ -608,7 +635,7 @@ function PaymentMethodForm({ onSuccess, onCancel, isLoading = false }) {
|
|
|
608
635
|
setIsSubmitting(false);
|
|
609
636
|
return;
|
|
610
637
|
}
|
|
611
|
-
if (setAsDefault && confirmedSetupIntent?.payment_method) {
|
|
638
|
+
if ((alwaysSetDefault || setAsDefault) && confirmedSetupIntent?.payment_method) {
|
|
612
639
|
await StripeCustomerService.setDefaultPaymentMethod({
|
|
613
640
|
paymentMethodId: typeof confirmedSetupIntent.payment_method === "string" ? confirmedSetupIntent.payment_method : confirmedSetupIntent.payment_method.id
|
|
614
641
|
});
|
|
@@ -647,8 +674,15 @@ function PaymentMethodForm({ onSuccess, onCancel, isLoading = false }) {
|
|
|
647
674
|
}
|
|
648
675
|
}
|
|
649
676
|
) }),
|
|
650
|
-
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-x-2", children: [
|
|
651
|
-
/* @__PURE__ */ jsx6(
|
|
677
|
+
!alwaysSetDefault && /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-x-2", children: [
|
|
678
|
+
/* @__PURE__ */ jsx6(
|
|
679
|
+
Checkbox,
|
|
680
|
+
{
|
|
681
|
+
id: "setAsDefault",
|
|
682
|
+
checked: setAsDefault,
|
|
683
|
+
onCheckedChange: (checked) => setSetAsDefault(!!checked)
|
|
684
|
+
}
|
|
685
|
+
),
|
|
652
686
|
/* @__PURE__ */ jsx6(Label, { htmlFor: "setAsDefault", className: "cursor-pointer font-normal", children: t("billing.ui.payment_form.set_default") })
|
|
653
687
|
] }),
|
|
654
688
|
error && /* @__PURE__ */ jsx6(Alert, { variant: "destructive", className: "bg-red-50 border-red-200", children: /* @__PURE__ */ jsx6(AlertDescription, { children: error }) }),
|
|
@@ -1426,6 +1460,7 @@ function SubscriptionsList({ subscriptions, onSubscriptionsChange, onChangePlan
|
|
|
1426
1460
|
/* @__PURE__ */ jsx18(TableBody, { children: subscriptions.map((subscription) => {
|
|
1427
1461
|
const price = subscription.price;
|
|
1428
1462
|
const amount = price?.unitAmount ? formatCurrency(price.unitAmount, price.currency, fmt) : "0";
|
|
1463
|
+
const discountedAmount = price ? applyDiscount(price.unitAmount, subscriptionDiscount(subscription)) : null;
|
|
1429
1464
|
const period = `${formatDate(subscription.currentPeriodStart, fmt)} - ${formatDate(subscription.currentPeriodEnd, fmt)}`;
|
|
1430
1465
|
return /* @__PURE__ */ jsxs15(
|
|
1431
1466
|
TableRow,
|
|
@@ -1442,7 +1477,10 @@ function SubscriptionsList({ subscriptions, onSubscriptionsChange, onChangePlan
|
|
|
1442
1477
|
) }),
|
|
1443
1478
|
/* @__PURE__ */ jsx18(TableCell, { className: "font-medium", children: formatPlanName3(price, t) }),
|
|
1444
1479
|
/* @__PURE__ */ jsx18(TableCell, { className: "text-muted-foreground text-sm", children: period }),
|
|
1445
|
-
/* @__PURE__ */ jsx18(TableCell, { className: "text-end font-medium", children:
|
|
1480
|
+
/* @__PURE__ */ jsx18(TableCell, { className: "text-end font-medium", children: discountedAmount !== null && price ? /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
1481
|
+
/* @__PURE__ */ jsx18("span", { className: "line-through text-muted-foreground me-2", children: amount }),
|
|
1482
|
+
/* @__PURE__ */ jsx18("span", { className: "text-success", children: formatCurrency(discountedAmount, price.currency, fmt) })
|
|
1483
|
+
] }) : amount }),
|
|
1446
1484
|
/* @__PURE__ */ jsx18(TableCell, { className: "text-end", children: (subscription.status === "active" /* ACTIVE */ || subscription.status === "trialing" /* TRIALING */) && onChangePlan && /* @__PURE__ */ jsx18(
|
|
1447
1485
|
Button,
|
|
1448
1486
|
{
|
|
@@ -1483,11 +1521,23 @@ __name(SubscriptionsList, "SubscriptionsList");
|
|
|
1483
1521
|
|
|
1484
1522
|
// src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx
|
|
1485
1523
|
import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1486
|
-
function SubscriptionsContainer({
|
|
1524
|
+
function SubscriptionsContainer({
|
|
1525
|
+
onOpenWizard,
|
|
1526
|
+
hasActiveRecurringSubscription,
|
|
1527
|
+
subscriptions: parentSubscriptions,
|
|
1528
|
+
onSubscriptionsChange
|
|
1529
|
+
}) {
|
|
1487
1530
|
const t = useTranslations18();
|
|
1488
|
-
const
|
|
1489
|
-
const [
|
|
1531
|
+
const isControlled = parentSubscriptions !== void 0;
|
|
1532
|
+
const [ownSubscriptions, setSubscriptions] = useState10([]);
|
|
1533
|
+
const [ownLoading, setLoading] = useState10(!isControlled);
|
|
1534
|
+
const subscriptions = parentSubscriptions ?? ownSubscriptions;
|
|
1535
|
+
const loading = isControlled ? false : ownLoading;
|
|
1490
1536
|
const loadSubscriptions = useCallback(async () => {
|
|
1537
|
+
if (isControlled) {
|
|
1538
|
+
onSubscriptionsChange?.();
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1491
1541
|
setLoading(true);
|
|
1492
1542
|
try {
|
|
1493
1543
|
const fetchedSubscriptions = await StripeSubscriptionService.listSubscriptions();
|
|
@@ -1497,9 +1547,9 @@ function SubscriptionsContainer({ onOpenWizard, hasActiveRecurringSubscription }
|
|
|
1497
1547
|
} finally {
|
|
1498
1548
|
setLoading(false);
|
|
1499
1549
|
}
|
|
1500
|
-
}, []);
|
|
1550
|
+
}, [isControlled, onSubscriptionsChange]);
|
|
1501
1551
|
useEffect5(() => {
|
|
1502
|
-
loadSubscriptions();
|
|
1552
|
+
if (!isControlled) loadSubscriptions();
|
|
1503
1553
|
}, []);
|
|
1504
1554
|
const criticalSubscriptions = subscriptions.filter(
|
|
1505
1555
|
(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
|
|
@@ -1824,13 +1874,128 @@ function SubscriptionConfirmation({ price, isLoading, onConfirm, onCancel }) {
|
|
|
1824
1874
|
__name(SubscriptionConfirmation, "SubscriptionConfirmation");
|
|
1825
1875
|
|
|
1826
1876
|
// src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx
|
|
1827
|
-
import { useTranslations as
|
|
1828
|
-
import { useCallback as
|
|
1877
|
+
import { useTranslations as useTranslations32 } from "next-intl";
|
|
1878
|
+
import { useCallback as useCallback4, useEffect as useEffect8, useRef as useRef2 } from "react";
|
|
1829
1879
|
|
|
1830
1880
|
// src/features/billing/stripe-subscription/hooks/useSubscriptionWizard.ts
|
|
1831
|
-
import { useTranslations as
|
|
1832
|
-
import { useCallback as
|
|
1881
|
+
import { useTranslations as useTranslations26 } from "next-intl";
|
|
1882
|
+
import { useCallback as useCallback3, useMemo, useReducer, useRef } from "react";
|
|
1833
1883
|
import { v4 } from "uuid";
|
|
1884
|
+
|
|
1885
|
+
// src/features/billing/stripe-subscription/hooks/wizard-promotion.ts
|
|
1886
|
+
function promotionCodeToSend(code) {
|
|
1887
|
+
if (!code) return void 0;
|
|
1888
|
+
return code.promotionCodeId;
|
|
1889
|
+
}
|
|
1890
|
+
__name(promotionCodeToSend, "promotionCodeToSend");
|
|
1891
|
+
function shouldLookUpAutomatic(code) {
|
|
1892
|
+
return !code || code.automatic === true;
|
|
1893
|
+
}
|
|
1894
|
+
__name(shouldLookUpAutomatic, "shouldLookUpAutomatic");
|
|
1895
|
+
function read(value, key) {
|
|
1896
|
+
return typeof value === "object" && value !== null ? Reflect.get(value, key) : void 0;
|
|
1897
|
+
}
|
|
1898
|
+
__name(read, "read");
|
|
1899
|
+
function errorStatus(error) {
|
|
1900
|
+
const status = read(error, "status") ?? read(read(error, "response"), "status");
|
|
1901
|
+
return typeof status === "number" ? status : void 0;
|
|
1902
|
+
}
|
|
1903
|
+
__name(errorStatus, "errorStatus");
|
|
1904
|
+
function errorCode(error) {
|
|
1905
|
+
const body = read(error, "body");
|
|
1906
|
+
const errors = read(body, "errors");
|
|
1907
|
+
const first = Array.isArray(errors) ? errors[0] : void 0;
|
|
1908
|
+
const code = read(first, "code") ?? read(read(read(first, "meta"), "context"), "code") ?? read(body, "code");
|
|
1909
|
+
return typeof code === "string" ? code : void 0;
|
|
1910
|
+
}
|
|
1911
|
+
__name(errorCode, "errorCode");
|
|
1912
|
+
function isDiscountSoldOut(error) {
|
|
1913
|
+
return errorStatus(error) === 410 && errorCode(error) === "discount_sold_out";
|
|
1914
|
+
}
|
|
1915
|
+
__name(isDiscountSoldOut, "isDiscountSoldOut");
|
|
1916
|
+
function isDiscountUnavailable(error) {
|
|
1917
|
+
return errorStatus(error) === 422 && errorCode(error) === "discount_unavailable";
|
|
1918
|
+
}
|
|
1919
|
+
__name(isDiscountUnavailable, "isDiscountUnavailable");
|
|
1920
|
+
|
|
1921
|
+
// src/features/billing/stripe-subscription/hooks/useConfirmSubscriptionPayment.ts
|
|
1922
|
+
import { useStripe as useStripe2 } from "@stripe/react-stripe-js";
|
|
1923
|
+
import { useTranslations as useTranslations25 } from "next-intl";
|
|
1924
|
+
import { useCallback as useCallback2, useState as useState11 } from "react";
|
|
1925
|
+
function useConfirmSubscriptionPayment() {
|
|
1926
|
+
const stripe = useStripe2();
|
|
1927
|
+
const t = useTranslations25();
|
|
1928
|
+
const [isConfirming, setIsConfirming] = useState11(false);
|
|
1929
|
+
const confirmPayment = useCallback2(
|
|
1930
|
+
async (clientSecret) => {
|
|
1931
|
+
if (!stripe) {
|
|
1932
|
+
console.error("[useConfirmSubscriptionPayment] Stripe not initialized");
|
|
1933
|
+
return {
|
|
1934
|
+
success: false,
|
|
1935
|
+
error: t("billing.ui.payment_confirm.errors.not_initialized")
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1938
|
+
if (!clientSecret) {
|
|
1939
|
+
console.error("[useConfirmSubscriptionPayment] No client secret provided");
|
|
1940
|
+
return {
|
|
1941
|
+
success: false,
|
|
1942
|
+
error: t("billing.ui.payment_confirm.errors.missing_details")
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
setIsConfirming(true);
|
|
1946
|
+
try {
|
|
1947
|
+
const { error: stripeError, paymentIntent } = await stripe.confirmCardPayment(clientSecret);
|
|
1948
|
+
if (stripeError) {
|
|
1949
|
+
console.error("[useConfirmSubscriptionPayment] Stripe error:", stripeError);
|
|
1950
|
+
return {
|
|
1951
|
+
success: false,
|
|
1952
|
+
error: stripeError.message || t("billing.ui.payment_confirm.errors.failed")
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
if (paymentIntent?.status === "succeeded") {
|
|
1956
|
+
return { success: true };
|
|
1957
|
+
}
|
|
1958
|
+
if (paymentIntent?.status === "requires_action") {
|
|
1959
|
+
return {
|
|
1960
|
+
success: false,
|
|
1961
|
+
error: t("billing.ui.payment_confirm.errors.requires_action")
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1964
|
+
return {
|
|
1965
|
+
success: false,
|
|
1966
|
+
error: t("billing.ui.payment_confirm.errors.not_completed")
|
|
1967
|
+
};
|
|
1968
|
+
} catch (err) {
|
|
1969
|
+
console.error("[useConfirmSubscriptionPayment] Unexpected error:", err);
|
|
1970
|
+
return {
|
|
1971
|
+
success: false,
|
|
1972
|
+
error: err.message || t("billing.ui.payment_confirm.errors.unexpected")
|
|
1973
|
+
};
|
|
1974
|
+
} finally {
|
|
1975
|
+
setIsConfirming(false);
|
|
1976
|
+
}
|
|
1977
|
+
},
|
|
1978
|
+
[stripe, t]
|
|
1979
|
+
);
|
|
1980
|
+
return {
|
|
1981
|
+
confirmPayment,
|
|
1982
|
+
isConfirming
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
__name(useConfirmSubscriptionPayment, "useConfirmSubscriptionPayment");
|
|
1986
|
+
|
|
1987
|
+
// src/features/billing/stripe-subscription/hooks/wizard-payment.ts
|
|
1988
|
+
function clientSecretToConfirm(meta) {
|
|
1989
|
+
if (!meta?.requiresAction) return null;
|
|
1990
|
+
return meta.clientSecret || null;
|
|
1991
|
+
}
|
|
1992
|
+
__name(clientSecretToConfirm, "clientSecretToConfirm");
|
|
1993
|
+
function subscriptionIdToSync(answered, opened) {
|
|
1994
|
+
return answered?.id || opened?.id || null;
|
|
1995
|
+
}
|
|
1996
|
+
__name(subscriptionIdToSync, "subscriptionIdToSync");
|
|
1997
|
+
|
|
1998
|
+
// src/features/billing/stripe-subscription/hooks/useSubscriptionWizard.ts
|
|
1834
1999
|
var initialState = {
|
|
1835
2000
|
step: "plan-selection",
|
|
1836
2001
|
selectedPrice: null,
|
|
@@ -1876,8 +2041,9 @@ function wizardReducer(state, action) {
|
|
|
1876
2041
|
}
|
|
1877
2042
|
__name(wizardReducer, "wizardReducer");
|
|
1878
2043
|
function useSubscriptionWizard({ subscription, onSuccess, onClose }) {
|
|
1879
|
-
const t =
|
|
2044
|
+
const t = useTranslations26();
|
|
1880
2045
|
const locale = useI18nLocale();
|
|
2046
|
+
const { confirmPayment } = useConfirmSubscriptionPayment();
|
|
1881
2047
|
const [state, dispatch] = useReducer(wizardReducer, {
|
|
1882
2048
|
...initialState,
|
|
1883
2049
|
selectedPrice: subscription?.price || null
|
|
@@ -1886,7 +2052,7 @@ function useSubscriptionWizard({ subscription, onSuccess, onClose }) {
|
|
|
1886
2052
|
const onCloseRef = useRef(onClose);
|
|
1887
2053
|
onSuccessRef.current = onSuccess;
|
|
1888
2054
|
onCloseRef.current = onClose;
|
|
1889
|
-
const checkPaymentMethod =
|
|
2055
|
+
const checkPaymentMethod = useCallback3(async () => {
|
|
1890
2056
|
try {
|
|
1891
2057
|
const methods = await StripeCustomerService.listPaymentMethods();
|
|
1892
2058
|
dispatch({ type: "SET_HAS_PAYMENT_METHOD", hasMethod: methods.length > 0 });
|
|
@@ -1895,16 +2061,40 @@ function useSubscriptionWizard({ subscription, onSuccess, onClose }) {
|
|
|
1895
2061
|
dispatch({ type: "SET_HAS_PAYMENT_METHOD", hasMethod: false });
|
|
1896
2062
|
}
|
|
1897
2063
|
}, []);
|
|
1898
|
-
const selectPrice =
|
|
2064
|
+
const selectPrice = useCallback3((price) => {
|
|
1899
2065
|
dispatch({ type: "SELECT_PRICE", price });
|
|
1900
2066
|
}, []);
|
|
1901
|
-
const setInterval =
|
|
2067
|
+
const setInterval = useCallback3((interval) => {
|
|
1902
2068
|
dispatch({ type: "SET_INTERVAL", interval });
|
|
1903
2069
|
}, []);
|
|
1904
|
-
const goToStep =
|
|
2070
|
+
const goToStep = useCallback3((step) => {
|
|
1905
2071
|
dispatch({ type: "SET_STEP", step });
|
|
1906
2072
|
}, []);
|
|
1907
|
-
const
|
|
2073
|
+
const lookUpAutomaticCode = useCallback3(async (priceId) => {
|
|
2074
|
+
try {
|
|
2075
|
+
const automaticCode = await StripeCouponCodeService.findAutomatic({ priceId });
|
|
2076
|
+
dispatch({
|
|
2077
|
+
type: "SET_PROMOTION_CODE",
|
|
2078
|
+
code: automaticCode ? toPromotionCodeResult(automaticCode) : null
|
|
2079
|
+
});
|
|
2080
|
+
return true;
|
|
2081
|
+
} catch (error) {
|
|
2082
|
+
console.error("[useSubscriptionWizard] Failed to look up the automatic promotion code:", error);
|
|
2083
|
+
dispatch({ type: "SET_PROMOTION_CODE", code: null });
|
|
2084
|
+
return false;
|
|
2085
|
+
}
|
|
2086
|
+
}, []);
|
|
2087
|
+
const refreshAutomaticCodeOrLeaveReview = useCallback3(
|
|
2088
|
+
async (priceId) => {
|
|
2089
|
+
const found = await lookUpAutomaticCode(priceId);
|
|
2090
|
+
if (!found) {
|
|
2091
|
+
dispatch({ type: "SET_STEP", step: "plan-selection" });
|
|
2092
|
+
dispatch({ type: "SET_ERROR", error: t("billing.ui.wizard.errors.prepare_review") });
|
|
2093
|
+
}
|
|
2094
|
+
},
|
|
2095
|
+
[lookUpAutomaticCode, t]
|
|
2096
|
+
);
|
|
2097
|
+
const goToReview = useCallback3(async () => {
|
|
1908
2098
|
if (!state.selectedPrice) return;
|
|
1909
2099
|
dispatch({ type: "SET_PROCESSING", isProcessing: true });
|
|
1910
2100
|
try {
|
|
@@ -1922,6 +2112,13 @@ function useSubscriptionWizard({ subscription, onSuccess, onClose }) {
|
|
|
1922
2112
|
return;
|
|
1923
2113
|
}
|
|
1924
2114
|
}
|
|
2115
|
+
if (shouldLookUpAutomatic(state.promotionCode)) {
|
|
2116
|
+
const found = await lookUpAutomaticCode(state.selectedPrice.id);
|
|
2117
|
+
if (!found) {
|
|
2118
|
+
dispatch({ type: "SET_ERROR", error: t("billing.ui.wizard.errors.prepare_review") });
|
|
2119
|
+
return;
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
1925
2122
|
if (subscription && state.selectedPrice.id !== subscription.price?.id) {
|
|
1926
2123
|
const preview = await StripeSubscriptionService.getProrationPreview({
|
|
1927
2124
|
subscriptionId: subscription.id,
|
|
@@ -1936,30 +2133,69 @@ function useSubscriptionWizard({ subscription, onSuccess, onClose }) {
|
|
|
1936
2133
|
} finally {
|
|
1937
2134
|
dispatch({ type: "SET_PROCESSING", isProcessing: false });
|
|
1938
2135
|
}
|
|
1939
|
-
}, [
|
|
1940
|
-
|
|
2136
|
+
}, [
|
|
2137
|
+
state.selectedPrice,
|
|
2138
|
+
state.hasPaymentMethod,
|
|
2139
|
+
state.promotionCode,
|
|
2140
|
+
subscription,
|
|
2141
|
+
checkPaymentMethod,
|
|
2142
|
+
lookUpAutomaticCode,
|
|
2143
|
+
t
|
|
2144
|
+
]);
|
|
2145
|
+
const confirmSubscription = useCallback3(async () => {
|
|
1941
2146
|
if (!state.selectedPrice) return;
|
|
1942
2147
|
dispatch({ type: "SET_PROCESSING", isProcessing: true });
|
|
1943
2148
|
dispatch({ type: "SET_ERROR", error: null });
|
|
1944
2149
|
try {
|
|
2150
|
+
let result;
|
|
1945
2151
|
if (subscription) {
|
|
1946
2152
|
const changePlanParams = {
|
|
1947
2153
|
id: subscription.id,
|
|
1948
2154
|
newPriceId: state.selectedPrice.id,
|
|
1949
|
-
promotionCode: state.promotionCode
|
|
2155
|
+
promotionCode: promotionCodeToSend(state.promotionCode)
|
|
1950
2156
|
};
|
|
1951
|
-
await StripeSubscriptionService.
|
|
2157
|
+
result = await StripeSubscriptionService.changePlanWithMeta(changePlanParams);
|
|
1952
2158
|
} else {
|
|
1953
2159
|
const createParams = {
|
|
1954
2160
|
id: v4(),
|
|
1955
2161
|
priceId: state.selectedPrice.id,
|
|
1956
|
-
promotionCode: state.promotionCode
|
|
2162
|
+
promotionCode: promotionCodeToSend(state.promotionCode)
|
|
1957
2163
|
};
|
|
1958
|
-
await StripeSubscriptionService.createSubscription(createParams);
|
|
2164
|
+
result = await StripeSubscriptionService.createSubscription(createParams);
|
|
2165
|
+
}
|
|
2166
|
+
const clientSecret = clientSecretToConfirm(result.meta);
|
|
2167
|
+
if (clientSecret) {
|
|
2168
|
+
const confirmation = await confirmPayment(clientSecret);
|
|
2169
|
+
if (!confirmation.success) {
|
|
2170
|
+
console.error("[useSubscriptionWizard] Payment not confirmed:", confirmation.error);
|
|
2171
|
+
dispatch({
|
|
2172
|
+
type: "SET_ERROR",
|
|
2173
|
+
error: t("billing.ui.wizard.errors.payment_not_confirmed", { reason: confirmation.error ?? "" })
|
|
2174
|
+
});
|
|
2175
|
+
return;
|
|
2176
|
+
}
|
|
2177
|
+
const subscriptionId = subscriptionIdToSync(result.subscription, subscription);
|
|
2178
|
+
if (subscriptionId) {
|
|
2179
|
+
try {
|
|
2180
|
+
await StripeSubscriptionService.syncSubscription({ subscriptionId });
|
|
2181
|
+
} catch (syncError) {
|
|
2182
|
+
console.error("[useSubscriptionWizard] Subscription sync after payment failed:", syncError);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
1959
2185
|
}
|
|
1960
2186
|
onSuccessRef.current();
|
|
1961
2187
|
onCloseRef.current();
|
|
1962
2188
|
} catch (error) {
|
|
2189
|
+
const soldOut = isDiscountSoldOut(error);
|
|
2190
|
+
if (soldOut || isDiscountUnavailable(error)) {
|
|
2191
|
+
dispatch({
|
|
2192
|
+
type: "SET_ERROR",
|
|
2193
|
+
error: soldOut ? t("billing.ui.wizard.errors.discount_sold_out") : t("billing.ui.wizard.errors.discount_unavailable")
|
|
2194
|
+
});
|
|
2195
|
+
dispatch({ type: "SET_PROMOTION_CODE", code: null });
|
|
2196
|
+
await refreshAutomaticCodeOrLeaveReview(state.selectedPrice.id);
|
|
2197
|
+
return;
|
|
2198
|
+
}
|
|
1963
2199
|
if (error?.status === 409 || error?.response?.status === 409) {
|
|
1964
2200
|
dispatch({
|
|
1965
2201
|
type: "SET_ERROR",
|
|
@@ -1976,15 +2212,15 @@ function useSubscriptionWizard({ subscription, onSuccess, onClose }) {
|
|
|
1976
2212
|
} finally {
|
|
1977
2213
|
dispatch({ type: "SET_PROCESSING", isProcessing: false });
|
|
1978
2214
|
}
|
|
1979
|
-
}, [state.selectedPrice, state.promotionCode, subscription, t]);
|
|
1980
|
-
const handlePaymentMethodSuccess =
|
|
2215
|
+
}, [state.selectedPrice, state.promotionCode, subscription, refreshAutomaticCodeOrLeaveReview, confirmPayment, t]);
|
|
2216
|
+
const handlePaymentMethodSuccess = useCallback3(async () => {
|
|
1981
2217
|
dispatch({ type: "SET_HAS_PAYMENT_METHOD", hasMethod: true });
|
|
1982
|
-
|
|
1983
|
-
}, []);
|
|
1984
|
-
const reset =
|
|
2218
|
+
await goToReview();
|
|
2219
|
+
}, [goToReview]);
|
|
2220
|
+
const reset = useCallback3(() => {
|
|
1985
2221
|
dispatch({ type: "RESET" });
|
|
1986
2222
|
}, []);
|
|
1987
|
-
const validatePromoCode =
|
|
2223
|
+
const validatePromoCode = useCallback3(
|
|
1988
2224
|
async (code) => {
|
|
1989
2225
|
dispatch({ type: "SET_VALIDATING_PROMO_CODE", isValidating: true });
|
|
1990
2226
|
dispatch({ type: "SET_PROMO_CODE_ERROR", error: null });
|
|
@@ -2014,11 +2250,18 @@ function useSubscriptionWizard({ subscription, onSuccess, onClose }) {
|
|
|
2014
2250
|
},
|
|
2015
2251
|
[state.selectedPrice?.id, t, locale]
|
|
2016
2252
|
);
|
|
2017
|
-
const clearPromoCode =
|
|
2253
|
+
const clearPromoCode = useCallback3(async () => {
|
|
2018
2254
|
dispatch({ type: "SET_PROMOTION_CODE", code: null });
|
|
2019
2255
|
dispatch({ type: "SET_PROMO_CODE_ERROR", error: null });
|
|
2020
2256
|
dispatch({ type: "SET_ERROR", error: null });
|
|
2021
|
-
|
|
2257
|
+
if (!state.selectedPrice) return;
|
|
2258
|
+
dispatch({ type: "SET_PROCESSING", isProcessing: true });
|
|
2259
|
+
try {
|
|
2260
|
+
await refreshAutomaticCodeOrLeaveReview(state.selectedPrice.id);
|
|
2261
|
+
} finally {
|
|
2262
|
+
dispatch({ type: "SET_PROCESSING", isProcessing: false });
|
|
2263
|
+
}
|
|
2264
|
+
}, [state.selectedPrice, refreshAutomaticCodeOrLeaveReview]);
|
|
2022
2265
|
const actions = useMemo(
|
|
2023
2266
|
() => ({
|
|
2024
2267
|
selectPrice,
|
|
@@ -2053,7 +2296,7 @@ function useSubscriptionWizard({ subscription, onSuccess, onClose }) {
|
|
|
2053
2296
|
__name(useSubscriptionWizard, "useSubscriptionWizard");
|
|
2054
2297
|
|
|
2055
2298
|
// src/features/billing/stripe-subscription/components/wizards/WizardProgressIndicator.tsx
|
|
2056
|
-
import { useTranslations as
|
|
2299
|
+
import { useTranslations as useTranslations27 } from "next-intl";
|
|
2057
2300
|
import { Check as Check3 } from "lucide-react";
|
|
2058
2301
|
import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2059
2302
|
var STEPS = [
|
|
@@ -2066,7 +2309,7 @@ function getStepIndex(step) {
|
|
|
2066
2309
|
}
|
|
2067
2310
|
__name(getStepIndex, "getStepIndex");
|
|
2068
2311
|
function WizardProgressIndicator({ currentStep }) {
|
|
2069
|
-
const t =
|
|
2312
|
+
const t = useTranslations27();
|
|
2070
2313
|
const currentIndex = getStepIndex(currentStep);
|
|
2071
2314
|
return /* @__PURE__ */ jsx26("div", { className: "flex items-center justify-center gap-x-2 py-4", children: STEPS.map((step, index) => {
|
|
2072
2315
|
const isCompleted = index < currentIndex;
|
|
@@ -2087,8 +2330,8 @@ function WizardProgressIndicator({ currentStep }) {
|
|
|
2087
2330
|
__name(WizardProgressIndicator, "WizardProgressIndicator");
|
|
2088
2331
|
|
|
2089
2332
|
// src/features/billing/stripe-subscription/components/wizards/WizardStepPlanSelection.tsx
|
|
2090
|
-
import { useTranslations as
|
|
2091
|
-
import { useEffect as useEffect6, useMemo as useMemo2, useState as
|
|
2333
|
+
import { useTranslations as useTranslations28 } from "next-intl";
|
|
2334
|
+
import { useEffect as useEffect6, useMemo as useMemo2, useState as useState12 } from "react";
|
|
2092
2335
|
import { jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2093
2336
|
function WizardStepPlanSelection({
|
|
2094
2337
|
selectedPrice,
|
|
@@ -2101,9 +2344,9 @@ function WizardStepPlanSelection({
|
|
|
2101
2344
|
onNext,
|
|
2102
2345
|
isProcessing
|
|
2103
2346
|
}) {
|
|
2104
|
-
const t =
|
|
2105
|
-
const [products, setProducts] =
|
|
2106
|
-
const [loading, setLoading] =
|
|
2347
|
+
const t = useTranslations28();
|
|
2348
|
+
const [products, setProducts] = useState12([]);
|
|
2349
|
+
const [loading, setLoading] = useState12(true);
|
|
2107
2350
|
useEffect6(() => {
|
|
2108
2351
|
const loadProducts = /* @__PURE__ */ __name(async () => {
|
|
2109
2352
|
try {
|
|
@@ -2164,12 +2407,13 @@ function WizardStepPlanSelection({
|
|
|
2164
2407
|
__name(WizardStepPlanSelection, "WizardStepPlanSelection");
|
|
2165
2408
|
|
|
2166
2409
|
// src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx
|
|
2167
|
-
import { useTranslations as
|
|
2410
|
+
import { useTranslations as useTranslations30 } from "next-intl";
|
|
2168
2411
|
import { AlertCircle } from "lucide-react";
|
|
2412
|
+
import { useEffect as useEffect7, useState as useState14 } from "react";
|
|
2169
2413
|
|
|
2170
2414
|
// src/features/billing/stripe-promotion-code/components/PromoCodeInput.tsx
|
|
2171
|
-
import { useTranslations as
|
|
2172
|
-
import { useState as
|
|
2415
|
+
import { useTranslations as useTranslations29 } from "next-intl";
|
|
2416
|
+
import { useState as useState13 } from "react";
|
|
2173
2417
|
import { Check as Check4, X, Loader2 as Loader22 } from "lucide-react";
|
|
2174
2418
|
import { jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2175
2419
|
function PromoCodeInput({
|
|
@@ -2180,9 +2424,19 @@ function PromoCodeInput({
|
|
|
2180
2424
|
onRemove,
|
|
2181
2425
|
disabled = false
|
|
2182
2426
|
}) {
|
|
2183
|
-
const t =
|
|
2427
|
+
const t = useTranslations29();
|
|
2184
2428
|
const locale = useI18nLocale();
|
|
2185
|
-
const [code, setCode] =
|
|
2429
|
+
const [code, setCode] = useState13("");
|
|
2430
|
+
const appliedKey = appliedCode?.valid ? `${appliedCode.automatic ? "auto" : "typed"}:${appliedCode.code ?? ""}` : null;
|
|
2431
|
+
const [previousAppliedKey, setPreviousAppliedKey] = useState13(appliedKey);
|
|
2432
|
+
if (appliedKey !== previousAppliedKey) {
|
|
2433
|
+
setPreviousAppliedKey(appliedKey);
|
|
2434
|
+
if (appliedKey !== null) setCode("");
|
|
2435
|
+
}
|
|
2436
|
+
const handleRemove = /* @__PURE__ */ __name(() => {
|
|
2437
|
+
setCode("");
|
|
2438
|
+
onRemove();
|
|
2439
|
+
}, "handleRemove");
|
|
2186
2440
|
const handleApply = /* @__PURE__ */ __name(() => {
|
|
2187
2441
|
if (code.trim()) {
|
|
2188
2442
|
onApply(code.trim().toUpperCase());
|
|
@@ -2214,6 +2468,39 @@ function PromoCodeInput({
|
|
|
2214
2468
|
return "";
|
|
2215
2469
|
}
|
|
2216
2470
|
}, "formatDuration");
|
|
2471
|
+
const codeInput = /* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
|
|
2472
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex gap-2", children: [
|
|
2473
|
+
/* @__PURE__ */ jsx28(
|
|
2474
|
+
Input,
|
|
2475
|
+
{
|
|
2476
|
+
placeholder: t("billing.ui.promo_code.placeholder"),
|
|
2477
|
+
value: code,
|
|
2478
|
+
onChange: (e) => setCode(e.target.value.toUpperCase()),
|
|
2479
|
+
onKeyDown: handleKeyDown,
|
|
2480
|
+
disabled: disabled || isValidating,
|
|
2481
|
+
className: "flex-1"
|
|
2482
|
+
}
|
|
2483
|
+
),
|
|
2484
|
+
/* @__PURE__ */ jsx28(Button, { variant: "outline", onClick: handleApply, disabled: disabled || isValidating || !code.trim(), children: isValidating ? /* @__PURE__ */ jsx28(Loader22, { className: "h-4 w-4 animate-spin" }) : t("billing.ui.promo_code.apply") })
|
|
2485
|
+
] }),
|
|
2486
|
+
error && /* @__PURE__ */ jsx28("p", { className: "text-destructive text-xs/relaxed", children: error })
|
|
2487
|
+
] });
|
|
2488
|
+
if (appliedCode?.valid && appliedCode.automatic) {
|
|
2489
|
+
return /* @__PURE__ */ jsxs25("div", { className: "space-y-3", children: [
|
|
2490
|
+
/* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
|
|
2491
|
+
/* @__PURE__ */ jsxs25("div", { className: "bg-success/15 border-success/30 flex items-center gap-2 rounded-lg border p-3", children: [
|
|
2492
|
+
/* @__PURE__ */ jsx28(Check4, { className: "text-success h-4 w-4" }),
|
|
2493
|
+
/* @__PURE__ */ jsx28("span", { className: "text-success font-medium", children: t("billing.ui.promo_code.automatic_label") }),
|
|
2494
|
+
/* @__PURE__ */ jsx28("span", { className: "text-success text-sm", children: formatDiscount(appliedCode) })
|
|
2495
|
+
] }),
|
|
2496
|
+
/* @__PURE__ */ jsx28("p", { className: "text-success text-sm", children: formatDuration(appliedCode) })
|
|
2497
|
+
] }),
|
|
2498
|
+
/* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
|
|
2499
|
+
/* @__PURE__ */ jsx28("p", { className: "text-muted-foreground text-xs/relaxed", children: t("billing.ui.promo_code.use_different") }),
|
|
2500
|
+
codeInput
|
|
2501
|
+
] })
|
|
2502
|
+
] });
|
|
2503
|
+
}
|
|
2217
2504
|
if (appliedCode?.valid) {
|
|
2218
2505
|
return /* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
|
|
2219
2506
|
/* @__PURE__ */ jsxs25("div", { className: "bg-success/15 border-success/30 flex items-center justify-between rounded-lg border p-3", children: [
|
|
@@ -2227,7 +2514,7 @@ function PromoCodeInput({
|
|
|
2227
2514
|
{
|
|
2228
2515
|
variant: "ghost",
|
|
2229
2516
|
size: "sm",
|
|
2230
|
-
onClick:
|
|
2517
|
+
onClick: handleRemove,
|
|
2231
2518
|
disabled,
|
|
2232
2519
|
className: "text-success hover:bg-success/25 hover:text-success",
|
|
2233
2520
|
children: /* @__PURE__ */ jsx28(X, { className: "h-4 w-4" })
|
|
@@ -2237,26 +2524,20 @@ function PromoCodeInput({
|
|
|
2237
2524
|
/* @__PURE__ */ jsx28("p", { className: "text-success text-sm", children: formatDuration(appliedCode) })
|
|
2238
2525
|
] });
|
|
2239
2526
|
}
|
|
2240
|
-
return
|
|
2241
|
-
/* @__PURE__ */ jsxs25("div", { className: "flex gap-2", children: [
|
|
2242
|
-
/* @__PURE__ */ jsx28(
|
|
2243
|
-
Input,
|
|
2244
|
-
{
|
|
2245
|
-
placeholder: t("billing.ui.promo_code.placeholder"),
|
|
2246
|
-
value: code,
|
|
2247
|
-
onChange: (e) => setCode(e.target.value.toUpperCase()),
|
|
2248
|
-
onKeyDown: handleKeyDown,
|
|
2249
|
-
disabled: disabled || isValidating,
|
|
2250
|
-
className: "flex-1"
|
|
2251
|
-
}
|
|
2252
|
-
),
|
|
2253
|
-
/* @__PURE__ */ jsx28(Button, { variant: "outline", onClick: handleApply, disabled: disabled || isValidating || !code.trim(), children: isValidating ? /* @__PURE__ */ jsx28(Loader22, { className: "h-4 w-4 animate-spin" }) : t("billing.ui.promo_code.apply") })
|
|
2254
|
-
] }),
|
|
2255
|
-
error && /* @__PURE__ */ jsx28("p", { className: "text-destructive text-xs/relaxed", children: error })
|
|
2256
|
-
] });
|
|
2527
|
+
return codeInput;
|
|
2257
2528
|
}
|
|
2258
2529
|
__name(PromoCodeInput, "PromoCodeInput");
|
|
2259
2530
|
|
|
2531
|
+
// src/features/billing/stripe-subscription/components/wizards/review-card.ts
|
|
2532
|
+
function reviewCard(methods, defaultPaymentMethodId) {
|
|
2533
|
+
if (!methods || methods.length === 0) return null;
|
|
2534
|
+
const method = defaultPaymentMethodId && methods.find((m) => m.id === defaultPaymentMethodId) || methods[0];
|
|
2535
|
+
if (!method.card?.last4) return null;
|
|
2536
|
+
const brand = method.card.brand ? method.card.brand.charAt(0).toUpperCase() + method.card.brand.slice(1) : method.type.charAt(0).toUpperCase() + method.type.slice(1);
|
|
2537
|
+
return { brand, last4: method.card.last4 };
|
|
2538
|
+
}
|
|
2539
|
+
__name(reviewCard, "reviewCard");
|
|
2540
|
+
|
|
2260
2541
|
// src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx
|
|
2261
2542
|
import { Fragment as Fragment6, jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2262
2543
|
function WizardStepReview({
|
|
@@ -2276,8 +2557,25 @@ function WizardStepReview({
|
|
|
2276
2557
|
onRemovePromoCode,
|
|
2277
2558
|
isTrialUpgrade
|
|
2278
2559
|
}) {
|
|
2279
|
-
const t =
|
|
2560
|
+
const t = useTranslations30();
|
|
2280
2561
|
const fmt = useBillingFormatOptions();
|
|
2562
|
+
const [card, setCard] = useState14(null);
|
|
2563
|
+
useEffect7(() => {
|
|
2564
|
+
if (!hasPaymentMethod) {
|
|
2565
|
+
setCard(null);
|
|
2566
|
+
return;
|
|
2567
|
+
}
|
|
2568
|
+
let cancelled = false;
|
|
2569
|
+
Promise.all([StripeCustomerService.listPaymentMethods(), StripeCustomerService.getCustomer()]).then(([methods, customer]) => {
|
|
2570
|
+
if (!cancelled) setCard(reviewCard(methods, customer?.defaultPaymentMethodId));
|
|
2571
|
+
}).catch((err) => {
|
|
2572
|
+
console.error("[WizardStepReview] Failed to load the card on file:", err);
|
|
2573
|
+
if (!cancelled) setCard(null);
|
|
2574
|
+
});
|
|
2575
|
+
return () => {
|
|
2576
|
+
cancelled = true;
|
|
2577
|
+
};
|
|
2578
|
+
}, [hasPaymentMethod]);
|
|
2281
2579
|
if (!selectedPrice) {
|
|
2282
2580
|
return /* @__PURE__ */ jsx29("div", { className: "text-center py-8 text-muted-foreground", children: t("billing.ui.wizard.review.no_plan") });
|
|
2283
2581
|
}
|
|
@@ -2287,41 +2585,19 @@ function WizardStepReview({
|
|
|
2287
2585
|
const interval = price.recurring?.interval || "month";
|
|
2288
2586
|
return interval === "year" ? t("billing.ui.wizard.review.interval_yearly") : t("billing.ui.wizard.review.interval_monthly");
|
|
2289
2587
|
}, "formatInterval");
|
|
2290
|
-
const
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
}
|
|
2296
|
-
if (promotionCode.discountType === "amount_off" && promotionCode.discountValue) {
|
|
2297
|
-
return Math.max(0, originalPrice - promotionCode.discountValue);
|
|
2298
|
-
}
|
|
2299
|
-
return null;
|
|
2300
|
-
}, "calculateDiscountedPrice");
|
|
2301
|
-
const discountedPrice = calculateDiscountedPrice();
|
|
2302
|
-
const calculateDiscountedImmediateCharge = /* @__PURE__ */ __name(() => {
|
|
2303
|
-
if (!promotionCode?.valid || !prorationPreview?.immediateCharge) return null;
|
|
2304
|
-
const originalCharge = prorationPreview.immediateCharge;
|
|
2305
|
-
if (promotionCode.discountType === "percent_off" && promotionCode.discountValue) {
|
|
2306
|
-
return originalCharge * (1 - promotionCode.discountValue / 100);
|
|
2307
|
-
}
|
|
2308
|
-
if (promotionCode.discountType === "amount_off" && promotionCode.discountValue) {
|
|
2309
|
-
return Math.max(0, originalCharge - promotionCode.discountValue);
|
|
2310
|
-
}
|
|
2311
|
-
return null;
|
|
2312
|
-
}, "calculateDiscountedImmediateCharge");
|
|
2313
|
-
const discountedImmediateCharge = calculateDiscountedImmediateCharge();
|
|
2588
|
+
const codeDiscount = promotionCodeDiscount(promotionCode);
|
|
2589
|
+
const carriedDiscount = isChangingPlan && !codeDiscount ? subscriptionDiscount(subscription) : null;
|
|
2590
|
+
const discount = codeDiscount ?? carriedDiscount;
|
|
2591
|
+
const discountedPrice = applyDiscount(selectedPrice.unitAmount, discount);
|
|
2592
|
+
const discountedImmediateCharge = applyDiscount(prorationPreview?.immediateCharge, discount);
|
|
2314
2593
|
const getDiscountDescription = /* @__PURE__ */ __name(() => {
|
|
2315
|
-
if (!
|
|
2316
|
-
if (
|
|
2317
|
-
return t("billing.ui.wizard.review.percent_off", { value:
|
|
2318
|
-
}
|
|
2319
|
-
if (promotionCode.discountType === "amount_off" && promotionCode.discountValue) {
|
|
2320
|
-
return t("billing.ui.wizard.review.amount_off", {
|
|
2321
|
-
amount: formatCurrency(promotionCode.discountValue, promotionCode.currency || selectedPrice.currency, fmt)
|
|
2322
|
-
});
|
|
2594
|
+
if (!discount) return null;
|
|
2595
|
+
if (discount.discountType === "percent_off") {
|
|
2596
|
+
return t("billing.ui.wizard.review.percent_off", { value: discount.discountValue });
|
|
2323
2597
|
}
|
|
2324
|
-
return
|
|
2598
|
+
return t("billing.ui.wizard.review.amount_off", {
|
|
2599
|
+
amount: formatCurrency(discount.discountValue, discount.currency || selectedPrice.currency, fmt)
|
|
2600
|
+
});
|
|
2325
2601
|
}, "getDiscountDescription");
|
|
2326
2602
|
const discountDescription = getDiscountDescription();
|
|
2327
2603
|
return /* @__PURE__ */ jsxs26("div", { className: "space-y-6", children: [
|
|
@@ -2336,7 +2612,8 @@ function WizardStepReview({
|
|
|
2336
2612
|
discountedPrice !== null ? /* @__PURE__ */ jsxs26(Fragment6, { children: [
|
|
2337
2613
|
/* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground line-through", children: formatCurrency(selectedPrice.unitAmount || 0, selectedPrice.currency, fmt) }),
|
|
2338
2614
|
/* @__PURE__ */ jsx29("p", { className: "text-success text-lg font-semibold", children: formatCurrency(discountedPrice, selectedPrice.currency, fmt) }),
|
|
2339
|
-
discountDescription && /* @__PURE__ */ jsx29(Badge, { variant: "softGreen", children: discountDescription })
|
|
2615
|
+
discountDescription && /* @__PURE__ */ jsx29(Badge, { variant: "softGreen", children: discountDescription }),
|
|
2616
|
+
carriedDiscount && /* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground", children: t("billing.ui.wizard.review.discount_carries_over") })
|
|
2340
2617
|
] }) : /* @__PURE__ */ jsx29("p", { className: "font-semibold text-lg", children: formatCurrency(selectedPrice.unitAmount || 0, selectedPrice.currency, fmt) }),
|
|
2341
2618
|
/* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground", children: formatInterval2(selectedPrice) })
|
|
2342
2619
|
] })
|
|
@@ -2348,7 +2625,7 @@ function WizardStepReview({
|
|
|
2348
2625
|
className: `${isTrialUpgrade ? "bg-warning/15 border-warning/30" : "bg-primary/10 border-primary/30"} space-y-2 rounded-lg border p-4`,
|
|
2349
2626
|
children: [
|
|
2350
2627
|
/* @__PURE__ */ jsx29("h4", { className: `font-medium ${isTrialUpgrade ? "text-warning" : "text-primary"}`, children: isTrialUpgrade ? t("billing.ui.wizard.review.trial_upgrade") : t("billing.ui.wizard.review.proration_summary") }),
|
|
2351
|
-
/* @__PURE__ */ jsx29("p", { className: `text-sm ${isTrialUpgrade ? "text-warning" : "text-foreground"}`, children: isTrialUpgrade ? t("billing.ui.wizard.review.trial_upgrade_description") : t("billing.ui.wizard.review.proration_description") }),
|
|
2628
|
+
/* @__PURE__ */ jsx29("p", { className: `text-sm ${isTrialUpgrade ? "text-warning" : "text-foreground"}`, children: isTrialUpgrade ? discountedImmediateCharge !== null ? t("billing.ui.wizard.review.trial_upgrade_description_discounted") : t("billing.ui.wizard.review.trial_upgrade_description") : t("billing.ui.wizard.review.proration_description") }),
|
|
2352
2629
|
/* @__PURE__ */ jsxs26("div", { className: "flex justify-between text-sm", children: [
|
|
2353
2630
|
/* @__PURE__ */ jsx29("span", { className: `${isTrialUpgrade ? "text-warning" : "text-muted-foreground"}`, children: isTrialUpgrade ? t("billing.ui.wizard.review.charge_now") : t("billing.ui.wizard.review.due_now") }),
|
|
2354
2631
|
/* @__PURE__ */ jsx29("span", { className: `font-medium ${isTrialUpgrade ? "text-warning" : "text-foreground"}`, children: discountedImmediateCharge !== null ? /* @__PURE__ */ jsxs26(Fragment6, { children: [
|
|
@@ -2376,9 +2653,9 @@ function WizardStepReview({
|
|
|
2376
2653
|
/* @__PURE__ */ jsx29("div", { className: "border rounded-lg p-4", children: /* @__PURE__ */ jsxs26("div", { className: "flex justify-between items-center", children: [
|
|
2377
2654
|
/* @__PURE__ */ jsxs26("div", { children: [
|
|
2378
2655
|
/* @__PURE__ */ jsx29("h4", { className: "font-medium", children: t("billing.ui.payment_method.title") }),
|
|
2379
|
-
/* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground", children: hasPaymentMethod ? t("billing.ui.wizard.review.payment_on_file") : t("billing.ui.wizard.review.no_payment_on_file") })
|
|
2656
|
+
/* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground", children: hasPaymentMethod ? card ? t("billing.ui.wizard.review.card_on_file", { brand: card.brand, last4: card.last4 }) : t("billing.ui.wizard.review.payment_on_file") : t("billing.ui.wizard.review.no_payment_on_file") })
|
|
2380
2657
|
] }),
|
|
2381
|
-
|
|
2658
|
+
hasPaymentMethod ? /* @__PURE__ */ jsx29(Button, { variant: "outline", onClick: onAddPaymentMethod, disabled: isProcessing, children: t("billing.ui.wizard.review.use_another_card") }) : /* @__PURE__ */ jsx29(Button, { variant: "outline", onClick: onAddPaymentMethod, children: t("billing.ui.payment_methods.add") })
|
|
2382
2659
|
] }) }),
|
|
2383
2660
|
error && /* @__PURE__ */ jsxs26(Alert, { variant: "destructive", children: [
|
|
2384
2661
|
/* @__PURE__ */ jsx29(AlertCircle, { className: "h-4 w-4" }),
|
|
@@ -2393,16 +2670,16 @@ function WizardStepReview({
|
|
|
2393
2670
|
__name(WizardStepReview, "WizardStepReview");
|
|
2394
2671
|
|
|
2395
2672
|
// src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx
|
|
2396
|
-
import { useTranslations as
|
|
2673
|
+
import { useTranslations as useTranslations31 } from "next-intl";
|
|
2397
2674
|
import { jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2398
2675
|
function WizardStepPaymentMethod({ onBack, onSuccess, isProcessing }) {
|
|
2399
|
-
const t =
|
|
2676
|
+
const t = useTranslations31();
|
|
2400
2677
|
return /* @__PURE__ */ jsxs27("div", { className: "space-y-6", children: [
|
|
2401
2678
|
/* @__PURE__ */ jsxs27("div", { className: "text-center", children: [
|
|
2402
2679
|
/* @__PURE__ */ jsx30(SectionHeader, { children: t("billing.ui.payment_methods.add") }),
|
|
2403
2680
|
/* @__PURE__ */ jsx30("p", { className: "text-sm text-muted-foreground", children: t("billing.ui.wizard.payment_description") })
|
|
2404
2681
|
] }),
|
|
2405
|
-
/* @__PURE__ */ jsx30(PaymentMethodForm, { onSuccess, onCancel: onBack, isLoading: isProcessing })
|
|
2682
|
+
/* @__PURE__ */ jsx30(PaymentMethodForm, { onSuccess, onCancel: onBack, isLoading: isProcessing, alwaysSetDefault: true })
|
|
2406
2683
|
] });
|
|
2407
2684
|
}
|
|
2408
2685
|
__name(WizardStepPaymentMethod, "WizardStepPaymentMethod");
|
|
@@ -2416,15 +2693,15 @@ function SubscriptionWizard({
|
|
|
2416
2693
|
hasActiveRecurringSubscription,
|
|
2417
2694
|
subscription
|
|
2418
2695
|
}) {
|
|
2419
|
-
const t =
|
|
2420
|
-
const handleClose =
|
|
2696
|
+
const t = useTranslations32();
|
|
2697
|
+
const handleClose = useCallback4(() => onOpenChange(false), [onOpenChange]);
|
|
2421
2698
|
const { state, actions } = useSubscriptionWizard({
|
|
2422
2699
|
subscription,
|
|
2423
2700
|
onSuccess,
|
|
2424
2701
|
onClose: handleClose
|
|
2425
2702
|
});
|
|
2426
2703
|
const hasCheckedRef = useRef2(false);
|
|
2427
|
-
|
|
2704
|
+
useEffect8(() => {
|
|
2428
2705
|
if (open && !hasCheckedRef.current) {
|
|
2429
2706
|
hasCheckedRef.current = true;
|
|
2430
2707
|
actions.checkPaymentMethod();
|
|
@@ -2433,7 +2710,7 @@ function SubscriptionWizard({
|
|
|
2433
2710
|
hasCheckedRef.current = false;
|
|
2434
2711
|
}
|
|
2435
2712
|
}, [open, actions.checkPaymentMethod]);
|
|
2436
|
-
|
|
2713
|
+
useEffect8(() => {
|
|
2437
2714
|
if (!open) {
|
|
2438
2715
|
actions.reset();
|
|
2439
2716
|
}
|
|
@@ -2442,7 +2719,7 @@ function SubscriptionWizard({
|
|
|
2442
2719
|
const isChangePlanMode = !!subscription;
|
|
2443
2720
|
const dialogTitle = subscription ? t("billing.ui.wizard.change_title") : isPurchasingAddons ? t("billing.ui.subscriptions.purchase_addons") : t("billing.ui.subscriptions.subscribe");
|
|
2444
2721
|
const dialogDescription = subscription ? t("billing.ui.wizard.change_description") : isPurchasingAddons ? t("billing.ui.wizard.addons_description") : t("billing.ui.wizard.subscribe_description");
|
|
2445
|
-
return /* @__PURE__ */ jsx31(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs28(DialogContent, { className: "max-w-2xl", children: [
|
|
2722
|
+
return /* @__PURE__ */ jsx31(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs28(DialogContent, { className: "max-w-2xl max-h-[90vh] overflow-y-auto", children: [
|
|
2446
2723
|
/* @__PURE__ */ jsxs28(DialogHeader, { children: [
|
|
2447
2724
|
/* @__PURE__ */ jsx31(DialogTitle, { children: dialogTitle }),
|
|
2448
2725
|
/* @__PURE__ */ jsx31(DialogDescription, { children: dialogDescription })
|
|
@@ -2495,12 +2772,12 @@ function SubscriptionWizard({
|
|
|
2495
2772
|
__name(SubscriptionWizard, "SubscriptionWizard");
|
|
2496
2773
|
|
|
2497
2774
|
// src/features/billing/stripe-usage/components/containers/UsageContainer.tsx
|
|
2498
|
-
import { useTranslations as
|
|
2775
|
+
import { useTranslations as useTranslations34 } from "next-intl";
|
|
2499
2776
|
import { Activity as Activity3 } from "lucide-react";
|
|
2500
|
-
import { useEffect as
|
|
2777
|
+
import { useEffect as useEffect9, useState as useState15 } from "react";
|
|
2501
2778
|
|
|
2502
2779
|
// src/features/billing/stripe-usage/components/details/UsageSummaryCard.tsx
|
|
2503
|
-
import { useTranslations as
|
|
2780
|
+
import { useTranslations as useTranslations33 } from "next-intl";
|
|
2504
2781
|
import { Activity as Activity2 } from "lucide-react";
|
|
2505
2782
|
import { jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2506
2783
|
function getProgressColor(percentage) {
|
|
@@ -2523,7 +2800,7 @@ function formatDate2(date, { locale, t }) {
|
|
|
2523
2800
|
}
|
|
2524
2801
|
__name(formatDate2, "formatDate");
|
|
2525
2802
|
function UsageSummaryCard({ meter, summary }) {
|
|
2526
|
-
const t =
|
|
2803
|
+
const t = useTranslations33();
|
|
2527
2804
|
const fmt = useBillingFormatOptions();
|
|
2528
2805
|
const currentUsage = summary?.aggregatedValue ?? 0;
|
|
2529
2806
|
const limit = meter.limit;
|
|
@@ -2570,12 +2847,12 @@ __name(UsageSummaryCards, "UsageSummaryCards");
|
|
|
2570
2847
|
// src/features/billing/stripe-usage/components/containers/UsageContainer.tsx
|
|
2571
2848
|
import { jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2572
2849
|
function UsageContainer() {
|
|
2573
|
-
const t =
|
|
2574
|
-
const [meters, setMeters] =
|
|
2575
|
-
const [summaries, setSummaries] =
|
|
2576
|
-
const [loading, setLoading] =
|
|
2577
|
-
const [subscriptions, setSubscriptions] =
|
|
2578
|
-
|
|
2850
|
+
const t = useTranslations34();
|
|
2851
|
+
const [meters, setMeters] = useState15([]);
|
|
2852
|
+
const [summaries, setSummaries] = useState15({});
|
|
2853
|
+
const [loading, setLoading] = useState15(true);
|
|
2854
|
+
const [subscriptions, setSubscriptions] = useState15([]);
|
|
2855
|
+
useEffect9(() => {
|
|
2579
2856
|
loadUsageData();
|
|
2580
2857
|
}, []);
|
|
2581
2858
|
const loadUsageData = /* @__PURE__ */ __name(async () => {
|
|
@@ -2639,7 +2916,7 @@ function UsageContainer() {
|
|
|
2639
2916
|
__name(UsageContainer, "UsageContainer");
|
|
2640
2917
|
|
|
2641
2918
|
// src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx
|
|
2642
|
-
import { useTranslations as
|
|
2919
|
+
import { useTranslations as useTranslations35 } from "next-intl";
|
|
2643
2920
|
import { jsx as jsx35, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
2644
2921
|
function formatDateTime(date, { locale, t }) {
|
|
2645
2922
|
if (!date) return t("billing.ui.not_available");
|
|
@@ -2658,7 +2935,7 @@ function formatDateTime(date, { locale, t }) {
|
|
|
2658
2935
|
}
|
|
2659
2936
|
__name(formatDateTime, "formatDateTime");
|
|
2660
2937
|
function UsageHistoryTable({ usageRecords }) {
|
|
2661
|
-
const t =
|
|
2938
|
+
const t = useTranslations35();
|
|
2662
2939
|
const fmt = useBillingFormatOptions();
|
|
2663
2940
|
if (usageRecords.length === 0) {
|
|
2664
2941
|
return /* @__PURE__ */ jsx35("div", { className: "rounded-lg border p-8 text-center", children: /* @__PURE__ */ jsx35("p", { className: "text-muted-foreground", children: t("billing.ui.usage.history_empty") }) });
|
|
@@ -2697,12 +2974,21 @@ function BillingDetailModal({ open, onOpenChange, title, children, className })
|
|
|
2697
2974
|
}
|
|
2698
2975
|
__name(BillingDetailModal, "BillingDetailModal");
|
|
2699
2976
|
|
|
2977
|
+
// src/features/billing/components/utils/url.ts
|
|
2978
|
+
function urlWithoutParam(params) {
|
|
2979
|
+
const query = new URLSearchParams(params.search);
|
|
2980
|
+
query.delete(params.param);
|
|
2981
|
+
const remaining = query.toString();
|
|
2982
|
+
return `${params.pathname}${remaining ? `?${remaining}` : ""}${params.hash ?? ""}`;
|
|
2983
|
+
}
|
|
2984
|
+
__name(urlWithoutParam, "urlWithoutParam");
|
|
2985
|
+
|
|
2700
2986
|
// src/features/billing/components/widgets/BillingAlertBanner.tsx
|
|
2701
|
-
import { useTranslations as
|
|
2987
|
+
import { useTranslations as useTranslations36 } from "next-intl";
|
|
2702
2988
|
import { AlertCircle as AlertCircle2 } from "lucide-react";
|
|
2703
2989
|
import { jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
2704
2990
|
function BillingAlertBanner({ subscription, onUpdatePayment, onAddPayment }) {
|
|
2705
|
-
const t =
|
|
2991
|
+
const t = useTranslations36();
|
|
2706
2992
|
if (subscription.status === "past_due" /* PAST_DUE */) {
|
|
2707
2993
|
return /* @__PURE__ */ jsxs33("div", { className: "bg-red-50 border border-red-200 rounded-lg p-4 flex items-start gap-x-3", children: [
|
|
2708
2994
|
/* @__PURE__ */ jsx37(AlertCircle2, { className: "text-destructive mt-0.5 h-5 w-5" }),
|
|
@@ -2735,8 +3021,8 @@ __name(BillingAlertBanner, "BillingAlertBanner");
|
|
|
2735
3021
|
// src/features/billing/components/containers/BillingDashboardContainer.tsx
|
|
2736
3022
|
import { Fragment as Fragment7, jsx as jsx38, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
2737
3023
|
function BillingDashboardContainer() {
|
|
2738
|
-
const t =
|
|
2739
|
-
const [data, setData] =
|
|
3024
|
+
const t = useTranslations37();
|
|
3025
|
+
const [data, setData] = useState16({
|
|
2740
3026
|
customer: null,
|
|
2741
3027
|
subscriptions: [],
|
|
2742
3028
|
paymentMethods: [],
|
|
@@ -2744,27 +3030,27 @@ function BillingDashboardContainer() {
|
|
|
2744
3030
|
meters: [],
|
|
2745
3031
|
meterSummaries: {}
|
|
2746
3032
|
});
|
|
2747
|
-
const [loading, setLoading] =
|
|
3033
|
+
const [loading, setLoading] = useState16({
|
|
2748
3034
|
customer: true,
|
|
2749
3035
|
subscriptions: true,
|
|
2750
3036
|
paymentMethods: true,
|
|
2751
3037
|
invoices: true,
|
|
2752
3038
|
usage: true
|
|
2753
3039
|
});
|
|
2754
|
-
const [errors, setErrors] =
|
|
3040
|
+
const [errors, setErrors] = useState16({
|
|
2755
3041
|
customer: null,
|
|
2756
3042
|
subscriptions: null,
|
|
2757
3043
|
paymentMethods: null,
|
|
2758
3044
|
invoices: null,
|
|
2759
3045
|
usage: null
|
|
2760
3046
|
});
|
|
2761
|
-
const [activeModal, setActiveModal] =
|
|
2762
|
-
const [noCustomerExists, setNoCustomerExists] =
|
|
2763
|
-
const [creatingCustomer, setCreatingCustomer] =
|
|
3047
|
+
const [activeModal, setActiveModal] = useState16(null);
|
|
3048
|
+
const [noCustomerExists, setNoCustomerExists] = useState16(false);
|
|
3049
|
+
const [creatingCustomer, setCreatingCustomer] = useState16(false);
|
|
2764
3050
|
const searchParams = useSearchParams();
|
|
2765
|
-
const [showWizard, setShowWizard] =
|
|
2766
|
-
const [editingSubscription, setEditingSubscription] =
|
|
2767
|
-
const hasMeteredSubscriptions =
|
|
3051
|
+
const [showWizard, setShowWizard] = useState16(false);
|
|
3052
|
+
const [editingSubscription, setEditingSubscription] = useState16(null);
|
|
3053
|
+
const hasMeteredSubscriptions = useCallback5(() => {
|
|
2768
3054
|
return data.subscriptions.some((sub) => sub.price?.recurring?.usageType === "metered");
|
|
2769
3055
|
}, [data.subscriptions]);
|
|
2770
3056
|
const hasActiveRecurringSubscription = useMemo3(() => {
|
|
@@ -2772,7 +3058,7 @@ function BillingDashboardContainer() {
|
|
|
2772
3058
|
(sub) => (sub.status === "active" /* ACTIVE */ || sub.status === "trialing" /* TRIALING */) && sub.price?.priceType === "recurring"
|
|
2773
3059
|
);
|
|
2774
3060
|
}, [data.subscriptions]);
|
|
2775
|
-
const fetchAllData =
|
|
3061
|
+
const fetchAllData = useCallback5(async () => {
|
|
2776
3062
|
setNoCustomerExists(false);
|
|
2777
3063
|
let customer = null;
|
|
2778
3064
|
try {
|
|
@@ -2891,7 +3177,7 @@ function BillingDashboardContainer() {
|
|
|
2891
3177
|
setLoading((prev) => ({ ...prev, usage: false }));
|
|
2892
3178
|
}
|
|
2893
3179
|
}, "fetchUsageData");
|
|
2894
|
-
const refreshData =
|
|
3180
|
+
const refreshData = useCallback5(async () => {
|
|
2895
3181
|
setLoading({
|
|
2896
3182
|
customer: true,
|
|
2897
3183
|
subscriptions: true,
|
|
@@ -2901,23 +3187,32 @@ function BillingDashboardContainer() {
|
|
|
2901
3187
|
});
|
|
2902
3188
|
await fetchAllData();
|
|
2903
3189
|
}, [fetchAllData]);
|
|
2904
|
-
const handleOpenWizard =
|
|
3190
|
+
const handleOpenWizard = useCallback5((subscription) => {
|
|
2905
3191
|
setEditingSubscription(subscription || null);
|
|
2906
3192
|
setShowWizard(true);
|
|
2907
3193
|
}, []);
|
|
2908
|
-
const handleWizardClose =
|
|
3194
|
+
const handleWizardClose = useCallback5(() => {
|
|
2909
3195
|
setShowWizard(false);
|
|
2910
3196
|
setEditingSubscription(null);
|
|
2911
3197
|
refreshData();
|
|
2912
3198
|
}, [refreshData]);
|
|
2913
|
-
|
|
3199
|
+
useEffect10(() => {
|
|
2914
3200
|
fetchAllData();
|
|
2915
3201
|
}, [fetchAllData]);
|
|
2916
|
-
|
|
3202
|
+
useEffect10(() => {
|
|
2917
3203
|
const action = searchParams.get("action");
|
|
2918
3204
|
if (action === "subscribe") {
|
|
2919
3205
|
setShowWizard(true);
|
|
2920
|
-
window.history.replaceState(
|
|
3206
|
+
window.history.replaceState(
|
|
3207
|
+
window.history.state,
|
|
3208
|
+
"",
|
|
3209
|
+
urlWithoutParam({
|
|
3210
|
+
pathname: window.location.pathname,
|
|
3211
|
+
search: window.location.search,
|
|
3212
|
+
hash: window.location.hash,
|
|
3213
|
+
param: "action"
|
|
3214
|
+
})
|
|
3215
|
+
);
|
|
2921
3216
|
}
|
|
2922
3217
|
}, [searchParams]);
|
|
2923
3218
|
const criticalSubscriptions = data.subscriptions.filter(
|
|
@@ -3036,7 +3331,9 @@ function BillingDashboardContainer() {
|
|
|
3036
3331
|
SubscriptionsContainer,
|
|
3037
3332
|
{
|
|
3038
3333
|
onOpenWizard: handleOpenWizard,
|
|
3039
|
-
hasActiveRecurringSubscription
|
|
3334
|
+
hasActiveRecurringSubscription,
|
|
3335
|
+
subscriptions: data.subscriptions,
|
|
3336
|
+
onSubscriptionsChange: refreshData
|
|
3040
3337
|
}
|
|
3041
3338
|
)
|
|
3042
3339
|
}
|
|
@@ -3071,7 +3368,7 @@ function BillingDashboardContainer() {
|
|
|
3071
3368
|
/* @__PURE__ */ jsx38(
|
|
3072
3369
|
SubscriptionWizard,
|
|
3073
3370
|
{
|
|
3074
|
-
open: showWizard,
|
|
3371
|
+
open: showWizard && !!data.customer,
|
|
3075
3372
|
onOpenChange: (open) => !open && handleWizardClose(),
|
|
3076
3373
|
onSuccess: refreshData,
|
|
3077
3374
|
hasActiveRecurringSubscription,
|
|
@@ -3117,19 +3414,19 @@ function isStripeConfigured() {
|
|
|
3117
3414
|
__name(isStripeConfigured, "isStripeConfigured");
|
|
3118
3415
|
|
|
3119
3416
|
// src/features/billing/contexts/PriceContext.tsx
|
|
3120
|
-
import { useTranslations as
|
|
3121
|
-
import { createContext, useCallback as
|
|
3417
|
+
import { useTranslations as useTranslations40 } from "next-intl";
|
|
3418
|
+
import { createContext, useCallback as useCallback7, useContext, useMemo as useMemo6, useState as useState19 } from "react";
|
|
3122
3419
|
|
|
3123
3420
|
// src/features/billing/stripe-price/components/forms/PriceArchiver.tsx
|
|
3124
3421
|
import { ArchiveIcon, RotateCcwIcon } from "lucide-react";
|
|
3125
|
-
import { useTranslations as
|
|
3126
|
-
import { useState as
|
|
3422
|
+
import { useTranslations as useTranslations38 } from "next-intl";
|
|
3423
|
+
import { useState as useState17 } from "react";
|
|
3127
3424
|
import { Fragment as Fragment9, jsx as jsx40, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
3128
3425
|
function PriceArchiver({ price }) {
|
|
3129
|
-
const t =
|
|
3426
|
+
const t = useTranslations38();
|
|
3130
3427
|
const fmt = useBillingFormatOptions();
|
|
3131
3428
|
const { archivePrice, restorePrice } = usePriceContext();
|
|
3132
|
-
const [open, setOpen] =
|
|
3429
|
+
const [open, setOpen] = useState17(false);
|
|
3133
3430
|
const isArchived = !price.active;
|
|
3134
3431
|
const scope = isArchived ? "restore" : "archive";
|
|
3135
3432
|
return /* @__PURE__ */ jsxs35(Fragment9, { children: [
|
|
@@ -3165,8 +3462,8 @@ __name(PriceArchiver, "PriceArchiver");
|
|
|
3165
3462
|
// src/features/billing/stripe-price/components/forms/PriceEditor.tsx
|
|
3166
3463
|
import { zodResolver as zodResolver2 } from "@hookform/resolvers/zod";
|
|
3167
3464
|
import { PlusIcon, XIcon } from "lucide-react";
|
|
3168
|
-
import { useTranslations as
|
|
3169
|
-
import { useCallback as
|
|
3465
|
+
import { useTranslations as useTranslations39 } from "next-intl";
|
|
3466
|
+
import { useCallback as useCallback6, useEffect as useEffect11, useMemo as useMemo5, useRef as useRef3, useState as useState18 } from "react";
|
|
3170
3467
|
import { useForm as useForm2 } from "react-hook-form";
|
|
3171
3468
|
import { v4 as v42 } from "uuid";
|
|
3172
3469
|
import { z as z2 } from "zod";
|
|
@@ -3183,14 +3480,14 @@ function PriceEditorInternal({
|
|
|
3183
3480
|
dialogOpen,
|
|
3184
3481
|
onDialogOpenChange
|
|
3185
3482
|
}) {
|
|
3186
|
-
const t =
|
|
3483
|
+
const t = useTranslations39();
|
|
3187
3484
|
const fmt = useBillingFormatOptions();
|
|
3188
3485
|
const router = useI18nRouter();
|
|
3189
3486
|
const { createPrice, updatePrice, productId: contextProductId } = usePriceContext();
|
|
3190
|
-
const [allFeatures, setAllFeatures] =
|
|
3487
|
+
const [allFeatures, setAllFeatures] = useState18([]);
|
|
3191
3488
|
const isEdit = !!price;
|
|
3192
3489
|
const targetProductId = productId ?? price?.product?.id ?? contextProductId;
|
|
3193
|
-
|
|
3490
|
+
useEffect11(() => {
|
|
3194
3491
|
const fetchFeatures = /* @__PURE__ */ __name(async () => {
|
|
3195
3492
|
setAllFeatures(await FeatureService.findMany({}));
|
|
3196
3493
|
}, "fetchFeatures");
|
|
@@ -3239,7 +3536,7 @@ function PriceEditorInternal({
|
|
|
3239
3536
|
}),
|
|
3240
3537
|
[t]
|
|
3241
3538
|
);
|
|
3242
|
-
const resolvePlatformFeatures =
|
|
3539
|
+
const resolvePlatformFeatures = useCallback6(
|
|
3243
3540
|
(names) => {
|
|
3244
3541
|
const ids = [];
|
|
3245
3542
|
const unknown = [];
|
|
@@ -3252,7 +3549,7 @@ function PriceEditorInternal({
|
|
|
3252
3549
|
},
|
|
3253
3550
|
[allFeatures]
|
|
3254
3551
|
);
|
|
3255
|
-
const getDefaultValues =
|
|
3552
|
+
const getDefaultValues = useCallback6(() => {
|
|
3256
3553
|
const coreFeatureIds = allFeatures.filter((feature) => feature.isCore).map((feature) => feature.id);
|
|
3257
3554
|
if (!price && seed) {
|
|
3258
3555
|
return {
|
|
@@ -3291,14 +3588,14 @@ function PriceEditorInternal({
|
|
|
3291
3588
|
defaultValues: getDefaultValues()
|
|
3292
3589
|
});
|
|
3293
3590
|
const hasCaughtUpWithFeatures = useRef3(false);
|
|
3294
|
-
|
|
3591
|
+
useEffect11(() => {
|
|
3295
3592
|
if (isEdit || hasCaughtUpWithFeatures.current || allFeatures.length === 0) return;
|
|
3296
3593
|
hasCaughtUpWithFeatures.current = true;
|
|
3297
3594
|
if (form.formState.isDirty) return;
|
|
3298
3595
|
form.reset(getDefaultValues());
|
|
3299
3596
|
}, [allFeatures, isEdit, form, getDefaultValues]);
|
|
3300
3597
|
const warnedUnknownFor = useRef3(void 0);
|
|
3301
|
-
|
|
3598
|
+
useEffect11(() => {
|
|
3302
3599
|
if (isEdit || !seed || allFeatures.length === 0) return;
|
|
3303
3600
|
const fingerprint = JSON.stringify(seed.platformFeatures ?? []);
|
|
3304
3601
|
if (warnedUnknownFor.current === fingerprint) return;
|
|
@@ -3592,21 +3889,21 @@ var PriceProvider = /* @__PURE__ */ __name(({
|
|
|
3592
3889
|
publishChrome = true,
|
|
3593
3890
|
productId: productIdProp
|
|
3594
3891
|
}) => {
|
|
3595
|
-
const t =
|
|
3892
|
+
const t = useTranslations40();
|
|
3596
3893
|
const fmt = useBillingFormatOptions();
|
|
3597
3894
|
const generateUrl = usePageUrlGenerator();
|
|
3598
3895
|
const { hasRole } = useCurrentUserContext();
|
|
3599
|
-
const [price, setPrice] =
|
|
3896
|
+
const [price, setPrice] = useState19(
|
|
3600
3897
|
dehydratedPrice ? rehydrate(Modules.StripePrice, dehydratedPrice) : void 0
|
|
3601
3898
|
);
|
|
3602
|
-
const [priceVersion, setPriceVersion] =
|
|
3603
|
-
const refreshPrices =
|
|
3899
|
+
const [priceVersion, setPriceVersion] = useState19(0);
|
|
3900
|
+
const refreshPrices = useCallback7(() => setPriceVersion((value2) => value2 + 1), []);
|
|
3604
3901
|
const productId = productIdProp ?? price?.product?.id;
|
|
3605
|
-
const reloadPrice =
|
|
3902
|
+
const reloadPrice = useCallback7(async () => {
|
|
3606
3903
|
if (!price?.id) return;
|
|
3607
3904
|
setPrice(await StripePriceService.getPrice({ id: price.id }));
|
|
3608
3905
|
}, [price?.id]);
|
|
3609
|
-
const createPrice =
|
|
3906
|
+
const createPrice = useCallback7(
|
|
3610
3907
|
async (input) => {
|
|
3611
3908
|
const saved = await StripePriceService.createPrice(input);
|
|
3612
3909
|
refreshPrices();
|
|
@@ -3614,7 +3911,7 @@ var PriceProvider = /* @__PURE__ */ __name(({
|
|
|
3614
3911
|
},
|
|
3615
3912
|
[refreshPrices]
|
|
3616
3913
|
);
|
|
3617
|
-
const updatePrice =
|
|
3914
|
+
const updatePrice = useCallback7(
|
|
3618
3915
|
async (input) => {
|
|
3619
3916
|
const saved = await StripePriceService.updatePrice(input);
|
|
3620
3917
|
setPrice(saved);
|
|
@@ -3623,7 +3920,7 @@ var PriceProvider = /* @__PURE__ */ __name(({
|
|
|
3623
3920
|
},
|
|
3624
3921
|
[refreshPrices]
|
|
3625
3922
|
);
|
|
3626
|
-
const archivePrice =
|
|
3923
|
+
const archivePrice = useCallback7(
|
|
3627
3924
|
async (id) => {
|
|
3628
3925
|
const target = id ?? price?.id;
|
|
3629
3926
|
if (!target) return;
|
|
@@ -3633,7 +3930,7 @@ var PriceProvider = /* @__PURE__ */ __name(({
|
|
|
3633
3930
|
},
|
|
3634
3931
|
[price?.id, reloadPrice, refreshPrices]
|
|
3635
3932
|
);
|
|
3636
|
-
const restorePrice =
|
|
3933
|
+
const restorePrice = useCallback7(
|
|
3637
3934
|
async (id) => {
|
|
3638
3935
|
const target = id ?? price?.id;
|
|
3639
3936
|
if (!target) return;
|
|
@@ -3688,18 +3985,18 @@ var PriceProvider = /* @__PURE__ */ __name(({
|
|
|
3688
3985
|
var usePriceContext = /* @__PURE__ */ __name(() => useContext(PriceContext) ?? defaultContextValue, "usePriceContext");
|
|
3689
3986
|
|
|
3690
3987
|
// src/features/billing/contexts/ProductContext.tsx
|
|
3691
|
-
import { useTranslations as
|
|
3692
|
-
import { createContext as createContext2, useCallback as
|
|
3988
|
+
import { useTranslations as useTranslations43 } from "next-intl";
|
|
3989
|
+
import { createContext as createContext2, useCallback as useCallback9, useContext as useContext2, useMemo as useMemo8, useState as useState21 } from "react";
|
|
3693
3990
|
|
|
3694
3991
|
// src/features/billing/stripe-product/components/forms/ProductArchiver.tsx
|
|
3695
3992
|
import { ArchiveIcon as ArchiveIcon2, RotateCcwIcon as RotateCcwIcon2 } from "lucide-react";
|
|
3696
|
-
import { useTranslations as
|
|
3697
|
-
import { useState as
|
|
3993
|
+
import { useTranslations as useTranslations41 } from "next-intl";
|
|
3994
|
+
import { useState as useState20 } from "react";
|
|
3698
3995
|
import { Fragment as Fragment10, jsx as jsx43, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
3699
3996
|
function ProductArchiver({ product }) {
|
|
3700
|
-
const t =
|
|
3997
|
+
const t = useTranslations41();
|
|
3701
3998
|
const { archiveProduct, restoreProduct } = useProductContext();
|
|
3702
|
-
const [open, setOpen] =
|
|
3999
|
+
const [open, setOpen] = useState20(false);
|
|
3703
4000
|
const isArchived = !product.active;
|
|
3704
4001
|
const scope = isArchived ? "restore" : "archive";
|
|
3705
4002
|
return /* @__PURE__ */ jsxs37(Fragment10, { children: [
|
|
@@ -3734,8 +4031,8 @@ __name(ProductArchiver, "ProductArchiver");
|
|
|
3734
4031
|
|
|
3735
4032
|
// src/features/billing/stripe-product/components/forms/ProductEditor.tsx
|
|
3736
4033
|
import { zodResolver as zodResolver3 } from "@hookform/resolvers/zod";
|
|
3737
|
-
import { useTranslations as
|
|
3738
|
-
import { useCallback as
|
|
4034
|
+
import { useTranslations as useTranslations42 } from "next-intl";
|
|
4035
|
+
import { useCallback as useCallback8, useMemo as useMemo7 } from "react";
|
|
3739
4036
|
import { useForm as useForm3 } from "react-hook-form";
|
|
3740
4037
|
import { v4 as v43 } from "uuid";
|
|
3741
4038
|
import { z as z3 } from "zod";
|
|
@@ -3750,7 +4047,7 @@ function ProductEditorInternal({
|
|
|
3750
4047
|
dialogOpen,
|
|
3751
4048
|
onDialogOpenChange
|
|
3752
4049
|
}) {
|
|
3753
|
-
const t =
|
|
4050
|
+
const t = useTranslations42();
|
|
3754
4051
|
const router = useI18nRouter();
|
|
3755
4052
|
const { createProduct, updateProduct } = useProductContext();
|
|
3756
4053
|
const isEdit = !!product;
|
|
@@ -3762,7 +4059,7 @@ function ProductEditorInternal({
|
|
|
3762
4059
|
}),
|
|
3763
4060
|
[t]
|
|
3764
4061
|
);
|
|
3765
|
-
const getDefaultValues =
|
|
4062
|
+
const getDefaultValues = useCallback8(
|
|
3766
4063
|
() => ({
|
|
3767
4064
|
id: product?.id ?? v43(),
|
|
3768
4065
|
name: product?.name ?? "",
|
|
@@ -3851,19 +4148,19 @@ var defaultContextValue2 = {
|
|
|
3851
4148
|
}, "refreshCatalogue")
|
|
3852
4149
|
};
|
|
3853
4150
|
var ProductProvider = /* @__PURE__ */ __name(({ children, dehydratedProduct, publishChrome = true }) => {
|
|
3854
|
-
const t =
|
|
4151
|
+
const t = useTranslations43();
|
|
3855
4152
|
const generateUrl = usePageUrlGenerator();
|
|
3856
4153
|
const { hasRole } = useCurrentUserContext();
|
|
3857
|
-
const [product, setProduct] =
|
|
4154
|
+
const [product, setProduct] = useState21(
|
|
3858
4155
|
dehydratedProduct ? rehydrate(Modules.StripeProduct, dehydratedProduct) : void 0
|
|
3859
4156
|
);
|
|
3860
|
-
const [catalogueVersion, setCatalogueVersion] =
|
|
3861
|
-
const refreshCatalogue =
|
|
3862
|
-
const reloadProduct =
|
|
4157
|
+
const [catalogueVersion, setCatalogueVersion] = useState21(0);
|
|
4158
|
+
const refreshCatalogue = useCallback9(() => setCatalogueVersion((value2) => value2 + 1), []);
|
|
4159
|
+
const reloadProduct = useCallback9(async () => {
|
|
3863
4160
|
if (!product?.id) return;
|
|
3864
4161
|
setProduct(await StripeProductService.getProduct({ id: product.id }));
|
|
3865
4162
|
}, [product?.id]);
|
|
3866
|
-
const createProduct =
|
|
4163
|
+
const createProduct = useCallback9(
|
|
3867
4164
|
async (input) => {
|
|
3868
4165
|
const saved = await StripeProductService.createProduct(input);
|
|
3869
4166
|
refreshCatalogue();
|
|
@@ -3871,7 +4168,7 @@ var ProductProvider = /* @__PURE__ */ __name(({ children, dehydratedProduct, pub
|
|
|
3871
4168
|
},
|
|
3872
4169
|
[refreshCatalogue]
|
|
3873
4170
|
);
|
|
3874
|
-
const updateProduct =
|
|
4171
|
+
const updateProduct = useCallback9(
|
|
3875
4172
|
async (input) => {
|
|
3876
4173
|
const saved = await StripeProductService.updateProduct(input);
|
|
3877
4174
|
setProduct(saved);
|
|
@@ -3880,7 +4177,7 @@ var ProductProvider = /* @__PURE__ */ __name(({ children, dehydratedProduct, pub
|
|
|
3880
4177
|
},
|
|
3881
4178
|
[refreshCatalogue]
|
|
3882
4179
|
);
|
|
3883
|
-
const archiveProduct =
|
|
4180
|
+
const archiveProduct = useCallback9(
|
|
3884
4181
|
async (id) => {
|
|
3885
4182
|
const target = id ?? product?.id;
|
|
3886
4183
|
if (!target) return;
|
|
@@ -3890,7 +4187,7 @@ var ProductProvider = /* @__PURE__ */ __name(({ children, dehydratedProduct, pub
|
|
|
3890
4187
|
},
|
|
3891
4188
|
[product?.id, reloadProduct, refreshCatalogue]
|
|
3892
4189
|
);
|
|
3893
|
-
const restoreProduct =
|
|
4190
|
+
const restoreProduct = useCallback9(
|
|
3894
4191
|
async (id) => {
|
|
3895
4192
|
const target = id ?? product?.id;
|
|
3896
4193
|
if (!target) return;
|
|
@@ -3952,10 +4249,10 @@ var ProductProvider = /* @__PURE__ */ __name(({ children, dehydratedProduct, pub
|
|
|
3952
4249
|
var useProductContext = /* @__PURE__ */ __name(() => useContext2(ProductContext) ?? defaultContextValue2, "useProductContext");
|
|
3953
4250
|
|
|
3954
4251
|
// src/features/billing/stripe-price/components/details/PriceDetails.tsx
|
|
3955
|
-
import { useTranslations as
|
|
4252
|
+
import { useTranslations as useTranslations44 } from "next-intl";
|
|
3956
4253
|
import { jsx as jsx46, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
3957
4254
|
function PriceDetails() {
|
|
3958
|
-
const t =
|
|
4255
|
+
const t = useTranslations44();
|
|
3959
4256
|
const fmt = useBillingFormatOptions();
|
|
3960
4257
|
const generateUrl = usePageUrlGenerator();
|
|
3961
4258
|
const { price } = usePriceContext();
|
|
@@ -4072,8 +4369,8 @@ __name(PriceContainer, "PriceContainer");
|
|
|
4072
4369
|
|
|
4073
4370
|
// src/features/billing/stripe-price/components/lists/PricesList.tsx
|
|
4074
4371
|
import { DollarSignIcon, UploadIcon } from "lucide-react";
|
|
4075
|
-
import { useTranslations as
|
|
4076
|
-
import { Fragment as Fragment11, useCallback as
|
|
4372
|
+
import { useTranslations as useTranslations45 } from "next-intl";
|
|
4373
|
+
import { Fragment as Fragment11, useCallback as useCallback10, useEffect as useEffect12, useMemo as useMemo9, useRef as useRef4, useState as useState22 } from "react";
|
|
4077
4374
|
|
|
4078
4375
|
// src/features/billing/stripe-price/data/stripe-price.transfer.ts
|
|
4079
4376
|
import { z as z4 } from "zod";
|
|
@@ -4093,9 +4390,9 @@ var seedSchema = z4.object({
|
|
|
4093
4390
|
isTrial: z4.boolean().optional(),
|
|
4094
4391
|
platformFeatures: z4.array(z4.string()).optional()
|
|
4095
4392
|
});
|
|
4096
|
-
function readOptional(
|
|
4393
|
+
function readOptional(read2) {
|
|
4097
4394
|
try {
|
|
4098
|
-
return
|
|
4395
|
+
return read2();
|
|
4099
4396
|
} catch {
|
|
4100
4397
|
return void 0;
|
|
4101
4398
|
}
|
|
@@ -4186,28 +4483,28 @@ __name(parseStripePriceTransferDocument, "parseStripePriceTransferDocument");
|
|
|
4186
4483
|
// src/features/billing/stripe-price/components/lists/PricesList.tsx
|
|
4187
4484
|
import { Fragment as Fragment12, jsx as jsx48, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
4188
4485
|
function PricesList({ productId, fullWidth }) {
|
|
4189
|
-
const t =
|
|
4486
|
+
const t = useTranslations45();
|
|
4190
4487
|
const { priceVersion } = usePriceContext();
|
|
4191
4488
|
const importInputRef = useRef4(null);
|
|
4192
|
-
const [seed, setSeed] =
|
|
4193
|
-
const [seedNonce, setSeedNonce] =
|
|
4489
|
+
const [seed, setSeed] = useState22(void 0);
|
|
4490
|
+
const [seedNonce, setSeedNonce] = useState22(0);
|
|
4194
4491
|
const data = useDataListRetriever({
|
|
4195
4492
|
retriever: /* @__PURE__ */ __name((params) => StripePriceService.listPrices({ ...params, productId }), "retriever"),
|
|
4196
4493
|
retrieverParams: {},
|
|
4197
4494
|
module: Modules.StripePrice
|
|
4198
4495
|
});
|
|
4199
|
-
|
|
4496
|
+
useEffect12(() => {
|
|
4200
4497
|
if (priceVersion > 0) void data.refresh();
|
|
4201
4498
|
}, [priceVersion]);
|
|
4202
|
-
const openSeededEditor =
|
|
4499
|
+
const openSeededEditor = useCallback10((next) => {
|
|
4203
4500
|
setSeed(next);
|
|
4204
4501
|
setSeedNonce((nonce) => nonce + 1);
|
|
4205
4502
|
}, []);
|
|
4206
|
-
const onClone =
|
|
4503
|
+
const onClone = useCallback10(
|
|
4207
4504
|
(price) => openSeededEditor(priceToSeed(price)),
|
|
4208
4505
|
[openSeededEditor]
|
|
4209
4506
|
);
|
|
4210
|
-
const onExport =
|
|
4507
|
+
const onExport = useCallback10((price) => {
|
|
4211
4508
|
const text = JSON.stringify(buildStripePriceTransferDocument(price), null, 2);
|
|
4212
4509
|
const blob = new Blob([text], { type: "application/json" });
|
|
4213
4510
|
const url = URL.createObjectURL(blob);
|
|
@@ -4218,7 +4515,7 @@ function PricesList({ productId, fullWidth }) {
|
|
|
4218
4515
|
URL.revokeObjectURL(url);
|
|
4219
4516
|
}, []);
|
|
4220
4517
|
const tableContext = useMemo9(() => ({ onClone, onExport }), [onClone, onExport]);
|
|
4221
|
-
const onImportFileChange =
|
|
4518
|
+
const onImportFileChange = useCallback10(
|
|
4222
4519
|
async (event) => {
|
|
4223
4520
|
const input = event.target;
|
|
4224
4521
|
const file = input.files?.[0];
|
|
@@ -4313,13 +4610,13 @@ function PricesList({ productId, fullWidth }) {
|
|
|
4313
4610
|
__name(PricesList, "PricesList");
|
|
4314
4611
|
|
|
4315
4612
|
// src/features/billing/stripe-product/components/containers/ProductContainer.tsx
|
|
4316
|
-
import { useTranslations as
|
|
4613
|
+
import { useTranslations as useTranslations47 } from "next-intl";
|
|
4317
4614
|
|
|
4318
4615
|
// src/features/billing/stripe-product/components/details/ProductDetails.tsx
|
|
4319
|
-
import { useTranslations as
|
|
4616
|
+
import { useTranslations as useTranslations46 } from "next-intl";
|
|
4320
4617
|
import { jsx as jsx49, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
4321
4618
|
function ProductDetails() {
|
|
4322
|
-
const t =
|
|
4619
|
+
const t = useTranslations46();
|
|
4323
4620
|
const { product } = useProductContext();
|
|
4324
4621
|
if (!product) return null;
|
|
4325
4622
|
return /* @__PURE__ */ jsxs41("div", { className: "flex w-full flex-col gap-y-4", children: [
|
|
@@ -4363,7 +4660,7 @@ __name(ProductDetails, "ProductDetails");
|
|
|
4363
4660
|
// src/features/billing/stripe-product/components/containers/ProductContainer.tsx
|
|
4364
4661
|
import { jsx as jsx50 } from "react/jsx-runtime";
|
|
4365
4662
|
function ProductContainer() {
|
|
4366
|
-
const t =
|
|
4663
|
+
const t = useTranslations47();
|
|
4367
4664
|
const { product } = useProductContext();
|
|
4368
4665
|
if (!product) return null;
|
|
4369
4666
|
return /* @__PURE__ */ jsx50(
|
|
@@ -4394,24 +4691,72 @@ function ProductContainer() {
|
|
|
4394
4691
|
}
|
|
4395
4692
|
__name(ProductContainer, "ProductContainer");
|
|
4396
4693
|
|
|
4694
|
+
// src/features/billing/stripe-product/components/containers/ProductsListContainer.tsx
|
|
4695
|
+
import { useTranslations as useTranslations50 } from "next-intl";
|
|
4696
|
+
|
|
4697
|
+
// src/features/billing/stripe-coupon/components/lists/CouponsList.tsx
|
|
4698
|
+
import { TicketPercentIcon } from "lucide-react";
|
|
4699
|
+
import { useTranslations as useTranslations48 } from "next-intl";
|
|
4700
|
+
import { useEffect as useEffect13 } from "react";
|
|
4701
|
+
import { jsx as jsx51 } from "react/jsx-runtime";
|
|
4702
|
+
function CouponsList({ fullWidth } = {}) {
|
|
4703
|
+
const t = useTranslations48();
|
|
4704
|
+
const { catalogueVersion } = useCouponContext();
|
|
4705
|
+
const data = useDataListRetriever({
|
|
4706
|
+
retriever: /* @__PURE__ */ __name((params) => StripeCouponService.listCoupons(params), "retriever"),
|
|
4707
|
+
retrieverParams: {},
|
|
4708
|
+
module: Modules.StripeCoupon
|
|
4709
|
+
});
|
|
4710
|
+
useEffect13(() => {
|
|
4711
|
+
if (catalogueVersion > 0) void data.refresh();
|
|
4712
|
+
}, [catalogueVersion]);
|
|
4713
|
+
const functions = [/* @__PURE__ */ jsx51(CouponEditor, {}, "create-coupon")];
|
|
4714
|
+
return /* @__PURE__ */ jsx51(
|
|
4715
|
+
ContentListTable,
|
|
4716
|
+
{
|
|
4717
|
+
data,
|
|
4718
|
+
fields: [
|
|
4719
|
+
"name" /* name */,
|
|
4720
|
+
"discount" /* discount */,
|
|
4721
|
+
"places" /* places */,
|
|
4722
|
+
"codes" /* codes */,
|
|
4723
|
+
"status" /* status */
|
|
4724
|
+
],
|
|
4725
|
+
tableGeneratorType: Modules.StripeCoupon,
|
|
4726
|
+
functions,
|
|
4727
|
+
fullWidth,
|
|
4728
|
+
title: t("billing.admin.coupons.title"),
|
|
4729
|
+
emptyState: /* @__PURE__ */ jsx51(
|
|
4730
|
+
EmptyState,
|
|
4731
|
+
{
|
|
4732
|
+
icon: TicketPercentIcon,
|
|
4733
|
+
title: t("billing.admin.coupons.empty.title"),
|
|
4734
|
+
description: t("billing.admin.coupons.empty.description")
|
|
4735
|
+
}
|
|
4736
|
+
)
|
|
4737
|
+
}
|
|
4738
|
+
);
|
|
4739
|
+
}
|
|
4740
|
+
__name(CouponsList, "CouponsList");
|
|
4741
|
+
|
|
4397
4742
|
// src/features/billing/stripe-product/components/lists/ProductsList.tsx
|
|
4398
4743
|
import { PackageIcon } from "lucide-react";
|
|
4399
|
-
import { useTranslations as
|
|
4400
|
-
import { useEffect as
|
|
4401
|
-
import { jsx as
|
|
4744
|
+
import { useTranslations as useTranslations49 } from "next-intl";
|
|
4745
|
+
import { useEffect as useEffect14 } from "react";
|
|
4746
|
+
import { jsx as jsx52 } from "react/jsx-runtime";
|
|
4402
4747
|
function ProductsList({ fullWidth } = {}) {
|
|
4403
|
-
const t =
|
|
4748
|
+
const t = useTranslations49();
|
|
4404
4749
|
const { catalogueVersion } = useProductContext();
|
|
4405
4750
|
const data = useDataListRetriever({
|
|
4406
4751
|
retriever: /* @__PURE__ */ __name((params) => StripeProductService.listProducts(params), "retriever"),
|
|
4407
4752
|
retrieverParams: {},
|
|
4408
4753
|
module: Modules.StripeProduct
|
|
4409
4754
|
});
|
|
4410
|
-
|
|
4755
|
+
useEffect14(() => {
|
|
4411
4756
|
if (catalogueVersion > 0) void data.refresh();
|
|
4412
4757
|
}, [catalogueVersion]);
|
|
4413
|
-
const functions = [/* @__PURE__ */
|
|
4414
|
-
return /* @__PURE__ */
|
|
4758
|
+
const functions = [/* @__PURE__ */ jsx52(ProductEditor, {}, "create-product")];
|
|
4759
|
+
return /* @__PURE__ */ jsx52(
|
|
4415
4760
|
ContentListTable,
|
|
4416
4761
|
{
|
|
4417
4762
|
data,
|
|
@@ -4420,7 +4765,7 @@ function ProductsList({ fullWidth } = {}) {
|
|
|
4420
4765
|
functions,
|
|
4421
4766
|
fullWidth,
|
|
4422
4767
|
title: t("billing.admin.products.title"),
|
|
4423
|
-
emptyState: /* @__PURE__ */
|
|
4768
|
+
emptyState: /* @__PURE__ */ jsx52(
|
|
4424
4769
|
EmptyState,
|
|
4425
4770
|
{
|
|
4426
4771
|
icon: PackageIcon,
|
|
@@ -4434,17 +4779,236 @@ function ProductsList({ fullWidth } = {}) {
|
|
|
4434
4779
|
__name(ProductsList, "ProductsList");
|
|
4435
4780
|
|
|
4436
4781
|
// src/features/billing/stripe-product/components/containers/ProductsListContainer.tsx
|
|
4437
|
-
import { jsx as
|
|
4782
|
+
import { jsx as jsx53 } from "react/jsx-runtime";
|
|
4438
4783
|
function ProductsListContainer() {
|
|
4439
|
-
|
|
4784
|
+
const t = useTranslations50();
|
|
4785
|
+
return /* @__PURE__ */ jsx53(ProductProvider, { children: /* @__PURE__ */ jsx53(CouponProvider, { publishChrome: false, children: /* @__PURE__ */ jsx53(
|
|
4786
|
+
RoundPageContainer,
|
|
4787
|
+
{
|
|
4788
|
+
module: Modules.StripeProduct,
|
|
4789
|
+
tabs: [
|
|
4790
|
+
{ sectionKey: "products", label: t("billing.admin.products.tabs.products"), content: /* @__PURE__ */ jsx53(ProductsList, {}) },
|
|
4791
|
+
{ sectionKey: "codes", label: t("billing.admin.products.tabs.codes"), content: /* @__PURE__ */ jsx53(CouponsList, {}) }
|
|
4792
|
+
]
|
|
4793
|
+
}
|
|
4794
|
+
) }) });
|
|
4440
4795
|
}
|
|
4441
4796
|
__name(ProductsListContainer, "ProductsListContainer");
|
|
4797
|
+
|
|
4798
|
+
// src/features/billing/stripe-coupon/components/containers/CouponContainer.tsx
|
|
4799
|
+
import { useTranslations as useTranslations54 } from "next-intl";
|
|
4800
|
+
|
|
4801
|
+
// src/features/billing/stripe-coupon/components/details/CouponDetails.tsx
|
|
4802
|
+
import { useTranslations as useTranslations51 } from "next-intl";
|
|
4803
|
+
import { jsx as jsx54, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
4804
|
+
function CouponDetails() {
|
|
4805
|
+
const t = useTranslations51();
|
|
4806
|
+
const fmt = useBillingFormatOptions();
|
|
4807
|
+
const { coupon } = useCouponContext();
|
|
4808
|
+
if (!coupon) return null;
|
|
4809
|
+
return /* @__PURE__ */ jsxs42("div", { className: "flex w-full flex-col gap-y-4", children: [
|
|
4810
|
+
/* @__PURE__ */ jsx54(SectionHeader, { children: t("billing.admin.products.tabs.details") }),
|
|
4811
|
+
/* @__PURE__ */ jsx54("p", { className: "text-muted-foreground text-xs/relaxed", children: t("billing.admin.coupons.fixed_notice") }),
|
|
4812
|
+
/* @__PURE__ */ jsxs42("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: [
|
|
4813
|
+
/* @__PURE__ */ jsx54(DetailField, { label: t("billing.admin.coupons.fields.name"), value: coupon.name }),
|
|
4814
|
+
/* @__PURE__ */ jsx54(
|
|
4815
|
+
DetailField,
|
|
4816
|
+
{
|
|
4817
|
+
label: t("billing.admin.coupons.fields.status"),
|
|
4818
|
+
value: coupon.valid ? /* @__PURE__ */ jsx54(Badge, { variant: "softGreen", children: t("billing.admin.coupons.status.active") }) : /* @__PURE__ */ jsx54(Badge, { variant: "softGray", children: t("billing.admin.coupons.status.ended") })
|
|
4819
|
+
}
|
|
4820
|
+
),
|
|
4821
|
+
/* @__PURE__ */ jsx54(
|
|
4822
|
+
DetailField,
|
|
4823
|
+
{
|
|
4824
|
+
label: t("billing.admin.coupons.fields.discount"),
|
|
4825
|
+
value: /* @__PURE__ */ jsx54("span", { className: "tabular-nums", children: formatCouponDiscount(coupon, t, fmt) })
|
|
4826
|
+
}
|
|
4827
|
+
),
|
|
4828
|
+
/* @__PURE__ */ jsx54(
|
|
4829
|
+
DetailField,
|
|
4830
|
+
{
|
|
4831
|
+
label: t("billing.admin.coupons.fields.places"),
|
|
4832
|
+
value: /* @__PURE__ */ jsx54("span", { className: "tabular-nums", children: coupon.maxRedemptions !== void 0 ? t("billing.admin.coupons.places.limited", {
|
|
4833
|
+
used: String(coupon.timesRedeemed),
|
|
4834
|
+
limit: String(coupon.maxRedemptions)
|
|
4835
|
+
}) : t("billing.admin.coupons.places.unlimited", { used: String(coupon.timesRedeemed) }) })
|
|
4836
|
+
}
|
|
4837
|
+
),
|
|
4838
|
+
/* @__PURE__ */ jsx54(
|
|
4839
|
+
DetailField,
|
|
4840
|
+
{
|
|
4841
|
+
label: t("billing.admin.coupons.fields.products"),
|
|
4842
|
+
value: coupon.stripeProductIds.length > 0 ? /* @__PURE__ */ jsx54("span", { className: "tabular-nums", children: coupon.stripeProductIds.join(", ") }) : t("billing.admin.coupons.all_products")
|
|
4843
|
+
}
|
|
4844
|
+
),
|
|
4845
|
+
/* @__PURE__ */ jsx54(
|
|
4846
|
+
DetailField,
|
|
4847
|
+
{
|
|
4848
|
+
label: t("billing.admin.coupons.fields.redeemBy"),
|
|
4849
|
+
value: /* @__PURE__ */ jsx54("span", { className: "tabular-nums", children: coupon.redeemBy ? formatDate(coupon.redeemBy, fmt) : "\u2014" })
|
|
4850
|
+
}
|
|
4851
|
+
),
|
|
4852
|
+
/* @__PURE__ */ jsx54(
|
|
4853
|
+
DetailField,
|
|
4854
|
+
{
|
|
4855
|
+
label: t("billing.admin.coupons.fields.stripeId"),
|
|
4856
|
+
value: /* @__PURE__ */ jsx54("span", { className: "tabular-nums", children: coupon.stripeCouponId })
|
|
4857
|
+
}
|
|
4858
|
+
)
|
|
4859
|
+
] })
|
|
4860
|
+
] });
|
|
4861
|
+
}
|
|
4862
|
+
__name(CouponDetails, "CouponDetails");
|
|
4863
|
+
|
|
4864
|
+
// src/features/billing/stripe-coupon/components/lists/CouponCodesList.tsx
|
|
4865
|
+
import { TicketIcon } from "lucide-react";
|
|
4866
|
+
import { useTranslations as useTranslations53 } from "next-intl";
|
|
4867
|
+
import { useEffect as useEffect15 } from "react";
|
|
4868
|
+
|
|
4869
|
+
// src/features/billing/stripe-coupon/components/forms/CouponCodeEditor.tsx
|
|
4870
|
+
import { zodResolver as zodResolver4 } from "@hookform/resolvers/zod";
|
|
4871
|
+
import { useTranslations as useTranslations52 } from "next-intl";
|
|
4872
|
+
import { useCallback as useCallback11, useMemo as useMemo10 } from "react";
|
|
4873
|
+
import { useForm as useForm4 } from "react-hook-form";
|
|
4874
|
+
import { v4 as v44 } from "uuid";
|
|
4875
|
+
import { z as z5 } from "zod";
|
|
4876
|
+
import { jsx as jsx55 } from "react/jsx-runtime";
|
|
4877
|
+
function CouponCodeEditorInternal({
|
|
4878
|
+
couponId,
|
|
4879
|
+
onSuccess,
|
|
4880
|
+
trigger,
|
|
4881
|
+
forceShow,
|
|
4882
|
+
onClose,
|
|
4883
|
+
dialogOpen,
|
|
4884
|
+
onDialogOpenChange
|
|
4885
|
+
}) {
|
|
4886
|
+
const t = useTranslations52();
|
|
4887
|
+
const router = useI18nRouter();
|
|
4888
|
+
const { createCode } = useCouponContext();
|
|
4889
|
+
const formSchema2 = useMemo10(() => z5.object({ id: z5.uuidv4() }).and(couponCodeSchema(t)), [t]);
|
|
4890
|
+
const getDefaultValues = useCallback11(() => ({ id: v44(), ...couponCodeDefaults() }), []);
|
|
4891
|
+
const form = useForm4({
|
|
4892
|
+
resolver: zodResolver4(formSchema2),
|
|
4893
|
+
defaultValues: getDefaultValues()
|
|
4894
|
+
});
|
|
4895
|
+
return /* @__PURE__ */ jsx55(
|
|
4896
|
+
EditorSheet,
|
|
4897
|
+
{
|
|
4898
|
+
form,
|
|
4899
|
+
entityType: t("billing.admin.coupons.codes.entity"),
|
|
4900
|
+
isEdit: false,
|
|
4901
|
+
module: Modules.StripeCouponCode,
|
|
4902
|
+
size: "md",
|
|
4903
|
+
onSuccess: onSuccess ?? (() => {
|
|
4904
|
+
}),
|
|
4905
|
+
onNavigate: (url) => router.push(url),
|
|
4906
|
+
onSubmit: async (values) => await createCode({ id: values.id, couponId, ...toCouponCodeInput(values) }),
|
|
4907
|
+
onReset: getDefaultValues,
|
|
4908
|
+
trigger,
|
|
4909
|
+
forceShow,
|
|
4910
|
+
onClose,
|
|
4911
|
+
dialogOpen,
|
|
4912
|
+
onDialogOpenChange,
|
|
4913
|
+
children: /* @__PURE__ */ jsx55(CouponCodeFields, { form })
|
|
4914
|
+
}
|
|
4915
|
+
);
|
|
4916
|
+
}
|
|
4917
|
+
__name(CouponCodeEditorInternal, "CouponCodeEditorInternal");
|
|
4918
|
+
function CouponCodeEditor(props) {
|
|
4919
|
+
return /* @__PURE__ */ jsx55(CouponCodeEditorInternal, { ...props });
|
|
4920
|
+
}
|
|
4921
|
+
__name(CouponCodeEditor, "CouponCodeEditor");
|
|
4922
|
+
|
|
4923
|
+
// src/features/billing/stripe-coupon/components/lists/CouponCodesList.tsx
|
|
4924
|
+
import { jsx as jsx56 } from "react/jsx-runtime";
|
|
4925
|
+
function CouponCodesList({ couponId }) {
|
|
4926
|
+
const t = useTranslations53();
|
|
4927
|
+
const { catalogueVersion } = useCouponContext();
|
|
4928
|
+
const data = useDataListRetriever({
|
|
4929
|
+
retriever: /* @__PURE__ */ __name((params) => StripeCouponCodeService.listByCoupon({ ...params, couponId }), "retriever"),
|
|
4930
|
+
retrieverParams: {},
|
|
4931
|
+
module: Modules.StripeCouponCode
|
|
4932
|
+
});
|
|
4933
|
+
useEffect15(() => {
|
|
4934
|
+
if (catalogueVersion > 0) void data.refresh();
|
|
4935
|
+
}, [catalogueVersion]);
|
|
4936
|
+
const functions = [/* @__PURE__ */ jsx56(CouponCodeEditor, { couponId }, "create-code")];
|
|
4937
|
+
return /* @__PURE__ */ jsx56(
|
|
4938
|
+
ContentListTable,
|
|
4939
|
+
{
|
|
4940
|
+
data,
|
|
4941
|
+
fields: [
|
|
4942
|
+
"code" /* code */,
|
|
4943
|
+
"places" /* places */,
|
|
4944
|
+
"expiresAt" /* expiresAt */,
|
|
4945
|
+
"automatic" /* automatic */,
|
|
4946
|
+
"status" /* status */,
|
|
4947
|
+
"actions" /* actions */
|
|
4948
|
+
],
|
|
4949
|
+
tableGeneratorType: Modules.StripeCouponCode,
|
|
4950
|
+
functions,
|
|
4951
|
+
title: t("billing.admin.coupons.codes.title"),
|
|
4952
|
+
emptyState: /* @__PURE__ */ jsx56(
|
|
4953
|
+
EmptyState,
|
|
4954
|
+
{
|
|
4955
|
+
icon: TicketIcon,
|
|
4956
|
+
title: t("billing.admin.coupons.codes.empty.title"),
|
|
4957
|
+
description: t("billing.admin.coupons.codes.empty.description")
|
|
4958
|
+
}
|
|
4959
|
+
)
|
|
4960
|
+
}
|
|
4961
|
+
);
|
|
4962
|
+
}
|
|
4963
|
+
__name(CouponCodesList, "CouponCodesList");
|
|
4964
|
+
|
|
4965
|
+
// src/features/billing/stripe-coupon/components/containers/CouponContainer.tsx
|
|
4966
|
+
import { jsx as jsx57 } from "react/jsx-runtime";
|
|
4967
|
+
function CouponContainer() {
|
|
4968
|
+
const t = useTranslations54();
|
|
4969
|
+
const { coupon } = useCouponContext();
|
|
4970
|
+
if (!coupon) return null;
|
|
4971
|
+
return /* @__PURE__ */ jsx57(
|
|
4972
|
+
RoundPageContainer,
|
|
4973
|
+
{
|
|
4974
|
+
module: Modules.StripeCoupon,
|
|
4975
|
+
id: coupon.id,
|
|
4976
|
+
tabs: [
|
|
4977
|
+
{
|
|
4978
|
+
sectionKey: "details",
|
|
4979
|
+
label: t("billing.admin.products.tabs.details"),
|
|
4980
|
+
content: /* @__PURE__ */ jsx57(CouponDetails, {})
|
|
4981
|
+
},
|
|
4982
|
+
{
|
|
4983
|
+
sectionKey: "codes",
|
|
4984
|
+
label: t("billing.admin.coupons.codes.title"),
|
|
4985
|
+
// No `fillHeight` and no `fullWidth` on the list: a ContentListTable in
|
|
4986
|
+
// a tab keeps its own bordered card and flows with the panel, exactly
|
|
4987
|
+
// as PricesList does inside ProductContainer. The codes list reads the
|
|
4988
|
+
// page's own CouponProvider, so no nested provider is needed.
|
|
4989
|
+
content: /* @__PURE__ */ jsx57(CouponCodesList, { couponId: coupon.id })
|
|
4990
|
+
}
|
|
4991
|
+
]
|
|
4992
|
+
}
|
|
4993
|
+
);
|
|
4994
|
+
}
|
|
4995
|
+
__name(CouponContainer, "CouponContainer");
|
|
4442
4996
|
export {
|
|
4443
4997
|
BillingAlertBanner,
|
|
4444
4998
|
BillingDashboardContainer,
|
|
4445
4999
|
BillingDetailModal,
|
|
4446
5000
|
BillingUsageSummaryCard,
|
|
4447
5001
|
CancelSubscriptionDialog,
|
|
5002
|
+
CouponCodeActions,
|
|
5003
|
+
CouponCodeEditor,
|
|
5004
|
+
CouponCodeFields,
|
|
5005
|
+
CouponCodesList,
|
|
5006
|
+
CouponContainer,
|
|
5007
|
+
CouponDetails,
|
|
5008
|
+
CouponEditor,
|
|
5009
|
+
CouponProvider,
|
|
5010
|
+
CouponRefresher,
|
|
5011
|
+
CouponsList,
|
|
4448
5012
|
CustomerInfoCard,
|
|
4449
5013
|
IntervalToggle,
|
|
4450
5014
|
InvoiceDetails,
|
|
@@ -4492,13 +5056,26 @@ export {
|
|
|
4492
5056
|
WizardStepPaymentMethod,
|
|
4493
5057
|
WizardStepPlanSelection,
|
|
4494
5058
|
WizardStepReview,
|
|
5059
|
+
applyDiscount,
|
|
5060
|
+
couponCodeDefaults,
|
|
5061
|
+
couponCodeSchema,
|
|
5062
|
+
couponCurrencyOptions,
|
|
4495
5063
|
formatCurrency,
|
|
4496
5064
|
formatDate,
|
|
4497
5065
|
formatFixed,
|
|
4498
5066
|
formatInterval,
|
|
4499
5067
|
isStripeConfigured,
|
|
5068
|
+
optionalDate,
|
|
5069
|
+
optionalDecimal,
|
|
5070
|
+
optionalPositiveInt,
|
|
5071
|
+
parseOptionalDecimal,
|
|
5072
|
+
parseOptionalPositiveInt,
|
|
4500
5073
|
priceLabel,
|
|
5074
|
+
promotionCodeDiscount,
|
|
5075
|
+
subscriptionDiscount,
|
|
5076
|
+
toCouponCodeInput,
|
|
4501
5077
|
useBillingFormatOptions,
|
|
5078
|
+
useCouponContext,
|
|
4502
5079
|
usePriceContext,
|
|
4503
5080
|
useProductContext
|
|
4504
5081
|
};
|