@fluid-app/portal-sdk 0.1.306 → 0.1.308

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 (56) hide show
  1. package/dist/{FluidProvider-BiULiWJY.cjs → FluidProvider-B1UsSiKO.cjs} +2 -2
  2. package/dist/{FluidProvider-BiULiWJY.cjs.map → FluidProvider-B1UsSiKO.cjs.map} +1 -1
  3. package/dist/{FluidProvider-CLIlEKSg.mjs → FluidProvider-D7BEpqDQ.mjs} +2 -2
  4. package/dist/{FluidProvider-CLIlEKSg.mjs.map → FluidProvider-D7BEpqDQ.mjs.map} +1 -1
  5. package/dist/{MessagingScreen-CYTxBDtC.cjs → MessagingScreen-0qwUGWGf.cjs} +3 -3
  6. package/dist/{MessagingScreen-CYTxBDtC.cjs.map → MessagingScreen-0qwUGWGf.cjs.map} +1 -1
  7. package/dist/{MessagingScreen-Cd2iUd_B.cjs → MessagingScreen-DCHGSAA7.cjs} +3 -3
  8. package/dist/{MessagingScreen-CZTploEf.mjs → MessagingScreen-DyFLJsQ-.mjs} +3 -3
  9. package/dist/{MessagingScreen-CZTploEf.mjs.map → MessagingScreen-DyFLJsQ-.mjs.map} +1 -1
  10. package/dist/{MessagingScreen-C_jBMCSt.mjs → MessagingScreen-L6vyOqgm.mjs} +3 -4
  11. package/dist/{OrdersScreen-CIxlNlYf.cjs → OrdersScreen-3WRoz4fR.cjs} +3 -3
  12. package/dist/{OrdersScreen-mHhOjrMk.mjs → OrdersScreen-BiB5eMA4.mjs} +3 -3
  13. package/dist/{OrdersScreen-mHhOjrMk.mjs.map → OrdersScreen-BiB5eMA4.mjs.map} +1 -1
  14. package/dist/{OrdersScreen-DHqx_gbA.mjs → OrdersScreen-UsFG95zY.mjs} +3 -5
  15. package/dist/{OrdersScreen-BVOQup59.cjs → OrdersScreen-rjcGVfyT.cjs} +3 -3
  16. package/dist/{OrdersScreen-BVOQup59.cjs.map → OrdersScreen-rjcGVfyT.cjs.map} +1 -1
  17. package/dist/{ProfileScreen-BiO036lk.cjs → ProfileScreen-Bm9gayW5.cjs} +3 -3
  18. package/dist/{ProfileScreen-7yjZmDoH.mjs → ProfileScreen-CIDEmZg9.mjs} +4 -4
  19. package/dist/{ProfileScreen-7yjZmDoH.mjs.map → ProfileScreen-CIDEmZg9.mjs.map} +1 -1
  20. package/dist/{ProfileScreen-BH7sSw_t.mjs → ProfileScreen-DioyTFVO.mjs} +3 -4
  21. package/dist/{ProfileScreen-Dsm-0XQY.cjs → ProfileScreen-xTVmveXS.cjs} +4 -4
  22. package/dist/{ProfileScreen-Dsm-0XQY.cjs.map → ProfileScreen-xTVmveXS.cjs.map} +1 -1
  23. package/dist/{ShareablesScreen-C8zQjj9l.mjs → ShareablesScreen-BXMbz5Fn.mjs} +3 -3
  24. package/dist/{ShareablesScreen-C8zQjj9l.mjs.map → ShareablesScreen-BXMbz5Fn.mjs.map} +1 -1
  25. package/dist/{ShareablesScreen-DFTD6x4A.cjs → ShareablesScreen-BsXn5-sp.cjs} +3 -3
  26. package/dist/{ShareablesScreen-DFTD6x4A.cjs.map → ShareablesScreen-BsXn5-sp.cjs.map} +1 -1
  27. package/dist/{ShareablesScreen-CH643xDj.mjs → ShareablesScreen-CetzfpS7.mjs} +2 -4
  28. package/dist/{ShareablesScreen-B1h2JbtB.cjs → ShareablesScreen-JAN0oKF9.cjs} +2 -2
  29. package/dist/{ShopScreen-C3pEQzV_.cjs → ShopScreen-BM0aj7oV.cjs} +2 -2
  30. package/dist/{ShopScreen-C3pEQzV_.cjs.map → ShopScreen-BM0aj7oV.cjs.map} +1 -1
  31. package/dist/{ShopScreen-B-lu9pT5.cjs → ShopScreen-BsTTHFit.cjs} +3 -3
  32. package/dist/{ShopScreen-NzcM2H-M.mjs → ShopScreen-DwyZ8pqV.mjs} +3 -5
  33. package/dist/{ShopScreen-H8OlaM3P.mjs → ShopScreen-u8MPbT1t.mjs} +2 -2
  34. package/dist/{ShopScreen-H8OlaM3P.mjs.map → ShopScreen-u8MPbT1t.mjs.map} +1 -1
  35. package/dist/{SubscriptionsScreen-kCkz_1xD.cjs → SubscriptionsScreen-D_HUnXQb.cjs} +2 -2
  36. package/dist/{SubscriptionsScreen-1hP0t23a.cjs → SubscriptionsScreen-Dfmqn6LY.cjs} +1344 -37
  37. package/dist/SubscriptionsScreen-Dfmqn6LY.cjs.map +1 -0
  38. package/dist/{SubscriptionsScreen-Ck3-dcqq.mjs → SubscriptionsScreen-HIIrxH3a.mjs} +1346 -39
  39. package/dist/SubscriptionsScreen-HIIrxH3a.mjs.map +1 -0
  40. package/dist/index.cjs +21 -21
  41. package/dist/index.d.cts.map +1 -1
  42. package/dist/index.d.mts.map +1 -1
  43. package/dist/index.mjs +21 -21
  44. package/dist/{portal_tenant-l4a-eX2B.mjs → portal_tenant-BLdhIM81.mjs} +22 -2
  45. package/dist/portal_tenant-BLdhIM81.mjs.map +1 -0
  46. package/dist/{portal_tenant-DG4gKXnu.cjs → portal_tenant-ywAHQ1k3.cjs} +33 -1
  47. package/dist/portal_tenant-ywAHQ1k3.cjs.map +1 -0
  48. package/dist/{use-account-BgustWSP.mjs → use-account-BOE1yhAH.mjs} +2 -2
  49. package/dist/{use-account-BgustWSP.mjs.map → use-account-BOE1yhAH.mjs.map} +1 -1
  50. package/dist/{use-account-EicAlO8K.cjs → use-account-DeRJHrd3.cjs} +2 -2
  51. package/dist/{use-account-EicAlO8K.cjs.map → use-account-DeRJHrd3.cjs.map} +1 -1
  52. package/package.json +18 -18
  53. package/dist/SubscriptionsScreen-1hP0t23a.cjs.map +0 -1
  54. package/dist/SubscriptionsScreen-Ck3-dcqq.mjs.map +0 -1
  55. package/dist/portal_tenant-DG4gKXnu.cjs.map +0 -1
  56. package/dist/portal_tenant-l4a-eX2B.mjs.map +0 -1
@@ -1,21 +1,21 @@
1
1
  import { I as __exportAll } from "./portal_tenant_content-CcKZMB9E.mjs";
2
- import { C as subscriptions_reactivate, D as subscriptions_update, E as subscriptions_skip, S as subscriptions_pause, T as subscriptions_show, b as subscriptions_cancel, w as subscriptions_resume, x as subscriptions_list } from "./portal_tenant-l4a-eX2B.mjs";
2
+ import { C as subscriptions_list, D as subscriptions_show, E as subscriptions_resume, O as subscriptions_skip, S as subscriptions_cancel, T as subscriptions_reactivate, b as subscription_bundles_create, k as subscriptions_update, w as subscriptions_pause, x as subscription_bundles_list } from "./portal_tenant-BLdhIM81.mjs";
3
3
  import { i as usePayApi } from "./api-context-3GmfqvIB.mjs";
4
4
  import { n as useCountriesApi } from "./countries-api-context-CMh13cfX.mjs";
5
5
  import { n as usePortalTenantClient } from "./PortalTenantClientProvider-BF-1Fy27.mjs";
6
6
  import { n as useActiveLocale } from "./locale-context-BbdKe_Fj.mjs";
7
- import { $t as constructFrom, At as isProtectedDayOfYearToken, C as SelectContent, Cn as cn$1, E as SelectValue, Ft as getWeekYear, Ht as addWeeks, It as getISOWeek, Jt as startOfWeek, Kt as getTimezoneOffsetInMilliseconds, Lt as enUS, Mt as warnOrThrowProtectedError, Nt as longFormatters, Ot as isAfter, Pt as getWeek, Q as DialogContent, Qt as toDate, R as Label, S as Select, T as SelectTrigger, Vt as addYears, X as Dialog, Xt as addMonths, Yt as getDefaultOptions$1, Zt as addDays, _n as AlertDialogTitle, an as BreadcrumbLink, at as Popover, cn as BreadcrumbSeparator, en as millisecondsInHour, et as DialogFooter, f as fluidToast, fn as AlertDialogCancel, gn as AlertDialogHeader, h as Skeleton, hn as AlertDialogFooter, in as BreadcrumbItem, it as DialogTitle, jt as isProtectedWeekYearToken, k as Input, kt as format, mn as AlertDialogDescription, nn as millisecondsInSecond, on as BreadcrumbList, ot as PopoverContent, pn as AlertDialogContent, qt as startOfISOWeek, rn as Breadcrumb, sn as BreadcrumbPage, st as PopoverTrigger, tn as millisecondsInMinute, tt as DialogHeader, un as AlertDialog, vn as Button, w as SelectItem, wt as Calendar$1, xn as PortalContainerProvider } from "./src-Cti2j6Gw.mjs";
7
+ import { $t as constructFrom, At as isProtectedDayOfYearToken, C as SelectContent, Cn as cn$1, E as SelectValue, Ft as getWeekYear, Ht as addWeeks, It as getISOWeek, Jt as startOfWeek, Kt as getTimezoneOffsetInMilliseconds, Lt as enUS, Mt as warnOrThrowProtectedError, Nt as longFormatters, Ot as isAfter, Pt as getWeek, Q as DialogContent, Qt as toDate, R as Label, S as Select, T as SelectTrigger, Vt as addYears, X as Dialog, Xt as addMonths, Yt as getDefaultOptions$1, Zt as addDays, _ as SheetContent, _n as AlertDialogTitle, an as BreadcrumbLink, at as Popover, b as SheetTitle, cn as BreadcrumbSeparator, en as millisecondsInHour, et as DialogFooter, f as fluidToast, fn as AlertDialogCancel, g as Sheet, gn as AlertDialogHeader, h as Skeleton, hn as AlertDialogFooter, in as BreadcrumbItem, it as DialogTitle, jt as isProtectedWeekYearToken, k as Input, kt as format, mn as AlertDialogDescription, nn as millisecondsInSecond, on as BreadcrumbList, ot as PopoverContent, pn as AlertDialogContent, qt as startOfISOWeek, rn as Breadcrumb, sn as BreadcrumbPage, st as PopoverTrigger, tn as millisecondsInMinute, tt as DialogHeader, un as AlertDialog, vn as Button, w as SelectItem, wt as Calendar$1, xn as PortalContainerProvider } from "./src-Cti2j6Gw.mjs";
8
8
  import { n as ScreenHeaderBreadcrumbs } from "./ScreenHeaderContext-BjpQOCck.mjs";
9
9
  import { t as createTranslationContext } from "./translation-api-context-factory-BS8OgyiY.mjs";
10
10
  import { n as payKeys, r as storeKeys } from "./query-keys-DXle2tm8.mjs";
11
- import { t as useAccount } from "./use-account-BgustWSP.mjs";
11
+ import { t as useAccount } from "./use-account-BOE1yhAH.mjs";
12
12
  import { n as useAppNavigation } from "./AppNavigationContext-BcZZMtV6.mjs";
13
13
  import { n as createDomainTranslations, r as useDomainDict, t as createStaticDictAdapter } from "./static-dict-adapter-RqA1zYr0.mjs";
14
14
  import { a as AddressFormDialog, c as PaymentIcon, f as ConfirmActionDialog, h as useProfileTranslation, i as CreditCardFormDialog, l as getCardDisplayName, m as ProfileTranslationBridge, n as createFluidPayApiAdapter, o as EditPaymentMethodDialog, p as EllipsesDropdown, r as mapToFluidPayPaymentMethod, s as FluidPayCoreProvider, t as AddressAutocompleteInput, u as getCardExpiry } from "./AddressAutocompleteInput-o0eNkq7T.mjs";
15
15
  import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
16
16
  import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
17
17
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
18
- import { AlertCircle, CalendarDays, CalendarIcon, ChevronDown, ChevronLeft, ChevronRight, ImageOff, Minus, Pause, Play, Plus, Repeat, RotateCcw, SkipForward, XCircle } from "lucide-react";
18
+ import { AlertCircle, CalendarDays, CalendarIcon, Check, ChevronDown, ChevronLeft, ChevronRight, CreditCard, ImageOff, Minus, Package, Pause, Play, Plus, Repeat, RotateCcw, SkipForward, Truck, XCircle } from "lucide-react";
19
19
  import { clsx } from "clsx";
20
20
  import { twMerge } from "tailwind-merge";
21
21
  //#region ../../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/getDefaultOptions.js
@@ -2108,6 +2108,27 @@ const subscriptionsKeys = {
2108
2108
  token
2109
2109
  ]
2110
2110
  };
2111
+ const bundlesKeys = {
2112
+ all: ["subscription_bundles"],
2113
+ list: () => [...bundlesKeys.all, "list"]
2114
+ };
2115
+ //#endregion
2116
+ //#region ../../subscriptions/core/src/bundles-api-context.ts
2117
+ const BundlesApiContext = createContext(null);
2118
+ const BundlesApiProvider = BundlesApiContext.Provider;
2119
+ function useBundlesApi() {
2120
+ const api = useContext(BundlesApiContext);
2121
+ if (!api) throw new Error("useBundlesApi must be used within a BundlesCoreProvider");
2122
+ return api;
2123
+ }
2124
+ //#endregion
2125
+ //#region ../../subscriptions/core/src/bundles-provider.tsx
2126
+ function BundlesCoreProvider({ api, children }) {
2127
+ return /* @__PURE__ */ jsx(BundlesApiProvider, {
2128
+ value: api,
2129
+ children
2130
+ });
2131
+ }
2111
2132
  //#endregion
2112
2133
  //#region ../../subscriptions/core/src/hooks/use-subscriptions.ts
2113
2134
  function useSubscriptions(params, options) {
@@ -2233,6 +2254,31 @@ function useUpdateSubscriptionInfo(options) {
2233
2254
  });
2234
2255
  }
2235
2256
  //#endregion
2257
+ //#region ../../subscriptions/core/src/hooks/use-bundles.ts
2258
+ function useBundles(options) {
2259
+ const api = useBundlesApi();
2260
+ return useQuery({
2261
+ queryKey: bundlesKeys.list(),
2262
+ queryFn: () => api.fetchBundles(),
2263
+ enabled: options?.enabled ?? true
2264
+ });
2265
+ }
2266
+ //#endregion
2267
+ //#region ../../subscriptions/core/src/hooks/use-create-bundle.ts
2268
+ function useCreateBundle(options) {
2269
+ const api = useBundlesApi();
2270
+ const queryClient = useQueryClient();
2271
+ return useMutation({
2272
+ mutationFn: (params) => api.createBundle(params),
2273
+ onSuccess: (bundle) => {
2274
+ queryClient.invalidateQueries({ queryKey: bundlesKeys.all });
2275
+ queryClient.invalidateQueries({ queryKey: subscriptionsKeys.all });
2276
+ options?.onSuccess?.(bundle);
2277
+ },
2278
+ onError: options?.onError
2279
+ });
2280
+ }
2281
+ //#endregion
2236
2282
  //#region ../../subscriptions/core/src/utils/subscription-helpers.ts
2237
2283
  function formatSubscriptionFrequency(frequency) {
2238
2284
  if (frequency === "day") return "Daily";
@@ -2524,7 +2570,23 @@ function mapDetailSubscription(bff) {
2524
2570
  name: ""
2525
2571
  },
2526
2572
  subscription_skips: [],
2527
- orders: []
2573
+ orders: [],
2574
+ bundled_items: bff.bundled_items ? bff.bundled_items.map((item) => ({
2575
+ quantity: item.quantity ?? 0,
2576
+ product_id: item.product_id ?? 0,
2577
+ variant_id: item.variant_id ?? 0,
2578
+ option_titles: item.option_titles ?? [],
2579
+ product_title: item.product_title ?? "",
2580
+ variant_title: item.variant_title ?? "",
2581
+ display_to_customer: item.display_to_customer ?? true
2582
+ })) : void 0,
2583
+ bundled_subscriptions: bff.bundled_subscriptions ? bff.bundled_subscriptions.map((sib) => ({
2584
+ subscription_token: sib.subscription_token ?? "",
2585
+ product_title: sib.product_title ?? "",
2586
+ variant_title: sib.variant_title ?? null,
2587
+ price: sib.price ?? 0,
2588
+ image_url: sib.image_url ?? null
2589
+ })) : void 0
2528
2590
  };
2529
2591
  }
2530
2592
  function mapSubscriptionDetail(response) {
@@ -2568,12 +2630,12 @@ function mapListItem(bff) {
2568
2630
  quantity: bff.quantity ?? 1,
2569
2631
  status: bff.status ?? "",
2570
2632
  subscription_plan: {
2571
- id: 0,
2633
+ id: bff.plan?.id ?? 0,
2572
2634
  active: true,
2573
- billing_interval: 1,
2574
- billing_interval_unit: bff.interval ?? "",
2575
- shipping_interval: 1,
2576
- shipping_interval_unit: bff.interval ?? ""
2635
+ billing_interval: bff.plan?.billing_interval ?? 1,
2636
+ billing_interval_unit: bff.plan?.billing_interval_unit ?? "",
2637
+ shipping_interval: bff.plan?.billing_interval ?? 1,
2638
+ shipping_interval_unit: bff.plan?.billing_interval_unit ?? ""
2577
2639
  },
2578
2640
  variant: {
2579
2641
  id: bff.product?.id ?? 0,
@@ -2584,7 +2646,14 @@ function mapListItem(bff) {
2584
2646
  image_url: bff.product?.image_url ?? "",
2585
2647
  price_in_currency: bff.total ?? "0"
2586
2648
  }
2587
- }
2649
+ },
2650
+ bundled_subscriptions: (bff.bundled_subscriptions ?? []).map((b) => ({
2651
+ subscription_token: b.subscription_token ?? "",
2652
+ product_title: b.product_title ?? "",
2653
+ variant_title: b.variant_title ?? null,
2654
+ price: b.price ?? 0,
2655
+ image_url: b.image_url ?? null
2656
+ }))
2588
2657
  };
2589
2658
  }
2590
2659
  function mapSubscriptionList(response) {
@@ -2662,6 +2731,68 @@ function PortalSubscriptionsApiProvider({ children }) {
2662
2731
  });
2663
2732
  }
2664
2733
  //#endregion
2734
+ //#region src/adapters/bundles-api-adapter.ts
2735
+ function mapAddress(bff) {
2736
+ if (!bff) return null;
2737
+ return {
2738
+ id: bff.id ?? 0,
2739
+ name: bff.name ?? null,
2740
+ address1: bff.address1 ?? "",
2741
+ address2: bff.address2 ?? null,
2742
+ city: bff.city ?? "",
2743
+ state: bff.state ?? "",
2744
+ subdivision_code: null,
2745
+ postal_code: bff.postal_code ?? "",
2746
+ country_code: bff.country_code ?? null,
2747
+ default: false
2748
+ };
2749
+ }
2750
+ function mapVolumeRep(bff) {
2751
+ if (!bff) return null;
2752
+ return {
2753
+ id: bff.id ?? 0,
2754
+ email: bff.email ?? null,
2755
+ full_name: bff.full_name ?? null,
2756
+ image_url: bff.image_url ?? null
2757
+ };
2758
+ }
2759
+ function mapBundle(bff) {
2760
+ return {
2761
+ next_bill_date: bff.next_bill_date ?? null,
2762
+ subscription_count: bff.subscription_count ?? 0,
2763
+ address: mapAddress(bff.address),
2764
+ volume_rep: mapVolumeRep(bff.volume_rep),
2765
+ subscriptions: (bff.subscriptions ?? []).map((s) => mapDetailSubscription(s))
2766
+ };
2767
+ }
2768
+ function createPortalBundlesAdapter(client) {
2769
+ return {
2770
+ fetchBundles: async () => {
2771
+ return ((await subscription_bundles_list(client)).subscription_bundles ?? []).map(mapBundle);
2772
+ },
2773
+ createBundle: async (params) => {
2774
+ const bundle = {
2775
+ subscription_tokens: params.subscription_tokens,
2776
+ next_bill_date: params.next_bill_date
2777
+ };
2778
+ if (params.address_id !== void 0) bundle.address_id = params.address_id;
2779
+ if (params.volume_rep_id !== void 0) bundle.volume_rep_id = params.volume_rep_id;
2780
+ const bff = (await subscription_bundles_create(client, { bundle })).subscription_bundle;
2781
+ if (!bff) throw new Error("BFF returned a successful response but bundle data was missing");
2782
+ return mapBundle(bff);
2783
+ }
2784
+ };
2785
+ }
2786
+ //#endregion
2787
+ //#region src/subscriptions/PortalBundlesApiProvider.tsx
2788
+ function PortalBundlesApiProvider({ children }) {
2789
+ const client = usePortalTenantClient();
2790
+ return /* @__PURE__ */ jsx(BundlesCoreProvider, {
2791
+ api: useMemo(() => createPortalBundlesAdapter(client), [client]),
2792
+ children
2793
+ });
2794
+ }
2795
+ //#endregion
2665
2796
  //#region ../../subscriptions/core/src/translation-api-context.ts
2666
2797
  const { Provider: SubscriptionsProvider, useTranslation } = createTranslationContext("Subscriptions");
2667
2798
  const SubscriptionsTranslationProvider = SubscriptionsProvider;
@@ -2837,7 +2968,43 @@ const subscriptionsDomain = createDomainTranslations({
2837
2968
  list_empty_hint: "When you subscribe to a product, it'll show up here.",
2838
2969
  list_load_failed_title: "Couldn't load your subscriptions",
2839
2970
  list_load_failed_hint: "Something went wrong on our end. Please try again.",
2840
- list_try_again: "Try again"
2971
+ list_try_again: "Try again",
2972
+ list_bundle_cta: "Bundle",
2973
+ list_bundle_card_label: "Bundle · {{n}} subscriptions",
2974
+ list_bundle_more_count: "+{{n}} more",
2975
+ list_bundle_members_title: "Bundle members",
2976
+ bundle_subscriptions: "Bundle subscriptions",
2977
+ bundle_back: "Back",
2978
+ bundle_done: "Done",
2979
+ bundle_create: "Create bundle",
2980
+ bundle_step_select: "Select",
2981
+ bundle_step_date: "Date",
2982
+ bundle_step_address: "Address",
2983
+ bundle_step_payment: "Payment",
2984
+ bundle_and_save: "Bundle and save",
2985
+ bundle_select_subs: "Select 2 or more subscriptions to bundle them onto one bill date and shipment.",
2986
+ bundle_step1_message: "✨ Bundle and save on shipping",
2987
+ bundle_select_new_bill_date: "Select a new bill date",
2988
+ bundle_select_new_bill_date_description: "All bundled subscriptions will share this bill date.",
2989
+ bundle_year: "Year",
2990
+ bundle_month: "Month",
2991
+ bundle_day: "Day",
2992
+ bundle_step2_message: "📅 Choose when they ship together",
2993
+ bundle_select_address: "Select shipping address",
2994
+ bundle_select_address_description: "All bundled subscriptions will ship to this address.",
2995
+ bundle_selected_bill_date: "Selected bill date:",
2996
+ bundle_add_new_address: "Add new address",
2997
+ bundle_default_badge: "Default",
2998
+ bundle_step3_message: "🎁 Almost there — pick delivery",
2999
+ bundle_select_payment: "Select payment method",
3000
+ bundle_select_payment_description: "All bundled subscriptions will be charged to this payment method.",
3001
+ bundle_no_usable_payment_methods: "No usable payment methods. Add one in your profile.",
3002
+ bundle_card_expires: "Expires",
3003
+ bundle_add_new_card: "Add new card",
3004
+ bundle_step4_message: "💳 Last step — choose payment method",
3005
+ bundle_succeeded: "Subscriptions bundled successfully.",
3006
+ bundle_partial_payment_failure: "Bundle created but payment method failed for {{n}} subscription(s).",
3007
+ bundle_failed: "Couldn't bundle subscriptions. Please try again."
2841
3008
  },
2842
3009
  loaders: {
2843
3010
  de: () => import("./de-B_jSDYq8.mjs").then((m) => m.default),
@@ -2996,10 +3163,17 @@ function pricingSuffix(plan, t) {
2996
3163
  }
2997
3164
  function formatLongDate(iso) {
2998
3165
  try {
2999
- return new Date(iso).toLocaleDateString(void 0, {
3166
+ const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso);
3167
+ if (!match) return iso;
3168
+ const y = Number(match[1]);
3169
+ const m = Number(match[2]);
3170
+ const d = Number(match[3]);
3171
+ if (!y || !m || !d) return iso;
3172
+ return new Date(Date.UTC(y, m - 1, d)).toLocaleDateString(void 0, {
3000
3173
  month: "long",
3001
3174
  day: "numeric",
3002
- year: "numeric"
3175
+ year: "numeric",
3176
+ timeZone: "UTC"
3003
3177
  });
3004
3178
  } catch {
3005
3179
  return iso;
@@ -3016,6 +3190,17 @@ function formatPrice(sub) {
3016
3190
  return sub.price.toFixed(2);
3017
3191
  }
3018
3192
  /**
3193
+ * Mirror of `formatPrice` for the detail subscription shape used inside a
3194
+ * `Bundle.subscriptions[]`. The detail variant carries `price_in_currency`
3195
+ * directly (no nested `product` like the list variant), so this peels off
3196
+ * one less layer.
3197
+ */
3198
+ function formatDetailPrice(sub) {
3199
+ const fromBff = sub.variant?.price_in_currency;
3200
+ if (fromBff && fromBff.trim().length > 0) return fromBff;
3201
+ return sub.price.toFixed(2);
3202
+ }
3203
+ /**
3019
3204
  * Compute progress through a billing cycle. Returns `null` when either
3020
3205
  * date is missing, unparseable, or `end <= start`. `now` is injectable
3021
3206
  * for tests.
@@ -3047,7 +3232,8 @@ function friendlyDaysAhead(end, t, now = Date.now()) {
3047
3232
  }
3048
3233
  //#endregion
3049
3234
  //#region src/screens/subscriptions/PortalSubscriptionsList.tsx
3050
- function PortalSubscriptionsList({ customerId, isLoadingCustomer, onSubscriptionClick, onResume }) {
3235
+ const EMPTY_BUNDLES = [];
3236
+ function PortalSubscriptionsList({ customerId, isLoadingCustomer, onSubscriptionClick, onResume, onBundleClick, bundles = EMPTY_BUNDLES }) {
3051
3237
  const { t } = useSubscriptionsTranslation();
3052
3238
  const [filter, setFilter] = useState("all");
3053
3239
  const { data, isLoading, isError, refetch, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteSubscriptions({
@@ -3057,13 +3243,26 @@ function PortalSubscriptionsList({ customerId, isLoadingCustomer, onSubscription
3057
3243
  const list = useMemo(() => data?.pages.flatMap((p) => p.subscriptions) ?? [], [data]);
3058
3244
  const counts = useMemo(() => countByFilter(list), [list]);
3059
3245
  const filteredList = useMemo(() => list.filter((s) => filterPredicate(filter, s)), [list, filter]);
3246
+ const relevantBundles = useMemo(() => bundles.filter((b) => b.subscription_count > 1), [bundles]);
3247
+ const bundledTokens = useMemo(() => {
3248
+ const tokens = /* @__PURE__ */ new Set();
3249
+ for (const bundle of relevantBundles) for (const sub of bundle.subscriptions) if (sub.subscription_token) tokens.add(sub.subscription_token);
3250
+ return tokens;
3251
+ }, [relevantBundles]);
3252
+ const showBundleSection = filter === "all" && relevantBundles.length > 0;
3060
3253
  const { activeSubs, inactiveSubs } = useMemo(() => {
3061
3254
  const sorted = filteredList.slice().sort((a, b) => STATUS_PRIORITY[mapStatus(a.status, t).kind] - STATUS_PRIORITY[mapStatus(b.status, t).kind]);
3255
+ const activeCandidates = sorted.filter((s) => isActiveKind(mapStatus(s.status, t).kind));
3062
3256
  return {
3063
- activeSubs: sorted.filter((s) => isActiveKind(mapStatus(s.status, t).kind)),
3257
+ activeSubs: showBundleSection ? activeCandidates.filter((s) => !bundledTokens.has(s.subscription_token)) : activeCandidates,
3064
3258
  inactiveSubs: sorted.filter((s) => !isActiveKind(mapStatus(s.status, t).kind))
3065
3259
  };
3066
- }, [filteredList, t]);
3260
+ }, [
3261
+ filteredList,
3262
+ t,
3263
+ showBundleSection,
3264
+ bundledTokens
3265
+ ]);
3067
3266
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ScreenHeaderBreadcrumbs, { children: /* @__PURE__ */ jsx(Breadcrumb, { children: /* @__PURE__ */ jsx(BreadcrumbList, {
3068
3267
  className: "text-lg",
3069
3268
  children: /* @__PURE__ */ jsx(BreadcrumbItem, { children: /* @__PURE__ */ jsx(BreadcrumbPage, {
@@ -3083,6 +3282,7 @@ function PortalSubscriptionsList({ customerId, isLoadingCustomer, onSubscription
3083
3282
  active: filter,
3084
3283
  counts,
3085
3284
  onChange: setFilter,
3285
+ onBundleClick,
3086
3286
  t
3087
3287
  }),
3088
3288
  isLoading || isLoadingCustomer ? /* @__PURE__ */ jsx("div", {
@@ -3094,14 +3294,18 @@ function PortalSubscriptionsList({ customerId, isLoadingCustomer, onSubscription
3094
3294
  }) : filteredList.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
3095
3295
  filtered: filter !== "all" && list.length > 0,
3096
3296
  t
3097
- }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [activeSubs.length > 0 && /* @__PURE__ */ jsx("div", {
3297
+ }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [(showBundleSection || activeSubs.length > 0) && /* @__PURE__ */ jsxs("div", {
3098
3298
  className: "mt-6 grid grid-cols-1 gap-5 md:grid-cols-2",
3099
- children: activeSubs.map((sub) => /* @__PURE__ */ jsx(SubscriptionCard, {
3299
+ children: [showBundleSection && relevantBundles.map((bundle, i) => /* @__PURE__ */ jsx(BundleCard, {
3300
+ bundle,
3301
+ onSubscriptionClick,
3302
+ t
3303
+ }, `bundle-${i}-${bundle.next_bill_date}`)), activeSubs.map((sub) => /* @__PURE__ */ jsx(SubscriptionCard, {
3100
3304
  sub,
3101
3305
  onClick: () => onSubscriptionClick(sub.subscription_token),
3102
3306
  onResume: onResume ? () => onResume(sub) : void 0,
3103
3307
  t
3104
- }, sub.id))
3308
+ }, sub.id))]
3105
3309
  }), inactiveSubs.length > 0 && /* @__PURE__ */ jsxs("div", {
3106
3310
  className: activeSubs.length > 0 ? "mt-10" : "mt-6",
3107
3311
  children: [/* @__PURE__ */ jsx("div", {
@@ -3148,10 +3352,10 @@ const FILTER_KEYS = [
3148
3352
  labelKey: "list_filter_cancelled"
3149
3353
  }
3150
3354
  ];
3151
- function FilterPills({ active, counts, onChange, t }) {
3152
- return /* @__PURE__ */ jsx("div", {
3153
- className: "-mx-1 mt-5 flex flex-wrap gap-1.5 px-1",
3154
- children: FILTER_KEYS.map((f) => {
3355
+ function FilterPills({ active, counts, onChange, onBundleClick, t }) {
3356
+ return /* @__PURE__ */ jsxs("div", {
3357
+ className: "-mx-1 mt-5 flex flex-wrap items-center gap-1.5 px-1",
3358
+ children: [FILTER_KEYS.map((f) => {
3155
3359
  const isActive = f.key === active;
3156
3360
  const count = counts[f.key];
3157
3361
  return /* @__PURE__ */ jsxs("button", {
@@ -3163,8 +3367,153 @@ function FilterPills({ active, counts, onChange, t }) {
3163
3367
  children: count
3164
3368
  })]
3165
3369
  }, f.key);
3166
- })
3370
+ }), counts.all >= 2 && /* @__PURE__ */ jsx("button", {
3371
+ type: "button",
3372
+ onClick: onBundleClick,
3373
+ className: "bg-primary text-primary-foreground hover:bg-primary/90 ml-auto inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-bold transition-all",
3374
+ children: t("list_bundle_cta")
3375
+ })]
3376
+ });
3377
+ }
3378
+ const BUNDLE_INLINE_LIMIT = 2;
3379
+ function BundleCard({ bundle, onSubscriptionClick, t }) {
3380
+ const [popoverOpen, setPopoverOpen] = useState(false);
3381
+ const total = bundle.subscriptions.reduce((sum, s) => sum + s.price, 0);
3382
+ const nextBillLong = bundle.next_bill_date ? formatLongDate(bundle.next_bill_date) : null;
3383
+ const inlineMembers = bundle.subscriptions.slice(0, BUNDLE_INLINE_LIMIT);
3384
+ const overflowCount = bundle.subscriptions.length - inlineMembers.length;
3385
+ const handleMemberClick = (token) => {
3386
+ onSubscriptionClick(token);
3387
+ setPopoverOpen(false);
3388
+ };
3389
+ return /* @__PURE__ */ jsxs("div", {
3390
+ className: "ring-border hover:ring-foreground/10 relative flex flex-col gap-4 rounded-2xl bg-transparent p-5 shadow-sm ring-1 transition-all duration-300 hover:shadow-lg",
3391
+ children: [
3392
+ /* @__PURE__ */ jsxs("div", {
3393
+ className: "flex items-center gap-2",
3394
+ children: [/* @__PURE__ */ jsx("div", {
3395
+ className: "bg-muted flex size-7 items-center justify-center rounded-full",
3396
+ children: /* @__PURE__ */ jsx(Package, {
3397
+ className: "text-foreground size-4",
3398
+ strokeWidth: 2
3399
+ })
3400
+ }), /* @__PURE__ */ jsx("div", {
3401
+ className: "text-foreground text-xs font-bold",
3402
+ children: t("list_bundle_card_label", { n: String(bundle.subscription_count) })
3403
+ })]
3404
+ }),
3405
+ /* @__PURE__ */ jsxs("div", {
3406
+ className: "flex flex-col",
3407
+ children: [inlineMembers.map((sub) => /* @__PURE__ */ jsx(BundleMemberRow, {
3408
+ sub,
3409
+ onClick: () => handleMemberClick(sub.subscription_token),
3410
+ t
3411
+ }, sub.subscription_token)), overflowCount > 0 && /* @__PURE__ */ jsxs(Popover, {
3412
+ open: popoverOpen,
3413
+ onOpenChange: setPopoverOpen,
3414
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
3415
+ asChild: true,
3416
+ children: /* @__PURE__ */ jsx("button", {
3417
+ type: "button",
3418
+ className: "text-muted-foreground hover:text-foreground focus-visible:ring-foreground/30 mt-1 inline-flex w-fit cursor-pointer items-center rounded-md px-2 py-1 text-xs font-bold transition-colors focus-visible:ring-2 focus-visible:outline-none",
3419
+ children: t("list_bundle_more_count", { n: String(overflowCount) })
3420
+ })
3421
+ }), /* @__PURE__ */ jsxs(PopoverContent, {
3422
+ align: "start",
3423
+ className: "bg-background ring-border w-80 rounded-2xl p-3 shadow-lg ring-1",
3424
+ children: [/* @__PURE__ */ jsx("div", {
3425
+ className: "text-muted-foreground px-2 pt-1 pb-2 text-xs font-bold uppercase",
3426
+ children: t("list_bundle_members_title")
3427
+ }), /* @__PURE__ */ jsx("div", {
3428
+ className: "flex max-h-80 flex-col overflow-y-auto",
3429
+ children: bundle.subscriptions.map((sub) => /* @__PURE__ */ jsx(BundleMemberRow, {
3430
+ sub,
3431
+ onClick: () => handleMemberClick(sub.subscription_token),
3432
+ t
3433
+ }, sub.subscription_token))
3434
+ })]
3435
+ })]
3436
+ })]
3437
+ }),
3438
+ /* @__PURE__ */ jsxs("div", {
3439
+ className: "bg-muted mt-auto rounded-xl px-4 py-3.5",
3440
+ children: [/* @__PURE__ */ jsx("div", {
3441
+ className: "text-foreground text-sm font-bold",
3442
+ children: t("list_next_bill_label")
3443
+ }), /* @__PURE__ */ jsxs("div", {
3444
+ className: "mt-3 grid grid-cols-2",
3445
+ children: [/* @__PURE__ */ jsxs("div", {
3446
+ className: "pr-4",
3447
+ children: [/* @__PURE__ */ jsx("div", {
3448
+ className: "text-muted-foreground text-xs",
3449
+ children: t("list_bill_date")
3450
+ }), /* @__PURE__ */ jsx("div", {
3451
+ className: "text-foreground mt-1 text-base font-bold tracking-tight tabular-nums",
3452
+ children: nextBillLong ?? "—"
3453
+ })]
3454
+ }), /* @__PURE__ */ jsxs("div", {
3455
+ className: "border-border border-l pl-4",
3456
+ children: [/* @__PURE__ */ jsx("div", {
3457
+ className: "text-muted-foreground text-xs",
3458
+ children: t("list_bill_amount")
3459
+ }), /* @__PURE__ */ jsx("div", {
3460
+ className: "text-foreground mt-1 text-base font-bold tracking-tight tabular-nums",
3461
+ children: total.toFixed(2)
3462
+ })]
3463
+ })]
3464
+ })]
3465
+ })
3466
+ ]
3467
+ });
3468
+ }
3469
+ function BundleMemberRow({ sub, onClick, t }) {
3470
+ return /* @__PURE__ */ jsxs("button", {
3471
+ type: "button",
3472
+ onClick,
3473
+ className: "hover:bg-muted/60 focus-visible:ring-foreground/30 -mx-2 flex cursor-pointer items-center gap-3 rounded-lg px-2 py-2 text-left transition-colors focus-visible:ring-2 focus-visible:outline-none",
3474
+ children: [
3475
+ /* @__PURE__ */ jsx("div", {
3476
+ className: "relative size-12 shrink-0",
3477
+ children: sub.variant.product.image_url ? /* @__PURE__ */ jsx("img", {
3478
+ src: sub.variant.product.image_url,
3479
+ alt: "",
3480
+ className: "ring-background size-12 rounded-lg bg-transparent object-cover ring-2"
3481
+ }) : /* @__PURE__ */ jsx("div", {
3482
+ className: "bg-muted ring-background text-muted-foreground flex size-12 items-center justify-center rounded-lg ring-2",
3483
+ children: /* @__PURE__ */ jsx(ImageOff, {
3484
+ className: "size-4",
3485
+ strokeWidth: 1.75
3486
+ })
3487
+ })
3488
+ }),
3489
+ /* @__PURE__ */ jsxs("div", {
3490
+ className: "min-w-0 flex-1",
3491
+ children: [/* @__PURE__ */ jsx("div", {
3492
+ className: "text-foreground truncate text-xs font-bold",
3493
+ children: sub.variant.product.title || t("list_product_fallback")
3494
+ }), /* @__PURE__ */ jsx("div", {
3495
+ className: "text-muted-foreground truncate text-xs",
3496
+ children: bundleMemberCadence(sub.subscription_plan, t)
3497
+ })]
3498
+ }),
3499
+ /* @__PURE__ */ jsx("div", {
3500
+ className: "text-foreground shrink-0 text-xs font-bold tabular-nums",
3501
+ children: formatDetailPrice(sub)
3502
+ })
3503
+ ]
3504
+ });
3505
+ }
3506
+ function bundleMemberCadence(plan, t) {
3507
+ const { billing_interval: interval, billing_interval_unit: unit } = plan;
3508
+ if (interval === 1 && unit === "month") return t("list_cadence_monthly");
3509
+ if (interval === 1 && unit === "week") return t("list_cadence_weekly");
3510
+ if (interval === 1 && unit === "year") return t("list_cadence_yearly");
3511
+ if (interval === 3 && unit === "month") return t("list_cadence_quarterly");
3512
+ if (interval > 1) return t("list_cadence_every_n_units", {
3513
+ n: String(interval),
3514
+ unit: unit + "s"
3167
3515
  });
3516
+ return t("list_cadence_every_unit", { unit });
3168
3517
  }
3169
3518
  function SubscriptionCard({ sub, onClick, onResume, t }) {
3170
3519
  const status = mapStatus(sub.status, t);
@@ -3182,7 +3531,7 @@ function SubscriptionCard({ sub, onClick, onResume, t }) {
3182
3531
  children: [/* @__PURE__ */ jsxs("button", {
3183
3532
  type: "button",
3184
3533
  onClick,
3185
- className: "flex cursor-pointer flex-col gap-5 text-left focus:outline-none",
3534
+ className: "flex flex-1 cursor-pointer flex-col gap-5 text-left focus:outline-none",
3186
3535
  children: [
3187
3536
  /* @__PURE__ */ jsxs("div", {
3188
3537
  className: "flex items-start gap-4",
@@ -3209,10 +3558,10 @@ function SubscriptionCard({ sub, onClick, onResume, t }) {
3209
3558
  })]
3210
3559
  }),
3211
3560
  isCancelled ? /* @__PURE__ */ jsx("div", {
3212
- className: "bg-muted text-muted-foreground rounded-xl px-4 py-3 text-sm font-semibold",
3561
+ className: "bg-muted text-muted-foreground mt-auto rounded-xl px-4 py-3 text-sm font-semibold",
3213
3562
  children: status.label
3214
3563
  }) : /* @__PURE__ */ jsxs("div", {
3215
- className: "bg-muted rounded-xl px-4 py-3.5",
3564
+ className: "bg-muted mt-auto rounded-xl px-4 py-3.5",
3216
3565
  children: [/* @__PURE__ */ jsx("div", {
3217
3566
  className: `text-sm font-bold ${isPastDue ? "text-destructive" : "text-foreground"}`,
3218
3567
  children: isPastDue ? daysOverdue === 1 ? t("list_bill_due_1_day_ago") : daysOverdue > 0 ? t("list_bill_due_n_days_ago", { n: String(daysOverdue) }) : t("list_bill_overdue") : isPaused ? t("list_last_bill_label") : t("list_next_bill_label")
@@ -3384,15 +3733,972 @@ function ErrorState({ onRetry, t }) {
3384
3733
  });
3385
3734
  }
3386
3735
  //#endregion
3736
+ //#region src/screens/subscriptions/bundle-drawer/payment-utils.ts
3737
+ /**
3738
+ * Mirrors fluid-checkout's `isPaymentMethodUsable`. A payment method is only
3739
+ * eligible for bundle attachment when it has a complete billing address.
3740
+ *
3741
+ * Note: the portal's `fluidPay.CustomerPaymentMethod` does not surface the
3742
+ * checkout-only `au_status` field, so we only check billing-address completeness.
3743
+ */
3744
+ function isPaymentMethodUsable(pm) {
3745
+ const addr = pm.billing_address;
3746
+ if (!addr) return false;
3747
+ return Boolean(addr.address1 && addr.city && addr.state && addr.zip && addr.country_code);
3748
+ }
3749
+ //#endregion
3750
+ //#region src/screens/subscriptions/bundle-drawer/use-bundle-form.ts
3751
+ function pickDefaultAddressId(addresses, defaultAddressId) {
3752
+ if (defaultAddressId != null) {
3753
+ const match = addresses.find((a) => a.id === defaultAddressId);
3754
+ if (match) return match.id;
3755
+ }
3756
+ const flagged = addresses.find((a) => a.default);
3757
+ if (flagged) return flagged.id;
3758
+ return addresses[0]?.id ?? null;
3759
+ }
3760
+ function pickDefaultPaymentMethodId(paymentMethods, defaultPaymentMethodId) {
3761
+ const usable = paymentMethods.filter(isPaymentMethodUsable);
3762
+ if (defaultPaymentMethodId != null) {
3763
+ const match = usable.find((pm) => pm.id === defaultPaymentMethodId);
3764
+ if (match) return match.id;
3765
+ }
3766
+ const flagged = usable.find((pm) => pm.default);
3767
+ if (flagged) return flagged.id;
3768
+ return usable[0]?.id ?? null;
3769
+ }
3770
+ function useBundleForm({ addresses, paymentMethods, defaultAddressId, defaultPaymentMethodId }) {
3771
+ const [step, setStep] = useState(1);
3772
+ const [selectedTokens, setSelectedTokens] = useState(() => /* @__PURE__ */ new Set());
3773
+ const today = useMemo(() => /* @__PURE__ */ new Date(), []);
3774
+ const [selectedYear, setYearState] = useState(today.getUTCFullYear());
3775
+ const [selectedMonth, setMonthState] = useState(null);
3776
+ const [selectedDay, setDayState] = useState(null);
3777
+ const [selectedAddressId, setSelectedAddressId] = useState(() => pickDefaultAddressId(addresses, defaultAddressId));
3778
+ const [selectedPaymentMethodId, setSelectedPaymentMethodId] = useState(() => pickDefaultPaymentMethodId(paymentMethods, defaultPaymentMethodId));
3779
+ useEffect(() => {
3780
+ if (selectedAddressId == null && addresses.length > 0) setSelectedAddressId(pickDefaultAddressId(addresses, defaultAddressId));
3781
+ }, [
3782
+ selectedAddressId,
3783
+ addresses,
3784
+ defaultAddressId
3785
+ ]);
3786
+ useEffect(() => {
3787
+ if (selectedPaymentMethodId == null && paymentMethods.some(isPaymentMethodUsable)) setSelectedPaymentMethodId(pickDefaultPaymentMethodId(paymentMethods, defaultPaymentMethodId));
3788
+ }, [
3789
+ selectedPaymentMethodId,
3790
+ paymentMethods,
3791
+ defaultPaymentMethodId
3792
+ ]);
3793
+ return {
3794
+ step,
3795
+ setStep,
3796
+ selectedTokens,
3797
+ toggleToken: useCallback((token) => {
3798
+ setSelectedTokens((prev) => {
3799
+ const next = new Set(prev);
3800
+ if (next.has(token)) next.delete(token);
3801
+ else next.add(token);
3802
+ return next;
3803
+ });
3804
+ }, []),
3805
+ selectedYear,
3806
+ selectedMonth,
3807
+ selectedDay,
3808
+ setYear: useCallback((y) => {
3809
+ setYearState(y);
3810
+ if (selectedMonth != null) {
3811
+ const isPastMonth = y < today.getUTCFullYear() || y === today.getUTCFullYear() && selectedMonth < today.getUTCMonth();
3812
+ const isPastDay = y === today.getUTCFullYear() && selectedMonth === today.getUTCMonth() && selectedDay != null && selectedDay < today.getUTCDate();
3813
+ if (isPastMonth) {
3814
+ setMonthState(null);
3815
+ setDayState(null);
3816
+ } else if (isPastDay) setDayState(null);
3817
+ }
3818
+ }, [
3819
+ selectedMonth,
3820
+ selectedDay,
3821
+ today
3822
+ ]),
3823
+ setMonth: useCallback((m) => {
3824
+ setMonthState(m);
3825
+ if (selectedDay != null) {
3826
+ const daysInNewMonth = new Date(Date.UTC(selectedYear, m + 1, 0)).getUTCDate();
3827
+ const isPastDay = selectedYear === today.getUTCFullYear() && m === today.getUTCMonth() && selectedDay < today.getUTCDate();
3828
+ if (selectedDay > daysInNewMonth || isPastDay) setDayState(null);
3829
+ }
3830
+ }, [
3831
+ selectedDay,
3832
+ selectedYear,
3833
+ today
3834
+ ]),
3835
+ setDay: useCallback((d) => setDayState(d), []),
3836
+ selectedAddressId,
3837
+ setSelectedAddressId,
3838
+ selectedPaymentMethodId,
3839
+ setSelectedPaymentMethodId,
3840
+ canProceedFromStep1: selectedTokens.size >= 2,
3841
+ canProceedFromStep2: selectedMonth !== null && selectedDay !== null,
3842
+ canProceedFromStep3: selectedAddressId != null,
3843
+ canProceedFromStep4: selectedPaymentMethodId != null,
3844
+ formattedDate: useMemo(() => {
3845
+ if (selectedMonth == null || selectedDay == null) return null;
3846
+ return `${selectedYear}-${String(selectedMonth + 1).padStart(2, "0")}-${String(selectedDay).padStart(2, "0")}`;
3847
+ }, [
3848
+ selectedYear,
3849
+ selectedMonth,
3850
+ selectedDay
3851
+ ])
3852
+ };
3853
+ }
3854
+ //#endregion
3855
+ //#region src/screens/subscriptions/bundle-drawer/DrawerStepTabs.tsx
3856
+ const TABS = [
3857
+ {
3858
+ id: 1,
3859
+ labelKey: "bundle_step_select"
3860
+ },
3861
+ {
3862
+ id: 2,
3863
+ labelKey: "bundle_step_date"
3864
+ },
3865
+ {
3866
+ id: 3,
3867
+ labelKey: "bundle_step_address"
3868
+ },
3869
+ {
3870
+ id: 4,
3871
+ labelKey: "bundle_step_payment"
3872
+ }
3873
+ ];
3874
+ const PROGRESS_BY_STEP = {
3875
+ 1: "0%",
3876
+ 2: "33%",
3877
+ 3: "66%",
3878
+ 4: "100%"
3879
+ };
3880
+ function DrawerStepTabs({ step, onStepClick, canProceedFromStep1, canProceedFromStep2, canProceedFromStep3 }) {
3881
+ const { t } = useSubscriptionsTranslation();
3882
+ const handleClick = (target) => {
3883
+ if (target < step) onStepClick(target);
3884
+ else if (target === 2 && canProceedFromStep1) onStepClick(2);
3885
+ else if (target === 3 && canProceedFromStep1 && canProceedFromStep2) onStepClick(3);
3886
+ else if (target === 4 && canProceedFromStep1 && canProceedFromStep2 && canProceedFromStep3) onStepClick(4);
3887
+ };
3888
+ const messageKey = step === 1 ? "bundle_step1_message" : step === 2 ? "bundle_step2_message" : step === 3 ? "bundle_step3_message" : "bundle_step4_message";
3889
+ return /* @__PURE__ */ jsxs("div", {
3890
+ className: "px-6 pt-4",
3891
+ children: [
3892
+ /* @__PURE__ */ jsx("div", {
3893
+ className: "mb-2 flex items-center justify-between",
3894
+ children: TABS.map((tab) => /* @__PURE__ */ jsx("button", {
3895
+ type: "button",
3896
+ onClick: () => handleClick(tab.id),
3897
+ className: `text-xs font-bold transition-colors ${step === tab.id ? "text-foreground" : step > tab.id ? "text-muted-foreground cursor-pointer" : "text-muted-foreground/75"}`,
3898
+ children: t(tab.labelKey)
3899
+ }, tab.id))
3900
+ }),
3901
+ /* @__PURE__ */ jsxs("div", {
3902
+ className: "bg-muted relative h-1 rounded-full",
3903
+ children: [/* @__PURE__ */ jsx("div", {
3904
+ className: "bg-primary absolute h-1 rounded-full transition-all duration-300",
3905
+ style: { width: PROGRESS_BY_STEP[step] }
3906
+ }), TABS.map((tab, index) => /* @__PURE__ */ jsx("div", {
3907
+ className: `absolute top-1/2 size-3 rounded-full transition-colors ${step >= tab.id ? "bg-primary" : "bg-muted ring-border ring-1"}`,
3908
+ style: index === 0 ? {
3909
+ left: 0,
3910
+ transform: "translateY(-50%)"
3911
+ } : index === TABS.length - 1 ? {
3912
+ right: 0,
3913
+ transform: "translateY(-50%)"
3914
+ } : {
3915
+ left: `${index / (TABS.length - 1) * 100}%`,
3916
+ transform: "translate(-50%, -50%)"
3917
+ }
3918
+ }, tab.id))]
3919
+ }),
3920
+ /* @__PURE__ */ jsx("div", {
3921
+ className: "text-muted-foreground mt-3 flex items-center justify-center text-xs",
3922
+ children: /* @__PURE__ */ jsx("span", { children: t(messageKey) })
3923
+ })
3924
+ ]
3925
+ });
3926
+ }
3927
+ //#endregion
3928
+ //#region src/screens/subscriptions/bundle-drawer/DrawerStepSelect.tsx
3929
+ function formatBillDate(value) {
3930
+ if (!value) return "—";
3931
+ try {
3932
+ const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(value);
3933
+ if (!match) return "—";
3934
+ const y = Number(match[1]);
3935
+ const m = Number(match[2]);
3936
+ const d = Number(match[3]);
3937
+ if (!y || !m || !d) return "—";
3938
+ return new Date(Date.UTC(y, m - 1, d)).toLocaleDateString(void 0, {
3939
+ month: "short",
3940
+ day: "numeric",
3941
+ timeZone: "UTC"
3942
+ });
3943
+ } catch {
3944
+ return "—";
3945
+ }
3946
+ }
3947
+ function DrawerStepSelect({ subscriptions, selectedTokens, onToggle }) {
3948
+ const { t } = useSubscriptionsTranslation();
3949
+ return /* @__PURE__ */ jsxs("div", {
3950
+ className: "flex-1 overflow-y-auto px-6 py-4",
3951
+ children: [/* @__PURE__ */ jsxs("div", {
3952
+ className: "bg-muted mt-2 rounded-2xl p-6 text-center",
3953
+ children: [
3954
+ /* @__PURE__ */ jsx("div", {
3955
+ className: "mx-auto mb-3 flex size-14 items-center justify-center rounded-2xl bg-transparent shadow-sm",
3956
+ children: /* @__PURE__ */ jsx(Truck, {
3957
+ className: "text-foreground size-7",
3958
+ strokeWidth: 2
3959
+ })
3960
+ }),
3961
+ /* @__PURE__ */ jsx("h3", {
3962
+ className: "text-foreground text-sm font-bold",
3963
+ children: t("bundle_and_save")
3964
+ }),
3965
+ /* @__PURE__ */ jsx("p", {
3966
+ className: "text-muted-foreground mt-1 text-xs",
3967
+ children: t("bundle_select_subs")
3968
+ })
3969
+ ]
3970
+ }), /* @__PURE__ */ jsx("div", {
3971
+ className: "mt-4 space-y-2",
3972
+ children: subscriptions.map((sub) => {
3973
+ const isSelected = selectedTokens.has(sub.subscription_token);
3974
+ return /* @__PURE__ */ jsxs("div", {
3975
+ role: "checkbox",
3976
+ "aria-checked": isSelected,
3977
+ tabIndex: 0,
3978
+ onClick: () => onToggle(sub.subscription_token),
3979
+ onKeyDown: (e) => {
3980
+ if (e.key === "Enter" || e.key === " ") {
3981
+ e.preventDefault();
3982
+ onToggle(sub.subscription_token);
3983
+ }
3984
+ },
3985
+ className: `flex cursor-pointer items-start gap-3 rounded-2xl p-4 transition-all ${isSelected ? "ring-primary bg-muted ring-2" : "ring-border hover:ring-foreground/20 ring-1"}`,
3986
+ children: [
3987
+ /* @__PURE__ */ jsx("div", {
3988
+ className: "pt-1",
3989
+ children: /* @__PURE__ */ jsx("div", {
3990
+ className: `flex size-5 items-center justify-center rounded-full transition-colors ${isSelected ? "bg-primary" : "ring-foreground/30 ring-2"}`,
3991
+ children: isSelected && /* @__PURE__ */ jsx(Check, {
3992
+ className: "text-primary-foreground size-3",
3993
+ strokeWidth: 3
3994
+ })
3995
+ })
3996
+ }),
3997
+ sub.variant?.product?.image_url ? /* @__PURE__ */ jsx("img", {
3998
+ src: sub.variant.product.image_url,
3999
+ alt: sub.variant.product.title ?? "",
4000
+ className: "size-12 shrink-0 rounded-lg object-cover"
4001
+ }) : /* @__PURE__ */ jsx("div", {
4002
+ className: "bg-muted flex size-12 shrink-0 items-center justify-center rounded-lg",
4003
+ children: /* @__PURE__ */ jsx(Package, {
4004
+ className: "text-muted-foreground size-6",
4005
+ strokeWidth: 1.75
4006
+ })
4007
+ }),
4008
+ /* @__PURE__ */ jsxs("div", {
4009
+ className: "min-w-0 flex-1",
4010
+ children: [
4011
+ /* @__PURE__ */ jsx("p", {
4012
+ className: "text-foreground truncate text-xs font-bold",
4013
+ children: sub.variant?.product?.title ?? t("list_product_fallback")
4014
+ }),
4015
+ /* @__PURE__ */ jsx("p", {
4016
+ className: "text-foreground mt-0.5 text-xs",
4017
+ children: formatPrice(sub)
4018
+ }),
4019
+ sub.customer?.full_name && /* @__PURE__ */ jsx("p", {
4020
+ className: "text-muted-foreground mt-0.5 truncate text-xs",
4021
+ children: sub.customer.full_name
4022
+ })
4023
+ ]
4024
+ }),
4025
+ /* @__PURE__ */ jsxs("div", {
4026
+ className: "shrink-0 text-right",
4027
+ children: [/* @__PURE__ */ jsxs("div", {
4028
+ className: "text-muted-foreground inline-flex items-center gap-1 text-xs",
4029
+ children: [/* @__PURE__ */ jsx(Repeat, {
4030
+ className: "size-3",
4031
+ strokeWidth: 2
4032
+ }), formatCadence(sub.subscription_plan, t)]
4033
+ }), /* @__PURE__ */ jsx("p", {
4034
+ className: "text-muted-foreground/75 mt-1 text-xs",
4035
+ children: formatBillDate(sub.next_bill_date)
4036
+ })]
4037
+ })
4038
+ ]
4039
+ }, sub.subscription_token);
4040
+ })
4041
+ })]
4042
+ });
4043
+ }
4044
+ //#endregion
4045
+ //#region src/screens/subscriptions/bundle-drawer/DrawerStepDate.tsx
4046
+ const MONTHS = [
4047
+ "Jan",
4048
+ "Feb",
4049
+ "Mar",
4050
+ "Apr",
4051
+ "May",
4052
+ "Jun",
4053
+ "Jul",
4054
+ "Aug",
4055
+ "Sep",
4056
+ "Oct",
4057
+ "Nov",
4058
+ "Dec"
4059
+ ];
4060
+ function getDaysInMonth(year, month) {
4061
+ return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
4062
+ }
4063
+ function DrawerStepDate({ selectedYear, selectedMonth, selectedDay, onYearChange, onMonthChange, onDayChange }) {
4064
+ const { t } = useSubscriptionsTranslation();
4065
+ const today = useMemo(() => /* @__PURE__ */ new Date(), []);
4066
+ const currentYear = today.getUTCFullYear();
4067
+ const currentMonth = today.getUTCMonth();
4068
+ const currentDay = today.getUTCDate();
4069
+ const availableYears = useMemo(() => [
4070
+ currentYear,
4071
+ currentYear + 1,
4072
+ currentYear + 2
4073
+ ], [currentYear]);
4074
+ const isMonthDisabled = (monthIndex) => {
4075
+ if (selectedYear < currentYear) return true;
4076
+ if (selectedYear > currentYear) return false;
4077
+ return monthIndex < currentMonth;
4078
+ };
4079
+ const daysInSelectedMonth = selectedMonth == null ? 31 : getDaysInMonth(selectedYear, selectedMonth);
4080
+ const isDayDisabled = (day) => {
4081
+ if (selectedMonth == null) return true;
4082
+ if (day > getDaysInMonth(selectedYear, selectedMonth)) return true;
4083
+ if (selectedYear < currentYear) return true;
4084
+ if (selectedYear > currentYear) return false;
4085
+ if (selectedMonth > currentMonth) return false;
4086
+ if (selectedMonth < currentMonth) return true;
4087
+ return day < currentDay;
4088
+ };
4089
+ return /* @__PURE__ */ jsxs("div", {
4090
+ className: "flex-1 overflow-y-auto px-6 py-4",
4091
+ children: [
4092
+ /* @__PURE__ */ jsxs("div", {
4093
+ className: "mt-2",
4094
+ children: [/* @__PURE__ */ jsx("h3", {
4095
+ className: "text-foreground text-sm font-bold",
4096
+ children: t("bundle_select_new_bill_date")
4097
+ }), /* @__PURE__ */ jsx("p", {
4098
+ className: "text-muted-foreground mt-1 text-xs",
4099
+ children: t("bundle_select_new_bill_date_description")
4100
+ })]
4101
+ }),
4102
+ /* @__PURE__ */ jsxs("div", {
4103
+ className: "mt-6",
4104
+ children: [/* @__PURE__ */ jsx("h4", {
4105
+ className: "text-foreground mb-3 text-xs font-bold",
4106
+ children: t("bundle_year")
4107
+ }), /* @__PURE__ */ jsx("div", {
4108
+ className: "grid grid-cols-3 gap-2",
4109
+ children: availableYears.map((year) => {
4110
+ return /* @__PURE__ */ jsx("button", {
4111
+ type: "button",
4112
+ onClick: () => onYearChange(year),
4113
+ 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"}`,
4114
+ children: year
4115
+ }, year);
4116
+ })
4117
+ })]
4118
+ }),
4119
+ /* @__PURE__ */ jsxs("div", {
4120
+ className: "mt-6",
4121
+ children: [/* @__PURE__ */ jsx("h4", {
4122
+ className: "text-foreground mb-3 text-xs font-bold",
4123
+ children: t("bundle_month")
4124
+ }), /* @__PURE__ */ jsx("div", {
4125
+ className: "grid grid-cols-4 gap-2",
4126
+ children: MONTHS.map((month, index) => {
4127
+ const isActive = selectedMonth === index;
4128
+ const disabled = isMonthDisabled(index);
4129
+ return /* @__PURE__ */ jsx("button", {
4130
+ type: "button",
4131
+ disabled,
4132
+ onClick: () => onMonthChange(index),
4133
+ 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" : ""}`,
4134
+ children: month
4135
+ }, month);
4136
+ })
4137
+ })]
4138
+ }),
4139
+ /* @__PURE__ */ jsxs("div", {
4140
+ className: "mt-6",
4141
+ children: [/* @__PURE__ */ jsx("h4", {
4142
+ className: "text-foreground mb-3 text-xs font-bold",
4143
+ children: t("bundle_day")
4144
+ }), /* @__PURE__ */ jsx("div", {
4145
+ className: "grid grid-cols-8 gap-1.5",
4146
+ children: Array.from({ length: daysInSelectedMonth }, (_, i) => i + 1).map((day) => {
4147
+ const isActive = selectedDay === day;
4148
+ const disabled = isDayDisabled(day);
4149
+ return /* @__PURE__ */ jsx("button", {
4150
+ type: "button",
4151
+ disabled,
4152
+ onClick: () => onDayChange(day),
4153
+ className: `rounded-lg py-2 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" : ""}`,
4154
+ children: day
4155
+ }, day);
4156
+ })
4157
+ })]
4158
+ })
4159
+ ]
4160
+ });
4161
+ }
4162
+ //#endregion
4163
+ //#region src/screens/subscriptions/bundle-drawer/DrawerStepAddress.tsx
4164
+ function DrawerStepAddress({ addresses, selectedAddressId, onAddressSelect, onAddAddress, formattedSelectedDate }) {
4165
+ const { t } = useSubscriptionsTranslation();
4166
+ const hasFlaggedDefault = addresses.some((a) => a.default);
4167
+ return /* @__PURE__ */ jsxs("div", {
4168
+ className: "flex-1 overflow-y-auto px-6 py-4",
4169
+ children: [
4170
+ /* @__PURE__ */ jsxs("div", {
4171
+ className: "mt-2",
4172
+ children: [/* @__PURE__ */ jsx("h3", {
4173
+ className: "text-foreground text-sm font-bold",
4174
+ children: t("bundle_select_address")
4175
+ }), /* @__PURE__ */ jsx("p", {
4176
+ className: "text-muted-foreground mt-1 text-xs",
4177
+ children: t("bundle_select_address_description")
4178
+ })]
4179
+ }),
4180
+ formattedSelectedDate && /* @__PURE__ */ jsxs("div", {
4181
+ className: "bg-muted text-muted-foreground mt-4 rounded-lg px-4 py-2.5 text-xs",
4182
+ children: [
4183
+ t("bundle_selected_bill_date"),
4184
+ " ",
4185
+ /* @__PURE__ */ jsx("span", {
4186
+ className: "text-foreground font-semibold",
4187
+ children: formatLongDate(formattedSelectedDate)
4188
+ })
4189
+ ]
4190
+ }),
4191
+ /* @__PURE__ */ jsx("div", {
4192
+ className: "mt-4 space-y-2",
4193
+ role: "radiogroup",
4194
+ children: addresses.map((address, index) => {
4195
+ const isSelected = selectedAddressId === address.id;
4196
+ const isDefault = address.default || !hasFlaggedDefault && index === 0;
4197
+ return /* @__PURE__ */ jsxs("button", {
4198
+ type: "button",
4199
+ role: "radio",
4200
+ "aria-checked": isSelected,
4201
+ onClick: () => onAddressSelect(address.id),
4202
+ className: `flex h-auto w-full cursor-pointer items-start gap-3 rounded-2xl p-4 text-left transition-all ${isSelected ? "ring-primary bg-muted ring-2" : "ring-border hover:ring-foreground/20 ring-1"}`,
4203
+ children: [/* @__PURE__ */ jsx("div", {
4204
+ className: "pt-0.5",
4205
+ children: /* @__PURE__ */ jsx("div", {
4206
+ className: `flex size-5 items-center justify-center rounded-full transition-colors ${isSelected ? "bg-primary" : "ring-foreground/30 ring-2"}`,
4207
+ children: isSelected && /* @__PURE__ */ jsx(Check, {
4208
+ className: "text-primary-foreground size-3",
4209
+ strokeWidth: 3
4210
+ })
4211
+ })
4212
+ }), /* @__PURE__ */ jsxs("div", {
4213
+ className: "min-w-0 flex-1",
4214
+ children: [
4215
+ /* @__PURE__ */ jsxs("div", {
4216
+ className: "flex flex-wrap items-center gap-2",
4217
+ children: [address.name && /* @__PURE__ */ jsx("p", {
4218
+ className: "text-foreground truncate text-xs font-bold",
4219
+ children: address.name
4220
+ }), isDefault && /* @__PURE__ */ jsx("span", {
4221
+ className: "text-foreground ring-border rounded-full bg-transparent px-2 py-0.5 text-xs font-bold ring-1",
4222
+ children: t("bundle_default_badge")
4223
+ })]
4224
+ }),
4225
+ /* @__PURE__ */ jsx("p", {
4226
+ className: "text-muted-foreground mt-0.5 truncate text-xs",
4227
+ children: address.address1
4228
+ }),
4229
+ /* @__PURE__ */ jsxs("p", {
4230
+ className: "text-muted-foreground truncate text-xs",
4231
+ children: [
4232
+ [address.city, address.state].filter(Boolean).join(", "),
4233
+ " ",
4234
+ address.postal_code
4235
+ ]
4236
+ })
4237
+ ]
4238
+ })]
4239
+ }, address.id);
4240
+ })
4241
+ }),
4242
+ /* @__PURE__ */ jsx("div", {
4243
+ className: "mt-4 text-right",
4244
+ children: /* @__PURE__ */ jsx("button", {
4245
+ type: "button",
4246
+ onClick: onAddAddress,
4247
+ className: "text-foreground inline-flex items-center text-xs font-bold underline-offset-2 hover:underline",
4248
+ children: t("bundle_add_new_address")
4249
+ })
4250
+ })
4251
+ ]
4252
+ });
4253
+ }
4254
+ //#endregion
4255
+ //#region src/screens/subscriptions/bundle-drawer/DrawerStepPayment.tsx
4256
+ function DrawerStepPayment({ paymentMethods, selectedPaymentMethodId, onPaymentMethodSelect, onAddPaymentMethod }) {
4257
+ const { t } = useSubscriptionsTranslation();
4258
+ const usable = useMemo(() => {
4259
+ const filtered = paymentMethods.filter(isPaymentMethodUsable);
4260
+ const defaultIdx = filtered.findIndex((pm) => pm.default);
4261
+ if (defaultIdx <= 0) return filtered;
4262
+ const defaultCard = filtered[defaultIdx];
4263
+ if (!defaultCard) return filtered;
4264
+ return [defaultCard, ...filtered.filter((_, i) => i !== defaultIdx)];
4265
+ }, [paymentMethods]);
4266
+ return /* @__PURE__ */ jsxs("div", {
4267
+ className: "flex-1 overflow-y-auto px-6 py-4",
4268
+ children: [
4269
+ /* @__PURE__ */ jsxs("div", {
4270
+ className: "mt-2",
4271
+ children: [/* @__PURE__ */ jsx("h3", {
4272
+ className: "text-foreground text-sm font-bold",
4273
+ children: t("bundle_select_payment")
4274
+ }), /* @__PURE__ */ jsx("p", {
4275
+ className: "text-muted-foreground mt-1 text-xs",
4276
+ children: t("bundle_select_payment_description")
4277
+ })]
4278
+ }),
4279
+ usable.length === 0 && /* @__PURE__ */ jsx("div", {
4280
+ className: "bg-muted text-muted-foreground mt-4 rounded-lg px-4 py-3 text-xs",
4281
+ children: t("bundle_no_usable_payment_methods")
4282
+ }),
4283
+ /* @__PURE__ */ jsx("div", {
4284
+ className: "mt-4 space-y-2",
4285
+ role: "radiogroup",
4286
+ children: usable.map((pm) => {
4287
+ const isSelected = selectedPaymentMethodId === pm.id;
4288
+ const brand = pm.details.card_brand ?? pm.details.card_type ?? pm.payment_type ?? "Card";
4289
+ const lastFour = pm.details.last_four ?? "••••";
4290
+ const expiry = pm.details.exp_month != null && pm.details.exp_year != null ? `${t("bundle_card_expires")} ${pm.details.exp_month}/${pm.details.exp_year}` : null;
4291
+ return /* @__PURE__ */ jsxs("button", {
4292
+ type: "button",
4293
+ role: "radio",
4294
+ "aria-checked": isSelected,
4295
+ onClick: () => onPaymentMethodSelect(pm.id),
4296
+ className: `flex h-auto w-full cursor-pointer items-start gap-3 rounded-2xl p-4 text-left transition-all ${isSelected ? "ring-primary bg-muted ring-2" : "ring-border hover:ring-foreground/20 ring-1"}`,
4297
+ children: [
4298
+ /* @__PURE__ */ jsx("div", {
4299
+ className: "pt-0.5",
4300
+ children: /* @__PURE__ */ jsx("div", {
4301
+ className: `flex size-5 items-center justify-center rounded-full transition-colors ${isSelected ? "bg-primary" : "ring-foreground/30 ring-2"}`,
4302
+ children: isSelected && /* @__PURE__ */ jsx(Check, {
4303
+ className: "text-primary-foreground size-3",
4304
+ strokeWidth: 3
4305
+ })
4306
+ })
4307
+ }),
4308
+ pm.details.logo_url ? /* @__PURE__ */ jsx("img", {
4309
+ src: pm.details.logo_url,
4310
+ alt: brand,
4311
+ className: "h-6 w-10 shrink-0 object-contain"
4312
+ }) : /* @__PURE__ */ jsx("div", {
4313
+ className: "bg-muted text-muted-foreground flex h-6 w-10 shrink-0 items-center justify-center rounded",
4314
+ children: /* @__PURE__ */ jsx(CreditCard, {
4315
+ className: "size-4",
4316
+ strokeWidth: 1.75
4317
+ })
4318
+ }),
4319
+ /* @__PURE__ */ jsxs("div", {
4320
+ className: "min-w-0 flex-1",
4321
+ children: [/* @__PURE__ */ jsxs("div", {
4322
+ className: "flex flex-wrap items-center gap-2",
4323
+ children: [/* @__PURE__ */ jsxs("p", {
4324
+ className: "text-foreground text-xs font-bold",
4325
+ children: [
4326
+ brand,
4327
+ " •••• ",
4328
+ lastFour
4329
+ ]
4330
+ }), pm.default && /* @__PURE__ */ jsx("span", {
4331
+ className: "text-foreground ring-border rounded-full bg-transparent px-2 py-0.5 text-xs font-bold ring-1",
4332
+ children: t("bundle_default_badge")
4333
+ })]
4334
+ }), expiry && /* @__PURE__ */ jsx("p", {
4335
+ className: "text-muted-foreground mt-0.5 text-xs",
4336
+ children: expiry
4337
+ })]
4338
+ })
4339
+ ]
4340
+ }, pm.id);
4341
+ })
4342
+ }),
4343
+ /* @__PURE__ */ jsx("div", {
4344
+ className: "mt-4 text-right",
4345
+ children: /* @__PURE__ */ jsx("button", {
4346
+ type: "button",
4347
+ onClick: onAddPaymentMethod,
4348
+ className: "text-foreground inline-flex items-center text-xs font-bold underline-offset-2 hover:underline",
4349
+ children: t("bundle_add_new_card")
4350
+ })
4351
+ })
4352
+ ]
4353
+ });
4354
+ }
4355
+ //#endregion
4356
+ //#region src/screens/subscriptions/bundle-drawer/BundleSubscriptionsDrawer.tsx
4357
+ function BundleSubscriptionsDrawer({ open, onOpenChange, subscriptions: subscriptionList, addresses, paymentMethods, defaultAddressId, defaultPaymentMethodId, onAddAddress, onAddPaymentMethod, onSubmit, isSubmitting }) {
4358
+ const { t } = useSubscriptionsTranslation();
4359
+ const form = useBundleForm({
4360
+ addresses,
4361
+ paymentMethods,
4362
+ defaultAddressId,
4363
+ defaultPaymentMethodId
4364
+ });
4365
+ const handleClose = () => onOpenChange(false);
4366
+ const handleNext = () => {
4367
+ if (form.step < 4) form.setStep(form.step + 1);
4368
+ };
4369
+ const handleBack = () => {
4370
+ if (form.step === 1) handleClose();
4371
+ else form.setStep(form.step - 1);
4372
+ };
4373
+ const handleSubmit = async () => {
4374
+ if (!form.canProceedFromStep1 || !form.canProceedFromStep2 || !form.canProceedFromStep3 || !form.canProceedFromStep4 || form.formattedDate == null || form.selectedAddressId == null || form.selectedPaymentMethodId == null) return;
4375
+ await onSubmit({
4376
+ subscription_tokens: Array.from(form.selectedTokens),
4377
+ next_bill_date: form.formattedDate,
4378
+ address_id: form.selectedAddressId,
4379
+ payment_method_id: form.selectedPaymentMethodId
4380
+ });
4381
+ };
4382
+ const nextDisabled = form.step === 1 && !form.canProceedFromStep1 || form.step === 2 && !form.canProceedFromStep2 || form.step === 3 && !form.canProceedFromStep3;
4383
+ return /* @__PURE__ */ jsx(Sheet, {
4384
+ open,
4385
+ onOpenChange,
4386
+ children: /* @__PURE__ */ jsxs(SheetContent, {
4387
+ side: "right",
4388
+ className: "bg-background flex h-full w-full flex-col gap-0 p-0 sm:max-w-md",
4389
+ children: [
4390
+ /* @__PURE__ */ jsx("div", {
4391
+ className: "border-border flex items-center justify-between border-b px-6 py-4",
4392
+ children: /* @__PURE__ */ jsx(SheetTitle, {
4393
+ className: "text-foreground text-base font-bold",
4394
+ children: t("bundle_subscriptions")
4395
+ })
4396
+ }),
4397
+ /* @__PURE__ */ jsx(DrawerStepTabs, {
4398
+ step: form.step,
4399
+ onStepClick: form.setStep,
4400
+ canProceedFromStep1: form.canProceedFromStep1,
4401
+ canProceedFromStep2: form.canProceedFromStep2,
4402
+ canProceedFromStep3: form.canProceedFromStep3
4403
+ }),
4404
+ form.step === 1 && /* @__PURE__ */ jsx(DrawerStepSelect, {
4405
+ subscriptions: subscriptionList,
4406
+ selectedTokens: form.selectedTokens,
4407
+ onToggle: form.toggleToken
4408
+ }),
4409
+ form.step === 2 && /* @__PURE__ */ jsx(DrawerStepDate, {
4410
+ selectedYear: form.selectedYear,
4411
+ selectedMonth: form.selectedMonth,
4412
+ selectedDay: form.selectedDay,
4413
+ onYearChange: form.setYear,
4414
+ onMonthChange: form.setMonth,
4415
+ onDayChange: form.setDay
4416
+ }),
4417
+ form.step === 3 && /* @__PURE__ */ jsx(DrawerStepAddress, {
4418
+ addresses,
4419
+ selectedAddressId: form.selectedAddressId,
4420
+ onAddressSelect: form.setSelectedAddressId,
4421
+ onAddAddress,
4422
+ formattedSelectedDate: form.formattedDate
4423
+ }),
4424
+ form.step === 4 && /* @__PURE__ */ jsx(DrawerStepPayment, {
4425
+ paymentMethods,
4426
+ selectedPaymentMethodId: form.selectedPaymentMethodId,
4427
+ onPaymentMethodSelect: form.setSelectedPaymentMethodId,
4428
+ onAddPaymentMethod
4429
+ }),
4430
+ /* @__PURE__ */ jsxs("div", {
4431
+ className: "border-border flex items-center justify-between gap-3 border-t px-6 py-4",
4432
+ children: [/* @__PURE__ */ jsx("button", {
4433
+ type: "button",
4434
+ onClick: handleBack,
4435
+ className: "text-foreground ring-border hover:bg-muted rounded-lg bg-transparent px-6 py-2.5 text-xs font-bold ring-1 transition-all",
4436
+ children: form.step === 1 ? t("bundle_done") : t("bundle_back")
4437
+ }), form.step < 4 ? /* @__PURE__ */ jsx("button", {
4438
+ type: "button",
4439
+ onClick: handleNext,
4440
+ disabled: nextDisabled,
4441
+ className: "bg-primary text-primary-foreground hover:bg-primary/90 rounded-lg px-6 py-2.5 text-xs font-bold transition-all disabled:cursor-not-allowed disabled:opacity-40",
4442
+ children: t("next")
4443
+ }) : /* @__PURE__ */ jsx("button", {
4444
+ type: "button",
4445
+ onClick: handleSubmit,
4446
+ disabled: !form.canProceedFromStep4 || isSubmitting,
4447
+ className: "bg-primary text-primary-foreground hover:bg-primary/90 inline-flex min-w-[140px] items-center justify-center rounded-lg px-6 py-2.5 text-xs font-bold transition-all disabled:cursor-not-allowed disabled:opacity-40",
4448
+ children: isSubmitting ? /* @__PURE__ */ jsx("span", { className: "border-primary-foreground size-5 animate-spin rounded-full border-2 border-t-transparent" }) : t("bundle_create")
4449
+ })]
4450
+ })
4451
+ ]
4452
+ })
4453
+ });
4454
+ }
4455
+ //#endregion
3387
4456
  //#region src/screens/SubscriptionsListScreen.tsx
3388
- function SubscriptionsListScreen({ customerId, isLoadingCustomer }) {
4457
+ const ADDRESS_DIALOG_LABELS = {
4458
+ add_an_address: "Add an Address",
4459
+ add_address: "Add Address",
4460
+ edit_address: "Edit Address",
4461
+ delete_address: "Delete Address",
4462
+ delete_address_message: "Are you sure you want to delete this address? This action cannot be undone.",
4463
+ delete: "Delete",
4464
+ cancel: "Cancel",
4465
+ country: "Country",
4466
+ name: "Name",
4467
+ address_line_1: "Address Line 1",
4468
+ address_line_2: "Address Line 2",
4469
+ city: "City",
4470
+ state: "State",
4471
+ province: "Province",
4472
+ zip_code: "Zip Code",
4473
+ postal_code: "Postal Code",
4474
+ select_state: "Select state",
4475
+ select_an_option: "Select an option",
4476
+ set_as_default_address: "Set as default address",
4477
+ save_address: "Save Address",
4478
+ first_name: "First Name",
4479
+ last_name: "Last Name",
4480
+ deleting: "Deleting…",
4481
+ tap_to_confirm: "Tap to confirm"
4482
+ };
4483
+ function addressDialogT(key) {
4484
+ return ADDRESS_DIALOG_LABELS[key] ?? key.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
4485
+ }
4486
+ function mapToFluidPayAddress$1(raw) {
4487
+ return {
4488
+ id: raw.id,
4489
+ address1: raw.street1,
4490
+ address2: raw.street2,
4491
+ city: raw.city,
4492
+ country_code: raw.country,
4493
+ default: raw.default,
4494
+ name: raw.name,
4495
+ postal_code: raw.zip,
4496
+ state: raw.state,
4497
+ subdivision_code: null
4498
+ };
4499
+ }
4500
+ function composeAddressName$1(firstName, lastName) {
4501
+ const combined = `${firstName} ${lastName}`.trim();
4502
+ return combined.length > 0 ? combined : null;
4503
+ }
4504
+ function SubscriptionsListScreenInner({ customerId, isLoadingCustomer, onToast }) {
3389
4505
  const { navigate } = useAppNavigation();
3390
- const goToDetail = (token) => navigate(`subscriptions/${token}`);
3391
- return /* @__PURE__ */ jsx(PortalSubscriptionsList, {
4506
+ const queryClient = useQueryClient();
4507
+ const payApi = usePayApi();
4508
+ const countriesApi = useCountriesApi();
4509
+ const { t } = useSubscriptionsTranslation();
4510
+ const { data: addresses = [] } = useQuery({
4511
+ queryKey: payKeys.addresses.list(),
4512
+ queryFn: async () => {
4513
+ return (await payApi.fetchAddresses()).addresses.map(mapToFluidPayAddress$1);
4514
+ },
4515
+ enabled: !!customerId && !isLoadingCustomer
4516
+ });
4517
+ const { data: paymentMethods = [] } = useQuery({
4518
+ queryKey: payKeys.paymentMethods.list(),
4519
+ queryFn: async () => {
4520
+ return (await payApi.fetchPaymentMethods()).payment_methods.map(mapToFluidPayPaymentMethod);
4521
+ },
4522
+ enabled: !!customerId && !isLoadingCustomer
4523
+ });
4524
+ const { data: subscriptionsData } = useInfiniteSubscriptions({
3392
4525
  customerId,
3393
- isLoadingCustomer,
3394
- onSubscriptionClick: goToDetail,
3395
- onResume: (sub) => goToDetail(sub.subscription_token)
4526
+ limit: 12
4527
+ }, { enabled: !!customerId && !isLoadingCustomer });
4528
+ const subscriptionsList = useMemo(() => subscriptionsData?.pages.flatMap((p) => p.subscriptions) ?? [], [subscriptionsData]);
4529
+ const { data: bundlesData } = useBundles({ enabled: !!customerId && !isLoadingCustomer });
4530
+ const [bundleDrawerOpen, setBundleDrawerOpen] = useState(false);
4531
+ const [bundleDrawerKey, setBundleDrawerKey] = useState(0);
4532
+ const [addressDialog, setAddressDialog] = useState({
4533
+ open: false,
4534
+ selected: null
4535
+ });
4536
+ const [isAddCardOpen, setIsAddCardOpen] = useState(false);
4537
+ const [isBundling, setIsBundling] = useState(false);
4538
+ const { data: countriesData } = useQuery({
4539
+ queryKey: storeKeys.countries(),
4540
+ queryFn: () => countriesApi.listCountries(),
4541
+ enabled: addressDialog.open || isAddCardOpen
4542
+ });
4543
+ const countryOptions = useMemo(() => [...countriesData?.countries ?? []].map((c) => ({
4544
+ iso: c.code,
4545
+ name: c.name
4546
+ })).sort((a, b) => a.name.localeCompare(b.name)), [countriesData]);
4547
+ const statesByCountry = useMemo(() => {
4548
+ const map = /* @__PURE__ */ new Map();
4549
+ for (const c of countriesData?.countries ?? []) map.set(c.code, c.states.map((s) => ({
4550
+ name: s.name,
4551
+ isoCode: s.code
4552
+ })));
4553
+ return map;
4554
+ }, [countriesData]);
4555
+ const fetchStatesFromCountries = useCallback((countryCode) => Promise.resolve(statesByCountry.get(countryCode) ?? []), [statesByCountry]);
4556
+ const createAddressMutation = useMutation({
4557
+ mutationFn: (body) => payApi.createAddress({ address: {
4558
+ name: composeAddressName$1(body.address.first_name, body.address.last_name),
4559
+ street1: body.address.address1,
4560
+ street2: body.address.address2,
4561
+ city: body.address.city,
4562
+ state: body.address.state,
4563
+ zip: body.address.postal_code,
4564
+ country: body.address.country_code,
4565
+ default: body.address.default
4566
+ } }),
4567
+ onSuccess: () => {
4568
+ queryClient.invalidateQueries({ queryKey: payKeys.addresses.all });
4569
+ onToast?.("Address added", "success");
4570
+ },
4571
+ onError: () => onToast?.("Failed to add address", "error")
4572
+ });
4573
+ const addCreditCardMutation = useMutation({
4574
+ mutationFn: (data) => payApi.createPaymentMethod({ payment_method: {
4575
+ type: "card",
4576
+ token: data.payment_method.token,
4577
+ default: data.default_payment_method
4578
+ } }),
4579
+ onSuccess: () => {
4580
+ queryClient.invalidateQueries({ queryKey: payKeys.paymentMethods.all });
4581
+ onToast?.("Payment method added", "success");
4582
+ },
4583
+ onError: () => onToast?.("Failed to add payment method", "error")
4584
+ });
4585
+ const createBundle = useCreateBundle();
4586
+ const updateSubscription = useUpdateSubscriptionInfo();
4587
+ const handleBundleClick = () => {
4588
+ setBundleDrawerKey((k) => k + 1);
4589
+ setBundleDrawerOpen(true);
4590
+ };
4591
+ const handleBundle = async (params) => {
4592
+ if (isBundling) return;
4593
+ setIsBundling(true);
4594
+ try {
4595
+ await createBundle.mutateAsync({
4596
+ subscription_tokens: params.subscription_tokens,
4597
+ next_bill_date: params.next_bill_date,
4598
+ address_id: params.address_id
4599
+ });
4600
+ } catch {
4601
+ onToast?.(t("bundle_failed"), "error");
4602
+ setIsBundling(false);
4603
+ return;
4604
+ }
4605
+ const failedCount = (await Promise.allSettled(params.subscription_tokens.map((token) => updateSubscription.mutateAsync({
4606
+ subscriptionToken: token,
4607
+ body: { payment_method_id: params.payment_method_id }
4608
+ })))).filter((r) => r.status === "rejected").length;
4609
+ if (failedCount === 0) onToast?.(t("bundle_succeeded"), "success");
4610
+ else onToast?.(t("bundle_partial_payment_failure", { n: String(failedCount) }), "warning");
4611
+ setBundleDrawerOpen(false);
4612
+ setIsBundling(false);
4613
+ };
4614
+ const goToDetail = (token) => navigate(`subscriptions/${token}`);
4615
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
4616
+ /* @__PURE__ */ jsx(PortalSubscriptionsList, {
4617
+ customerId,
4618
+ isLoadingCustomer,
4619
+ onSubscriptionClick: goToDetail,
4620
+ onResume: (sub) => goToDetail(sub.subscription_token),
4621
+ onBundleClick: handleBundleClick,
4622
+ bundles: bundlesData
4623
+ }),
4624
+ /* @__PURE__ */ jsx(BundleSubscriptionsDrawer, {
4625
+ open: bundleDrawerOpen,
4626
+ onOpenChange: setBundleDrawerOpen,
4627
+ subscriptions: subscriptionsList,
4628
+ addresses,
4629
+ paymentMethods,
4630
+ defaultAddressId: addresses.find((a) => a.default)?.id ?? addresses[0]?.id ?? null,
4631
+ defaultPaymentMethodId: paymentMethods.find((pm) => pm.default)?.id ?? paymentMethods[0]?.id ?? null,
4632
+ onAddAddress: () => setAddressDialog({
4633
+ open: true,
4634
+ selected: null
4635
+ }),
4636
+ onAddPaymentMethod: () => setIsAddCardOpen(true),
4637
+ onSubmit: handleBundle,
4638
+ isSubmitting: isBundling
4639
+ }, bundleDrawerKey),
4640
+ /* @__PURE__ */ jsx(AddressFormDialog, {
4641
+ isOpen: addressDialog.open,
4642
+ onClose: () => setAddressDialog({
4643
+ open: false,
4644
+ selected: null
4645
+ }),
4646
+ selectedAddress: addressDialog.selected,
4647
+ t: addressDialogT,
4648
+ onSubmit: async (formData) => {
4649
+ try {
4650
+ await createAddressMutation.mutateAsync(formData);
4651
+ setAddressDialog({
4652
+ open: false,
4653
+ selected: null
4654
+ });
4655
+ } catch {}
4656
+ },
4657
+ isSubmitting: createAddressMutation.isPending,
4658
+ countries: countryOptions,
4659
+ fetchStates: fetchStatesFromCountries,
4660
+ renderAddressAutocomplete: ({ control, setValue, countryCode }) => /* @__PURE__ */ jsx(AddressAutocompleteInput, {
4661
+ control,
4662
+ setValue,
4663
+ countryIso: countryCode,
4664
+ addressLineField: "address1",
4665
+ cityField: "city",
4666
+ stateField: "state",
4667
+ postalCodeField: "postal_code",
4668
+ placeholder: addressDialogT("address_line_1")
4669
+ })
4670
+ }),
4671
+ /* @__PURE__ */ jsx(CreditCardFormDialog, {
4672
+ isOpen: isAddCardOpen,
4673
+ onClose: () => setIsAddCardOpen(false),
4674
+ t: addressDialogT,
4675
+ onSubmit: async (data) => {
4676
+ try {
4677
+ await addCreditCardMutation.mutateAsync(data);
4678
+ setIsAddCardOpen(false);
4679
+ } catch {}
4680
+ },
4681
+ isSubmitting: addCreditCardMutation.isPending,
4682
+ countries: countryOptions,
4683
+ jwt: "",
4684
+ renderAddressAutocomplete: ({ control, setValue, countryCode }) => /* @__PURE__ */ jsx(AddressAutocompleteInput, {
4685
+ control,
4686
+ setValue,
4687
+ countryIso: countryCode,
4688
+ addressLineField: "address1",
4689
+ cityField: "city",
4690
+ stateField: "state",
4691
+ postalCodeField: "zip",
4692
+ placeholder: addressDialogT("address_line_1")
4693
+ })
4694
+ })
4695
+ ] });
4696
+ }
4697
+ function SubscriptionsListScreen(props) {
4698
+ const payApi = usePayApi();
4699
+ return /* @__PURE__ */ jsx(FluidPayCoreProvider, {
4700
+ api: useMemo(() => createFluidPayApiAdapter(payApi), [payApi]),
4701
+ children: /* @__PURE__ */ jsx(SubscriptionsListScreenInner, { ...props })
3396
4702
  });
3397
4703
  }
3398
4704
  //#endregion
@@ -5722,13 +7028,14 @@ function SubscriptionsScreen({ onToast, background, textColor, accentColor, padd
5722
7028
  const isLoadingCustomer = account === void 0;
5723
7029
  return /* @__PURE__ */ jsx("div", {
5724
7030
  ...divProps,
5725
- children: /* @__PURE__ */ jsx(SubscriptionsTranslationBridge, { children: isDetailView ? /* @__PURE__ */ jsx(PortalSubscriptionsApiProvider, { children: /* @__PURE__ */ jsx(SubscriptionDetailScreen, {
7031
+ children: /* @__PURE__ */ jsx(SubscriptionsTranslationBridge, { children: /* @__PURE__ */ jsx(PortalSubscriptionsApiProvider, { children: /* @__PURE__ */ jsx(PortalBundlesApiProvider, { children: isDetailView ? /* @__PURE__ */ jsx(SubscriptionDetailScreen, {
5726
7032
  token: detailToken,
5727
7033
  onToast: effectiveToast
5728
- }) }) : /* @__PURE__ */ jsx(PortalSubscriptionsApiProvider, { children: /* @__PURE__ */ jsx(SubscriptionsListScreen, {
7034
+ }) : /* @__PURE__ */ jsx(SubscriptionsListScreen, {
5729
7035
  customerId,
5730
- isLoadingCustomer
5731
- }) }) })
7036
+ isLoadingCustomer,
7037
+ onToast: effectiveToast
7038
+ }) }) }) })
5732
7039
  });
5733
7040
  }
5734
7041
  const subscriptionsScreenPropertySchema = {
@@ -5743,4 +7050,4 @@ const subscriptionsScreenPropertySchema = {
5743
7050
  //#endregion
5744
7051
  export { SubscriptionsScreen_exports as n, subscriptionsScreenPropertySchema as r, SubscriptionsScreen as t };
5745
7052
 
5746
- //# sourceMappingURL=SubscriptionsScreen-Ck3-dcqq.mjs.map
7053
+ //# sourceMappingURL=SubscriptionsScreen-HIIrxH3a.mjs.map