@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 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.14';
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(subscriptions);
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: subscriptions, showInactive: showInactive })] }, subscriptionIndex));
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: subscriptions, showInactive: showInactive })] }, subscriptionIndex));
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
 
@@ -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";
@@ -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.14';
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(subscriptions);
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: subscriptions, showInactive: showInactive })] }, subscriptionIndex));
25438
- return (jsxs("div", { className: `bunny-flex bunny-flex-col bunny-gap-${gap}`, children: [jsx(SubscriptionCardDesktop, { subscription: subscription }), jsx(AddonSubscriptionsCards, { subscription: subscription, subscriptions: subscriptions, showInactive: showInactive })] }, subscriptionIndex));
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
 
@@ -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";
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunnyapp/components",
3
- "version": "1.7.0-beta.15",
3
+ "version": "1.7.0-beta.16",
4
4
  "description": "Components from the Bunny portal to embed Bunny UI functionality into your application.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",