@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.
@@ -2538,6 +2538,19 @@ function getMaxEditableBillDate(billingInterval = 1, billingIntervalUnit = "mont
2538
2538
  if (maxDate.getDate() !== day) maxDate.setDate(0);
2539
2539
  return maxDate;
2540
2540
  }
2541
+ /**
2542
+ * Latest date a whole bundle's bill date may be edited to. Every member moves
2543
+ * to one shared date, so the strictest member binds: a bundle holding one
2544
+ * monthly plan is capped at 12 months even if its other members are yearly.
2545
+ *
2546
+ * An empty list falls back to the 12-month cap, which is also what a member of
2547
+ * unknown cadence resolves to via getMaxEditableBillDate's own default.
2548
+ */
2549
+ function getMaxEditableBillDateForBundle(members, from = /* @__PURE__ */ new Date()) {
2550
+ const [first, ...rest] = members.map((member) => getMaxEditableBillDate(member.billingInterval, member.billingIntervalUnit, from));
2551
+ if (!first) return getMaxEditableBillDate(1, "month", from);
2552
+ return rest.reduce((strictest, current) => current < strictest ? current : strictest, first);
2553
+ }
2541
2554
  function calculateNextBillDate(billingInterval = 1, billingIntervalUnit = "month") {
2542
2555
  const now = /* @__PURE__ */ new Date();
2543
2556
  const nextBillDate = new Date(now);
@@ -2558,6 +2571,36 @@ function calculateNextBillDate(billingInterval = 1, billingIntervalUnit = "month
2558
2571
  }
2559
2572
  return nextBillDate.toISOString().split("T")[0] || "";
2560
2573
  }
2574
+ /** Longest pause the dialogs have ever offered, before the cap narrows it. */
2575
+ const MAX_SKIPPABLE_ORDERS = 99;
2576
+ /**
2577
+ * How many upcoming orders a pause may skip: the largest count whose resume
2578
+ * date still lands inside the bill-date cap.
2579
+ *
2580
+ * Pausing for a number of orders sets a bill date like any other edit, so the
2581
+ * API caps the date it derives from that count, 2 years out for a yearly cadence
2582
+ * and 12 months for everything else, and rejects the request naming
2583
+ * number_of_orders. Offering counts past that offers a pause the API refuses.
2584
+ *
2585
+ * Returns 0 when even a single skip overshoots, which happens on a subscription
2586
+ * whose next bill date already sits near the cap. Callers should read that as
2587
+ * "pausing by order count is unavailable here" and leave the indefinite pause.
2588
+ *
2589
+ * The two ends deliberately measure from different places: the resume date
2590
+ * counts forward from the current bill date, the cap counts forward from today.
2591
+ * The API stays the backstop, so the exact boundary count depends on its
2592
+ * resume-date arithmetic agreeing with calculateResumeDate.
2593
+ */
2594
+ function getMaxSkippableOrders(billingInterval = 1, billingIntervalUnit = "month", currentNextBillDate, from = /* @__PURE__ */ new Date()) {
2595
+ const cap = getMaxEditableBillDate(billingInterval, billingIntervalUnit, from);
2596
+ let allowed = 0;
2597
+ for (let count = 1; count <= MAX_SKIPPABLE_ORDERS; count += 1) {
2598
+ const resume = calculateResumeDate(billingInterval, billingIntervalUnit, count, currentNextBillDate ?? from);
2599
+ if (new Date(resume.getUTCFullYear(), resume.getUTCMonth(), resume.getUTCDate()) > cap) break;
2600
+ allowed = count;
2601
+ }
2602
+ return allowed;
2603
+ }
2561
2604
  function formatDate(dateString, locale = "en-US") {
2562
2605
  if (!dateString) return "";
2563
2606
  const parts = (dateString.split("T")[0] ?? dateString).split("-");
@@ -2618,22 +2661,30 @@ function formatTime(dateString, timezone, locale = "en-US") {
2618
2661
  return "";
2619
2662
  }
2620
2663
  }
2621
- function getNextBillDisplay(subscription, labels = {
2664
+ function getNextBillDisplay(nextBillDate, displayStatus, labels = {
2622
2665
  cancelled: "Cancelled",
2623
2666
  completed: "Completed",
2624
2667
  paused: "Paused",
2625
2668
  notAvailable: "N/A"
2626
2669
  }, locale = "en-US") {
2627
- if (subscription?.billing_cycle_limit_reached) return labels.completed;
2628
- if (subscription?.status === "cancelled") return labels.cancelled;
2629
- if (subscription?.next_bill_date) return formatDate(subscription.next_bill_date, locale);
2630
- if (subscription?.status === "paused") return labels.paused;
2670
+ if (displayStatus === "completed") return labels.completed;
2671
+ if (displayStatus === "cancelled") return labels.cancelled;
2672
+ if (nextBillDate) return formatDate(nextBillDate, locale);
2673
+ if (displayStatus === "paused") return labels.paused;
2631
2674
  return labels.notAvailable;
2632
2675
  }
2633
2676
  //#endregion
2634
2677
  //#region ../../subscriptions/core/src/utils/subscription-actions.ts
2678
+ /**
2679
+ * The one rule for a completed subscription: its current plan's
2680
+ * billing-cycle limit is reached. Every "completed" presentation and
2681
+ * gate reads this, so a new terminal condition lands here once.
2682
+ */
2683
+ function isSubscriptionCompleted(subscription) {
2684
+ return subscription?.billing_cycle_limit_reached === true;
2685
+ }
2635
2686
  function getSubscriptionDisplayStatus(subscription) {
2636
- return subscription.billingCycleLimitReached === true ? "completed" : subscription.status;
2687
+ return isSubscriptionCompleted(subscription) ? "completed" : subscription.status;
2637
2688
  }
2638
2689
  function hasPendingSubscriptionSkip(skips, today = /* @__PURE__ */ new Date()) {
2639
2690
  if (!skips?.length) return false;
@@ -2645,13 +2696,11 @@ function hasPendingSubscriptionSkip(skips, today = /* @__PURE__ */ new Date()) {
2645
2696
  });
2646
2697
  }
2647
2698
  function getSubscriptionActionPolicy(subscription) {
2648
- if (subscription.billingCycleLimitReached === true) return {
2649
- primary: null,
2650
- order: [],
2651
- menu: [],
2652
- recovery: null
2653
- };
2654
- if (subscription.status === "active") {
2699
+ const status = getSubscriptionDisplayStatus({
2700
+ status: subscription.status,
2701
+ billing_cycle_limit_reached: subscription.billingCycleLimitReached
2702
+ });
2703
+ if (status === "active") {
2655
2704
  const skipAllowanceAvailable = subscription.maxSkips == null || (subscription.skippedCount ?? 0) < subscription.maxSkips;
2656
2705
  const canSkip = subscription.allowSkipping === true && subscription.nextBillDate != null && skipAllowanceAvailable;
2657
2706
  const order = [];
@@ -2666,19 +2715,19 @@ function getSubscriptionActionPolicy(subscription) {
2666
2715
  recovery: null
2667
2716
  };
2668
2717
  }
2669
- if (subscription.status === "paused") return {
2718
+ if (status === "paused") return {
2670
2719
  primary: "resume_subscription",
2671
2720
  order: [],
2672
2721
  menu: ["cancel_subscription"],
2673
2722
  recovery: null
2674
2723
  };
2675
- if (subscription.status === "cancelled") return {
2724
+ if (status === "cancelled") return {
2676
2725
  primary: "reactivate_subscription",
2677
2726
  order: [],
2678
2727
  menu: [],
2679
2728
  recovery: null
2680
2729
  };
2681
- if (subscription.status === "past_due") {
2730
+ if (status === "past_due") {
2682
2731
  const isExhausted = subscription.nextRetryAt == null;
2683
2732
  const secondary = subscription.canChangePaymentMethod ? "change_payment_method" : null;
2684
2733
  return {
@@ -2694,13 +2743,13 @@ function getSubscriptionActionPolicy(subscription) {
2694
2743
  }
2695
2744
  };
2696
2745
  }
2697
- if (subscription.status === "completed") return {
2746
+ if (status === "completed") return {
2698
2747
  primary: null,
2699
2748
  order: [],
2700
2749
  menu: [],
2701
2750
  recovery: null
2702
2751
  };
2703
- if (["pending", "trial"].includes(subscription.status)) return {
2752
+ if (["pending", "trial"].includes(status)) return {
2704
2753
  primary: null,
2705
2754
  order: [],
2706
2755
  menu: ["cancel_subscription"],
@@ -4126,6 +4175,30 @@ function isPaymentMethodUsable(pm) {
4126
4175
  return Boolean(addr.address1 && addr.city && addr.state && addr.zip && addr.country_code);
4127
4176
  }
4128
4177
  //#endregion
4178
+ //#region src/screens/subscriptions/bundle-drawer/bill-date-bounds.ts
4179
+ function getDaysInMonth(year, month) {
4180
+ return new Date(year, month + 1, 0).getDate();
4181
+ }
4182
+ function isYearOutOfRange(year, { today, maxDate }) {
4183
+ return year < today.getFullYear() || year > maxDate.getFullYear();
4184
+ }
4185
+ function isMonthOutOfRange(year, month, bounds) {
4186
+ const { today, maxDate } = bounds;
4187
+ if (isYearOutOfRange(year, bounds)) return true;
4188
+ if (year === maxDate.getFullYear() && month > maxDate.getMonth()) return true;
4189
+ if (year > today.getFullYear()) return false;
4190
+ return month < today.getMonth();
4191
+ }
4192
+ function isDayOutOfRange(year, month, day, bounds) {
4193
+ const { today, maxDate } = bounds;
4194
+ if (day > getDaysInMonth(year, month)) return true;
4195
+ if (isMonthOutOfRange(year, month, bounds)) return true;
4196
+ if (year === maxDate.getFullYear() && month === maxDate.getMonth() && day > maxDate.getDate()) return true;
4197
+ if (year > today.getFullYear()) return false;
4198
+ if (month > today.getMonth()) return false;
4199
+ return day < today.getDate();
4200
+ }
4201
+ //#endregion
4129
4202
  //#region src/screens/subscriptions/bundle-drawer/use-bundle-form.ts
4130
4203
  function pickDefaultAddressId(addresses, defaultAddressId) {
4131
4204
  if (defaultAddressId != null) {
@@ -4146,11 +4219,11 @@ function pickDefaultPaymentMethodId(paymentMethods, defaultPaymentMethodId) {
4146
4219
  if (flagged) return flagged.id;
4147
4220
  return usable[0]?.id ?? null;
4148
4221
  }
4149
- function useBundleForm({ addresses, paymentMethods, defaultAddressId, defaultPaymentMethodId }) {
4222
+ function useBundleForm({ subscriptions: subscriptionList, addresses, paymentMethods, defaultAddressId, defaultPaymentMethodId }) {
4150
4223
  const [step, setStep] = useState(1);
4151
4224
  const [selectedTokens, setSelectedTokens] = useState(() => /* @__PURE__ */ new Set());
4152
4225
  const today = useMemo(() => /* @__PURE__ */ new Date(), []);
4153
- const [selectedYear, setYearState] = useState(today.getUTCFullYear());
4226
+ const [selectedYear, setYearState] = useState(today.getFullYear());
4154
4227
  const [selectedMonth, setMonthState] = useState(null);
4155
4228
  const [selectedDay, setDayState] = useState(null);
4156
4229
  const [selectedAddressId, setSelectedAddressId] = useState(() => pickDefaultAddressId(addresses, defaultAddressId));
@@ -4169,55 +4242,73 @@ function useBundleForm({ addresses, paymentMethods, defaultAddressId, defaultPay
4169
4242
  paymentMethods,
4170
4243
  defaultPaymentMethodId
4171
4244
  ]);
4245
+ const toggleToken = useCallback((token) => {
4246
+ setSelectedTokens((prev) => {
4247
+ const next = new Set(prev);
4248
+ if (next.has(token)) next.delete(token);
4249
+ else next.add(token);
4250
+ return next;
4251
+ });
4252
+ }, []);
4253
+ const bounds = useMemo(() => {
4254
+ return {
4255
+ today,
4256
+ maxDate: getMaxEditableBillDateForBundle(Array.from(selectedTokens).map((token) => {
4257
+ const plan = subscriptionList.find((sub) => sub.subscription_token === token)?.subscription_plan;
4258
+ return {
4259
+ billingInterval: plan?.billing_interval,
4260
+ billingIntervalUnit: plan?.billing_interval_unit
4261
+ };
4262
+ }), today)
4263
+ };
4264
+ }, [
4265
+ selectedTokens,
4266
+ subscriptionList,
4267
+ today
4268
+ ]);
4269
+ const setYear = useCallback((y) => {
4270
+ setYearState(y);
4271
+ if (selectedMonth == null) return;
4272
+ if (isMonthOutOfRange(y, selectedMonth, bounds)) {
4273
+ setMonthState(null);
4274
+ setDayState(null);
4275
+ return;
4276
+ }
4277
+ if (selectedDay != null && isDayOutOfRange(y, selectedMonth, selectedDay, bounds)) setDayState(null);
4278
+ }, [
4279
+ selectedMonth,
4280
+ selectedDay,
4281
+ bounds
4282
+ ]);
4283
+ const setMonth = useCallback((m) => {
4284
+ setMonthState(m);
4285
+ if (selectedDay != null && isDayOutOfRange(selectedYear, m, selectedDay, bounds)) setDayState(null);
4286
+ }, [
4287
+ selectedDay,
4288
+ selectedYear,
4289
+ bounds
4290
+ ]);
4291
+ const setDay = useCallback((d) => setDayState(d), []);
4292
+ const canProceedFromStep1 = selectedTokens.size >= 2;
4293
+ const selectedDateOutOfRange = selectedMonth !== null && selectedDay !== null && isDayOutOfRange(selectedYear, selectedMonth, selectedDay, bounds);
4172
4294
  return {
4173
4295
  step,
4174
4296
  setStep,
4175
4297
  selectedTokens,
4176
- toggleToken: useCallback((token) => {
4177
- setSelectedTokens((prev) => {
4178
- const next = new Set(prev);
4179
- if (next.has(token)) next.delete(token);
4180
- else next.add(token);
4181
- return next;
4182
- });
4183
- }, []),
4298
+ toggleToken,
4184
4299
  selectedYear,
4185
4300
  selectedMonth,
4186
4301
  selectedDay,
4187
- setYear: useCallback((y) => {
4188
- setYearState(y);
4189
- if (selectedMonth != null) {
4190
- const isPastMonth = y < today.getUTCFullYear() || y === today.getUTCFullYear() && selectedMonth < today.getUTCMonth();
4191
- const isPastDay = y === today.getUTCFullYear() && selectedMonth === today.getUTCMonth() && selectedDay != null && selectedDay < today.getUTCDate();
4192
- if (isPastMonth) {
4193
- setMonthState(null);
4194
- setDayState(null);
4195
- } else if (isPastDay) setDayState(null);
4196
- }
4197
- }, [
4198
- selectedMonth,
4199
- selectedDay,
4200
- today
4201
- ]),
4202
- setMonth: useCallback((m) => {
4203
- setMonthState(m);
4204
- if (selectedDay != null) {
4205
- const daysInNewMonth = new Date(Date.UTC(selectedYear, m + 1, 0)).getUTCDate();
4206
- const isPastDay = selectedYear === today.getUTCFullYear() && m === today.getUTCMonth() && selectedDay < today.getUTCDate();
4207
- if (selectedDay > daysInNewMonth || isPastDay) setDayState(null);
4208
- }
4209
- }, [
4210
- selectedDay,
4211
- selectedYear,
4212
- today
4213
- ]),
4214
- setDay: useCallback((d) => setDayState(d), []),
4302
+ setYear,
4303
+ setMonth,
4304
+ setDay,
4215
4305
  selectedAddressId,
4216
4306
  setSelectedAddressId,
4217
4307
  selectedPaymentMethodId,
4218
4308
  setSelectedPaymentMethodId,
4219
- canProceedFromStep1: selectedTokens.size >= 2,
4220
- canProceedFromStep2: selectedMonth !== null && selectedDay !== null,
4309
+ bounds,
4310
+ canProceedFromStep1,
4311
+ canProceedFromStep2: selectedMonth !== null && selectedDay !== null && !selectedDateOutOfRange,
4221
4312
  canProceedFromStep3: selectedAddressId != null,
4222
4313
  canProceedFromStep4: selectedPaymentMethodId != null,
4223
4314
  formattedDate: useMemo(() => {
@@ -4446,34 +4537,20 @@ const MONTHS = [
4446
4537
  "Nov",
4447
4538
  "Dec"
4448
4539
  ];
4449
- function getDaysInMonth(year, month) {
4450
- return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
4451
- }
4452
- function DrawerStepDate({ selectedYear, selectedMonth, selectedDay, onYearChange, onMonthChange, onDayChange }) {
4540
+ function DrawerStepDate({ selectedYear, selectedMonth, selectedDay, onYearChange, onMonthChange, onDayChange, bounds }) {
4453
4541
  const { t } = useSubscriptionsTranslation();
4454
- const today = useMemo(() => /* @__PURE__ */ new Date(), []);
4455
- const currentYear = today.getUTCFullYear();
4456
- const currentMonth = today.getUTCMonth();
4457
- const currentDay = today.getUTCDate();
4542
+ const currentYear = bounds.today.getFullYear();
4458
4543
  const availableYears = useMemo(() => [
4459
4544
  currentYear,
4460
4545
  currentYear + 1,
4461
4546
  currentYear + 2
4462
4547
  ], [currentYear]);
4463
- const isMonthDisabled = (monthIndex) => {
4464
- if (selectedYear < currentYear) return true;
4465
- if (selectedYear > currentYear) return false;
4466
- return monthIndex < currentMonth;
4467
- };
4548
+ const isYearDisabled = (year) => isYearOutOfRange(year, bounds);
4549
+ const isMonthDisabled = (monthIndex) => isMonthOutOfRange(selectedYear, monthIndex, bounds);
4468
4550
  const daysInSelectedMonth = selectedMonth == null ? 31 : getDaysInMonth(selectedYear, selectedMonth);
4469
4551
  const isDayDisabled = (day) => {
4470
4552
  if (selectedMonth == null) return true;
4471
- if (day > getDaysInMonth(selectedYear, selectedMonth)) return true;
4472
- if (selectedYear < currentYear) return true;
4473
- if (selectedYear > currentYear) return false;
4474
- if (selectedMonth > currentMonth) return false;
4475
- if (selectedMonth < currentMonth) return true;
4476
- return day < currentDay;
4553
+ return isDayOutOfRange(selectedYear, selectedMonth, day, bounds);
4477
4554
  };
4478
4555
  return /* @__PURE__ */ jsxs("div", {
4479
4556
  className: "flex-1 overflow-y-auto px-6 py-4",
@@ -4496,10 +4573,13 @@ function DrawerStepDate({ selectedYear, selectedMonth, selectedDay, onYearChange
4496
4573
  }), /* @__PURE__ */ jsx("div", {
4497
4574
  className: "grid grid-cols-3 gap-2",
4498
4575
  children: availableYears.map((year) => {
4576
+ const isActive = selectedYear === year;
4577
+ const disabled = isYearDisabled(year);
4499
4578
  return /* @__PURE__ */ jsx("button", {
4500
4579
  type: "button",
4580
+ disabled,
4501
4581
  onClick: () => onYearChange(year),
4502
- 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"}`,
4582
+ 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" : ""}`,
4503
4583
  children: year
4504
4584
  }, year);
4505
4585
  })
@@ -4743,6 +4823,7 @@ function BundleSubscriptionsDrawer({ open, onOpenChange, subscriptions: subscrip
4743
4823
  const { t } = useSubscriptionsTranslation();
4744
4824
  const { isMobile } = useSidebar();
4745
4825
  const form = useBundleForm({
4826
+ subscriptions: subscriptionList,
4746
4827
  addresses,
4747
4828
  paymentMethods,
4748
4829
  defaultAddressId,
@@ -4787,7 +4868,8 @@ function BundleSubscriptionsDrawer({ open, onOpenChange, subscriptions: subscrip
4787
4868
  selectedDay: form.selectedDay,
4788
4869
  onYearChange: form.setYear,
4789
4870
  onMonthChange: form.setMonth,
4790
- onDayChange: form.setDay
4871
+ onDayChange: form.setDay,
4872
+ bounds: form.bounds
4791
4873
  }),
4792
4874
  form.step === 3 && /* @__PURE__ */ jsx(DrawerStepAddress, {
4793
4875
  addresses,
@@ -5348,7 +5430,8 @@ function PauseSubscriptionDialog({ open, onOpenChange, onConfirm, isLoading, err
5348
5430
  const { t, locale } = useSubscriptionsTranslation();
5349
5431
  const [pauseType, setPauseType] = useState("indefinite");
5350
5432
  const [selectedOrderCount, setSelectedOrderCount] = useState(null);
5351
- const orderCountOptions = Array.from({ length: 99 }, (_, i) => i + 1);
5433
+ const maxSkippableOrders = getMaxSkippableOrders(billingInterval, billingIntervalUnit, currentNextBillDate);
5434
+ const orderCountOptions = Array.from({ length: maxSkippableOrders }, (_, i) => i + 1);
5352
5435
  const calculatedResumeDate = selectedOrderCount && currentNextBillDate ? calculateResumeDate(billingInterval, billingIntervalUnit, selectedOrderCount, currentNextBillDate) : null;
5353
5436
  useEffect(() => {
5354
5437
  if (!open) return;
@@ -5428,10 +5511,11 @@ function PauseSubscriptionDialog({ open, onOpenChange, onConfirm, isLoading, err
5428
5511
  type: "radio",
5429
5512
  checked: pauseType === "order_count",
5430
5513
  onChange: () => setPauseType("order_count"),
5431
- className: "border-border accent-primary size-4 focus:ring-0"
5514
+ disabled: maxSkippableOrders === 0,
5515
+ className: "border-border accent-primary size-4 focus:ring-0 disabled:opacity-40"
5432
5516
  }), /* @__PURE__ */ jsx(Label, {
5433
5517
  htmlFor: "pause-order-count",
5434
- className: "text-foreground cursor-pointer text-sm font-medium",
5518
+ className: `text-sm font-medium ${maxSkippableOrders === 0 ? "text-muted-foreground cursor-not-allowed" : "text-foreground cursor-pointer"}`,
5435
5519
  children: t("pause_for_orders")
5436
5520
  })]
5437
5521
  }), pauseType === "order_count" && /* @__PURE__ */ jsxs("div", {
@@ -6425,12 +6509,14 @@ function SubscriptionItemsSection({ subscription, displayQuantity, displayNextBi
6425
6509
  const variant = subscription.variant;
6426
6510
  const product = variant?.product;
6427
6511
  const quantity = displayQuantity ?? subscription.quantity;
6512
+ const displayStatus = getSubscriptionDisplayStatus(subscription);
6513
+ const paymentFailed = displayStatus === "past_due";
6428
6514
  const quantityInputId = `subscription-${subscription.subscription_token}-quantity`;
6429
6515
  const pricing = computeSubscriptionPricing(subscription);
6430
6516
  const subtotal = formatCurrency(pricing.subtotalUnit * quantity);
6431
6517
  const totalPrice = formatCurrency(pricing.totalUnit * quantity);
6432
6518
  const effectiveNextBillDate = displayNextBillDate ?? subscription.next_bill_date;
6433
- const nextOrderTimeOfDay = subscription.billing_cycle_limit_reached ? "" : formatTime(effectiveNextBillDate, subscription.timezone, locale);
6519
+ const nextOrderTimeOfDay = displayStatus === "completed" ? "" : formatTime(effectiveNextBillDate, subscription.timezone, locale);
6434
6520
  const nextBillDisplayLabels = {
6435
6521
  cancelled: t("date_cancelled"),
6436
6522
  completed: t("list_status_completed"),
@@ -6454,14 +6540,11 @@ function SubscriptionItemsSection({ subscription, displayQuantity, displayNextBi
6454
6540
  children: [
6455
6541
  /* @__PURE__ */ jsx("div", {
6456
6542
  className: "text-muted-foreground mb-1 text-sm",
6457
- children: subscription.status === "past_due" && !subscription.billing_cycle_limit_reached ? t("payment_failed_on") : t("next_order_date")
6543
+ children: paymentFailed ? t("payment_failed_on") : t("next_order_date")
6458
6544
  }),
6459
6545
  /* @__PURE__ */ jsx("div", {
6460
6546
  className: "text-foreground text-2xl font-bold",
6461
- children: subscription.status === "past_due" && !subscription.billing_cycle_limit_reached ? subscription.last_failed_at ? formatOptionalDate(subscription.last_failed_at, locale) : "—" : getNextBillDisplay(displayNextBillDate ? {
6462
- ...subscription,
6463
- next_bill_date: displayNextBillDate
6464
- } : subscription, nextBillDisplayLabels, locale)
6547
+ children: paymentFailed ? subscription.last_failed_at ? formatOptionalDate(subscription.last_failed_at, locale) : "—" : getNextBillDisplay(effectiveNextBillDate, displayStatus, nextBillDisplayLabels, locale)
6465
6548
  }),
6466
6549
  nextOrderTimeOfDay && /* @__PURE__ */ jsxs("div", {
6467
6550
  className: "text-muted-foreground mt-1 text-xs",
@@ -6592,10 +6675,7 @@ function SubscriptionManagementSection({ subscription, actionPolicy, isMutating,
6592
6675
  const plan = subscription.subscription_plan;
6593
6676
  const quantity = subscription.quantity;
6594
6677
  const totalPrice = formatCurrency(subscription.price * quantity);
6595
- const displayStatus = getSubscriptionDisplayStatus({
6596
- status: subscription.status,
6597
- billingCycleLimitReached: subscription.billing_cycle_limit_reached
6598
- });
6678
+ const displayStatus = getSubscriptionDisplayStatus(subscription);
6599
6679
  const cadenceKey = getCadenceKey(plan.billing_interval_unit);
6600
6680
  const interval = plan.billing_interval;
6601
6681
  const unitKeys = UNIT_KEYS[plan.billing_interval_unit.toLowerCase()];
@@ -6790,7 +6870,7 @@ function SubscriptionManagementSection({ subscription, actionPolicy, isMutating,
6790
6870
  subscriptionToken: subscription.subscription_token,
6791
6871
  customerId: subscription.customer?.id ?? 0,
6792
6872
  countryCode: subscription.address?.country_code ?? "US",
6793
- disabled: subscription.billing_cycle_limit_reached
6873
+ disabled: isSubscriptionCompleted(subscription)
6794
6874
  }) : subscription.address && /* @__PURE__ */ jsxs("div", {
6795
6875
  className: "border-border mb-6 border-b pb-4",
6796
6876
  children: [/* @__PURE__ */ jsx("div", {
@@ -6927,7 +7007,7 @@ function SubscriptionDetail({ token, onNotFound, onError, onSuccess, onMutationE
6927
7007
  const { data, isLoading, error } = useSubscription(token);
6928
7008
  const subscription = data?.subscription;
6929
7009
  const customerId = subscription?.customer?.id ?? 0;
6930
- const waiverPreviewEnabled = subscription?.status === "past_due" && subscription.billing_cycle_limit_reached !== true && subscription.next_retry_at == null;
7010
+ const waiverPreviewEnabled = subscription?.status === "past_due" && !isSubscriptionCompleted(subscription) && subscription.next_retry_at == null;
6931
7011
  const waiverPreviewQuery = useFailedCycleWaiverPreview(token, { enabled: waiverPreviewEnabled });
6932
7012
  const isWaiverPreviewLoading = waiverPreviewEnabled && waiverPreviewQuery.isFetching;
6933
7013
  const waiverPreview = waiverPreviewQuery.data?.failed_cycle_waiver;
@@ -7087,7 +7167,8 @@ function SubscriptionDetail({ token, onNotFound, onError, onSuccess, onMutationE
7087
7167
  if (isLoading) return /* @__PURE__ */ jsx(SubscriptionDetailSkeleton, {});
7088
7168
  if (!subscription) return null;
7089
7169
  const plan = subscription.subscription_plan;
7090
- const isActive = subscription.status === "active" && !subscription.billing_cycle_limit_reached;
7170
+ const displayStatus = getSubscriptionDisplayStatus(subscription);
7171
+ const isActive = displayStatus === "active";
7091
7172
  const actionPolicy = getSubscriptionActionPolicy({
7092
7173
  status: subscription.status,
7093
7174
  billingCycleLimitReached: subscription.billing_cycle_limit_reached,
@@ -7106,7 +7187,7 @@ function SubscriptionDetail({ token, onNotFound, onError, onSuccess, onMutationE
7106
7187
  const childPaymentRecoveryDisabled = childPaymentRecoveryToken === token;
7107
7188
  const waiverMutationPending = waiverMutation.isPending && waiverMutation.variables?.subscriptionToken === token;
7108
7189
  const aggregateRecoveryDisabled = directParentRecoveryDisabled || childPaymentRecoveryDisabled || waiverMutationPending;
7109
- const paymentMethodDisabled = subscription.billing_cycle_limit_reached || directParentRecoveryDisabled || waiverMutationPending || independentParentMutationDisabled;
7190
+ const paymentMethodDisabled = isSubscriptionCompleted(subscription) || directParentRecoveryDisabled || waiverMutationPending || independentParentMutationDisabled;
7110
7191
  const isMutating = independentParentMutationDisabled || aggregateRecoveryDisabled;
7111
7192
  const handlePauseConfirm = (selection) => {
7112
7193
  const pauseParams = { customerId };
@@ -7163,7 +7244,7 @@ function SubscriptionDetail({ token, onNotFound, onError, onSuccess, onMutationE
7163
7244
  }, { onSuccess: () => setShowReactivateModal(false) });
7164
7245
  };
7165
7246
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
7166
- subscription.status === "past_due" && !subscription.billing_cycle_limit_reached && /* @__PURE__ */ jsx("div", {
7247
+ displayStatus === "past_due" && /* @__PURE__ */ jsx("div", {
7167
7248
  className: "mb-4 px-2 sm:px-0 lg:mb-6",
7168
7249
  children: /* @__PURE__ */ jsx(PastDueBanner, {
7169
7250
  productTitle: subscription.variant?.product?.title ?? null,
@@ -7483,7 +7564,7 @@ async function invalidateSubscriptionPaymentRecoveryCache(queryClient, subscript
7483
7564
  }
7484
7565
  async function completeSubscriptionPaymentUpdate(args) {
7485
7566
  const cached = args.queryClient.getQueryData(subscriptionsKeys.detail(args.updatedSubscriptionToken));
7486
- if (cached?.subscription.status === "past_due" && cached.subscription.billing_cycle_limit_reached !== true) {
7567
+ if (cached?.subscription.status === "past_due" && !isSubscriptionCompleted(cached.subscription)) {
7487
7568
  args.queueRetry(args.updatedSubscriptionToken);
7488
7569
  return;
7489
7570
  }
@@ -8274,7 +8355,6 @@ function PortalSubscriptionShippingAddressSectionInner({ addressId, address: cur
8274
8355
  addressList: addresses,
8275
8356
  selectedAddressId: addressId ?? 0,
8276
8357
  onSelectAddress: (addr) => {
8277
- if (disabled) return;
8278
8358
  if (addr.id === addressId) return;
8279
8359
  updateSubscription.mutate({
8280
8360
  subscriptionToken,
@@ -8282,7 +8362,6 @@ function PortalSubscriptionShippingAddressSectionInner({ addressId, address: cur
8282
8362
  });
8283
8363
  },
8284
8364
  onAddAddressClick: () => {
8285
- if (disabled) return;
8286
8365
  setEditingAddress(null);
8287
8366
  setIsFormDialogOpen(true);
8288
8367
  },
@@ -8541,4 +8620,4 @@ const subscriptionsScreenPropertySchema = {
8541
8620
  //#endregion
8542
8621
  export { subscriptionsScreenPropertySchema as n, SubscriptionsScreen as t };
8543
8622
 
8544
- //# sourceMappingURL=SubscriptionsScreen-0Giqwkng.mjs.map
8623
+ //# sourceMappingURL=SubscriptionsScreen-C44-Ft7P.mjs.map