@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,11 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
import isEmpty from 'lodash/isEmpty';
|
|
3
|
-
import { BillingAddress, PreviewSubscription,
|
|
3
|
+
import { BillingAddress, PreviewSubscription, StiggClient, SubscriptionPreviewV2 } from '@stigg/js-client-sdk';
|
|
4
4
|
import { useStiggContext } from '../../StiggProvider';
|
|
5
5
|
import { useCheckoutContext } from '../CheckoutProvider';
|
|
6
6
|
import { useCheckoutModel } from './useCheckoutModel';
|
|
7
|
-
import { useSubscriptionModel } from './useSubscriptionModel';
|
|
7
|
+
import { SubscriptionState, useSubscriptionModel } from './useSubscriptionModel';
|
|
8
|
+
import { MockCheckoutPreviewCallback } from '../types';
|
|
8
9
|
|
|
9
10
|
function mapBillingInformation({
|
|
10
11
|
billingAddress,
|
|
@@ -25,79 +26,121 @@ function mapBillingInformation({
|
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
type UsePreviewSubscriptionProps = {
|
|
30
|
+
onMockCheckoutPreview?: MockCheckoutPreviewCallback;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type PreviewSubscriptionProps = {
|
|
34
|
+
customerId?: string;
|
|
35
|
+
planId?: string;
|
|
36
|
+
resourceId?: string;
|
|
37
|
+
stigg: StiggClient;
|
|
38
|
+
} & SubscriptionState &
|
|
39
|
+
UsePreviewSubscriptionProps;
|
|
40
|
+
|
|
41
|
+
const previewSubscription = async ({
|
|
42
|
+
stigg,
|
|
43
|
+
customerId,
|
|
44
|
+
planId,
|
|
45
|
+
resourceId,
|
|
46
|
+
promotionCode,
|
|
47
|
+
addons,
|
|
48
|
+
billableFeatures,
|
|
49
|
+
billingCountryCode,
|
|
50
|
+
billingPeriod,
|
|
51
|
+
billingAddress,
|
|
52
|
+
taxPercentage,
|
|
53
|
+
onMockCheckoutPreview,
|
|
54
|
+
}: PreviewSubscriptionProps) => {
|
|
55
|
+
const estimateAddons = addons.map(({ addon, quantity }) => ({ addonId: addon.id, quantity }));
|
|
56
|
+
let subscriptionPreview: SubscriptionPreviewV2 | null = null;
|
|
57
|
+
let errorMessage: string | null = null;
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
if (customerId && planId) {
|
|
61
|
+
const previewSubscriptionProps: PreviewSubscription = {
|
|
62
|
+
customerId,
|
|
63
|
+
planId,
|
|
64
|
+
resourceId,
|
|
65
|
+
billingCountryCode,
|
|
66
|
+
addons: estimateAddons,
|
|
67
|
+
billingPeriod,
|
|
68
|
+
promotionCode,
|
|
69
|
+
billableFeatures: isEmpty(billableFeatures) ? undefined : billableFeatures,
|
|
70
|
+
...mapBillingInformation({ billingAddress, taxPercentage }),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
subscriptionPreview = onMockCheckoutPreview
|
|
74
|
+
? onMockCheckoutPreview(previewSubscriptionProps)
|
|
75
|
+
: await stigg.previewSubscription(previewSubscriptionProps);
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
const [, errorMsg] = (error as any)?.message?.split('Error:') || [];
|
|
79
|
+
|
|
80
|
+
errorMessage = errorMsg?.trim();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { subscriptionPreview, errorMessage };
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const usePreviewSubscriptionAction = ({ onMockCheckoutPreview }: UsePreviewSubscriptionProps = {}) => {
|
|
29
87
|
const { stigg } = useStiggContext();
|
|
30
88
|
const subscription = useSubscriptionModel();
|
|
31
|
-
const [{ resourceId
|
|
32
|
-
const { checkoutState } = useCheckoutModel();
|
|
89
|
+
const [{ resourceId }] = useCheckoutContext();
|
|
90
|
+
const { checkoutState, widgetState } = useCheckoutModel();
|
|
33
91
|
const { plan, customer } = checkoutState || {};
|
|
34
92
|
|
|
35
93
|
const previewSubscriptionAction = useCallback(
|
|
36
94
|
async ({ promotionCode }: { promotionCode?: string | null } = {}) => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
let errorMessage: string | null = null;
|
|
40
|
-
|
|
41
|
-
let isValid = !subscription.billableFeatures.some(({ quantity }) => quantity === null || quantity <= 0);
|
|
42
|
-
isValid = isValid && !estimateAddons.some(({ quantity }) => quantity === null || quantity <= 0);
|
|
43
|
-
if (!isValid) {
|
|
44
|
-
return { subscriptionPreview };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
if (customer?.id && plan?.id) {
|
|
49
|
-
const previewSubscriptionProps: PreviewSubscription = {
|
|
50
|
-
customerId: customer.id,
|
|
51
|
-
planId: plan?.id,
|
|
52
|
-
billableFeatures: isEmpty(subscription.billableFeatures) ? undefined : subscription.billableFeatures,
|
|
53
|
-
addons: estimateAddons,
|
|
54
|
-
billingPeriod: subscription.billingPeriod,
|
|
55
|
-
promotionCode: promotionCode ?? subscription.promotionCode,
|
|
56
|
-
resourceId,
|
|
57
|
-
billingCountryCode: planStep.billingCountryCode,
|
|
58
|
-
...mapBillingInformation({
|
|
59
|
-
billingAddress: subscription.billingAddress,
|
|
60
|
-
taxPercentage: subscription.taxPercentage,
|
|
61
|
-
}),
|
|
62
|
-
};
|
|
63
|
-
subscriptionPreview = await stigg.previewSubscription(previewSubscriptionProps);
|
|
64
|
-
}
|
|
65
|
-
} catch (error) {
|
|
66
|
-
const [, errorMsg] = (error as any)?.message?.split('Error:') || [];
|
|
67
|
-
|
|
68
|
-
errorMessage = errorMsg?.trim();
|
|
95
|
+
if (!widgetState.isValid) {
|
|
96
|
+
return { subscriptionPreview: null, errorMessage: null };
|
|
69
97
|
}
|
|
70
98
|
|
|
71
|
-
return {
|
|
99
|
+
return previewSubscription({
|
|
100
|
+
stigg,
|
|
101
|
+
customerId: customer?.id,
|
|
102
|
+
planId: plan?.id,
|
|
103
|
+
resourceId,
|
|
104
|
+
addons: subscription.addons,
|
|
105
|
+
billableFeatures: subscription.billableFeatures,
|
|
106
|
+
billingCountryCode: subscription.billingCountryCode,
|
|
107
|
+
billingPeriod: subscription.billingPeriod,
|
|
108
|
+
billingAddress: subscription.billingAddress,
|
|
109
|
+
taxPercentage: subscription.taxPercentage,
|
|
110
|
+
promotionCode: promotionCode ?? subscription.promotionCode,
|
|
111
|
+
onMockCheckoutPreview,
|
|
112
|
+
});
|
|
72
113
|
},
|
|
73
114
|
[
|
|
74
|
-
customer,
|
|
75
|
-
plan,
|
|
76
|
-
resourceId,
|
|
77
115
|
stigg,
|
|
116
|
+
customer?.id,
|
|
117
|
+
plan?.id,
|
|
118
|
+
resourceId,
|
|
78
119
|
subscription.addons,
|
|
79
|
-
subscription.billingPeriod,
|
|
80
120
|
subscription.billableFeatures,
|
|
81
|
-
subscription.
|
|
121
|
+
subscription.billingCountryCode,
|
|
122
|
+
subscription.billingPeriod,
|
|
82
123
|
subscription.billingAddress,
|
|
83
124
|
subscription.taxPercentage,
|
|
84
|
-
|
|
125
|
+
subscription.promotionCode,
|
|
126
|
+
widgetState.isValid,
|
|
127
|
+
onMockCheckoutPreview,
|
|
85
128
|
],
|
|
86
129
|
);
|
|
87
130
|
|
|
88
131
|
return { previewSubscriptionAction };
|
|
89
132
|
};
|
|
90
133
|
|
|
91
|
-
|
|
92
|
-
|
|
134
|
+
const SUBSCRIPTION_PREVIEW_DEBOUNCE_TIME = 500;
|
|
135
|
+
|
|
136
|
+
export const usePreviewSubscription = ({ onMockCheckoutPreview }: UsePreviewSubscriptionProps = {}) => {
|
|
137
|
+
const [subscriptionPreview, setSubscriptionPreview] = useState<SubscriptionPreviewV2 | null>(null);
|
|
93
138
|
const [isFetchingSubscriptionPreview, setIsFetchingSubscriptionPreview] = useState(false);
|
|
94
139
|
|
|
95
|
-
const { previewSubscriptionAction } = usePreviewSubscriptionAction();
|
|
140
|
+
const { previewSubscriptionAction } = usePreviewSubscriptionAction({ onMockCheckoutPreview });
|
|
96
141
|
|
|
97
142
|
useEffect(() => {
|
|
98
143
|
const estimateSubscription = async () => {
|
|
99
|
-
setIsFetchingSubscriptionPreview(true);
|
|
100
|
-
|
|
101
144
|
const { subscriptionPreview } = await previewSubscriptionAction();
|
|
102
145
|
if (subscriptionPreview) {
|
|
103
146
|
setSubscriptionPreview(subscriptionPreview);
|
|
@@ -106,7 +149,16 @@ export const usePreviewSubscription = () => {
|
|
|
106
149
|
setIsFetchingSubscriptionPreview(false);
|
|
107
150
|
};
|
|
108
151
|
|
|
109
|
-
|
|
152
|
+
setIsFetchingSubscriptionPreview(true);
|
|
153
|
+
|
|
154
|
+
const timer = setTimeout(() => {
|
|
155
|
+
setIsFetchingSubscriptionPreview(true);
|
|
156
|
+
void estimateSubscription();
|
|
157
|
+
}, SUBSCRIPTION_PREVIEW_DEBOUNCE_TIME);
|
|
158
|
+
|
|
159
|
+
return () => {
|
|
160
|
+
clearTimeout(timer);
|
|
161
|
+
};
|
|
110
162
|
}, [previewSubscriptionAction]);
|
|
111
163
|
|
|
112
164
|
return { subscriptionPreview, isFetchingSubscriptionPreview };
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
import { Divider } from '@mui/material';
|
|
3
|
+
import { Box, Divider } from '@mui/material';
|
|
4
4
|
|
|
5
5
|
import { Typography } from '../../common/Typography';
|
|
6
6
|
import { useCheckoutModel } from '../hooks/useCheckoutModel';
|
|
7
|
-
import { PlanHeaderContainer, PlanPathContainer, StyledArrowRightIcon } from './PlanHeader.style';
|
|
8
7
|
import { CheckoutContainerProps } from '../CheckoutContainer';
|
|
9
8
|
import { ChangePlanButton } from '../components/ChangePlanButton';
|
|
10
9
|
|
|
@@ -14,37 +13,31 @@ type PlanHeaderProps = {
|
|
|
14
13
|
|
|
15
14
|
export function PlanHeader({ allowChangePlan = false, onChangePlan }: PlanHeaderProps) {
|
|
16
15
|
const { checkoutState, checkoutLocalization } = useCheckoutModel();
|
|
17
|
-
const { plan
|
|
18
|
-
const isPlanChanged = plan?.id !== activeSubscription?.plan.id;
|
|
16
|
+
const { plan } = checkoutState || {};
|
|
19
17
|
|
|
20
18
|
return (
|
|
21
19
|
<>
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<Typography variant="h6" color="secondary">
|
|
27
|
-
{activeSubscription.plan.displayName}
|
|
28
|
-
</Typography>
|
|
29
|
-
<StyledArrowRightIcon />
|
|
30
|
-
</>
|
|
31
|
-
)}
|
|
20
|
+
<Box sx={{ marginBottom: '16px' }}>
|
|
21
|
+
<Typography variant="body1" color="disabled" style={{ opacity: 0.6, marginBottom: '8px' }}>
|
|
22
|
+
Selected plan
|
|
23
|
+
</Typography>
|
|
32
24
|
|
|
25
|
+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
33
26
|
<Typography variant="h3" bold>
|
|
34
27
|
{plan?.displayName}
|
|
35
28
|
</Typography>
|
|
36
|
-
</PlanPathContainer>
|
|
37
29
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
30
|
+
{allowChangePlan && onChangePlan && (
|
|
31
|
+
<ChangePlanButton
|
|
32
|
+
onClick={() => {
|
|
33
|
+
onChangePlan({ currentPlan: plan });
|
|
34
|
+
}}
|
|
35
|
+
checkoutLocalization={checkoutLocalization}
|
|
36
|
+
size="medium"
|
|
37
|
+
/>
|
|
38
|
+
)}
|
|
39
|
+
</Box>
|
|
40
|
+
</Box>
|
|
48
41
|
<Divider className="stigg-checkout-plan-header-divider" />
|
|
49
42
|
</>
|
|
50
43
|
);
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import styled from '@emotion/styled/macro';
|
|
2
2
|
import { Button, buttonClasses, LinearProgress, linearProgressClasses } from '@mui/material';
|
|
3
|
-
import Color from 'color';
|
|
4
|
-
|
|
5
3
|
import { Icon } from '../../common/Icon';
|
|
6
4
|
|
|
7
5
|
export const StyledProgress = styled(LinearProgress, { shouldForwardProp: (prop) => !prop.startsWith('$') })<{
|
|
@@ -12,9 +10,7 @@ export const StyledProgress = styled(LinearProgress, { shouldForwardProp: (prop)
|
|
|
12
10
|
backgroundColor: theme.stigg.palette.outlinedBorder,
|
|
13
11
|
},
|
|
14
12
|
[`& .${linearProgressClasses.bar}`]: {
|
|
15
|
-
backgroundColor: $disabled
|
|
16
|
-
? Color(theme.stigg.palette.outlinedBorder).darken(0.2).hex()
|
|
17
|
-
: theme.stigg.palette.primary,
|
|
13
|
+
backgroundColor: $disabled ? theme.stigg.palette.primaryLight : theme.stigg.palette.primary,
|
|
18
14
|
},
|
|
19
15
|
}));
|
|
20
16
|
|
|
@@ -27,10 +23,13 @@ export const StyledStepButton = styled(Button)(() => ({
|
|
|
27
23
|
},
|
|
28
24
|
}));
|
|
29
25
|
|
|
30
|
-
export const StyledIcon = styled(Icon
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)
|
|
26
|
+
export const StyledIcon = styled(Icon, { shouldForwardProp: (prop) => !prop.startsWith('$') })<{
|
|
27
|
+
$disabled?: boolean;
|
|
28
|
+
$shouldStroke?: boolean;
|
|
29
|
+
$shouldFill?: boolean;
|
|
30
|
+
}>(({ theme, $disabled, $shouldStroke = true, $shouldFill }) => ({
|
|
31
|
+
circle: {
|
|
32
|
+
stroke: $shouldStroke ? ($disabled ? theme.stigg.palette.primaryLight : theme.stigg.palette.primary) : undefined,
|
|
33
|
+
fill: $shouldFill ? ($disabled ? theme.stigg.palette.primaryLight : theme.stigg.palette.primary) : undefined,
|
|
34
|
+
},
|
|
35
|
+
}));
|
|
@@ -34,15 +34,16 @@ export const CheckoutProgressBar = () => {
|
|
|
34
34
|
<Grid item display="flex" flexDirection="row" alignItems="center" gap={1}>
|
|
35
35
|
<StepIcon
|
|
36
36
|
icon={
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
<StyledIcon
|
|
38
|
+
icon={isCompleted ? checkedIcon : 'OutlinedCircle'}
|
|
39
|
+
$disabled={isDisabled}
|
|
40
|
+
$shouldStroke={(isCompleted && !isDisabled) || isDisabled}
|
|
41
|
+
$shouldFill={isCompleted}
|
|
42
|
+
/>
|
|
42
43
|
}
|
|
43
44
|
/>
|
|
44
45
|
|
|
45
|
-
<Typography variant="h6" color={isDisabled ? '
|
|
46
|
+
<Typography variant="h6" color={isDisabled ? 'primary.main.light' : 'primary.main'}>
|
|
46
47
|
{label}
|
|
47
48
|
</Typography>
|
|
48
49
|
</Grid>
|
|
@@ -9,7 +9,7 @@ import { Button, InputField } from '../components';
|
|
|
9
9
|
import { usePreviewSubscriptionAction } from '../hooks';
|
|
10
10
|
import { usePromotionCodeModel } from '../hooks/useCouponModel';
|
|
11
11
|
import { AddPromotionCodeButton } from './AddPromotionCodeButton';
|
|
12
|
-
import {
|
|
12
|
+
import { PromotionCodeSectionProps } from './PromotionCodeSection';
|
|
13
13
|
|
|
14
14
|
const CouponCodeAddButton = styled(Button)`
|
|
15
15
|
padding: 4px 10px;
|
|
@@ -19,13 +19,17 @@ const CouponCodeAddButton = styled(Button)`
|
|
|
19
19
|
align-items: center;
|
|
20
20
|
`;
|
|
21
21
|
|
|
22
|
-
export const AddPromotionCode = ({
|
|
22
|
+
export const AddPromotionCode = ({
|
|
23
|
+
disabled,
|
|
24
|
+
checkoutLocalization,
|
|
25
|
+
onMockCheckoutPreview,
|
|
26
|
+
}: PromotionCodeSectionProps) => {
|
|
23
27
|
const { setPromotionCode: persistPromotionCode } = usePromotionCodeModel();
|
|
24
28
|
const [showInput, setShowInput] = useState(false);
|
|
25
29
|
const [promotionCode, setPromotionCode] = React.useState('');
|
|
26
30
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
27
31
|
const [errorMessage, setErrorMessage] = React.useState('');
|
|
28
|
-
const { previewSubscriptionAction } = usePreviewSubscriptionAction();
|
|
32
|
+
const { previewSubscriptionAction } = usePreviewSubscriptionAction({ onMockCheckoutPreview });
|
|
29
33
|
|
|
30
34
|
const handlePromotionCode = async () => {
|
|
31
35
|
setIsLoading(true);
|
|
@@ -33,26 +37,33 @@ export const AddPromotionCode = ({ checkoutLocalization }: { checkoutLocalizatio
|
|
|
33
37
|
|
|
34
38
|
const { subscriptionPreview, errorMessage } = await previewSubscriptionAction({ promotionCode });
|
|
35
39
|
|
|
36
|
-
if (!errorMessage && subscriptionPreview?.
|
|
40
|
+
if (!errorMessage && subscriptionPreview?.discountDetails) {
|
|
37
41
|
persistPromotionCode(promotionCode.toUpperCase());
|
|
38
42
|
setShowInput(false);
|
|
39
|
-
} else if (
|
|
43
|
+
} else if (errorMessage) {
|
|
40
44
|
setErrorMessage(errorMessage);
|
|
41
45
|
}
|
|
42
46
|
setIsLoading(false);
|
|
43
47
|
};
|
|
44
48
|
|
|
45
49
|
if (!showInput) {
|
|
46
|
-
return
|
|
50
|
+
return (
|
|
51
|
+
<AddPromotionCodeButton
|
|
52
|
+
disabled={disabled}
|
|
53
|
+
onAddClick={() => setShowInput(true)}
|
|
54
|
+
checkoutLocalization={checkoutLocalization}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
47
57
|
}
|
|
48
58
|
|
|
49
59
|
return (
|
|
50
60
|
<Grid>
|
|
51
|
-
<Typography variant="body1" color={errorMessage ? 'error' : 'primary.main'}>
|
|
52
|
-
{checkoutLocalization.couponCodeTitle}
|
|
61
|
+
<Typography variant="body1" color={errorMessage ? 'error' : disabled ? 'disabled' : 'primary.main'}>
|
|
62
|
+
{checkoutLocalization.summary.couponCodeTitle}
|
|
53
63
|
</Typography>
|
|
54
64
|
|
|
55
65
|
<InputField
|
|
66
|
+
disabled={disabled}
|
|
56
67
|
autoFocus
|
|
57
68
|
variant="outlined"
|
|
58
69
|
fullWidth
|
|
@@ -62,9 +73,21 @@ export const AddPromotionCode = ({ checkoutLocalization }: { checkoutLocalizatio
|
|
|
62
73
|
setPromotionCode(e.target.value);
|
|
63
74
|
}}
|
|
64
75
|
inputProps={{ maxLength: 20 }}
|
|
76
|
+
onKeyDown={(e: any) => {
|
|
77
|
+
if (e.key === 'Enter') {
|
|
78
|
+
void handlePromotionCode();
|
|
79
|
+
e.preventDefault();
|
|
80
|
+
}
|
|
81
|
+
}}
|
|
82
|
+
// eslint-disable-next-line react/jsx-no-duplicate-props
|
|
65
83
|
InputProps={{
|
|
66
84
|
endAdornment: (
|
|
67
|
-
<CouponCodeAddButton
|
|
85
|
+
<CouponCodeAddButton
|
|
86
|
+
variant="contained"
|
|
87
|
+
disabled={disabled}
|
|
88
|
+
onClick={() => {
|
|
89
|
+
void handlePromotionCode();
|
|
90
|
+
}}>
|
|
68
91
|
{isLoading ? (
|
|
69
92
|
<CircularProgress size={18} sx={{ color: 'white' }} />
|
|
70
93
|
) : (
|
|
@@ -6,34 +6,38 @@ import { Typography } from '../../common/Typography';
|
|
|
6
6
|
import { Button } from '../components';
|
|
7
7
|
import { CheckoutLocalization } from '../textOverrides';
|
|
8
8
|
|
|
9
|
-
const StyledPlusIcon = styled(PlusIcon)
|
|
9
|
+
const StyledPlusIcon = styled(PlusIcon, { shouldForwardProp: (prop) => !prop.startsWith('$') })<{ $disabled: boolean }>`
|
|
10
10
|
path {
|
|
11
|
-
stroke: ${({ theme }) => theme.stigg.palette.primary};
|
|
11
|
+
stroke: ${({ theme, $disabled }) => ($disabled ? theme.stigg.palette.text.disabled : theme.stigg.palette.primary)};
|
|
12
12
|
}
|
|
13
13
|
`;
|
|
14
14
|
|
|
15
15
|
export type AddPromotionCodeButtonProps = {
|
|
16
16
|
onAddClick: () => void;
|
|
17
17
|
checkoutLocalization: CheckoutLocalization;
|
|
18
|
+
disabled: boolean;
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
export const AddPromotionCodeButton = ({ onAddClick, checkoutLocalization }: AddPromotionCodeButtonProps) => (
|
|
21
|
+
export const AddPromotionCodeButton = ({ onAddClick, checkoutLocalization, disabled }: AddPromotionCodeButtonProps) => (
|
|
21
22
|
<Button
|
|
23
|
+
disabled={disabled}
|
|
22
24
|
fullWidth
|
|
23
25
|
className="stigg-checkout-summary-add-coupon-code-button"
|
|
24
|
-
sx={{
|
|
26
|
+
sx={{
|
|
27
|
+
textTransform: 'none',
|
|
28
|
+
justifyContent: 'flex-start',
|
|
29
|
+
color: disabled ? 'text.disabled' : 'primary.main',
|
|
30
|
+
}}
|
|
25
31
|
variant="text"
|
|
26
32
|
size="medium"
|
|
27
|
-
onClick={onAddClick}
|
|
28
|
-
|
|
29
|
-
<StyledPlusIcon className="stigg-checkout-summary-add-coupon-code-button-icon" />
|
|
33
|
+
onClick={onAddClick}>
|
|
34
|
+
<StyledPlusIcon $disabled={disabled} className="stigg-checkout-summary-add-coupon-code-button-icon" />
|
|
30
35
|
<Typography
|
|
31
36
|
className="stigg-checkout-change-plan-button-text"
|
|
32
|
-
color=
|
|
37
|
+
color={disabled ? 'disabled' : 'primary.main'}
|
|
33
38
|
style={{ lineHeight: '24px' }}
|
|
34
|
-
variant="body1"
|
|
35
|
-
|
|
36
|
-
{checkoutLocalization.addCouponCodeText}
|
|
39
|
+
variant="body1">
|
|
40
|
+
{checkoutLocalization.summary.addCouponCodeText}
|
|
37
41
|
</Typography>
|
|
38
42
|
</Button>
|
|
39
43
|
);
|
|
@@ -11,7 +11,7 @@ const AppliedCouponContainer = styled(Grid)`
|
|
|
11
11
|
align-items: center;
|
|
12
12
|
flex-direction: row;
|
|
13
13
|
gap: 8px;
|
|
14
|
-
padding: 6px
|
|
14
|
+
padding: 6px 8px;
|
|
15
15
|
border-radius: ${({ theme }) => theme.stigg.border.radius};
|
|
16
16
|
border: 1px solid ${({ theme }) => theme.stigg.palette.primary};
|
|
17
17
|
`;
|
|
@@ -23,8 +23,9 @@ export type AppliedPromotionCodeProps = {
|
|
|
23
23
|
|
|
24
24
|
export const AppliedPromotionCode = ({ promotionCode, onClearPromotionCode }: AppliedPromotionCodeProps) => (
|
|
25
25
|
<AppliedCouponContainer container>
|
|
26
|
-
<Grid item>
|
|
27
|
-
<
|
|
26
|
+
<Grid item display="flex" gap={1} alignItems="center">
|
|
27
|
+
<Icon icon="Coupon" style={{ display: 'flex' }} />
|
|
28
|
+
<Typography variant="body1" color="primary.main" lineHeight="auto">
|
|
28
29
|
{promotionCode}
|
|
29
30
|
</Typography>
|
|
30
31
|
</Grid>
|
|
@@ -4,24 +4,38 @@ import { usePromotionCodeModel } from '../hooks/useCouponModel';
|
|
|
4
4
|
import { AddPromotionCode } from './AddPromotionCode';
|
|
5
5
|
import { AppliedPromotionCode } from './AppliedPromotionCode';
|
|
6
6
|
import { CheckoutLocalization } from '../textOverrides';
|
|
7
|
+
import { MockCheckoutPreviewCallback } from '../types';
|
|
7
8
|
|
|
8
9
|
const PromotionCodeSectionContainer = styled.div`
|
|
9
10
|
margin: 16px 0;
|
|
10
11
|
`;
|
|
11
12
|
|
|
12
|
-
export
|
|
13
|
+
export type PromotionCodeSectionProps = {
|
|
14
|
+
disabled: boolean;
|
|
15
|
+
checkoutLocalization: CheckoutLocalization;
|
|
16
|
+
onMockCheckoutPreview?: MockCheckoutPreviewCallback;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const PromotionCodeSection = ({
|
|
20
|
+
disabled,
|
|
21
|
+
checkoutLocalization,
|
|
22
|
+
onMockCheckoutPreview,
|
|
23
|
+
}: PromotionCodeSectionProps) => {
|
|
13
24
|
const { promotionCode, setPromotionCode } = usePromotionCodeModel();
|
|
14
25
|
|
|
15
26
|
const onClearPromotionCode = () => {
|
|
16
27
|
setPromotionCode('');
|
|
17
28
|
};
|
|
18
29
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
const content = promotionCode ? (
|
|
31
|
+
<AppliedPromotionCode promotionCode={promotionCode} onClearPromotionCode={onClearPromotionCode} />
|
|
32
|
+
) : (
|
|
33
|
+
<AddPromotionCode
|
|
34
|
+
disabled={disabled}
|
|
35
|
+
checkoutLocalization={checkoutLocalization}
|
|
36
|
+
onMockCheckoutPreview={onMockCheckoutPreview}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
25
39
|
|
|
26
40
|
return <PromotionCodeSectionContainer>{content}</PromotionCodeSectionContainer>;
|
|
27
41
|
};
|
|
@@ -42,7 +42,6 @@ function AddonListItem({
|
|
|
42
42
|
const isAdded = !!addonState;
|
|
43
43
|
const hasChanges =
|
|
44
44
|
(!!addonState && !!initialAddonState && addonState.quantity !== initialAddonState.quantity) ||
|
|
45
|
-
(!initialAddonState && !!addonState) ||
|
|
46
45
|
(!!initialAddonState && !addonState);
|
|
47
46
|
|
|
48
47
|
const handleQuantityChange = (quantity: number | null) => {
|
|
@@ -79,6 +78,7 @@ function AddonListItem({
|
|
|
79
78
|
{`${currencyPriceFormatter({
|
|
80
79
|
amount: addonPrice.amount!,
|
|
81
80
|
currency: addonPrice.currency,
|
|
81
|
+
minimumFractionDigits: 2,
|
|
82
82
|
})}/${billingPeriod === BillingPeriod.Annually ? 'year' : 'month'}`}
|
|
83
83
|
</Typography>
|
|
84
84
|
)}
|
|
@@ -126,7 +126,7 @@ function AddonListItem({
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
export function CheckoutAddonsStep() {
|
|
129
|
-
const { checkoutLocalization } = useCheckoutModel();
|
|
129
|
+
const { checkoutLocalization, setIsValid } = useCheckoutModel();
|
|
130
130
|
const { billingPeriod } = usePlanStepModel();
|
|
131
131
|
const { setIsDisabled } = useProgressBarModel();
|
|
132
132
|
const { initialAddons, addons, availableAddons, setAddon, removeAddon } = useAddonsStepModel();
|
|
@@ -140,7 +140,8 @@ export function CheckoutAddonsStep() {
|
|
|
140
140
|
useEffect(() => {
|
|
141
141
|
const isDisabled = Object.values(addonsValidation).some((x) => !x);
|
|
142
142
|
setIsDisabled(isDisabled);
|
|
143
|
-
|
|
143
|
+
setIsValid(!isDisabled);
|
|
144
|
+
}, [addonsValidation, setIsDisabled, setIsValid]);
|
|
144
145
|
|
|
145
146
|
return (
|
|
146
147
|
<CheckoutAddonsContainer container>
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import styled from '@emotion/styled';
|
|
2
2
|
import { Grid } from '@mui/material';
|
|
3
3
|
|
|
4
|
-
export const PaymentMethodContainer = styled(Grid
|
|
4
|
+
export const PaymentMethodContainer = styled(Grid, { shouldForwardProp: (prop) => !prop.startsWith('$') })<{
|
|
5
|
+
$disabled?: boolean;
|
|
6
|
+
}>`
|
|
5
7
|
padding: 8px;
|
|
6
8
|
border-radius: 10px;
|
|
7
9
|
border: 1px solid ${({ theme }) => theme.stigg.palette.outlinedBorder};
|
|
8
10
|
cursor: ${({ $disabled }) => ($disabled ? 'not-allowed' : 'pointer')};
|
|
11
|
+
opacity: ${({ $disabled }) => ($disabled ? 0.6 : 1)};
|
|
9
12
|
`;
|
|
10
13
|
|
|
11
14
|
export const NewPaymentMethodContainer = styled(PaymentMethodContainer)`
|
|
@@ -30,8 +30,9 @@ export const BillingPeriodButton = styled.button<{
|
|
|
30
30
|
background: ${({ theme, $isActive, $isOnlyBillingPeriod }) => {
|
|
31
31
|
if ($isOnlyBillingPeriod) {
|
|
32
32
|
return 'transparent';
|
|
33
|
-
}
|
|
34
|
-
|
|
33
|
+
}
|
|
34
|
+
if ($isActive) {
|
|
35
|
+
return theme.stigg.palette.primaryLight;
|
|
35
36
|
}
|
|
36
37
|
return 'transparent';
|
|
37
38
|
}};
|
|
@@ -49,13 +49,16 @@ type BillingPeriodPickerProps = {
|
|
|
49
49
|
checkoutLocalization: CheckoutLocalization;
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
export
|
|
52
|
+
export function BillingPeriodPicker({ plan, checkoutLocalization }: BillingPeriodPickerProps) {
|
|
53
53
|
const [monthlyPrices, annualPrices] = partition(
|
|
54
54
|
plan?.pricePoints,
|
|
55
55
|
(price) => price.billingPeriod === BillingPeriod.Monthly,
|
|
56
56
|
);
|
|
57
57
|
|
|
58
58
|
const hasBothBillingPeriods = !!monthlyPrices?.length && !!annualPrices?.length;
|
|
59
|
+
if (!hasBothBillingPeriods) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
59
62
|
|
|
60
63
|
return (
|
|
61
64
|
<BillingPeriodPickerContainer>
|
|
@@ -81,4 +84,4 @@ export const BillingPeriodPicker = ({ plan, checkoutLocalization }: BillingPerio
|
|
|
81
84
|
</BillingPeriodOptions>
|
|
82
85
|
</BillingPeriodPickerContainer>
|
|
83
86
|
);
|
|
84
|
-
}
|
|
87
|
+
}
|