@stigg/react-sdk 5.2.1 → 5.4.0
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/components/common/TiersSelectContainer.d.ts +2 -4
- package/dist/components/common/VolumeBulkSelect.d.ts +1 -1
- package/dist/components/common/VolumePerUnitInput.d.ts +1 -1
- package/dist/components/paywall/PlanOfferingButton.d.ts +2 -3
- package/dist/components/paywall/PlanPrice.d.ts +2 -4
- package/dist/components/utils/fixtures/price.fixtures.d.ts +7 -0
- package/dist/components/utils/getPaidPriceText.d.ts +2 -3
- package/dist/components/utils/getPlanPrice.d.ts +1 -1
- package/dist/components/utils/priceTierUtils.d.ts +11 -6
- package/dist/components/utils/priceTierUtils.spec.d.ts +1 -0
- package/dist/react-sdk.cjs.development.js +137 -188
- package/dist/react-sdk.cjs.development.js.map +1 -1
- package/dist/react-sdk.cjs.production.min.js +1 -1
- package/dist/react-sdk.cjs.production.min.js.map +1 -1
- package/dist/react-sdk.esm.js +140 -191
- package/dist/react-sdk.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/components/checkout/hooks/usePlanStepModel.ts +1 -1
- package/src/components/checkout/steps/plan/CheckoutChargeList.tsx +4 -11
- package/src/components/checkout/summary/components/LineItems.tsx +5 -8
- package/src/components/common/TiersSelectContainer.tsx +5 -16
- package/src/components/common/VolumeBulkSelect.tsx +7 -4
- package/src/components/common/VolumePerUnitInput.tsx +3 -5
- package/src/components/paywall/Paywall.tsx +2 -2
- package/src/components/paywall/PlanOffering.tsx +6 -27
- package/src/components/paywall/PlanOfferingButton.tsx +2 -8
- package/src/components/paywall/PlanPrice.tsx +4 -23
- package/src/components/utils/calculateDiscountRate.ts +2 -2
- package/src/components/utils/fixtures/price.fixtures.ts +42 -0
- package/src/components/utils/getPaidPriceText.ts +6 -24
- package/src/components/utils/getPlanPrice.ts +0 -2
- package/src/components/utils/priceTierUtils.spec.ts +84 -0
- package/src/components/utils/priceTierUtils.ts +78 -73
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BillingModel, TiersMode, BillingPeriod, Price, PriceTierFragment, Subscription } from '@stigg/js-client-sdk';
|
|
2
2
|
import isNil from 'lodash/isNil';
|
|
3
|
+
import { sum } from 'lodash';
|
|
3
4
|
import { PaywallPlan } from '../paywall';
|
|
4
5
|
import { SelectDefaultTierIndexFn } from '../paywall/types';
|
|
5
6
|
|
|
@@ -11,9 +12,7 @@ export function getPriceFeatureUnit(price: Price) {
|
|
|
11
12
|
return (price.feature.unitQuantity !== 1 ? price.feature.unitsPlural : price.feature.units) || '';
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export function getTierByQuantity(tiers: PriceTierFragment[], quantity: number) {
|
|
15
|
-
if (!tiers) return undefined;
|
|
16
|
-
|
|
15
|
+
export function getTierByQuantity(tiers: PriceTierFragment[], quantity: number): PriceTierFragment {
|
|
17
16
|
const ascendingTiers = [...tiers];
|
|
18
17
|
// Sort tiers by upTo value, ascending, if upTo is not set,
|
|
19
18
|
// put it at the end as it represent the last infinity tier
|
|
@@ -32,7 +31,7 @@ function getAmount(amount: number, selectedBillingPeriod?: BillingPeriod, should
|
|
|
32
31
|
return selectedBillingPeriod === BillingPeriod.Annually && shouldShowMonthlyPriceAmount ? amount / 12 : amount;
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
function calculateTierPriceVolume(
|
|
36
35
|
currentTier: PriceTierFragment,
|
|
37
36
|
perUnitQuantity: number,
|
|
38
37
|
selectedBillingPeriod?: BillingPeriod,
|
|
@@ -52,66 +51,84 @@ export function calculateTierPriceVolume(
|
|
|
52
51
|
return amount;
|
|
53
52
|
}
|
|
54
53
|
|
|
54
|
+
export function calculateTierPriceGraduated(
|
|
55
|
+
tiers: PriceTierFragment[],
|
|
56
|
+
unitQuantity: number,
|
|
57
|
+
selectedBillingPeriod?: BillingPeriod,
|
|
58
|
+
shouldShowMonthlyPriceAmount?: boolean,
|
|
59
|
+
): { total: number; breakdown: Array<{ unitQuantity: number; amount: number }> } {
|
|
60
|
+
let remainingQuantity = unitQuantity;
|
|
61
|
+
let prevUpTo = 0;
|
|
62
|
+
let currentTierIndex = 0;
|
|
63
|
+
|
|
64
|
+
const breakdown: Array<{ unitQuantity: number; amount: number }> = [];
|
|
65
|
+
|
|
66
|
+
while (remainingQuantity > 0 && currentTierIndex < tiers.length) {
|
|
67
|
+
const tier = tiers[currentTierIndex];
|
|
68
|
+
const { upTo } = tier;
|
|
69
|
+
|
|
70
|
+
if (isNil(upTo)) {
|
|
71
|
+
breakdown.push({
|
|
72
|
+
unitQuantity: remainingQuantity,
|
|
73
|
+
amount: calculateTierPriceVolume(tier, remainingQuantity, selectedBillingPeriod, shouldShowMonthlyPriceAmount),
|
|
74
|
+
});
|
|
75
|
+
remainingQuantity = 0;
|
|
76
|
+
} else {
|
|
77
|
+
const unitsInTier = upTo - prevUpTo;
|
|
78
|
+
const consumed = Math.min(remainingQuantity, unitsInTier);
|
|
79
|
+
breakdown.push({
|
|
80
|
+
unitQuantity: consumed,
|
|
81
|
+
amount: calculateTierPriceVolume(tier, consumed, selectedBillingPeriod, shouldShowMonthlyPriceAmount),
|
|
82
|
+
});
|
|
83
|
+
remainingQuantity -= consumed;
|
|
84
|
+
prevUpTo = upTo;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
currentTierIndex += 1;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const total = sum(breakdown.map(({ amount }) => amount));
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
breakdown,
|
|
94
|
+
total,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
55
98
|
export function calculateTierPrice(
|
|
56
|
-
price: Price,
|
|
57
|
-
|
|
58
|
-
selectedBillingPeriod
|
|
59
|
-
shouldShowMonthlyPriceAmount
|
|
60
|
-
perUnitQuantity: number,
|
|
99
|
+
price: Pick<Price, 'tiers' | 'tiersMode'>,
|
|
100
|
+
unitQuantity: number,
|
|
101
|
+
selectedBillingPeriod?: BillingPeriod,
|
|
102
|
+
shouldShowMonthlyPriceAmount?: boolean,
|
|
61
103
|
): number {
|
|
104
|
+
if (!price.tiers) {
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
62
108
|
switch (price.tiersMode) {
|
|
63
109
|
case TiersMode.Volume: {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
110
|
+
const currentTier = getTierByQuantity(price.tiers, unitQuantity);
|
|
111
|
+
return calculateTierPriceVolume(currentTier, unitQuantity, selectedBillingPeriod, shouldShowMonthlyPriceAmount);
|
|
112
|
+
}
|
|
113
|
+
case TiersMode.Graduated: {
|
|
114
|
+
return calculateTierPriceGraduated(price.tiers, unitQuantity, selectedBillingPeriod, shouldShowMonthlyPriceAmount)
|
|
115
|
+
.total;
|
|
70
116
|
}
|
|
71
117
|
default:
|
|
72
118
|
return 0;
|
|
73
119
|
}
|
|
74
120
|
}
|
|
75
121
|
|
|
76
|
-
export function getSelectedTierQuantityBeFeature(
|
|
77
|
-
plan: PaywallPlan,
|
|
78
|
-
billingPeriod: BillingPeriod,
|
|
79
|
-
currentSubscription: Subscription | null,
|
|
80
|
-
) {
|
|
81
|
-
const result: Record<string, number> = {};
|
|
82
|
-
const planTierPrices = plan.pricePoints.filter(
|
|
83
|
-
(price) => price.billingPeriod === billingPeriod && price.isTieredPrice,
|
|
84
|
-
);
|
|
85
|
-
if (planTierPrices.length === 1) {
|
|
86
|
-
const [price] = planTierPrices;
|
|
87
|
-
const featureId = price?.feature!.featureId;
|
|
88
|
-
if (currentSubscription && currentSubscription.plan.id === plan.id) {
|
|
89
|
-
const tieredPrice = currentSubscription?.prices.find(
|
|
90
|
-
(subscriptionPrice) =>
|
|
91
|
-
subscriptionPrice.pricingModel === BillingModel.PerUnit &&
|
|
92
|
-
subscriptionPrice.tiersMode &&
|
|
93
|
-
subscriptionPrice.feature?.featureId === featureId,
|
|
94
|
-
);
|
|
95
|
-
if (tieredPrice) {
|
|
96
|
-
result[featureId] = tieredPrice.feature?.unitQuantity || 1;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return result;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
122
|
export function hasTierWithUnitPrice(tiers: PriceTierFragment[] | null | undefined) {
|
|
105
123
|
return tiers?.some(({ unitPrice, upTo }) => unitPrice && !isNil(upTo));
|
|
106
124
|
}
|
|
107
125
|
|
|
108
|
-
export function
|
|
126
|
+
export function getTiersPerUnitQuantities(
|
|
109
127
|
plan: PaywallPlan,
|
|
110
128
|
billingPeriod: BillingPeriod,
|
|
111
129
|
currentSubscription: Subscription | null,
|
|
112
|
-
selectedTierByFeature: Record<string, PriceTierFragment>,
|
|
113
130
|
selectDefaultTierIndex?: SelectDefaultTierIndexFn,
|
|
114
|
-
): Record<string,
|
|
131
|
+
): Record<string, number> {
|
|
115
132
|
const planTierPrices = plan.pricePoints.filter(
|
|
116
133
|
(price) => price.billingPeriod === billingPeriod && price.isTieredPrice,
|
|
117
134
|
);
|
|
@@ -120,11 +137,10 @@ export function getSelectedTier(
|
|
|
120
137
|
const [price] = planTierPrices;
|
|
121
138
|
const { featureId } = price.feature!;
|
|
122
139
|
const selectedDefaultTierIndex = selectDefaultTierIndex ? selectDefaultTierIndex({ plan }) : 0;
|
|
123
|
-
|
|
140
|
+
const currentTier = price.tiers![selectedDefaultTierIndex];
|
|
141
|
+
let quantity = hasTierWithUnitPrice(price.tiers) ? 1 : currentTier.upTo || 1;
|
|
124
142
|
|
|
125
|
-
if (
|
|
126
|
-
currentTier = price.tiers?.find((tier) => tier.upTo === selectedTierByFeature[featureId].upTo) || currentTier;
|
|
127
|
-
} else if (currentSubscription && currentSubscription.plan.id === plan.id) {
|
|
143
|
+
if (currentSubscription && currentSubscription.plan.id === plan.id) {
|
|
128
144
|
const tieredPrice = currentSubscription.prices.find(
|
|
129
145
|
(subscriptionPrice) =>
|
|
130
146
|
subscriptionPrice.pricingModel === BillingModel.PerUnit &&
|
|
@@ -133,12 +149,12 @@ export function getSelectedTier(
|
|
|
133
149
|
);
|
|
134
150
|
|
|
135
151
|
if (tieredPrice) {
|
|
136
|
-
|
|
152
|
+
quantity = tieredPrice.feature!.unitQuantity || 1;
|
|
137
153
|
}
|
|
138
154
|
}
|
|
139
155
|
|
|
140
|
-
const result: Record<string,
|
|
141
|
-
result[featureId] =
|
|
156
|
+
const result: Record<string, number> = {};
|
|
157
|
+
result[featureId] = quantity;
|
|
142
158
|
|
|
143
159
|
return result;
|
|
144
160
|
}
|
|
@@ -153,11 +169,10 @@ export enum PriceTierComparison {
|
|
|
153
169
|
}
|
|
154
170
|
|
|
155
171
|
export function compareSelectedTierToCurrentTier(
|
|
156
|
-
selectedTierByFeature: Record<string, PriceTierFragment>,
|
|
157
172
|
perUnitQuantityByFeature: Record<string, number>,
|
|
158
173
|
currentSubscription: Subscription | null,
|
|
159
174
|
): PriceTierComparison {
|
|
160
|
-
if (!currentSubscription
|
|
175
|
+
if (!currentSubscription) {
|
|
161
176
|
return PriceTierComparison.Equal;
|
|
162
177
|
}
|
|
163
178
|
|
|
@@ -168,29 +183,19 @@ export function compareSelectedTierToCurrentTier(
|
|
|
168
183
|
return PriceTierComparison.Equal;
|
|
169
184
|
}
|
|
170
185
|
|
|
171
|
-
const { featureId, unitQuantity } = currentTierPrice.feature!;
|
|
172
|
-
const { tiers } = currentTierPrice;
|
|
186
|
+
const { featureId, unitQuantity: oldQuantity } = currentTierPrice.feature!;
|
|
173
187
|
|
|
174
|
-
if (
|
|
188
|
+
if (isNil(oldQuantity)) {
|
|
175
189
|
return PriceTierComparison.Equal;
|
|
176
190
|
}
|
|
177
191
|
|
|
178
|
-
const
|
|
179
|
-
const selectedQuantity = perUnitQuantityByFeature[featureId];
|
|
180
|
-
if (!selectedTier) {
|
|
181
|
-
return PriceTierComparison.Equal;
|
|
182
|
-
}
|
|
192
|
+
const newQuantity = perUnitQuantityByFeature[featureId];
|
|
183
193
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
if (effectiveQuantity > unitQuantity) {
|
|
191
|
-
return PriceTierComparison.Higher;
|
|
192
|
-
}
|
|
194
|
+
if (newQuantity < oldQuantity) {
|
|
195
|
+
return PriceTierComparison.Lower;
|
|
196
|
+
}
|
|
197
|
+
if (newQuantity > oldQuantity) {
|
|
198
|
+
return PriceTierComparison.Higher;
|
|
193
199
|
}
|
|
194
|
-
|
|
195
200
|
return PriceTierComparison.Equal;
|
|
196
201
|
}
|