@stigg/react-sdk 4.4.0-beta.8 → 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 +5 -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/payment/stripe/useSubmit.d.ts +2 -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/CheckoutSuccess.d.ts +4 -1
- package/dist/components/checkout/summary/CheckoutSummary.d.ts +1 -1
- package/dist/components/checkout/summary/components/CheckoutCaptions.d.ts +3 -2
- package/dist/components/checkout/summary/components/LineItems.d.ts +12 -8
- package/dist/components/checkout/textOverrides.d.ts +65 -21
- 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 +1153 -624
- 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 +1176 -651
- 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 +21 -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/usePlanStepModel.ts +22 -7
- package/src/components/checkout/hooks/usePreviewSubscription.ts +103 -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 +7 -4
- 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/payment/stripe/useSubmit.ts +8 -4
- package/src/components/checkout/steps/plan/BillingPeriodPicker.style.tsx +3 -2
- package/src/components/checkout/steps/plan/BillingPeriodPicker.tsx +11 -8
- 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 +52 -6
- package/src/components/checkout/summary/CheckoutSummary.tsx +166 -59
- package/src/components/checkout/summary/components/CheckoutCaptions.tsx +63 -39
- package/src/components/checkout/summary/components/LineItems.tsx +77 -28
- package/src/components/checkout/textOverrides.ts +112 -30
- 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 -5
- 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,32 +1,42 @@
|
|
|
1
|
+
/* eslint-disable react/no-unused-prop-types */
|
|
1
2
|
import React from 'react';
|
|
2
3
|
import moment from 'moment';
|
|
3
|
-
import { Plan, Subscription,
|
|
4
|
+
import { BillingModel, BillingPeriod, Plan, Subscription, SubscriptionPreviewV2 } from '@stigg/js-client-sdk';
|
|
4
5
|
import { Typography } from '../../../common/Typography';
|
|
5
6
|
import { currencyPriceFormatter } from '../../../utils/currencyUtils';
|
|
6
7
|
import { CheckoutLocalization } from '../../textOverrides';
|
|
7
8
|
import { WithSkeleton } from './WithSkeleton';
|
|
8
9
|
|
|
9
10
|
export type CheckoutCaptionProps = {
|
|
10
|
-
subscriptionPreview?:
|
|
11
|
+
subscriptionPreview?: SubscriptionPreviewV2 | null;
|
|
11
12
|
isFetchingSubscriptionPreview: boolean;
|
|
12
13
|
activeSubscription?: Subscription | null;
|
|
13
14
|
plan?: Plan;
|
|
14
15
|
checkoutLocalization: CheckoutLocalization;
|
|
16
|
+
billingPeriod: BillingPeriod;
|
|
15
17
|
};
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
const RemainingCreditsCaption = ({
|
|
20
|
+
subscriptionPreview,
|
|
21
|
+
isFetchingSubscriptionPreview,
|
|
22
|
+
checkoutLocalization,
|
|
23
|
+
}: CheckoutCaptionProps) => {
|
|
20
24
|
if (!subscriptionPreview?.proration?.netAmount?.amount || subscriptionPreview?.proration?.netAmount?.amount >= 0) {
|
|
21
25
|
return null;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
const
|
|
28
|
+
const positiveAmount = subscriptionPreview.proration.netAmount.amount * -1;
|
|
29
|
+
|
|
30
|
+
const credits = currencyPriceFormatter({
|
|
31
|
+
amount: positiveAmount,
|
|
32
|
+
currency: subscriptionPreview.proration.netAmount.currency,
|
|
33
|
+
minimumFractionDigits: 2,
|
|
34
|
+
});
|
|
25
35
|
|
|
26
36
|
return (
|
|
27
37
|
<Typography variant="caption" style={{ marginTop: 14 }}>
|
|
28
38
|
<WithSkeleton isLoading={isFetchingSubscriptionPreview} width="100%">
|
|
29
|
-
{
|
|
39
|
+
{checkoutLocalization.summary.creditsForUnusedTimeText({ credits })}
|
|
30
40
|
</WithSkeleton>
|
|
31
41
|
</Typography>
|
|
32
42
|
);
|
|
@@ -44,9 +54,9 @@ const ScheduledUpdatesCaption = ({
|
|
|
44
54
|
|
|
45
55
|
const { currentBillingPeriodEnd } = activeSubscription;
|
|
46
56
|
const changesWillApplyText =
|
|
47
|
-
typeof checkoutLocalization.changesWillApplyAtEndOfBillingPeriod === 'function'
|
|
48
|
-
? checkoutLocalization.changesWillApplyAtEndOfBillingPeriod({ billingPeriodEnd: currentBillingPeriodEnd })
|
|
49
|
-
: checkoutLocalization.changesWillApplyAtEndOfBillingPeriod;
|
|
57
|
+
typeof checkoutLocalization.summary.changesWillApplyAtEndOfBillingPeriod === 'function'
|
|
58
|
+
? checkoutLocalization.summary.changesWillApplyAtEndOfBillingPeriod({ billingPeriodEnd: currentBillingPeriodEnd })
|
|
59
|
+
: checkoutLocalization.summary.changesWillApplyAtEndOfBillingPeriod;
|
|
50
60
|
|
|
51
61
|
return (
|
|
52
62
|
<Typography variant="caption" style={{ marginTop: 14 }}>
|
|
@@ -57,49 +67,65 @@ const ScheduledUpdatesCaption = ({
|
|
|
57
67
|
);
|
|
58
68
|
};
|
|
59
69
|
|
|
60
|
-
const ChargeDueTodayCaption = ({ subscriptionPreview, plan, isFetchingSubscriptionPreview }: CheckoutCaptionProps) => {
|
|
61
|
-
if (!subscriptionPreview?.total || subscriptionPreview.total.amount <= 0) {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const total = currencyPriceFormatter(subscriptionPreview.total);
|
|
66
|
-
const usedCredits = !!subscriptionPreview.credits?.used;
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
<Typography variant="caption" style={{ marginTop: 14 }}>
|
|
70
|
-
<WithSkeleton isLoading={isFetchingSubscriptionPreview} width="100%">
|
|
71
|
-
{`Today we’ll charge you ${total} (incl. taxes) for your new ${plan?.displayName} plan${
|
|
72
|
-
usedCredits ? ' minus credits left from your existing plan.' : ''
|
|
73
|
-
} `}
|
|
74
|
-
</WithSkeleton>
|
|
75
|
-
</Typography>
|
|
76
|
-
);
|
|
77
|
-
};
|
|
78
|
-
|
|
79
70
|
const NextBillingCaption = ({
|
|
80
71
|
subscriptionPreview,
|
|
81
72
|
activeSubscription,
|
|
82
73
|
plan,
|
|
83
74
|
isFetchingSubscriptionPreview,
|
|
75
|
+
billingPeriod,
|
|
84
76
|
}: CheckoutCaptionProps) => {
|
|
85
|
-
if (!subscriptionPreview?.
|
|
77
|
+
if (!subscriptionPreview?.recurringSubscription?.total) {
|
|
86
78
|
return null;
|
|
87
79
|
}
|
|
88
80
|
|
|
89
|
-
const isUpdatingSubscription = activeSubscription?.plan?.id && plan?.id && activeSubscription.plan.id === plan.id;
|
|
90
|
-
const isScheduledPlanDowngrade = subscriptionPreview.isPlanDowngrade && subscriptionPreview.hasScheduledUpdates;
|
|
91
81
|
const currentBillingPeriodEnd = subscriptionPreview.hasScheduledUpdates
|
|
92
82
|
? activeSubscription?.currentBillingPeriodEnd
|
|
93
83
|
: subscriptionPreview?.billingPeriodRange.end;
|
|
94
|
-
|
|
95
|
-
const total = currencyPriceFormatter(
|
|
84
|
+
|
|
85
|
+
const total = currencyPriceFormatter({
|
|
86
|
+
...subscriptionPreview?.recurringSubscription.total,
|
|
87
|
+
minimumFractionDigits: 2,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const hasUnitBasedPricing = plan?.pricePoints.some((price) => price.pricingModel === BillingModel.UsageBased);
|
|
91
|
+
const hasNonUnitBasedPricing = plan?.pricePoints.some((price) => price.pricingModel !== BillingModel.UsageBased);
|
|
92
|
+
|
|
93
|
+
let text = 'We will bill you ';
|
|
94
|
+
let totalAmountText = `${total} `;
|
|
95
|
+
|
|
96
|
+
if (hasUnitBasedPricing) {
|
|
97
|
+
if (!hasNonUnitBasedPricing) {
|
|
98
|
+
totalAmountText = 'for';
|
|
99
|
+
} else {
|
|
100
|
+
totalAmountText += '+';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
totalAmountText += ' applicable usage-based charges for this subscription ';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let nextBillingDate;
|
|
107
|
+
let billingPeriodText;
|
|
108
|
+
|
|
109
|
+
switch (billingPeriod) {
|
|
110
|
+
case BillingPeriod.Monthly:
|
|
111
|
+
billingPeriodText = 'month';
|
|
112
|
+
nextBillingDate = `the ${moment(currentBillingPeriodEnd).format('Do')}`;
|
|
113
|
+
break;
|
|
114
|
+
case BillingPeriod.Annually:
|
|
115
|
+
billingPeriodText = 'year';
|
|
116
|
+
nextBillingDate = moment(currentBillingPeriodEnd).format('MMMM Do');
|
|
117
|
+
break;
|
|
118
|
+
default: {
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
text += `${totalAmountText}for this subscription every ${billingPeriodText} on ${nextBillingDate}, unless you cancel.`;
|
|
96
124
|
|
|
97
125
|
return (
|
|
98
126
|
<Typography variant="caption" style={{ marginTop: 14 }}>
|
|
99
127
|
<WithSkeleton isLoading={isFetchingSubscriptionPreview} width="100%">
|
|
100
|
-
{
|
|
101
|
-
isScheduledPlanDowngrade ? 'start' : 'renew'
|
|
102
|
-
} on ${billingDate} for ${total} (incl. taxes) unless you cancel it.`}
|
|
128
|
+
{text}
|
|
103
129
|
</WithSkeleton>
|
|
104
130
|
</Typography>
|
|
105
131
|
);
|
|
@@ -112,8 +138,6 @@ export function CheckoutCaptions(props: CheckoutCaptionProps) {
|
|
|
112
138
|
|
|
113
139
|
<ScheduledUpdatesCaption {...props} />
|
|
114
140
|
|
|
115
|
-
<ChargeDueTodayCaption {...props} />
|
|
116
|
-
|
|
117
141
|
<NextBillingCaption {...props} />
|
|
118
142
|
</>
|
|
119
143
|
);
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import styled from '@emotion/styled/macro';
|
|
3
3
|
import { Grid } from '@mui/material';
|
|
4
|
-
import {
|
|
4
|
+
import { BillingModel, BillingPeriod, Price, SubscriptionPreviewV2 } from '@stigg/js-client-sdk';
|
|
5
5
|
import { Typography } from '../../../common/Typography';
|
|
6
6
|
import { currencyPriceFormatter } from '../../../utils/currencyUtils';
|
|
7
7
|
import { getTierByQuantity } from '../../../utils/priceTierUtils';
|
|
8
8
|
import { WithSkeleton } from './WithSkeleton';
|
|
9
9
|
import { Skeleton } from '../../components/Skeletons.style';
|
|
10
10
|
import { CheckoutLocalization } from '../../textOverrides';
|
|
11
|
+
import { Icon } from '../../../common/Icon';
|
|
12
|
+
import { InformationTooltip } from '../../../common/InformationTooltip';
|
|
11
13
|
|
|
12
14
|
export const LineItemContainer = styled.div`
|
|
13
15
|
& + & {
|
|
@@ -30,15 +32,35 @@ export const getPriceString = ({ amount, price, quantity }: { amount: number; pr
|
|
|
30
32
|
billingPeriodString = '12 months';
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
const addonPriceFormat = currencyPriceFormatter({ amount, currency: price.currency });
|
|
35
|
+
const addonPriceFormat = currencyPriceFormatter({ amount, currency: price.currency, minimumFractionDigits: 2 });
|
|
34
36
|
|
|
35
37
|
return `${quantity > 1 ? `${quantity} x ${addonPriceFormat} each` : addonPriceFormat}${
|
|
36
38
|
billingPeriodString ? ` x ${billingPeriodString}` : ''
|
|
37
39
|
}`;
|
|
38
40
|
};
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
const PayAsYouGoPriceTooltip = ({ checkoutLocalization }: { checkoutLocalization: CheckoutLocalization }) => {
|
|
43
|
+
const title = <Typography variant="body1">{checkoutLocalization.summary.payAsYouGoTooltipText}</Typography>;
|
|
44
|
+
return (
|
|
45
|
+
<InformationTooltip placement="left" title={title}>
|
|
46
|
+
<Icon icon="PayAsYouGoCharge" style={{ display: 'flex' }} />
|
|
47
|
+
</InformationTooltip>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const BilledPriceLineItem = ({
|
|
52
|
+
checkoutLocalization,
|
|
53
|
+
label,
|
|
54
|
+
quantity,
|
|
55
|
+
price,
|
|
56
|
+
}: {
|
|
57
|
+
checkoutLocalization: CheckoutLocalization;
|
|
58
|
+
label: string;
|
|
59
|
+
quantity: number;
|
|
60
|
+
price: Price;
|
|
61
|
+
}) => {
|
|
41
62
|
const { billingPeriod } = price;
|
|
63
|
+
const isPayAsYouGo = price.pricingModel === BillingModel.UsageBased;
|
|
42
64
|
|
|
43
65
|
let amount;
|
|
44
66
|
if (price.isTieredPrice) {
|
|
@@ -52,7 +74,7 @@ export const BilledPriceLineItem = ({ label, quantity, price }: { label: string;
|
|
|
52
74
|
<LineItemContainer>
|
|
53
75
|
<LineItemRow style={{ alignItems: 'flex-end' }}>
|
|
54
76
|
<Grid item>
|
|
55
|
-
<Typography variant="
|
|
77
|
+
<Typography variant="body1" color="secondary">
|
|
56
78
|
{label}
|
|
57
79
|
</Typography>
|
|
58
80
|
{(quantity > 1 || billingPeriod === BillingPeriod.Annually) && (
|
|
@@ -61,9 +83,30 @@ export const BilledPriceLineItem = ({ label, quantity, price }: { label: string;
|
|
|
61
83
|
</Typography>
|
|
62
84
|
)}
|
|
63
85
|
</Grid>
|
|
86
|
+
<Grid item display="flex" gap={1} alignItems="center">
|
|
87
|
+
{isPayAsYouGo && <PayAsYouGoPriceTooltip checkoutLocalization={checkoutLocalization} />}
|
|
88
|
+
<Typography variant="body1" color="secondary" style={{ wordBreak: 'break-word' }}>
|
|
89
|
+
{currencyPriceFormatter({ amount: quantity * amount, currency: price.currency, minimumFractionDigits: 2 })}
|
|
90
|
+
{isPayAsYouGo && ' / unit'}
|
|
91
|
+
</Typography>
|
|
92
|
+
</Grid>
|
|
93
|
+
</LineItemRow>
|
|
94
|
+
</LineItemContainer>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const FreeChargeLineItem = ({ label }: { label: string }) => {
|
|
99
|
+
return (
|
|
100
|
+
<LineItemContainer>
|
|
101
|
+
<LineItemRow style={{ alignItems: 'flex-end' }}>
|
|
64
102
|
<Grid item>
|
|
103
|
+
<Typography variant="body1" color="secondary">
|
|
104
|
+
{label}
|
|
105
|
+
</Typography>
|
|
106
|
+
</Grid>
|
|
107
|
+
<Grid item display="flex" gap={1} alignItems="center">
|
|
65
108
|
<Typography variant="body1" color="secondary" style={{ wordBreak: 'break-word' }}>
|
|
66
|
-
|
|
109
|
+
Free
|
|
67
110
|
</Typography>
|
|
68
111
|
</Grid>
|
|
69
112
|
</LineItemRow>
|
|
@@ -74,33 +117,35 @@ export const BilledPriceLineItem = ({ label, quantity, price }: { label: string;
|
|
|
74
117
|
export const DiscountLineItem = ({
|
|
75
118
|
subscriptionPreview,
|
|
76
119
|
isFetchingSubscriptionPreview,
|
|
120
|
+
checkoutLocalization,
|
|
77
121
|
}: {
|
|
78
|
-
subscriptionPreview:
|
|
122
|
+
subscriptionPreview: SubscriptionPreviewV2;
|
|
79
123
|
isFetchingSubscriptionPreview: boolean;
|
|
124
|
+
checkoutLocalization: CheckoutLocalization;
|
|
80
125
|
}) => {
|
|
81
|
-
const {
|
|
82
|
-
if (!
|
|
126
|
+
const { discount, discountDetails } = subscriptionPreview.recurringSubscription || {};
|
|
127
|
+
if (!discount || !discountDetails) {
|
|
83
128
|
return null;
|
|
84
129
|
}
|
|
85
130
|
|
|
86
|
-
let discountAmount: number;
|
|
87
|
-
if (discount.type === DiscountType.Percentage) {
|
|
88
|
-
discountAmount = -1 * Math.abs((discount.value / 100) * subTotal.amount);
|
|
89
|
-
} else {
|
|
90
|
-
discountAmount = -1 * Math.abs(discount.value);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
131
|
return (
|
|
94
132
|
<LineItemContainer>
|
|
95
133
|
<LineItemRow>
|
|
96
134
|
<Typography variant="body1" color="secondary">
|
|
97
|
-
{
|
|
135
|
+
{checkoutLocalization.summary.discountText({
|
|
136
|
+
discountDetails,
|
|
137
|
+
currency: subscriptionPreview.total.currency,
|
|
138
|
+
})}
|
|
98
139
|
</Typography>
|
|
99
140
|
<Typography variant="body1" color="secondary">
|
|
100
141
|
{isFetchingSubscriptionPreview ? (
|
|
101
142
|
<Skeleton width={50} height={16} />
|
|
102
143
|
) : (
|
|
103
|
-
currencyPriceFormatter({
|
|
144
|
+
currencyPriceFormatter({
|
|
145
|
+
amount: discount.amount,
|
|
146
|
+
currency: discount.currency,
|
|
147
|
+
minimumFractionDigits: 2,
|
|
148
|
+
})
|
|
104
149
|
)}
|
|
105
150
|
</Typography>
|
|
106
151
|
</LineItemRow>
|
|
@@ -113,12 +158,13 @@ export const AppliedCreditsLineItem = ({
|
|
|
113
158
|
isFetchingSubscriptionPreview,
|
|
114
159
|
checkoutLocalization,
|
|
115
160
|
}: {
|
|
116
|
-
subscriptionPreview:
|
|
161
|
+
subscriptionPreview: SubscriptionPreviewV2 | null;
|
|
117
162
|
isFetchingSubscriptionPreview: boolean;
|
|
118
163
|
checkoutLocalization: CheckoutLocalization;
|
|
119
164
|
}) => {
|
|
120
165
|
const { credits } = subscriptionPreview || {};
|
|
121
|
-
|
|
166
|
+
|
|
167
|
+
if (!credits || !credits.used || credits.used.amount <= 0) {
|
|
122
168
|
return null;
|
|
123
169
|
}
|
|
124
170
|
|
|
@@ -126,11 +172,15 @@ export const AppliedCreditsLineItem = ({
|
|
|
126
172
|
<LineItemContainer>
|
|
127
173
|
<LineItemRow>
|
|
128
174
|
<Typography variant="body1" color="secondary">
|
|
129
|
-
{checkoutLocalization.appliedCreditsTitle}
|
|
175
|
+
{checkoutLocalization.summary.appliedCreditsTitle}
|
|
130
176
|
</Typography>
|
|
131
177
|
<Typography variant="body1" color="secondary">
|
|
132
178
|
<WithSkeleton isLoading={isFetchingSubscriptionPreview}>
|
|
133
|
-
{currencyPriceFormatter({
|
|
179
|
+
{currencyPriceFormatter({
|
|
180
|
+
amount: -1 * credits.used.amount,
|
|
181
|
+
currency: credits.used.currency,
|
|
182
|
+
minimumFractionDigits: 2,
|
|
183
|
+
})}
|
|
134
184
|
</WithSkeleton>
|
|
135
185
|
</Typography>
|
|
136
186
|
</LineItemRow>
|
|
@@ -139,16 +189,15 @@ export const AppliedCreditsLineItem = ({
|
|
|
139
189
|
};
|
|
140
190
|
|
|
141
191
|
export const TaxLineItem = ({
|
|
142
|
-
|
|
192
|
+
tax,
|
|
193
|
+
taxDetails,
|
|
143
194
|
isFetchingSubscriptionPreview,
|
|
144
195
|
checkoutLocalization,
|
|
145
196
|
}: {
|
|
146
|
-
subscriptionPreview: SubscriptionPreview | null;
|
|
147
197
|
isFetchingSubscriptionPreview: boolean;
|
|
148
198
|
checkoutLocalization: CheckoutLocalization;
|
|
149
|
-
}) => {
|
|
150
|
-
|
|
151
|
-
if (!taxDetails || !tax) {
|
|
199
|
+
} & Pick<SubscriptionPreviewV2, 'tax' | 'taxDetails'>) => {
|
|
200
|
+
if (!taxDetails || !tax || tax?.amount <= 0) {
|
|
152
201
|
return null;
|
|
153
202
|
}
|
|
154
203
|
|
|
@@ -156,11 +205,11 @@ export const TaxLineItem = ({
|
|
|
156
205
|
<LineItemContainer>
|
|
157
206
|
<LineItemRow>
|
|
158
207
|
<Typography variant="body1" color="secondary">
|
|
159
|
-
{checkoutLocalization.taxTitle({ taxDetails })}
|
|
208
|
+
{checkoutLocalization.summary.taxTitle({ taxDetails })}
|
|
160
209
|
</Typography>
|
|
161
210
|
<Typography variant="body1" color="secondary">
|
|
162
211
|
<WithSkeleton isLoading={isFetchingSubscriptionPreview}>
|
|
163
|
-
{currencyPriceFormatter({ amount: tax?.amount, currency: tax?.currency })}
|
|
212
|
+
{currencyPriceFormatter({ amount: tax?.amount, currency: tax?.currency, minimumFractionDigits: 2 })}
|
|
164
213
|
</WithSkeleton>
|
|
165
214
|
</Typography>
|
|
166
215
|
</LineItemRow>
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BillingPeriod,
|
|
3
|
+
DiscountType,
|
|
4
|
+
Plan,
|
|
5
|
+
SubscriptionPreviewDiscount,
|
|
6
|
+
SubscriptionPreviewTaxDetails,
|
|
7
|
+
DiscountDurationType,
|
|
8
|
+
} from '@stigg/js-client-sdk';
|
|
2
9
|
import moment from 'moment';
|
|
3
10
|
import merge from 'lodash/merge';
|
|
4
11
|
import { DeepPartial } from '../../types';
|
|
12
|
+
import { currencyPriceFormatter } from '../utils/currencyUtils';
|
|
5
13
|
|
|
6
14
|
export type CheckoutLocalization = {
|
|
7
15
|
changePlan: string;
|
|
@@ -10,53 +18,127 @@ export type CheckoutLocalization = {
|
|
|
10
18
|
newPaymentMethodText: string;
|
|
11
19
|
newPaymentMethodCardTitle: string;
|
|
12
20
|
newPaymentMethodBillingAddressTitle: string;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
summary: {
|
|
22
|
+
title: string;
|
|
23
|
+
planName: (params: { plan: Plan }) => string;
|
|
24
|
+
addCouponCodeText: string;
|
|
25
|
+
couponCodeTitle: string;
|
|
26
|
+
addonsSectionTitle: string;
|
|
27
|
+
discountsSectionTitle: string;
|
|
28
|
+
onlyPayAsYouGoText: string;
|
|
29
|
+
startsAtText: string;
|
|
30
|
+
proratedTotalDueText: string;
|
|
31
|
+
baseChargeText: string | ((params: { billingPeriod: BillingPeriod }) => string);
|
|
32
|
+
totalText: string;
|
|
33
|
+
totalDueText: string;
|
|
34
|
+
totalBillingPeriodText: (params: { billingPeriod: BillingPeriod }) => string;
|
|
35
|
+
discountText: (params: { discountDetails: SubscriptionPreviewDiscount; currency: string }) => string;
|
|
36
|
+
appliedCreditsTitle: string;
|
|
37
|
+
taxTitle: (params: { taxDetails: SubscriptionPreviewTaxDetails }) => string;
|
|
38
|
+
checkoutSuccessText: string;
|
|
39
|
+
payAsYouGoTooltipText: string;
|
|
40
|
+
changesWillApplyAtEndOfBillingPeriod: string | ((params: { billingPeriodEnd: Date }) => string);
|
|
41
|
+
creditsForUnusedTimeText: (params: { credits: string }) => string;
|
|
42
|
+
};
|
|
20
43
|
checkoutButton: {
|
|
21
44
|
nextText: string;
|
|
22
|
-
|
|
45
|
+
noChangesText: string;
|
|
46
|
+
updateText: string;
|
|
47
|
+
downgradeToFreeText: string;
|
|
23
48
|
upgradeText: string;
|
|
24
|
-
purchaseText: string;
|
|
25
49
|
};
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
50
|
+
downgradeToFree: {
|
|
51
|
+
alertText: (params: { plan: Plan }) => string;
|
|
52
|
+
freePlanHeader: (params: { plan: Plan }) => string;
|
|
53
|
+
freePlanName: (params: { plan: Plan }) => string;
|
|
54
|
+
freePlanPriceText: (params: { plan: Plan }) => string;
|
|
55
|
+
paidPlanHeader: (params: { plan: Plan }) => string;
|
|
56
|
+
paidPlanName: (params: { plan: Plan }) => string;
|
|
57
|
+
paidPlanPriceText: (params: { plan: Plan; billingPeriod?: BillingPeriod }) => string;
|
|
58
|
+
};
|
|
29
59
|
};
|
|
30
60
|
|
|
31
61
|
export function getResolvedCheckoutLocalize(
|
|
32
62
|
localizeOverride?: DeepPartial<CheckoutLocalization>,
|
|
33
63
|
): CheckoutLocalization {
|
|
34
64
|
const checkoutDefaultLocalization: CheckoutLocalization = {
|
|
35
|
-
changePlan: 'Change
|
|
65
|
+
changePlan: 'Change',
|
|
36
66
|
billingPeriodsTitle: 'Billing cycle',
|
|
37
67
|
addAddonText: 'Add',
|
|
38
68
|
newPaymentMethodText: 'New payment method',
|
|
39
69
|
newPaymentMethodBillingAddressTitle: 'Billing address',
|
|
40
70
|
newPaymentMethodCardTitle: 'Payment method',
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
71
|
+
summary: {
|
|
72
|
+
title: 'Order summary',
|
|
73
|
+
planName: ({ plan }) => `${plan.displayName} plan`,
|
|
74
|
+
addCouponCodeText: 'Add promotion code',
|
|
75
|
+
couponCodeTitle: 'Promotion code',
|
|
76
|
+
addonsSectionTitle: 'Add-ons',
|
|
77
|
+
discountsSectionTitle: 'Discounts',
|
|
78
|
+
onlyPayAsYouGoText: 'Varies by usage',
|
|
79
|
+
startsAtText: 'Starts at ',
|
|
80
|
+
proratedTotalDueText: 'Prorated charge today',
|
|
81
|
+
baseChargeText: () => 'Base charge',
|
|
82
|
+
totalText: 'Total',
|
|
83
|
+
totalDueText: 'Total due today',
|
|
84
|
+
totalBillingPeriodText: ({ billingPeriod }) => `Billed ${billingPeriod.toLowerCase()}`,
|
|
85
|
+
appliedCreditsTitle: 'Applied credits',
|
|
86
|
+
creditsForUnusedTimeText: ({ credits }) =>
|
|
87
|
+
`Your account will be granted credits worth ${credits} for unused time, which will be automatically applied to future payments.`,
|
|
88
|
+
discountText: ({ discountDetails, currency }) => {
|
|
89
|
+
let discountText = '';
|
|
90
|
+
if (discountDetails.type === DiscountType.Percentage) {
|
|
91
|
+
discountText = `${discountDetails.value}% off`;
|
|
92
|
+
} else {
|
|
93
|
+
discountText = `${currencyPriceFormatter({ amount: discountDetails.value, currency })}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let discountTextPostfix = '';
|
|
97
|
+
switch (discountDetails.durationType) {
|
|
98
|
+
case DiscountDurationType.Once: {
|
|
99
|
+
discountTextPostfix = ' once';
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case DiscountDurationType.Repeating: {
|
|
103
|
+
const prefix = discountDetails.type === DiscountType.Fixed ? ' per month' : '';
|
|
104
|
+
discountTextPostfix = `${prefix} for ${discountDetails.durationInMonths} months`;
|
|
105
|
+
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
case DiscountDurationType.Forever:
|
|
110
|
+
default: {
|
|
111
|
+
discountTextPostfix = '';
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return `${discountText}${discountTextPostfix}`;
|
|
116
|
+
},
|
|
117
|
+
taxTitle: ({ taxDetails }) => `Tax (${taxDetails?.percentage}%)`,
|
|
118
|
+
changesWillApplyAtEndOfBillingPeriod: ({ billingPeriodEnd }) =>
|
|
119
|
+
`Your changes will take effect at the end of your current billing cycle on ${moment(billingPeriodEnd).format(
|
|
120
|
+
'MMMM D, YYYY',
|
|
121
|
+
)}.`,
|
|
122
|
+
checkoutSuccessText: 'Changes applied',
|
|
123
|
+
payAsYouGoTooltipText: 'Billed according to actual usage',
|
|
124
|
+
},
|
|
51
125
|
checkoutButton: {
|
|
52
126
|
nextText: 'Next',
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
127
|
+
noChangesText: 'No changes',
|
|
128
|
+
updateText: 'Update subscription',
|
|
129
|
+
downgradeToFreeText: 'Cancel subscription',
|
|
130
|
+
upgradeText: 'Subscribe',
|
|
131
|
+
},
|
|
132
|
+
downgradeToFree: {
|
|
133
|
+
alertText: () => `We’re sorry to see you cancel your paid subscription 😭`,
|
|
134
|
+
freePlanHeader: () => 'New plan',
|
|
135
|
+
freePlanName: ({ plan }) => `${plan.displayName}`,
|
|
136
|
+
freePlanPriceText: () => 'Free',
|
|
137
|
+
paidPlanHeader: () => 'Current plan',
|
|
138
|
+
paidPlanName: ({ plan }) => `${plan.displayName}`,
|
|
139
|
+
paidPlanPriceText: ({ billingPeriod }) =>
|
|
140
|
+
`Paid plan${billingPeriod ? `, billed ${billingPeriod.toLowerCase()}` : ''}`,
|
|
56
141
|
},
|
|
57
|
-
appliedCreditsTitle: 'Applied credits',
|
|
58
|
-
taxTitle: ({ taxDetails }) => `Tax (${taxDetails?.percentage}%)`,
|
|
59
|
-
downgradeToFreeAlertText: () => `We’re sorry to see you cancel your paid subscription 😭`,
|
|
60
142
|
};
|
|
61
143
|
|
|
62
144
|
return merge(checkoutDefaultLocalization, localizeOverride);
|
|
@@ -7,7 +7,6 @@ export type CheckoutTheme = {
|
|
|
7
7
|
textColor: string;
|
|
8
8
|
backgroundColor: string;
|
|
9
9
|
borderColor: string;
|
|
10
|
-
selectionColor: string;
|
|
11
10
|
summaryBackgroundColor: string;
|
|
12
11
|
};
|
|
13
12
|
|
|
@@ -16,7 +15,6 @@ const defaultCheckoutTheme: CheckoutTheme = {
|
|
|
16
15
|
textColor: 'rgb(0, 30, 108)',
|
|
17
16
|
backgroundColor: 'rgb(255, 255, 255)',
|
|
18
17
|
borderColor: 'rgb(235, 237, 243)',
|
|
19
|
-
selectionColor: 'rgb(229, 242, 255)',
|
|
20
18
|
summaryBackgroundColor: 'rgb(109, 121, 144)',
|
|
21
19
|
};
|
|
22
20
|
|
|
@@ -33,8 +31,6 @@ export function getResolvedCheckoutTheme(
|
|
|
33
31
|
backgroundColor:
|
|
34
32
|
themeOverride?.backgroundColor || globalPalette?.backgroundPaper || defaultCheckoutTheme.backgroundColor,
|
|
35
33
|
borderColor: themeOverride?.borderColor || globalPalette?.outlinedBorder || defaultCheckoutTheme.borderColor,
|
|
36
|
-
selectionColor:
|
|
37
|
-
themeOverride?.selectionColor || globalPalette?.backgroundSection || defaultCheckoutTheme.selectionColor,
|
|
38
34
|
summaryBackgroundColor:
|
|
39
35
|
themeOverride?.summaryBackgroundColor ||
|
|
40
36
|
globalPalette?.backgroundHighlight ||
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
GetCheckoutState,
|
|
3
|
+
GetCheckoutStateResults,
|
|
4
|
+
PreviewSubscription,
|
|
5
|
+
SubscriptionBillingInfo,
|
|
6
|
+
SubscriptionPreviewV2,
|
|
7
|
+
} from '@stigg/js-client-sdk';
|
|
2
8
|
|
|
3
9
|
export type BillingInformation = {
|
|
4
10
|
taxDetails?: TaxDetailsInput;
|
|
@@ -7,3 +13,11 @@ export type BillingInformation = {
|
|
|
7
13
|
export type TaxDetailsInput = {
|
|
8
14
|
taxPercentage?: SubscriptionBillingInfo['taxPercentage'];
|
|
9
15
|
};
|
|
16
|
+
|
|
17
|
+
export type MockCheckoutPreviewCallback = (params: PreviewSubscription) => SubscriptionPreviewV2;
|
|
18
|
+
export type MockCheckoutStateCallback = (params: GetCheckoutState) => GetCheckoutStateResults;
|
|
19
|
+
|
|
20
|
+
export type CheckoutMockProps = {
|
|
21
|
+
onMockCheckoutPreview?: MockCheckoutPreviewCallback;
|
|
22
|
+
onMockCheckoutState?: MockCheckoutStateCallback;
|
|
23
|
+
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import styled from '@emotion/styled/macro';
|
|
3
|
-
import { CSSProperties } from 'react';
|
|
4
|
-
import { css,
|
|
2
|
+
import React, { CSSProperties } from 'react';
|
|
3
|
+
import { css, useTheme } from '@emotion/react';
|
|
5
4
|
import { getIconColor, IconColor } from './iconColor';
|
|
6
5
|
import * as customIcons from './customIcons';
|
|
7
6
|
|
|
@@ -45,7 +44,7 @@ export type IconProps = {
|
|
|
45
44
|
|
|
46
45
|
export function Icon({ icon, className, style, svgPathColor, svgRectColor, svgStrokeColor }: IconProps) {
|
|
47
46
|
const IconComponent = (customIcons as any)[icon];
|
|
48
|
-
const theme = useTheme()
|
|
47
|
+
const theme = useTheme();
|
|
49
48
|
|
|
50
49
|
return (
|
|
51
50
|
<IconWrapper
|
|
@@ -53,8 +52,7 @@ export function Icon({ icon, className, style, svgPathColor, svgRectColor, svgSt
|
|
|
53
52
|
style={style}
|
|
54
53
|
$pathColor={getIconColor(svgPathColor, theme)}
|
|
55
54
|
$rectColor={getIconColor(svgRectColor, theme)}
|
|
56
|
-
$strokeColor={getIconColor(svgStrokeColor, theme)}
|
|
57
|
-
>
|
|
55
|
+
$strokeColor={getIconColor(svgStrokeColor, theme)}>
|
|
58
56
|
<IconComponent />
|
|
59
57
|
</IconWrapper>
|
|
60
58
|
);
|