@stigg/react-sdk 4.4.0-beta.9 → 4.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/checkout/Checkout.d.ts +3 -2
- package/dist/components/checkout/CheckoutContainer.d.ts +4 -2
- package/dist/components/checkout/CheckoutProvider.d.ts +3 -2
- package/dist/components/checkout/components/DowngradeToFreeContainer.d.ts +3 -7
- package/dist/components/checkout/components/StyledArrow.d.ts +5 -0
- package/dist/components/checkout/hooks/useCheckoutModel.d.ts +2 -0
- package/dist/components/checkout/hooks/useLoadCheckout.d.ts +3 -1
- package/dist/components/checkout/hooks/usePreviewSubscription.d.ts +17 -8
- package/dist/components/checkout/progressBar/CheckoutProgressBar.style.d.ts +1 -0
- package/dist/components/checkout/promotionCode/AddPromotionCode.d.ts +2 -4
- package/dist/components/checkout/promotionCode/AddPromotionCodeButton.d.ts +2 -1
- package/dist/components/checkout/promotionCode/PromotionCodeSection.d.ts +6 -2
- package/dist/components/checkout/steps/plan/BillingPeriodPicker.d.ts +1 -1
- package/dist/components/checkout/steps/plan/CheckoutPlanStep.d.ts +2 -1
- package/dist/components/checkout/summary/CheckoutSummary.d.ts +1 -1
- package/dist/components/checkout/summary/components/CheckoutCaptions.d.ts +2 -2
- package/dist/components/checkout/summary/components/LineItems.d.ts +12 -8
- package/dist/components/checkout/textOverrides.d.ts +63 -20
- package/dist/components/checkout/theme.d.ts +0 -1
- package/dist/components/checkout/types.d.ts +7 -1
- package/dist/components/common/TiersSelectContainer.d.ts +1 -2
- package/dist/components/common/Typography.d.ts +2 -2
- package/dist/components/common/customIcons.d.ts +2 -0
- package/dist/components/customerPortal/subscriptionOverview/subscriptionView/SubscriptionView.style.d.ts +1 -1
- package/dist/components/paywall/PlanPrice.d.ts +1 -1
- package/dist/components/utils/currencyUtils.d.ts +2 -1
- package/dist/components/utils/getFeatureName.d.ts +1 -0
- package/dist/react-sdk.cjs.development.js +1027 -559
- 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 +1041 -582
- package/dist/react-sdk.esm.js.map +1 -1
- package/dist/stories/mocks/checkout/consts.d.ts +11 -0
- package/dist/stories/mocks/checkout/mockCheckoutPreview.d.ts +2 -0
- package/dist/stories/mocks/checkout/mockCheckoutState.d.ts +2 -0
- package/dist/theme/getResolvedTheme.d.ts +1 -0
- package/dist/theme/types.d.ts +1 -0
- package/package.json +28 -20
- package/src/assets/coupon.svg +6 -0
- package/src/assets/pay-as-you-go-charge.svg +11 -0
- package/src/components/checkout/Checkout.tsx +5 -2
- package/src/components/checkout/CheckoutContainer.tsx +18 -12
- package/src/components/checkout/CheckoutProvider.tsx +5 -3
- package/src/components/checkout/components/DowngradeToFreeContainer.tsx +33 -36
- package/src/components/checkout/components/StyledArrow.tsx +9 -0
- package/src/components/checkout/hooks/useCheckoutModel.ts +12 -2
- package/src/components/checkout/hooks/useLoadCheckout.ts +10 -2
- package/src/components/checkout/hooks/usePreviewSubscription.ts +102 -50
- package/src/components/checkout/planHeader/PlanHeader.tsx +18 -25
- package/src/components/checkout/progressBar/CheckoutProgressBar.style.ts +11 -12
- package/src/components/checkout/progressBar/CheckoutProgressBar.tsx +7 -6
- package/src/components/checkout/promotionCode/AddPromotionCode.tsx +32 -9
- package/src/components/checkout/promotionCode/AddPromotionCodeButton.tsx +15 -11
- package/src/components/checkout/promotionCode/AppliedPromotionCode.tsx +4 -3
- package/src/components/checkout/promotionCode/PromotionCodeSection.tsx +21 -7
- package/src/components/checkout/steps/addons/CheckoutAddonsStep.style.tsx +0 -1
- package/src/components/checkout/steps/addons/CheckoutAddonsStep.tsx +4 -3
- package/src/components/checkout/steps/payment/PaymentMethods.style.ts +4 -1
- package/src/components/checkout/steps/payment/PaymentStep.tsx +0 -1
- package/src/components/checkout/steps/plan/BillingPeriodPicker.style.tsx +3 -2
- package/src/components/checkout/steps/plan/BillingPeriodPicker.tsx +5 -2
- package/src/components/checkout/steps/plan/CheckoutChargeList.tsx +35 -14
- package/src/components/checkout/steps/plan/CheckoutPlanStep.tsx +10 -5
- package/src/components/checkout/summary/CheckoutSuccess.tsx +1 -1
- package/src/components/checkout/summary/CheckoutSummary.tsx +143 -46
- package/src/components/checkout/summary/components/CheckoutCaptions.tsx +38 -15
- package/src/components/checkout/summary/components/LineItems.tsx +77 -28
- package/src/components/checkout/textOverrides.ts +107 -27
- package/src/components/checkout/theme.ts +0 -4
- package/src/components/checkout/types.ts +15 -1
- package/src/components/common/Icon.tsx +4 -6
- package/src/components/common/TiersSelectContainer.tsx +7 -8
- package/src/components/common/Typography.tsx +12 -3
- package/src/components/common/customIcons.ts +2 -0
- package/src/components/common/mapExternalTheme.ts +1 -2
- package/src/components/customerPortal/paywall/CustomerPortalPaywall.style.ts +4 -3
- package/src/components/paywall/PlanOfferingButton.tsx +6 -8
- package/src/components/paywall/PlanPrice.tsx +14 -17
- package/src/components/utils/currencyUtils.ts +4 -2
- package/src/components/utils/getFeatureName.ts +13 -5
- package/src/stories/Checkout.stories.tsx +37 -6
- package/src/stories/CustomerPortal.stories.tsx +2 -2
- package/src/stories/mocks/checkout/consts.ts +15 -0
- package/src/stories/mocks/checkout/mockCheckoutPreview.ts +138 -0
- package/src/stories/mocks/checkout/mockCheckoutState.ts +206 -0
- package/src/theme/Theme.tsx +10 -1
- package/src/theme/getResolvedTheme.ts +1 -0
- package/src/theme/types.ts +1 -0
- package/dist/components/checkout/planHeader/PlanHeader.style.d.ts +0 -25
- package/src/components/checkout/planHeader/PlanHeader.style.tsx +0 -23
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import styled from '@emotion/styled/macro';
|
|
3
|
+
import { BillingPeriod, PriceTierFragment } from '@stigg/js-client-sdk';
|
|
1
4
|
import { PaywallPlan } from './types';
|
|
2
5
|
import { PaywallLocalization } from './paywallTextOverrides';
|
|
3
6
|
import { getPlanPrice } from '../utils/getPlanPrice';
|
|
4
7
|
import { Typography } from '../common/Typography';
|
|
5
|
-
import React, { useEffect, useState } from 'react';
|
|
6
|
-
import styled from '@emotion/styled/macro';
|
|
7
|
-
import { BillingPeriod, PriceTierFragment } from '@stigg/js-client-sdk';
|
|
8
8
|
import { TiersSelectContainer } from '../common/TiersSelectContainer';
|
|
9
9
|
|
|
10
10
|
const EMPTY_CHAR = '';
|
|
@@ -38,7 +38,7 @@ function PriceBillingPeriod({
|
|
|
38
38
|
hasAnnuallyPrice,
|
|
39
39
|
paywallLocale,
|
|
40
40
|
}: PriceBillingPeriodProps) {
|
|
41
|
-
const hasPrice = plan.pricePoints.find(pricePoint => pricePoint.billingPeriod === billingPeriod);
|
|
41
|
+
const hasPrice = plan.pricePoints.find((pricePoint) => pricePoint.billingPeriod === billingPeriod);
|
|
42
42
|
|
|
43
43
|
let content = EMPTY_CHAR;
|
|
44
44
|
if (hasPrice && hasMonthlyPrice && hasAnnuallyPrice) {
|
|
@@ -79,7 +79,7 @@ export const PlanPrice = ({
|
|
|
79
79
|
hasAnnuallyPrice: boolean;
|
|
80
80
|
locale: string;
|
|
81
81
|
}) => {
|
|
82
|
-
const { price, unit, tiers
|
|
82
|
+
const { price, unit, tiers } = getPlanPrice(
|
|
83
83
|
plan,
|
|
84
84
|
billingPeriod,
|
|
85
85
|
paywallLocale,
|
|
@@ -87,18 +87,17 @@ export const PlanPrice = ({
|
|
|
87
87
|
hasMonthlyPrice,
|
|
88
88
|
selectedTierByFeature,
|
|
89
89
|
);
|
|
90
|
-
|
|
91
90
|
const [selectedTier, setSelectedTier] = useState<PriceTierFragment>();
|
|
92
91
|
|
|
93
|
-
useEffect(() => {
|
|
94
|
-
setSelectedTier(selectedTierByFeature[featureId!]);
|
|
95
|
-
}, [selectedTierByFeature]);
|
|
96
|
-
|
|
97
92
|
// We currently only support prices with one tier - so we select the first one
|
|
98
|
-
const tieredPrice = plan.pricePoints.find(planPrice => {
|
|
93
|
+
const tieredPrice = plan.pricePoints.find((planPrice) => {
|
|
99
94
|
return planPrice.billingPeriod === billingPeriod && planPrice.isTieredPrice;
|
|
100
95
|
});
|
|
101
|
-
const featureId = tieredPrice ? tieredPrice
|
|
96
|
+
const featureId = tieredPrice ? tieredPrice.feature!.featureId : undefined;
|
|
97
|
+
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
setSelectedTier(featureId ? selectedTierByFeature[featureId] : undefined);
|
|
100
|
+
}, [featureId, selectedTierByFeature]);
|
|
102
101
|
|
|
103
102
|
const handleTierChange = (tier: PriceTierFragment) => {
|
|
104
103
|
const updatedTierByFeature: Record<string, PriceTierFragment> = {};
|
|
@@ -121,8 +120,7 @@ export const PlanPrice = ({
|
|
|
121
120
|
style={{ minHeight: '20px' }}
|
|
122
121
|
className="stigg-starting-at-text"
|
|
123
122
|
variant="body1"
|
|
124
|
-
color="secondary"
|
|
125
|
-
>
|
|
123
|
+
color="secondary">
|
|
126
124
|
{showStartingAt ? paywallLocale.price.startingAtCaption : EMPTY_CHAR}
|
|
127
125
|
</Typography>
|
|
128
126
|
)}
|
|
@@ -145,15 +143,14 @@ export const PlanPrice = ({
|
|
|
145
143
|
</Typography>
|
|
146
144
|
)}
|
|
147
145
|
|
|
148
|
-
{withTiersRow
|
|
146
|
+
{withTiersRow ? (
|
|
149
147
|
<TiersSelectContainer
|
|
150
148
|
componentId={`${plan.id}_${featureId}_tier`}
|
|
151
149
|
tiers={tiers}
|
|
152
|
-
tierUnits={tierUnits}
|
|
153
150
|
selectedTier={selectedTier}
|
|
154
151
|
handleTierChange={handleTierChange}
|
|
155
152
|
/>
|
|
156
|
-
)}
|
|
153
|
+
) : null}
|
|
157
154
|
</>
|
|
158
155
|
</PlanPriceContainer>
|
|
159
156
|
);
|
|
@@ -6,17 +6,19 @@ export const currencyPriceFormatter = ({
|
|
|
6
6
|
currency = Currency.Usd,
|
|
7
7
|
locale,
|
|
8
8
|
maximumFractionDigits = 5,
|
|
9
|
+
minimumFractionDigits = 0,
|
|
9
10
|
}: {
|
|
10
11
|
amount: number;
|
|
11
12
|
currency?: Currency | string;
|
|
12
13
|
locale?: string;
|
|
13
14
|
maximumFractionDigits?: number;
|
|
15
|
+
minimumFractionDigits?: number;
|
|
14
16
|
}) => {
|
|
15
17
|
const currencyString = currency.toString();
|
|
16
18
|
const currencySymbol = getSymbolFromCurrency(currencyString);
|
|
17
19
|
let formattedPrice = new Intl.NumberFormat(locale, {
|
|
18
|
-
maximumFractionDigits
|
|
19
|
-
minimumFractionDigits
|
|
20
|
+
maximumFractionDigits,
|
|
21
|
+
minimumFractionDigits,
|
|
20
22
|
style: 'currency',
|
|
21
23
|
currency: currencyString,
|
|
22
24
|
...(currencySymbol ? { currencyDisplay: 'code' } : {}),
|
|
@@ -2,13 +2,21 @@ import { FeatureFragment } from '@stigg/api-client-js/src/generated/sdk';
|
|
|
2
2
|
import lowercase from 'lodash/lowerCase';
|
|
3
3
|
|
|
4
4
|
export function getFeatureDisplayName(feature: FeatureFragment) {
|
|
5
|
-
|
|
5
|
+
return getFeatureDisplayNameText(feature.displayName, feature.featureUnits, feature.featureUnitsPlural);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function getFeatureDisplayNameText(featureDisplayName: string, featureUnits: string | undefined | null, featureUnitsPlural: string | undefined | null) {
|
|
9
|
+
if(!featureUnits && !featureUnitsPlural) {
|
|
10
|
+
return featureDisplayName;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const displayNameLowerCase = lowercase(featureDisplayName);
|
|
6
14
|
if (
|
|
7
|
-
displayNameLowerCase === lowercase(
|
|
8
|
-
displayNameLowerCase === lowercase(
|
|
15
|
+
displayNameLowerCase === lowercase(featureUnits || '') ||
|
|
16
|
+
displayNameLowerCase === lowercase(featureUnitsPlural || '')
|
|
9
17
|
) {
|
|
10
|
-
return
|
|
18
|
+
return featureDisplayName;
|
|
11
19
|
}
|
|
12
20
|
|
|
13
|
-
return `${
|
|
21
|
+
return `${featureDisplayName} (${featureUnitsPlural})`;
|
|
14
22
|
}
|
|
@@ -4,6 +4,8 @@ import { ComponentMeta, ComponentStory } from '@storybook/react';
|
|
|
4
4
|
import { Checkout } from '../components/checkout';
|
|
5
5
|
import { StiggProvider } from '../components/StiggProvider';
|
|
6
6
|
import { defaultArgsWithCustomer } from './baseArgs';
|
|
7
|
+
import { mockCheckoutState } from './mocks/checkout/mockCheckoutState';
|
|
8
|
+
import { mockPreviewSubscription } from './mocks/checkout/mockCheckoutPreview';
|
|
7
9
|
|
|
8
10
|
export default {
|
|
9
11
|
title: 'Stigg React SDK/Checkout',
|
|
@@ -21,6 +23,18 @@ export default {
|
|
|
21
23
|
</StiggProvider>
|
|
22
24
|
),
|
|
23
25
|
],
|
|
26
|
+
parameters: {
|
|
27
|
+
controls: {
|
|
28
|
+
exclude: [
|
|
29
|
+
'onCheckout',
|
|
30
|
+
'onCheckoutCompleted',
|
|
31
|
+
'onChangePlan',
|
|
32
|
+
'onBillingAddressChange',
|
|
33
|
+
'onMockCheckoutState',
|
|
34
|
+
'onMockCheckoutPreview',
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
24
38
|
} as ComponentMeta<typeof Checkout>;
|
|
25
39
|
|
|
26
40
|
const Wrapper = styled.div`
|
|
@@ -40,14 +54,34 @@ const Template: ComponentStory<any> = (args) => (
|
|
|
40
54
|
}}
|
|
41
55
|
onCheckout={async ({ checkoutParams, checkoutAction }) => {
|
|
42
56
|
console.log('checkout started!', checkoutParams);
|
|
57
|
+
if (args.useMockData) {
|
|
58
|
+
console.log('returning mock checkout success response');
|
|
59
|
+
return { success: true };
|
|
60
|
+
}
|
|
61
|
+
|
|
43
62
|
const { success, errorMessage } = await checkoutAction();
|
|
44
63
|
return { success, errorMessage };
|
|
45
64
|
}}
|
|
65
|
+
billableFeatures={[{ featureId: 'feature-seat', quantity: 30 }]}
|
|
46
66
|
onChangePlan={({ currentPlan }) => {
|
|
47
67
|
console.log('plan changed clicked!', { currentPlan });
|
|
48
68
|
}}
|
|
49
|
-
|
|
50
|
-
|
|
69
|
+
onMockCheckoutState={
|
|
70
|
+
args.useMockData
|
|
71
|
+
? (params) => {
|
|
72
|
+
console.log('mocking checkout state', params);
|
|
73
|
+
return mockCheckoutState(params);
|
|
74
|
+
}
|
|
75
|
+
: undefined
|
|
76
|
+
}
|
|
77
|
+
onMockCheckoutPreview={
|
|
78
|
+
args.useMockData
|
|
79
|
+
? (params) => {
|
|
80
|
+
console.log('mocking checkout preview');
|
|
81
|
+
return mockPreviewSubscription(params);
|
|
82
|
+
}
|
|
83
|
+
: undefined
|
|
84
|
+
}
|
|
51
85
|
/>
|
|
52
86
|
</Wrapper>
|
|
53
87
|
);
|
|
@@ -56,8 +90,5 @@ export const DefaultCheckout = Template.bind({});
|
|
|
56
90
|
DefaultCheckout.args = {
|
|
57
91
|
...defaultArgsWithCustomer,
|
|
58
92
|
planId: 'plan-revvenu-essentials',
|
|
59
|
-
|
|
60
|
-
// apiKey: 'client-72b058a6-0f22-4c86-adce-bf266d12e12e:9f356ceb-c94c-42a4-9572-10b12824da81',
|
|
61
|
-
baseUri: 'https://api-staging.stigg.io',
|
|
62
|
-
apiKey: 'client-79584f52-7ef9-4c58-b9ac-5080acf492e4:71f2274c-100a-4fa4-8a43-48fa3b16c627',
|
|
93
|
+
useMockData: false,
|
|
63
94
|
};
|
|
@@ -33,7 +33,7 @@ export default {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
baseUri={args.baseUri}
|
|
36
|
-
enableEdge={args.disableEdge}>
|
|
36
|
+
enableEdge={!args.disableEdge}>
|
|
37
37
|
<Story />
|
|
38
38
|
</StiggProvider>
|
|
39
39
|
),
|
|
@@ -70,7 +70,7 @@ const Template: ComponentStory<any> = (args) => {
|
|
|
70
70
|
onPlanSelected={(...args) => {
|
|
71
71
|
console.log('onPlanSelected', args);
|
|
72
72
|
}}
|
|
73
|
-
highlightedPlanId="plan-revvenu-
|
|
73
|
+
highlightedPlanId="plan-revvenu-essentials"
|
|
74
74
|
/>
|
|
75
75
|
}
|
|
76
76
|
theme={customerPortalTheme}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const BASE_FEE_MONTHLY = 200;
|
|
2
|
+
export const BASE_FEE_YEARLY = 175 * 12;
|
|
3
|
+
export const TIERS = [100, 500, 750];
|
|
4
|
+
export const TIERS_PRICE_MONTHLY = [10, 49, 70];
|
|
5
|
+
export const TIERS_PRICE_YEARLY = [8, 35, 50].map((p) => p * 12);
|
|
6
|
+
|
|
7
|
+
export const PER_UNIT_PRICE_MONTHLY = 12;
|
|
8
|
+
export const PER_UNIT_PRICE_YEARLY = 10 * 12;
|
|
9
|
+
|
|
10
|
+
export const ADDON_PRICE_MONTHLY = 50;
|
|
11
|
+
export const ADDON_PRICE_YEARLY = 35 * 12;
|
|
12
|
+
|
|
13
|
+
export const STRIPE_MOCK_ACCOUNT_ID = 'acct_1NnHoQG6EyqgvTaj';
|
|
14
|
+
export const STRIPE_MOCK_ACCOUNT_PK =
|
|
15
|
+
'pk_test_51NnHoQG6EyqgvTajznajopWC01AozNtq7zgySeQ1qx4PH9TAXvMj0TnbZvYT3yOt46jbQAcCDs1EU2QKcfG8eEoO00tlW0Jp3r';
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import moment from 'moment';
|
|
2
|
+
import {
|
|
3
|
+
BillingPeriod,
|
|
4
|
+
BillingModel,
|
|
5
|
+
Currency,
|
|
6
|
+
BillableFeature,
|
|
7
|
+
DateRange,
|
|
8
|
+
Money,
|
|
9
|
+
Plan,
|
|
10
|
+
PreviewSubscription,
|
|
11
|
+
SubscriptionEstimationAddon,
|
|
12
|
+
SubscriptionPreviewV2,
|
|
13
|
+
} from '@stigg/js-client-sdk';
|
|
14
|
+
import { mockCheckoutState } from './mockCheckoutState';
|
|
15
|
+
|
|
16
|
+
const mockBillingPeriod = (period?: BillingPeriod): DateRange => {
|
|
17
|
+
return {
|
|
18
|
+
start: moment().toDate(),
|
|
19
|
+
end: moment()
|
|
20
|
+
.add(1, period === BillingPeriod.Monthly ? 'month' : 'year')
|
|
21
|
+
.toDate(),
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const defaultPreviewSubscription = (): SubscriptionPreviewV2 => {
|
|
26
|
+
const defaultCost: Money = {
|
|
27
|
+
amount: 0,
|
|
28
|
+
currency: Currency.Usd,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
subTotal: defaultCost,
|
|
33
|
+
total: defaultCost,
|
|
34
|
+
totalExcludingTax: defaultCost,
|
|
35
|
+
billingPeriodRange: mockBillingPeriod(),
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const flatFeeCost = (plan: Plan, billingPeriod: BillingPeriod | undefined) => {
|
|
40
|
+
const filteredPrice = plan.pricePoints?.find(
|
|
41
|
+
(price) => price.billingPeriod === billingPeriod && price.pricingModel === BillingModel.FlatFee,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return filteredPrice?.amount || 0;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const addonCost = (plan: Plan, billingPeriod: BillingPeriod, addonId: string, quantity: number) => {
|
|
48
|
+
const addon = plan.compatibleAddons?.find((addon) => addon.id === addonId);
|
|
49
|
+
|
|
50
|
+
const price = addon?.pricePoints.find((price) => price.billingPeriod === billingPeriod);
|
|
51
|
+
|
|
52
|
+
return (price?.amount || 0) * quantity;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const billableFeatureCost = (plan: Plan, billingPeriod: BillingPeriod, featureId: string, quantity: number) => {
|
|
56
|
+
const price = plan.pricePoints.find(
|
|
57
|
+
(price) => price.feature?.featureId === featureId && price.billingPeriod === billingPeriod,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if (!price) return 0;
|
|
61
|
+
|
|
62
|
+
const { tiers } = price;
|
|
63
|
+
|
|
64
|
+
if (tiers) {
|
|
65
|
+
const quantityTier = tiers.find((tier) => tier.upTo >= quantity);
|
|
66
|
+
const priceTier = quantityTier || tiers[tiers.length - 1];
|
|
67
|
+
return quantity * priceTier.unitPrice.amount;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return (price?.amount || 0) * quantity;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const mockTotalPrice = (
|
|
74
|
+
plan: Plan,
|
|
75
|
+
billingPeriod: BillingPeriod,
|
|
76
|
+
addons: SubscriptionEstimationAddon[],
|
|
77
|
+
features: BillableFeature[],
|
|
78
|
+
) => {
|
|
79
|
+
const currency = plan.pricePoints?.[0].currency || Currency.Usd;
|
|
80
|
+
|
|
81
|
+
const totalFlatFeeCost = flatFeeCost(plan, billingPeriod);
|
|
82
|
+
const totalFeaturesCost = features.reduce(
|
|
83
|
+
(total, feature) => total + billableFeatureCost(plan, billingPeriod, feature.featureId, feature.quantity || 0),
|
|
84
|
+
0,
|
|
85
|
+
);
|
|
86
|
+
const totalAddonsCost = addons.reduce(
|
|
87
|
+
(total, addon) => total + addonCost(plan, billingPeriod, addon.addonId, addon.quantity || 0),
|
|
88
|
+
0,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const cost: Money = {
|
|
92
|
+
amount: totalFlatFeeCost + totalFeaturesCost + totalAddonsCost,
|
|
93
|
+
currency: currency || Currency.Usd,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
subTotal: cost,
|
|
98
|
+
total: cost,
|
|
99
|
+
totalExcludingTax: cost,
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export function mockPreviewSubscription(input: PreviewSubscription): SubscriptionPreviewV2 {
|
|
104
|
+
const {
|
|
105
|
+
planId,
|
|
106
|
+
billingPeriod = BillingPeriod.Monthly,
|
|
107
|
+
billableFeatures: features = [],
|
|
108
|
+
addons = [],
|
|
109
|
+
promotionCode,
|
|
110
|
+
} = input;
|
|
111
|
+
|
|
112
|
+
if (promotionCode) {
|
|
113
|
+
throw new Error('Error: Invalid promotion code');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const data = mockCheckoutState({ planId });
|
|
117
|
+
|
|
118
|
+
if (!data || !data.plan) {
|
|
119
|
+
return defaultPreviewSubscription();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const total = mockTotalPrice(data.plan, billingPeriod, addons, features);
|
|
123
|
+
|
|
124
|
+
if (!total) {
|
|
125
|
+
return defaultPreviewSubscription();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
...total,
|
|
130
|
+
billingPeriodRange: mockBillingPeriod(billingPeriod),
|
|
131
|
+
proration: {
|
|
132
|
+
prorationDate: moment().toDate(),
|
|
133
|
+
credit: total.total,
|
|
134
|
+
debit: total.total,
|
|
135
|
+
netAmount: total.total,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import moment from 'moment';
|
|
2
|
+
import { camelCase, startCase } from 'lodash';
|
|
3
|
+
import {
|
|
4
|
+
BillingModel,
|
|
5
|
+
BillingPeriod,
|
|
6
|
+
BillingVendorIdentifier,
|
|
7
|
+
Currency,
|
|
8
|
+
PricingType,
|
|
9
|
+
TiersMode,
|
|
10
|
+
CheckoutStatePlan,
|
|
11
|
+
Customer,
|
|
12
|
+
GetCheckoutState,
|
|
13
|
+
GetCheckoutStateResults,
|
|
14
|
+
Price,
|
|
15
|
+
Product,
|
|
16
|
+
PromotionalEntitlement,
|
|
17
|
+
Subscription,
|
|
18
|
+
Addon,
|
|
19
|
+
} from '../../../../../js-client-sdk';
|
|
20
|
+
import {
|
|
21
|
+
BASE_FEE_MONTHLY,
|
|
22
|
+
TIERS_PRICE_MONTHLY,
|
|
23
|
+
PER_UNIT_PRICE_MONTHLY,
|
|
24
|
+
PER_UNIT_PRICE_YEARLY,
|
|
25
|
+
STRIPE_MOCK_ACCOUNT_ID,
|
|
26
|
+
STRIPE_MOCK_ACCOUNT_PK,
|
|
27
|
+
TIERS,
|
|
28
|
+
BASE_FEE_YEARLY,
|
|
29
|
+
TIERS_PRICE_YEARLY,
|
|
30
|
+
ADDON_PRICE_MONTHLY,
|
|
31
|
+
ADDON_PRICE_YEARLY,
|
|
32
|
+
} from './consts';
|
|
33
|
+
|
|
34
|
+
const mockCustomer: Customer = {
|
|
35
|
+
id: '462d5d8a-22c4-4f22-9306-38d1a3047675',
|
|
36
|
+
name: 'John Doe',
|
|
37
|
+
email: 'john.doe@example.com',
|
|
38
|
+
createdAt: moment().subtract(1, 'year').toDate(),
|
|
39
|
+
updatedAt: moment().subtract(1, 'month').toDate(),
|
|
40
|
+
hasPaymentMethod: false,
|
|
41
|
+
promotionalEntitlements: [],
|
|
42
|
+
subscriptions: [],
|
|
43
|
+
getActivePromotionalEntitlements(): PromotionalEntitlement[] {
|
|
44
|
+
return [];
|
|
45
|
+
},
|
|
46
|
+
getActiveSubscriptions(): Subscription[] {
|
|
47
|
+
return [];
|
|
48
|
+
},
|
|
49
|
+
getActiveTrials(): Subscription[] {
|
|
50
|
+
return [];
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const mockProduct: Product = {
|
|
55
|
+
id: 'product-1',
|
|
56
|
+
displayName: 'Product 1',
|
|
57
|
+
description: 'Product 1 description',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const additionalStorageAddons: Addon = {
|
|
61
|
+
id: 'addon-additional-storage',
|
|
62
|
+
description: 'Additional storage',
|
|
63
|
+
displayName: 'Additional storage',
|
|
64
|
+
entitlements: [],
|
|
65
|
+
pricePoints: [
|
|
66
|
+
{
|
|
67
|
+
pricingModel: BillingModel.FlatFee,
|
|
68
|
+
billingPeriod: BillingPeriod.Monthly,
|
|
69
|
+
amount: ADDON_PRICE_MONTHLY,
|
|
70
|
+
currency: Currency.Usd,
|
|
71
|
+
tiersMode: undefined,
|
|
72
|
+
isTieredPrice: false,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
pricingModel: BillingModel.FlatFee,
|
|
76
|
+
billingPeriod: BillingPeriod.Annually,
|
|
77
|
+
amount: ADDON_PRICE_YEARLY,
|
|
78
|
+
currency: Currency.Usd,
|
|
79
|
+
tiersMode: undefined,
|
|
80
|
+
isTieredPrice: false,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const flatFeePricing: Price[] = [
|
|
86
|
+
{
|
|
87
|
+
pricingModel: BillingModel.FlatFee,
|
|
88
|
+
billingPeriod: BillingPeriod.Monthly,
|
|
89
|
+
amount: BASE_FEE_MONTHLY,
|
|
90
|
+
currency: Currency.Usd,
|
|
91
|
+
tiersMode: undefined,
|
|
92
|
+
isTieredPrice: false,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
pricingModel: BillingModel.FlatFee,
|
|
96
|
+
billingPeriod: BillingPeriod.Annually,
|
|
97
|
+
amount: BASE_FEE_YEARLY,
|
|
98
|
+
currency: Currency.Usd,
|
|
99
|
+
tiersMode: undefined,
|
|
100
|
+
isTieredPrice: false,
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
function tieredPricing(): Price[] {
|
|
105
|
+
const feature = {
|
|
106
|
+
featureId: 'feature-campaigns',
|
|
107
|
+
displayName: 'Campaigns per month',
|
|
108
|
+
units: 'campaign',
|
|
109
|
+
unitsPlural: 'campaigns',
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return [
|
|
113
|
+
{
|
|
114
|
+
pricingModel: BillingModel.PerUnit,
|
|
115
|
+
billingPeriod: BillingPeriod.Monthly,
|
|
116
|
+
tiersMode: TiersMode.Volume,
|
|
117
|
+
tiers: TIERS.map((tier, index) => ({
|
|
118
|
+
upTo: tier,
|
|
119
|
+
unitPrice: {
|
|
120
|
+
amount: TIERS_PRICE_MONTHLY[index] / tier,
|
|
121
|
+
currency: Currency.Usd,
|
|
122
|
+
},
|
|
123
|
+
})),
|
|
124
|
+
feature,
|
|
125
|
+
currency: Currency.Usd,
|
|
126
|
+
isTieredPrice: true,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
pricingModel: BillingModel.PerUnit,
|
|
130
|
+
billingPeriod: BillingPeriod.Annually,
|
|
131
|
+
tiersMode: TiersMode.Volume,
|
|
132
|
+
tiers: TIERS.map((tier, index) => ({
|
|
133
|
+
upTo: tier,
|
|
134
|
+
unitPrice: {
|
|
135
|
+
amount: TIERS_PRICE_YEARLY[index] / tier,
|
|
136
|
+
currency: Currency.Usd,
|
|
137
|
+
},
|
|
138
|
+
})),
|
|
139
|
+
feature,
|
|
140
|
+
currency: Currency.Usd,
|
|
141
|
+
isTieredPrice: true,
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function perUnitPricing(): Price[] {
|
|
147
|
+
const feature = {
|
|
148
|
+
featureId: 'feature-seats',
|
|
149
|
+
displayName: 'Seats',
|
|
150
|
+
units: 'seat',
|
|
151
|
+
unitsPlural: 'seats',
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
return [
|
|
155
|
+
{
|
|
156
|
+
pricingModel: BillingModel.PerUnit,
|
|
157
|
+
billingPeriod: BillingPeriod.Monthly,
|
|
158
|
+
amount: PER_UNIT_PRICE_MONTHLY,
|
|
159
|
+
currency: Currency.Usd,
|
|
160
|
+
feature,
|
|
161
|
+
tiersMode: undefined,
|
|
162
|
+
isTieredPrice: false,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
pricingModel: BillingModel.PerUnit,
|
|
166
|
+
billingPeriod: BillingPeriod.Annually,
|
|
167
|
+
amount: PER_UNIT_PRICE_YEARLY,
|
|
168
|
+
currency: Currency.Usd,
|
|
169
|
+
feature,
|
|
170
|
+
tiersMode: undefined,
|
|
171
|
+
isTieredPrice: false,
|
|
172
|
+
},
|
|
173
|
+
];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function mockPlan(planRefId: string): CheckoutStatePlan {
|
|
177
|
+
return {
|
|
178
|
+
id: '4b6c639e-1100-4ae1-86a9-31324994f992',
|
|
179
|
+
displayName: startCase(camelCase(planRefId)),
|
|
180
|
+
pricingType: PricingType.Paid,
|
|
181
|
+
pricePoints: [...flatFeePricing, ...perUnitPricing(), ...tieredPricing()],
|
|
182
|
+
product: mockProduct,
|
|
183
|
+
order: 0,
|
|
184
|
+
compatibleAddons: [additionalStorageAddons],
|
|
185
|
+
entitlements: [],
|
|
186
|
+
inheritedEntitlements: [],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function mockCheckoutState(params: GetCheckoutState): GetCheckoutStateResults {
|
|
191
|
+
const { planId } = params;
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
setupSecret: '',
|
|
195
|
+
customer: mockCustomer,
|
|
196
|
+
plan: mockPlan(planId),
|
|
197
|
+
billingIntegration: {
|
|
198
|
+
billingIdentifier: BillingVendorIdentifier.Stripe,
|
|
199
|
+
credentials: {
|
|
200
|
+
accountId: STRIPE_MOCK_ACCOUNT_ID,
|
|
201
|
+
publicKey: STRIPE_MOCK_ACCOUNT_PK,
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
resource: null,
|
|
205
|
+
};
|
|
206
|
+
}
|
package/src/theme/Theme.tsx
CHANGED
|
@@ -61,13 +61,22 @@ function createTypographyTheme(theme: StiggTheme): Partial<TypographyProps> {
|
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
const createMuiPalette = (theme: StiggTheme) => {
|
|
65
|
+
return {
|
|
66
|
+
primary: {
|
|
67
|
+
main: theme.palette.primary,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
64
72
|
export const SdkThemeProvider: React.FC<PropsWithChildren<SdkThemeProviderProps>> = ({ children, componentTheme }) => {
|
|
65
73
|
const theme = useStiggTheme(componentTheme);
|
|
74
|
+
const muiPalette = createMuiPalette(theme);
|
|
66
75
|
|
|
67
76
|
return (
|
|
68
77
|
// We are using styled-components theme here only because we are using styled-typography
|
|
69
78
|
// which depends on styled-components for the typography theming
|
|
70
|
-
<MuiThemeProvider theme={createTheme({ stigg: theme })}>
|
|
79
|
+
<MuiThemeProvider theme={createTheme({ palette: muiPalette, stigg: theme })}>
|
|
71
80
|
<ThemeProvider theme={{ stigg: theme }}>
|
|
72
81
|
<CustomCssGlobal customCss={theme.customCss}>
|
|
73
82
|
<Fonts externalFontUrl={theme.typography.fontFamilyUrl} />
|
|
@@ -21,6 +21,7 @@ export const getResolvedTheme = (customizedTheme?: CustomizedTheme): StiggTheme
|
|
|
21
21
|
palette: {
|
|
22
22
|
primary: primaryColor.hex(),
|
|
23
23
|
primaryDark: primaryColor.darken(0.3).hex(),
|
|
24
|
+
primaryLight: primaryColor.lighten(0.6).hex(),
|
|
24
25
|
backgroundPaper: '#FFFFFF',
|
|
25
26
|
backgroundHighlight: '#F5F6F9',
|
|
26
27
|
backgroundSection: primaryColor.alpha(0.1).toString(),
|