@fluid-app/portal-sdk 0.1.484 → 0.1.486

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 (23) hide show
  1. package/dist/{ProfileScreen-CUkBqifi.mjs → ProfileScreen-B6a4dd62.mjs} +2 -2
  2. package/dist/{ProfileScreen-yi6_DEsN.cjs → ProfileScreen-B9ew5EIJ.cjs} +2 -2
  3. package/dist/{ProfileScreen-Bo0Pnojo.cjs → ProfileScreen-BQMf7w0k.cjs} +2 -2
  4. package/dist/{ProfileScreen-Bo0Pnojo.cjs.map → ProfileScreen-BQMf7w0k.cjs.map} +1 -1
  5. package/dist/{ProfileScreen-DX_erSoF.mjs → ProfileScreen-D4FpU_l8.mjs} +2 -2
  6. package/dist/{ProfileScreen-DX_erSoF.mjs.map → ProfileScreen-D4FpU_l8.mjs.map} +1 -1
  7. package/dist/{SubscriptionsScreen-DPymXVC4.cjs → SubscriptionsScreen-ByVgv3qf.cjs} +144 -66
  8. package/dist/SubscriptionsScreen-ByVgv3qf.cjs.map +1 -0
  9. package/dist/{SubscriptionsScreen-BKJALFrF.mjs → SubscriptionsScreen-DQuqYGHH.mjs} +144 -66
  10. package/dist/SubscriptionsScreen-DQuqYGHH.mjs.map +1 -0
  11. package/dist/{SubscriptionsScreen-B8vSO2d9.mjs → SubscriptionsScreen-DaJsb9sg.mjs} +2 -2
  12. package/dist/{SubscriptionsScreen-DIxkq3nu.cjs → SubscriptionsScreen-f5mFmNJ_.cjs} +2 -2
  13. package/dist/index.cjs +7 -7
  14. package/dist/index.mjs +7 -7
  15. package/dist/{use-portal-card-add-3ds-flow-DPX94F0H.cjs → use-portal-card-add-3ds-flow-B6h1F8qd.cjs} +48 -28
  16. package/dist/use-portal-card-add-3ds-flow-B6h1F8qd.cjs.map +1 -0
  17. package/dist/{use-portal-card-add-3ds-flow-CaxgEoqx.mjs → use-portal-card-add-3ds-flow-CCxDpsu-.mjs} +48 -28
  18. package/dist/use-portal-card-add-3ds-flow-CCxDpsu-.mjs.map +1 -0
  19. package/package.json +13 -13
  20. package/dist/SubscriptionsScreen-BKJALFrF.mjs.map +0 -1
  21. package/dist/SubscriptionsScreen-DPymXVC4.cjs.map +0 -1
  22. package/dist/use-portal-card-add-3ds-flow-CaxgEoqx.mjs.map +0 -1
  23. package/dist/use-portal-card-add-3ds-flow-DPX94F0H.cjs.map +0 -1
@@ -15,7 +15,7 @@ import { n as useAppNavigation } from "./AppNavigationContext-BFStgikI.mjs";
15
15
  import { t as useStore } from "./use-store-CEDJ-H-8.mjs";
16
16
  import { f as useSidebar } from "./sidebar-DwB6s4af.mjs";
17
17
  import { n as ScreenHeaderBreadcrumbs } from "./ScreenHeaderContext-DnGLNAvp.mjs";
18
- import { _ as ProfileTranslationBridge, a as getBrowserInfo, c as EditPaymentMethodDialog, d as getCardDisplayName, f as getCardExpiry, g as EllipsesDropdown, h as ConfirmActionDialog, i as isThreeDsRequiredCreate, l as FluidPayCoreProvider, n as createFluidPayApiAdapter, o as CreditCardFormDialog, p as getPaymentMethodLogoBrand, r as mapToFluidPayPaymentMethod, s as AddressFormDialog, t as usePortalCardAdd3DSFlow, u as PaymentIcon, v as useProfileTranslation } from "./use-portal-card-add-3ds-flow-CaxgEoqx.mjs";
18
+ import { _ as ProfileTranslationBridge, a as getBrowserInfo, c as EditPaymentMethodDialog, d as getCardDisplayName, f as getCardExpiry, g as EllipsesDropdown, h as ConfirmActionDialog, i as isThreeDsRequiredCreate, l as FluidPayCoreProvider, n as createFluidPayApiAdapter, o as CreditCardFormDialog, p as getPaymentMethodLogoBrand, r as mapToFluidPayPaymentMethod, s as AddressFormDialog, t as usePortalCardAdd3DSFlow, u as PaymentIcon, v as useProfileTranslation } from "./use-portal-card-add-3ds-flow-CCxDpsu-.mjs";
19
19
  import { a as paymentDescriptorLabel, d as normalizeCardBrand, i as normalizePaymentDescriptor, s as resolvePaymentMethodDisplay, t as isCardPaymentDescriptor, u as getPaymentBrandName } from "./display-BEVe3Jb4.mjs";
20
20
  import { createContext, use, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
21
21
  import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
@@ -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("-");
@@ -4132,6 +4175,30 @@ function isPaymentMethodUsable(pm) {
4132
4175
  return Boolean(addr.address1 && addr.city && addr.state && addr.zip && addr.country_code);
4133
4176
  }
4134
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
4135
4202
  //#region src/screens/subscriptions/bundle-drawer/use-bundle-form.ts
4136
4203
  function pickDefaultAddressId(addresses, defaultAddressId) {
4137
4204
  if (defaultAddressId != null) {
@@ -4152,11 +4219,11 @@ function pickDefaultPaymentMethodId(paymentMethods, defaultPaymentMethodId) {
4152
4219
  if (flagged) return flagged.id;
4153
4220
  return usable[0]?.id ?? null;
4154
4221
  }
4155
- function useBundleForm({ addresses, paymentMethods, defaultAddressId, defaultPaymentMethodId }) {
4222
+ function useBundleForm({ subscriptions: subscriptionList, addresses, paymentMethods, defaultAddressId, defaultPaymentMethodId }) {
4156
4223
  const [step, setStep] = useState(1);
4157
4224
  const [selectedTokens, setSelectedTokens] = useState(() => /* @__PURE__ */ new Set());
4158
4225
  const today = useMemo(() => /* @__PURE__ */ new Date(), []);
4159
- const [selectedYear, setYearState] = useState(today.getUTCFullYear());
4226
+ const [selectedYear, setYearState] = useState(today.getFullYear());
4160
4227
  const [selectedMonth, setMonthState] = useState(null);
4161
4228
  const [selectedDay, setDayState] = useState(null);
4162
4229
  const [selectedAddressId, setSelectedAddressId] = useState(() => pickDefaultAddressId(addresses, defaultAddressId));
@@ -4175,55 +4242,73 @@ function useBundleForm({ addresses, paymentMethods, defaultAddressId, defaultPay
4175
4242
  paymentMethods,
4176
4243
  defaultPaymentMethodId
4177
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);
4178
4294
  return {
4179
4295
  step,
4180
4296
  setStep,
4181
4297
  selectedTokens,
4182
- toggleToken: useCallback((token) => {
4183
- setSelectedTokens((prev) => {
4184
- const next = new Set(prev);
4185
- if (next.has(token)) next.delete(token);
4186
- else next.add(token);
4187
- return next;
4188
- });
4189
- }, []),
4298
+ toggleToken,
4190
4299
  selectedYear,
4191
4300
  selectedMonth,
4192
4301
  selectedDay,
4193
- setYear: useCallback((y) => {
4194
- setYearState(y);
4195
- if (selectedMonth != null) {
4196
- const isPastMonth = y < today.getUTCFullYear() || y === today.getUTCFullYear() && selectedMonth < today.getUTCMonth();
4197
- const isPastDay = y === today.getUTCFullYear() && selectedMonth === today.getUTCMonth() && selectedDay != null && selectedDay < today.getUTCDate();
4198
- if (isPastMonth) {
4199
- setMonthState(null);
4200
- setDayState(null);
4201
- } else if (isPastDay) setDayState(null);
4202
- }
4203
- }, [
4204
- selectedMonth,
4205
- selectedDay,
4206
- today
4207
- ]),
4208
- setMonth: useCallback((m) => {
4209
- setMonthState(m);
4210
- if (selectedDay != null) {
4211
- const daysInNewMonth = new Date(Date.UTC(selectedYear, m + 1, 0)).getUTCDate();
4212
- const isPastDay = selectedYear === today.getUTCFullYear() && m === today.getUTCMonth() && selectedDay < today.getUTCDate();
4213
- if (selectedDay > daysInNewMonth || isPastDay) setDayState(null);
4214
- }
4215
- }, [
4216
- selectedDay,
4217
- selectedYear,
4218
- today
4219
- ]),
4220
- setDay: useCallback((d) => setDayState(d), []),
4302
+ setYear,
4303
+ setMonth,
4304
+ setDay,
4221
4305
  selectedAddressId,
4222
4306
  setSelectedAddressId,
4223
4307
  selectedPaymentMethodId,
4224
4308
  setSelectedPaymentMethodId,
4225
- canProceedFromStep1: selectedTokens.size >= 2,
4226
- canProceedFromStep2: selectedMonth !== null && selectedDay !== null,
4309
+ bounds,
4310
+ canProceedFromStep1,
4311
+ canProceedFromStep2: selectedMonth !== null && selectedDay !== null && !selectedDateOutOfRange,
4227
4312
  canProceedFromStep3: selectedAddressId != null,
4228
4313
  canProceedFromStep4: selectedPaymentMethodId != null,
4229
4314
  formattedDate: useMemo(() => {
@@ -4452,34 +4537,20 @@ const MONTHS = [
4452
4537
  "Nov",
4453
4538
  "Dec"
4454
4539
  ];
4455
- function getDaysInMonth(year, month) {
4456
- return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
4457
- }
4458
- function DrawerStepDate({ selectedYear, selectedMonth, selectedDay, onYearChange, onMonthChange, onDayChange }) {
4540
+ function DrawerStepDate({ selectedYear, selectedMonth, selectedDay, onYearChange, onMonthChange, onDayChange, bounds }) {
4459
4541
  const { t } = useSubscriptionsTranslation();
4460
- const today = useMemo(() => /* @__PURE__ */ new Date(), []);
4461
- const currentYear = today.getUTCFullYear();
4462
- const currentMonth = today.getUTCMonth();
4463
- const currentDay = today.getUTCDate();
4542
+ const currentYear = bounds.today.getFullYear();
4464
4543
  const availableYears = useMemo(() => [
4465
4544
  currentYear,
4466
4545
  currentYear + 1,
4467
4546
  currentYear + 2
4468
4547
  ], [currentYear]);
4469
- const isMonthDisabled = (monthIndex) => {
4470
- if (selectedYear < currentYear) return true;
4471
- if (selectedYear > currentYear) return false;
4472
- return monthIndex < currentMonth;
4473
- };
4548
+ const isYearDisabled = (year) => isYearOutOfRange(year, bounds);
4549
+ const isMonthDisabled = (monthIndex) => isMonthOutOfRange(selectedYear, monthIndex, bounds);
4474
4550
  const daysInSelectedMonth = selectedMonth == null ? 31 : getDaysInMonth(selectedYear, selectedMonth);
4475
4551
  const isDayDisabled = (day) => {
4476
4552
  if (selectedMonth == null) return true;
4477
- if (day > getDaysInMonth(selectedYear, selectedMonth)) return true;
4478
- if (selectedYear < currentYear) return true;
4479
- if (selectedYear > currentYear) return false;
4480
- if (selectedMonth > currentMonth) return false;
4481
- if (selectedMonth < currentMonth) return true;
4482
- return day < currentDay;
4553
+ return isDayOutOfRange(selectedYear, selectedMonth, day, bounds);
4483
4554
  };
4484
4555
  return /* @__PURE__ */ jsxs("div", {
4485
4556
  className: "flex-1 overflow-y-auto px-6 py-4",
@@ -4502,10 +4573,13 @@ function DrawerStepDate({ selectedYear, selectedMonth, selectedDay, onYearChange
4502
4573
  }), /* @__PURE__ */ jsx("div", {
4503
4574
  className: "grid grid-cols-3 gap-2",
4504
4575
  children: availableYears.map((year) => {
4576
+ const isActive = selectedYear === year;
4577
+ const disabled = isYearDisabled(year);
4505
4578
  return /* @__PURE__ */ jsx("button", {
4506
4579
  type: "button",
4580
+ disabled,
4507
4581
  onClick: () => onYearChange(year),
4508
- 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" : ""}`,
4509
4583
  children: year
4510
4584
  }, year);
4511
4585
  })
@@ -4749,6 +4823,7 @@ function BundleSubscriptionsDrawer({ open, onOpenChange, subscriptions: subscrip
4749
4823
  const { t } = useSubscriptionsTranslation();
4750
4824
  const { isMobile } = useSidebar();
4751
4825
  const form = useBundleForm({
4826
+ subscriptions: subscriptionList,
4752
4827
  addresses,
4753
4828
  paymentMethods,
4754
4829
  defaultAddressId,
@@ -4793,7 +4868,8 @@ function BundleSubscriptionsDrawer({ open, onOpenChange, subscriptions: subscrip
4793
4868
  selectedDay: form.selectedDay,
4794
4869
  onYearChange: form.setYear,
4795
4870
  onMonthChange: form.setMonth,
4796
- onDayChange: form.setDay
4871
+ onDayChange: form.setDay,
4872
+ bounds: form.bounds
4797
4873
  }),
4798
4874
  form.step === 3 && /* @__PURE__ */ jsx(DrawerStepAddress, {
4799
4875
  addresses,
@@ -5354,7 +5430,8 @@ function PauseSubscriptionDialog({ open, onOpenChange, onConfirm, isLoading, err
5354
5430
  const { t, locale } = useSubscriptionsTranslation();
5355
5431
  const [pauseType, setPauseType] = useState("indefinite");
5356
5432
  const [selectedOrderCount, setSelectedOrderCount] = useState(null);
5357
- 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);
5358
5435
  const calculatedResumeDate = selectedOrderCount && currentNextBillDate ? calculateResumeDate(billingInterval, billingIntervalUnit, selectedOrderCount, currentNextBillDate) : null;
5359
5436
  useEffect(() => {
5360
5437
  if (!open) return;
@@ -5434,10 +5511,11 @@ function PauseSubscriptionDialog({ open, onOpenChange, onConfirm, isLoading, err
5434
5511
  type: "radio",
5435
5512
  checked: pauseType === "order_count",
5436
5513
  onChange: () => setPauseType("order_count"),
5437
- 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"
5438
5516
  }), /* @__PURE__ */ jsx(Label, {
5439
5517
  htmlFor: "pause-order-count",
5440
- 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"}`,
5441
5519
  children: t("pause_for_orders")
5442
5520
  })]
5443
5521
  }), pauseType === "order_count" && /* @__PURE__ */ jsxs("div", {
@@ -8542,4 +8620,4 @@ const subscriptionsScreenPropertySchema = {
8542
8620
  //#endregion
8543
8621
  export { subscriptionsScreenPropertySchema as n, SubscriptionsScreen as t };
8544
8622
 
8545
- //# sourceMappingURL=SubscriptionsScreen-BKJALFrF.mjs.map
8623
+ //# sourceMappingURL=SubscriptionsScreen-DQuqYGHH.mjs.map