@carlonicora/nextjs-jsonapi 1.27.0 → 1.29.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-5NYZBYPY.js → BlockNoteEditor-YBVEOPV4.js} +13 -13
- package/dist/{BlockNoteEditor-5NYZBYPY.js.map → BlockNoteEditor-YBVEOPV4.js.map} +1 -1
- package/dist/{BlockNoteEditor-B6K7OEN6.mjs → BlockNoteEditor-ZM4YPXHO.mjs} +3 -3
- package/dist/billing/index.d.mts +48 -18
- package/dist/billing/index.d.ts +48 -18
- package/dist/billing/index.js +1251 -1076
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +1384 -1209
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-ZR26XYRO.js → chunk-3X7EEFMN.js} +488 -431
- package/dist/chunk-3X7EEFMN.js.map +1 -0
- package/dist/{chunk-NAB52T2P.mjs → chunk-DU64WMZD.mjs} +14 -3
- package/dist/chunk-DU64WMZD.mjs.map +1 -0
- package/dist/{chunk-2VZZFQP4.js → chunk-J22NEVSK.js} +29 -18
- package/dist/chunk-J22NEVSK.js.map +1 -0
- package/dist/{chunk-RU4GQIB6.mjs → chunk-UCD5CUE4.mjs} +81 -24
- package/dist/chunk-UCD5CUE4.mjs.map +1 -0
- package/dist/client/index.d.mts +14 -5
- package/dist/client/index.d.ts +14 -5
- package/dist/client/index.js +5 -3
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +4 -2
- package/dist/components/index.d.mts +2 -2
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.js +3 -3
- package/dist/components/index.mjs +2 -2
- package/dist/{config-B4pZpLT9.d.ts → config-CHwoRDOp.d.ts} +1 -1
- package/dist/{config-DT1K-t6I.d.mts → config-DiWyJzk9.d.mts} +1 -1
- package/dist/{content.interface-B2Ldg0vg.d.mts → content.interface-BSpowEiW.d.mts} +1 -1
- package/dist/{content.interface-D8NHv3DX.d.ts → content.interface-DFQ7mkpL.d.ts} +1 -1
- package/dist/contexts/index.d.mts +2 -2
- package/dist/contexts/index.d.ts +2 -2
- package/dist/contexts/index.js +3 -3
- package/dist/contexts/index.mjs +2 -2
- package/dist/core/index.d.mts +43 -39
- package/dist/core/index.d.ts +43 -39
- package/dist/core/index.js +2 -2
- package/dist/core/index.mjs +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{notification.interface-H0L9WBge.d.ts → notification.interface-CmKmObIU.d.ts} +1 -0
- package/dist/{notification.interface-DEn-Yp_b.d.mts → notification.interface-D5MbtfZK.d.mts} +1 -0
- package/dist/{s3.service-BNytYanU.d.mts → s3.service-BMT7W6KS.d.mts} +19 -19
- package/dist/{s3.service-C7f_Ygz5.d.ts → s3.service-DsXo9nop.d.ts} +19 -19
- package/dist/server/index.d.mts +3 -3
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +1 -1
- package/dist/{stripe-subscription.interface-CNTsrbAx.d.ts → stripe-subscription.interface-BaZUngWe.d.ts} +2 -0
- package/dist/{stripe-subscription.interface-CFtupgYh.d.mts → stripe-subscription.interface-Cm_It1fz.d.mts} +2 -0
- package/dist/{useSocket-BcnThTD0.d.mts → useSocket-DUqGoPya.d.mts} +1 -1
- package/dist/{useSocket-QZTOCzRF.d.ts → useSocket-QuHa0ZmO.d.ts} +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +1 -0
- package/src/components/forms/FormSelect.tsx +2 -1
- package/src/features/auth/data/auth.ts +0 -2
- package/src/features/billing/components/containers/BillingDashboardContainer.tsx +60 -3
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodEditor.tsx +12 -152
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodForm.tsx +168 -0
- package/src/features/billing/stripe-customer/components/forms/index.ts +1 -0
- package/src/features/billing/stripe-price/components/forms/PriceEditor.tsx +30 -2
- package/src/features/billing/stripe-price/data/stripe-price.interface.ts +2 -0
- package/src/features/billing/stripe-price/data/stripe-price.ts +10 -0
- package/src/features/billing/stripe-product/components/forms/ProductEditor.tsx +2 -2
- package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +24 -235
- package/src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx +7 -18
- package/src/features/billing/stripe-subscription/components/forms/index.ts +0 -1
- package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +10 -1
- package/src/features/billing/stripe-subscription/components/widgets/IntervalToggle.tsx +28 -0
- package/src/features/billing/stripe-subscription/components/widgets/ProductPricingList.tsx +128 -0
- package/src/features/billing/stripe-subscription/components/widgets/ProductPricingRow.tsx +54 -0
- package/src/features/billing/stripe-subscription/components/widgets/SubscriptionConfirmation.tsx +68 -0
- package/src/features/billing/stripe-subscription/components/widgets/index.ts +4 -1
- package/src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx +114 -0
- package/src/features/billing/stripe-subscription/components/wizards/WizardProgressIndicator.tsx +66 -0
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx +32 -0
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepPlanSelection.tsx +103 -0
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +133 -0
- package/src/features/billing/stripe-subscription/components/wizards/index.ts +6 -0
- package/src/features/billing/stripe-subscription/hooks/useSubscriptionWizard.ts +217 -0
- package/src/features/billing/stripe-subscription/index.ts +3 -2
- package/src/features/company/components/details/TokenStatusIndicator.tsx +19 -9
- package/src/features/company/data/company.interface.ts +2 -0
- package/src/features/company/data/company.ts +7 -0
- package/src/features/company/hooks/index.ts +1 -0
- package/src/features/company/hooks/useSubscriptionStatus.ts +71 -0
- package/src/features/user/components/forms/UserEditor.tsx +1 -1
- package/src/features/user/components/lists/AdminUsersList.tsx +1 -1
- package/src/features/user/contexts/CurrentUserContext.tsx +1 -1
- package/src/features/user/data/user.ts +1 -1
- package/dist/chunk-2VZZFQP4.js.map +0 -1
- package/dist/chunk-NAB52T2P.mjs.map +0 -1
- package/dist/chunk-RU4GQIB6.mjs.map +0 -1
- package/dist/chunk-ZR26XYRO.js.map +0 -1
- package/src/features/billing/stripe-subscription/components/forms/SubscriptionEditor.tsx +0 -331
- package/src/features/billing/stripe-subscription/components/widgets/PricingCardsGrid.tsx +0 -110
- /package/dist/{BlockNoteEditor-B6K7OEN6.mjs.map → BlockNoteEditor-ZM4YPXHO.mjs.map} +0 -0
package/dist/billing/index.mjs
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
AlertDialogFooter,
|
|
11
11
|
AlertDialogHeader,
|
|
12
12
|
AlertDialogTitle,
|
|
13
|
-
AlertTitle,
|
|
14
13
|
Badge,
|
|
15
14
|
Button,
|
|
16
15
|
Card,
|
|
@@ -48,7 +47,7 @@ import {
|
|
|
48
47
|
TabsList,
|
|
49
48
|
TabsTrigger,
|
|
50
49
|
useCurrentUserContext
|
|
51
|
-
} from "../chunk-
|
|
50
|
+
} from "../chunk-UCD5CUE4.mjs";
|
|
52
51
|
import {
|
|
53
52
|
getRoleId,
|
|
54
53
|
getStripePublishableKey
|
|
@@ -61,7 +60,7 @@ import {
|
|
|
61
60
|
StripeSubscriptionService,
|
|
62
61
|
StripeUsageService,
|
|
63
62
|
cn
|
|
64
|
-
} from "../chunk-
|
|
63
|
+
} from "../chunk-DU64WMZD.mjs";
|
|
65
64
|
import "../chunk-AUXK7QSA.mjs";
|
|
66
65
|
import "../chunk-C7C7VY4F.mjs";
|
|
67
66
|
import "../chunk-U4MTVHOC.mjs";
|
|
@@ -533,18 +532,19 @@ function BillingUsageSummaryCard({
|
|
|
533
532
|
__name(BillingUsageSummaryCard, "BillingUsageSummaryCard");
|
|
534
533
|
|
|
535
534
|
// src/features/billing/components/containers/BillingDashboardContainer.tsx
|
|
536
|
-
import { CreditCard as CreditCard4, Loader2 as
|
|
537
|
-
import {
|
|
535
|
+
import { CreditCard as CreditCard4, Loader2 as Loader22, Wallet as Wallet2 } from "lucide-react";
|
|
536
|
+
import { useSearchParams } from "next/navigation";
|
|
537
|
+
import { useCallback as useCallback4, useEffect as useEffect9, useMemo as useMemo3, useState as useState13 } from "react";
|
|
538
538
|
|
|
539
539
|
// src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx
|
|
540
540
|
import { CreditCard as CreditCard2 } from "lucide-react";
|
|
541
541
|
import { useEffect as useEffect3, useState as useState4 } from "react";
|
|
542
542
|
|
|
543
|
-
// src/features/billing/stripe-customer/components/forms/
|
|
543
|
+
// src/features/billing/stripe-customer/components/forms/PaymentMethodForm.tsx
|
|
544
544
|
import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
|
|
545
545
|
import { useEffect, useState as useState2 } from "react";
|
|
546
546
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
547
|
-
function
|
|
547
|
+
function PaymentMethodForm({ onSuccess, onCancel, isLoading = false }) {
|
|
548
548
|
const stripe = useStripe();
|
|
549
549
|
const elements = useElements();
|
|
550
550
|
const [setupIntent, setSetupIntent] = useState2(null);
|
|
@@ -559,16 +559,14 @@ function PaymentMethodEditor({ open, onOpenChange, onSuccess }) {
|
|
|
559
559
|
const intent = await StripeCustomerService.createSetupIntent();
|
|
560
560
|
setSetupIntent(intent);
|
|
561
561
|
} catch (err) {
|
|
562
|
-
console.error("[
|
|
562
|
+
console.error("[PaymentMethodForm] Failed to create setup intent:", err);
|
|
563
563
|
setError("Failed to initialize payment form. Please try again.");
|
|
564
564
|
} finally {
|
|
565
565
|
setLoading(false);
|
|
566
566
|
}
|
|
567
567
|
}, "fetchSetupIntent");
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
}
|
|
571
|
-
}, [open]);
|
|
568
|
+
fetchSetupIntent();
|
|
569
|
+
}, []);
|
|
572
570
|
const handleSubmit = /* @__PURE__ */ __name(async (e) => {
|
|
573
571
|
e.preventDefault();
|
|
574
572
|
if (!stripe || !elements || !setupIntent) {
|
|
@@ -590,7 +588,7 @@ function PaymentMethodEditor({ open, onOpenChange, onSuccess }) {
|
|
|
590
588
|
}
|
|
591
589
|
);
|
|
592
590
|
if (stripeError) {
|
|
593
|
-
console.error("[
|
|
591
|
+
console.error("[PaymentMethodForm] Stripe error:", stripeError);
|
|
594
592
|
setError(stripeError.message || "Failed to add payment method. Please check your card details.");
|
|
595
593
|
setIsSubmitting(false);
|
|
596
594
|
return;
|
|
@@ -601,58 +599,81 @@ function PaymentMethodEditor({ open, onOpenChange, onSuccess }) {
|
|
|
601
599
|
});
|
|
602
600
|
}
|
|
603
601
|
onSuccess();
|
|
604
|
-
onOpenChange(false);
|
|
605
602
|
} catch (err) {
|
|
606
|
-
console.error("[
|
|
603
|
+
console.error("[PaymentMethodForm] Error:", err);
|
|
607
604
|
setError(err.message || "An unexpected error occurred. Please try again.");
|
|
608
605
|
} finally {
|
|
609
606
|
setIsSubmitting(false);
|
|
610
607
|
}
|
|
611
608
|
}, "handleSubmit");
|
|
612
|
-
|
|
613
|
-
/* @__PURE__ */
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}
|
|
630
|
-
},
|
|
631
|
-
invalid: {
|
|
632
|
-
color: "#9e2146"
|
|
609
|
+
if (loading) {
|
|
610
|
+
return /* @__PURE__ */ jsx6("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx6("p", { className: "text-muted-foreground", children: "Loading payment form..." }) });
|
|
611
|
+
}
|
|
612
|
+
if (!setupIntent && error) {
|
|
613
|
+
return /* @__PURE__ */ jsx6(Alert, { variant: "destructive", className: "bg-red-50 border-red-200", children: /* @__PURE__ */ jsx6(AlertDescription, { children: error }) });
|
|
614
|
+
}
|
|
615
|
+
return /* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
616
|
+
/* @__PURE__ */ jsx6("div", { className: "rounded-md border border-gray-300 p-3", children: /* @__PURE__ */ jsx6(
|
|
617
|
+
CardElement,
|
|
618
|
+
{
|
|
619
|
+
options: {
|
|
620
|
+
style: {
|
|
621
|
+
base: {
|
|
622
|
+
fontSize: "16px",
|
|
623
|
+
color: "#424770",
|
|
624
|
+
"::placeholder": {
|
|
625
|
+
color: "#aab7c4"
|
|
633
626
|
}
|
|
627
|
+
},
|
|
628
|
+
invalid: {
|
|
629
|
+
color: "#9e2146"
|
|
634
630
|
}
|
|
635
631
|
}
|
|
636
632
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
error && /* @__PURE__ */ jsx6(Alert, { variant: "destructive", className: "bg-red-50 border-red-200", children: /* @__PURE__ */ jsx6(AlertDescription, { children: error }) }),
|
|
650
|
-
/* @__PURE__ */ jsxs6("div", { className: "flex justify-end gap-x-2", children: [
|
|
651
|
-
/* @__PURE__ */ jsx6(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isSubmitting, children: "Cancel" }),
|
|
652
|
-
/* @__PURE__ */ jsx6(Button, { type: "submit", disabled: !stripe || isSubmitting, children: isSubmitting ? "Processing..." : "Add Card" })
|
|
653
|
-
] })
|
|
633
|
+
}
|
|
634
|
+
) }),
|
|
635
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-x-2", children: [
|
|
636
|
+
/* @__PURE__ */ jsx6(
|
|
637
|
+
Checkbox,
|
|
638
|
+
{
|
|
639
|
+
id: "setAsDefault",
|
|
640
|
+
checked: setAsDefault,
|
|
641
|
+
onCheckedChange: (checked) => setSetAsDefault(!!checked)
|
|
642
|
+
}
|
|
643
|
+
),
|
|
644
|
+
/* @__PURE__ */ jsx6(Label, { htmlFor: "setAsDefault", className: "text-sm font-normal", children: "Set as default payment method" })
|
|
654
645
|
] }),
|
|
655
|
-
|
|
646
|
+
error && /* @__PURE__ */ jsx6(Alert, { variant: "destructive", className: "bg-red-50 border-red-200", children: /* @__PURE__ */ jsx6(AlertDescription, { children: error }) }),
|
|
647
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex justify-end gap-x-2", children: [
|
|
648
|
+
/* @__PURE__ */ jsx6(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: isSubmitting || isLoading, children: "Cancel" }),
|
|
649
|
+
/* @__PURE__ */ jsx6(Button, { type: "submit", disabled: !stripe || isSubmitting || isLoading, children: isSubmitting ? "Processing..." : "Add Card" })
|
|
650
|
+
] })
|
|
651
|
+
] });
|
|
652
|
+
}
|
|
653
|
+
__name(PaymentMethodForm, "PaymentMethodForm");
|
|
654
|
+
|
|
655
|
+
// src/features/billing/stripe-customer/components/forms/PaymentMethodEditor.tsx
|
|
656
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
657
|
+
function PaymentMethodEditor({ open, onOpenChange, onSuccess }) {
|
|
658
|
+
const handleSuccess = /* @__PURE__ */ __name(() => {
|
|
659
|
+
onSuccess();
|
|
660
|
+
onOpenChange(false);
|
|
661
|
+
}, "handleSuccess");
|
|
662
|
+
const handleCancel = /* @__PURE__ */ __name(() => {
|
|
663
|
+
onOpenChange(false);
|
|
664
|
+
}, "handleCancel");
|
|
665
|
+
return /* @__PURE__ */ jsx7(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs7(DialogContent, { className: "max-w-md", children: [
|
|
666
|
+
/* @__PURE__ */ jsxs7(DialogHeader, { children: [
|
|
667
|
+
/* @__PURE__ */ jsx7(DialogTitle, { children: "Add Payment Method" }),
|
|
668
|
+
/* @__PURE__ */ jsx7(DialogDescription, { children: "Add a new payment method to your account. Your card information is securely processed by Stripe." })
|
|
669
|
+
] }),
|
|
670
|
+
open && /* @__PURE__ */ jsx7(
|
|
671
|
+
PaymentMethodForm,
|
|
672
|
+
{
|
|
673
|
+
onSuccess: handleSuccess,
|
|
674
|
+
onCancel: handleCancel
|
|
675
|
+
}
|
|
676
|
+
)
|
|
656
677
|
] }) });
|
|
657
678
|
}
|
|
658
679
|
__name(PaymentMethodEditor, "PaymentMethodEditor");
|
|
@@ -660,7 +681,7 @@ __name(PaymentMethodEditor, "PaymentMethodEditor");
|
|
|
660
681
|
// src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx
|
|
661
682
|
import { MoreVertical } from "lucide-react";
|
|
662
683
|
import { useEffect as useEffect2, useState as useState3 } from "react";
|
|
663
|
-
import { Fragment, jsx as
|
|
684
|
+
import { Fragment, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
664
685
|
var brandIcons = {
|
|
665
686
|
visa: "\u{1F4B3}",
|
|
666
687
|
mastercard: "\u{1F4B3}",
|
|
@@ -710,28 +731,28 @@ function PaymentMethodCard({ paymentMethod, onUpdate }) {
|
|
|
710
731
|
setLoading(false);
|
|
711
732
|
}
|
|
712
733
|
}, "handleRemove");
|
|
713
|
-
return /* @__PURE__ */
|
|
714
|
-
/* @__PURE__ */
|
|
715
|
-
isDefault && /* @__PURE__ */
|
|
716
|
-
/* @__PURE__ */
|
|
717
|
-
/* @__PURE__ */
|
|
718
|
-
/* @__PURE__ */
|
|
719
|
-
/* @__PURE__ */
|
|
734
|
+
return /* @__PURE__ */ jsxs8(Fragment, { children: [
|
|
735
|
+
/* @__PURE__ */ jsxs8(Card, { className: "relative", children: [
|
|
736
|
+
isDefault && /* @__PURE__ */ jsx8(Badge, { className: "absolute right-2 top-2 bg-green-100 text-green-800 hover:bg-green-100", children: "Default" }),
|
|
737
|
+
/* @__PURE__ */ jsxs8(CardHeader, { className: "flex flex-row items-center justify-between pb-2", children: [
|
|
738
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-x-2", children: [
|
|
739
|
+
/* @__PURE__ */ jsx8("span", { className: "text-2xl", children: brandIcon }),
|
|
740
|
+
/* @__PURE__ */ jsx8("span", { className: "text-sm font-medium capitalize", children: brand })
|
|
720
741
|
] }),
|
|
721
|
-
/* @__PURE__ */
|
|
722
|
-
/* @__PURE__ */
|
|
723
|
-
/* @__PURE__ */
|
|
724
|
-
!isDefault && /* @__PURE__ */
|
|
725
|
-
/* @__PURE__ */
|
|
742
|
+
/* @__PURE__ */ jsxs8(DropdownMenu, { children: [
|
|
743
|
+
/* @__PURE__ */ jsx8(DropdownMenuTrigger, { children: /* @__PURE__ */ jsx8(Button, { render: /* @__PURE__ */ jsx8("div", {}), nativeButton: false, variant: "ghost", size: "sm", disabled: loading, className: "h-8 w-8 p-0", children: /* @__PURE__ */ jsx8(MoreVertical, { className: "h-4 w-4" }) }) }),
|
|
744
|
+
/* @__PURE__ */ jsxs8(DropdownMenuContent, { align: "end", children: [
|
|
745
|
+
!isDefault && /* @__PURE__ */ jsx8(DropdownMenuItem, { onClick: handleSetDefault, disabled: loading, children: "Set as Default" }),
|
|
746
|
+
/* @__PURE__ */ jsx8(DropdownMenuItem, { onClick: () => setShowRemoveDialog(true), disabled: loading, className: "text-red-600", children: "Remove" })
|
|
726
747
|
] })
|
|
727
748
|
] })
|
|
728
749
|
] }),
|
|
729
|
-
/* @__PURE__ */
|
|
730
|
-
/* @__PURE__ */
|
|
750
|
+
/* @__PURE__ */ jsx8(CardContent, { children: /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-y-1", children: [
|
|
751
|
+
/* @__PURE__ */ jsxs8("p", { className: "text-lg font-semibold", children: [
|
|
731
752
|
"\u2022\u2022\u2022\u2022 ",
|
|
732
753
|
last4
|
|
733
754
|
] }),
|
|
734
|
-
/* @__PURE__ */
|
|
755
|
+
/* @__PURE__ */ jsxs8("p", { className: "text-sm text-muted-foreground", children: [
|
|
735
756
|
"Expires ",
|
|
736
757
|
String(expMonth).padStart(2, "0"),
|
|
737
758
|
"/",
|
|
@@ -739,17 +760,17 @@ function PaymentMethodCard({ paymentMethod, onUpdate }) {
|
|
|
739
760
|
] })
|
|
740
761
|
] }) })
|
|
741
762
|
] }),
|
|
742
|
-
/* @__PURE__ */
|
|
743
|
-
/* @__PURE__ */
|
|
744
|
-
/* @__PURE__ */
|
|
745
|
-
/* @__PURE__ */
|
|
763
|
+
/* @__PURE__ */ jsx8(AlertDialog, { open: showRemoveDialog, onOpenChange: setShowRemoveDialog, children: /* @__PURE__ */ jsxs8(AlertDialogContent, { children: [
|
|
764
|
+
/* @__PURE__ */ jsxs8(AlertDialogHeader, { children: [
|
|
765
|
+
/* @__PURE__ */ jsx8(AlertDialogTitle, { children: "Remove Payment Method" }),
|
|
766
|
+
/* @__PURE__ */ jsxs8(AlertDialogDescription, { children: [
|
|
746
767
|
"Are you sure you want to remove this payment method? This action cannot be undone.",
|
|
747
768
|
isDefault && " This is your default payment method."
|
|
748
769
|
] })
|
|
749
770
|
] }),
|
|
750
|
-
/* @__PURE__ */
|
|
751
|
-
/* @__PURE__ */
|
|
752
|
-
/* @__PURE__ */
|
|
771
|
+
/* @__PURE__ */ jsxs8(AlertDialogFooter, { children: [
|
|
772
|
+
/* @__PURE__ */ jsx8(AlertDialogCancel, { disabled: loading, children: "Cancel" }),
|
|
773
|
+
/* @__PURE__ */ jsx8(AlertDialogAction, { onClick: handleRemove, disabled: loading, className: "bg-red-600 hover:bg-red-700", children: loading ? "Removing..." : "Remove" })
|
|
753
774
|
] })
|
|
754
775
|
] }) })
|
|
755
776
|
] });
|
|
@@ -757,14 +778,14 @@ function PaymentMethodCard({ paymentMethod, onUpdate }) {
|
|
|
757
778
|
__name(PaymentMethodCard, "PaymentMethodCard");
|
|
758
779
|
|
|
759
780
|
// src/features/billing/stripe-customer/components/lists/PaymentMethodsList.tsx
|
|
760
|
-
import { jsx as
|
|
781
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
761
782
|
function PaymentMethodsList({ paymentMethods, onUpdate }) {
|
|
762
|
-
return /* @__PURE__ */
|
|
783
|
+
return /* @__PURE__ */ jsx9("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3", children: paymentMethods.map((paymentMethod) => /* @__PURE__ */ jsx9(PaymentMethodCard, { paymentMethod, onUpdate }, paymentMethod.id)) });
|
|
763
784
|
}
|
|
764
785
|
__name(PaymentMethodsList, "PaymentMethodsList");
|
|
765
786
|
|
|
766
787
|
// src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx
|
|
767
|
-
import { jsx as
|
|
788
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
768
789
|
function PaymentMethodsContainer() {
|
|
769
790
|
const [paymentMethods, setPaymentMethods] = useState4([]);
|
|
770
791
|
const [loading, setLoading] = useState4(true);
|
|
@@ -784,26 +805,26 @@ function PaymentMethodsContainer() {
|
|
|
784
805
|
loadPaymentMethods();
|
|
785
806
|
}, []);
|
|
786
807
|
if (loading) {
|
|
787
|
-
return /* @__PURE__ */
|
|
808
|
+
return /* @__PURE__ */ jsx10("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx10("p", { className: "text-muted-foreground", children: "Loading payment methods..." }) });
|
|
788
809
|
}
|
|
789
|
-
return /* @__PURE__ */
|
|
790
|
-
/* @__PURE__ */
|
|
791
|
-
/* @__PURE__ */
|
|
792
|
-
/* @__PURE__ */
|
|
793
|
-
/* @__PURE__ */
|
|
810
|
+
return /* @__PURE__ */ jsxs9("div", { className: "flex w-full flex-col gap-y-6", children: [
|
|
811
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
|
|
812
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-x-3", children: [
|
|
813
|
+
/* @__PURE__ */ jsx10(CreditCard2, { className: "h-8 w-8" }),
|
|
814
|
+
/* @__PURE__ */ jsx10("h1", { className: "text-3xl font-bold", children: "Payment Methods" })
|
|
794
815
|
] }),
|
|
795
|
-
/* @__PURE__ */
|
|
816
|
+
/* @__PURE__ */ jsx10(Button, { onClick: () => setShowAddPaymentMethod(true), children: "Add Payment Method" })
|
|
796
817
|
] }),
|
|
797
|
-
paymentMethods.length === 0 && /* @__PURE__ */
|
|
798
|
-
/* @__PURE__ */
|
|
799
|
-
/* @__PURE__ */
|
|
800
|
-
/* @__PURE__ */
|
|
801
|
-
/* @__PURE__ */
|
|
802
|
-
/* @__PURE__ */
|
|
818
|
+
paymentMethods.length === 0 && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center justify-center gap-y-4 rounded-lg border-2 border-dashed border-gray-300 bg-muted/50 p-12", children: [
|
|
819
|
+
/* @__PURE__ */ jsx10(CreditCard2, { className: "h-16 w-16 text-muted-foreground" }),
|
|
820
|
+
/* @__PURE__ */ jsxs9("div", { className: "text-center", children: [
|
|
821
|
+
/* @__PURE__ */ jsx10("h3", { className: "mb-2 text-xl font-semibold", children: "No payment methods" }),
|
|
822
|
+
/* @__PURE__ */ jsx10("p", { className: "mb-4 text-muted-foreground", children: "Add a payment method to enable subscriptions and secure checkout." }),
|
|
823
|
+
/* @__PURE__ */ jsx10(Button, { onClick: () => setShowAddPaymentMethod(true), children: "Add Your First Card" })
|
|
803
824
|
] })
|
|
804
825
|
] }),
|
|
805
|
-
paymentMethods.length > 0 && /* @__PURE__ */
|
|
806
|
-
showAddPaymentMethod && /* @__PURE__ */
|
|
826
|
+
paymentMethods.length > 0 && /* @__PURE__ */ jsx10(PaymentMethodsList, { paymentMethods, onUpdate: loadPaymentMethods }),
|
|
827
|
+
showAddPaymentMethod && /* @__PURE__ */ jsx10(
|
|
807
828
|
PaymentMethodEditor,
|
|
808
829
|
{
|
|
809
830
|
open: showAddPaymentMethod,
|
|
@@ -872,7 +893,7 @@ __name(formatDate3, "formatDate");
|
|
|
872
893
|
import { Download, ExternalLink as ExternalLink2, RefreshCw } from "lucide-react";
|
|
873
894
|
|
|
874
895
|
// src/features/billing/stripe-invoice/components/widgets/InvoiceStatusBadge.tsx
|
|
875
|
-
import { jsx as
|
|
896
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
876
897
|
var statusConfig = {
|
|
877
898
|
["draft" /* DRAFT */]: {
|
|
878
899
|
label: "Draft",
|
|
@@ -897,12 +918,12 @@ var statusConfig = {
|
|
|
897
918
|
};
|
|
898
919
|
function InvoiceStatusBadge({ status }) {
|
|
899
920
|
const config = statusConfig[status] || statusConfig["draft" /* DRAFT */];
|
|
900
|
-
return /* @__PURE__ */
|
|
921
|
+
return /* @__PURE__ */ jsx11("span", { className: `${config.color} text-xs px-2 py-1 rounded-full font-medium`, children: config.label });
|
|
901
922
|
}
|
|
902
923
|
__name(InvoiceStatusBadge, "InvoiceStatusBadge");
|
|
903
924
|
|
|
904
925
|
// src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx
|
|
905
|
-
import { jsx as
|
|
926
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
906
927
|
function InvoiceDetails({ invoice, open, onOpenChange, onInvoiceChange }) {
|
|
907
928
|
const handleDownloadPDF = /* @__PURE__ */ __name(() => {
|
|
908
929
|
if (invoice.stripePdfUrl) {
|
|
@@ -923,87 +944,87 @@ function InvoiceDetails({ invoice, open, onOpenChange, onInvoiceChange }) {
|
|
|
923
944
|
return invoice.stripeInvoiceId.slice(-8);
|
|
924
945
|
}, "getInvoiceNumber");
|
|
925
946
|
const productName = invoice.subscription?.price?.product?.name || "Subscription";
|
|
926
|
-
return /* @__PURE__ */
|
|
927
|
-
/* @__PURE__ */
|
|
928
|
-
/* @__PURE__ */
|
|
947
|
+
return /* @__PURE__ */ jsx12(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs10(DialogContent, { className: "max-w-2xl", children: [
|
|
948
|
+
/* @__PURE__ */ jsxs10(DialogHeader, { children: [
|
|
949
|
+
/* @__PURE__ */ jsxs10(DialogTitle, { children: [
|
|
929
950
|
"Invoice ",
|
|
930
951
|
getInvoiceNumber()
|
|
931
952
|
] }),
|
|
932
|
-
/* @__PURE__ */
|
|
953
|
+
/* @__PURE__ */ jsx12(DialogDescription, { children: formatDate3(invoice.periodStart) })
|
|
933
954
|
] }),
|
|
934
|
-
/* @__PURE__ */
|
|
935
|
-
/* @__PURE__ */
|
|
936
|
-
/* @__PURE__ */
|
|
937
|
-
/* @__PURE__ */
|
|
955
|
+
/* @__PURE__ */ jsxs10("div", { className: "space-y-6", children: [
|
|
956
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-x-3", children: [
|
|
957
|
+
/* @__PURE__ */ jsx12("span", { className: "text-sm font-medium text-muted-foreground", children: "Status:" }),
|
|
958
|
+
/* @__PURE__ */ jsx12(InvoiceStatusBadge, { status: invoice.status })
|
|
938
959
|
] }),
|
|
939
|
-
/* @__PURE__ */
|
|
940
|
-
/* @__PURE__ */
|
|
941
|
-
/* @__PURE__ */
|
|
942
|
-
/* @__PURE__ */
|
|
960
|
+
/* @__PURE__ */ jsxs10("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
961
|
+
/* @__PURE__ */ jsxs10("div", { children: [
|
|
962
|
+
/* @__PURE__ */ jsx12("span", { className: "text-sm font-medium text-muted-foreground", children: "Billing Period:" }),
|
|
963
|
+
/* @__PURE__ */ jsxs10("p", { className: "font-medium", children: [
|
|
943
964
|
formatDate3(invoice.periodStart),
|
|
944
965
|
" - ",
|
|
945
966
|
formatDate3(invoice.periodEnd)
|
|
946
967
|
] })
|
|
947
968
|
] }),
|
|
948
|
-
invoice.dueDate && /* @__PURE__ */
|
|
949
|
-
/* @__PURE__ */
|
|
950
|
-
/* @__PURE__ */
|
|
969
|
+
invoice.dueDate && /* @__PURE__ */ jsxs10("div", { children: [
|
|
970
|
+
/* @__PURE__ */ jsx12("span", { className: "text-sm font-medium text-muted-foreground", children: "Due Date:" }),
|
|
971
|
+
/* @__PURE__ */ jsx12("p", { className: "font-medium", children: formatDate3(invoice.dueDate) })
|
|
951
972
|
] }),
|
|
952
|
-
invoice.paidAt && /* @__PURE__ */
|
|
953
|
-
/* @__PURE__ */
|
|
954
|
-
/* @__PURE__ */
|
|
973
|
+
invoice.paidAt && /* @__PURE__ */ jsxs10("div", { children: [
|
|
974
|
+
/* @__PURE__ */ jsx12("span", { className: "text-sm font-medium text-muted-foreground", children: "Paid Date:" }),
|
|
975
|
+
/* @__PURE__ */ jsx12("p", { className: "font-medium", children: formatDate3(invoice.paidAt) })
|
|
955
976
|
] }),
|
|
956
|
-
/* @__PURE__ */
|
|
957
|
-
/* @__PURE__ */
|
|
958
|
-
/* @__PURE__ */
|
|
977
|
+
/* @__PURE__ */ jsxs10("div", { children: [
|
|
978
|
+
/* @__PURE__ */ jsx12("span", { className: "text-sm font-medium text-muted-foreground", children: "Attempt Count:" }),
|
|
979
|
+
/* @__PURE__ */ jsx12("p", { className: "font-medium", children: invoice.attemptCount })
|
|
959
980
|
] })
|
|
960
981
|
] }),
|
|
961
|
-
/* @__PURE__ */
|
|
962
|
-
/* @__PURE__ */
|
|
963
|
-
/* @__PURE__ */
|
|
964
|
-
/* @__PURE__ */
|
|
965
|
-
/* @__PURE__ */
|
|
966
|
-
/* @__PURE__ */
|
|
982
|
+
/* @__PURE__ */ jsxs10("div", { children: [
|
|
983
|
+
/* @__PURE__ */ jsx12("h4", { className: "text-sm font-medium text-muted-foreground mb-2", children: "Line Items" }),
|
|
984
|
+
/* @__PURE__ */ jsx12("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs10("table", { className: "w-full", children: [
|
|
985
|
+
/* @__PURE__ */ jsx12("thead", { className: "bg-muted", children: /* @__PURE__ */ jsxs10("tr", { children: [
|
|
986
|
+
/* @__PURE__ */ jsx12("th", { className: "text-left p-3 text-sm font-medium", children: "Description" }),
|
|
987
|
+
/* @__PURE__ */ jsx12("th", { className: "text-right p-3 text-sm font-medium", children: "Amount" })
|
|
967
988
|
] }) }),
|
|
968
|
-
/* @__PURE__ */
|
|
969
|
-
/* @__PURE__ */
|
|
970
|
-
/* @__PURE__ */
|
|
989
|
+
/* @__PURE__ */ jsx12("tbody", { children: /* @__PURE__ */ jsxs10("tr", { className: "border-t", children: [
|
|
990
|
+
/* @__PURE__ */ jsx12("td", { className: "p-3", children: productName }),
|
|
991
|
+
/* @__PURE__ */ jsx12("td", { className: "p-3 text-right", children: formatCurrency(invoice.subtotal, invoice.currency) })
|
|
971
992
|
] }) })
|
|
972
993
|
] }) })
|
|
973
994
|
] }),
|
|
974
|
-
/* @__PURE__ */
|
|
975
|
-
/* @__PURE__ */
|
|
976
|
-
/* @__PURE__ */
|
|
977
|
-
/* @__PURE__ */
|
|
995
|
+
/* @__PURE__ */ jsxs10("div", { className: "space-y-2 border-t pt-4", children: [
|
|
996
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex justify-between", children: [
|
|
997
|
+
/* @__PURE__ */ jsx12("span", { className: "text-sm font-medium text-muted-foreground", children: "Subtotal:" }),
|
|
998
|
+
/* @__PURE__ */ jsx12("span", { className: "font-medium", children: formatCurrency(invoice.subtotal, invoice.currency) })
|
|
978
999
|
] }),
|
|
979
|
-
invoice.tax !== void 0 && invoice.tax > 0 && /* @__PURE__ */
|
|
980
|
-
/* @__PURE__ */
|
|
981
|
-
/* @__PURE__ */
|
|
1000
|
+
invoice.tax !== void 0 && invoice.tax > 0 && /* @__PURE__ */ jsxs10("div", { className: "flex justify-between", children: [
|
|
1001
|
+
/* @__PURE__ */ jsx12("span", { className: "text-sm font-medium text-muted-foreground", children: "Tax:" }),
|
|
1002
|
+
/* @__PURE__ */ jsx12("span", { className: "font-medium", children: formatCurrency(invoice.tax, invoice.currency) })
|
|
982
1003
|
] }),
|
|
983
|
-
/* @__PURE__ */
|
|
984
|
-
/* @__PURE__ */
|
|
985
|
-
/* @__PURE__ */
|
|
1004
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex justify-between text-lg font-semibold border-t pt-2", children: [
|
|
1005
|
+
/* @__PURE__ */ jsx12("span", { children: "Total:" }),
|
|
1006
|
+
/* @__PURE__ */ jsx12("span", { children: formatCurrency(invoice.total, invoice.currency) })
|
|
986
1007
|
] }),
|
|
987
|
-
invoice.amountPaid > 0 && /* @__PURE__ */
|
|
988
|
-
/* @__PURE__ */
|
|
989
|
-
/* @__PURE__ */
|
|
1008
|
+
invoice.amountPaid > 0 && /* @__PURE__ */ jsxs10("div", { className: "flex justify-between", children: [
|
|
1009
|
+
/* @__PURE__ */ jsx12("span", { className: "text-sm font-medium text-muted-foreground", children: "Amount Paid:" }),
|
|
1010
|
+
/* @__PURE__ */ jsx12("span", { className: "font-medium text-green-600", children: formatCurrency(invoice.amountPaid, invoice.currency) })
|
|
990
1011
|
] }),
|
|
991
|
-
invoice.amountRemaining > 0 && /* @__PURE__ */
|
|
992
|
-
/* @__PURE__ */
|
|
993
|
-
/* @__PURE__ */
|
|
1012
|
+
invoice.amountRemaining > 0 && /* @__PURE__ */ jsxs10("div", { className: "flex justify-between", children: [
|
|
1013
|
+
/* @__PURE__ */ jsx12("span", { className: "text-sm font-medium text-muted-foreground", children: "Amount Due:" }),
|
|
1014
|
+
/* @__PURE__ */ jsx12("span", { className: "font-medium text-red-600", children: formatCurrency(invoice.amountRemaining, invoice.currency) })
|
|
994
1015
|
] })
|
|
995
1016
|
] }),
|
|
996
|
-
/* @__PURE__ */
|
|
997
|
-
invoice.stripePdfUrl && /* @__PURE__ */
|
|
998
|
-
/* @__PURE__ */
|
|
1017
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap gap-2 pt-4 border-t", children: [
|
|
1018
|
+
invoice.stripePdfUrl && /* @__PURE__ */ jsxs10(Button, { variant: "outline", onClick: handleDownloadPDF, children: [
|
|
1019
|
+
/* @__PURE__ */ jsx12(Download, { className: "mr-2 h-4 w-4" }),
|
|
999
1020
|
"Download PDF"
|
|
1000
1021
|
] }),
|
|
1001
|
-
invoice.status === "open" /* OPEN */ && invoice.attempted && /* @__PURE__ */
|
|
1002
|
-
/* @__PURE__ */
|
|
1022
|
+
invoice.status === "open" /* OPEN */ && invoice.attempted && /* @__PURE__ */ jsxs10(Button, { variant: "default", onClick: handleRetryPayment, children: [
|
|
1023
|
+
/* @__PURE__ */ jsx12(RefreshCw, { className: "mr-2 h-4 w-4" }),
|
|
1003
1024
|
"Retry Payment"
|
|
1004
1025
|
] }),
|
|
1005
|
-
invoice.stripeHostedInvoiceUrl && /* @__PURE__ */
|
|
1006
|
-
/* @__PURE__ */
|
|
1026
|
+
invoice.stripeHostedInvoiceUrl && /* @__PURE__ */ jsxs10(Button, { variant: "outline", onClick: handleViewInStripe, children: [
|
|
1027
|
+
/* @__PURE__ */ jsx12(ExternalLink2, { className: "mr-2 h-4 w-4" }),
|
|
1007
1028
|
"View in Stripe"
|
|
1008
1029
|
] })
|
|
1009
1030
|
] })
|
|
@@ -1013,7 +1034,7 @@ function InvoiceDetails({ invoice, open, onOpenChange, onInvoiceChange }) {
|
|
|
1013
1034
|
__name(InvoiceDetails, "InvoiceDetails");
|
|
1014
1035
|
|
|
1015
1036
|
// src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx
|
|
1016
|
-
import { Fragment as Fragment2, jsx as
|
|
1037
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1017
1038
|
function InvoicesList({ invoices, onInvoicesChange }) {
|
|
1018
1039
|
const [selectedInvoice, setSelectedInvoice] = useState5(null);
|
|
1019
1040
|
const handleRowClick = /* @__PURE__ */ __name((invoice) => {
|
|
@@ -1025,38 +1046,38 @@ function InvoicesList({ invoices, onInvoicesChange }) {
|
|
|
1025
1046
|
}
|
|
1026
1047
|
return invoice.stripeInvoiceId.slice(-8);
|
|
1027
1048
|
}, "getInvoiceNumber");
|
|
1028
|
-
return /* @__PURE__ */
|
|
1029
|
-
/* @__PURE__ */
|
|
1030
|
-
/* @__PURE__ */
|
|
1031
|
-
/* @__PURE__ */
|
|
1032
|
-
/* @__PURE__ */
|
|
1033
|
-
/* @__PURE__ */
|
|
1034
|
-
/* @__PURE__ */
|
|
1035
|
-
/* @__PURE__ */
|
|
1049
|
+
return /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
1050
|
+
/* @__PURE__ */ jsx13("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs11(Table, { children: [
|
|
1051
|
+
/* @__PURE__ */ jsx13(TableHeader, { className: "bg-muted", children: /* @__PURE__ */ jsxs11(TableRow, { children: [
|
|
1052
|
+
/* @__PURE__ */ jsx13(TableHead, { children: "Invoice #" }),
|
|
1053
|
+
/* @__PURE__ */ jsx13(TableHead, { children: "Date" }),
|
|
1054
|
+
/* @__PURE__ */ jsx13(TableHead, { children: "Status" }),
|
|
1055
|
+
/* @__PURE__ */ jsx13(TableHead, { className: "text-right", children: "Amount" }),
|
|
1056
|
+
/* @__PURE__ */ jsx13(TableHead, { children: "Period" })
|
|
1036
1057
|
] }) }),
|
|
1037
|
-
/* @__PURE__ */
|
|
1058
|
+
/* @__PURE__ */ jsx13(TableBody, { children: invoices.map((invoice) => {
|
|
1038
1059
|
const invoiceNumber = getInvoiceNumber(invoice);
|
|
1039
1060
|
const date = formatDate3(invoice.periodStart);
|
|
1040
1061
|
const amount = formatCurrency(invoice.total, invoice.currency);
|
|
1041
1062
|
const period = `${formatDate3(invoice.periodStart)} - ${formatDate3(invoice.periodEnd)}`;
|
|
1042
|
-
return /* @__PURE__ */
|
|
1063
|
+
return /* @__PURE__ */ jsxs11(
|
|
1043
1064
|
TableRow,
|
|
1044
1065
|
{
|
|
1045
1066
|
onClick: () => handleRowClick(invoice),
|
|
1046
1067
|
className: "cursor-pointer hover:bg-muted/50",
|
|
1047
1068
|
children: [
|
|
1048
|
-
/* @__PURE__ */
|
|
1049
|
-
/* @__PURE__ */
|
|
1050
|
-
/* @__PURE__ */
|
|
1051
|
-
/* @__PURE__ */
|
|
1052
|
-
/* @__PURE__ */
|
|
1069
|
+
/* @__PURE__ */ jsx13(TableCell, { className: "font-medium", children: invoiceNumber }),
|
|
1070
|
+
/* @__PURE__ */ jsx13(TableCell, { className: "text-muted-foreground text-sm", children: date }),
|
|
1071
|
+
/* @__PURE__ */ jsx13(TableCell, { children: /* @__PURE__ */ jsx13(InvoiceStatusBadge, { status: invoice.status }) }),
|
|
1072
|
+
/* @__PURE__ */ jsx13(TableCell, { className: "text-right font-medium", children: amount }),
|
|
1073
|
+
/* @__PURE__ */ jsx13(TableCell, { className: "text-muted-foreground text-sm", children: period })
|
|
1053
1074
|
]
|
|
1054
1075
|
},
|
|
1055
1076
|
invoice.id
|
|
1056
1077
|
);
|
|
1057
1078
|
}) })
|
|
1058
1079
|
] }) }),
|
|
1059
|
-
selectedInvoice && /* @__PURE__ */
|
|
1080
|
+
selectedInvoice && /* @__PURE__ */ jsx13(
|
|
1060
1081
|
InvoiceDetails,
|
|
1061
1082
|
{
|
|
1062
1083
|
invoice: selectedInvoice,
|
|
@@ -1073,7 +1094,7 @@ function InvoicesList({ invoices, onInvoicesChange }) {
|
|
|
1073
1094
|
__name(InvoicesList, "InvoicesList");
|
|
1074
1095
|
|
|
1075
1096
|
// src/features/billing/stripe-invoice/components/containers/InvoicesContainer.tsx
|
|
1076
|
-
import { jsx as
|
|
1097
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1077
1098
|
function InvoicesContainer() {
|
|
1078
1099
|
const [invoices, setInvoices] = useState6([]);
|
|
1079
1100
|
const [loading, setLoading] = useState6(true);
|
|
@@ -1097,99 +1118,40 @@ function InvoicesContainer() {
|
|
|
1097
1118
|
const handleFilterChange = /* @__PURE__ */ __name((value) => {
|
|
1098
1119
|
setStatusFilter(value);
|
|
1099
1120
|
}, "handleFilterChange");
|
|
1100
|
-
return /* @__PURE__ */
|
|
1101
|
-
/* @__PURE__ */
|
|
1102
|
-
/* @__PURE__ */
|
|
1103
|
-
/* @__PURE__ */
|
|
1104
|
-
/* @__PURE__ */
|
|
1105
|
-
/* @__PURE__ */
|
|
1106
|
-
/* @__PURE__ */
|
|
1121
|
+
return /* @__PURE__ */ jsxs12("div", { className: "space-y-4", children: [
|
|
1122
|
+
/* @__PURE__ */ jsx14(Tabs, { value: statusFilter, onValueChange: handleFilterChange, children: /* @__PURE__ */ jsxs12(TabsList, { children: [
|
|
1123
|
+
/* @__PURE__ */ jsx14(TabsTrigger, { value: "all", children: "All" }),
|
|
1124
|
+
/* @__PURE__ */ jsx14(TabsTrigger, { value: "paid" /* PAID */, children: "Paid" }),
|
|
1125
|
+
/* @__PURE__ */ jsx14(TabsTrigger, { value: "open" /* OPEN */, children: "Open" }),
|
|
1126
|
+
/* @__PURE__ */ jsx14(TabsTrigger, { value: "void" /* VOID */, children: "Void" }),
|
|
1127
|
+
/* @__PURE__ */ jsx14(TabsTrigger, { value: "uncollectible" /* UNCOLLECTIBLE */, children: "Uncollectible" })
|
|
1107
1128
|
] }) }),
|
|
1108
|
-
loading && /* @__PURE__ */
|
|
1109
|
-
!loading && invoices.length === 0 && /* @__PURE__ */
|
|
1110
|
-
/* @__PURE__ */
|
|
1111
|
-
/* @__PURE__ */
|
|
1129
|
+
loading && /* @__PURE__ */ jsx14("div", { className: "text-center py-8 text-muted-foreground", children: "Loading invoices..." }),
|
|
1130
|
+
!loading && invoices.length === 0 && /* @__PURE__ */ jsxs12("div", { className: "border border-dashed border-gray-300 rounded-lg p-8 text-center", children: [
|
|
1131
|
+
/* @__PURE__ */ jsx14("p", { className: "text-lg font-medium text-muted-foreground mb-2", children: "No invoices yet" }),
|
|
1132
|
+
/* @__PURE__ */ jsx14("p", { className: "text-sm text-muted-foreground", children: "Invoices will appear here after your first billing cycle" })
|
|
1112
1133
|
] }),
|
|
1113
|
-
!loading && invoices.length > 0 && /* @__PURE__ */
|
|
1134
|
+
!loading && invoices.length > 0 && /* @__PURE__ */ jsx14(InvoicesList, { invoices, onInvoicesChange: loadInvoices })
|
|
1114
1135
|
] });
|
|
1115
1136
|
}
|
|
1116
1137
|
__name(InvoicesContainer, "InvoicesContainer");
|
|
1117
1138
|
|
|
1118
1139
|
// src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx
|
|
1119
|
-
import {
|
|
1120
|
-
import { useEffect as
|
|
1121
|
-
import { v4 as v42 } from "uuid";
|
|
1140
|
+
import { CreditCard as CreditCard3 } from "lucide-react";
|
|
1141
|
+
import { useCallback, useEffect as useEffect5, useState as useState10 } from "react";
|
|
1122
1142
|
|
|
1123
|
-
// src/features/billing/stripe-subscription/
|
|
1124
|
-
import {
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
const [isConfirming, setIsConfirming] = useState7(false);
|
|
1129
|
-
const confirmPayment = useCallback(
|
|
1130
|
-
async (clientSecret) => {
|
|
1131
|
-
if (!stripe) {
|
|
1132
|
-
console.error("[useConfirmSubscriptionPayment] Stripe not initialized");
|
|
1133
|
-
return {
|
|
1134
|
-
success: false,
|
|
1135
|
-
error: "Payment system not initialized. Please refresh the page and try again."
|
|
1136
|
-
};
|
|
1137
|
-
}
|
|
1138
|
-
if (!clientSecret) {
|
|
1139
|
-
console.error("[useConfirmSubscriptionPayment] No client secret provided");
|
|
1140
|
-
return {
|
|
1141
|
-
success: false,
|
|
1142
|
-
error: "Payment confirmation failed. Missing payment details."
|
|
1143
|
-
};
|
|
1144
|
-
}
|
|
1145
|
-
setIsConfirming(true);
|
|
1146
|
-
try {
|
|
1147
|
-
const { error: stripeError, paymentIntent } = await stripe.confirmCardPayment(clientSecret);
|
|
1148
|
-
if (stripeError) {
|
|
1149
|
-
console.error("[useConfirmSubscriptionPayment] Stripe error:", stripeError);
|
|
1150
|
-
return {
|
|
1151
|
-
success: false,
|
|
1152
|
-
error: stripeError.message || "Payment confirmation failed. Please try again."
|
|
1153
|
-
};
|
|
1154
|
-
}
|
|
1155
|
-
if (paymentIntent?.status === "succeeded") {
|
|
1156
|
-
return { success: true };
|
|
1157
|
-
}
|
|
1158
|
-
if (paymentIntent?.status === "requires_action") {
|
|
1159
|
-
return {
|
|
1160
|
-
success: false,
|
|
1161
|
-
error: "Additional authentication required. Please complete the verification."
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1164
|
-
return {
|
|
1165
|
-
success: false,
|
|
1166
|
-
error: "Payment could not be completed. Please try again."
|
|
1167
|
-
};
|
|
1168
|
-
} catch (err) {
|
|
1169
|
-
console.error("[useConfirmSubscriptionPayment] Unexpected error:", err);
|
|
1170
|
-
return {
|
|
1171
|
-
success: false,
|
|
1172
|
-
error: err.message || "An unexpected error occurred during payment confirmation."
|
|
1173
|
-
};
|
|
1174
|
-
} finally {
|
|
1175
|
-
setIsConfirming(false);
|
|
1176
|
-
}
|
|
1177
|
-
},
|
|
1178
|
-
[stripe]
|
|
1179
|
-
);
|
|
1180
|
-
return {
|
|
1181
|
-
confirmPayment,
|
|
1182
|
-
isConfirming
|
|
1183
|
-
};
|
|
1184
|
-
}
|
|
1185
|
-
__name(useConfirmSubscriptionPayment, "useConfirmSubscriptionPayment");
|
|
1143
|
+
// src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx
|
|
1144
|
+
import { useState as useState9 } from "react";
|
|
1145
|
+
|
|
1146
|
+
// src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx
|
|
1147
|
+
import { useState as useState8 } from "react";
|
|
1186
1148
|
|
|
1187
1149
|
// src/features/billing/stripe-subscription/components/forms/CancelSubscriptionDialog.tsx
|
|
1188
1150
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
1189
|
-
import { useState as
|
|
1151
|
+
import { useState as useState7 } from "react";
|
|
1190
1152
|
import { useForm } from "react-hook-form";
|
|
1191
1153
|
import { z } from "zod";
|
|
1192
|
-
import { jsx as
|
|
1154
|
+
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1193
1155
|
var formSchema = z.object({
|
|
1194
1156
|
cancelImmediately: z.boolean(),
|
|
1195
1157
|
reason: z.string().optional()
|
|
@@ -1200,7 +1162,7 @@ function CancelSubscriptionDialog({
|
|
|
1200
1162
|
onOpenChange,
|
|
1201
1163
|
onSuccess
|
|
1202
1164
|
}) {
|
|
1203
|
-
const [isSubmitting, setIsSubmitting] =
|
|
1165
|
+
const [isSubmitting, setIsSubmitting] = useState7(false);
|
|
1204
1166
|
const form = useForm({
|
|
1205
1167
|
resolver: zodResolver(formSchema),
|
|
1206
1168
|
defaultValues: {
|
|
@@ -1225,19 +1187,19 @@ function CancelSubscriptionDialog({
|
|
|
1225
1187
|
}
|
|
1226
1188
|
}, "onSubmit");
|
|
1227
1189
|
const periodEndDate = formatDate3(subscription.currentPeriodEnd);
|
|
1228
|
-
return /* @__PURE__ */
|
|
1229
|
-
/* @__PURE__ */
|
|
1230
|
-
/* @__PURE__ */
|
|
1231
|
-
/* @__PURE__ */
|
|
1190
|
+
return /* @__PURE__ */ jsx15(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs13(DialogContent, { className: "max-w-md", children: [
|
|
1191
|
+
/* @__PURE__ */ jsxs13(DialogHeader, { children: [
|
|
1192
|
+
/* @__PURE__ */ jsx15(DialogTitle, { children: "Cancel Subscription" }),
|
|
1193
|
+
/* @__PURE__ */ jsx15(DialogDescription, { children: "Are you sure you want to cancel this subscription? This action cannot be undone." })
|
|
1232
1194
|
] }),
|
|
1233
|
-
/* @__PURE__ */
|
|
1234
|
-
/* @__PURE__ */
|
|
1235
|
-
cancelImmediately ? /* @__PURE__ */
|
|
1195
|
+
/* @__PURE__ */ jsx15(Form, { ...form, children: /* @__PURE__ */ jsxs13("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-col gap-y-4", children: [
|
|
1196
|
+
/* @__PURE__ */ jsx15(FormCheckbox, { form, id: "cancelImmediately", name: "Cancel Immediately" }),
|
|
1197
|
+
cancelImmediately ? /* @__PURE__ */ jsx15("div", { className: "bg-red-50 border border-red-200 rounded-lg p-3 text-sm text-red-800", children: "Your subscription will be canceled immediately and you will lose access right away." }) : /* @__PURE__ */ jsxs13("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-3 text-sm text-blue-800", children: [
|
|
1236
1198
|
"Your subscription will remain active until ",
|
|
1237
1199
|
periodEndDate,
|
|
1238
1200
|
". You can continue using the service until then."
|
|
1239
1201
|
] }),
|
|
1240
|
-
/* @__PURE__ */
|
|
1202
|
+
/* @__PURE__ */ jsx15(
|
|
1241
1203
|
FormTextarea,
|
|
1242
1204
|
{
|
|
1243
1205
|
form,
|
|
@@ -1247,407 +1209,17 @@ function CancelSubscriptionDialog({
|
|
|
1247
1209
|
className: "min-h-24"
|
|
1248
1210
|
}
|
|
1249
1211
|
),
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
/* @__PURE__ */
|
|
1252
|
-
/* @__PURE__ */
|
|
1212
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex gap-x-2 justify-end pt-2", children: [
|
|
1213
|
+
/* @__PURE__ */ jsx15(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isSubmitting, children: "Keep Subscription" }),
|
|
1214
|
+
/* @__PURE__ */ jsx15(Button, { type: "submit", variant: "destructive", disabled: isSubmitting, children: isSubmitting ? "Canceling..." : "Confirm Cancellation" })
|
|
1253
1215
|
] })
|
|
1254
1216
|
] }) })
|
|
1255
1217
|
] }) });
|
|
1256
1218
|
}
|
|
1257
1219
|
__name(CancelSubscriptionDialog, "CancelSubscriptionDialog");
|
|
1258
1220
|
|
|
1259
|
-
// src/features/billing/stripe-subscription/components/forms/SubscriptionEditor.tsx
|
|
1260
|
-
import { CheckCircle, Loader2 } from "lucide-react";
|
|
1261
|
-
import { useEffect as useEffect5, useState as useState9 } from "react";
|
|
1262
|
-
import { v4 } from "uuid";
|
|
1263
|
-
|
|
1264
|
-
// src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx
|
|
1265
|
-
import { Check } from "lucide-react";
|
|
1266
|
-
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1267
|
-
function PricingCard({ price, isCurrentPlan = false, isSelected = false, isDisabled = false, isLoading = false, onSelect }) {
|
|
1268
|
-
const description = price.description || price.nickname || "Standard";
|
|
1269
|
-
const features = price.features || [];
|
|
1270
|
-
const formattedPrice = formatCurrency(price.unitAmount, price.currency);
|
|
1271
|
-
const interval = formatInterval(price);
|
|
1272
|
-
const handleKeyDown = /* @__PURE__ */ __name((e) => {
|
|
1273
|
-
if ((e.key === "Enter" || e.key === " ") && !isDisabled && !isCurrentPlan) {
|
|
1274
|
-
e.preventDefault();
|
|
1275
|
-
onSelect(price);
|
|
1276
|
-
}
|
|
1277
|
-
}, "handleKeyDown");
|
|
1278
|
-
const handleClick = /* @__PURE__ */ __name(() => {
|
|
1279
|
-
if (!isDisabled && !isCurrentPlan && !isLoading) {
|
|
1280
|
-
onSelect(price);
|
|
1281
|
-
}
|
|
1282
|
-
}, "handleClick");
|
|
1283
|
-
return /* @__PURE__ */ jsxs13(
|
|
1284
|
-
Card,
|
|
1285
|
-
{
|
|
1286
|
-
role: "radio",
|
|
1287
|
-
"aria-checked": isSelected,
|
|
1288
|
-
"aria-label": `${description} plan at ${formattedPrice} ${interval}`,
|
|
1289
|
-
tabIndex: isDisabled ? -1 : 0,
|
|
1290
|
-
onKeyDown: handleKeyDown,
|
|
1291
|
-
onClick: handleClick,
|
|
1292
|
-
className: cn(
|
|
1293
|
-
"relative cursor-pointer transition-all duration-200 flex flex-col h-full",
|
|
1294
|
-
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
1295
|
-
isCurrentPlan && "bg-muted/30",
|
|
1296
|
-
isSelected && !isCurrentPlan && "ring-2 ring-primary",
|
|
1297
|
-
!isDisabled && !isCurrentPlan && "hover:shadow-md hover:border-primary/50",
|
|
1298
|
-
isDisabled && "opacity-50 pointer-events-none",
|
|
1299
|
-
isLoading && "pointer-events-none"
|
|
1300
|
-
),
|
|
1301
|
-
children: [
|
|
1302
|
-
isCurrentPlan && /* @__PURE__ */ jsx15(Badge, { variant: "secondary", className: "absolute top-2 right-2", children: "Current" }),
|
|
1303
|
-
/* @__PURE__ */ jsx15(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx15("h3", { className: "font-semibold text-lg", children: description }) }),
|
|
1304
|
-
/* @__PURE__ */ jsxs13(CardContent, { className: "pb-4 grow", children: [
|
|
1305
|
-
/* @__PURE__ */ jsxs13("div", { className: "mb-4", children: [
|
|
1306
|
-
/* @__PURE__ */ jsx15("span", { className: "text-3xl font-bold", children: formattedPrice }),
|
|
1307
|
-
/* @__PURE__ */ jsx15("span", { className: "text-muted-foreground ml-1", children: interval })
|
|
1308
|
-
] }),
|
|
1309
|
-
features.length > 0 && /* @__PURE__ */ jsx15("ul", { className: "space-y-2", children: features.map((feature, index) => /* @__PURE__ */ jsxs13("li", { className: "flex items-start gap-2", children: [
|
|
1310
|
-
/* @__PURE__ */ jsx15(Check, { className: "h-4 w-4 text-green-500 mt-0.5 shrink-0" }),
|
|
1311
|
-
/* @__PURE__ */ jsx15("span", { className: "text-sm text-muted-foreground", children: feature })
|
|
1312
|
-
] }, index)) })
|
|
1313
|
-
] }),
|
|
1314
|
-
/* @__PURE__ */ jsx15(CardFooter, { children: /* @__PURE__ */ jsx15(
|
|
1315
|
-
Button,
|
|
1316
|
-
{
|
|
1317
|
-
variant: isCurrentPlan ? "secondary" : isSelected ? "default" : "outline",
|
|
1318
|
-
className: "w-full",
|
|
1319
|
-
disabled: isDisabled || isCurrentPlan || isLoading,
|
|
1320
|
-
children: isLoading ? "Processing..." : isCurrentPlan ? "Current Plan" : isSelected ? "Selected" : "Select Plan"
|
|
1321
|
-
}
|
|
1322
|
-
) })
|
|
1323
|
-
]
|
|
1324
|
-
}
|
|
1325
|
-
);
|
|
1326
|
-
}
|
|
1327
|
-
__name(PricingCard, "PricingCard");
|
|
1328
|
-
|
|
1329
|
-
// src/features/billing/stripe-subscription/components/widgets/PricingCardsGrid.tsx
|
|
1330
|
-
import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1331
|
-
function PricingCardsGrid({
|
|
1332
|
-
products,
|
|
1333
|
-
pricesByProduct,
|
|
1334
|
-
currentPriceId,
|
|
1335
|
-
selectedPriceId,
|
|
1336
|
-
loadingPriceId,
|
|
1337
|
-
loading = false,
|
|
1338
|
-
onSelectPrice
|
|
1339
|
-
}) {
|
|
1340
|
-
if (loading) {
|
|
1341
|
-
return /* @__PURE__ */ jsx16(PricingCardsGridSkeleton, {});
|
|
1342
|
-
}
|
|
1343
|
-
if (products.length === 0) {
|
|
1344
|
-
return /* @__PURE__ */ jsx16("div", { className: "text-center py-8 text-muted-foreground", children: "No plans available" });
|
|
1345
|
-
}
|
|
1346
|
-
return /* @__PURE__ */ jsx16("div", { className: "space-y-8", role: "radiogroup", "aria-label": "Available pricing plans", children: products.map((product) => {
|
|
1347
|
-
const prices = pricesByProduct.get(product.id) || [];
|
|
1348
|
-
if (prices.length === 0) return null;
|
|
1349
|
-
const sortedPrices = [...prices].sort((a, b) => (a.unitAmount ?? 0) - (b.unitAmount ?? 0));
|
|
1350
|
-
return /* @__PURE__ */ jsxs14("div", { className: "space-y-4", children: [
|
|
1351
|
-
/* @__PURE__ */ jsx16("h3", { className: "text-lg font-semibold", children: product.name }),
|
|
1352
|
-
/* @__PURE__ */ jsx16("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: sortedPrices.map((price) => /* @__PURE__ */ jsx16(
|
|
1353
|
-
PricingCard,
|
|
1354
|
-
{
|
|
1355
|
-
price,
|
|
1356
|
-
isCurrentPlan: price.stripePriceId === currentPriceId,
|
|
1357
|
-
isSelected: price.stripePriceId === selectedPriceId,
|
|
1358
|
-
isLoading: price.stripePriceId === loadingPriceId,
|
|
1359
|
-
onSelect: onSelectPrice
|
|
1360
|
-
},
|
|
1361
|
-
price.stripePriceId
|
|
1362
|
-
)) })
|
|
1363
|
-
] }, product.id);
|
|
1364
|
-
}) });
|
|
1365
|
-
}
|
|
1366
|
-
__name(PricingCardsGrid, "PricingCardsGrid");
|
|
1367
|
-
function PricingCardsGridSkeleton() {
|
|
1368
|
-
return /* @__PURE__ */ jsx16("div", { className: "space-y-8", children: [1, 2].map((productIndex) => /* @__PURE__ */ jsxs14("div", { className: "space-y-4", children: [
|
|
1369
|
-
/* @__PURE__ */ jsx16(Skeleton, { className: "h-6 w-32" }),
|
|
1370
|
-
/* @__PURE__ */ jsx16("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: [1, 2, 3].map((cardIndex) => /* @__PURE__ */ jsxs14(Card, { className: "animate-pulse", children: [
|
|
1371
|
-
/* @__PURE__ */ jsx16(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx16(Skeleton, { className: "h-5 w-24" }) }),
|
|
1372
|
-
/* @__PURE__ */ jsxs14(CardContent, { className: "pb-4", children: [
|
|
1373
|
-
/* @__PURE__ */ jsxs14("div", { className: "mb-4", children: [
|
|
1374
|
-
/* @__PURE__ */ jsx16(Skeleton, { className: "h-9 w-20 inline-block" }),
|
|
1375
|
-
/* @__PURE__ */ jsx16(Skeleton, { className: "h-4 w-12 inline-block ml-2" })
|
|
1376
|
-
] }),
|
|
1377
|
-
/* @__PURE__ */ jsxs14("div", { className: "space-y-2", children: [
|
|
1378
|
-
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
|
|
1379
|
-
/* @__PURE__ */ jsx16(Skeleton, { className: "h-4 w-4 rounded-full" }),
|
|
1380
|
-
/* @__PURE__ */ jsx16(Skeleton, { className: "h-4 w-32" })
|
|
1381
|
-
] }),
|
|
1382
|
-
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
|
|
1383
|
-
/* @__PURE__ */ jsx16(Skeleton, { className: "h-4 w-4 rounded-full" }),
|
|
1384
|
-
/* @__PURE__ */ jsx16(Skeleton, { className: "h-4 w-28" })
|
|
1385
|
-
] })
|
|
1386
|
-
] })
|
|
1387
|
-
] }),
|
|
1388
|
-
/* @__PURE__ */ jsx16(CardFooter, { children: /* @__PURE__ */ jsx16(Skeleton, { className: "h-9 w-full" }) })
|
|
1389
|
-
] }, cardIndex)) })
|
|
1390
|
-
] }, productIndex)) });
|
|
1391
|
-
}
|
|
1392
|
-
__name(PricingCardsGridSkeleton, "PricingCardsGridSkeleton");
|
|
1393
|
-
|
|
1394
|
-
// src/features/billing/stripe-subscription/components/widgets/ProrationPreview.tsx
|
|
1395
|
-
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1396
|
-
function ProrationPreview({ preview }) {
|
|
1397
|
-
return /* @__PURE__ */ jsxs15("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-4", children: [
|
|
1398
|
-
/* @__PURE__ */ jsx17("h4", { className: "font-semibold text-blue-900 mb-3", children: "Proration Breakdown" }),
|
|
1399
|
-
/* @__PURE__ */ jsxs15("div", { className: "space-y-2", children: [
|
|
1400
|
-
preview.lineItems.map((item, index) => /* @__PURE__ */ jsxs15("div", { className: "flex justify-between text-sm", children: [
|
|
1401
|
-
/* @__PURE__ */ jsx17("span", { className: "text-blue-800", children: item.description }),
|
|
1402
|
-
/* @__PURE__ */ jsx17("span", { className: `font-medium ${item.amount < 0 ? "text-green-600" : "text-blue-900"}`, children: formatCurrency(item.amount, preview.currency) })
|
|
1403
|
-
] }, index)),
|
|
1404
|
-
/* @__PURE__ */ jsx17("div", { className: "border-t border-blue-200 pt-2 mt-2", children: /* @__PURE__ */ jsxs15("div", { className: "flex justify-between font-semibold", children: [
|
|
1405
|
-
/* @__PURE__ */ jsx17("span", { className: "text-blue-900", children: "Net Due Today" }),
|
|
1406
|
-
/* @__PURE__ */ jsx17("span", { className: "text-blue-900", children: formatCurrency(preview.immediateCharge, preview.currency) })
|
|
1407
|
-
] }) }),
|
|
1408
|
-
preview.lineItems.length > 0 && preview.lineItems[0].period && /* @__PURE__ */ jsxs15("div", { className: "text-xs text-blue-700 mt-2", children: [
|
|
1409
|
-
"Next invoice on ",
|
|
1410
|
-
formatDate3(preview.lineItems[0].period.end),
|
|
1411
|
-
" for",
|
|
1412
|
-
" ",
|
|
1413
|
-
formatCurrency(preview.amountDue, preview.currency)
|
|
1414
|
-
] })
|
|
1415
|
-
] })
|
|
1416
|
-
] });
|
|
1417
|
-
}
|
|
1418
|
-
__name(ProrationPreview, "ProrationPreview");
|
|
1419
|
-
|
|
1420
|
-
// src/features/billing/stripe-subscription/components/forms/SubscriptionEditor.tsx
|
|
1421
|
-
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1422
|
-
function SubscriptionEditor({
|
|
1423
|
-
subscription,
|
|
1424
|
-
open,
|
|
1425
|
-
onOpenChange,
|
|
1426
|
-
onSuccess,
|
|
1427
|
-
onAddPaymentMethod
|
|
1428
|
-
}) {
|
|
1429
|
-
const { confirmPayment, isConfirming } = useConfirmSubscriptionPayment();
|
|
1430
|
-
const [products, setProducts] = useState9([]);
|
|
1431
|
-
const [pricesByProduct, setPricesByProduct] = useState9(/* @__PURE__ */ new Map());
|
|
1432
|
-
const [loading, setLoading] = useState9(true);
|
|
1433
|
-
const [selectedPriceId, setSelectedPriceId] = useState9(null);
|
|
1434
|
-
const [loadingPriceId, setLoadingPriceId] = useState9(null);
|
|
1435
|
-
const [prorationPreview, setProrationPreview] = useState9(null);
|
|
1436
|
-
const [loadingProration, setLoadingProration] = useState9(false);
|
|
1437
|
-
const [hasPaymentMethod, setHasPaymentMethod] = useState9(true);
|
|
1438
|
-
const [loadingPaymentMethods, setLoadingPaymentMethods] = useState9(true);
|
|
1439
|
-
const [paymentRequiredError, setPaymentRequiredError] = useState9(false);
|
|
1440
|
-
const [paymentConfirmationState, setPaymentConfirmationState] = useState9("idle");
|
|
1441
|
-
const [paymentError, setPaymentError] = useState9(null);
|
|
1442
|
-
const currentPriceId = subscription?.price?.id;
|
|
1443
|
-
const isEditMode = !!subscription;
|
|
1444
|
-
useEffect5(() => {
|
|
1445
|
-
const checkPaymentMethods = /* @__PURE__ */ __name(async () => {
|
|
1446
|
-
if (subscription) {
|
|
1447
|
-
setLoadingPaymentMethods(false);
|
|
1448
|
-
return;
|
|
1449
|
-
}
|
|
1450
|
-
setLoadingPaymentMethods(true);
|
|
1451
|
-
try {
|
|
1452
|
-
const paymentMethods = await StripeCustomerService.listPaymentMethods();
|
|
1453
|
-
const hasMethod = paymentMethods.length > 0;
|
|
1454
|
-
setHasPaymentMethod(hasMethod);
|
|
1455
|
-
} catch (error) {
|
|
1456
|
-
console.error("[SubscriptionEditor] Failed to check payment methods:", error);
|
|
1457
|
-
setHasPaymentMethod(false);
|
|
1458
|
-
} finally {
|
|
1459
|
-
setLoadingPaymentMethods(false);
|
|
1460
|
-
}
|
|
1461
|
-
}, "checkPaymentMethods");
|
|
1462
|
-
if (open) {
|
|
1463
|
-
checkPaymentMethods();
|
|
1464
|
-
}
|
|
1465
|
-
}, [open, subscription]);
|
|
1466
|
-
useEffect5(() => {
|
|
1467
|
-
const loadData = /* @__PURE__ */ __name(async () => {
|
|
1468
|
-
setLoading(true);
|
|
1469
|
-
try {
|
|
1470
|
-
const fetchedProducts = await StripeProductService.listProducts({ active: true });
|
|
1471
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
1472
|
-
for (const product of fetchedProducts) {
|
|
1473
|
-
if (product.stripePrices && product.stripePrices.length > 0) {
|
|
1474
|
-
grouped.set(product.id, product.stripePrices);
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
setProducts(fetchedProducts);
|
|
1478
|
-
setPricesByProduct(grouped);
|
|
1479
|
-
} catch (error) {
|
|
1480
|
-
console.error("[SubscriptionEditor] Failed to load products/prices:", error);
|
|
1481
|
-
} finally {
|
|
1482
|
-
setLoading(false);
|
|
1483
|
-
}
|
|
1484
|
-
}, "loadData");
|
|
1485
|
-
if (open) {
|
|
1486
|
-
loadData();
|
|
1487
|
-
}
|
|
1488
|
-
}, [open]);
|
|
1489
|
-
useEffect5(() => {
|
|
1490
|
-
const loadProration = /* @__PURE__ */ __name(async () => {
|
|
1491
|
-
if (!subscription || !selectedPriceId || selectedPriceId === currentPriceId) {
|
|
1492
|
-
setProrationPreview(null);
|
|
1493
|
-
return;
|
|
1494
|
-
}
|
|
1495
|
-
setLoadingProration(true);
|
|
1496
|
-
try {
|
|
1497
|
-
const preview = await StripeSubscriptionService.getProrationPreview({
|
|
1498
|
-
subscriptionId: subscription.id,
|
|
1499
|
-
newPriceId: selectedPriceId
|
|
1500
|
-
});
|
|
1501
|
-
setProrationPreview(preview);
|
|
1502
|
-
} catch (error) {
|
|
1503
|
-
console.error("[SubscriptionEditor] Failed to load proration preview:", error);
|
|
1504
|
-
setProrationPreview(null);
|
|
1505
|
-
} finally {
|
|
1506
|
-
setLoadingProration(false);
|
|
1507
|
-
}
|
|
1508
|
-
}, "loadProration");
|
|
1509
|
-
loadProration();
|
|
1510
|
-
}, [selectedPriceId, subscription, currentPriceId]);
|
|
1511
|
-
const handleSelectPrice = /* @__PURE__ */ __name(async (price) => {
|
|
1512
|
-
const priceId = price.id;
|
|
1513
|
-
if (isEditMode) {
|
|
1514
|
-
setSelectedPriceId(priceId);
|
|
1515
|
-
} else {
|
|
1516
|
-
setLoadingPriceId(priceId);
|
|
1517
|
-
setSelectedPriceId(priceId);
|
|
1518
|
-
setPaymentError(null);
|
|
1519
|
-
setPaymentConfirmationState("idle");
|
|
1520
|
-
try {
|
|
1521
|
-
const result = await StripeSubscriptionService.createSubscription({
|
|
1522
|
-
id: v4(),
|
|
1523
|
-
priceId
|
|
1524
|
-
});
|
|
1525
|
-
if (result.meta.requiresAction && result.meta.clientSecret) {
|
|
1526
|
-
setPaymentConfirmationState("confirming");
|
|
1527
|
-
const confirmation = await confirmPayment(result.meta.clientSecret);
|
|
1528
|
-
if (!confirmation.success) {
|
|
1529
|
-
console.error("[SubscriptionEditor] Payment confirmation failed:", confirmation.error);
|
|
1530
|
-
setPaymentConfirmationState("error");
|
|
1531
|
-
setPaymentError(confirmation.error || "Payment confirmation failed");
|
|
1532
|
-
setLoadingPriceId(null);
|
|
1533
|
-
return;
|
|
1534
|
-
}
|
|
1535
|
-
await StripeSubscriptionService.syncSubscription({
|
|
1536
|
-
subscriptionId: result.subscription.id
|
|
1537
|
-
});
|
|
1538
|
-
}
|
|
1539
|
-
setPaymentConfirmationState("success");
|
|
1540
|
-
setTimeout(() => {
|
|
1541
|
-
onSuccess();
|
|
1542
|
-
onOpenChange(false);
|
|
1543
|
-
}, 1e3);
|
|
1544
|
-
} catch (error) {
|
|
1545
|
-
console.error("[SubscriptionEditor] Failed to create subscription:", error);
|
|
1546
|
-
if (error?.status === 402 || error?.response?.status === 402) {
|
|
1547
|
-
setPaymentRequiredError(true);
|
|
1548
|
-
setHasPaymentMethod(false);
|
|
1549
|
-
} else {
|
|
1550
|
-
setPaymentConfirmationState("error");
|
|
1551
|
-
setPaymentError(error?.message || "Failed to create subscription");
|
|
1552
|
-
}
|
|
1553
|
-
setLoadingPriceId(null);
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
}, "handleSelectPrice");
|
|
1557
|
-
const handleConfirmPlanChange = /* @__PURE__ */ __name(async () => {
|
|
1558
|
-
if (!subscription || !selectedPriceId) return;
|
|
1559
|
-
setLoadingPriceId(selectedPriceId);
|
|
1560
|
-
try {
|
|
1561
|
-
await StripeSubscriptionService.changePlan({
|
|
1562
|
-
id: subscription.id,
|
|
1563
|
-
newPriceId: selectedPriceId
|
|
1564
|
-
});
|
|
1565
|
-
onSuccess();
|
|
1566
|
-
onOpenChange(false);
|
|
1567
|
-
} catch (error) {
|
|
1568
|
-
console.error("[SubscriptionEditor] Failed to change plan:", error);
|
|
1569
|
-
} finally {
|
|
1570
|
-
setLoadingPriceId(null);
|
|
1571
|
-
}
|
|
1572
|
-
}, "handleConfirmPlanChange");
|
|
1573
|
-
const handleCancel = /* @__PURE__ */ __name(() => {
|
|
1574
|
-
setSelectedPriceId(null);
|
|
1575
|
-
setProrationPreview(null);
|
|
1576
|
-
}, "handleCancel");
|
|
1577
|
-
return /* @__PURE__ */ jsx18(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs16(DialogContent, { className: "max-w-4xl max-h-[90vh] overflow-y-auto", children: [
|
|
1578
|
-
/* @__PURE__ */ jsxs16(DialogHeader, { children: [
|
|
1579
|
-
/* @__PURE__ */ jsx18(DialogTitle, { children: subscription ? "Change Plan" : "Subscribe to a Plan" }),
|
|
1580
|
-
/* @__PURE__ */ jsx18(DialogDescription, { children: subscription ? "Select a new plan to switch to. You'll see a proration preview before confirming." : "Choose a plan to start your subscription." })
|
|
1581
|
-
] }),
|
|
1582
|
-
loadingPaymentMethods && !subscription ? /* @__PURE__ */ jsx18("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx18("div", { className: "text-muted-foreground", children: "Checking payment methods..." }) }) : !hasPaymentMethod && !subscription ? /* @__PURE__ */ jsxs16(Alert, { variant: "destructive", children: [
|
|
1583
|
-
/* @__PURE__ */ jsx18(AlertTitle, { children: "Payment Method Required" }),
|
|
1584
|
-
/* @__PURE__ */ jsxs16(AlertDescription, { className: "mt-2", children: [
|
|
1585
|
-
/* @__PURE__ */ jsx18("p", { className: "mb-4", children: paymentRequiredError ? "Your subscription could not be created because no payment method is on file." : "You need to add a payment method before you can subscribe to a plan." }),
|
|
1586
|
-
onAddPaymentMethod && /* @__PURE__ */ jsx18(Button, { onClick: onAddPaymentMethod, variant: "outline", children: "Add Payment Method" })
|
|
1587
|
-
] })
|
|
1588
|
-
] }) : paymentConfirmationState === "confirming" || isConfirming ? /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-center justify-center py-12 space-y-4", children: [
|
|
1589
|
-
/* @__PURE__ */ jsx18(Loader2, { className: "h-8 w-8 animate-spin text-primary" }),
|
|
1590
|
-
/* @__PURE__ */ jsxs16("div", { className: "text-center", children: [
|
|
1591
|
-
/* @__PURE__ */ jsx18("p", { className: "font-medium", children: "Processing payment..." }),
|
|
1592
|
-
/* @__PURE__ */ jsx18("p", { className: "text-sm text-muted-foreground", children: "Please complete any verification if prompted." })
|
|
1593
|
-
] })
|
|
1594
|
-
] }) : paymentConfirmationState === "success" ? /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-center justify-center py-12 space-y-4", children: [
|
|
1595
|
-
/* @__PURE__ */ jsx18(CheckCircle, { className: "h-12 w-12 text-green-500" }),
|
|
1596
|
-
/* @__PURE__ */ jsxs16("div", { className: "text-center", children: [
|
|
1597
|
-
/* @__PURE__ */ jsx18("p", { className: "font-medium text-green-600", children: "Payment successful!" }),
|
|
1598
|
-
/* @__PURE__ */ jsx18("p", { className: "text-sm text-muted-foreground", children: "Your subscription is now active." })
|
|
1599
|
-
] })
|
|
1600
|
-
] }) : paymentConfirmationState === "error" ? /* @__PURE__ */ jsx18("div", { className: "space-y-4", children: /* @__PURE__ */ jsxs16(Alert, { variant: "destructive", children: [
|
|
1601
|
-
/* @__PURE__ */ jsx18(AlertTitle, { children: "Payment Failed" }),
|
|
1602
|
-
/* @__PURE__ */ jsxs16(AlertDescription, { className: "mt-2", children: [
|
|
1603
|
-
/* @__PURE__ */ jsx18("p", { className: "mb-4", children: paymentError || "We couldn't process your payment. Please try again." }),
|
|
1604
|
-
/* @__PURE__ */ jsx18(
|
|
1605
|
-
Button,
|
|
1606
|
-
{
|
|
1607
|
-
onClick: () => {
|
|
1608
|
-
setPaymentConfirmationState("idle");
|
|
1609
|
-
setPaymentError(null);
|
|
1610
|
-
setLoadingPriceId(null);
|
|
1611
|
-
},
|
|
1612
|
-
variant: "outline",
|
|
1613
|
-
children: "Try Again"
|
|
1614
|
-
}
|
|
1615
|
-
)
|
|
1616
|
-
] })
|
|
1617
|
-
] }) }) : /* @__PURE__ */ jsxs16("div", { className: "space-y-6", children: [
|
|
1618
|
-
/* @__PURE__ */ jsx18(
|
|
1619
|
-
PricingCardsGrid,
|
|
1620
|
-
{
|
|
1621
|
-
products,
|
|
1622
|
-
pricesByProduct,
|
|
1623
|
-
currentPriceId,
|
|
1624
|
-
selectedPriceId: selectedPriceId ?? void 0,
|
|
1625
|
-
loadingPriceId: loadingPriceId ?? void 0,
|
|
1626
|
-
loading,
|
|
1627
|
-
onSelectPrice: handleSelectPrice
|
|
1628
|
-
}
|
|
1629
|
-
),
|
|
1630
|
-
isEditMode && loadingProration && /* @__PURE__ */ jsx18("div", { className: "bg-muted/50 rounded-lg p-4 text-sm text-muted-foreground text-center", children: "Loading proration preview..." }),
|
|
1631
|
-
isEditMode && prorationPreview && !loadingProration && /* @__PURE__ */ jsxs16("div", { className: "space-y-4", children: [
|
|
1632
|
-
/* @__PURE__ */ jsx18(ProrationPreview, { preview: prorationPreview }),
|
|
1633
|
-
/* @__PURE__ */ jsxs16("div", { className: "flex justify-end gap-3", children: [
|
|
1634
|
-
/* @__PURE__ */ jsx18(Button, { variant: "outline", onClick: handleCancel, disabled: !!loadingPriceId, children: "Cancel" }),
|
|
1635
|
-
/* @__PURE__ */ jsx18(Button, { onClick: handleConfirmPlanChange, disabled: !!loadingPriceId, children: loadingPriceId ? "Processing..." : "Confirm Plan Change" })
|
|
1636
|
-
] })
|
|
1637
|
-
] })
|
|
1638
|
-
] })
|
|
1639
|
-
] }) });
|
|
1640
|
-
}
|
|
1641
|
-
__name(SubscriptionEditor, "SubscriptionEditor");
|
|
1642
|
-
|
|
1643
|
-
// src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx
|
|
1644
|
-
import { useState as useState11 } from "react";
|
|
1645
|
-
|
|
1646
|
-
// src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx
|
|
1647
|
-
import { useState as useState10 } from "react";
|
|
1648
|
-
|
|
1649
1221
|
// src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx
|
|
1650
|
-
import { jsx as
|
|
1222
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
1651
1223
|
var statusConfig2 = {
|
|
1652
1224
|
["active" /* ACTIVE */]: {
|
|
1653
1225
|
label: "Active",
|
|
@@ -1688,12 +1260,12 @@ var cancelingConfig = {
|
|
|
1688
1260
|
};
|
|
1689
1261
|
function SubscriptionStatusBadge({ status, cancelAtPeriodEnd }) {
|
|
1690
1262
|
const config = cancelAtPeriodEnd ? cancelingConfig : statusConfig2[status] || statusConfig2["canceled" /* CANCELED */];
|
|
1691
|
-
return /* @__PURE__ */
|
|
1263
|
+
return /* @__PURE__ */ jsx16("span", { className: `${config.color} text-xs px-2 py-1 rounded-full font-medium`, children: config.label });
|
|
1692
1264
|
}
|
|
1693
1265
|
__name(SubscriptionStatusBadge, "SubscriptionStatusBadge");
|
|
1694
1266
|
|
|
1695
1267
|
// src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx
|
|
1696
|
-
import { Fragment as Fragment3, jsx as
|
|
1268
|
+
import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1697
1269
|
function formatPlanName2(price) {
|
|
1698
1270
|
if (!price) return "N/A";
|
|
1699
1271
|
const productName = price.product?.name || "";
|
|
@@ -1722,11 +1294,11 @@ function SubscriptionDetails({
|
|
|
1722
1294
|
subscription,
|
|
1723
1295
|
open,
|
|
1724
1296
|
onOpenChange,
|
|
1725
|
-
onSubscriptionChange
|
|
1297
|
+
onSubscriptionChange,
|
|
1298
|
+
onChangePlan
|
|
1726
1299
|
}) {
|
|
1727
|
-
const [
|
|
1728
|
-
const [
|
|
1729
|
-
const [isProcessing, setIsProcessing] = useState10(false);
|
|
1300
|
+
const [showCancel, setShowCancel] = useState8(false);
|
|
1301
|
+
const [isProcessing, setIsProcessing] = useState8(false);
|
|
1730
1302
|
const handlePause = /* @__PURE__ */ __name(async () => {
|
|
1731
1303
|
setIsProcessing(true);
|
|
1732
1304
|
try {
|
|
@@ -1760,67 +1332,55 @@ function SubscriptionDetails({
|
|
|
1760
1332
|
const canPause = subscription.status === "active" /* ACTIVE */;
|
|
1761
1333
|
const canResume = subscription.status === "paused" /* PAUSED */;
|
|
1762
1334
|
const canCancel = subscription.status === "active" /* ACTIVE */ || subscription.status === "trialing" /* TRIALING */ || subscription.status === "paused" /* PAUSED */;
|
|
1763
|
-
return /* @__PURE__ */
|
|
1764
|
-
/* @__PURE__ */
|
|
1765
|
-
/* @__PURE__ */
|
|
1766
|
-
/* @__PURE__ */
|
|
1767
|
-
/* @__PURE__ */
|
|
1335
|
+
return /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
1336
|
+
/* @__PURE__ */ jsx17(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs14(DialogContent, { className: "max-w-2xl", children: [
|
|
1337
|
+
/* @__PURE__ */ jsxs14(DialogHeader, { children: [
|
|
1338
|
+
/* @__PURE__ */ jsx17(DialogTitle, { children: "Subscription Details" }),
|
|
1339
|
+
/* @__PURE__ */ jsx17(DialogDescription, { children: "View and manage your subscription" })
|
|
1768
1340
|
] }),
|
|
1769
|
-
/* @__PURE__ */
|
|
1770
|
-
/* @__PURE__ */
|
|
1771
|
-
/* @__PURE__ */
|
|
1772
|
-
/* @__PURE__ */
|
|
1341
|
+
/* @__PURE__ */ jsxs14("div", { className: "space-y-6", children: [
|
|
1342
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-x-3", children: [
|
|
1343
|
+
/* @__PURE__ */ jsx17("span", { className: "text-sm font-medium text-muted-foreground", children: "Status:" }),
|
|
1344
|
+
/* @__PURE__ */ jsx17(SubscriptionStatusBadge, { status: subscription.status, cancelAtPeriodEnd: subscription.cancelAtPeriodEnd })
|
|
1773
1345
|
] }),
|
|
1774
|
-
/* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
1776
|
-
/* @__PURE__ */
|
|
1777
|
-
/* @__PURE__ */
|
|
1346
|
+
/* @__PURE__ */ jsxs14("div", { className: "space-y-2", children: [
|
|
1347
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex justify-between", children: [
|
|
1348
|
+
/* @__PURE__ */ jsx17("span", { className: "text-sm font-medium text-muted-foreground", children: "Plan:" }),
|
|
1349
|
+
/* @__PURE__ */ jsx17("span", { className: "font-medium", children: formatPlanName2(subscription.price) })
|
|
1778
1350
|
] }),
|
|
1779
|
-
/* @__PURE__ */
|
|
1780
|
-
/* @__PURE__ */
|
|
1781
|
-
/* @__PURE__ */
|
|
1351
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex justify-between", children: [
|
|
1352
|
+
/* @__PURE__ */ jsx17("span", { className: "text-sm font-medium text-muted-foreground", children: "Billing Amount:" }),
|
|
1353
|
+
/* @__PURE__ */ jsx17("span", { className: "font-medium", children: formatBillingAmount(subscription.price) })
|
|
1782
1354
|
] })
|
|
1783
1355
|
] }),
|
|
1784
|
-
/* @__PURE__ */
|
|
1785
|
-
/* @__PURE__ */
|
|
1786
|
-
/* @__PURE__ */
|
|
1356
|
+
/* @__PURE__ */ jsx17("div", { className: "space-y-2", children: /* @__PURE__ */ jsxs14("div", { className: "flex justify-between", children: [
|
|
1357
|
+
/* @__PURE__ */ jsx17("span", { className: "text-sm font-medium text-muted-foreground", children: "Current Period:" }),
|
|
1358
|
+
/* @__PURE__ */ jsxs14("span", { className: "font-medium", children: [
|
|
1787
1359
|
formatDate3(subscription.currentPeriodStart),
|
|
1788
1360
|
" - ",
|
|
1789
1361
|
formatDate3(subscription.currentPeriodEnd)
|
|
1790
1362
|
] })
|
|
1791
1363
|
] }) }),
|
|
1792
|
-
subscription.trialEnd && /* @__PURE__ */
|
|
1793
|
-
/* @__PURE__ */
|
|
1794
|
-
/* @__PURE__ */
|
|
1364
|
+
subscription.trialEnd && /* @__PURE__ */ jsxs14("div", { className: "flex justify-between", children: [
|
|
1365
|
+
/* @__PURE__ */ jsx17("span", { className: "text-sm font-medium text-muted-foreground", children: "Trial Ends:" }),
|
|
1366
|
+
/* @__PURE__ */ jsx17("span", { className: "font-medium", children: formatDate3(subscription.trialEnd) })
|
|
1795
1367
|
] }),
|
|
1796
|
-
subscription.cancelAtPeriodEnd && /* @__PURE__ */
|
|
1368
|
+
subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsx17("div", { className: "bg-yellow-50 border border-yellow-200 rounded-lg p-3", children: /* @__PURE__ */ jsxs14("p", { className: "text-sm text-yellow-800", children: [
|
|
1797
1369
|
"This subscription will be canceled at the end of the current period on",
|
|
1798
1370
|
" ",
|
|
1799
1371
|
formatDate3(subscription.currentPeriodEnd),
|
|
1800
1372
|
"."
|
|
1801
1373
|
] }) }),
|
|
1802
|
-
/* @__PURE__ */
|
|
1803
|
-
/* @__PURE__ */
|
|
1804
|
-
canPause && /* @__PURE__ */
|
|
1805
|
-
canResume && /* @__PURE__ */
|
|
1806
|
-
canCancel && /* @__PURE__ */
|
|
1807
|
-
/* @__PURE__ */
|
|
1374
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap gap-2 pt-4 border-t", children: [
|
|
1375
|
+
onChangePlan && /* @__PURE__ */ jsx17(Button, { variant: "default", onClick: () => onChangePlan(subscription), children: "Change Plan" }),
|
|
1376
|
+
canPause && /* @__PURE__ */ jsx17(Button, { variant: "outline", onClick: handlePause, disabled: isProcessing, children: isProcessing ? "Pausing..." : "Pause" }),
|
|
1377
|
+
canResume && /* @__PURE__ */ jsx17(Button, { variant: "outline", onClick: handleResume, disabled: isProcessing, children: isProcessing ? "Resuming..." : "Resume" }),
|
|
1378
|
+
canCancel && /* @__PURE__ */ jsx17(Button, { variant: "destructive", onClick: () => setShowCancel(true), children: "Cancel" }),
|
|
1379
|
+
/* @__PURE__ */ jsx17(Button, { variant: "outline", onClick: handleManageViaPortal, children: "Manage via Portal" })
|
|
1808
1380
|
] })
|
|
1809
1381
|
] })
|
|
1810
1382
|
] }) }),
|
|
1811
|
-
|
|
1812
|
-
SubscriptionEditor,
|
|
1813
|
-
{
|
|
1814
|
-
subscription,
|
|
1815
|
-
open: showEdit,
|
|
1816
|
-
onOpenChange: setShowEdit,
|
|
1817
|
-
onSuccess: () => {
|
|
1818
|
-
onSubscriptionChange();
|
|
1819
|
-
setShowEdit(false);
|
|
1820
|
-
}
|
|
1821
|
-
}
|
|
1822
|
-
),
|
|
1823
|
-
showCancel && /* @__PURE__ */ jsx20(
|
|
1383
|
+
showCancel && /* @__PURE__ */ jsx17(
|
|
1824
1384
|
CancelSubscriptionDialog,
|
|
1825
1385
|
{
|
|
1826
1386
|
subscription,
|
|
@@ -1837,7 +1397,7 @@ function SubscriptionDetails({
|
|
|
1837
1397
|
__name(SubscriptionDetails, "SubscriptionDetails");
|
|
1838
1398
|
|
|
1839
1399
|
// src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx
|
|
1840
|
-
import { Fragment as Fragment4, jsx as
|
|
1400
|
+
import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1841
1401
|
function formatPlanName3(price) {
|
|
1842
1402
|
if (!price) return "N/A";
|
|
1843
1403
|
const productName = price.product?.name || "";
|
|
@@ -1857,277 +1417,826 @@ function formatPlanName3(price) {
|
|
|
1857
1417
|
return interval ? `${planLabel} (${interval})` : planLabel || "N/A";
|
|
1858
1418
|
}
|
|
1859
1419
|
__name(formatPlanName3, "formatPlanName");
|
|
1860
|
-
function SubscriptionsList({ subscriptions, onSubscriptionsChange }) {
|
|
1861
|
-
const [selectedSub, setSelectedSub] =
|
|
1420
|
+
function SubscriptionsList({ subscriptions, onSubscriptionsChange, onChangePlan }) {
|
|
1421
|
+
const [selectedSub, setSelectedSub] = useState9(null);
|
|
1862
1422
|
const handleRowClick = /* @__PURE__ */ __name((subscription) => {
|
|
1863
1423
|
setSelectedSub(subscription);
|
|
1864
1424
|
}, "handleRowClick");
|
|
1865
|
-
return /* @__PURE__ */
|
|
1866
|
-
/* @__PURE__ */
|
|
1867
|
-
/* @__PURE__ */
|
|
1868
|
-
/* @__PURE__ */
|
|
1869
|
-
/* @__PURE__ */
|
|
1870
|
-
/* @__PURE__ */
|
|
1871
|
-
/* @__PURE__ */
|
|
1425
|
+
return /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
1426
|
+
/* @__PURE__ */ jsx18("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs15(Table, { children: [
|
|
1427
|
+
/* @__PURE__ */ jsx18(TableHeader, { className: "bg-muted", children: /* @__PURE__ */ jsxs15(TableRow, { children: [
|
|
1428
|
+
/* @__PURE__ */ jsx18(TableHead, { children: "Status" }),
|
|
1429
|
+
/* @__PURE__ */ jsx18(TableHead, { children: "Plan" }),
|
|
1430
|
+
/* @__PURE__ */ jsx18(TableHead, { children: "Period" }),
|
|
1431
|
+
/* @__PURE__ */ jsx18(TableHead, { className: "text-right", children: "Amount" })
|
|
1872
1432
|
] }) }),
|
|
1873
|
-
/* @__PURE__ */
|
|
1433
|
+
/* @__PURE__ */ jsx18(TableBody, { children: subscriptions.map((subscription) => {
|
|
1874
1434
|
const price = subscription.price;
|
|
1875
1435
|
const amount = price?.unitAmount ? formatCurrency(price.unitAmount, price.currency) : "N/A";
|
|
1876
1436
|
const period = `${formatDate3(subscription.currentPeriodStart)} - ${formatDate3(subscription.currentPeriodEnd)}`;
|
|
1877
|
-
return /* @__PURE__ */
|
|
1437
|
+
return /* @__PURE__ */ jsxs15(
|
|
1878
1438
|
TableRow,
|
|
1879
1439
|
{
|
|
1880
|
-
onClick: () => handleRowClick(subscription),
|
|
1881
|
-
className: "cursor-pointer hover:bg-muted/50",
|
|
1882
|
-
children: [
|
|
1883
|
-
/* @__PURE__ */
|
|
1884
|
-
/* @__PURE__ */
|
|
1885
|
-
/* @__PURE__ */
|
|
1886
|
-
/* @__PURE__ */
|
|
1887
|
-
]
|
|
1440
|
+
onClick: () => handleRowClick(subscription),
|
|
1441
|
+
className: "cursor-pointer hover:bg-muted/50",
|
|
1442
|
+
children: [
|
|
1443
|
+
/* @__PURE__ */ jsx18(TableCell, { children: /* @__PURE__ */ jsx18(SubscriptionStatusBadge, { status: subscription.status, cancelAtPeriodEnd: subscription.cancelAtPeriodEnd }) }),
|
|
1444
|
+
/* @__PURE__ */ jsx18(TableCell, { className: "font-medium", children: formatPlanName3(price) }),
|
|
1445
|
+
/* @__PURE__ */ jsx18(TableCell, { className: "text-muted-foreground text-sm", children: period }),
|
|
1446
|
+
/* @__PURE__ */ jsx18(TableCell, { className: "text-right font-medium", children: amount })
|
|
1447
|
+
]
|
|
1448
|
+
},
|
|
1449
|
+
subscription.id
|
|
1450
|
+
);
|
|
1451
|
+
}) })
|
|
1452
|
+
] }) }),
|
|
1453
|
+
selectedSub && /* @__PURE__ */ jsx18(
|
|
1454
|
+
SubscriptionDetails,
|
|
1455
|
+
{
|
|
1456
|
+
subscription: selectedSub,
|
|
1457
|
+
open: !!selectedSub,
|
|
1458
|
+
onOpenChange: (open) => !open && setSelectedSub(null),
|
|
1459
|
+
onSubscriptionChange: () => {
|
|
1460
|
+
onSubscriptionsChange();
|
|
1461
|
+
setSelectedSub(null);
|
|
1462
|
+
},
|
|
1463
|
+
onChangePlan: onChangePlan ? (sub) => {
|
|
1464
|
+
setSelectedSub(null);
|
|
1465
|
+
onChangePlan(sub);
|
|
1466
|
+
} : void 0
|
|
1467
|
+
}
|
|
1468
|
+
)
|
|
1469
|
+
] });
|
|
1470
|
+
}
|
|
1471
|
+
__name(SubscriptionsList, "SubscriptionsList");
|
|
1472
|
+
|
|
1473
|
+
// src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx
|
|
1474
|
+
import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1475
|
+
function SubscriptionsContainer({ onOpenWizard }) {
|
|
1476
|
+
const [subscriptions, setSubscriptions] = useState10([]);
|
|
1477
|
+
const [loading, setLoading] = useState10(true);
|
|
1478
|
+
const loadSubscriptions = useCallback(async () => {
|
|
1479
|
+
setLoading(true);
|
|
1480
|
+
try {
|
|
1481
|
+
const fetchedSubscriptions = await StripeSubscriptionService.listSubscriptions();
|
|
1482
|
+
setSubscriptions(fetchedSubscriptions);
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
console.error("[SubscriptionsContainer] Failed to load subscriptions:", error);
|
|
1485
|
+
} finally {
|
|
1486
|
+
setLoading(false);
|
|
1487
|
+
}
|
|
1488
|
+
}, []);
|
|
1489
|
+
useEffect5(() => {
|
|
1490
|
+
loadSubscriptions();
|
|
1491
|
+
}, []);
|
|
1492
|
+
const criticalSubscriptions = subscriptions.filter(
|
|
1493
|
+
(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
|
|
1494
|
+
);
|
|
1495
|
+
if (loading) {
|
|
1496
|
+
return /* @__PURE__ */ jsx19("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx19("p", { className: "text-muted-foreground", children: "Loading subscriptions..." }) });
|
|
1497
|
+
}
|
|
1498
|
+
return /* @__PURE__ */ jsxs16("div", { className: "flex w-full flex-col gap-y-6", children: [
|
|
1499
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex items-center justify-between", children: [
|
|
1500
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-x-3", children: [
|
|
1501
|
+
/* @__PURE__ */ jsx19(CreditCard3, { className: "h-8 w-8" }),
|
|
1502
|
+
/* @__PURE__ */ jsx19("h1", { className: "text-3xl font-bold", children: "Subscriptions" })
|
|
1503
|
+
] }),
|
|
1504
|
+
subscriptions.length > 0 && /* @__PURE__ */ jsx19(Button, { onClick: () => onOpenWizard?.(), children: "Subscribe to a Plan" })
|
|
1505
|
+
] }),
|
|
1506
|
+
criticalSubscriptions.map((subscription) => /* @__PURE__ */ jsx19(BillingAlertBanner, { subscription }, subscription.id)),
|
|
1507
|
+
subscriptions.length === 0 && /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-center justify-center py-12 space-y-4", children: [
|
|
1508
|
+
/* @__PURE__ */ jsx19(CreditCard3, { className: "h-16 w-16 text-muted-foreground" }),
|
|
1509
|
+
/* @__PURE__ */ jsxs16("div", { className: "text-center", children: [
|
|
1510
|
+
/* @__PURE__ */ jsx19("h3", { className: "text-xl font-semibold mb-2", children: "No Active Subscriptions" }),
|
|
1511
|
+
/* @__PURE__ */ jsx19("p", { className: "text-muted-foreground mb-6", children: "Choose a subscription plan to get started with our services." }),
|
|
1512
|
+
/* @__PURE__ */ jsx19(Button, { onClick: () => onOpenWizard?.(), children: "Subscribe to a Plan" })
|
|
1513
|
+
] })
|
|
1514
|
+
] }),
|
|
1515
|
+
subscriptions.length > 0 && /* @__PURE__ */ jsx19(
|
|
1516
|
+
SubscriptionsList,
|
|
1517
|
+
{
|
|
1518
|
+
subscriptions,
|
|
1519
|
+
onSubscriptionsChange: loadSubscriptions,
|
|
1520
|
+
onChangePlan: (sub) => onOpenWizard?.(sub)
|
|
1521
|
+
}
|
|
1522
|
+
)
|
|
1523
|
+
] });
|
|
1524
|
+
}
|
|
1525
|
+
__name(SubscriptionsContainer, "SubscriptionsContainer");
|
|
1526
|
+
|
|
1527
|
+
// src/features/billing/stripe-subscription/components/widgets/IntervalToggle.tsx
|
|
1528
|
+
import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1529
|
+
function IntervalToggle({ value, onChange, hasMonthly, hasYearly }) {
|
|
1530
|
+
if (!hasMonthly || !hasYearly) {
|
|
1531
|
+
return null;
|
|
1532
|
+
}
|
|
1533
|
+
return /* @__PURE__ */ jsx20(Tabs, { value, onValueChange: (v) => onChange(v), children: /* @__PURE__ */ jsxs17(TabsList, { children: [
|
|
1534
|
+
/* @__PURE__ */ jsx20(TabsTrigger, { value: "month", children: "Monthly" }),
|
|
1535
|
+
/* @__PURE__ */ jsx20(TabsTrigger, { value: "year", children: "Yearly" })
|
|
1536
|
+
] }) });
|
|
1537
|
+
}
|
|
1538
|
+
__name(IntervalToggle, "IntervalToggle");
|
|
1539
|
+
|
|
1540
|
+
// src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx
|
|
1541
|
+
import { Check } from "lucide-react";
|
|
1542
|
+
import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1543
|
+
function PricingCard({ price, isCurrentPlan = false, isSelected = false, isDisabled = false, isLoading = false, onSelect }) {
|
|
1544
|
+
const description = price.description || price.nickname || "Standard";
|
|
1545
|
+
const features = price.features || [];
|
|
1546
|
+
const formattedPrice = formatCurrency(price.unitAmount, price.currency);
|
|
1547
|
+
const interval = formatInterval(price);
|
|
1548
|
+
const handleKeyDown = /* @__PURE__ */ __name((e) => {
|
|
1549
|
+
if ((e.key === "Enter" || e.key === " ") && !isDisabled && !isCurrentPlan) {
|
|
1550
|
+
e.preventDefault();
|
|
1551
|
+
onSelect(price);
|
|
1552
|
+
}
|
|
1553
|
+
}, "handleKeyDown");
|
|
1554
|
+
const handleClick = /* @__PURE__ */ __name(() => {
|
|
1555
|
+
if (!isDisabled && !isCurrentPlan && !isLoading) {
|
|
1556
|
+
onSelect(price);
|
|
1557
|
+
}
|
|
1558
|
+
}, "handleClick");
|
|
1559
|
+
return /* @__PURE__ */ jsxs18(
|
|
1560
|
+
Card,
|
|
1561
|
+
{
|
|
1562
|
+
role: "radio",
|
|
1563
|
+
"aria-checked": isSelected,
|
|
1564
|
+
"aria-label": `${description} plan at ${formattedPrice} ${interval}`,
|
|
1565
|
+
tabIndex: isDisabled ? -1 : 0,
|
|
1566
|
+
onKeyDown: handleKeyDown,
|
|
1567
|
+
onClick: handleClick,
|
|
1568
|
+
className: cn(
|
|
1569
|
+
"relative cursor-pointer transition-all duration-200 flex flex-col h-full",
|
|
1570
|
+
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
1571
|
+
isCurrentPlan && "bg-muted/30",
|
|
1572
|
+
isSelected && !isCurrentPlan && "ring-2 ring-primary",
|
|
1573
|
+
!isDisabled && !isCurrentPlan && "hover:shadow-md hover:border-primary/50",
|
|
1574
|
+
isDisabled && "opacity-50 pointer-events-none",
|
|
1575
|
+
isLoading && "pointer-events-none"
|
|
1576
|
+
),
|
|
1577
|
+
children: [
|
|
1578
|
+
isCurrentPlan && /* @__PURE__ */ jsx21(Badge, { variant: "secondary", className: "absolute top-2 right-2", children: "Current" }),
|
|
1579
|
+
/* @__PURE__ */ jsx21(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx21("h3", { className: "font-semibold text-lg", children: description }) }),
|
|
1580
|
+
/* @__PURE__ */ jsxs18(CardContent, { className: "pb-4 grow", children: [
|
|
1581
|
+
/* @__PURE__ */ jsxs18("div", { className: "mb-4", children: [
|
|
1582
|
+
/* @__PURE__ */ jsx21("span", { className: "text-3xl font-bold", children: formattedPrice }),
|
|
1583
|
+
/* @__PURE__ */ jsx21("span", { className: "text-muted-foreground ml-1", children: interval })
|
|
1584
|
+
] }),
|
|
1585
|
+
features.length > 0 && /* @__PURE__ */ jsx21("ul", { className: "space-y-2", children: features.map((feature, index) => /* @__PURE__ */ jsxs18("li", { className: "flex items-start gap-2", children: [
|
|
1586
|
+
/* @__PURE__ */ jsx21(Check, { className: "h-4 w-4 text-green-500 mt-0.5 shrink-0" }),
|
|
1587
|
+
/* @__PURE__ */ jsx21("span", { className: "text-sm text-muted-foreground", children: feature })
|
|
1588
|
+
] }, index)) })
|
|
1589
|
+
] }),
|
|
1590
|
+
/* @__PURE__ */ jsx21(CardFooter, { children: /* @__PURE__ */ jsx21(
|
|
1591
|
+
Button,
|
|
1592
|
+
{
|
|
1593
|
+
variant: isCurrentPlan ? "secondary" : isSelected ? "default" : "outline",
|
|
1594
|
+
className: "w-full",
|
|
1595
|
+
disabled: isDisabled || isCurrentPlan || isLoading,
|
|
1596
|
+
children: isLoading ? "Processing..." : isCurrentPlan ? "Current Plan" : isSelected ? "Selected" : "Select Plan"
|
|
1597
|
+
}
|
|
1598
|
+
) })
|
|
1599
|
+
]
|
|
1600
|
+
}
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1603
|
+
__name(PricingCard, "PricingCard");
|
|
1604
|
+
|
|
1605
|
+
// src/features/billing/stripe-subscription/components/widgets/ProductPricingRow.tsx
|
|
1606
|
+
import { jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1607
|
+
function ProductPricingRow({
|
|
1608
|
+
product,
|
|
1609
|
+
prices,
|
|
1610
|
+
currentPriceId,
|
|
1611
|
+
selectedPriceId,
|
|
1612
|
+
loadingPriceId,
|
|
1613
|
+
onSelectPrice
|
|
1614
|
+
}) {
|
|
1615
|
+
if (prices.length === 0) {
|
|
1616
|
+
return null;
|
|
1617
|
+
}
|
|
1618
|
+
return /* @__PURE__ */ jsxs19("div", { className: "space-y-3", children: [
|
|
1619
|
+
/* @__PURE__ */ jsx22("h3", { className: "font-semibold text-lg", children: product.name }),
|
|
1620
|
+
/* @__PURE__ */ jsx22(
|
|
1621
|
+
"div",
|
|
1622
|
+
{
|
|
1623
|
+
className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4",
|
|
1624
|
+
role: "radiogroup",
|
|
1625
|
+
"aria-label": `Pricing options for ${product.name}`,
|
|
1626
|
+
children: prices.sort((a, b) => (a.unitAmount ?? 0) - (b.unitAmount ?? 0)).map((price) => /* @__PURE__ */ jsx22(
|
|
1627
|
+
PricingCard,
|
|
1628
|
+
{
|
|
1629
|
+
price,
|
|
1630
|
+
isCurrentPlan: price.id === currentPriceId,
|
|
1631
|
+
isSelected: price.id === selectedPriceId,
|
|
1632
|
+
isLoading: price.id === loadingPriceId,
|
|
1633
|
+
onSelect: onSelectPrice
|
|
1888
1634
|
},
|
|
1889
|
-
|
|
1890
|
-
)
|
|
1891
|
-
}) })
|
|
1892
|
-
] }) }),
|
|
1893
|
-
selectedSub && /* @__PURE__ */ jsx21(
|
|
1894
|
-
SubscriptionDetails,
|
|
1895
|
-
{
|
|
1896
|
-
subscription: selectedSub,
|
|
1897
|
-
open: !!selectedSub,
|
|
1898
|
-
onOpenChange: (open) => !open && setSelectedSub(null),
|
|
1899
|
-
onSubscriptionChange: () => {
|
|
1900
|
-
onSubscriptionsChange();
|
|
1901
|
-
setSelectedSub(null);
|
|
1902
|
-
}
|
|
1635
|
+
price.id
|
|
1636
|
+
))
|
|
1903
1637
|
}
|
|
1904
1638
|
)
|
|
1905
1639
|
] });
|
|
1906
1640
|
}
|
|
1907
|
-
__name(
|
|
1641
|
+
__name(ProductPricingRow, "ProductPricingRow");
|
|
1908
1642
|
|
|
1909
|
-
// src/features/billing/stripe-subscription/components/
|
|
1910
|
-
import {
|
|
1911
|
-
function
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
const
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
const
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1643
|
+
// src/features/billing/stripe-subscription/components/widgets/ProductPricingList.tsx
|
|
1644
|
+
import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1645
|
+
function isRecurringProduct(prices) {
|
|
1646
|
+
return prices.some((p) => p.priceType === "recurring");
|
|
1647
|
+
}
|
|
1648
|
+
__name(isRecurringProduct, "isRecurringProduct");
|
|
1649
|
+
function getFilteredPrices(prices, selectedInterval) {
|
|
1650
|
+
const isRecurring = isRecurringProduct(prices);
|
|
1651
|
+
if (!isRecurring) {
|
|
1652
|
+
return prices.filter((p) => p.priceType === "one_time");
|
|
1653
|
+
}
|
|
1654
|
+
const intervalPrices = prices.filter(
|
|
1655
|
+
(p) => p.priceType === "recurring" && p.recurring?.interval === selectedInterval
|
|
1656
|
+
);
|
|
1657
|
+
if (intervalPrices.length === 0) {
|
|
1658
|
+
const fallbackInterval = selectedInterval === "month" ? "year" : "month";
|
|
1659
|
+
return prices.filter((p) => p.priceType === "recurring" && p.recurring?.interval === fallbackInterval);
|
|
1660
|
+
}
|
|
1661
|
+
return intervalPrices;
|
|
1662
|
+
}
|
|
1663
|
+
__name(getFilteredPrices, "getFilteredPrices");
|
|
1664
|
+
function ProductPricingList({
|
|
1665
|
+
products,
|
|
1666
|
+
selectedInterval,
|
|
1667
|
+
currentPriceId,
|
|
1668
|
+
selectedPriceId,
|
|
1669
|
+
loadingPriceId,
|
|
1670
|
+
loading = false,
|
|
1671
|
+
onSelectPrice,
|
|
1672
|
+
hideRecurringPrices = false
|
|
1673
|
+
}) {
|
|
1674
|
+
if (loading) {
|
|
1675
|
+
return /* @__PURE__ */ jsx23(ProductPricingListSkeleton, {});
|
|
1676
|
+
}
|
|
1677
|
+
if (products.length === 0) {
|
|
1678
|
+
return /* @__PURE__ */ jsx23("div", { className: "text-center py-8 text-muted-foreground", children: "No plans available" });
|
|
1679
|
+
}
|
|
1680
|
+
const sortedProducts = [...products].sort((a, b) => {
|
|
1681
|
+
const aRecurring = isRecurringProduct(a.stripePrices || []);
|
|
1682
|
+
const bRecurring = isRecurringProduct(b.stripePrices || []);
|
|
1683
|
+
if (aRecurring && !bRecurring) return -1;
|
|
1684
|
+
if (!aRecurring && bRecurring) return 1;
|
|
1685
|
+
return 0;
|
|
1686
|
+
});
|
|
1687
|
+
const filteredProducts = hideRecurringPrices ? sortedProducts.map((product) => ({
|
|
1688
|
+
...product,
|
|
1689
|
+
stripePrices: (product.stripePrices || []).filter((price) => price.priceType !== "recurring")
|
|
1690
|
+
})).filter((product) => product.stripePrices.length > 0) : sortedProducts;
|
|
1691
|
+
return /* @__PURE__ */ jsx23("div", { className: "space-y-6", children: filteredProducts.map((product) => {
|
|
1692
|
+
const allPrices = product.stripePrices || [];
|
|
1693
|
+
const filteredPrices = getFilteredPrices(allPrices, selectedInterval);
|
|
1694
|
+
if (filteredPrices.length === 0) {
|
|
1695
|
+
return null;
|
|
1696
|
+
}
|
|
1697
|
+
return /* @__PURE__ */ jsx23(
|
|
1698
|
+
ProductPricingRow,
|
|
1699
|
+
{
|
|
1700
|
+
product,
|
|
1701
|
+
prices: filteredPrices,
|
|
1702
|
+
currentPriceId,
|
|
1703
|
+
selectedPriceId,
|
|
1704
|
+
loadingPriceId,
|
|
1705
|
+
onSelectPrice
|
|
1706
|
+
},
|
|
1707
|
+
product.id
|
|
1708
|
+
);
|
|
1709
|
+
}) });
|
|
1710
|
+
}
|
|
1711
|
+
__name(ProductPricingList, "ProductPricingList");
|
|
1712
|
+
function ProductPricingListSkeleton() {
|
|
1713
|
+
return /* @__PURE__ */ jsx23("div", { className: "space-y-6", children: [1, 2].map((rowIndex) => /* @__PURE__ */ jsxs20("div", { className: "space-y-3", children: [
|
|
1714
|
+
/* @__PURE__ */ jsx23(Skeleton, { className: "h-6 w-32" }),
|
|
1715
|
+
/* @__PURE__ */ jsx23("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: [1, 2, 3].map((cardIndex) => /* @__PURE__ */ jsxs20("div", { className: "p-4 rounded-lg border animate-pulse space-y-3", children: [
|
|
1716
|
+
/* @__PURE__ */ jsx23(Skeleton, { className: "h-6 w-24" }),
|
|
1717
|
+
/* @__PURE__ */ jsx23(Skeleton, { className: "h-8 w-32" }),
|
|
1718
|
+
/* @__PURE__ */ jsxs20("div", { className: "space-y-2", children: [
|
|
1719
|
+
/* @__PURE__ */ jsx23(Skeleton, { className: "h-4 w-full" }),
|
|
1720
|
+
/* @__PURE__ */ jsx23(Skeleton, { className: "h-4 w-3/4" })
|
|
1721
|
+
] }),
|
|
1722
|
+
/* @__PURE__ */ jsx23(Skeleton, { className: "h-10 w-full" })
|
|
1723
|
+
] }, cardIndex)) })
|
|
1724
|
+
] }, rowIndex)) });
|
|
1725
|
+
}
|
|
1726
|
+
__name(ProductPricingListSkeleton, "ProductPricingListSkeleton");
|
|
1727
|
+
|
|
1728
|
+
// src/features/billing/stripe-subscription/components/widgets/ProrationPreview.tsx
|
|
1729
|
+
import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1730
|
+
function ProrationPreview({ preview }) {
|
|
1731
|
+
return /* @__PURE__ */ jsxs21("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-4", children: [
|
|
1732
|
+
/* @__PURE__ */ jsx24("h4", { className: "font-semibold text-blue-900 mb-3", children: "Proration Breakdown" }),
|
|
1733
|
+
/* @__PURE__ */ jsxs21("div", { className: "space-y-2", children: [
|
|
1734
|
+
preview.lineItems.map((item, index) => /* @__PURE__ */ jsxs21("div", { className: "flex justify-between text-sm", children: [
|
|
1735
|
+
/* @__PURE__ */ jsx24("span", { className: "text-blue-800", children: item.description }),
|
|
1736
|
+
/* @__PURE__ */ jsx24("span", { className: `font-medium ${item.amount < 0 ? "text-green-600" : "text-blue-900"}`, children: formatCurrency(item.amount, preview.currency) })
|
|
1737
|
+
] }, index)),
|
|
1738
|
+
/* @__PURE__ */ jsx24("div", { className: "border-t border-blue-200 pt-2 mt-2", children: /* @__PURE__ */ jsxs21("div", { className: "flex justify-between font-semibold", children: [
|
|
1739
|
+
/* @__PURE__ */ jsx24("span", { className: "text-blue-900", children: "Net Due Today" }),
|
|
1740
|
+
/* @__PURE__ */ jsx24("span", { className: "text-blue-900", children: formatCurrency(preview.immediateCharge, preview.currency) })
|
|
1741
|
+
] }) }),
|
|
1742
|
+
preview.lineItems.length > 0 && preview.lineItems[0].period && /* @__PURE__ */ jsxs21("div", { className: "text-xs text-blue-700 mt-2", children: [
|
|
1743
|
+
"Next invoice on ",
|
|
1744
|
+
formatDate3(preview.lineItems[0].period.end),
|
|
1745
|
+
" for",
|
|
1746
|
+
" ",
|
|
1747
|
+
formatCurrency(preview.amountDue, preview.currency)
|
|
1748
|
+
] })
|
|
1749
|
+
] })
|
|
1750
|
+
] });
|
|
1751
|
+
}
|
|
1752
|
+
__name(ProrationPreview, "ProrationPreview");
|
|
1753
|
+
|
|
1754
|
+
// src/features/billing/stripe-subscription/components/widgets/SubscriptionConfirmation.tsx
|
|
1755
|
+
import { Check as Check2, Loader2 } from "lucide-react";
|
|
1756
|
+
import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1757
|
+
function SubscriptionConfirmation({ price, isLoading, onConfirm, onCancel }) {
|
|
1758
|
+
const productName = price.product?.name || price.nickname || "Selected Plan";
|
|
1759
|
+
const productDescription = price.product?.description || price.description;
|
|
1760
|
+
const features = price.features || [];
|
|
1761
|
+
return /* @__PURE__ */ jsxs22("div", { className: "bg-accent/10 border border-accent/30 rounded-lg p-4", children: [
|
|
1762
|
+
/* @__PURE__ */ jsx25("h4", { className: "font-semibold mb-3", children: "Confirm Your Subscription" }),
|
|
1763
|
+
/* @__PURE__ */ jsxs22("div", { className: "space-y-3", children: [
|
|
1764
|
+
/* @__PURE__ */ jsxs22("div", { children: [
|
|
1765
|
+
/* @__PURE__ */ jsx25("div", { className: "font-medium", children: productName }),
|
|
1766
|
+
productDescription && /* @__PURE__ */ jsx25("div", { className: "text-sm text-muted-foreground", children: productDescription })
|
|
1767
|
+
] }),
|
|
1768
|
+
/* @__PURE__ */ jsxs22("div", { className: "text-lg font-semibold", children: [
|
|
1769
|
+
formatCurrency(price.unitAmount, price.currency),
|
|
1770
|
+
/* @__PURE__ */ jsx25("span", { className: "text-sm font-normal text-muted-foreground", children: formatInterval(price) })
|
|
1771
|
+
] }),
|
|
1772
|
+
features.length > 0 && /* @__PURE__ */ jsx25("div", { className: "space-y-1", children: features.map((feature, index) => /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2 text-sm ", children: [
|
|
1773
|
+
/* @__PURE__ */ jsx25(Check2, { className: "h-4 w-4 text-primary" }),
|
|
1774
|
+
/* @__PURE__ */ jsx25("span", { children: feature })
|
|
1775
|
+
] }, index)) }),
|
|
1776
|
+
/* @__PURE__ */ jsxs22("div", { className: "flex justify-end gap-3 pt-2 border-t border-accent/30", children: [
|
|
1777
|
+
/* @__PURE__ */ jsx25(Button, { variant: "outline", onClick: onCancel, disabled: isLoading, children: "Cancel" }),
|
|
1778
|
+
/* @__PURE__ */ jsx25(Button, { onClick: onConfirm, disabled: isLoading, children: isLoading ? /* @__PURE__ */ jsxs22(Fragment5, { children: [
|
|
1779
|
+
/* @__PURE__ */ jsx25(Loader2, { className: "h-4 w-4 animate-spin mr-2" }),
|
|
1780
|
+
"Processing..."
|
|
1781
|
+
] }) : "Subscribe" })
|
|
1782
|
+
] })
|
|
1783
|
+
] })
|
|
1784
|
+
] });
|
|
1785
|
+
}
|
|
1786
|
+
__name(SubscriptionConfirmation, "SubscriptionConfirmation");
|
|
1787
|
+
|
|
1788
|
+
// src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx
|
|
1789
|
+
import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef2 } from "react";
|
|
1790
|
+
|
|
1791
|
+
// src/features/billing/stripe-subscription/hooks/useSubscriptionWizard.ts
|
|
1792
|
+
import { useCallback as useCallback2, useMemo, useReducer, useRef } from "react";
|
|
1793
|
+
var initialState = {
|
|
1794
|
+
step: "plan-selection",
|
|
1795
|
+
selectedPrice: null,
|
|
1796
|
+
selectedInterval: "month",
|
|
1797
|
+
hasPaymentMethod: false,
|
|
1798
|
+
isProcessing: false,
|
|
1799
|
+
error: null,
|
|
1800
|
+
prorationPreview: null
|
|
1801
|
+
};
|
|
1802
|
+
function wizardReducer(state, action) {
|
|
1803
|
+
switch (action.type) {
|
|
1804
|
+
case "SET_STEP":
|
|
1805
|
+
return { ...state, step: action.step, error: null };
|
|
1806
|
+
case "SELECT_PRICE":
|
|
1807
|
+
return { ...state, selectedPrice: action.price };
|
|
1808
|
+
case "SET_INTERVAL":
|
|
1809
|
+
return { ...state, selectedInterval: action.interval };
|
|
1810
|
+
case "SET_HAS_PAYMENT_METHOD":
|
|
1811
|
+
return { ...state, hasPaymentMethod: action.hasMethod };
|
|
1812
|
+
case "SET_PROCESSING":
|
|
1813
|
+
return { ...state, isProcessing: action.isProcessing };
|
|
1814
|
+
case "SET_ERROR":
|
|
1815
|
+
return { ...state, error: action.error };
|
|
1816
|
+
case "SET_PRORATION_PREVIEW":
|
|
1817
|
+
return { ...state, prorationPreview: action.preview };
|
|
1818
|
+
case "RESET":
|
|
1819
|
+
return initialState;
|
|
1820
|
+
default:
|
|
1821
|
+
return state;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
__name(wizardReducer, "wizardReducer");
|
|
1825
|
+
function useSubscriptionWizard({ subscription, onSuccess, onClose }) {
|
|
1826
|
+
const [state, dispatch] = useReducer(wizardReducer, {
|
|
1827
|
+
...initialState,
|
|
1828
|
+
selectedPrice: subscription?.price || null
|
|
1829
|
+
});
|
|
1830
|
+
const onSuccessRef = useRef(onSuccess);
|
|
1831
|
+
const onCloseRef = useRef(onClose);
|
|
1832
|
+
onSuccessRef.current = onSuccess;
|
|
1833
|
+
onCloseRef.current = onClose;
|
|
1834
|
+
const checkPaymentMethod = useCallback2(async () => {
|
|
1927
1835
|
try {
|
|
1928
|
-
const
|
|
1929
|
-
|
|
1836
|
+
const methods = await StripeCustomerService.listPaymentMethods();
|
|
1837
|
+
dispatch({ type: "SET_HAS_PAYMENT_METHOD", hasMethod: methods.length > 0 });
|
|
1930
1838
|
} catch (error) {
|
|
1931
|
-
console.error("[
|
|
1932
|
-
|
|
1933
|
-
setLoading(false);
|
|
1839
|
+
console.error("[useSubscriptionWizard] Failed to check payment methods:", error);
|
|
1840
|
+
dispatch({ type: "SET_HAS_PAYMENT_METHOD", hasMethod: false });
|
|
1934
1841
|
}
|
|
1935
|
-
},
|
|
1936
|
-
const
|
|
1937
|
-
|
|
1842
|
+
}, []);
|
|
1843
|
+
const selectPrice = useCallback2((price) => {
|
|
1844
|
+
dispatch({ type: "SELECT_PRICE", price });
|
|
1845
|
+
}, []);
|
|
1846
|
+
const setInterval = useCallback2((interval) => {
|
|
1847
|
+
dispatch({ type: "SET_INTERVAL", interval });
|
|
1848
|
+
}, []);
|
|
1849
|
+
const goToStep = useCallback2((step) => {
|
|
1850
|
+
dispatch({ type: "SET_STEP", step });
|
|
1851
|
+
}, []);
|
|
1852
|
+
const goToReview = useCallback2(async () => {
|
|
1853
|
+
if (!state.selectedPrice) return;
|
|
1854
|
+
dispatch({ type: "SET_PROCESSING", isProcessing: true });
|
|
1938
1855
|
try {
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
}
|
|
1856
|
+
await checkPaymentMethod();
|
|
1857
|
+
if (subscription && state.selectedPrice.id !== subscription.price?.id) {
|
|
1858
|
+
const preview = await StripeSubscriptionService.getProrationPreview({
|
|
1859
|
+
subscriptionId: subscription.id,
|
|
1860
|
+
newPriceId: state.selectedPrice.id
|
|
1861
|
+
});
|
|
1862
|
+
dispatch({ type: "SET_PRORATION_PREVIEW", preview });
|
|
1945
1863
|
}
|
|
1946
|
-
|
|
1947
|
-
setPricesByProduct(grouped);
|
|
1864
|
+
dispatch({ type: "SET_STEP", step: "review" });
|
|
1948
1865
|
} catch (error) {
|
|
1949
|
-
console.error("[
|
|
1866
|
+
console.error("[useSubscriptionWizard] Error preparing review:", error);
|
|
1867
|
+
dispatch({ type: "SET_ERROR", error: error?.message || "Failed to prepare review" });
|
|
1950
1868
|
} finally {
|
|
1951
|
-
|
|
1952
|
-
}
|
|
1953
|
-
}, "loadPricingData");
|
|
1954
|
-
const checkPaymentMethod = /* @__PURE__ */ __name(async () => {
|
|
1955
|
-
try {
|
|
1956
|
-
const paymentMethods = await StripeCustomerService.listPaymentMethods();
|
|
1957
|
-
const hasMethod = paymentMethods.length > 0;
|
|
1958
|
-
setHasPaymentMethod(hasMethod);
|
|
1959
|
-
} catch (error) {
|
|
1960
|
-
console.error("[SubscriptionsContainer] Failed to check payment methods:", error);
|
|
1961
|
-
setHasPaymentMethod(false);
|
|
1869
|
+
dispatch({ type: "SET_PROCESSING", isProcessing: false });
|
|
1962
1870
|
}
|
|
1963
|
-
},
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1871
|
+
}, [state.selectedPrice, subscription, checkPaymentMethod]);
|
|
1872
|
+
const confirmSubscription = useCallback2(async () => {
|
|
1873
|
+
if (!state.selectedPrice) return;
|
|
1874
|
+
dispatch({ type: "SET_PROCESSING", isProcessing: true });
|
|
1875
|
+
dispatch({ type: "SET_ERROR", error: null });
|
|
1968
1876
|
try {
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
setPaymentConfirmationState("error");
|
|
1979
|
-
setPaymentError(confirmation.error || "Payment confirmation failed");
|
|
1980
|
-
setCreatingSubscription(false);
|
|
1981
|
-
return;
|
|
1982
|
-
}
|
|
1983
|
-
await StripeSubscriptionService.syncSubscription({
|
|
1984
|
-
subscriptionId: result.subscription.id
|
|
1877
|
+
if (subscription) {
|
|
1878
|
+
await StripeSubscriptionService.changePlan({
|
|
1879
|
+
id: subscription.id,
|
|
1880
|
+
newPriceId: state.selectedPrice.id
|
|
1881
|
+
});
|
|
1882
|
+
} else {
|
|
1883
|
+
await StripeSubscriptionService.createSubscription({
|
|
1884
|
+
id: crypto.randomUUID(),
|
|
1885
|
+
priceId: state.selectedPrice.id
|
|
1985
1886
|
});
|
|
1986
1887
|
}
|
|
1987
|
-
|
|
1988
|
-
|
|
1888
|
+
onSuccessRef.current();
|
|
1889
|
+
onCloseRef.current();
|
|
1989
1890
|
} catch (error) {
|
|
1990
|
-
console.error("[
|
|
1891
|
+
console.error("[useSubscriptionWizard] Subscription error:", error);
|
|
1892
|
+
if (error?.status === 409 || error?.response?.status === 409) {
|
|
1893
|
+
dispatch({
|
|
1894
|
+
type: "SET_ERROR",
|
|
1895
|
+
error: "You already have an active subscription. Please change your existing plan instead."
|
|
1896
|
+
});
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1991
1899
|
if (error?.status === 402 || error?.response?.status === 402) {
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
} else {
|
|
1996
|
-
setPaymentConfirmationState("error");
|
|
1997
|
-
setPaymentError(error?.message || "Failed to create subscription");
|
|
1900
|
+
dispatch({ type: "SET_HAS_PAYMENT_METHOD", hasMethod: false });
|
|
1901
|
+
dispatch({ type: "SET_STEP", step: "payment-method" });
|
|
1902
|
+
return;
|
|
1998
1903
|
}
|
|
1904
|
+
dispatch({ type: "SET_ERROR", error: error?.message || "Failed to process subscription" });
|
|
1999
1905
|
} finally {
|
|
2000
|
-
|
|
2001
|
-
}
|
|
2002
|
-
}, "createSubscriptionWithPrice");
|
|
2003
|
-
const handleSelectPrice = /* @__PURE__ */ __name(async (price) => {
|
|
2004
|
-
const priceId = price.id;
|
|
2005
|
-
if (!hasPaymentMethod) {
|
|
2006
|
-
setPendingPriceId(priceId);
|
|
2007
|
-
setShowPaymentMethodEditor(true);
|
|
2008
|
-
return;
|
|
2009
|
-
}
|
|
2010
|
-
await createSubscriptionWithPrice(priceId);
|
|
2011
|
-
}, "handleSelectPrice");
|
|
2012
|
-
const handlePaymentMethodSuccess = /* @__PURE__ */ __name(async () => {
|
|
2013
|
-
setShowPaymentMethodEditor(false);
|
|
2014
|
-
setHasPaymentMethod(true);
|
|
2015
|
-
if (pendingPriceId) {
|
|
2016
|
-
await createSubscriptionWithPrice(pendingPriceId);
|
|
2017
|
-
setPendingPriceId(null);
|
|
1906
|
+
dispatch({ type: "SET_PROCESSING", isProcessing: false });
|
|
2018
1907
|
}
|
|
2019
|
-
},
|
|
2020
|
-
|
|
2021
|
-
|
|
1908
|
+
}, [state.selectedPrice, subscription]);
|
|
1909
|
+
const handlePaymentMethodSuccess = useCallback2(async () => {
|
|
1910
|
+
dispatch({ type: "SET_HAS_PAYMENT_METHOD", hasMethod: true });
|
|
1911
|
+
dispatch({ type: "SET_STEP", step: "review" });
|
|
1912
|
+
}, []);
|
|
1913
|
+
const reset = useCallback2(() => {
|
|
1914
|
+
dispatch({ type: "RESET" });
|
|
2022
1915
|
}, []);
|
|
1916
|
+
const actions = useMemo(
|
|
1917
|
+
() => ({
|
|
1918
|
+
selectPrice,
|
|
1919
|
+
setInterval,
|
|
1920
|
+
goToStep,
|
|
1921
|
+
goToReview,
|
|
1922
|
+
confirmSubscription,
|
|
1923
|
+
handlePaymentMethodSuccess,
|
|
1924
|
+
checkPaymentMethod,
|
|
1925
|
+
reset
|
|
1926
|
+
}),
|
|
1927
|
+
[
|
|
1928
|
+
selectPrice,
|
|
1929
|
+
setInterval,
|
|
1930
|
+
goToStep,
|
|
1931
|
+
goToReview,
|
|
1932
|
+
confirmSubscription,
|
|
1933
|
+
handlePaymentMethodSuccess,
|
|
1934
|
+
checkPaymentMethod,
|
|
1935
|
+
reset
|
|
1936
|
+
]
|
|
1937
|
+
);
|
|
1938
|
+
return {
|
|
1939
|
+
state,
|
|
1940
|
+
actions
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
__name(useSubscriptionWizard, "useSubscriptionWizard");
|
|
1944
|
+
|
|
1945
|
+
// src/features/billing/stripe-subscription/components/wizards/WizardProgressIndicator.tsx
|
|
1946
|
+
import { Check as Check3 } from "lucide-react";
|
|
1947
|
+
import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1948
|
+
var STEPS = [
|
|
1949
|
+
{ key: "plan-selection", label: "Select Plan" },
|
|
1950
|
+
{ key: "review", label: "Review" },
|
|
1951
|
+
{ key: "payment-method", label: "Payment" }
|
|
1952
|
+
];
|
|
1953
|
+
function getStepIndex(step) {
|
|
1954
|
+
return STEPS.findIndex((s) => s.key === step);
|
|
1955
|
+
}
|
|
1956
|
+
__name(getStepIndex, "getStepIndex");
|
|
1957
|
+
function WizardProgressIndicator({ currentStep }) {
|
|
1958
|
+
const currentIndex = getStepIndex(currentStep);
|
|
1959
|
+
return /* @__PURE__ */ jsx26("div", { className: "flex items-center justify-center gap-x-2 py-4", children: STEPS.map((step, index) => {
|
|
1960
|
+
const isCompleted = index < currentIndex;
|
|
1961
|
+
const isCurrent = index === currentIndex;
|
|
1962
|
+
return /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-x-2", children: [
|
|
1963
|
+
/* @__PURE__ */ jsx26(
|
|
1964
|
+
"div",
|
|
1965
|
+
{
|
|
1966
|
+
className: `flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium ${isCompleted ? "bg-primary text-primary-foreground" : isCurrent ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
1967
|
+
children: isCompleted ? /* @__PURE__ */ jsx26(Check3, { className: "h-4 w-4" }) : index + 1
|
|
1968
|
+
}
|
|
1969
|
+
),
|
|
1970
|
+
/* @__PURE__ */ jsx26(
|
|
1971
|
+
"span",
|
|
1972
|
+
{
|
|
1973
|
+
className: `text-sm ${isCurrent ? "font-medium text-foreground" : "text-muted-foreground"}`,
|
|
1974
|
+
children: step.label
|
|
1975
|
+
}
|
|
1976
|
+
),
|
|
1977
|
+
index < STEPS.length - 1 && /* @__PURE__ */ jsx26(
|
|
1978
|
+
"div",
|
|
1979
|
+
{
|
|
1980
|
+
className: `h-0.5 w-8 ${index < currentIndex ? "bg-primary" : "bg-muted"}`
|
|
1981
|
+
}
|
|
1982
|
+
)
|
|
1983
|
+
] }, step.key);
|
|
1984
|
+
}) });
|
|
1985
|
+
}
|
|
1986
|
+
__name(WizardProgressIndicator, "WizardProgressIndicator");
|
|
1987
|
+
|
|
1988
|
+
// src/features/billing/stripe-subscription/components/wizards/WizardStepPlanSelection.tsx
|
|
1989
|
+
import { useEffect as useEffect6, useMemo as useMemo2, useState as useState11 } from "react";
|
|
1990
|
+
import { jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1991
|
+
function WizardStepPlanSelection({
|
|
1992
|
+
selectedPrice,
|
|
1993
|
+
selectedInterval,
|
|
1994
|
+
currentPriceId,
|
|
1995
|
+
hideRecurringPrices,
|
|
1996
|
+
onSelectPrice,
|
|
1997
|
+
onIntervalChange,
|
|
1998
|
+
onNext,
|
|
1999
|
+
isProcessing
|
|
2000
|
+
}) {
|
|
2001
|
+
const [products, setProducts] = useState11([]);
|
|
2002
|
+
const [loading, setLoading] = useState11(true);
|
|
2023
2003
|
useEffect6(() => {
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2004
|
+
const loadProducts = /* @__PURE__ */ __name(async () => {
|
|
2005
|
+
try {
|
|
2006
|
+
const fetchedProducts = await StripeProductService.listProducts({ active: true });
|
|
2007
|
+
setProducts(fetchedProducts);
|
|
2008
|
+
} catch (error) {
|
|
2009
|
+
console.error("[WizardStepPlanSelection] Failed to load products:", error);
|
|
2010
|
+
} finally {
|
|
2011
|
+
setLoading(false);
|
|
2012
|
+
}
|
|
2013
|
+
}, "loadProducts");
|
|
2014
|
+
loadProducts();
|
|
2015
|
+
}, []);
|
|
2016
|
+
const { hasMonthly, hasYearly } = useMemo2(() => {
|
|
2017
|
+
let hasMonthly2 = false;
|
|
2018
|
+
let hasYearly2 = false;
|
|
2019
|
+
for (const product of products) {
|
|
2020
|
+
for (const price of product.stripePrices || []) {
|
|
2021
|
+
if (price.priceType === "recurring" && price.recurring?.interval === "month") {
|
|
2022
|
+
hasMonthly2 = true;
|
|
2023
|
+
}
|
|
2024
|
+
if (price.priceType === "recurring" && price.recurring?.interval === "year") {
|
|
2025
|
+
hasYearly2 = true;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2027
2028
|
}
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2029
|
+
return { hasMonthly: hasMonthly2, hasYearly: hasYearly2 };
|
|
2030
|
+
}, [products]);
|
|
2031
|
+
const handleSelectPrice = /* @__PURE__ */ __name((price) => {
|
|
2032
|
+
onSelectPrice(price);
|
|
2033
|
+
}, "handleSelectPrice");
|
|
2034
|
+
return /* @__PURE__ */ jsxs24("div", { className: "space-y-6", children: [
|
|
2035
|
+
/* @__PURE__ */ jsx27("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx27(
|
|
2036
|
+
IntervalToggle,
|
|
2037
|
+
{
|
|
2038
|
+
value: selectedInterval,
|
|
2039
|
+
onChange: onIntervalChange,
|
|
2040
|
+
hasMonthly,
|
|
2041
|
+
hasYearly
|
|
2042
|
+
}
|
|
2043
|
+
) }),
|
|
2044
|
+
/* @__PURE__ */ jsx27(
|
|
2045
|
+
ProductPricingList,
|
|
2046
|
+
{
|
|
2047
|
+
products,
|
|
2048
|
+
selectedInterval,
|
|
2049
|
+
currentPriceId,
|
|
2050
|
+
selectedPriceId: selectedPrice?.id,
|
|
2051
|
+
loading,
|
|
2052
|
+
hideRecurringPrices,
|
|
2053
|
+
onSelectPrice: handleSelectPrice
|
|
2054
|
+
}
|
|
2055
|
+
),
|
|
2056
|
+
/* @__PURE__ */ jsx27("div", { className: "flex justify-end pt-4 border-t", children: /* @__PURE__ */ jsx27(Button, { onClick: onNext, disabled: !selectedPrice || isProcessing, children: isProcessing ? "Loading..." : "Next: Review" }) })
|
|
2057
|
+
] });
|
|
2058
|
+
}
|
|
2059
|
+
__name(WizardStepPlanSelection, "WizardStepPlanSelection");
|
|
2060
|
+
|
|
2061
|
+
// src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx
|
|
2062
|
+
import { AlertCircle } from "lucide-react";
|
|
2063
|
+
import { jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2064
|
+
function WizardStepReview({
|
|
2065
|
+
selectedPrice,
|
|
2066
|
+
subscription,
|
|
2067
|
+
prorationPreview,
|
|
2068
|
+
hasPaymentMethod,
|
|
2069
|
+
error,
|
|
2070
|
+
isProcessing,
|
|
2071
|
+
onBack,
|
|
2072
|
+
onAddPaymentMethod,
|
|
2073
|
+
onConfirm
|
|
2074
|
+
}) {
|
|
2075
|
+
if (!selectedPrice) {
|
|
2076
|
+
return /* @__PURE__ */ jsx28("div", { className: "text-center py-8 text-muted-foreground", children: "No plan selected. Please go back and select a plan." });
|
|
2034
2077
|
}
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
/* @__PURE__ */ jsx22("p", { className: "font-medium", children: "Processing payment..." }),
|
|
2049
|
-
/* @__PURE__ */ jsx22("p", { className: "text-sm text-muted-foreground", children: "Please complete any verification if prompted." })
|
|
2050
|
-
] })
|
|
2051
|
-
] }),
|
|
2052
|
-
paymentConfirmationState === "success" && /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center justify-center py-8 space-y-4 bg-green-50 rounded-lg border border-green-200", children: [
|
|
2053
|
-
/* @__PURE__ */ jsx22(CheckCircle2, { className: "h-12 w-12 text-green-500" }),
|
|
2054
|
-
/* @__PURE__ */ jsxs19("div", { className: "text-center", children: [
|
|
2055
|
-
/* @__PURE__ */ jsx22("p", { className: "font-medium text-green-600", children: "Payment successful!" }),
|
|
2056
|
-
/* @__PURE__ */ jsx22("p", { className: "text-sm text-muted-foreground", children: "Your subscription is now active." })
|
|
2057
|
-
] })
|
|
2058
|
-
] }),
|
|
2059
|
-
paymentConfirmationState === "error" && /* @__PURE__ */ jsxs19(Alert, { variant: "destructive", children: [
|
|
2060
|
-
/* @__PURE__ */ jsx22(AlertTitle, { children: "Payment Failed" }),
|
|
2061
|
-
/* @__PURE__ */ jsxs19(AlertDescription, { className: "mt-2", children: [
|
|
2062
|
-
/* @__PURE__ */ jsx22("p", { className: "mb-4", children: paymentError || "We couldn't process your payment. Please try again." }),
|
|
2063
|
-
/* @__PURE__ */ jsx22(
|
|
2064
|
-
Button,
|
|
2065
|
-
{
|
|
2066
|
-
onClick: () => {
|
|
2067
|
-
setPaymentConfirmationState("idle");
|
|
2068
|
-
setPaymentError(null);
|
|
2069
|
-
},
|
|
2070
|
-
variant: "outline",
|
|
2071
|
-
children: "Try Again"
|
|
2072
|
-
}
|
|
2073
|
-
)
|
|
2074
|
-
] })
|
|
2075
|
-
] }),
|
|
2076
|
-
paymentConfirmationState === "idle" && !isConfirming && /* @__PURE__ */ jsxs19(Fragment5, { children: [
|
|
2077
|
-
/* @__PURE__ */ jsxs19("div", { className: "text-center", children: [
|
|
2078
|
-
/* @__PURE__ */ jsx22(CreditCard3, { className: "text-muted-foreground mx-auto h-16 w-16 mb-4" }),
|
|
2079
|
-
/* @__PURE__ */ jsx22("h3", { className: "mb-2 text-xl font-semibold", children: "Choose Your Plan" }),
|
|
2080
|
-
/* @__PURE__ */ jsx22("p", { className: "text-muted-foreground mb-6", children: "Select a subscription plan to get started with our services." })
|
|
2078
|
+
const isChangingPlan = subscription && subscription.price?.id !== selectedPrice.id;
|
|
2079
|
+
const formatInterval2 = /* @__PURE__ */ __name((price) => {
|
|
2080
|
+
if (price.priceType === "one_time") return "one-time";
|
|
2081
|
+
const interval = price.recurring?.interval || "month";
|
|
2082
|
+
return interval === "year" ? "yearly" : "monthly";
|
|
2083
|
+
}, "formatInterval");
|
|
2084
|
+
return /* @__PURE__ */ jsxs25("div", { className: "space-y-6", children: [
|
|
2085
|
+
/* @__PURE__ */ jsxs25("div", { className: "bg-muted/50 rounded-lg p-4 space-y-3", children: [
|
|
2086
|
+
/* @__PURE__ */ jsx28("h3", { className: "font-semibold text-lg", children: "Selected Plan" }),
|
|
2087
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex justify-between items-center", children: [
|
|
2088
|
+
/* @__PURE__ */ jsxs25("div", { children: [
|
|
2089
|
+
/* @__PURE__ */ jsx28("p", { className: "font-medium", children: selectedPrice.product?.name }),
|
|
2090
|
+
selectedPrice.nickname && /* @__PURE__ */ jsx28("p", { className: "text-sm text-muted-foreground", children: selectedPrice.nickname })
|
|
2081
2091
|
] }),
|
|
2082
|
-
/* @__PURE__ */
|
|
2083
|
-
|
|
2084
|
-
{
|
|
2085
|
-
|
|
2086
|
-
pricesByProduct,
|
|
2087
|
-
loading: loadingPricing,
|
|
2088
|
-
loadingPriceId: creatingSubscription ? pendingPriceId ?? void 0 : void 0,
|
|
2089
|
-
onSelectPrice: handleSelectPrice
|
|
2090
|
-
}
|
|
2091
|
-
)
|
|
2092
|
+
/* @__PURE__ */ jsxs25("div", { className: "text-right", children: [
|
|
2093
|
+
/* @__PURE__ */ jsx28("p", { className: "font-semibold text-lg", children: formatCurrency(selectedPrice.unitAmount || 0, selectedPrice.currency) }),
|
|
2094
|
+
/* @__PURE__ */ jsx28("p", { className: "text-sm text-muted-foreground", children: formatInterval2(selectedPrice) })
|
|
2095
|
+
] })
|
|
2092
2096
|
] })
|
|
2093
2097
|
] }),
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
{
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2098
|
+
isChangingPlan && prorationPreview && /* @__PURE__ */ jsxs25("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-4 space-y-2", children: [
|
|
2099
|
+
/* @__PURE__ */ jsx28("h4", { className: "font-medium text-blue-800", children: "Proration Summary" }),
|
|
2100
|
+
/* @__PURE__ */ jsx28("p", { className: "text-sm text-blue-700", children: "Your next charge will be adjusted to account for the plan change." }),
|
|
2101
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex justify-between text-sm", children: [
|
|
2102
|
+
/* @__PURE__ */ jsx28("span", { className: "text-blue-600", children: "Amount due now:" }),
|
|
2103
|
+
/* @__PURE__ */ jsx28("span", { className: "font-medium text-blue-800", children: formatCurrency(prorationPreview.amountDue, prorationPreview.currency) })
|
|
2104
|
+
] })
|
|
2105
|
+
] }),
|
|
2106
|
+
/* @__PURE__ */ jsx28("div", { className: "border rounded-lg p-4", children: /* @__PURE__ */ jsxs25("div", { className: "flex justify-between items-center", children: [
|
|
2107
|
+
/* @__PURE__ */ jsxs25("div", { children: [
|
|
2108
|
+
/* @__PURE__ */ jsx28("h4", { className: "font-medium", children: "Payment Method" }),
|
|
2109
|
+
/* @__PURE__ */ jsx28("p", { className: "text-sm text-muted-foreground", children: hasPaymentMethod ? "A payment method is on file" : "No payment method on file" })
|
|
2110
|
+
] }),
|
|
2111
|
+
!hasPaymentMethod && /* @__PURE__ */ jsx28(Button, { variant: "outline", onClick: onAddPaymentMethod, children: "Add Payment Method" })
|
|
2112
|
+
] }) }),
|
|
2113
|
+
error && /* @__PURE__ */ jsxs25(Alert, { variant: "destructive", children: [
|
|
2114
|
+
/* @__PURE__ */ jsx28(AlertCircle, { className: "h-4 w-4" }),
|
|
2115
|
+
/* @__PURE__ */ jsx28(AlertDescription, { children: error })
|
|
2116
|
+
] }),
|
|
2117
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex justify-between pt-4 border-t", children: [
|
|
2118
|
+
/* @__PURE__ */ jsx28(Button, { variant: "outline", onClick: onBack, disabled: isProcessing, children: "Back" }),
|
|
2119
|
+
/* @__PURE__ */ jsx28(
|
|
2120
|
+
Button,
|
|
2121
|
+
{
|
|
2122
|
+
onClick: hasPaymentMethod ? onConfirm : onAddPaymentMethod,
|
|
2123
|
+
disabled: isProcessing,
|
|
2124
|
+
children: isProcessing ? "Processing..." : hasPaymentMethod ? isChangingPlan ? "Confirm Plan Change" : "Subscribe Now" : "Add Payment Method"
|
|
2104
2125
|
}
|
|
2126
|
+
)
|
|
2127
|
+
] })
|
|
2128
|
+
] });
|
|
2129
|
+
}
|
|
2130
|
+
__name(WizardStepReview, "WizardStepReview");
|
|
2131
|
+
|
|
2132
|
+
// src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx
|
|
2133
|
+
import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2134
|
+
function WizardStepPaymentMethod({
|
|
2135
|
+
onBack,
|
|
2136
|
+
onSuccess,
|
|
2137
|
+
isProcessing
|
|
2138
|
+
}) {
|
|
2139
|
+
return /* @__PURE__ */ jsxs26("div", { className: "space-y-6", children: [
|
|
2140
|
+
/* @__PURE__ */ jsxs26("div", { className: "text-center", children: [
|
|
2141
|
+
/* @__PURE__ */ jsx29("h3", { className: "font-semibold text-lg", children: "Add Payment Method" }),
|
|
2142
|
+
/* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground", children: "Enter your card details to complete your subscription" })
|
|
2143
|
+
] }),
|
|
2144
|
+
/* @__PURE__ */ jsx29(
|
|
2145
|
+
PaymentMethodForm,
|
|
2146
|
+
{
|
|
2147
|
+
onSuccess,
|
|
2148
|
+
onCancel: onBack,
|
|
2149
|
+
isLoading: isProcessing
|
|
2150
|
+
}
|
|
2151
|
+
)
|
|
2152
|
+
] });
|
|
2153
|
+
}
|
|
2154
|
+
__name(WizardStepPaymentMethod, "WizardStepPaymentMethod");
|
|
2155
|
+
|
|
2156
|
+
// src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx
|
|
2157
|
+
import { jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2158
|
+
function SubscriptionWizard({
|
|
2159
|
+
open,
|
|
2160
|
+
onOpenChange,
|
|
2161
|
+
onSuccess,
|
|
2162
|
+
hasActiveRecurringSubscription,
|
|
2163
|
+
subscription
|
|
2164
|
+
}) {
|
|
2165
|
+
const handleClose = useCallback3(() => onOpenChange(false), [onOpenChange]);
|
|
2166
|
+
const { state, actions } = useSubscriptionWizard({
|
|
2167
|
+
subscription,
|
|
2168
|
+
onSuccess,
|
|
2169
|
+
onClose: handleClose
|
|
2170
|
+
});
|
|
2171
|
+
const hasCheckedRef = useRef2(false);
|
|
2172
|
+
useEffect7(() => {
|
|
2173
|
+
if (open && !hasCheckedRef.current) {
|
|
2174
|
+
hasCheckedRef.current = true;
|
|
2175
|
+
actions.checkPaymentMethod();
|
|
2176
|
+
}
|
|
2177
|
+
if (!open) {
|
|
2178
|
+
hasCheckedRef.current = false;
|
|
2179
|
+
}
|
|
2180
|
+
}, [open, actions.checkPaymentMethod]);
|
|
2181
|
+
useEffect7(() => {
|
|
2182
|
+
if (!open) {
|
|
2183
|
+
actions.reset();
|
|
2184
|
+
}
|
|
2185
|
+
}, [open, actions.reset]);
|
|
2186
|
+
const dialogTitle = subscription ? "Change Subscription Plan" : "Subscribe to a Plan";
|
|
2187
|
+
const dialogDescription = subscription ? "Select a new plan for your subscription" : "Choose a subscription plan to get started";
|
|
2188
|
+
return /* @__PURE__ */ jsx30(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs27(DialogContent, { className: "max-w-2xl", children: [
|
|
2189
|
+
/* @__PURE__ */ jsxs27(DialogHeader, { children: [
|
|
2190
|
+
/* @__PURE__ */ jsx30(DialogTitle, { children: dialogTitle }),
|
|
2191
|
+
/* @__PURE__ */ jsx30(DialogDescription, { children: dialogDescription })
|
|
2192
|
+
] }),
|
|
2193
|
+
/* @__PURE__ */ jsx30(WizardProgressIndicator, { currentStep: state.step }),
|
|
2194
|
+
state.step === "plan-selection" && /* @__PURE__ */ jsx30(
|
|
2195
|
+
WizardStepPlanSelection,
|
|
2196
|
+
{
|
|
2197
|
+
selectedPrice: state.selectedPrice,
|
|
2198
|
+
selectedInterval: state.selectedInterval,
|
|
2199
|
+
currentPriceId: subscription?.price?.id,
|
|
2200
|
+
hideRecurringPrices: hasActiveRecurringSubscription && !subscription,
|
|
2201
|
+
onSelectPrice: actions.selectPrice,
|
|
2202
|
+
onIntervalChange: actions.setInterval,
|
|
2203
|
+
onNext: actions.goToReview,
|
|
2204
|
+
isProcessing: state.isProcessing
|
|
2105
2205
|
}
|
|
2106
2206
|
),
|
|
2107
|
-
|
|
2108
|
-
|
|
2207
|
+
state.step === "review" && /* @__PURE__ */ jsx30(
|
|
2208
|
+
WizardStepReview,
|
|
2109
2209
|
{
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2210
|
+
selectedPrice: state.selectedPrice,
|
|
2211
|
+
subscription,
|
|
2212
|
+
prorationPreview: state.prorationPreview,
|
|
2213
|
+
hasPaymentMethod: state.hasPaymentMethod,
|
|
2214
|
+
error: state.error,
|
|
2215
|
+
isProcessing: state.isProcessing,
|
|
2216
|
+
onBack: () => actions.goToStep("plan-selection"),
|
|
2217
|
+
onAddPaymentMethod: () => actions.goToStep("payment-method"),
|
|
2218
|
+
onConfirm: actions.confirmSubscription
|
|
2219
|
+
}
|
|
2220
|
+
),
|
|
2221
|
+
state.step === "payment-method" && /* @__PURE__ */ jsx30(
|
|
2222
|
+
WizardStepPaymentMethod,
|
|
2223
|
+
{
|
|
2224
|
+
onBack: () => actions.goToStep("review"),
|
|
2225
|
+
onSuccess: actions.handlePaymentMethodSuccess,
|
|
2226
|
+
isProcessing: state.isProcessing
|
|
2118
2227
|
}
|
|
2119
2228
|
)
|
|
2120
|
-
] });
|
|
2229
|
+
] }) });
|
|
2121
2230
|
}
|
|
2122
|
-
__name(
|
|
2231
|
+
__name(SubscriptionWizard, "SubscriptionWizard");
|
|
2123
2232
|
|
|
2124
2233
|
// src/features/billing/stripe-usage/components/containers/UsageContainer.tsx
|
|
2125
2234
|
import { Activity as Activity3 } from "lucide-react";
|
|
2126
|
-
import { useEffect as
|
|
2235
|
+
import { useEffect as useEffect8, useState as useState12 } from "react";
|
|
2127
2236
|
|
|
2128
2237
|
// src/features/billing/stripe-usage/components/details/UsageSummaryCard.tsx
|
|
2129
2238
|
import { Activity as Activity2 } from "lucide-react";
|
|
2130
|
-
import { jsx as
|
|
2239
|
+
import { jsx as jsx31, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2131
2240
|
function getProgressColor(percentage) {
|
|
2132
2241
|
if (percentage === null) return "bg-blue-500";
|
|
2133
2242
|
if (percentage >= 90) return "bg-red-500";
|
|
@@ -2155,31 +2264,31 @@ function UsageSummaryCard({ meter, summary }) {
|
|
|
2155
2264
|
const progressWidth = percentage !== null ? Math.min(percentage, 100) : 0;
|
|
2156
2265
|
const displayName = meter.displayName || meter.eventName;
|
|
2157
2266
|
const hasLimit = limit !== null && limit !== void 0;
|
|
2158
|
-
return /* @__PURE__ */
|
|
2159
|
-
/* @__PURE__ */
|
|
2160
|
-
/* @__PURE__ */
|
|
2161
|
-
/* @__PURE__ */
|
|
2162
|
-
/* @__PURE__ */
|
|
2163
|
-
/* @__PURE__ */
|
|
2267
|
+
return /* @__PURE__ */ jsxs28(Card, { children: [
|
|
2268
|
+
/* @__PURE__ */ jsxs28(CardHeader, { className: "flex flex-row items-center gap-x-3 pb-3", children: [
|
|
2269
|
+
/* @__PURE__ */ jsx31("div", { className: "flex h-10 w-10 items-center justify-center rounded-lg bg-blue-100 text-blue-600", children: /* @__PURE__ */ jsx31(Activity2, { className: "h-5 w-5" }) }),
|
|
2270
|
+
/* @__PURE__ */ jsxs28("div", { className: "flex flex-col", children: [
|
|
2271
|
+
/* @__PURE__ */ jsx31("h3", { className: "font-semibold", children: displayName }),
|
|
2272
|
+
/* @__PURE__ */ jsx31("p", { className: "text-xs text-gray-500", children: meter.id })
|
|
2164
2273
|
] })
|
|
2165
2274
|
] }),
|
|
2166
|
-
/* @__PURE__ */
|
|
2167
|
-
/* @__PURE__ */
|
|
2168
|
-
/* @__PURE__ */
|
|
2169
|
-
hasLimit && /* @__PURE__ */
|
|
2275
|
+
/* @__PURE__ */ jsxs28(CardContent, { className: "flex flex-col gap-y-4", children: [
|
|
2276
|
+
/* @__PURE__ */ jsxs28("div", { children: [
|
|
2277
|
+
/* @__PURE__ */ jsx31("p", { className: "text-3xl font-bold", children: currentUsage.toLocaleString() }),
|
|
2278
|
+
hasLimit && /* @__PURE__ */ jsxs28("p", { className: "text-sm text-gray-500", children: [
|
|
2170
2279
|
"of ",
|
|
2171
2280
|
limit.toLocaleString(),
|
|
2172
2281
|
" used"
|
|
2173
2282
|
] })
|
|
2174
2283
|
] }),
|
|
2175
|
-
hasLimit ? /* @__PURE__ */
|
|
2176
|
-
/* @__PURE__ */
|
|
2177
|
-
/* @__PURE__ */
|
|
2284
|
+
hasLimit ? /* @__PURE__ */ jsxs28("div", { className: "flex flex-col gap-y-2", children: [
|
|
2285
|
+
/* @__PURE__ */ jsx31("div", { className: "h-2 w-full overflow-hidden rounded-full bg-gray-200", children: /* @__PURE__ */ jsx31("div", { className: `h-full transition-all ${progressColor}`, style: { width: `${progressWidth}%` } }) }),
|
|
2286
|
+
/* @__PURE__ */ jsxs28("p", { className: "text-sm text-gray-500", children: [
|
|
2178
2287
|
percentage?.toFixed(1),
|
|
2179
2288
|
"% used"
|
|
2180
2289
|
] })
|
|
2181
|
-
] }) : /* @__PURE__ */
|
|
2182
|
-
summary && summary.start && summary.end && /* @__PURE__ */
|
|
2290
|
+
] }) : /* @__PURE__ */ jsx31("p", { className: "text-sm text-gray-500", children: "No limit set" }),
|
|
2291
|
+
summary && summary.start && summary.end && /* @__PURE__ */ jsx31("div", { className: "border-t pt-3", children: /* @__PURE__ */ jsxs28("p", { className: "text-xs text-gray-500", children: [
|
|
2183
2292
|
"Period: ",
|
|
2184
2293
|
formatDate4(summary.start),
|
|
2185
2294
|
" - ",
|
|
@@ -2191,20 +2300,20 @@ function UsageSummaryCard({ meter, summary }) {
|
|
|
2191
2300
|
__name(UsageSummaryCard, "UsageSummaryCard");
|
|
2192
2301
|
|
|
2193
2302
|
// src/features/billing/stripe-usage/components/widgets/UsageSummaryCards.tsx
|
|
2194
|
-
import { jsx as
|
|
2303
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
2195
2304
|
function UsageSummaryCards({ meters, summaries }) {
|
|
2196
|
-
return /* @__PURE__ */
|
|
2305
|
+
return /* @__PURE__ */ jsx32("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: meters.map((meter) => /* @__PURE__ */ jsx32(UsageSummaryCard, { meter, summary: summaries[meter.id] || null }, meter.id)) });
|
|
2197
2306
|
}
|
|
2198
2307
|
__name(UsageSummaryCards, "UsageSummaryCards");
|
|
2199
2308
|
|
|
2200
2309
|
// src/features/billing/stripe-usage/components/containers/UsageContainer.tsx
|
|
2201
|
-
import { jsx as
|
|
2310
|
+
import { jsx as jsx33, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2202
2311
|
function UsageContainer() {
|
|
2203
|
-
const [meters, setMeters] =
|
|
2204
|
-
const [summaries, setSummaries] =
|
|
2205
|
-
const [loading, setLoading] =
|
|
2206
|
-
const [subscriptions, setSubscriptions] =
|
|
2207
|
-
|
|
2312
|
+
const [meters, setMeters] = useState12([]);
|
|
2313
|
+
const [summaries, setSummaries] = useState12({});
|
|
2314
|
+
const [loading, setLoading] = useState12(true);
|
|
2315
|
+
const [subscriptions, setSubscriptions] = useState12([]);
|
|
2316
|
+
useEffect8(() => {
|
|
2208
2317
|
loadUsageData();
|
|
2209
2318
|
}, []);
|
|
2210
2319
|
const loadUsageData = /* @__PURE__ */ __name(async () => {
|
|
@@ -2248,27 +2357,27 @@ function UsageContainer() {
|
|
|
2248
2357
|
return null;
|
|
2249
2358
|
}
|
|
2250
2359
|
if (loading) {
|
|
2251
|
-
return /* @__PURE__ */
|
|
2360
|
+
return /* @__PURE__ */ jsx33("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx33("p", { className: "text-muted-foreground", children: "Loading usage data..." }) });
|
|
2252
2361
|
}
|
|
2253
|
-
return /* @__PURE__ */
|
|
2254
|
-
/* @__PURE__ */
|
|
2255
|
-
/* @__PURE__ */
|
|
2256
|
-
/* @__PURE__ */
|
|
2362
|
+
return /* @__PURE__ */ jsxs29("div", { className: "flex w-full flex-col gap-y-6", children: [
|
|
2363
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-x-3", children: [
|
|
2364
|
+
/* @__PURE__ */ jsx33(Activity3, { className: "h-8 w-8" }),
|
|
2365
|
+
/* @__PURE__ */ jsx33("h1", { className: "text-3xl font-bold", children: "Usage Tracking" })
|
|
2257
2366
|
] }),
|
|
2258
|
-
meters.length === 0 && /* @__PURE__ */
|
|
2259
|
-
/* @__PURE__ */
|
|
2260
|
-
/* @__PURE__ */
|
|
2261
|
-
/* @__PURE__ */
|
|
2262
|
-
/* @__PURE__ */
|
|
2367
|
+
meters.length === 0 && /* @__PURE__ */ jsxs29("div", { className: "bg-muted/50 flex flex-col items-center justify-center gap-y-4 rounded-lg border-2 border-dashed border-gray-300 p-12", children: [
|
|
2368
|
+
/* @__PURE__ */ jsx33(Activity3, { className: "text-muted-foreground h-16 w-16" }),
|
|
2369
|
+
/* @__PURE__ */ jsxs29("div", { className: "text-center", children: [
|
|
2370
|
+
/* @__PURE__ */ jsx33("h3", { className: "mb-2 text-xl font-semibold", children: "No usage meters configured" }),
|
|
2371
|
+
/* @__PURE__ */ jsx33("p", { className: "text-muted-foreground", children: "Usage tracking will appear here when you have metered subscriptions with configured meters." })
|
|
2263
2372
|
] })
|
|
2264
2373
|
] }),
|
|
2265
|
-
meters.length > 0 && /* @__PURE__ */
|
|
2374
|
+
meters.length > 0 && /* @__PURE__ */ jsx33(UsageSummaryCards, { meters, summaries })
|
|
2266
2375
|
] });
|
|
2267
2376
|
}
|
|
2268
2377
|
__name(UsageContainer, "UsageContainer");
|
|
2269
2378
|
|
|
2270
2379
|
// src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx
|
|
2271
|
-
import { jsx as
|
|
2380
|
+
import { jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2272
2381
|
function formatDateTime(date) {
|
|
2273
2382
|
if (!date) return "N/A";
|
|
2274
2383
|
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
@@ -2287,25 +2396,25 @@ function formatDateTime(date) {
|
|
|
2287
2396
|
__name(formatDateTime, "formatDateTime");
|
|
2288
2397
|
function UsageHistoryTable({ usageRecords }) {
|
|
2289
2398
|
if (usageRecords.length === 0) {
|
|
2290
|
-
return /* @__PURE__ */
|
|
2399
|
+
return /* @__PURE__ */ jsx34("div", { className: "rounded-lg border p-8 text-center", children: /* @__PURE__ */ jsx34("p", { className: "text-muted-foreground", children: "No usage history available." }) });
|
|
2291
2400
|
}
|
|
2292
|
-
return /* @__PURE__ */
|
|
2293
|
-
/* @__PURE__ */
|
|
2294
|
-
/* @__PURE__ */
|
|
2295
|
-
/* @__PURE__ */
|
|
2296
|
-
/* @__PURE__ */
|
|
2297
|
-
/* @__PURE__ */
|
|
2298
|
-
/* @__PURE__ */
|
|
2299
|
-
/* @__PURE__ */
|
|
2401
|
+
return /* @__PURE__ */ jsxs30("div", { className: "flex w-full flex-col gap-y-4", children: [
|
|
2402
|
+
/* @__PURE__ */ jsx34("h2", { className: "text-xl font-semibold", children: "Usage History" }),
|
|
2403
|
+
/* @__PURE__ */ jsx34("div", { className: "overflow-hidden rounded-lg border", children: /* @__PURE__ */ jsxs30(Table, { children: [
|
|
2404
|
+
/* @__PURE__ */ jsx34(TableHeader, { className: "bg-muted", children: /* @__PURE__ */ jsxs30(TableRow, { children: [
|
|
2405
|
+
/* @__PURE__ */ jsx34(TableHead, { children: "Date & Time" }),
|
|
2406
|
+
/* @__PURE__ */ jsx34(TableHead, { children: "Meter Event" }),
|
|
2407
|
+
/* @__PURE__ */ jsx34(TableHead, { className: "text-right", children: "Quantity" }),
|
|
2408
|
+
/* @__PURE__ */ jsx34(TableHead, { children: "Event ID" })
|
|
2300
2409
|
] }) }),
|
|
2301
|
-
/* @__PURE__ */
|
|
2410
|
+
/* @__PURE__ */ jsx34(TableBody, { children: usageRecords.map((record) => {
|
|
2302
2411
|
const dateTime = formatDateTime(record.timestamp);
|
|
2303
2412
|
const quantity = record.quantity.toLocaleString();
|
|
2304
|
-
return /* @__PURE__ */
|
|
2305
|
-
/* @__PURE__ */
|
|
2306
|
-
/* @__PURE__ */
|
|
2307
|
-
/* @__PURE__ */
|
|
2308
|
-
/* @__PURE__ */
|
|
2413
|
+
return /* @__PURE__ */ jsxs30(TableRow, { children: [
|
|
2414
|
+
/* @__PURE__ */ jsx34(TableCell, { className: "font-medium", children: dateTime }),
|
|
2415
|
+
/* @__PURE__ */ jsx34(TableCell, { className: "text-muted-foreground", children: record.meterEventName }),
|
|
2416
|
+
/* @__PURE__ */ jsx34(TableCell, { className: "text-right font-medium", children: quantity }),
|
|
2417
|
+
/* @__PURE__ */ jsx34(TableCell, { className: "text-muted-foreground text-sm font-mono", children: record.stripeEventId })
|
|
2309
2418
|
] }, record.id);
|
|
2310
2419
|
}) })
|
|
2311
2420
|
] }) })
|
|
@@ -2314,7 +2423,7 @@ function UsageHistoryTable({ usageRecords }) {
|
|
|
2314
2423
|
__name(UsageHistoryTable, "UsageHistoryTable");
|
|
2315
2424
|
|
|
2316
2425
|
// src/features/billing/components/modals/BillingDetailModal.tsx
|
|
2317
|
-
import { jsx as
|
|
2426
|
+
import { jsx as jsx35, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
2318
2427
|
function BillingDetailModal({
|
|
2319
2428
|
open,
|
|
2320
2429
|
onOpenChange,
|
|
@@ -2322,25 +2431,25 @@ function BillingDetailModal({
|
|
|
2322
2431
|
children,
|
|
2323
2432
|
className
|
|
2324
2433
|
}) {
|
|
2325
|
-
return /* @__PURE__ */
|
|
2326
|
-
/* @__PURE__ */
|
|
2434
|
+
return /* @__PURE__ */ jsx35(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs31(DialogContent, { className: className ?? "max-w-4xl max-h-[90vh] overflow-y-auto", children: [
|
|
2435
|
+
/* @__PURE__ */ jsx35(DialogHeader, { children: /* @__PURE__ */ jsx35(DialogTitle, { children: title }) }),
|
|
2327
2436
|
children
|
|
2328
2437
|
] }) });
|
|
2329
2438
|
}
|
|
2330
2439
|
__name(BillingDetailModal, "BillingDetailModal");
|
|
2331
2440
|
|
|
2332
2441
|
// src/features/billing/components/widgets/BillingAlertBanner.tsx
|
|
2333
|
-
import { AlertCircle } from "lucide-react";
|
|
2334
|
-
import { jsx as
|
|
2442
|
+
import { AlertCircle as AlertCircle2 } from "lucide-react";
|
|
2443
|
+
import { jsx as jsx36, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
2335
2444
|
function BillingAlertBanner({ subscription, onUpdatePayment, onAddPayment }) {
|
|
2336
2445
|
if (subscription.status === "past_due" /* PAST_DUE */) {
|
|
2337
|
-
return /* @__PURE__ */
|
|
2338
|
-
/* @__PURE__ */
|
|
2339
|
-
/* @__PURE__ */
|
|
2340
|
-
/* @__PURE__ */
|
|
2341
|
-
/* @__PURE__ */
|
|
2446
|
+
return /* @__PURE__ */ jsxs32("div", { className: "bg-red-50 border border-red-200 rounded-lg p-4 flex items-start gap-x-3", children: [
|
|
2447
|
+
/* @__PURE__ */ jsx36(AlertCircle2, { className: "h-5 w-5 text-red-600 mt-0.5" }),
|
|
2448
|
+
/* @__PURE__ */ jsxs32("div", { className: "flex-1", children: [
|
|
2449
|
+
/* @__PURE__ */ jsx36("h3", { className: "font-semibold text-red-900", children: "Payment Failed" }),
|
|
2450
|
+
/* @__PURE__ */ jsx36("p", { className: "text-sm text-red-700 mt-1", children: "Your last payment failed. Please update your payment method to avoid service interruption." })
|
|
2342
2451
|
] }),
|
|
2343
|
-
onUpdatePayment && /* @__PURE__ */
|
|
2452
|
+
onUpdatePayment && /* @__PURE__ */ jsx36(Button, { variant: "outline", size: "sm", onClick: onUpdatePayment, className: "border-red-300 text-red-700", children: "Update Payment Method" })
|
|
2344
2453
|
] });
|
|
2345
2454
|
}
|
|
2346
2455
|
if (subscription.status === "trialing" /* TRIALING */ && subscription.trialEnd) {
|
|
@@ -2348,11 +2457,11 @@ function BillingAlertBanner({ subscription, onUpdatePayment, onAddPayment }) {
|
|
|
2348
2457
|
const now = /* @__PURE__ */ new Date();
|
|
2349
2458
|
const daysRemaining = Math.ceil((trialEnd.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24));
|
|
2350
2459
|
if (daysRemaining <= 7) {
|
|
2351
|
-
return /* @__PURE__ */
|
|
2352
|
-
/* @__PURE__ */
|
|
2353
|
-
/* @__PURE__ */
|
|
2354
|
-
/* @__PURE__ */
|
|
2355
|
-
/* @__PURE__ */
|
|
2460
|
+
return /* @__PURE__ */ jsxs32("div", { className: "bg-yellow-50 border border-yellow-200 rounded-lg p-4 flex items-start gap-x-3", children: [
|
|
2461
|
+
/* @__PURE__ */ jsx36(AlertCircle2, { className: "h-5 w-5 text-yellow-600 mt-0.5" }),
|
|
2462
|
+
/* @__PURE__ */ jsxs32("div", { className: "flex-1", children: [
|
|
2463
|
+
/* @__PURE__ */ jsx36("h3", { className: "font-semibold text-yellow-900", children: "Trial Ending Soon" }),
|
|
2464
|
+
/* @__PURE__ */ jsxs32("p", { className: "text-sm text-yellow-700 mt-1", children: [
|
|
2356
2465
|
"Your trial ends in ",
|
|
2357
2466
|
daysRemaining,
|
|
2358
2467
|
" ",
|
|
@@ -2360,7 +2469,7 @@ function BillingAlertBanner({ subscription, onUpdatePayment, onAddPayment }) {
|
|
|
2360
2469
|
". Add a payment method to continue your subscription."
|
|
2361
2470
|
] })
|
|
2362
2471
|
] }),
|
|
2363
|
-
onAddPayment && /* @__PURE__ */
|
|
2472
|
+
onAddPayment && /* @__PURE__ */ jsx36(Button, { variant: "outline", size: "sm", onClick: onAddPayment, className: "border-yellow-300 text-yellow-700", children: "Add Payment Method" })
|
|
2364
2473
|
] });
|
|
2365
2474
|
}
|
|
2366
2475
|
}
|
|
@@ -2369,9 +2478,9 @@ function BillingAlertBanner({ subscription, onUpdatePayment, onAddPayment }) {
|
|
|
2369
2478
|
__name(BillingAlertBanner, "BillingAlertBanner");
|
|
2370
2479
|
|
|
2371
2480
|
// src/features/billing/components/containers/BillingDashboardContainer.tsx
|
|
2372
|
-
import { Fragment as Fragment6, jsx as
|
|
2481
|
+
import { Fragment as Fragment6, jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
2373
2482
|
function BillingDashboardContainer() {
|
|
2374
|
-
const [data, setData] =
|
|
2483
|
+
const [data, setData] = useState13({
|
|
2375
2484
|
customer: null,
|
|
2376
2485
|
subscriptions: [],
|
|
2377
2486
|
paymentMethods: [],
|
|
@@ -2379,27 +2488,35 @@ function BillingDashboardContainer() {
|
|
|
2379
2488
|
meters: [],
|
|
2380
2489
|
meterSummaries: {}
|
|
2381
2490
|
});
|
|
2382
|
-
const [loading, setLoading] =
|
|
2491
|
+
const [loading, setLoading] = useState13({
|
|
2383
2492
|
customer: true,
|
|
2384
2493
|
subscriptions: true,
|
|
2385
2494
|
paymentMethods: true,
|
|
2386
2495
|
invoices: true,
|
|
2387
2496
|
usage: true
|
|
2388
2497
|
});
|
|
2389
|
-
const [errors, setErrors] =
|
|
2498
|
+
const [errors, setErrors] = useState13({
|
|
2390
2499
|
customer: null,
|
|
2391
2500
|
subscriptions: null,
|
|
2392
2501
|
paymentMethods: null,
|
|
2393
2502
|
invoices: null,
|
|
2394
2503
|
usage: null
|
|
2395
2504
|
});
|
|
2396
|
-
const [activeModal, setActiveModal] =
|
|
2397
|
-
const [noCustomerExists, setNoCustomerExists] =
|
|
2398
|
-
const [creatingCustomer, setCreatingCustomer] =
|
|
2399
|
-
const
|
|
2505
|
+
const [activeModal, setActiveModal] = useState13(null);
|
|
2506
|
+
const [noCustomerExists, setNoCustomerExists] = useState13(false);
|
|
2507
|
+
const [creatingCustomer, setCreatingCustomer] = useState13(false);
|
|
2508
|
+
const searchParams = useSearchParams();
|
|
2509
|
+
const [showWizard, setShowWizard] = useState13(false);
|
|
2510
|
+
const [editingSubscription, setEditingSubscription] = useState13(null);
|
|
2511
|
+
const hasMeteredSubscriptions = useCallback4(() => {
|
|
2400
2512
|
return data.subscriptions.some((sub) => sub.price?.recurring?.usageType === "metered");
|
|
2401
2513
|
}, [data.subscriptions]);
|
|
2402
|
-
const
|
|
2514
|
+
const hasActiveRecurringSubscription = useMemo3(() => {
|
|
2515
|
+
return data.subscriptions.some(
|
|
2516
|
+
(sub) => (sub.status === "active" /* ACTIVE */ || sub.status === "trialing" /* TRIALING */) && sub.price?.priceType === "recurring"
|
|
2517
|
+
);
|
|
2518
|
+
}, [data.subscriptions]);
|
|
2519
|
+
const fetchAllData = useCallback4(async () => {
|
|
2403
2520
|
setNoCustomerExists(false);
|
|
2404
2521
|
let customer = null;
|
|
2405
2522
|
try {
|
|
@@ -2518,7 +2635,7 @@ function BillingDashboardContainer() {
|
|
|
2518
2635
|
setLoading((prev) => ({ ...prev, usage: false }));
|
|
2519
2636
|
}
|
|
2520
2637
|
}, "fetchUsageData");
|
|
2521
|
-
const refreshData =
|
|
2638
|
+
const refreshData = useCallback4(async () => {
|
|
2522
2639
|
setLoading({
|
|
2523
2640
|
customer: true,
|
|
2524
2641
|
subscriptions: true,
|
|
@@ -2528,9 +2645,25 @@ function BillingDashboardContainer() {
|
|
|
2528
2645
|
});
|
|
2529
2646
|
await fetchAllData();
|
|
2530
2647
|
}, [fetchAllData]);
|
|
2531
|
-
|
|
2648
|
+
const handleOpenWizard = useCallback4((subscription) => {
|
|
2649
|
+
setEditingSubscription(subscription || null);
|
|
2650
|
+
setShowWizard(true);
|
|
2651
|
+
}, []);
|
|
2652
|
+
const handleWizardClose = useCallback4(() => {
|
|
2653
|
+
setShowWizard(false);
|
|
2654
|
+
setEditingSubscription(null);
|
|
2655
|
+
refreshData();
|
|
2656
|
+
}, [refreshData]);
|
|
2657
|
+
useEffect9(() => {
|
|
2532
2658
|
fetchAllData();
|
|
2533
2659
|
}, [fetchAllData]);
|
|
2660
|
+
useEffect9(() => {
|
|
2661
|
+
const action = searchParams.get("action");
|
|
2662
|
+
if (action === "subscribe") {
|
|
2663
|
+
setShowWizard(true);
|
|
2664
|
+
window.history.replaceState({}, "", window.location.pathname);
|
|
2665
|
+
}
|
|
2666
|
+
}, [searchParams]);
|
|
2534
2667
|
const criticalSubscriptions = data.subscriptions.filter(
|
|
2535
2668
|
(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
|
|
2536
2669
|
);
|
|
@@ -2555,26 +2688,26 @@ function BillingDashboardContainer() {
|
|
|
2555
2688
|
}
|
|
2556
2689
|
}, "getModalTitle");
|
|
2557
2690
|
const isInitialLoading = loading.customer && !noCustomerExists && !data.customer;
|
|
2558
|
-
return /* @__PURE__ */
|
|
2559
|
-
/* @__PURE__ */
|
|
2560
|
-
/* @__PURE__ */
|
|
2561
|
-
/* @__PURE__ */
|
|
2691
|
+
return /* @__PURE__ */ jsxs33("div", { className: "flex w-full flex-col gap-y-6", children: [
|
|
2692
|
+
/* @__PURE__ */ jsxs33("div", { className: "flex items-center gap-x-3", children: [
|
|
2693
|
+
/* @__PURE__ */ jsx37(Wallet2, { className: "h-8 w-8" }),
|
|
2694
|
+
/* @__PURE__ */ jsx37("h1", { className: "text-3xl font-bold", children: "Billing" })
|
|
2562
2695
|
] }),
|
|
2563
|
-
isInitialLoading && /* @__PURE__ */
|
|
2564
|
-
noCustomerExists && !isInitialLoading && /* @__PURE__ */
|
|
2565
|
-
/* @__PURE__ */
|
|
2566
|
-
/* @__PURE__ */
|
|
2567
|
-
/* @__PURE__ */
|
|
2568
|
-
/* @__PURE__ */
|
|
2696
|
+
isInitialLoading && /* @__PURE__ */ jsx37(Card, { children: /* @__PURE__ */ jsx37(CardContent, { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx37(Loader22, { className: "h-8 w-8 animate-spin text-muted-foreground" }) }) }),
|
|
2697
|
+
noCustomerExists && !isInitialLoading && /* @__PURE__ */ jsxs33(Card, { children: [
|
|
2698
|
+
/* @__PURE__ */ jsxs33(CardHeader, { className: "text-center", children: [
|
|
2699
|
+
/* @__PURE__ */ jsx37("div", { className: "mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsx37(CreditCard4, { className: "h-8 w-8 text-primary" }) }),
|
|
2700
|
+
/* @__PURE__ */ jsx37(CardTitle, { children: "Set Up Billing" }),
|
|
2701
|
+
/* @__PURE__ */ jsx37(CardDescription, { children: "Your company doesn't have a billing account yet. Set one up to manage subscriptions, payment methods, and view invoices." })
|
|
2569
2702
|
] }),
|
|
2570
|
-
/* @__PURE__ */
|
|
2571
|
-
/* @__PURE__ */
|
|
2703
|
+
/* @__PURE__ */ jsx37(CardContent, { className: "flex justify-center pb-8", children: /* @__PURE__ */ jsx37(Button, { onClick: handleCreateCustomer, disabled: creatingCustomer, size: "lg", children: creatingCustomer ? /* @__PURE__ */ jsxs33(Fragment6, { children: [
|
|
2704
|
+
/* @__PURE__ */ jsx37(Loader22, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
2572
2705
|
"Setting up..."
|
|
2573
2706
|
] }) : "Set Up Billing Account" }) }),
|
|
2574
|
-
errors.customer && /* @__PURE__ */
|
|
2707
|
+
errors.customer && /* @__PURE__ */ jsx37(CardContent, { className: "pt-0", children: /* @__PURE__ */ jsx37("p", { className: "text-center text-sm text-destructive", children: errors.customer }) })
|
|
2575
2708
|
] }),
|
|
2576
|
-
!noCustomerExists && !isInitialLoading && /* @__PURE__ */
|
|
2577
|
-
criticalSubscriptions.map((subscription) => /* @__PURE__ */
|
|
2709
|
+
!noCustomerExists && !isInitialLoading && /* @__PURE__ */ jsxs33(Fragment6, { children: [
|
|
2710
|
+
criticalSubscriptions.map((subscription) => /* @__PURE__ */ jsx37(
|
|
2578
2711
|
BillingAlertBanner,
|
|
2579
2712
|
{
|
|
2580
2713
|
subscription,
|
|
@@ -2583,17 +2716,23 @@ function BillingDashboardContainer() {
|
|
|
2583
2716
|
},
|
|
2584
2717
|
subscription.id
|
|
2585
2718
|
)),
|
|
2586
|
-
/* @__PURE__ */
|
|
2587
|
-
/* @__PURE__ */
|
|
2719
|
+
/* @__PURE__ */ jsxs33("div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
2720
|
+
/* @__PURE__ */ jsx37(
|
|
2588
2721
|
SubscriptionSummaryCard,
|
|
2589
2722
|
{
|
|
2590
2723
|
subscriptions: data.subscriptions,
|
|
2591
2724
|
loading: loading.subscriptions,
|
|
2592
2725
|
error: errors.subscriptions || void 0,
|
|
2593
|
-
onManageClick: () =>
|
|
2726
|
+
onManageClick: () => {
|
|
2727
|
+
if (data.subscriptions.length === 0) {
|
|
2728
|
+
setShowWizard(true);
|
|
2729
|
+
} else {
|
|
2730
|
+
setActiveModal("subscriptions");
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2594
2733
|
}
|
|
2595
2734
|
),
|
|
2596
|
-
/* @__PURE__ */
|
|
2735
|
+
/* @__PURE__ */ jsx37(
|
|
2597
2736
|
PaymentMethodSummaryCard,
|
|
2598
2737
|
{
|
|
2599
2738
|
paymentMethods: data.paymentMethods,
|
|
@@ -2603,7 +2742,7 @@ function BillingDashboardContainer() {
|
|
|
2603
2742
|
onManageClick: () => setActiveModal("payment-methods")
|
|
2604
2743
|
}
|
|
2605
2744
|
),
|
|
2606
|
-
/* @__PURE__ */
|
|
2745
|
+
/* @__PURE__ */ jsx37(
|
|
2607
2746
|
CustomerInfoCard,
|
|
2608
2747
|
{
|
|
2609
2748
|
customer: data.customer,
|
|
@@ -2611,7 +2750,7 @@ function BillingDashboardContainer() {
|
|
|
2611
2750
|
error: errors.customer || void 0
|
|
2612
2751
|
}
|
|
2613
2752
|
),
|
|
2614
|
-
/* @__PURE__ */
|
|
2753
|
+
/* @__PURE__ */ jsx37(
|
|
2615
2754
|
InvoicesSummaryCard,
|
|
2616
2755
|
{
|
|
2617
2756
|
invoices: data.invoices,
|
|
@@ -2620,7 +2759,7 @@ function BillingDashboardContainer() {
|
|
|
2620
2759
|
onViewAllClick: () => setActiveModal("invoices")
|
|
2621
2760
|
}
|
|
2622
2761
|
),
|
|
2623
|
-
hasMeteredSubscriptions() && /* @__PURE__ */
|
|
2762
|
+
hasMeteredSubscriptions() && /* @__PURE__ */ jsx37(
|
|
2624
2763
|
BillingUsageSummaryCard,
|
|
2625
2764
|
{
|
|
2626
2765
|
meters: data.meters,
|
|
@@ -2631,40 +2770,50 @@ function BillingDashboardContainer() {
|
|
|
2631
2770
|
}
|
|
2632
2771
|
)
|
|
2633
2772
|
] }),
|
|
2634
|
-
/* @__PURE__ */
|
|
2773
|
+
/* @__PURE__ */ jsx37(
|
|
2635
2774
|
BillingDetailModal,
|
|
2636
2775
|
{
|
|
2637
2776
|
open: activeModal === "subscriptions",
|
|
2638
2777
|
onOpenChange: handleModalClose,
|
|
2639
2778
|
title: getModalTitle("subscriptions"),
|
|
2640
|
-
children: /* @__PURE__ */
|
|
2779
|
+
children: /* @__PURE__ */ jsx37(SubscriptionsContainer, { onOpenWizard: handleOpenWizard })
|
|
2641
2780
|
}
|
|
2642
2781
|
),
|
|
2643
|
-
/* @__PURE__ */
|
|
2782
|
+
/* @__PURE__ */ jsx37(
|
|
2644
2783
|
BillingDetailModal,
|
|
2645
2784
|
{
|
|
2646
2785
|
open: activeModal === "payment-methods",
|
|
2647
2786
|
onOpenChange: handleModalClose,
|
|
2648
2787
|
title: getModalTitle("payment-methods"),
|
|
2649
|
-
children: /* @__PURE__ */
|
|
2788
|
+
children: /* @__PURE__ */ jsx37(PaymentMethodsContainer, {})
|
|
2650
2789
|
}
|
|
2651
2790
|
),
|
|
2652
|
-
/* @__PURE__ */
|
|
2791
|
+
/* @__PURE__ */ jsx37(
|
|
2653
2792
|
BillingDetailModal,
|
|
2654
2793
|
{
|
|
2655
2794
|
open: activeModal === "invoices",
|
|
2656
2795
|
onOpenChange: handleModalClose,
|
|
2657
2796
|
title: getModalTitle("invoices"),
|
|
2658
|
-
children: /* @__PURE__ */
|
|
2797
|
+
children: /* @__PURE__ */ jsx37(InvoicesContainer, {})
|
|
2659
2798
|
}
|
|
2660
2799
|
),
|
|
2661
|
-
/* @__PURE__ */
|
|
2800
|
+
/* @__PURE__ */ jsx37(
|
|
2662
2801
|
BillingDetailModal,
|
|
2663
2802
|
{
|
|
2664
2803
|
open: activeModal === "usage",
|
|
2665
2804
|
onOpenChange: handleModalClose,
|
|
2666
2805
|
title: getModalTitle("usage"),
|
|
2667
|
-
children: /* @__PURE__ */
|
|
2806
|
+
children: /* @__PURE__ */ jsx37(UsageContainer, {})
|
|
2807
|
+
}
|
|
2808
|
+
),
|
|
2809
|
+
/* @__PURE__ */ jsx37(
|
|
2810
|
+
SubscriptionWizard,
|
|
2811
|
+
{
|
|
2812
|
+
open: showWizard,
|
|
2813
|
+
onOpenChange: (open) => !open && handleWizardClose(),
|
|
2814
|
+
onSuccess: refreshData,
|
|
2815
|
+
hasActiveRecurringSubscription,
|
|
2816
|
+
subscription: editingSubscription ?? void 0
|
|
2668
2817
|
}
|
|
2669
2818
|
)
|
|
2670
2819
|
] })
|
|
@@ -2675,8 +2824,8 @@ __name(BillingDashboardContainer, "BillingDashboardContainer");
|
|
|
2675
2824
|
// src/features/billing/components/providers/StripeProvider.tsx
|
|
2676
2825
|
import { Elements } from "@stripe/react-stripe-js";
|
|
2677
2826
|
import { loadStripe } from "@stripe/stripe-js";
|
|
2678
|
-
import { useMemo } from "react";
|
|
2679
|
-
import { Fragment as Fragment7, jsx as
|
|
2827
|
+
import { useMemo as useMemo4 } from "react";
|
|
2828
|
+
import { Fragment as Fragment7, jsx as jsx38 } from "react/jsx-runtime";
|
|
2680
2829
|
var stripePromiseCache = null;
|
|
2681
2830
|
function getStripePromise(publishableKey) {
|
|
2682
2831
|
if (!publishableKey) {
|
|
@@ -2692,12 +2841,12 @@ function getStripePromise(publishableKey) {
|
|
|
2692
2841
|
__name(getStripePromise, "getStripePromise");
|
|
2693
2842
|
function StripeProvider({ children }) {
|
|
2694
2843
|
const publishableKey = getStripePublishableKey();
|
|
2695
|
-
const stripePromise =
|
|
2696
|
-
const options =
|
|
2844
|
+
const stripePromise = useMemo4(() => getStripePromise(publishableKey), [publishableKey]);
|
|
2845
|
+
const options = useMemo4(() => ({}), []);
|
|
2697
2846
|
if (!publishableKey) {
|
|
2698
|
-
return /* @__PURE__ */
|
|
2847
|
+
return /* @__PURE__ */ jsx38(Fragment7, { children });
|
|
2699
2848
|
}
|
|
2700
|
-
return /* @__PURE__ */
|
|
2849
|
+
return /* @__PURE__ */ jsx38(Elements, { stripe: stripePromise, options, children });
|
|
2701
2850
|
}
|
|
2702
2851
|
__name(StripeProvider, "StripeProvider");
|
|
2703
2852
|
function isStripeConfigured() {
|
|
@@ -2707,14 +2856,14 @@ __name(isStripeConfigured, "isStripeConfigured");
|
|
|
2707
2856
|
|
|
2708
2857
|
// src/features/billing/stripe-price/components/forms/PriceEditor.tsx
|
|
2709
2858
|
import { zodResolver as zodResolver2 } from "@hookform/resolvers/zod";
|
|
2710
|
-
import { AlertCircle as
|
|
2711
|
-
import { useState as
|
|
2859
|
+
import { AlertCircle as AlertCircle3, PlusIcon, XIcon } from "lucide-react";
|
|
2860
|
+
import { useEffect as useEffect10, useState as useState14 } from "react";
|
|
2712
2861
|
import { useForm as useForm2 } from "react-hook-form";
|
|
2713
|
-
import { v4
|
|
2862
|
+
import { v4 } from "uuid";
|
|
2714
2863
|
import { z as z2 } from "zod";
|
|
2715
|
-
import { jsx as
|
|
2864
|
+
import { jsx as jsx39, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
2716
2865
|
function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
2717
|
-
const [isSubmitting, setIsSubmitting] =
|
|
2866
|
+
const [isSubmitting, setIsSubmitting] = useState14(false);
|
|
2718
2867
|
const formSchema2 = z2.object({
|
|
2719
2868
|
unitAmount: z2.preprocess(
|
|
2720
2869
|
(val) => typeof val === "string" ? parseFloat(val) : val,
|
|
@@ -2730,7 +2879,8 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2730
2879
|
nickname: z2.string().optional(),
|
|
2731
2880
|
active: z2.boolean(),
|
|
2732
2881
|
description: z2.string().optional(),
|
|
2733
|
-
features: z2.array(z2.string())
|
|
2882
|
+
features: z2.array(z2.string()),
|
|
2883
|
+
token: z2.string()
|
|
2734
2884
|
});
|
|
2735
2885
|
const isEditMode = !!price;
|
|
2736
2886
|
const defaultUnitAmount = price?.unitAmount ? price.unitAmount / 100 : 0;
|
|
@@ -2745,9 +2895,26 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2745
2895
|
nickname: price?.nickname || "",
|
|
2746
2896
|
active: price?.active ?? true,
|
|
2747
2897
|
description: price?.description || "",
|
|
2748
|
-
features: price?.features || []
|
|
2898
|
+
features: price?.features || [],
|
|
2899
|
+
token: price?.token?.toString() ?? ""
|
|
2749
2900
|
}
|
|
2750
2901
|
});
|
|
2902
|
+
useEffect10(() => {
|
|
2903
|
+
if (open) {
|
|
2904
|
+
form.reset({
|
|
2905
|
+
unitAmount: price?.unitAmount ? price.unitAmount / 100 : 0,
|
|
2906
|
+
currency: price?.currency || "usd",
|
|
2907
|
+
interval: price?.priceType === "one_time" ? "one_time" : price?.recurring?.interval || "month",
|
|
2908
|
+
intervalCount: price?.recurring?.intervalCount || 1,
|
|
2909
|
+
usageType: price?.recurring?.usageType || "licensed",
|
|
2910
|
+
nickname: price?.nickname || "",
|
|
2911
|
+
active: price?.active ?? true,
|
|
2912
|
+
description: price?.description || "",
|
|
2913
|
+
features: price?.features || [],
|
|
2914
|
+
token: price?.token?.toString() ?? ""
|
|
2915
|
+
});
|
|
2916
|
+
}
|
|
2917
|
+
}, [open, price?.id]);
|
|
2751
2918
|
const watchInterval = form.watch("interval");
|
|
2752
2919
|
const isRecurring = watchInterval !== "one_time";
|
|
2753
2920
|
const onSubmit = /* @__PURE__ */ __name(async (values) => {
|
|
@@ -2759,11 +2926,12 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2759
2926
|
id: price.id,
|
|
2760
2927
|
nickname: values.nickname || void 0,
|
|
2761
2928
|
description: values.description || void 0,
|
|
2762
|
-
features: values.features.filter((f) => f.trim()) || void 0
|
|
2929
|
+
features: values.features.filter((f) => f.trim()) || void 0,
|
|
2930
|
+
token: values.token ? parseInt(values.token, 10) : void 0
|
|
2763
2931
|
});
|
|
2764
2932
|
} else {
|
|
2765
2933
|
const createInput = {
|
|
2766
|
-
id:
|
|
2934
|
+
id: v4(),
|
|
2767
2935
|
productId,
|
|
2768
2936
|
currency: values.currency,
|
|
2769
2937
|
unitAmount: unitAmountInCents
|
|
@@ -2785,6 +2953,9 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2785
2953
|
if (filteredFeatures.length > 0) {
|
|
2786
2954
|
createInput.features = filteredFeatures;
|
|
2787
2955
|
}
|
|
2956
|
+
if (values.token) {
|
|
2957
|
+
createInput.token = parseInt(values.token, 10);
|
|
2958
|
+
}
|
|
2788
2959
|
await StripePriceService.createPrice(createInput);
|
|
2789
2960
|
}
|
|
2790
2961
|
onSuccess();
|
|
@@ -2811,21 +2982,21 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2811
2982
|
{ id: "licensed", text: "Licensed (per unit)" },
|
|
2812
2983
|
{ id: "metered", text: "Metered (usage-based)" }
|
|
2813
2984
|
];
|
|
2814
|
-
return /* @__PURE__ */
|
|
2815
|
-
/* @__PURE__ */
|
|
2816
|
-
/* @__PURE__ */
|
|
2817
|
-
/* @__PURE__ */
|
|
2985
|
+
return /* @__PURE__ */ jsx39(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs34(DialogContent, { className: "max-w-2xl", children: [
|
|
2986
|
+
/* @__PURE__ */ jsxs34(DialogHeader, { children: [
|
|
2987
|
+
/* @__PURE__ */ jsx39(DialogTitle, { children: isEditMode ? "Edit Price" : "Create Price" }),
|
|
2988
|
+
/* @__PURE__ */ jsx39(DialogDescription, { children: isEditMode ? "Update the price details. Note: Only nickname and active status can be changed." : "Create a new price for this product" })
|
|
2818
2989
|
] }),
|
|
2819
|
-
isEditMode && /* @__PURE__ */
|
|
2820
|
-
/* @__PURE__ */
|
|
2821
|
-
/* @__PURE__ */
|
|
2822
|
-
/* @__PURE__ */
|
|
2823
|
-
/* @__PURE__ */
|
|
2990
|
+
isEditMode && /* @__PURE__ */ jsxs34("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-4 flex gap-x-3", children: [
|
|
2991
|
+
/* @__PURE__ */ jsx39(AlertCircle3, { className: "h-5 w-5 text-blue-600 flex-shrink-0 mt-0.5" }),
|
|
2992
|
+
/* @__PURE__ */ jsxs34("div", { className: "text-sm text-blue-800", children: [
|
|
2993
|
+
/* @__PURE__ */ jsx39("p", { className: "font-semibold mb-1", children: "Stripe Price Immutability" }),
|
|
2994
|
+
/* @__PURE__ */ jsx39("p", { children: "Due to Stripe's architecture, only the nickname and active status can be modified after creation. To change amount, currency, or billing interval, create a new price." })
|
|
2824
2995
|
] })
|
|
2825
2996
|
] }),
|
|
2826
|
-
/* @__PURE__ */
|
|
2827
|
-
/* @__PURE__ */
|
|
2828
|
-
/* @__PURE__ */
|
|
2997
|
+
/* @__PURE__ */ jsx39(Form, { ...form, children: /* @__PURE__ */ jsxs34("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-col gap-y-4", children: [
|
|
2998
|
+
/* @__PURE__ */ jsxs34("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
2999
|
+
/* @__PURE__ */ jsx39(
|
|
2829
3000
|
FormInput,
|
|
2830
3001
|
{
|
|
2831
3002
|
form,
|
|
@@ -2836,9 +3007,9 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2836
3007
|
isRequired: true
|
|
2837
3008
|
}
|
|
2838
3009
|
),
|
|
2839
|
-
/* @__PURE__ */
|
|
3010
|
+
/* @__PURE__ */ jsx39(FormSelect, { form, id: "currency", name: "Currency", values: currencyOptions, disabled: isEditMode })
|
|
2840
3011
|
] }),
|
|
2841
|
-
/* @__PURE__ */
|
|
3012
|
+
/* @__PURE__ */ jsx39(
|
|
2842
3013
|
FormSelect,
|
|
2843
3014
|
{
|
|
2844
3015
|
form,
|
|
@@ -2848,8 +3019,8 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2848
3019
|
disabled: isEditMode
|
|
2849
3020
|
}
|
|
2850
3021
|
),
|
|
2851
|
-
isRecurring && /* @__PURE__ */
|
|
2852
|
-
/* @__PURE__ */
|
|
3022
|
+
isRecurring && /* @__PURE__ */ jsxs34("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
3023
|
+
/* @__PURE__ */ jsx39(
|
|
2853
3024
|
FormInput,
|
|
2854
3025
|
{
|
|
2855
3026
|
form,
|
|
@@ -2860,7 +3031,7 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2860
3031
|
disabled: isEditMode
|
|
2861
3032
|
}
|
|
2862
3033
|
),
|
|
2863
|
-
/* @__PURE__ */
|
|
3034
|
+
/* @__PURE__ */ jsx39(
|
|
2864
3035
|
FormSelect,
|
|
2865
3036
|
{
|
|
2866
3037
|
form,
|
|
@@ -2871,7 +3042,7 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2871
3042
|
}
|
|
2872
3043
|
)
|
|
2873
3044
|
] }),
|
|
2874
|
-
/* @__PURE__ */
|
|
3045
|
+
/* @__PURE__ */ jsx39(
|
|
2875
3046
|
FormInput,
|
|
2876
3047
|
{
|
|
2877
3048
|
form,
|
|
@@ -2880,7 +3051,7 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2880
3051
|
placeholder: "e.g., Standard Plan, Pro Tier"
|
|
2881
3052
|
}
|
|
2882
3053
|
),
|
|
2883
|
-
/* @__PURE__ */
|
|
3054
|
+
/* @__PURE__ */ jsx39(
|
|
2884
3055
|
FormTextarea,
|
|
2885
3056
|
{
|
|
2886
3057
|
form,
|
|
@@ -2890,11 +3061,12 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2890
3061
|
className: "min-h-24"
|
|
2891
3062
|
}
|
|
2892
3063
|
),
|
|
2893
|
-
/* @__PURE__ */
|
|
2894
|
-
|
|
2895
|
-
/* @__PURE__ */
|
|
2896
|
-
|
|
2897
|
-
|
|
3064
|
+
/* @__PURE__ */ jsx39(FormInput, { form, id: "token", name: "Token (optional)", placeholder: "Enter token value" }),
|
|
3065
|
+
/* @__PURE__ */ jsxs34("div", { className: "space-y-2", children: [
|
|
3066
|
+
/* @__PURE__ */ jsx39(Label, { children: "Features (optional)" }),
|
|
3067
|
+
/* @__PURE__ */ jsxs34("div", { className: "space-y-2", children: [
|
|
3068
|
+
form.watch("features").map((_, index) => /* @__PURE__ */ jsxs34("div", { className: "flex gap-2", children: [
|
|
3069
|
+
/* @__PURE__ */ jsx39(
|
|
2898
3070
|
Input,
|
|
2899
3071
|
{
|
|
2900
3072
|
...form.register(`features.${index}`),
|
|
@@ -2902,7 +3074,7 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2902
3074
|
className: "flex-1"
|
|
2903
3075
|
}
|
|
2904
3076
|
),
|
|
2905
|
-
/* @__PURE__ */
|
|
3077
|
+
/* @__PURE__ */ jsx39(
|
|
2906
3078
|
Button,
|
|
2907
3079
|
{
|
|
2908
3080
|
type: "button",
|
|
@@ -2915,11 +3087,11 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2915
3087
|
currentFeatures.filter((_2, i) => i !== index)
|
|
2916
3088
|
);
|
|
2917
3089
|
},
|
|
2918
|
-
children: /* @__PURE__ */
|
|
3090
|
+
children: /* @__PURE__ */ jsx39(XIcon, { className: "h-4 w-4" })
|
|
2919
3091
|
}
|
|
2920
3092
|
)
|
|
2921
3093
|
] }, index)),
|
|
2922
|
-
/* @__PURE__ */
|
|
3094
|
+
/* @__PURE__ */ jsxs34(
|
|
2923
3095
|
Button,
|
|
2924
3096
|
{
|
|
2925
3097
|
type: "button",
|
|
@@ -2931,15 +3103,15 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
|
2931
3103
|
},
|
|
2932
3104
|
className: "mt-2",
|
|
2933
3105
|
children: [
|
|
2934
|
-
/* @__PURE__ */
|
|
3106
|
+
/* @__PURE__ */ jsx39(PlusIcon, { className: "h-4 w-4 mr-2" }),
|
|
2935
3107
|
"Add Feature"
|
|
2936
3108
|
]
|
|
2937
3109
|
}
|
|
2938
3110
|
)
|
|
2939
3111
|
] })
|
|
2940
3112
|
] }),
|
|
2941
|
-
/* @__PURE__ */
|
|
2942
|
-
/* @__PURE__ */
|
|
3113
|
+
/* @__PURE__ */ jsx39(FormCheckbox, { form, id: "active", name: "Active" }),
|
|
3114
|
+
/* @__PURE__ */ jsx39(CommonEditorButtons, { isEdit: isEditMode, form, disabled: isSubmitting, setOpen: onOpenChange })
|
|
2943
3115
|
] }) })
|
|
2944
3116
|
] }) });
|
|
2945
3117
|
}
|
|
@@ -2947,17 +3119,17 @@ __name(PriceEditor, "PriceEditor");
|
|
|
2947
3119
|
|
|
2948
3120
|
// src/features/billing/stripe-price/components/lists/PricesList.tsx
|
|
2949
3121
|
import { Archive, DollarSign, Edit, RotateCcw } from "lucide-react";
|
|
2950
|
-
import { useEffect as
|
|
2951
|
-
import { jsx as
|
|
3122
|
+
import { useEffect as useEffect11, useState as useState15 } from "react";
|
|
3123
|
+
import { jsx as jsx40, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
2952
3124
|
function PricesList({ productId, onPricesChange }) {
|
|
2953
|
-
const [prices, setPrices] =
|
|
2954
|
-
const [loading, setLoading] =
|
|
2955
|
-
const [showCreatePrice, setShowCreatePrice] =
|
|
2956
|
-
const [editingPrice, setEditingPrice] =
|
|
2957
|
-
const [priceToArchive, setPriceToArchive] =
|
|
2958
|
-
const [priceToReactivate, setPriceToReactivate] =
|
|
2959
|
-
const [archivingPriceId, setArchivingPriceId] =
|
|
2960
|
-
const [reactivatingPriceId, setReactivatingPriceId] =
|
|
3125
|
+
const [prices, setPrices] = useState15([]);
|
|
3126
|
+
const [loading, setLoading] = useState15(true);
|
|
3127
|
+
const [showCreatePrice, setShowCreatePrice] = useState15(false);
|
|
3128
|
+
const [editingPrice, setEditingPrice] = useState15(null);
|
|
3129
|
+
const [priceToArchive, setPriceToArchive] = useState15(null);
|
|
3130
|
+
const [priceToReactivate, setPriceToReactivate] = useState15(null);
|
|
3131
|
+
const [archivingPriceId, setArchivingPriceId] = useState15(null);
|
|
3132
|
+
const [reactivatingPriceId, setReactivatingPriceId] = useState15(null);
|
|
2961
3133
|
const loadPrices = /* @__PURE__ */ __name(async () => {
|
|
2962
3134
|
setLoading(true);
|
|
2963
3135
|
try {
|
|
@@ -2969,7 +3141,7 @@ function PricesList({ productId, onPricesChange }) {
|
|
|
2969
3141
|
setLoading(false);
|
|
2970
3142
|
}
|
|
2971
3143
|
}, "loadPrices");
|
|
2972
|
-
|
|
3144
|
+
useEffect11(() => {
|
|
2973
3145
|
loadPrices();
|
|
2974
3146
|
}, [productId]);
|
|
2975
3147
|
const handleArchive = /* @__PURE__ */ __name(async () => {
|
|
@@ -3020,27 +3192,27 @@ function PricesList({ productId, onPricesChange }) {
|
|
|
3020
3192
|
return "";
|
|
3021
3193
|
}, "formatInterval");
|
|
3022
3194
|
if (loading) {
|
|
3023
|
-
return /* @__PURE__ */
|
|
3195
|
+
return /* @__PURE__ */ jsx40("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx40("p", { className: "text-muted-foreground", children: "Loading prices..." }) });
|
|
3024
3196
|
}
|
|
3025
|
-
return /* @__PURE__ */
|
|
3026
|
-
/* @__PURE__ */
|
|
3027
|
-
/* @__PURE__ */
|
|
3028
|
-
/* @__PURE__ */
|
|
3197
|
+
return /* @__PURE__ */ jsxs35("div", { className: "flex flex-col gap-y-4", children: [
|
|
3198
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex items-center justify-between mb-4", children: [
|
|
3199
|
+
/* @__PURE__ */ jsx40("h4", { className: "text-lg font-semibold", children: "Prices" }),
|
|
3200
|
+
/* @__PURE__ */ jsx40(Button, { size: "sm", onClick: () => setShowCreatePrice(true), children: "Add Price" })
|
|
3029
3201
|
] }),
|
|
3030
|
-
prices.length === 0 && /* @__PURE__ */
|
|
3031
|
-
/* @__PURE__ */
|
|
3032
|
-
/* @__PURE__ */
|
|
3033
|
-
/* @__PURE__ */
|
|
3202
|
+
prices.length === 0 && /* @__PURE__ */ jsxs35("div", { className: "bg-background flex flex-col items-center justify-center gap-y-3 rounded-lg border border-dashed p-8", children: [
|
|
3203
|
+
/* @__PURE__ */ jsx40(DollarSign, { className: "text-muted-foreground h-12 w-12" }),
|
|
3204
|
+
/* @__PURE__ */ jsx40("p", { className: "text-muted-foreground text-sm", children: "No prices yet. Add a price to enable subscriptions." }),
|
|
3205
|
+
/* @__PURE__ */ jsx40(Button, { size: "sm", onClick: () => setShowCreatePrice(true), children: "Add Price" })
|
|
3034
3206
|
] }),
|
|
3035
|
-
prices.length > 0 && /* @__PURE__ */
|
|
3207
|
+
prices.length > 0 && /* @__PURE__ */ jsx40("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: prices.map((price) => {
|
|
3036
3208
|
const isArchiving = archivingPriceId === price.id;
|
|
3037
3209
|
const isReactivating = reactivatingPriceId === price.id;
|
|
3038
|
-
return /* @__PURE__ */
|
|
3039
|
-
/* @__PURE__ */
|
|
3040
|
-
/* @__PURE__ */
|
|
3041
|
-
/* @__PURE__ */
|
|
3042
|
-
/* @__PURE__ */
|
|
3043
|
-
price.active ? /* @__PURE__ */
|
|
3210
|
+
return /* @__PURE__ */ jsxs35("div", { className: "border rounded-lg bg-white p-4 hover:shadow-sm transition-shadow", children: [
|
|
3211
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex items-start justify-between mb-3", children: [
|
|
3212
|
+
/* @__PURE__ */ jsx40(DollarSign, { className: "h-5 w-5 text-primary" }),
|
|
3213
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex gap-1", children: [
|
|
3214
|
+
/* @__PURE__ */ jsx40(Button, { variant: "ghost", size: "sm", onClick: () => setEditingPrice(price), className: "h-8 w-8 p-0", children: /* @__PURE__ */ jsx40(Edit, { className: "h-4 w-4" }) }),
|
|
3215
|
+
price.active ? /* @__PURE__ */ jsx40(
|
|
3044
3216
|
Button,
|
|
3045
3217
|
{
|
|
3046
3218
|
variant: "ghost",
|
|
@@ -3048,9 +3220,9 @@ function PricesList({ productId, onPricesChange }) {
|
|
|
3048
3220
|
onClick: () => setPriceToArchive(price),
|
|
3049
3221
|
className: "h-8 w-8 p-0",
|
|
3050
3222
|
disabled: isArchiving,
|
|
3051
|
-
children: /* @__PURE__ */
|
|
3223
|
+
children: /* @__PURE__ */ jsx40(Archive, { className: "h-4 w-4" })
|
|
3052
3224
|
}
|
|
3053
|
-
) : /* @__PURE__ */
|
|
3225
|
+
) : /* @__PURE__ */ jsx40(
|
|
3054
3226
|
Button,
|
|
3055
3227
|
{
|
|
3056
3228
|
variant: "ghost",
|
|
@@ -3058,25 +3230,25 @@ function PricesList({ productId, onPricesChange }) {
|
|
|
3058
3230
|
onClick: () => setPriceToReactivate(price),
|
|
3059
3231
|
className: "h-8 w-8 p-0",
|
|
3060
3232
|
disabled: isReactivating,
|
|
3061
|
-
children: /* @__PURE__ */
|
|
3233
|
+
children: /* @__PURE__ */ jsx40(RotateCcw, { className: "h-4 w-4" })
|
|
3062
3234
|
}
|
|
3063
3235
|
)
|
|
3064
3236
|
] })
|
|
3065
3237
|
] }),
|
|
3066
|
-
/* @__PURE__ */
|
|
3238
|
+
/* @__PURE__ */ jsx40("div", { className: "mb-2", children: /* @__PURE__ */ jsxs35("div", { className: "text-2xl font-bold", children: [
|
|
3067
3239
|
formatCurrency(price.unitAmount, price.currency),
|
|
3068
3240
|
" ",
|
|
3069
|
-
/* @__PURE__ */
|
|
3241
|
+
/* @__PURE__ */ jsx40("span", { className: "text-muted-foreground text-sm font-normal", children: formatInterval2(price) })
|
|
3070
3242
|
] }) }),
|
|
3071
|
-
price.metadata?.nickname && /* @__PURE__ */
|
|
3072
|
-
/* @__PURE__ */
|
|
3073
|
-
price.active ? /* @__PURE__ */
|
|
3074
|
-
price.recurring?.usageType === "metered" && /* @__PURE__ */
|
|
3075
|
-
/* @__PURE__ */
|
|
3243
|
+
price.metadata?.nickname && /* @__PURE__ */ jsx40("p", { className: "text-sm font-medium mb-2", children: price.metadata.nickname }),
|
|
3244
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex flex-wrap gap-2", children: [
|
|
3245
|
+
price.active ? /* @__PURE__ */ jsx40("span", { className: "bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full font-medium", children: "Active" }) : /* @__PURE__ */ jsx40("span", { className: "bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full font-medium", children: "Inactive" }),
|
|
3246
|
+
price.recurring?.usageType === "metered" && /* @__PURE__ */ jsx40("span", { className: "bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full font-medium", children: "Metered" }),
|
|
3247
|
+
/* @__PURE__ */ jsx40("span", { className: "bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full font-medium uppercase", children: price.currency })
|
|
3076
3248
|
] })
|
|
3077
3249
|
] }, price.id);
|
|
3078
3250
|
}) }),
|
|
3079
|
-
showCreatePrice && /* @__PURE__ */
|
|
3251
|
+
showCreatePrice && /* @__PURE__ */ jsx40(
|
|
3080
3252
|
PriceEditor,
|
|
3081
3253
|
{
|
|
3082
3254
|
productId,
|
|
@@ -3088,7 +3260,7 @@ function PricesList({ productId, onPricesChange }) {
|
|
|
3088
3260
|
}
|
|
3089
3261
|
}
|
|
3090
3262
|
),
|
|
3091
|
-
editingPrice && /* @__PURE__ */
|
|
3263
|
+
editingPrice && /* @__PURE__ */ jsx40(
|
|
3092
3264
|
PriceEditor,
|
|
3093
3265
|
{
|
|
3094
3266
|
productId,
|
|
@@ -3102,19 +3274,19 @@ function PricesList({ productId, onPricesChange }) {
|
|
|
3102
3274
|
}
|
|
3103
3275
|
}
|
|
3104
3276
|
),
|
|
3105
|
-
/* @__PURE__ */
|
|
3106
|
-
/* @__PURE__ */
|
|
3107
|
-
/* @__PURE__ */
|
|
3108
|
-
/* @__PURE__ */
|
|
3277
|
+
/* @__PURE__ */ jsx40(AlertDialog, { open: !!priceToArchive, onOpenChange: (open) => !open && setPriceToArchive(null), children: /* @__PURE__ */ jsxs35(AlertDialogContent, { children: [
|
|
3278
|
+
/* @__PURE__ */ jsxs35(AlertDialogHeader, { children: [
|
|
3279
|
+
/* @__PURE__ */ jsx40(AlertDialogTitle, { children: "Archive Price" }),
|
|
3280
|
+
/* @__PURE__ */ jsxs35(AlertDialogDescription, { children: [
|
|
3109
3281
|
"Are you sure you want to archive the price for",
|
|
3110
3282
|
" ",
|
|
3111
3283
|
priceToArchive && `${formatCurrency(priceToArchive.unitAmount, priceToArchive.currency)} ${formatInterval2(priceToArchive)}`,
|
|
3112
3284
|
"? This will prevent new subscriptions but existing ones will continue."
|
|
3113
3285
|
] })
|
|
3114
3286
|
] }),
|
|
3115
|
-
/* @__PURE__ */
|
|
3116
|
-
/* @__PURE__ */
|
|
3117
|
-
/* @__PURE__ */
|
|
3287
|
+
/* @__PURE__ */ jsxs35(AlertDialogFooter, { children: [
|
|
3288
|
+
/* @__PURE__ */ jsx40(AlertDialogCancel, { disabled: !!archivingPriceId, children: "Cancel" }),
|
|
3289
|
+
/* @__PURE__ */ jsx40(
|
|
3118
3290
|
AlertDialogAction,
|
|
3119
3291
|
{
|
|
3120
3292
|
onClick: handleArchive,
|
|
@@ -3125,19 +3297,19 @@ function PricesList({ productId, onPricesChange }) {
|
|
|
3125
3297
|
)
|
|
3126
3298
|
] })
|
|
3127
3299
|
] }) }),
|
|
3128
|
-
/* @__PURE__ */
|
|
3129
|
-
/* @__PURE__ */
|
|
3130
|
-
/* @__PURE__ */
|
|
3131
|
-
/* @__PURE__ */
|
|
3300
|
+
/* @__PURE__ */ jsx40(AlertDialog, { open: !!priceToReactivate, onOpenChange: (open) => !open && setPriceToReactivate(null), children: /* @__PURE__ */ jsxs35(AlertDialogContent, { children: [
|
|
3301
|
+
/* @__PURE__ */ jsxs35(AlertDialogHeader, { children: [
|
|
3302
|
+
/* @__PURE__ */ jsx40(AlertDialogTitle, { children: "Reactivate Price" }),
|
|
3303
|
+
/* @__PURE__ */ jsxs35(AlertDialogDescription, { children: [
|
|
3132
3304
|
"Are you sure you want to reactivate the price for",
|
|
3133
3305
|
" ",
|
|
3134
3306
|
priceToReactivate && `${formatCurrency(priceToReactivate.unitAmount, priceToReactivate.currency)} ${formatInterval2(priceToReactivate)}`,
|
|
3135
3307
|
"? This will allow new subscriptions again."
|
|
3136
3308
|
] })
|
|
3137
3309
|
] }),
|
|
3138
|
-
/* @__PURE__ */
|
|
3139
|
-
/* @__PURE__ */
|
|
3140
|
-
/* @__PURE__ */
|
|
3310
|
+
/* @__PURE__ */ jsxs35(AlertDialogFooter, { children: [
|
|
3311
|
+
/* @__PURE__ */ jsx40(AlertDialogCancel, { disabled: !!reactivatingPriceId, children: "Cancel" }),
|
|
3312
|
+
/* @__PURE__ */ jsx40(
|
|
3141
3313
|
AlertDialogAction,
|
|
3142
3314
|
{
|
|
3143
3315
|
onClick: handleReactivate,
|
|
@@ -3154,20 +3326,20 @@ __name(PricesList, "PricesList");
|
|
|
3154
3326
|
|
|
3155
3327
|
// src/features/billing/stripe-product/components/containers/ProductsAdminContainer.tsx
|
|
3156
3328
|
import { Package as Package2 } from "lucide-react";
|
|
3157
|
-
import { useEffect as
|
|
3329
|
+
import { useEffect as useEffect12, useState as useState18 } from "react";
|
|
3158
3330
|
|
|
3159
3331
|
// src/features/billing/stripe-product/components/forms/ProductEditor.tsx
|
|
3160
3332
|
import { zodResolver as zodResolver3 } from "@hookform/resolvers/zod";
|
|
3161
|
-
import { useState as
|
|
3333
|
+
import { useState as useState16 } from "react";
|
|
3162
3334
|
import { useForm as useForm3 } from "react-hook-form";
|
|
3163
|
-
import { v4 as
|
|
3335
|
+
import { v4 as v42 } from "uuid";
|
|
3164
3336
|
import { z as z3 } from "zod";
|
|
3165
|
-
import { jsx as
|
|
3337
|
+
import { jsx as jsx41, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
3166
3338
|
function ProductEditor({ product, open, onOpenChange, onSuccess }) {
|
|
3167
|
-
const [isSubmitting, setIsSubmitting] =
|
|
3339
|
+
const [isSubmitting, setIsSubmitting] = useState16(false);
|
|
3168
3340
|
const formSchema2 = z3.object({
|
|
3169
3341
|
name: z3.string().min(1, { message: "Product name is required" }),
|
|
3170
|
-
description: z3.string().
|
|
3342
|
+
description: z3.string().min(1, { message: "Description is required" }),
|
|
3171
3343
|
active: z3.boolean()
|
|
3172
3344
|
});
|
|
3173
3345
|
const form = useForm3({
|
|
@@ -3190,7 +3362,7 @@ function ProductEditor({ product, open, onOpenChange, onSuccess }) {
|
|
|
3190
3362
|
});
|
|
3191
3363
|
} else {
|
|
3192
3364
|
await StripeProductService.createProduct({
|
|
3193
|
-
id:
|
|
3365
|
+
id: v42(),
|
|
3194
3366
|
name: values.name,
|
|
3195
3367
|
description: values.description,
|
|
3196
3368
|
active: values.active
|
|
@@ -3204,25 +3376,25 @@ function ProductEditor({ product, open, onOpenChange, onSuccess }) {
|
|
|
3204
3376
|
setIsSubmitting(false);
|
|
3205
3377
|
}
|
|
3206
3378
|
}, "onSubmit");
|
|
3207
|
-
return /* @__PURE__ */
|
|
3208
|
-
/* @__PURE__ */
|
|
3209
|
-
/* @__PURE__ */
|
|
3210
|
-
/* @__PURE__ */
|
|
3379
|
+
return /* @__PURE__ */ jsx41(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs36(DialogContent, { className: "max-w-2xl", children: [
|
|
3380
|
+
/* @__PURE__ */ jsxs36(DialogHeader, { children: [
|
|
3381
|
+
/* @__PURE__ */ jsx41(DialogTitle, { children: product ? "Edit Product" : "Create Product" }),
|
|
3382
|
+
/* @__PURE__ */ jsx41(DialogDescription, { children: product ? `Update the details for ${product.name}` : "Create a new product to offer to your customers" })
|
|
3211
3383
|
] }),
|
|
3212
|
-
/* @__PURE__ */
|
|
3213
|
-
/* @__PURE__ */
|
|
3214
|
-
/* @__PURE__ */
|
|
3384
|
+
/* @__PURE__ */ jsx41(Form, { ...form, children: /* @__PURE__ */ jsxs36("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-col gap-y-4", children: [
|
|
3385
|
+
/* @__PURE__ */ jsx41(FormInput, { form, id: "name", name: "Product Name", placeholder: "Enter product name", isRequired: true }),
|
|
3386
|
+
/* @__PURE__ */ jsx41(
|
|
3215
3387
|
FormTextarea,
|
|
3216
3388
|
{
|
|
3217
3389
|
form,
|
|
3218
3390
|
id: "description",
|
|
3219
3391
|
name: "Description",
|
|
3220
|
-
placeholder: "Enter product description
|
|
3392
|
+
placeholder: "Enter product description",
|
|
3221
3393
|
className: "min-h-32"
|
|
3222
3394
|
}
|
|
3223
3395
|
),
|
|
3224
|
-
/* @__PURE__ */
|
|
3225
|
-
/* @__PURE__ */
|
|
3396
|
+
/* @__PURE__ */ jsx41(FormCheckbox, { form, id: "active", name: "Active" }),
|
|
3397
|
+
/* @__PURE__ */ jsx41(CommonEditorButtons, { isEdit: !!product, form, disabled: isSubmitting, setOpen: onOpenChange })
|
|
3226
3398
|
] }) })
|
|
3227
3399
|
] }) });
|
|
3228
3400
|
}
|
|
@@ -3230,15 +3402,15 @@ __name(ProductEditor, "ProductEditor");
|
|
|
3230
3402
|
|
|
3231
3403
|
// src/features/billing/stripe-product/components/lists/ProductsList.tsx
|
|
3232
3404
|
import { Archive as Archive2, ChevronDown, ChevronUp, Edit as Edit2, Package, RefreshCw as RefreshCw2 } from "lucide-react";
|
|
3233
|
-
import { useState as
|
|
3234
|
-
import { jsx as
|
|
3405
|
+
import { useState as useState17 } from "react";
|
|
3406
|
+
import { jsx as jsx42, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
3235
3407
|
function ProductsList({ products, onProductsChange }) {
|
|
3236
|
-
const [expandedProductId, setExpandedProductId] =
|
|
3237
|
-
const [editingProduct, setEditingProduct] =
|
|
3238
|
-
const [archivingProductId, setArchivingProductId] =
|
|
3239
|
-
const [reactivatingProductId, setReactivatingProductId] =
|
|
3240
|
-
const [productToArchive, setProductToArchive] =
|
|
3241
|
-
const [productToReactivate, setProductToReactivate] =
|
|
3408
|
+
const [expandedProductId, setExpandedProductId] = useState17(null);
|
|
3409
|
+
const [editingProduct, setEditingProduct] = useState17(null);
|
|
3410
|
+
const [archivingProductId, setArchivingProductId] = useState17(null);
|
|
3411
|
+
const [reactivatingProductId, setReactivatingProductId] = useState17(null);
|
|
3412
|
+
const [productToArchive, setProductToArchive] = useState17(null);
|
|
3413
|
+
const [productToReactivate, setProductToReactivate] = useState17(null);
|
|
3242
3414
|
const handleArchive = /* @__PURE__ */ __name(async () => {
|
|
3243
3415
|
if (!productToArchive) {
|
|
3244
3416
|
return;
|
|
@@ -3271,29 +3443,29 @@ function ProductsList({ products, onProductsChange }) {
|
|
|
3271
3443
|
const toggleExpand = /* @__PURE__ */ __name((productId) => {
|
|
3272
3444
|
setExpandedProductId(expandedProductId === productId ? null : productId);
|
|
3273
3445
|
}, "toggleExpand");
|
|
3274
|
-
return /* @__PURE__ */
|
|
3446
|
+
return /* @__PURE__ */ jsxs37("div", { className: "flex flex-col gap-y-4", children: [
|
|
3275
3447
|
products.map((product) => {
|
|
3276
3448
|
const isExpanded = expandedProductId === product.id;
|
|
3277
3449
|
const isArchiving = archivingProductId === product.id;
|
|
3278
3450
|
const isReactivating = reactivatingProductId === product.id;
|
|
3279
|
-
return /* @__PURE__ */
|
|
3280
|
-
/* @__PURE__ */
|
|
3281
|
-
/* @__PURE__ */
|
|
3282
|
-
/* @__PURE__ */
|
|
3283
|
-
/* @__PURE__ */
|
|
3284
|
-
/* @__PURE__ */
|
|
3285
|
-
/* @__PURE__ */
|
|
3286
|
-
product.active ? /* @__PURE__ */
|
|
3451
|
+
return /* @__PURE__ */ jsxs37("div", { className: "border rounded-lg bg-white shadow-sm hover:shadow-md transition-shadow", children: [
|
|
3452
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex items-center justify-between p-6", children: [
|
|
3453
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-x-4 flex-1", children: [
|
|
3454
|
+
/* @__PURE__ */ jsx42(Package, { className: "h-6 w-6 text-primary" }),
|
|
3455
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex-1", children: [
|
|
3456
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-x-3", children: [
|
|
3457
|
+
/* @__PURE__ */ jsx42("h3", { className: "text-lg font-semibold", children: product.name }),
|
|
3458
|
+
product.active ? /* @__PURE__ */ jsx42("span", { className: "bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full font-medium", children: "Active" }) : /* @__PURE__ */ jsx42("span", { className: "bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full font-medium", children: "Inactive" })
|
|
3287
3459
|
] }),
|
|
3288
|
-
product.description && /* @__PURE__ */
|
|
3460
|
+
product.description && /* @__PURE__ */ jsx42("p", { className: "text-muted-foreground text-sm mt-1", children: product.description })
|
|
3289
3461
|
] })
|
|
3290
3462
|
] }),
|
|
3291
|
-
/* @__PURE__ */
|
|
3292
|
-
/* @__PURE__ */
|
|
3293
|
-
/* @__PURE__ */
|
|
3463
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-x-2", children: [
|
|
3464
|
+
/* @__PURE__ */ jsxs37(Button, { variant: "outline", size: "sm", onClick: () => setEditingProduct(product), children: [
|
|
3465
|
+
/* @__PURE__ */ jsx42(Edit2, { className: "h-4 w-4 mr-1" }),
|
|
3294
3466
|
"Edit"
|
|
3295
3467
|
] }),
|
|
3296
|
-
product.active ? /* @__PURE__ */
|
|
3468
|
+
product.active ? /* @__PURE__ */ jsxs37(
|
|
3297
3469
|
Button,
|
|
3298
3470
|
{
|
|
3299
3471
|
variant: "outline",
|
|
@@ -3301,11 +3473,11 @@ function ProductsList({ products, onProductsChange }) {
|
|
|
3301
3473
|
onClick: () => setProductToArchive(product),
|
|
3302
3474
|
disabled: isArchiving,
|
|
3303
3475
|
children: [
|
|
3304
|
-
/* @__PURE__ */
|
|
3476
|
+
/* @__PURE__ */ jsx42(Archive2, { className: "h-4 w-4 mr-1" }),
|
|
3305
3477
|
isArchiving ? "Archiving..." : "Archive"
|
|
3306
3478
|
]
|
|
3307
3479
|
}
|
|
3308
|
-
) : /* @__PURE__ */
|
|
3480
|
+
) : /* @__PURE__ */ jsxs37(
|
|
3309
3481
|
Button,
|
|
3310
3482
|
{
|
|
3311
3483
|
variant: "outline",
|
|
@@ -3313,18 +3485,18 @@ function ProductsList({ products, onProductsChange }) {
|
|
|
3313
3485
|
onClick: () => setProductToReactivate(product),
|
|
3314
3486
|
disabled: isReactivating,
|
|
3315
3487
|
children: [
|
|
3316
|
-
/* @__PURE__ */
|
|
3488
|
+
/* @__PURE__ */ jsx42(RefreshCw2, { className: "h-4 w-4 mr-1" }),
|
|
3317
3489
|
isReactivating ? "Reactivating..." : "Reactivate"
|
|
3318
3490
|
]
|
|
3319
3491
|
}
|
|
3320
3492
|
),
|
|
3321
|
-
/* @__PURE__ */
|
|
3493
|
+
/* @__PURE__ */ jsx42(Button, { variant: "ghost", size: "sm", onClick: () => toggleExpand(product.id), children: isExpanded ? /* @__PURE__ */ jsx42(ChevronUp, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx42(ChevronDown, { className: "h-5 w-5" }) })
|
|
3322
3494
|
] })
|
|
3323
3495
|
] }),
|
|
3324
|
-
isExpanded && /* @__PURE__ */
|
|
3496
|
+
isExpanded && /* @__PURE__ */ jsx42("div", { className: "border-t bg-muted/30 p-6", children: /* @__PURE__ */ jsx42(PricesList, { productId: product.id, onPricesChange: onProductsChange }) })
|
|
3325
3497
|
] }, product.id);
|
|
3326
3498
|
}),
|
|
3327
|
-
editingProduct && /* @__PURE__ */
|
|
3499
|
+
editingProduct && /* @__PURE__ */ jsx42(
|
|
3328
3500
|
ProductEditor,
|
|
3329
3501
|
{
|
|
3330
3502
|
product: editingProduct,
|
|
@@ -3336,18 +3508,18 @@ function ProductsList({ products, onProductsChange }) {
|
|
|
3336
3508
|
}
|
|
3337
3509
|
}
|
|
3338
3510
|
),
|
|
3339
|
-
/* @__PURE__ */
|
|
3340
|
-
/* @__PURE__ */
|
|
3341
|
-
/* @__PURE__ */
|
|
3342
|
-
/* @__PURE__ */
|
|
3511
|
+
/* @__PURE__ */ jsx42(AlertDialog, { open: !!productToArchive, onOpenChange: (open) => !open && setProductToArchive(null), children: /* @__PURE__ */ jsxs37(AlertDialogContent, { children: [
|
|
3512
|
+
/* @__PURE__ */ jsxs37(AlertDialogHeader, { children: [
|
|
3513
|
+
/* @__PURE__ */ jsx42(AlertDialogTitle, { children: "Archive Product" }),
|
|
3514
|
+
/* @__PURE__ */ jsxs37(AlertDialogDescription, { children: [
|
|
3343
3515
|
'Are you sure you want to archive "',
|
|
3344
3516
|
productToArchive?.name,
|
|
3345
3517
|
'"? This will deactivate it and it will no longer be available for new subscriptions.'
|
|
3346
3518
|
] })
|
|
3347
3519
|
] }),
|
|
3348
|
-
/* @__PURE__ */
|
|
3349
|
-
/* @__PURE__ */
|
|
3350
|
-
/* @__PURE__ */
|
|
3520
|
+
/* @__PURE__ */ jsxs37(AlertDialogFooter, { children: [
|
|
3521
|
+
/* @__PURE__ */ jsx42(AlertDialogCancel, { disabled: !!archivingProductId, children: "Cancel" }),
|
|
3522
|
+
/* @__PURE__ */ jsx42(
|
|
3351
3523
|
AlertDialogAction,
|
|
3352
3524
|
{
|
|
3353
3525
|
onClick: handleArchive,
|
|
@@ -3358,18 +3530,18 @@ function ProductsList({ products, onProductsChange }) {
|
|
|
3358
3530
|
)
|
|
3359
3531
|
] })
|
|
3360
3532
|
] }) }),
|
|
3361
|
-
/* @__PURE__ */
|
|
3362
|
-
/* @__PURE__ */
|
|
3363
|
-
/* @__PURE__ */
|
|
3364
|
-
/* @__PURE__ */
|
|
3533
|
+
/* @__PURE__ */ jsx42(AlertDialog, { open: !!productToReactivate, onOpenChange: (open) => !open && setProductToReactivate(null), children: /* @__PURE__ */ jsxs37(AlertDialogContent, { children: [
|
|
3534
|
+
/* @__PURE__ */ jsxs37(AlertDialogHeader, { children: [
|
|
3535
|
+
/* @__PURE__ */ jsx42(AlertDialogTitle, { children: "Reactivate Product" }),
|
|
3536
|
+
/* @__PURE__ */ jsxs37(AlertDialogDescription, { children: [
|
|
3365
3537
|
'Are you sure you want to reactivate "',
|
|
3366
3538
|
productToReactivate?.name,
|
|
3367
3539
|
'"? This will make it available for new subscriptions again.'
|
|
3368
3540
|
] })
|
|
3369
3541
|
] }),
|
|
3370
|
-
/* @__PURE__ */
|
|
3371
|
-
/* @__PURE__ */
|
|
3372
|
-
/* @__PURE__ */
|
|
3542
|
+
/* @__PURE__ */ jsxs37(AlertDialogFooter, { children: [
|
|
3543
|
+
/* @__PURE__ */ jsx42(AlertDialogCancel, { disabled: !!reactivatingProductId, children: "Cancel" }),
|
|
3544
|
+
/* @__PURE__ */ jsx42(
|
|
3373
3545
|
AlertDialogAction,
|
|
3374
3546
|
{
|
|
3375
3547
|
onClick: handleReactivate,
|
|
@@ -3385,14 +3557,14 @@ function ProductsList({ products, onProductsChange }) {
|
|
|
3385
3557
|
__name(ProductsList, "ProductsList");
|
|
3386
3558
|
|
|
3387
3559
|
// src/features/billing/stripe-product/components/containers/ProductsAdminContainer.tsx
|
|
3388
|
-
import { jsx as
|
|
3560
|
+
import { jsx as jsx43, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
3389
3561
|
function ProductsAdminContainer() {
|
|
3390
3562
|
const { hasRole } = useCurrentUserContext();
|
|
3391
|
-
const [products, setProducts] =
|
|
3392
|
-
const [loading, setLoading] =
|
|
3393
|
-
const [showCreateProduct, setShowCreateProduct] =
|
|
3563
|
+
const [products, setProducts] = useState18([]);
|
|
3564
|
+
const [loading, setLoading] = useState18(true);
|
|
3565
|
+
const [showCreateProduct, setShowCreateProduct] = useState18(false);
|
|
3394
3566
|
if (!hasRole(getRoleId().Administrator)) {
|
|
3395
|
-
return /* @__PURE__ */
|
|
3567
|
+
return /* @__PURE__ */ jsx43("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx43("p", { className: "text-red-600 font-semibold", children: "Permission denied. Administrator access required." }) });
|
|
3396
3568
|
}
|
|
3397
3569
|
const loadProducts = /* @__PURE__ */ __name(async () => {
|
|
3398
3570
|
setLoading(true);
|
|
@@ -3405,30 +3577,30 @@ function ProductsAdminContainer() {
|
|
|
3405
3577
|
setLoading(false);
|
|
3406
3578
|
}
|
|
3407
3579
|
}, "loadProducts");
|
|
3408
|
-
|
|
3580
|
+
useEffect12(() => {
|
|
3409
3581
|
loadProducts();
|
|
3410
3582
|
}, []);
|
|
3411
3583
|
if (loading) {
|
|
3412
|
-
return /* @__PURE__ */
|
|
3584
|
+
return /* @__PURE__ */ jsx43("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx43("p", { className: "text-muted-foreground", children: "Loading products..." }) });
|
|
3413
3585
|
}
|
|
3414
|
-
return /* @__PURE__ */
|
|
3415
|
-
/* @__PURE__ */
|
|
3416
|
-
/* @__PURE__ */
|
|
3417
|
-
/* @__PURE__ */
|
|
3418
|
-
/* @__PURE__ */
|
|
3586
|
+
return /* @__PURE__ */ jsxs38("div", { className: "flex w-full flex-col gap-y-6", children: [
|
|
3587
|
+
/* @__PURE__ */ jsxs38("div", { className: "flex items-center justify-between", children: [
|
|
3588
|
+
/* @__PURE__ */ jsxs38("div", { className: "flex items-center gap-x-3", children: [
|
|
3589
|
+
/* @__PURE__ */ jsx43(Package2, { className: "h-8 w-8" }),
|
|
3590
|
+
/* @__PURE__ */ jsx43("h1", { className: "text-3xl font-bold", children: "Product & Price Management" })
|
|
3419
3591
|
] }),
|
|
3420
|
-
/* @__PURE__ */
|
|
3592
|
+
/* @__PURE__ */ jsx43(Button, { onClick: () => setShowCreateProduct(true), children: "Create Product" })
|
|
3421
3593
|
] }),
|
|
3422
|
-
products.length === 0 && /* @__PURE__ */
|
|
3423
|
-
/* @__PURE__ */
|
|
3424
|
-
/* @__PURE__ */
|
|
3425
|
-
/* @__PURE__ */
|
|
3426
|
-
/* @__PURE__ */
|
|
3427
|
-
/* @__PURE__ */
|
|
3594
|
+
products.length === 0 && /* @__PURE__ */ jsxs38("div", { className: "bg-muted/50 flex flex-col items-center justify-center gap-y-4 rounded-lg border-2 border-dashed p-12", children: [
|
|
3595
|
+
/* @__PURE__ */ jsx43(Package2, { className: "text-muted-foreground h-16 w-16" }),
|
|
3596
|
+
/* @__PURE__ */ jsxs38("div", { className: "text-center", children: [
|
|
3597
|
+
/* @__PURE__ */ jsx43("h3", { className: "mb-2 text-xl font-semibold", children: "No products yet" }),
|
|
3598
|
+
/* @__PURE__ */ jsx43("p", { className: "text-muted-foreground mb-4", children: "Create your first product to start offering subscriptions to your customers." }),
|
|
3599
|
+
/* @__PURE__ */ jsx43(Button, { onClick: () => setShowCreateProduct(true), children: "Create Your First Product" })
|
|
3428
3600
|
] })
|
|
3429
3601
|
] }),
|
|
3430
|
-
products.length > 0 && /* @__PURE__ */
|
|
3431
|
-
showCreateProduct && /* @__PURE__ */
|
|
3602
|
+
products.length > 0 && /* @__PURE__ */ jsx43(ProductsList, { products, onProductsChange: loadProducts }),
|
|
3603
|
+
showCreateProduct && /* @__PURE__ */ jsx43(ProductEditor, { open: showCreateProduct, onOpenChange: setShowCreateProduct, onSuccess: loadProducts })
|
|
3432
3604
|
] });
|
|
3433
3605
|
}
|
|
3434
3606
|
__name(ProductsAdminContainer, "ProductsAdminContainer");
|
|
@@ -3439,6 +3611,7 @@ export {
|
|
|
3439
3611
|
BillingUsageSummaryCard,
|
|
3440
3612
|
CancelSubscriptionDialog,
|
|
3441
3613
|
CustomerInfoCard,
|
|
3614
|
+
IntervalToggle,
|
|
3442
3615
|
InvoiceDetails,
|
|
3443
3616
|
InvoiceStatusBadge,
|
|
3444
3617
|
InvoicesContainer,
|
|
@@ -3446,20 +3619,22 @@ export {
|
|
|
3446
3619
|
InvoicesSummaryCard,
|
|
3447
3620
|
PaymentMethodCard,
|
|
3448
3621
|
PaymentMethodEditor,
|
|
3622
|
+
PaymentMethodForm,
|
|
3449
3623
|
PaymentMethodSummaryCard,
|
|
3450
3624
|
PaymentMethodsContainer,
|
|
3451
3625
|
PaymentMethodsList,
|
|
3452
3626
|
PriceEditor,
|
|
3453
3627
|
PricesList,
|
|
3454
3628
|
PricingCard,
|
|
3455
|
-
PricingCardsGrid,
|
|
3456
3629
|
ProductEditor,
|
|
3630
|
+
ProductPricingList,
|
|
3631
|
+
ProductPricingRow,
|
|
3457
3632
|
ProductsAdminContainer,
|
|
3458
3633
|
ProductsList,
|
|
3459
3634
|
ProrationPreview,
|
|
3460
3635
|
StripeProvider,
|
|
3636
|
+
SubscriptionConfirmation,
|
|
3461
3637
|
SubscriptionDetails,
|
|
3462
|
-
SubscriptionEditor,
|
|
3463
3638
|
SubscriptionStatusBadge,
|
|
3464
3639
|
SubscriptionSummaryCard,
|
|
3465
3640
|
SubscriptionsContainer,
|