@stigg/react-sdk 4.8.1 → 4.10.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/customerPortal/subscriptionOverview/subscriptionView/SubscriptionView.d.ts +1 -1
- package/dist/components/customerPortal/usage/featureUsage/EntitlementCTAButton.d.ts +1 -1
- package/dist/components/paywall/Paywall.d.ts +3 -2
- package/dist/components/paywall/PaywallContainer.d.ts +3 -2
- package/dist/components/paywall/types.d.ts +3 -0
- package/dist/components/paywall/utils/getPlansToDisplay.d.ts +2 -2
- package/dist/react-sdk.cjs.development.js +30 -23
- 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 +30 -23
- package/dist/react-sdk.esm.js.map +1 -1
- package/package.json +7 -4
- package/src/components/checkout/hooks/useCouponModel.ts +1 -1
- package/src/components/checkout/steps/addons/addon.utils.ts +6 -6
- package/src/components/common/PoweredByStigg.tsx +1 -2
- package/src/components/common/iconColor.ts +2 -4
- package/src/components/customerPortal/CustomerPortal.style.ts +2 -2
- package/src/components/customerPortal/CustomerPortalProvider.tsx +1 -2
- package/src/components/customerPortal/billing/InformationGrid.tsx +1 -2
- package/src/components/customerPortal/billing/InvoicesSection.tsx +9 -4
- package/src/components/customerPortal/common/ExternalLinkButton.tsx +2 -3
- package/src/components/customerPortal/common/SectionContainer.tsx +1 -1
- package/src/components/customerPortal/common/SectionHeader.ts +4 -2
- package/src/components/customerPortal/common/SkeletonButton.tsx +1 -1
- package/src/components/customerPortal/subscriptionOverview/ContactCustomerSupport.tsx +1 -2
- package/src/components/customerPortal/subscriptionOverview/SubscriptionsOverview.tsx +1 -2
- package/src/components/customerPortal/subscriptionOverview/SubscriptionsOverviewHeader.tsx +3 -8
- package/src/components/customerPortal/subscriptionOverview/charges/ChargeItem.tsx +1 -1
- package/src/components/customerPortal/subscriptionOverview/charges/ChargeList.tsx +1 -1
- package/src/components/customerPortal/subscriptionOverview/subscriptionView/SubscriptionView.tsx +8 -4
- package/src/components/customerPortal/subscriptionOverview/tabs/AddonsList.tsx +1 -1
- package/src/components/customerPortal/subscriptionOverview/tabs/Promotions.tsx +1 -1
- package/src/components/customerPortal/subscriptionOverview/tabs/SubscriptionTabs.tsx +2 -2
- package/src/components/customerPortal/subscriptionOverview/upcomingBilling/NoUpcomingBilling.tsx +4 -2
- package/src/components/customerPortal/subscriptionOverview/upcomingBilling/UpcomingBilling.tsx +1 -1
- package/src/components/customerPortal/types.ts +2 -2
- package/src/components/customerPortal/usage/featureUsage/EntitlementCTAButton.tsx +10 -3
- package/src/components/customerPortal/usage/featureUsage/FeatureUsage.style.ts +1 -1
- package/src/components/customerPortal/usage/featureUsage/FeatureUsage.tsx +2 -4
- package/src/components/hooks/useChargeSort.ts +2 -2
- package/src/components/paywall/Paywall.tsx +4 -2
- package/src/components/paywall/PaywallContainer.tsx +4 -1
- package/src/components/paywall/PlanEntitlements.tsx +4 -4
- package/src/components/paywall/paywallTextOverrides.ts +2 -2
- package/src/components/paywall/types.ts +2 -0
- package/src/components/paywall/utils/computeDefaultBillingPeriod.ts +2 -2
- package/src/components/paywall/utils/getPlansToDisplay.ts +10 -4
- package/src/components/paywall/utils/hasPricePoints.ts +3 -3
- package/src/components/utils/calculateDiscountRate.ts +3 -3
- package/src/components/utils/getFeatureName.ts +7 -3
- package/src/components/utils/getPlanPrice.ts +2 -2
- package/src/components/utils/priceTierUtils.ts +6 -4
- package/src/stories/Paywall.stories.tsx +1 -0
- package/src/types.ts +1 -1
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.
|
|
2
|
+
"version": "4.10.0",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"typings": "dist/index.d.ts",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"lint-fix": "yarn run lint --fix",
|
|
19
19
|
"size": "size-limit",
|
|
20
20
|
"analyze": "size-limit --why",
|
|
21
|
+
"fix:prettier": "prettier \"src/**/*.(js|jsx|ts|tsx)\" --write",
|
|
22
|
+
"pretty-quick": "pretty-quick --staged --pattern 'src/**/*.*(js|jsx|ts|tsx)'",
|
|
21
23
|
"storybook": "export NODE_OPTIONS=--openssl-legacy-provider && start-storybook -p 6006",
|
|
22
24
|
"build-storybook": "build-storybook",
|
|
23
25
|
"prepare": "husky install",
|
|
@@ -87,14 +89,15 @@
|
|
|
87
89
|
"eslint-plugin-import": "^2.28.1",
|
|
88
90
|
"eslint-plugin-jest": "^25.3.2",
|
|
89
91
|
"eslint-plugin-jsx-a11y": "^6.3.1",
|
|
90
|
-
"eslint-plugin-prettier": "^
|
|
92
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
91
93
|
"eslint-plugin-react": "^7.28.0",
|
|
92
94
|
"eslint-plugin-react-hooks": "^4.2.0",
|
|
93
95
|
"eslint-plugin-unused-imports": "^2.0.0",
|
|
94
96
|
"husky": "^7.0.4",
|
|
95
97
|
"lint-staged": "^14.0.1",
|
|
96
98
|
"postcss": "^8.4.24",
|
|
97
|
-
"prettier": "^
|
|
99
|
+
"prettier": "^2.4.1",
|
|
100
|
+
"pretty-quick": "^3.1.3",
|
|
98
101
|
"react": "^18.0.0",
|
|
99
102
|
"react-dom": "^18.0.0",
|
|
100
103
|
"rollup-plugin-postcss": "^4.0.2",
|
|
@@ -119,10 +122,10 @@
|
|
|
119
122
|
"immer": "^10.0.2",
|
|
120
123
|
"lodash": "^4.17.21",
|
|
121
124
|
"lodash-es": "^4.17.21",
|
|
125
|
+
"lottie-react": "^2.4.0",
|
|
122
126
|
"moment": "^2.29.4",
|
|
123
127
|
"react-feather": "^2.0.10",
|
|
124
128
|
"react-loading-skeleton": "^3.1.0",
|
|
125
|
-
"lottie-react": "^2.4.0",
|
|
126
129
|
"react-spinners": "^0.13.3",
|
|
127
130
|
"react-switch": "^7.0.0",
|
|
128
131
|
"styled-components": "^5.3.6",
|
|
@@ -23,8 +23,8 @@ export function filterAddons({
|
|
|
23
23
|
}: { addons?: Addon[] } & filterAddonsBaseProps): Addon[] {
|
|
24
24
|
return (
|
|
25
25
|
addons
|
|
26
|
-
?.filter(addon => filterAddonPricePointsByBillingPeriod(addon, billingPeriod))
|
|
27
|
-
?.map(addon => mapAddonPricePointsByBillingCountryCode({ addon, currency, billingCountryCode })) || []
|
|
26
|
+
?.filter((addon) => filterAddonPricePointsByBillingPeriod(addon, billingPeriod))
|
|
27
|
+
?.map((addon) => mapAddonPricePointsByBillingCountryCode({ addon, currency, billingCountryCode })) || []
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -36,8 +36,8 @@ export function filterSubscriptionAddons({
|
|
|
36
36
|
}: { addons?: SubscriptionAddon[] } & filterAddonsBaseProps): SubscriptionAddon[] {
|
|
37
37
|
return (
|
|
38
38
|
addons
|
|
39
|
-
?.filter(addon => filterAddonPricePointsByBillingPeriod(addon.addon, billingPeriod))
|
|
40
|
-
?.map(addon => ({
|
|
39
|
+
?.filter((addon) => filterAddonPricePointsByBillingPeriod(addon.addon, billingPeriod))
|
|
40
|
+
?.map((addon) => ({
|
|
41
41
|
...addon,
|
|
42
42
|
addon: mapAddonPricePointsByBillingCountryCode({ addon: addon.addon, currency, billingCountryCode }),
|
|
43
43
|
})) || []
|
|
@@ -45,7 +45,7 @@ export function filterSubscriptionAddons({
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function filterAddonPricePointsByBillingPeriod(addon: Addon, billingPeriod: BillingPeriod) {
|
|
48
|
-
return addon.pricePoints.some(pricePoint => pricePoint.billingPeriod === billingPeriod);
|
|
48
|
+
return addon.pricePoints.some((pricePoint) => pricePoint.billingPeriod === billingPeriod);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function mapAddonPricePointsByBillingCountryCode({
|
|
@@ -60,7 +60,7 @@ function mapAddonPricePointsByBillingCountryCode({
|
|
|
60
60
|
return {
|
|
61
61
|
...addon,
|
|
62
62
|
pricePoints: addon.pricePoints.filter(
|
|
63
|
-
pricePoint =>
|
|
63
|
+
(pricePoint) =>
|
|
64
64
|
pricePoint.currency === currency &&
|
|
65
65
|
(billingCountryCode ? pricePoint.billingCountryCode === billingCountryCode : true),
|
|
66
66
|
),
|
|
@@ -45,8 +45,7 @@ export function PoweredByStigg({ source, showWatermark, align, style = {} }: Pow
|
|
|
45
45
|
$alignSelf={align}
|
|
46
46
|
className={STIGG_WATERMARK_CLASSNAME}
|
|
47
47
|
style={{ cursor: 'pointer', ...style }}
|
|
48
|
-
onClick={onWatermarkClick}
|
|
49
|
-
>
|
|
48
|
+
onClick={onWatermarkClick}>
|
|
50
49
|
<PoweredByStiggThemedSvg />
|
|
51
50
|
</StyledPoweredByStigg>
|
|
52
51
|
);
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { Theme } from '@emotion/react';
|
|
2
2
|
|
|
3
|
-
export type IconColor =
|
|
4
|
-
| 'primary'
|
|
5
|
-
| 'secondary'
|
|
3
|
+
export type IconColor = 'primary' | 'secondary';
|
|
6
4
|
|
|
7
|
-
export const getIconColor = (color: IconColor | string | undefined, theme: Theme
|
|
5
|
+
export const getIconColor = (color: IconColor | string | undefined, theme: Theme) => {
|
|
8
6
|
switch (color) {
|
|
9
7
|
case 'primary':
|
|
10
8
|
return theme.stigg.palette.text.primary;
|
|
@@ -5,7 +5,7 @@ export const CustomerPortalLayout = styled.div`
|
|
|
5
5
|
display: flex;
|
|
6
6
|
flex-direction: column;
|
|
7
7
|
align-items: center;
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
& * {
|
|
10
10
|
box-sizing: border-box;
|
|
11
11
|
}
|
|
@@ -17,4 +17,4 @@ export const CustomerPortalSections = styled.div`
|
|
|
17
17
|
flex-direction: column;
|
|
18
18
|
align-items: center;
|
|
19
19
|
row-gap: 24px;
|
|
20
|
-
`;
|
|
20
|
+
`;
|
|
@@ -42,8 +42,7 @@ export function InformationGrid({ title, rows }: InformationGridProps) {
|
|
|
42
42
|
variant="body1"
|
|
43
43
|
color="secondary"
|
|
44
44
|
style={{ width: 80 }}
|
|
45
|
-
{...labelTypographyProps}
|
|
46
|
-
>
|
|
45
|
+
{...labelTypographyProps}>
|
|
47
46
|
{label}
|
|
48
47
|
</Typography>
|
|
49
48
|
<Typography className={`${classNamePrefix}-text`} variant="body1" color="secondary" {...valueTypographyProps}>
|
|
@@ -10,7 +10,9 @@ export function InvoicesSection() {
|
|
|
10
10
|
const { customerPortal, isLoading, textOverrides, theme } = useCustomerPortalContext();
|
|
11
11
|
const isLoadingData = !customerPortal || isLoading;
|
|
12
12
|
|
|
13
|
-
const viewInvoiceHistoryButton = isLoadingData ?
|
|
13
|
+
const viewInvoiceHistoryButton = isLoadingData ? (
|
|
14
|
+
<SkeletonButton />
|
|
15
|
+
) : customerPortal?.billingPortalUrl ? (
|
|
14
16
|
<ExternalLinkButton
|
|
15
17
|
url={customerPortal.billingPortalUrl}
|
|
16
18
|
text={textOverrides.viewInvoiceHistory}
|
|
@@ -26,10 +28,13 @@ export function InvoicesSection() {
|
|
|
26
28
|
<SectionContainer
|
|
27
29
|
className="stigg-invoices-section-layout"
|
|
28
30
|
$backgroundColor={theme.backgroundColor}
|
|
29
|
-
$borderColor={theme.borderColor}
|
|
30
|
-
>
|
|
31
|
+
$borderColor={theme.borderColor}>
|
|
31
32
|
<SectionHeader className="stigg-invoices-section-header">
|
|
32
|
-
<SectionTitle
|
|
33
|
+
<SectionTitle
|
|
34
|
+
isLoading={isLoadingData}
|
|
35
|
+
className="stigg-invoices-section-title"
|
|
36
|
+
title={textOverrides.invoicesTitle}
|
|
37
|
+
/>
|
|
33
38
|
{viewInvoiceHistoryButton}
|
|
34
39
|
</SectionHeader>
|
|
35
40
|
</SectionContainer>
|
|
@@ -28,9 +28,8 @@ export function ExternalLinkButton({ text, url, className }: ExternalLinkButtonP
|
|
|
28
28
|
className={className}
|
|
29
29
|
variant="outlined"
|
|
30
30
|
startIcon={<LinkIcon size={20} strokeWidth={2.5} />}
|
|
31
|
-
onClick={onClick}
|
|
32
|
-
>
|
|
31
|
+
onClick={onClick}>
|
|
33
32
|
<ExternalLinkText color="primary.main">{text}</ExternalLinkText>
|
|
34
33
|
</StyledButton>
|
|
35
34
|
);
|
|
36
|
-
}
|
|
35
|
+
}
|
|
@@ -5,9 +5,11 @@ export const SectionHeader = styled.div<{ $disableMargin?: boolean }>`
|
|
|
5
5
|
align-items: center;
|
|
6
6
|
justify-content: space-between;
|
|
7
7
|
|
|
8
|
-
${({ $disableMargin }) =>
|
|
8
|
+
${({ $disableMargin }) =>
|
|
9
|
+
!$disableMargin &&
|
|
10
|
+
`
|
|
9
11
|
&:not(:last-child) {
|
|
10
12
|
margin-bottom: 32px;
|
|
11
13
|
}
|
|
12
14
|
`};
|
|
13
|
-
`;
|
|
15
|
+
`;
|
|
@@ -38,8 +38,7 @@ export function ContactCustomerSupport({ onContactSupport, label, linkLabel }: C
|
|
|
38
38
|
className="stigg-contact-support-button"
|
|
39
39
|
style={{ textTransform: 'none' }}
|
|
40
40
|
variant="text"
|
|
41
|
-
onClick={onContactSupport}
|
|
42
|
-
>
|
|
41
|
+
onClick={onContactSupport}>
|
|
43
42
|
{contactSupportText}
|
|
44
43
|
</ContactSupportButton>
|
|
45
44
|
) : (
|
|
@@ -42,8 +42,7 @@ export function SubscriptionsOverview({
|
|
|
42
42
|
<SectionContainer
|
|
43
43
|
className="stigg-customer-portal-subscriptions-overview"
|
|
44
44
|
$backgroundColor={theme.backgroundColor}
|
|
45
|
-
$borderColor={theme.borderColor}
|
|
46
|
-
>
|
|
45
|
+
$borderColor={theme.borderColor}>
|
|
47
46
|
{!isLoadingData && activeSubscription && activeSubscription.scheduledUpdates && (
|
|
48
47
|
<SubscriptionScheduledUpdatesAlert
|
|
49
48
|
theme={theme}
|
|
@@ -28,20 +28,15 @@ export function SubscriptionsOverviewHeader({
|
|
|
28
28
|
}: SubscriptionsOverviewHeaderProps) {
|
|
29
29
|
return (
|
|
30
30
|
<SectionHeader className="stigg-customer-portal-overview-header" $disableMargin>
|
|
31
|
-
<SectionTitle title=
|
|
31
|
+
<SectionTitle title="Subscription" className="stigg-overview-title" />
|
|
32
32
|
{!hideManageButton && (
|
|
33
|
-
<StyledButton
|
|
34
|
-
className="stigg-manage-subscription-button"
|
|
35
|
-
variant="outlined"
|
|
36
|
-
onClick={onManageSubscription}
|
|
37
|
-
>
|
|
33
|
+
<StyledButton className="stigg-manage-subscription-button" variant="outlined" onClick={onManageSubscription}>
|
|
38
34
|
<StyledEditIcon className="stigg-manage-subscription-button-image" />
|
|
39
35
|
<Typography
|
|
40
36
|
className="stigg-manage-subscription-button-text"
|
|
41
37
|
color="primary.main"
|
|
42
38
|
style={{ marginLeft: '8px' }}
|
|
43
|
-
variant="body1"
|
|
44
|
-
>
|
|
39
|
+
variant="body1">
|
|
45
40
|
{textOverrides?.manageSubscription}
|
|
46
41
|
</Typography>
|
|
47
42
|
</StyledButton>
|
|
@@ -32,7 +32,7 @@ export function ChargeItem({
|
|
|
32
32
|
hasCustomSubscription,
|
|
33
33
|
}: UsageBasedChargeProps) {
|
|
34
34
|
return (
|
|
35
|
-
<div className=
|
|
35
|
+
<div className="stigg-charge-list-item" style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
|
36
36
|
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 12 }}>
|
|
37
37
|
<div style={{ display: 'flex', gap: 12 }}>
|
|
38
38
|
<LongText variant="body1" color="primary">
|
|
@@ -32,7 +32,7 @@ export function ChargeList({
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
|
-
<div className=
|
|
35
|
+
<div className="stigg-charge-list" style={{ display: 'flex', flexDirection: 'column', gap: 16, marginTop: 32 }}>
|
|
36
36
|
{filteredSubscriptionPrices.map(({ feature, price, billingModel }) => {
|
|
37
37
|
if (!feature || !price) {
|
|
38
38
|
return null;
|
package/src/components/customerPortal/subscriptionOverview/subscriptionView/SubscriptionView.tsx
CHANGED
|
@@ -11,15 +11,19 @@ type SubscriptionViewProps = {
|
|
|
11
11
|
theme: CustomerPortalTheme;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
export function SubscriptionView({
|
|
14
|
+
export function SubscriptionView({
|
|
15
|
+
mainSubscription,
|
|
16
|
+
trialSubscription,
|
|
17
|
+
onManageSubscription,
|
|
18
|
+
theme,
|
|
19
|
+
}: SubscriptionViewProps) {
|
|
15
20
|
return (
|
|
16
21
|
<div className="stigg-subscription-view-layout">
|
|
17
22
|
<Typography
|
|
18
23
|
className="stigg-subscription-plan-name"
|
|
19
|
-
variant=
|
|
24
|
+
variant="h1"
|
|
20
25
|
overrideColor={theme.planNameColor}
|
|
21
|
-
style={{ marginTop: 8 }}
|
|
22
|
-
>
|
|
26
|
+
style={{ marginTop: 8 }}>
|
|
23
27
|
{mainSubscription.planName} plan
|
|
24
28
|
</Typography>
|
|
25
29
|
|
|
@@ -6,7 +6,7 @@ import { useCustomerPortalContext } from '../../CustomerPortalProvider';
|
|
|
6
6
|
|
|
7
7
|
export function AddonsList() {
|
|
8
8
|
const { customerPortal } = useCustomerPortalContext();
|
|
9
|
-
const addons = flatMap(customerPortal?.subscriptions, subscription => subscription.addons);
|
|
9
|
+
const addons = flatMap(customerPortal?.subscriptions, (subscription) => subscription.addons);
|
|
10
10
|
if (isEmpty(addons)) {
|
|
11
11
|
return null;
|
|
12
12
|
}
|
|
@@ -47,7 +47,7 @@ export function Promotions() {
|
|
|
47
47
|
Expiration date
|
|
48
48
|
</Typography>
|
|
49
49
|
</Row>
|
|
50
|
-
{promotionalEntitlements?.map(promotionalEntitlement => (
|
|
50
|
+
{promotionalEntitlements?.map((promotionalEntitlement) => (
|
|
51
51
|
<Row $borderColor={theme.borderColor}>
|
|
52
52
|
<Typography variant="body1">{getPromotionTitle(promotionalEntitlement)}</Typography>
|
|
53
53
|
<Typography variant="body1" color="disabled">
|
|
@@ -50,9 +50,9 @@ function TabTitle({ isSelected, label, icon }: { isSelected: boolean; label: str
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export function SubscriptionTabs({ customerPortal, hiddenSections, textOverrides, theme }: SubscriptionTabsProps) {
|
|
53
|
-
const allAddons = flatMap(customerPortal.subscriptions, subscription => subscription.addons);
|
|
53
|
+
const allAddons = flatMap(customerPortal.subscriptions, (subscription) => subscription.addons);
|
|
54
54
|
const isSectionHidden = (sectionName: CustomerPortalSection) =>
|
|
55
|
-
hiddenSections?.some(section => section === sectionName);
|
|
55
|
+
hiddenSections?.some((section) => section === sectionName);
|
|
56
56
|
const showAddons = allAddons.length > 0 && !isSectionHidden('addons');
|
|
57
57
|
const showPromotions =
|
|
58
58
|
customerPortal.promotionalEntitlements.length > 0 && !isSectionHidden('promotionalEntitlements');
|
package/src/components/customerPortal/subscriptionOverview/upcomingBilling/NoUpcomingBilling.tsx
CHANGED
|
@@ -8,8 +8,10 @@ type NoUpcomingBillingProps = {
|
|
|
8
8
|
export function NoUpcomingBilling({ description }: NoUpcomingBillingProps) {
|
|
9
9
|
return (
|
|
10
10
|
<>
|
|
11
|
-
<Typography variant=
|
|
12
|
-
|
|
11
|
+
<Typography variant="h6" color="secondary">
|
|
12
|
+
No upcoming bill
|
|
13
|
+
</Typography>
|
|
14
|
+
<Typography variant="body1" color="secondary">
|
|
13
15
|
{description}
|
|
14
16
|
</Typography>
|
|
15
17
|
</>
|
package/src/components/customerPortal/subscriptionOverview/upcomingBilling/UpcomingBilling.tsx
CHANGED
|
@@ -35,7 +35,7 @@ export function UpcomingBilling({ subscription, theme, textOverrides, onContactS
|
|
|
35
35
|
const { pricingType } = subscription;
|
|
36
36
|
let content: ReactNode;
|
|
37
37
|
const noUsageBasedPrices =
|
|
38
|
-
subscription.prices?.every(price => price.billingModel !== BillingModel.UsageBased) ?? true;
|
|
38
|
+
subscription.prices?.every((price) => price.billingModel !== BillingModel.UsageBased) ?? true;
|
|
39
39
|
const scheduledDowngradeToFree = subscription.scheduledUpdates?.find(
|
|
40
40
|
({ subscriptionScheduleType, targetPackage }) =>
|
|
41
41
|
subscriptionScheduleType === SubscriptionScheduleType.Downgrade &&
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { CustomerPortalEntitlement } from '@stigg/js-client-sdk';
|
|
2
|
-
import { FeatureFragment
|
|
2
|
+
import { FeatureFragment } from '@stigg/api-client-js/src/generated/sdk';
|
|
3
3
|
|
|
4
|
-
export type OnBuyMoreCallbackFn = (feature: FeatureFragment, entitlement: CustomerPortalEntitlement) => void
|
|
4
|
+
export type OnBuyMoreCallbackFn = (feature: FeatureFragment, entitlement: CustomerPortalEntitlement) => void;
|
|
@@ -23,8 +23,15 @@ export type EntitlementCtaButtonProps = {
|
|
|
23
23
|
hasCustomSubscription: boolean;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
export function EntitlementCtaButton({
|
|
27
|
-
|
|
26
|
+
export function EntitlementCtaButton({
|
|
27
|
+
entitlement,
|
|
28
|
+
feature,
|
|
29
|
+
onManageSubscription,
|
|
30
|
+
onBuyMore,
|
|
31
|
+
canBuyMore,
|
|
32
|
+
canUpgradeSubscription,
|
|
33
|
+
hasCustomSubscription,
|
|
34
|
+
}: EntitlementCtaButtonProps) {
|
|
28
35
|
if (hasCustomSubscription || entitlement.hasUnlimitedUsage) {
|
|
29
36
|
return null;
|
|
30
37
|
}
|
|
@@ -50,4 +57,4 @@ export function EntitlementCtaButton({ entitlement, feature, onManageSubscriptio
|
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
return null;
|
|
53
|
-
}
|
|
60
|
+
}
|
|
@@ -51,8 +51,7 @@ export function FeatureUsage({
|
|
|
51
51
|
? 'Billed for committed usage'
|
|
52
52
|
: 'Billed for actual usage'}
|
|
53
53
|
</Typography>
|
|
54
|
-
}
|
|
55
|
-
>
|
|
54
|
+
}>
|
|
56
55
|
<Icon icon="DollarCoin" svgStrokeColor={theme.iconsColor} />
|
|
57
56
|
</InformationTooltip>
|
|
58
57
|
)}
|
|
@@ -62,8 +61,7 @@ export function FeatureUsage({
|
|
|
62
61
|
<LongText
|
|
63
62
|
variant="body1"
|
|
64
63
|
color={hasOverageUsage ? 'error' : 'primary'}
|
|
65
|
-
data-testid={`usage-data-${entitlement.feature?.refId}`}
|
|
66
|
-
>
|
|
64
|
+
data-testid={`usage-data-${entitlement.feature?.refId}`}>
|
|
67
65
|
{entitlement.currentUsage?.toLocaleString()}
|
|
68
66
|
{!entitlement.hasUnlimitedUsage &&
|
|
69
67
|
` / ${entitlement.usageLimit?.toLocaleString()} (${getUsagePercentage(
|
|
@@ -7,8 +7,8 @@ const CHARGES_BILLING_MODEL_ORDER = [BillingModel.FlatFee, BillingModel.PerUnit,
|
|
|
7
7
|
|
|
8
8
|
export const sortCharges = (charges: Price[]) => {
|
|
9
9
|
return sortBy(charges, [
|
|
10
|
-
charge => CHARGES_BILLING_MODEL_ORDER.indexOf(charge.pricingModel),
|
|
11
|
-
charge => charge.feature?.displayName,
|
|
10
|
+
(charge) => CHARGES_BILLING_MODEL_ORDER.indexOf(charge.pricingModel),
|
|
11
|
+
(charge) => charge.feature?.displayName,
|
|
12
12
|
]);
|
|
13
13
|
};
|
|
14
14
|
|
|
@@ -4,7 +4,7 @@ import styled from '@emotion/styled/macro';
|
|
|
4
4
|
import { PlanOffering } from './PlanOffering';
|
|
5
5
|
import { BillingPeriodPicker } from './BillingPeriodPicker';
|
|
6
6
|
import { calculatePaywallDiscountRate } from '../utils/calculateDiscountRate';
|
|
7
|
-
import { OnPlanSelectedCallbackFn, PaywallPlan, SubscribeIntentionType } from './types';
|
|
7
|
+
import { ShouldHidePlanFn, OnPlanSelectedCallbackFn, PaywallPlan, SubscribeIntentionType } from './types';
|
|
8
8
|
import { PaywallLocalization } from './paywallTextOverrides';
|
|
9
9
|
import { PoweredByStigg } from '../common/PoweredByStigg';
|
|
10
10
|
import { useStiggContext } from '../..';
|
|
@@ -51,6 +51,7 @@ type PaywallProps = {
|
|
|
51
51
|
onPlanSelected: OnPlanSelectedCallbackFn;
|
|
52
52
|
paywallLocale: PaywallLocalization;
|
|
53
53
|
locale: string;
|
|
54
|
+
shouldHidePlan?: ShouldHidePlanFn;
|
|
54
55
|
};
|
|
55
56
|
|
|
56
57
|
export const Paywall = ({
|
|
@@ -65,13 +66,14 @@ export const Paywall = ({
|
|
|
65
66
|
onPlanSelected,
|
|
66
67
|
paywallLocale,
|
|
67
68
|
locale,
|
|
69
|
+
shouldHidePlan,
|
|
68
70
|
}: PaywallProps) => {
|
|
69
71
|
const { stigg } = useStiggContext();
|
|
70
72
|
const discountRate = calculatePaywallDiscountRate(plans);
|
|
71
73
|
const shouldShowDescriptionSection = plans.some((plan) => !!plan.description);
|
|
72
74
|
const hasMonthlyPrice = hasPricePointsForPlans(plans, BillingPeriod.Monthly);
|
|
73
75
|
const hasAnnuallyPrice = hasPricePointsForPlans(plans, BillingPeriod.Annually);
|
|
74
|
-
const plansToShow = getPlansToDisplay(plans, selectedBillingPeriod);
|
|
76
|
+
const plansToShow = getPlansToDisplay(plans, selectedBillingPeriod, shouldHidePlan);
|
|
75
77
|
|
|
76
78
|
const handleOnSubscribe = useCallback(
|
|
77
79
|
(plan: Plan, intentionType: SubscribeIntentionType, billableFeatures: BillableFeature[]) => {
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { BillingPeriod } from '@stigg/js-client-sdk';
|
|
3
3
|
import { Paywall } from './Paywall';
|
|
4
4
|
import { useLoadPaywallData } from './hooks/useLoadPaywallData';
|
|
5
|
-
import { OnPlanSelectedCallbackFn } from './types';
|
|
5
|
+
import { ShouldHidePlanFn, OnPlanSelectedCallbackFn } from './types';
|
|
6
6
|
import { getResolvedPaywallLocalize, PaywallLocalization } from './paywallTextOverrides';
|
|
7
7
|
import { DeepPartial } from '../../types';
|
|
8
8
|
import { PaywallLoader } from './PaywallLoader';
|
|
@@ -23,6 +23,7 @@ export type PaywallContainerProps = {
|
|
|
23
23
|
onBillingPeriodChange?: (billingPeriod: BillingPeriod) => void;
|
|
24
24
|
textOverrides?: DeepPartial<PaywallLocalization>;
|
|
25
25
|
billingCountryCode?: string;
|
|
26
|
+
shouldHidePlan?: ShouldHidePlanFn;
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
export const PaywallContainer = ({
|
|
@@ -35,6 +36,7 @@ export const PaywallContainer = ({
|
|
|
35
36
|
preferredBillingPeriod,
|
|
36
37
|
onBillingPeriodChange,
|
|
37
38
|
billingCountryCode,
|
|
39
|
+
shouldHidePlan,
|
|
38
40
|
}: PaywallContainerProps) => {
|
|
39
41
|
const hasCustomerPortalContext = useCheckContextExists(CustomerPortalContext);
|
|
40
42
|
let isCustomerPortalLoading = false;
|
|
@@ -86,6 +88,7 @@ export const PaywallContainer = ({
|
|
|
86
88
|
onPlanSelected={onPlanSelected}
|
|
87
89
|
paywallLocale={paywallLocale}
|
|
88
90
|
locale={locale}
|
|
91
|
+
shouldHidePlan={shouldHidePlan}
|
|
89
92
|
/>
|
|
90
93
|
);
|
|
91
94
|
|
|
@@ -33,8 +33,8 @@ export function PlanEntitlements({
|
|
|
33
33
|
billingPeriod: BillingPeriod;
|
|
34
34
|
paywallLocale: PaywallLocalization;
|
|
35
35
|
}) {
|
|
36
|
-
const prices = plan.pricePoints?.filter(price => price.billingPeriod === billingPeriod);
|
|
37
|
-
const unitBasedEntitlements = prices?.map(price => {
|
|
36
|
+
const prices = plan.pricePoints?.filter((price) => price.billingPeriod === billingPeriod);
|
|
37
|
+
const unitBasedEntitlements = prices?.map((price) => {
|
|
38
38
|
const shouldRenderUnitBasedEntitlement =
|
|
39
39
|
(price?.minUnitQuantity && price?.minUnitQuantity > 1) || price?.maxUnitQuantity;
|
|
40
40
|
|
|
@@ -59,8 +59,8 @@ export function PlanEntitlements({
|
|
|
59
59
|
</Typography>
|
|
60
60
|
{unitBasedEntitlements}
|
|
61
61
|
{plan.entitlements
|
|
62
|
-
.filter(entitlement => !(entitlement.hiddenFromWidgets || []).includes(WidgetType.Paywall))
|
|
63
|
-
.map(entitlement => (
|
|
62
|
+
.filter((entitlement) => !(entitlement.hiddenFromWidgets || []).includes(WidgetType.Paywall))
|
|
63
|
+
.map((entitlement) => (
|
|
64
64
|
<EntitlementRow
|
|
65
65
|
key={entitlement.feature?.id}
|
|
66
66
|
displayNameOverride={entitlement.displayNameOverride}
|
|
@@ -51,7 +51,7 @@ export function getResolvedPaywallLocalize(localizeOverride?: DeepPartial<Paywal
|
|
|
51
51
|
custom: 'Contact us',
|
|
52
52
|
currentPlan: 'Current plan',
|
|
53
53
|
startNew: 'Get started',
|
|
54
|
-
switchToBillingPeriod: billingPeriod => {
|
|
54
|
+
switchToBillingPeriod: (billingPeriod) => {
|
|
55
55
|
return billingPeriod === BillingPeriod.Monthly ? 'Switch to monthly billing' : 'Switch to annual billing';
|
|
56
56
|
},
|
|
57
57
|
cancelScheduledUpdate: 'Cancel',
|
|
@@ -59,7 +59,7 @@ export function getResolvedPaywallLocalize(localizeOverride?: DeepPartial<Paywal
|
|
|
59
59
|
price: {
|
|
60
60
|
startingAtCaption: 'Starts at',
|
|
61
61
|
pricePeriod: (billingPeriod: BillingPeriod) => (billingPeriod === BillingPeriod.Monthly ? '/ month' : '/ year'),
|
|
62
|
-
free: currency => ({
|
|
62
|
+
free: (currency) => ({
|
|
63
63
|
price: `${currency?.symbol}0`,
|
|
64
64
|
}),
|
|
65
65
|
custom: 'Custom',
|
|
@@ -6,7 +6,7 @@ export function computeBillingPeriods(
|
|
|
6
6
|
activeSubscriptions?: Subscription[] | null,
|
|
7
7
|
preferredBillingPeriod?: BillingPeriod,
|
|
8
8
|
): { defaultBillingPeriod: BillingPeriod; availableBillingPeriods: BillingPeriod[] } {
|
|
9
|
-
const billingPeriods = plans.flatMap(x => x.pricePoints).map(x => x.billingPeriod);
|
|
9
|
+
const billingPeriods = plans.flatMap((x) => x.pricePoints).map((x) => x.billingPeriod);
|
|
10
10
|
const counts = countBy(billingPeriods);
|
|
11
11
|
|
|
12
12
|
const availableBillingPeriods = uniq(billingPeriods);
|
|
@@ -15,7 +15,7 @@ export function computeBillingPeriods(
|
|
|
15
15
|
if (preferredBillingPeriod && availableBillingPeriods.includes(preferredBillingPeriod)) {
|
|
16
16
|
defaultBillingPeriod = preferredBillingPeriod;
|
|
17
17
|
} else if (activeSubscriptions) {
|
|
18
|
-
const activeSubscription = activeSubscriptions.find(x => x.status == SubscriptionStatus.Active);
|
|
18
|
+
const activeSubscription = activeSubscriptions.find((x) => x.status == SubscriptionStatus.Active);
|
|
19
19
|
|
|
20
20
|
if (activeSubscription && activeSubscription.prices.length > 0) {
|
|
21
21
|
defaultBillingPeriod = activeSubscription?.prices[0].billingPeriod;
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { BillingPeriod, PricingType } from '@stigg/js-client-sdk';
|
|
2
|
-
import { PaywallPlan } from '../types';
|
|
2
|
+
import { ShouldHidePlanFn, PaywallPlan } from '../types';
|
|
3
3
|
import { hasPricePoints } from './hasPricePoints';
|
|
4
4
|
|
|
5
|
-
export function getPlansToDisplay(
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
export function getPlansToDisplay(
|
|
6
|
+
plans: PaywallPlan[],
|
|
7
|
+
selectedBillingPeriod: BillingPeriod,
|
|
8
|
+
shouldHidePlan?: ShouldHidePlanFn,
|
|
9
|
+
): PaywallPlan[] {
|
|
10
|
+
return plans
|
|
11
|
+
.filter((plan) => plan.pricingType !== PricingType.Paid || hasPricePoints(plan, selectedBillingPeriod))
|
|
12
|
+
.filter((plan) => !shouldHidePlan || !shouldHidePlan({ plan }));
|
|
13
|
+
}
|
|
@@ -2,9 +2,9 @@ import { PaywallPlan } from '../types';
|
|
|
2
2
|
import { BillingPeriod } from '@stigg/js-client-sdk';
|
|
3
3
|
|
|
4
4
|
export function hasPricePointsForPlans(plans: PaywallPlan[], billingPeriod: BillingPeriod): boolean {
|
|
5
|
-
return plans.some(plan => hasPricePoints(plan, billingPeriod));
|
|
5
|
+
return plans.some((plan) => hasPricePoints(plan, billingPeriod));
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function hasPricePoints(plan: PaywallPlan, billingPeriod: BillingPeriod): boolean {
|
|
9
|
-
return plan.pricePoints.some(pricePoint => pricePoint.billingPeriod === billingPeriod);
|
|
10
|
-
}
|
|
9
|
+
return plan.pricePoints.some((pricePoint) => pricePoint.billingPeriod === billingPeriod);
|
|
10
|
+
}
|
|
@@ -14,14 +14,14 @@ export function calculateDiscountRate(monthlyPrice?: number | null, annuallyPric
|
|
|
14
14
|
function getPlanBillingPeriodAmount(plan: PaywallPlan, billingPeriod: BillingPeriod) {
|
|
15
15
|
let pricePoint: PaywallCalculatedPricePoint | Price | undefined;
|
|
16
16
|
|
|
17
|
-
pricePoint = plan.paywallCalculatedPricePoints?.find(price => price.billingPeriod === billingPeriod);
|
|
17
|
+
pricePoint = plan.paywallCalculatedPricePoints?.find((price) => price.billingPeriod === billingPeriod);
|
|
18
18
|
|
|
19
19
|
if (!pricePoint) {
|
|
20
|
-
pricePoint = plan.pricePoints.find(price => price.billingPeriod === billingPeriod);
|
|
20
|
+
pricePoint = plan.pricePoints.find((price) => price.billingPeriod === billingPeriod);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
if (!pricePoint?.amount) {
|
|
24
|
-
const tieredPrice = plan.pricePoints.find(price => {
|
|
24
|
+
const tieredPrice = plan.pricePoints.find((price) => {
|
|
25
25
|
return price.isTieredPrice && price.billingPeriod === billingPeriod;
|
|
26
26
|
});
|
|
27
27
|
|