@bunnyapp/components 1.7.0-beta.15 → 1.7.0-beta.18
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/cjs/index.js +227 -4
- package/dist/cjs/types/src/components/Subscriptions/subscriptionsList/SubscriptionsList.d.ts +2 -0
- package/dist/cjs/types/src/components/Subscriptions/subscriptionsList/subscriptionUtils.d.ts +59 -0
- package/dist/esm/index.js +227 -4
- package/dist/esm/types/src/components/Subscriptions/subscriptionsList/SubscriptionsList.d.ts +2 -0
- package/dist/esm/types/src/components/Subscriptions/subscriptionsList/subscriptionUtils.d.ts +59 -0
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
|
@@ -1280,7 +1280,7 @@ const DEFAULT_CONFIG = {
|
|
|
1280
1280
|
};
|
|
1281
1281
|
|
|
1282
1282
|
// This will be replaced at build time by rollup-plugin-replace
|
|
1283
|
-
const PACKAGE_VERSION = '1.7.0-beta.
|
|
1283
|
+
const PACKAGE_VERSION = '1.7.0-beta.17';
|
|
1284
1284
|
const createRequestHeaders = (token) => {
|
|
1285
1285
|
const headers = createClientDevHeaders({ token });
|
|
1286
1286
|
// Add the components version header
|
|
@@ -24512,6 +24512,225 @@ const hasPriceTiers = (charge) => {
|
|
|
24512
24512
|
var _a;
|
|
24513
24513
|
return Boolean((_a = charge === null || charge === void 0 ? void 0 : charge.priceTiers) === null || _a === void 0 ? void 0 : _a.length);
|
|
24514
24514
|
};
|
|
24515
|
+
const splitSubscriptionsByPriceList_SubscriptionFragment = t(`
|
|
24516
|
+
fragment splitSubscriptionsByPriceList_SubscriptionFragment on Subscription {
|
|
24517
|
+
id
|
|
24518
|
+
state
|
|
24519
|
+
evergreen
|
|
24520
|
+
startDate
|
|
24521
|
+
endDate
|
|
24522
|
+
addonSubscriptions {
|
|
24523
|
+
id
|
|
24524
|
+
priceList {
|
|
24525
|
+
id
|
|
24526
|
+
}
|
|
24527
|
+
plan {
|
|
24528
|
+
id
|
|
24529
|
+
}
|
|
24530
|
+
}
|
|
24531
|
+
chargeReport {
|
|
24532
|
+
kind
|
|
24533
|
+
startDate
|
|
24534
|
+
endDate
|
|
24535
|
+
priceList {
|
|
24536
|
+
id
|
|
24537
|
+
plan {
|
|
24538
|
+
id
|
|
24539
|
+
name
|
|
24540
|
+
selfServiceBuy
|
|
24541
|
+
selfServiceCancel
|
|
24542
|
+
selfServiceRenew
|
|
24543
|
+
addon
|
|
24544
|
+
addonPlans {
|
|
24545
|
+
id
|
|
24546
|
+
}
|
|
24547
|
+
}
|
|
24548
|
+
product {
|
|
24549
|
+
id
|
|
24550
|
+
name
|
|
24551
|
+
showProductNameOnLineItem
|
|
24552
|
+
}
|
|
24553
|
+
periodMonths
|
|
24554
|
+
name
|
|
24555
|
+
}
|
|
24556
|
+
}
|
|
24557
|
+
charges {
|
|
24558
|
+
priceList {
|
|
24559
|
+
id
|
|
24560
|
+
}
|
|
24561
|
+
}
|
|
24562
|
+
}
|
|
24563
|
+
`, []);
|
|
24564
|
+
// Helper to safely get date as string
|
|
24565
|
+
const getDateString = (date) => {
|
|
24566
|
+
return date;
|
|
24567
|
+
};
|
|
24568
|
+
/**
|
|
24569
|
+
* Splits a subscription into multiple subscriptions if it has RENEW charges
|
|
24570
|
+
* with different price lists.
|
|
24571
|
+
* This handles cases where a subscription has been changed to a different plan/price list.
|
|
24572
|
+
* Each resulting subscription is a complete duplicate with just the charges filtered.
|
|
24573
|
+
*/
|
|
24574
|
+
function splitSubscriptionsByPriceList(maskedSubscriptions) {
|
|
24575
|
+
const result = [];
|
|
24576
|
+
maskedSubscriptions.forEach(maskedSubscription => {
|
|
24577
|
+
var _a;
|
|
24578
|
+
// Read the fragment to get typed data
|
|
24579
|
+
const subscription = readFragment(splitSubscriptionsByPriceList_SubscriptionFragment, maskedSubscription);
|
|
24580
|
+
// Skip if no chargeReport
|
|
24581
|
+
if (!subscription.chargeReport) {
|
|
24582
|
+
result.push(maskedSubscription);
|
|
24583
|
+
return;
|
|
24584
|
+
}
|
|
24585
|
+
// Group chargeReport by priceListId
|
|
24586
|
+
const chargesByPriceList = new Map();
|
|
24587
|
+
subscription.chargeReport.forEach(charge => {
|
|
24588
|
+
var _a;
|
|
24589
|
+
const priceListId = (_a = charge.priceList) === null || _a === void 0 ? void 0 : _a.id;
|
|
24590
|
+
if (!priceListId)
|
|
24591
|
+
return;
|
|
24592
|
+
if (!chargesByPriceList.has(priceListId)) {
|
|
24593
|
+
chargesByPriceList.set(priceListId, []);
|
|
24594
|
+
}
|
|
24595
|
+
chargesByPriceList.get(priceListId).push(charge);
|
|
24596
|
+
});
|
|
24597
|
+
// Check if there are RENEW charges with different price lists
|
|
24598
|
+
const hasRenewChargesWithDifferentPriceLists = chargesByPriceList.size > 1 &&
|
|
24599
|
+
subscription.chargeReport.some(charge => charge.kind === 'RENEW');
|
|
24600
|
+
// If all charges have the same priceListId or no RENEW charges, keep subscription as-is
|
|
24601
|
+
if (!hasRenewChargesWithDifferentPriceLists) {
|
|
24602
|
+
result.push(maskedSubscription);
|
|
24603
|
+
}
|
|
24604
|
+
else {
|
|
24605
|
+
// Find the charge with the latest endDate to determine which subscription will actually renew
|
|
24606
|
+
const latestCharge = subscription.chargeReport.reduce((latest, current) => {
|
|
24607
|
+
if (!latest)
|
|
24608
|
+
return current;
|
|
24609
|
+
// Compare endDates, and if equal, compare startDates
|
|
24610
|
+
const currentEnd = getDateString(current.endDate);
|
|
24611
|
+
const latestEnd = getDateString(latest.endDate);
|
|
24612
|
+
const currentStart = getDateString(current.startDate);
|
|
24613
|
+
const latestStart = getDateString(latest.startDate);
|
|
24614
|
+
if (currentEnd > latestEnd)
|
|
24615
|
+
return current;
|
|
24616
|
+
if (currentEnd === latestEnd && currentStart > latestStart)
|
|
24617
|
+
return current;
|
|
24618
|
+
return latest;
|
|
24619
|
+
}, subscription.chargeReport[0]);
|
|
24620
|
+
const latestPriceListId = (_a = latestCharge.priceList) === null || _a === void 0 ? void 0 : _a.id;
|
|
24621
|
+
if (!latestPriceListId) {
|
|
24622
|
+
result.push(maskedSubscription);
|
|
24623
|
+
return;
|
|
24624
|
+
}
|
|
24625
|
+
// Split into multiple subscriptions, one per price list
|
|
24626
|
+
// DUPLICATE the entire subscription, just filter the charges
|
|
24627
|
+
const splitSubscriptions = [];
|
|
24628
|
+
chargesByPriceList.forEach((charges, priceListId) => {
|
|
24629
|
+
var _a, _b, _c, _d, _e, _f;
|
|
24630
|
+
// Get the first charge to extract priceList, plan, and product info
|
|
24631
|
+
const firstCharge = charges === null || charges === void 0 ? void 0 : charges[0];
|
|
24632
|
+
if (!firstCharge || !charges)
|
|
24633
|
+
return;
|
|
24634
|
+
// Find the latest endDate from the charges in this group
|
|
24635
|
+
const latestEndDate = charges.reduce((latest, current) => {
|
|
24636
|
+
const currentEnd = getDateString(current.endDate);
|
|
24637
|
+
if (!latest)
|
|
24638
|
+
return currentEnd;
|
|
24639
|
+
return currentEnd > latest ? currentEnd : latest;
|
|
24640
|
+
}, getDateString(charges[0].endDate));
|
|
24641
|
+
// Find the earliest startDate from the charges in this group
|
|
24642
|
+
const earliestStartDate = charges.reduce((earliest, current) => {
|
|
24643
|
+
const currentStart = getDateString(current.startDate);
|
|
24644
|
+
if (!earliest)
|
|
24645
|
+
return currentStart;
|
|
24646
|
+
return currentStart < earliest ? currentStart : earliest;
|
|
24647
|
+
}, getDateString(charges[0].startDate));
|
|
24648
|
+
// Determine the state based on the date range of charges
|
|
24649
|
+
const now = new Date().toISOString().split('T')[0]; // Get current date in YYYY-MM-DD format
|
|
24650
|
+
let state = subscription.state;
|
|
24651
|
+
// If the latest endDate is before today, it's expired
|
|
24652
|
+
if (latestEndDate < now) {
|
|
24653
|
+
state = t.scalar('SubscriptionState', 'EXPIRED');
|
|
24654
|
+
}
|
|
24655
|
+
// If the earliest startDate is after today, it's pending
|
|
24656
|
+
else if (earliestStartDate > now) {
|
|
24657
|
+
state = t.scalar('SubscriptionState', 'PENDING');
|
|
24658
|
+
}
|
|
24659
|
+
// Otherwise, if current date is within the range, keep it active
|
|
24660
|
+
else if (earliestStartDate <= now && latestEndDate >= now) {
|
|
24661
|
+
state = t.scalar('SubscriptionState', 'ACTIVE');
|
|
24662
|
+
}
|
|
24663
|
+
// Only the subscription with the latest charge will actually renew
|
|
24664
|
+
const willRenew = priceListId === latestPriceListId;
|
|
24665
|
+
// Filter addon subscriptions to only include those compatible with the new plan
|
|
24666
|
+
// If the plan has changed, we need to check if the addon subscriptions are still valid
|
|
24667
|
+
const newPlan = (_a = firstCharge.priceList) === null || _a === void 0 ? void 0 : _a.plan;
|
|
24668
|
+
const addonPlanIds = (_c = (_b = newPlan === null || newPlan === void 0 ? void 0 : newPlan.addonPlans) === null || _b === void 0 ? void 0 : _b.map(plan => plan.id)) !== null && _c !== void 0 ? _c : [];
|
|
24669
|
+
// For split subscriptions that represent a future period (after plan change),
|
|
24670
|
+
// filter out addon subscriptions that are not compatible with the new plan
|
|
24671
|
+
const filteredAddonSubscriptions = (_d = subscription.addonSubscriptions) === null || _d === void 0 ? void 0 : _d.filter(addonSub => {
|
|
24672
|
+
var _a;
|
|
24673
|
+
// If the new plan has no addon plans defined, keep all addons (backward compatibility)
|
|
24674
|
+
if (!(newPlan === null || newPlan === void 0 ? void 0 : newPlan.addonPlans) || newPlan.addonPlans.length === 0) {
|
|
24675
|
+
return true;
|
|
24676
|
+
}
|
|
24677
|
+
// Check if this addon subscription's plan is in the new plan's addonPlans list
|
|
24678
|
+
const addonPlanId = (_a = addonSub.plan) === null || _a === void 0 ? void 0 : _a.id;
|
|
24679
|
+
return addonPlanId ? addonPlanIds.includes(addonPlanId) : false;
|
|
24680
|
+
});
|
|
24681
|
+
splitSubscriptions.push({
|
|
24682
|
+
...maskedSubscription,
|
|
24683
|
+
// Keep the original subscription ID intact (from maskedSubscription spread)
|
|
24684
|
+
// Update priceList from the charge
|
|
24685
|
+
priceList: firstCharge.priceList,
|
|
24686
|
+
// Update plan from the charge's priceList.plan
|
|
24687
|
+
plan: firstCharge.priceList.plan,
|
|
24688
|
+
// Update product from the charge's priceList.product
|
|
24689
|
+
product: firstCharge.priceList.product,
|
|
24690
|
+
// Update endDate to match the latest charge endDate in this group
|
|
24691
|
+
endDate: latestEndDate,
|
|
24692
|
+
// Update startDate to match the earliest charge startDate in this group
|
|
24693
|
+
startDate: earliestStartDate,
|
|
24694
|
+
// Update state based on the charge dates
|
|
24695
|
+
state,
|
|
24696
|
+
// Set evergreen to false for subscriptions that won't renew (will transition instead)
|
|
24697
|
+
evergreen: willRenew ? subscription.evergreen : false,
|
|
24698
|
+
// Filter chargeReport to only include charges for this price list
|
|
24699
|
+
chargeReport: charges,
|
|
24700
|
+
// Filter charges array to only include charges for this price list
|
|
24701
|
+
charges: (_f = (_e = subscription.charges) === null || _e === void 0 ? void 0 : _e.filter(charge => {
|
|
24702
|
+
var _a;
|
|
24703
|
+
const chargeWithPriceList = charge;
|
|
24704
|
+
return ((_a = chargeWithPriceList.priceList) === null || _a === void 0 ? void 0 : _a.id) === priceListId;
|
|
24705
|
+
})) !== null && _f !== void 0 ? _f : [],
|
|
24706
|
+
// Filter addon subscriptions to only include those compatible with the new plan
|
|
24707
|
+
addonSubscriptions: filteredAddonSubscriptions,
|
|
24708
|
+
});
|
|
24709
|
+
});
|
|
24710
|
+
// Sort split subscriptions by endDate (earliest first)
|
|
24711
|
+
splitSubscriptions.sort((a, b) => {
|
|
24712
|
+
const aData = readFragment(splitSubscriptionsByPriceList_SubscriptionFragment, a);
|
|
24713
|
+
const bData = readFragment(splitSubscriptionsByPriceList_SubscriptionFragment, b);
|
|
24714
|
+
const aEnd = getDateString(aData.endDate);
|
|
24715
|
+
const bEnd = getDateString(bData.endDate);
|
|
24716
|
+
const aStart = getDateString(aData.startDate);
|
|
24717
|
+
const bStart = getDateString(bData.startDate);
|
|
24718
|
+
if (aEnd < bEnd)
|
|
24719
|
+
return -1;
|
|
24720
|
+
if (aEnd > bEnd)
|
|
24721
|
+
return 1;
|
|
24722
|
+
// If endDates are equal, sort by startDate
|
|
24723
|
+
if (aStart < bStart)
|
|
24724
|
+
return -1;
|
|
24725
|
+
if (aStart > bStart)
|
|
24726
|
+
return 1;
|
|
24727
|
+
return 0;
|
|
24728
|
+
});
|
|
24729
|
+
result.push(...splitSubscriptions);
|
|
24730
|
+
}
|
|
24731
|
+
});
|
|
24732
|
+
return result;
|
|
24733
|
+
}
|
|
24515
24734
|
|
|
24516
24735
|
const SubscriptionCardActions_PriceListChangeOptionsFragment = t(`
|
|
24517
24736
|
fragment SubscriptionCardActions_PriceListChangeOptionsFragment on PriceListChangeOptions {
|
|
@@ -25413,12 +25632,14 @@ const SubscriptionsList_SubscriptionFragment = t(`
|
|
|
25413
25632
|
fragment SubscriptionsList_SubscriptionFragment on Subscription {
|
|
25414
25633
|
id
|
|
25415
25634
|
state
|
|
25635
|
+
...splitSubscriptionsByPriceList_SubscriptionFragment
|
|
25416
25636
|
...findNonAddonSubscriptions_SubscriptionFragment
|
|
25417
25637
|
...SubscriptionCardDesktop_SubscriptionFragment
|
|
25418
25638
|
...SubscriptionCardMobile_SubscriptionFragment
|
|
25419
25639
|
...AddonSubscriptionsCards_SubscriptionFragment
|
|
25420
25640
|
}
|
|
25421
25641
|
`, [
|
|
25642
|
+
splitSubscriptionsByPriceList_SubscriptionFragment,
|
|
25422
25643
|
findNonAddonSubscriptions_SubscriptionFragment,
|
|
25423
25644
|
SubscriptionCardDesktop_SubscriptionFragment,
|
|
25424
25645
|
SubscriptionCardMobile_SubscriptionFragment,
|
|
@@ -25429,15 +25650,17 @@ const SubscriptionsList = ({ showInactive, subscriptions: maskedSubscriptions, }
|
|
|
25429
25650
|
const { gap } = useSubscriptionProps();
|
|
25430
25651
|
// Read fragments
|
|
25431
25652
|
const subscriptions = maskedSubscriptions.map(maskedSubscription => readFragment(SubscriptionsList_SubscriptionFragment, maskedSubscription));
|
|
25653
|
+
// Split subscriptions by price list
|
|
25654
|
+
const splitSubscriptions = splitSubscriptionsByPriceList(subscriptions);
|
|
25432
25655
|
// Hooks
|
|
25433
25656
|
const isMobile = useIsMobile();
|
|
25434
|
-
const nonAddonSubscriptions = findNonAddonSubscriptions(
|
|
25657
|
+
const nonAddonSubscriptions = findNonAddonSubscriptions(splitSubscriptions);
|
|
25435
25658
|
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: nonAddonSubscriptions === null || nonAddonSubscriptions === void 0 ? void 0 : nonAddonSubscriptions.map((subscription, subscriptionIndex) => {
|
|
25436
25659
|
if (!showInactive && isSubscriptionNotActive(subscription.state))
|
|
25437
25660
|
return null;
|
|
25438
25661
|
if (isMobile)
|
|
25439
|
-
return (jsxRuntime.jsxs("div", { className: `bunny-flex bunny-flex-col bunny-gap-${gap}`, children: [jsxRuntime.jsx(SubscriptionCard, { subscription: subscription }), jsxRuntime.jsx(AddonSubscriptionsCards, { subscription: subscription, subscriptions:
|
|
25440
|
-
return (jsxRuntime.jsxs("div", { className: `bunny-flex bunny-flex-col bunny-gap-${gap}`, children: [jsxRuntime.jsx(SubscriptionCardDesktop, { subscription: subscription }), jsxRuntime.jsx(AddonSubscriptionsCards, { subscription: subscription, subscriptions:
|
|
25662
|
+
return (jsxRuntime.jsxs("div", { className: `bunny-flex bunny-flex-col bunny-gap-${gap}`, children: [jsxRuntime.jsx(SubscriptionCard, { subscription: subscription }), jsxRuntime.jsx(AddonSubscriptionsCards, { subscription: subscription, subscriptions: splitSubscriptions, showInactive: showInactive })] }, subscriptionIndex));
|
|
25663
|
+
return (jsxRuntime.jsxs("div", { className: `bunny-flex bunny-flex-col bunny-gap-${gap}`, children: [jsxRuntime.jsx(SubscriptionCardDesktop, { subscription: subscription }), jsxRuntime.jsx(AddonSubscriptionsCards, { subscription: subscription, subscriptions: splitSubscriptions, showInactive: showInactive })] }, subscriptionIndex));
|
|
25441
25664
|
}) }));
|
|
25442
25665
|
};
|
|
25443
25666
|
|
package/dist/cjs/types/src/components/Subscriptions/subscriptionsList/SubscriptionsList.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export declare const SubscriptionsList_SubscriptionFragment: import("gql.tada").
|
|
|
3
3
|
id: string;
|
|
4
4
|
state: "ACTIVE" | "TRIAL" | "CANCELED" | "EXPIRED" | "TRIAL_EXPIRED" | "PENDING";
|
|
5
5
|
[$tada.fragmentRefs]: {
|
|
6
|
+
splitSubscriptionsByPriceList_SubscriptionFragment: "Subscription";
|
|
7
|
+
} & {
|
|
6
8
|
findNonAddonSubscriptions_SubscriptionFragment: "Subscription";
|
|
7
9
|
} & {
|
|
8
10
|
SubscriptionCardDesktop_SubscriptionFragment: "Subscription";
|
package/dist/cjs/types/src/components/Subscriptions/subscriptionsList/subscriptionUtils.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Plan } from '@/types/Plan';
|
|
|
3
3
|
import Product from '@/types/Product';
|
|
4
4
|
import Subscription from '@/types/Subscription';
|
|
5
5
|
import { SubscriptionCharge } from '@/types/SubscriptionCharge';
|
|
6
|
+
import { FragmentOf } from 'gql.tada';
|
|
6
7
|
export declare const isSubscriptionNotActive: (subscriptionState: SubscriptionState) => boolean;
|
|
7
8
|
export declare const productPlanName: ({ plan, product }: {
|
|
8
9
|
plan: Plan;
|
|
@@ -16,3 +17,61 @@ export declare const hasPriceTiers: (charge: {
|
|
|
16
17
|
priceTiers: {}[] | null;
|
|
17
18
|
}) => boolean;
|
|
18
19
|
export declare const getUpdatingChargeQuantityId: (charge: SubscriptionCharge, subscription: Subscription) => string;
|
|
20
|
+
export declare const splitSubscriptionsByPriceList_SubscriptionFragment: import("gql.tada").TadaDocumentNode<{
|
|
21
|
+
id: string;
|
|
22
|
+
state: "ACTIVE" | "TRIAL" | "CANCELED" | "EXPIRED" | "TRIAL_EXPIRED" | "PENDING";
|
|
23
|
+
evergreen: boolean;
|
|
24
|
+
startDate: unknown;
|
|
25
|
+
endDate: unknown;
|
|
26
|
+
addonSubscriptions: {
|
|
27
|
+
id: string;
|
|
28
|
+
priceList: {
|
|
29
|
+
id: string;
|
|
30
|
+
} | null;
|
|
31
|
+
plan: {
|
|
32
|
+
id: string;
|
|
33
|
+
} | null;
|
|
34
|
+
}[] | null;
|
|
35
|
+
chargeReport: {
|
|
36
|
+
kind: "SUBSCRIBE" | "UPDATE" | "RENEW" | "REINSTATE" | "UNSUBSCRIBE" | "ADJUSTMENT" | "COUPON" | "DISCOUNT" | "CREDIT" | "PRICE_UPDATE" | "QUANTITY_UPDATE" | "FREE_PERIOD_DISCOUNT" | "ACTIVATE" | null;
|
|
37
|
+
startDate: unknown;
|
|
38
|
+
endDate: unknown;
|
|
39
|
+
priceList: {
|
|
40
|
+
id: string;
|
|
41
|
+
plan: {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
selfServiceBuy: boolean | null;
|
|
45
|
+
selfServiceCancel: boolean | null;
|
|
46
|
+
selfServiceRenew: boolean | null;
|
|
47
|
+
addon: boolean | null;
|
|
48
|
+
addonPlans: {
|
|
49
|
+
id: string;
|
|
50
|
+
}[] | null;
|
|
51
|
+
} | null;
|
|
52
|
+
product: {
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
showProductNameOnLineItem: boolean | null;
|
|
56
|
+
} | null;
|
|
57
|
+
periodMonths: number | null;
|
|
58
|
+
name: string;
|
|
59
|
+
} | null;
|
|
60
|
+
}[] | null;
|
|
61
|
+
charges: {
|
|
62
|
+
priceList: {
|
|
63
|
+
id: string;
|
|
64
|
+
} | null;
|
|
65
|
+
}[] | null;
|
|
66
|
+
}, {}, {
|
|
67
|
+
fragment: "splitSubscriptionsByPriceList_SubscriptionFragment";
|
|
68
|
+
on: "Subscription";
|
|
69
|
+
masked: true;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Splits a subscription into multiple subscriptions if it has RENEW charges
|
|
73
|
+
* with different price lists.
|
|
74
|
+
* This handles cases where a subscription has been changed to a different plan/price list.
|
|
75
|
+
* Each resulting subscription is a complete duplicate with just the charges filtered.
|
|
76
|
+
*/
|
|
77
|
+
export declare function splitSubscriptionsByPriceList<T extends FragmentOf<typeof splitSubscriptionsByPriceList_SubscriptionFragment>>(maskedSubscriptions: T[]): T[];
|
package/dist/esm/index.js
CHANGED
|
@@ -1278,7 +1278,7 @@ const DEFAULT_CONFIG = {
|
|
|
1278
1278
|
};
|
|
1279
1279
|
|
|
1280
1280
|
// This will be replaced at build time by rollup-plugin-replace
|
|
1281
|
-
const PACKAGE_VERSION = '1.7.0-beta.
|
|
1281
|
+
const PACKAGE_VERSION = '1.7.0-beta.17';
|
|
1282
1282
|
const createRequestHeaders = (token) => {
|
|
1283
1283
|
const headers = createClientDevHeaders({ token });
|
|
1284
1284
|
// Add the components version header
|
|
@@ -24510,6 +24510,225 @@ const hasPriceTiers = (charge) => {
|
|
|
24510
24510
|
var _a;
|
|
24511
24511
|
return Boolean((_a = charge === null || charge === void 0 ? void 0 : charge.priceTiers) === null || _a === void 0 ? void 0 : _a.length);
|
|
24512
24512
|
};
|
|
24513
|
+
const splitSubscriptionsByPriceList_SubscriptionFragment = t(`
|
|
24514
|
+
fragment splitSubscriptionsByPriceList_SubscriptionFragment on Subscription {
|
|
24515
|
+
id
|
|
24516
|
+
state
|
|
24517
|
+
evergreen
|
|
24518
|
+
startDate
|
|
24519
|
+
endDate
|
|
24520
|
+
addonSubscriptions {
|
|
24521
|
+
id
|
|
24522
|
+
priceList {
|
|
24523
|
+
id
|
|
24524
|
+
}
|
|
24525
|
+
plan {
|
|
24526
|
+
id
|
|
24527
|
+
}
|
|
24528
|
+
}
|
|
24529
|
+
chargeReport {
|
|
24530
|
+
kind
|
|
24531
|
+
startDate
|
|
24532
|
+
endDate
|
|
24533
|
+
priceList {
|
|
24534
|
+
id
|
|
24535
|
+
plan {
|
|
24536
|
+
id
|
|
24537
|
+
name
|
|
24538
|
+
selfServiceBuy
|
|
24539
|
+
selfServiceCancel
|
|
24540
|
+
selfServiceRenew
|
|
24541
|
+
addon
|
|
24542
|
+
addonPlans {
|
|
24543
|
+
id
|
|
24544
|
+
}
|
|
24545
|
+
}
|
|
24546
|
+
product {
|
|
24547
|
+
id
|
|
24548
|
+
name
|
|
24549
|
+
showProductNameOnLineItem
|
|
24550
|
+
}
|
|
24551
|
+
periodMonths
|
|
24552
|
+
name
|
|
24553
|
+
}
|
|
24554
|
+
}
|
|
24555
|
+
charges {
|
|
24556
|
+
priceList {
|
|
24557
|
+
id
|
|
24558
|
+
}
|
|
24559
|
+
}
|
|
24560
|
+
}
|
|
24561
|
+
`, []);
|
|
24562
|
+
// Helper to safely get date as string
|
|
24563
|
+
const getDateString = (date) => {
|
|
24564
|
+
return date;
|
|
24565
|
+
};
|
|
24566
|
+
/**
|
|
24567
|
+
* Splits a subscription into multiple subscriptions if it has RENEW charges
|
|
24568
|
+
* with different price lists.
|
|
24569
|
+
* This handles cases where a subscription has been changed to a different plan/price list.
|
|
24570
|
+
* Each resulting subscription is a complete duplicate with just the charges filtered.
|
|
24571
|
+
*/
|
|
24572
|
+
function splitSubscriptionsByPriceList(maskedSubscriptions) {
|
|
24573
|
+
const result = [];
|
|
24574
|
+
maskedSubscriptions.forEach(maskedSubscription => {
|
|
24575
|
+
var _a;
|
|
24576
|
+
// Read the fragment to get typed data
|
|
24577
|
+
const subscription = readFragment(splitSubscriptionsByPriceList_SubscriptionFragment, maskedSubscription);
|
|
24578
|
+
// Skip if no chargeReport
|
|
24579
|
+
if (!subscription.chargeReport) {
|
|
24580
|
+
result.push(maskedSubscription);
|
|
24581
|
+
return;
|
|
24582
|
+
}
|
|
24583
|
+
// Group chargeReport by priceListId
|
|
24584
|
+
const chargesByPriceList = new Map();
|
|
24585
|
+
subscription.chargeReport.forEach(charge => {
|
|
24586
|
+
var _a;
|
|
24587
|
+
const priceListId = (_a = charge.priceList) === null || _a === void 0 ? void 0 : _a.id;
|
|
24588
|
+
if (!priceListId)
|
|
24589
|
+
return;
|
|
24590
|
+
if (!chargesByPriceList.has(priceListId)) {
|
|
24591
|
+
chargesByPriceList.set(priceListId, []);
|
|
24592
|
+
}
|
|
24593
|
+
chargesByPriceList.get(priceListId).push(charge);
|
|
24594
|
+
});
|
|
24595
|
+
// Check if there are RENEW charges with different price lists
|
|
24596
|
+
const hasRenewChargesWithDifferentPriceLists = chargesByPriceList.size > 1 &&
|
|
24597
|
+
subscription.chargeReport.some(charge => charge.kind === 'RENEW');
|
|
24598
|
+
// If all charges have the same priceListId or no RENEW charges, keep subscription as-is
|
|
24599
|
+
if (!hasRenewChargesWithDifferentPriceLists) {
|
|
24600
|
+
result.push(maskedSubscription);
|
|
24601
|
+
}
|
|
24602
|
+
else {
|
|
24603
|
+
// Find the charge with the latest endDate to determine which subscription will actually renew
|
|
24604
|
+
const latestCharge = subscription.chargeReport.reduce((latest, current) => {
|
|
24605
|
+
if (!latest)
|
|
24606
|
+
return current;
|
|
24607
|
+
// Compare endDates, and if equal, compare startDates
|
|
24608
|
+
const currentEnd = getDateString(current.endDate);
|
|
24609
|
+
const latestEnd = getDateString(latest.endDate);
|
|
24610
|
+
const currentStart = getDateString(current.startDate);
|
|
24611
|
+
const latestStart = getDateString(latest.startDate);
|
|
24612
|
+
if (currentEnd > latestEnd)
|
|
24613
|
+
return current;
|
|
24614
|
+
if (currentEnd === latestEnd && currentStart > latestStart)
|
|
24615
|
+
return current;
|
|
24616
|
+
return latest;
|
|
24617
|
+
}, subscription.chargeReport[0]);
|
|
24618
|
+
const latestPriceListId = (_a = latestCharge.priceList) === null || _a === void 0 ? void 0 : _a.id;
|
|
24619
|
+
if (!latestPriceListId) {
|
|
24620
|
+
result.push(maskedSubscription);
|
|
24621
|
+
return;
|
|
24622
|
+
}
|
|
24623
|
+
// Split into multiple subscriptions, one per price list
|
|
24624
|
+
// DUPLICATE the entire subscription, just filter the charges
|
|
24625
|
+
const splitSubscriptions = [];
|
|
24626
|
+
chargesByPriceList.forEach((charges, priceListId) => {
|
|
24627
|
+
var _a, _b, _c, _d, _e, _f;
|
|
24628
|
+
// Get the first charge to extract priceList, plan, and product info
|
|
24629
|
+
const firstCharge = charges === null || charges === void 0 ? void 0 : charges[0];
|
|
24630
|
+
if (!firstCharge || !charges)
|
|
24631
|
+
return;
|
|
24632
|
+
// Find the latest endDate from the charges in this group
|
|
24633
|
+
const latestEndDate = charges.reduce((latest, current) => {
|
|
24634
|
+
const currentEnd = getDateString(current.endDate);
|
|
24635
|
+
if (!latest)
|
|
24636
|
+
return currentEnd;
|
|
24637
|
+
return currentEnd > latest ? currentEnd : latest;
|
|
24638
|
+
}, getDateString(charges[0].endDate));
|
|
24639
|
+
// Find the earliest startDate from the charges in this group
|
|
24640
|
+
const earliestStartDate = charges.reduce((earliest, current) => {
|
|
24641
|
+
const currentStart = getDateString(current.startDate);
|
|
24642
|
+
if (!earliest)
|
|
24643
|
+
return currentStart;
|
|
24644
|
+
return currentStart < earliest ? currentStart : earliest;
|
|
24645
|
+
}, getDateString(charges[0].startDate));
|
|
24646
|
+
// Determine the state based on the date range of charges
|
|
24647
|
+
const now = new Date().toISOString().split('T')[0]; // Get current date in YYYY-MM-DD format
|
|
24648
|
+
let state = subscription.state;
|
|
24649
|
+
// If the latest endDate is before today, it's expired
|
|
24650
|
+
if (latestEndDate < now) {
|
|
24651
|
+
state = t.scalar('SubscriptionState', 'EXPIRED');
|
|
24652
|
+
}
|
|
24653
|
+
// If the earliest startDate is after today, it's pending
|
|
24654
|
+
else if (earliestStartDate > now) {
|
|
24655
|
+
state = t.scalar('SubscriptionState', 'PENDING');
|
|
24656
|
+
}
|
|
24657
|
+
// Otherwise, if current date is within the range, keep it active
|
|
24658
|
+
else if (earliestStartDate <= now && latestEndDate >= now) {
|
|
24659
|
+
state = t.scalar('SubscriptionState', 'ACTIVE');
|
|
24660
|
+
}
|
|
24661
|
+
// Only the subscription with the latest charge will actually renew
|
|
24662
|
+
const willRenew = priceListId === latestPriceListId;
|
|
24663
|
+
// Filter addon subscriptions to only include those compatible with the new plan
|
|
24664
|
+
// If the plan has changed, we need to check if the addon subscriptions are still valid
|
|
24665
|
+
const newPlan = (_a = firstCharge.priceList) === null || _a === void 0 ? void 0 : _a.plan;
|
|
24666
|
+
const addonPlanIds = (_c = (_b = newPlan === null || newPlan === void 0 ? void 0 : newPlan.addonPlans) === null || _b === void 0 ? void 0 : _b.map(plan => plan.id)) !== null && _c !== void 0 ? _c : [];
|
|
24667
|
+
// For split subscriptions that represent a future period (after plan change),
|
|
24668
|
+
// filter out addon subscriptions that are not compatible with the new plan
|
|
24669
|
+
const filteredAddonSubscriptions = (_d = subscription.addonSubscriptions) === null || _d === void 0 ? void 0 : _d.filter(addonSub => {
|
|
24670
|
+
var _a;
|
|
24671
|
+
// If the new plan has no addon plans defined, keep all addons (backward compatibility)
|
|
24672
|
+
if (!(newPlan === null || newPlan === void 0 ? void 0 : newPlan.addonPlans) || newPlan.addonPlans.length === 0) {
|
|
24673
|
+
return true;
|
|
24674
|
+
}
|
|
24675
|
+
// Check if this addon subscription's plan is in the new plan's addonPlans list
|
|
24676
|
+
const addonPlanId = (_a = addonSub.plan) === null || _a === void 0 ? void 0 : _a.id;
|
|
24677
|
+
return addonPlanId ? addonPlanIds.includes(addonPlanId) : false;
|
|
24678
|
+
});
|
|
24679
|
+
splitSubscriptions.push({
|
|
24680
|
+
...maskedSubscription,
|
|
24681
|
+
// Keep the original subscription ID intact (from maskedSubscription spread)
|
|
24682
|
+
// Update priceList from the charge
|
|
24683
|
+
priceList: firstCharge.priceList,
|
|
24684
|
+
// Update plan from the charge's priceList.plan
|
|
24685
|
+
plan: firstCharge.priceList.plan,
|
|
24686
|
+
// Update product from the charge's priceList.product
|
|
24687
|
+
product: firstCharge.priceList.product,
|
|
24688
|
+
// Update endDate to match the latest charge endDate in this group
|
|
24689
|
+
endDate: latestEndDate,
|
|
24690
|
+
// Update startDate to match the earliest charge startDate in this group
|
|
24691
|
+
startDate: earliestStartDate,
|
|
24692
|
+
// Update state based on the charge dates
|
|
24693
|
+
state,
|
|
24694
|
+
// Set evergreen to false for subscriptions that won't renew (will transition instead)
|
|
24695
|
+
evergreen: willRenew ? subscription.evergreen : false,
|
|
24696
|
+
// Filter chargeReport to only include charges for this price list
|
|
24697
|
+
chargeReport: charges,
|
|
24698
|
+
// Filter charges array to only include charges for this price list
|
|
24699
|
+
charges: (_f = (_e = subscription.charges) === null || _e === void 0 ? void 0 : _e.filter(charge => {
|
|
24700
|
+
var _a;
|
|
24701
|
+
const chargeWithPriceList = charge;
|
|
24702
|
+
return ((_a = chargeWithPriceList.priceList) === null || _a === void 0 ? void 0 : _a.id) === priceListId;
|
|
24703
|
+
})) !== null && _f !== void 0 ? _f : [],
|
|
24704
|
+
// Filter addon subscriptions to only include those compatible with the new plan
|
|
24705
|
+
addonSubscriptions: filteredAddonSubscriptions,
|
|
24706
|
+
});
|
|
24707
|
+
});
|
|
24708
|
+
// Sort split subscriptions by endDate (earliest first)
|
|
24709
|
+
splitSubscriptions.sort((a, b) => {
|
|
24710
|
+
const aData = readFragment(splitSubscriptionsByPriceList_SubscriptionFragment, a);
|
|
24711
|
+
const bData = readFragment(splitSubscriptionsByPriceList_SubscriptionFragment, b);
|
|
24712
|
+
const aEnd = getDateString(aData.endDate);
|
|
24713
|
+
const bEnd = getDateString(bData.endDate);
|
|
24714
|
+
const aStart = getDateString(aData.startDate);
|
|
24715
|
+
const bStart = getDateString(bData.startDate);
|
|
24716
|
+
if (aEnd < bEnd)
|
|
24717
|
+
return -1;
|
|
24718
|
+
if (aEnd > bEnd)
|
|
24719
|
+
return 1;
|
|
24720
|
+
// If endDates are equal, sort by startDate
|
|
24721
|
+
if (aStart < bStart)
|
|
24722
|
+
return -1;
|
|
24723
|
+
if (aStart > bStart)
|
|
24724
|
+
return 1;
|
|
24725
|
+
return 0;
|
|
24726
|
+
});
|
|
24727
|
+
result.push(...splitSubscriptions);
|
|
24728
|
+
}
|
|
24729
|
+
});
|
|
24730
|
+
return result;
|
|
24731
|
+
}
|
|
24513
24732
|
|
|
24514
24733
|
const SubscriptionCardActions_PriceListChangeOptionsFragment = t(`
|
|
24515
24734
|
fragment SubscriptionCardActions_PriceListChangeOptionsFragment on PriceListChangeOptions {
|
|
@@ -25411,12 +25630,14 @@ const SubscriptionsList_SubscriptionFragment = t(`
|
|
|
25411
25630
|
fragment SubscriptionsList_SubscriptionFragment on Subscription {
|
|
25412
25631
|
id
|
|
25413
25632
|
state
|
|
25633
|
+
...splitSubscriptionsByPriceList_SubscriptionFragment
|
|
25414
25634
|
...findNonAddonSubscriptions_SubscriptionFragment
|
|
25415
25635
|
...SubscriptionCardDesktop_SubscriptionFragment
|
|
25416
25636
|
...SubscriptionCardMobile_SubscriptionFragment
|
|
25417
25637
|
...AddonSubscriptionsCards_SubscriptionFragment
|
|
25418
25638
|
}
|
|
25419
25639
|
`, [
|
|
25640
|
+
splitSubscriptionsByPriceList_SubscriptionFragment,
|
|
25420
25641
|
findNonAddonSubscriptions_SubscriptionFragment,
|
|
25421
25642
|
SubscriptionCardDesktop_SubscriptionFragment,
|
|
25422
25643
|
SubscriptionCardMobile_SubscriptionFragment,
|
|
@@ -25427,15 +25648,17 @@ const SubscriptionsList = ({ showInactive, subscriptions: maskedSubscriptions, }
|
|
|
25427
25648
|
const { gap } = useSubscriptionProps();
|
|
25428
25649
|
// Read fragments
|
|
25429
25650
|
const subscriptions = maskedSubscriptions.map(maskedSubscription => readFragment(SubscriptionsList_SubscriptionFragment, maskedSubscription));
|
|
25651
|
+
// Split subscriptions by price list
|
|
25652
|
+
const splitSubscriptions = splitSubscriptionsByPriceList(subscriptions);
|
|
25430
25653
|
// Hooks
|
|
25431
25654
|
const isMobile = useIsMobile();
|
|
25432
|
-
const nonAddonSubscriptions = findNonAddonSubscriptions(
|
|
25655
|
+
const nonAddonSubscriptions = findNonAddonSubscriptions(splitSubscriptions);
|
|
25433
25656
|
return (jsx(Fragment, { children: nonAddonSubscriptions === null || nonAddonSubscriptions === void 0 ? void 0 : nonAddonSubscriptions.map((subscription, subscriptionIndex) => {
|
|
25434
25657
|
if (!showInactive && isSubscriptionNotActive(subscription.state))
|
|
25435
25658
|
return null;
|
|
25436
25659
|
if (isMobile)
|
|
25437
|
-
return (jsxs("div", { className: `bunny-flex bunny-flex-col bunny-gap-${gap}`, children: [jsx(SubscriptionCard, { subscription: subscription }), jsx(AddonSubscriptionsCards, { subscription: subscription, subscriptions:
|
|
25438
|
-
return (jsxs("div", { className: `bunny-flex bunny-flex-col bunny-gap-${gap}`, children: [jsx(SubscriptionCardDesktop, { subscription: subscription }), jsx(AddonSubscriptionsCards, { subscription: subscription, subscriptions:
|
|
25660
|
+
return (jsxs("div", { className: `bunny-flex bunny-flex-col bunny-gap-${gap}`, children: [jsx(SubscriptionCard, { subscription: subscription }), jsx(AddonSubscriptionsCards, { subscription: subscription, subscriptions: splitSubscriptions, showInactive: showInactive })] }, subscriptionIndex));
|
|
25661
|
+
return (jsxs("div", { className: `bunny-flex bunny-flex-col bunny-gap-${gap}`, children: [jsx(SubscriptionCardDesktop, { subscription: subscription }), jsx(AddonSubscriptionsCards, { subscription: subscription, subscriptions: splitSubscriptions, showInactive: showInactive })] }, subscriptionIndex));
|
|
25439
25662
|
}) }));
|
|
25440
25663
|
};
|
|
25441
25664
|
|
package/dist/esm/types/src/components/Subscriptions/subscriptionsList/SubscriptionsList.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export declare const SubscriptionsList_SubscriptionFragment: import("gql.tada").
|
|
|
3
3
|
id: string;
|
|
4
4
|
state: "ACTIVE" | "TRIAL" | "CANCELED" | "EXPIRED" | "TRIAL_EXPIRED" | "PENDING";
|
|
5
5
|
[$tada.fragmentRefs]: {
|
|
6
|
+
splitSubscriptionsByPriceList_SubscriptionFragment: "Subscription";
|
|
7
|
+
} & {
|
|
6
8
|
findNonAddonSubscriptions_SubscriptionFragment: "Subscription";
|
|
7
9
|
} & {
|
|
8
10
|
SubscriptionCardDesktop_SubscriptionFragment: "Subscription";
|
package/dist/esm/types/src/components/Subscriptions/subscriptionsList/subscriptionUtils.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Plan } from '@/types/Plan';
|
|
|
3
3
|
import Product from '@/types/Product';
|
|
4
4
|
import Subscription from '@/types/Subscription';
|
|
5
5
|
import { SubscriptionCharge } from '@/types/SubscriptionCharge';
|
|
6
|
+
import { FragmentOf } from 'gql.tada';
|
|
6
7
|
export declare const isSubscriptionNotActive: (subscriptionState: SubscriptionState) => boolean;
|
|
7
8
|
export declare const productPlanName: ({ plan, product }: {
|
|
8
9
|
plan: Plan;
|
|
@@ -16,3 +17,61 @@ export declare const hasPriceTiers: (charge: {
|
|
|
16
17
|
priceTiers: {}[] | null;
|
|
17
18
|
}) => boolean;
|
|
18
19
|
export declare const getUpdatingChargeQuantityId: (charge: SubscriptionCharge, subscription: Subscription) => string;
|
|
20
|
+
export declare const splitSubscriptionsByPriceList_SubscriptionFragment: import("gql.tada").TadaDocumentNode<{
|
|
21
|
+
id: string;
|
|
22
|
+
state: "ACTIVE" | "TRIAL" | "CANCELED" | "EXPIRED" | "TRIAL_EXPIRED" | "PENDING";
|
|
23
|
+
evergreen: boolean;
|
|
24
|
+
startDate: unknown;
|
|
25
|
+
endDate: unknown;
|
|
26
|
+
addonSubscriptions: {
|
|
27
|
+
id: string;
|
|
28
|
+
priceList: {
|
|
29
|
+
id: string;
|
|
30
|
+
} | null;
|
|
31
|
+
plan: {
|
|
32
|
+
id: string;
|
|
33
|
+
} | null;
|
|
34
|
+
}[] | null;
|
|
35
|
+
chargeReport: {
|
|
36
|
+
kind: "SUBSCRIBE" | "UPDATE" | "RENEW" | "REINSTATE" | "UNSUBSCRIBE" | "ADJUSTMENT" | "COUPON" | "DISCOUNT" | "CREDIT" | "PRICE_UPDATE" | "QUANTITY_UPDATE" | "FREE_PERIOD_DISCOUNT" | "ACTIVATE" | null;
|
|
37
|
+
startDate: unknown;
|
|
38
|
+
endDate: unknown;
|
|
39
|
+
priceList: {
|
|
40
|
+
id: string;
|
|
41
|
+
plan: {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
selfServiceBuy: boolean | null;
|
|
45
|
+
selfServiceCancel: boolean | null;
|
|
46
|
+
selfServiceRenew: boolean | null;
|
|
47
|
+
addon: boolean | null;
|
|
48
|
+
addonPlans: {
|
|
49
|
+
id: string;
|
|
50
|
+
}[] | null;
|
|
51
|
+
} | null;
|
|
52
|
+
product: {
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
showProductNameOnLineItem: boolean | null;
|
|
56
|
+
} | null;
|
|
57
|
+
periodMonths: number | null;
|
|
58
|
+
name: string;
|
|
59
|
+
} | null;
|
|
60
|
+
}[] | null;
|
|
61
|
+
charges: {
|
|
62
|
+
priceList: {
|
|
63
|
+
id: string;
|
|
64
|
+
} | null;
|
|
65
|
+
}[] | null;
|
|
66
|
+
}, {}, {
|
|
67
|
+
fragment: "splitSubscriptionsByPriceList_SubscriptionFragment";
|
|
68
|
+
on: "Subscription";
|
|
69
|
+
masked: true;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Splits a subscription into multiple subscriptions if it has RENEW charges
|
|
73
|
+
* with different price lists.
|
|
74
|
+
* This handles cases where a subscription has been changed to a different plan/price list.
|
|
75
|
+
* Each resulting subscription is a complete duplicate with just the charges filtered.
|
|
76
|
+
*/
|
|
77
|
+
export declare function splitSubscriptionsByPriceList<T extends FragmentOf<typeof splitSubscriptionsByPriceList_SubscriptionFragment>>(maskedSubscriptions: T[]): T[];
|
package/package.json
CHANGED