@fluid-app/portal-sdk 0.1.483 → 0.1.485

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.
@@ -2600,6 +2600,19 @@ function getMaxEditableBillDate(billingInterval = 1, billingIntervalUnit = "mont
2600
2600
  if (maxDate.getDate() !== day) maxDate.setDate(0);
2601
2601
  return maxDate;
2602
2602
  }
2603
+ /**
2604
+ * Latest date a whole bundle's bill date may be edited to. Every member moves
2605
+ * to one shared date, so the strictest member binds: a bundle holding one
2606
+ * monthly plan is capped at 12 months even if its other members are yearly.
2607
+ *
2608
+ * An empty list falls back to the 12-month cap, which is also what a member of
2609
+ * unknown cadence resolves to via getMaxEditableBillDate's own default.
2610
+ */
2611
+ function getMaxEditableBillDateForBundle(members, from = /* @__PURE__ */ new Date()) {
2612
+ const [first, ...rest] = members.map((member) => getMaxEditableBillDate(member.billingInterval, member.billingIntervalUnit, from));
2613
+ if (!first) return getMaxEditableBillDate(1, "month", from);
2614
+ return rest.reduce((strictest, current) => current < strictest ? current : strictest, first);
2615
+ }
2603
2616
  function calculateNextBillDate(billingInterval = 1, billingIntervalUnit = "month") {
2604
2617
  const now = /* @__PURE__ */ new Date();
2605
2618
  const nextBillDate = new Date(now);
@@ -2620,6 +2633,36 @@ function calculateNextBillDate(billingInterval = 1, billingIntervalUnit = "month
2620
2633
  }
2621
2634
  return nextBillDate.toISOString().split("T")[0] || "";
2622
2635
  }
2636
+ /** Longest pause the dialogs have ever offered, before the cap narrows it. */
2637
+ const MAX_SKIPPABLE_ORDERS = 99;
2638
+ /**
2639
+ * How many upcoming orders a pause may skip: the largest count whose resume
2640
+ * date still lands inside the bill-date cap.
2641
+ *
2642
+ * Pausing for a number of orders sets a bill date like any other edit, so the
2643
+ * API caps the date it derives from that count, 2 years out for a yearly cadence
2644
+ * and 12 months for everything else, and rejects the request naming
2645
+ * number_of_orders. Offering counts past that offers a pause the API refuses.
2646
+ *
2647
+ * Returns 0 when even a single skip overshoots, which happens on a subscription
2648
+ * whose next bill date already sits near the cap. Callers should read that as
2649
+ * "pausing by order count is unavailable here" and leave the indefinite pause.
2650
+ *
2651
+ * The two ends deliberately measure from different places: the resume date
2652
+ * counts forward from the current bill date, the cap counts forward from today.
2653
+ * The API stays the backstop, so the exact boundary count depends on its
2654
+ * resume-date arithmetic agreeing with calculateResumeDate.
2655
+ */
2656
+ function getMaxSkippableOrders(billingInterval = 1, billingIntervalUnit = "month", currentNextBillDate, from = /* @__PURE__ */ new Date()) {
2657
+ const cap = getMaxEditableBillDate(billingInterval, billingIntervalUnit, from);
2658
+ let allowed = 0;
2659
+ for (let count = 1; count <= MAX_SKIPPABLE_ORDERS; count += 1) {
2660
+ const resume = calculateResumeDate(billingInterval, billingIntervalUnit, count, currentNextBillDate ?? from);
2661
+ if (new Date(resume.getUTCFullYear(), resume.getUTCMonth(), resume.getUTCDate()) > cap) break;
2662
+ allowed = count;
2663
+ }
2664
+ return allowed;
2665
+ }
2623
2666
  function formatDate(dateString, locale = "en-US") {
2624
2667
  if (!dateString) return "";
2625
2668
  const parts = (dateString.split("T")[0] ?? dateString).split("-");
@@ -2680,22 +2723,30 @@ function formatTime(dateString, timezone, locale = "en-US") {
2680
2723
  return "";
2681
2724
  }
2682
2725
  }
2683
- function getNextBillDisplay(subscription, labels = {
2726
+ function getNextBillDisplay(nextBillDate, displayStatus, labels = {
2684
2727
  cancelled: "Cancelled",
2685
2728
  completed: "Completed",
2686
2729
  paused: "Paused",
2687
2730
  notAvailable: "N/A"
2688
2731
  }, locale = "en-US") {
2689
- if (subscription?.billing_cycle_limit_reached) return labels.completed;
2690
- if (subscription?.status === "cancelled") return labels.cancelled;
2691
- if (subscription?.next_bill_date) return formatDate(subscription.next_bill_date, locale);
2692
- if (subscription?.status === "paused") return labels.paused;
2732
+ if (displayStatus === "completed") return labels.completed;
2733
+ if (displayStatus === "cancelled") return labels.cancelled;
2734
+ if (nextBillDate) return formatDate(nextBillDate, locale);
2735
+ if (displayStatus === "paused") return labels.paused;
2693
2736
  return labels.notAvailable;
2694
2737
  }
2695
2738
  //#endregion
2696
2739
  //#region ../../subscriptions/core/src/utils/subscription-actions.ts
2740
+ /**
2741
+ * The one rule for a completed subscription: its current plan's
2742
+ * billing-cycle limit is reached. Every "completed" presentation and
2743
+ * gate reads this, so a new terminal condition lands here once.
2744
+ */
2745
+ function isSubscriptionCompleted(subscription) {
2746
+ return subscription?.billing_cycle_limit_reached === true;
2747
+ }
2697
2748
  function getSubscriptionDisplayStatus(subscription) {
2698
- return subscription.billingCycleLimitReached === true ? "completed" : subscription.status;
2749
+ return isSubscriptionCompleted(subscription) ? "completed" : subscription.status;
2699
2750
  }
2700
2751
  function hasPendingSubscriptionSkip(skips, today = /* @__PURE__ */ new Date()) {
2701
2752
  if (!skips?.length) return false;
@@ -2707,13 +2758,11 @@ function hasPendingSubscriptionSkip(skips, today = /* @__PURE__ */ new Date()) {
2707
2758
  });
2708
2759
  }
2709
2760
  function getSubscriptionActionPolicy(subscription) {
2710
- if (subscription.billingCycleLimitReached === true) return {
2711
- primary: null,
2712
- order: [],
2713
- menu: [],
2714
- recovery: null
2715
- };
2716
- if (subscription.status === "active") {
2761
+ const status = getSubscriptionDisplayStatus({
2762
+ status: subscription.status,
2763
+ billing_cycle_limit_reached: subscription.billingCycleLimitReached
2764
+ });
2765
+ if (status === "active") {
2717
2766
  const skipAllowanceAvailable = subscription.maxSkips == null || (subscription.skippedCount ?? 0) < subscription.maxSkips;
2718
2767
  const canSkip = subscription.allowSkipping === true && subscription.nextBillDate != null && skipAllowanceAvailable;
2719
2768
  const order = [];
@@ -2728,19 +2777,19 @@ function getSubscriptionActionPolicy(subscription) {
2728
2777
  recovery: null
2729
2778
  };
2730
2779
  }
2731
- if (subscription.status === "paused") return {
2780
+ if (status === "paused") return {
2732
2781
  primary: "resume_subscription",
2733
2782
  order: [],
2734
2783
  menu: ["cancel_subscription"],
2735
2784
  recovery: null
2736
2785
  };
2737
- if (subscription.status === "cancelled") return {
2786
+ if (status === "cancelled") return {
2738
2787
  primary: "reactivate_subscription",
2739
2788
  order: [],
2740
2789
  menu: [],
2741
2790
  recovery: null
2742
2791
  };
2743
- if (subscription.status === "past_due") {
2792
+ if (status === "past_due") {
2744
2793
  const isExhausted = subscription.nextRetryAt == null;
2745
2794
  const secondary = subscription.canChangePaymentMethod ? "change_payment_method" : null;
2746
2795
  return {
@@ -2756,13 +2805,13 @@ function getSubscriptionActionPolicy(subscription) {
2756
2805
  }
2757
2806
  };
2758
2807
  }
2759
- if (subscription.status === "completed") return {
2808
+ if (status === "completed") return {
2760
2809
  primary: null,
2761
2810
  order: [],
2762
2811
  menu: [],
2763
2812
  recovery: null
2764
2813
  };
2765
- if (["pending", "trial"].includes(subscription.status)) return {
2814
+ if (["pending", "trial"].includes(status)) return {
2766
2815
  primary: null,
2767
2816
  order: [],
2768
2817
  menu: ["cancel_subscription"],
@@ -4188,6 +4237,30 @@ function isPaymentMethodUsable(pm) {
4188
4237
  return Boolean(addr.address1 && addr.city && addr.state && addr.zip && addr.country_code);
4189
4238
  }
4190
4239
  //#endregion
4240
+ //#region src/screens/subscriptions/bundle-drawer/bill-date-bounds.ts
4241
+ function getDaysInMonth(year, month) {
4242
+ return new Date(year, month + 1, 0).getDate();
4243
+ }
4244
+ function isYearOutOfRange(year, { today, maxDate }) {
4245
+ return year < today.getFullYear() || year > maxDate.getFullYear();
4246
+ }
4247
+ function isMonthOutOfRange(year, month, bounds) {
4248
+ const { today, maxDate } = bounds;
4249
+ if (isYearOutOfRange(year, bounds)) return true;
4250
+ if (year === maxDate.getFullYear() && month > maxDate.getMonth()) return true;
4251
+ if (year > today.getFullYear()) return false;
4252
+ return month < today.getMonth();
4253
+ }
4254
+ function isDayOutOfRange(year, month, day, bounds) {
4255
+ const { today, maxDate } = bounds;
4256
+ if (day > getDaysInMonth(year, month)) return true;
4257
+ if (isMonthOutOfRange(year, month, bounds)) return true;
4258
+ if (year === maxDate.getFullYear() && month === maxDate.getMonth() && day > maxDate.getDate()) return true;
4259
+ if (year > today.getFullYear()) return false;
4260
+ if (month > today.getMonth()) return false;
4261
+ return day < today.getDate();
4262
+ }
4263
+ //#endregion
4191
4264
  //#region src/screens/subscriptions/bundle-drawer/use-bundle-form.ts
4192
4265
  function pickDefaultAddressId(addresses, defaultAddressId) {
4193
4266
  if (defaultAddressId != null) {
@@ -4208,11 +4281,11 @@ function pickDefaultPaymentMethodId(paymentMethods, defaultPaymentMethodId) {
4208
4281
  if (flagged) return flagged.id;
4209
4282
  return usable[0]?.id ?? null;
4210
4283
  }
4211
- function useBundleForm({ addresses, paymentMethods, defaultAddressId, defaultPaymentMethodId }) {
4284
+ function useBundleForm({ subscriptions: subscriptionList, addresses, paymentMethods, defaultAddressId, defaultPaymentMethodId }) {
4212
4285
  const [step, setStep] = (0, react.useState)(1);
4213
4286
  const [selectedTokens, setSelectedTokens] = (0, react.useState)(() => /* @__PURE__ */ new Set());
4214
4287
  const today = (0, react.useMemo)(() => /* @__PURE__ */ new Date(), []);
4215
- const [selectedYear, setYearState] = (0, react.useState)(today.getUTCFullYear());
4288
+ const [selectedYear, setYearState] = (0, react.useState)(today.getFullYear());
4216
4289
  const [selectedMonth, setMonthState] = (0, react.useState)(null);
4217
4290
  const [selectedDay, setDayState] = (0, react.useState)(null);
4218
4291
  const [selectedAddressId, setSelectedAddressId] = (0, react.useState)(() => pickDefaultAddressId(addresses, defaultAddressId));
@@ -4231,55 +4304,73 @@ function useBundleForm({ addresses, paymentMethods, defaultAddressId, defaultPay
4231
4304
  paymentMethods,
4232
4305
  defaultPaymentMethodId
4233
4306
  ]);
4307
+ const toggleToken = (0, react.useCallback)((token) => {
4308
+ setSelectedTokens((prev) => {
4309
+ const next = new Set(prev);
4310
+ if (next.has(token)) next.delete(token);
4311
+ else next.add(token);
4312
+ return next;
4313
+ });
4314
+ }, []);
4315
+ const bounds = (0, react.useMemo)(() => {
4316
+ return {
4317
+ today,
4318
+ maxDate: getMaxEditableBillDateForBundle(Array.from(selectedTokens).map((token) => {
4319
+ const plan = subscriptionList.find((sub) => sub.subscription_token === token)?.subscription_plan;
4320
+ return {
4321
+ billingInterval: plan?.billing_interval,
4322
+ billingIntervalUnit: plan?.billing_interval_unit
4323
+ };
4324
+ }), today)
4325
+ };
4326
+ }, [
4327
+ selectedTokens,
4328
+ subscriptionList,
4329
+ today
4330
+ ]);
4331
+ const setYear = (0, react.useCallback)((y) => {
4332
+ setYearState(y);
4333
+ if (selectedMonth == null) return;
4334
+ if (isMonthOutOfRange(y, selectedMonth, bounds)) {
4335
+ setMonthState(null);
4336
+ setDayState(null);
4337
+ return;
4338
+ }
4339
+ if (selectedDay != null && isDayOutOfRange(y, selectedMonth, selectedDay, bounds)) setDayState(null);
4340
+ }, [
4341
+ selectedMonth,
4342
+ selectedDay,
4343
+ bounds
4344
+ ]);
4345
+ const setMonth = (0, react.useCallback)((m) => {
4346
+ setMonthState(m);
4347
+ if (selectedDay != null && isDayOutOfRange(selectedYear, m, selectedDay, bounds)) setDayState(null);
4348
+ }, [
4349
+ selectedDay,
4350
+ selectedYear,
4351
+ bounds
4352
+ ]);
4353
+ const setDay = (0, react.useCallback)((d) => setDayState(d), []);
4354
+ const canProceedFromStep1 = selectedTokens.size >= 2;
4355
+ const selectedDateOutOfRange = selectedMonth !== null && selectedDay !== null && isDayOutOfRange(selectedYear, selectedMonth, selectedDay, bounds);
4234
4356
  return {
4235
4357
  step,
4236
4358
  setStep,
4237
4359
  selectedTokens,
4238
- toggleToken: (0, react.useCallback)((token) => {
4239
- setSelectedTokens((prev) => {
4240
- const next = new Set(prev);
4241
- if (next.has(token)) next.delete(token);
4242
- else next.add(token);
4243
- return next;
4244
- });
4245
- }, []),
4360
+ toggleToken,
4246
4361
  selectedYear,
4247
4362
  selectedMonth,
4248
4363
  selectedDay,
4249
- setYear: (0, react.useCallback)((y) => {
4250
- setYearState(y);
4251
- if (selectedMonth != null) {
4252
- const isPastMonth = y < today.getUTCFullYear() || y === today.getUTCFullYear() && selectedMonth < today.getUTCMonth();
4253
- const isPastDay = y === today.getUTCFullYear() && selectedMonth === today.getUTCMonth() && selectedDay != null && selectedDay < today.getUTCDate();
4254
- if (isPastMonth) {
4255
- setMonthState(null);
4256
- setDayState(null);
4257
- } else if (isPastDay) setDayState(null);
4258
- }
4259
- }, [
4260
- selectedMonth,
4261
- selectedDay,
4262
- today
4263
- ]),
4264
- setMonth: (0, react.useCallback)((m) => {
4265
- setMonthState(m);
4266
- if (selectedDay != null) {
4267
- const daysInNewMonth = new Date(Date.UTC(selectedYear, m + 1, 0)).getUTCDate();
4268
- const isPastDay = selectedYear === today.getUTCFullYear() && m === today.getUTCMonth() && selectedDay < today.getUTCDate();
4269
- if (selectedDay > daysInNewMonth || isPastDay) setDayState(null);
4270
- }
4271
- }, [
4272
- selectedDay,
4273
- selectedYear,
4274
- today
4275
- ]),
4276
- setDay: (0, react.useCallback)((d) => setDayState(d), []),
4364
+ setYear,
4365
+ setMonth,
4366
+ setDay,
4277
4367
  selectedAddressId,
4278
4368
  setSelectedAddressId,
4279
4369
  selectedPaymentMethodId,
4280
4370
  setSelectedPaymentMethodId,
4281
- canProceedFromStep1: selectedTokens.size >= 2,
4282
- canProceedFromStep2: selectedMonth !== null && selectedDay !== null,
4371
+ bounds,
4372
+ canProceedFromStep1,
4373
+ canProceedFromStep2: selectedMonth !== null && selectedDay !== null && !selectedDateOutOfRange,
4283
4374
  canProceedFromStep3: selectedAddressId != null,
4284
4375
  canProceedFromStep4: selectedPaymentMethodId != null,
4285
4376
  formattedDate: (0, react.useMemo)(() => {
@@ -4508,34 +4599,20 @@ const MONTHS = [
4508
4599
  "Nov",
4509
4600
  "Dec"
4510
4601
  ];
4511
- function getDaysInMonth(year, month) {
4512
- return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
4513
- }
4514
- function DrawerStepDate({ selectedYear, selectedMonth, selectedDay, onYearChange, onMonthChange, onDayChange }) {
4602
+ function DrawerStepDate({ selectedYear, selectedMonth, selectedDay, onYearChange, onMonthChange, onDayChange, bounds }) {
4515
4603
  const { t } = useSubscriptionsTranslation();
4516
- const today = (0, react.useMemo)(() => /* @__PURE__ */ new Date(), []);
4517
- const currentYear = today.getUTCFullYear();
4518
- const currentMonth = today.getUTCMonth();
4519
- const currentDay = today.getUTCDate();
4604
+ const currentYear = bounds.today.getFullYear();
4520
4605
  const availableYears = (0, react.useMemo)(() => [
4521
4606
  currentYear,
4522
4607
  currentYear + 1,
4523
4608
  currentYear + 2
4524
4609
  ], [currentYear]);
4525
- const isMonthDisabled = (monthIndex) => {
4526
- if (selectedYear < currentYear) return true;
4527
- if (selectedYear > currentYear) return false;
4528
- return monthIndex < currentMonth;
4529
- };
4610
+ const isYearDisabled = (year) => isYearOutOfRange(year, bounds);
4611
+ const isMonthDisabled = (monthIndex) => isMonthOutOfRange(selectedYear, monthIndex, bounds);
4530
4612
  const daysInSelectedMonth = selectedMonth == null ? 31 : getDaysInMonth(selectedYear, selectedMonth);
4531
4613
  const isDayDisabled = (day) => {
4532
4614
  if (selectedMonth == null) return true;
4533
- if (day > getDaysInMonth(selectedYear, selectedMonth)) return true;
4534
- if (selectedYear < currentYear) return true;
4535
- if (selectedYear > currentYear) return false;
4536
- if (selectedMonth > currentMonth) return false;
4537
- if (selectedMonth < currentMonth) return true;
4538
- return day < currentDay;
4615
+ return isDayOutOfRange(selectedYear, selectedMonth, day, bounds);
4539
4616
  };
4540
4617
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4541
4618
  className: "flex-1 overflow-y-auto px-6 py-4",
@@ -4558,10 +4635,13 @@ function DrawerStepDate({ selectedYear, selectedMonth, selectedDay, onYearChange
4558
4635
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4559
4636
  className: "grid grid-cols-3 gap-2",
4560
4637
  children: availableYears.map((year) => {
4638
+ const isActive = selectedYear === year;
4639
+ const disabled = isYearDisabled(year);
4561
4640
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4562
4641
  type: "button",
4642
+ disabled,
4563
4643
  onClick: () => onYearChange(year),
4564
- className: `rounded-lg px-3 py-2.5 text-xs font-semibold transition-all ${selectedYear === year ? "bg-primary text-primary-foreground" : "text-foreground ring-border hover:ring-foreground/20 bg-transparent ring-1"}`,
4644
+ className: `rounded-lg px-3 py-2.5 text-xs font-semibold transition-all ${isActive ? "bg-primary text-primary-foreground" : "text-foreground ring-border hover:ring-foreground/20 bg-transparent ring-1"} ${disabled ? "cursor-not-allowed opacity-40" : ""}`,
4565
4645
  children: year
4566
4646
  }, year);
4567
4647
  })
@@ -4805,6 +4885,7 @@ function BundleSubscriptionsDrawer({ open, onOpenChange, subscriptions: subscrip
4805
4885
  const { t } = useSubscriptionsTranslation();
4806
4886
  const { isMobile } = require_sidebar.useSidebar();
4807
4887
  const form = useBundleForm({
4888
+ subscriptions: subscriptionList,
4808
4889
  addresses,
4809
4890
  paymentMethods,
4810
4891
  defaultAddressId,
@@ -4849,7 +4930,8 @@ function BundleSubscriptionsDrawer({ open, onOpenChange, subscriptions: subscrip
4849
4930
  selectedDay: form.selectedDay,
4850
4931
  onYearChange: form.setYear,
4851
4932
  onMonthChange: form.setMonth,
4852
- onDayChange: form.setDay
4933
+ onDayChange: form.setDay,
4934
+ bounds: form.bounds
4853
4935
  }),
4854
4936
  form.step === 3 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DrawerStepAddress, {
4855
4937
  addresses,
@@ -5410,7 +5492,8 @@ function PauseSubscriptionDialog({ open, onOpenChange, onConfirm, isLoading, err
5410
5492
  const { t, locale } = useSubscriptionsTranslation();
5411
5493
  const [pauseType, setPauseType] = (0, react.useState)("indefinite");
5412
5494
  const [selectedOrderCount, setSelectedOrderCount] = (0, react.useState)(null);
5413
- const orderCountOptions = Array.from({ length: 99 }, (_, i) => i + 1);
5495
+ const maxSkippableOrders = getMaxSkippableOrders(billingInterval, billingIntervalUnit, currentNextBillDate);
5496
+ const orderCountOptions = Array.from({ length: maxSkippableOrders }, (_, i) => i + 1);
5414
5497
  const calculatedResumeDate = selectedOrderCount && currentNextBillDate ? calculateResumeDate(billingInterval, billingIntervalUnit, selectedOrderCount, currentNextBillDate) : null;
5415
5498
  (0, react.useEffect)(() => {
5416
5499
  if (!open) return;
@@ -5490,10 +5573,11 @@ function PauseSubscriptionDialog({ open, onOpenChange, onConfirm, isLoading, err
5490
5573
  type: "radio",
5491
5574
  checked: pauseType === "order_count",
5492
5575
  onChange: () => setPauseType("order_count"),
5493
- className: "border-border accent-primary size-4 focus:ring-0"
5576
+ disabled: maxSkippableOrders === 0,
5577
+ className: "border-border accent-primary size-4 focus:ring-0 disabled:opacity-40"
5494
5578
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Label, {
5495
5579
  htmlFor: "pause-order-count",
5496
- className: "text-foreground cursor-pointer text-sm font-medium",
5580
+ className: `text-sm font-medium ${maxSkippableOrders === 0 ? "text-muted-foreground cursor-not-allowed" : "text-foreground cursor-pointer"}`,
5497
5581
  children: t("pause_for_orders")
5498
5582
  })]
5499
5583
  }), pauseType === "order_count" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -6487,12 +6571,14 @@ function SubscriptionItemsSection({ subscription, displayQuantity, displayNextBi
6487
6571
  const variant = subscription.variant;
6488
6572
  const product = variant?.product;
6489
6573
  const quantity = displayQuantity ?? subscription.quantity;
6574
+ const displayStatus = getSubscriptionDisplayStatus(subscription);
6575
+ const paymentFailed = displayStatus === "past_due";
6490
6576
  const quantityInputId = `subscription-${subscription.subscription_token}-quantity`;
6491
6577
  const pricing = computeSubscriptionPricing(subscription);
6492
6578
  const subtotal = formatCurrency(pricing.subtotalUnit * quantity);
6493
6579
  const totalPrice = formatCurrency(pricing.totalUnit * quantity);
6494
6580
  const effectiveNextBillDate = displayNextBillDate ?? subscription.next_bill_date;
6495
- const nextOrderTimeOfDay = subscription.billing_cycle_limit_reached ? "" : formatTime(effectiveNextBillDate, subscription.timezone, locale);
6581
+ const nextOrderTimeOfDay = displayStatus === "completed" ? "" : formatTime(effectiveNextBillDate, subscription.timezone, locale);
6496
6582
  const nextBillDisplayLabels = {
6497
6583
  cancelled: t("date_cancelled"),
6498
6584
  completed: t("list_status_completed"),
@@ -6516,14 +6602,11 @@ function SubscriptionItemsSection({ subscription, displayQuantity, displayNextBi
6516
6602
  children: [
6517
6603
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6518
6604
  className: "text-muted-foreground mb-1 text-sm",
6519
- children: subscription.status === "past_due" && !subscription.billing_cycle_limit_reached ? t("payment_failed_on") : t("next_order_date")
6605
+ children: paymentFailed ? t("payment_failed_on") : t("next_order_date")
6520
6606
  }),
6521
6607
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6522
6608
  className: "text-foreground text-2xl font-bold",
6523
- children: subscription.status === "past_due" && !subscription.billing_cycle_limit_reached ? subscription.last_failed_at ? formatOptionalDate(subscription.last_failed_at, locale) : "—" : getNextBillDisplay(displayNextBillDate ? {
6524
- ...subscription,
6525
- next_bill_date: displayNextBillDate
6526
- } : subscription, nextBillDisplayLabels, locale)
6609
+ children: paymentFailed ? subscription.last_failed_at ? formatOptionalDate(subscription.last_failed_at, locale) : "—" : getNextBillDisplay(effectiveNextBillDate, displayStatus, nextBillDisplayLabels, locale)
6527
6610
  }),
6528
6611
  nextOrderTimeOfDay && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6529
6612
  className: "text-muted-foreground mt-1 text-xs",
@@ -6654,10 +6737,7 @@ function SubscriptionManagementSection({ subscription, actionPolicy, isMutating,
6654
6737
  const plan = subscription.subscription_plan;
6655
6738
  const quantity = subscription.quantity;
6656
6739
  const totalPrice = formatCurrency(subscription.price * quantity);
6657
- const displayStatus = getSubscriptionDisplayStatus({
6658
- status: subscription.status,
6659
- billingCycleLimitReached: subscription.billing_cycle_limit_reached
6660
- });
6740
+ const displayStatus = getSubscriptionDisplayStatus(subscription);
6661
6741
  const cadenceKey = getCadenceKey(plan.billing_interval_unit);
6662
6742
  const interval = plan.billing_interval;
6663
6743
  const unitKeys = UNIT_KEYS[plan.billing_interval_unit.toLowerCase()];
@@ -6852,7 +6932,7 @@ function SubscriptionManagementSection({ subscription, actionPolicy, isMutating,
6852
6932
  subscriptionToken: subscription.subscription_token,
6853
6933
  customerId: subscription.customer?.id ?? 0,
6854
6934
  countryCode: subscription.address?.country_code ?? "US",
6855
- disabled: subscription.billing_cycle_limit_reached
6935
+ disabled: isSubscriptionCompleted(subscription)
6856
6936
  }) : subscription.address && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6857
6937
  className: "border-border mb-6 border-b pb-4",
6858
6938
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -6989,7 +7069,7 @@ function SubscriptionDetail({ token, onNotFound, onError, onSuccess, onMutationE
6989
7069
  const { data, isLoading, error } = useSubscription(token);
6990
7070
  const subscription = data?.subscription;
6991
7071
  const customerId = subscription?.customer?.id ?? 0;
6992
- const waiverPreviewEnabled = subscription?.status === "past_due" && subscription.billing_cycle_limit_reached !== true && subscription.next_retry_at == null;
7072
+ const waiverPreviewEnabled = subscription?.status === "past_due" && !isSubscriptionCompleted(subscription) && subscription.next_retry_at == null;
6993
7073
  const waiverPreviewQuery = useFailedCycleWaiverPreview(token, { enabled: waiverPreviewEnabled });
6994
7074
  const isWaiverPreviewLoading = waiverPreviewEnabled && waiverPreviewQuery.isFetching;
6995
7075
  const waiverPreview = waiverPreviewQuery.data?.failed_cycle_waiver;
@@ -7149,7 +7229,8 @@ function SubscriptionDetail({ token, onNotFound, onError, onSuccess, onMutationE
7149
7229
  if (isLoading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubscriptionDetailSkeleton, {});
7150
7230
  if (!subscription) return null;
7151
7231
  const plan = subscription.subscription_plan;
7152
- const isActive = subscription.status === "active" && !subscription.billing_cycle_limit_reached;
7232
+ const displayStatus = getSubscriptionDisplayStatus(subscription);
7233
+ const isActive = displayStatus === "active";
7153
7234
  const actionPolicy = getSubscriptionActionPolicy({
7154
7235
  status: subscription.status,
7155
7236
  billingCycleLimitReached: subscription.billing_cycle_limit_reached,
@@ -7168,7 +7249,7 @@ function SubscriptionDetail({ token, onNotFound, onError, onSuccess, onMutationE
7168
7249
  const childPaymentRecoveryDisabled = childPaymentRecoveryToken === token;
7169
7250
  const waiverMutationPending = waiverMutation.isPending && waiverMutation.variables?.subscriptionToken === token;
7170
7251
  const aggregateRecoveryDisabled = directParentRecoveryDisabled || childPaymentRecoveryDisabled || waiverMutationPending;
7171
- const paymentMethodDisabled = subscription.billing_cycle_limit_reached || directParentRecoveryDisabled || waiverMutationPending || independentParentMutationDisabled;
7252
+ const paymentMethodDisabled = isSubscriptionCompleted(subscription) || directParentRecoveryDisabled || waiverMutationPending || independentParentMutationDisabled;
7172
7253
  const isMutating = independentParentMutationDisabled || aggregateRecoveryDisabled;
7173
7254
  const handlePauseConfirm = (selection) => {
7174
7255
  const pauseParams = { customerId };
@@ -7225,7 +7306,7 @@ function SubscriptionDetail({ token, onNotFound, onError, onSuccess, onMutationE
7225
7306
  }, { onSuccess: () => setShowReactivateModal(false) });
7226
7307
  };
7227
7308
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
7228
- subscription.status === "past_due" && !subscription.billing_cycle_limit_reached && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7309
+ displayStatus === "past_due" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7229
7310
  className: "mb-4 px-2 sm:px-0 lg:mb-6",
7230
7311
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PastDueBanner, {
7231
7312
  productTitle: subscription.variant?.product?.title ?? null,
@@ -7545,7 +7626,7 @@ async function invalidateSubscriptionPaymentRecoveryCache(queryClient, subscript
7545
7626
  }
7546
7627
  async function completeSubscriptionPaymentUpdate(args) {
7547
7628
  const cached = args.queryClient.getQueryData(subscriptionsKeys.detail(args.updatedSubscriptionToken));
7548
- if (cached?.subscription.status === "past_due" && cached.subscription.billing_cycle_limit_reached !== true) {
7629
+ if (cached?.subscription.status === "past_due" && !isSubscriptionCompleted(cached.subscription)) {
7549
7630
  args.queueRetry(args.updatedSubscriptionToken);
7550
7631
  return;
7551
7632
  }
@@ -8336,7 +8417,6 @@ function PortalSubscriptionShippingAddressSectionInner({ addressId, address: cur
8336
8417
  addressList: addresses,
8337
8418
  selectedAddressId: addressId ?? 0,
8338
8419
  onSelectAddress: (addr) => {
8339
- if (disabled) return;
8340
8420
  if (addr.id === addressId) return;
8341
8421
  updateSubscription.mutate({
8342
8422
  subscriptionToken,
@@ -8344,7 +8424,6 @@ function PortalSubscriptionShippingAddressSectionInner({ addressId, address: cur
8344
8424
  });
8345
8425
  },
8346
8426
  onAddAddressClick: () => {
8347
- if (disabled) return;
8348
8427
  setEditingAddress(null);
8349
8428
  setIsFormDialogOpen(true);
8350
8429
  },
@@ -8614,4 +8693,4 @@ Object.defineProperty(exports, "subscriptionsScreenPropertySchema", {
8614
8693
  }
8615
8694
  });
8616
8695
 
8617
- //# sourceMappingURL=SubscriptionsScreen-DoyP5YBP.cjs.map
8696
+ //# sourceMappingURL=SubscriptionsScreen-9NJY-K63.cjs.map