@carlonicora/nextjs-jsonapi 1.32.1 → 1.33.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.
Files changed (103) hide show
  1. package/dist/{BlockNoteEditor-YEVSJSOI.js → BlockNoteEditor-CUXI6ZTZ.js} +14 -14
  2. package/dist/{BlockNoteEditor-YEVSJSOI.js.map → BlockNoteEditor-CUXI6ZTZ.js.map} +1 -1
  3. package/dist/{BlockNoteEditor-TFL6ZXIJ.mjs → BlockNoteEditor-UTZ7F23J.mjs} +4 -4
  4. package/dist/billing/index.d.mts +6 -3
  5. package/dist/billing/index.d.ts +6 -3
  6. package/dist/billing/index.js +465 -384
  7. package/dist/billing/index.js.map +1 -1
  8. package/dist/billing/index.mjs +114 -33
  9. package/dist/billing/index.mjs.map +1 -1
  10. package/dist/{chunk-NPNKFWV2.js → chunk-2PHWAL6Q.js} +4 -4
  11. package/dist/chunk-2PHWAL6Q.js.map +1 -0
  12. package/dist/{chunk-SLANIL6B.mjs → chunk-53WT73E6.mjs} +56 -64
  13. package/dist/chunk-53WT73E6.mjs.map +1 -0
  14. package/dist/{chunk-YCP2OMFD.mjs → chunk-HWQBSVBT.mjs} +40 -7
  15. package/dist/chunk-HWQBSVBT.mjs.map +1 -0
  16. package/dist/{chunk-HIF7DYR3.js → chunk-RSHCU3TI.js} +553 -561
  17. package/dist/chunk-RSHCU3TI.js.map +1 -0
  18. package/dist/{chunk-KYG2PIRB.js → chunk-TZRAOUAR.js} +118 -85
  19. package/dist/chunk-TZRAOUAR.js.map +1 -0
  20. package/dist/{chunk-IXVNXOZT.mjs → chunk-XLMJPA4N.mjs} +4 -4
  21. package/dist/{chunk-IXVNXOZT.mjs.map → chunk-XLMJPA4N.mjs.map} +1 -1
  22. package/dist/client/index.d.mts +7 -6
  23. package/dist/client/index.d.ts +7 -6
  24. package/dist/client/index.js +4 -4
  25. package/dist/client/index.mjs +3 -3
  26. package/dist/components/index.d.mts +4 -3
  27. package/dist/components/index.d.ts +4 -3
  28. package/dist/components/index.js +4 -4
  29. package/dist/components/index.mjs +3 -3
  30. package/dist/{config-CHwoRDOp.d.ts → config-BbaBV_yk.d.ts} +1 -1
  31. package/dist/{config-DiWyJzk9.d.mts → config-BxwhHdCD.d.mts} +1 -1
  32. package/dist/{content.interface-BSpowEiW.d.mts → content.interface-CWV0q4lZ.d.mts} +1 -1
  33. package/dist/{content.interface-DFQ7mkpL.d.ts → content.interface-CgUu4771.d.ts} +1 -1
  34. package/dist/contexts/index.d.mts +3 -2
  35. package/dist/contexts/index.d.ts +3 -2
  36. package/dist/contexts/index.js +4 -4
  37. package/dist/contexts/index.mjs +3 -3
  38. package/dist/core/index.d.mts +17 -8
  39. package/dist/core/index.d.ts +17 -8
  40. package/dist/core/index.js +6 -2
  41. package/dist/core/index.js.map +1 -1
  42. package/dist/core/index.mjs +5 -1
  43. package/dist/feature.interface-BxFFOPNq.d.mts +19 -0
  44. package/dist/feature.interface-CIWxo8NP.d.ts +19 -0
  45. package/dist/index.d.mts +10 -9
  46. package/dist/index.d.ts +10 -9
  47. package/dist/index.js +7 -3
  48. package/dist/index.js.map +1 -1
  49. package/dist/index.mjs +6 -2
  50. package/dist/{notification.interface-D5MbtfZK.d.mts → notification.interface-DIln2r7X.d.mts} +2 -17
  51. package/dist/{notification.interface-CmKmObIU.d.ts → notification.interface-XARGKJAq.d.ts} +2 -17
  52. package/dist/{s3.service-CoC0k0iu.d.ts → s3.service-DcqkGrKD.d.ts} +12 -3
  53. package/dist/{s3.service-Duh9HW2n.d.mts → s3.service-ag6M_7GO.d.mts} +12 -3
  54. package/dist/scripts/generate-web-module/templates/pages/detail-page.template.js +1 -1
  55. package/dist/scripts/generate-web-module/templates/pages/detail-page.template.js.map +1 -1
  56. package/dist/scripts/generate-web-module/templates/pages/list-page.template.js +1 -1
  57. package/dist/scripts/generate-web-module/templates/pages/list-page.template.js.map +1 -1
  58. package/dist/server/index.d.mts +4 -3
  59. package/dist/server/index.d.ts +4 -3
  60. package/dist/server/index.js +3 -3
  61. package/dist/server/index.mjs +1 -1
  62. package/dist/{stripe-subscription.interface-BaZUngWe.d.ts → stripe-subscription.interface-Dm__xmvE.d.ts} +3 -0
  63. package/dist/{stripe-subscription.interface-Cm_It1fz.d.mts → stripe-subscription.interface-_VWPY2AA.d.mts} +3 -0
  64. package/dist/{useDataListRetriever-futhx3OP.d.mts → useDataListRetriever-BqJSFBck.d.mts} +1 -0
  65. package/dist/{useDataListRetriever-futhx3OP.d.ts → useDataListRetriever-BqJSFBck.d.ts} +1 -0
  66. package/dist/{useSocket-DUqGoPya.d.mts → useSocket-BILAdmZ0.d.mts} +1 -1
  67. package/dist/{useSocket-QuHa0ZmO.d.ts → useSocket-awibcC9B.d.ts} +1 -1
  68. package/package.json +1 -1
  69. package/scripts/generate-web-module/templates/pages/detail-page.template.ts +1 -1
  70. package/scripts/generate-web-module/templates/pages/list-page.template.ts +1 -1
  71. package/src/components/forms/DatePickerPopover.tsx +17 -15
  72. package/src/components/tables/ContentListTable.tsx +2 -2
  73. package/src/core/abstracts/AbstractService.ts +25 -0
  74. package/src/core/abstracts/ClientAbstractService.ts +10 -0
  75. package/src/features/billing/components/containers/BillingDashboardContainer.tsx +4 -1
  76. package/src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx +1 -1
  77. package/src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx +1 -1
  78. package/src/features/billing/stripe-price/components/forms/PriceEditor.tsx +85 -1
  79. package/src/features/billing/stripe-price/data/stripe-price.interface.ts +3 -0
  80. package/src/features/billing/stripe-price/data/stripe-price.ts +18 -0
  81. package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +5 -2
  82. package/src/features/billing/stripe-subscription/components/forms/CancelSubscriptionDialog.tsx +5 -18
  83. package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +1 -1
  84. package/src/features/billing/stripe-subscription/components/widgets/ProductPricingList.tsx +16 -12
  85. package/src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx +14 -3
  86. package/src/features/billing/stripe-subscription/components/wizards/WizardStepPlanSelection.tsx +14 -9
  87. package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +1 -1
  88. package/src/features/billing/stripe-subscription/data/stripe-subscription.service.ts +2 -2
  89. package/src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx +1 -1
  90. package/src/features/company/components/details/TokenStatusIndicator.tsx +4 -6
  91. package/src/features/company/hooks/useSubscriptionStatus.ts +18 -0
  92. package/src/features/content/hooks/useContentTableStructure.tsx +1 -1
  93. package/src/features/user/contexts/CurrentUserContext.tsx +2 -1
  94. package/src/features/user/hooks/useUserTableStructure.tsx +1 -1
  95. package/src/hooks/useDataListRetriever.ts +13 -0
  96. package/src/login/config.ts +6 -6
  97. package/src/shadcnui/ui/table.tsx +20 -49
  98. package/dist/chunk-HIF7DYR3.js.map +0 -1
  99. package/dist/chunk-KYG2PIRB.js.map +0 -1
  100. package/dist/chunk-NPNKFWV2.js.map +0 -1
  101. package/dist/chunk-SLANIL6B.mjs.map +0 -1
  102. package/dist/chunk-YCP2OMFD.mjs.map +0 -1
  103. /package/dist/{BlockNoteEditor-TFL6ZXIJ.mjs.map → BlockNoteEditor-UTZ7F23J.mjs.map} +0 -0
@@ -47,12 +47,13 @@ import {
47
47
  TabsList,
48
48
  TabsTrigger,
49
49
  useCurrentUserContext
50
- } from "../chunk-SLANIL6B.mjs";
50
+ } from "../chunk-53WT73E6.mjs";
51
51
  import {
52
52
  getRoleId,
53
53
  getStripePublishableKey
54
- } from "../chunk-IXVNXOZT.mjs";
54
+ } from "../chunk-XLMJPA4N.mjs";
55
55
  import {
56
+ FeatureService,
56
57
  StripeCustomerService,
57
58
  StripeInvoiceService,
58
59
  StripePriceService,
@@ -60,7 +61,7 @@ import {
60
61
  StripeSubscriptionService,
61
62
  StripeUsageService,
62
63
  cn
63
- } from "../chunk-YCP2OMFD.mjs";
64
+ } from "../chunk-HWQBSVBT.mjs";
64
65
  import "../chunk-AUXK7QSA.mjs";
65
66
  import "../chunk-C7C7VY4F.mjs";
66
67
  import "../chunk-U4MTVHOC.mjs";
@@ -981,7 +982,7 @@ function InvoiceDetails({ invoice, open, onOpenChange, onInvoiceChange }) {
981
982
  ] }),
982
983
  /* @__PURE__ */ jsxs10("div", { children: [
983
984
  /* @__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("div", { className: "border rounded-lg overflow-clip", children: /* @__PURE__ */ jsxs10("table", { className: "w-full", children: [
985
986
  /* @__PURE__ */ jsx12("thead", { className: "bg-muted", children: /* @__PURE__ */ jsxs10("tr", { children: [
986
987
  /* @__PURE__ */ jsx12("th", { className: "text-left p-3 text-sm font-medium", children: "Description" }),
987
988
  /* @__PURE__ */ jsx12("th", { className: "text-right p-3 text-sm font-medium", children: "Amount" })
@@ -1047,7 +1048,7 @@ function InvoicesList({ invoices, onInvoicesChange }) {
1047
1048
  return invoice.stripeInvoiceId.slice(-8);
1048
1049
  }, "getInvoiceNumber");
1049
1050
  return /* @__PURE__ */ jsxs11(Fragment2, { children: [
1050
- /* @__PURE__ */ jsx13("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs11(Table, { children: [
1051
+ /* @__PURE__ */ jsx13("div", { className: "border rounded-lg overflow-clip", children: /* @__PURE__ */ jsxs11(Table, { children: [
1051
1052
  /* @__PURE__ */ jsx13(TableHeader, { className: "bg-muted", children: /* @__PURE__ */ jsxs11(TableRow, { children: [
1052
1053
  /* @__PURE__ */ jsx13(TableHead, { children: "Invoice #" }),
1053
1054
  /* @__PURE__ */ jsx13(TableHead, { children: "Date" }),
@@ -1153,7 +1154,6 @@ import { useForm } from "react-hook-form";
1153
1154
  import { z } from "zod";
1154
1155
  import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1155
1156
  var formSchema = z.object({
1156
- cancelImmediately: z.boolean(),
1157
1157
  reason: z.string().optional()
1158
1158
  });
1159
1159
  function CancelSubscriptionDialog({
@@ -1166,17 +1166,15 @@ function CancelSubscriptionDialog({
1166
1166
  const form = useForm({
1167
1167
  resolver: zodResolver(formSchema),
1168
1168
  defaultValues: {
1169
- cancelImmediately: false,
1170
1169
  reason: ""
1171
1170
  }
1172
1171
  });
1173
- const cancelImmediately = form.watch("cancelImmediately");
1174
1172
  const onSubmit = /* @__PURE__ */ __name(async (values) => {
1175
1173
  setIsSubmitting(true);
1176
1174
  try {
1177
1175
  await StripeSubscriptionService.cancelSubscription({
1178
1176
  id: subscription.id,
1179
- cancelImmediately: values.cancelImmediately
1177
+ cancelImmediately: false
1180
1178
  });
1181
1179
  onSuccess();
1182
1180
  onOpenChange(false);
@@ -1193,8 +1191,7 @@ function CancelSubscriptionDialog({
1193
1191
  /* @__PURE__ */ jsx15(DialogDescription, { children: "Are you sure you want to cancel this subscription? This action cannot be undone." })
1194
1192
  ] }),
1195
1193
  /* @__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: [
1194
+ /* @__PURE__ */ jsxs13("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-3 text-sm text-blue-800", children: [
1198
1195
  "Your subscription will remain active until ",
1199
1196
  periodEndDate,
1200
1197
  ". You can continue using the service until then."
@@ -1423,7 +1420,7 @@ function SubscriptionsList({ subscriptions, onSubscriptionsChange, onChangePlan
1423
1420
  setSelectedSub(subscription);
1424
1421
  }, "handleRowClick");
1425
1422
  return /* @__PURE__ */ jsxs15(Fragment4, { children: [
1426
- /* @__PURE__ */ jsx18("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs15(Table, { children: [
1423
+ /* @__PURE__ */ jsx18("div", { className: "border rounded-lg overflow-clip", children: /* @__PURE__ */ jsxs15(Table, { children: [
1427
1424
  /* @__PURE__ */ jsx18(TableHeader, { className: "bg-muted", children: /* @__PURE__ */ jsxs15(TableRow, { children: [
1428
1425
  /* @__PURE__ */ jsx18(TableHead, { children: "Status" }),
1429
1426
  /* @__PURE__ */ jsx18(TableHead, { children: "Plan" }),
@@ -1472,7 +1469,7 @@ __name(SubscriptionsList, "SubscriptionsList");
1472
1469
 
1473
1470
  // src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx
1474
1471
  import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
1475
- function SubscriptionsContainer({ onOpenWizard }) {
1472
+ function SubscriptionsContainer({ onOpenWizard, hasActiveRecurringSubscription }) {
1476
1473
  const [subscriptions, setSubscriptions] = useState10([]);
1477
1474
  const [loading, setLoading] = useState10(true);
1478
1475
  const loadSubscriptions = useCallback(async () => {
@@ -1501,7 +1498,7 @@ function SubscriptionsContainer({ onOpenWizard }) {
1501
1498
  /* @__PURE__ */ jsx19(CreditCard3, { className: "h-8 w-8" }),
1502
1499
  /* @__PURE__ */ jsx19("h1", { className: "text-3xl font-bold", children: "Subscriptions" })
1503
1500
  ] }),
1504
- subscriptions.length > 0 && /* @__PURE__ */ jsx19(Button, { onClick: () => onOpenWizard?.(), children: "Subscribe to a Plan" })
1501
+ subscriptions.length > 0 && /* @__PURE__ */ jsx19(Button, { onClick: () => onOpenWizard?.(), children: hasActiveRecurringSubscription ? "Purchase Add-ons" : "Subscribe to a Plan" })
1505
1502
  ] }),
1506
1503
  criticalSubscriptions.map((subscription) => /* @__PURE__ */ jsx19(BillingAlertBanner, { subscription }, subscription.id)),
1507
1504
  subscriptions.length === 0 && /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-center justify-center py-12 space-y-4", children: [
@@ -1669,7 +1666,8 @@ function ProductPricingList({
1669
1666
  loadingPriceId,
1670
1667
  loading = false,
1671
1668
  onSelectPrice,
1672
- hideRecurringPrices = false
1669
+ hideRecurringPrices = false,
1670
+ hideOneTimePrices = false
1673
1671
  }) {
1674
1672
  if (loading) {
1675
1673
  return /* @__PURE__ */ jsx23(ProductPricingListSkeleton, {});
@@ -1684,13 +1682,16 @@ function ProductPricingList({
1684
1682
  if (!aRecurring && bRecurring) return 1;
1685
1683
  return 0;
1686
1684
  });
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) => {
1685
+ return /* @__PURE__ */ jsx23("div", { className: "space-y-6", children: sortedProducts.map((product) => {
1692
1686
  const allPrices = product.stripePrices || [];
1693
- const filteredPrices = getFilteredPrices(allPrices, selectedInterval);
1687
+ let pricesToFilter = allPrices;
1688
+ if (hideRecurringPrices) {
1689
+ pricesToFilter = pricesToFilter.filter((price) => price.priceType !== "recurring");
1690
+ }
1691
+ if (hideOneTimePrices) {
1692
+ pricesToFilter = pricesToFilter.filter((price) => price.priceType !== "one_time");
1693
+ }
1694
+ const filteredPrices = getFilteredPrices(pricesToFilter, selectedInterval);
1694
1695
  if (filteredPrices.length === 0) {
1695
1696
  return null;
1696
1697
  }
@@ -1994,6 +1995,7 @@ function WizardStepPlanSelection({
1994
1995
  selectedInterval,
1995
1996
  currentPriceId,
1996
1997
  hideRecurringPrices,
1998
+ hideOneTimePrices,
1997
1999
  onSelectPrice,
1998
2000
  onIntervalChange,
1999
2001
  onNext,
@@ -2033,7 +2035,7 @@ function WizardStepPlanSelection({
2033
2035
  onSelectPrice(price);
2034
2036
  }, "handleSelectPrice");
2035
2037
  return /* @__PURE__ */ jsxs24("div", { className: "space-y-6", children: [
2036
- /* @__PURE__ */ jsx27("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx27(
2038
+ !hideRecurringPrices && /* @__PURE__ */ jsx27("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx27(
2037
2039
  IntervalToggle,
2038
2040
  {
2039
2041
  value: selectedInterval,
@@ -2051,6 +2053,7 @@ function WizardStepPlanSelection({
2051
2053
  selectedPriceId: selectedPrice?.id,
2052
2054
  loading,
2053
2055
  hideRecurringPrices,
2056
+ hideOneTimePrices,
2054
2057
  onSelectPrice: handleSelectPrice
2055
2058
  }
2056
2059
  ),
@@ -2101,7 +2104,7 @@ function WizardStepReview({
2101
2104
  /* @__PURE__ */ jsx28("p", { className: "text-sm text-blue-700", children: "Your next charge will be adjusted to account for the plan change." }),
2102
2105
  /* @__PURE__ */ jsxs25("div", { className: "flex justify-between text-sm", children: [
2103
2106
  /* @__PURE__ */ jsx28("span", { className: "text-blue-600", children: "Amount due now:" }),
2104
- /* @__PURE__ */ jsx28("span", { className: "font-medium text-blue-800", children: formatCurrency(prorationPreview.amountDue, prorationPreview.currency) })
2107
+ /* @__PURE__ */ jsx28("span", { className: "font-medium text-blue-800", children: formatCurrency(prorationPreview.immediateCharge, prorationPreview.currency) })
2105
2108
  ] })
2106
2109
  ] }),
2107
2110
  /* @__PURE__ */ jsx28("div", { className: "border rounded-lg p-4", children: /* @__PURE__ */ jsxs25("div", { className: "flex justify-between items-center", children: [
@@ -2184,8 +2187,10 @@ function SubscriptionWizard({
2184
2187
  actions.reset();
2185
2188
  }
2186
2189
  }, [open, actions.reset]);
2187
- const dialogTitle = subscription ? "Change Subscription Plan" : "Subscribe to a Plan";
2188
- const dialogDescription = subscription ? "Select a new plan for your subscription" : "Choose a subscription plan to get started";
2190
+ const isPurchasingAddons = hasActiveRecurringSubscription && !subscription;
2191
+ const isChangePlanMode = !!subscription;
2192
+ const dialogTitle = subscription ? "Change Subscription Plan" : isPurchasingAddons ? "Purchase Add-ons" : "Subscribe to a Plan";
2193
+ const dialogDescription = subscription ? "Select a new plan for your subscription" : isPurchasingAddons ? "Select one-time products to purchase" : "Choose a subscription plan to get started";
2189
2194
  return /* @__PURE__ */ jsx30(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs27(DialogContent, { className: "max-w-2xl", children: [
2190
2195
  /* @__PURE__ */ jsxs27(DialogHeader, { children: [
2191
2196
  /* @__PURE__ */ jsx30(DialogTitle, { children: dialogTitle }),
@@ -2198,7 +2203,8 @@ function SubscriptionWizard({
2198
2203
  selectedPrice: state.selectedPrice,
2199
2204
  selectedInterval: state.selectedInterval,
2200
2205
  currentPriceId: subscription?.price?.id,
2201
- hideRecurringPrices: hasActiveRecurringSubscription && !subscription,
2206
+ hideRecurringPrices: isPurchasingAddons,
2207
+ hideOneTimePrices: isChangePlanMode,
2202
2208
  onSelectPrice: actions.selectPrice,
2203
2209
  onIntervalChange: actions.setInterval,
2204
2210
  onNext: actions.goToReview,
@@ -2401,7 +2407,7 @@ function UsageHistoryTable({ usageRecords }) {
2401
2407
  }
2402
2408
  return /* @__PURE__ */ jsxs30("div", { className: "flex w-full flex-col gap-y-4", children: [
2403
2409
  /* @__PURE__ */ jsx34("h2", { className: "text-xl font-semibold", children: "Usage History" }),
2404
- /* @__PURE__ */ jsx34("div", { className: "overflow-hidden rounded-lg border", children: /* @__PURE__ */ jsxs30(Table, { children: [
2410
+ /* @__PURE__ */ jsx34("div", { className: "overflow-clip rounded-lg border", children: /* @__PURE__ */ jsxs30(Table, { children: [
2405
2411
  /* @__PURE__ */ jsx34(TableHeader, { className: "bg-muted", children: /* @__PURE__ */ jsxs30(TableRow, { children: [
2406
2412
  /* @__PURE__ */ jsx34(TableHead, { children: "Date & Time" }),
2407
2413
  /* @__PURE__ */ jsx34(TableHead, { children: "Meter Event" }),
@@ -2777,7 +2783,13 @@ function BillingDashboardContainer() {
2777
2783
  open: activeModal === "subscriptions",
2778
2784
  onOpenChange: handleModalClose,
2779
2785
  title: getModalTitle("subscriptions"),
2780
- children: /* @__PURE__ */ jsx37(SubscriptionsContainer, { onOpenWizard: handleOpenWizard })
2786
+ children: /* @__PURE__ */ jsx37(
2787
+ SubscriptionsContainer,
2788
+ {
2789
+ onOpenWizard: handleOpenWizard,
2790
+ hasActiveRecurringSubscription
2791
+ }
2792
+ )
2781
2793
  }
2782
2794
  ),
2783
2795
  /* @__PURE__ */ jsx37(
@@ -2865,6 +2877,18 @@ import { z as z2 } from "zod";
2865
2877
  import { jsx as jsx39, jsxs as jsxs34 } from "react/jsx-runtime";
2866
2878
  function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
2867
2879
  const [isSubmitting, setIsSubmitting] = useState14(false);
2880
+ const [allFeatures, setAllFeatures] = useState14([]);
2881
+ useEffect10(() => {
2882
+ const fetchFeatures = /* @__PURE__ */ __name(async () => {
2883
+ try {
2884
+ const features = await FeatureService.findMany({});
2885
+ setAllFeatures(features);
2886
+ } catch (error) {
2887
+ console.error("[PriceEditor] Failed to fetch features:", error);
2888
+ }
2889
+ }, "fetchFeatures");
2890
+ fetchFeatures();
2891
+ }, []);
2868
2892
  const formSchema2 = z2.object({
2869
2893
  unitAmount: z2.preprocess(
2870
2894
  (val) => typeof val === "string" ? parseFloat(val) : val,
@@ -2881,10 +2905,13 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
2881
2905
  active: z2.boolean(),
2882
2906
  description: z2.string().optional(),
2883
2907
  features: z2.array(z2.string()),
2884
- token: z2.string()
2908
+ token: z2.string(),
2909
+ featureIds: z2.array(z2.string())
2885
2910
  });
2886
2911
  const isEditMode = !!price;
2887
2912
  const defaultUnitAmount = price?.unitAmount ? price.unitAmount / 100 : 0;
2913
+ const coreFeatureIds = allFeatures.filter((f) => f.isCore).map((f) => f.id);
2914
+ const defaultFeatureIds = [.../* @__PURE__ */ new Set([...price?.priceFeatures?.map((f) => f.id) ?? [], ...coreFeatureIds])];
2888
2915
  const form = useForm2({
2889
2916
  resolver: zodResolver2(formSchema2),
2890
2917
  defaultValues: {
@@ -2897,11 +2924,16 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
2897
2924
  active: price?.active ?? true,
2898
2925
  description: price?.description || "",
2899
2926
  features: price?.features || [],
2900
- token: price?.token?.toString() ?? ""
2927
+ token: price?.token?.toString() ?? "",
2928
+ featureIds: defaultFeatureIds
2901
2929
  }
2902
2930
  });
2903
2931
  useEffect10(() => {
2904
2932
  if (open) {
2933
+ const currentCoreFeatureIds = allFeatures.filter((f) => f.isCore).map((f) => f.id);
2934
+ const resetFeatureIds = [
2935
+ .../* @__PURE__ */ new Set([...price?.priceFeatures?.map((f) => f.id) ?? [], ...currentCoreFeatureIds])
2936
+ ];
2905
2937
  form.reset({
2906
2938
  unitAmount: price?.unitAmount ? price.unitAmount / 100 : 0,
2907
2939
  currency: price?.currency || "usd",
@@ -2912,10 +2944,11 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
2912
2944
  active: price?.active ?? true,
2913
2945
  description: price?.description || "",
2914
2946
  features: price?.features || [],
2915
- token: price?.token?.toString() ?? ""
2947
+ token: price?.token?.toString() ?? "",
2948
+ featureIds: resetFeatureIds
2916
2949
  });
2917
2950
  }
2918
- }, [open, price?.id]);
2951
+ }, [open, price?.id, allFeatures]);
2919
2952
  const watchInterval = form.watch("interval");
2920
2953
  const isRecurring = watchInterval !== "one_time";
2921
2954
  const onSubmit = /* @__PURE__ */ __name(async (values) => {
@@ -2928,7 +2961,9 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
2928
2961
  nickname: values.nickname || void 0,
2929
2962
  description: values.description || void 0,
2930
2963
  features: values.features.filter((f) => f.trim()) || void 0,
2931
- token: values.token ? parseInt(values.token, 10) : void 0
2964
+ token: values.token ? parseInt(values.token, 10) : void 0,
2965
+ // Only include featureIds for recurring prices (one-time prices don't support platform features)
2966
+ ...price?.priceType === "recurring" ? { featureIds: values.featureIds } : {}
2932
2967
  });
2933
2968
  } else {
2934
2969
  const createInput = {
@@ -2957,6 +2992,9 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
2957
2992
  if (values.token) {
2958
2993
  createInput.token = parseInt(values.token, 10);
2959
2994
  }
2995
+ if (isRecurring && values.featureIds.length > 0) {
2996
+ createInput.featureIds = values.featureIds;
2997
+ }
2960
2998
  await StripePriceService.createPrice(createInput);
2961
2999
  }
2962
3000
  onSuccess();
@@ -3111,6 +3149,49 @@ function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
3111
3149
  )
3112
3150
  ] })
3113
3151
  ] }),
3152
+ isRecurring && allFeatures.length > 0 && /* @__PURE__ */ jsxs34("div", { className: "space-y-2", children: [
3153
+ /* @__PURE__ */ jsx39(Label, { children: "Platform Features" }),
3154
+ /* @__PURE__ */ jsx39("div", { className: "border rounded-md p-4 space-y-2 max-h-48 overflow-y-auto", children: allFeatures.map((feature) => {
3155
+ const isCore = feature.isCore;
3156
+ const isChecked = form.watch("featureIds").includes(feature.id);
3157
+ return /* @__PURE__ */ jsxs34("div", { className: "flex items-center space-x-2", children: [
3158
+ /* @__PURE__ */ jsx39(
3159
+ "input",
3160
+ {
3161
+ type: "checkbox",
3162
+ id: `feature-${feature.id}`,
3163
+ checked: isChecked,
3164
+ disabled: isCore,
3165
+ onChange: (e) => {
3166
+ const currentIds = form.getValues("featureIds");
3167
+ if (e.target.checked) {
3168
+ form.setValue("featureIds", [...currentIds, feature.id]);
3169
+ } else {
3170
+ if (!isCore) {
3171
+ form.setValue(
3172
+ "featureIds",
3173
+ currentIds.filter((id) => id !== feature.id)
3174
+ );
3175
+ }
3176
+ }
3177
+ },
3178
+ className: "h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary disabled:opacity-50"
3179
+ }
3180
+ ),
3181
+ /* @__PURE__ */ jsxs34(
3182
+ "label",
3183
+ {
3184
+ htmlFor: `feature-${feature.id}`,
3185
+ className: `text-sm ${isCore ? "text-muted-foreground" : ""}`,
3186
+ children: [
3187
+ feature.name,
3188
+ isCore && /* @__PURE__ */ jsx39("span", { className: "ml-2 text-xs text-muted-foreground", children: "(Core - Required)" })
3189
+ ]
3190
+ }
3191
+ )
3192
+ ] }, feature.id);
3193
+ }) })
3194
+ ] }),
3114
3195
  /* @__PURE__ */ jsx39(FormCheckbox, { form, id: "active", name: "Active" }),
3115
3196
  /* @__PURE__ */ jsx39(CommonEditorButtons, { isEdit: isEditMode, form, disabled: isSubmitting, setOpen: onOpenChange })
3116
3197
  ] }) })