@stigg/react-sdk 4.4.0-beta.9 → 4.4.1
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 -1
- 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 +1030 -560
- 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 +1051 -590
- 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 +20 -13
- package/src/components/checkout/CheckoutProvider.tsx +5 -3
- package/src/components/checkout/components/DowngradeToFreeContainer.tsx +33 -36
- package/src/components/checkout/components/InputField.tsx +3 -0
- 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 +10 -12
- package/src/components/checkout/progressBar/CheckoutProgressBar.tsx +6 -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
|
@@ -10,10 +10,11 @@ import { useChargesSort } from '../../../hooks/useChargeSort';
|
|
|
10
10
|
import { calculateUnitQuantityText } from '../../../paywall/utils/calculateUnitQuantityText';
|
|
11
11
|
import { currencyPriceFormatter } from '../../../utils/currencyUtils';
|
|
12
12
|
import { InputField } from '../../components';
|
|
13
|
-
import { usePlanStepModel, useProgressBarModel } from '../../hooks';
|
|
13
|
+
import { useCheckoutModel, usePlanStepModel, useProgressBarModel } from '../../hooks';
|
|
14
14
|
import { TiersSelectContainer } from '../../../common/TiersSelectContainer';
|
|
15
|
-
import {
|
|
15
|
+
import { getTierByQuantity } from '../../../utils/priceTierUtils';
|
|
16
16
|
import { getValidPriceQuantity } from '../../../utils/priceUtils';
|
|
17
|
+
import { getFeatureDisplayNameText } from '../../../utils/getFeatureName';
|
|
17
18
|
|
|
18
19
|
export type UsePlanStepModel = ReturnType<typeof usePlanStepModel>;
|
|
19
20
|
|
|
@@ -31,6 +32,19 @@ const StyledPlanCharge = styled.div`
|
|
|
31
32
|
margin-top: 16px;
|
|
32
33
|
`;
|
|
33
34
|
|
|
35
|
+
const getValidationText = (charge: Price, quantity?: number) => {
|
|
36
|
+
const { minUnitQuantity, maxUnitQuantity } = charge;
|
|
37
|
+
if (!quantity || quantity < (minUnitQuantity || 1)) {
|
|
38
|
+
return `Minimum ${minUnitQuantity || 1}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (maxUnitQuantity && quantity > maxUnitQuantity) {
|
|
42
|
+
return `Maximum ${maxUnitQuantity}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return '';
|
|
46
|
+
};
|
|
47
|
+
|
|
34
48
|
export function PlanCharge({
|
|
35
49
|
charge,
|
|
36
50
|
isValid,
|
|
@@ -47,14 +61,22 @@ export function PlanCharge({
|
|
|
47
61
|
const featureId = charge.feature?.featureId;
|
|
48
62
|
const isBaseCharge = !featureId;
|
|
49
63
|
const isPayAsYouGo = charge.pricingModel === BillingModel.UsageBased;
|
|
50
|
-
const displayName = isBaseCharge
|
|
64
|
+
const displayName = isBaseCharge
|
|
65
|
+
? 'Base charge'
|
|
66
|
+
: getFeatureDisplayNameText(charge.feature?.displayName || '', charge.feature?.units, charge.feature?.unitsPlural);
|
|
51
67
|
const hasQuantityRestrictions = !!(charge?.minUnitQuantity || charge?.maxUnitQuantity);
|
|
52
68
|
|
|
53
69
|
const handleQuantityChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
54
70
|
if (isBaseCharge || !featureId) return;
|
|
55
71
|
|
|
56
|
-
const
|
|
57
|
-
|
|
72
|
+
const { minUnitQuantity, maxUnitQuantity } = charge;
|
|
73
|
+
const value = event?.target?.value ? Number(event?.target?.value) : null;
|
|
74
|
+
if (
|
|
75
|
+
!value ||
|
|
76
|
+
value <= 0 ||
|
|
77
|
+
(minUnitQuantity && value < minUnitQuantity) ||
|
|
78
|
+
(maxUnitQuantity && value > maxUnitQuantity)
|
|
79
|
+
) {
|
|
58
80
|
onValidationChange({ featureId, isValid: false });
|
|
59
81
|
// Reset the input value to null
|
|
60
82
|
// @ts-ignore
|
|
@@ -74,22 +96,22 @@ export function PlanCharge({
|
|
|
74
96
|
amount: charge.amount!,
|
|
75
97
|
currency: charge.currency,
|
|
76
98
|
locale: 'en-us',
|
|
99
|
+
minimumFractionDigits: 2,
|
|
77
100
|
});
|
|
78
101
|
|
|
79
102
|
chargeRow = `${formattedAmount}`;
|
|
80
103
|
if (isPayAsYouGo) {
|
|
81
104
|
chargeRow += ' / unit';
|
|
82
105
|
}
|
|
83
|
-
} else if (charge.isTieredPrice) {
|
|
106
|
+
} else if (charge.isTieredPrice && featureId) {
|
|
84
107
|
const tier = getTierByQuantity(charge.tiers!, billableFeature!.quantity || 1);
|
|
85
108
|
chargeRow = (
|
|
86
109
|
<TiersSelectContainer
|
|
87
110
|
componentId={`${featureId}-tiers`}
|
|
88
111
|
tiers={charge.tiers}
|
|
89
|
-
tierUnits={getPriceFeatureUnit(charge)}
|
|
90
112
|
selectedTier={tier}
|
|
91
113
|
handleTierChange={(tier: PriceTierFragment) => {
|
|
92
|
-
setBillableFeature(featureId
|
|
114
|
+
setBillableFeature(featureId, tier.upTo);
|
|
93
115
|
}}
|
|
94
116
|
/>
|
|
95
117
|
);
|
|
@@ -99,13 +121,10 @@ export function PlanCharge({
|
|
|
99
121
|
sx={{ width: 120 }}
|
|
100
122
|
id={`${featureId}-input`}
|
|
101
123
|
type="number"
|
|
102
|
-
InputProps={
|
|
103
|
-
hasQuantityRestrictions ? { inputProps: { min: charge.minUnitQuantity, max: charge.maxUnitQuantity } } : {}
|
|
104
|
-
}
|
|
105
124
|
error={!isValid}
|
|
106
|
-
helperText={!isValid ?
|
|
125
|
+
helperText={!isValid ? getValidationText(charge, billableFeature?.quantity) : undefined}
|
|
107
126
|
FormHelperTextProps={{ sx: { margin: '4px' } }}
|
|
108
|
-
value={billableFeature?.quantity ??
|
|
127
|
+
value={billableFeature?.quantity ?? ''}
|
|
109
128
|
onChange={handleQuantityChange}
|
|
110
129
|
onWheel={(e: React.WheelEvent<HTMLInputElement>) => (e.target as HTMLElement).blur()}
|
|
111
130
|
/>
|
|
@@ -135,6 +154,7 @@ export function PlanCharge({
|
|
|
135
154
|
export function CheckoutChargeList({ plan, billingPeriod }: CheckoutChargeListProps) {
|
|
136
155
|
const { billableFeatures, setBillableFeature } = usePlanStepModel();
|
|
137
156
|
const { setIsDisabled } = useProgressBarModel();
|
|
157
|
+
const { setIsValid } = useCheckoutModel();
|
|
138
158
|
const planCharges = useChargesSort(plan?.pricePoints?.filter((p) => p.billingPeriod === billingPeriod) || []);
|
|
139
159
|
const [chargesValidation, setChargesValidation] = useState(
|
|
140
160
|
planCharges?.reduce<Record<string, boolean>>((acc, curr) => {
|
|
@@ -146,7 +166,8 @@ export function CheckoutChargeList({ plan, billingPeriod }: CheckoutChargeListPr
|
|
|
146
166
|
useEffect(() => {
|
|
147
167
|
const isDisabled = Object.values(chargesValidation).some((x) => !x);
|
|
148
168
|
setIsDisabled(isDisabled);
|
|
149
|
-
|
|
169
|
+
setIsValid(!isDisabled);
|
|
170
|
+
}, [chargesValidation, setIsDisabled, setIsValid]);
|
|
150
171
|
|
|
151
172
|
return (
|
|
152
173
|
<div>
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { CheckoutContainerProps } from '../../CheckoutContainer';
|
|
2
3
|
|
|
3
4
|
import { useCheckoutModel } from '../../hooks/useCheckoutModel';
|
|
4
5
|
import { usePlanStepModel } from '../../hooks/usePlanStepModel';
|
|
6
|
+
import { PlanHeader } from '../../planHeader';
|
|
5
7
|
import { BillingPeriodPicker } from './BillingPeriodPicker';
|
|
6
8
|
import { CheckoutChargeList } from './CheckoutChargeList';
|
|
7
9
|
import { CheckoutPlanContainer } from './CheckoutPlanStep.style';
|
|
8
10
|
|
|
9
|
-
export const CheckoutPlanStep = () => {
|
|
11
|
+
export const CheckoutPlanStep = ({ onChangePlan }: Pick<CheckoutContainerProps, 'onChangePlan'>) => {
|
|
10
12
|
const { checkoutState, checkoutLocalization } = useCheckoutModel();
|
|
11
13
|
const { plan } = checkoutState || {};
|
|
12
14
|
const { billingPeriod } = usePlanStepModel();
|
|
13
15
|
|
|
14
16
|
return (
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
<
|
|
18
|
-
|
|
17
|
+
<>
|
|
18
|
+
<PlanHeader allowChangePlan onChangePlan={onChangePlan} />
|
|
19
|
+
<CheckoutPlanContainer>
|
|
20
|
+
<BillingPeriodPicker plan={plan} checkoutLocalization={checkoutLocalization} />
|
|
21
|
+
<CheckoutChargeList plan={plan} billingPeriod={billingPeriod} />
|
|
22
|
+
</CheckoutPlanContainer>
|
|
23
|
+
</>
|
|
19
24
|
);
|
|
20
25
|
};
|
|
@@ -69,7 +69,7 @@ export function CheckoutSuccess({ checkoutLocalization }: { checkoutLocalization
|
|
|
69
69
|
options={{ loop: false, autoplay: true, animationData }}
|
|
70
70
|
/>
|
|
71
71
|
<CheckoutSuccessText variant="h1" color="primary.main">
|
|
72
|
-
{checkoutLocalization.checkoutSuccessText}
|
|
72
|
+
{checkoutLocalization.summary.checkoutSuccessText}
|
|
73
73
|
</CheckoutSuccessText>
|
|
74
74
|
</CheckoutSuccessContainer>
|
|
75
75
|
);
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
AppliedCreditsLineItem,
|
|
24
24
|
BilledPriceLineItem,
|
|
25
25
|
DiscountLineItem,
|
|
26
|
+
FreeChargeLineItem,
|
|
26
27
|
LineItemContainer,
|
|
27
28
|
LineItemRow,
|
|
28
29
|
TaxLineItem,
|
|
@@ -31,6 +32,7 @@ import { WithSkeleton } from './components/WithSkeleton';
|
|
|
31
32
|
import { Icon } from '../../common/Icon';
|
|
32
33
|
import { CheckoutLocalization } from '../textOverrides';
|
|
33
34
|
import { CheckoutSuccess } from './CheckoutSuccess';
|
|
35
|
+
import { getFeatureDisplayNameText } from '../../utils/getFeatureName';
|
|
34
36
|
|
|
35
37
|
export const SummaryCard = styled(Paper)`
|
|
36
38
|
border-radius: 10px;
|
|
@@ -42,29 +44,28 @@ SummaryCard.defaultProps = {
|
|
|
42
44
|
elevation: 0,
|
|
43
45
|
};
|
|
44
46
|
|
|
45
|
-
const
|
|
47
|
+
const SummaryTitle = styled(Typography)`
|
|
46
48
|
margin-bottom: 16px;
|
|
49
|
+
font-weight: 500;
|
|
47
50
|
`;
|
|
48
51
|
|
|
49
52
|
const StyledDivider = styled(Divider)`
|
|
50
53
|
margin: 16px 0;
|
|
51
54
|
`;
|
|
52
55
|
|
|
53
|
-
const SubtotalText = styled(Typography)`
|
|
54
|
-
margin-top: 24px;
|
|
55
|
-
`;
|
|
56
|
-
|
|
57
56
|
const TotalDueText = styled(Typography)`
|
|
58
57
|
margin-bottom: 8px;
|
|
59
58
|
`;
|
|
60
59
|
|
|
61
60
|
function resolveCheckoutButtonText({
|
|
62
61
|
isLastStep,
|
|
62
|
+
checkoutHasChanges,
|
|
63
63
|
isFreeDowngrade,
|
|
64
64
|
checkoutLocalization,
|
|
65
65
|
isPlanUpdate,
|
|
66
66
|
}: {
|
|
67
67
|
isLastStep?: boolean;
|
|
68
|
+
checkoutHasChanges: boolean;
|
|
68
69
|
isFreeDowngrade: boolean;
|
|
69
70
|
checkoutLocalization: CheckoutLocalization;
|
|
70
71
|
isPlanUpdate?: boolean;
|
|
@@ -73,6 +74,10 @@ function resolveCheckoutButtonText({
|
|
|
73
74
|
return checkoutLocalization.checkoutButton.nextText;
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
if (!checkoutHasChanges) {
|
|
78
|
+
return checkoutLocalization.checkoutButton.noChangesText;
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
if (isPlanUpdate) {
|
|
77
82
|
return checkoutLocalization.checkoutButton.updateText;
|
|
78
83
|
}
|
|
@@ -90,6 +95,7 @@ export const CheckoutSummary = ({
|
|
|
90
95
|
disablePromotionCode,
|
|
91
96
|
disableSuccessAnimation,
|
|
92
97
|
isFreeDowngrade,
|
|
98
|
+
onMockCheckoutPreview,
|
|
93
99
|
}: CheckoutContainerProps & { isFreeDowngrade: boolean }) => {
|
|
94
100
|
const [isCheckoutCompletedSuccessfully, setIsCheckoutCompletedSuccessfully] = useState(false);
|
|
95
101
|
const { setErrorMessage } = usePaymentStepModel();
|
|
@@ -104,7 +110,7 @@ export const CheckoutSummary = ({
|
|
|
104
110
|
const [baseCharge] = baseCharges || [];
|
|
105
111
|
const isLastStep = isFreeDowngrade || (progressBar.isCheckoutComplete && progressBar.isLastStep);
|
|
106
112
|
|
|
107
|
-
const { subscriptionPreview, isFetchingSubscriptionPreview } = usePreviewSubscription();
|
|
113
|
+
const { subscriptionPreview, isFetchingSubscriptionPreview } = usePreviewSubscription({ onMockCheckoutPreview });
|
|
108
114
|
|
|
109
115
|
const { handleSubmit, isLoading } = useSubmit({
|
|
110
116
|
disableSuccessAnimation,
|
|
@@ -142,25 +148,47 @@ export const CheckoutSummary = ({
|
|
|
142
148
|
}
|
|
143
149
|
};
|
|
144
150
|
|
|
151
|
+
const checkoutHasChanges =
|
|
152
|
+
!!subscriptionPreview && (!!subscriptionPreview.proration || !!subscriptionPreview.hasScheduledUpdates);
|
|
153
|
+
const showPromotionCodeLine = !disablePromotionCode && !isFreeDowngrade;
|
|
154
|
+
const showDiscountLine =
|
|
155
|
+
!!subscriptionPreview?.recurringSubscription &&
|
|
156
|
+
!!subscriptionPreview?.recurringSubscription?.discountDetails &&
|
|
157
|
+
!isFreeDowngrade;
|
|
158
|
+
const hasDiscounts = showPromotionCodeLine || showDiscountLine;
|
|
159
|
+
|
|
160
|
+
const hasPayAsYouGoCharges = usageCharges.some((price) => price.pricingModel === BillingModel.UsageBased);
|
|
161
|
+
const onlyPayAsYouGoCharges =
|
|
162
|
+
hasPayAsYouGoCharges &&
|
|
163
|
+
!baseCharge &&
|
|
164
|
+
usageCharges.every((price) => price.pricingModel === BillingModel.UsageBased);
|
|
165
|
+
|
|
166
|
+
const baseChargeLabel =
|
|
167
|
+
typeof checkoutLocalization.summary.baseChargeText === 'function'
|
|
168
|
+
? checkoutLocalization.summary.baseChargeText({ billingPeriod: subscription.billingPeriod })
|
|
169
|
+
: checkoutLocalization.summary.baseChargeText;
|
|
170
|
+
|
|
145
171
|
return (
|
|
146
172
|
<Box flex={1}>
|
|
147
173
|
<SummaryCard>
|
|
148
|
-
<
|
|
174
|
+
<SummaryTitle variant="h6">{checkoutLocalization.summary.title}</SummaryTitle>
|
|
175
|
+
|
|
176
|
+
<Grid display="flex" flexDirection="row" alignItems="center" marginY={2}>
|
|
177
|
+
<Typography variant="body1" color="primary" style={{ paddingRight: '8px' }}>
|
|
178
|
+
{checkoutLocalization.summary.planName({ plan: plan! })}
|
|
179
|
+
</Typography>
|
|
180
|
+
<StyledDivider className="stigg-checkout-summary-divider" sx={{ flex: 1, margin: '0 !important' }} />
|
|
181
|
+
</Grid>
|
|
149
182
|
|
|
150
183
|
{baseCharge && (
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
</Typography>
|
|
158
|
-
<Typography variant="body1" color="secondary">
|
|
159
|
-
{currencyPriceFormatter({ amount: baseCharge.amount!, currency: baseCharge.currency })}
|
|
160
|
-
</Typography>
|
|
161
|
-
</LineItemRow>
|
|
162
|
-
</LineItemContainer>
|
|
184
|
+
<BilledPriceLineItem
|
|
185
|
+
checkoutLocalization={checkoutLocalization}
|
|
186
|
+
label={baseChargeLabel}
|
|
187
|
+
quantity={1}
|
|
188
|
+
price={baseCharge}
|
|
189
|
+
/>
|
|
163
190
|
)}
|
|
191
|
+
{!baseCharge && isFreeDowngrade ? <FreeChargeLineItem label={baseChargeLabel} /> : null}
|
|
164
192
|
|
|
165
193
|
{usageCharges.map((price) => {
|
|
166
194
|
const priceBillableFeature = subscription.billableFeatures?.find(
|
|
@@ -169,8 +197,13 @@ export const CheckoutSummary = ({
|
|
|
169
197
|
|
|
170
198
|
return (
|
|
171
199
|
<BilledPriceLineItem
|
|
200
|
+
checkoutLocalization={checkoutLocalization}
|
|
172
201
|
key={price.feature?.featureId}
|
|
173
|
-
label={
|
|
202
|
+
label={getFeatureDisplayNameText(
|
|
203
|
+
price.feature?.displayName || '',
|
|
204
|
+
price.feature?.units,
|
|
205
|
+
price.feature?.unitsPlural,
|
|
206
|
+
)}
|
|
174
207
|
quantity={priceBillableFeature?.quantity || 1}
|
|
175
208
|
price={price}
|
|
176
209
|
/>
|
|
@@ -179,11 +212,11 @@ export const CheckoutSummary = ({
|
|
|
179
212
|
|
|
180
213
|
{!!subscription.addons?.length && (
|
|
181
214
|
<>
|
|
182
|
-
<Grid display="flex" flexDirection="row" alignItems="center" marginY={
|
|
215
|
+
<Grid display="flex" flexDirection="row" alignItems="center" marginY={2}>
|
|
183
216
|
<Typography variant="body1" color="primary" style={{ paddingRight: '8px' }}>
|
|
184
|
-
{checkoutLocalization.addonsSectionTitle}
|
|
217
|
+
{checkoutLocalization.summary.addonsSectionTitle}
|
|
185
218
|
</Typography>
|
|
186
|
-
<StyledDivider className="stigg-checkout-summary-divider" sx={{ flex: 1 }} />
|
|
219
|
+
<StyledDivider className="stigg-checkout-summary-divider" sx={{ flex: 1, margin: '0 !important' }} />
|
|
187
220
|
</Grid>
|
|
188
221
|
{subscription.addons.map((addon) => {
|
|
189
222
|
const addonPrice = addon.addon.pricePoints?.find(
|
|
@@ -196,6 +229,7 @@ export const CheckoutSummary = ({
|
|
|
196
229
|
|
|
197
230
|
return (
|
|
198
231
|
<BilledPriceLineItem
|
|
232
|
+
checkoutLocalization={checkoutLocalization}
|
|
199
233
|
key={addon?.addon?.id}
|
|
200
234
|
label={addon.addon.displayName}
|
|
201
235
|
quantity={addonQuantity}
|
|
@@ -206,36 +240,93 @@ export const CheckoutSummary = ({
|
|
|
206
240
|
</>
|
|
207
241
|
)}
|
|
208
242
|
|
|
209
|
-
<
|
|
210
|
-
|
|
211
|
-
<
|
|
212
|
-
<
|
|
213
|
-
{
|
|
214
|
-
</
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
<StyledDivider className="stigg-checkout-summary-divider" />
|
|
243
|
+
{!hasDiscounts && <StyledDivider className="stigg-checkout-summary-divider" />}
|
|
244
|
+
{hasDiscounts && (
|
|
245
|
+
<Grid display="flex" flexDirection="row" alignItems="center" marginY={2}>
|
|
246
|
+
<Typography variant="body1" color="primary" style={{ paddingRight: '8px' }}>
|
|
247
|
+
{checkoutLocalization.summary.discountsSectionTitle}
|
|
248
|
+
</Typography>
|
|
249
|
+
<StyledDivider className="stigg-checkout-summary-divider" sx={{ flex: 1, margin: '0 !important' }} />
|
|
250
|
+
</Grid>
|
|
251
|
+
)}
|
|
219
252
|
|
|
220
|
-
{
|
|
221
|
-
|
|
222
|
-
|
|
253
|
+
{showPromotionCodeLine && (
|
|
254
|
+
<PromotionCodeSection
|
|
255
|
+
disabled={isLoading || isFetchingSubscriptionPreview}
|
|
256
|
+
checkoutLocalization={checkoutLocalization}
|
|
257
|
+
onMockCheckoutPreview={onMockCheckoutPreview}
|
|
258
|
+
/>
|
|
259
|
+
)}
|
|
223
260
|
|
|
224
|
-
|
|
225
|
-
|
|
261
|
+
{showDiscountLine && (
|
|
262
|
+
<DiscountLineItem
|
|
263
|
+
subscriptionPreview={subscriptionPreview!}
|
|
264
|
+
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
265
|
+
checkoutLocalization={checkoutLocalization}
|
|
266
|
+
/>
|
|
226
267
|
)}
|
|
227
268
|
|
|
228
|
-
<
|
|
229
|
-
subscriptionPreview={subscriptionPreview}
|
|
230
|
-
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
231
|
-
/>
|
|
269
|
+
{hasDiscounts && <StyledDivider className="stigg-checkout-summary-divider" />}
|
|
232
270
|
|
|
233
271
|
<TaxLineItem
|
|
234
|
-
|
|
272
|
+
tax={subscriptionPreview?.recurringSubscription?.tax}
|
|
273
|
+
taxDetails={subscriptionPreview?.recurringSubscription?.taxDetails}
|
|
235
274
|
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
236
275
|
checkoutLocalization={checkoutLocalization}
|
|
237
276
|
/>
|
|
238
277
|
|
|
278
|
+
{!isFreeDowngrade ? (
|
|
279
|
+
<>
|
|
280
|
+
<LineItemRow style={{ marginTop: 16 }}>
|
|
281
|
+
<Grid display="flex" flexDirection="column" container>
|
|
282
|
+
<Grid item display="flex" justifyContent="space-between">
|
|
283
|
+
<Typography variant="h6">{checkoutLocalization.summary.totalText}</Typography>
|
|
284
|
+
<Typography variant="h6">
|
|
285
|
+
<WithSkeleton isLoading={isFetchingSubscriptionPreview}>
|
|
286
|
+
{onlyPayAsYouGoCharges ? checkoutLocalization.summary.onlyPayAsYouGoText : null}
|
|
287
|
+
{!onlyPayAsYouGoCharges && hasPayAsYouGoCharges
|
|
288
|
+
? checkoutLocalization.summary.startsAtText
|
|
289
|
+
: null}
|
|
290
|
+
{!onlyPayAsYouGoCharges
|
|
291
|
+
? currencyPriceFormatter({
|
|
292
|
+
amount: 0,
|
|
293
|
+
...subscriptionPreview?.recurringSubscription?.total,
|
|
294
|
+
minimumFractionDigits: 2,
|
|
295
|
+
})
|
|
296
|
+
: null}
|
|
297
|
+
</WithSkeleton>
|
|
298
|
+
</Typography>
|
|
299
|
+
</Grid>
|
|
300
|
+
<Grid item>
|
|
301
|
+
<Typography variant="body1" color="secondary">
|
|
302
|
+
{checkoutLocalization.summary.totalBillingPeriodText({ billingPeriod: subscription.billingPeriod })}
|
|
303
|
+
</Typography>
|
|
304
|
+
</Grid>
|
|
305
|
+
</Grid>
|
|
306
|
+
</LineItemRow>
|
|
307
|
+
<StyledDivider className="stigg-checkout-summary-divider" />
|
|
308
|
+
</>
|
|
309
|
+
) : null}
|
|
310
|
+
|
|
311
|
+
{subscriptionPreview?.subTotal && subscriptionPreview?.subTotal.amount > 0 ? (
|
|
312
|
+
<LineItemContainer>
|
|
313
|
+
<LineItemRow>
|
|
314
|
+
<Typography variant="body1" color="secondary">
|
|
315
|
+
{checkoutLocalization.summary.proratedTotalDueText}
|
|
316
|
+
</Typography>
|
|
317
|
+
<Typography variant="body1" color="secondary">
|
|
318
|
+
<WithSkeleton isLoading={isFetchingSubscriptionPreview}>
|
|
319
|
+
{currencyPriceFormatter({
|
|
320
|
+
amount: subscriptionPreview?.subTotal.amount + (subscriptionPreview?.tax?.amount || 0),
|
|
321
|
+
currency: subscriptionPreview?.subTotal.currency,
|
|
322
|
+
minimumFractionDigits: 2,
|
|
323
|
+
})}
|
|
324
|
+
</WithSkeleton>
|
|
325
|
+
</Typography>
|
|
326
|
+
</LineItemRow>
|
|
327
|
+
</LineItemContainer>
|
|
328
|
+
) : null}
|
|
329
|
+
|
|
239
330
|
<AppliedCreditsLineItem
|
|
240
331
|
subscriptionPreview={subscriptionPreview}
|
|
241
332
|
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
@@ -244,10 +335,10 @@ export const CheckoutSummary = ({
|
|
|
244
335
|
|
|
245
336
|
<LineItemContainer>
|
|
246
337
|
<LineItemRow>
|
|
247
|
-
<TotalDueText variant="h6">{checkoutLocalization.
|
|
338
|
+
<TotalDueText variant="h6">{checkoutLocalization.summary.totalDueText}</TotalDueText>
|
|
248
339
|
<TotalDueText variant="h6">
|
|
249
340
|
<WithSkeleton isLoading={isFetchingSubscriptionPreview}>
|
|
250
|
-
{currencyPriceFormatter({ amount: 0, ...subscriptionPreview?.total })}
|
|
341
|
+
{currencyPriceFormatter({ amount: 0, ...subscriptionPreview?.total, minimumFractionDigits: 2 })}
|
|
251
342
|
</WithSkeleton>
|
|
252
343
|
</TotalDueText>
|
|
253
344
|
</LineItemRow>
|
|
@@ -265,7 +356,12 @@ export const CheckoutSummary = ({
|
|
|
265
356
|
<Button
|
|
266
357
|
$success={isCheckoutCompletedSuccessfully}
|
|
267
358
|
$error={isLastStep && isFreeDowngrade}
|
|
268
|
-
disabled={
|
|
359
|
+
disabled={
|
|
360
|
+
isLoading ||
|
|
361
|
+
isFetchingSubscriptionPreview ||
|
|
362
|
+
progressBar.progressBarState.isDisabled ||
|
|
363
|
+
(isLastStep && !checkoutHasChanges)
|
|
364
|
+
}
|
|
269
365
|
className="stigg-checkout-summary-cta-button"
|
|
270
366
|
sx={{ textTransform: 'none', borderRadius: '10px', marginTop: '24px', height: '36px' }}
|
|
271
367
|
variant="contained"
|
|
@@ -282,6 +378,7 @@ export const CheckoutSummary = ({
|
|
|
282
378
|
) : (
|
|
283
379
|
resolveCheckoutButtonText({
|
|
284
380
|
isLastStep,
|
|
381
|
+
checkoutHasChanges,
|
|
285
382
|
isFreeDowngrade,
|
|
286
383
|
checkoutLocalization,
|
|
287
384
|
isPlanUpdate: !!activeSubscription && activeSubscription?.plan.id === plan?.id,
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
/* eslint-disable react/no-unused-prop-types */
|
|
1
2
|
import React from 'react';
|
|
2
3
|
import moment from 'moment';
|
|
3
|
-
import { BillingModel, BillingPeriod, 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;
|
|
@@ -15,9 +16,11 @@ export type CheckoutCaptionProps = {
|
|
|
15
16
|
billingPeriod: BillingPeriod;
|
|
16
17
|
};
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const RemainingCreditsCaption = ({
|
|
20
|
+
subscriptionPreview,
|
|
21
|
+
isFetchingSubscriptionPreview,
|
|
22
|
+
checkoutLocalization,
|
|
23
|
+
}: CheckoutCaptionProps) => {
|
|
21
24
|
if (!subscriptionPreview?.proration?.netAmount?.amount || subscriptionPreview?.proration?.netAmount?.amount >= 0) {
|
|
22
25
|
return null;
|
|
23
26
|
}
|
|
@@ -27,12 +30,13 @@ const RemainingCreditsCaption = ({ subscriptionPreview, isFetchingSubscriptionPr
|
|
|
27
30
|
const credits = currencyPriceFormatter({
|
|
28
31
|
amount: positiveAmount,
|
|
29
32
|
currency: subscriptionPreview.proration.netAmount.currency,
|
|
33
|
+
minimumFractionDigits: 2,
|
|
30
34
|
});
|
|
31
35
|
|
|
32
36
|
return (
|
|
33
37
|
<Typography variant="caption" style={{ marginTop: 14 }}>
|
|
34
38
|
<WithSkeleton isLoading={isFetchingSubscriptionPreview} width="100%">
|
|
35
|
-
{
|
|
39
|
+
{checkoutLocalization.summary.creditsForUnusedTimeText({ credits })}
|
|
36
40
|
</WithSkeleton>
|
|
37
41
|
</Typography>
|
|
38
42
|
);
|
|
@@ -50,9 +54,9 @@ const ScheduledUpdatesCaption = ({
|
|
|
50
54
|
|
|
51
55
|
const { currentBillingPeriodEnd } = activeSubscription;
|
|
52
56
|
const changesWillApplyText =
|
|
53
|
-
typeof checkoutLocalization.changesWillApplyAtEndOfBillingPeriod === 'function'
|
|
54
|
-
? checkoutLocalization.changesWillApplyAtEndOfBillingPeriod({ billingPeriodEnd: currentBillingPeriodEnd })
|
|
55
|
-
: checkoutLocalization.changesWillApplyAtEndOfBillingPeriod;
|
|
57
|
+
typeof checkoutLocalization.summary.changesWillApplyAtEndOfBillingPeriod === 'function'
|
|
58
|
+
? checkoutLocalization.summary.changesWillApplyAtEndOfBillingPeriod({ billingPeriodEnd: currentBillingPeriodEnd })
|
|
59
|
+
: checkoutLocalization.summary.changesWillApplyAtEndOfBillingPeriod;
|
|
56
60
|
|
|
57
61
|
return (
|
|
58
62
|
<Typography variant="caption" style={{ marginTop: 14 }}>
|
|
@@ -70,15 +74,18 @@ const NextBillingCaption = ({
|
|
|
70
74
|
isFetchingSubscriptionPreview,
|
|
71
75
|
billingPeriod,
|
|
72
76
|
}: CheckoutCaptionProps) => {
|
|
73
|
-
if (!subscriptionPreview?.
|
|
77
|
+
if (!subscriptionPreview?.recurringSubscription?.total) {
|
|
74
78
|
return null;
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
const currentBillingPeriodEnd = subscriptionPreview.hasScheduledUpdates
|
|
78
82
|
? activeSubscription?.currentBillingPeriodEnd
|
|
79
83
|
: subscriptionPreview?.billingPeriodRange.end;
|
|
80
|
-
|
|
81
|
-
const total = currencyPriceFormatter(
|
|
84
|
+
|
|
85
|
+
const total = currencyPriceFormatter({
|
|
86
|
+
...subscriptionPreview?.recurringSubscription.total,
|
|
87
|
+
minimumFractionDigits: 2,
|
|
88
|
+
});
|
|
82
89
|
|
|
83
90
|
const hasUnitBasedPricing = plan?.pricePoints.some((price) => price.pricingModel === BillingModel.UsageBased);
|
|
84
91
|
const hasNonUnitBasedPricing = plan?.pricePoints.some((price) => price.pricingModel !== BillingModel.UsageBased);
|
|
@@ -93,11 +100,27 @@ const NextBillingCaption = ({
|
|
|
93
100
|
totalAmountText += '+';
|
|
94
101
|
}
|
|
95
102
|
|
|
96
|
-
totalAmountText += ' applicable usage-based
|
|
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
|
+
}
|
|
97
121
|
}
|
|
98
122
|
|
|
99
|
-
|
|
100
|
-
text += `${totalAmountText}for this subscription every ${billingPeriodText} on ${billingDate}, unless you cancel.`;
|
|
123
|
+
text += `${totalAmountText}for this subscription every ${billingPeriodText} on ${nextBillingDate}, unless you cancel.`;
|
|
101
124
|
|
|
102
125
|
return (
|
|
103
126
|
<Typography variant="caption" style={{ marginTop: 14 }}>
|