@bunnyapp/components 1.7.0-beta.15 → 1.7.0-beta.16
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 +200 -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 +50 -0
- package/dist/esm/index.js +200 -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 +50 -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.15';
|
|
1284
1284
|
const createRequestHeaders = (token) => {
|
|
1285
1285
|
const headers = createClientDevHeaders({ token });
|
|
1286
1286
|
// Add the components version header
|
|
@@ -24512,6 +24512,198 @@ 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
|
+
chargeReport {
|
|
24523
|
+
kind
|
|
24524
|
+
startDate
|
|
24525
|
+
endDate
|
|
24526
|
+
priceList {
|
|
24527
|
+
id
|
|
24528
|
+
plan {
|
|
24529
|
+
id
|
|
24530
|
+
name
|
|
24531
|
+
selfServiceBuy
|
|
24532
|
+
selfServiceCancel
|
|
24533
|
+
selfServiceRenew
|
|
24534
|
+
addon
|
|
24535
|
+
addonPlans {
|
|
24536
|
+
id
|
|
24537
|
+
}
|
|
24538
|
+
}
|
|
24539
|
+
product {
|
|
24540
|
+
id
|
|
24541
|
+
name
|
|
24542
|
+
showProductNameOnLineItem
|
|
24543
|
+
}
|
|
24544
|
+
periodMonths
|
|
24545
|
+
name
|
|
24546
|
+
}
|
|
24547
|
+
}
|
|
24548
|
+
charges {
|
|
24549
|
+
priceList {
|
|
24550
|
+
id
|
|
24551
|
+
}
|
|
24552
|
+
}
|
|
24553
|
+
}
|
|
24554
|
+
`, []);
|
|
24555
|
+
// Helper to safely get date as string
|
|
24556
|
+
const getDateString = (date) => {
|
|
24557
|
+
return date;
|
|
24558
|
+
};
|
|
24559
|
+
/**
|
|
24560
|
+
* Splits a subscription into multiple subscriptions if it has RENEW charges
|
|
24561
|
+
* with different price lists.
|
|
24562
|
+
* This handles cases where a subscription has been changed to a different plan/price list.
|
|
24563
|
+
* Each resulting subscription is a complete duplicate with just the charges filtered.
|
|
24564
|
+
*/
|
|
24565
|
+
function splitSubscriptionsByPriceList(maskedSubscriptions) {
|
|
24566
|
+
const result = [];
|
|
24567
|
+
maskedSubscriptions.forEach(maskedSubscription => {
|
|
24568
|
+
var _a;
|
|
24569
|
+
// Read the fragment to get typed data
|
|
24570
|
+
const subscription = readFragment(splitSubscriptionsByPriceList_SubscriptionFragment, maskedSubscription);
|
|
24571
|
+
// Skip if no chargeReport
|
|
24572
|
+
if (!subscription.chargeReport) {
|
|
24573
|
+
result.push(maskedSubscription);
|
|
24574
|
+
return;
|
|
24575
|
+
}
|
|
24576
|
+
// Group chargeReport by priceListId
|
|
24577
|
+
const chargesByPriceList = new Map();
|
|
24578
|
+
subscription.chargeReport.forEach(charge => {
|
|
24579
|
+
var _a;
|
|
24580
|
+
const priceListId = (_a = charge.priceList) === null || _a === void 0 ? void 0 : _a.id;
|
|
24581
|
+
if (!priceListId)
|
|
24582
|
+
return;
|
|
24583
|
+
if (!chargesByPriceList.has(priceListId)) {
|
|
24584
|
+
chargesByPriceList.set(priceListId, []);
|
|
24585
|
+
}
|
|
24586
|
+
chargesByPriceList.get(priceListId).push(charge);
|
|
24587
|
+
});
|
|
24588
|
+
// Check if there are RENEW charges with different price lists
|
|
24589
|
+
const hasRenewChargesWithDifferentPriceLists = chargesByPriceList.size > 1 &&
|
|
24590
|
+
subscription.chargeReport.some(charge => charge.kind === 'RENEW');
|
|
24591
|
+
// If all charges have the same priceListId or no RENEW charges, keep subscription as-is
|
|
24592
|
+
if (!hasRenewChargesWithDifferentPriceLists) {
|
|
24593
|
+
result.push(maskedSubscription);
|
|
24594
|
+
}
|
|
24595
|
+
else {
|
|
24596
|
+
// Find the charge with the latest endDate to determine which subscription will actually renew
|
|
24597
|
+
const latestCharge = subscription.chargeReport.reduce((latest, current) => {
|
|
24598
|
+
if (!latest)
|
|
24599
|
+
return current;
|
|
24600
|
+
// Compare endDates, and if equal, compare startDates
|
|
24601
|
+
const currentEnd = getDateString(current.endDate);
|
|
24602
|
+
const latestEnd = getDateString(latest.endDate);
|
|
24603
|
+
const currentStart = getDateString(current.startDate);
|
|
24604
|
+
const latestStart = getDateString(latest.startDate);
|
|
24605
|
+
if (currentEnd > latestEnd)
|
|
24606
|
+
return current;
|
|
24607
|
+
if (currentEnd === latestEnd && currentStart > latestStart)
|
|
24608
|
+
return current;
|
|
24609
|
+
return latest;
|
|
24610
|
+
}, subscription.chargeReport[0]);
|
|
24611
|
+
const latestPriceListId = (_a = latestCharge.priceList) === null || _a === void 0 ? void 0 : _a.id;
|
|
24612
|
+
if (!latestPriceListId) {
|
|
24613
|
+
result.push(maskedSubscription);
|
|
24614
|
+
return;
|
|
24615
|
+
}
|
|
24616
|
+
// Split into multiple subscriptions, one per price list
|
|
24617
|
+
// DUPLICATE the entire subscription, just filter the charges
|
|
24618
|
+
const splitSubscriptions = [];
|
|
24619
|
+
chargesByPriceList.forEach((charges, priceListId) => {
|
|
24620
|
+
var _a, _b;
|
|
24621
|
+
// Get the first charge to extract priceList, plan, and product info
|
|
24622
|
+
const firstCharge = charges === null || charges === void 0 ? void 0 : charges[0];
|
|
24623
|
+
if (!firstCharge || !charges)
|
|
24624
|
+
return;
|
|
24625
|
+
// Find the latest endDate from the charges in this group
|
|
24626
|
+
const latestEndDate = charges.reduce((latest, current) => {
|
|
24627
|
+
const currentEnd = getDateString(current.endDate);
|
|
24628
|
+
if (!latest)
|
|
24629
|
+
return currentEnd;
|
|
24630
|
+
return currentEnd > latest ? currentEnd : latest;
|
|
24631
|
+
}, getDateString(charges[0].endDate));
|
|
24632
|
+
// Find the earliest startDate from the charges in this group
|
|
24633
|
+
const earliestStartDate = charges.reduce((earliest, current) => {
|
|
24634
|
+
const currentStart = getDateString(current.startDate);
|
|
24635
|
+
if (!earliest)
|
|
24636
|
+
return currentStart;
|
|
24637
|
+
return currentStart < earliest ? currentStart : earliest;
|
|
24638
|
+
}, getDateString(charges[0].startDate));
|
|
24639
|
+
// Determine the state based on the date range of charges
|
|
24640
|
+
const now = new Date().toISOString().split('T')[0]; // Get current date in YYYY-MM-DD format
|
|
24641
|
+
let state = subscription.state;
|
|
24642
|
+
// If the latest endDate is before today, it's expired
|
|
24643
|
+
if (latestEndDate < now) {
|
|
24644
|
+
state = t.scalar('SubscriptionState', 'EXPIRED');
|
|
24645
|
+
}
|
|
24646
|
+
// If the earliest startDate is after today, it's pending
|
|
24647
|
+
else if (earliestStartDate > now) {
|
|
24648
|
+
state = t.scalar('SubscriptionState', 'PENDING');
|
|
24649
|
+
}
|
|
24650
|
+
// Otherwise, if current date is within the range, keep it active
|
|
24651
|
+
else if (earliestStartDate <= now && latestEndDate >= now) {
|
|
24652
|
+
state = t.scalar('SubscriptionState', 'ACTIVE');
|
|
24653
|
+
}
|
|
24654
|
+
// Only the subscription with the latest charge will actually renew
|
|
24655
|
+
const willRenew = priceListId === latestPriceListId;
|
|
24656
|
+
splitSubscriptions.push({
|
|
24657
|
+
...maskedSubscription,
|
|
24658
|
+
// Keep the original subscription ID intact (from maskedSubscription spread)
|
|
24659
|
+
// Update priceList from the charge
|
|
24660
|
+
priceList: firstCharge.priceList,
|
|
24661
|
+
// Update plan from the charge's priceList.plan
|
|
24662
|
+
plan: firstCharge.priceList.plan,
|
|
24663
|
+
// Update product from the charge's priceList.product
|
|
24664
|
+
product: firstCharge.priceList.product,
|
|
24665
|
+
// Update endDate to match the latest charge endDate in this group
|
|
24666
|
+
endDate: latestEndDate,
|
|
24667
|
+
// Update startDate to match the earliest charge startDate in this group
|
|
24668
|
+
startDate: earliestStartDate,
|
|
24669
|
+
// Update state based on the charge dates
|
|
24670
|
+
state,
|
|
24671
|
+
// Set evergreen to false for subscriptions that won't renew (will transition instead)
|
|
24672
|
+
evergreen: willRenew ? subscription.evergreen : false,
|
|
24673
|
+
// Filter chargeReport to only include charges for this price list
|
|
24674
|
+
chargeReport: charges,
|
|
24675
|
+
// Filter charges array to only include charges for this price list
|
|
24676
|
+
charges: (_b = (_a = subscription.charges) === null || _a === void 0 ? void 0 : _a.filter(charge => {
|
|
24677
|
+
var _a;
|
|
24678
|
+
const chargeWithPriceList = charge;
|
|
24679
|
+
return ((_a = chargeWithPriceList.priceList) === null || _a === void 0 ? void 0 : _a.id) === priceListId;
|
|
24680
|
+
})) !== null && _b !== void 0 ? _b : [],
|
|
24681
|
+
});
|
|
24682
|
+
});
|
|
24683
|
+
// Sort split subscriptions by endDate (earliest first)
|
|
24684
|
+
splitSubscriptions.sort((a, b) => {
|
|
24685
|
+
const aData = readFragment(splitSubscriptionsByPriceList_SubscriptionFragment, a);
|
|
24686
|
+
const bData = readFragment(splitSubscriptionsByPriceList_SubscriptionFragment, b);
|
|
24687
|
+
const aEnd = getDateString(aData.endDate);
|
|
24688
|
+
const bEnd = getDateString(bData.endDate);
|
|
24689
|
+
const aStart = getDateString(aData.startDate);
|
|
24690
|
+
const bStart = getDateString(bData.startDate);
|
|
24691
|
+
if (aEnd < bEnd)
|
|
24692
|
+
return -1;
|
|
24693
|
+
if (aEnd > bEnd)
|
|
24694
|
+
return 1;
|
|
24695
|
+
// If endDates are equal, sort by startDate
|
|
24696
|
+
if (aStart < bStart)
|
|
24697
|
+
return -1;
|
|
24698
|
+
if (aStart > bStart)
|
|
24699
|
+
return 1;
|
|
24700
|
+
return 0;
|
|
24701
|
+
});
|
|
24702
|
+
result.push(...splitSubscriptions);
|
|
24703
|
+
}
|
|
24704
|
+
});
|
|
24705
|
+
return result;
|
|
24706
|
+
}
|
|
24515
24707
|
|
|
24516
24708
|
const SubscriptionCardActions_PriceListChangeOptionsFragment = t(`
|
|
24517
24709
|
fragment SubscriptionCardActions_PriceListChangeOptionsFragment on PriceListChangeOptions {
|
|
@@ -25413,12 +25605,14 @@ const SubscriptionsList_SubscriptionFragment = t(`
|
|
|
25413
25605
|
fragment SubscriptionsList_SubscriptionFragment on Subscription {
|
|
25414
25606
|
id
|
|
25415
25607
|
state
|
|
25608
|
+
...splitSubscriptionsByPriceList_SubscriptionFragment
|
|
25416
25609
|
...findNonAddonSubscriptions_SubscriptionFragment
|
|
25417
25610
|
...SubscriptionCardDesktop_SubscriptionFragment
|
|
25418
25611
|
...SubscriptionCardMobile_SubscriptionFragment
|
|
25419
25612
|
...AddonSubscriptionsCards_SubscriptionFragment
|
|
25420
25613
|
}
|
|
25421
25614
|
`, [
|
|
25615
|
+
splitSubscriptionsByPriceList_SubscriptionFragment,
|
|
25422
25616
|
findNonAddonSubscriptions_SubscriptionFragment,
|
|
25423
25617
|
SubscriptionCardDesktop_SubscriptionFragment,
|
|
25424
25618
|
SubscriptionCardMobile_SubscriptionFragment,
|
|
@@ -25429,15 +25623,17 @@ const SubscriptionsList = ({ showInactive, subscriptions: maskedSubscriptions, }
|
|
|
25429
25623
|
const { gap } = useSubscriptionProps();
|
|
25430
25624
|
// Read fragments
|
|
25431
25625
|
const subscriptions = maskedSubscriptions.map(maskedSubscription => readFragment(SubscriptionsList_SubscriptionFragment, maskedSubscription));
|
|
25626
|
+
// Split subscriptions by price list
|
|
25627
|
+
const splitSubscriptions = splitSubscriptionsByPriceList(subscriptions);
|
|
25432
25628
|
// Hooks
|
|
25433
25629
|
const isMobile = useIsMobile();
|
|
25434
|
-
const nonAddonSubscriptions = findNonAddonSubscriptions(
|
|
25630
|
+
const nonAddonSubscriptions = findNonAddonSubscriptions(splitSubscriptions);
|
|
25435
25631
|
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: nonAddonSubscriptions === null || nonAddonSubscriptions === void 0 ? void 0 : nonAddonSubscriptions.map((subscription, subscriptionIndex) => {
|
|
25436
25632
|
if (!showInactive && isSubscriptionNotActive(subscription.state))
|
|
25437
25633
|
return null;
|
|
25438
25634
|
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:
|
|
25635
|
+
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));
|
|
25636
|
+
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
25637
|
}) }));
|
|
25442
25638
|
};
|
|
25443
25639
|
|
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,52 @@ 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
|
+
chargeReport: {
|
|
27
|
+
kind: "SUBSCRIBE" | "UPDATE" | "RENEW" | "REINSTATE" | "UNSUBSCRIBE" | "ADJUSTMENT" | "COUPON" | "DISCOUNT" | "CREDIT" | "PRICE_UPDATE" | "QUANTITY_UPDATE" | "FREE_PERIOD_DISCOUNT" | "ACTIVATE" | null;
|
|
28
|
+
startDate: unknown;
|
|
29
|
+
endDate: unknown;
|
|
30
|
+
priceList: {
|
|
31
|
+
id: string;
|
|
32
|
+
plan: {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
selfServiceBuy: boolean | null;
|
|
36
|
+
selfServiceCancel: boolean | null;
|
|
37
|
+
selfServiceRenew: boolean | null;
|
|
38
|
+
addon: boolean | null;
|
|
39
|
+
addonPlans: {
|
|
40
|
+
id: string;
|
|
41
|
+
}[] | null;
|
|
42
|
+
} | null;
|
|
43
|
+
product: {
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
showProductNameOnLineItem: boolean | null;
|
|
47
|
+
} | null;
|
|
48
|
+
periodMonths: number | null;
|
|
49
|
+
name: string;
|
|
50
|
+
} | null;
|
|
51
|
+
}[] | null;
|
|
52
|
+
charges: {
|
|
53
|
+
priceList: {
|
|
54
|
+
id: string;
|
|
55
|
+
} | null;
|
|
56
|
+
}[] | null;
|
|
57
|
+
}, {}, {
|
|
58
|
+
fragment: "splitSubscriptionsByPriceList_SubscriptionFragment";
|
|
59
|
+
on: "Subscription";
|
|
60
|
+
masked: true;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Splits a subscription into multiple subscriptions if it has RENEW charges
|
|
64
|
+
* with different price lists.
|
|
65
|
+
* This handles cases where a subscription has been changed to a different plan/price list.
|
|
66
|
+
* Each resulting subscription is a complete duplicate with just the charges filtered.
|
|
67
|
+
*/
|
|
68
|
+
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.15';
|
|
1282
1282
|
const createRequestHeaders = (token) => {
|
|
1283
1283
|
const headers = createClientDevHeaders({ token });
|
|
1284
1284
|
// Add the components version header
|
|
@@ -24510,6 +24510,198 @@ 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
|
+
chargeReport {
|
|
24521
|
+
kind
|
|
24522
|
+
startDate
|
|
24523
|
+
endDate
|
|
24524
|
+
priceList {
|
|
24525
|
+
id
|
|
24526
|
+
plan {
|
|
24527
|
+
id
|
|
24528
|
+
name
|
|
24529
|
+
selfServiceBuy
|
|
24530
|
+
selfServiceCancel
|
|
24531
|
+
selfServiceRenew
|
|
24532
|
+
addon
|
|
24533
|
+
addonPlans {
|
|
24534
|
+
id
|
|
24535
|
+
}
|
|
24536
|
+
}
|
|
24537
|
+
product {
|
|
24538
|
+
id
|
|
24539
|
+
name
|
|
24540
|
+
showProductNameOnLineItem
|
|
24541
|
+
}
|
|
24542
|
+
periodMonths
|
|
24543
|
+
name
|
|
24544
|
+
}
|
|
24545
|
+
}
|
|
24546
|
+
charges {
|
|
24547
|
+
priceList {
|
|
24548
|
+
id
|
|
24549
|
+
}
|
|
24550
|
+
}
|
|
24551
|
+
}
|
|
24552
|
+
`, []);
|
|
24553
|
+
// Helper to safely get date as string
|
|
24554
|
+
const getDateString = (date) => {
|
|
24555
|
+
return date;
|
|
24556
|
+
};
|
|
24557
|
+
/**
|
|
24558
|
+
* Splits a subscription into multiple subscriptions if it has RENEW charges
|
|
24559
|
+
* with different price lists.
|
|
24560
|
+
* This handles cases where a subscription has been changed to a different plan/price list.
|
|
24561
|
+
* Each resulting subscription is a complete duplicate with just the charges filtered.
|
|
24562
|
+
*/
|
|
24563
|
+
function splitSubscriptionsByPriceList(maskedSubscriptions) {
|
|
24564
|
+
const result = [];
|
|
24565
|
+
maskedSubscriptions.forEach(maskedSubscription => {
|
|
24566
|
+
var _a;
|
|
24567
|
+
// Read the fragment to get typed data
|
|
24568
|
+
const subscription = readFragment(splitSubscriptionsByPriceList_SubscriptionFragment, maskedSubscription);
|
|
24569
|
+
// Skip if no chargeReport
|
|
24570
|
+
if (!subscription.chargeReport) {
|
|
24571
|
+
result.push(maskedSubscription);
|
|
24572
|
+
return;
|
|
24573
|
+
}
|
|
24574
|
+
// Group chargeReport by priceListId
|
|
24575
|
+
const chargesByPriceList = new Map();
|
|
24576
|
+
subscription.chargeReport.forEach(charge => {
|
|
24577
|
+
var _a;
|
|
24578
|
+
const priceListId = (_a = charge.priceList) === null || _a === void 0 ? void 0 : _a.id;
|
|
24579
|
+
if (!priceListId)
|
|
24580
|
+
return;
|
|
24581
|
+
if (!chargesByPriceList.has(priceListId)) {
|
|
24582
|
+
chargesByPriceList.set(priceListId, []);
|
|
24583
|
+
}
|
|
24584
|
+
chargesByPriceList.get(priceListId).push(charge);
|
|
24585
|
+
});
|
|
24586
|
+
// Check if there are RENEW charges with different price lists
|
|
24587
|
+
const hasRenewChargesWithDifferentPriceLists = chargesByPriceList.size > 1 &&
|
|
24588
|
+
subscription.chargeReport.some(charge => charge.kind === 'RENEW');
|
|
24589
|
+
// If all charges have the same priceListId or no RENEW charges, keep subscription as-is
|
|
24590
|
+
if (!hasRenewChargesWithDifferentPriceLists) {
|
|
24591
|
+
result.push(maskedSubscription);
|
|
24592
|
+
}
|
|
24593
|
+
else {
|
|
24594
|
+
// Find the charge with the latest endDate to determine which subscription will actually renew
|
|
24595
|
+
const latestCharge = subscription.chargeReport.reduce((latest, current) => {
|
|
24596
|
+
if (!latest)
|
|
24597
|
+
return current;
|
|
24598
|
+
// Compare endDates, and if equal, compare startDates
|
|
24599
|
+
const currentEnd = getDateString(current.endDate);
|
|
24600
|
+
const latestEnd = getDateString(latest.endDate);
|
|
24601
|
+
const currentStart = getDateString(current.startDate);
|
|
24602
|
+
const latestStart = getDateString(latest.startDate);
|
|
24603
|
+
if (currentEnd > latestEnd)
|
|
24604
|
+
return current;
|
|
24605
|
+
if (currentEnd === latestEnd && currentStart > latestStart)
|
|
24606
|
+
return current;
|
|
24607
|
+
return latest;
|
|
24608
|
+
}, subscription.chargeReport[0]);
|
|
24609
|
+
const latestPriceListId = (_a = latestCharge.priceList) === null || _a === void 0 ? void 0 : _a.id;
|
|
24610
|
+
if (!latestPriceListId) {
|
|
24611
|
+
result.push(maskedSubscription);
|
|
24612
|
+
return;
|
|
24613
|
+
}
|
|
24614
|
+
// Split into multiple subscriptions, one per price list
|
|
24615
|
+
// DUPLICATE the entire subscription, just filter the charges
|
|
24616
|
+
const splitSubscriptions = [];
|
|
24617
|
+
chargesByPriceList.forEach((charges, priceListId) => {
|
|
24618
|
+
var _a, _b;
|
|
24619
|
+
// Get the first charge to extract priceList, plan, and product info
|
|
24620
|
+
const firstCharge = charges === null || charges === void 0 ? void 0 : charges[0];
|
|
24621
|
+
if (!firstCharge || !charges)
|
|
24622
|
+
return;
|
|
24623
|
+
// Find the latest endDate from the charges in this group
|
|
24624
|
+
const latestEndDate = charges.reduce((latest, current) => {
|
|
24625
|
+
const currentEnd = getDateString(current.endDate);
|
|
24626
|
+
if (!latest)
|
|
24627
|
+
return currentEnd;
|
|
24628
|
+
return currentEnd > latest ? currentEnd : latest;
|
|
24629
|
+
}, getDateString(charges[0].endDate));
|
|
24630
|
+
// Find the earliest startDate from the charges in this group
|
|
24631
|
+
const earliestStartDate = charges.reduce((earliest, current) => {
|
|
24632
|
+
const currentStart = getDateString(current.startDate);
|
|
24633
|
+
if (!earliest)
|
|
24634
|
+
return currentStart;
|
|
24635
|
+
return currentStart < earliest ? currentStart : earliest;
|
|
24636
|
+
}, getDateString(charges[0].startDate));
|
|
24637
|
+
// Determine the state based on the date range of charges
|
|
24638
|
+
const now = new Date().toISOString().split('T')[0]; // Get current date in YYYY-MM-DD format
|
|
24639
|
+
let state = subscription.state;
|
|
24640
|
+
// If the latest endDate is before today, it's expired
|
|
24641
|
+
if (latestEndDate < now) {
|
|
24642
|
+
state = t.scalar('SubscriptionState', 'EXPIRED');
|
|
24643
|
+
}
|
|
24644
|
+
// If the earliest startDate is after today, it's pending
|
|
24645
|
+
else if (earliestStartDate > now) {
|
|
24646
|
+
state = t.scalar('SubscriptionState', 'PENDING');
|
|
24647
|
+
}
|
|
24648
|
+
// Otherwise, if current date is within the range, keep it active
|
|
24649
|
+
else if (earliestStartDate <= now && latestEndDate >= now) {
|
|
24650
|
+
state = t.scalar('SubscriptionState', 'ACTIVE');
|
|
24651
|
+
}
|
|
24652
|
+
// Only the subscription with the latest charge will actually renew
|
|
24653
|
+
const willRenew = priceListId === latestPriceListId;
|
|
24654
|
+
splitSubscriptions.push({
|
|
24655
|
+
...maskedSubscription,
|
|
24656
|
+
// Keep the original subscription ID intact (from maskedSubscription spread)
|
|
24657
|
+
// Update priceList from the charge
|
|
24658
|
+
priceList: firstCharge.priceList,
|
|
24659
|
+
// Update plan from the charge's priceList.plan
|
|
24660
|
+
plan: firstCharge.priceList.plan,
|
|
24661
|
+
// Update product from the charge's priceList.product
|
|
24662
|
+
product: firstCharge.priceList.product,
|
|
24663
|
+
// Update endDate to match the latest charge endDate in this group
|
|
24664
|
+
endDate: latestEndDate,
|
|
24665
|
+
// Update startDate to match the earliest charge startDate in this group
|
|
24666
|
+
startDate: earliestStartDate,
|
|
24667
|
+
// Update state based on the charge dates
|
|
24668
|
+
state,
|
|
24669
|
+
// Set evergreen to false for subscriptions that won't renew (will transition instead)
|
|
24670
|
+
evergreen: willRenew ? subscription.evergreen : false,
|
|
24671
|
+
// Filter chargeReport to only include charges for this price list
|
|
24672
|
+
chargeReport: charges,
|
|
24673
|
+
// Filter charges array to only include charges for this price list
|
|
24674
|
+
charges: (_b = (_a = subscription.charges) === null || _a === void 0 ? void 0 : _a.filter(charge => {
|
|
24675
|
+
var _a;
|
|
24676
|
+
const chargeWithPriceList = charge;
|
|
24677
|
+
return ((_a = chargeWithPriceList.priceList) === null || _a === void 0 ? void 0 : _a.id) === priceListId;
|
|
24678
|
+
})) !== null && _b !== void 0 ? _b : [],
|
|
24679
|
+
});
|
|
24680
|
+
});
|
|
24681
|
+
// Sort split subscriptions by endDate (earliest first)
|
|
24682
|
+
splitSubscriptions.sort((a, b) => {
|
|
24683
|
+
const aData = readFragment(splitSubscriptionsByPriceList_SubscriptionFragment, a);
|
|
24684
|
+
const bData = readFragment(splitSubscriptionsByPriceList_SubscriptionFragment, b);
|
|
24685
|
+
const aEnd = getDateString(aData.endDate);
|
|
24686
|
+
const bEnd = getDateString(bData.endDate);
|
|
24687
|
+
const aStart = getDateString(aData.startDate);
|
|
24688
|
+
const bStart = getDateString(bData.startDate);
|
|
24689
|
+
if (aEnd < bEnd)
|
|
24690
|
+
return -1;
|
|
24691
|
+
if (aEnd > bEnd)
|
|
24692
|
+
return 1;
|
|
24693
|
+
// If endDates are equal, sort by startDate
|
|
24694
|
+
if (aStart < bStart)
|
|
24695
|
+
return -1;
|
|
24696
|
+
if (aStart > bStart)
|
|
24697
|
+
return 1;
|
|
24698
|
+
return 0;
|
|
24699
|
+
});
|
|
24700
|
+
result.push(...splitSubscriptions);
|
|
24701
|
+
}
|
|
24702
|
+
});
|
|
24703
|
+
return result;
|
|
24704
|
+
}
|
|
24513
24705
|
|
|
24514
24706
|
const SubscriptionCardActions_PriceListChangeOptionsFragment = t(`
|
|
24515
24707
|
fragment SubscriptionCardActions_PriceListChangeOptionsFragment on PriceListChangeOptions {
|
|
@@ -25411,12 +25603,14 @@ const SubscriptionsList_SubscriptionFragment = t(`
|
|
|
25411
25603
|
fragment SubscriptionsList_SubscriptionFragment on Subscription {
|
|
25412
25604
|
id
|
|
25413
25605
|
state
|
|
25606
|
+
...splitSubscriptionsByPriceList_SubscriptionFragment
|
|
25414
25607
|
...findNonAddonSubscriptions_SubscriptionFragment
|
|
25415
25608
|
...SubscriptionCardDesktop_SubscriptionFragment
|
|
25416
25609
|
...SubscriptionCardMobile_SubscriptionFragment
|
|
25417
25610
|
...AddonSubscriptionsCards_SubscriptionFragment
|
|
25418
25611
|
}
|
|
25419
25612
|
`, [
|
|
25613
|
+
splitSubscriptionsByPriceList_SubscriptionFragment,
|
|
25420
25614
|
findNonAddonSubscriptions_SubscriptionFragment,
|
|
25421
25615
|
SubscriptionCardDesktop_SubscriptionFragment,
|
|
25422
25616
|
SubscriptionCardMobile_SubscriptionFragment,
|
|
@@ -25427,15 +25621,17 @@ const SubscriptionsList = ({ showInactive, subscriptions: maskedSubscriptions, }
|
|
|
25427
25621
|
const { gap } = useSubscriptionProps();
|
|
25428
25622
|
// Read fragments
|
|
25429
25623
|
const subscriptions = maskedSubscriptions.map(maskedSubscription => readFragment(SubscriptionsList_SubscriptionFragment, maskedSubscription));
|
|
25624
|
+
// Split subscriptions by price list
|
|
25625
|
+
const splitSubscriptions = splitSubscriptionsByPriceList(subscriptions);
|
|
25430
25626
|
// Hooks
|
|
25431
25627
|
const isMobile = useIsMobile();
|
|
25432
|
-
const nonAddonSubscriptions = findNonAddonSubscriptions(
|
|
25628
|
+
const nonAddonSubscriptions = findNonAddonSubscriptions(splitSubscriptions);
|
|
25433
25629
|
return (jsx(Fragment, { children: nonAddonSubscriptions === null || nonAddonSubscriptions === void 0 ? void 0 : nonAddonSubscriptions.map((subscription, subscriptionIndex) => {
|
|
25434
25630
|
if (!showInactive && isSubscriptionNotActive(subscription.state))
|
|
25435
25631
|
return null;
|
|
25436
25632
|
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:
|
|
25633
|
+
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));
|
|
25634
|
+
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
25635
|
}) }));
|
|
25440
25636
|
};
|
|
25441
25637
|
|
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,52 @@ 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
|
+
chargeReport: {
|
|
27
|
+
kind: "SUBSCRIBE" | "UPDATE" | "RENEW" | "REINSTATE" | "UNSUBSCRIBE" | "ADJUSTMENT" | "COUPON" | "DISCOUNT" | "CREDIT" | "PRICE_UPDATE" | "QUANTITY_UPDATE" | "FREE_PERIOD_DISCOUNT" | "ACTIVATE" | null;
|
|
28
|
+
startDate: unknown;
|
|
29
|
+
endDate: unknown;
|
|
30
|
+
priceList: {
|
|
31
|
+
id: string;
|
|
32
|
+
plan: {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
selfServiceBuy: boolean | null;
|
|
36
|
+
selfServiceCancel: boolean | null;
|
|
37
|
+
selfServiceRenew: boolean | null;
|
|
38
|
+
addon: boolean | null;
|
|
39
|
+
addonPlans: {
|
|
40
|
+
id: string;
|
|
41
|
+
}[] | null;
|
|
42
|
+
} | null;
|
|
43
|
+
product: {
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
showProductNameOnLineItem: boolean | null;
|
|
47
|
+
} | null;
|
|
48
|
+
periodMonths: number | null;
|
|
49
|
+
name: string;
|
|
50
|
+
} | null;
|
|
51
|
+
}[] | null;
|
|
52
|
+
charges: {
|
|
53
|
+
priceList: {
|
|
54
|
+
id: string;
|
|
55
|
+
} | null;
|
|
56
|
+
}[] | null;
|
|
57
|
+
}, {}, {
|
|
58
|
+
fragment: "splitSubscriptionsByPriceList_SubscriptionFragment";
|
|
59
|
+
on: "Subscription";
|
|
60
|
+
masked: true;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Splits a subscription into multiple subscriptions if it has RENEW charges
|
|
64
|
+
* with different price lists.
|
|
65
|
+
* This handles cases where a subscription has been changed to a different plan/price list.
|
|
66
|
+
* Each resulting subscription is a complete duplicate with just the charges filtered.
|
|
67
|
+
*/
|
|
68
|
+
export declare function splitSubscriptionsByPriceList<T extends FragmentOf<typeof splitSubscriptionsByPriceList_SubscriptionFragment>>(maskedSubscriptions: T[]): T[];
|
package/package.json
CHANGED