@fluid-app/portal-sdk 0.1.307 → 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.
- package/dist/{FluidProvider-BiULiWJY.cjs → FluidProvider-B1UsSiKO.cjs} +2 -2
- package/dist/{FluidProvider-BiULiWJY.cjs.map → FluidProvider-B1UsSiKO.cjs.map} +1 -1
- package/dist/{FluidProvider-CLIlEKSg.mjs → FluidProvider-D7BEpqDQ.mjs} +2 -2
- package/dist/{FluidProvider-CLIlEKSg.mjs.map → FluidProvider-D7BEpqDQ.mjs.map} +1 -1
- package/dist/{MessagingScreen-CYTxBDtC.cjs → MessagingScreen-0qwUGWGf.cjs} +3 -3
- package/dist/{MessagingScreen-CYTxBDtC.cjs.map → MessagingScreen-0qwUGWGf.cjs.map} +1 -1
- package/dist/{MessagingScreen-Cd2iUd_B.cjs → MessagingScreen-DCHGSAA7.cjs} +3 -3
- package/dist/{MessagingScreen-CZTploEf.mjs → MessagingScreen-DyFLJsQ-.mjs} +3 -3
- package/dist/{MessagingScreen-CZTploEf.mjs.map → MessagingScreen-DyFLJsQ-.mjs.map} +1 -1
- package/dist/{MessagingScreen-CKNGfS56.mjs → MessagingScreen-L6vyOqgm.mjs} +3 -3
- package/dist/{OrdersScreen-CIxlNlYf.cjs → OrdersScreen-3WRoz4fR.cjs} +3 -3
- package/dist/{OrdersScreen-mHhOjrMk.mjs → OrdersScreen-BiB5eMA4.mjs} +3 -3
- package/dist/{OrdersScreen-mHhOjrMk.mjs.map → OrdersScreen-BiB5eMA4.mjs.map} +1 -1
- package/dist/{OrdersScreen-BB63IDwz.mjs → OrdersScreen-UsFG95zY.mjs} +3 -3
- package/dist/{OrdersScreen-BVOQup59.cjs → OrdersScreen-rjcGVfyT.cjs} +3 -3
- package/dist/{OrdersScreen-BVOQup59.cjs.map → OrdersScreen-rjcGVfyT.cjs.map} +1 -1
- package/dist/{ProfileScreen-BiO036lk.cjs → ProfileScreen-Bm9gayW5.cjs} +3 -3
- package/dist/{ProfileScreen-7yjZmDoH.mjs → ProfileScreen-CIDEmZg9.mjs} +4 -4
- package/dist/{ProfileScreen-7yjZmDoH.mjs.map → ProfileScreen-CIDEmZg9.mjs.map} +1 -1
- package/dist/{ProfileScreen-DLWOaN4I.mjs → ProfileScreen-DioyTFVO.mjs} +3 -3
- package/dist/{ProfileScreen-Dsm-0XQY.cjs → ProfileScreen-xTVmveXS.cjs} +4 -4
- package/dist/{ProfileScreen-Dsm-0XQY.cjs.map → ProfileScreen-xTVmveXS.cjs.map} +1 -1
- package/dist/{ShareablesScreen-C8zQjj9l.mjs → ShareablesScreen-BXMbz5Fn.mjs} +3 -3
- package/dist/{ShareablesScreen-C8zQjj9l.mjs.map → ShareablesScreen-BXMbz5Fn.mjs.map} +1 -1
- package/dist/{ShareablesScreen-DFTD6x4A.cjs → ShareablesScreen-BsXn5-sp.cjs} +3 -3
- package/dist/{ShareablesScreen-DFTD6x4A.cjs.map → ShareablesScreen-BsXn5-sp.cjs.map} +1 -1
- package/dist/{ShareablesScreen-6c24GuTW.mjs → ShareablesScreen-CetzfpS7.mjs} +2 -2
- package/dist/{ShareablesScreen-B1h2JbtB.cjs → ShareablesScreen-JAN0oKF9.cjs} +2 -2
- package/dist/{ShopScreen-C3pEQzV_.cjs → ShopScreen-BM0aj7oV.cjs} +2 -2
- package/dist/{ShopScreen-C3pEQzV_.cjs.map → ShopScreen-BM0aj7oV.cjs.map} +1 -1
- package/dist/{ShopScreen-B-lu9pT5.cjs → ShopScreen-BsTTHFit.cjs} +3 -3
- package/dist/{ShopScreen-CuXcm9mj.mjs → ShopScreen-DwyZ8pqV.mjs} +3 -3
- package/dist/{ShopScreen-H8OlaM3P.mjs → ShopScreen-u8MPbT1t.mjs} +2 -2
- package/dist/{ShopScreen-H8OlaM3P.mjs.map → ShopScreen-u8MPbT1t.mjs.map} +1 -1
- package/dist/{SubscriptionsScreen-kCkz_1xD.cjs → SubscriptionsScreen-D_HUnXQb.cjs} +2 -2
- package/dist/{SubscriptionsScreen-1hP0t23a.cjs → SubscriptionsScreen-Dfmqn6LY.cjs} +1344 -37
- package/dist/SubscriptionsScreen-Dfmqn6LY.cjs.map +1 -0
- package/dist/{SubscriptionsScreen-Ck3-dcqq.mjs → SubscriptionsScreen-HIIrxH3a.mjs} +1346 -39
- package/dist/SubscriptionsScreen-HIIrxH3a.mjs.map +1 -0
- package/dist/index.cjs +21 -21
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +21 -21
- package/dist/{portal_tenant-l4a-eX2B.mjs → portal_tenant-BLdhIM81.mjs} +22 -2
- package/dist/portal_tenant-BLdhIM81.mjs.map +1 -0
- package/dist/{portal_tenant-DG4gKXnu.cjs → portal_tenant-ywAHQ1k3.cjs} +33 -1
- package/dist/portal_tenant-ywAHQ1k3.cjs.map +1 -0
- package/dist/{use-account-BgustWSP.mjs → use-account-BOE1yhAH.mjs} +2 -2
- package/dist/{use-account-BgustWSP.mjs.map → use-account-BOE1yhAH.mjs.map} +1 -1
- package/dist/{use-account-EicAlO8K.cjs → use-account-DeRJHrd3.cjs} +2 -2
- package/dist/{use-account-EicAlO8K.cjs.map → use-account-DeRJHrd3.cjs.map} +1 -1
- package/package.json +16 -16
- package/dist/SubscriptionsScreen-1hP0t23a.cjs.map +0 -1
- package/dist/SubscriptionsScreen-Ck3-dcqq.mjs.map +0 -1
- package/dist/portal_tenant-DG4gKXnu.cjs.map +0 -1
- 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
|
|
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-
|
|
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.
|
|
2575
|
-
shipping_interval: 1,
|
|
2576
|
-
shipping_interval_unit: bff.
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
}, [
|
|
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__ */
|
|
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:
|
|
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__ */
|
|
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
|
-
|
|
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
|
|
3391
|
-
|
|
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
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
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:
|
|
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
|
-
})
|
|
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-
|
|
7053
|
+
//# sourceMappingURL=SubscriptionsScreen-HIIrxH3a.mjs.map
|